Procházet zdrojové kódy

Three patch version update, types update, Fix image source being -1 in gltf export, use writer.checkEmptyMap in gltf extensions during export, Add FBXLoader2 which loads invalid image with default 1px white image, Support loading embedded files of various extensions in GLTFLoader by plugins, add SVGTextureLoader, Add AssetImporter.WHITE_IMAGE_DATA,

master
Palash Bansal před 1 rokem
rodič
revize
4657f635e1
Žádný účet není propojen s e-mailovou adresou tvůrce revize

+ 7
- 7
package.json Zobrazit soubor

"rimraf": "^5.0.1", "rimraf": "^5.0.1",
"rollup-plugin-glsl": "^1.3.0", "rollup-plugin-glsl": "^1.3.0",
"rollup-plugin-license": "^3.0.1", "rollup-plugin-license": "^3.0.1",
"three": "https://github.com/repalash/three.js-modded/releases/download/v0.157.1004/package.tgz",
"three": "https://github.com/repalash/three.js-modded/releases/download/v0.157.1007/package.tgz",
"tslib": "^2.5.0", "tslib": "^2.5.0",
"typedoc": "^0.27.5", "typedoc": "^0.27.5",
"typescript": "5.7.2", "typescript": "5.7.2",
"vitepress-plugin-nprogress": "^0.0.4" "vitepress-plugin-nprogress": "^0.0.4"
}, },
"dependencies": { "dependencies": {
"@types/three": "https://github.com/repalash/three-ts-types/releases/download/v0.157.1004/package.tgz",
"@types/three": "https://github.com/repalash/three-ts-types/releases/download/v0.157.1005/package.tgz",
"@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",
"popmotion": "^11.0.5" "popmotion": "^11.0.5"
}, },
"peerDependencies": { "peerDependencies": {
"three": "https://github.com/repalash/three.js-modded/releases/download/v0.157.1004/package.tgz"
"three": "https://github.com/repalash/three.js-modded/releases/download/v0.157.1007/package.tgz"
}, },
"peerDependenciesMeta": { "peerDependenciesMeta": {
"three": { "three": {
"dependencies": { "dependencies": {
"uiconfig.js": "^0.1.3", "uiconfig.js": "^0.1.3",
"ts-browser-helpers": "^0.16.2", "ts-browser-helpers": "^0.16.2",
"three": "https://github.com/repalash/three.js-modded/releases/download/v0.157.1004/package.tgz",
"three-f": "https://github.com/repalash/three.js-modded/archive/refs/tags/v0.157.1004.tar.gz",
"@types/three": "https://github.com/repalash/three-ts-types/releases/download/v0.157.1004/package.tgz",
"@types/three-f": "https://github.com/repalash/three-ts-types/archive/refs/tags/v0.157.1004.tar.gz",
"three": "https://github.com/repalash/three.js-modded/releases/download/v0.157.1007/package.tgz",
"three-f": "https://github.com/repalash/three.js-modded/archive/refs/tags/v0.157.1007.tar.gz",
"@types/three": "https://github.com/repalash/three-ts-types/releases/download/v0.157.1005/package.tgz",
"@types/three-f": "https://github.com/repalash/three-ts-types/archive/refs/tags/v0.157.1005.tar.gz",
"@types/three-pkg": "https://gitpkg.now.sh/repalash/three-ts-types/types/three?modded_three" "@types/three-pkg": "https://gitpkg.now.sh/repalash/three-ts-types/types/three?modded_three"
}, },
"local_dependencies": { "local_dependencies": {

+ 2
- 0
src/assetmanager/AssetImporter.ts Zobrazit soubor

private _fileDatabase: Map<string, IFile> = new Map<string, IFile>() private _fileDatabase: Map<string, IFile> = new Map<string, IFile>()
private _cachedAssets: IAsset[] = [] private _cachedAssets: IAsset[] = []


static WHITE_IMAGE_DATA = new ImageData(new Uint8ClampedArray([255, 255, 255, 255]), 1, 1)

readonly importers: IImporter[] = [ readonly importers: IImporter[] = [
// new Importer(VideoTextureLoader, ['mp4', 'ogg', 'mov', 'data:video'], false), // new Importer(VideoTextureLoader, ['mp4', 'ogg', 'mov', 'data:video'], false),
new Importer(SimpleJSONLoader, ['json', 'vjson'], ['application/json'], false), new Importer(SimpleJSONLoader, ['json', 'vjson'], ['application/json'], false),

+ 16
- 5
src/assetmanager/AssetManager.ts Zobrazit soubor

} from '../core' } from '../core'
import {Importer} from './Importer' import {Importer} from './Importer'
import {MaterialManager} from './MaterialManager' import {MaterialManager} from './MaterialManager'
import {DRACOLoader2, GLTFLoader2, JSONMaterialLoader, MTLLoader2, OBJLoader2, ZipLoader} from './import'
import {
DRACOLoader2,
FBXLoader2,
GLTFLoader2,
JSONMaterialLoader,
MTLLoader2,
OBJLoader2,
SVGTextureLoader,
ZipLoader,
} from './import'
import {RGBELoader} from 'three/examples/jsm/loaders/RGBELoader.js' import {RGBELoader} from 'three/examples/jsm/loaders/RGBELoader.js'
import {FBXLoader} from 'three/examples/jsm/loaders/FBXLoader.js'
import {EXRLoader} from 'three/examples/jsm/loaders/EXRLoader.js' import {EXRLoader} from 'three/examples/jsm/loaders/EXRLoader.js'
import {Class, ValOrArr} from 'ts-browser-helpers' import {Class, ValOrArr} from 'ts-browser-helpers'
import {ILoader} from './IImporter' import {ILoader} from './IImporter'
const viewer = this.viewer const viewer = this.viewer
if (!viewer) return if (!viewer) return


// todo fix - loading manager getHandler matches backwards?
const importers: Importer[] = [ const importers: Importer[] = [
new Importer(TextureLoader, ['webp', 'png', 'jpeg', 'jpg', 'svg', 'ico', 'data:image', 'avif'], [
'image/webp', 'image/png', 'image/jpeg', 'image/svg+xml', 'image/gif', 'image/bmp', 'image/tiff', 'image/x-icon', 'image/avif',
new Importer(SVGTextureLoader, ['svg', 'data:image/svg'], ['image/svg+xml'], false), // todo: use ImageBitmapLoader if supported (better performance)

new Importer(TextureLoader, ['webp', 'png', 'jpeg', 'jpg', 'ico', 'data:image', 'avif'], [
'image/webp', 'image/png', 'image/jpeg', 'image/gif', 'image/bmp', 'image/tiff', 'image/x-icon', 'image/avif',
], false), // todo: use ImageBitmapLoader if supported (better performance) ], false), // todo: use ImageBitmapLoader if supported (better performance)


new Importer<JSONMaterialLoader>(JSONMaterialLoader, new Importer<JSONMaterialLoader>(JSONMaterialLoader,
} }
}, ['exr'], ['image/x-exr'], false), }, ['exr'], ['image/x-exr'], false),


new Importer(FBXLoader, ['fbx'], ['model/fbx'], true),
new Importer(FBXLoader2, ['fbx'], ['model/fbx'], true),
new Importer(ZipLoader, ['zip', 'glbz', 'gltfz'], ['application/zip', 'model/gltf+zip', 'model/zip'], true), // gltfz and glbz are invented zip files with gltf/glb inside along with resources new Importer(ZipLoader, ['zip', 'glbz', 'gltfz'], ['application/zip', 'model/gltf+zip', 'model/zip'], true), // gltfz and glbz are invented zip files with gltf/glb inside along with resources


new Importer(OBJLoader2 as any as Class<ILoader>, ['obj'], ['model/obj'], true), new Importer(OBJLoader2 as any as Class<ILoader>, ['obj'], ['model/obj'], true),

+ 3
- 1
src/assetmanager/export/GLTFExporter2.ts Zobrazit soubor

maxTextureSize: options.maxTextureSize ?? Infinity, maxTextureSize: options.maxTextureSize ?? Infinity,
animations: options.animations ?? [], animations: options.animations ?? [],
includeCustomExtensions: options.includeCustomExtensions ?? true, includeCustomExtensions: options.includeCustomExtensions ?? true,
forceIndices: options.forceIndices ?? false,
forceIndices: options.forceIndices ?? false, // todo implement
exporterOptions: options, exporterOptions: options,
ignoreInvalidMorphTargetTracks: options.ignoreInvalidMorphTargetTracks,
ignoreEmptyTextures: options.ignoreEmptyTextures,
} }
if (options.exportExt === 'glb') { if (options.exportExt === 'glb') {
gltfOptions.binary = true gltfOptions.binary = true

+ 1
- 0
src/assetmanager/export/GLTFWriter2.ts Zobrazit soubor

} }
if (textureDef.source < 0) { if (textureDef.source < 0) {
console.error('textureDef.source cannot be saved', textureDef, map) console.error('textureDef.source cannot be saved', textureDef, map)
delete textureDef.source // gltf spec allows undefined, not -1
} }


return processed return processed

+ 1
- 1
src/assetmanager/gltf/GLTFMaterialsAlphaMapExtension.ts Zobrazit soubor



const extensionDef: any = {} const extensionDef: any = {}


if (material.alphaMap) {
if (material.alphaMap && writer.checkEmptyMap(material.alphaMap)) {


const alphaMapDef = {index: writer.processTexture(material.alphaMap)} const alphaMapDef = {index: writer.processTexture(material.alphaMap)}
writer.applyTextureTransform(alphaMapDef, material.alphaMap) writer.applyTextureTransform(alphaMapDef, material.alphaMap)

+ 1
- 1
src/assetmanager/gltf/GLTFMaterialsBumpMapExtension.ts Zobrazit soubor



extensionDef.bumpScale = material.bumpScale extensionDef.bumpScale = material.bumpScale


if (material.bumpMap) {
if (material.bumpMap && writer.checkEmptyMap(material.bumpMap)) {


const bumpMapDef = {index: writer.processTexture(material.bumpMap)} const bumpMapDef = {index: writer.processTexture(material.bumpMap)}
writer.applyTextureTransform(bumpMapDef, material.bumpMap) writer.applyTextureTransform(bumpMapDef, material.bumpMap)

+ 1
- 1
src/assetmanager/gltf/GLTFMaterialsDisplacementMapExtension.ts Zobrazit soubor

extensionDef.displacementScale = material.displacementScale extensionDef.displacementScale = material.displacementScale
extensionDef.displacementBias = material.displacementBias extensionDef.displacementBias = material.displacementBias


if (material.displacementMap) {
if (material.displacementMap && writer.checkEmptyMap(material.displacementMap)) {


const displacementMapDef = {index: writer.processTexture(material.displacementMap)} const displacementMapDef = {index: writer.processTexture(material.displacementMap)}
writer.applyTextureTransform(displacementMapDef, material.displacementMap) writer.applyTextureTransform(displacementMapDef, material.displacementMap)

+ 1
- 1
src/assetmanager/gltf/GLTFMaterialsLightMapExtension.ts Zobrazit soubor



extensionDef.lightMapIntensity = material.lightMapIntensity extensionDef.lightMapIntensity = material.lightMapIntensity


if (material.lightMap) {
if (material.lightMap && writer.checkEmptyMap(material.lightMap)) {


const lightMapDef = {index: writer.processTexture(material.lightMap)} const lightMapDef = {index: writer.processTexture(material.lightMap)}
writer.applyTextureTransform(lightMapDef, material.lightMap) writer.applyTextureTransform(lightMapDef, material.lightMap)

+ 23
- 0
src/assetmanager/import/FBXLoader2.ts Zobrazit soubor

import {FBXLoader} from 'three/examples/jsm/loaders/FBXLoader.js'
import {Group, Texture} from 'three'
import {AssetImporter} from '../AssetImporter'

/**
* Extended FBXLoader that sets the default image from AssetImporter (for invalid/missing textures)
*/
export class FBXLoader2 extends FBXLoader {
async loadAsync(url: string, onProgress?: (event: ProgressEvent) => void): Promise<Group> {

const val = Texture.DEFAULT_IMAGE

// this will be used when doing new Texture(). Which is done for not found images or when some error happens in loading. See FBXLoader.
// todo save the path of invalid textures, check if they can be found in the loaded libs, and ask the user in UI to remap it to something else manually
if (!Texture.DEFAULT_IMAGE) Texture.DEFAULT_IMAGE = AssetImporter.WHITE_IMAGE_DATA

const res = await super.loadAsync(url, onProgress)

Texture.DEFAULT_IMAGE = val

return res
}
}

+ 35
- 10
src/assetmanager/import/GLTFLoader2.ts Zobrazit soubor

import type {GLTF, GLTFLoaderPlugin, GLTFParser} from 'three/examples/jsm/loaders/GLTFLoader.js' import type {GLTF, GLTFLoaderPlugin, GLTFParser} from 'three/examples/jsm/loaders/GLTFLoader.js'
import {GLTFLoader} from 'three/examples/jsm/loaders/GLTFLoader.js' import {GLTFLoader} from 'three/examples/jsm/loaders/GLTFLoader.js'
import {LoadingManager, Object3D, OrthographicCamera} from 'three'
import {LoadingManager, Object3D, OrthographicCamera, Texture} from 'three'
import {AnyOptions, safeSetProperty} from 'ts-browser-helpers' import {AnyOptions, safeSetProperty} from 'ts-browser-helpers'
import {ThreeViewer} from '../../viewer' import {ThreeViewer} from '../../viewer'
import {generateUUID} from '../../three' import {generateUUID} from '../../three'
UnlitLineMaterial, UnlitLineMaterial,
UnlitMaterial, UnlitMaterial,
} from '../../core' } from '../../core'
import {AssetImporter} from '../AssetImporter'

// todo move somewhere
const supportedEmbeddedFiles = ['hdr', 'exr', 'webp', 'avif', 'ktx', 'hdrpng', 'svg', 'cube'] // ktx2, drc handled separately


export class GLTFLoader2 extends GLTFLoader implements ILoader<GLTF, Object3D|undefined> { export class GLTFLoader2 extends GLTFLoader implements ILoader<GLTF, Object3D|undefined> {
isGLTFLoader2 = true isGLTFLoader2 = true


parse(data: ArrayBuffer | string, path: string, onLoad: (gltf: GLTF) => void, onError?: (event: ErrorEvent) => void, url?: string) { parse(data: ArrayBuffer | string, path: string, onLoad: (gltf: GLTF) => void, onError?: (event: ErrorEvent) => void, url?: string) {
this.preparse.call(this, data, url || path) this.preparse.call(this, data, url || path)
.then((res: ArrayBuffer | string) => res ? super.parse(res, path, onLoad, onError) : onError && onError(new ErrorEvent('no data')))
.then((res: ArrayBuffer|string) => {
const val = Texture.DEFAULT_IMAGE

// this will be used when doing new Texture(). Which is done for not found images or when some error happens in loading. See FBXLoader.
// todo save the path of invalid textures, check if they can be found in the loaded libs, and ask the user in UI to remap it to something else manually
if (!Texture.DEFAULT_IMAGE) Texture.DEFAULT_IMAGE = AssetImporter.WHITE_IMAGE_DATA

return res ? super.parse(res, path, (ret)=>{
Texture.DEFAULT_IMAGE = val
onLoad && onLoad(ret)
}, onError) : onError && onError(new ErrorEvent('no data'))
})
.catch((e: any) => { .catch((e: any) => {
console.error(e) console.error(e)
if (onError) onError(e ?? new ErrorEvent('unknown error')) if (onError) onError(e ?? new ErrorEvent('unknown error'))
console.error('Add GLTFMeshOptPlugin(and initialize it) to viewer to enable EXT_meshopt_compression decode') console.error('Add GLTFMeshOptPlugin(and initialize it) to viewer to enable EXT_meshopt_compression decode')
} }
} }
const needsBasisU = parser.json?.extensionsUsed?.includes?.('KHR_texture_basisu')
if (needsBasisU) {
const ktx2 = viewer.assetManager.importer.registerFile(tempPathKtx2)
if (ktx2) {
this.setKTX2Loader(ktx2 as any) // todo: check class?
parser.options.ktx2Loader = ktx2 as any
}

// create ktx2 loader so it can be used with getHandler, we need to do this even when extension is not used since we dont know
const ktx2 = viewer.assetManager.importer.registerFile(tempPathKtx2)
// const needsBasisU = parser.json?.extensionsUsed?.includes?.('KHR_texture_basisu')
// if (needsBasisU) {
// const ktx2 = viewer.assetManager.importer.registerFile(tempPathKtx2)
if (ktx2) {
this.setKTX2Loader(ktx2 as any) // todo: check class?
parser.options.ktx2Loader = ktx2 as any
} }
// }

// registering temp file creates and makes a loader available to the loading manager of that type
const tempFiles = supportedEmbeddedFiles.map(f=>generateUUID() + '.' + f)
tempFiles.forEach(f=>viewer.assetManager.importer.registerFile(f))

return {name: 'GLTF2_HELPER_PLUGIN', afterRoot: async(result: GLTF) => { return {name: 'GLTF2_HELPER_PLUGIN', afterRoot: async(result: GLTF) => {
if (needsDrc) viewer.assetManager.importer.unregisterFile(tempPathDrc) if (needsDrc) viewer.assetManager.importer.unregisterFile(tempPathDrc)
if (needsBasisU) viewer.assetManager.importer.unregisterFile(tempPathKtx2)
if (ktx2) viewer.assetManager.importer.unregisterFile(tempPathKtx2)
tempFiles.forEach(f=>viewer.assetManager.importer.unregisterFile(f))

await GLTFViewerConfigExtension.ImportViewerConfig(parser, viewer, result.scenes || [result.scene]) await GLTFViewerConfigExtension.ImportViewerConfig(parser, viewer, result.scenes || [result.scene])
}} }}
} }

+ 1
- 1
src/assetmanager/import/Rhino3dmLoader2.ts Zobrazit soubor

}) })
return super._createMaterial(material) return super._createMaterial(material)
} }
private _compareMaterials!: (material: Material) => Material
private declare _compareMaterials: (material: Material) => Material


async loadAsync(url: string, onProgress?: (event: ProgressEvent) => void): Promise<Object3D> { async loadAsync(url: string, onProgress?: (event: ProgressEvent) => void): Promise<Object3D> {
const ret = await super.loadAsync(url, onProgress) const ret = await super.loadAsync(url, onProgress)

+ 69
- 0
src/assetmanager/import/SVGTextureLoader.ts Zobrazit soubor

import {CanvasTexture, ImageLoader, Loader, LoadingManager, Texture} from 'three'
import {getUrlQueryParam} from 'ts-browser-helpers'

/**
* Same as TextureLoader but loads SVG images, fixes issues with windows not loading svg files without a defined size.
* See - https://github.com/mrdoob/three.js/issues/30899
*
* todo - create example for test, see sample code in gh issue.
*/
class SVGTextureLoader extends Loader {

constructor(manager: LoadingManager) {

super(manager)

}

static USE_CANVAS_TEXTURE = getUrlQueryParam('svg-load-disable-canvas') !== 'true'

load(url: string, onLoad: (texture: Texture) => void, onProgress?: (event: ProgressEvent) => void, onError?: (err: unknown) => void): Texture {

const canvas = SVGTextureLoader.USE_CANVAS_TEXTURE ? document.createElement('canvas') : undefined
const texture = SVGTextureLoader.USE_CANVAS_TEXTURE ? new CanvasTexture(canvas!) : new Texture()

const loader = new ImageLoader(this.manager)
loader.setCrossOrigin(this.crossOrigin)
loader.setPath(this.path)

loader.load(url, function(image) {

if (canvas) {
SVGTextureLoader.CopyImageToCanvas(canvas, image)
} else {

texture.image = image

}
texture.needsUpdate = true

if (onLoad !== undefined) {

onLoad(texture)

}

}, onProgress, onError)

return texture

}

static CopyImageToCanvas(canvas: HTMLCanvasElement, image: HTMLImageElement) {
// size can be scaled here, this is based on the viewBox aspect ratio and minimum size of 150hx300w
canvas.width = image.naturalWidth || image.width || 512
canvas.height = image.naturalHeight || image.height || 512

const ctx = canvas.getContext('2d')
if (ctx) {
ctx.clearRect(0, 0, canvas.width, canvas.height)
ctx.drawImage(image, 0, 0, canvas.width, canvas.height)
} else {
console.error('SVGTextureLoader: Failed to get canvas context.')
}
}

}


export {SVGTextureLoader}

+ 12
- 0
src/plugins/export/AssetExporterPlugin.ts Zobrazit soubor

embedUrlImages: false, embedUrlImages: false,
encrypt: false, encrypt: false,
encryptKey: '', encryptKey: '',
ignoreInvalidMorphTargetTracks: true,
ignoreEmptyTextures: true,
} }


async exportScene(options?: ExportAssetOptions): Promise<BlobExt | undefined> { async exportScene(options?: ExportAssetOptions): Promise<BlobExt | undefined> {
// label: 'Convert to indexed', // label: 'Convert to indexed',
// property: [this.exportOptions, 'convertMeshToIndexed'], // property: [this.exportOptions, 'convertMeshToIndexed'],
// }, // },
{
type: 'checkbox',
label: 'Ignore invalid animations',
property: [this.exportOptions, 'ignoreInvalidMorphTargetTracks'],
},
{
type: 'checkbox',
label: 'Ignore invalid textures',
property: [this.exportOptions, 'ignoreInvalidTextures'],
},
{ {
type: 'button', type: 'button',
label: 'Export GLB', label: 'Export GLB',

+ 1
- 1
src/plugins/material/CustomBumpMapPlugin.ts Zobrazit soubor



extensionDef.customBumpScale = material.userData._customBumpScale || 1.0 extensionDef.customBumpScale = material.userData._customBumpScale || 1.0


if (material.userData._customBumpMap) {
if (w.checkEmptyMap(material.userData._customBumpMap)) {


const customBumpMapDef = {index: w.processTexture(material.userData._customBumpMap)} const customBumpMapDef = {index: w.processTexture(material.userData._customBumpMap)}
w.applyTextureTransform(customBumpMapDef, material.userData._customBumpMap) w.applyTextureTransform(customBumpMapDef, material.userData._customBumpMap)

Načítá se…
Zrušit
Uložit