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

Add ignoreWidgets, ignoreObject in RootScene.getBounds, Add syncMaxDuration in GLTFAnimationPlugin, Add ignoreObject in Box3B.expandByObject, Button check in CustomContextMenu pointerdown listener, Minor changes

master
Palash Bansal 2 лет назад
Родитель
Сommit
fe4d525bc4
Аккаунт пользователя с таким Email не найден

+ 12
- 5
README.md Просмотреть файл

@@ -999,10 +999,10 @@ Notes:
* All plugins that are present in the dependencies array when the plugin is added to the viewer, are created and attached to the viewer in `super.onAdded`
* Custom events can be dispatched with `this.dispatchEvent`, and subscribed to with `plugin.addEventListener`. The event type must be described in the class signature for typescript autocomplete to work.
* Event listeners and other hooks can be added and removed in `onAdded` and `onRemove` functions for the viewer and other plugins.
* To the viewer render the next frame, viewer.setDirty() can be called, or set this.dirty = true in preFrame and reset in postFrame to stop the rendering. (Note that rendering may continue if some other plugin sets the viewer dirty)
* All Plugins which inherit from AViewerPlugin support serialisation. Create property `serializeWithViewer = false` to disable serialization with the viewer in config and glb or `toJSON: any = undefined` to disable serialisation entirely
* plugin.toJSON() and plugin.fromJSON() or ThreeSerialization can be used to serialize and deserialize plugins. viewer.exportPluginConfig and viewer.importPluginConfig also exist for this.
* @serialize('label') decorator can be used to mark any public/private variable as serialisable. label (optional) corresponds to the key in JSON.
* To the viewer render the next frame, `viewer.setDirty()` can be called, or set `this.dirty = true` in preFrame and reset in postFrame to stop the rendering. (Note that rendering may continue if some other plugin sets the viewer dirty like `ProgressivePlugin` or any of the animation plugins). Check `isConverged` in `ProgressivePlugin` to check if its the final frame.
* All Plugins which inherit from AViewerPlugin support serialisation. Create property `serializeWithViewer = false` to disable serialisation with the viewer in config and glb or `toJSON: any = undefined` to disable serialisation entirely
* `plugin.toJSON()` and `plugin.fromJSON()` or `ThreeSerialization` can be used to serialize and deserialize plugins. `viewer.exportPluginConfig` and `viewer.importPluginConfig` also exist for this.
* @serialize('label') decorator can be used to mark any public/private variable as serializable. label (optional) corresponds to the key in JSON.
* @serialize supports instances of ITexture, IMaterial, all primitive types, simple JS objects, three.js math classes(Vector2, Vector3, Matrix3...), and some more.
* uiDecorators can be used to mark properties and functions that will be shown in the Ui. The Ui shows up automatically when TweakpaneUiPlugin is added to the viewer. Plugins have special features in the UI for download preset and saving state.

