Quellcode durchsuchen

Add dollyFov, add geometry.center2 with undo functionality, add pivotToBoundsCenter, pivotToPoint in IObject, add function getFittingDistance for camera, support function for target in GBufferRenderPass.

master
Palash Bansal vor 1 Jahr
Ursprung
Commit
8282af67f0
Es ist kein Account mit der E-Mail-Adresse des Committers verbunden

+ 1
- 1
examples/unreal-bloom-pass/script.ts Datei anzeigen

rgbm: false, // The pass from three.js doesn't support RGBM encoded render targets rgbm: false, // The pass from three.js doesn't support RGBM encoded render targets
zPrepass: false, zPrepass: false,
renderScale: 1, renderScale: 1,
maxHDRIntensity: 8,
maxHDRIntensity: 100,
dropzone: { dropzone: {
addOptions: { addOptions: {
disposeSceneObjects: true, disposeSceneObjects: true,

+ 1
- 0
package.json Datei anzeigen

"exports": { "exports": {
".": { ".": {
"import": "./dist/index.mjs", "import": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"require": "./dist/index.js" "require": "./dist/index.js"
}, },
"./dist/": { "./dist/": {

+ 7
- 0
src/core/ICamera.ts Datei anzeigen

* @default false * @default false
*/ */
autoLookAtTarget?: boolean autoLookAtTarget?: boolean
/**
* Automatically move the camera(dolly) based on the scene size when the field of view(fov) changes.
* Works when controls are enabled or autoLookAtTarget is true.
*
* Note - The camera must be added to RootScene for this to work
*/
dollyFov?: boolean


/** /**
* Disable jitter for this camera. (for {@link SSAAPlugin}) * Disable jitter for this camera. (for {@link SSAAPlugin})

+ 15
- 1
src/core/IGeometry.ts Datei anzeigen

refreshUi(): void; refreshUi(): void;
uiConfig?: UiObjectConfig uiConfig?: UiObjectConfig
appliedMeshes: Set<IObject3D> appliedMeshes: Set<IObject3D>
center(offset?: Vector3, keepWorldPosition?: boolean): this

/**
* Centers the geometry.
* @param offset - returns the offset applied to the geometry
* @param keepWorldPosition - Updates the attached meshes, so that the world position of the geometry remains the same.
* @param setDirty
*/
center(offset?: Vector3, keepWorldPosition?: boolean, setDirty?: boolean): this

/**
* Same as center but returns a function to undo the centering
* @param offset
* @param keepWorldPosition
*/
center2(offset?: Vector3, keepWorldPosition?: boolean): ()=>void


// Note: for userData: add _ in front of for private use, which is preserved while cloning but not serialisation, and __ for private use, which is not preserved while cloning and serialisation // Note: for userData: add _ in front of for private use, which is preserved while cloning but not serialisation, and __ for private use, which is not preserved while cloning and serialisation
userData: IGeometryUserData userData: IGeometryUserData

+ 24
- 1
src/core/IObject.ts Datei anzeigen

import {IDisposable} from 'ts-browser-helpers' import {IDisposable} from 'ts-browser-helpers'
import {IMaterial} from './IMaterial' import {IMaterial} from './IMaterial'
import {Event, Object3D} from 'three'
import {Event, Object3D, Vector3} from 'three'
import {ChangeEvent, IUiConfigContainer, UiObjectConfig} from 'uiconfig.js' import {ChangeEvent, IUiConfigContainer, UiObjectConfig} from 'uiconfig.js'
import {IGeometry, IGeometryEvent} from './IGeometry' import {IGeometry, IGeometryEvent} from './IGeometry'
import {IImportResultUserData} from '../assetmanager' import {IImportResultUserData} from '../assetmanager'
userData: IObject3DUserData userData: IObject3DUserData


/** /**
* Scales the object to fit the given radius.
* *
* @param autoScaleRadius - optional (taken from userData.autoScaleRadius by default) * @param autoScaleRadius - optional (taken from userData.autoScaleRadius by default)
* @param isCentered - optional (taken from userData.isCentered by default) * @param isCentered - optional (taken from userData.isCentered by default)
autoScale?(autoScaleRadius?: number, isCentered?: boolean, setDirty?: boolean, undo?: boolean): this autoScale?(autoScaleRadius?: number, isCentered?: boolean, setDirty?: boolean, undo?: boolean): this


/** /**
* Moves the bounding box center of the object to the center of the world
* *
* @param setDirty - calls {@link setDirty} @default true * @param setDirty - calls {@link setDirty} @default true
* @param undo - undo any previous autoCenter operation * @param undo - undo any previous autoCenter operation
*/ */
autoCenter?(setDirty?: boolean, undo?: boolean): this autoCenter?(setDirty?: boolean, undo?: boolean): this


/**
* Moves the object pivot to the center of the bounding box.
*
* The object will rotate around the new pivot.
*
* @param setDirty - calls {@link setDirty} @default true
* @returns undo function
*/
pivotToBoundsCenter?(setDirty?: boolean): () => void

/**
* Moves the object pivot to the given point
*
* The object will rotate around the new pivot.
*
* @param point - point to move the pivot to
* @param setDirty - calls {@link setDirty} @default true
* @returns undo function
*/
pivotToPoint?(point: Vector3, setDirty?: boolean): this

/** /**
* @deprecated use object directly * @deprecated use object directly
*/ */

+ 18
- 0
src/core/camera/PerspectiveCamera2.ts Datei anzeigen

*/ */
@bindToValue({obj: 'userData', onChange: 'setDirty'}) @bindToValue({obj: 'userData', onChange: 'setDirty'})
minNearPlane = 0.5 minNearPlane = 0.5

/** /**
* Maximum far clipping plane allowed. (Distance from camera) * Maximum far clipping plane allowed. (Distance from camera)
* Used in RootScene when {@link autoNearFar} is true. * Used in RootScene when {@link autoNearFar} is true.
@bindToValue({obj: 'userData', onChange: 'setDirty'}) @bindToValue({obj: 'userData', onChange: 'setDirty'})
maxFarPlane = 1000 maxFarPlane = 1000


/**
* Automatically move the camera(dolly) when the field of view(fov) changes.
* Works when controls are enabled or autoLookAtTarget is true.
*
* Note - this is not exact
*/
@bindToValue({obj: 'userData'})
dollyFov = false // bound to userData so that it's saved in the glb.

constructor(controlsMode?: TCameraControlsMode, domElement?: HTMLCanvasElement, autoAspect?: boolean, fov?: number, aspect?: number) { constructor(controlsMode?: TCameraControlsMode, domElement?: HTMLCanvasElement, autoAspect?: boolean, fov?: number, aspect?: number) {
super(fov, aspect) super(fov, aspect)
this._canvas = domElement this._canvas = domElement
} }


// endregion // endregion

// region utils/others // region utils/others


// for shader prop updater // for shader prop updater
return this return this
} }



dispose(): void { dispose(): void {
this._disposeCameraControls() this._disposeCameraControls()
// todo: anything else? // todo: anything else?
label: 'Auto Near Far', label: 'Auto Near Far',
property: [this, 'autoNearFar'], property: [this, 'autoNearFar'],
}, },
{
type: 'input',
label: 'Dolly FoV',
property: [this, 'dollyFov'],
},
()=>({ // because _controlsCtors can change ()=>({ // because _controlsCtors can change
type: 'dropdown', type: 'dropdown',
label: 'Controls Mode', label: 'Controls Mode',
export class PerspectiveCamera0 extends PerspectiveCamera2 { export class PerspectiveCamera0 extends PerspectiveCamera2 {
constructor(fov?: number, aspect?: number, near?: number, far?: number) { constructor(fov?: number, aspect?: number, near?: number, far?: number) {
super(undefined, undefined, undefined, fov, aspect || 1) super(undefined, undefined, undefined, fov, aspect || 1)
this.dollyFov = false
if (near || far) { if (near || far) {
this.autoNearFar = false this.autoNearFar = false
if (near) { if (near) {

+ 45
- 7
src/core/geometry/iGeometryCommons.ts Datei anzeigen

import {autoGPUInstanceMeshes, isInScene, toIndexedGeometry} from '../../three/utils' import {autoGPUInstanceMeshes, isInScene, toIndexedGeometry} from '../../three/utils'
import {BufferGeometry, Vector3} from 'three' import {BufferGeometry, Vector3} from 'three'
import {ThreeViewer} from '../../viewer' import {ThreeViewer} from '../../viewer'
import {IObject3D} from '../IObject'


export const iGeometryCommons = { export const iGeometryCommons = {
setDirty: function(this: IGeometry, options?: IGeometrySetDirtyOptions): void { setDirty: function(this: IGeometry, options?: IGeometrySetDirtyOptions): void {
}, },
upgradeGeometry: upgradeGeometry, upgradeGeometry: upgradeGeometry,
center: (superCenter: BufferGeometry['center']): IGeometry['center'] => center: (superCenter: BufferGeometry['center']): IGeometry['center'] =>
function(this: IGeometry, offset?: Vector3, keepWorldPosition = false): IGeometry {
function(this: IGeometry, offset?: Vector3, keepWorldPosition = false, setDirty = true): IGeometry {
if (keepWorldPosition) { if (keepWorldPosition) {
offset = offset ? offset.clone() : new Vector3() offset = offset ? offset.clone() : new Vector3()
superCenter.call(this, offset) superCenter.call(this, offset)
for (const m of meshes) { for (const m of meshes) {
m.updateMatrix() m.updateMatrix()
m.position.copy(offset).applyMatrix4(m.matrix) m.position.copy(offset).applyMatrix4(m.matrix)
m.setDirty()
if (setDirty) m.setDirty()
} }
} else { } else {
superCenter.call(this, offset) superCenter.call(this, offset)
} }
this.setDirty()
if (setDirty) this.setDirty()
return this return this
}, },
center2: function(this: IGeometry, offset?: Vector3, keepWorldPosition = false, setDirty = true): ()=>void {
const offset1 = offset ? offset : new Vector3()
if (keepWorldPosition) {
this.center(offset1, false, false)
const meshes = this.appliedMeshes
const positions = new WeakMap<IObject3D, Vector3>()
for (const m of meshes) {
m.updateMatrix()
positions.set(m, m.position.clone())
m.position.set(-offset1.x, -offset1.y, -offset1.z).applyMatrix4(m.matrix)
if (setDirty) m.setDirty()
}
if (setDirty) this.setDirty()
return ()=>{
// undo
for (const m of meshes) {
const pos = positions.get(m)
if (!pos) {
console.warn('GeometryCommons: No position found for mesh', m)
continue
}
m.position.copy(pos)
if (setDirty) m.setDirty()
}
if (setDirty) this.setDirty()
}
} else {
this.center(offset1, false, false)
if (setDirty) this.setDirty()
return ()=>{
// undo
this.translate(-offset1.x, -offset1.y, -offset1.z)
if (setDirty) this.setDirty()
}
}
},
makeUiConfig: function(this: IGeometry): UiObjectConfig { makeUiConfig: function(this: IGeometry): UiObjectConfig {
if (this.uiConfig) return this.uiConfig if (this.uiConfig) return this.uiConfig
return { return {
type: 'button', type: 'button',
label: 'Center Geometry', label: 'Center Geometry',
value: async() => { value: async() => {
if (!await ThreeViewer.Dialog.confirm('This will move the objects based on the geometry center, do you want to continue?\nThis action cannot be undone.')) return
this.center()
if (!await ThreeViewer.Dialog.confirm('This will move the objects based on the geometry center, do you want to continue?')) return
return this.center2()
}, },
}, },
{ {
type: 'button', type: 'button',
label: 'Center Geometry (keep position)', label: 'Center Geometry (keep position)',
value: async() => { value: async() => {
if (!await ThreeViewer.Dialog.confirm('This will move the geometry center keeping the object position, do you want to continue?\nThis action cannot be undone.')) return
this.center(undefined, true)
if (!await ThreeViewer.Dialog.confirm('This will move the geometry center keeping the object position, do you want to continue?')) return
return this.center2(undefined, true)
}, },
}, },
{ {
this.dispose = iGeometryCommons.dispose(this.dispose) this.dispose = iGeometryCommons.dispose(this.dispose)
this.center = iGeometryCommons.center(this.center) this.center = iGeometryCommons.center(this.center)
this.clone = iGeometryCommons.clone(this.clone) this.clone = iGeometryCommons.clone(this.clone)
if (!this.center2) this.center2 = iGeometryCommons.center2


if (!this.setDirty) this.setDirty = iGeometryCommons.setDirty if (!this.setDirty) this.setDirty = iGeometryCommons.setDirty
if (!this.refreshUi) this.refreshUi = iGeometryCommons.refreshUi if (!this.refreshUi) this.refreshUi = iGeometryCommons.refreshUi

+ 9
- 0
src/core/object/IObjectUi.ts Datei anzeigen

} }
}, },
}, },
{
type: 'button',
label: 'Pivot to Node Center',
value: async()=>{
const res = await ThreeViewer.Dialog.confirm('Pivot to Center: Adjust the pivot to bounding box center. The object will rotate around the new pivot, are you sure you want to proceed?')
if (!res) return
return this.pivotToBoundsCenter?.(true) // return value is the undo function
},
},
{ {
type: 'folder', type: 'folder',
label: 'Rotate model', label: 'Rotate model',

+ 26
- 1
src/core/object/RootScene.ts Datei anzeigen

import {RootSceneImportResult} from '../../assetmanager' import {RootSceneImportResult} from '../../assetmanager'
import {uiButton, uiColor, uiConfig, uiFolderContainer, uiImage, UiObjectConfig, uiSlider, uiToggle} from 'uiconfig.js' import {uiButton, uiColor, uiConfig, uiFolderContainer, uiImage, UiObjectConfig, uiSlider, uiToggle} from 'uiconfig.js'
import {IGeometry} from '../IGeometry' import {IGeometry} from '../IGeometry'
import {getFittingDistance} from '../../three/utils/camera'


export type TCamera = ICamera export type TCamera = ICamera


centerAllGeometries(keepPosition = true, obj?: IObject3D) { centerAllGeometries(keepPosition = true, obj?: IObject3D) {
const geoms = new Set<IGeometry>() const geoms = new Set<IGeometry>()
;(obj ?? this.modelRoot).traverse((o) => o.geometry && geoms.add(o.geometry)) ;(obj ?? this.modelRoot).traverse((o) => o.geometry && geoms.add(o.geometry))
geoms.forEach(g => g.center(undefined, keepPosition))
const undos: (()=>void)[] = []
geoms.forEach(g => undos.push(g.center2(undefined, keepPosition)))
return ()=>undos.forEach(u=>u())
} }


clearSceneModels(dispose = false, setDirty = true): void { clearSceneModels(dispose = false, setDirty = true): void {
private _mainCameraUpdate = (e: any) => { private _mainCameraUpdate = (e: any) => {
this.setDirty({refreshScene: false}) this.setDirty({refreshScene: false})
this.refreshActiveCameraNearFar() this.refreshActiveCameraNearFar()
if (e.key === 'fov') this.dollyActiveCameraFov()
this.dispatchEvent({...e, type: 'mainCameraUpdate'}) this.dispatchEvent({...e, type: 'mainCameraUpdate'})
this.dispatchEvent({...e, type: 'activeCameraUpdate'}) // deprecated this.dispatchEvent({...e, type: 'activeCameraUpdate'}) // deprecated
} }
if (event?.sceneUpdate === false || event?.refreshScene === false || event?.object?.isCamera) return this.setDirty(event) // so that it doesn't trigger frame fade, shadow refresh etc if (event?.sceneUpdate === false || event?.refreshScene === false || event?.object?.isCamera) return this.setDirty(event) // so that it doesn't trigger frame fade, shadow refresh etc
// console.warn(event) // console.warn(event)
this.refreshActiveCameraNearFar() this.refreshActiveCameraNearFar()
// this.dollyActiveCameraFov()
this._sceneBounds = this.getBounds(false, true) this._sceneBounds = this.getBounds(false, true)
// this.boxHelper?.boxHelper?.copy?.(this._sceneBounds) // this.boxHelper?.boxHelper?.copy?.(this._sceneBounds)
this._sceneBoundingRadius = this._sceneBounds.getSize(new Vector3()).length() / 2. this._sceneBoundingRadius = this._sceneBounds.getSize(new Vector3()).length() / 2.
// camera.far = 20 // camera.far = 20
} }


/**
* Refreshes the scene active camera near far values, based on the scene bounding box.
* This is called automatically every time the camera fov is updated.
*/
dollyActiveCameraFov(): void {
const camera = this.mainCamera as TCamera
if (!camera) return
if (!camera.userData.dollyFov) {
return
}

const bbox = this.getModelBounds(false, true, true)

// todo this is not exact because of 1.5, this needs to be calculated based on current position and last fov
const cameraZ = getFittingDistance(camera, bbox) * 1.5
const direction = new Vector3().subVectors(camera.target, camera.position).normalize()
camera.position.copy(direction.multiplyScalar(-cameraZ).add(camera.target))
camera.setDirty()
}

updateShaderProperties(material: {defines: Record<string, string|number|undefined>, uniforms: {[name: string]: IUniform}}): this { updateShaderProperties(material: {defines: Record<string, string|number|undefined>, uniforms: {[name: string]: IUniform}}): this {
if (material.uniforms.sceneBoundingRadius) material.uniforms.sceneBoundingRadius.value = this._sceneBoundingRadius if (material.uniforms.sceneBoundingRadius) material.uniforms.sceneBoundingRadius.value = this._sceneBoundingRadius
else console.warn('RootScene: no uniform: sceneBoundingRadius') else console.warn('RootScene: no uniform: sceneBoundingRadius')

+ 53
- 1
src/core/object/iObjectCommons.ts Datei anzeigen

import {Event, Mesh, Vector3} from 'three'
import {Event, Matrix4, Mesh, Vector3} from 'three'
import {IMaterial} from '../IMaterial' import {IMaterial} from '../IMaterial'
import {objectHasOwn} from 'ts-browser-helpers' import {objectHasOwn} from 'ts-browser-helpers'
import {IObject3D, IObject3DEvent, IObjectProcessor, IObjectSetDirtyOptions} from '../IObject' import {IObject3D, IObject3DEvent, IObjectProcessor, IObjectSetDirtyOptions} from '../IObject'
if (setDirty) this.setDirty({change: 'autoCenter', undo}) if (setDirty) this.setDirty({change: 'autoCenter', undo})
return this return this
}, },

autoScale: function<T extends IObject3D>(this: T, autoScaleRadius?: number, isCentered?: boolean, setDirty = true, undo = false): T { autoScale: function<T extends IObject3D>(this: T, autoScaleRadius?: number, isCentered?: boolean, setDirty = true, undo = false): T {
let scale = 1 let scale = 1
if (undo) { // Note - undo only works for quick undo, not for multiple times if (undo) { // Note - undo only works for quick undo, not for multiple times
return this return this
}, },


pivotToBoundsCenter: function<T extends IObject3D>(this: T, setDirty = true): ()=>void {
const bb = new Box3B().expandByObject(this, true, true)
const center = bb.getCenter(new Vector3())
return iObjectCommons.pivotToPoint.call(this, center, setDirty)
},

pivotToPoint: function<T extends IObject3D>(this: T, point: Vector3, setDirty = true): ()=>void {
const worldCenter = new Vector3().copy(point)
const localCenter = new Vector3().copy(worldCenter)

const worldMatrixInv = new Matrix4().copy(this.matrixWorld).invert()
const m = this.parent?.matrixWorld
const parentWorldMatrixInv = new Matrix4()
if (m !== undefined)
parentWorldMatrixInv.copy(m).invert()

// Get the center with respect to the parent
worldCenter.applyMatrix4(parentWorldMatrixInv)
const lastPosition = this.position.clone()

// Apply the new position
this.position.copy(worldCenter)

// local center
localCenter.applyMatrix4(worldMatrixInv).negate()

// Shift the geometry
if (this.geometry) {
this.geometry.translate(localCenter.x, localCenter.y, localCenter.z)
}
// Add offsets
this.children.forEach((object)=> {
object.position.add(localCenter)
})
if (setDirty) this.setDirty({change: 'pivotToPoint', undo: false})

return ()=>{
// undo
this.position.copy(lastPosition)
if (this.geometry) {
this.geometry.translate(-localCenter.x, -localCenter.y, -localCenter.z)
}
this.children.forEach((object)=> {
object.position.sub(localCenter)
})
if (setDirty) this.setDirty({change: 'pivotToPoint', undo: true})
}
},

eventCallbacks: { eventCallbacks: {
onAddedToParent: function(this: IObject3D, e: Event): void { onAddedToParent: function(this: IObject3D, e: Event): void {
// added to some parent // added to some parent
if (!this.refreshUi) this.refreshUi = iObjectCommons.refreshUi if (!this.refreshUi) this.refreshUi = iObjectCommons.refreshUi
if (!this.autoScale) this.autoScale = iObjectCommons.autoScale.bind(this) if (!this.autoScale) this.autoScale = iObjectCommons.autoScale.bind(this)
if (!this.autoCenter) this.autoCenter = iObjectCommons.autoCenter.bind(this) if (!this.autoCenter) this.autoCenter = iObjectCommons.autoCenter.bind(this)
if (!this.pivotToBoundsCenter) this.pivotToBoundsCenter = iObjectCommons.pivotToBoundsCenter.bind(this)
if (!this.pivotToPoint) this.pivotToPoint = iObjectCommons.pivotToPoint.bind(this)


// fired from Object3D.js // fired from Object3D.js
this.addEventListener('added', iObjectCommons.eventCallbacks.onAddedToParent) this.addEventListener('added', iObjectCommons.eventCallbacks.onAddedToParent)

+ 3
- 16
src/plugins/animation/CameraViewPlugin.ts Datei anzeigen

import {onChange, serialize, timeout} from 'ts-browser-helpers' import {onChange, serialize, timeout} from 'ts-browser-helpers'
import {generateUiConfig, uiButton, uiDropdown, uiInput, UiObjectConfig, uiSlider, uiToggle} from 'uiconfig.js' import {generateUiConfig, uiButton, uiDropdown, uiInput, UiObjectConfig, uiSlider, uiToggle} from 'uiconfig.js'
import {EasingFunctions, EasingFunctionType} from '../../utils' import {EasingFunctions, EasingFunctionType} from '../../utils'
import {CameraView, ICamera, ICameraView, PerspectiveCamera2} from '../../core'
import {CameraView, ICamera, ICameraView} from '../../core'
import {AnimationResult, PopmotionPlugin} from './PopmotionPlugin' import {AnimationResult, PopmotionPlugin} from './PopmotionPlugin'
import {InteractionPromptPlugin} from '../interaction/InteractionPromptPlugin' import {InteractionPromptPlugin} from '../interaction/InteractionPromptPlugin'
import {getFittingDistance} from '../../three/utils/camera'


export interface CameraViewPluginOptions{duration?: number, ease?: EasingFunctionType, interpolateMode?: 'spherical'|'linear'} export interface CameraViewPluginOptions{duration?: number, ease?: EasingFunctionType, interpolateMode?: 'spherical'|'linear'}


public async animateToFitObject(selected?: Object3D, distanceMultiplier = 1.5, duration = 1000, ease?: Easing|EasingFunctionType, distanceBounds = {min: 0.5, max: 50.0}) { public async animateToFitObject(selected?: Object3D, distanceMultiplier = 1.5, duration = 1000, ease?: Easing|EasingFunctionType, distanceBounds = {min: 0.5, max: 50.0}) {
if (!this._viewer) return if (!this._viewer) return
const bbox = new Box3B().expandByObject(selected || this._viewer.scene.modelRoot, false, true) const bbox = new Box3B().expandByObject(selected || this._viewer.scene.modelRoot, false, true)
const cameraZ = getFittingDistance(this._viewer.scene.mainCamera, bbox)
const center = bbox.getCenter(new Vector3()) // world position const center = bbox.getCenter(new Vector3()) // world position
const size = bbox.getSize(new Vector3())

const cam = this._viewer.scene.mainCamera
let cameraZ = 1
if (cam.isPerspectiveCamera && size.length() > 0.0001) {
const aspect = isFinite(cam.aspect) ? cam.aspect : 1
// get the max side of the bounding box (fits to width OR height as needed )
const fov = Math.max(1, (cam as PerspectiveCamera2).fov) * (Math.PI / 180)
const fovh = 2 * Math.atan(Math.tan(fov / 2) * aspect)
const dx = size.z / 2 + Math.abs(size.x / 2 / Math.tan(fovh / 2))
const dy = size.z / 2 + Math.abs(size.y / 2 / Math.tan(fov / 2))
cameraZ = Math.max(dx, dy)

}

await this.animateToTarget(Math.min(distanceBounds.max, Math.max(distanceBounds.min, cameraZ * distanceMultiplier)), center, duration, ease) await this.animateToTarget(Math.min(distanceBounds.max, Math.max(distanceBounds.min, cameraZ * distanceMultiplier)), center, duration, ease)
} }



+ 1
- 1
src/plugins/interaction/TransformControlsPlugin.ts Datei anzeigen



@uiButton('Center All Meshes') @uiButton('Center All Meshes')
centerAllMeshes() { centerAllMeshes() {
this._viewer?.scene.centerAllGeometries(true)
return this._viewer?.scene.centerAllGeometries(true)
} }


} }

+ 3
- 2
src/postprocessing/GBufferRenderPass.ts Datei anzeigen

import {IPassID, IPipelinePass} from './Pass' import {IPassID, IPipelinePass} from './Pass'
import {ICamera, IMaterial, IRenderManager, IScene, IWebGLRenderer, PhysicalMaterial} from '../core' import {ICamera, IMaterial, IRenderManager, IScene, IWebGLRenderer, PhysicalMaterial} from '../core'
import {uiFolderContainer, UiObjectConfig, uiToggle} from 'uiconfig.js' import {uiFolderContainer, UiObjectConfig, uiToggle} from 'uiconfig.js'
import {getOrCall, ValOrFunc} from 'ts-browser-helpers'


@uiFolderContainer<GBufferRenderPass>((c)=>c.passId + ' Render Pass') @uiFolderContainer<GBufferRenderPass>((c)=>c.passId + ' Render Pass')
export class GBufferRenderPass<TP extends IPassID=IPassID, T extends WebGLMultipleRenderTargets | WebGLRenderTarget=WebGLMultipleRenderTargets | WebGLRenderTarget> extends RenderPass implements IPipelinePass<TP> { // todo: extend from jittered? export class GBufferRenderPass<TP extends IPassID=IPassID, T extends WebGLMultipleRenderTargets | WebGLRenderTarget=WebGLMultipleRenderTargets | WebGLRenderTarget> extends RenderPass implements IPipelinePass<TP> { // todo: extend from jittered?
after?: IPassID[] after?: IPassID[]
required?: IPassID[] required?: IPassID[]


constructor(public readonly passId: TP, public target: T, material: Material, clearColor: Color = new Color(1, 1, 1), clearAlpha = 1) {
constructor(public readonly passId: TP, public target: ValOrFunc<T>, material: Material, clearColor: Color = new Color(1, 1, 1), clearAlpha = 1) {
super(undefined, undefined, material, clearColor, clearAlpha) super(undefined, undefined, material, clearColor, clearAlpha)
} }


transparentRender: false, transparentRender: false,
transmissionRender: false, transmissionRender: false,
mainRenderPass: false, mainRenderPass: false,
}, ()=> super.render(renderer, null, this.target, deltaTime as any, maskActive as any)) // here this.target is the write-buffer, variable writeBuffer is ignored
}, ()=> super.render(renderer, null, getOrCall(this.target), deltaTime as any, maskActive as any)) // here this.target is the write-buffer, variable writeBuffer is ignored


this._transparentMats.forEach(m => m.transparent = !m.transparent) this._transparentMats.forEach(m => m.transparent = !m.transparent)
this._transparentMats.clear() this._transparentMats.clear()

+ 23
- 0
src/three/utils/camera.ts Datei anzeigen

import {Vector3} from 'three'
import {Box3B} from '../math/Box3B'
import {ICamera, PerspectiveCamera2} from '../../core'

/**
* Find distance of camera at which the camera's fov fits the given bounding box dimensions
* @param cam
* @param box
*/
export function getFittingDistance(cam: ICamera, box: Box3B): number {
const size = box.getSize(new Vector3())
let cameraZ = 1
if (cam.isPerspectiveCamera && size.length() > 0.0001) {
const aspect = isFinite(cam.aspect) ? cam.aspect : 1
// get the max side of the bounding box (fits to width OR height as needed )
const fov = Math.max(1, (cam as PerspectiveCamera2).fov) * (Math.PI / 180)
const fovh = 2 * Math.atan(Math.tan(fov / 2) * aspect)
const dx = size.z / 2 + Math.abs(size.x / 2 / Math.tan(fovh / 2))
const dy = size.z / 2 + Math.abs(size.y / 2 / Math.tan(fov / 2))
cameraZ = Math.max(dx, dy)
}
return cameraZ
}

+ 6
- 5
src/viewer/ThreeViewer.ts Datei anzeigen

// noinspection ES6PreferShortImport // noinspection ES6PreferShortImport
import {TonemapPlugin} from '../plugins/postprocessing/TonemapPlugin' import {TonemapPlugin} from '../plugins/postprocessing/TonemapPlugin'
import {VERSION} from './version' import {VERSION} from './version'
import {Easing} from 'popmotion'
import {OrbitControls3} from '../three' import {OrbitControls3} from '../three'


export interface IViewerEvent extends BaseEvent, Partial<IAnimationLoopEvent> { export interface IViewerEvent extends BaseEvent, Partial<IAnimationLoopEvent> {
*/ */
rgbm?: boolean rgbm?: boolean
/** /**
* Use rendered gbuffer as depth-prepass / z-prepass. (Requires DepthBufferPlugin/GBufferPlugin)
* todo: It will be disabled when there are any transparent/transmissive objects with render to depth buffer enabled.
* Use rendered gbuffer as depth-prepass / z-prepass. (Requires DepthBufferPlugin/GBufferPlugin).
* Set it to true if you only have opaque objects in the scene to get better performance.
*
* todo fix: It will be disabled when there are any transparent/transmissive objects with render to depth buffer enabled, see forceZPrepass
*/ */
zPrepass?: boolean zPrepass?: boolean
/** /**
target?: Vector3, target?: Vector3,


} }
// values above this might be clamped in post processing // values above this might be clamped in post processing
maxHDRIntensity?: number maxHDRIntensity?: number


// this.fromJSON(config, config.resources) // this.fromJSON(config, config.resources)
// } // }


public async fitToView(selected?: Object3D, distanceMultiplier = 1.5, duration?: number, ease?: Easing|EasingFunctionType) {
public async fitToView(selected?: Object3D, distanceMultiplier = 1.5, duration?: number, ease?: ((v: number) => number)|EasingFunctionType) {
const camViews = this.getPlugin<CameraViewPlugin>('CameraViews') const camViews = this.getPlugin<CameraViewPlugin>('CameraViews')
if (!camViews) { if (!camViews) {
this.console.error('ThreeViewer: CameraViewPlugin (CameraViews) is required for fitToView to work') this.console.error('ThreeViewer: CameraViewPlugin (CameraViews) is required for fitToView to work')

Laden…
Abbrechen
Speichern