You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1689 lines
52 KiB
1689 lines
52 KiB
|
|
const EPSILON14 = 0.00000000000001; |
|
const PI_OVER_TWO = Math.PI / 2.0; |
|
const SHIFT_LEFT_12 = Math.pow(2.0, 12.0); |
|
const TerrainQuantization = { |
|
NONE: 0, |
|
BITS12: 1 |
|
} |
|
function defined(target) { |
|
return target !== undefined && target !== null; |
|
} |
|
|
|
function defaultValue(value, defaultValue) { |
|
if (!defined(value)) return defaultValue; |
|
return value; |
|
} |
|
|
|
function lerp(p, q, time) { |
|
return (1.0 - time) * p + time * q; |
|
}; |
|
function clamp(value, min, max) { |
|
return value < min ? min : value > max ? max : value; |
|
}; |
|
|
|
function equalsEpsilon(a, b, epsilon) { |
|
return Math.abs(a - b) <= epsilon; |
|
} |
|
|
|
function sign(a) { |
|
if (a == 0) return 0; |
|
return Math.abs(a) === a ? 1.0 : -1.0; |
|
} |
|
class Vector3 { |
|
constructor (x,y,z) { |
|
this.x = x || 0; |
|
this.y = y || 0; |
|
this.z = z || 0; |
|
} |
|
|
|
static minimumByComponent(v1, v2, result) { |
|
if (!defined(result)) result = new Vector3(); |
|
result.set( |
|
Math.min(v1.x, v2.x), |
|
Math.min(v1.y, v2.y), |
|
Math.min(v1.z, v2.z) |
|
) |
|
return result; |
|
} |
|
|
|
static maximumByComponent(v1, v2, result) { |
|
if (!defined(result)) result = new Vector3(); |
|
result.set( |
|
Math.max(v1.x, v2.x), |
|
Math.max(v1.y, v2.y), |
|
Math.max(v1.z, v2.z) |
|
); |
|
return result; |
|
} |
|
|
|
static maximumComponent(v) { |
|
return Math.max(v.x, v.y, v.z); |
|
} |
|
|
|
static equals(v1, v2) { |
|
return (v1.x == v2.x && |
|
v1.y == v2.x && |
|
v1.z == v2.z); |
|
} |
|
|
|
static equalsEpsilon(v1, v2, epsilon) { |
|
return (Math.abs(v1.x - v2.x) <= epsilon && |
|
Math.abs(v1.y - v2.y) <= epsilon && |
|
Math.abs(v1.z - v2.z) <= epsilon |
|
); |
|
} |
|
|
|
static unpack(array, startIndex, v) { |
|
v.x = array[startIndex]; |
|
v.y = array[startIndex + 1]; |
|
v.z = array[startIndex + 2]; |
|
} |
|
|
|
static multiplyByScalar(v, scalar, result) { |
|
if (!defined(result)) result = new Vector3(); |
|
result.x = v.x * scalar; |
|
result.y = v.y * scalar; |
|
result.z = v.z * scalar; |
|
|
|
return result; |
|
} |
|
|
|
static multiplyComponents(left, right, result) { |
|
if (!defined(result)) result = new Vector3(); |
|
result.x = left.x * right.x; |
|
result.y = left.y * right.y; |
|
result.z = left.z * right.z; |
|
return result; |
|
} |
|
|
|
static divideByScalar(v, scalar, result) { |
|
if (!defined(result)) result = new Vector3(); |
|
result.x = v.x / scalar; |
|
result.y = v.y / scalar; |
|
result.z = v.z / scalar; |
|
return result; |
|
} |
|
|
|
static subtract(left, right, result) { |
|
if (!defined(result)) { |
|
result = new Vector3(); |
|
} |
|
|
|
result.set( |
|
left.x - right.x, |
|
left.y - right.y, |
|
left.z - right.z |
|
); |
|
return result; |
|
} |
|
|
|
static negate(v, result) { |
|
if (!defined(result)) { |
|
result = new Vector3(); |
|
} |
|
|
|
result.set( |
|
-v.x, -v.y, -v.z |
|
); |
|
return result; |
|
} |
|
|
|
static dot(left, right) { |
|
return left.x * right.x + left.y * right.y + left.z * right.z; |
|
} |
|
|
|
static cross(left, right, result) { |
|
if (!defined(result)) result = new Vector3(); |
|
const leftX = left.x; |
|
const leftY = left.y; |
|
const leftZ = left.z; |
|
const rightX = right.x; |
|
const rightY = right.y; |
|
const rightZ = right.z; |
|
|
|
const x = leftY * rightZ - leftZ * rightY; |
|
const y = leftZ * rightX - leftX * rightZ; |
|
const z = leftX * rightY - leftY * rightX; |
|
|
|
result.x = x; |
|
result.y = y; |
|
result.z = z; |
|
return result; |
|
} |
|
|
|
clone() { |
|
return new Vector3(this.x, this.y, this.z); |
|
} |
|
|
|
static clone(source) { |
|
return new Vector3(source.x, source.y, source.z); |
|
} |
|
|
|
copy(source) { |
|
this.x = source.x; |
|
this.y = source.y; |
|
this.z = source.z; |
|
|
|
return this; |
|
} |
|
|
|
length() { |
|
return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z); |
|
} |
|
|
|
lengthSquare() { |
|
return this.x * this.x + this.y * this.y + this.z * this.z; |
|
} |
|
|
|
normalize() { |
|
const length = this.length() || 1; |
|
return Vector3.divideByScalar(this, length, this); |
|
} |
|
|
|
set(x, y, z) { |
|
this.x = x; |
|
this.y = y; |
|
this.z = z; |
|
} |
|
|
|
static add(left, right, result) { |
|
if (!defined(result)) result = new Vector3(); |
|
result.set( |
|
left.x + right.x, |
|
left.y + right.y, |
|
left.z + right.z |
|
); |
|
return result; |
|
} |
|
|
|
static normalize(v, result) { |
|
if (!defined(result)) result = new Vector3(); |
|
const length = v.length() || 1.0; |
|
return Vector3.divideByScalar(v, length, result); |
|
|
|
} |
|
|
|
static ZERO = Object.freeze(new Vector3(0, 0, 0)) |
|
} |
|
|
|
class Vector2 { |
|
constructor(x, y) { |
|
this.x = x || 0; |
|
this.y = y || 0; |
|
} |
|
|
|
set(x, y) { |
|
this.x = x; |
|
this.y = y; |
|
} |
|
} |
|
|
|
class Matrix4 { |
|
constructor( |
|
column0Row0, |
|
column1Row0, |
|
column2Row0, |
|
column3Row0, |
|
column0Row1, |
|
column1Row1, |
|
column2Row1, |
|
column3Row1, |
|
column0Row2, |
|
column1Row2, |
|
column2Row2, |
|
column3Row2, |
|
column0Row3, |
|
column1Row3, |
|
column2Row3, |
|
column3Row3 |
|
) { |
|
this[0] = defaultValue(column0Row0, 0.0); |
|
this[1] = defaultValue(column0Row1, 0.0); |
|
this[2] = defaultValue(column0Row2, 0.0); |
|
this[3] = defaultValue(column0Row3, 0.0); |
|
this[4] = defaultValue(column1Row0, 0.0); |
|
this[5] = defaultValue(column1Row1, 0.0); |
|
this[6] = defaultValue(column1Row2, 0.0); |
|
this[7] = defaultValue(column1Row3, 0.0); |
|
this[8] = defaultValue(column2Row0, 0.0); |
|
this[9] = defaultValue(column2Row1, 0.0); |
|
this[10] = defaultValue(column2Row2, 0.0); |
|
this[11] = defaultValue(column2Row3, 0.0); |
|
this[12] = defaultValue(column3Row0, 0.0); |
|
this[13] = defaultValue(column3Row1, 0.0); |
|
this[14] = defaultValue(column3Row2, 0.0); |
|
this[15] = defaultValue(column3Row3, 0.0); |
|
} |
|
static multiplyByPoint(matrix, cartesian, result) { |
|
if (!defined(result)) result = new Vector3(); |
|
const vX = cartesian.x; |
|
const vY = cartesian.y; |
|
const vZ = cartesian.z; |
|
|
|
const x = matrix[0] * vX + matrix[4] * vY + matrix[8] * vZ + matrix[12]; |
|
const y = matrix[1] * vX + matrix[5] * vY + matrix[9] * vZ + matrix[13]; |
|
const z = matrix[2] * vX + matrix[6] * vY + matrix[10] * vZ + matrix[14]; |
|
|
|
result.x = x; |
|
result.y = y; |
|
result.z = z; |
|
return result; |
|
} |
|
static inverseTransformation(matrix, result) { |
|
if (!defined(result)) result = new Matrix4(); |
|
const matrix0 = matrix[0]; |
|
const matrix1 = matrix[1]; |
|
const matrix2 = matrix[2]; |
|
const matrix4 = matrix[4]; |
|
const matrix5 = matrix[5]; |
|
const matrix6 = matrix[6]; |
|
const matrix8 = matrix[8]; |
|
const matrix9 = matrix[9]; |
|
const matrix10 = matrix[10]; |
|
|
|
const vX = matrix[12]; |
|
const vY = matrix[13]; |
|
const vZ = matrix[14]; |
|
|
|
const x = -matrix0 * vX - matrix1 * vY - matrix2 * vZ; |
|
const y = -matrix4 * vX - matrix5 * vY - matrix6 * vZ; |
|
const z = -matrix8 * vX - matrix9 * vY - matrix10 * vZ; |
|
|
|
result[0] = matrix0; |
|
result[1] = matrix4; |
|
result[2] = matrix8; |
|
result[3] = 0.0; |
|
result[4] = matrix1; |
|
result[5] = matrix5; |
|
result[6] = matrix9; |
|
result[7] = 0.0; |
|
result[8] = matrix2; |
|
result[9] = matrix6; |
|
result[10] = matrix10; |
|
result[11] = 0.0; |
|
result[12] = x; |
|
result[13] = y; |
|
result[14] = z; |
|
result[15] = 1.0; |
|
return result; |
|
} |
|
|
|
static multiply(left, right, result) { |
|
if (!defined(result)) { |
|
result = new Matrix4(); |
|
} |
|
|
|
const left0 = left[0]; |
|
const left1 = left[1]; |
|
const left2 = left[2]; |
|
const left3 = left[3]; |
|
const left4 = left[4]; |
|
const left5 = left[5]; |
|
const left6 = left[6]; |
|
const left7 = left[7]; |
|
const left8 = left[8]; |
|
const left9 = left[9]; |
|
const left10 = left[10]; |
|
const left11 = left[11]; |
|
const left12 = left[12]; |
|
const left13 = left[13]; |
|
const left14 = left[14]; |
|
const left15 = left[15]; |
|
|
|
const right0 = right[0]; |
|
const right1 = right[1]; |
|
const right2 = right[2]; |
|
const right3 = right[3]; |
|
const right4 = right[4]; |
|
const right5 = right[5]; |
|
const right6 = right[6]; |
|
const right7 = right[7]; |
|
const right8 = right[8]; |
|
const right9 = right[9]; |
|
const right10 = right[10]; |
|
const right11 = right[11]; |
|
const right12 = right[12]; |
|
const right13 = right[13]; |
|
const right14 = right[14]; |
|
const right15 = right[15]; |
|
|
|
const column0Row0 = |
|
left0 * right0 + left4 * right1 + left8 * right2 + left12 * right3; |
|
const column0Row1 = |
|
left1 * right0 + left5 * right1 + left9 * right2 + left13 * right3; |
|
const column0Row2 = |
|
left2 * right0 + left6 * right1 + left10 * right2 + left14 * right3; |
|
const column0Row3 = |
|
left3 * right0 + left7 * right1 + left11 * right2 + left15 * right3; |
|
|
|
const column1Row0 = |
|
left0 * right4 + left4 * right5 + left8 * right6 + left12 * right7; |
|
const column1Row1 = |
|
left1 * right4 + left5 * right5 + left9 * right6 + left13 * right7; |
|
const column1Row2 = |
|
left2 * right4 + left6 * right5 + left10 * right6 + left14 * right7; |
|
const column1Row3 = |
|
left3 * right4 + left7 * right5 + left11 * right6 + left15 * right7; |
|
|
|
const column2Row0 = |
|
left0 * right8 + left4 * right9 + left8 * right10 + left12 * right11; |
|
const column2Row1 = |
|
left1 * right8 + left5 * right9 + left9 * right10 + left13 * right11; |
|
const column2Row2 = |
|
left2 * right8 + left6 * right9 + left10 * right10 + left14 * right11; |
|
const column2Row3 = |
|
left3 * right8 + left7 * right9 + left11 * right10 + left15 * right11; |
|
|
|
const column3Row0 = |
|
left0 * right12 + left4 * right13 + left8 * right14 + left12 * right15; |
|
const column3Row1 = |
|
left1 * right12 + left5 * right13 + left9 * right14 + left13 * right15; |
|
const column3Row2 = |
|
left2 * right12 + left6 * right13 + left10 * right14 + left14 * right15; |
|
const column3Row3 = |
|
left3 * right12 + left7 * right13 + left11 * right14 + left15 * right15; |
|
|
|
result[0] = column0Row0; |
|
result[1] = column0Row1; |
|
result[2] = column0Row2; |
|
result[3] = column0Row3; |
|
result[4] = column1Row0; |
|
result[5] = column1Row1; |
|
result[6] = column1Row2; |
|
result[7] = column1Row3; |
|
result[8] = column2Row0; |
|
result[9] = column2Row1; |
|
result[10] = column2Row2; |
|
result[11] = column2Row3; |
|
result[12] = column3Row0; |
|
result[13] = column3Row1; |
|
result[14] = column3Row2; |
|
result[15] = column3Row3; |
|
return result; |
|
} |
|
|
|
static fromTranslation(translation, result) { |
|
if (!defined(result)) { |
|
return new Matrix4( |
|
1.0, 0.0, 0.0, translation.x, |
|
0.0, 1.0, 0.0, translation.y, |
|
0.0, 0.0, 1.0, translation.z, |
|
0.0, 0.0, 0.0, 1.0 |
|
); |
|
} |
|
|
|
result[0] = 1.0; |
|
result[1] = 0.0; |
|
result[2] = 0.0; |
|
result[3] = 0.0; |
|
result[4] = 0.0; |
|
result[5] = 1.0; |
|
result[6] = 0.0; |
|
result[7] = 0.0; |
|
result[8] = 0.0; |
|
result[9] = 0.0; |
|
result[10] = 1.0; |
|
result[11] = 0.0; |
|
result[12] = translation.x; |
|
result[13] = translation.y; |
|
result[14] = translation.z; |
|
result[15] = 1.0; |
|
return result; |
|
|
|
|
|
} |
|
|
|
static fromRotationTranslation(rotation, translation, result) { |
|
translation = defaultValue(translation, Vector3.ZERO); |
|
|
|
if (!defined(result)) { |
|
return new Matrix4( |
|
rotation[0], |
|
rotation[3], |
|
rotation[6], |
|
translation.x, |
|
rotation[1], |
|
rotation[4], |
|
rotation[7], |
|
translation.y, |
|
rotation[2], |
|
rotation[5], |
|
rotation[8], |
|
translation.z, |
|
0.0, |
|
0.0, |
|
0.0, |
|
1.0 |
|
); |
|
} |
|
|
|
result[0] = rotation[0]; |
|
result[1] = rotation[1]; |
|
result[2] = rotation[2]; |
|
result[3] = 0.0; |
|
result[4] = rotation[3]; |
|
result[5] = rotation[4]; |
|
result[6] = rotation[5]; |
|
result[7] = 0.0; |
|
result[8] = rotation[6]; |
|
result[9] = rotation[7]; |
|
result[10] = rotation[8]; |
|
result[11] = 0.0; |
|
result[12] = translation.x; |
|
result[13] = translation.y; |
|
result[14] = translation.z; |
|
result[15] = 1.0; |
|
return result; |
|
} |
|
|
|
static fromScale(scale, result) { |
|
if (!defined(result)) { |
|
return new Matrix4( |
|
scale.x, |
|
0.0, |
|
0.0, |
|
0.0, |
|
0.0, |
|
scale.y, |
|
0.0, |
|
0.0, |
|
0.0, |
|
0.0, |
|
scale.z, |
|
0.0, |
|
0.0, |
|
0.0, |
|
0.0, |
|
1.0 |
|
); |
|
} |
|
|
|
result[0] = scale.x; |
|
result[1] = 0.0; |
|
result[2] = 0.0; |
|
result[3] = 0.0; |
|
result[4] = 0.0; |
|
result[5] = scale.y; |
|
result[6] = 0.0; |
|
result[7] = 0.0; |
|
result[8] = 0.0; |
|
result[9] = 0.0; |
|
result[10] = scale.z; |
|
result[11] = 0.0; |
|
result[12] = 0.0; |
|
result[13] = 0.0; |
|
result[14] = 0.0; |
|
result[15] = 1.0; |
|
return result; |
|
} |
|
|
|
static clone(matrix, result) { |
|
if (!defined(result)) { |
|
return new Matrix4( |
|
matrix[0], |
|
matrix[4], |
|
matrix[8], |
|
matrix[12], |
|
matrix[1], |
|
matrix[5], |
|
matrix[9], |
|
matrix[13], |
|
matrix[2], |
|
matrix[6], |
|
matrix[10], |
|
matrix[14], |
|
matrix[3], |
|
matrix[7], |
|
matrix[11], |
|
matrix[15] |
|
); |
|
} |
|
|
|
result[0] = matrix[0]; |
|
result[1] = matrix[1]; |
|
result[2] = matrix[2]; |
|
result[3] = matrix[3]; |
|
result[4] = matrix[4]; |
|
result[5] = matrix[5]; |
|
result[6] = matrix[6]; |
|
result[7] = matrix[7]; |
|
result[8] = matrix[8]; |
|
result[9] = matrix[9]; |
|
result[10] = matrix[10]; |
|
result[11] = matrix[11]; |
|
result[12] = matrix[12]; |
|
result[13] = matrix[13]; |
|
result[14] = matrix[14]; |
|
result[15] = matrix[15]; |
|
return result; |
|
} |
|
|
|
static setTranslation(matrix, translation, result) { |
|
if (!defined(result)) result = new Matrix4(); |
|
result[0] = matrix[0]; |
|
result[1] = matrix[1]; |
|
result[2] = matrix[2]; |
|
result[3] = matrix[3]; |
|
|
|
result[4] = matrix[4]; |
|
result[5] = matrix[5]; |
|
result[6] = matrix[6]; |
|
result[7] = matrix[7]; |
|
|
|
result[8] = matrix[8]; |
|
result[9] = matrix[9]; |
|
result[10] = matrix[10]; |
|
result[11] = matrix[11]; |
|
|
|
result[12] = translation.x; |
|
result[13] = translation.y; |
|
result[14] = translation.z; |
|
result[15] = matrix[15]; |
|
|
|
return result; |
|
} |
|
} |
|
|
|
class Rectangle { |
|
constructor(west, south, east, north) { |
|
this.west = west || 0; |
|
this.south = south || 0; |
|
this.east = east || 0; |
|
this.north = north || 0; |
|
} |
|
|
|
clone(source) { |
|
return new Rectangle( |
|
source.west, |
|
source.south, |
|
source.east, |
|
source.north |
|
); |
|
} |
|
} |
|
|
|
class Cartographic { |
|
constructor(longitude, latitude, height) { |
|
this.longitude = longitude || 0; |
|
this.latitude = latitude || 0; |
|
this.height = height || 0; |
|
} |
|
|
|
|
|
|
|
} |
|
|
|
const scratchFirstArray = [0, 1, 0]; |
|
const scratchSecondArray = [-1, 0, 0]; |
|
const scratchThirdArray = [0, 0, 1]; |
|
|
|
const scratchFirstCartesian = new Vector3(); |
|
const scratchSecondCartesian = new Vector3(); |
|
const scratchThirdCartesian = new Vector3(); |
|
|
|
const scratchCalculateCartesian = { |
|
east: new Vector3(), |
|
north: new Vector3(), |
|
up: new Vector3(), |
|
west: new Vector3(), |
|
south: new Vector3(), |
|
down: new Vector3(), |
|
} |
|
|
|
class Transforms { |
|
constructor(){} |
|
|
|
static eastNorthUpToFixedFrame(origin, ellipsoid, result) { |
|
|
|
if (!defined(result)) result = new Matrix4(); |
|
|
|
if (Vector3.equalsEpsilon(origin, Vector3.ZERO, EPSILON14)) { |
|
Vector3.unpack(scratchFirstArray, 0, scratchFirstCartesian); |
|
Vector3.unpack(scratchSecondArray, 0, scratchSecondCartesian); |
|
Vector3.unpack(scratchThirdArray, 0, scratchThirdCartesian); |
|
} else if ( |
|
equalsEpsilon(origin.x, 0.0, EPSILON14) && |
|
equalsEpsilon(origin.y, 0.0, EPSILON14) |
|
) { |
|
const sign = sign(origin.z); |
|
|
|
Vector3.unpack(scratchFirstArray, 0, scratchFirstCartesian); |
|
|
|
Vector3.unpack(scratchSecondArray, 0, scratchSecondCartesian); |
|
Vector3.multiplyByScalar(scratchSecondCartesian, sign, scratchSecondCartesian); |
|
|
|
Vector3.unpack(scratchThirdArray, 0, scratchThirdCartesian); |
|
Vector3.multiplyByScalar(scratchThirdCartesian, sign, scratchThirdCartesian); |
|
} else { |
|
ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84); |
|
ellipsoid.geodeticSurfaceNormal(origin, scratchCalculateCartesian.up); |
|
const up = scratchCalculateCartesian.up; |
|
const east = scratchCalculateCartesian.east; |
|
east.x = -origin.y; |
|
east.y = origin.x; |
|
east.z = 0.0; |
|
Vector3.normalize(east, scratchCalculateCartesian.east); |
|
Vector3.cross(up, east, scratchCalculateCartesian.north); |
|
|
|
Vector3.multiplyByScalar(scratchCalculateCartesian.up, -1, scratchCalculateCartesian.down); |
|
Vector3.multiplyByScalar(scratchCalculateCartesian.east, -1, scratchCalculateCartesian.west); |
|
Vector3.multiplyByScalar(scratchCalculateCartesian.north, -1, scratchCalculateCartesian.south); |
|
|
|
scratchFirstCartesian.copy(scratchCalculateCartesian.east); |
|
scratchSecondCartesian.copy(scratchCalculateCartesian.north); |
|
scratchThirdCartesian.copy(scratchCalculateCartesian.up); |
|
} |
|
|
|
result[0] = scratchFirstCartesian.x; |
|
result[1] = scratchFirstCartesian.y; |
|
result[2] = scratchFirstCartesian.z; |
|
result[3] = 0.0; |
|
result[4] = scratchSecondCartesian.x; |
|
result[5] = scratchSecondCartesian.y; |
|
result[6] = scratchSecondCartesian.z; |
|
result[7] = 0.0; |
|
result[8] = scratchThirdCartesian.x; |
|
result[9] = scratchThirdCartesian.y; |
|
result[10] = scratchThirdCartesian.z; |
|
result[11] = 0.0; |
|
result[12] = origin.x; |
|
result[13] = origin.y; |
|
result[14] = origin.z; |
|
result[15] = 1.0; |
|
return result; |
|
} |
|
} |
|
|
|
class Ellipsoid { |
|
constructor(x, y, z) { |
|
x = x || 0; |
|
y = y || 0; |
|
z = z || 0; |
|
|
|
this.x = x; |
|
this.y = y; |
|
this.z = z; |
|
|
|
this.radii = new Vector3(x, y, z); |
|
this.radiiSquared = new Vector3(x * x, y * y, z * z); |
|
this.radiiToTheFourth = new Vector3( |
|
x * x * x * x, |
|
y * y * y * y, |
|
z * z * z * z |
|
); |
|
this.oneOverRadii = new Vector3( |
|
x === 0.0 ? 0.0 : 1.0 / x, |
|
y === 0.0 ? 0.0 : 1.0 / y, |
|
z === 0.0 ? 0.0 : 1.0 / z |
|
); |
|
|
|
this.oneOverRadiiSquared = new Vector3( |
|
x === 0.0 ? 0.0 : 1.0 / (x * x), |
|
y === 0.0 ? 0.0 : 1.0 / (y * y), |
|
z === 0.0 ? 0.0 : 1.0 / (z * z) |
|
); |
|
|
|
this.minimumRadius = Math.min(x, y, z); |
|
this.maximumRadius = Math.max(x, y, z); |
|
this.centerToleranceSquared = 0.1; |
|
this.squaredFirstEccentricity = 1 - (z * z) / (x * x); |
|
|
|
if (this.radiiSquared.z !== 0) { |
|
this.squaredXOverSquaredZ = this.radiiSquared.x / this.radiiSquared.z; |
|
} |
|
|
|
} |
|
|
|
transformPositionToScaledSpace(position, result) { |
|
if (!defined(result)) { |
|
result = new Vector3(); |
|
} |
|
|
|
return Vector3.multiplyComponents(position, this.oneOverRadii, result); |
|
} |
|
|
|
geodeticSurfaceNormal(cartesian, result) { |
|
if (Vector3.equalsEpsilon(cartesian, Vector3.ZERO, EPSILON14)) { |
|
return undefined; |
|
} |
|
|
|
if (!defined(result)) result = new Vector3(); |
|
|
|
let temp = this.oneOverRadiiSquared.clone(); |
|
result = Vector3.multiplyComponents(cartesian, temp, result); |
|
return result.normalize(); |
|
} |
|
cartographicToCartesian(cartographic, result) { |
|
|
|
if (!defined(result)) result = new Vector3(); |
|
|
|
const cartographicToCartesianNormal = new Vector3(); |
|
const cartographicToCartesianK = new Vector3(); |
|
|
|
this.geodeticSurfaceNormalCartographic(cartographic, cartographicToCartesianNormal); |
|
|
|
Vector3.multiplyComponents(this.radiiSquared, cartographicToCartesianNormal, cartographicToCartesianK); |
|
const gamma = Math.sqrt(Vector3.dot(cartographicToCartesianNormal, cartographicToCartesianK)); |
|
Vector3.divideByScalar(cartographicToCartesianK, gamma, cartographicToCartesianK); |
|
Vector3.multiplyByScalar(cartographicToCartesianNormal, cartographic.height, cartographicToCartesianNormal); |
|
|
|
return Vector3.add(cartographicToCartesianK, cartographicToCartesianNormal, result); |
|
|
|
} |
|
|
|
geodeticSurfaceNormalCartographic(cartographic, result) { |
|
if (!defined(result)) result = new Vector3(); |
|
const longitude = cartographic.longitude; |
|
const latitude = cartographic.latitude; |
|
const cosLatitude = Math.cos(latitude); |
|
|
|
const x = cosLatitude * Math.cos(longitude); |
|
const y = cosLatitude * Math.sin(longitude); |
|
const z = Math.sin(latitude); |
|
|
|
result.set(x, y, z); |
|
return result.normalize(); |
|
} |
|
|
|
|
|
} |
|
|
|
Ellipsoid.WGS84 = Object.freeze(new Ellipsoid(6378137.0, 6378137.0, 6356752.3142451793)) |
|
|
|
class WebMercatorProjection { |
|
constructor(){} |
|
static mercatorAngleToGeodeticLatitude(mercatorAngle) { |
|
return PI_OVER_TWO - 2.0 * Math.atan(Math.exp(-mercatorAngle)); |
|
} |
|
static geodeticLatitudeToMercatorAngle(latitude) { |
|
if (latitude > WebMercatorProjection.MaximumLatitude) { |
|
latitude = WebMercatorProjection.MaximumLatitude; |
|
} else if (latitude < -WebMercatorProjection.MaximumLatitude) { |
|
latitude = -WebMercatorProjection.MaximumLatitude; |
|
} |
|
const sinLatitude = Math.sin(latitude); |
|
return 0.5 * Math.log((1.0 + sinLatitude) / (1.0 - sinLatitude)); |
|
} |
|
} |
|
|
|
WebMercatorProjection.MaximumLatitude = WebMercatorProjection.mercatorAngleToGeodeticLatitude(Math.PI); |
|
|
|
class EllipsoidalOccluder { |
|
constructor(ellipsoid) { |
|
this.ellipsoid = ellipsoid; |
|
this.cameraPosition = new Vector3(); |
|
this.cameraPositionInScaledSpace = new Vector3(); |
|
this.distanceToLimbInScaledSquared = 0.0; |
|
} |
|
computeHorizonCullingPointPossiblyUnderEllipsoid(directionToPoint, positions, minimumHeight, result) { |
|
const possiblyShrunkEllipsoid = EllipsoidalOccluder.getPossiblyShrunkEllipsoid( |
|
this.ellipsoid, |
|
minimumHeight |
|
); |
|
return EllipsoidalOccluder.computeHorizonCullingPointFromPositions( |
|
possiblyShrunkEllipsoid, |
|
directionToPoint, |
|
positions, |
|
result |
|
); |
|
} |
|
|
|
static getPossiblyShrunkEllipsoid(ellipsoid, minimumHeight) { |
|
if (defined(minimumHeight) && |
|
minimumHeight < 0.0 && |
|
ellipsoid.minimumRadius > - minimumHeight |
|
) { |
|
const ellipsoidShrunkRadii = new Vector3( |
|
ellipsoid.radii.x + minimumHeight, |
|
ellipsoid.radii.y + minimumHeight, |
|
ellipsoid.radii.z + minimumHeight, |
|
); |
|
ellipsoid = new Ellipsoid( |
|
ellipsoidShrunkRadii.x, |
|
ellipsoidShrunkRadii.y, |
|
ellipsoidShrunkRadii.z |
|
); |
|
} |
|
return ellipsoid; |
|
} |
|
|
|
static computeHorizonCullingPointFromPositions(ellipsoid, directionToPoint, positions, result) { |
|
if (!defined(result)) result = new Vector3(); |
|
const scaledSpaceDirectionToPoint = EllipsoidalOccluder.computeScaledSpaceDirectionToPoint(ellipsoid, directionToPoint); |
|
|
|
let resultMagnitude = 0.0; |
|
for (let i = 0, len = positions.length; i < len; ++i) { |
|
const position = positions[i]; |
|
const candidateMagnitude = EllipsoidalOccluder.computeMagnitude( |
|
ellipsoid, |
|
position, |
|
scaledSpaceDirectionToPoint |
|
); |
|
if (candidateMagnitude < 0.0) { |
|
// all points should face the same direction, but this one doesn't, so return undefined |
|
return undefined; |
|
} |
|
resultMagnitude = Math.max(resultMagnitude, candidateMagnitude); |
|
} |
|
|
|
return EllipsoidalOccluder.magnitudeToPoint(scaledSpaceDirectionToPoint, resultMagnitude, result); |
|
} |
|
|
|
static computeScaledSpaceDirectionToPoint(ellipsoid, directionToPoint){ |
|
if (Vector3.equals(directionToPoint, Vector3.ZERO)) { |
|
return directionToPoint; |
|
} |
|
|
|
const directionToPointScratch = ellipsoid.transformPositionToScaledSpace( |
|
directionToPoint |
|
); |
|
|
|
return directionToPointScratch.normalize(); |
|
} |
|
|
|
static computeMagnitude(ellipsoid, position, scaledSpaceDirectionToPoint) { |
|
const scaledSpacePosition = ellipsoid.transformPositionToScaledSpace( |
|
position |
|
); |
|
let magnitudeSquared = scaledSpacePosition.lengthSquare(); |
|
let magnitude = scaledSpacePosition.length(); |
|
const direction = Vector3.divideByScalar(scaledSpacePosition, magnitude); |
|
|
|
magnitudeSquared = Math.max(1.0, magnitudeSquared); |
|
magnitude = Math.max(1.0, magnitude); |
|
|
|
const cosAlpha = Vector3.dot(direction, scaledSpaceDirectionToPoint); |
|
const sinAlpha = Vector3.cross(direction, scaledSpaceDirectionToPoint, direction).length() |
|
|
|
const cosBeta = 1.0 / magnitude; |
|
const sinBeta = Math.sqrt(magnitudeSquared - 1.0) * cosBeta; |
|
|
|
return 1.0 / (cosAlpha * cosBeta - sinAlpha * sinBeta); |
|
} |
|
|
|
static magnitudeToPoint(scaledSpaceDirectionToPoint, resultMagnitude, result) { |
|
if (resultMagnitude <= 0.0 || |
|
resultMagnitude === 1.0 / 0.0 || |
|
resultMagnitude !== resultMagnitude) { |
|
return undefined; |
|
} |
|
|
|
return Vector3.multiplyByScalar(scaledSpaceDirectionToPoint, resultMagnitude, result); |
|
} |
|
} |
|
|
|
class AxisAlignedBoundingBox { |
|
constructor(minimum, maximum, center) { |
|
this.minimum = defaultValue(minimum, Vector3.ZERO).clone(); |
|
this.maximum = defaultValue(maximum, Vector3.ZERO).clone(); |
|
if (!defined(center)) { |
|
center = new Vector3( |
|
(this.minimum.x + this.maximum.x) * 0.5, |
|
(this.minimum.y + this.maximum.y) * 0.5, |
|
(this.minimum.z + this.maximum.z) * 0.5 |
|
); |
|
} else { |
|
center = new Vector3(center.x, center.y, center.z); |
|
} |
|
this.center = center; |
|
} |
|
} |
|
|
|
class TerrainEncoding { |
|
constructor( |
|
center, |
|
axisAlignedBoundingBox, |
|
minimumHeight, |
|
maximumHeight, |
|
fromENU, |
|
hasVertexNormals, |
|
hasWebMercatorT, |
|
hasGeodeticSurfaceNormals, |
|
exaggeration, |
|
exaggerationRelativeHeight, |
|
name |
|
) { |
|
|
|
let quantization = TerrainQuantization.NONE; |
|
let toENU; |
|
let matrix; |
|
|
|
const cartesian3DimScratch = new Vector3(); |
|
|
|
if ( |
|
defined(axisAlignedBoundingBox) && |
|
defined(minimumHeight) && |
|
defined(maximumHeight) && |
|
defined(fromENU) |
|
) { |
|
const minimum = axisAlignedBoundingBox.minimum; |
|
const maximum = axisAlignedBoundingBox.maximum; |
|
|
|
const dimensions = Vector3.subtract( |
|
maximum, |
|
minimum, |
|
cartesian3DimScratch |
|
); |
|
const hDim = maximumHeight - minimumHeight; |
|
const maxDim = Math.max(Vector3.maximumComponent(dimensions), hDim); |
|
|
|
if (maxDim < SHIFT_LEFT_12 - 1.0) { |
|
quantization = TerrainQuantization.BITS12; |
|
} else { |
|
quantization = TerrainQuantization.NONE; |
|
} |
|
|
|
toENU = Matrix4.inverseTransformation(fromENU, new Matrix4()); |
|
const matrix4Scratch13T = new Matrix4(); |
|
const cartesian3Scratch13T = new Vector3(); |
|
const matrix4Scratch2 = new Matrix4(); |
|
|
|
const translation = Vector3.negate(minimum, cartesian3Scratch13T); |
|
|
|
Matrix4.multiply( |
|
Matrix4.fromTranslation(translation, matrix4Scratch13T), |
|
toENU, |
|
toENU |
|
); |
|
|
|
const scale = cartesian3Scratch13T; |
|
scale.x = 1.0 / dimensions.x; |
|
scale.y = 1.0 / dimensions.y; |
|
scale.z = 1.0 / dimensions.z; |
|
Matrix4.multiply(Matrix4.fromScale(scale, matrix4Scratch13T), toENU, toENU); |
|
|
|
matrix = Matrix4.clone(fromENU); |
|
Matrix4.setTranslation(matrix, Vector3.ZERO, matrix); |
|
|
|
fromENU = Matrix4.clone(fromENU, new Matrix4()); |
|
|
|
|
|
const translationMatrix = Matrix4.fromTranslation(minimum, matrix4Scratch13T); |
|
|
|
const scaleMatrix = Matrix4.fromScale(dimensions, matrix4Scratch2); |
|
|
|
const st = Matrix4.multiply(translationMatrix, scaleMatrix, matrix4Scratch13T); |
|
|
|
Matrix4.multiply(fromENU, st, fromENU); |
|
|
|
Matrix4.multiply(matrix, st, matrix); |
|
} |
|
this.quantization = quantization; |
|
this.minimumHeight = minimumHeight; |
|
this.maximumHeight = maximumHeight; |
|
this.center = Vector3.clone(center); |
|
this.toScaledENU = toENU; |
|
this.fromScaledENU = fromENU; |
|
|
|
this.matrix = matrix; |
|
this.hasVertexNormals = hasVertexNormals; |
|
this.hasWebMercatorT = defaultValue(hasWebMercatorT, false); |
|
this.hasGeodeticSurfaceNormals = defaultValue( |
|
hasGeodeticSurfaceNormals, |
|
false |
|
); |
|
this.exaggeration = defaultValue(exaggeration, 1.0); |
|
this.exaggerationRelativeHeight = defaultValue( |
|
exaggerationRelativeHeight, |
|
0.0 |
|
); |
|
|
|
this.stride = 0; |
|
this._offsetGeodeticSurfaceNormal = 0; |
|
this._offsetVertexNormal = 0; |
|
this._calculateStrideAndOffsets(); |
|
} |
|
_calculateStrideAndOffsets() { |
|
let vertexStride = 0; |
|
|
|
switch (this.quantization) { |
|
case TerrainQuantization.BITS12: |
|
vertexStride += 3; |
|
break; |
|
default: |
|
vertexStride += 6; |
|
} |
|
if (this.hasWebMercatorT) { |
|
vertexStride += 1; |
|
} |
|
if (this.hasVertexNormals) { |
|
this._offsetVertexNormal = vertexStride; |
|
vertexStride += 1; |
|
} |
|
if (this.hasGeodeticSurfaceNormals) { |
|
this._offsetGeodeticSurfaceNormal = vertexStride; |
|
vertexStride += 3; |
|
} |
|
|
|
this.stride = vertexStride; |
|
} |
|
encode(vertexBuffer, bufferIndex, position, uv, height, normalToPack, webMercatorT, geodeticSurfaceNormal) { |
|
const cartesian3Scratch13T = new Vector3(); |
|
const cartesian2Scratch = new Vector2(); |
|
const u = uv.x; |
|
const v = uv.y; |
|
if (this.quantization === TerrainQuantization.BITS12) { |
|
position = Matrix4.multiplyByPoint( |
|
this.toScaledENU, |
|
position, |
|
cartesian3Scratch13T |
|
); |
|
|
|
position.x = clamp(position.x, 0.0, 1.0); |
|
position.y = clamp(position.y, 0.0, 1.0); |
|
position.z = clamp(position.z, 0.0, 1.0); |
|
|
|
const hDim = this.maximumHeight - this.minimumHeight; |
|
const h = clamp((height - this.minimumHeight) / hDim, 0.0, 1.0); |
|
|
|
cartesian2Scratch.set(position.x, position.y); |
|
const compressed0 = AttributeCompression.compressTextureCoordinates( |
|
cartesian2Scratch |
|
); |
|
|
|
cartesian2Scratch.set(position.z, h); |
|
const compressed1 = AttributeCompression.compressTextureCoordinates( |
|
cartesian2Scratch |
|
); |
|
|
|
cartesian2Scratch.set(u, v); |
|
const compressed2 = AttributeCompression.compressTextureCoordinates( |
|
cartesian2Scratch |
|
); |
|
|
|
vertexBuffer[bufferIndex++] = compressed0; |
|
|
|
vertexBuffer[bufferIndex++] = compressed1; |
|
|
|
vertexBuffer[bufferIndex++] = compressed2; |
|
|
|
if (this.hasWebMercatorT) { |
|
cartesian2Scratch.set(webMercatorT, 0.0); |
|
const compressed3 = AttributeCompression.compressTextureCoordinates( |
|
cartesian2Scratch |
|
); |
|
vertexBuffer[bufferIndex++] = compressed3; |
|
} |
|
} else { |
|
Vector3.subtract(position, this.center, cartesian3Scratch13T); |
|
vertexBuffer[bufferIndex++] = cartesian3Scratch13T.x; |
|
vertexBuffer[bufferIndex++] = cartesian3Scratch13T.y; |
|
vertexBuffer[bufferIndex++] = cartesian3Scratch13T.z; |
|
vertexBuffer[bufferIndex++] = height; |
|
vertexBuffer[bufferIndex++] = u; |
|
vertexBuffer[bufferIndex++] = v; |
|
|
|
if (this.hasWebMercatorT) { |
|
vertexBuffer[bufferIndex++] = webMercatorT; |
|
} |
|
} |
|
|
|
|
|
|
|
if (this.hasVertexNormals) { |
|
vertexBuffer[bufferIndex++] = AttributeCompression.octPackFloat( |
|
normalToPack |
|
); |
|
} |
|
|
|
if (this.hasGeodeticSurfaceNormals) { |
|
vertexBuffer[bufferIndex++] = geodeticSurfaceNormal.x; |
|
vertexBuffer[bufferIndex++] = geodeticSurfaceNormal.y; |
|
vertexBuffer[bufferIndex++] = geodeticSurfaceNormal.z; |
|
} |
|
|
|
return bufferIndex; |
|
} |
|
} |
|
|
|
class IndexDatatype { |
|
constructor() {} |
|
static createTypedArray(numberOfVertices, indicesLengthOrArray) { |
|
if (!defined(numberOfVertices)) { |
|
throw new Error("numberOfVertices is required"); |
|
} |
|
|
|
if (numberOfVertices >= 6 * 1024 * 1024) { |
|
return new Uint32Array(indicesLengthOrArray); |
|
} |
|
|
|
return new Uint16Array(indicesLengthOrArray); |
|
} |
|
} |
|
|
|
class TerrainProvider { |
|
constructor(){} |
|
static addSkirtIndice(edgeIndices, vertexIndex, indices, offset) { |
|
let previousIndex = edgeIndices[0]; |
|
const length = edgeIndices.length; |
|
for (let i = 1; i < length; ++i) { |
|
const index = edgeIndices[i]; |
|
indices[offset++] = previousIndex; |
|
indices[offset++] = index; |
|
indices[offset++] = vertexIndex; |
|
indices[offset++] = vertexIndex; |
|
indices[offset++] = index; |
|
indices[offset++] = vertexIndex + 1; |
|
previousIndex = index; |
|
++vertexIndex; |
|
} |
|
return offset; |
|
} |
|
static addSkirtIndices( |
|
westIndicesSouthToNorth, |
|
southIndicesEastToWest, |
|
eastIndicesNorthToSouth, |
|
northIndicesWestToEast, |
|
vertexCount, |
|
indices, |
|
offset |
|
) { |
|
let vertexIndex = vertexCount; |
|
offset = TerrainProvider.addSkirtIndice( |
|
westIndicesSouthToNorth, |
|
vertexIndex, |
|
indices, |
|
offset |
|
); |
|
vertexIndex += westIndicesSouthToNorth.length; |
|
offset = TerrainProvider.addSkirtIndice( |
|
southIndicesEastToWest, |
|
vertexIndex, |
|
indices, |
|
offset |
|
); |
|
vertexIndex += southIndicesEastToWest.length; |
|
offset = TerrainProvider.addSkirtIndice( |
|
eastIndicesNorthToSouth, |
|
vertexIndex, |
|
indices, |
|
offset |
|
); |
|
vertexIndex += eastIndicesNorthToSouth.length; |
|
TerrainProvider.addSkirtIndice(northIndicesWestToEast, vertexIndex, indices, offset); |
|
} |
|
} |
|
|
|
// class Vertex {} |
|
|
|
class AttributeCompression { |
|
constructor(){} |
|
static compressTextureCoordinates(textureCoordinates) { |
|
const x = (textureCoordinates.x * 4095.0) | 0; |
|
const y = (textureCoordinates.y * 4095.0) | 0; |
|
return 4096 * x + y; |
|
} |
|
|
|
static octPackFloat(encoded) { |
|
return 256.0 * encoded.x + encoded.y; |
|
} |
|
} |
|
|
|
|
|
const cartographicScratch = new Cartographic(); |
|
const cartesian3Scratch = new Vector3(); |
|
const toPack = new Vector2(); |
|
|
|
const maxShort = 32767; |
|
const halfMaxShort = (maxShort / 2) || 0; |
|
function createVerticesFromQuantizedTerrainMesh(parameters) { |
|
const quantizedVertices = parameters.quantizedVertices; |
|
const quantizedVerticeCount = quantizedVertices.length / 3; |
|
const octEncodedNormals = parameters.octEncodedNormals; |
|
const edgeVertexCount = |
|
parameters.westIndices.length + |
|
parameters.eastIndices.length + |
|
parameters.southIndices.length + |
|
parameters.northIndices.length; |
|
const includeWebMercatorT = parameters.includeWebMercatorT; |
|
const exaggeration = parameters.exaggeration; |
|
const exaggerationRelativeHeight = parameters.exaggerationRelativeHeight; |
|
const hasExaggeration = exaggeration !== 1.0; |
|
const includeGeodeticSurfaceNormals = hasExaggeration; |
|
|
|
const rectangle = new Rectangle().clone(parameters.rectangle); |
|
const [west, east, north, south] = [rectangle.west, rectangle.east, rectangle.north, rectangle.south]; |
|
|
|
const ellipsoid = new Ellipsoid(parameters.ellipsoid.x, parameters.ellipsoid.y, parameters.ellipsoid.z); // parameters.ellipsoid |
|
|
|
const minimumHeight = parameters.minimumHeight; |
|
const maximumHeight = parameters.maximumHeight; |
|
|
|
const center = parameters.relativeToCenter; |
|
|
|
const fromENU = Transforms.eastNorthUpToFixedFrame(center, ellipsoid); |
|
const name = parameters.name; |
|
|
|
const toENU = Matrix4.inverseTransformation(fromENU); |
|
|
|
let southMercatorY, oneOverMercatorHeight; |
|
if (includeWebMercatorT) { |
|
southMercatorY = WebMercatorProjection.geodeticLatitudeToMercatorAngle(south); |
|
oneOverMercatorHeight = 1.0 / (WebMercatorProjection.geodeticLatitudeToMercatorAngle(north) - southMercatorY); |
|
} |
|
|
|
const uBuffer = quantizedVertices.subarray(0, quantizedVerticeCount); |
|
const vBuffer = quantizedVertices.subarray(quantizedVerticeCount, quantizedVerticeCount * 2); |
|
const heightBuffer = quantizedVertices.subarray(quantizedVerticeCount * 2, quantizedVerticeCount * 3); |
|
const hasVertexNormals = defined(octEncodedNormals); |
|
|
|
// 准备UV、高度、经纬度数组 |
|
const uvs = new Array(quantizedVerticeCount), |
|
heights = new Array(quantizedVerticeCount), |
|
positions = new Array(quantizedVerticeCount); |
|
const webMercatorTs = includeWebMercatorT ? new Array(quantizedVerticeCount) : []; |
|
const geodeticSurfaceNormals = includeGeodeticSurfaceNormals ? new Array(quantizedVerticeCount) : []; |
|
|
|
let minimum = new Vector3( |
|
Number.POSITIVE_INFINITY, |
|
Number.POSITIVE_INFINITY, |
|
Number.POSITIVE_INFINITY |
|
); |
|
let maximum = new Vector3( |
|
Number.NEGATIVE_INFINITY, |
|
Number.NEGATIVE_INFINITY, |
|
Number.NEGATIVE_INFINITY |
|
); |
|
let minLongitude = Number.POSITIVE_INFINITY; |
|
let maxLongitude = Number.NEGATIVE_INFINITY; |
|
let minLatitude = Number.POSITIVE_INFINITY; |
|
let maxLatitude = Number.NEGATIVE_INFINITY; |
|
|
|
for (let i = 0; i < quantizedVerticeCount; ++i) { |
|
const rawU = uBuffer[i]; |
|
const rawV = vBuffer[i]; |
|
|
|
const u = rawU / maxShort; |
|
const v = rawV / maxShort; |
|
const height = lerp(minimumHeight, maximumHeight, heightBuffer[i] / maxShort); |
|
|
|
cartographicScratch.longitude = lerp(west, east, u); |
|
cartographicScratch.latitude = lerp(south, north, v); |
|
cartographicScratch.height = height; |
|
|
|
minLongitude = Math.min( |
|
cartographicScratch.longitude, |
|
minLongitude |
|
); |
|
|
|
maxLongitude = Math.max( |
|
cartographicScratch.longitude, |
|
maxLongitude |
|
); |
|
|
|
minLatitude = Math.min( |
|
cartographicScratch.latitude, |
|
minLatitude |
|
); |
|
|
|
maxLatitude = Math.max( |
|
cartographicScratch.latitude, |
|
maxLatitude |
|
); |
|
|
|
const position = ellipsoid.cartographicToCartesian(cartographicScratch); |
|
|
|
uvs[i] = new Vector3(u, v, 0); |
|
heights[i] = height; |
|
positions[i] = position; |
|
|
|
if (includeWebMercatorT) { |
|
webMercatorTs[i] = ( |
|
WebMercatorProjection.geodeticLatitudeToMercatorAngle(cartographicScratch.latitude) |
|
- |
|
southMercatorY |
|
) * oneOverMercatorHeight; |
|
} |
|
|
|
if (includeGeodeticSurfaceNormals) { |
|
geodeticSurfaceNormals[i] = ellipsoid.geodeticSurfaceNormal(position); |
|
} |
|
|
|
Matrix4.multiplyByPoint(toENU, position, cartesian3Scratch); |
|
Vector3.minimumByComponent(cartesian3Scratch, minimum, minimum); |
|
Vector3.maximumByComponent(cartesian3Scratch, maximum, maximum); |
|
|
|
} |
|
|
|
|
|
|
|
// 处理四条边的索引 |
|
const westIndicesSouthToNorth = copyAndSort( |
|
parameters.westIndices, |
|
function(a, b) { return uvs[a].y - uvs[b].y; } |
|
); |
|
|
|
const eastIndicesNorthToSouth = copyAndSort( |
|
parameters.eastIndices, |
|
function(a, b) { return uvs[b].y - uvs[a].y; } |
|
); |
|
const southIndicesEastToWest = copyAndSort( |
|
parameters.southIndices, |
|
function(a, b) { return uvs[b].x - uvs[a].x; } |
|
); |
|
const northIndicesWestToEast = copyAndSort( |
|
parameters.northIndices, |
|
function(a, b) { return uvs[a].x - uvs[b].x; } |
|
); |
|
|
|
// 处理地平线 |
|
let occludeePointInScaledSpace; |
|
if (minimumHeight < 0.0) { |
|
const occluder = new EllipsoidalOccluder(ellipsoid); |
|
occludeePointInScaledSpace = occluder.computeHorizonCullingPointPossiblyUnderEllipsoid(center, positions, minimumHeight); |
|
} |
|
|
|
// 处理高程 |
|
let hMin = minimumHeight; |
|
hMin = Math.min( |
|
hMin, |
|
findMinMaxSkirts( |
|
parameters.westIndices, |
|
parameters.westSkirtHeight, |
|
heights, |
|
uvs, |
|
rectangle, |
|
ellipsoid, |
|
toENU, |
|
minimum, |
|
maximum |
|
) |
|
); |
|
|
|
hMin = Math.min( |
|
hMin, |
|
findMinMaxSkirts( |
|
parameters.southIndices, |
|
parameters.southSkirtHeight, |
|
heights, |
|
uvs, |
|
rectangle, |
|
ellipsoid, |
|
toENU, |
|
minimum, |
|
maximum |
|
) |
|
); |
|
|
|
hMin = Math.min( |
|
hMin, |
|
findMinMaxSkirts( |
|
parameters.eastIndices, |
|
parameters.eastSkirtHeight, |
|
heights, |
|
uvs, |
|
rectangle, |
|
ellipsoid, |
|
toENU, |
|
minimum, |
|
maximum |
|
) |
|
); |
|
|
|
hMin = Math.min( |
|
hMin, |
|
findMinMaxSkirts( |
|
parameters.northIndices, |
|
parameters.northSkirtHeight, |
|
heights, |
|
uvs, |
|
rectangle, |
|
ellipsoid, |
|
toENU, |
|
minimum, |
|
maximum |
|
) |
|
); |
|
const aaBox = new AxisAlignedBoundingBox(minimum, maximum, center); |
|
// console.log(name, center, aaBox, hMin, maximumHeight, fromENU, hasVertexNormals, includeWebMercatorT, includeGeodeticSurfaceNormals, exaggeration, exaggerationRelativeHeight) |
|
const encoding = new TerrainEncoding(center, aaBox, hMin, maximumHeight, fromENU, hasVertexNormals, includeWebMercatorT, includeGeodeticSurfaceNormals, exaggeration, exaggerationRelativeHeight, name); |
|
const vertexStride = encoding.stride; |
|
const size = (quantizedVerticeCount + edgeVertexCount) * vertexStride; |
|
const vertexBuffer = new Float32Array(size); |
|
|
|
let bufferIndex = 0; |
|
|
|
for (let j = 0; j < quantizedVerticeCount; ++j) { |
|
if (hasVertexNormals) { |
|
const n = j * 2.0; |
|
toPack.x = octEncodedNormals[n]; |
|
toPack.y = octEncodedNormals[n + 1]; |
|
} |
|
|
|
bufferIndex = encoding.encode( |
|
vertexBuffer, |
|
bufferIndex, |
|
positions[j], |
|
uvs[j], |
|
heights[j], |
|
toPack, |
|
webMercatorTs[j], |
|
geodeticSurfaceNormals[j] |
|
); |
|
} |
|
|
|
const edgeTraiangleCount = Math.max(0, (edgeVertexCount - 4) * 2); |
|
const indexBufferLength = parameters.indices.length + edgeTraiangleCount * 3; |
|
const indexBuffer = IndexDatatype.createTypedArray(quantizedVerticeCount + edgeVertexCount, indexBufferLength); |
|
indexBuffer.set(parameters.indices, 0); |
|
|
|
const percentage = 0.0001; |
|
const lonOffset = (maxLongitude - minLongitude) * percentage; |
|
const latOffset = (maxLatitude - minLatitude) * percentage; |
|
const westLongitudeOffset = -lonOffset; |
|
const westLatitudeOffset = 0.0; |
|
const eastLongitudeOffset = lonOffset; |
|
const eastLatitudeOffset = 0.0; |
|
const northLongitudeOffset = 0.0; |
|
const northLatitudeOffset = latOffset; |
|
const southLongitudeOffset = 0.0; |
|
const southLatitudeOffset = -latOffset; |
|
|
|
// 添加裙边 |
|
let vertexBufferIndex = quantizedVerticeCount * vertexStride; |
|
addSkirt(vertexBuffer, vertexBufferIndex, westIndicesSouthToNorth, encoding, heights, uvs, octEncodedNormals, ellipsoid, rectangle, parameters.westSkirtHeight, southMercatorY, oneOverMercatorHeight, westLongitudeOffset, westLatitudeOffset); |
|
vertexBufferIndex += parameters.westIndices.length * vertexStride; |
|
|
|
addSkirt(vertexBuffer, vertexBufferIndex, southIndicesEastToWest, encoding, heights, uvs, octEncodedNormals, ellipsoid, rectangle, parameters.southSkirtHeight, southMercatorY, oneOverMercatorHeight, southLongitudeOffset, southLatitudeOffset); |
|
vertexBufferIndex += parameters.southIndices.length * vertexStride; |
|
|
|
addSkirt(vertexBuffer, vertexBufferIndex, eastIndicesNorthToSouth, encoding, heights, uvs, octEncodedNormals, ellipsoid, rectangle, parameters.eastSkirtHeight, southMercatorY, oneOverMercatorHeight, eastLongitudeOffset, eastLatitudeOffset); |
|
vertexBufferIndex += parameters.eastIndices.length * vertexStride; |
|
|
|
addSkirt(vertexBuffer, vertexBufferIndex, northIndicesWestToEast, encoding, heights, uvs, octEncodedNormals, ellipsoid, rectangle, parameters.northSkirtHeight, southMercatorY, oneOverMercatorHeight, northLongitudeOffset, northLatitudeOffset); |
|
|
|
TerrainProvider.addSkirtIndices( |
|
westIndicesSouthToNorth, |
|
southIndicesEastToWest, |
|
eastIndicesNorthToSouth, |
|
northIndicesWestToEast, |
|
quantizedVerticeCount, |
|
indexBuffer, |
|
parameters.indices.length |
|
); |
|
|
|
return { |
|
vertices: vertexBuffer.buffer, |
|
indices: indexBuffer.buffer, |
|
westIndicesSouthToNorth: westIndicesSouthToNorth, |
|
southIndicesEastToWest: southIndicesEastToWest, |
|
eastIndicesNorthToSouth: eastIndicesNorthToSouth, |
|
northIndicesWestToEast: northIndicesWestToEast, |
|
vertexStride: vertexStride, |
|
center: center, |
|
minimumHeight: minimumHeight, |
|
maximumHeight: maximumHeight, |
|
occludeePointInScaledSpace: occludeePointInScaledSpace, |
|
encoding: encoding, |
|
indexCountWithoutSkirts: parameters.indices.length |
|
}; |
|
} |
|
|
|
function copyAndSort(typedArray, comparator) { |
|
let copy; |
|
if (typeof typedArray.slice === "function") { |
|
copy = typedArray.slice(); |
|
if (typeof copy.sort !== "function") { |
|
copy = undefined; |
|
} |
|
} |
|
|
|
if (!defined(copy)) { |
|
copy = Array.prototype.slice.call(typedArray); |
|
} |
|
|
|
copy.sort(comparator); |
|
|
|
return copy; |
|
} |
|
|
|
function findMinMaxSkirts( |
|
edgeIndices, |
|
edgeHeight, |
|
heights, |
|
uvs, |
|
rectangle, |
|
ellipsoid, |
|
toENU, |
|
minimum, |
|
maximum |
|
) { |
|
let hMin = Number.POSITIVE_INFINITY; |
|
const north = rectangle.north; |
|
const south = rectangle.south; |
|
let east = rectangle.east; |
|
const west = rectangle.west; |
|
|
|
if (east < west) { |
|
east += Math.PI * 2; |
|
} |
|
|
|
const length = edgeIndices.length; |
|
for (let i = 0; i < length; ++i) { |
|
const index = edgeIndices[i]; |
|
const h = heights[index]; |
|
const uv = uvs[index]; |
|
|
|
cartographicScratch.longitude = lerp(west, east, uv.x); |
|
cartographicScratch.latitude = lerp(south, north, uv.y); |
|
cartographicScratch.height = h - edgeHeight; |
|
|
|
const position = ellipsoid.cartographicToCartesian( |
|
cartographicScratch, |
|
cartesian3Scratch, |
|
); |
|
|
|
Matrix4.multiplyByPoint(toENU, position, position); |
|
|
|
Vector3.minimumByComponent(position, minimum, minimum); |
|
Vector3.maximumByComponent(position, maximum, maximum); |
|
|
|
hMin = Math.min(hMin, cartographicScratch.height); |
|
} |
|
|
|
return hMin; |
|
} |
|
|
|
function addSkirt( |
|
vertexBuffer, |
|
vertexBufferIndex, |
|
edgeVertices, |
|
encoding, |
|
heights, |
|
uvs, |
|
octEncodedNormals, |
|
ellipsoid, |
|
rectangle, |
|
skirtLength, |
|
southMercatorY, |
|
oneOverMercatorHeight, |
|
longitudeOffset, |
|
latitudeOffset |
|
) { |
|
const hasVertexNormals = defined(octEncodedNormals); |
|
const north = rectangle.north; |
|
const south = rectangle.south; |
|
let east = rectangle.east; |
|
const west = rectangle.west; |
|
|
|
if (east < west) { |
|
east += Math.PI * 2; |
|
} |
|
|
|
const length = edgeVertices.length; |
|
for(let i = 0; i < length; ++i) { |
|
const index = edgeVertices[i]; |
|
const h = heights[index]; |
|
const uv = uvs[index]; |
|
|
|
cartographicScratch.longitude = lerp(west, east, uv.x) + longitudeOffset; |
|
cartographicScratch.latitude = lerp(south, north, uv.y) + latitudeOffset; |
|
cartographicScratch.height = h - skirtLength; |
|
|
|
const position = ellipsoid.cartographicToCartesian( |
|
cartographicScratch, |
|
cartesian3Scratch |
|
); |
|
|
|
if (hasVertexNormals) { |
|
const n = index * 2.0; |
|
toPack.x = octEncodedNormals[n]; |
|
toPack.y = octEncodedNormals[n + 1]; |
|
} |
|
|
|
let webMercatorT; |
|
if (encoding.hasWebMercatorT) { |
|
webMercatorT = ( |
|
WebMercatorProjection.geodeticLatitudeToMercatorAngle(cartographicScratch.latitude) |
|
- |
|
southMercatorY |
|
) * oneOverMercatorHeight; |
|
} |
|
|
|
let geodeticSurfaceNormal; |
|
if (encoding.hasGeodeticSurfaceNormals) { |
|
geodeticSurfaceNormal = ellipsoid.geodeticSurfaceNormal(position, geodeticSurfaceNormalScratch); |
|
} |
|
|
|
vertexBufferIndex = encoding.encode( |
|
vertexBuffer, |
|
vertexBufferIndex, |
|
position, |
|
uv, |
|
cartographicScratch.height, |
|
toPack, |
|
webMercatorT, |
|
geodeticSurfaceNormal |
|
); |
|
} |
|
} |
|
|
|
onmessage = function(e) { |
|
const data = e.data; |
|
const id = data.id; |
|
const parameters = data.parameters; |
|
const responseData = { |
|
id: id, |
|
result: undefined, |
|
error: undefined |
|
} |
|
|
|
return Promise.resolve( |
|
createVerticesFromQuantizedTerrainMesh(parameters) |
|
).then(result => { |
|
responseData.result = result |
|
}).catch(e => { |
|
if (e instanceof Error) { |
|
responseData.error = { |
|
name: e.name, |
|
message: e.message, |
|
stack: e.stack |
|
} |
|
} else { |
|
responseData.error = e; |
|
} |
|
}).finally(() => { |
|
postMessage(responseData); |
|
}) |
|
} |