-
Notifications
You must be signed in to change notification settings - Fork 409
Expand file tree
/
Copy pathEllipsoidRegionHelper.js
More file actions
243 lines (159 loc) · 6.21 KB
/
Copy pathEllipsoidRegionHelper.js
File metadata and controls
243 lines (159 loc) · 6.21 KB
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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
import { Mesh, Vector3, MathUtils, BoxGeometry, BufferGeometry, LineSegments, BufferAttribute } from 'three';
import { EllipsoidRegion } from '3d-tiles-renderer/three';
const _norm = /* @__PURE__ */ new Vector3();
const _norm2 = /* @__PURE__ */ new Vector3();
const _pos = /* @__PURE__ */ new Vector3();
const _vec1 = /* @__PURE__ */ new Vector3();
const _vec2 = /* @__PURE__ */ new Vector3();
const _zAxis = /* @__PURE__ */ new Vector3( 0, 0, 1 );
// Converts a geometry with a given set of groups rendering a smaller set of
// geometry into a new one with only the relevant triangles.
function toGroupGeometry( geometry ) {
// non indexed makes this process easier
geometry = geometry.toNonIndexed();
// prep the arrays
const { groups } = geometry;
const { position, normal } = geometry.attributes;
const newNorm = [];
const newPos = [];
// add the normals and the positions
for ( const group of groups ) {
const { start, count } = group;
for ( let i = start, l = ( start + count ); i < l; i ++ ) {
_vec1.fromBufferAttribute( position, i );
_vec2.fromBufferAttribute( normal, i );
newPos.push( ..._vec1 );
newNorm.push( ..._vec2 );
}
}
// set the new geometry
const newGeometry = new BufferGeometry();
newGeometry.setAttribute( 'position', new BufferAttribute( new Float32Array( newPos ), 3 ) );
newGeometry.setAttribute( 'normal', new BufferAttribute( new Float32Array( newNorm ), 3 ) );
return newGeometry;
}
function getRegionGeometry( ellipsoidRegion, segments = 32 ) {
// retrieve the relevant fields
const {
latStart = - Math.PI / 2, latEnd = Math.PI / 2,
lonStart = 0, lonEnd = 2 * Math.PI,
heightStart = 0, heightEnd = 0,
} = ellipsoidRegion;
// get the attributes
const geometry = new BoxGeometry( 1, 1, 1, segments, segments );
const { normal, position } = geometry.attributes;
// The box positions map linearly onto the region, and the box's axis-aligned face normals identify
// which face each vertex is on
for ( let i = 0, l = position.count; i < l; i ++ ) {
// the box position maps to a cartographic coordinate; the z sign selects the inner / outer shell
_pos.fromBufferAttribute( position, i );
const lat = MathUtils.mapLinear( _pos.x, - 0.5, 0.5, latStart, latEnd );
const lon = MathUtils.mapLinear( _pos.y, - 0.5, 0.5, lonStart, lonEnd );
const outer = _pos.z < 0;
// the box face normal tells us whether this vertex is on a cap or a side wall
_norm.fromBufferAttribute( normal, i );
// perturb onto the region surface
ellipsoidRegion.getCartographicToPosition( lat, lon, outer ? heightEnd : heightStart, _pos );
position.setXYZ( i, _pos.x, _pos.y, _pos.z );
// analytic surface normal
ellipsoidRegion.getCartographicToNormal( lat, lon, _norm2 );
if ( _norm.z !== 0 ) {
// caps
_norm2.multiplyScalar( outer ? 1 : - 1 );
} else {
// side walls
_vec1.crossVectors( _zAxis, _norm2 );
if ( _vec1.lengthSq() < 1e-12 ) {
_vec1.set( 1, 0, 0 );
}
_vec1.normalize();
if ( _norm.x !== 0 ) {
// constant-latitude wall faces north / south
_norm2.crossVectors( _norm2, _vec1 ).normalize().multiplyScalar( Math.sign( _norm.x ) );
} else {
// constant-longitude wall faces east / west
_norm2.copy( _vec1 ).multiplyScalar( Math.sign( _norm.y ) );
}
}
normal.setXYZ( i, _norm2.x, _norm2.y, _norm2.z );
}
return geometry;
}
// Builds the 12 curved edges of a region's bounding box directly as line segments. This avoids
// tessellating a dense solid box and running "EdgesGeometry" over it per tile, which is expensive.
function getRegionLineGeometry( region, segments = 32 ) {
const {
latStart = - Math.PI / 2, latEnd = Math.PI / 2,
lonStart = 0, lonEnd = 2 * Math.PI,
heightStart = 0, heightEnd = 0,
} = region;
const positions = [];
// sample a curved edge between two cartographic endpoints as connected line segments
const addEdge = ( lat0, lon0, h0, lat1, lon1, h1 ) => {
for ( let i = 0; i < segments; i ++ ) {
const t0 = i / segments;
const t1 = ( i + 1 ) / segments;
region.getCartographicToPosition( MathUtils.lerp( lat0, lat1, t0 ), MathUtils.lerp( lon0, lon1, t0 ), MathUtils.lerp( h0, h1, t0 ), _vec1 );
region.getCartographicToPosition( MathUtils.lerp( lat0, lat1, t1 ), MathUtils.lerp( lon0, lon1, t1 ), MathUtils.lerp( h0, h1, t1 ), _vec2 );
positions.push( _vec1.x, _vec1.y, _vec1.z, _vec2.x, _vec2.y, _vec2.z );
}
};
// top and bottom rings at each height extreme
for ( const h of [ heightStart, heightEnd ] ) {
addEdge( latStart, lonStart, h, latStart, lonEnd, h );
addEdge( latEnd, lonStart, h, latEnd, lonEnd, h );
addEdge( latStart, lonStart, h, latEnd, lonStart, h );
addEdge( latStart, lonEnd, h, latEnd, lonEnd, h );
}
// vertical edges connecting the four corners between the two heights
for ( const lat of [ latStart, latEnd ] ) {
for ( const lon of [ lonStart, lonEnd ] ) {
addEdge( lat, lon, heightStart, lat, lon, heightEnd );
}
}
const geometry = new BufferGeometry();
geometry.setAttribute( 'position', new BufferAttribute( new Float32Array( positions ), 3 ) );
return geometry;
}
export class EllipsoidRegionLineHelper extends LineSegments {
constructor( ellipsoidRegion = new EllipsoidRegion(), color = 0xffff00 ) {
super();
this.ellipsoidRegion = ellipsoidRegion;
this.material.color.set( color );
this.update();
}
update() {
this.geometry.dispose();
this.geometry = getRegionLineGeometry( this.ellipsoidRegion );
}
dispose() {
this.geometry.dispose();
this.material.dispose();
}
}
export class EllipsoidRegionHelper extends Mesh {
constructor( ellipsoidRegion = new EllipsoidRegion(), color = 0xffff00 ) {
super();
this.ellipsoidRegion = ellipsoidRegion;
this.material.color.set( color );
this.update();
}
update() {
// dispose of the existing geometry
this.geometry.dispose();
// retrieve the relevant fields
const geometry = getRegionGeometry( this.ellipsoidRegion );
const { lonStart, lonEnd } = this;
// exclude the side tris if the region wraps around
if ( lonEnd - lonStart >= 2 * Math.PI ) {
geometry.groups.splice( 2, 2 );
this.geometry = toGroupGeometry( geometry );
} else {
this.geometry = geometry;
}
}
dispose() {
this.geometry.dispose();
this.material.dispose();
}
}