-
Notifications
You must be signed in to change notification settings - Fork 105
/
LODRaycast.ts
97 lines (83 loc) · 2.5 KB
/
LODRaycast.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
import {LODControl} from './LODControl';
import {Camera, Object3D, Raycaster, Vector2, Vector3, WebGLRenderer} from 'three';
import {MapView} from '../MapView';
/**
* Use random raycasting to randomly pick n objects to be tested on screen space.
*
* Overall the fastest solution but does not include out of screen objects.
*/
export class LODRaycast implements LODControl
{
/**
* Number of rays used to test nodes and subdivide the map.
*
* N rays are cast each frame dependeing on this value to check distance to the visible map nodes. A single ray should be enough for must scenarios.
*/
public subdivisionRays: number = 1;
/**
* Threshold to subdivide the map tiles.
*
* Lower value will subdivide earlier (less zoom required to subdivide).
*/
public thresholdUp: number = 0.6;
/**
* Threshold to simplify the map tiles.
*
* Higher value will simplify earlier.
*/
public thresholdDown: number = 0.15;
/**
* Raycaster object used to cast rays into the world and check for hits.
*/
public raycaster: Raycaster = new Raycaster();
/**
* Normalized mouse coordinates.
*/
public mouse: Vector2 = new Vector2();
/**
* Consider the distance powered to level of the node.
*/
public powerDistance: boolean = false;
/**
* Consider the scale of the node when calculating the distance.
*
* If distance is not considered threshold values should be absolute distances.
*/
public scaleDistance: boolean = true;
public updateLOD(view: MapView, camera: Camera, renderer: WebGLRenderer, scene: Object3D): void
{
const intersects = [];
for (let t = 0; t < this.subdivisionRays; t++)
{
// Generate random point in viewport
this.mouse.set(Math.random() * 2 - 1, Math.random() * 2 - 1);
// Check intersection
this.raycaster.setFromCamera(this.mouse, camera);
this.raycaster.intersectObjects(view.children, true, intersects);
}
for (let i = 0; i < intersects.length; i++)
{
const node = intersects[i].object;
let distance = intersects[i].distance;
if (this.powerDistance)
{
distance = Math.pow(distance * 2, node.level);
}
if (this.scaleDistance)
{
// Get scale from transformation matrix directly
const matrix = node.matrixWorld.elements;
const vector = new Vector3(matrix[0], matrix[1], matrix[2]);
distance = vector.length() / distance;
}
if (distance > this.thresholdUp)
{
node.subdivide();
}
else if (distance < this.thresholdDown && node.parentNode)
{
node.parentNode.simplify();
}
}
}
}