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

Add different types of widgets and refactor existing ones.

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

src/three/utils/TransformControls.d.ts → src/three/controls/TransformControls.d.ts Просмотреть файл

@@ -1,5 +1,15 @@
/* eslint-disable */
import {Camera, Mesh, MOUSE, Object3D, Quaternion, Raycaster, Vector3} from 'three';
import {
Camera,
LineBasicMaterial,
Mesh,
MeshBasicMaterial,
MOUSE,
Object3D,
Quaternion,
Raycaster,
Vector3
} from 'three';

export class TransformControls extends Object3D {
constructor(object: Camera, domElement?: HTMLElement);
@@ -36,6 +46,12 @@ export class TransformControls extends Object3D {
setSpace(space: 'world' | 'local'): void;
reset(): void;
dispose(): void;


static ObjectConstructors: {
MeshBasicMaterial: typeof MeshBasicMaterial;
LineBasicMaterial: typeof LineBasicMaterial;
}
}

export class TransformControlsGizmo extends Object3D {

src/three/utils/TransformControls.js → src/three/controls/TransformControls.js Просмотреть файл

@@ -662,6 +662,11 @@ class TransformControls extends Object3D {

}

TransformControls.ObjectConstructors = {
'MeshBasicMaterial': MeshBasicMaterial,
'LineBasicMaterial': LineBasicMaterial,
};

// mouse / touch event handlers

function getPointer( event ) {
@@ -793,7 +798,7 @@ class TransformControlsGizmo extends Object3D {

// shared materials

const gizmoMaterial = new MeshBasicMaterial( {
const gizmoMaterial = new TransformControls.ObjectConstructors.MeshBasicMaterial( {
depthTest: false,
depthWrite: false,
fog: false,
@@ -801,7 +806,7 @@ class TransformControlsGizmo extends Object3D {
transparent: true
} );

const gizmoLineMaterial = new LineBasicMaterial( {
const gizmoLineMaterial = new TransformControls.ObjectConstructors.LineBasicMaterial( {
depthTest: false,
depthWrite: false,
fog: false,
@@ -1519,7 +1524,7 @@ class TransformControlsPlane extends Mesh {

super(
new PlaneGeometry( 100000, 100000, 2, 2 ),
new MeshBasicMaterial( { visible: false, wireframe: true, side: DoubleSide, transparent: true, opacity: 0.1, toneMapped: false } )
new TransformControls.ObjectConstructors.MeshBasicMaterial( { visible: false, wireframe: true, side: DoubleSide, transparent: true, opacity: 0.1, toneMapped: false } )
);

this.isTransformControlsPlane = true;

src/three/utils/TransformControls2.ts → src/three/controls/TransformControls2.ts Просмотреть файл

@@ -1,6 +1,7 @@
import {TransformControls} from './TransformControls.js'
import {MathUtils} from 'three'
import {ICamera, IObject3D, iObjectCommons, ISceneEvent, IWidget} from '../../core'
import type {ICamera, IObject3D, ISceneEvent, IWidget} from '../../core'
import {iObjectCommons} from '../../core'
import {uiDropdown, uiNumber, uiPanelContainer, uiToggle} from 'uiconfig.js'

@uiPanelContainer('Transform Controls')

+ 3
- 0
src/three/index.ts Просмотреть файл

@@ -1,3 +1,6 @@
export {OrbitControls3, type TOrbitControlsEvents} from './controls/OrbitControls3'
export {TransformControls2} from './controls/TransformControls2'
export {TransformControls, TransformControlsGizmo, TransformControlsPlane} from './controls/TransformControls'
export {Box3B} from './math/Box3B'
export * from './utils/index'
export * from './widgets/index'

+ 0
- 3
src/three/utils/index.ts Просмотреть файл

@@ -7,10 +7,7 @@ export {generateUUID, toIndexedGeometry, isInScene, localToWorldQuaternion, worl
export {getTextureDataType, textureToCanvas, textureDataToImageData, textureToDataUrl, textureToBlob, texImageToCanvas} from './texture'
export {threeConstMappings} from './const-mappings'
export {ObjectPicker} from './ObjectPicker'
export {SelectionWidget, BoxSelectionWidget} from './SelectionWidget'
export {autoGPUInstanceMeshes} from './gpu-instancing'
export {ViewHelper2, type GizmoOrientation, type DomPlacement} from './ViewHelper2'
export {TransformControls2} from './TransformControls2'
export {TransformControls, TransformControlsGizmo, TransformControlsPlane} from './TransformControls'

// export {} from './constants'

+ 17
- 0
src/three/widgets/ACameraHelperWidget.ts Просмотреть файл

@@ -0,0 +1,17 @@
import {Camera} from 'three'
import {IUiConfigContainer} from 'uiconfig.js'
import {AHelperWidget} from './AHelperWidget'

export abstract class ACameraHelperWidget extends AHelperWidget {
camera: (Camera & IUiConfigContainer) | undefined
protected constructor(object: Camera & IUiConfigContainer) {
super(object)
this.camera = object
this.traverse(o => {
o.userData.__keepShadowDef = true
o.castShadow = false
o.receiveShadow = false
})
}

}

+ 79
- 0
src/three/widgets/AHelperWidget.ts Просмотреть файл

@@ -0,0 +1,79 @@
import {Object3D, PerspectiveCamera} from 'three'
import {generateUiFolder, IUiConfigContainer, uiToggle} from 'uiconfig.js'
import {iObjectCommons, IWidget} from '../../core'
import {onChange2} from 'ts-browser-helpers'

export abstract class AHelperWidget extends Object3D implements IWidget {
modelObject = this
isWidget = true as const
assetType = 'widget'

object: (Object3D & IUiConfigContainer) | undefined

@uiToggle()
@onChange2(AHelperWidget.prototype.update)
visible = true

protected constructor(object: Object3D & IUiConfigContainer) {
super()
this.object = object
this.object.updateMatrixWorld()
if ((this.object as PerspectiveCamera).updateProjectionMatrix)
(this.object as PerspectiveCamera).updateProjectionMatrix()

this.matrix = object.matrixWorld
this.matrixAutoUpdate = false

this._objectUpdate = this._objectUpdate.bind(this)
this.attach(object)
this.traverse(o => {
o.userData.__keepShadowDef = true
o.castShadow = false
o.receiveShadow = false
})
}

dispose() {
this.detach()
}

update() {
iObjectCommons.setDirty.call(this)
}

private _objectUpdate() {
if (this.object) this.update()
}

attach(object: Object3D): this {
if (this.object) this.detach()
this.object = object
if (this.object) {
this.update()
this.object.addEventListener('objectUpdate', this._objectUpdate)
this.object.addEventListener('dispose', this.dispose)
this.uiConfig && this.object.uiConfig?.children?.push(this.uiConfig)
this.visible = true
}
return this
}

detach(): this {
if (this.object) {
this.object.removeEventListener('objectUpdate', this._objectUpdate)
this.object.removeEventListener('dispose', this.dispose)
if (this.uiConfig) {
const i = this.object.uiConfig?.children?.indexOf(this.uiConfig)
if (i !== undefined && i >= 0)
this.object.uiConfig?.children?.splice(i, 1)
}
this.object = undefined
this.visible = false
}
return this
}

uiConfig = generateUiFolder('Widget', this)

}


+ 18
- 0
src/three/widgets/ALightHelperWidget.ts Просмотреть файл

@@ -0,0 +1,18 @@
import {Light} from 'three'
import {IUiConfigContainer} from 'uiconfig.js'
import {AHelperWidget} from './AHelperWidget'

export abstract class ALightHelperWidget extends AHelperWidget {

light: (Light & IUiConfigContainer)|undefined
protected constructor(object: Light & IUiConfigContainer) {
super(object)
this.light = object
this.traverse(o => {
o.userData.__keepShadowDef = true
o.castShadow = false
o.receiveShadow = false
})
}

}

+ 40
- 0
src/three/widgets/BoxSelectionWidget.ts Просмотреть файл

@@ -0,0 +1,40 @@
import {LineMaterial2} from '../../core/material/LineMaterial2'
import {Vector2} from 'three'
import {LineSegmentsGeometry} from 'three/examples/jsm/lines/LineSegmentsGeometry.js'
import {LineSegments2} from 'three/examples/jsm/lines/LineSegments2.js'
import {Box3B} from '../math/Box3B'
import {SelectionWidget} from './SelectionWidget'

export class BoxSelectionWidget extends SelectionWidget {
constructor() {
super()
const matLine = new LineMaterial2({
color: '#ff2222' as any, transparent: true, opacity: 0.9,
linewidth: 5, // in pixels
resolution: new Vector2(1024, 1024), // to be set by renderer, eventually
dashed: false,
toneMapped: false,
})
this.lineMaterial = matLine

const ls = new LineSegmentsGeometry()
ls.setPositions([1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1].map(v => v - 0.5))

const wireframe = new LineSegments2(ls, matLine as any)
wireframe.computeLineDistances()
wireframe.scale.set(1, 1, 1)
wireframe.visible = true
this.add(wireframe)
}

protected _updater() {
super._updater()
const selected = this.object
if (selected) {
const bbox = new Box3B().expandByObject(selected, false)
// const scale = bbox.getBoundingSphere(new Sphere()).radius
bbox.getSize(this.scale).multiplyScalar(this.boundingScaleMultiplier).clampScalar(0.1, 100)
this.setVisible(true)
}
}
}

+ 311
- 0
src/three/widgets/CameraHelper2.ts Просмотреть файл

@@ -0,0 +1,311 @@
import {Camera, Color, Object3D, OrthographicCamera, PerspectiveCamera, Vector3} from 'three'
import {ACameraHelperWidget} from './ACameraHelperWidget'
import {LineSegments2} from 'three/examples/jsm/lines/LineSegments2.js'
import {LineSegmentsGeometry} from 'three/examples/jsm/lines/LineSegmentsGeometry.js'
import {InterleavedBufferAttribute} from 'three/src/core/InterleavedBufferAttribute'
import {LineMaterial2} from '../../core'

/**
* Fork of CameraHelper from three.js
* - shows frustum, line of sight and up of the camera
* - suitable for fast updates
* - based on frustum visualization in lightgl.js shadowmap example
* https://github.com/evanw/lightgl.js/blob/master/tests/shadowmap.html
*/
export class CameraHelper2 extends ACameraHelperWidget {
protected _vector = new Vector3()
// @ts-expect-error update three-ts-types remove abstract
protected _camera = new Camera()

line: LineSegments2
pointMap: Record<string, number[]>

constructor(camera: PerspectiveCamera|OrthographicCamera) {
super(camera)

const geometry = new LineSegmentsGeometry()
const material = new LineMaterial2({
color: 0xffffff,
linewidth: 0.005, // in world units with size attenuation, pixels otherwise
vertexColors: true,

dashed: false,
alphaToCoverage: true,

toneMapped: false,
transparent: true,
depthTest: false,
depthWrite: false,
})

const {vertices, colors, pointMap} = generateVertices()

geometry.setPositions(vertices)
geometry.setColors(colors)

// @ts-expect-error update three-ts-types
this.line = new LineSegments2(geometry, material)
this.line.frustumCulled = false
this.add(this.line)

this.pointMap = pointMap

this.update()

// colors

const colorFrustum = new Color(0xffaa00)
const colorCone = new Color(0xff0000)
const colorUp = new Color(0x00aaff)
const colorTarget = new Color(0xffffff)
const colorCross = new Color(0x333333)

this.setColors(colorFrustum, colorCone, colorUp, colorTarget, colorCross)

}

setColors(frustum: Color, cone: Color, up: Color, target: Color, cross: Color) {

const geometry = this.line.geometry

const colorAttribute = geometry.getAttribute('instanceColorStart')
const colorAttribute2 = geometry.getAttribute('instanceColorEnd')

function setXYZ(i: number, color: Color) {

colorAttribute.setXYZ(i / 2, color.r, color.g, color.b)
colorAttribute2.setXYZ(i / 2, color.r, color.g, color.b)

}
// near

setXYZ(0, frustum) // n1, n2
setXYZ(2, frustum) // n2, n4
setXYZ(4, frustum) // n4, n3
setXYZ(6, frustum) // n3, n1

// far

setXYZ(8, frustum) // f1, f2
setXYZ(10, frustum) // f2, f4
setXYZ(12, frustum) // f4, f3
setXYZ(14, frustum) // f3, f1

// sides

setXYZ(16, frustum) // n1, f1
setXYZ(18, frustum) // n2, f2
setXYZ(20, frustum) // n3, f3
setXYZ(22, frustum) // n4, f4

// cone

setXYZ(24, cone) // p, n1
setXYZ(26, cone) // p, n2
setXYZ(28, cone) // p, n3
setXYZ(30, cone) // p, n4

// up

setXYZ(32, up) // u1, u2
setXYZ(34, up) // u2, u3
setXYZ(36, up) // u3, u1

// target

setXYZ(38, target) // c, t
setXYZ(40, cross) // p, c

// cross

setXYZ(42, cross) // cn1, cn2
setXYZ(44, cross) // cn3, cn4

setXYZ(46, cross) // cf1, cf2
setXYZ(48, cross) // cf3, cf4

colorAttribute.needsUpdate = true
colorAttribute2.needsUpdate = true

}

update() {
if (!this.camera) return

const geometry = this.line.geometry
const pointMap = this.pointMap

const w = 1, h = 1

// we need just camera projection matrix inverse
// world matrix must be identity

// eslint-disable-next-line @typescript-eslint/naming-convention
const {_camera, _vector} = this

_camera.projectionMatrixInverse.copy(this.camera.projectionMatrixInverse)

// center / target

setPoint('c', pointMap, geometry, _camera, 0, 0, -1, _vector)
setPoint('t', pointMap, geometry, _camera, 0, 0, 1, _vector)

// near

setPoint('n1', pointMap, geometry, _camera, -w, -h, -1, _vector)
setPoint('n2', pointMap, geometry, _camera, w, -h, -1, _vector)
setPoint('n3', pointMap, geometry, _camera, -w, h, -1, _vector)
setPoint('n4', pointMap, geometry, _camera, w, h, -1, _vector)

// far

setPoint('f1', pointMap, geometry, _camera, -w, -h, 1, _vector)
setPoint('f2', pointMap, geometry, _camera, w, -h, 1, _vector)
setPoint('f3', pointMap, geometry, _camera, -w, h, 1, _vector)
setPoint('f4', pointMap, geometry, _camera, w, h, 1, _vector)

// up

setPoint('u1', pointMap, geometry, _camera, w * 0.7, h * 1.1, -1, _vector)
setPoint('u2', pointMap, geometry, _camera, -w * 0.7, h * 1.1, -1, _vector)
setPoint('u3', pointMap, geometry, _camera, 0, h * 2, -1, _vector)

// cross

setPoint('cf1', pointMap, geometry, _camera, -w, 0, 1, _vector)
setPoint('cf2', pointMap, geometry, _camera, w, 0, 1, _vector)
setPoint('cf3', pointMap, geometry, _camera, 0, -h, 1, _vector)
setPoint('cf4', pointMap, geometry, _camera, 0, h, 1, _vector)

setPoint('cn1', pointMap, geometry, _camera, -w, 0, -1, _vector)
setPoint('cn2', pointMap, geometry, _camera, w, 0, -1, _vector)
setPoint('cn3', pointMap, geometry, _camera, 0, -h, -1, _vector)
setPoint('cn4', pointMap, geometry, _camera, 0, h, -1, _vector)

geometry.getAttribute('instanceStart').needsUpdate = true
geometry.getAttribute('instanceEnd').needsUpdate = true

geometry.computeBoundingBox()
geometry.computeBoundingSphere()

super.update()
}

dispose() {

this.line.geometry.dispose()
this.line.material.dispose()

super.dispose()
}

static Check(camera: Object3D) {
return (camera as any).isCamera
}
static Create(camera: Object3D) {
return new CameraHelper2(camera as any)
}

}


function setPoint(point: string, pointMap: Record<string, number[]>, geometry: LineSegmentsGeometry, camera: Camera, x: number, y: number, z: number, _vector: Vector3) {

_vector.set(x, y, z).unproject(camera)

const points = pointMap[ point ]

if (points !== undefined) {

const position1 = geometry.getAttribute('instanceStart') as InterleavedBufferAttribute
const position2 = geometry.getAttribute('instanceEnd') as InterleavedBufferAttribute

for (let i = 0, l = points.length; i < l; i++) {

const j = Math.floor(points[ i ] / 2.)
;(points[ i ] % 2 === 0 ? position1 : position2).setXYZ(j, _vector.x, _vector.y, _vector.z)
// (i % 2 === 0 ? position1 : position2).setXYZ(points[ i ], _vector.x, _vector.y, _vector.z)

}

}

}

function generateVertices() {
const vertices: number[] = []
const colors: number[] = []

const pointMap: any = {}

// near

addLine('n1', 'n2')
addLine('n2', 'n4')
addLine('n4', 'n3')
addLine('n3', 'n1')

// far

addLine('f1', 'f2')
addLine('f2', 'f4')
addLine('f4', 'f3')
addLine('f3', 'f1')

// sides

addLine('n1', 'f1')
addLine('n2', 'f2')
addLine('n3', 'f3')
addLine('n4', 'f4')

// cone

addLine('p', 'n1')
addLine('p', 'n2')
addLine('p', 'n3')
addLine('p', 'n4')

// up

addLine('u1', 'u2')
addLine('u2', 'u3')
addLine('u3', 'u1')

// target

addLine('c', 't')
addLine('p', 'c')

// cross

addLine('cn1', 'cn2')
addLine('cn3', 'cn4')

addLine('cf1', 'cf2')
addLine('cf3', 'cf4')

function addLine(a: string, b: string) {

addPoint(a)
addPoint(b)

}

function addPoint(id: string) {

vertices.push(0, 0, 0)
colors.push(0, 0, 0)

if (pointMap[id] === undefined) {

pointMap[id] = []

}

pointMap[id].push(vertices.length / 3 - 1)

}

return {vertices, colors, pointMap}
}

+ 117
- 0
src/three/widgets/DirectionalLightHelper2.ts Просмотреть файл

@@ -0,0 +1,117 @@
import {ColorRepresentation, DirectionalLight, Object3D, Vector3} from 'three'
import {Line2} from 'three/examples/jsm/lines/Line2.js'
import {LineGeometry} from 'three/examples/jsm/lines/LineGeometry.js'
import {onChange} from 'ts-browser-helpers'
import {ALightHelperWidget} from './ALightHelperWidget'
import {IUiConfigContainer, uiSlider} from 'uiconfig.js'
import {LineMaterial2} from '../../core'

export class DirectionalLightHelper2 extends ALightHelperWidget {
color: ColorRepresentation|undefined
lightPlane: Line2
targetLine: Line2
light: (DirectionalLight&IUiConfigContainer)|undefined

@onChange(DirectionalLightHelper2.prototype.update)
material: LineMaterial2
@onChange(DirectionalLightHelper2.prototype.update)
@uiSlider(undefined, [0.1, 20], 0.01)
lineWidth = 5
@onChange(DirectionalLightHelper2.prototype.update)
@uiSlider(undefined, [0.01, 10], 0.01)
size = 0.5

constructor(light: DirectionalLight, size?: number, color?: ColorRepresentation) {
super(light)

this.color = color

if (size !== undefined) this.size = size

let geometry = new LineGeometry()

this.material = new LineMaterial2({
color: 0xff0000,
linewidth: 0.005, // in world units with size attenuation, pixels otherwise
vertexColors: false,

dashed: false,
alphaToCoverage: true,

toneMapped: false,
transparent: true,
depthTest: false,
depthWrite: false,
})

// @ts-expect-error update three-ts-types
this.lightPlane = new Line2(geometry, this.material)
this.add(this.lightPlane)

geometry = new LineGeometry()
geometry.setPositions([0, 0, 0, 0, 0, 1])

// @ts-expect-error update three-ts-types
this.targetLine = new Line2(geometry, this.material)
this.add(this.targetLine)

this.update()

this.traverse(o=>{
o.userData.__keepShadowDef = true
o.castShadow = false
o.receiveShadow = false
})
}

dispose() {

this.lightPlane.geometry.dispose()
this.lightPlane.material.dispose()
this.targetLine.geometry.dispose()
this.targetLine.material.dispose()

super.dispose()
}

private _v1 = new Vector3()
private _v2 = new Vector3()
private _v3 = new Vector3()

update() {

if (!this.light || !this.lightPlane) return
this._v1.setFromMatrixPosition(this.light.matrixWorld)
this._v2.setFromMatrixPosition(this.light.target.matrixWorld)
this._v3.subVectors(this._v2, this._v1)

this.lightPlane.geometry.setPositions([
-this.size, this.size, 0,
this.size, this.size, 0,
this.size, -this.size, 0,
-this.size, -this.size, 0,
-this.size, this.size, 0,
])
this.lightPlane.lookAt(this._v2)
// @ts-expect-error update three-ts-types
this.lightPlane.material = this.material
// @ts-expect-error update three-ts-types
this.targetLine.material = this.material
this.material.color.set(this.color ?? this.light.color)
this.material.linewidth = this.lineWidth * 0.001

this.targetLine.lookAt(this._v2)
this.targetLine.scale.z = this.light.intensity / 3.

super.update()
}

static Check(light: Object3D) {
return (light as DirectionalLight).isDirectionalLight
}
static Create(light: Object3D) {
return new DirectionalLightHelper2(light as DirectionalLight)
}

}


+ 87
- 0
src/three/widgets/PointLightHelper2.ts Просмотреть файл

@@ -0,0 +1,87 @@
import {ColorRepresentation, Object3D, PointLight, SphereGeometry} from 'three'
import {WireframeGeometry2} from 'three/examples/jsm/lines/WireframeGeometry2.js'
import {Wireframe} from 'three/examples/jsm/lines/Wireframe.js'
import {onChange} from 'ts-browser-helpers'
import {ALightHelperWidget} from './ALightHelperWidget'
import {IUiConfigContainer, uiSlider} from 'uiconfig.js'
import {LineMaterial2} from '../../core'

export class PointLightHelper2 extends ALightHelperWidget {
color: ColorRepresentation | undefined
lightSphere: Wireframe
light: (PointLight & IUiConfigContainer) | undefined
@onChange(PointLightHelper2.prototype.update)
material: LineMaterial2
@onChange(PointLightHelper2.prototype.update)
@uiSlider(undefined, [0.1, 20], 0.01)
lineWidth = 5
@onChange(PointLightHelper2.prototype.update)
@uiSlider(undefined, [0.01, 10], 0.01)
size = 0.5

constructor(light: PointLight, size?: number, color?: ColorRepresentation) {
super(light)

this.color = color

if (size !== undefined) this.size = size

const geometry = new WireframeGeometry2(new SphereGeometry(0.5, 4, 2))

this.material = new LineMaterial2({
color: 0xff0000,
linewidth: 0.005, // in world units with size attenuation, pixels otherwise
vertexColors: false,

dashed: false,
alphaToCoverage: true,

toneMapped: false,
transparent: true,
depthTest: false,
depthWrite: false,
})

// @ts-expect-error update three-ts-types
this.lightSphere = new Wireframe(geometry, this.material)
this.lightSphere.computeLineDistances()
this.add(this.lightSphere)

this.update()

this.traverse(o => {
o.userData.__keepShadowDef = true
o.castShadow = false
o.receiveShadow = false
})
}

dispose() {

this.lightSphere.geometry.dispose()
;(this.lightSphere.material as any).dispose()

super.dispose()
}

update() {

if (!this.light || !this.lightSphere) return

this.material.color.set(this.color ?? this.light.color)
this.material.linewidth = this.lineWidth * 0.001
this.lightSphere.scale.setScalar(this.size)

super.update()
}

static Check(light: Object3D) {
return (light as PointLight).isPointLight
}

static Create(light: Object3D) {
return new PointLightHelper2(light as PointLight)
}


}

src/three/utils/SelectionWidget.ts → src/three/widgets/SelectionWidget.ts Просмотреть файл

@@ -1,9 +1,7 @@
import {Group, Sphere, Vector2} from 'three'
import {Group, Sphere} from 'three'
import {AnyOptions} from 'ts-browser-helpers'
import {Box3B} from '../math/Box3B'
import {IMaterial, IObject3D, IWidget, LineMaterial2} from '../../core'
import {LineSegments2} from 'three/examples/jsm/lines/LineSegments2.js'
import {LineSegmentsGeometry} from 'three/examples/jsm/lines/LineSegmentsGeometry.js'
import {IMaterial, IObject3D, IWidget} from '../../core'

export class SelectionWidget extends Group implements IWidget {
isWidget = true as const
@@ -78,36 +76,3 @@ export class SelectionWidget extends Group implements IWidget {

}

export class BoxSelectionWidget extends SelectionWidget {
constructor() {
super()
const matLine = new LineMaterial2({
color: '#ff2222' as any, transparent: true, opacity: 0.9,
linewidth: 5, // in pixels
resolution: new Vector2(1024, 1024), // to be set by renderer, eventually
dashed: false,
toneMapped: false,
})
this.lineMaterial = matLine

const ls = new LineSegmentsGeometry()
ls.setPositions([1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1].map(v=>v - 0.5))

const wireframe = new LineSegments2(ls, matLine as any)
wireframe.computeLineDistances()
wireframe.scale.set(1, 1, 1)
wireframe.visible = true
this.add(wireframe)
}

protected _updater() {
super._updater()
const selected = this.object
if (selected) {
const bbox = new Box3B().expandByObject(selected, false)
// const scale = bbox.getBoundingSphere(new Sphere()).radius
bbox.getSize(this.scale).multiplyScalar(this.boundingScaleMultiplier).clampScalar(0.1, 100)
this.setVisible(true)
}
}
}

+ 119
- 0
src/three/widgets/SpotLightHelper2.ts Просмотреть файл

@@ -0,0 +1,119 @@
import {ColorRepresentation, Object3D, SpotLight, Vector3} from 'three'
import {LineGeometry} from 'three/examples/jsm/lines/LineGeometry.js'
import {LineSegments2} from 'three/examples/jsm/lines/LineSegments2.js'
import {LineSegmentsGeometry} from 'three/examples/jsm/lines/LineSegmentsGeometry.js'
import {onChange} from 'ts-browser-helpers'
import {ALightHelperWidget} from './ALightHelperWidget'
import {IUiConfigContainer, uiSlider} from 'uiconfig.js'
import {LineMaterial2} from '../../core'

export class SpotLightHelper2 extends ALightHelperWidget {
color: ColorRepresentation | undefined
cone: LineSegments2
light: (SpotLight & IUiConfigContainer) | undefined
@onChange(SpotLightHelper2.prototype.update)
material: LineMaterial2
@onChange(SpotLightHelper2.prototype.update)
@uiSlider(undefined, [0.1, 20], 0.01)
lineWidth = 5

constructor(light: SpotLight, size?: number, color?: ColorRepresentation) {
super(light)

this.color = color

if (size === undefined) size = 0.5

let geometry = new LineSegmentsGeometry()
const positions = [
0, 0, 0, 0, 0, 1,
0, 0, 0, 1, 0, 1,
0, 0, 0, -1, 0, 1,
0, 0, 0, 0, 1, 1,
0, 0, 0, 0, -1, 1,
]

for (let i = 0, j = 1, l = 32; i < l; i++, j++) {

const p1 = i / l * Math.PI * 2
const p2 = j / l * Math.PI * 2

positions.push(
Math.cos(p1), Math.sin(p1), 1,
Math.cos(p2), Math.sin(p2), 1
)

}
geometry.setPositions(positions)

this.material = new LineMaterial2({
color: 0xff0000,
linewidth: 0.005, // in world units with size attenuation, pixels otherwise
vertexColors: false,

dashed: false,
alphaToCoverage: true,

toneMapped: false,
transparent: true,
depthTest: false,
depthWrite: false,
})

// @ts-expect-error update three-ts-types
this.cone = new LineSegments2(geometry, this.material)
this.add(this.cone)

geometry = new LineGeometry()
geometry.setPositions([0, 0, 0, 0, 0, 1])

this.update()

this.traverse(o => {
o.userData.__keepShadowDef = true
o.castShadow = false
o.receiveShadow = false
})
}

dispose() {

this.cone.geometry.dispose()
this.cone.material.dispose()

super.dispose()
}

private _v1 = new Vector3()

update() {

if (!this.light || !this.cone) return

this.light.updateWorldMatrix(true, false)
this.light.target.updateWorldMatrix(true, false)

const coneLength = this.light.distance ? this.light.distance : 1000
const coneWidth = coneLength * Math.tan(this.light.angle)

this.cone.scale.set(coneWidth, coneWidth, coneLength)

this._v1.setFromMatrixPosition(this.light.target.matrixWorld)

this.cone.lookAt(this._v1)

this.material.color.set(this.color ?? this.light.color)
this.material.linewidth = this.lineWidth * 0.001

super.update()
}

static Check(light: Object3D) {
return (light as SpotLight).isSpotLight
}

static Create(light: Object3D) {
return new SpotLightHelper2(light as SpotLight)
}

}

+ 9
- 0
src/three/widgets/index.ts Просмотреть файл

@@ -0,0 +1,9 @@
export {SelectionWidget} from './SelectionWidget'
export {ACameraHelperWidget} from './ACameraHelperWidget'
export {AHelperWidget} from './AHelperWidget'
export {ALightHelperWidget} from './ALightHelperWidget'
export {BoxSelectionWidget} from './BoxSelectionWidget'
export {CameraHelper2} from './CameraHelper2'
export {DirectionalLightHelper2} from './DirectionalLightHelper2'
export {PointLightHelper2} from './PointLightHelper2'
export {SpotLightHelper2} from './SpotLightHelper2'

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