| @@ -9,7 +9,7 @@ | |||
| "version": "0.0.13", | |||
| "license": "Apache-2.0", | |||
| "dependencies": { | |||
| "@types/three": "https://github.com/repalash/three-ts-types/releases/download/v0.152.1014/package.tgz", | |||
| "@types/three": "https://github.com/repalash/three-ts-types/releases/download/v0.152.1016/package.tgz", | |||
| "@types/webxr": "^0.5.1", | |||
| "@types/wicg-file-system-access": "^2020.9.5", | |||
| "ts-browser-helpers": "^0.8.0" | |||
| @@ -39,7 +39,7 @@ | |||
| "rollup-plugin-license": "^3.0.1", | |||
| "rollup-plugin-postcss": "^4.0.2", | |||
| "stats.js": "^0.17.0", | |||
| "three": "https://github.com/repalash/three.js-modded/releases/download/v0.152.2012/package.tgz", | |||
| "three": "https://github.com/repalash/three.js-modded/releases/download/v0.152.2015/package.tgz", | |||
| "tslib": "^2.5.0", | |||
| "typedoc": "^0.24.7", | |||
| "typescript": "^5.0.4", | |||
| @@ -684,9 +684,9 @@ | |||
| "dev": true | |||
| }, | |||
| "node_modules/@types/three": { | |||
| "version": "0.152.1014", | |||
| "resolved": "https://github.com/repalash/three-ts-types/releases/download/v0.152.1014/package.tgz", | |||
| "integrity": "sha512-1sR9iALwIFtfSXxJshAglvMjLy5litWF2hTbh0JQ+44d+21D2t0nppRZBnWtNQP5XsBYdhCNygnDQNeF6kd+NQ==", | |||
| "version": "0.152.1016", | |||
| "resolved": "https://github.com/repalash/three-ts-types/releases/download/v0.152.1016/package.tgz", | |||
| "integrity": "sha512-S4AczoUaWTfyh7ApgEXZWhhusqF9DGX8ynfIa8OzS0+ES0DmD1UQuyVQUmwOTxgTDGO10vc7zWrObZGPvM88NQ==", | |||
| "dependencies": { | |||
| "@tweenjs/tween.js": "~18.6.4", | |||
| "fflate": "~0.6.9", | |||
| @@ -9533,9 +9533,9 @@ | |||
| } | |||
| }, | |||
| "node_modules/three": { | |||
| "version": "0.152.2011", | |||
| "resolved": "https://github.com/repalash/three.js-modded/releases/download/v0.152.2012/package.tgz", | |||
| "integrity": "sha512-f2WKlSeuz9uvpfHNngEJMrQtKbyuE2iHjvpBaF1Wl8LcoMT3WUs7nmJzbEeheEr+J8BYnyRLNMDzR2xU0l1+Yw==", | |||
| "version": "0.152.2015", | |||
| "resolved": "https://github.com/repalash/three.js-modded/releases/download/v0.152.2015/package.tgz", | |||
| "integrity": "sha512-qON7KCzBCV2cWH4uOg6rfSJw5otIRk3JK7i8VtRq9K0KWaD/c3aW2Uz/WRqOJDxW/ENNrKhb171nqlToJIkgcg==", | |||
| "dev": true, | |||
| "license": "MIT" | |||
| }, | |||
| @@ -10874,8 +10874,8 @@ | |||
| "dev": true | |||
| }, | |||
| "@types/three": { | |||
| "version": "https://github.com/repalash/three-ts-types/releases/download/v0.152.1014/package.tgz", | |||
| "integrity": "sha512-1sR9iALwIFtfSXxJshAglvMjLy5litWF2hTbh0JQ+44d+21D2t0nppRZBnWtNQP5XsBYdhCNygnDQNeF6kd+NQ==", | |||
| "version": "https://github.com/repalash/three-ts-types/releases/download/v0.152.1016/package.tgz", | |||
| "integrity": "sha512-S4AczoUaWTfyh7ApgEXZWhhusqF9DGX8ynfIa8OzS0+ES0DmD1UQuyVQUmwOTxgTDGO10vc7zWrObZGPvM88NQ==", | |||
| "requires": { | |||
| "@tweenjs/tween.js": "~18.6.4", | |||
| "fflate": "~0.6.9", | |||
| @@ -17285,8 +17285,8 @@ | |||
| } | |||
| }, | |||
| "three": { | |||
| "version": "https://github.com/repalash/three.js-modded/releases/download/v0.152.2012/package.tgz", | |||
| "integrity": "sha512-f2WKlSeuz9uvpfHNngEJMrQtKbyuE2iHjvpBaF1Wl8LcoMT3WUs7nmJzbEeheEr+J8BYnyRLNMDzR2xU0l1+Yw==", | |||
| "version": "https://github.com/repalash/three.js-modded/releases/download/v0.152.2015/package.tgz", | |||
| "integrity": "sha512-qON7KCzBCV2cWH4uOg6rfSJw5otIRk3JK7i8VtRq9K0KWaD/c3aW2Uz/WRqOJDxW/ENNrKhb171nqlToJIkgcg==", | |||
| "dev": true | |||
| }, | |||
| "through": { | |||
| @@ -92,7 +92,7 @@ | |||
| "rollup-plugin-glsl": "^1.3.0", | |||
| "rollup-plugin-postcss": "^4.0.2", | |||
| "stats.js": "^0.17.0", | |||
| "three": "https://github.com/repalash/three.js-modded/releases/download/v0.152.2012/package.tgz", | |||
| "three": "https://github.com/repalash/three.js-modded/releases/download/v0.152.2015/package.tgz", | |||
| "tslib": "^2.5.0", | |||
| "typedoc": "^0.24.7", | |||
| "typescript": "^5.0.4", | |||
| @@ -102,7 +102,7 @@ | |||
| "popmotion": "^11.0.5" | |||
| }, | |||
| "dependencies": { | |||
| "@types/three": "https://github.com/repalash/three-ts-types/releases/download/v0.152.1014/package.tgz", | |||
| "@types/three": "https://github.com/repalash/three-ts-types/releases/download/v0.152.1016/package.tgz", | |||
| "@types/webxr": "^0.5.1", | |||
| "@types/wicg-file-system-access": "^2020.9.5", | |||
| "ts-browser-helpers": "^0.8.0" | |||
| @@ -111,10 +111,10 @@ | |||
| "dependencies": { | |||
| "uiconfig.js": "^0.0.6", | |||
| "ts-browser-helpers": "^0.8.0", | |||
| "three": "https://github.com/repalash/three.js-modded/releases/download/v0.152.2012/package.tgz", | |||
| "three-f": "https://github.com/repalash/three.js-modded/archive/refs/tags/v0.152.2012.tar.gz", | |||
| "@types/three": "https://github.com/repalash/three-ts-types/releases/download/v0.152.1014/package.tgz", | |||
| "@types/three-f": "https://github.com/repalash/three-ts-types/archive/refs/tags/v0.152.1014.tar.gz", | |||
| "three": "https://github.com/repalash/three.js-modded/releases/download/v0.152.2015/package.tgz", | |||
| "three-f": "https://github.com/repalash/three.js-modded/archive/refs/tags/v0.152.2015.tar.gz", | |||
| "@types/three": "https://github.com/repalash/three-ts-types/releases/download/v0.152.1016/package.tgz", | |||
| "@types/three-f": "https://github.com/repalash/three-ts-types/archive/refs/tags/v0.152.1016.tar.gz", | |||
| "@types/three-pkg": "https://gitpkg.now.sh/repalash/three-ts-types/types/three?modded_three" | |||
| }, | |||
| "local_dependencies": { | |||
| @@ -1,4 +1,4 @@ | |||
| import {Event, EventDispatcher, FileLoader, LoaderUtils, LoadingManager} from 'three' | |||
| import {Event, EventDispatcher, EventListener, FileLoader, LoaderUtils, LoadingManager} from 'three' | |||
| import { | |||
| IAssetImporter, | |||
| IAssetImporterEventTypes, | |||
| @@ -543,6 +543,15 @@ export class AssetImporter extends EventDispatcher<IAssetImporterEvent, IAssetIm | |||
| return loader | |||
| } | |||
| addEventListener<T extends IAssetImporterEvent['type'] & IAssetImporterEventTypes>(type: T, listener: EventListener<IAssetImporterEvent, T, this>) { | |||
| super.addEventListener(type, listener) | |||
| if (type === 'loaderCreate') { | |||
| for (const loaderCacheElement of this._loaderCache) { | |||
| this.dispatchEvent({type: 'loaderCreate', loader: loaderCacheElement.loader}) | |||
| } | |||
| } | |||
| } | |||
| // endregion | |||
| // region Loader Event Dispatchers | |||
| @@ -213,8 +213,8 @@ export class MaterialManager<T = ''> extends EventDispatcher<BaseEvent, T> { | |||
| return !uuid ? undefined : this._materials.find(v=>v.uuid === uuid) | |||
| } | |||
| public findMaterialsByName(name: string): IMaterial[] { | |||
| return this._materials.filter(v=>v.name === name) | |||
| public findMaterialsByName(name: string|RegExp, regex = false): IMaterial[] { | |||
| return this._materials.filter(v=>typeof name !== 'string' || regex ? v.name.match(name) !== null : v.name === name) | |||
| } | |||
| public getMaterialsOfType<TM extends IMaterial = IMaterial>(typeSlug: string | undefined): TM[] { | |||
| @@ -227,7 +227,7 @@ export interface IObject3D<E extends Event = IObject3DEvent, ET = IObject3DEvent | |||
| getObjectByProperty<T extends IObject3D = IObject3D>(name: string, value: string): T | undefined | |||
| copy(source: this, recursive?: boolean, ...args: any[]): this | |||
| clone(recursive?: boolean): this | |||
| add(...object: IObject3D[]): this | |||
| add(...object: Object3D[]): this | |||
| remove(...object: IObject3D[]): this | |||
| parent: IObject3D | null | |||
| children: IObject3D[] | |||
| @@ -1,7 +1,7 @@ | |||
| import {UiObjectConfig} from 'uiconfig.js' | |||
| import {IGeometry, IGeometrySetDirtyOptions} from '../IGeometry' | |||
| import {isInScene, toIndexedGeometry} from '../../three' | |||
| import {BufferGeometry} from 'three' | |||
| import {autoGPUInstanceMeshes, isInScene, toIndexedGeometry} from '../../three' | |||
| import {BufferGeometry, Vector3} from 'three' | |||
| export const iGeometryCommons = { | |||
| setDirty: function(this: IGeometry, options?: IGeometrySetDirtyOptions): void { | |||
| @@ -40,6 +40,19 @@ export const iGeometryCommons = { | |||
| this.setDirty() | |||
| }, | |||
| }, | |||
| { | |||
| type: 'button', | |||
| label: 'Center Geometry (keep position)', | |||
| value: () => { | |||
| const offset = new Vector3() | |||
| this.center(offset) | |||
| const meshes = this.appliedMeshes | |||
| meshes.forEach(m=>{ | |||
| m.position.sub(offset) | |||
| m.setDirty && m.setDirty() | |||
| }) | |||
| }, | |||
| }, | |||
| { | |||
| type: 'button', | |||
| label: 'Compute vertex normals', | |||
| @@ -112,6 +125,15 @@ export const iGeometryCommons = { | |||
| this.setDirty() | |||
| }, | |||
| }, | |||
| { | |||
| type: 'button', | |||
| label: 'Auto GPU Instances', | |||
| hidden: ()=> !this.appliedMeshes || this.appliedMeshes.size < 2, | |||
| value: ()=>{ | |||
| if (!confirm('This action is irreversible, do you want to continue?')) return | |||
| autoGPUInstanceMeshes(this) | |||
| }, | |||
| }, | |||
| { | |||
| type: 'input', | |||
| label: 'Mesh count', | |||
| @@ -106,6 +106,7 @@ export const iMaterialUI = { | |||
| { | |||
| type: 'slider', | |||
| bounds: [0, 1], | |||
| stepSize: 0.001, | |||
| property: [material, 'alphaTest'], | |||
| }, | |||
| { | |||
| @@ -171,7 +171,7 @@ export class PhysicalMaterial extends MeshPhysicalMaterial<IMaterialEvent, Physi | |||
| if (!isFinite(this.attenuationDistance)) this.attenuationDistance = 0 // hack for ui | |||
| this.userData.uuid = this.uuid // just in case | |||
| this.userData.uuid = this.uuid | |||
| return this | |||
| } | |||
| @@ -118,7 +118,8 @@ export class UnlitMaterial extends MeshBasicMaterial<IMaterialEvent, UnlitMateri | |||
| if (clearCurrentUserData === undefined) clearCurrentUserData = (<Material>parameters).isMaterial | |||
| if (clearCurrentUserData) this.userData = {} | |||
| iMaterialCommons.setValues(super.setValues).call(this, parameters) | |||
| this.userData.uuid = this.uuid // just in case | |||
| this.userData.uuid = this.uuid | |||
| return this | |||
| } | |||
| copy(source: Material|any): this { | |||
| @@ -0,0 +1,85 @@ | |||
| import {IGeometry, IMaterial, IObject3D} from '../../core' | |||
| import {BufferAttribute, InstancedMesh} from 'three' | |||
| import {copyObject3DUserData} from '../../utils' | |||
| export function autoGPUInstanceMeshes(matOrGeom: IMaterial|IGeometry) { | |||
| if (!(<IMaterial>matOrGeom).isMaterial && !(<IGeometry>matOrGeom).isBufferGeometry) return | |||
| const meshes = Array.from(matOrGeom.appliedMeshes).filter((m: any) => | |||
| !m.isInstancedMesh && | |||
| !!m.parent && | |||
| m.children.length === 0 && | |||
| !Array.isArray(m.material) | |||
| ) | |||
| if (meshes.length < 2) return | |||
| const getKey = (m: IObject3D) => { | |||
| return m.parent!.uuid + '_' + m.geometry?.uuid + '_' + (m.material as IMaterial)?.uuid // + '_' + (m.matrix.determinant()<0) | |||
| } | |||
| const keyMeshMap = new Map<string, IObject3D[]>() | |||
| for (const mesh1 of meshes) { | |||
| const key = getKey(mesh1) | |||
| if (!keyMeshMap.has(key)) keyMeshMap.set(key, []) | |||
| keyMeshMap.get(key)!.push(mesh1) | |||
| mesh1.updateMatrix() | |||
| } | |||
| const keys = keyMeshMap.keys() | |||
| for (const key of keys) { | |||
| const iMeshes = keyMeshMap.get(key)! | |||
| const baseMesh = iMeshes[0] | |||
| if (!baseMesh) continue | |||
| if (iMeshes.length < 2) continue | |||
| const inst = new InstancedMesh(baseMesh.geometry, baseMesh.material, iMeshes.length) | |||
| const ud = baseMesh.userData | |||
| baseMesh.userData = {} | |||
| inst.copy(baseMesh) | |||
| copyObject3DUserData(inst.userData, ud) | |||
| const parent = baseMesh.parent! | |||
| inst.position.set(0, 0, 0) | |||
| inst.rotation.set(0, 0, 0) | |||
| inst.scale.set(1, 1, 1) | |||
| inst.updateMatrix() | |||
| const translationAttr = new Float32Array(inst.count * 3) | |||
| const rotationAttr = new Float32Array(inst.count * 4) | |||
| const scaleAttr = new Float32Array(inst.count * 3) | |||
| // const pos = new Vector3() | |||
| // const quat = new Quaternion() | |||
| // const scale = new Vector3() | |||
| for (let i = 0; i < iMeshes.length; i++) { | |||
| const m = iMeshes[i] | |||
| // const mat = inst.matrix.clone().invert().multiply(m.matrix) | |||
| const mat = m.matrix | |||
| // mat.decompose(pos, quat, scale) | |||
| if (mat.determinant() < 0) { | |||
| mat.elements[0] *= -1 | |||
| mat.elements[1] *= -1 | |||
| mat.elements[2] *= -1 | |||
| } | |||
| inst.setMatrixAt(i, mat) | |||
| m.position.toArray(translationAttr, i * 3) | |||
| m.quaternion.toArray(rotationAttr, i * 4) | |||
| m.scale.toArray(scaleAttr, i * 3) | |||
| m.removeFromParent() | |||
| // ;(m.material as any)?.appliedMeshes?.delete(m) | |||
| // m.geometry?.appliedMeshes?.delete(m) | |||
| m.material = undefined | |||
| m.geometry = undefined | |||
| } | |||
| // (inst.material as IMaterial).appliedMeshes?.add(inst) | |||
| // inst.geometry.userData.__appliedMeshes.add(inst) | |||
| // todo set position to center of all instances | |||
| // @ts-expect-error todo not in ts | |||
| inst.sourceTrs = { | |||
| TRANSLATION: new BufferAttribute(translationAttr, 3), | |||
| ROTATION: new BufferAttribute(rotationAttr, 4), | |||
| SCALE: new BufferAttribute(scaleAttr, 3), | |||
| } | |||
| inst.instanceMatrix.needsUpdate = true | |||
| parent.add(inst) | |||
| ;(parent as any).setDirty() | |||
| } | |||
| } | |||
| @@ -8,5 +8,6 @@ export {getTextureDataType, textureToCanvas, textureDataToImageData, textureToDa | |||
| export {threeConstMappings} from './const-mappings' | |||
| export {ObjectPicker} from './ObjectPicker' | |||
| export {SelectionWidget, BoxSelectionWidget} from './SelectionWidget' | |||
| export {autoGPUInstanceMeshes} from './gpu-instancing' | |||
| // export {} from './constants' | |||
| @@ -719,12 +719,12 @@ export class MetaImporter { | |||
| } | |||
| static async LoadRootPathTextures({textures, images}: Pick<SerializationMetaType, 'textures'|'images'>, importer: IAssetImporter) { | |||
| static async LoadRootPathTextures({textures, images}: Pick<SerializationMetaType, 'textures'|'images'>, importer: IAssetImporter, usePreviewImages = true) { | |||
| const pms = [] | |||
| for (const inpTexture of Array.isArray(textures) ? textures : Object.values(textures ?? {} as any) as any as any[]) { | |||
| const path = inpTexture?.userData?.rootPath | |||
| const hasImage = inpTexture.image && images[inpTexture.image] // its possible to have both image and rootPath, then the image will be preview image. | |||
| const hasImage = usePreviewImages && inpTexture.image && images[inpTexture.image] // its possible to have both image and rootPath, then the image will be preview image. | |||
| if (!path) continue | |||
| // console.warn(path, inpTexture, images) | |||
| const promise = importer.importSingle<ITexture>(path, {processRaw: false}).then((texture) => { | |||