@@ -2448,9 +2448,16 @@ API Reference: [Rhino3dmLoadPlugin](https://threepipe.org/docs/classes/Rhino3dmL

Adds support for loading .3dm files generated by [Rhino 3D](https://www.rhino3d.com/). This plugin includes some changes with how 3dm files are loaded in three.js. The changes are around loading layer and primitive properties when set as reference in the 3dm files.

It also adds some helpful options to process the model after load.

```typescript
import {Rhino3dmLoadPlugin} from 'threepipe'
viewer.addPluginSync(new Rhino3dmLoadPlugin())
const rhino3dmPlugin = viewer.addPluginSync(new Rhino3dmLoadPlugin())

rhino3dmPlugin.importMaterials = true // import materials source from 3dm file
rhino3dmPlugin.forceLayerMaterials = true // force material source to be layer in 3dm file.
rhino3dmPlugin.hideLineMesh = true // hide all lines and points in the model.
rhino3dmPlugin.replaceWithInstancedMesh = true // replace meshes with the same parent, geometry and material with a single instance mesh.

const mesh = await viewer.load('file.3dm')
```

+ 6
- 0
src/core/IObject.ts Просмотреть файл

@@ -96,6 +96,12 @@ export interface IObject3DUserData extends IImportResultUserData {
*/
userSelectable?: boolean

/**
* see {@link GLTFAnimationPlugin}
*/
// eslint-disable-next-line @typescript-eslint/naming-convention
gltfAnim_SyncMaxDuration?: boolean

// region root scene model root

/**

+ 8
- 3
src/core/object/RootScene.ts Просмотреть файл

@@ -15,7 +15,7 @@ import {AnyOptions, onChange2, onChange3, serialize} from 'ts-browser-helpers'
import {PerspectiveCamera2} from '../camera/PerspectiveCamera2'
import {ThreeSerialization} from '../../utils'
import {ITexture} from '../ITexture'
import {AddObjectOptions, IScene, ISceneEvent, ISceneEventTypes, ISceneSetDirtyOptions} from '../IScene'
import {AddObjectOptions, IScene, ISceneEvent, ISceneEventTypes, ISceneSetDirtyOptions, IWidget} from '../IScene'
import {iObjectCommons} from './iObjectCommons'
import {RootSceneImportResult} from '../../assetmanager'
import {uiColor, uiConfig, uiFolderContainer, uiImage, UiObjectConfig, uiSlider, uiToggle} from 'uiconfig.js'
@@ -362,11 +362,16 @@ export class RootScene extends Scene<ISceneEvent, ISceneEventTypes> implements I
* Returns the bounding box of the scene model root.
* @param precise
* @param ignoreInvisible
* @param ignoreWidgets
* @param ignoreObject
* @returns {Box3B}
*/
getBounds(precise = false, ignoreInvisible = true): Box3B {
getBounds(precise = false, ignoreInvisible = true, ignoreWidgets = true, ignoreObject?: (obj: Object3D)=>boolean): Box3B {
// See bboxVisible in userdata in Box3B
return new Box3B().expandByObject(this, precise, ignoreInvisible)
return new Box3B().expandByObject(this, precise, ignoreInvisible, (o: any)=>{
if (ignoreWidgets && ((o as IWidget).isWidget || o.assetType === 'widget')) return true
return ignoreObject?.(o) ?? false
})
}

private _v1 = new Vector3()

+ 9
- 1
src/plugins/animation/GLTFAnimationPlugin.ts Просмотреть файл

@@ -113,6 +113,11 @@ export class GLTFAnimationPlugin extends AViewerPluginSync<'checkpointEnd'|'chec
*/
@uiToggle() @serialize() autoplayOnLoad = false

/**
* Sync the duration of all clips based on the max duration, helpful for things like timeline markers
*/
@uiToggle('syncMaxDuration(dev)') @serialize() syncMaxDuration = false

/**
* Get the current state of the animation. (read only)
* use {@link playAnimation}, {@link pauseAnimation}, {@link stopAnimation} to change the state.
@@ -476,7 +481,10 @@ export class GLTFAnimationPlugin extends AViewerPluginSync<'checkpointEnd'|'chec
if (clips.length < 1) return

const duration = Math.max(...clips.map(an=>an.duration))
// clips.forEach(cp=>cp.duration = duration) // todo: check why do we need to do this? wont this create problems with looping or is it for that so that looping works in sync.
if (object.userData.gltfAnim_SyncMaxDuration ?? this.syncMaxDuration) {
clips.forEach(cp=>cp.duration = duration)
object.userData.gltfAnim_SyncMaxDuration = true
} // todo: check why do we need to do this? wont this create problems with looping or is it for that so that looping works in sync.

const mixer = new AnimationMixer(this._viewer.scene.modelRoot) // add to modelRoot so it works with GLTF export...
const actions = clips.map(an=>mixer.clipAction(an).setLoop(this.loopAnimations ? LoopRepeat : LoopOnce, this.loopRepetitions))

+ 3
- 3
src/postprocessing/ExtendedRenderPass.ts Просмотреть файл

@@ -164,7 +164,7 @@ export class ExtendedRenderPass extends RenderPass implements IPipelinePass<'ren
}
this.renderToScreen = false // for super RenderPass.render

if (!renderer.info.autoReset) throw 'renderer.info.autoReset must be true'
if (renderer.info && !renderer.info.autoReset) throw 'renderer.info.autoReset must be true'

// Opaque
{
@@ -210,7 +210,7 @@ export class ExtendedRenderPass extends RenderPass implements IPipelinePass<'ren
renderer.autoClearDepth = curClearDepth
}

if (renderer.info.render.calls > 0) {
if (!renderer.info || renderer.info.render.calls > 0) {

this._blendPass.uniforms.tDiffuse2.value = this.transparentTarget.texture
this._blendPass.render(renderer, writeBuffer, readBuffer, deltaTime, maskActive)
@@ -246,7 +246,7 @@ export class ExtendedRenderPass extends RenderPass implements IPipelinePass<'ren
}

// console.log(renderer.info.render.calls)
if (renderer.info.render.calls > 0) {
if (!renderer.info || renderer.info.render.calls > 0) {

// console.log('missive blit', renderer.info.render.frame)
this._blendPass.uniforms.tDiffuse2.value = this.transparentTarget.texture

+ 2
- 1
src/three/math/Box3B.ts Просмотреть файл

@@ -5,9 +5,10 @@ export class Box3B extends Box3 {
private static _box = new Box3B()
private _vector = new Vector3()

expandByObject(object: Object3D|IObject3D, precise = false, ignoreInvisible = false): this {
expandByObject(object: Object3D|IObject3D, precise = false, ignoreInvisible = false, ignoreObject?: (obj: Object3D)=>boolean): this {
if (object.userData?.bboxVisible === false) return this
if (!object.visible && ignoreInvisible) return this
if (ignoreObject && ignoreObject(object)) return this

// copied the whole function from three.js to pass in ignoreInvisible


+ 1
- 1
src/utils/CustomContextMenu.ts Просмотреть файл

@@ -21,7 +21,7 @@ export class CustomContextMenu {
private static _initialize(): void {
this._inited = true
document.addEventListener('pointerdown', (e) => {
if (this.Element && !this.Element.contains(e.target as any)) {
if (this.Element && !this.Element.contains(e.target as any) && e.button === 0) {
this.Remove()
}
})

+ 4
- 6
src/viewer/ThreeViewer.ts Просмотреть файл

@@ -490,7 +490,7 @@ export class ThreeViewer extends EventDispatcher<IViewerEvent, IViewerEventTypes
return this.assetManager.exporter.exportObject(this._scene.modelRoot, options)
}

async getScreenshotBlob({mimeType = 'image/jpeg', quality = 90} = {}): Promise<Blob | null> {
async getScreenshotBlob({mimeType = 'image/jpeg', quality = 90} = {}): Promise<Blob | null | undefined> {
const blobPromise = async()=> new Promise<Blob|null>((resolve) => {
this._canvas.toBlob((blob) => {
resolve(blob)
@@ -505,7 +505,7 @@ export class ThreeViewer extends EventDispatcher<IViewerEvent, IViewerEventTypes
})
}

async getScreenshotDataUrl({mimeType = 'image/jpeg', quality = 90} = {}): Promise<string | null> {
async getScreenshotDataUrl({mimeType = 'image/jpeg', quality = 90} = {}): Promise<string | null | undefined> {
if (!this.renderEnabled) return this._canvas.toDataURL(mimeType, quality)
return await this.doOnce('postFrame', () => this._canvas.toDataURL(mimeType, quality))
}
@@ -1039,11 +1039,11 @@ export class ThreeViewer extends EventDispatcher<IViewerEvent, IViewerEventTypes
return await MetaImporter.ImportMeta(meta, extraResources)
}

async doOnce<TRet>(event: IViewerEventTypes, func: (...args: any[]) => TRet): Promise<TRet> {
async doOnce<TRet>(event: IViewerEventTypes, func?: (...args: any[]) => TRet): Promise<TRet|undefined> {
return new Promise((resolve) => {
const listener = async(...args: any[]) => {
this.removeEventListener(event, listener)
resolve(await func(...args))
resolve(await func?.(...args))
}
this.addEventListener(event, listener)
})
@@ -1063,8 +1063,6 @@ export class ThreeViewer extends EventDispatcher<IViewerEvent, IViewerEventTypes

private _resolvePluginOrClass<T extends IViewerPlugin>(plugin: T | Class<T>, ...args: ConstructorParameters<Class<T>>): T {
let p: T
if ((plugin as Class<IViewerPlugin>).prototype) p = new (plugin as Class<T>)(...args)
else p = plugin as T
if ((plugin as Class<IViewerPlugin>).prototype) {
const p1 = this.getPlugin(plugin as Class<T>)
if (p1) {

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