| @@ -52,13 +52,10 @@ async function init() { | |||
| light.position.set(2, 2, 2) | |||
| light.lookAt(0, 0, 0) | |||
| light.castShadow = true | |||
| light.shadow.mapSize.setScalar(1024) | |||
| light.shadow.camera.near = 0.1 | |||
| light.shadow.camera.far = 10 | |||
| light.shadow.camera.top = 2 | |||
| light.shadow.camera.bottom = -2 | |||
| light.shadow.camera.left = -2 | |||
| light.shadow.camera.right = 2 | |||
| light.shadowMapSize.setScalar(1024) | |||
| light.shadowNear = 0.1 | |||
| light.shadowFar = 10 | |||
| light.shadowFrustum = 4 | |||
| viewer.renderManager.renderer.shadowMap.type = PCFSoftShadowMap | |||
| @@ -54,9 +54,9 @@ async function init() { | |||
| light.distance = 10 | |||
| light.decay = 1 | |||
| light.castShadow = true | |||
| light.shadow.mapSize.setScalar(1024) | |||
| light.shadow.camera.near = 0.1 | |||
| light.shadow.camera.far = 10 | |||
| light.shadowMapSize.setScalar(1024) | |||
| light.shadowNear = 0.1 | |||
| light.shadowFar = 10 | |||
| viewer.renderManager.renderer.shadowMap.type = PCFSoftShadowMap | |||
| @@ -4,7 +4,7 @@ import { | |||
| BasicShadowMap, | |||
| Color, | |||
| DataUtils, | |||
| DirectionalLight, | |||
| DirectionalLight, DirectionalLight2, | |||
| IObject3D, | |||
| LoadingScreenPlugin, | |||
| MaterialExtension, | |||
| @@ -141,19 +141,16 @@ float ambientOcclusion = getShadow( directionalShadowMap[ ii ], edls.shadowMapSi | |||
| } | |||
| function createDirLight(viewer: ThreeViewer) { | |||
| const directionalLight = new DirectionalLight(0xffffff, 4) | |||
| const directionalLight = new DirectionalLight2(0xffffff, 4) | |||
| directionalLight.position.set(-2, -2, 2) | |||
| directionalLight.lookAt(0, 0, 0) | |||
| directionalLight.color.set(0xffffff) | |||
| directionalLight.intensity = 0 | |||
| directionalLight.castShadow = true | |||
| directionalLight.shadow.mapSize.setScalar(1024) | |||
| directionalLight.shadow.camera.near = 0.1 | |||
| directionalLight.shadow.camera.far = 10 | |||
| directionalLight.shadow.camera.top = 2 | |||
| directionalLight.shadow.camera.bottom = -2 | |||
| directionalLight.shadow.camera.left = -2 | |||
| directionalLight.shadow.camera.right = 2 | |||
| directionalLight.shadowMapSize.setScalar(1024) | |||
| directionalLight.shadowNear = 0.1 | |||
| directionalLight.shadowFar = 10 | |||
| directionalLight.shadowFrustum = 4 | |||
| viewer.scene.addObject(directionalLight, {addToRoot: true}) | |||
| // move to index 0 in parent.children, so that directionalLight always has index 0 in shader. required for material extension | |||
| const parent = directionalLight.parent! | |||
| @@ -2,7 +2,7 @@ import { | |||
| _testFinish, | |||
| BasicShadowMap, | |||
| Box3B, | |||
| DirectionalLight, | |||
| DirectionalLight2, | |||
| IObject3D, | |||
| LoadingScreenPlugin, | |||
| Mesh, | |||
| @@ -50,17 +50,14 @@ async function init() { | |||
| ground.receiveShadow = true | |||
| viewer.scene.addObject(ground) | |||
| const directionalLight = viewer.scene.addObject(new DirectionalLight(0xffffff, 4)) | |||
| const directionalLight = viewer.scene.addObject(new DirectionalLight2(0xffffff, 4)) | |||
| directionalLight.position.set(2, 2, 2) | |||
| directionalLight.lookAt(0, 0, 0) | |||
| directionalLight.castShadow = true | |||
| directionalLight.shadow.mapSize.setScalar(1024) | |||
| directionalLight.shadow.camera.near = 0.1 | |||
| directionalLight.shadow.camera.far = 10 | |||
| directionalLight.shadow.camera.top = 2 | |||
| directionalLight.shadow.camera.bottom = -2 | |||
| directionalLight.shadow.camera.left = -2 | |||
| directionalLight.shadow.camera.right = 2 | |||
| directionalLight.shadowMapSize.setScalar(1024) | |||
| directionalLight.shadowNear = 0.1 | |||
| directionalLight.shadowFar = 10 | |||
| directionalLight.shadowFrustum = 4 | |||
| viewer.renderManager.renderer.shadowMap.type = BasicShadowMap | |||
| @@ -56,11 +56,11 @@ async function init() { | |||
| light.distance = 5 | |||
| light.decay = 0.5 | |||
| light.castShadow = true | |||
| light.shadow.mapSize.setScalar(1024) | |||
| light.shadow.camera.near = 0.1 | |||
| light.shadow.camera.far = 10 | |||
| light.shadow.camera.aspect = 1 | |||
| light.shadow.camera.fov = 45 | |||
| light.shadowMapSize.setScalar(1024) | |||
| light.shadowNear = 0.1 | |||
| light.shadowFar = 10 | |||
| light.shadowAspect = 1 | |||
| light.shadowFov = 45 | |||
| viewer.renderManager.renderer.shadowMap.type = PCFSoftShadowMap | |||
| @@ -54,13 +54,10 @@ async function init() { | |||
| light.position.set(2, 2, 2) | |||
| light.lookAt(0, 0, 0) | |||
| light.castShadow = true | |||
| light.shadow.mapSize.setScalar(1024) | |||
| light.shadow.camera.near = 0.1 | |||
| light.shadow.camera.far = 10 | |||
| light.shadow.camera.top = 2 | |||
| light.shadow.camera.bottom = -2 | |||
| light.shadow.camera.left = -2 | |||
| light.shadow.camera.right = 2 | |||
| light.shadowMapSize.setScalar(1024) | |||
| light.shadowNear = 0.1 | |||
| light.shadowFar = 10 | |||
| light.shadowFrustum = 4 | |||
| viewer.renderManager.renderer.shadowMap.type = PCFSoftShadowMap | |||
| @@ -82,63 +82,8 @@ async function init() { | |||
| light.lookAt(-25, 0, 0) | |||
| light.intensity = 30 | |||
| light.castShadow = true | |||
| light.shadow.camera.left = -25 | |||
| light.shadow.camera.right = 25 | |||
| light.shadow.camera.top = 25 | |||
| light.shadow.camera.bottom = -25 | |||
| light.shadow.mapSize.set(1024, 1024) | |||
| // todo add to DirectionalLight | |||
| light.uiConfig.children!.push({ | |||
| type: 'vec2', | |||
| label: 'Shadow Map Size', | |||
| property: [light?.shadow, 'mapSize'], | |||
| onChange: ()=>{ | |||
| light.shadow.map?.dispose() | |||
| light.shadow.mapPass?.dispose() | |||
| light.shadow.map = null as any | |||
| light.shadow.mapPass = null as any | |||
| }, | |||
| }, | |||
| { | |||
| type: 'slider', | |||
| bounds: [-0.001, 0.001], | |||
| stepSize: 0.00002, | |||
| label: 'Shadow Bias', | |||
| property: [light?.shadow, 'bias'], | |||
| onChange: (e)=>light.setDirty(e), | |||
| }, | |||
| { | |||
| type: 'slider', | |||
| bounds: [-0.1, 0.1], | |||
| stepSize: 0.005, | |||
| label: 'Shadow Normal Bias', | |||
| property: [light?.shadow, 'normalBias'], | |||
| onChange: (e)=>light.setDirty(e), | |||
| }, | |||
| { | |||
| type: 'slider', | |||
| bounds: [0, 5], | |||
| label: 'Shadow radius', | |||
| property: [light?.shadow, 'radius'], | |||
| onChange: (e)=>light.setDirty(e), | |||
| }, | |||
| { | |||
| type: 'slider', | |||
| bounds: [0.1, 50], | |||
| label: 'Shadow frustum', | |||
| // property: [light.shadow, 'radius'], | |||
| getValue: ()=>{ | |||
| return light.shadow.camera.right * 2 | |||
| }, | |||
| setValue: (v: number)=>{ | |||
| light.shadow.camera.left = -v / 2 | |||
| light.shadow.camera.right = v / 2 | |||
| light.shadow.camera.top = v / 2 | |||
| light.shadow.camera.bottom = -v / 2 | |||
| }, | |||
| onChange: (e)=>light.setDirty(e), | |||
| }) | |||
| light.shadowFrustum = 50 | |||
| light.shadowMapSize.set(1024, 1024) | |||
| ui.appendChild(light.uiConfig) | |||
| } | |||
| @@ -12,6 +12,7 @@ import { | |||
| PhysicalMaterial, | |||
| UnlitLineMaterial, | |||
| UnlitMaterial, | |||
| ObjectShaderMaterial, | |||
| } from '../core' | |||
| import {downloadFile} from 'ts-browser-helpers' | |||
| import {MaterialExtension} from '../materials' | |||
| @@ -31,6 +32,7 @@ export class MaterialManager<T = ''> extends EventDispatcher<BaseEvent, T> { | |||
| UnlitLineMaterial.MaterialTemplate, | |||
| LineMaterial2.MaterialTemplate, | |||
| LegacyPhongMaterial.MaterialTemplate, | |||
| ObjectShaderMaterial.MaterialTemplate, | |||
| ] | |||
| private _materials: IMaterial[] = [] | |||
| @@ -66,7 +66,7 @@ export class AmbientLight2 extends AmbientLight implements ILight<undefined> { | |||
| clone: (recursive?: boolean) => this | |||
| remove: (...object: IObject3D[]) => this | |||
| dispatchEvent: (event: ILightEvent) => void | |||
| declare parent: null | |||
| declare parent: IObject3D | null | |||
| declare children: IObject3D[] | |||
| // endregion | |||
| @@ -1,11 +1,19 @@ | |||
| import {Color, ColorRepresentation, DirectionalLight, DirectionalLightShadow, Euler, Vector3} from 'three' | |||
| import {Color, ColorRepresentation, DirectionalLight, DirectionalLightShadow, Euler, Vector2, Vector3} from 'three' | |||
| import {ILight, ILightEvent, ILightEventTypes} from './ILight' | |||
| import {iLightCommons} from '../object/iLightCommons' | |||
| import {IObject3D} from '../IObject' | |||
| import {uiColor, UiObjectConfig, uiPanelContainer, uiSlider, uiToggle, uiVector} from 'uiconfig.js' | |||
| import {onChange3} from 'ts-browser-helpers' | |||
| import {uiColor, uiNumber, UiObjectConfig, uiPanelContainer, uiSlider, uiToggle, uiVector} from 'uiconfig.js' | |||
| import {onChange2, onChange3} from 'ts-browser-helpers' | |||
| import {bindToValue} from '../../three' | |||
| // todo: add LightShadow uiconfig | |||
| /** | |||
| * Extension of three.js DirectionalLight with additional properties for serialization and UI | |||
| * A directional light is a light source that has a position but no dimensions - a single point in space that emits light in a specific direction. | |||
| * | |||
| * Note - gltf serialization is handled by {@link GLTFLightExtrasExtension} | |||
| * | |||
| * @category Lights | |||
| */ | |||
| // todo: add Light section in the readme detailing these ...2 lights | |||
| @uiPanelContainer('Directional Light') | |||
| export class DirectionalLight2< | |||
| @@ -35,20 +43,72 @@ export class DirectionalLight2< | |||
| @onChange3('setDirty') | |||
| declare castShadow: boolean | |||
| @uiVector('Shadow Map Size') | |||
| @bindToValue({obj: 'shadow', key: 'mapSize', onChange: DirectionalLight2.prototype._mapSizeChanged, onChangeParams: false}) | |||
| shadowMapSize: Vector2 | |||
| protected _mapSizeChanged() { | |||
| this.shadow.map?.dispose() | |||
| this.shadow.mapPass?.dispose() | |||
| this.shadow.map = null as any | |||
| this.shadow.mapPass = null as any | |||
| this.setDirty({change: 'shadowMapSize'}) | |||
| } | |||
| @uiSlider('Shadow Bias', [-0.001, 0.001], 0.00001) | |||
| @bindToValue({obj: 'shadow', key: 'bias', onChange: 'setDirty'}) | |||
| shadowBias: number | |||
| @uiSlider('Shadow Normal Bias', [-0.1, 0.1], 0.005) | |||
| @bindToValue({obj: 'shadow', key: 'normalBias', onChange: 'setDirty'}) | |||
| shadowNormalBias: number | |||
| @uiSlider('Shadow Radius', [0, 5], 0.01) | |||
| @bindToValue({obj: 'shadow', key: 'radius', onChange: 'setDirty'}) | |||
| shadowRadius: number | |||
| @uiSlider('Shadow Frustum', [0.1, 50], 0.01) | |||
| @onChange2(DirectionalLight2.prototype._shadowFrustumChanged) | |||
| shadowFrustum: number | |||
| @uiNumber('Shadow Near') | |||
| @bindToValue({obj: 'shadow', key: ['camera', 'near'], onChange: DirectionalLight2.prototype._shadowCamUpdate}) | |||
| shadowNear: number | |||
| @uiNumber('Shadow Far') | |||
| @bindToValue({obj: 'shadow', key: ['camera', 'far'], onChange: DirectionalLight2.prototype._shadowCamUpdate}) | |||
| shadowFar: number | |||
| protected _shadowFrustumChanged() { | |||
| const v = this.shadowFrustum | |||
| this.shadow.camera.left = -v / 2 | |||
| this.shadow.camera.right = v / 2 | |||
| this.shadow.camera.top = v / 2 | |||
| this.shadow.camera.bottom = -v / 2 | |||
| this.shadow.camera.updateProjectionMatrix() | |||
| this.setDirty({change: 'shadowFrustum'}) | |||
| } | |||
| protected _shadowCamUpdate(change?: string) { | |||
| this.shadow.camera.updateProjectionMatrix() | |||
| this.setDirty({change}) | |||
| } | |||
| constructor(color?: ColorRepresentation, intensity?: number) { | |||
| super(color, intensity) | |||
| this.target.position.set(0, 0, -1) // because of GLTF spec: https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_lights_punctual | |||
| this.add(this.target) // todo: make sure the child isn't exported in gltf | |||
| iLightCommons.upgradeLight.call(this) | |||
| this.shadowFrustum = 10 | |||
| } | |||
| autoScale() { | |||
| console.warn('AutoScale not supported on Lights') | |||
| console.warn('DirectionalLight2: AutoScale not supported on Lights') | |||
| return this | |||
| } | |||
| autoCenter() { | |||
| console.warn('AutoCenter not supported on Lights') | |||
| console.warn('DirectionalLight2: AutoCenter not supported on Lights') | |||
| return this | |||
| } | |||
| @@ -66,7 +126,6 @@ export class DirectionalLight2< | |||
| return this | |||
| } | |||
| // region inherited type fixes | |||
| // re-declaring from IObject3D because: https://github.com/microsoft/TypeScript/issues/16936 | |||
| @@ -80,7 +139,7 @@ export class DirectionalLight2< | |||
| clone: (recursive?: boolean) => this | |||
| remove: (...object: IObject3D[]) => this | |||
| dispatchEvent: (event: ILightEvent) => void | |||
| declare parent: null | |||
| declare parent: IObject3D | null | |||
| declare children: IObject3D[] | |||
| // endregion | |||
| @@ -70,7 +70,7 @@ export class HemisphereLight2 extends HemisphereLight implements ILight<undefine | |||
| clone: (recursive?: boolean) => this | |||
| remove: (...object: IObject3D[]) => this | |||
| dispatchEvent: (event: ILightEvent) => void | |||
| declare parent: null | |||
| declare parent: IObject3D | null | |||
| declare children: IObject3D[] | |||
| // endregion | |||
| @@ -1,10 +1,16 @@ | |||
| import {Color, ColorRepresentation, PointLight, PointLightShadow, Vector3} from 'three' | |||
| import {Color, ColorRepresentation, PointLight, PointLightShadow, Vector2, Vector3} from 'three' | |||
| import {ILight, ILightEvent} from './ILight' | |||
| import {iLightCommons} from '../object/iLightCommons' | |||
| import {IObject3D} from '../IObject' | |||
| import {uiColor, uiNumber, UiObjectConfig, uiPanelContainer, uiSlider, uiToggle, uiVector} from 'uiconfig.js' | |||
| import {onChange3} from 'ts-browser-helpers' | |||
| import {bindToValue} from '../../three' | |||
| /** | |||
| * Extension of three.js PointLight with additional properties for serialization and UI | |||
| * | |||
| * Note - gltf serialization is handled by {@link GLTFLightExtrasExtension} | |||
| */ | |||
| @uiPanelContainer('Point Light') | |||
| export class PointLight2 extends PointLight implements ILight<PointLightShadow> { | |||
| assetType = 'light' as const | |||
| @@ -28,12 +34,57 @@ export class PointLight2 extends PointLight implements ILight<PointLightShadow> | |||
| @uiNumber('Decay') | |||
| @onChange3('setDirty') | |||
| declare decay: number | |||
| @uiNumber('Power') | |||
| @onChange3('setDirty') | |||
| declare power: number | |||
| @uiVector('Position', undefined, undefined, (that: PointLight2)=>({onChange: ()=>that.setDirty()})) | |||
| declare readonly position: Vector3 | |||
| @uiToggle('Cast Shadow') | |||
| @onChange3('setDirty') | |||
| declare castShadow: boolean | |||
| @uiVector('Shadow Map Size') | |||
| @bindToValue({obj: 'shadow', key: 'mapSize', onChange: PointLight2.prototype._mapSizeChanged, onChangeParams: false}) | |||
| shadowMapSize: Vector2 | |||
| protected _mapSizeChanged() { | |||
| this.shadow.map?.dispose() | |||
| this.shadow.mapPass?.dispose() | |||
| this.shadow.map = null as any | |||
| this.shadow.mapPass = null as any | |||
| this.setDirty({change: 'shadowMapSize'}) | |||
| } | |||
| @uiSlider('Shadow Bias', [-0.001, 0.001], 0.00001) | |||
| @bindToValue({obj: 'shadow', key: 'bias', onChange: 'setDirty'}) | |||
| shadowBias: number | |||
| @uiSlider('Shadow Radius', [0, 5], 0.01) | |||
| @bindToValue({obj: 'shadow', key: 'radius', onChange: 'setDirty'}) | |||
| shadowRadius: number | |||
| @uiNumber('Shadow Near') | |||
| @bindToValue({obj: 'shadow', key: ['camera', 'near'], onChange: PointLight2.prototype._shadowCamUpdate}) | |||
| shadowNear: number | |||
| @uiNumber('Shadow Far') | |||
| @bindToValue({obj: 'shadow', key: ['camera', 'far'], onChange: PointLight2.prototype._shadowCamUpdate}) | |||
| shadowFar: number | |||
| @uiNumber('Shadow Aspect') | |||
| @bindToValue({obj: 'shadow', key: 'aspect', onChange: PointLight2.prototype._shadowCamUpdate}) | |||
| shadowAspect: number | |||
| @uiSlider('Shadow FOV', [1, 179], 1) | |||
| @bindToValue({obj: 'shadow', key: 'fov', onChange: PointLight2.prototype._shadowCamUpdate}) | |||
| shadowFov: number | |||
| protected _shadowCamUpdate(change?: string) { | |||
| this.shadow.camera.updateProjectionMatrix() | |||
| this.setDirty({change}) | |||
| } | |||
| constructor(color?: ColorRepresentation, intensity?: number, distance?: number, decay?: number) { | |||
| super(color, intensity, distance, decay) | |||
| iLightCommons.upgradeLight.call(this) | |||
| @@ -77,7 +128,7 @@ export class PointLight2 extends PointLight implements ILight<PointLightShadow> | |||
| clone: (recursive?: boolean) => this | |||
| remove: (...object: IObject3D[]) => this | |||
| dispatchEvent: (event: ILightEvent) => void | |||
| declare parent: null | |||
| declare parent: IObject3D | null | |||
| declare children: IObject3D[] | |||
| // endregion | |||
| @@ -75,7 +75,7 @@ export class RectAreaLight2 extends RectAreaLight implements ILight<undefined> { | |||
| clone: (recursive?: boolean) => this | |||
| remove: (...object: IObject3D[]) => this | |||
| dispatchEvent: (event: ILightEvent) => void | |||
| declare parent: null | |||
| declare parent: IObject3D | null | |||
| declare children: IObject3D[] | |||
| // endregion | |||
| @@ -1,10 +1,16 @@ | |||
| import {Color, ColorRepresentation, Euler, SpotLight, SpotLightShadow, Vector3} from 'three' | |||
| import {Color, ColorRepresentation, Euler, SpotLight, SpotLightShadow, Vector2, Vector3} from 'three' | |||
| import {ILight, ILightEvent} from './ILight' | |||
| import {iLightCommons} from '../object/iLightCommons' | |||
| import {IObject3D} from '../IObject' | |||
| import {uiColor, uiInput, UiObjectConfig, uiPanelContainer, uiSlider, uiToggle, uiVector} from 'uiconfig.js' | |||
| import {uiColor, uiInput, uiNumber, UiObjectConfig, uiPanelContainer, uiSlider, uiToggle, uiVector} from 'uiconfig.js' | |||
| import {onChange3} from 'ts-browser-helpers' | |||
| import {bindToValue} from '../../three' | |||
| /** | |||
| * Extension of three.js SpotLight with additional properties for serialization and UI | |||
| * | |||
| * Note - gltf serialization is handled by {@link GLTFLightExtrasExtension} | |||
| */ | |||
| @uiPanelContainer('Spot Light') | |||
| export class SpotLight2 extends SpotLight implements ILight<SpotLightShadow> { | |||
| assetType = 'light' as const | |||
| @@ -42,6 +48,51 @@ export class SpotLight2 extends SpotLight implements ILight<SpotLightShadow> { | |||
| @onChange3('setDirty') | |||
| declare castShadow: boolean | |||
| @uiVector('Shadow Map Size') | |||
| @bindToValue({obj: 'shadow', key: 'mapSize', onChange: SpotLight2.prototype._mapSizeChanged, onChangeParams: false}) | |||
| shadowMapSize: Vector2 | |||
| protected _mapSizeChanged() { | |||
| this.shadow.map?.dispose() | |||
| this.shadow.mapPass?.dispose() | |||
| this.shadow.map = null as any | |||
| this.shadow.mapPass = null as any | |||
| this.setDirty({change: 'shadowMapSize'}) | |||
| } | |||
| @uiSlider('Shadow Bias', [-0.001, 0.001], 0.00001) | |||
| @bindToValue({obj: 'shadow', key: 'bias', onChange: 'setDirty'}) | |||
| shadowBias: number | |||
| @uiSlider('Shadow Radius', [0, 5], 0.01) | |||
| @bindToValue({obj: 'shadow', key: 'radius', onChange: 'setDirty'}) | |||
| shadowRadius: number | |||
| @uiSlider('Shadow Focus', [0, 1], 0.001) | |||
| @bindToValue({obj: 'shadow', key: 'focus', onChange: 'setDirty'}) | |||
| shadowFocus: number | |||
| @uiNumber('Shadow Near') | |||
| @bindToValue({obj: 'shadow', key: ['camera', 'near'], onChange: SpotLight2.prototype._shadowCamUpdate}) | |||
| shadowNear: number | |||
| @uiNumber('Shadow Far') | |||
| @bindToValue({obj: 'shadow', key: ['camera', 'far'], onChange: SpotLight2.prototype._shadowCamUpdate}) | |||
| shadowFar: number | |||
| @uiNumber('Shadow Aspect') | |||
| @bindToValue({obj: 'shadow', key: 'aspect', onChange: SpotLight2.prototype._shadowCamUpdate}) | |||
| shadowAspect: number | |||
| @uiSlider('Shadow FOV', [1, 179], 1) | |||
| @bindToValue({obj: 'shadow', key: 'fov', onChange: SpotLight2.prototype._shadowCamUpdate}) | |||
| shadowFov: number | |||
| protected _shadowCamUpdate(change?: string) { | |||
| this.shadow.camera.updateProjectionMatrix() | |||
| this.setDirty({change}) | |||
| } | |||
| constructor(color?: ColorRepresentation, intensity?: number, distance?: number, | |||
| angle?: number, | |||
| penumbra?: number, | |||
| @@ -76,7 +127,6 @@ export class SpotLight2 extends SpotLight implements ILight<SpotLightShadow> { | |||
| return this | |||
| } | |||
| // region inherited type fixes | |||
| // re-declaring from IObject3D because: https://github.com/microsoft/TypeScript/issues/16936 | |||
| @@ -90,7 +140,7 @@ export class SpotLight2 extends SpotLight implements ILight<SpotLightShadow> { | |||
| clone: (recursive?: boolean) => this | |||
| remove: (...object: IObject3D[]) => this | |||
| dispatchEvent: (event: ILightEvent) => void | |||
| declare parent: null | |||
| declare parent: IObject3D | null | |||
| declare children: IObject3D[] | |||
| // endregion | |||
| @@ -44,7 +44,7 @@ export class Mesh2< | |||
| clone: (recursive?: boolean) => this | |||
| remove: (...object: IObject3D[]) => this | |||
| dispatchEvent: (event: ILightEvent) => void | |||
| declare parent: null | |||
| declare parent: IObject3D | null | |||
| declare children: IObject3D[] | |||
| dispose: (removeFromParent?: boolean) => void | |||
| @@ -587,7 +587,7 @@ export class RootScene extends Scene<ISceneEvent, ISceneEventTypes> implements I | |||
| clone: (recursive?: boolean) => this | |||
| remove: (...object: IObject3D[]) => this | |||
| dispatchEvent: (event: ISceneEvent) => void | |||
| declare parent: null | |||
| declare parent: IObject3D | null | |||
| declare children: IObject3D[] | |||
| // endregion | |||
| @@ -3,9 +3,12 @@ import {IObjectProcessor, IObjectSetDirtyOptions} from '../IObject' | |||
| import {iObjectCommons} from './iObjectCommons' | |||
| export const iLightCommons = { | |||
| setDirty: function(this: ILight, options?: IObjectSetDirtyOptions): void { | |||
| this.dispatchEvent({bubbleToParent: true, ...options, type: 'lightUpdate', light: this, object: this}) // this sets sceneUpdate in root scene | |||
| iObjectCommons.setDirty.call(this, options) | |||
| setDirty: function(this: ILight, options?: IObjectSetDirtyOptions, ...args: any[]): void { | |||
| if (typeof options === 'string') { // just incase called by decorators | |||
| options = {change: options} | |||
| } | |||
| this.dispatchEvent({bubbleToParent: true, ...options, type: 'lightUpdate', light: this, object: this, args}) // this sets sceneUpdate in root scene | |||
| iObjectCommons.setDirty.call(this, options, ...args) | |||
| }, | |||
| upgradeLight: upgradeLight, | |||
| refreshUi: iObjectCommons.refreshUi, | |||
| @@ -11,8 +11,11 @@ import {iMaterialCommons} from '../material/iMaterialCommons' | |||
| import {ILight} from '../light/ILight' | |||
| export const iObjectCommons = { | |||
| setDirty: function(this: IObject3D, options?: IObjectSetDirtyOptions): void { | |||
| this.dispatchEvent({bubbleToParent: true, ...options, type: 'objectUpdate', object: this}) // this sets sceneUpdate in root scene | |||
| setDirty: function(this: IObject3D, options?: IObjectSetDirtyOptions, ...args: any[]): void { | |||
| if (typeof options === 'string') { // just incase called by decorators | |||
| options = {change: options} | |||
| } | |||
| this.dispatchEvent({bubbleToParent: true, ...options, type: 'objectUpdate', object: this, args}) // this sets sceneUpdate in root scene | |||
| if (options?.refreshUi !== false && options?.last !== false) this.refreshUi?.() | |||
| // console.log('object update') | |||
| }, | |||
| @@ -1,4 +1,4 @@ | |||
| import {FnCaller, getOrCall, objectHasOwn, safeSetProperty, ValOrFunc} from 'ts-browser-helpers' | |||
| import {FnCaller, getOrCall, objectHasOwn, safeSetProperty, ValOrArr, ValOrFunc} from 'ts-browser-helpers' | |||
| /** | |||
| * | |||
| @@ -132,15 +132,23 @@ export function matDefineBool(key?: string|symbol, customDefines?: any, thisMat | |||
| * @param processVal - function that processes the value before setting it. | |||
| * @param invProcessVal - function that processes the value before returning it. | |||
| */ | |||
| export function bindToValue({obj, key, processVal, invProcessVal, onChange, onChangeParams = true}: {obj?: ValOrFunc<any>, key?: ValOrFunc<string | symbol>, onChange?: ((...args: any[]) => any)|string, processVal?: (newVal: any) => any, invProcessVal?: (val: any) => any, onChangeParams?: boolean}): PropertyDecorator { | |||
| export function bindToValue({obj, key, processVal, invProcessVal, onChange, onChangeParams = true}: {obj?: ValOrFunc<any>, key?: ValOrFunc<ValOrArr<string | symbol>>, onChange?: ((...args: any[]) => any)|string, processVal?: (newVal: any) => any, invProcessVal?: (val: any) => any, onChangeParams?: boolean}): PropertyDecorator { | |||
| const cPropKey = !!key | |||
| return (targetPrototype: any, propertyKey: string|symbol, descriptor?: TypedPropertyDescriptor<any>) => { | |||
| const getTarget = (_this: any)=>{ | |||
| let t = getOrCall(obj) || _this | |||
| if (typeof t === 'string') t = _this[t] | |||
| const p = cPropKey ? getOrCall(key) || propertyKey : propertyKey | |||
| return {t, p} | |||
| let p1 = cPropKey ? getOrCall(key) || propertyKey : propertyKey | |||
| let p: string|symbol | |||
| if (Array.isArray(p1)) { | |||
| while (p1.length > 1 && t && typeof t === 'object') { | |||
| t = t[p1[0]] | |||
| p1 = p1.slice(1) | |||
| } | |||
| p = p1.length ? p1[0] : propertyKey | |||
| } else p = p1 | |||
| return {t, p: p} | |||
| } | |||
| const prop = { | |||
| get() { | |||