浏览代码

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 1年前
父节点
当前提交
4657f635e1
没有帐户链接到提交者的电子邮件

+ 7
- 7
package.json 查看文件

@@ -107,7 +107,7 @@
"rimraf": "^5.0.1",
"rollup-plugin-glsl": "^1.3.0",
"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",
"typedoc": "^0.27.5",
"typescript": "5.7.2",
@@ -118,7 +118,7 @@
"vitepress-plugin-nprogress": "^0.0.4"
},
"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/wicg-file-system-access": "^2020.9.5",
"stats.js": "^0.17.0",
@@ -127,7 +127,7 @@
"popmotion": "^11.0.5"
},
"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": {
"three": {
@@ -138,10 +138,10 @@
"dependencies": {
"uiconfig.js": "^0.1.3",
"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"
},
"local_dependencies": {

+ 2
- 0
src/assetmanager/AssetImporter.ts 查看文件

@@ -68,6 +68,8 @@ export class AssetImporter extends EventDispatcher<IAssetImporterEventMap> imple
private _fileDatabase: Map<string, IFile> = new Map<string, IFile>()
private _cachedAssets: IAsset[] = []

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

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

+ 16
- 5
src/assetmanager/AssetManager.ts 查看文件

@@ -39,9 +39,17 @@ import {
} from '../core'
import {Importer} from './Importer'
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 {FBXLoader} from 'three/examples/jsm/loaders/FBXLoader.js'
import {EXRLoader} from 'three/examples/jsm/loaders/EXRLoader.js'
import {Class, ValOrArr} from 'ts-browser-helpers'
import {ILoader} from './IImporter'
@@ -279,9 +287,12 @@ export class AssetManager extends EventDispatcher<AssetManagerEventMap> {
const viewer = this.viewer
if (!viewer) return

// todo fix - loading manager getHandler matches backwards?
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)

new Importer<JSONMaterialLoader>(JSONMaterialLoader,
@@ -305,7 +316,7 @@ export class AssetManager extends EventDispatcher<AssetManagerEventMap> {
}
}, ['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(OBJLoader2 as any as Class<ILoader>, ['obj'], ['model/obj'], true),

+ 3
- 1
src/assetmanager/export/GLTFExporter2.ts 查看文件

@@ -158,8 +158,10 @@ export class GLTFExporter2 extends GLTFExporter implements IExportParser {
maxTextureSize: options.maxTextureSize ?? Infinity,
animations: options.animations ?? [],
includeCustomExtensions: options.includeCustomExtensions ?? true,
forceIndices: options.forceIndices ?? false,
forceIndices: options.forceIndices ?? false, // todo implement
exporterOptions: options,
ignoreInvalidMorphTargetTracks: options.ignoreInvalidMorphTargetTracks,
ignoreEmptyTextures: options.ignoreEmptyTextures,
}
if (options.exportExt === 'glb') {
gltfOptions.binary = true

+ 1
- 0
src/assetmanager/export/GLTFWriter2.ts 查看文件

@@ -228,6 +228,7 @@ export class GLTFWriter2 extends GLTFExporter.Utils.GLTFWriter {
}
if (textureDef.source < 0) {
console.error('textureDef.source cannot be saved', textureDef, map)
delete textureDef.source // gltf spec allows undefined, not -1
}

return processed

+ 1
- 1
src/assetmanager/gltf/GLTFMaterialsAlphaMapExtension.ts 查看文件

@@ -88,7 +88,7 @@ class GLTFMaterialsAlphaMapExtensionExport {

const extensionDef: any = {}

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

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

+ 1
- 1
src/assetmanager/gltf/GLTFMaterialsBumpMapExtension.ts 查看文件

@@ -95,7 +95,7 @@ class GLTFMaterialsBumpMapExtensionExport {

extensionDef.bumpScale = material.bumpScale

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

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

+ 1
- 1
src/assetmanager/gltf/GLTFMaterialsDisplacementMapExtension.ts 查看文件

@@ -90,7 +90,7 @@ class GLTFMaterialsDisplacementMapExtensionExport {
extensionDef.displacementScale = material.displacementScale
extensionDef.displacementBias = material.displacementBias

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

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

+ 1
- 1
src/assetmanager/gltf/GLTFMaterialsLightMapExtension.ts 查看文件

@@ -95,7 +95,7 @@ class GLTFMaterialsLightMapExtensionExport {

extensionDef.lightMapIntensity = material.lightMapIntensity

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

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

+ 23
- 0
src/assetmanager/import/FBXLoader2.ts 查看文件

@@ -0,0 +1,23 @@
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 查看文件

@@ -1,6 +1,6 @@
import type {GLTF, GLTFLoaderPlugin, GLTFParser} 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 {ThreeViewer} from '../../viewer'
import {generateUUID} from '../../three'
@@ -27,6 +27,10 @@ import {
UnlitLineMaterial,
UnlitMaterial,
} 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> {
isGLTFLoader2 = true
@@ -69,7 +73,18 @@ export class GLTFLoader2 extends GLTFLoader implements ILoader<GLTF, Object3D|un

parse(data: ArrayBuffer | string, path: string, onLoad: (gltf: GLTF) => void, onError?: (event: ErrorEvent) => void, url?: string) {
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) => {
console.error(e)
if (onError) onError(e ?? new ErrorEvent('unknown error'))
@@ -146,17 +161,27 @@ export class GLTFLoader2 extends GLTFLoader implements ILoader<GLTF, Object3D|un
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) => {
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])
}}
}

+ 1
- 1
src/assetmanager/import/Rhino3dmLoader2.ts 查看文件

@@ -40,7 +40,7 @@ export class Rhino3dmLoader2 extends Rhino3dmLoader {
})
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> {
const ret = await super.loadAsync(url, onProgress)

+ 69
- 0
src/assetmanager/import/SVGTextureLoader.ts 查看文件

@@ -0,0 +1,69 @@
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 查看文件

@@ -61,6 +61,8 @@ export class AssetExporterPlugin extends AViewerPluginSync {
embedUrlImages: false,
encrypt: false,
encryptKey: '',
ignoreInvalidMorphTargetTracks: true,
ignoreEmptyTextures: true,
}

async exportScene(options?: ExportAssetOptions): Promise<BlobExt | undefined> {
@@ -129,6 +131,16 @@ export class AssetExporterPlugin extends AViewerPluginSync {
// label: 'Convert to indexed',
// 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',
label: 'Export GLB',

+ 1
- 1
src/plugins/material/CustomBumpMapPlugin.ts 查看文件

@@ -281,7 +281,7 @@ const glTFMaterialsCustomBumpMapExport = (w: GLTFWriter2)=> ({

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)}
w.applyTextureTransform(customBumpMapDef, material.userData._customBumpMap)

正在加载...
取消
保存