Просмотр исходного кода

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 год назад
Родитель
Сommit
4657f635e1
Аккаунт пользователя с таким Email не найден

+ 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)

Загрузка…
Отмена
Сохранить