|
|
@@ -1,40 +1,79 @@
|
|
|
-import { Mesh } from '../objects/Mesh.js';
|
|
|
-import { MeshBasicMaterial } from '../materials/MeshBasicMaterial.js';
|
|
|
-import { SphereGeometry } from '../geometries/SphereGeometry.js';
|
|
|
+import { LineSegments } from '../objects/LineSegments.js';
|
|
|
+import { LineBasicMaterial } from '../materials/LineBasicMaterial.js';
|
|
|
+import { Float32BufferAttribute } from '../core/BufferAttribute.js';
|
|
|
+import { BufferGeometry } from '../core/BufferGeometry.js';
|
|
|
+import { Vector3 } from '../math/Vector3.js';
|
|
|
+
|
|
|
+const _cameraWorldPosition = /*@__PURE__*/ new Vector3();
|
|
|
+
|
|
|
+function createCirclesGeometry() {
|
|
|
+
|
|
|
+ const geometry = new BufferGeometry();
|
|
|
+ const positions = [];
|
|
|
+ const segments = 32;
|
|
|
+
|
|
|
+ for ( let i = 0, j = 1; i < segments; i ++, j ++ ) {
|
|
|
+
|
|
|
+ const p1 = ( i / segments ) * Math.PI * 2;
|
|
|
+ const p2 = ( j / segments ) * Math.PI * 2;
|
|
|
+
|
|
|
+ // XY plane circle
|
|
|
+ positions.push( Math.cos( p1 ), Math.sin( p1 ), 0 );
|
|
|
+ positions.push( Math.cos( p2 ), Math.sin( p2 ), 0 );
|
|
|
+
|
|
|
+ // XZ plane circle
|
|
|
+ positions.push( Math.cos( p1 ), 0, Math.sin( p1 ) );
|
|
|
+ positions.push( Math.cos( p2 ), 0, Math.sin( p2 ) );
|
|
|
+
|
|
|
+ // YZ plane circle
|
|
|
+ positions.push( 0, Math.cos( p1 ), Math.sin( p1 ) );
|
|
|
+ positions.push( 0, Math.cos( p2 ), Math.sin( p2 ) );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ geometry.setAttribute( 'position', new Float32BufferAttribute( positions, 3 ) );
|
|
|
+
|
|
|
+ return geometry;
|
|
|
+
|
|
|
+}
|
|
|
|
|
|
/**
|
|
|
- * This displays a helper object consisting of a spherical mesh for
|
|
|
- * visualizing an instance of {@link PointLight}.
|
|
|
+ * This displays a helper object consisting of three orthogonal circles
|
|
|
+ * for visualizing an instance of {@link PointLight}.
|
|
|
+ * The circles maintain constant screen-space size regardless of distance.
|
|
|
*
|
|
|
* ```js
|
|
|
- * const pointLight = new THREE.PointLight( 0xff0000, 1, 100 );
|
|
|
+ * const pointLight = new THREE.PointLight( 0xff0000, 1 );
|
|
|
* pointLight.position.set( 10, 10, 10 );
|
|
|
* scene.add( pointLight );
|
|
|
*
|
|
|
- * const sphereSize = 1;
|
|
|
- * const pointLightHelper = new THREE.PointLightHelper( pointLight, sphereSize );
|
|
|
+ * const pointLightHelper = new THREE.PointLightHelper( pointLight );
|
|
|
* scene.add( pointLightHelper );
|
|
|
* ```
|
|
|
*
|
|
|
- * @augments Mesh
|
|
|
+ * @augments LineSegments
|
|
|
*/
|
|
|
-class PointLightHelper extends Mesh {
|
|
|
+class PointLightHelper extends LineSegments {
|
|
|
|
|
|
/**
|
|
|
* Constructs a new point light helper.
|
|
|
*
|
|
|
* @param {PointLight} light - The light to be visualized.
|
|
|
- * @param {number} [sphereSize=1] - The size of the sphere helper.
|
|
|
- * @param {number|Color|string} [color] - The helper's color. If not set, the helper will take
|
|
|
- * the color of the light.
|
|
|
+ * @param {number} [size=1] - The size of the helper.
|
|
|
*/
|
|
|
- constructor( light, sphereSize, color ) {
|
|
|
+ constructor( light, size = 1 ) {
|
|
|
|
|
|
- const geometry = new SphereGeometry( sphereSize, 4, 2 );
|
|
|
- const material = new MeshBasicMaterial( { wireframe: true, fog: false, toneMapped: false } );
|
|
|
+ const geometry = createCirclesGeometry();
|
|
|
+ const material = new LineBasicMaterial( { fog: false, toneMapped: false } );
|
|
|
|
|
|
super( geometry, material );
|
|
|
|
|
|
+ // Range/distance circles
|
|
|
+ const rangeGeometry = createCirclesGeometry();
|
|
|
+ const rangeMaterial = new LineBasicMaterial( { fog: false, toneMapped: false, opacity: 0.5, transparent: true } );
|
|
|
+ this.range = new LineSegments( rangeGeometry, rangeMaterial );
|
|
|
+ this.add( this.range );
|
|
|
+
|
|
|
/**
|
|
|
* The light being visualized.
|
|
|
*
|
|
|
@@ -43,43 +82,29 @@ class PointLightHelper extends Mesh {
|
|
|
this.light = light;
|
|
|
|
|
|
/**
|
|
|
- * The color parameter passed in the constructor.
|
|
|
- * If not set, the helper will take the color of the light.
|
|
|
+ * The size of the helper.
|
|
|
*
|
|
|
- * @type {number|Color|string}
|
|
|
+ * @type {number}
|
|
|
*/
|
|
|
- this.color = color;
|
|
|
-
|
|
|
- this.type = 'PointLightHelper';
|
|
|
-
|
|
|
- this.matrix = this.light.matrixWorld;
|
|
|
- this.matrixAutoUpdate = false;
|
|
|
-
|
|
|
- this.update();
|
|
|
-
|
|
|
-
|
|
|
- /*
|
|
|
- // TODO: delete this comment?
|
|
|
- const distanceGeometry = new THREE.IcosahedronGeometry( 1, 2 );
|
|
|
- const distanceMaterial = new THREE.MeshBasicMaterial( { color: hexColor, fog: false, wireframe: true, opacity: 0.1, transparent: true } );
|
|
|
-
|
|
|
- this.lightSphere = new THREE.Mesh( bulbGeometry, bulbMaterial );
|
|
|
- this.lightDistance = new THREE.Mesh( distanceGeometry, distanceMaterial );
|
|
|
-
|
|
|
- const d = light.distance;
|
|
|
-
|
|
|
- if ( d === 0.0 ) {
|
|
|
+ this.size = size;
|
|
|
|
|
|
- this.lightDistance.visible = false;
|
|
|
+ /** @private */
|
|
|
+ this._showRange = false;
|
|
|
|
|
|
- } else {
|
|
|
+ this.range.visible = false;
|
|
|
|
|
|
- this.lightDistance.scale.set( d, d, d );
|
|
|
+ /**
|
|
|
+ * This flag can be used for type testing.
|
|
|
+ *
|
|
|
+ * @type {boolean}
|
|
|
+ * @readonly
|
|
|
+ * @default true
|
|
|
+ */
|
|
|
+ this.isPointLightHelper = true;
|
|
|
|
|
|
- }
|
|
|
+ this.type = 'PointLightHelper';
|
|
|
|
|
|
- this.add( this.lightDistance );
|
|
|
- */
|
|
|
+ this.matrixAutoUpdate = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
@@ -92,40 +117,73 @@ class PointLightHelper extends Mesh {
|
|
|
this.geometry.dispose();
|
|
|
this.material.dispose();
|
|
|
|
|
|
+ this.range.geometry.dispose();
|
|
|
+ this.range.material.dispose();
|
|
|
+
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Updates the helper to match the position of the
|
|
|
- * light being visualized.
|
|
|
+ * Whether to show the range circles indicating the light's distance.
|
|
|
+ *
|
|
|
+ * @type {boolean}
|
|
|
+ * @default false
|
|
|
+ */
|
|
|
+ get showRange() {
|
|
|
+
|
|
|
+ return this._showRange;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ set showRange( value ) {
|
|
|
+
|
|
|
+ this._showRange = value;
|
|
|
+ this.range.visible = value && this.light.distance > 0;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Updates the helper.
|
|
|
*/
|
|
|
update() {
|
|
|
|
|
|
+ }
|
|
|
+
|
|
|
+ onBeforeRender( renderer, scene, camera ) {
|
|
|
+
|
|
|
this.light.updateWorldMatrix( true, false );
|
|
|
+ this.position.setFromMatrixPosition( this.light.matrixWorld );
|
|
|
+
|
|
|
+ // Calculate scale for constant screen-space size
|
|
|
|
|
|
- if ( this.color !== undefined ) {
|
|
|
+ _cameraWorldPosition.setFromMatrixPosition( camera.matrixWorld );
|
|
|
+ const distance = this.position.distanceTo( _cameraWorldPosition );
|
|
|
|
|
|
- this.material.color.set( this.color );
|
|
|
+ if ( camera.isPerspectiveCamera ) {
|
|
|
+
|
|
|
+ const fov = camera.fov * ( Math.PI / 180 );
|
|
|
+ const scale = distance * Math.tan( fov / 2 ) * this.size * 0.04;
|
|
|
+ this.scale.setScalar( scale );
|
|
|
|
|
|
} else {
|
|
|
|
|
|
- this.material.color.copy( this.light.color );
|
|
|
+ const scale = this.size * 2 / camera.zoom;
|
|
|
+ this.scale.setScalar( scale );
|
|
|
|
|
|
}
|
|
|
|
|
|
- /*
|
|
|
- const d = this.light.distance;
|
|
|
-
|
|
|
- if ( d === 0.0 ) {
|
|
|
+ this.updateMatrix();
|
|
|
+ this.matrixWorld.copy( this.matrix );
|
|
|
|
|
|
- this.lightDistance.visible = false;
|
|
|
+ // Update range circles based on light distance
|
|
|
|
|
|
- } else {
|
|
|
+ if ( this.range.visible ) {
|
|
|
|
|
|
- this.lightDistance.visible = true;
|
|
|
- this.lightDistance.scale.set( d, d, d );
|
|
|
+ const lightDistance = this.light.distance;
|
|
|
+ this.range.scale.setScalar( lightDistance / this.scale.x );
|
|
|
+ this.range.updateMatrix();
|
|
|
+ this.range.matrixWorld.multiplyMatrices( this.matrixWorld, this.range.matrix );
|
|
|
|
|
|
}
|
|
|
- */
|
|
|
|
|
|
}
|
|
|
|