| @@ -102,7 +102,7 @@ | |||
| "rollup-plugin-glsl": "^1.3.0", | |||
| "rollup-plugin-license": "^3.0.1", | |||
| "rollup-plugin-postcss": "^4.0.2", | |||
| "three": "https://github.com/repalash/three.js-modded/releases/download/v0.152.2020/package.tgz", | |||
| "three": "https://github.com/repalash/three.js-modded/releases/download/v0.152.2021/package.tgz", | |||
| "tslib": "^2.5.0", | |||
| "typedoc": "^0.25.7", | |||
| "typescript": "^5.3.3", | |||
| @@ -122,8 +122,8 @@ | |||
| "dependencies": { | |||
| "uiconfig.js": "^0.0.12", | |||
| "ts-browser-helpers": "^0.12.0", | |||
| "three": "https://github.com/repalash/three.js-modded/releases/download/v0.152.2020/package.tgz", | |||
| "three-f": "https://github.com/repalash/three.js-modded/archive/refs/tags/v0.152.2020.tar.gz", | |||
| "three": "https://github.com/repalash/three.js-modded/releases/download/v0.152.2021/package.tgz", | |||
| "three-f": "https://github.com/repalash/three.js-modded/archive/refs/tags/v0.152.2021.tar.gz", | |||
| "@types/three": "https://github.com/repalash/three-ts-types/releases/download/v0.152.1020/package.tgz", | |||
| "@types/three-f": "https://github.com/repalash/three-ts-types/archive/refs/tags/v0.152.1020.tar.gz", | |||
| "@types/three-pkg": "https://gitpkg.now.sh/repalash/three-ts-types/types/three?modded_three" | |||
| @@ -15,7 +15,7 @@ export class GLTFObject3DExtrasExtension { | |||
| const scenes = result.scenes || (result.scene ? [result.scene] : []) | |||
| scenes.forEach(s=>{ | |||
| s.traverse((o: any)=>{ | |||
| if (!o.isObject3D) return | |||
| if (!o || !o.isObject3D) return | |||
| const ext = o.userData?.gltfExtensions?.[this.WebGiObject3DExtrasExtension] | |||
| if (!ext) { | |||
| if (o.isLight && !o.isAmbientLight) o.castShadow = true | |||
| @@ -122,7 +122,7 @@ export class GLTFLoader2 extends GLTFLoader implements ILoader<GLTF, Object3D|un | |||
| const getDependency = parser.getDependency | |||
| parser.getDependency = async(type: string, index: number) => { | |||
| const res = await getDependency.call(parser, type, index) | |||
| if (res.userData) { | |||
| if (res && res.userData) { | |||
| const gltfExtensions = res.userData.gltfExtensions | |||
| delete res.userData.gltfExtensions | |||
| res.userData = ThreeSerialization.Deserialize(res.userData, {}) | |||
| @@ -204,7 +204,12 @@ export class RenderManager extends RenderTargetManager<IRenderManagerEvent, IRen | |||
| // // todo gizmos | |||
| // } | |||
| render(scene: IScene, renderToScreen = true): void { | |||
| /** | |||
| * Default value for renderToScreen in {@link render} | |||
| */ | |||
| defaultRenderToScreen = true | |||
| render(scene: IScene, renderToScreen?: boolean): void { | |||
| if (this._passesNeedsUpdate) { | |||
| this._refreshPipeline() | |||
| this.refreshPasses() | |||
| @@ -212,7 +217,7 @@ export class RenderManager extends RenderTargetManager<IRenderManagerEvent, IRen | |||
| for (const pass of this._passes) { | |||
| if (pass.enabled && pass.beforeRender) pass.beforeRender(scene, scene.renderCamera, this) | |||
| } | |||
| this._composer.renderToScreen = renderToScreen | |||
| this._composer.renderToScreen = renderToScreen ?? this.defaultRenderToScreen | |||
| this._composer.render() | |||
| this._composer.renderToScreen = true | |||
| if (renderToScreen) { | |||
| @@ -54,28 +54,34 @@ export class ThreeSerialization { | |||
| if (meta?.textures[obj.uuid]) return {uuid: obj.uuid, resource: 'textures'} | |||
| const imgData = obj.source.data | |||
| const hasRootPath = !obj.isRenderTargetTexture && obj.userData.rootPath | |||
| if (hasRootPath) { | |||
| if (obj.source.data) { | |||
| if (!obj.userData.embedUrlImagePreviews) // todo make sure its only Texture, check for svg etc | |||
| obj.source.data = null // handled in GLTFWriter2.processImage | |||
| else { | |||
| obj.source.data = textureToCanvas(obj, 16, obj.flipY) // todo: check flipY | |||
| let res = {} as any | |||
| const ud = obj.userData | |||
| try { // need try catch here because of hasRootPath | |||
| if (hasRootPath) { | |||
| if (obj.source.data) { | |||
| if (!obj.userData.embedUrlImagePreviews) // todo make sure its only Texture, check for svg etc | |||
| obj.source.data = null // handled in GLTFWriter2.processImage | |||
| else { | |||
| obj.source.data = textureToCanvas(obj, 16, obj.flipY) // todo: check flipY | |||
| } | |||
| } | |||
| } | |||
| obj.userData = {} // toJSON will call JSON.stringify, which will serialize userData | |||
| const meta2 = {images: {} as any} // in-case meta is undefined | |||
| res = obj.toJSON(meta || meta2) | |||
| if (!meta && res.image) res.image = hasRootPath && !obj.userData.embedUrlImagePreviews ? undefined : meta2.images[res.image] | |||
| res.userData = Serialization.Serialize(copyTextureUserData({}, ud), meta, false) | |||
| } catch (e) { | |||
| console.error('Threepipe Serialization: Unable to serialize texture') | |||
| console.error(e) | |||
| } | |||
| const ud = obj.userData | |||
| obj.userData = {} // toJSON will call JSON.stringify, which will serialize userData | |||
| const meta2 = {images: {} as any} // in-case meta is undefined | |||
| let res = obj.toJSON(meta || meta2) | |||
| if (!meta && res.image) res.image = hasRootPath && !obj.userData.embedUrlImagePreviews ? undefined : meta2.images[res.image] | |||
| obj.userData = ud | |||
| res.userData = Serialization.Serialize(copyTextureUserData({}, ud), meta, false) | |||
| obj.userData = ud // should be outside try catch | |||
| if (hasRootPath) { | |||
| if (meta && !obj.userData.embedUrlImagePreviews) delete meta.images[obj.source.uuid] // because its empty. uuid still stored in the texture.image | |||
| obj.source.data = imgData | |||
| } | |||
| if (meta?.textures && !res.resource) { | |||
| if (meta?.textures && res && !res.resource) { | |||
| if (!meta.textures[res.uuid]) | |||
| meta.textures[res.uuid] = res | |||
| res = {uuid: res.uuid, resource: 'textures'} | |||
| @@ -155,18 +161,20 @@ export class ThreeSerialization { | |||
| // Serialize without userData because three.js tries to convert it to string. We are serializing it separately | |||
| const userData = obj.userData | |||
| obj.userData = {} | |||
| let res = obj.toJSON(meta, true) // copying userData is handled in toJSON, see MeshStandardMaterial2 | |||
| let res = {} as any | |||
| try { | |||
| res = obj.toJSON(meta, true) // copying userData is handled in toJSON, see MeshStandardMaterial2 | |||
| serializeMaterialUserData(res, userData, meta) | |||
| res.userData.uuid = userData.uuid | |||
| // todo: override generator to mention that this is a custom serializer? | |||
| if (obj.constructor.TYPE) res.type = obj.constructor.TYPE // override type if specified as static property in the class | |||
| // Remove undefined values. Note that null values are kept. | |||
| for (const key of Object.keys(res)) if (res[key] === undefined) delete res[key] | |||
| } catch (e) { | |||
| console.error('Threepipe Serialization: Unable to serialize material') | |||
| console.error(e) | |||
| } | |||
| obj.userData = userData | |||
| serializeMaterialUserData(res, userData, meta) | |||
| // todo: override generator to mention that this is a custom serializer? | |||
| res.userData.uuid = obj.userData.uuid | |||
| if (obj.constructor.TYPE) res.type = obj.constructor.TYPE // override type if specified as static property in the class | |||
| // Remove undefined values. Note that null values are kept. | |||
| for (const key of Object.keys(res)) if (res[key] === undefined) delete res[key] | |||
| // Restore textures | |||
| for (const [k, v] of Object.entries(tempTextures)) { | |||
| obj[k] = v | |||
| @@ -174,21 +182,23 @@ export class ThreeSerialization { | |||
| } | |||
| // Add material, textures, images to meta | |||
| // serialize textures are already added to meta by the texture serializer | |||
| if (meta) { | |||
| for (const [k, v] of Object.entries(objTextures)) { | |||
| if (v) res[k] = v // can be undefined because of RenderTargetTexture... | |||
| } | |||
| if (meta.materials) { | |||
| if (!meta.materials[res.uuid]) | |||
| meta.materials[res.uuid] = res | |||
| res = {uuid: res.uuid, resource: 'materials'} | |||
| } | |||
| } else { | |||
| for (const [k, v] of Object.entries(objTextures)) { | |||
| if (v) res[k] = (v as any).uuid // to remain compatible with how three.js saves | |||
| if (res) { | |||
| if (meta) { | |||
| for (const [k, v] of Object.entries(objTextures)) { | |||
| if (v) res[k] = v // can be undefined because of RenderTargetTexture... | |||
| } | |||
| if (meta.materials) { | |||
| if (!meta.materials[res.uuid]) | |||
| meta.materials[res.uuid] = res | |||
| res = {uuid: res.uuid, resource: 'materials'} | |||
| } | |||
| } else { | |||
| for (const [k, v] of Object.entries(objTextures)) { | |||
| if (v) res[k] = (v as any).uuid // to remain compatible with how three.js saves | |||
| } | |||
| res.textures = Object.values(meta2.textures) | |||
| res.images = Object.values(meta2.images) | |||
| } | |||
| res.textures = Object.values(meta2.textures) | |||
| res.images = Object.values(meta2.images) | |||
| } | |||
| return res | |||
| }, | |||
| @@ -1,5 +1,6 @@ | |||
| import { | |||
| BaseEvent, | |||
| CanvasTexture, | |||
| Color, | |||
| Event, | |||
| EventDispatcher, | |||
| @@ -120,6 +121,10 @@ export interface ThreeViewerOptions { | |||
| * Use rendered gbuffer as depth-prepass / z-prepass. | |||
| */ | |||
| zPrepass?: boolean | |||
| /** | |||
| * Force z-prepass even if there are transparent/transmissive objects with render to depth buffer enabled. | |||
| */ | |||
| forceZPrepass?: boolean // todo | |||
| /* | |||
| * Render scale, 1 = full resolution, 0.5 = half resolution, 2 = double resolution. | |||
| @@ -239,6 +244,11 @@ export class ThreeViewer extends EventDispatcher<IViewerEvent, IViewerEventTypes | |||
| public maxFramePerLoop = 1 | |||
| readonly debug: boolean | |||
| /** | |||
| * Number of times to run composer render. If set to more than 1, preRender and postRender events will also be called multiple times. | |||
| */ | |||
| rendersPerFrame = 1 | |||
| /** | |||
| * Get the HTML Element containing the canvas | |||
| * @returns {HTMLElement} | |||
| @@ -619,20 +629,21 @@ export class ThreeViewer extends EventDispatcher<IViewerEvent, IViewerEventTypes | |||
| // Check if the renderManger is dirty, which happens when it's reset above or if any pass in the composer is dirty | |||
| const needsRender = this.renderManager.needsRender | |||
| if (needsRender) { | |||
| for (let j = 0; j < this.rendersPerFrame; j++) { | |||
| this.dispatchEvent({type: 'preRender', target: this}) | |||
| try { | |||
| this._scene.renderCamera = this._scene.mainCamera | |||
| this.renderManager.render(this._scene, this.renderManager.defaultRenderToScreen) | |||
| } catch (e) { | |||
| this.console.error(e) | |||
| if (this.debug) throw e | |||
| // this.enabled = false | |||
| } | |||
| this.dispatchEvent({type: 'preRender', target: this}) | |||
| try { | |||
| this._scene.renderCamera = this._scene.mainCamera | |||
| this.renderManager.render(this._scene) | |||
| } catch (e) { | |||
| this.console.error(e) | |||
| if (this.debug) throw e | |||
| // this.enabled = false | |||
| this.dispatchEvent({type: 'postRender', target: this}) | |||
| } | |||
| this.dispatchEvent({type: 'postRender', target: this}) | |||
| } | |||
| this.dispatchEvent({type: 'postFrame', target: this}) | |||
| @@ -1136,6 +1147,21 @@ export class ThreeViewer extends EventDispatcher<IViewerEvent, IViewerEventTypes | |||
| await camViews?.animateToFitObject(selected, distanceMultiplier, duration, ease, {min: ((<OrbitControls3> this.scene.mainCamera.controls)?.minDistance ?? 0.5) + 0.5, max: 1000.0}) | |||
| } | |||
| private _canvasTexture?: CanvasTexture&ITexture | |||
| /** | |||
| * Create and get a three.js CanvasTexture from the viewer's canvas. | |||
| */ | |||
| get canvasTexture(): CanvasTexture { | |||
| if (!this._canvas) throw new Error('Canvas not found') | |||
| if (!this._canvasTexture) { | |||
| this._canvasTexture = new CanvasTexture(this._canvas) | |||
| this._canvasTexture.flipY = false | |||
| this._canvasTexture.needsUpdate = true | |||
| } | |||
| return this._canvasTexture | |||
| } | |||
| // todo: create/load texture utils | |||
| // region legacy creation functions | |||