mirror of
https://github.com/openglobus/openglobus.git
synced 2025-12-08 19:25:27 +00:00
git core.ignorecase set to false
This commit is contained in:
parent
32a68ee62e
commit
80e7a33ff9
170
src/og/Clock.js
Normal file
170
src/og/Clock.js
Normal file
@ -0,0 +1,170 @@
|
||||
/**
|
||||
* @module og/Clock
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { Events } from './Events.js';
|
||||
import * as jd from './astro/jd.js';
|
||||
|
||||
/**
|
||||
* Class represents application timer that stores custom current julian datetime, and time speed multiplier.
|
||||
* @class
|
||||
* @param {Object} [params]: - Clock parameters:
|
||||
* @param {number} [params.startDate=0.0] - Julian start date.
|
||||
* @param {number} [params.endDate=0.0] - Julian end date.
|
||||
* @param {number} [params.currentDate] - Julian current date. Default: current date.
|
||||
* @param {number} [params.multiplier=1.0] - Time speed multiolier.
|
||||
*/
|
||||
class Clock {
|
||||
|
||||
static get _staticCounter() {
|
||||
if (!this._counter && this._counter !== 0) {
|
||||
this._counter = 0;
|
||||
}
|
||||
return this._counter;
|
||||
}
|
||||
|
||||
static set _staticCounter(n) {
|
||||
this._counter = n;
|
||||
}
|
||||
|
||||
constructor(params) {
|
||||
params = params || {};
|
||||
|
||||
this._id = Clock._staticCounter++;
|
||||
|
||||
/**
|
||||
* Clock name.
|
||||
* @public
|
||||
* @type {string}
|
||||
*/
|
||||
this.name = params.name || "";
|
||||
|
||||
/**
|
||||
* Clock events.
|
||||
* @public
|
||||
* @type {Events}
|
||||
*/
|
||||
this.events = new Events([
|
||||
"tick",
|
||||
"end"
|
||||
]);
|
||||
|
||||
/**
|
||||
* Start julian date clock loop.
|
||||
* @public
|
||||
* @type {number}
|
||||
*/
|
||||
this.startDate = params.startDate || 0;
|
||||
|
||||
/**
|
||||
* End julian date clock loop.
|
||||
* @public
|
||||
* @type {number}
|
||||
*/
|
||||
this.endDate = params.endDate || 0;
|
||||
|
||||
var currentDate = params.currentDate || jd.DateToUTC(new Date());
|
||||
if (params.startDate && currentDate < params.startDate) {
|
||||
currentDate = params.startDate;
|
||||
}
|
||||
if (params.endDate && currentDate > params.endDate) {
|
||||
currentDate = params.endDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Current julian datetime.
|
||||
* @public
|
||||
* @type {number}
|
||||
*/
|
||||
this.currentDate = currentDate;
|
||||
|
||||
/**
|
||||
* Timer speed multiplier.
|
||||
* @public
|
||||
* @type {number}
|
||||
*/
|
||||
this.multiplier = params.multiplier !== undefined ? params.multiplier : 1.0;
|
||||
|
||||
/**
|
||||
* Animation frame delta time.
|
||||
* @public
|
||||
* @readonly
|
||||
* @type {number}
|
||||
*/
|
||||
this.deltaTicks = 0;
|
||||
|
||||
/**
|
||||
* Timer activity.
|
||||
* @public
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.active = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets current clock datetime.
|
||||
* @public
|
||||
* @param {Object} date - JavaScript Date object.
|
||||
*/
|
||||
setDate(date) {
|
||||
var d = jd.DateToUTC(date);
|
||||
if (this.startDate && d < this.startDate) {
|
||||
d = this.startDate;
|
||||
}
|
||||
if (this.endDate && d > this.endDate) {
|
||||
d = this.endDate;
|
||||
}
|
||||
this.currentDate = d;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns current application date.
|
||||
* @public
|
||||
* @returns {Date} - Current date.
|
||||
*/
|
||||
getDate() {
|
||||
return jd.UTCtoDate(this.currentDate);
|
||||
}
|
||||
|
||||
reset() {
|
||||
if (this.startDate) {
|
||||
this.currentDate = this.startDate;
|
||||
}
|
||||
}
|
||||
|
||||
_tick(dt) {
|
||||
this.deltaTicks = dt * this.multiplier;
|
||||
if (this.active) {
|
||||
var cd = jd.addMilliseconds(this.currentDate, this.deltaTicks);
|
||||
if (this.multiplier > 0) {
|
||||
if (this.endDate && cd > this.endDate) {
|
||||
this.currentDate = this.startDate;
|
||||
this.events.dispatch(this.events.end, this);
|
||||
} else {
|
||||
this.currentDate = cd;
|
||||
}
|
||||
} else {
|
||||
if (this.startDate && cd < this.startDate) {
|
||||
this.currentDate = this.endDate;
|
||||
this.events.dispatch(this.events.end, this);
|
||||
} else {
|
||||
this.currentDate = cd;
|
||||
}
|
||||
}
|
||||
this.events.dispatch(this.events.tick, this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @param {Clock} clock - Clock instance to compare.
|
||||
* @returns {boolean} - Returns true if a clock is the same instance.
|
||||
*/
|
||||
equal(clock) {
|
||||
return this._id === clock._id;
|
||||
}
|
||||
};
|
||||
|
||||
export { Clock };
|
||||
144
src/og/Frustum.js
Normal file
144
src/og/Frustum.js
Normal file
@ -0,0 +1,144 @@
|
||||
/**
|
||||
* @module og/Frustum
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Frustum object, part of the camera object.
|
||||
* @class
|
||||
*/
|
||||
class Frustum {
|
||||
constructor() {
|
||||
/**
|
||||
* Frustum planes.
|
||||
* @private
|
||||
* @type {Array.<Array.<number>>}
|
||||
*/
|
||||
this._f = new Array(6);
|
||||
for (var i = 0; i < 6; i++)
|
||||
this._f[i] = new Array(4);
|
||||
}
|
||||
/**
|
||||
* Normalize frustum plane.
|
||||
* @static
|
||||
* @param {Array.<number>} plane - Frustum plane coordinates.
|
||||
*/
|
||||
static planeNormalize(plane) {
|
||||
var t = Math.sqrt(plane[0] * plane[0] + plane[1] * plane[1] + plane[2] * plane[2]);
|
||||
plane[0] /= t;
|
||||
plane[1] /= t;
|
||||
plane[2] /= t;
|
||||
plane[3] /= t;
|
||||
}
|
||||
|
||||
/**
|
||||
* Camera's projection matrix values.
|
||||
* @public
|
||||
* @param {Array.<number>} clip - Projection matrix parameters.
|
||||
*/
|
||||
setFrustum(clip) {
|
||||
/* Right */
|
||||
this._f[0][0] = clip[3] - clip[0];
|
||||
this._f[0][1] = clip[7] - clip[4];
|
||||
this._f[0][2] = clip[11] - clip[8];
|
||||
this._f[0][3] = clip[15] - clip[12];
|
||||
Frustum.planeNormalize(this._f[0]);
|
||||
|
||||
/* Left */
|
||||
this._f[1][0] = clip[3] + clip[0];
|
||||
this._f[1][1] = clip[7] + clip[4];
|
||||
this._f[1][2] = clip[11] + clip[8];
|
||||
this._f[1][3] = clip[15] + clip[12];
|
||||
Frustum.planeNormalize(this._f[1]);
|
||||
|
||||
/* Bottom */
|
||||
this._f[2][0] = clip[3] + clip[1];
|
||||
this._f[2][1] = clip[7] + clip[5];
|
||||
this._f[2][2] = clip[11] + clip[9];
|
||||
this._f[2][3] = clip[15] + clip[13];
|
||||
Frustum.planeNormalize(this._f[2]);
|
||||
|
||||
/* Top */
|
||||
this._f[3][0] = clip[3] - clip[1];
|
||||
this._f[3][1] = clip[7] - clip[5];
|
||||
this._f[3][2] = clip[11] - clip[9];
|
||||
this._f[3][3] = clip[15] - clip[13];
|
||||
Frustum.planeNormalize(this._f[3]);
|
||||
|
||||
/* Backward */
|
||||
this._f[4][0] = clip[3] - clip[2];
|
||||
this._f[4][1] = clip[7] - clip[6];
|
||||
this._f[4][2] = clip[11] - clip[10];
|
||||
this._f[4][3] = clip[15] - clip[14];
|
||||
Frustum.planeNormalize(this._f[4]);
|
||||
|
||||
/* Forward */
|
||||
this._f[5][0] = clip[3] + clip[2];
|
||||
this._f[5][1] = clip[7] + clip[6];
|
||||
this._f[5][2] = clip[11] + clip[10];
|
||||
this._f[5][3] = clip[15] + clip[14];
|
||||
Frustum.planeNormalize(this._f[5]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if a point in the frustum.
|
||||
* @public
|
||||
* @param {og.math.Vec3} point - Cartesian point.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
containsPoint(point) {
|
||||
var d;
|
||||
for (var p = 0; p < 6; p++) {
|
||||
d = point.dotArr(this._f[p]) + this._f[p][3];
|
||||
if (d <= 0)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the frustum contains a bonding sphere.
|
||||
* @public
|
||||
* @param {og.bv.Sphere} sphere - Bounding sphere.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
containsSphere(sphere) {
|
||||
var r = -sphere.radius;
|
||||
if (sphere.center.dotArr(this._f[0]) + this._f[0][3] <= r) return false;
|
||||
if (sphere.center.dotArr(this._f[1]) + this._f[1][3] <= r) return false;
|
||||
if (sphere.center.dotArr(this._f[2]) + this._f[2][3] <= r) return false;
|
||||
if (sphere.center.dotArr(this._f[3]) + this._f[3][3] <= r) return false;
|
||||
if (sphere.center.dotArr(this._f[4]) + this._f[4][3] <= r) return false;
|
||||
if (sphere.center.dotArr(this._f[5]) + this._f[5][3] <= r) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the frustum contains a bounding box.
|
||||
* @public
|
||||
* @param {og.bv.Box} box - Bounding box.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
containsBox(box) {
|
||||
var result = true, cout, cin;
|
||||
|
||||
for (var i = 0; i < 6; i++) {
|
||||
cout = 0; cin = 0;
|
||||
for (var k = 0; k < 8 && (cin == 0 || cout == 0); k++) {
|
||||
var d = box.vertices[k].dotArr(this._f[i]) + this._f[i][3];
|
||||
if (d < 0)
|
||||
cout++;
|
||||
else
|
||||
cin++;
|
||||
}
|
||||
if (cin == 0)
|
||||
return false;
|
||||
else if (cout > 0)
|
||||
result = true;
|
||||
}
|
||||
return (result);
|
||||
}
|
||||
};
|
||||
|
||||
export { Frustum };
|
||||
48
src/og/Lock.js
Normal file
48
src/og/Lock.js
Normal file
@ -0,0 +1,48 @@
|
||||
/**
|
||||
* @module og/Lock
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
class Lock {
|
||||
|
||||
constructor() {
|
||||
this._lock = 0;
|
||||
}
|
||||
|
||||
lock(key) {
|
||||
this._lock |= (1 << key._id);
|
||||
}
|
||||
|
||||
free(key) {
|
||||
this._lock &= ~(1 << key._id);
|
||||
}
|
||||
|
||||
isFree() {
|
||||
return this._lock === 0;
|
||||
}
|
||||
|
||||
isLocked() {
|
||||
return this._lock !== 0;
|
||||
}
|
||||
};
|
||||
|
||||
class Key {
|
||||
|
||||
static get _staticCounter() {
|
||||
if (!this._counter && this._counter !== 0) {
|
||||
this._counter = 0;
|
||||
}
|
||||
return this._counter;
|
||||
}
|
||||
|
||||
static set _staticCounter(n) {
|
||||
this._counter = n;
|
||||
}
|
||||
|
||||
constructor() {
|
||||
this._id = Key._staticCounter++;
|
||||
}
|
||||
};
|
||||
|
||||
export { Lock, Key };
|
||||
175
src/og/LonLat.js
Normal file
175
src/og/LonLat.js
Normal file
@ -0,0 +1,175 @@
|
||||
/**
|
||||
* @module og/LonLat
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as mercator from './mercator.js';
|
||||
|
||||
/**
|
||||
* Represents a geographical point with a certain latitude, longitude and height.
|
||||
* @class
|
||||
* @param {number} [lon] - Longitude.
|
||||
* @param {number} [lat] - Latitude.
|
||||
* @param {number} [height] - Height over the surface.
|
||||
*/
|
||||
const LonLat = function (lon, lat, height) {
|
||||
|
||||
/**
|
||||
* Longitude.
|
||||
* @public
|
||||
* @type {number}
|
||||
*/
|
||||
this.lon = lon || 0;
|
||||
|
||||
/**
|
||||
* Latitude.
|
||||
* @public
|
||||
* @type {number}
|
||||
*/
|
||||
this.lat = lat || 0;
|
||||
|
||||
/**
|
||||
* Height.
|
||||
* @public
|
||||
* @type {number}
|
||||
*/
|
||||
this.height = height || 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates coordinates array.
|
||||
* @static
|
||||
* @param{Array.<Array<number,number,number>>} arr - Coordinates array data.
|
||||
* @return{Array.<og.LonLat>} the same coordinates array but each element is LonLat instance.
|
||||
*/
|
||||
LonLat.join = function (arr) {
|
||||
var res = [];
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
var ai = arr[i];
|
||||
res[i] = new LonLat(ai[0], ai[1], ai[2]);
|
||||
}
|
||||
return res;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates an object by coordinate array.
|
||||
* @static
|
||||
* @param {Array.<number,number,number>} arr - Coordiante array, where first is longitude, second is latitude and third is a height.
|
||||
* @returns {og.LonLat}
|
||||
*/
|
||||
LonLat.createFromArray = function (arr) {
|
||||
return new LonLat(arr[0], arr[1], arr[2]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts degrees to mercator coordinates.
|
||||
* @static
|
||||
* @param {number} lon - Degrees longitude.
|
||||
* @param {number} lat - Degrees latitude.
|
||||
* @param {number} [height] - Height.
|
||||
* @returns {og.LonLat}
|
||||
*/
|
||||
LonLat.forwardMercator = function (lon, lat, height) {
|
||||
var x = lon * mercator.POLE / 180;
|
||||
var y = Math.log(Math.tan((90 + lat) * Math.PI / 360)) / Math.PI * mercator.POLE;
|
||||
return new LonLat(x, y, height);
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts mercator to degrees coordinates.
|
||||
* @static
|
||||
* @param {number} x - Mercator longitude.
|
||||
* @param {number} y - Mercator latitude.
|
||||
* @param {number} [height] - Height.
|
||||
* @returns {og.LonLat}
|
||||
*/
|
||||
LonLat.inverseMercator = function (x, y, height) {
|
||||
var lon = 180 * x / mercator.POLE;
|
||||
var lat = 180 / Math.PI * (2 * Math.atan(Math.exp((y / mercator.POLE) * Math.PI)) - Math.PI / 2);
|
||||
return new LonLat(lon, lat, height);
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets coordinates.
|
||||
* @public
|
||||
* @param {number} [lon] - Longitude.
|
||||
* @param {number} [lat] - Latitude.
|
||||
* @param {number} [height] - Height.
|
||||
* @returns {og.LonLat}
|
||||
*/
|
||||
LonLat.prototype.set = function (lon, lat, height) {
|
||||
this.lon = lon || 0;
|
||||
this.lat = lat || 0;
|
||||
this.height = height || 0;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Copy coordinates.
|
||||
* @public
|
||||
* @param {og.LonLat} [lonLat] - Coordinates to copy.
|
||||
* @returns {og.LonLat}
|
||||
*/
|
||||
LonLat.prototype.copy = function (lonLat) {
|
||||
this.lon = lonLat.lon;
|
||||
this.lat = lonLat.lat;
|
||||
this.height = lonLat.height;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Clone the coordiante.
|
||||
* @public
|
||||
* @returns {og.LonLat}
|
||||
*/
|
||||
LonLat.prototype.clone = function () {
|
||||
return new LonLat(this.lon, this.lat, this.height);
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts to mercator coordinates.
|
||||
* @public
|
||||
* @returns {og.LonLat}
|
||||
*/
|
||||
LonLat.prototype.forwardMercator = function () {
|
||||
return LonLat.forwardMercator(this.lon, this.lat, this.height);
|
||||
};
|
||||
|
||||
LonLat.prototype.forwardMercatorEPS01 = function () {
|
||||
var lat = this.lat;
|
||||
if (lat > 89.9) {
|
||||
lat = 89.9;
|
||||
} else if (lat < -89.9) {
|
||||
lat = -89.9;
|
||||
}
|
||||
return new LonLat(
|
||||
this.lon * mercator.POLE / 180,
|
||||
Math.log(Math.tan((90 + lat) * Math.PI / 360)) / Math.PI * mercator.POLE);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Converts from mercator coordinates.
|
||||
* @public
|
||||
* @returns {og.LonLat}
|
||||
*/
|
||||
LonLat.prototype.inverseMercator = function () {
|
||||
return LonLat.inverseMercator(this.lon, this.lat, this.height);
|
||||
};
|
||||
|
||||
/**
|
||||
* Compares coordinates.
|
||||
* @public
|
||||
* @param {og.LonLat} b - Coordinate to compare with.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
LonLat.prototype.equal = function (b) {
|
||||
if (b.height) {
|
||||
return this.lon == b.lon && this.lat == b.lat && this.height == b.height;
|
||||
} else {
|
||||
return this.lon == b.lon && this.lat == b.lat;
|
||||
}
|
||||
};
|
||||
|
||||
export { LonLat };
|
||||
69
src/og/QueueArray.js
Normal file
69
src/og/QueueArray.js
Normal file
@ -0,0 +1,69 @@
|
||||
/**
|
||||
* @module og/QueueArray
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
class QueueArray {
|
||||
constructor(size) {
|
||||
this._size = size || 2048;
|
||||
this._array = new Array(this._size);
|
||||
this._popIndex = parseInt(this._size * 0.5);
|
||||
this._shiftIndex = this._popIndex;
|
||||
this.length = 0;
|
||||
}
|
||||
|
||||
clear() {
|
||||
this._array.length = 0;
|
||||
this._array = new Array(this._size);
|
||||
this._popIndex = parseInt(this._size * 0.5);
|
||||
this._shiftIndex = this._popIndex;
|
||||
this.length = 0;
|
||||
}
|
||||
|
||||
push(data) {
|
||||
this.length++;
|
||||
this._array[this._popIndex++] = data;
|
||||
}
|
||||
|
||||
pop() {
|
||||
if (this.length) {
|
||||
this.length--;
|
||||
var res = this._array[--this._popIndex]
|
||||
this._array[this._popIndex] = null;
|
||||
if (!this._array[this._popIndex - 1]) {
|
||||
this._popIndex = parseInt(this._size * 0.5);
|
||||
this._shiftIndex = this._popIndex;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
unshift(data) {
|
||||
this.length++;
|
||||
this._array[--this._shiftIndex] = data;
|
||||
}
|
||||
|
||||
shift() {
|
||||
if (this.length) {
|
||||
this.length--;
|
||||
var res = this._array[this._shiftIndex];
|
||||
this._array[this._shiftIndex++] = null;
|
||||
if (!this._array[this._shiftIndex]) {
|
||||
this._popIndex = parseInt(this._size * 0.5);
|
||||
this._shiftIndex = this._popIndex;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
each(callback) {
|
||||
for (var i = this._shiftIndex; i < this._popIndex; i++) {
|
||||
callback(this._array[i]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export { QueueArray };
|
||||
106
src/og/Rectangle.js
Normal file
106
src/og/Rectangle.js
Normal file
@ -0,0 +1,106 @@
|
||||
/**
|
||||
* @module og/Rectangle
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* 2D Rectangle class.
|
||||
* @class
|
||||
* @param {number} [left] - Left coordinate. 0 - default.
|
||||
* @param {number} [top] - Top coordinate. 0 - default.
|
||||
* @param {number} [right] - Right coordinate. 0 - default.
|
||||
* @param {number} [bottom] - Bottom coordinate. 0 - default.
|
||||
*/
|
||||
class Rectangle {
|
||||
constructor(left, top, right, bottom) {
|
||||
|
||||
/**
|
||||
* Left coordinate.
|
||||
* @public
|
||||
* @type {number}
|
||||
*/
|
||||
this.left = left || 0;
|
||||
|
||||
/**
|
||||
* Right coordinate.
|
||||
* @public
|
||||
* @type {number}
|
||||
*/
|
||||
this.right = right || 0;
|
||||
|
||||
/**
|
||||
* Top coordinate.
|
||||
* @public
|
||||
* @type {number}
|
||||
*/
|
||||
this.top = top || 0;
|
||||
|
||||
/**
|
||||
* Top coordinate.
|
||||
* @public
|
||||
* @type {number}
|
||||
*/
|
||||
this.bottom = bottom || 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone rectangle object.
|
||||
* @public
|
||||
* @returns {og.Rectangle}
|
||||
*/
|
||||
clone() {
|
||||
return new Rectangle(this.left, this.top, this.right, this.bottom);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns rectangle width.
|
||||
* @public
|
||||
* @type {number}
|
||||
*/
|
||||
getWidth() {
|
||||
return Math.abs(this.right - this.left);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns rectangle height.
|
||||
* @public
|
||||
* @type {number}
|
||||
*/
|
||||
getHeight() {
|
||||
return Math.abs(this.bottom - this.top);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns rectangle area.
|
||||
* @public
|
||||
* @type {number}
|
||||
*/
|
||||
getSquare() {
|
||||
return this.getHeight() * this.getWidth();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns rectangle diagonal size.
|
||||
* @public
|
||||
* @type {number}
|
||||
*/
|
||||
getDiagonal() {
|
||||
var w = this.getWidth(),
|
||||
h = this.getHeight();
|
||||
return Math.sqrt(h * h + w * w);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if rectangle fits their size in width and height.
|
||||
* @public
|
||||
* @param {number} width - Width.
|
||||
* @param {number} height - Height.
|
||||
* @type {boolean}
|
||||
*/
|
||||
fit(width, height) {
|
||||
return (this.getWidth() == width && this.getHeight() == height);
|
||||
}
|
||||
};
|
||||
|
||||
export { Rectangle };
|
||||
50
src/og/Stack.js
Normal file
50
src/og/Stack.js
Normal file
@ -0,0 +1,50 @@
|
||||
/**
|
||||
* @module og/Stack
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
class Node {
|
||||
constructor() {
|
||||
this.next = null;
|
||||
this.prev = null;
|
||||
this.data = null;
|
||||
}
|
||||
};
|
||||
|
||||
class Stack {
|
||||
constructor(size = 256) {
|
||||
|
||||
this._current = new Node();
|
||||
this._head = this._current;
|
||||
|
||||
for (var i = 0; i < size; i++) {
|
||||
var n = new Node();
|
||||
n.prev = this._current;
|
||||
this._current.next = n;
|
||||
this._current = n;
|
||||
}
|
||||
this._current = this._head;
|
||||
}
|
||||
|
||||
current() {
|
||||
return this._current;
|
||||
}
|
||||
|
||||
push(data) {
|
||||
this._current = this._current.next;
|
||||
this._current.data = data;
|
||||
};
|
||||
|
||||
pop(data) {
|
||||
this._current = this._current.prev;
|
||||
return this._current.next.data;
|
||||
}
|
||||
|
||||
popPrev(data) {
|
||||
this._current = this._current.prev;
|
||||
return this._current.data;
|
||||
}
|
||||
};
|
||||
|
||||
export { Stack };
|
||||
52
src/og/bv/Box.js
Normal file
52
src/og/bv/Box.js
Normal file
@ -0,0 +1,52 @@
|
||||
/**
|
||||
* @module og/bv/Box
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { Vec3 } from '../math/Vec3.js';
|
||||
|
||||
/**
|
||||
* Bounding box class.
|
||||
* @class
|
||||
*/
|
||||
class Box {
|
||||
constructor() {
|
||||
/**
|
||||
* Vertices array.
|
||||
* @public
|
||||
* @type{Array.<og.math.Vector3>}
|
||||
*/
|
||||
this.vertices = [new Vec3(), new Vec3(), new Vec3(), new Vec3(), new Vec3(), new Vec3(), new Vec3(), new Vec3()];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets bounding box coordinates by the bounds array.
|
||||
* @param {Array.<number>} bounds - Bounds is an array where [minX, maxX, minY, maxY, minZ, maxZ]
|
||||
*/
|
||||
setFromBounds(bounds) {
|
||||
var xmin = bounds[0], xmax = bounds[1],
|
||||
ymin = bounds[2], ymax = bounds[3],
|
||||
zmin = bounds[4], zmax = bounds[5];
|
||||
|
||||
this.vertices[0].set(xmin, ymin, zmin);
|
||||
this.vertices[1].set(xmax, ymin, zmin);
|
||||
this.vertices[2].set(xmax, ymin, zmax);
|
||||
this.vertices[3].set(xmin, ymin, zmax);
|
||||
this.vertices[4].set(xmin, ymax, zmin);
|
||||
this.vertices[5].set(xmax, ymax, zmin);
|
||||
this.vertices[6].set(xmax, ymax, zmax);
|
||||
this.vertices[7].set(xmin, ymax, zmax);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets bounding box coordiantes by ellipsoid geodetic extend.
|
||||
* @param {og.Ellipsoid} ellipsoid - Ellipsoid.
|
||||
* @param {og.Extent} extent - Geodetic extent.
|
||||
*/
|
||||
setFromExtent(ellipsoid, extent) {
|
||||
this.setFromBounds(extent.getCartesianBounds(ellipsoid));
|
||||
}
|
||||
};
|
||||
|
||||
export { Box };
|
||||
52
src/og/bv/Sphere.js
Normal file
52
src/og/bv/Sphere.js
Normal file
@ -0,0 +1,52 @@
|
||||
/**
|
||||
* @module og/bv/Sphere
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { Vec3 } from '../math/Vec3.js';
|
||||
|
||||
/**
|
||||
* Bounding sphere class.
|
||||
* @class
|
||||
* @param {Number} [radius] - Bounding sphere radius.
|
||||
* @param {og.math.Vector3} [center] - Bounding sphere coordiantes.
|
||||
*/
|
||||
class Sphere {
|
||||
constructor(radius, center) {
|
||||
|
||||
/**
|
||||
* Sphere radius.
|
||||
* @public
|
||||
* @type {Number}
|
||||
*/
|
||||
this.radius = radius || 0;
|
||||
|
||||
/**
|
||||
* Sphere coordiantes.
|
||||
* @public
|
||||
* @type {og.math.Vector3}
|
||||
*/
|
||||
this.center = center ? center.clone() : new Vec3();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets bounding sphere coordinates by the bounds array.
|
||||
* @param {Array.<number>} bounds - Bounds is an array where [minX, maxX, minY, maxY, minZ, maxZ]
|
||||
*/
|
||||
setFromBounds(bounds) {
|
||||
this.center.set(bounds[0] + (bounds[1] - bounds[0]) / 2, bounds[2] + (bounds[3] - bounds[2]) / 2, bounds[4] + (bounds[5] - bounds[4]) / 2);
|
||||
this.radius = this.center.distance(new Vec3(bounds[0], bounds[2], bounds[4]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets bounding sphere coordiantes by ellipsoid geodetic extend.
|
||||
* @param {og.Ellipsoid} ellipsoid - Ellipsoid.
|
||||
* @param {og.Extent} extent - Geodetic extent.
|
||||
*/
|
||||
setFromExtent(ellipsoid, extent) {
|
||||
this.setFromBounds(extent.getCartesianBounds(ellipsoid));
|
||||
}
|
||||
};
|
||||
|
||||
export { Sphere };
|
||||
649
src/og/camera/Camera.js
Normal file
649
src/og/camera/Camera.js
Normal file
@ -0,0 +1,649 @@
|
||||
/**
|
||||
* @module og/camera/Camera
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as math from '../math.js';
|
||||
import { Events } from '../Events.js';
|
||||
import { Frustum } from '../Frustum.js';
|
||||
import { Vec2 } from '../math/Vec2.js';
|
||||
import { Vec3 } from '../math/Vec3.js';
|
||||
import { Vec4 } from '../math/Vec4.js';
|
||||
import { Mat3 } from '../math/Mat3.js';
|
||||
import { Mat4 } from '../math/Mat4.js';
|
||||
|
||||
/**
|
||||
* Camera class.
|
||||
* @class
|
||||
* @param {og.Renderer} [renderer] - Renderer uses the camera instance.
|
||||
* @param {Object} [options] - Camera options:
|
||||
* @param {Object} [options.name] - Camera name.
|
||||
* @param {number} [options.viewAngle=30] - Camera angle of view. Default is 30.0
|
||||
* @param {number} [options.near=1] - Camera near plane distance. Default is 1.0
|
||||
* @param {number} [options.far=og.math.MAX] - Camera far plane distance. Deafult is og.math.MAX
|
||||
* @param {og.math.Vector3} [options.eye=[0,0,0]] - Camera eye position. Default (0,0,0)
|
||||
* @param {og.math.Vector3} [options.look=[0,0,0]] - Camera look position. Default (0,0,0)
|
||||
* @param {og.math.Vector3} [options.up=[0,1,0]] - Camera eye position. Default (0,1,0)
|
||||
*
|
||||
* @fires og.Camera#viewchange
|
||||
*/
|
||||
class Camera {
|
||||
constructor(renderer, options) {
|
||||
|
||||
/**
|
||||
* Assigned renderer.
|
||||
* @public
|
||||
* @type {og.Renderer}
|
||||
*/
|
||||
this.renderer = null;
|
||||
|
||||
/**
|
||||
* Camera events handler.
|
||||
* @public
|
||||
* @type {og.Events}
|
||||
*/
|
||||
this.events = new Events(EVENT_NAMES);
|
||||
|
||||
/**
|
||||
* Camera position.
|
||||
* @public
|
||||
* @type {og.math.Vector3}
|
||||
*/
|
||||
this.eye = new Vec3();
|
||||
|
||||
/**
|
||||
* Camera frustum.
|
||||
* @public
|
||||
* @type {og.Frustum}
|
||||
*/
|
||||
this.frustum = new Frustum();
|
||||
|
||||
/**
|
||||
* Aspect ratio.
|
||||
* @protected
|
||||
* @type {Number}
|
||||
*/
|
||||
this._aspect = options.aspect || 0;
|
||||
|
||||
/**
|
||||
* Camera near distance.
|
||||
* @protected
|
||||
* @type {Number}
|
||||
*/
|
||||
this._nearDist = 0;
|
||||
|
||||
/**
|
||||
* Camera far distance.
|
||||
* @protected
|
||||
* @type {Number}
|
||||
*/
|
||||
this._farDist = 0;
|
||||
|
||||
/**
|
||||
* Camera view angle in degrees.
|
||||
* @protected
|
||||
* @type {Number}
|
||||
*/
|
||||
this._viewAngle = 0;
|
||||
|
||||
/**
|
||||
* Camera normal matrix.
|
||||
* @protected
|
||||
* @type {og.math.Matrix3}
|
||||
*/
|
||||
this._normalMatrix = new Mat3();
|
||||
|
||||
/**
|
||||
* Camera projection matrix.
|
||||
* @protected
|
||||
* @type {og.math.Matrix4}
|
||||
*/
|
||||
this._projectionMatrix = new Mat4();
|
||||
|
||||
/**
|
||||
* Camera view matrix.
|
||||
* @protected
|
||||
* @type {og.math.Matrix4}
|
||||
*/
|
||||
this._viewMatrix = new Mat4();
|
||||
|
||||
/**
|
||||
* Product of projection and view matrices.
|
||||
* @protected
|
||||
* @type {og.math.Matrix4}
|
||||
*/
|
||||
this._projectionViewMatrix = new Mat4();
|
||||
|
||||
/**
|
||||
* Inverse projectionView Matrix.
|
||||
* @protected
|
||||
* @type {og.math.Matrix4}
|
||||
*/
|
||||
this._inverseProjectionViewMatrix = new Mat4();
|
||||
|
||||
/**
|
||||
* Camera projection matrix for small near and far distances.
|
||||
* @protected
|
||||
* @type {og.math.Matrix4}
|
||||
*/
|
||||
this._projectionMatrixPrecise = new Mat4();
|
||||
|
||||
/**
|
||||
* Camera right vector.
|
||||
* @protected
|
||||
* @type {og.math.Vector3}
|
||||
*/
|
||||
this._u = new Vec3(0, 1, 0); //up x n
|
||||
|
||||
/**
|
||||
* Camera up vector.
|
||||
* @protected
|
||||
* @type {og.math.Vector3}
|
||||
*/
|
||||
this._v = new Vec3(1, 0, 0); //n x u - UP
|
||||
|
||||
this.slope = 0;
|
||||
|
||||
/**
|
||||
* Camera forward vector.
|
||||
* @protected
|
||||
* @type {og.math.Vector3}
|
||||
*/
|
||||
this._n = new Vec3(0, 0, 1); //eye - look - FORWARD
|
||||
|
||||
this._pu = this._u;
|
||||
this._pv = this._v;
|
||||
this._pn = this._n;
|
||||
this._peye = this.eye;
|
||||
this._moved = false;
|
||||
|
||||
this._tanViewAngle_hrad = 0;
|
||||
this._tanViewAngle_hradOneByHeight = 0;
|
||||
|
||||
this.renderer = renderer;
|
||||
|
||||
renderer && this._initialize(options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates model view matrix.
|
||||
* @protected
|
||||
*/
|
||||
_setViewMatrix() {
|
||||
var u = this._u,
|
||||
v = this._v,
|
||||
n = this._n,
|
||||
eye = this.eye;
|
||||
|
||||
this._viewMatrix.set([u.x, v.x, n.x, 0,
|
||||
u.y, v.y, n.y, 0,
|
||||
u.z, v.z, n.z, 0,
|
||||
-eye.dot(u), -eye.dot(v), -eye.dot(n), 1.0]);
|
||||
}
|
||||
|
||||
checkMoveEnd() {
|
||||
var u = this._u,
|
||||
v = this._v,
|
||||
n = this._n,
|
||||
eye = this.eye;
|
||||
|
||||
if (this.events.moveend.handlers.length) {
|
||||
if (this._peye.equal(eye) &&
|
||||
this._pu.equal(u) &&
|
||||
this._pv.equal(v) &&
|
||||
this._pn.equal(n)) {
|
||||
if (this._moved) {
|
||||
this.events.dispatch(this.events.moveend, this);
|
||||
}
|
||||
this._moved = false;
|
||||
} else {
|
||||
this._moved = true;
|
||||
}
|
||||
}
|
||||
|
||||
this._pu = u;
|
||||
this._pv = v;
|
||||
this._pn = n;
|
||||
this._peye = eye;
|
||||
}
|
||||
|
||||
/**
|
||||
* Camera initialization.
|
||||
* @public
|
||||
* @param {og.Renderer} renderer - OpenGlobus renderer object.
|
||||
* @param {Object} [options] - Camera options:
|
||||
* @param {number} [options.viewAngle] - Camera angle of view. Default is 30.0
|
||||
* @param {number} [options.near] - Camera near plane distance. Default is 1.0
|
||||
* @param {number} [options.far] - Camera far plane distance. Deafult is og.math.MAX
|
||||
* @param {og.math.Vector3} [options.eye] - Camera eye position. Default (0,0,0)
|
||||
* @param {og.math.Vector3} [options.look] - Camera look position. Default (0,0,0)
|
||||
* @param {og.math.Vector3} [options.up] - Camera eye position. Default (0,1,0)
|
||||
*/
|
||||
_initialize(options) {
|
||||
|
||||
this.setProjectionMatrix(
|
||||
options.viewAngle || defaultOptions.viewAngle,
|
||||
this._aspect || this.renderer.handler.getClientAspect(),
|
||||
options.near || defaultOptions.near,
|
||||
options.far || defaultOptions.far);
|
||||
|
||||
this.set(
|
||||
options.eye || defaultOptions.eye.clone(),
|
||||
options.look || defaultOptions.look.clone(),
|
||||
options.up || defaultOptions.up.clone());
|
||||
}
|
||||
|
||||
getUp() {
|
||||
return this._v;
|
||||
}
|
||||
|
||||
getDown() {
|
||||
return this._v.negateTo();
|
||||
}
|
||||
|
||||
getRight() {
|
||||
return this._u;
|
||||
}
|
||||
|
||||
getLeft() {
|
||||
return this._u.negateTo();
|
||||
}
|
||||
|
||||
getForward() {
|
||||
return this._n;
|
||||
}
|
||||
|
||||
getBackward() {
|
||||
return this._n.negateTo();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone camera instance to another one.
|
||||
* @public
|
||||
* @virtual
|
||||
* @returns {og.Camera} - Cloned camera instance.
|
||||
*/
|
||||
clone() {
|
||||
var newcam = new Camera();
|
||||
newcam.eye.copy(cam.eye);
|
||||
newcam._u.copy(cam._u);
|
||||
newcam._v.copy(cam._v);
|
||||
newcam._n.copy(cam._n);
|
||||
newcam.renderer = cam.renderer;
|
||||
newcam._projectionMatrix.copy(cam._projectionMatrix);
|
||||
newcam._viewMatrix.copy(cam._viewMatrix);
|
||||
newcam._projectionViewMatrix.copy(cam._projectionViewMatrix);
|
||||
newcam._inverseProjectionViewMatrix.copy(cam._inverseProjectionViewMatrix);
|
||||
newcam.frustum.setFrustum(newcam._projectionViewMatrix);
|
||||
return newcam;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates camera view space.
|
||||
* @public
|
||||
* @virtual
|
||||
*/
|
||||
update() {
|
||||
|
||||
this._setViewMatrix();
|
||||
|
||||
this._projectionViewMatrix = this._projectionMatrix.mul(this._viewMatrix);
|
||||
this.frustum.setFrustum(this._projectionViewMatrix._m);
|
||||
this._inverseProjectionViewMatrix = this._projectionViewMatrix.inverseTo();
|
||||
this._normalMatrix = this._viewMatrix.toMatrix3();//this._viewMatrix.toInverseMatrix3().transposeTo();
|
||||
|
||||
this.events.dispatch(this.events.viewchange, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh camera matrices.
|
||||
* @public
|
||||
*/
|
||||
refresh() {
|
||||
this.setProjectionMatrix(this._viewAngle, this._aspect, this._nearDist, this._farDist);
|
||||
this.update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets aspect ratio.
|
||||
* @public
|
||||
* @param {Number} aspect - Camera aspect ratio.
|
||||
*/
|
||||
setAspectRatio(aspect) {
|
||||
this._aspect = aspect;
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns aspect ratio.
|
||||
* @public
|
||||
* @returns {number} - Aspect ratio.
|
||||
*/
|
||||
getAspectRatio() {
|
||||
return this._aspect;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets far camera distance.
|
||||
* @public
|
||||
* @param {number} distance - Far distance.
|
||||
*/
|
||||
setFar(distance) {
|
||||
this._farDist = distance;
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets far distance.
|
||||
* @public
|
||||
* @returns {number} - Far plane distance.
|
||||
*/
|
||||
getFar() {
|
||||
return this._farDist;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets camera's near distance.
|
||||
* @public
|
||||
* @param {number} distance - Near distance.
|
||||
*/
|
||||
setNear(distance) {
|
||||
this._nearDist = distance;
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets near distance.
|
||||
* @public
|
||||
* @returns {number} - Near plane distance.
|
||||
*/
|
||||
getNear() {
|
||||
return this._nearDist;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up camera projection matrix.
|
||||
* @public
|
||||
* @param {nnumber} angle - Camera's view angle.
|
||||
* @param {number} aspect - Screen aspect ration.
|
||||
* @param {number} near - Near camera distance.
|
||||
* @param {number} far - Far camera distance.
|
||||
*/
|
||||
setProjectionMatrix(angle, aspect, near, far) {
|
||||
this._viewAngle = angle;
|
||||
this._aspect = aspect;
|
||||
this._nearDist = near;
|
||||
this._farDist = far;
|
||||
|
||||
this._tanViewAngle_hrad = Math.tan(angle * math.RADIANS_HALF);
|
||||
this._tanViewAngle_hradOneByHeight = this._tanViewAngle_hrad * this.renderer.handler._oneByHeight;
|
||||
|
||||
var c = this.renderer.handler.canvas;
|
||||
this._projSizeConst = Math.min(c.clientWidth, c.clientHeight) / (this._viewAngle * math.RADIANS);
|
||||
|
||||
this._projectionMatrix.setPerspective(angle, aspect, near, far);
|
||||
this._projectionMatrixPrecise.setPerspective(angle, aspect, 0.1, 10);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets camera view angle in degrees.
|
||||
* @public
|
||||
* @param {number} angle - View angle.
|
||||
*/
|
||||
setViewAngle(angle) {
|
||||
this._viewAngle = angle;
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets camera to eye position.
|
||||
* @public
|
||||
* @param {og.math.Vector3} eye - Camera position.
|
||||
* @param {og.math.Vector3} look - Look point.
|
||||
* @param {og.math.Vector3} up - Camera up vector.
|
||||
* @returns {og.Camera} - This camera.
|
||||
*/
|
||||
set(eye, look, up) {
|
||||
this.eye.x = eye.x;
|
||||
this.eye.y = eye.y;
|
||||
this.eye.z = eye.z;
|
||||
look = look || this._n;
|
||||
up = up || this._v;
|
||||
this._n.x = eye.x - look.x;
|
||||
this._n.y = eye.y - look.y;
|
||||
this._n.z = eye.z - look.z;
|
||||
this._u.copy(up.cross(this._n));
|
||||
this._n.normalize();
|
||||
this._u.normalize();
|
||||
this._v.copy(this._n.cross(this._u));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets camera look point.
|
||||
* @public
|
||||
* @param {og.math.Vector3} look - Look point.
|
||||
* @param {og.math.Vector3} [up] - Camera up vector otherwise camera current up vector(this._v)
|
||||
*/
|
||||
look(look, up) {
|
||||
this._n.set(this.eye.x - look.x, this.eye.y - look.y, this.eye.z - look.z);
|
||||
this._u.copy((up || this._v).cross(this._n));
|
||||
this._n.normalize();
|
||||
this._u.normalize();
|
||||
this._v.copy(this._n.cross(this._u));
|
||||
}
|
||||
|
||||
/**
|
||||
* Slides camera to vector d - (du, dv, dn).
|
||||
* @public
|
||||
* @param {number} du - delta X.
|
||||
* @param {number} dv - delta Y.
|
||||
* @param {number} dn - delta Z.
|
||||
*/
|
||||
slide(du, dv, dn) {
|
||||
this.eye.x += du * this._u.x + dv * this._v.x + dn * this._n.x;
|
||||
this.eye.y += du * this._u.y + dv * this._v.y + dn * this._n.y;
|
||||
this.eye.z += du * this._u.z + dv * this._v.z + dn * this._n.z;
|
||||
}
|
||||
|
||||
/**
|
||||
* Roll the camera to the angle in degrees.
|
||||
* @public
|
||||
* @param {number} angle - Delta roll angle in degrees.
|
||||
*/
|
||||
roll(angle) {
|
||||
var cs = Math.cos(math.RADIANS * angle);
|
||||
var sn = Math.sin(math.RADIANS * angle);
|
||||
var t = this._u.clone();
|
||||
this._u.set(cs * t.x - sn * this._v.x, cs * t.y - sn * this._v.y, cs * t.z - sn * this._v.z);
|
||||
this._v.set(sn * t.x + cs * this._v.x, sn * t.y + cs * this._v.y, sn * t.z + cs * this._v.z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pitch the camera to the angle in degrees.
|
||||
* @public
|
||||
* @param {number} angle - Delta pitch angle in degrees.
|
||||
*/
|
||||
pitch(angle) {
|
||||
var cs = Math.cos(math.RADIANS * angle);
|
||||
var sn = Math.sin(math.RADIANS * angle);
|
||||
var t = this._n.clone();
|
||||
this._n.set(cs * t.x - sn * this._v.x, cs * t.y - sn * this._v.y, cs * t.z - sn * this._v.z);
|
||||
this._v.set(sn * t.x + cs * this._v.x, sn * t.y + cs * this._v.y, sn * t.z + cs * this._v.z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Yaw the camera to the angle in degrees.
|
||||
* @public
|
||||
* @param {number} angle - Delta yaw angle in degrees.
|
||||
*/
|
||||
yaw(angle) {
|
||||
var cs = Math.cos(math.RADIANS * angle);
|
||||
var sn = Math.sin(math.RADIANS * angle);
|
||||
var t = this._u.clone();
|
||||
this._u.set(cs * t.x - sn * this._n.x, cs * t.y - sn * this._n.y, cs * t.z - sn * this._n.z);
|
||||
this._n.set(sn * t.x + cs * this._n.x, sn * t.y + cs * this._n.y, sn * t.z + cs * this._n.z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns normal vector direction to to the unprojected screen point from camera eye.
|
||||
* @public
|
||||
* @param {number} x - Scren X coordinate.
|
||||
* @param {number} y - Scren Y coordinate.
|
||||
* @returns {og.math.Vector3} - Direction vector.
|
||||
*/
|
||||
unproject(x, y) {
|
||||
var c = this.renderer.handler.canvas,
|
||||
w = c.width * 0.5,
|
||||
h = c.height * 0.5;
|
||||
|
||||
var px = (x - w) / w,
|
||||
py = -(y - h) / h;
|
||||
|
||||
var world1 = this._inverseProjectionViewMatrix.mulVec4(new Vec4(px, py, -1, 1)).affinity(),
|
||||
world2 = this._inverseProjectionViewMatrix.mulVec4(new Vec4(px, py, 0, 1)).affinity();
|
||||
|
||||
return world2.subA(world1).toVec3().normalize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets projected 3d point to the 2d screen coordiantes.
|
||||
* @public
|
||||
* @param {og.math.Vector3} v - Cartesian 3d coordiantes.
|
||||
* @returns {og.math.Vector2} - Screen point coordinates.
|
||||
*/
|
||||
project(v) {
|
||||
var r = this._projectionViewMatrix.mulVec4(v.toVector4()),
|
||||
c = this.renderer.handler.canvas;
|
||||
return new Vec2((1 + r.x / r.w) * c.width * 0.5, (1 - r.y / r.w) * c.height * 0.5);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotates camera around center point.
|
||||
* @public
|
||||
* @param {number} angle - Rotation angle in radians.
|
||||
* @param {boolaen} isArc - If true camera up vector gets from current up vector every frame,
|
||||
* otherwise up is always input parameter.
|
||||
* @param {og.math.Vector3} center - Point that the camera rotates around.
|
||||
* @param {og.math.Vecto3} [up] - Camera up vector.
|
||||
*/
|
||||
rotateAround(angle, isArc, center, up) {
|
||||
center = center || Vec3.ZERO;
|
||||
up = up || Vec3.UP;
|
||||
|
||||
var rot = new Mat4().setRotation(isArc ? this._v : up, angle);
|
||||
var tr = new Mat4().setIdentity().translate(center);
|
||||
var ntr = new Mat4().setIdentity().translate(center.negateTo());
|
||||
|
||||
var trm = tr.mul(rot).mul(ntr);
|
||||
|
||||
this.eye = trm.mulVec3(this.eye);
|
||||
this._v = rot.mulVec3(this._v).normalize();
|
||||
this._u = rot.mulVec3(this._u).normalize();
|
||||
this._n = rot.mulVec3(this._n).normalize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotates camera around center point by horizontal.
|
||||
* @public
|
||||
* @param {number} angle - Rotation angle in radians.
|
||||
* @param {boolaen} isArc - If true camera up vector gets from current up vector every frame,
|
||||
* otherwise up is always input parameter.
|
||||
* @param {og.math.Vector3} center - Point that the camera rotates around.
|
||||
* @param {og.math.Vector3} [up] - Camera up vector.
|
||||
*/
|
||||
rotateHorizontal(angle, isArc, center, up) {
|
||||
this.rotateAround(angle, isArc, center, up);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotates camera around center point by vecrtical.
|
||||
* @param {number} angle - Rotation angle in radians.
|
||||
* @param {og.math.Vector3} center - Point that the camera rotates around.
|
||||
*/
|
||||
rotateVertical(angle, center) {
|
||||
this.rotateAround(angle, false, center, this._u);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets 3d size factor. Uses in LOD distance calculation.
|
||||
* @public
|
||||
* @param {og.math.Vector3} p - Far point.
|
||||
* @param {og.math.Vector3} r - Far point.
|
||||
* @returns {number} - Size factor.
|
||||
*/
|
||||
projectedSize(p, r) {
|
||||
return Math.atan(r / this.eye.distance(p)) * this._projSizeConst;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns normal matrix.
|
||||
* @public
|
||||
* @returns {og.math.Matrix3} - Normal matrix.
|
||||
*/
|
||||
getNormalMatrix() {
|
||||
return this._normalMatrix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns projection matrix.
|
||||
* @public
|
||||
* @returns {og.math.Matrix4} - Projection matrix.
|
||||
*/
|
||||
getProjectionMatrix() {
|
||||
return this._projectionMatrix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns model matrix.
|
||||
* @public
|
||||
* @returns {og.math.Matrix4} - View matrix.
|
||||
*/
|
||||
getViewMatrix() {
|
||||
return this._viewMatrix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns projection and model matrix product.
|
||||
* @public
|
||||
* @return {og.math.Matrix4} - Projection-view matrix.
|
||||
*/
|
||||
getProjectionViewMatrix() {
|
||||
return this._projectionViewMatrix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns inverse projection and model matrix product.
|
||||
* @public
|
||||
* @returns {og.math.Matrix4} - Inversed projection-view matrix.
|
||||
*/
|
||||
getInverseProjecttionViewMatrix() {
|
||||
return this._inverseProjectionViewMatrix;
|
||||
}
|
||||
};
|
||||
|
||||
const EVENT_NAMES = [
|
||||
/**
|
||||
* When camera has been updated.
|
||||
* @event og.Camera#viewchange
|
||||
*/
|
||||
"viewchange",
|
||||
|
||||
/**
|
||||
* Camera is stopped.
|
||||
* @event og.Camera#moveend
|
||||
*/
|
||||
"moveend"
|
||||
];
|
||||
|
||||
const defaultOptions = {
|
||||
'viewAngle': 30,
|
||||
'near': 1,
|
||||
'far': math.MAX,
|
||||
'eye': new Vec3(0, 0, 0),
|
||||
'look': new Vec3(0, 0, 0),
|
||||
'up': new Vec3(0, 1, 0)
|
||||
};
|
||||
|
||||
export { Camera };
|
||||
529
src/og/camera/PlanetCamera.js
Normal file
529
src/og/camera/PlanetCamera.js
Normal file
@ -0,0 +1,529 @@
|
||||
/**
|
||||
* @module og/camera/PlanetCamera
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as math from '../math.js';
|
||||
import * as mercator from '../mercator.js';
|
||||
import { Camera } from './Camera.js';
|
||||
import { Vec3 } from '../math/Vec3.js';
|
||||
import { Key } from '../Lock.js';
|
||||
import { LonLat } from '../LonLat.js';
|
||||
import { Mat4 } from '../math/Mat4.js';
|
||||
import { Ray } from '../math/Ray.js';
|
||||
|
||||
/**
|
||||
* Planet camera.
|
||||
* @class
|
||||
* @extends {og.Camera}
|
||||
* @param {og.RenderNode} planet - Planet render node.
|
||||
* @param {Object} [options] - Planet camera options:
|
||||
* @param {Object} [options.name] - Camera name.
|
||||
* @param {number} [options.viewAngle] - Camera angle of view. Default is 35.0
|
||||
* @param {number} [options.near] - Camera near plane distance. Default is 1.0
|
||||
* @param {number} [options.far] - Camera far plane distance. Deafult is og.math.MAX
|
||||
* @param {number} [options.minAltitude] - Minimal altitude for the camera. Deafult is 50
|
||||
* @param {og.math.Vector3} [options.eye] - Camera eye position. Default (0,0,0)
|
||||
* @param {og.math.Vector3} [options.look] - Camera look position. Default (0,0,0)
|
||||
* @param {og.math.Vector3} [options.up] - Camera eye position. Default (0,1,0)
|
||||
*/
|
||||
class PlanetCamera extends Camera {
|
||||
constructor(planet, options) {
|
||||
super(planet.renderer, options);
|
||||
/**
|
||||
* Assigned camera's planet.
|
||||
* @public
|
||||
* @type {og.scene.Planet}
|
||||
*/
|
||||
this.planet = planet;
|
||||
|
||||
/**
|
||||
* Minimal alltitude that camera can reach over the terrain.
|
||||
* @public
|
||||
* @type {number}
|
||||
*/
|
||||
this.minAltitude = options.minAltitude || 50;
|
||||
|
||||
/**
|
||||
* Current geographical degree position.
|
||||
* @protected
|
||||
* @type {og.LonLat}
|
||||
*/
|
||||
this._lonLat = this.planet.ellipsoid.cartesianToLonLat(this.eye);
|
||||
|
||||
/**
|
||||
* Current geographical mercator position.
|
||||
* @protected
|
||||
* @type {og.LonLat}
|
||||
*/
|
||||
this._lonLatMerc = this._lonLat.forwardMercator();
|
||||
|
||||
/**
|
||||
* Current altitude.
|
||||
* @protected
|
||||
* @type {number}
|
||||
*/
|
||||
this._terrainAltitude = this._lonLat.height;
|
||||
|
||||
/**
|
||||
* Cartesian coordinates on the terrain.
|
||||
* @protected
|
||||
* @type {og.math.Vector3}
|
||||
*/
|
||||
this._terrainPoint = new Vec3();
|
||||
|
||||
/**
|
||||
* Quad node that camera flies over.
|
||||
* @protected
|
||||
* @type {og.quadTree.Node}
|
||||
*/
|
||||
this._insideSegment = null;
|
||||
|
||||
this.slope = 0;
|
||||
|
||||
/**
|
||||
* Coordinates that depends on what segment class we are fling over.
|
||||
* It can be WGS84 or Mercator coordinates. Gets in og.quadTree.Node
|
||||
* @protected
|
||||
* @type {og.LonLat}
|
||||
*/
|
||||
this._insideSegmentPosition = null;
|
||||
|
||||
this._keyLock = new Key();
|
||||
|
||||
//Camera's flying frames
|
||||
this._framesArr = [];
|
||||
this._framesCounter = 0;
|
||||
this._numFrames = 50;
|
||||
this._completeCallback = null;
|
||||
this._flying = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone planet camera instance to another one.
|
||||
* @public
|
||||
* @virtual
|
||||
* @returns {og.PlanetCamera}
|
||||
*/
|
||||
clone() {
|
||||
var newcam = new PlanetCamera();
|
||||
newcam.eye.copy(cam.eye);
|
||||
newcam._u.copy(cam._u);
|
||||
newcam._v.copy(cam._v);
|
||||
newcam._n.copy(cam._n);
|
||||
newcam.renderer = cam.renderer;
|
||||
newcam._projectionMatrix.copy(cam._projectionMatrix);
|
||||
newcam._viewMatrix.copy(cam._viewMatrix);
|
||||
newcam._projectionViewMatrix.copy(cam._projectionViewMatrix);
|
||||
newcam._inverseProjectionViewMatrix.copy(cam._inverseProjectionViewMatrix);
|
||||
newcam.frustum.setFrustum(newcam._projectionViewMatrix);
|
||||
newcam.planet = cam.planet;
|
||||
newcam._lonLat = cam._lonLat.clone();
|
||||
return newcam;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates camera view space.
|
||||
* @public
|
||||
* @virtual
|
||||
*/
|
||||
update() {
|
||||
|
||||
this._setViewMatrix();
|
||||
|
||||
this._projectionViewMatrix = this._projectionMatrix.mul(this._viewMatrix);
|
||||
this.frustum.setFrustum(this._projectionViewMatrix._m);
|
||||
|
||||
this._inverseProjectionViewMatrix = this._projectionMatrixPrecise.mul(this._viewMatrix).inverseTo();
|
||||
|
||||
//this._normalMatrix = this._viewMatrix.toInverseMatrix3().transposeTo();
|
||||
this._normalMatrix = this._viewMatrix.toMatrix3();
|
||||
|
||||
this.updateGeodeticPosition();
|
||||
|
||||
this.slope = this._n.dot(this.eye.normal());
|
||||
|
||||
this.events.dispatch(this.events.viewchange, this);
|
||||
}
|
||||
|
||||
updateGeodeticPosition() {
|
||||
this._lonLat = this.planet.ellipsoid.cartesianToLonLat(this.eye);
|
||||
if (Math.abs(this._lonLat.lat) <= mercator.MAX_LAT) {
|
||||
this._lonLatMerc = this._lonLat.forwardMercator();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets altitude over the terrain.
|
||||
* @public
|
||||
* @param {number} alt - Altitude over the terrain.
|
||||
*/
|
||||
setAltitude(alt) {
|
||||
var n = this.eye.normal();
|
||||
var t = this._terrainPoint;
|
||||
this.eye.x = n.x * alt + t.x;
|
||||
this.eye.y = n.y * alt + t.y;
|
||||
this.eye.z = n.z * alt + t.z;
|
||||
this._terrainAltitude = alt;
|
||||
this.update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets altitude over the terrain.
|
||||
* @public
|
||||
*/
|
||||
getAltitude() {
|
||||
return this._terrainAltitude;
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves camera to the geographical position.
|
||||
* @public
|
||||
* @param {og.LonLat} lonlat - Geographical position.
|
||||
*/
|
||||
setLonLat(lonlat, up) {
|
||||
this._lonLat.set(lonlat.lon, lonlat.lat, lonlat.height || this._lonLat.height);
|
||||
var newEye = this.planet.ellipsoid.lonLatToCartesian(this._lonLat);
|
||||
var rot = new Mat4().rotateBetweenVectors(newEye.normal(), this.eye.normal());
|
||||
this.eye = newEye;
|
||||
this._v = rot.mulVec3(this._v);
|
||||
this._u = rot.mulVec3(this._u);
|
||||
this._n = rot.mulVec3(this._n);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns camera geographical position.
|
||||
* @public
|
||||
* @returns {og.LonLat}
|
||||
*/
|
||||
getLonLat() {
|
||||
return this._lonLat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns camera height.
|
||||
* @public
|
||||
* @returns {number}
|
||||
*/
|
||||
getHeight() {
|
||||
return this._lonLat.height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Places camera to view to the geographical point.
|
||||
* @public
|
||||
* @param {og.LonLat} lonlat - New camera and camera view position.
|
||||
* @param {og.math.Vector3} [up] - Camera UP vector. Default (0,1,0)
|
||||
*/
|
||||
viewLonLat(lonlat, up) {
|
||||
this._lonLat.set(lonlat.lon, lonlat.lat, lonlat.height || this._lonLat.height);
|
||||
var el = this.planet.ellipsoid;
|
||||
var newEye = el.lonLatToCartesian(this._lonLat);
|
||||
var newLook = el.lonLatToCartesian(new LonLat(this._lonLat.lon, this._lonLat.lat, 0));
|
||||
this.set(newEye, newLook, up || Vec3.UP);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets position by viewable extent.
|
||||
* @public
|
||||
* @param {og.Extent} extent - Viewable extent.
|
||||
* @returns {og.math.Vector3}
|
||||
*/
|
||||
getExtentPosition(extent) {
|
||||
|
||||
var north = extent.getNorth();
|
||||
var south = extent.getSouth();
|
||||
var east = extent.getEast();
|
||||
var west = extent.getWest();
|
||||
|
||||
if (west > east) {
|
||||
east += 360;
|
||||
}
|
||||
|
||||
var e = this.planet.ellipsoid;
|
||||
|
||||
var cart = new LonLat(east, north);
|
||||
var northEast = e.lonLatToCartesian(cart);
|
||||
cart.lat = south;
|
||||
var southEast = e.lonLatToCartesian(cart);
|
||||
cart.lon = west;
|
||||
var southWest = e.lonLatToCartesian(cart);
|
||||
cart.lat = north;
|
||||
var northWest = e.lonLatToCartesian(cart);
|
||||
|
||||
var center = Vec3.sub(northEast, southWest).scale(0.5).addA(southWest);
|
||||
|
||||
var mag = center.length();
|
||||
if (mag < 0.000001) {
|
||||
cart.lon = (east + west) * 0.5;
|
||||
cart.lat = (north + south) * 0.5;
|
||||
center = e.lonLatToCartesian(cart);
|
||||
}
|
||||
|
||||
northWest.subA(center);
|
||||
southEast.subA(center);
|
||||
northEast.subA(center);
|
||||
southWest.subA(center);
|
||||
|
||||
var direction = center.normal();//ellipsoid.getSurfaceNormal(center).negate().normalize();
|
||||
var right = direction.cross(Vec3.UP).normalize();
|
||||
var up = right.cross(direction).normalize();
|
||||
|
||||
var height = Math.max(
|
||||
Math.abs(up.dot(northWest)),
|
||||
Math.abs(up.dot(southEast)),
|
||||
Math.abs(up.dot(northEast)),
|
||||
Math.abs(up.dot(southWest))
|
||||
);
|
||||
|
||||
var width = Math.max(
|
||||
Math.abs(right.dot(northWest)),
|
||||
Math.abs(right.dot(southEast)),
|
||||
Math.abs(right.dot(northEast)),
|
||||
Math.abs(right.dot(southWest))
|
||||
);
|
||||
|
||||
var tanPhi = Math.tan(this._viewAngle * math.RADIANS * 0.5);
|
||||
var tanTheta = this._aspect * tanPhi;
|
||||
var d = Math.max(width / tanTheta, height / tanPhi);
|
||||
|
||||
center.normalize();
|
||||
center.scale(mag + d);
|
||||
return center;
|
||||
}
|
||||
|
||||
/**
|
||||
* View current extent.
|
||||
* @public
|
||||
* @param {og.Extent} extent - Current extent.
|
||||
*/
|
||||
viewExtent(extent) {
|
||||
this.stopFlying();
|
||||
this.set(this.getExtentPosition(extent), Vec3.ZERO, Vec3.UP);
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* Flies to the current extent.
|
||||
* @public
|
||||
* @param {og.Extent} extent - Current extent.
|
||||
* @param {og.math.Vector3} [up] - Camera UP in the end of flying. Default - (0,1,0)
|
||||
* @param {cameraCallback} [completeCallback] - Callback that calls after flying when flying is finished.
|
||||
* @param {cameraCallback} [startCallback] - Callback that calls befor the flying begins.
|
||||
*/
|
||||
flyExtent(extent, up, completeCallback, startCallback) {
|
||||
this.flyCartesian(this.getExtentPosition(extent), Vec3.ZERO,
|
||||
up, completeCallback, startCallback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flies to the cartesian coordinates.
|
||||
* @public
|
||||
* @param {og.math.Vector3} cartesian - Finish cartesian coordinates.
|
||||
* @param {og.math.Vector3} [look] - Camera LOOK in the end of flying. Default - (0,0,0)
|
||||
* @param {og.math.Vector3} [up] - Camera UP vector in the end of flying. Default - (0,1,0)
|
||||
* @param {cameraCallback} [completeCallback] - Callback that calls after flying when flying is finished.
|
||||
* @param {cameraCallback} [startCallback] - Callback that calls befor the flying begins.
|
||||
*/
|
||||
flyCartesian(cartesian, look, up, completeCallback, startCallback) {
|
||||
|
||||
//???????
|
||||
//if (this.eye.distance(cartesian) < 23000) {
|
||||
// return;
|
||||
//}
|
||||
|
||||
this.stopFlying();
|
||||
|
||||
this._completeCallback = completeCallback;
|
||||
|
||||
if (startCallback) {
|
||||
startCallback.call(this);
|
||||
}
|
||||
|
||||
var _look = look || Vec3.ZERO;
|
||||
if (look instanceof LonLat) {
|
||||
_look = this.planet.ellipsoid.lonLatToCartesian(look);
|
||||
}
|
||||
|
||||
var ground_a = this.planet.ellipsoid.lonLatToCartesian(new LonLat(this._lonLat.lon, this._lonLat.lat));
|
||||
var v_a = this._v,
|
||||
n_a = this._n;
|
||||
|
||||
var lonlat_b = this.planet.ellipsoid.cartesianToLonLat(cartesian);
|
||||
var up_b = up || Vec3.UP;
|
||||
var ground_b = this.planet.ellipsoid.lonLatToCartesian(new LonLat(lonlat_b.lon, lonlat_b.lat, 0));
|
||||
var eye_b = cartesian;
|
||||
var n_b = Vec3.sub(eye_b, _look);
|
||||
var u_b = up_b.cross(n_b);
|
||||
n_b.normalize();
|
||||
u_b.normalize();
|
||||
var v_b = n_b.cross(u_b);
|
||||
|
||||
var an = ground_a.normal();
|
||||
var bn = ground_b.normal();
|
||||
var anbn = 1.0 - an.dot(bn);
|
||||
var hM_a = math.SQRT_HALF * Math.sqrt((anbn) > 0.0 ? anbn : 0.0);
|
||||
|
||||
var maxHeight = 6639613;
|
||||
var currMaxHeight = Math.max(this._lonLat.height, lonlat_b.height);
|
||||
if (currMaxHeight > maxHeight) {
|
||||
maxHeight = currMaxHeight;
|
||||
}
|
||||
var max_h = currMaxHeight + 2.5 * hM_a * (maxHeight - currMaxHeight);
|
||||
var zero = Vec3.ZERO;
|
||||
|
||||
//camera path and orientations calculation
|
||||
for (var i = 0; i <= this._numFrames; i++) {
|
||||
var d = 1 - i / this._numFrames;
|
||||
d = d * d * (3 - 2 * d);
|
||||
d *= d;
|
||||
|
||||
//Error here
|
||||
var g_i = ground_a.smerp(ground_b, d).normalize();
|
||||
var ground_i = this.planet.getRayIntersectionEllipsoid(new Ray(zero, g_i));
|
||||
var t = 1 - d;
|
||||
var height_i = this._lonLat.height * d * d * d + max_h * 3 * d * d * t + max_h * 3 * d * t * t + lonlat_b.height * t * t * t;
|
||||
|
||||
var eye_i = ground_i.addA(g_i.scale(height_i));
|
||||
var up_i = v_a.smerp(v_b, d);
|
||||
var look_i = Vec3.add(eye_i, n_a.smerp(n_b, d).negateTo());
|
||||
|
||||
var n = new Vec3(eye_i.x - look_i.x, eye_i.y - look_i.y, eye_i.z - look_i.z);
|
||||
var u = up_i.cross(n);
|
||||
n.normalize();
|
||||
u.normalize();
|
||||
|
||||
var v = n.cross(u);
|
||||
this._framesArr[i] = {
|
||||
"eye": eye_i,
|
||||
"n": n,
|
||||
"u": u,
|
||||
"v": v
|
||||
};
|
||||
}
|
||||
|
||||
this._framesCounter = this._numFrames;
|
||||
this._flying = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flies to the geo coordiantes.
|
||||
* @public
|
||||
* @param {og.LonLat} lonlat - Finish coordinates.
|
||||
* @param {og.math.Vector3} [look] - Camera LOOK in the end of flying. Default - (0,0,0)
|
||||
* @param {og.math.Vector3} [up] - Camera UP vector in the end of flying. Default - (0,1,0)
|
||||
* @param {cameraCallback} [completeCallback] - Callback that calls after flying when flying is finished.
|
||||
* @param {cameraCallback} [startCallback] - Callback that calls befor the flying begins.
|
||||
*/
|
||||
flyLonLat(lonlat, look, up, completeCallback, startCallback) {
|
||||
var _lonlat = new LonLat(lonlat.lon, lonlat.lat, lonlat.height || this._lonLat.height);
|
||||
this.flyCartesian(this.planet.ellipsoid.lonLatToCartesian(_lonlat), look, up, completeCallback, startCallback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Breaks the flight.
|
||||
* @public
|
||||
*/
|
||||
stopFlying() {
|
||||
|
||||
this.planet.layerLock.free(this._keyLock);
|
||||
this.planet.terrainLock.free(this._keyLock);
|
||||
this.planet._normalMapCreator.free(this._keyLock);
|
||||
|
||||
this._flying = false;
|
||||
this._framesArr.length = 0;
|
||||
this._framesArr = [];
|
||||
this._framesCounter = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns camera is flying.
|
||||
* @public
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isFlying() {
|
||||
return this._flying;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotates around planet to the left.
|
||||
* @public
|
||||
* @param {number} angle - Rotation angle.
|
||||
* @param {boolean} [spin] - If its true rotates around globe spin.
|
||||
*/
|
||||
rotateLeft(angle, spin) {
|
||||
this.rotateHorizontal(angle * math.RADIANS, spin ^ true, Vec3.ZERO);
|
||||
this.update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotates around planet to the right.
|
||||
* @public
|
||||
* @param {number} angle - Rotation angle.
|
||||
* @param {boolean} [spin] - If its true rotates around globe spin.
|
||||
*/
|
||||
rotateRight(angle, spin) {
|
||||
this.rotateHorizontal(-angle * math.RADIANS, spin ^ true, Vec3.ZERO);
|
||||
this.update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotates around planet to the north pole.
|
||||
* @public
|
||||
* @param {number} angle - Rotation angle.
|
||||
*/
|
||||
rotateUp(angle) {
|
||||
this.rotateVertical(angle * math.RADIANS, Vec3.ZERO);
|
||||
this.update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotates around planet to the south pole.
|
||||
* @public
|
||||
* @param {number} angle - Rotation angle.
|
||||
*/
|
||||
rotateDown(angle) {
|
||||
this.rotateVertical(-angle * math.RADIANS, Vec3.ZERO);
|
||||
this.update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare camera to the frame. Used in render node frame function.
|
||||
* @public
|
||||
*/
|
||||
prepareFrame() {
|
||||
if (this._flying) {
|
||||
var c = this._numFrames - this._framesCounter;
|
||||
|
||||
this.planet.layerLock.lock(this._keyLock);
|
||||
this.planet.terrainLock.lock(this._keyLock);
|
||||
this.planet._normalMapCreator.lock(this._keyLock);
|
||||
|
||||
this.eye = this._framesArr[c].eye;
|
||||
this._u = this._framesArr[c].u;
|
||||
this._v = this._framesArr[c].v;
|
||||
this._n = this._framesArr[c].n;
|
||||
this.update();
|
||||
this._framesCounter--;
|
||||
|
||||
if (this._framesCounter < 0) {
|
||||
this.stopFlying();
|
||||
if (this._completeCallback) {
|
||||
this._completeCallback();
|
||||
this._completeCallback = null;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this._terrainAltitude = this._lonLat.height;
|
||||
if (this._lonLat.height < 1000000) {
|
||||
this._terrainAltitude = this._insideSegment.getTerrainPoint(this._terrainPoint, this.eye, this._insideSegmentPosition);
|
||||
if (this._terrainAltitude < this.minAltitude) {
|
||||
this.setAltitude(this.minAltitude);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export { PlanetCamera };
|
||||
138
src/og/control/BaseControl.js
Normal file
138
src/og/control/BaseControl.js
Normal file
@ -0,0 +1,138 @@
|
||||
/**
|
||||
* @module og/control/BaseControl
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Base control class for implementing renderer controls.
|
||||
* All other controls extend from this class.
|
||||
* @class
|
||||
* @param {Object} [options] - Control activation options:
|
||||
* @param {Boolean} [options.autoActivated=true] - If true - calls initialize function after the renderer assigning.
|
||||
*/
|
||||
class BaseControl {
|
||||
constructor(options) {
|
||||
options = options || {};
|
||||
|
||||
/**
|
||||
* Control initialized.
|
||||
* @protected
|
||||
* @type {Boolean}
|
||||
*/
|
||||
this._initialized = false;
|
||||
|
||||
/**
|
||||
* Assigned renderer.
|
||||
* @public
|
||||
* @type {og.Renderer}
|
||||
*/
|
||||
this.renderer = null;
|
||||
|
||||
/**
|
||||
* Auto activation flag.
|
||||
* @public
|
||||
* @type {Boolean}
|
||||
*/
|
||||
this.autoActivate = options.autoActivate != undefined ? options.autoActivate : true;
|
||||
|
||||
/**
|
||||
* Control activity.
|
||||
* @protected
|
||||
* @type {Boolean}
|
||||
*/
|
||||
this._active = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Control initialization function have to be overriden.
|
||||
* @public
|
||||
* @virtual
|
||||
*/
|
||||
oninit() { }
|
||||
|
||||
/**
|
||||
* Control renderer assigning function have to be overriden.
|
||||
* @public
|
||||
* @virtual
|
||||
*/
|
||||
onadd() { }
|
||||
|
||||
/**
|
||||
* Control remove function have to be overriden.
|
||||
* @public
|
||||
* @virtual
|
||||
*/
|
||||
onremove() { }
|
||||
|
||||
/**
|
||||
* Control activation function have to be overriden.
|
||||
* @public
|
||||
* @virtual
|
||||
*/
|
||||
onactivate() { }
|
||||
|
||||
/**
|
||||
* Control deactivation function have to be overriden.
|
||||
* @public
|
||||
* @virtual
|
||||
*/
|
||||
ondeactivate() { }
|
||||
|
||||
/**
|
||||
* Assign renderer to the control.
|
||||
* @public
|
||||
* @type {og.Renderer}
|
||||
*/
|
||||
addTo(renderer) {
|
||||
if (renderer) {
|
||||
this.renderer = renderer;
|
||||
renderer.controls.push(this);
|
||||
this.onadd && this.onadd();
|
||||
if (this.autoActivate) {
|
||||
this.oninit && this.oninit();
|
||||
this._initialized = true;
|
||||
this._active = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign renderer to the control.
|
||||
* @public
|
||||
*/
|
||||
remove() {
|
||||
this.onremove && this.onremove();
|
||||
this.renderer = null;
|
||||
this._active = false;
|
||||
this._initialized = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate control.
|
||||
* @public
|
||||
*/
|
||||
activate() {
|
||||
this._active = true;
|
||||
this.onactivate && this.onactivate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Deactivate control.
|
||||
* @public
|
||||
*/
|
||||
deactivate() {
|
||||
this._active = false;
|
||||
this.ondeactivate && this.ondeactivate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Is control active.
|
||||
* @public
|
||||
*/
|
||||
isActive() {
|
||||
return this._active;
|
||||
}
|
||||
};
|
||||
|
||||
export { BaseControl };
|
||||
199
src/og/control/EarthCoordinates.js
Normal file
199
src/og/control/EarthCoordinates.js
Normal file
@ -0,0 +1,199 @@
|
||||
/**
|
||||
* @module og/control/EarthCoordinates
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { BaseControl } from './BaseControl.js';
|
||||
|
||||
function dec2deg(base) {
|
||||
var t, t2;
|
||||
var degrees = base < 0 ? Math.ceil(base) : Math.floor(base);
|
||||
var minutes = Math.floor(t = Math.abs((base - degrees)) * 60);
|
||||
var seconds = Math.floor(t2 = (t - minutes) * 6000);
|
||||
seconds = seconds / 100.00;
|
||||
return (numToFixedString(degrees, 3) + "\u00B0" +
|
||||
numToFixedString(minutes, 2) + "\u0027" +
|
||||
numToFixedString(seconds.toFixed(2), 2) + "\u0022");
|
||||
};
|
||||
|
||||
function numToFixedString(num, fixed) {
|
||||
var dl = num.toString().split('.')[0].length;
|
||||
var white = " ";
|
||||
for (var i = dl; i < fixed; i++) {
|
||||
white += ' ';
|
||||
}
|
||||
return white + num.toString();
|
||||
};
|
||||
|
||||
function toDecimal(ll) {
|
||||
return ll.lat.toFixed(5) + ", " + ll.lon.toFixed(5);
|
||||
};
|
||||
|
||||
function toDegrees(ll) {
|
||||
return dec2deg(ll.lat) + ", " + dec2deg(ll.lon);
|
||||
};
|
||||
|
||||
function toMercator(ll) {
|
||||
var m = ll.forwardMercator();
|
||||
return m.lat.toFixed(5) + ", " + m.lon.toFixed(5);
|
||||
};
|
||||
|
||||
const DisplayTypesConverters = [
|
||||
toDecimal,
|
||||
toDegrees,
|
||||
toMercator
|
||||
];
|
||||
|
||||
/**
|
||||
* Control displays mouse or screen center Earth coordinates.
|
||||
* @class
|
||||
* @extends {og.control.BaseControl}
|
||||
* @param {Object} [options] - Options:
|
||||
* @param {Boolean} [options.center] - Earth coordiantes by screen center otherwise mouse pointer. False is default.
|
||||
* @param {Boolean} [options.type] - Coordinates shown: 0 - is decimal degrees, 1 - degrees, 2 - mercator geodetic coordinates.
|
||||
*/
|
||||
class EarthCoordinates extends BaseControl {
|
||||
constructor(options) {
|
||||
super(options);
|
||||
|
||||
options = options || {};
|
||||
|
||||
/**
|
||||
* Display type.
|
||||
* @private
|
||||
* @type {Boolean}
|
||||
*/
|
||||
this._displayType = options.type || 0;
|
||||
|
||||
/**
|
||||
* Current coordinates type converter.
|
||||
* @private
|
||||
* @function
|
||||
*/
|
||||
this._converter = DisplayTypesConverters[0];
|
||||
|
||||
/**
|
||||
* Display dom element.
|
||||
* @private
|
||||
* @type {Object}
|
||||
*/
|
||||
this._display = null;
|
||||
|
||||
/**
|
||||
* Screen center or mouse pointer coordinates show flag.
|
||||
* @private
|
||||
* @type {Boolean}
|
||||
*/
|
||||
|
||||
var pad = false;
|
||||
if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) {
|
||||
pad = true;
|
||||
}
|
||||
|
||||
this._center = options.center || pad;
|
||||
|
||||
/**
|
||||
* Current position.
|
||||
* @public
|
||||
* @type {og.math.Vector3}
|
||||
*/
|
||||
this.position = null;
|
||||
}
|
||||
|
||||
oninit() {
|
||||
this._display = document.createElement('div');
|
||||
this._display.className = 'ogEarthCoordinatesControl';
|
||||
var that = this;
|
||||
|
||||
function _refresh(el) {
|
||||
if (that._displayType >= DisplayTypesConverters.length)
|
||||
that._displayType = 0;
|
||||
if (that._displayType == 0) {
|
||||
el.style.width = "275px";
|
||||
} else if (that._displayType == 1) {
|
||||
el.style.width = "355px";
|
||||
} else if (that._displayType == 2) {
|
||||
el.style.width = "350px";
|
||||
}
|
||||
that._converter = DisplayTypesConverters[that._displayType];
|
||||
that._showPosition();
|
||||
};
|
||||
|
||||
this._display.onclick = function (e) {
|
||||
that._displayType += 1;
|
||||
_refresh(this);
|
||||
};
|
||||
|
||||
this.renderer.div.appendChild(this._display);
|
||||
|
||||
_refresh(this._display);
|
||||
|
||||
let centerDiv = document.createElement('div');
|
||||
centerDiv.className = 'ogCenterIcon';
|
||||
centerDiv.innerHTML = '<svg width="12" height="12"><g><path stroke-width="1" stroke-opacity="1" d="M6 0L6 12M0 6L12 6" stroke="#009DFF"></path></g></svg>';
|
||||
this.renderer.div.appendChild(centerDiv);
|
||||
|
||||
if (this._center) {
|
||||
this.renderer.activeCamera.events.on("moveend", this._grabCoordinates, this);
|
||||
centerDiv.style.display = "block";
|
||||
} else {
|
||||
this.renderer.events.on("mousemove", this._onMouseMove, this);
|
||||
centerDiv.style.display = "none";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets coordinates capturing type.
|
||||
* @public
|
||||
* @param {Boolean} center - True - capture screen center, false - mouse pointer.
|
||||
*/
|
||||
setCenter(center) {
|
||||
if (center != this._center) {
|
||||
this._center = center;
|
||||
if (center) {
|
||||
this.renderer.events.off("mousemove", this._onMouseMove);
|
||||
this.renderer.activeCamera.events.on("moveend", this._grabCoordinates, this);
|
||||
centerDiv.style.display = "block";
|
||||
} else {
|
||||
this.renderer.events.off("draw", this._draw);
|
||||
this.renderer.events.on("mousemove", this._onMouseMove, this);
|
||||
centerDiv.style.display = "none";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_showPosition() {
|
||||
if (this.position) {
|
||||
this.position.height = ((this.position.height > 10000 || this.position.height < -10000) ? 0 : this.position.height);
|
||||
this._display.innerHTML = "Lat/Lon: " + this._converter(this.position) + " h(km): " + (this.position.height > 0 ? "~" + (Math.round(this.position.height) / 1000).toFixed(2) : "-");
|
||||
} else {
|
||||
this._display.innerHTML = "Lat/Lon: " + "_____________________";
|
||||
}
|
||||
}
|
||||
|
||||
_grabCoordinates() {
|
||||
var r = this.renderer;
|
||||
var ts = r.events.touchState;
|
||||
this.position = this.planet.getLonLatFromPixelTerrain(r.handler.getCenter());
|
||||
this._showPosition();
|
||||
}
|
||||
|
||||
_onMouseMove() {
|
||||
var r = this.renderer;
|
||||
var ms = r.events.mouseState;
|
||||
if (!(ms.leftButtonDown || ms.rightButtonDown) &&
|
||||
r.controlsBag.scaleRot <= 0 &&
|
||||
!r.activeCamera._flying) {
|
||||
this.position = this.planet.getLonLatFromPixelTerrain(ms, true);
|
||||
this._showPosition();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export function earthCoordinates(options) {
|
||||
return new EarthCoordinates(options);
|
||||
};
|
||||
|
||||
export { EarthCoordinates };
|
||||
62
src/og/control/GeoImageDragControl.js
Normal file
62
src/og/control/GeoImageDragControl.js
Normal file
@ -0,0 +1,62 @@
|
||||
/**
|
||||
* @module og/control/GeoImageDragControl
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { BaseControl } from './BaseControl.js';
|
||||
import { BaseGeoImage } from '../layer/BaseGeoImage.js';
|
||||
|
||||
class GeoImageDragControl extends BaseControl {
|
||||
constructor(options) {
|
||||
super(options);
|
||||
|
||||
options = options || {};
|
||||
|
||||
this._cornerIndex = -1;
|
||||
this._catchCorner = false;
|
||||
|
||||
}
|
||||
|
||||
oninit() {
|
||||
var that = this;
|
||||
var p = this.planet;
|
||||
p.events.on('layeradd', function (e) {
|
||||
if (e instanceof BaseGeoImage) {
|
||||
e.events.on('mousemove', function (ms) {
|
||||
if (that._active) {
|
||||
if (that._catchCorner) {
|
||||
var corners = e.getCornersLonLat();
|
||||
corners[that._cornerIndex] = p.getLonLatFromPixelTerrain(ms, true);
|
||||
e.setCornersLonLat(corners);
|
||||
} else {
|
||||
that._cornerIndex = -1;
|
||||
for (var i = 0; i < e._cornersWgs84.length; i++) {
|
||||
var ground = p.getLonLatFromPixelTerrain(ms, true)
|
||||
if (ground && p.ellipsoid.getGreatCircleDistance(e._cornersWgs84[i], ground) / p.getDistanceFromPixel(ms, true) <= 0.05) {
|
||||
that._cornerIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
e.events.on('ldown', function (ms) {
|
||||
if (that._active && that._cornerIndex != -1) {
|
||||
that._catchCorner = true;
|
||||
p.renderer.controls[0]._active = false;
|
||||
}
|
||||
});
|
||||
|
||||
e.events.on('lup', function (ms) {
|
||||
if (that._active) {
|
||||
that._catchCorner = false;
|
||||
p.renderer.controls[0]._active = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export { GeoImageDragControl };
|
||||
117
src/og/control/KeyboardNavigation.js
Normal file
117
src/og/control/KeyboardNavigation.js
Normal file
@ -0,0 +1,117 @@
|
||||
/**
|
||||
* @module og/control/KeyboardNavigation
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as math from '../math.js';
|
||||
import { BaseControl } from './BaseControl.js';
|
||||
import { input } from '../input/input.js';
|
||||
import { Vec3 } from '../math/Vec3.js';
|
||||
|
||||
/**
|
||||
* Planet camera keyboard navigation. Use W,S,A,D and left shift key for fly around a planet.
|
||||
* @class
|
||||
* @extends {og.control.BaseControl}
|
||||
* @param {Object} [options] - Control options.
|
||||
*/
|
||||
class KeyboardNavigation extends BaseControl {
|
||||
constructor(options) {
|
||||
options = options || {};
|
||||
super(options);
|
||||
|
||||
}
|
||||
|
||||
oninit() {
|
||||
this.renderer.events.on("keypress", input.KEY_W, this.onCameraMoveForward, this);
|
||||
this.renderer.events.on("keypress", input.KEY_S, this.onCameraMoveBackward, this);
|
||||
this.renderer.events.on("keypress", input.KEY_A, this.onCameraStrifeLeft, this);
|
||||
this.renderer.events.on("keypress", input.KEY_D, this.onCameraStrifeRight, this);
|
||||
this.renderer.events.on("keypress", input.KEY_UP, this.onCameraLookUp, this);
|
||||
this.renderer.events.on("keypress", input.KEY_DOWN, this.onCameraLookDown, this);
|
||||
this.renderer.events.on("keypress", input.KEY_LEFT, this.onCameraTurnLeft, this);
|
||||
this.renderer.events.on("keypress", input.KEY_RIGHT, this.onCameraTurnRight, this);
|
||||
this.renderer.events.on("keypress", input.KEY_Q, this.onCameraRollLeft, this);
|
||||
this.renderer.events.on("keypress", input.KEY_E, this.onCameraRollRight, this);
|
||||
}
|
||||
|
||||
onCameraMoveForward(event) {
|
||||
var camera = this.renderer.activeCamera;
|
||||
camera.slide(0, 0, -camera._lonLat.height / 30);
|
||||
camera.update();
|
||||
}
|
||||
|
||||
onCameraMoveBackward(event) {
|
||||
var camera = this.renderer.activeCamera;
|
||||
camera.slide(0, 0, camera._lonLat.height / 30);
|
||||
camera.update();
|
||||
}
|
||||
|
||||
onCameraStrifeLeft(event) {
|
||||
var camera = this.renderer.activeCamera;
|
||||
camera.slide(-camera._lonLat.height / 30, 0, 0);
|
||||
camera.update();
|
||||
}
|
||||
|
||||
onCameraStrifeRight(event) {
|
||||
var camera = this.renderer.activeCamera;
|
||||
camera.slide(camera._lonLat.height / 30, 0, 0);
|
||||
camera.update();
|
||||
}
|
||||
|
||||
onCameraLookUp(event) {
|
||||
var cam = this.renderer.activeCamera;
|
||||
if (this.renderer.events.isKeyPressed(input.KEY_SHIFT)) {
|
||||
cam.pitch(5);
|
||||
} else {
|
||||
cam.rotateVertical(cam._lonLat.height / 3000000 * math.RADIANS, Vec3.ZERO);
|
||||
}
|
||||
cam.update();
|
||||
}
|
||||
|
||||
onCameraLookDown(event) {
|
||||
var cam = this.renderer.activeCamera;
|
||||
if (this.renderer.events.isKeyPressed(input.KEY_SHIFT)) {
|
||||
cam.pitch(-5);
|
||||
} else {
|
||||
cam.rotateVertical(-cam._lonLat.height / 3000000 * math.RADIANS, Vec3.ZERO);
|
||||
}
|
||||
cam.update();
|
||||
}
|
||||
|
||||
onCameraTurnLeft(event) {
|
||||
var cam = this.renderer.activeCamera;
|
||||
if (this.renderer.events.isKeyPressed(input.KEY_SHIFT)) {
|
||||
cam.yaw(5);
|
||||
} else {
|
||||
cam.rotateHorizontal(cam._lonLat.height / 3000000 * math.RADIANS, false, Vec3.ZERO);
|
||||
}
|
||||
cam.update();
|
||||
}
|
||||
|
||||
onCameraTurnRight(event) {
|
||||
var cam = this.renderer.activeCamera;
|
||||
if (this.renderer.events.isKeyPressed(input.KEY_SHIFT)) {
|
||||
cam.yaw(-5);
|
||||
} else {
|
||||
cam.rotateHorizontal(-cam._lonLat.height / 3000000 * math.RADIANS, false, Vec3.ZERO);
|
||||
}
|
||||
cam.update();
|
||||
}
|
||||
|
||||
onCameraRollLeft(event) {
|
||||
this.renderer.activeCamera.roll(-5);
|
||||
this.renderer.activeCamera.update();
|
||||
}
|
||||
|
||||
onCameraRollRight(event) {
|
||||
this.renderer.activeCamera.roll(5);
|
||||
this.renderer.activeCamera.update();
|
||||
}
|
||||
};
|
||||
|
||||
export function keyboardNavigation(options) {
|
||||
return new KeyboardNavigation(options);
|
||||
};
|
||||
|
||||
export { KeyboardNavigation };
|
||||
159
src/og/control/LayerSwitcher.js
Normal file
159
src/og/control/LayerSwitcher.js
Normal file
@ -0,0 +1,159 @@
|
||||
/**
|
||||
* @module og/control/LayerSwitcher
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { BaseControl } from './BaseControl.js';
|
||||
|
||||
/**
|
||||
* Simple(OpenLayers like)layer switcher, includes base layers, overlays, geo images etc. groups.
|
||||
* @class
|
||||
* @extends {og.control.BaseControl}
|
||||
* @param {Object} [options] - Control options.
|
||||
*/
|
||||
class LayerSwitcher extends BaseControl {
|
||||
constructor(options) {
|
||||
super(options);
|
||||
|
||||
this.dialog = null;
|
||||
this.baseLayersDiv = null;
|
||||
this.overlaysDiv = null;
|
||||
this._id = LayerSwitcher.numSwitches++;
|
||||
}
|
||||
|
||||
static get numSwitches() {
|
||||
if (!this._counter && this._counter !== 0) {
|
||||
this._counter = 0;
|
||||
}
|
||||
return this._counter;
|
||||
}
|
||||
|
||||
static set numSwitches(n) {
|
||||
this._counter = n;
|
||||
}
|
||||
|
||||
oninit() {
|
||||
this.planet.events.on("layeradd", this.onLayerAdded, this);
|
||||
this.planet.events.on("layerremove", this.onLayerRemoved, this);
|
||||
this.createSwitcher();
|
||||
this.createDialog();
|
||||
}
|
||||
|
||||
onLayerAdded(layer) {
|
||||
if (layer.displayInLayerSwitcher) {
|
||||
if (layer.isBaseLayer()) {
|
||||
this.addSwitcher("radio", layer, this.baseLayersDiv);
|
||||
} else {
|
||||
this.addSwitcher("checkbox", layer, this.overlaysDiv, this._id);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
onLayerRemoved(layer) {
|
||||
layer._removeCallback();
|
||||
layer._removeCallback = null;
|
||||
}
|
||||
|
||||
addSwitcher(type, obj, container, id) {
|
||||
var lineDiv = document.createElement('div');
|
||||
|
||||
var that = this;
|
||||
var center = document.createElement('div');
|
||||
center.classList.add('ogViewExtentBtn');
|
||||
center.onclick = function () {
|
||||
that.planet.flyExtent(obj.getExtent());
|
||||
};
|
||||
|
||||
var inp = document.createElement('input');
|
||||
inp.type = type;
|
||||
inp.name = "ogBaseLayerRadiosId" + (id || "");
|
||||
inp.checked = obj.getVisibility();
|
||||
inp.className = "ogLayerSwitcherInput";
|
||||
inp.onclick = function () {
|
||||
obj.setVisibility(this.checked);
|
||||
};
|
||||
|
||||
obj.events && obj.events.on("visibilitychange", function (e) {
|
||||
inp.checked = e.getVisibility();
|
||||
});
|
||||
|
||||
var lbl = document.createElement('label');
|
||||
lbl.className = "ogLayerSwitcherLabel";
|
||||
lbl.innerHTML = (obj.name || obj.src || "noname") + "</br>";
|
||||
|
||||
obj._removeCallback = function () {
|
||||
container.removeChild(lineDiv);
|
||||
}
|
||||
|
||||
lineDiv.appendChild(center);
|
||||
lineDiv.appendChild(inp);
|
||||
lineDiv.appendChild(lbl);
|
||||
|
||||
container.appendChild(lineDiv);
|
||||
}
|
||||
|
||||
createBaseLayersContainer() {
|
||||
var layersDiv = document.createElement('div');
|
||||
layersDiv.className = "layersDiv";
|
||||
this.dialog.appendChild(layersDiv);
|
||||
|
||||
var baseLayersLbl = document.createElement('div');
|
||||
baseLayersLbl.className = "layersDiv";
|
||||
baseLayersLbl.innerHTML = "Base Layer";
|
||||
layersDiv.appendChild(baseLayersLbl);
|
||||
|
||||
this.baseLayersDiv = document.createElement('div');
|
||||
layersDiv.appendChild(this.baseLayersDiv);
|
||||
}
|
||||
|
||||
createOverlaysContainer() {
|
||||
var overlaysDiv = document.createElement('div');
|
||||
overlaysDiv.className = "layersDiv";
|
||||
this.dialog.appendChild(overlaysDiv);
|
||||
|
||||
var overlaysLbl = document.createElement('div');
|
||||
overlaysLbl.className = "layersDiv";
|
||||
overlaysLbl.innerHTML = "Overlays";
|
||||
overlaysDiv.appendChild(overlaysLbl);
|
||||
|
||||
this.overlaysDiv = document.createElement('div');
|
||||
overlaysDiv.appendChild(this.overlaysDiv);
|
||||
}
|
||||
|
||||
createDialog() {
|
||||
this.dialog = document.createElement('div');
|
||||
this.dialog.id = "ogLayerSwitcherDialog";
|
||||
this.dialog.className = "displayNone";
|
||||
this.renderer.div.appendChild(this.dialog);
|
||||
|
||||
this.createBaseLayersContainer();
|
||||
this.createOverlaysContainer();
|
||||
|
||||
if (this.planet) {
|
||||
for (var i = 0; i < this.planet.layers.length; i++) {
|
||||
this.onLayerAdded(this.planet.layers[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
createSwitcher() {
|
||||
var button = document.createElement('div');
|
||||
button.className = 'ogLayerSwitcherButton';
|
||||
button.id = "ogLayerSwitcherButtonMaximize";
|
||||
var that = this;
|
||||
button.onclick = function (e) {
|
||||
if (this.id === "ogLayerSwitcherButtonMaximize") {
|
||||
this.id = "ogLayerSwitcherButtonMinimize";
|
||||
that.dialog.className = "displayBlock";
|
||||
} else {
|
||||
this.id = "ogLayerSwitcherButtonMaximize";
|
||||
that.dialog.className = "displayNone";
|
||||
}
|
||||
};
|
||||
this.renderer.div.appendChild(button);
|
||||
}
|
||||
};
|
||||
|
||||
export { LayerSwitcher };
|
||||
|
||||
327
src/og/control/MouseNavigation.js
Normal file
327
src/og/control/MouseNavigation.js
Normal file
@ -0,0 +1,327 @@
|
||||
/**
|
||||
* @module og/control/MouseNavigation
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as math from '../math.js';
|
||||
import { BaseControl } from './BaseControl.js';
|
||||
import { input } from '../input/input.js';
|
||||
import { Key } from '../Lock.js';
|
||||
import { LonLat } from '../LonLat.js';
|
||||
import { Mat4 } from '../math/Mat4.js';
|
||||
import { Quat } from '../math/Quat.js';
|
||||
import { Ray } from '../math/Ray.js';
|
||||
import { Sphere } from '../bv/Sphere.js';
|
||||
import { Vec3 } from '../math/Vec3.js';
|
||||
|
||||
|
||||
/**
|
||||
* Mouse planet camera dragging control.
|
||||
* @class
|
||||
* @extends {og.control.BaseControl}
|
||||
* @param {Object} [options] - Control options.
|
||||
*/
|
||||
class MouseNavigation extends BaseControl {
|
||||
constructor(options) {
|
||||
super(options);
|
||||
|
||||
options = options || {};
|
||||
|
||||
this.grabbedPoint = new Vec3();
|
||||
this._eye0 = new Vec3();
|
||||
this.pointOnEarth = new Vec3();
|
||||
this.earthUp = new Vec3();
|
||||
this.inertia = 0.007;
|
||||
this.grabbedSpheroid = new Sphere();
|
||||
this.planet = null;
|
||||
this.qRot = new Quat();
|
||||
this.scaleRot = 0;
|
||||
|
||||
this.distDiff = 0.33;
|
||||
this.stepsCount = 5;
|
||||
this.stepsForward = null;
|
||||
this.stepIndex = 0;
|
||||
|
||||
this._keyLock = new Key();
|
||||
}
|
||||
|
||||
static getMovePointsFromPixelTerrain(cam, planet, stepsCount, delta, point, forward, dir) {
|
||||
|
||||
var steps = []
|
||||
|
||||
var eye = cam.eye.clone(),
|
||||
n = cam._n.clone(),
|
||||
u = cam._u.clone(),
|
||||
v = cam._v.clone();
|
||||
|
||||
var a = planet.getCartesianFromPixelTerrain(point, true);
|
||||
|
||||
if (!dir) {
|
||||
dir = Vec3.sub(a, cam.eye).normalize();
|
||||
}
|
||||
|
||||
var d = a ? delta * cam.eye.distance(a) / stepsCount : 1000;
|
||||
|
||||
if (forward) {
|
||||
d = -d;
|
||||
} else {
|
||||
d *= 2;
|
||||
}
|
||||
|
||||
var scaled_n = n.scaleTo(d);
|
||||
|
||||
if (a && cam._lonLat.height > 9000 && cam.slope > 0.6) {
|
||||
var grabbedSpheroid = new Sphere();
|
||||
grabbedSpheroid.radius = a.length();
|
||||
|
||||
var rotArr = [],
|
||||
eyeArr = []
|
||||
|
||||
var breaked = false;
|
||||
for (var i = 0; i < stepsCount; i++) {
|
||||
eye.addA(scaled_n);
|
||||
var b = new Ray(eye, dir).hitSphere(grabbedSpheroid);
|
||||
eyeArr[i] = eye.clone();
|
||||
if (b) {
|
||||
rotArr[i] = new Mat4().rotateBetweenVectors(a.normal(), b.normal());
|
||||
} else {
|
||||
breaked = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!breaked) {
|
||||
for (var i = 0; i < stepsCount; i++) {
|
||||
var rot = rotArr[i];
|
||||
steps[i] = {};
|
||||
steps[i].eye = rot.mulVec3(eyeArr[i]);
|
||||
steps[i].v = rot.mulVec3(v);
|
||||
steps[i].u = rot.mulVec3(u);
|
||||
steps[i].n = rot.mulVec3(n);
|
||||
}
|
||||
} else {
|
||||
eye = cam.eye.clone();
|
||||
for (var i = 0; i < stepsCount; i++) {
|
||||
steps[i] = {};
|
||||
steps[i].eye = eye.addA(scaled_n).clone();
|
||||
steps[i].v = v;
|
||||
steps[i].u = u;
|
||||
steps[i].n = n;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (var i = 0; i < stepsCount; i++) {
|
||||
steps[i] = {};
|
||||
steps[i].eye = eye.addA(dir.scaleTo(-d)).clone();
|
||||
steps[i].v = v;
|
||||
steps[i].u = u;
|
||||
steps[i].n = n;
|
||||
}
|
||||
}
|
||||
|
||||
return steps;
|
||||
}
|
||||
|
||||
onactivate() {
|
||||
this.renderer.events.on("mousewheel", this.onMouseWheel, this);
|
||||
this.renderer.events.on("lhold", this.onMouseLeftButtonDown, this);
|
||||
this.renderer.events.on("rhold", this.onMouseRightButtonDown, this);
|
||||
this.renderer.events.on("ldown", this.onMouseLeftButtonClick, this);
|
||||
this.renderer.events.on("lup", this.onMouseLeftButtonUp, this);
|
||||
this.renderer.events.on("rdown", this.onMouseRightButtonClick, this);
|
||||
this.renderer.events.on("ldblclick", this.onMouseLeftButtonDoubleClick, this);
|
||||
this.renderer.events.on("draw", this.onDraw, this);
|
||||
}
|
||||
|
||||
ondeactivate() {
|
||||
this.renderer.events.off("mousewheel", this.onMouseWheel);
|
||||
this.renderer.events.off("lhold", this.onMouseLeftButtonDown);
|
||||
this.renderer.events.off("rhold", this.onMouseRightButtonDown);
|
||||
this.renderer.events.off("ldown", this.onMouseLeftButtonClick);
|
||||
this.renderer.events.off("lup", this.onMouseLeftButtonUp);
|
||||
this.renderer.events.off("rdown", this.onMouseRightButtonClick);
|
||||
this.renderer.events.off("ldblclick", this.onMouseLeftButtonDoubleClick);
|
||||
this.renderer.events.off("draw", this.onDraw);
|
||||
};
|
||||
|
||||
onMouseWheel(event) {
|
||||
|
||||
if (this.stepIndex)
|
||||
return;
|
||||
|
||||
this.planet.stopFlying();
|
||||
|
||||
this.stopRotation();
|
||||
|
||||
this._deactivate = true;
|
||||
|
||||
this.planet.layerLock.lock(this._keyLock);
|
||||
this.planet.terrainLock.lock(this._keyLock);
|
||||
this.planet._normalMapCreator.lock(this._keyLock);
|
||||
|
||||
var ms = this.renderer.events.mouseState;
|
||||
this.stepIndex = this.stepsCount;
|
||||
this.stepsForward = MouseNavigation.getMovePointsFromPixelTerrain(this.renderer.activeCamera,
|
||||
this.planet, this.stepsCount, this.distDiff, ms, event.wheelDelta > 0, ms.direction);
|
||||
}
|
||||
|
||||
oninit() {
|
||||
this.activate();
|
||||
}
|
||||
|
||||
onMouseLeftButtonDoubleClick() {
|
||||
this.planet.stopFlying();
|
||||
this.stopRotation();
|
||||
var p = this.planet.getCartesianFromPixelTerrain(this.renderer.events.mouseState, true),
|
||||
g = this.planet.ellipsoid.cartesianToLonLat(p);
|
||||
if (this.renderer.events.isKeyPressed(input.KEY_SHIFT)) {
|
||||
this.planet.flyLonLat(new LonLat(g.lon, g.lat, this.renderer.activeCamera.eye.distance(p) * 2.0));
|
||||
} else {
|
||||
this.planet.flyLonLat(new LonLat(g.lon, g.lat, this.renderer.activeCamera.eye.distance(p) * 0.57));
|
||||
}
|
||||
}
|
||||
|
||||
onMouseLeftButtonClick() {
|
||||
if (this._active) {
|
||||
this.renderer.handler.canvas.classList.add("ogGrabbingPoiner");
|
||||
this.grabbedPoint = this.planet.getCartesianFromMouseTerrain(true);
|
||||
if (this.grabbedPoint) {
|
||||
this._eye0.copy(this.renderer.activeCamera.eye);
|
||||
this.grabbedSpheroid.radius = this.grabbedPoint.length();
|
||||
this.stopRotation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stopRotation() {
|
||||
this.qRot.clear();
|
||||
this.planet.layerLock.free(this._keyLock);
|
||||
this.planet.terrainLock.free(this._keyLock);
|
||||
this.planet._normalMapCreator.free(this._keyLock);
|
||||
}
|
||||
|
||||
onMouseLeftButtonUp(e) {
|
||||
this.renderer.handler.canvas.classList.remove("ogGrabbingPoiner");
|
||||
}
|
||||
|
||||
onMouseLeftButtonDown(e) {
|
||||
if (this._active) {
|
||||
if (!this.grabbedPoint)
|
||||
return;
|
||||
|
||||
this.planet.stopFlying();
|
||||
|
||||
if (this.renderer.events.mouseState.moving) {
|
||||
|
||||
var cam = this.renderer.activeCamera;
|
||||
|
||||
if (cam.slope > 0.28) {
|
||||
var targetPoint = new Ray(cam.eye, e.direction).hitSphere(this.grabbedSpheroid);
|
||||
if (targetPoint) {
|
||||
this.scaleRot = 1;
|
||||
this.qRot = Quat.getRotationBetweenVectors(targetPoint.normal(), this.grabbedPoint.normal());
|
||||
var rot = this.qRot;
|
||||
cam.eye = rot.mulVec3(cam.eye);
|
||||
cam._v = rot.mulVec3(cam._v);
|
||||
cam._u = rot.mulVec3(cam._u);
|
||||
cam._n = rot.mulVec3(cam._n);
|
||||
cam.update();
|
||||
}
|
||||
} else {
|
||||
var p0 = this.grabbedPoint,
|
||||
p1 = Vec3.add(p0, cam._u),
|
||||
p2 = Vec3.add(p0, p0.normal());
|
||||
|
||||
var px = new Vec3();
|
||||
if (new Ray(cam.eye, e.direction).hitPlane(p0, p1, p2, px) === Ray.INSIDE) {
|
||||
cam.eye = this._eye0.addA(px.subA(p0).negate());
|
||||
cam.update();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.scaleRot = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onMouseRightButtonClick(e) {
|
||||
this.stopRotation();
|
||||
this.planet.stopFlying();
|
||||
this.pointOnEarth = this.planet.getCartesianFromPixelTerrain({ x: e.x, y: e.y }, true);
|
||||
if (this.pointOnEarth) {
|
||||
this.earthUp = this.pointOnEarth.normal();
|
||||
}
|
||||
};
|
||||
|
||||
onMouseRightButtonDown(e) {
|
||||
var cam = this.renderer.activeCamera;
|
||||
if (this.pointOnEarth && this.renderer.events.mouseState.moving) {
|
||||
this.renderer.controlsBag.scaleRot = 1;
|
||||
var l = 0.5 / cam.eye.distance(this.pointOnEarth) * cam._lonLat.height * math.RADIANS;
|
||||
if (l > 0.007) l = 0.007;
|
||||
cam.rotateHorizontal(l * (e.x - e.prev_x), false, this.pointOnEarth, this.earthUp);
|
||||
cam.rotateVertical(l * (e.y - e.prev_y), this.pointOnEarth);
|
||||
cam.update();
|
||||
}
|
||||
}
|
||||
|
||||
onDraw(e) {
|
||||
|
||||
if (this._active) {
|
||||
|
||||
var r = this.renderer;
|
||||
var cam = r.activeCamera;
|
||||
var prevEye = cam.eye.clone();
|
||||
|
||||
if (this.stepIndex) {
|
||||
r.controlsBag.scaleRot = 1;
|
||||
var sf = this.stepsForward[this.stepsCount - this.stepIndex--];
|
||||
cam.eye = sf.eye;
|
||||
cam._v = sf.v;
|
||||
cam._u = sf.u;
|
||||
cam._n = sf.n;
|
||||
cam.update();
|
||||
} else {
|
||||
if (this._deactivate) {
|
||||
this._deactivate = false;
|
||||
|
||||
this.planet.layerLock.free(this._keyLock);
|
||||
this.planet.terrainLock.free(this._keyLock);
|
||||
this.planet._normalMapCreator.free(this._keyLock);
|
||||
}
|
||||
}
|
||||
|
||||
if (r.events.mouseState.leftButtonDown || !this.scaleRot)
|
||||
return;
|
||||
|
||||
this.scaleRot -= this.inertia;
|
||||
if (this.scaleRot <= 0) {
|
||||
this.scaleRot = 0;
|
||||
} else {
|
||||
r.controlsBag.scaleRot = this.scaleRot;
|
||||
var rot = this.qRot.slerp(Quat.IDENTITY, 1 - this.scaleRot * this.scaleRot * this.scaleRot).normalize();
|
||||
if (!(rot.x || rot.y || rot.z)) {
|
||||
this.scaleRot = 0;
|
||||
}
|
||||
cam.eye = rot.mulVec3(cam.eye);
|
||||
cam._v = rot.mulVec3(cam._v);
|
||||
cam._u = rot.mulVec3(cam._u);
|
||||
cam._n = rot.mulVec3(cam._n);
|
||||
cam.update();
|
||||
}
|
||||
|
||||
if (cam.eye.distance(prevEye) / cam._terrainAltitude > 0.01) {
|
||||
this.planet.layerLock.lock(this._keyLock);
|
||||
this.planet.terrainLock.lock(this._keyLock);
|
||||
this.planet._normalMapCreator.lock(this._keyLock);
|
||||
} else {
|
||||
this.planet.layerLock.free(this._keyLock);
|
||||
this.planet.terrainLock.free(this._keyLock);
|
||||
this.planet._normalMapCreator.free(this._keyLock);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export { MouseNavigation };
|
||||
39
src/og/control/ShowFps.js
Normal file
39
src/og/control/ShowFps.js
Normal file
@ -0,0 +1,39 @@
|
||||
/**
|
||||
* @module og/control/ShowFps
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { BaseControl } from './BaseControl.js';
|
||||
import { print2d } from '../utils/Shared.js';
|
||||
|
||||
/**
|
||||
* Frame per second(FPS) display control.
|
||||
* @class
|
||||
* @extends {og.control.BaseControl}
|
||||
* @param {Object} [options] - Control options.
|
||||
*/
|
||||
class ShowFps extends BaseControl {
|
||||
constructor(options) {
|
||||
super(options);
|
||||
}
|
||||
|
||||
oninit() {
|
||||
var d = document.createElement('div');
|
||||
d.className = 'defaultText ';
|
||||
d.id = "ogShowFpsControl";
|
||||
document.body.appendChild(d);
|
||||
this.renderer.events.on("draw", this._draw, this);
|
||||
}
|
||||
|
||||
|
||||
_draw() {
|
||||
print2d("ogShowFpsControl", (1000.0 / this.renderer.handler.deltaTime).toFixed(1), this.renderer.handler.canvas.clientWidth - 60, 0);
|
||||
}
|
||||
};
|
||||
|
||||
export function showFps(options) {
|
||||
return new ShowFps(options);
|
||||
};
|
||||
|
||||
export { ShowFps };
|
||||
110
src/og/control/SimpleNavigation.js
Normal file
110
src/og/control/SimpleNavigation.js
Normal file
@ -0,0 +1,110 @@
|
||||
/**
|
||||
* @module og/control/SimpleNavigation
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as math from '../math.js';
|
||||
import { BaseControl } from './BaseControl.js';
|
||||
import { input } from '../input/input.js';
|
||||
|
||||
/**
|
||||
* Simple keyboard camera navigation with W,S,A,D and shift keys to fly around the scene.
|
||||
* @class
|
||||
* @extends {og.control.BaseControl}
|
||||
* @param {Object} [options] - Control options.
|
||||
*/
|
||||
class SimpleNavigation extends BaseControl {
|
||||
constructor(options) {
|
||||
options = options || {};
|
||||
super(options);
|
||||
|
||||
this.camera = null;
|
||||
this.speed = options.speed || 1.0;
|
||||
}
|
||||
|
||||
|
||||
oninit() {
|
||||
this.camera = this.renderer.activeCamera;
|
||||
this.renderer.events.on("keypress", input.KEY_W, this.onCameraMoveForward, this);
|
||||
this.renderer.events.on("keypress", input.KEY_S, this.onCameraMoveBackward, this);
|
||||
this.renderer.events.on("keypress", input.KEY_A, this.onCameraStrifeLeft, this);
|
||||
this.renderer.events.on("keypress", input.KEY_D, this.onCameraStrifeRight, this);
|
||||
this.renderer.events.on("keypress", input.KEY_UP, this.onCameraLookUp, this);
|
||||
this.renderer.events.on("keypress", input.KEY_DOWN, this.onCameraLookDown, this);
|
||||
this.renderer.events.on("keypress", input.KEY_LEFT, this.onCameraTurnLeft, this);
|
||||
this.renderer.events.on("keypress", input.KEY_RIGHT, this.onCameraTurnRight, this);
|
||||
this.renderer.events.on("keypress", input.KEY_Q, this.onCameraRollLeft, this);
|
||||
this.renderer.events.on("keypress", input.KEY_E, this.onCameraRollRight, this);
|
||||
}
|
||||
|
||||
onCameraMoveForward(event) {
|
||||
var camera = this.camera;
|
||||
camera.slide(0, 0, -this.speed);
|
||||
camera.update();
|
||||
}
|
||||
|
||||
onCameraMoveBackward(event) {
|
||||
var camera = this.camera;
|
||||
camera.slide(0, 0, this.speed);
|
||||
camera.update();
|
||||
}
|
||||
|
||||
onCameraStrifeLeft(event) {
|
||||
var camera = this.camera;
|
||||
camera.slide(-this.speed, 0, 0);
|
||||
camera.update();
|
||||
}
|
||||
|
||||
onCameraStrifeRight(event) {
|
||||
var camera = this.camera;
|
||||
camera.slide(this.speed, 0, 0);
|
||||
camera.update();
|
||||
}
|
||||
|
||||
onCameraLookUp(event) {
|
||||
var cam = this.camera;
|
||||
cam.pitch(0.5);
|
||||
cam.update();
|
||||
}
|
||||
|
||||
onCameraLookDown(event) {
|
||||
var cam = this.camera;
|
||||
cam.pitch(-0.5);
|
||||
cam.update();
|
||||
}
|
||||
|
||||
onCameraTurnLeft(event) {
|
||||
var cam = this.camera;
|
||||
cam.yaw(0.5);
|
||||
cam.update();
|
||||
}
|
||||
|
||||
onCameraTurnRight(event) {
|
||||
var cam = this.camera;
|
||||
cam.yaw(-0.5);
|
||||
cam.update();
|
||||
}
|
||||
|
||||
onCameraRollLeft(event) {
|
||||
var cam = this.camera;
|
||||
cam.roll(-0.5);
|
||||
cam.update();
|
||||
}
|
||||
|
||||
onCameraRollRight(event) {
|
||||
var cam = this.camera;
|
||||
cam.roll(0.5);
|
||||
cam.update();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates simple navigation control instance.
|
||||
*/
|
||||
export function simpleNavigation(options) {
|
||||
return new SimpleNavigation(options);
|
||||
};
|
||||
|
||||
export { SimpleNavigation };
|
||||
153
src/og/control/Sun.js
Normal file
153
src/og/control/Sun.js
Normal file
@ -0,0 +1,153 @@
|
||||
/**
|
||||
* @module og/control/Sun
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { BaseControl } from './BaseControl.js';
|
||||
import { getSunPosition } from '../astro/earth.js';
|
||||
import { input } from '../input/input.js';
|
||||
import { LightSource } from '../light/LightSource.js';
|
||||
import { Quat } from '../math/Quat.js';
|
||||
import { Vec3 } from '../math/Vec3.js';
|
||||
|
||||
/**
|
||||
* Real Sun geocentric position control that place the Sun on the right place by the Earth.
|
||||
* @class
|
||||
* @extends {og.control.BaseControl}
|
||||
* @param {Object} [options] - Control options.
|
||||
*/
|
||||
class Sun extends BaseControl {
|
||||
constructor(options) {
|
||||
super(options);
|
||||
|
||||
options = options || {};
|
||||
|
||||
/**
|
||||
* Earth planet node.
|
||||
* @public
|
||||
* @type {og.scene.Planet}
|
||||
*/
|
||||
this.planet = null;
|
||||
|
||||
/**
|
||||
* Sunlight position placed in the camera eye.
|
||||
* @private
|
||||
* @type {boolean}
|
||||
*/
|
||||
//this._isCameraSunlight = false;
|
||||
|
||||
this.offsetVertical = options.offsetVertical || -5000000;
|
||||
|
||||
this.offsetHorizontal = options.offsetHorizontal || 5000000;
|
||||
|
||||
/**
|
||||
* Light source.
|
||||
* @public
|
||||
* @type {og.LightSource}
|
||||
*/
|
||||
this.sunlight = null;
|
||||
|
||||
/**
|
||||
* Current frame handler clock date and time.
|
||||
* @private
|
||||
* @type {Number}
|
||||
*/
|
||||
this._currDate = 0;
|
||||
|
||||
/**
|
||||
* Previous frame handler clock date and time.
|
||||
* @private
|
||||
* @type {Number}
|
||||
*/
|
||||
this._prevDate = 0;
|
||||
|
||||
this._clockPtr = null;
|
||||
|
||||
this._lightOn = false;
|
||||
|
||||
this._stopped = options.stopped || false;
|
||||
}
|
||||
|
||||
oninit() {
|
||||
|
||||
this.planet.lightEnabled = true;
|
||||
|
||||
//sunlight initialization
|
||||
this.sunlight = new LightSource("Sun", {
|
||||
'ambient': new Vec3(0.15, 0.15, 0.25),
|
||||
'diffuse': new Vec3(0.9, 0.9, 0.8),
|
||||
'specular': new Vec3(0.1, 0.1, 0.06),
|
||||
'shininess': 110
|
||||
});
|
||||
this.sunlight.addTo(this.planet);
|
||||
|
||||
var that = this;
|
||||
this.renderer.events.on("draw", this._draw, this);
|
||||
|
||||
this.renderer.events.on("charkeypress", input.KEY_L, function () {
|
||||
that.planet.lightEnabled = !that.planet.lightEnabled;
|
||||
});
|
||||
|
||||
if (!this._clockPtr)
|
||||
this._clockPtr = this.renderer.handler.defaultClock;
|
||||
}
|
||||
|
||||
stop() {
|
||||
this._stopped = true;
|
||||
}
|
||||
|
||||
onactivate() {
|
||||
this._stopped = false;
|
||||
}
|
||||
|
||||
bindClock(clock) {
|
||||
this._clockPtr = clock;
|
||||
}
|
||||
|
||||
_draw() {
|
||||
if (!this._stopped) {
|
||||
this._currDate = this._clockPtr.currentDate;
|
||||
var cam = this.renderer.activeCamera;
|
||||
if (cam.getHeight() < 4650000 || !this._active) {
|
||||
this._lightOn = true;
|
||||
this._f = 1;
|
||||
var n = cam.eye.normal();
|
||||
var tu = Vec3.proj_b_to_plane(cam._v, n, cam._v).normalize().scale(this.offsetVertical);
|
||||
var tr = Vec3.proj_b_to_plane(cam._u, n, cam._u).normalize().scale(this.offsetHorizontal);
|
||||
var d = tu.add(tr);
|
||||
var pos = cam.eye.add(d);
|
||||
if (this._k > 0) {
|
||||
this._k -= 0.01;
|
||||
var rot = Quat.getRotationBetweenVectors(this.sunlight._position.normal(), pos.normal());
|
||||
var r = rot.slerp(Quat.IDENTITY, this._k).normalize();
|
||||
this.sunlight.setPosition(r.mulVec3(this.sunlight._position));
|
||||
} else {
|
||||
this.sunlight.setPosition(pos);
|
||||
}
|
||||
} else {
|
||||
this._k = 1;
|
||||
if (this._f > 0) {
|
||||
this._f -= 0.01;
|
||||
var rot = Quat.getRotationBetweenVectors(this.sunlight._position.normal(), getSunPosition(this._currDate).normal());
|
||||
var r = rot.slerp(Quat.IDENTITY, this._f).normalize();
|
||||
this.sunlight.setPosition(r.mulVec3(this.sunlight._position));
|
||||
} else {
|
||||
if (Math.abs(this._currDate - this._prevDate) > 0.00034 && this._active || this._lightOn) {
|
||||
this._lightOn = false;
|
||||
this._prevDate = this._currDate;
|
||||
this.sunlight.setPosition(getSunPosition(this._currDate));
|
||||
this._f = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export function sun(options) {
|
||||
return Sun(options);
|
||||
};
|
||||
|
||||
export { Sun };
|
||||
|
||||
35
src/og/control/ToggleWireframe.js
Normal file
35
src/og/control/ToggleWireframe.js
Normal file
@ -0,0 +1,35 @@
|
||||
/**
|
||||
* @module og/control/ToggleWireframe
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { BaseControl } from './BaseControl.js';
|
||||
import { input } from '../input/input.js';
|
||||
|
||||
/**
|
||||
* Planet GL draw mode(TRIANGLE_STRIP/LINE_STRING) changer.
|
||||
* @class
|
||||
* @extends {og.control.BaseControl}
|
||||
* @param {Object} [options] - Control options.
|
||||
*/
|
||||
class ToggleWireframe extends BaseControl {
|
||||
constructor(options) {
|
||||
super(options);
|
||||
}
|
||||
|
||||
|
||||
oninit() {
|
||||
this.renderer.events.on("charkeypress", input.KEY_X, this.toogleWireframe, this);
|
||||
}
|
||||
|
||||
toogleWireframe(e) {
|
||||
if (this.planet.drawMode === this.renderer.handler.gl.LINE_STRIP) {
|
||||
this.planet.setDrawMode(this.renderer.handler.gl.TRIANGLE_STRIP);
|
||||
} else {
|
||||
this.planet.setDrawMode(this.renderer.handler.gl.LINE_STRIP);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export { ToggleWireframe };
|
||||
310
src/og/control/TouchNavigation.js
Normal file
310
src/og/control/TouchNavigation.js
Normal file
@ -0,0 +1,310 @@
|
||||
/**
|
||||
* @module og/control/TouchNavigation
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as math from '../math.js';
|
||||
import { BaseControl } from './BaseControl.js';
|
||||
import { input } from '../input/input.js';
|
||||
import { Key } from '../Lock.js';
|
||||
import { LonLat } from '../LonLat.js';
|
||||
import { Mat4 } from '../math/Mat4.js';
|
||||
import { Quat } from '../math/Quat.js';
|
||||
import { Ray } from '../math/Ray.js';
|
||||
import { Sphere } from '../bv/Sphere.js';
|
||||
import { Vec3 } from '../math/Vec3.js';
|
||||
|
||||
|
||||
/**
|
||||
* Touch pad planet camera dragging control.
|
||||
* @class
|
||||
* @extends {og.control.BaseControl}
|
||||
* @param {Object} [options] - Control options.
|
||||
*/
|
||||
class TouchNavigation extends BaseControl {
|
||||
constructor(options) {
|
||||
super(options);
|
||||
|
||||
this.grabbedPoint = new Vec3();
|
||||
this.inertia = 0.007;
|
||||
this.grabbedSpheroid = new Sphere();
|
||||
this.planet = null;
|
||||
this.qRot = new Quat();
|
||||
this.scaleRot = 0;
|
||||
this.rot = 1;
|
||||
this._eye0 = new Vec3();
|
||||
|
||||
this.stepsCount = 5;
|
||||
this.stepsForward = null;
|
||||
this.stepIndex = 0;
|
||||
|
||||
var Touch = function () {
|
||||
this.x = 0;
|
||||
this.y = 0;
|
||||
this.prev_x = 0;
|
||||
this.prev_y = 0;
|
||||
this.grabbedPoint = new Vec3();
|
||||
this.grabbedSpheroid = new Sphere();
|
||||
this.dX = function () { return this.x - this.prev_x; };
|
||||
this.dY = function () { return this.y - this.prev_y; };
|
||||
};
|
||||
|
||||
this.pointOnEarth = null;
|
||||
this.earthUp = null;
|
||||
|
||||
this.touches = [new Touch(), new Touch()];
|
||||
|
||||
this._keyLock = new Key();
|
||||
}
|
||||
|
||||
oninit() {
|
||||
this.renderer.events.on("touchstart", this.onTouchStart, this);
|
||||
this.renderer.events.on("touchend", this.onTouchEnd, this);
|
||||
this.renderer.events.on("doubletouch", this.onDoubleTouch, this);
|
||||
this.renderer.events.on("touchcancel", this.onTouchCancel, this);
|
||||
this.renderer.events.on("touchmove", this.onTouchMove, this);
|
||||
this.renderer.events.on("draw", this.onDraw, this);
|
||||
}
|
||||
|
||||
onTouchStart(e) {
|
||||
|
||||
this._touching = true;
|
||||
|
||||
if (e.sys.touches.length === 2) {
|
||||
|
||||
var t0 = this.touches[0],
|
||||
t1 = this.touches[1];
|
||||
|
||||
t0.x = e.sys.touches.item(0).clientX - e.sys.offsetLeft;
|
||||
t0.y = e.sys.touches.item(0).clientY - e.sys.offsetTop;
|
||||
t0.prev_x = e.sys.touches.item(0).clientX - e.sys.offsetLeft;
|
||||
t0.prev_y = e.sys.touches.item(0).clientY - e.sys.offsetTop;
|
||||
t0.grabbedPoint = this.planet.getCartesianFromPixelTerrain(t0, true);
|
||||
|
||||
t1.x = e.sys.touches.item(1).clientX - e.sys.offsetLeft;
|
||||
t1.y = e.sys.touches.item(1).clientY - e.sys.offsetTop;
|
||||
t1.prev_x = e.sys.touches.item(1).clientX - e.sys.offsetLeft;
|
||||
t1.prev_y = e.sys.touches.item(1).clientY - e.sys.offsetTop;
|
||||
t1.grabbedPoint = this.planet.getCartesianFromPixelTerrain(t1, true);
|
||||
|
||||
//this.planet._viewChanged = true;
|
||||
this.pointOnEarth = this.planet.getCartesianFromPixelTerrain(this.renderer.handler.getCenter(), true);
|
||||
|
||||
if (this.pointOnEarth) {
|
||||
this.earthUp = this.pointOnEarth.normal();
|
||||
}
|
||||
|
||||
if (t0.grabbedPoint && t1.grabbedPoint) {
|
||||
t0.grabbedSpheroid.radius = t0.grabbedPoint.length();
|
||||
t1.grabbedSpheroid.radius = t1.grabbedPoint.length();
|
||||
this.stopRotation();
|
||||
}
|
||||
|
||||
} else if (e.sys.touches.length === 1) {
|
||||
this._startTouchOne(e);
|
||||
}
|
||||
}
|
||||
|
||||
_startTouchOne(e) {
|
||||
var t = this.touches[0];
|
||||
|
||||
t.x = e.sys.touches.item(0).clientX - e.sys.offsetLeft;
|
||||
t.y = e.sys.touches.item(0).clientY - e.sys.offsetTop;
|
||||
t.prev_x = e.sys.touches.item(0).clientX - e.sys.offsetLeft;
|
||||
t.prev_y = e.sys.touches.item(0).clientY - e.sys.offsetTop;
|
||||
|
||||
t.grabbedPoint = this.planet.getCartesianFromPixelTerrain(t, true);
|
||||
this._eye0.copy(this.renderer.activeCamera.eye);
|
||||
|
||||
if (t.grabbedPoint) {
|
||||
t.grabbedSpheroid.radius = t.grabbedPoint.length();
|
||||
this.stopRotation();
|
||||
}
|
||||
}
|
||||
|
||||
stopRotation() {
|
||||
this.qRot.clear();
|
||||
this.planet.layerLock.free(this._keyLock);
|
||||
this.planet.terrainLock.free(this._keyLock);
|
||||
this.planet._normalMapCreator.free(this._keyLock);
|
||||
}
|
||||
|
||||
onDoubleTouch(e) {
|
||||
if (this.stepIndex)
|
||||
return;
|
||||
|
||||
this.planet.stopFlying();
|
||||
this.stopRotation();
|
||||
var p = this.planet.getCartesianFromPixelTerrain(this.touches[0], true),
|
||||
g = this.planet.ellipsoid.cartesianToLonLat(p);
|
||||
this.planet.flyLonLat(new LonLat(g.lon, g.lat, this.renderer.activeCamera.eye.distance(p) * 0.57));
|
||||
}
|
||||
|
||||
onTouchEnd(e) {
|
||||
|
||||
if (e.sys.touches.length === 0)
|
||||
this._touching = false;
|
||||
|
||||
if (e.sys.touches.length === 1) {
|
||||
this._startTouchOne(e);
|
||||
}
|
||||
|
||||
if (Math.abs(this.touches[0].x - this.touches[0].prev_x) < 3 &&
|
||||
Math.abs(this.touches[0].y - this.touches[0].prev_y) < 3)
|
||||
this.scaleRot = 0;
|
||||
}
|
||||
|
||||
onTouchCancel(e) {
|
||||
}
|
||||
|
||||
onTouchMove(e) {
|
||||
|
||||
var cam = this.renderer.activeCamera;
|
||||
|
||||
if (e.sys.touches.length === 2) {
|
||||
|
||||
this.renderer.controlsBag.scaleRot = 1;
|
||||
|
||||
var t0 = this.touches[0],
|
||||
t1 = this.touches[1];
|
||||
|
||||
if (!t0.grabbedPoint || !t1.grabbedPoint)
|
||||
return;
|
||||
|
||||
this.planet.stopFlying();
|
||||
|
||||
t0.prev_x = t0.x;
|
||||
t0.prev_y = t0.y;
|
||||
t0.x = e.sys.touches.item(0).clientX - e.sys.offsetLeft;
|
||||
t0.y = e.sys.touches.item(0).clientY - e.sys.offsetTop;
|
||||
|
||||
t1.prev_x = t1.x;
|
||||
t1.prev_y = t1.y;
|
||||
t1.x = e.sys.touches.item(1).clientX - e.sys.offsetLeft;
|
||||
t1.y = e.sys.touches.item(1).clientY - e.sys.offsetTop;
|
||||
|
||||
//var center_x = Math.round(t0.x + (t1.x - t0.x) * 0.5);
|
||||
//var center_y = Math.round(t0.y + (t1.y - t0.y) * 0.5);
|
||||
|
||||
//var dirC = cam.unproject(center_x, center_y);
|
||||
//var targetPointC = this.planet.getCartesianFromPixelTerrain(new og.math.Pixel(center_x, center_y));
|
||||
|
||||
//var dir0 = cam.unproject(t0.x, t0.y);
|
||||
//var targetPoint0 = new og.math.Ray(cam.eye, dir0).hitSphere(t0.grabbedSpheroid);
|
||||
|
||||
//var dir1 = cam.unproject(t1.x, t1.y);
|
||||
//var targetPoint1 = new og.math.Ray(cam.eye, dir1).hitSphere(t1.grabbedSpheroid);
|
||||
|
||||
//print2d("t1", center_x + "," + center_y, 100, 100);
|
||||
//print2d("t2", targetPointC.x + "," + targetPointC.y + "," + targetPointC.z, 100, 120);
|
||||
|
||||
if (t0.dY() > 0 && t1.dY() > 0 || t0.dY() < 0 && t1.dY() < 0 ||
|
||||
t0.dX() > 0 && t1.dX() > 0 || t0.dX() < 0 && t1.dX() < 0) {
|
||||
var l = 0.5 / cam.eye.distance(this.pointOnEarth) * cam._lonLat.height * math.RADIANS;
|
||||
if (l > 0.007) l = 0.007;
|
||||
cam.rotateHorizontal(l * t0.dX(), false, this.pointOnEarth, this.earthUp);
|
||||
cam.rotateVertical(l * t0.dY(), this.pointOnEarth);
|
||||
cam.update();
|
||||
}
|
||||
|
||||
this.scaleRot = 0;
|
||||
|
||||
} else if (e.sys.touches.length === 1) {
|
||||
|
||||
var t = this.touches[0];
|
||||
|
||||
t.prev_x = t.x;
|
||||
t.prev_y = t.y;
|
||||
t.x = e.sys.touches.item(0).clientX - e.sys.offsetLeft;
|
||||
t.y = e.sys.touches.item(0).clientY - e.sys.offsetTop;
|
||||
|
||||
if (!t.grabbedPoint)
|
||||
return;
|
||||
|
||||
this.planet.stopFlying();
|
||||
|
||||
var direction = cam.unproject(t.x, t.y);
|
||||
var targetPoint = new Ray(cam.eye, direction).hitSphere(t.grabbedSpheroid);
|
||||
|
||||
if (targetPoint) {
|
||||
if (cam._n.dot(cam.eye.normal()) > 0.28) {
|
||||
this.qRot = Quat.getRotationBetweenVectors(targetPoint.normal(), t.grabbedPoint.normal());
|
||||
var rot = this.qRot;
|
||||
cam.eye = rot.mulVec3(cam.eye);
|
||||
cam._v = rot.mulVec3(cam._v);
|
||||
cam._u = rot.mulVec3(cam._u);
|
||||
cam._n = rot.mulVec3(cam._n);
|
||||
cam.update();
|
||||
this.scaleRot = 1;
|
||||
} else {
|
||||
var p0 = t.grabbedPoint,
|
||||
p1 = Vec3.add(p0, cam._u),
|
||||
p2 = Vec3.add(p0, p0.normal());
|
||||
var dir = cam.unproject(t.x, t.y);
|
||||
var px = new Vec3();
|
||||
if (new Ray(cam.eye, dir).hitPlane(p0, p1, p2, px) === Ray.INSIDE) {
|
||||
cam.eye = this._eye0.addA(px.subA(p0).negate());
|
||||
cam.update();
|
||||
this.scaleRot = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
onDraw(e) {
|
||||
|
||||
this.renderer.controlsBag.scaleRot = this.scaleRot;
|
||||
|
||||
if (this._touching)
|
||||
return;
|
||||
|
||||
var r = this.renderer;
|
||||
var cam = r.activeCamera;
|
||||
var prevEye = cam.eye.clone();
|
||||
|
||||
if (this.stepIndex) {
|
||||
r.controlsBag.scaleRot = 1;
|
||||
var sf = this.stepsForward[this.stepsCount - this.stepIndex--];
|
||||
var cam = this.renderer.activeCamera;
|
||||
cam.eye = sf.eye;
|
||||
cam._v = sf.v;
|
||||
cam._u = sf.u;
|
||||
cam._n = sf.n;
|
||||
cam.update();
|
||||
}
|
||||
|
||||
if (r.events.mouseState.leftButtonDown || !this.scaleRot)
|
||||
return;
|
||||
|
||||
this.scaleRot -= this.inertia;
|
||||
if (this.scaleRot <= 0)
|
||||
this.scaleRot = 0;
|
||||
else {
|
||||
r.controlsBag.scaleRot = this.scaleRot;
|
||||
var cam = r.activeCamera;
|
||||
var rot = this.qRot.slerp(Quat.IDENTITY, 1 - this.scaleRot * this.scaleRot * this.scaleRot).normalize();
|
||||
if (!(rot.x || rot.y || rot.z)) {
|
||||
this.scaleRot = 0;
|
||||
}
|
||||
cam.eye = rot.mulVec3(cam.eye);
|
||||
cam._v = rot.mulVec3(cam._v);
|
||||
cam._u = rot.mulVec3(cam._u);
|
||||
cam._n = rot.mulVec3(cam._n);
|
||||
cam.update();
|
||||
}
|
||||
|
||||
if (cam.eye.distance(prevEye) / cam._terrainAltitude > 0.01) {
|
||||
this.planet.layerLock.lock(this._keyLock);
|
||||
this.planet.terrainLock.lock(this._keyLock);
|
||||
this.planet._normalMapCreator.lock(this._keyLock);
|
||||
} else {
|
||||
this.planet.layerLock.free(this._keyLock);
|
||||
this.planet.terrainLock.free(this._keyLock);
|
||||
this.planet._normalMapCreator.free(this._keyLock);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export { TouchNavigation };
|
||||
122
src/og/control/ZoomControl.js
Normal file
122
src/og/control/ZoomControl.js
Normal file
@ -0,0 +1,122 @@
|
||||
/**
|
||||
* @module og/control/ZoomControl
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { BaseControl } from './BaseControl.js';
|
||||
import { Key } from '../Lock.js';
|
||||
import { MouseNavigation } from './MouseNavigation.js';
|
||||
|
||||
|
||||
/**
|
||||
* Planet zoom buttons control.
|
||||
* @class
|
||||
* @extends {og.control.BaseControl}
|
||||
* @params {Object} [options] - Control options.
|
||||
*/
|
||||
class ZoomControl extends BaseControl {
|
||||
constructor(options) {
|
||||
super(options);
|
||||
|
||||
options = options || {};
|
||||
|
||||
this.distDiff = 0.33;
|
||||
this.stepsCount = 5;
|
||||
this.stepsForward = null;
|
||||
this.stepIndex = 0;
|
||||
this._keyLock = new Key();
|
||||
|
||||
this.planet = null;
|
||||
}
|
||||
|
||||
oninit() {
|
||||
var zoomDiv = document.createElement('div'),
|
||||
btnZoomIn = document.createElement('button'),
|
||||
btnZoomOut = document.createElement('button');
|
||||
|
||||
zoomDiv.className = 'ogZoomControl';
|
||||
btnZoomIn.className = 'ogZoomButton ogZoomIn';
|
||||
btnZoomOut.className = 'ogZoomButton ogZoomOut';
|
||||
|
||||
zoomDiv.appendChild(btnZoomIn);
|
||||
zoomDiv.appendChild(btnZoomOut);
|
||||
|
||||
this.renderer.div.appendChild(zoomDiv);
|
||||
|
||||
var that = this;
|
||||
btnZoomIn.onclick = function (e) {
|
||||
that.zoomIn();
|
||||
};
|
||||
|
||||
btnZoomOut.onclick = function (e) {
|
||||
that.zoomOut();
|
||||
};
|
||||
|
||||
this.renderer.events.on("draw", this._draw, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Planet zoom in.
|
||||
* @public
|
||||
*/
|
||||
zoomIn() {
|
||||
|
||||
this._deactivate = true;
|
||||
|
||||
this.planet.layerLock.lock(this._keyLock);
|
||||
this.planet.terrainLock.lock(this._keyLock);
|
||||
this.planet._normalMapCreator.lock(this._keyLock);
|
||||
|
||||
this.stepIndex = this.stepsCount;
|
||||
this.stepsForward = MouseNavigation.getMovePointsFromPixelTerrain(this.renderer.activeCamera,
|
||||
this.planet, this.stepsCount, this.distDiff * 1.7, this.renderer.getCenter(), true, this.renderer.activeCamera._n.negateTo());
|
||||
}
|
||||
|
||||
/**
|
||||
* Planet zoom out.
|
||||
* @public
|
||||
*/
|
||||
zoomOut() {
|
||||
|
||||
this._deactivate = true;
|
||||
|
||||
this.planet.layerLock.lock(this._keyLock);
|
||||
this.planet.terrainLock.lock(this._keyLock);
|
||||
this.planet._normalMapCreator.lock(this._keyLock);
|
||||
|
||||
this.stepIndex = this.stepsCount;
|
||||
this.stepsForward = MouseNavigation.getMovePointsFromPixelTerrain(this.renderer.activeCamera,
|
||||
this.planet, this.stepsCount, this.distDiff * 2, this.renderer.getCenter(), false, this.renderer.activeCamera._n.negateTo());
|
||||
}
|
||||
|
||||
_draw(e) {
|
||||
|
||||
var cam = this.renderer.activeCamera;
|
||||
|
||||
if (this.stepIndex) {
|
||||
var sf = this.stepsForward[this.stepsCount - this.stepIndex--];
|
||||
cam.eye = sf.eye;
|
||||
cam._v = sf.v;
|
||||
cam._u = sf.u;
|
||||
cam._n = sf.n;
|
||||
cam.update();
|
||||
} else if (!cam._flying) {
|
||||
if (this._deactivate) {
|
||||
|
||||
this.planet.layerLock.free(this._keyLock);
|
||||
this.planet.terrainLock.free(this._keyLock);
|
||||
this.planet._normalMapCreator.free(this._keyLock);
|
||||
|
||||
this._deactivate = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
export function zoomControl(options) {
|
||||
return new ZoomControl(options);
|
||||
};
|
||||
|
||||
export { ZoomControl };
|
||||
393
src/og/ellipsoid/Ellipsoid.js
Normal file
393
src/og/ellipsoid/Ellipsoid.js
Normal file
@ -0,0 +1,393 @@
|
||||
/**
|
||||
* @module og/ellipsoid/Ellipsoid
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as math from '../math.js';
|
||||
import { Vec3 } from '../math/Vec3.js';
|
||||
import { LonLat } from '../LonLat.js';
|
||||
|
||||
/**
|
||||
* Class represents a plant ellipsoid.
|
||||
* @class
|
||||
* @param {number} equatorialSize - Equatorial ellipsoid size.
|
||||
* @param {number} polarSize - Polar ellipsoid size.
|
||||
*/
|
||||
class Ellipsoid {
|
||||
constructor(equatorialSize, polarSize) {
|
||||
this._a = equatorialSize;
|
||||
this._b = polarSize;
|
||||
this._flattening = equatorialSize / polarSize;
|
||||
|
||||
this._a2 = equatorialSize * equatorialSize;
|
||||
this._b2 = polarSize * polarSize;
|
||||
|
||||
var qa2b2 = Math.sqrt(this._a2 - this._b2);
|
||||
|
||||
this._e = qa2b2 / equatorialSize;
|
||||
this._e2 = this._e * this._e;
|
||||
this._e22 = this._e2 * this._e2;
|
||||
|
||||
this._k = qa2b2 / polarSize;
|
||||
this._k2 = this._k * this._k;
|
||||
|
||||
this._radii = new Vec3(equatorialSize, polarSize, equatorialSize);
|
||||
this._radii2 = new Vec3(this._a2, this._b2, this._a2);
|
||||
this._invRadii = new Vec3(1 / equatorialSize, 1 / polarSize, 1 / equatorialSize);
|
||||
this._invRadii2 = new Vec3(1 / this._a2, 1 / this._b2, 1 / this._a2);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets ellipsoid equatorial size.
|
||||
* @public
|
||||
* @returns {number} -
|
||||
*/
|
||||
getEquatorialSize() {
|
||||
return this._a;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets ellipsoid polar size.
|
||||
* @public
|
||||
* @returns {number} -
|
||||
*/
|
||||
getPolarSize() {
|
||||
return this._b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets cartesian ECEF from Wgs84 geodetic coordiantes.
|
||||
* @public
|
||||
* @param {og.LonLat} lonlat - Degrees geodetic coordiantes.
|
||||
* @returns {og.math.Vector3} -
|
||||
*/
|
||||
lonLatToCartesian(lonlat) {
|
||||
var latrad = math.RADIANS * lonlat.lat,
|
||||
lonrad = math.RADIANS * lonlat.lon;
|
||||
|
||||
var slt = Math.sin(latrad);
|
||||
|
||||
var N = this._a / Math.sqrt(1 - this._e2 * slt * slt);
|
||||
var nc = (N + lonlat.height) * Math.cos(latrad);
|
||||
|
||||
return new Vec3(
|
||||
nc * Math.sin(lonrad),
|
||||
(N * (1 - this._e2) + lonlat.height) * slt,
|
||||
nc * Math.cos(lonrad));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets Wgs84 geodetic coordiantes from cartesian ECEF.
|
||||
* @public
|
||||
* @param {og.math.Vector3} cartesian - Cartesian coordinates.
|
||||
* @returns {og.LonLat} -
|
||||
*/
|
||||
cartesianToLonLat(cartesian) {
|
||||
var x = cartesian.z, y = cartesian.x, z = cartesian.y;
|
||||
var ecc2 = this._e2;
|
||||
var ecc22 = this._e22;
|
||||
var r2 = x * x + y * y;
|
||||
var r = Math.sqrt(r2);
|
||||
var e2 = this._a2 - this._b2;
|
||||
var z2 = z * z;
|
||||
var f = 54.0 * this._b2 * z2;
|
||||
var g = r2 + (1 - ecc2) * z2 + ecc2 * e2;
|
||||
var g2 = g * g;
|
||||
var c = ecc22 * f * r2 / (g2 * g);
|
||||
var s = Math.pow((1 + c + Math.sqrt(c * (c + 2))), 0.33333333333333333);
|
||||
var p = f / (3 * Math.pow((1 + s + 1 / s), 2) * g2);
|
||||
var q = Math.sqrt(1 + 2 * ecc22 * p);
|
||||
var r0 = -(p * ecc2 * r) / 1 + q + Math.sqrt(0.5 * this._a2 * (1 + 1 / q) - p * (1 - ecc2) * z2 / (q * (1 + q)) - 0.5 * p * r2);
|
||||
var recc2r0 = r - ecc2 * r0;
|
||||
var recc2r02 = recc2r0 * recc2r0;
|
||||
var u = Math.sqrt(recc2r02 + z2);
|
||||
var v = Math.sqrt(recc2r02 + (1 - ecc2) * z2);
|
||||
var z0 = this._b2 * z / (this._a * v);
|
||||
var h = u * (1 - this._b2 / (this._a * v));
|
||||
var phi = Math.atan((z + this._k2 * z0) / r);
|
||||
var lambda = Math.atan2(y, x);
|
||||
var lat = phi * math.DEGREES;
|
||||
var lon = lambda * math.DEGREES;
|
||||
return new LonLat(lon, lat, h);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets ellipsoid surface normal.
|
||||
* @public
|
||||
* @param {og.math.Vector3} coord - Spatial coordiantes.
|
||||
* @returns {og.math.Vector3} -
|
||||
*/
|
||||
getSurfaceNormal3v(coord) {
|
||||
var r2 = this._invRadii2;
|
||||
var nx = coord.x * r2.x, ny = coord.y * r2.y, nz = coord.z * r2.z;
|
||||
var l = 1 / Math.sqrt(nx * nx + ny * ny + nz * nz);
|
||||
return new Vec3(nx * l, ny * l, nz * l);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the cartesian point on the height over the ellipsoid surface.
|
||||
* @public
|
||||
* @param {og.math.Vector3} coord - Spatial ellipsoid coordiantes.
|
||||
* @param {number} h - Height this spatial coordinates.
|
||||
* @return {og.math.Vector3} -
|
||||
*/
|
||||
getSurfaceHeight3v(coord, h) {
|
||||
var r2 = this._invRadii2;
|
||||
var nx = coord.x * r2.x, ny = coord.y * r2.y, nz = coord.z * r2.z;
|
||||
var l = 1 / Math.sqrt(nx * nx + ny * ny + nz * nz);
|
||||
return new Vec3(coord.x + h * nx * l, coord.y + h * ny * l, coord.z + h * nz * l);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the distance from one point to another(using haversine formula) on the great circle.
|
||||
* @param {og.LonLat} lonLat1 - Longitude/latitude of source point.
|
||||
* @param {og.LonLat} lonLat2 - Longitude/latitude of destination point.
|
||||
* @return {number} Distance between points.
|
||||
*/
|
||||
getGreatCircleDistance(lonLat1, lonLat2) {
|
||||
var dLat = (lonLat2.lat - lonLat1.lat) * math.RADIANS;
|
||||
var dLon = (lonLat2.lon - lonLat1.lon) * math.RADIANS;
|
||||
var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
|
||||
Math.sin(dLon / 2) * Math.sin(dLon / 2) * Math.cos(lonLat1.lat * math.RADIANS) * Math.cos(lonLat2.lat * math.RADIANS);
|
||||
return this._a * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the midpoint between two points on the great circle.
|
||||
* @param {og.LonLat} lonLat1 - Longitude/latitude of first point.
|
||||
* @param {og.LonLat} lonLat2 - Longitude/latitude of second point.
|
||||
* @return {og.LonLat} Midpoint between points.
|
||||
*/
|
||||
getMiddlePointOnGreatCircle(lonLat1, lonLat2) {
|
||||
var f1 = lonLat1.lat * math.RADIANS,
|
||||
l1 = lonLat1.lon * math.RADIANS;
|
||||
var f2 = lonLat2.lat * math.RADIANS;
|
||||
var dl = (lonLat2.lon - lonLat1.lon) * math.RADIANS;
|
||||
|
||||
var Bx = Math.cos(f2) * Math.cos(dl);
|
||||
var By = Math.cos(f2) * Math.sin(dl);
|
||||
|
||||
var x = Math.sqrt((Math.cos(f1) + Bx) * (Math.cos(f1) + Bx) + By * By);
|
||||
var y = Math.sin(f1) + Math.sin(f2);
|
||||
var f3 = Math.atan2(y, x);
|
||||
|
||||
var l3 = l1 + Math.atan2(By, Math.cos(f1) + Bx);
|
||||
|
||||
return new LonLat((l3 * math.DEGREES + 540) % 360 - 180, f3 * math.DEGREES);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the point at given fraction between two points on the great circle.
|
||||
* @param {og.LonLat} lonLat1 - Longitude/Latitude of source point.
|
||||
* @param {og.LonLat} lonLat2 - Longitude/Latitude of destination point.
|
||||
* @param {number} fraction - Fraction between the two points (0 = source point, 1 = destination point).
|
||||
* @returns {og.LonLat} Intermediate point between points.
|
||||
*/
|
||||
getIntermediatePointOnGreatCircle(lonLat1, lonLat2, fraction) {
|
||||
var f1 = lonLat1.lat * math.RADIANS, l1 = lonLat1.lon * math.RADIANS;
|
||||
var f2 = lonLat2.lat * math.RADIANS, l2 = lonLat2.lon * math.RADIANS;
|
||||
|
||||
var sinf1 = Math.sin(f1), cosf1 = Math.cos(f1), sinl1 = Math.sin(l1), cosl1 = Math.cos(l1);
|
||||
var sinf2 = Math.sin(f2), cosf2 = Math.cos(f2), sinl2 = Math.sin(l2), cosl2 = Math.cos(l2);
|
||||
|
||||
var df = f2 - f1,
|
||||
dl = l2 - l1;
|
||||
var a = Math.sin(df / 2) * Math.sin(df / 2) + Math.cos(f1) * Math.cos(f2) * Math.sin(dl / 2) * Math.sin(dl / 2);
|
||||
var d = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
||||
|
||||
var A = Math.sin((1 - fraction) * d) / Math.sin(d);
|
||||
var B = Math.sin(fraction * d) / Math.sin(d);
|
||||
|
||||
var x = A * cosf1 * cosl1 + B * cosf2 * cosl2;
|
||||
var y = A * cosf1 * sinl1 + B * cosf2 * sinl2;
|
||||
var z = A * sinf1 + B * sinf2;
|
||||
|
||||
var f3 = Math.atan2(z, Math.sqrt(x * x + y * y));
|
||||
var l3 = Math.atan2(y, x);
|
||||
|
||||
return new LonLat((l3 * math.DEGREES + 540) % 360 - 180, f3 * math.DEGREES);
|
||||
}
|
||||
|
||||
getRhumbBearing(lonLat1, lonLat2) {
|
||||
var dLon = (lonLat2.lon - lonLat1.lon) * math.RADIANS;
|
||||
var dPhi = Math.log(Math.tan(lonLat2.lat * math.RADIANS / 2 + Math.PI / 4) / Math.tan(lonLat1.lat * math.RADIANS / 2 + Math.PI / 4));
|
||||
if (Math.abs(dLon) > Math.PI) {
|
||||
if (dLon > 0) {
|
||||
dLon = (2 * Math.PI - dLon) * -1;
|
||||
}
|
||||
else {
|
||||
dLon = 2 * Math.PI + dLon;
|
||||
}
|
||||
}
|
||||
return (Math.atan2(dLon, dPhi) * math.DEGREES + 360) % 360;
|
||||
}
|
||||
|
||||
getBearing(lonLat1, lonLat2) {
|
||||
var f1 = lonLat1.lat * math.RADIANS, l1 = lonLat1.lon * math.RADIANS;
|
||||
var f2 = lonLat2.lat * math.RADIANS, l2 = lonLat2.lon * math.RADIANS;
|
||||
var y = Math.sin(l2 - l1) * Math.cos(f2);
|
||||
var x = Math.cos(f1) * Math.sin(f2) - Math.sin(f1) * Math.cos(f2) * Math.cos(l2 - l1);
|
||||
return Math.atan2(y, x) * math.DEGREES;
|
||||
}
|
||||
|
||||
getBearingDestination(lonLat1, bearing, distance) {
|
||||
bearing = bearing * math.RADIANS;
|
||||
var nlon = (lonLat1.lon + 540) % 360 - 180;
|
||||
var f1 = lonLat1.lat * math.RADIANS, l1 = nlon * math.RADIANS;
|
||||
var dR = distance / this._a;
|
||||
var f2 = Math.asin(Math.sin(f1) * Math.cos(dR) + Math.cos(f1) * Math.sin(dR) * Math.cos(bearing));
|
||||
return new og.LonLat((l1 + Math.atan2(Math.sin(bearing) * Math.sin(dR) * Math.cos(f1), Math.cos(dR) - Math.sin(f1) * Math.sin(f2)))
|
||||
* math.DEGREES, f2 * math.DEGREES);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the (initial) bearing from source to destination point on the great circle.
|
||||
* @param {og.LonLat} lonLat1 - Longitude/latitude of source point.
|
||||
* @param {og.LonLat} lonLat2 - Longitude/latitude of destination point.
|
||||
* @return {number} Initial bearing in degrees from north.
|
||||
*/
|
||||
getInitialBearing(lonLat1, lonLat2) {
|
||||
var f1 = lonLat1.lat * math.RADIANS,
|
||||
f2 = lonLat2.lat * math.RADIANS;
|
||||
var dl = (lonLat2.lon - lonLat1.lon) * math.RADIANS;
|
||||
var y = Math.sin(dl) * Math.cos(f2);
|
||||
var x = Math.cos(f1) * Math.sin(f2) -
|
||||
Math.sin(f1) * Math.cos(f2) * Math.cos(dl);
|
||||
var D = Math.atan2(y, x);
|
||||
return (D * math.DEGREES + 360) % 360;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns final bearing arriving at destination destination point from lonLat1 point; the final bearing
|
||||
* will differ from the initial bearing by varying degrees according to distance and latitude.
|
||||
* @param {og.LonLat} lonLat1 - Longitude/latitude of source point.
|
||||
* @param {og.LonLat} lonLat2 - Longitude/latitude of destination point.
|
||||
* @return {number} Final bearing in degrees from north.
|
||||
*/
|
||||
getFinalBearing(lonLat1, lonLat2) {
|
||||
// get initial bearing from destination lonLat2 to lonLat1 & reverse it by adding 180°
|
||||
return (this.getInitialBearing(lonLat2, lonLat1) + 180) % 360;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the point of intersection of two paths defined by point and bearing.
|
||||
* @param {og.LonLat} p1 - First point.
|
||||
* @param {number} brng1 - Initial bearing from first point.
|
||||
* @param {og.LonLat} p2 - Second point.
|
||||
* @param {number} brng2 - Initial bearing from second point.
|
||||
* @return {og.LonLat|null} Destination point (null if no unique intersection defined).
|
||||
*/
|
||||
intersection(p1, brng1, p2, brng2) {
|
||||
var f1 = p1.lat * math.RADIANS,
|
||||
l1 = p1.lon * math.RADIANS;
|
||||
var f2 = p2.lat * math.RADIANS,
|
||||
l2 = p2.lon * math.RADIANS;
|
||||
var D13 = brng1 * math.RADIANS,
|
||||
D23 = brng2 * math.RADIANS;
|
||||
var df = f2 - f1,
|
||||
dl = l2 - l1;
|
||||
|
||||
var d12 = 2 * Math.asin(Math.sqrt(Math.sin(df / 2) * Math.sin(df / 2)
|
||||
+ Math.cos(f1) * Math.cos(f2) * Math.sin(dl / 2) * Math.sin(dl / 2)));
|
||||
if (d12 == 0) return null;
|
||||
|
||||
// initial/final bearings between points
|
||||
var Da = Math.acos((Math.sin(f2) - Math.sin(f1) * Math.cos(d12)) / (Math.sin(d12) * Math.cos(f1)));
|
||||
if (isNaN(Da)) Da = 0; // protect against rounding
|
||||
var Db = Math.acos((Math.sin(f1) - Math.sin(f2) * Math.cos(d12)) / (Math.sin(d12) * Math.cos(f2)));
|
||||
|
||||
var D12 = Math.sin(l2 - l1) > 0 ? Da : 2 * Math.PI - Da;
|
||||
var D21 = Math.sin(l2 - l1) > 0 ? 2 * Math.PI - Db : Db;
|
||||
|
||||
var a1 = (D13 - D12 + Math.PI) % (2 * Math.PI) - Math.PI;
|
||||
var a2 = (D21 - D23 + Math.PI) % (2 * Math.PI) - Math.PI;
|
||||
|
||||
if (Math.sin(a1) == 0 && Math.sin(a2) == 0) return null; // infinite intersections
|
||||
if (Math.sin(a1) * Math.sin(a2) < 0) return null; // ambiguous intersection
|
||||
|
||||
//a1 = Math.abs(a1);
|
||||
//a2 = Math.abs(a2);
|
||||
// ... Ed Williams takes abs of a1/a2, but seems to break calculation?
|
||||
|
||||
var a3 = Math.acos(-Math.cos(a1) * Math.cos(a2) + Math.sin(a1) * Math.sin(a2) * Math.cos(d12));
|
||||
var d13 = Math.atan2(Math.sin(d12) * Math.sin(a1) * Math.sin(a2), Math.cos(a2) + Math.cos(a1) * Math.cos(a3));
|
||||
var f3 = Math.asin(Math.sin(f1) * Math.cos(d13) + Math.cos(f1) * Math.sin(d13) * Math.cos(D13));
|
||||
var dl13 = Math.atan2(Math.sin(D13) * Math.sin(d13) * Math.cos(f1), Math.cos(d13) - Math.sin(f1) * Math.sin(f3));
|
||||
var l3 = l1 + dl13;
|
||||
|
||||
return new LonLat((l3 * math.DEGREES + 540) % 360 - 180, f3 * math.DEGREES);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns ray vector hit ellipsoid coordinates.
|
||||
* If the ray doesn't hit ellipsoid returns null.
|
||||
* @public
|
||||
* @param {og.math.Vector3} origin - Ray origin point.
|
||||
* @param {og.math.Vector3} direction - Ray direction.
|
||||
* @returns {og.math.Vector3} -
|
||||
*/
|
||||
hitRay(origin, direction) {
|
||||
|
||||
var q = this._invRadii.mul(origin);
|
||||
var w = this._invRadii.mul(direction);
|
||||
|
||||
var q2 = q.dot(q);
|
||||
var qw = q.dot(w);
|
||||
|
||||
var difference, w2, product, discriminant, temp;
|
||||
|
||||
if (q2 > 1.0) {
|
||||
// Outside ellipsoid.
|
||||
if (qw >= 0.0) {
|
||||
// Looking outward or tangent (0 intersections).
|
||||
return null;
|
||||
}
|
||||
|
||||
// qw < 0.0.
|
||||
var qw2 = qw * qw;
|
||||
difference = q2 - 1.0; // Positively valued.
|
||||
w2 = w.dot(w);
|
||||
product = w2 * difference;
|
||||
|
||||
if (qw2 < product) {
|
||||
// Imaginary roots (0 intersections).
|
||||
return null;
|
||||
} else if (qw2 > product) {
|
||||
// Distinct roots (2 intersections).
|
||||
discriminant = qw * qw - product;
|
||||
temp = -qw + Math.sqrt(discriminant); // Avoid cancellation.
|
||||
var root0 = temp / w2;
|
||||
var root1 = difference / temp;
|
||||
if (root0 < root1) {
|
||||
return origin.add(direction.scaleTo(root0));
|
||||
}
|
||||
return origin.add(direction.scaleTo(root1));
|
||||
} else {
|
||||
// qw2 == product. Repeated roots (2 intersections).
|
||||
var root = Math.sqrt(difference / w2);
|
||||
return origin.add(direction.scaleTo(root));
|
||||
}
|
||||
} else if (q2 < 1.0) {
|
||||
// Inside ellipsoid (2 intersections).
|
||||
difference = q2 - 1.0; // Negatively valued.
|
||||
w2 = w.dot(w);
|
||||
product = w2 * difference; // Negatively valued.
|
||||
discriminant = qw * qw - product;
|
||||
temp = -qw + Math.sqrt(discriminant); // Positively valued.
|
||||
return origin.add(direction.scaleTo(temp / w2));
|
||||
} else {
|
||||
// q2 == 1.0. On ellipsoid.
|
||||
if (qw < 0.0) {
|
||||
// Looking inward.
|
||||
w2 = w.dot(w);
|
||||
return origin.add(direction.scaleTo(-qw / w2));
|
||||
}
|
||||
// qw >= 0.0. Looking outward or tangent.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export { Ellipsoid };
|
||||
352
src/og/entity/BaseBillboard.js
Normal file
352
src/og/entity/BaseBillboard.js
Normal file
@ -0,0 +1,352 @@
|
||||
/**
|
||||
* @module og/entity/BaseBillboard
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as utils from '../utils/shared.js';
|
||||
|
||||
/**
|
||||
* Base prototype for billboard and label classes.
|
||||
* @class
|
||||
* @param {Object} [options] - Options:
|
||||
* @param {og.math.Vector3|Array.<number>} [options.position] - Billboard spatial position.
|
||||
* @param {number} [options.rotation] - Screen angle rotaion.
|
||||
* @param {og.math.Vector4|string|Array.<number>} [options.color] - Billboard color.
|
||||
* @param {og.math.Vector3|Array.<number>} [options.alignedAxis] - Billboard aligned vector.
|
||||
* @param {og.math.Vector3|Array.<number>} [options.offset] - Billboard center screen offset.
|
||||
* @param {boolean} [options.visibility] - Visibility.
|
||||
*/
|
||||
class BaseBillboard {
|
||||
constructor(options) {
|
||||
options = options || {};
|
||||
|
||||
/**
|
||||
* Object unic identifier.
|
||||
* @public
|
||||
* @readonly
|
||||
* @type {number}
|
||||
*/
|
||||
this.id = BaseBillboard._staticCounter++;
|
||||
|
||||
/**
|
||||
* Billboard center cartesian position.
|
||||
* @protected
|
||||
* @type {og.math.Vector3}
|
||||
*/
|
||||
this._position = utils.createVector3(options.position);
|
||||
|
||||
/**
|
||||
* Screen space rotation angle.
|
||||
* @protected
|
||||
* @type {number}
|
||||
*/
|
||||
this._rotation = options.rotation || 0;
|
||||
|
||||
/**
|
||||
* RGBA color.
|
||||
* @protected
|
||||
* @type {og.math.Vector4}
|
||||
*/
|
||||
this._color = utils.createColorRGBA(options.color);
|
||||
|
||||
/**
|
||||
* Cartesian aligned axis vector.
|
||||
* @protected
|
||||
* @type {og.math.Vector3}
|
||||
*/
|
||||
this._alignedAxis = utils.createVector3(options.alignedAxis);
|
||||
|
||||
/**
|
||||
* Billboard center screen space offset. Where x,y - screen space offset and z - depth offset.
|
||||
* @protected
|
||||
* @type {og.math.Vecto3}
|
||||
*/
|
||||
this._offset = utils.createVector3(options.offset);
|
||||
|
||||
/**
|
||||
* Billboard visibility.
|
||||
* @protected
|
||||
* @type {boolean}
|
||||
*/
|
||||
this._visibility = options.visibility != undefined ? options.visibility : true;
|
||||
|
||||
/**
|
||||
* Billboard scale.
|
||||
* @protected
|
||||
* @type {number}
|
||||
*/
|
||||
this._scale = options.scale || 1.0;
|
||||
|
||||
/**
|
||||
* Entity instance that holds this billboard.
|
||||
* @protected
|
||||
* @type {og.Entity}
|
||||
*/
|
||||
this._entity = null;
|
||||
|
||||
/**
|
||||
* Handler that stores and renders this billboard object.
|
||||
* @protected
|
||||
* @type {og.BillboardHandler}
|
||||
*/
|
||||
this._handler = null;
|
||||
|
||||
/**
|
||||
* Billboard handler array index.
|
||||
* @protected
|
||||
* @type {number}
|
||||
*/
|
||||
this._handlerIndex = -1;
|
||||
}
|
||||
|
||||
static get _staticCounter() {
|
||||
if (!this._counter && this._counter !== 0) {
|
||||
this._counter = 0;
|
||||
}
|
||||
return this._counter;
|
||||
}
|
||||
|
||||
static set _staticCounter(n) {
|
||||
this._counter = n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets billboard position.
|
||||
* @public
|
||||
* @param {number} x - X coordinate.
|
||||
* @param {number} y - Y coordinate.
|
||||
* @param {number} z - Z coordinate.
|
||||
*/
|
||||
setPosition(x, y, z) {
|
||||
this._position.x = x;
|
||||
this._position.y = y;
|
||||
this._position.z = z;
|
||||
this._handler && this._handler.setPositionArr(this._handlerIndex, this._position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets billboard position.
|
||||
* @public
|
||||
* @param {og.math.Vector3} position - Cartesian coordinates.
|
||||
*/
|
||||
setPosition3v(position) {
|
||||
this._position.x = position.x;
|
||||
this._position.y = position.y;
|
||||
this._position.z = position.z;
|
||||
this._handler && this._handler.setPositionArr(this._handlerIndex, position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns billboard position.
|
||||
* @public
|
||||
* @returns {og.math.Vector3}
|
||||
*/
|
||||
getPosition() {
|
||||
return this._position;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets screen space offset.
|
||||
* @public
|
||||
* @param {number} x - X offset.
|
||||
* @param {number} y - Y offset.
|
||||
* @param {number} [z] - Z offset.
|
||||
*/
|
||||
setOffset(x, y, z) {
|
||||
this._offset.x = x;
|
||||
this._offset.y = y;
|
||||
(z != undefined) && (this._offset.z = z);
|
||||
this._handler && this._handler.setOffsetArr(this._handlerIndex, this._offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets billboard scale.
|
||||
* @public
|
||||
* @param {number} scale - Scale.
|
||||
*/
|
||||
setScale(scale) {
|
||||
this._scale = scale;
|
||||
this._handler && this._handler.setScaleArr(this._handlerIndex, scale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets billboard scale.
|
||||
* @public
|
||||
* @returns {number}
|
||||
*/
|
||||
getScale() {
|
||||
return this._scale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets screen space offset.
|
||||
* @public
|
||||
* @param {og.math.Vector2} offset - Offset size.
|
||||
*/
|
||||
setOffset3v(offset) {
|
||||
this._offset.x = offset.x;
|
||||
this._offset.y = offset.y;
|
||||
(offset.z != undefined) && (this._offset.z = offset.z);
|
||||
this._handler && this._handler.setOffsetArr(this._handlerIndex, offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns billboard screen space offset size.
|
||||
* @public
|
||||
* @returns {og.math.Vector3}
|
||||
*/
|
||||
getOffset() {
|
||||
return this._offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets billboard screen space rotation in radians.
|
||||
* @public
|
||||
* @param {number} rotation - Screen space rotation in radians.
|
||||
*/
|
||||
setRotation(rotation) {
|
||||
this._rotation = rotation;
|
||||
this._handler && this._handler.setRotationArr(this._handlerIndex, rotation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets screen space rotation.
|
||||
* @public
|
||||
* @returns {number}
|
||||
*/
|
||||
getRotation() {
|
||||
return this._rotation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets billboard opacity.
|
||||
* @public
|
||||
* @param {number} a - Billboard opacity.
|
||||
*/
|
||||
setOpacity(a) {
|
||||
this._color.w = a;
|
||||
this.setColor(this._color.x, this._color.y, this._color.z, a);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets RGBA color. Each channel from 0.0 to 1.0.
|
||||
* @public
|
||||
* @param {number} r - Red.
|
||||
* @param {number} g - Green.
|
||||
* @param {number} b - Blue.
|
||||
* @param {number} a - Alpha.
|
||||
*/
|
||||
setColor(r, g, b, a) {
|
||||
this._color.x = r;
|
||||
this._color.y = g;
|
||||
this._color.z = b;
|
||||
(a != undefined) && (this._color.w = a);
|
||||
this._handler && this._handler.setRgbaArr(this._handlerIndex, this._color);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets RGBA color. Each channel from 0.0 to 1.0.
|
||||
* @public
|
||||
* @param {og.math.Vector4} color - RGBA vector.
|
||||
*/
|
||||
setColor4v(color) {
|
||||
this._color.x = color.x;
|
||||
this._color.y = color.y;
|
||||
this._color.z = color.z;
|
||||
(color.w != undefined) && (this._color.w = color.w);
|
||||
this._handler && this._handler.setRgbaArr(this._handlerIndex, color);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets billboard color.
|
||||
* @public
|
||||
* @param {string} color - HTML style color.
|
||||
*/
|
||||
setColorHTML(color) {
|
||||
this.setColor4v(og.utils.htmlColorToRgba(color));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns RGBA color.
|
||||
* @public
|
||||
* @returns {og.math.Vector4}
|
||||
*/
|
||||
getColor() {
|
||||
return this._color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets billboard visibility.
|
||||
* @public
|
||||
* @param {boolean} visibility - Visibility flag.
|
||||
*/
|
||||
setVisibility(visibility) {
|
||||
this._visibility = visibility;
|
||||
this._handler && this._handler.setVisibility(this._handlerIndex, visibility);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns billboard visibility.
|
||||
* @public
|
||||
* @returns {boolean}
|
||||
*/
|
||||
getVisibility() {
|
||||
return this._visibility;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets billboard cartesian aligned vector.
|
||||
* @public
|
||||
* @param {number} x - Aligned vector X coordinate.
|
||||
* @param {number} y - Aligned vector Y coordinate.
|
||||
* @param {number} z - Aligned vector Z coordinate.
|
||||
*/
|
||||
setAlignedAxis(x, y, z) {
|
||||
this._alignedAxis.x = x;
|
||||
this._alignedAxis.y = y;
|
||||
this._alignedAxis.z = z;
|
||||
this._handler && this._handler.setAlignedAxisArr(this._handlerIndex, this._alignedAxis);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets billboard aligned vector.
|
||||
* @public
|
||||
* @param {og.math.Vecto3} alignedAxis - Vector to align.
|
||||
*/
|
||||
setAlignedAxis3v(alignedAxis) {
|
||||
this._alignedAxis.x = alignedAxis.x;
|
||||
this._alignedAxis.y = alignedAxis.y;
|
||||
this._alignedAxis.z = alignedAxis.z;
|
||||
this._handler && this._handler.setAlignedAxisArr(this._handlerIndex, alignedAxis);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns aligned vector.
|
||||
* @public
|
||||
* @returns {og.math.Vector3}
|
||||
*/
|
||||
getAlignedAxis() {
|
||||
return this._alignedAxis;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes billboard from hander.
|
||||
* @public
|
||||
*/
|
||||
remove() {
|
||||
this._entity = null;
|
||||
this._handler && this._handler.remove(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets billboard picking color.
|
||||
* @public
|
||||
* @param {og.math.Vector3} color - Picking color.
|
||||
*/
|
||||
setPickingColor3v(color) {
|
||||
this._handler && this._handler.setPickingColorArr(this._handlerIndex, color);
|
||||
}
|
||||
};
|
||||
|
||||
export { BaseBillboard };
|
||||
160
src/og/entity/Billboard.js
Normal file
160
src/og/entity/Billboard.js
Normal file
@ -0,0 +1,160 @@
|
||||
/**
|
||||
* @module og/entity/Billboard
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { BaseBillboard } from './BaseBillboard.js';
|
||||
|
||||
|
||||
/**
|
||||
* Represents basic quad billboard image.
|
||||
* @class
|
||||
* @extends {og.BaseBillboard}
|
||||
* @param {Object} [options] - Options:
|
||||
* @param {og.math.Vector3|Array.<number>} [options.position] - Billboard spatial position.
|
||||
* @param {number} [options.rotation] - Screen angle rotaion.
|
||||
* @param {og.math.Vector4|string|Array.<number>} [options.color] - Billboard color.
|
||||
* @param {og.math.Vector3|Array.<number>} [options.alignedAxis] - Billboard aligned vector.
|
||||
* @param {og.math.Vector3|Array.<number>} [options.offset] - Billboard center screen offset.
|
||||
* @param {boolean} [options.visibility] - Visibility.
|
||||
* @param {string} [options.src] - Billboard image url source.
|
||||
* @param {Image} [options.image] - Billboard image object.
|
||||
* @param {number} [options.width] - Screen width.
|
||||
* @param {number} [options.height] - Screen height.
|
||||
* @param {number} [options.scale] - Billboard scale.
|
||||
*/
|
||||
class Billboard extends BaseBillboard {
|
||||
constructor(options) {
|
||||
super(options);
|
||||
|
||||
options = options || {};
|
||||
|
||||
/**
|
||||
* Image src.
|
||||
* @protected
|
||||
* @type {string}
|
||||
*/
|
||||
this._src = options.src || null;
|
||||
|
||||
/**
|
||||
* Image object.
|
||||
* @protected
|
||||
* @type {Object}
|
||||
*/
|
||||
this._image = options.image || null;
|
||||
|
||||
/**
|
||||
* Billboard screen width.
|
||||
* @protected
|
||||
* @type {number}
|
||||
*/
|
||||
this._width = options.width || (options.size ? options.size[0] : 30);
|
||||
|
||||
/**
|
||||
* Billboard screen height.
|
||||
* @protected
|
||||
* @type {number}
|
||||
*/
|
||||
this._height = options.height || (options.size ? options.size[1] : 30);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets billboard image url source.
|
||||
* @public
|
||||
* @param {string} src - Image url.
|
||||
*/
|
||||
setSrc(src) {
|
||||
this._src = src;
|
||||
var bh = this._handler;
|
||||
if (bh && src) {
|
||||
var rn = bh._entityCollection.renderNode;
|
||||
if (rn) {
|
||||
var ta = rn.billboardsTextureAtlas;
|
||||
var that = this;
|
||||
ta.loadImage(src, function (img) {
|
||||
if (ta.nodes[img.__nodeIndex]) {
|
||||
that._image = img;
|
||||
bh.setTexCoordArr(that._handlerIndex, ta.nodes[that._image.__nodeIndex].texCoords);
|
||||
} else {
|
||||
ta.addImage(img);
|
||||
ta.createTexture();
|
||||
that._image = img;
|
||||
rn.updateBillboardsTexCoords();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets image object.
|
||||
* @public
|
||||
* @param {Object} image - JavaScript image object.
|
||||
*/
|
||||
setImage(image) {
|
||||
this.setSrc(image.src);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets billboard screen size in pixels.
|
||||
* @public
|
||||
* @param {number} width - Billboard width.
|
||||
* @param {number} height - Billboard height.
|
||||
*/
|
||||
setSize(width, height) {
|
||||
this._width = width;
|
||||
this._height = height;
|
||||
this._handler && this._handler.setSizeArr(this._handlerIndex, width * this._scale, height * this._scale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns billboard screen size.
|
||||
* @public
|
||||
* @returns {Object}
|
||||
*/
|
||||
getSize() {
|
||||
return {
|
||||
"width": this._width,
|
||||
"height": this._height
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets billboard screen width.
|
||||
* @public
|
||||
* @param {number} width - Width.
|
||||
*/
|
||||
setWidth(width) {
|
||||
this.setSize(width, this._height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets billboard screen width.
|
||||
* @public
|
||||
* @returns {number}
|
||||
*/
|
||||
getWidth() {
|
||||
return this._width;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets billboard screen heigh.
|
||||
* @public
|
||||
* @param {number} height - Height.
|
||||
*/
|
||||
setHeight(height) {
|
||||
this.setSize(this._width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets billboard screen height.
|
||||
* @public
|
||||
* @returns {number}
|
||||
*/
|
||||
getHeight() {
|
||||
return this._height;
|
||||
}
|
||||
};
|
||||
|
||||
export { Billboard };
|
||||
736
src/og/entity/BillboardHandler.js
Normal file
736
src/og/entity/BillboardHandler.js
Normal file
@ -0,0 +1,736 @@
|
||||
/**
|
||||
* @module og/entity/BillboardHandler
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as shaders from '../shaders/billboard.js';
|
||||
|
||||
const PICKINGCOLOR_BUFFER = 0;
|
||||
const POSITION_BUFFER = 1;
|
||||
const SIZE_BUFFER = 2;
|
||||
const OFFSET_BUFFER = 3;
|
||||
const RGBA_BUFFER = 4;
|
||||
const ROTATION_BUFFER = 5;
|
||||
const TEXCOORD_BUFFER = 6;
|
||||
const VERTEX_BUFFER = 7;
|
||||
const ALIGNEDAXIS_BUFFER = 8;
|
||||
|
||||
/*
|
||||
* og.BillboardHandler
|
||||
*
|
||||
*
|
||||
*/
|
||||
class BillboardHandler {
|
||||
constructor(entityCollection) {
|
||||
|
||||
/**
|
||||
* Picking rendering option.
|
||||
* @public
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.pickingEnabled = true;
|
||||
|
||||
this._entityCollection = entityCollection;
|
||||
|
||||
this._renderer = null;
|
||||
|
||||
this._billboards = [];
|
||||
|
||||
this._positionBuffer = null;
|
||||
this._sizeBuffer = null;
|
||||
this._offsetBuffer = null;
|
||||
this._rgbaBuffer = null;
|
||||
this._rotationBuffer = null;
|
||||
this._texCoordBuffer = null;
|
||||
this._vertexBuffer = null;
|
||||
this._alignedAxisBuffer = null;
|
||||
|
||||
this._texCoordArr = [];
|
||||
this._vertexArr = [];
|
||||
this._positionArr = [];
|
||||
this._sizeArr = [];
|
||||
this._offsetArr = [];
|
||||
this._rgbaArr = [];
|
||||
this._rotationArr = [];
|
||||
this._alignedAxisArr = [];
|
||||
|
||||
this._pickingColorBuffer = null;
|
||||
this._pickingColorArr = [];
|
||||
|
||||
this._buffersUpdateCallbacks = [];
|
||||
this._buffersUpdateCallbacks[PICKINGCOLOR_BUFFER] = this.createPickingColorBuffer;
|
||||
this._buffersUpdateCallbacks[POSITION_BUFFER] = this.createPositionBuffer;
|
||||
this._buffersUpdateCallbacks[SIZE_BUFFER] = this.createSizeBuffer;
|
||||
this._buffersUpdateCallbacks[OFFSET_BUFFER] = this.createOffsetBuffer;
|
||||
this._buffersUpdateCallbacks[RGBA_BUFFER] = this.createRgbaBuffer;
|
||||
this._buffersUpdateCallbacks[ROTATION_BUFFER] = this.createRotationBuffer;
|
||||
this._buffersUpdateCallbacks[TEXCOORD_BUFFER] = this.createTexCoordBuffer;
|
||||
this._buffersUpdateCallbacks[VERTEX_BUFFER] = this.createVertexBuffer;
|
||||
this._buffersUpdateCallbacks[ALIGNEDAXIS_BUFFER] = this.createAlignedAxisBuffer;
|
||||
|
||||
this._changedBuffers = new Array(this._buffersUpdateCallbacks.length);
|
||||
|
||||
this.__staticId = BillboardHandler.staticCounter++;
|
||||
}
|
||||
|
||||
static get _staticCounter() {
|
||||
if (!this._counter && this._counter !== 0) {
|
||||
this._counter = 0;
|
||||
}
|
||||
return this._counter;
|
||||
}
|
||||
|
||||
static set _staticCounter(n) {
|
||||
this._counter = n;
|
||||
}
|
||||
|
||||
static concArr(dest, curr) {
|
||||
for (var i = 0; i < curr.length; i++) {
|
||||
dest.push(curr[i]);
|
||||
}
|
||||
}
|
||||
|
||||
initShaderProgram() {
|
||||
if (this._renderer.handler) {
|
||||
if (!this._renderer.handler.shaderPrograms.billboard) {
|
||||
var isSingleBuffer = !this._renderer.isMultiFramebufferCompatible();
|
||||
this._renderer.handler.addShaderProgram(shaders.billboard(isSingleBuffer));
|
||||
}
|
||||
if (!this._renderer.handler.shaderPrograms.billboardPicking) {
|
||||
this._renderer.handler.addShaderProgram(shaders.billboardPicking());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setRenderer(renderer) {
|
||||
this._renderer = renderer;
|
||||
this.initShaderProgram();
|
||||
}
|
||||
|
||||
refresh() {
|
||||
var i = this._changedBuffers.length;
|
||||
while (i--) {
|
||||
this._changedBuffers[i] = true;
|
||||
}
|
||||
}
|
||||
|
||||
_removeBillboards() {
|
||||
var i = this._billboards.length;
|
||||
while (i--) {
|
||||
var bi = this._billboards[i];
|
||||
bi._handlerIndex = -1;
|
||||
bi._handler = null;
|
||||
}
|
||||
this._billboards.length = 0;
|
||||
this._billboards = [];
|
||||
}
|
||||
|
||||
clear() {
|
||||
this._texCoordArr.length = 0;
|
||||
this._vertexArr.length = 0;
|
||||
this._positionArr.length = 0;
|
||||
this._sizeArr.length = 0;
|
||||
this._offsetArr.length = 0;
|
||||
this._rgbaArr.length = 0;
|
||||
this._rotationArr.length = 0;
|
||||
this._alignedAxisArr.length = 0;
|
||||
this._pickingColorArr.length = 0;
|
||||
|
||||
this._texCoordArr = [];
|
||||
this._vertexArr = [];
|
||||
this._positionArr = [];
|
||||
this._sizeArr = [];
|
||||
this._offsetArr = [];
|
||||
this._rgbaArr = [];
|
||||
this._rotationArr = [];
|
||||
this._alignedAxisArr = [];
|
||||
this._pickingColorArr = [];
|
||||
|
||||
this._removeBillboards();
|
||||
this._deleteBuffers();
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
_deleteBuffers() {
|
||||
var gl = this._renderer.handler.gl;
|
||||
gl.deleteBuffer(this._positionBuffer);
|
||||
gl.deleteBuffer(this._sizeBuffer);
|
||||
gl.deleteBuffer(this._offsetBuffer);
|
||||
gl.deleteBuffer(this._rgbaBuffer);
|
||||
gl.deleteBuffer(this._rotationBuffer);
|
||||
gl.deleteBuffer(this._vertexBuffer);
|
||||
gl.deleteBuffer(this._texCoordBuffer);
|
||||
gl.deleteBuffer(this._alignedAxisBuffer);
|
||||
gl.deleteBuffer(this._pickingColorBuffer);
|
||||
|
||||
this._positionBuffer = null;
|
||||
this._sizeBuffer = null;
|
||||
this._offsetBuffer = null;
|
||||
this._rgbaBuffer = null;
|
||||
this._rotationBuffer = null;
|
||||
this._vertexBuffer = null;
|
||||
this._texCoordBuffer = null;
|
||||
this._alignedAxisBuffer = null;
|
||||
this._pickingColorBuffer = null;
|
||||
}
|
||||
|
||||
update() {
|
||||
if (this._renderer) {
|
||||
var i = this._changedBuffers.length;
|
||||
while (i--) {
|
||||
if (this._changedBuffers[i]) {
|
||||
this._buffersUpdateCallbacks[i].call(this);
|
||||
this._changedBuffers[i] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
add(billboard) {
|
||||
if (billboard._handlerIndex == -1) {
|
||||
billboard._handler = this;
|
||||
billboard._handlerIndex = this._billboards.length;
|
||||
this._billboards.push(billboard);
|
||||
this._addBillboardToArrays(billboard);
|
||||
this.refresh();
|
||||
billboard.setSrc(billboard._src || billboard._image && billboard._image.src);
|
||||
}
|
||||
}
|
||||
|
||||
_addBillboardToArrays(billboard) {
|
||||
if (billboard._visibility) {
|
||||
BillboardHandler.concArr(this._vertexArr, [-0.5, 0.5, -0.5, -0.5, 0.5, -0.5, 0.5, -0.5, 0.5, 0.5, -0.5, 0.5]);
|
||||
} else {
|
||||
BillboardHandler.concArr(this._vertexArr, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||
}
|
||||
|
||||
BillboardHandler.concArr(this._texCoordArr, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||
|
||||
var x = billboard._position.x, y = billboard._position.y, z = billboard._position.z, w = billboard._scale;
|
||||
BillboardHandler.concArr(this._positionArr, [x, y, z, w, x, y, z, w, x, y, z, w, x, y, z, w, x, y, z, w, x, y, z, w]);
|
||||
|
||||
x = billboard._width; y = billboard._height;
|
||||
BillboardHandler.concArr(this._sizeArr, [x, y, x, y, x, y, x, y, x, y, x, y]);
|
||||
|
||||
x = billboard._offset.x; y = billboard._offset.y; z = billboard._offset.z;
|
||||
BillboardHandler.concArr(this._offsetArr, [x, y, z, x, y, z, x, y, z, x, y, z, x, y, z, x, y, z]);
|
||||
|
||||
x = billboard._color.x; y = billboard._color.y; z = billboard._color.z; w = billboard._color.w;
|
||||
BillboardHandler.concArr(this._rgbaArr, [x, y, z, w, x, y, z, w, x, y, z, w, x, y, z, w, x, y, z, w, x, y, z, w]);
|
||||
|
||||
x = billboard._rotation;
|
||||
BillboardHandler.concArr(this._rotationArr, [x, x, x, x, x, x]);
|
||||
|
||||
x = billboard._alignedAxis.x, y = billboard._alignedAxis.y, z = billboard._alignedAxis.z;
|
||||
BillboardHandler.concArr(this._alignedAxisArr, [x, y, z, x, y, z, x, y, z, x, y, z, x, y, z, x, y, z]);
|
||||
|
||||
x = billboard._entity._pickingColor.x / 255, y = billboard._entity._pickingColor.y / 255, z = billboard._entity._pickingColor.z / 255;
|
||||
BillboardHandler.concArr(this._pickingColorArr, [x, y, z, x, y, z, x, y, z, x, y, z, x, y, z, x, y, z]);
|
||||
}
|
||||
|
||||
_displayPASS() {
|
||||
var r = this._renderer;
|
||||
var h = r.handler;
|
||||
h.shaderPrograms.billboard.activate();
|
||||
var sh = h.shaderPrograms.billboard._program;
|
||||
var sha = sh.attributes,
|
||||
shu = sh.uniforms;
|
||||
|
||||
var gl = h.gl;
|
||||
|
||||
gl.uniform1i(shu.u_texture._pName, 0);
|
||||
|
||||
gl.uniformMatrix4fv(shu.viewMatrix._pName, false, r.activeCamera._viewMatrix._m);
|
||||
gl.uniformMatrix4fv(shu.projectionMatrix._pName, false, r.activeCamera._projectionMatrix._m);
|
||||
|
||||
gl.uniform3fv(shu.uCamPos._pName, r.activeCamera.eye.toVec());
|
||||
|
||||
gl.uniform3fv(shu.uScaleByDistance._pName, this._entityCollection.scaleByDistance);
|
||||
|
||||
gl.uniform1f(shu.uOpacity._pName, this._entityCollection._animatedOpacity);
|
||||
|
||||
gl.uniform2fv(shu.uFloatParams._pName, [this._entityCollection.renderNode._planetRadius2 || 0, r.activeCamera._tanViewAngle_hradOneByHeight]);
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this._texCoordBuffer);
|
||||
gl.vertexAttribPointer(sha.a_texCoord._pName, this._texCoordBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this._vertexBuffer);
|
||||
gl.vertexAttribPointer(sha.a_vertices._pName, this._vertexBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this._positionBuffer);
|
||||
gl.vertexAttribPointer(sha.a_positions._pName, this._positionBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this._rgbaBuffer);
|
||||
gl.vertexAttribPointer(sha.a_rgba._pName, this._rgbaBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this._sizeBuffer);
|
||||
gl.vertexAttribPointer(sha.a_size._pName, this._sizeBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this._offsetBuffer);
|
||||
gl.vertexAttribPointer(sha.a_offset._pName, this._offsetBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this._rotationBuffer);
|
||||
gl.vertexAttribPointer(sha.a_rotation._pName, this._rotationBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this._alignedAxisBuffer);
|
||||
gl.vertexAttribPointer(sha.a_alignedAxis._pName, this._alignedAxisBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
|
||||
gl.drawArrays(gl.TRIANGLES, 0, this._vertexBuffer.numItems);
|
||||
}
|
||||
|
||||
_pickingPASS() {
|
||||
var r = this._renderer;
|
||||
var h = r.handler;
|
||||
h.shaderPrograms.billboardPicking.activate();
|
||||
var sh = h.shaderPrograms.billboardPicking._program;
|
||||
var sha = sh.attributes,
|
||||
shu = sh.uniforms;
|
||||
|
||||
var gl = h.gl;
|
||||
|
||||
gl.uniformMatrix4fv(shu.viewMatrix._pName, false, r.activeCamera._viewMatrix._m);
|
||||
gl.uniformMatrix4fv(shu.projectionMatrix._pName, false, r.activeCamera._projectionMatrix._m);
|
||||
|
||||
gl.uniform3fv(shu.uCamPos._pName, r.activeCamera.eye.toVec());
|
||||
|
||||
gl.uniform3fv(shu.uScaleByDistance._pName, this._entityCollection.scaleByDistance);
|
||||
|
||||
gl.uniform1f(shu.uOpacity._pName, this._entityCollection._animatedOpacity);
|
||||
|
||||
gl.uniform2fv(shu.uFloatParams._pName, [this._entityCollection.renderNode._planetRadius2 || 0, r.activeCamera._tanViewAngle_hradOneByHeight]);
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this._vertexBuffer);
|
||||
gl.vertexAttribPointer(sha.a_vertices._pName, this._vertexBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this._positionBuffer);
|
||||
gl.vertexAttribPointer(sha.a_positions._pName, this._positionBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this._pickingColorBuffer);
|
||||
gl.vertexAttribPointer(sha.a_pickingColor._pName, this._pickingColorBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this._sizeBuffer);
|
||||
gl.vertexAttribPointer(sha.a_size._pName, this._sizeBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this._offsetBuffer);
|
||||
gl.vertexAttribPointer(sha.a_offset._pName, this._offsetBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this._rotationBuffer);
|
||||
gl.vertexAttribPointer(sha.a_rotation._pName, this._rotationBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this._alignedAxisBuffer);
|
||||
gl.vertexAttribPointer(sha.a_alignedAxis._pName, this._alignedAxisBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
|
||||
gl.drawArrays(gl.TRIANGLES, 0, this._vertexBuffer.numItems);
|
||||
};
|
||||
|
||||
draw() {
|
||||
if (this._billboards.length) {
|
||||
this.update();
|
||||
this._displayPASS();
|
||||
}
|
||||
}
|
||||
|
||||
drawPicking() {
|
||||
if (this._billboards.length && this.pickingEnabled) {
|
||||
this._pickingPASS();
|
||||
}
|
||||
}
|
||||
|
||||
reindexBillbordsArray(startIndex) {
|
||||
var b = this._billboards;
|
||||
for (var i = startIndex; i < b.length; i++) {
|
||||
b[i]._handlerIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
_removeBillboard(billboard) {
|
||||
var bi = billboard._handlerIndex;
|
||||
|
||||
this._billboards.splice(bi, 1);
|
||||
|
||||
var i = bi * 24;
|
||||
this._rgbaArr.splice(i, 24);
|
||||
this._positionArr.splice(i, 24);
|
||||
|
||||
i = bi * 18;
|
||||
this._offsetArr.splice(i, 18);
|
||||
this._alignedAxisArr.splice(i, 18);
|
||||
this._pickingColorArr.splice(i, 18);
|
||||
|
||||
i = bi * 12;
|
||||
this._vertexArr.splice(i, 12);
|
||||
this._sizeArr.splice(i, 12);
|
||||
this._texCoordArr.splice(i, 12);
|
||||
|
||||
i = bi * 6;
|
||||
this._rotationArr.splice(i, 6);
|
||||
|
||||
this.reindexBillbordsArray(bi);
|
||||
this.refresh();
|
||||
|
||||
billboard._handlerIndex = -1;
|
||||
billboard._handler = null;
|
||||
}
|
||||
|
||||
remove(billboard) {
|
||||
if (billboard._handler && this.__staticId == billboard._handler.__staticId) {
|
||||
this._removeBillboard(billboard);
|
||||
}
|
||||
}
|
||||
|
||||
setPositionArr(index, position) {
|
||||
|
||||
var i = index * 24;
|
||||
var a = this._positionArr, x = position.x, y = position.y, z = position.z;
|
||||
|
||||
a[i] = x;
|
||||
a[i + 1] = y;
|
||||
a[i + 2] = z;
|
||||
|
||||
a[i + 4] = x;
|
||||
a[i + 5] = y;
|
||||
a[i + 6] = z;
|
||||
|
||||
a[i + 8] = x;
|
||||
a[i + 9] = y;
|
||||
a[i + 10] = z;
|
||||
|
||||
a[i + 12] = x;
|
||||
a[i + 13] = y;
|
||||
a[i + 14] = z;
|
||||
|
||||
a[i + 16] = x;
|
||||
a[i + 17] = y;
|
||||
a[i + 18] = z;
|
||||
|
||||
a[i + 20] = x;
|
||||
a[i + 21] = y;
|
||||
a[i + 22] = z;
|
||||
|
||||
this._changedBuffers[POSITION_BUFFER] = true;
|
||||
}
|
||||
|
||||
setScaleArr(index, scale) {
|
||||
|
||||
var i = index * 24;
|
||||
var a = this._positionArr;
|
||||
|
||||
a[i + 3] = scale;
|
||||
a[i + 7] = scale;
|
||||
a[i + 11] = scale;
|
||||
a[i + 15] = scale;
|
||||
a[i + 19] = scale;
|
||||
a[i + 23] = scale;
|
||||
|
||||
this._changedBuffers[POSITION_BUFFER] = true;
|
||||
}
|
||||
|
||||
setPickingColorArr(index, color) {
|
||||
|
||||
var i = index * 18;
|
||||
var a = this._pickingColorArr, x = color.x / 255, y = color.y / 255, z = color.z / 255;
|
||||
|
||||
a[i] = x;
|
||||
a[i + 1] = y;
|
||||
a[i + 2] = z;
|
||||
|
||||
a[i + 3] = x;
|
||||
a[i + 4] = y;
|
||||
a[i + 5] = z;
|
||||
|
||||
a[i + 6] = x;
|
||||
a[i + 7] = y;
|
||||
a[i + 8] = z;
|
||||
|
||||
a[i + 9] = x;
|
||||
a[i + 10] = y;
|
||||
a[i + 11] = z;
|
||||
|
||||
a[i + 12] = x;
|
||||
a[i + 13] = y;
|
||||
a[i + 14] = z;
|
||||
|
||||
a[i + 15] = x;
|
||||
a[i + 16] = y;
|
||||
a[i + 17] = z;
|
||||
|
||||
this._changedBuffers[PICKINGCOLOR_BUFFER] = true;
|
||||
}
|
||||
|
||||
setSizeArr(index, width, height) {
|
||||
|
||||
var i = index * 12;
|
||||
var a = this._sizeArr, x = width, y = height;
|
||||
|
||||
a[i] = x;
|
||||
a[i + 1] = y;
|
||||
|
||||
a[i + 2] = x;
|
||||
a[i + 3] = y;
|
||||
|
||||
a[i + 4] = x;
|
||||
a[i + 5] = y;
|
||||
|
||||
a[i + 6] = x;
|
||||
a[i + 7] = y;
|
||||
|
||||
a[i + 8] = x;
|
||||
a[i + 9] = y;
|
||||
|
||||
a[i + 10] = x;
|
||||
a[i + 11] = y;
|
||||
|
||||
this._changedBuffers[SIZE_BUFFER] = true;
|
||||
}
|
||||
|
||||
setOffsetArr(index, offset) {
|
||||
|
||||
var i = index * 18;
|
||||
var a = this._offsetArr, x = offset.x, y = offset.y, z = offset.z;
|
||||
|
||||
a[i] = x;
|
||||
a[i + 1] = y;
|
||||
a[i + 2] = z;
|
||||
|
||||
a[i + 3] = x;
|
||||
a[i + 4] = y;
|
||||
a[i + 5] = z;
|
||||
|
||||
a[i + 6] = x;
|
||||
a[i + 7] = y;
|
||||
a[i + 8] = z;
|
||||
|
||||
a[i + 9] = x;
|
||||
a[i + 10] = y;
|
||||
a[i + 11] = z;
|
||||
|
||||
a[i + 12] = x;
|
||||
a[i + 13] = y;
|
||||
a[i + 14] = z;
|
||||
|
||||
a[i + 15] = x;
|
||||
a[i + 16] = y;
|
||||
a[i + 17] = z;
|
||||
|
||||
this._changedBuffers[OFFSET_BUFFER] = true;
|
||||
}
|
||||
|
||||
setRgbaArr(index, rgba) {
|
||||
|
||||
var i = index * 24;
|
||||
var a = this._rgbaArr, x = rgba.x, y = rgba.y, z = rgba.z, w = rgba.w;
|
||||
|
||||
a[i] = x;
|
||||
a[i + 1] = y;
|
||||
a[i + 2] = z;
|
||||
a[i + 3] = w;
|
||||
|
||||
a[i + 4] = x;
|
||||
a[i + 5] = y;
|
||||
a[i + 6] = z;
|
||||
a[i + 7] = w;
|
||||
|
||||
a[i + 8] = x;
|
||||
a[i + 9] = y;
|
||||
a[i + 10] = z;
|
||||
a[i + 11] = w;
|
||||
|
||||
a[i + 12] = x;
|
||||
a[i + 13] = y;
|
||||
a[i + 14] = z;
|
||||
a[i + 15] = w;
|
||||
|
||||
a[i + 16] = x;
|
||||
a[i + 17] = y;
|
||||
a[i + 18] = z;
|
||||
a[i + 19] = w;
|
||||
|
||||
a[i + 20] = x;
|
||||
a[i + 21] = y;
|
||||
a[i + 22] = z;
|
||||
a[i + 23] = w;
|
||||
|
||||
this._changedBuffers[RGBA_BUFFER] = true;
|
||||
}
|
||||
|
||||
setRotationArr(index, rotation) {
|
||||
|
||||
var i = index * 6;
|
||||
var a = this._rotationArr;
|
||||
|
||||
a[i] = rotation;
|
||||
a[i + 1] = rotation;
|
||||
a[i + 2] = rotation;
|
||||
a[i + 3] = rotation;
|
||||
a[i + 4] = rotation;
|
||||
a[i + 5] = rotation;
|
||||
|
||||
this._changedBuffers[ROTATION_BUFFER] = true
|
||||
}
|
||||
|
||||
setTexCoordArr(index, tcoordArr) {
|
||||
|
||||
var i = index * 12;
|
||||
var a = this._texCoordArr;
|
||||
|
||||
a[i] = tcoordArr[0];
|
||||
a[i + 1] = tcoordArr[1];
|
||||
|
||||
a[i + 2] = tcoordArr[2];
|
||||
a[i + 3] = tcoordArr[3];
|
||||
|
||||
a[i + 4] = tcoordArr[4];
|
||||
a[i + 5] = tcoordArr[5];
|
||||
|
||||
a[i + 6] = tcoordArr[6];
|
||||
a[i + 7] = tcoordArr[7];
|
||||
|
||||
a[i + 8] = tcoordArr[8];
|
||||
a[i + 9] = tcoordArr[9];
|
||||
|
||||
a[i + 10] = tcoordArr[10];
|
||||
a[i + 11] = tcoordArr[11];
|
||||
|
||||
this._changedBuffers[TEXCOORD_BUFFER] = true;
|
||||
}
|
||||
|
||||
setVisibility(index, visibility) {
|
||||
var vArr;
|
||||
if (visibility) {
|
||||
vArr = [-0.5, 0.5, -0.5, -0.5, 0.5, -0.5, 0.5, -0.5, 0.5, 0.5, -0.5, 0.5];
|
||||
} else {
|
||||
vArr = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
}
|
||||
this.setVertexArr(index, vArr);
|
||||
}
|
||||
|
||||
setVertexArr(index, vertexArr) {
|
||||
|
||||
var i = index * 12;
|
||||
var a = this._vertexArr;
|
||||
|
||||
a[i] = vertexArr[0];
|
||||
a[i + 1] = vertexArr[1];
|
||||
a[i + 2] = vertexArr[2];
|
||||
|
||||
a[i + 3] = vertexArr[3];
|
||||
a[i + 4] = vertexArr[4];
|
||||
a[i + 5] = vertexArr[5];
|
||||
|
||||
a[i + 6] = vertexArr[6];
|
||||
a[i + 7] = vertexArr[7];
|
||||
a[i + 8] = vertexArr[8];
|
||||
|
||||
a[i + 9] = vertexArr[9];
|
||||
a[i + 10] = vertexArr[10];
|
||||
a[i + 11] = vertexArr[11];
|
||||
|
||||
this._changedBuffers[VERTEX_BUFFER] = true;
|
||||
}
|
||||
|
||||
setAlignedAxisArr(index, alignedAxis) {
|
||||
|
||||
var i = index * 18;
|
||||
var a = this._alignedAxisArr, x = alignedAxis.x, y = alignedAxis.y, z = alignedAxis.z;
|
||||
|
||||
a[i] = x;
|
||||
a[i + 1] = y;
|
||||
a[i + 2] = z;
|
||||
|
||||
a[i + 3] = x;
|
||||
a[i + 4] = y;
|
||||
a[i + 5] = z;
|
||||
|
||||
a[i + 6] = x;
|
||||
a[i + 7] = y;
|
||||
a[i + 8] = z;
|
||||
|
||||
a[i + 9] = x;
|
||||
a[i + 10] = y;
|
||||
a[i + 11] = z;
|
||||
|
||||
a[i + 12] = x;
|
||||
a[i + 13] = y;
|
||||
a[i + 14] = z;
|
||||
|
||||
a[i + 15] = x;
|
||||
a[i + 16] = y;
|
||||
a[i + 17] = z;
|
||||
|
||||
this._changedBuffers[ALIGNEDAXIS_BUFFER] = true;
|
||||
}
|
||||
|
||||
createPositionBuffer() {
|
||||
var h = this._renderer.handler;
|
||||
h.gl.deleteBuffer(this._positionBuffer);
|
||||
this._positionBuffer = h.createArrayBuffer(new Float32Array(this._positionArr), 4, this._positionArr.length / 4, h.gl.DYNAMIC_DRAW);
|
||||
}
|
||||
|
||||
createSizeBuffer() {
|
||||
var h = this._renderer.handler;
|
||||
h.gl.deleteBuffer(this._sizeBuffer);
|
||||
this._sizeBuffer = h.createArrayBuffer(new Float32Array(this._sizeArr), 2, this._sizeArr.length / 2);
|
||||
}
|
||||
|
||||
createOffsetBuffer() {
|
||||
var h = this._renderer.handler;
|
||||
h.gl.deleteBuffer(this._offsetBuffer);
|
||||
this._offsetBuffer = h.createArrayBuffer(new Float32Array(this._offsetArr), 3, this._offsetArr.length / 3);
|
||||
}
|
||||
|
||||
createRgbaBuffer() {
|
||||
var h = this._renderer.handler;
|
||||
h.gl.deleteBuffer(this._rgbaBuffer);
|
||||
this._rgbaBuffer = h.createArrayBuffer(new Float32Array(this._rgbaArr), 4, this._rgbaArr.length / 4);
|
||||
}
|
||||
|
||||
createRotationBuffer() {
|
||||
var h = this._renderer.handler;
|
||||
h.gl.deleteBuffer(this._rotationBuffer);
|
||||
this._rotationBuffer = h.createArrayBuffer(new Float32Array(this._rotationArr), 1, this._rotationArr.length, h.gl.DYNAMIC_DRAW);
|
||||
}
|
||||
|
||||
createVertexBuffer() {
|
||||
var h = this._renderer.handler;
|
||||
h.gl.deleteBuffer(this._vertexBuffer);
|
||||
this._vertexBuffer = h.createArrayBuffer(new Float32Array(this._vertexArr), 2, this._vertexArr.length / 2, h.gl.DYNAMIC_DRAW);
|
||||
}
|
||||
|
||||
createTexCoordBuffer() {
|
||||
var h = this._renderer.handler;
|
||||
h.gl.deleteBuffer(this._texCoordBuffer);
|
||||
this._texCoordBuffer = h.createArrayBuffer(new Float32Array(this._texCoordArr), 2, this._texCoordArr.length / 2, h.gl.DYNAMIC_DRAW);
|
||||
}
|
||||
|
||||
createAlignedAxisBuffer() {
|
||||
var h = this._renderer.handler;
|
||||
h.gl.deleteBuffer(this._alignedAxisBuffer);
|
||||
this._alignedAxisBuffer = h.createArrayBuffer(new Float32Array(this._alignedAxisArr), 3, this._alignedAxisArr.length / 3);
|
||||
}
|
||||
|
||||
createPickingColorBuffer() {
|
||||
var h = this._renderer.handler;
|
||||
h.gl.deleteBuffer(this._pickingColorBuffer);
|
||||
this._pickingColorBuffer = h.createArrayBuffer(new Float32Array(this._pickingColorArr), 3, this._pickingColorArr.length / 3);
|
||||
}
|
||||
|
||||
refreshTexCoordsArr() {
|
||||
var bc = this._entityCollection;
|
||||
if (bc && bc.renderNode) {
|
||||
var ta = bc.renderNode.billboardsTextureAtlas;
|
||||
for (var i = 0; i < this._billboards.length; i++) {
|
||||
var bi = this._billboards[i];
|
||||
var img = bi._image;
|
||||
if (img) {
|
||||
var imageNode = ta.nodes[bi._image.__nodeIndex];
|
||||
if (imageNode) {
|
||||
this.setTexCoordArr(bi._handlerIndex, imageNode.texCoords);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export { BillboardHandler };
|
||||
607
src/og/entity/Entity.js
Normal file
607
src/og/entity/Entity.js
Normal file
@ -0,0 +1,607 @@
|
||||
/**
|
||||
* @module og/entity/Entity
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as mercator from '../mercator.js';
|
||||
import * as utils from '../utils/shared.js';
|
||||
import { Billboard } from './Billboard.js';
|
||||
//import { Box } from '../shapes/Box.js';
|
||||
import { Extent } from '../Extent.js';
|
||||
import { Geometry } from './Geometry.js';
|
||||
import { Label } from './Label.js';
|
||||
import { LonLat } from '../LonLat.js';
|
||||
import { Polyline } from './Polyline.js';
|
||||
import { PointCloud } from './PointCloud.js';
|
||||
//import { Sphere } from '../shapes/Sphere.js';
|
||||
import { Vec3 } from '../math/Vec3.js';
|
||||
|
||||
|
||||
/**
|
||||
* Entity instances aggregate multiple forms of visualization into a single high-level object.
|
||||
* They can be created manually and added to entity collection.
|
||||
*
|
||||
* @class
|
||||
* @param {Object} [options] - Entity options:
|
||||
* @param {string} [options.name] - A human readable name to display to users. It does not have to be unique.
|
||||
* @param {og.math.Vector3|Array.<number>} [options.cartesian] - Spatial entities like billboard, label, sphere etc. cartesian position.
|
||||
* @param {og.LonLat} [options.lonlat] - Geodetic coordiantes for an entities like billboard, label, sphere etc.
|
||||
* @param {boolean} [options.aground] - True for entities that have to be placed on the relief.
|
||||
* @param {boolean} [options.visibility] - Entity visibility.
|
||||
* @param {*} [options.billboard] - Billboard options(see {@link og.Billboard}).
|
||||
* @param {*} [options.label] - Label options(see {@link og.Label}).
|
||||
* @param {*} [options.sphere] - Sphere options(see {@link og.shape.Sphere}).
|
||||
* @param {*} [options.box] - Sphere options(see {@link og.shape.Box}).
|
||||
* @param {*} [options.Polyline] - Polyline options(see {@link og.Polyline}).
|
||||
* @param {*} [options.pointCloud] - Point cloud options(see {@link og.PointCloud}).
|
||||
* @param {*} [options.geometry] - Geometry options (see {@link og.Geometry}), available for vector layer only.
|
||||
* @param {*} [options.properties] - Entity custom properties.
|
||||
*/
|
||||
class Entity {
|
||||
constructor(options) {
|
||||
|
||||
options = options || {};
|
||||
|
||||
options.properties = options.properties || {};
|
||||
|
||||
/**
|
||||
* Unic identifier.
|
||||
* @public
|
||||
* @readonly
|
||||
*/
|
||||
this.id = Entity.__staticCounter++;
|
||||
|
||||
/**
|
||||
* Entity user defined properties.
|
||||
* @public
|
||||
* @type {Object}
|
||||
*/
|
||||
this.properties = options.properties || {};
|
||||
|
||||
/**
|
||||
* Entity name.
|
||||
* @public
|
||||
* @type {string}
|
||||
*/
|
||||
this.properties.name = this.properties.name || "noname";
|
||||
|
||||
/**
|
||||
* Children entities.
|
||||
* @public
|
||||
* @type {Array.<og.Entity>}
|
||||
*/
|
||||
this.childrenNodes = [];
|
||||
|
||||
/**
|
||||
* Parent entity.
|
||||
* @public
|
||||
* @type {og.Entity}
|
||||
*/
|
||||
this.parent = null;
|
||||
|
||||
/**
|
||||
* Entity cartesian position.
|
||||
* @protected
|
||||
* @type {og.math.Vector3}
|
||||
*/
|
||||
this._cartesian = utils.createVector3(options.cartesian);
|
||||
|
||||
/**
|
||||
* Geodetic entity coordiantes.
|
||||
* @protected
|
||||
* @type {og.LonLat}
|
||||
*/
|
||||
this._lonlat = utils.createLonLat(options.lonlat);
|
||||
|
||||
/**
|
||||
* World Mercator entity coordinates.
|
||||
* @protected
|
||||
* @type {og.LonLat}
|
||||
*/
|
||||
this._lonlatMerc = null;
|
||||
|
||||
/**
|
||||
* Entity visible terrain altitude.
|
||||
* @protected
|
||||
* @type {number}
|
||||
*/
|
||||
this._altitude = options.altitude || 0.0;
|
||||
|
||||
/**
|
||||
* Visibility flag.
|
||||
* @protected
|
||||
* @type {boolean}
|
||||
*/
|
||||
this._visibility = options.visibility != undefined ? options.visibility : true;
|
||||
|
||||
/**
|
||||
* Entity collection that this entity belongs to.
|
||||
* @protected
|
||||
* @type {og.EntityCollection}
|
||||
*/
|
||||
this._entityCollection = null;
|
||||
|
||||
/**
|
||||
* Entity collection array store index.
|
||||
* @protected
|
||||
* @type {number}
|
||||
*/
|
||||
this._entityCollectionIndex = -1;
|
||||
|
||||
/**
|
||||
* Assigned vector layer pointer.
|
||||
* @protected
|
||||
* @type {og.layer.Vector}
|
||||
*/
|
||||
this._layer = null;
|
||||
|
||||
/**
|
||||
* Assigned vector layer entity array index.
|
||||
* @protected
|
||||
* @type {number}
|
||||
*/
|
||||
this._layerIndex = -1;
|
||||
|
||||
/**
|
||||
* Picking color.
|
||||
* @protected
|
||||
* @type {og.math.Vector3}
|
||||
*/
|
||||
this._pickingColor = new Vec3(0, 0, 0);
|
||||
|
||||
this._featureConstructorArray = {
|
||||
"billboard": [Billboard, this.setBillboard],
|
||||
"label": [Label, this.setLabel],
|
||||
"sphere": [Sphere, this.setShape],
|
||||
"box": [Box, this.setShape],
|
||||
"polyline": [Polyline, this.setPolyline],
|
||||
"pointCloud": [PointCloud, this.setPointCloud],
|
||||
"geometry": [Geometry, this.setGeometry],
|
||||
};
|
||||
|
||||
/**
|
||||
* Billboard entity.
|
||||
* @public
|
||||
* @type {og.Billboard}
|
||||
*/
|
||||
this.billboard = this._createOptionFeature('billboard', options.billboard);
|
||||
|
||||
/**
|
||||
* Text label entity.
|
||||
* @public
|
||||
* @type {og.Label}
|
||||
*/
|
||||
this.label = this._createOptionFeature('label', options.label);
|
||||
|
||||
/**
|
||||
* Shape entity.
|
||||
* @public
|
||||
* @type {og.shape.BaseShape}
|
||||
*/
|
||||
this.shape = this._createOptionFeature('sphere', options.sphere || options.box);
|
||||
|
||||
/**
|
||||
* Polyline entity.
|
||||
* @public
|
||||
* @type {og.Polyline}
|
||||
*/
|
||||
this.polyline = this._createOptionFeature('polyline', options.polyline);
|
||||
|
||||
/**
|
||||
* PointCloud entity.
|
||||
* @public
|
||||
* @type {og.PointCloud}
|
||||
*/
|
||||
this.pointCloud = this._createOptionFeature('pointCloud', options.pointCloud);
|
||||
|
||||
/**
|
||||
* Geometry entity(available only for vector layer).
|
||||
* @public
|
||||
* @type {og.Geometry}
|
||||
*/
|
||||
this.geometry = this._createOptionFeature('geometry', options.geometry);
|
||||
|
||||
|
||||
//this.model = null;
|
||||
//...
|
||||
}
|
||||
|
||||
static get _staticCounter() {
|
||||
if (!this._counter && this._counter !== 0) {
|
||||
this._counter = 0;
|
||||
}
|
||||
return this._counter;
|
||||
}
|
||||
|
||||
static set _staticCounter(n) {
|
||||
this._counter = n;
|
||||
}
|
||||
|
||||
_createOptionFeature(featureName, options) {
|
||||
if (options) {
|
||||
var c = this._featureConstructorArray[featureName];
|
||||
return c[1].call(this, new c[0](options));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds current entity into the specified entity collection.
|
||||
* @public
|
||||
* @param {og.EntityCollection|og.layer.Vector} collection - Specified entity collection or vector layer.
|
||||
* @param {Boolean} [rightNow=false] - Entity insertion option for vector layer.
|
||||
* @returns {og.Entity} - This object.
|
||||
*/
|
||||
addTo(collection, rightNow) {
|
||||
collection.add(this, rightNow);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes current entity from collection and layer.
|
||||
* @public
|
||||
*/
|
||||
remove() {
|
||||
this._layer && this._layer.removeEntity(this);
|
||||
this._entityCollection && this._entityCollection.removeEntity(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the entity visibility.
|
||||
* @public
|
||||
* @param {boolean} visibility - Entity visibility.
|
||||
*/
|
||||
setVisibility(visibility) {
|
||||
this._visibility = visibility;
|
||||
|
||||
//billboards
|
||||
this.billboard && this.billboard.setVisibility(visibility);
|
||||
|
||||
//labels
|
||||
this.label && this.label.setVisibility(visibility);
|
||||
|
||||
//shape
|
||||
this.shape && this.shape.setVisibility(visibility);
|
||||
|
||||
//polyline
|
||||
this.polyline && this.polyline.setVisibility(visibility);
|
||||
|
||||
//geometry
|
||||
this.geometry && this.geometry.setVisibility(visibility);
|
||||
|
||||
for (var i = 0; i < this.childrenNodes.length; i++) {
|
||||
this.childrenNodes[i].setVisibility(visibility);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns entity visibility.
|
||||
* @public
|
||||
* @returns {boolean} -
|
||||
*/
|
||||
getVisibility() {
|
||||
return this._visibility;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets entity cartesian position.
|
||||
* @public
|
||||
* @param {og.math.Vector3} cartesian - Cartesian position in 3d space.
|
||||
*/
|
||||
setCartesian3v(cartesian) {
|
||||
this.setCartesian(cartesian.x, cartesian.y, cartesian.z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets entity cartesian position.
|
||||
* @public
|
||||
* @param {number} x - 3d space X - position.
|
||||
* @param {number} y - 3d space Y - position.
|
||||
* @param {number} z - 3d space Z - position.
|
||||
*/
|
||||
setCartesian(x, y, z) {
|
||||
|
||||
var p = this._cartesian;
|
||||
|
||||
p.x = x;
|
||||
p.y = y;
|
||||
p.z = z;
|
||||
|
||||
//billboards
|
||||
this.billboard && this.billboard.setPosition3v(p);
|
||||
|
||||
//labels
|
||||
this.label && this.label.setPosition3v(p);
|
||||
|
||||
//shape
|
||||
this.shape && this.shape.setPosition3v(p);
|
||||
|
||||
for (var i = 0; i < this.childrenNodes.length; i++) {
|
||||
this.childrenNodes[i].setCartesian(x, y, z);
|
||||
}
|
||||
|
||||
var ec = this._entityCollection;
|
||||
|
||||
if (ec && ec.renderNode && ec.renderNode.ellipsoid) {
|
||||
|
||||
this._lonlat = ec.renderNode.ellipsoid.cartesianToLonLat(p);
|
||||
|
||||
if (Math.abs(this._lonlat.lat) < mercator.MAX_LAT) {
|
||||
this._lonlatMerc = this._lonlat.forwardMercator();
|
||||
} else {
|
||||
this._lonlatMerc = null;
|
||||
}
|
||||
}
|
||||
|
||||
ec && ec.events.dispatch(ec.events.entitymove, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets entity cartesian position without moveentity event dispatching.
|
||||
* @protected
|
||||
* @param {og.math.Vector3} cartesian - Cartesian position in 3d space.
|
||||
*/
|
||||
_setCartesian3vSilent(cartesian) {
|
||||
|
||||
var p = this._cartesian;
|
||||
|
||||
p.x = cartesian.x;
|
||||
p.y = cartesian.y;
|
||||
p.z = cartesian.z;
|
||||
|
||||
//billboards
|
||||
this.billboard && this.billboard.setPosition3v(p);
|
||||
|
||||
//labels
|
||||
this.label && this.label.setPosition3v(p);
|
||||
|
||||
//shape
|
||||
this.shape && this.shape.setPosition3v(p);
|
||||
|
||||
for (var i = 0; i < this.childrenNodes.length; i++) {
|
||||
this.childrenNodes[i].setCartesian(x, y, z);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets entity geodetic coordinates.
|
||||
* @public
|
||||
* @returns {og.LonLat} -
|
||||
*/
|
||||
getLonLat() {
|
||||
return this._lonlat.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets geodetic coordinates of the entity point object.
|
||||
* @public
|
||||
* @param {og.LonLat} lonlat - WGS84 coordinates.
|
||||
*/
|
||||
setLonLat(lonlat) {
|
||||
var l = this._lonlat;
|
||||
|
||||
l.lon = lonlat.lon;
|
||||
l.lat = lonlat.lat;
|
||||
l.height = lonlat.height;
|
||||
|
||||
var ec = this._entityCollection;
|
||||
if (ec && ec.renderNode && ec.renderNode.ellipsoid) {
|
||||
|
||||
if (Math.abs(lonlat.lat) < mercator.MAX_LAT) {
|
||||
this._lonlatMerc = lonlat.forwardMercator();
|
||||
} else {
|
||||
this._lonlatMerc = null;
|
||||
}
|
||||
|
||||
this._cartesian = ec.renderNode.ellipsoid.lonLatToCartesian(lonlat);
|
||||
this.setCartesian3v(this._cartesian);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets entity altitude over the planet.
|
||||
* @public
|
||||
* @param {number} altitude - Altitude.
|
||||
*/
|
||||
setAltitude(altitude) {
|
||||
this._altitude = altitude;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns carteain position.
|
||||
* @public
|
||||
* @returns {og.math.Vector3} -
|
||||
*/
|
||||
getCartesian() {
|
||||
return this._cartesian;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets entity billboard.
|
||||
* @public
|
||||
* @param {og.Billboard} billboard - Billboard object.
|
||||
* @returns {og.Billboard} -
|
||||
*/
|
||||
setBillboard(billboard) {
|
||||
if (this.billboard) {
|
||||
this.billboard.remove();
|
||||
}
|
||||
this.billboard = billboard;
|
||||
this.billboard._entity = this;
|
||||
this.billboard.setPosition3v(this._cartesian);
|
||||
this.billboard.setVisibility(this._visibility);
|
||||
this._entityCollection && this._entityCollection._billboardHandler.add(billboard);
|
||||
return billboard;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets entity label.
|
||||
* @public
|
||||
* @param {og.Label} label - Text label.
|
||||
* @returns {og.Label} -
|
||||
*/
|
||||
setLabel(label) {
|
||||
if (this.label) {
|
||||
this.label.remove();
|
||||
}
|
||||
this.label = label;
|
||||
this.label._entity = this;
|
||||
this.label.setPosition3v(this._cartesian);
|
||||
this.label.setVisibility(this._visibility);
|
||||
this._entityCollection && this._entityCollection._labelHandler.add(label);
|
||||
return label;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets entity shape.
|
||||
* @public
|
||||
* @param {og.BaseShape} shape - Shape object.
|
||||
* @returns {og.Polyline} -
|
||||
*/
|
||||
setShape(shape) {
|
||||
if (this.shape) {
|
||||
this.shape.remove();
|
||||
}
|
||||
this.shape = shape;
|
||||
this.shape._entity = this;
|
||||
this.shape.setPosition3v(this._cartesian);
|
||||
this.shape.setVisibility(this._visibility);
|
||||
this._entityCollection && this._entityCollection._shapeHandler.add(shape);
|
||||
return shape;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets entity polyline.
|
||||
* @public
|
||||
* @param {og.Polyline} polyline - Polyline object.
|
||||
* @returns {og.Polyline} -
|
||||
*/
|
||||
setPolyline(polyline) {
|
||||
if (this.polyline) {
|
||||
this.polyline.remove();
|
||||
}
|
||||
this.polyline = polyline;
|
||||
this.polyline._entity = this;
|
||||
this.polyline.setVisibility(this._visibility);
|
||||
this._entityCollection && this._entityCollection._polylineHandler.add(polyline);
|
||||
return polyline;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets entity pointCloud.
|
||||
* @public
|
||||
* @param {og.PointCloud} pointCloud - PointCloud object.
|
||||
* @returns {og.PointCloud} -
|
||||
*/
|
||||
setPointCloud(pointCloud) {
|
||||
if (this.pointCloud) {
|
||||
this.pointCloud.remove();
|
||||
}
|
||||
this.pointCloud = pointCloud;
|
||||
this.pointCloud._entity = this;
|
||||
this.pointCloud.setVisibility(this._visibility);
|
||||
this._entityCollection && this._entityCollection._pointCloudHandler.add(pointCloud);
|
||||
return pointCloud;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets entity geometry.
|
||||
* @public
|
||||
* @param {og.Geometry} geometry - Geometry object.
|
||||
* @returns {og.Geometry} -
|
||||
*/
|
||||
setGeometry(geometry) {
|
||||
if (this.geometry) {
|
||||
this.geometry.remove();
|
||||
}
|
||||
this.geometry = geometry;
|
||||
this.geometry._entity = this;
|
||||
this.geometry.setVisibility(this._visibility);
|
||||
this._layer && this._layer.add(this);
|
||||
return geometry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append child entity.
|
||||
* @public
|
||||
* @param {og.Entity} entity - Child entity.
|
||||
*/
|
||||
appendChild(entity) {
|
||||
entity._entityCollection = this._entityCollection;
|
||||
entity._pickingColor = this._pickingColor;
|
||||
entity.parent = this;
|
||||
this.childrenNodes.push(entity);
|
||||
this._entityCollection && this._entityCollection._addRecursively(entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends entity items(billboard, label etc.) picking color.
|
||||
* @public
|
||||
*/
|
||||
setPickingColor() {
|
||||
|
||||
var c = this._pickingColor;
|
||||
|
||||
//billboard
|
||||
this.billboard && this.billboard.setPickingColor3v(c);
|
||||
|
||||
//label
|
||||
this.label && this.label.setPickingColor3v(c);
|
||||
|
||||
//shape
|
||||
this.shape && this.shape.setPickingColor3v(c);
|
||||
|
||||
//polyline
|
||||
this.polyline && this.polyline.setPickingColor3v(c);
|
||||
|
||||
for (var i = 0; i < this.childrenNodes.length; i++) {
|
||||
this.childrenNodes[i].setPickingColor();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return geodethic extent.
|
||||
* @returns {og.Extent} -
|
||||
*/
|
||||
getExtent() {
|
||||
var res;
|
||||
var c = this._lonlat;
|
||||
if (this.billboard || this.label) {
|
||||
res = new Extent(new LonLat(c.lon, c.lat), new LonLat(c.lon, c.lat));
|
||||
} else {
|
||||
res = new Extent(new LonLat(180.0, 90.0), new LonLat(-180.0, -90.0));
|
||||
}
|
||||
|
||||
var sw = res.southWest,
|
||||
ne = res.northEast;
|
||||
|
||||
if (this.polyline) {
|
||||
var e = this.polyline.getExtent();
|
||||
if (e.southWest.lon < sw.lon) sw.lon = e.southWest.lon;
|
||||
if (e.southWest.lat < sw.lat) sw.lat = e.southWest.lat;
|
||||
if (e.northEast.lon > ne.lon) ne.lon = e.northEast.lon;
|
||||
if (e.northEast.lat > ne.lat) ne.lat = e.northEast.lat;
|
||||
}
|
||||
|
||||
if (this.geometry) {
|
||||
var e = this.geometry.getExtent();
|
||||
if (e.southWest.lon < sw.lon) sw.lon = e.southWest.lon;
|
||||
if (e.southWest.lat < sw.lat) sw.lat = e.southWest.lat;
|
||||
if (e.northEast.lon > ne.lon) ne.lon = e.northEast.lon;
|
||||
if (e.northEast.lat > ne.lat) ne.lat = e.northEast.lat;
|
||||
}
|
||||
|
||||
for (var i = 0; i < this.childrenNodes.length; i++) {
|
||||
var e = this.childrenNodes[i].getExtent();
|
||||
if (e.southWest.lon < sw.lon) sw.lon = e.southWest.lon;
|
||||
if (e.southWest.lat < sw.lat) sw.lat = e.southWest.lat;
|
||||
if (e.northEast.lon > ne.lon) ne.lon = e.northEast.lon;
|
||||
if (e.northEast.lat > ne.lat) ne.lat = e.northEast.lat;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
export { Entity };
|
||||
774
src/og/entity/EntityCollection.js
Normal file
774
src/og/entity/EntityCollection.js
Normal file
@ -0,0 +1,774 @@
|
||||
/**
|
||||
* @module og/entity/EntityCollection
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as math from '../math.js';
|
||||
import { BillboardHandler } from './BillboardHandler.js';
|
||||
import { Events } from '../Events.js';
|
||||
import { LabelHandler } from './LabelHandler.js';
|
||||
import { PolylineHandler } from './PolylineHandler.js';
|
||||
import { PointCloudHandler } from './PointCloudHandler.js';
|
||||
import { ShapeHandler } from './ShapeHandler.js';
|
||||
|
||||
/**
|
||||
* An observable collection of og.Entity instances where each entity has a unique id.
|
||||
* Entity collection provide handlers for an each type of entity like billboard, label or 3ds object.
|
||||
* @constructor
|
||||
* @param {Object} [options] - Entity options:
|
||||
* @param {Array.<og.Entity>} [options.entities] - Entities array.
|
||||
* @param {boolean} [options.visibility=true] - Entity visibility.
|
||||
* @param {Array.<number,number,number>} [options.scaleByDistance] - Entity scale by distance parameters.
|
||||
* First index - near distance to the entity, after entity becomes full scale.
|
||||
* Second index - far distance to the entity, when entity becomes zero scale.
|
||||
* Third index - far distance to the entity, when entity becomes invisible.
|
||||
* @param {number} [options.opacity] - Entity global opacity.
|
||||
* @param {boolean} [options.pickingEnabled=true] - Entity picking enable.
|
||||
* @fires og.EntityCollection#entitymove
|
||||
* @fires og.EntityCollection#draw
|
||||
* @fires og.EntityCollection#drawend
|
||||
* @fires og.EntityCollection#add
|
||||
* @fires og.EntityCollection#remove
|
||||
* @fires og.EntityCollection#entityadd
|
||||
* @fires og.EntityCollection#entityremove
|
||||
* @fires og.EntityCollection#visibilitychange
|
||||
* @fires og.EntityCollection#mousemove
|
||||
* @fires og.EntityCollection#mouseenter
|
||||
* @fires og.EntityCollection#mouseleave
|
||||
* @fires og.EntityCollection#lclick
|
||||
* @fires og.EntityCollection#rclick
|
||||
* @fires og.EntityCollection#mclick
|
||||
* @fires og.EntityCollection#ldblclick
|
||||
* @fires og.EntityCollection#rdblclick
|
||||
* @fires og.EntityCollection#mdblclick
|
||||
* @fires og.EntityCollection#lup
|
||||
* @fires og.EntityCollection#rup
|
||||
* @fires og.EntityCollection#mup
|
||||
* @fires og.EntityCollection#ldown
|
||||
* @fires og.EntityCollection#rdown
|
||||
* @fires og.EntityCollection#mdown
|
||||
* @fires og.EntityCollection#lhold
|
||||
* @fires og.EntityCollection#rhold
|
||||
* @fires og.EntityCollection#mhold
|
||||
* @fires og.EntityCollection#mousewheel
|
||||
* @fires og.EntityCollection#touchmove
|
||||
* @fires og.EntityCollection#touchstart
|
||||
* @fires og.EntityCollection#touchend
|
||||
* @fires og.EntityCollection#doubletouch
|
||||
* @fires og.EntityCollection#touchleave
|
||||
* @fires og.EntityCollection#touchenter
|
||||
*/
|
||||
class EntityCollection {
|
||||
constructor(options) {
|
||||
|
||||
options = options || {};
|
||||
|
||||
/**
|
||||
* Unic identifier.
|
||||
* @public
|
||||
* @readonly
|
||||
*/
|
||||
this.id = EntityCollection.__staticCounter++;
|
||||
|
||||
/**
|
||||
* Render node collections array index.
|
||||
* @protected
|
||||
* @type {number}
|
||||
*/
|
||||
this._renderNodeIndex = -1;
|
||||
|
||||
/**
|
||||
* Render node context.
|
||||
* @public
|
||||
* @type {og.scene.RenderNode}
|
||||
*/
|
||||
this.renderNode = null;
|
||||
|
||||
/**
|
||||
* Visibility option.
|
||||
* @protected
|
||||
* @type {boolean}
|
||||
*/
|
||||
this._visibility = options.visibility == undefined ? true : options.visibility;
|
||||
|
||||
/**
|
||||
* Billboards handler
|
||||
* @public
|
||||
* @type {og.BillboardHandler}
|
||||
*/
|
||||
this.billboardHandler = new BillboardHandler(this);
|
||||
|
||||
/**
|
||||
* Labels handler
|
||||
* @public
|
||||
* @type {og.LabelHandler}
|
||||
*/
|
||||
this.labelHandler = new LabelHandler(this);
|
||||
|
||||
/**
|
||||
* Shape handler
|
||||
* @public
|
||||
* @type {og.ShapeHandler}
|
||||
*/
|
||||
this.shapeHandler = new ShapeHandler(this);
|
||||
|
||||
/**
|
||||
* Polyline handler
|
||||
* @public
|
||||
* @type {og.PolylineHandler}
|
||||
*/
|
||||
this.polylineHandler = new PolylineHandler(this);
|
||||
|
||||
/**
|
||||
* PointCloud handler
|
||||
* @public
|
||||
* @type {og.PointCloudHandler}
|
||||
*/
|
||||
this.pointCloudHandler = new PointCloudHandler(this);
|
||||
//
|
||||
//...
|
||||
|
||||
if (options.pickingEnabled != undefined) {
|
||||
this.setPickingEnabled(options.pickingEnabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Entities array.
|
||||
* @protected
|
||||
* @type {Array.<og.Entity>}
|
||||
*/
|
||||
this._entities = options.entities || [];
|
||||
|
||||
/**
|
||||
* First index - near distance to the entity, after entity becomes full scale.
|
||||
* Second index - far distance to the entity, when entity becomes zero scale.
|
||||
* Third index - far distance to the entity, when entity becomes invisible.
|
||||
* @public
|
||||
* @type {Array.<number,number,number>}
|
||||
*/
|
||||
this.scaleByDistance = options.scaleByDistance || [math.MAX32, math.MAX32, math.MAX32];
|
||||
|
||||
/**
|
||||
* Global opacity.
|
||||
* @protected
|
||||
* @type {number}
|
||||
*/
|
||||
this._opacity = options.opacity == undefined ? 1.0 : options.opacity;
|
||||
|
||||
/**
|
||||
* Opacity state during the animated opacity.
|
||||
* @protected
|
||||
* @type {number}
|
||||
*/
|
||||
this._animatedOpacity = this._opacity;
|
||||
|
||||
/**
|
||||
* Entity collection events handler.
|
||||
* @public
|
||||
* @type {og.Events}
|
||||
*/
|
||||
this.events = new Events(EVENT_NAMES);
|
||||
|
||||
//initialize current entities
|
||||
this.addEntities(this._entities);
|
||||
}
|
||||
|
||||
static get _staticCounter() {
|
||||
if (!this._counter && this._counter !== 0) {
|
||||
this._counter = 0;
|
||||
}
|
||||
return this._counter;
|
||||
}
|
||||
|
||||
static set _staticCounter(n) {
|
||||
this._counter = n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets collection visibility.
|
||||
* @public
|
||||
* @param {boolean} visibility - Visibility flag.
|
||||
*/
|
||||
setVisibility(visibility) {
|
||||
this._visibility = visibility;
|
||||
this._animatedOpacity = this._opacity * (visibility ? 1 : 0);
|
||||
this.events.dispatch(this.events.visibilitychange, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns collection visibility.
|
||||
* @public
|
||||
* @returns {boolean}
|
||||
*/
|
||||
getVisibility() {
|
||||
return this._visibility;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets collection opacity.
|
||||
* @public
|
||||
* @param {number} opacity - Opacity.
|
||||
*/
|
||||
setOpacity(opacity) {
|
||||
this._opacity = opacity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets collection picking ability.
|
||||
* @public
|
||||
* @param {boolean} enable - Picking enable flag.
|
||||
*/
|
||||
setPickingEnabled(enable) {
|
||||
this.billboardHandler.pickingEnabled = enable;
|
||||
this.labelHandler.pickingEnabled = enable;
|
||||
this.polylineHandler.pickingEnabled = enable;
|
||||
this.shapeHandler.pickingEnabled = enable;
|
||||
this.pointCloudHandler.pickingEnabled = enable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets collection opacity.
|
||||
* @public
|
||||
* @param {number} opacity - Opacity.
|
||||
*/
|
||||
getOpacity() {
|
||||
return this._opacity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets scale by distance parameters.
|
||||
* @public
|
||||
* @param {number} near - Full scale entity distance.
|
||||
* @param {number} far - Zerol scale entity distance.
|
||||
* @param {number} [farInvisible] - Entity visibility distance.
|
||||
*/
|
||||
setScaleByDistance(near, far, farInvisible) {
|
||||
this.scaleByDistance[0] = near;
|
||||
this.scaleByDistance[1] = far;
|
||||
this.scaleByDistance[2] = farInvisible || math.MAX32;
|
||||
}
|
||||
|
||||
_addRecursively(entity) {
|
||||
|
||||
//billboard
|
||||
entity.billboard && this.billboardHandler.add(entity.billboard);
|
||||
|
||||
//label
|
||||
entity.label && this.labelHandler.add(entity.label);
|
||||
|
||||
//shape
|
||||
entity.shape && this.shapeHandler.add(entity.shape);
|
||||
|
||||
//polyline
|
||||
entity.polyline && this.polylineHandler.add(entity.polyline);
|
||||
|
||||
//pointCloud
|
||||
entity.pointCloud && this.pointCloudHandler.add(entity.pointCloud);
|
||||
|
||||
this.events.dispatch(this.events.entityadd, entity);
|
||||
|
||||
for (var i = 0; i < entity.childrenNodes.length; i++) {
|
||||
entity.childrenNodes[i]._entityCollection = this;
|
||||
entity.childrenNodes[i]._entityCollectionIndex = entity._entityCollectionIndex;
|
||||
entity.childrenNodes[i]._pickingColor = entity._pickingColor;
|
||||
this._addRecursively(entity.childrenNodes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds entity to the collection and returns collection.
|
||||
* @public
|
||||
* @param {og.Entity} entity - Entity.
|
||||
* @returns {og.EntityCollection}
|
||||
*/
|
||||
add(entity) {
|
||||
if (!entity._entityCollection) {
|
||||
entity._entityCollection = this;
|
||||
entity._entityCollectionIndex = this._entities.length;
|
||||
this._entities.push(entity);
|
||||
var rn = this.renderNode;
|
||||
if (rn) {
|
||||
rn.renderer && rn.renderer.assignPickingColor(entity);
|
||||
rn.ellipsoid && entity._lonlat && entity.setCartesian3v(rn.ellipsoid.lonLatToCartesian(entity._lonlat));
|
||||
}
|
||||
this._addRecursively(entity);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds entities array to the collection and returns collection.
|
||||
* @public
|
||||
* @param {Array.<og.Entity>} entities - Entities array.
|
||||
* @returns {og.EntityCollection}
|
||||
*/
|
||||
addEntities(entities) {
|
||||
var i = entities.length;
|
||||
while (i--) {
|
||||
this.add(entities[i]);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the entity belongs this collection, otherwise returns false.
|
||||
* @public
|
||||
* @param {og.Entity} entity - Entity.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
belongs(entity) {
|
||||
return (entity._entityCollection && this._renderNodeIndex == entity._entityCollection._renderNodeIndex);
|
||||
}
|
||||
|
||||
_removeRecursively(entity) {
|
||||
entity._entityCollection = null;
|
||||
entity._entityCollectionIndex = -1;
|
||||
|
||||
//billboard
|
||||
entity.billboard && this.billboardHandler.remove(entity.billboard);
|
||||
|
||||
//label
|
||||
entity.label && this.labelHandler.remove(entity.label);
|
||||
|
||||
//shape
|
||||
entity.shape && this.shapeHandler.remove(entity.shape);
|
||||
|
||||
//polyline
|
||||
entity.polyline && this.polylineHandler.remove(entity.polyline);
|
||||
|
||||
//pointCloud
|
||||
entity.pointCloud && this.pointCloudHandler.remove(entity.pointCloud);
|
||||
|
||||
for (var i = 0; i < entity.childrenNodes.length; i++) {
|
||||
this._removeRecursively(entity.childrenNodes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes entity from this collection.
|
||||
* @public
|
||||
* @param {og.Entity} entity - Entity to remove.
|
||||
*/
|
||||
removeEntity(entity) {
|
||||
this._entities.splice(entity._entityCollectionIndex, 1);
|
||||
this.reindexEntitiesArray(entity._entityCollectionIndex);
|
||||
|
||||
//clear picking color
|
||||
if (this.renderNode && this.renderNode.renderer) {
|
||||
this.renderNode.renderer.clearPickingColor(entity);
|
||||
entity._pickingColor.clear();
|
||||
}
|
||||
|
||||
if (this.belongs(entity)) {
|
||||
this._removeRecursively(entity);
|
||||
}
|
||||
|
||||
this.events.dispatch(this.events.entityremove, entity);
|
||||
}
|
||||
|
||||
_removeEntitySilent(entity) {
|
||||
this._entities.splice(entity._entityCollectionIndex, 1);
|
||||
this.reindexEntitiesArray(entity._entityCollectionIndex);
|
||||
|
||||
//clear picking color
|
||||
if (this.renderNode && this.renderNode.renderer) {
|
||||
this.renderNode.renderer.clearPickingColor(entity);
|
||||
entity._pickingColor.clear();
|
||||
}
|
||||
|
||||
if (this.belongs(entity)) {
|
||||
this._removeRecursively(entity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates or refresh collected entities picking color.
|
||||
* @public
|
||||
*/
|
||||
createPickingColors() {
|
||||
var e = this._entities;
|
||||
for (var i = 0; i < e.length; i++) {
|
||||
if (!e[i].parent) {
|
||||
this.renderNode.renderer.assignPickingColor(e[i]);
|
||||
e[i].setPickingColor();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh collected entities indexes from startIndex entitytes collection array position.
|
||||
* @public
|
||||
* @param {number} startIndex - Entities collection array index.
|
||||
*/
|
||||
reindexEntitiesArray(startIndex) {
|
||||
var e = this._entities;
|
||||
for (var i = startIndex; i < e.length; i++) {
|
||||
e[i]._entityCollectionIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds this collection to render node.
|
||||
* @public
|
||||
* @param {og.scene.RenderNode} renderNode - Render node.
|
||||
* @param {boolean} [isHidden] - Uses in vector layers that render in planet render specific function.
|
||||
* @returns {og.EntityCollection}
|
||||
*/
|
||||
addTo(renderNode, isHidden) {
|
||||
if (!this.renderNode) {
|
||||
this.renderNode = renderNode;
|
||||
if (!isHidden) {
|
||||
this._renderNodeIndex = renderNode.entityCollections.length;
|
||||
renderNode.entityCollections.push(this);
|
||||
}
|
||||
renderNode.ellipsoid && this._updateGeodeticCoordinates(renderNode.ellipsoid);
|
||||
this.setRenderer(renderNode.renderer);
|
||||
|
||||
this.shapeHandler.setRenderNode(renderNode);
|
||||
this.polylineHandler.setRenderNode(renderNode);
|
||||
this.pointCloudHandler.setRenderNode(renderNode);
|
||||
|
||||
this.events.dispatch(this.events.add, this);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates coordiantes all lonLat entities in collection after collecction attached to the planet node.
|
||||
* @private
|
||||
* @param {og.Ellipsoid} ellipsoid - Globe ellipsoid.
|
||||
*/
|
||||
_updateGeodeticCoordinates(ellipsoid) {
|
||||
var e = this._entities;
|
||||
var i = e.length;
|
||||
while (i--) {
|
||||
var ei = e[i];
|
||||
ei._lonlat && ei.setCartesian3v(ellipsoid.lonLatToCartesian(ei._lonlat));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets renderer. Used in renderer initialization, when entity collection starts before renderer has initialized.
|
||||
* @public
|
||||
* @param {og.Renderer} renderer - Renderer.
|
||||
*/
|
||||
setRenderer(renderer) {
|
||||
//todo: better to replace to setRenderNode function
|
||||
if (renderer) {
|
||||
this.billboardHandler.setRenderer(renderer);
|
||||
this.labelHandler.setRenderer(renderer);
|
||||
this.updateBillboardsTextureAtlas();
|
||||
this.updateLabelsFontAtlas();
|
||||
this.createPickingColors();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates billboard texture atlas.
|
||||
* @public
|
||||
*/
|
||||
updateBillboardsTextureAtlas() {
|
||||
var b = this.billboardHandler._billboards;
|
||||
for (var i = 0; i < b.length; i++) {
|
||||
b[i].setSrc(b[i]._src);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates labels font atlas.
|
||||
* @public
|
||||
*/
|
||||
updateLabelsFontAtlas() {
|
||||
if (this.renderNode) {
|
||||
var l = this.labelHandler._billboards;
|
||||
for (var i = 0; i < l.length; i++) {
|
||||
l[i].assignFontAtlas(this.renderNode.fontAtlas);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes collection from render node.
|
||||
* @public
|
||||
*/
|
||||
remove() {
|
||||
if (this.renderNode) {
|
||||
if (this._renderNodeIndex != -1) {
|
||||
this.renderNode.entityCollections.splice(this._renderNodeIndex, 1);
|
||||
//reindex in the renderNode
|
||||
for (var i = this._renderNodeIndex; i < this.renderNode.entityCollections.length; i++) {
|
||||
this.renderNode.entityCollections._renderNodeIndex = i;
|
||||
}
|
||||
}
|
||||
this.renderNode = null;
|
||||
this._renderNodeIndex = -1;
|
||||
this.events.dispatch(this.events.remove, this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets entities.
|
||||
* @public
|
||||
* @returns {Array.<og.Entity>}
|
||||
*/
|
||||
getEntities() {
|
||||
return [].concat(this._entities);
|
||||
}
|
||||
|
||||
/**
|
||||
* Safety entities loop.
|
||||
* @public
|
||||
* @param {function} callback - Entity callback.
|
||||
*/
|
||||
each(callback) {
|
||||
var i = this._entities.length;
|
||||
while (i--) {
|
||||
var ei = this._entities[i];
|
||||
ei && callback(ei);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all entities from colection and clear handlers.
|
||||
* @public
|
||||
*/
|
||||
clear() {
|
||||
|
||||
//TODO: Optimize by replace delete
|
||||
//code to the clearEntity function.
|
||||
this.billboardHandler.clear();
|
||||
this.labelHandler.clear();
|
||||
this.shapeHandler.clear();
|
||||
this.polylineHandler.clear();
|
||||
this.pointCloudHandler.clear();
|
||||
|
||||
var i = this._entities.length;
|
||||
while (i--) {
|
||||
var ei = this._entities[i];
|
||||
if (this.renderNode && this.renderNode.renderer) {
|
||||
this.renderNode.renderer.clearPickingColor(ei);
|
||||
ei._pickingColor.clear();
|
||||
}
|
||||
this._clearEntity(ei);
|
||||
}
|
||||
this._entities.length = 0;
|
||||
this._entities = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears entity recursevely.
|
||||
* @private
|
||||
* @param {og.Entity} entity - Entity to clear.
|
||||
*/
|
||||
_clearEntity(entity) {
|
||||
entity._entityCollection = null;
|
||||
entity._entityCollectionIndex = -1;
|
||||
for (var i = 0; i < entity.childrenNodes.length; i++) {
|
||||
this._clearEntity(entity.childrenNodes[i]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const EVENT_NAMES = [
|
||||
/**
|
||||
* Triggered when entity has moved.
|
||||
* @event og.EntityCollection#entitymove
|
||||
*/
|
||||
"entitymove",
|
||||
|
||||
/**
|
||||
* Triggered when collection entities begin draw.
|
||||
* @event og.EntityCollection#draw
|
||||
*/
|
||||
"draw",
|
||||
|
||||
/**
|
||||
* Triggered after collection has drawn.
|
||||
* @event og.EntityCollection#drawend
|
||||
*/
|
||||
"drawend",
|
||||
|
||||
/**
|
||||
* Triggered when added to the render node.
|
||||
* @event og.EntityCollection#add
|
||||
*/
|
||||
"add",
|
||||
|
||||
/**
|
||||
* Triggered when removed from the render node.
|
||||
* @event og.EntityCollection#remove
|
||||
*/
|
||||
"remove",
|
||||
|
||||
/**
|
||||
* Triggered when new entity added to the collection.
|
||||
* @event og.EntityCollection#entityadd
|
||||
*/
|
||||
"entityadd",
|
||||
|
||||
/**
|
||||
* Triggered when entity removes from the collection.
|
||||
* @event og.EntityCollection#entityremove
|
||||
*/
|
||||
"entityremove",
|
||||
|
||||
/**
|
||||
* Triggered when visibility changes.
|
||||
* @event og.EntityCollection#visibilitychange
|
||||
*/
|
||||
"visibilitychange",
|
||||
|
||||
/**
|
||||
* Triggered when mouse moves over the entity.
|
||||
* @event og.EntityCollection#mousemove
|
||||
*/
|
||||
"mousemove",
|
||||
|
||||
/**
|
||||
* Triggered when mouse has entered over the entity.
|
||||
* @event og.EntityCollection#mouseenter
|
||||
*/
|
||||
"mouseenter",
|
||||
|
||||
/**
|
||||
* Triggered when mouse leaves the entity.
|
||||
* @event og.EntityCollection#mouseleave
|
||||
*/
|
||||
"mouseleave",
|
||||
|
||||
/**
|
||||
* Mouse left button clicked.
|
||||
* @event og.EntityCollection#lclick
|
||||
*/
|
||||
"lclick",
|
||||
|
||||
/**
|
||||
* Mouse right button clicked.
|
||||
* @event og.EntityCollection#rclick
|
||||
*/
|
||||
"rclick",
|
||||
|
||||
/**
|
||||
* Mouse right button clicked.
|
||||
* @event og.EntityCollection#mclick
|
||||
*/
|
||||
"mclick",
|
||||
|
||||
/**
|
||||
* Mouse left button double click.
|
||||
* @event og.EntityCollection#ldblclick
|
||||
*/
|
||||
"ldblclick",
|
||||
|
||||
/**
|
||||
* Mouse right button double click.
|
||||
* @event og.EntityCollection#rdblclick
|
||||
*/
|
||||
"rdblclick",
|
||||
|
||||
/**
|
||||
* Mouse middle button double click.
|
||||
* @event og.EntityCollection#mdblclick
|
||||
*/
|
||||
"mdblclick",
|
||||
|
||||
/**
|
||||
* Mouse left button up(stop pressing).
|
||||
* @event og.EntityCollection#lup
|
||||
*/
|
||||
"lup",
|
||||
|
||||
/**
|
||||
* Mouse right button up(stop pressing).
|
||||
* @event og.EntityCollection#rup
|
||||
*/
|
||||
"rup",
|
||||
|
||||
/**
|
||||
* Mouse middle button up(stop pressing).
|
||||
* @event og.EntityCollection#mup
|
||||
*/
|
||||
"mup",
|
||||
|
||||
/**
|
||||
* Mouse left button is just pressed down(start pressing).
|
||||
* @event og.EntityCollection#ldown
|
||||
*/
|
||||
"ldown",
|
||||
|
||||
/**
|
||||
* Mouse right button is just pressed down(start pressing).
|
||||
* @event og.EntityCollection#rdown
|
||||
*/
|
||||
"rdown",
|
||||
|
||||
/**
|
||||
* Mouse middle button is just pressed down(start pressing).
|
||||
* @event og.EntityCollection#mdown
|
||||
*/
|
||||
"mdown",
|
||||
|
||||
/**
|
||||
* Mouse left button is pressing.
|
||||
* @event og.EntityCollection#lhold
|
||||
*/
|
||||
"lhold",
|
||||
|
||||
/**
|
||||
* Mouse right button is pressing.
|
||||
* @event og.EntityCollection#rhold
|
||||
*/
|
||||
"rhold",
|
||||
|
||||
/**
|
||||
* Mouse middle button is pressing.
|
||||
* @event og.EntityCollection#mhold
|
||||
*/
|
||||
"mhold",
|
||||
|
||||
/**
|
||||
* Mouse wheel is rotated.
|
||||
* @event og.EntityCollection#mousewheel
|
||||
*/
|
||||
"mousewheel",
|
||||
|
||||
/**
|
||||
* Triggered when touch moves over the entity.
|
||||
* @event og.EntityCollection#touchmove
|
||||
*/
|
||||
"touchmove",
|
||||
|
||||
/**
|
||||
* Triggered when entity begins to touch.
|
||||
* @event og.EntityCollection#touchstart
|
||||
*/
|
||||
"touchstart",
|
||||
|
||||
/**
|
||||
* Triggered when entity ends touching.
|
||||
* @event og.EntityCollection#touchend
|
||||
*/
|
||||
"touchend",
|
||||
|
||||
/**
|
||||
* Triggered entity double touch.
|
||||
* @event og.EntityCollection#doubletouch
|
||||
*/
|
||||
"doubletouch",
|
||||
|
||||
/**
|
||||
* Triggered when touching leaves entity.
|
||||
* @event og.EntityCollection#touchleave
|
||||
*/
|
||||
"touchleave",
|
||||
|
||||
/**
|
||||
* Triggered when touch enters over the entity.
|
||||
* @event og.EntityCollection#touchenter
|
||||
*/
|
||||
"touchenter"
|
||||
];
|
||||
|
||||
export { EntityCollection };
|
||||
317
src/og/entity/Geometry.js
Normal file
317
src/og/entity/Geometry.js
Normal file
@ -0,0 +1,317 @@
|
||||
/**
|
||||
* @module og/entity/Geometry
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as utils from '../utils/shared.js';
|
||||
import { Extent } from '../Extent.js';
|
||||
import { Vec4 } from '../math/Vec4.js';
|
||||
import { LonLat } from '../LonLat.js';
|
||||
|
||||
const GeometryType = {
|
||||
POINT: 1,
|
||||
LINESTRING: 2,
|
||||
POLYGON: 3,
|
||||
MULTIPOLYGON: 4,
|
||||
MULTILINESTRING: 5
|
||||
};
|
||||
|
||||
class Geometry {
|
||||
constructor(options) {
|
||||
this._id = Geometry._staticCounter++;
|
||||
|
||||
options = options || {};
|
||||
options.style = options.style || {};
|
||||
|
||||
/**
|
||||
* Entity instance that holds this geometry.
|
||||
* @protected
|
||||
* @type {og.Entity}
|
||||
*/
|
||||
this._entity = null;
|
||||
|
||||
this._handler = null;
|
||||
this._handlerIndex = -1;
|
||||
|
||||
//Polygon
|
||||
this._polyVerticesMerc = [];
|
||||
this._polyVerticesLength = -1;
|
||||
this._polyIndexesLength = -1;
|
||||
this._polyVerticesHandlerIndex = -1;
|
||||
this._polyIndexesHandlerIndex = -1;
|
||||
|
||||
//Line(Linestring and polygon's stroke(s)
|
||||
this._lineVerticesMerc = [];
|
||||
this._lineVerticesLength = -1;
|
||||
this._lineOrdersLength = -1;
|
||||
this._lineIndexesLength = -1;
|
||||
this._lineColorsLength = -1;
|
||||
this._lineThicknessLength = -1;
|
||||
this._lineVerticesHandlerIndex = -1;
|
||||
this._lineOrdersHandlerIndex = -1;
|
||||
this._lineIndexesHandlerIndex = -1;
|
||||
this._lineThicknessHandlerIndex = -1;
|
||||
this._lineColorsHandlerIndex = -1;
|
||||
|
||||
this._type = options.type && Geometry.getType(options.type) || GeometryType.POINT;
|
||||
this._coordinates = [];
|
||||
this._extent = Geometry.getExtent({
|
||||
'type': options.type || "Point",
|
||||
'coordinates': options.coordinates || []
|
||||
}, this._coordinates);
|
||||
|
||||
this._style = options.style || {};
|
||||
this._style.fillColor = utils.createColorRGBA(options.style.fillColor, new Vec4(0.19, 0.62, 0.85, 0.4));
|
||||
this._style.lineColor = utils.createColorRGBA(options.style.lineColor, new Vec4(0.19, 0.62, 0.85, 1));
|
||||
this._style.strokeColor = utils.createColorRGBA(options.style.strokeColor, new Vec4(1, 1, 1, 0.95));
|
||||
this._style.lineWidth = options.style.lineWidth || 3;
|
||||
this._style.strokeWidth = options.style.strokeWidth || 0;
|
||||
|
||||
this._visibility = options.visibility || true;
|
||||
|
||||
//optimization flag for picking mask rendering pass
|
||||
this._pickingReady = false;
|
||||
}
|
||||
|
||||
static get _staticCounter() {
|
||||
if (!this._counter && this._counter !== 0) {
|
||||
this._counter = 0;
|
||||
}
|
||||
return this._counter;
|
||||
}
|
||||
|
||||
static set _staticCounter(n) {
|
||||
this._counter = n;
|
||||
}
|
||||
|
||||
static getType(typeStr) {
|
||||
return GeometryType[typeStr.toUpperCase()];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns geometry feature extent.
|
||||
@static
|
||||
@param {Object} geometryObj - GeoJSON style geometry feature.
|
||||
@param {Array} outoordinates - Geometry feature coordinates clone.
|
||||
@returns {og.Extent}
|
||||
*/
|
||||
static getExtent(geometryObj, outCoordinates) {
|
||||
var res = new Extent(new LonLat(180.0, 90.0), new LonLat(-180.0, -90.0));
|
||||
var t = Geometry.getType(geometryObj.type);
|
||||
|
||||
if (t === GeometryType.POINT) {
|
||||
|
||||
var lon = res.coordinates[0],
|
||||
lat = res.coordinates[1];
|
||||
res.southWest.lon = lon;
|
||||
res.southWest.lat = lat;
|
||||
res.northEast.lon = lon;
|
||||
res.northEast.lat = lat;
|
||||
outCoordinates && (outCoordinates[0] = lon) && (outCoordinates[1] = lat);
|
||||
|
||||
} else if (t === GeometryType.LINESTRING) {
|
||||
|
||||
var c = geometryObj.coordinates;
|
||||
for (var i = 0; i < c.length; i++) {
|
||||
var lon = c[i][0],
|
||||
lat = c[i][1];
|
||||
if (lon < res.southWest.lon) res.southWest.lon = lon;
|
||||
if (lat < res.southWest.lat) res.southWest.lat = lat;
|
||||
if (lon > res.northEast.lon) res.northEast.lon = lon;
|
||||
if (lat > res.northEast.lat) res.northEast.lat = lat;
|
||||
outCoordinates && (outCoordinates[i] = [lon, lat]);
|
||||
}
|
||||
|
||||
} else if (t === GeometryType.POLYGON) {
|
||||
|
||||
var c = geometryObj.coordinates;
|
||||
for (var i = 0; i < c.length; i++) {
|
||||
var ci = c[i];
|
||||
outCoordinates && (outCoordinates[i] = []);
|
||||
for (var j = 0; j < ci.length; j++) {
|
||||
var cij = ci[j];
|
||||
var lon = cij[0],
|
||||
lat = cij[1];
|
||||
if (lon < res.southWest.lon) res.southWest.lon = lon;
|
||||
if (lat < res.southWest.lat) res.southWest.lat = lat;
|
||||
if (lon > res.northEast.lon) res.northEast.lon = lon;
|
||||
if (lat > res.northEast.lat) res.northEast.lat = lat;
|
||||
outCoordinates && (outCoordinates[i][j] = [lon, lat]);
|
||||
}
|
||||
}
|
||||
|
||||
} else if (t === GeometryType.MULTIPOLYGON) {
|
||||
|
||||
var p = geometryObj.coordinates;
|
||||
for (var i = 0; i < p.length; i++) {
|
||||
var pi = p[i];
|
||||
outCoordinates && (outCoordinates[i] = []);
|
||||
for (var j = 0; j < pi.length; j++) {
|
||||
var pij = pi[j];
|
||||
outCoordinates && (outCoordinates[i][j] = []);
|
||||
for (var k = 0; k < pij.length; k++) {
|
||||
var pijk = pij[k];
|
||||
var lon = pijk[0],
|
||||
lat = pijk[1];
|
||||
if (lon < res.southWest.lon) res.southWest.lon = lon;
|
||||
if (lat < res.southWest.lat) res.southWest.lat = lat;
|
||||
if (lon > res.northEast.lon) res.northEast.lon = lon;
|
||||
if (lat > res.northEast.lat) res.northEast.lat = lat;
|
||||
outCoordinates && (outCoordinates[i][j][k] = [lon, lat]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else if (t === GeometryType.MULTILINESTRING) {
|
||||
|
||||
var c = geometryObj.coordinates;
|
||||
for (var i = 0; i < c.length; i++) {
|
||||
var ci = c[i];
|
||||
outCoordinates && (outCoordinates[i] = []);
|
||||
for (var j = 0; j < ci.length; j++) {
|
||||
var cij = ci[j];
|
||||
var lon = cij[0],
|
||||
lat = cij[1];
|
||||
if (lon < res.southWest.lon) res.southWest.lon = lon;
|
||||
if (lat < res.southWest.lat) res.southWest.lat = lat;
|
||||
if (lon > res.northEast.lon) res.northEast.lon = lon;
|
||||
if (lat > res.northEast.lat) res.northEast.lat = lat;
|
||||
outCoordinates && (outCoordinates[i][j] = [lon, lat]);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
res.southWest.lon = res.southWest.lat = res.northEast.lon = res.northEast.lat = 0.0;
|
||||
outCoordinates && (outCoordinates[0] = lon) && (outCoordinates[1] = lat);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
setGeometry(geoJson) {
|
||||
var h = this._handler;
|
||||
this.remove();
|
||||
this._type = Geometry.getType(geoJson.type || "Point");
|
||||
this._extent = Geometry.getExtent(geoJson, this._coordinates);
|
||||
h.add(this);
|
||||
return this;
|
||||
}
|
||||
|
||||
setFillColor(r, g, b, a) {
|
||||
var c = this._style.fillColor;
|
||||
if (c.w === 0.0 && a !== 0.0 || c.w !== 0.0 && a === 0.0) {
|
||||
this._pickingReady = false;
|
||||
}
|
||||
c.x = r;
|
||||
c.y = g;
|
||||
c.z = b;
|
||||
c.w = a;
|
||||
this._handler && this._handler.setPolyColorArr(this, c);
|
||||
return this;
|
||||
}
|
||||
|
||||
setFillColor4v(rgba) {
|
||||
return this.setFillColor(rgba.x, rgba.y, rgba.z, rgba.w);
|
||||
}
|
||||
|
||||
setStrokeColor(r, g, b, a) {
|
||||
var c = this._style.strokeColor;
|
||||
if (c.w === 0.0 && a !== 0.0 || c.w !== 0.0 && a === 0.0) {
|
||||
this._pickingReady = false;
|
||||
}
|
||||
c.x = r;
|
||||
c.y = g;
|
||||
c.z = b;
|
||||
c.w = a;
|
||||
this._handler && this._handler.setStrokeColorArr(this, c);
|
||||
return this;
|
||||
}
|
||||
|
||||
setLineColor(r, g, b, a) {
|
||||
var c = this._style.lineColor;
|
||||
if (c.w === 0.0 && a !== 0.0 || c.w !== 0.0 && a === 0.0) {
|
||||
this._pickingReady = false;
|
||||
}
|
||||
c.x = r;
|
||||
c.y = g;
|
||||
c.z = b;
|
||||
c.w = a;
|
||||
this._handler && this._handler.setLineColorArr(this, c);
|
||||
return this;
|
||||
}
|
||||
|
||||
setStrokeColor4v(rgba) {
|
||||
return this.setStrokeColor(rgba.x, rgba.y, rgba.z, rgba.w);
|
||||
}
|
||||
|
||||
setLineColor4v(rgba) {
|
||||
return this.setLineColor(rgba.x, rgba.y, rgba.z, rgba.w);
|
||||
}
|
||||
|
||||
setStrokeOpacity(opacity) {
|
||||
var c = this._style.strokeColor;
|
||||
c.w = opacity;
|
||||
return this.setStrokeColor(c.x, c.y, c.z, opacity);
|
||||
}
|
||||
|
||||
setLineOpacity(opacity) {
|
||||
var c = this._style.lineColor;
|
||||
c.w = opacity;
|
||||
return this.setLineColor(c.x, c.y, c.z, opacity);
|
||||
}
|
||||
|
||||
setStrokeWidth(width) {
|
||||
this._style.strokeWidth = width;
|
||||
this._pickingReady = false;
|
||||
this._handler && this._handler.setLineStrokeArr(this, width);
|
||||
return this;
|
||||
}
|
||||
|
||||
bringToFront() {
|
||||
this._handler && this._handler.bringToFront(this);
|
||||
return this;
|
||||
}
|
||||
|
||||
setLineWidth(width) {
|
||||
this._style.lineWidth = width;
|
||||
this._pickingReady = false;
|
||||
this._handler && this._handler.setLineThicknessArr(this, width);
|
||||
return this;
|
||||
}
|
||||
|
||||
setFillOpacity(opacity) {
|
||||
var c = this._style.fillColor;
|
||||
if (c.w === 0.0 && opacity !== 0.0 || c.w !== 0.0 && opacity === 0.0) {
|
||||
this._pickingReady = false;
|
||||
}
|
||||
c.w = opacity;
|
||||
this._handler && this._handler.setPolyColorArr(this, c);
|
||||
return this;
|
||||
}
|
||||
|
||||
setVisibility(visibility) {
|
||||
this._visibility = visibility;
|
||||
this._handler && this._handler.setGeometryVisibility(this);
|
||||
return this;
|
||||
}
|
||||
|
||||
getVisibility() {
|
||||
return this._visibility;
|
||||
}
|
||||
|
||||
remove() {
|
||||
this._handler && this._handler.remove(this);
|
||||
}
|
||||
|
||||
etExtent() {
|
||||
return this._extent.clone();
|
||||
}
|
||||
|
||||
getType() {
|
||||
return this._type;
|
||||
}
|
||||
};
|
||||
|
||||
export { Geometry, GeometryType };
|
||||
372
src/og/entity/Label.js
Normal file
372
src/og/entity/Label.js
Normal file
@ -0,0 +1,372 @@
|
||||
/**
|
||||
* @module og/entity/Label
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as utils from '../utils/shared.js';
|
||||
import { BaseBillboard } from './BaseBillboard.js';
|
||||
import { Vec4 } from '../math/Vec4.js';
|
||||
|
||||
|
||||
const ALIGN = {
|
||||
RIGHT: 0,
|
||||
LEFT: 1,
|
||||
CENTER: 2
|
||||
};
|
||||
|
||||
/**
|
||||
* Text align options.
|
||||
* @readonly
|
||||
* @enum {number}
|
||||
*/
|
||||
const STR2ALIGN = {
|
||||
"left": ALIGN.LEFT,
|
||||
"right": ALIGN.RIGHT,
|
||||
"center": ALIGN.CENTER
|
||||
};
|
||||
|
||||
/**
|
||||
* Billboard text label.
|
||||
* @class
|
||||
* @extends {og.BaseBillboard}
|
||||
* @param {Object} [options] - Label options:
|
||||
* @param {og.math.Vector3|Array.<number>} [options.position] - Billboard spatial position.
|
||||
* @param {number} [options.rotation] - Screen angle rotaion.
|
||||
* @param {og.math.Vector4|string|Array.<number>} [options.color] - Billboard color.
|
||||
* @param {og.math.Vector3|Array.<number>} [options.alignedAxis] - Billboard aligned vector.
|
||||
* @param {og.math.Vector3|Array.<number>} [options.offset] - Billboard center screen offset.
|
||||
* @param {boolean} [options.visibility] - Visibility.
|
||||
* @param {string} [options.text] - Text string.
|
||||
* @param {string} [options.face] - HTML5 font face.
|
||||
* @param {number} [options.size] - Font size in pixels.
|
||||
* @param {string} [options.style] - HTML5 font style. Example 'normal', 'italic'.
|
||||
* @param {string} [options.weight] - HTML5 font weight. Example 'normal', 'bold'.
|
||||
* @param {number} [options.outline] - Text outline size. 0 - no outline, 1 - maximum outline. Default 0.58.
|
||||
* @param {og.math.Vector4|string|Array.<number>} [options.outlineColor] - Outline color.
|
||||
* @param {og.Label.ALIGN} [options.align] - Text horizontal align: "left", "right" and "center".
|
||||
*/
|
||||
class Label extends BaseBillboard {
|
||||
constructor(options) {
|
||||
super(options);
|
||||
|
||||
options = options || {};
|
||||
|
||||
/**
|
||||
* Label text string.
|
||||
* @private
|
||||
* @type {string}
|
||||
*/
|
||||
this._text = options.text;
|
||||
|
||||
/**
|
||||
* HTML5 font face.
|
||||
* @private
|
||||
* @type {string}
|
||||
*/
|
||||
this._face = utils.defaultString(options.face, null);
|
||||
|
||||
/**
|
||||
* Font size in pixels.
|
||||
* @private
|
||||
* @type {number}
|
||||
*/
|
||||
this._size = options.size || 33;
|
||||
|
||||
/**
|
||||
* HTML5 font style. Example 'normal', 'italic'.
|
||||
* @private
|
||||
* @type {string}
|
||||
*/
|
||||
this._style = utils.defaultString(options.style, null);
|
||||
|
||||
/**
|
||||
* HTML5 font weight style. Example 'normal', 'bold'.
|
||||
* @private
|
||||
* @type {string}
|
||||
*/
|
||||
this._weight = utils.defaultString(options.weight, null);
|
||||
|
||||
/**
|
||||
* Label outline.
|
||||
* @private
|
||||
* @type {number}
|
||||
*/
|
||||
this._outline = options.outline != undefined ? options.outline : 0.5;
|
||||
|
||||
/**
|
||||
* Label outline color.
|
||||
* @private
|
||||
* @type {og.math.Vector4}
|
||||
*/
|
||||
this._outlineColor = utils.createColorRGBA(options.outlineColor, new Vec4(0.0, 0.0, 0.0, 1.0));
|
||||
|
||||
/**
|
||||
* Text horizontal align: "left", "right" and "center".
|
||||
* @private
|
||||
* @type {og.Label.ALIGN}
|
||||
*/
|
||||
this._align = options.align ? STR2ALIGN[options.align.trim().toLowerCase()] || ALIGN.RIGHT : ALIGN.RIGHT;
|
||||
|
||||
/**
|
||||
* Label font atlas index.
|
||||
* @private
|
||||
* @type {number}
|
||||
*/
|
||||
this._fontIndex = 0;
|
||||
|
||||
/**
|
||||
* Font atlas pointer.
|
||||
* @private
|
||||
* @type {og.utils.FontAtlas}
|
||||
*/
|
||||
this._fontAtlas = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets lablel text.
|
||||
* @public
|
||||
* @param {string} text - Text string.
|
||||
* It can't be bigger than maximum labelHandler _maxLetters value.
|
||||
*/
|
||||
setText(text) {
|
||||
this._text = text;
|
||||
this._handler && this._handler.setText(this._handlerIndex, text, this._fontIndex, this._align);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets current text string.
|
||||
* @public
|
||||
* @returns {string}
|
||||
*/
|
||||
getText() {
|
||||
return this._text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets label text align. Could be center, left or right. Left is default.
|
||||
* @public
|
||||
* @param {og.Label.ALIGN} align - Text align.
|
||||
*/
|
||||
setAlign(align) {
|
||||
this._align = STR2ALIGN[align.trim().toLowerCase()];
|
||||
this._handler && this._handler.setText(this._handlerIndex, this._text, this._fontIndex, this._align);
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets label text current alignment.
|
||||
* @public
|
||||
* @returns {og.Label.ALIGN}
|
||||
*/
|
||||
getAlign() {
|
||||
return this._align;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets font face family.
|
||||
* @public
|
||||
* @param {string} face - Font face family.
|
||||
*/
|
||||
setFace(face) {
|
||||
this._face = face.trim().toLowerCase();
|
||||
this.update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets current font face.
|
||||
* @public
|
||||
* @returns {string}
|
||||
*/
|
||||
getFace() {
|
||||
return this._face;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets label font size in pixels.
|
||||
* @public
|
||||
* @param {number} size - Label size in pixels.
|
||||
*/
|
||||
setSize(size) {
|
||||
this._size = size;
|
||||
this._handler && this._handler.setSizeArr(this._handlerIndex, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets label size in pixels.
|
||||
* @public
|
||||
* @returns {number}
|
||||
*/
|
||||
getSize() {
|
||||
return this._size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets font HTML5 style. It's can be Italic or Normal values.
|
||||
* @public
|
||||
* @param {string} style - HTML5 font style.
|
||||
*/
|
||||
setStyle(style) {
|
||||
this._style = style.trim().toLowerCase();
|
||||
this.update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets label font style.
|
||||
* @public
|
||||
* @returns {string}
|
||||
*/
|
||||
getStyle() {
|
||||
return this._style;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets label font HTML5 weight style. It's can be bold or normal.
|
||||
* @public
|
||||
* @param {string} weight - HTML5 font weight style.
|
||||
*/
|
||||
setWeight(weight) {
|
||||
this._weight = weight.trim().toLowerCase();
|
||||
this.update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets label font weight.
|
||||
* @public
|
||||
* @returns {string}
|
||||
*/
|
||||
getWeight() {
|
||||
return this._wight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets text outline border size. Where 0 - is no outline and 1 - is the maximum outline size.
|
||||
* @public
|
||||
* @param {number} outline - Text outline size.
|
||||
*/
|
||||
setOutline(outline) {
|
||||
this._outline = outline;
|
||||
this._handler && this._handler.setOutlineArr(this._handlerIndex, 1.0 - outline);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets text current outline size.
|
||||
* @public
|
||||
* @returns {number}
|
||||
*/
|
||||
getOutline() {
|
||||
return this._outline;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets label opacity.
|
||||
* @public
|
||||
* @param {number} a - Label opacity.
|
||||
*/
|
||||
setOpacity(a) {
|
||||
this._color.w = a;
|
||||
this.setColor4v(this._color);
|
||||
this._outlineColor.w = a;
|
||||
this.setOutlineColor4v(this._outlineColor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets text outline color.
|
||||
* @public
|
||||
* @param {number} r - Red.
|
||||
* @param {number} g - Green.
|
||||
* @param {number} b - Blue.
|
||||
* @param {number} a - Alpha.
|
||||
*/
|
||||
setOutlineColor(r, g, b, a) {
|
||||
this._outlineColor.x = r;
|
||||
this._outlineColor.y = g;
|
||||
this._outlineColor.z = b;
|
||||
this._outlineColor.w = a;
|
||||
this._handler && this._handler.setOutlineColorArr(this._handlerIndex, this._outlineColor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets text outline color.
|
||||
* @public
|
||||
* @param {og.math.Vector4} rgba - Color vector.
|
||||
*/
|
||||
setOutlineColor4v(rgba) {
|
||||
this._outlineColor.x = rgba.x;
|
||||
this._outlineColor.y = rgba.y;
|
||||
this._outlineColor.z = rgba.z;
|
||||
this._outlineColor.w = rgba.w;
|
||||
this._handler && this._handler.setOutlineColorArr(this._handlerIndex, rgba);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets text outline color HTML string.
|
||||
* @public
|
||||
* @param {string} color - HTML string color.
|
||||
*/
|
||||
setOutlineColorHTML(color) {
|
||||
this.setOutlineColor4v(utils.htmlColorToRgba(color));
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets outline color vector.
|
||||
* @public
|
||||
* @returns {og.math.Vector4}
|
||||
*/
|
||||
getOutlineColor() {
|
||||
return this._outlineColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets outline opacity. Actually outline color alpha value.
|
||||
* @public
|
||||
* @param {number} opacity - Outline opacity.
|
||||
*/
|
||||
setOutlineOpacity(opacity) {
|
||||
this._outlineColor.w = opacity;
|
||||
this._handler && this._handler.setOutlineColorArr(this._handlerIndex, this._outlineColor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets outline opacity value.
|
||||
* @public
|
||||
* @returns {number}
|
||||
*/
|
||||
getOutlineOpacity() {
|
||||
return this._outlineColor.w;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates label parameters.
|
||||
* @public
|
||||
*/
|
||||
update() {
|
||||
if (this._fontAtlas) {
|
||||
var fontIndex = this._fontAtlas.getFontIndex(this._face, this._style, this._weight);
|
||||
if (fontIndex == undefined) {
|
||||
this._fontAtlas.createFontAsync(this._face, this._style, this._weight, this._applyFontIndex.bind(this));
|
||||
} else {
|
||||
this._applyFontIndex(fontIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_applyFontIndex(fontIndex) {
|
||||
this._fontIndex = fontIndex;
|
||||
if (this._handler) {
|
||||
this._handler.setFontIndexArr(this._handlerIndex, this._fontIndex);
|
||||
this._handler.setText(this._handlerIndex, this._text, this._fontIndex, this._align);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns font atlas and update.
|
||||
* @public
|
||||
* @param {og.utils.FontAtlas} fontAtlas - Font atlas.
|
||||
*/
|
||||
assignFontAtlas(fontAtlas) {
|
||||
!this._fontAtlas && (this._fontAtlas = fontAtlas);
|
||||
this.update();
|
||||
}
|
||||
};
|
||||
|
||||
export { Label, ALIGN };
|
||||
818
src/og/entity/LabelHandler.js
Normal file
818
src/og/entity/LabelHandler.js
Normal file
@ -0,0 +1,818 @@
|
||||
/**
|
||||
* @module og/entity/LabelHandler
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as shaders from '../shaders/label.js';
|
||||
import { ALIGN } from './Label.js';
|
||||
import { BillboardHandler } from './BillboardHandler.js';
|
||||
|
||||
|
||||
const FONTINDEX_BUFFER = 9;
|
||||
const OUTLINE_BUFFER = 10;
|
||||
const OUTLINECOLOR_BUFFER = 11;
|
||||
|
||||
/*
|
||||
* og.LabelHandler
|
||||
*
|
||||
*
|
||||
*/
|
||||
class LabelHandler extends BillboardHandler {
|
||||
constructor(entityCollection) {
|
||||
|
||||
super(entityCollection);
|
||||
|
||||
this._fontIndexBuffer = null;
|
||||
this._noOutlineBuffer = null;
|
||||
this._outlineBuffer = null;
|
||||
this._outlineColorBuffer = null;
|
||||
|
||||
this._fontIndexArr = [];
|
||||
this._noOutlineArr = [];
|
||||
this._outlineArr = [];
|
||||
this._outlineColorArr = [];
|
||||
|
||||
this._buffersUpdateCallbacks[FONTINDEX_BUFFER] = this.createFontIndexBuffer;
|
||||
this._buffersUpdateCallbacks[OUTLINE_BUFFER] = this.createOutlineBuffer;
|
||||
this._buffersUpdateCallbacks[OUTLINECOLOR_BUFFER] = this.createOutlineColorBuffer;
|
||||
|
||||
this._changedBuffers = new Array(this._buffersUpdateCallbacks.length);
|
||||
|
||||
this._maxLetters = 25;
|
||||
}
|
||||
|
||||
|
||||
initShaderProgram() {
|
||||
if (this._renderer.handler) {
|
||||
if (!this._renderer.handler.shaderPrograms.label) {
|
||||
var isSingleBuffer = !this._renderer.isMultiFramebufferCompatible();
|
||||
this._renderer.handler.addShaderProgram(shaders.label(isSingleBuffer));
|
||||
}
|
||||
if (!this._renderer.handler.shaderPrograms.labelPicking) {
|
||||
this._renderer.handler.addShaderProgram(shaders.labelPicking());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
add(label) {
|
||||
if (label._handlerIndex == -1) {
|
||||
label._handler = this;
|
||||
label._handlerIndex = this._billboards.length;
|
||||
this._billboards.push(label);
|
||||
this._addBillboardToArrays(label);
|
||||
this.refresh();
|
||||
this.assignFontAtlas(label);
|
||||
}
|
||||
}
|
||||
|
||||
assignFontAtlas(label) {
|
||||
if (this._entityCollection && this._entityCollection.renderNode) {
|
||||
label.assignFontAtlas(this._entityCollection.renderNode.fontAtlas);
|
||||
}
|
||||
}
|
||||
|
||||
clear() {
|
||||
|
||||
this._texCoordArr.length = 0;
|
||||
this._vertexArr.length = 0;
|
||||
this._positionArr.length = 0;
|
||||
this._sizeArr.length = 0;
|
||||
this._offsetArr.length = 0;
|
||||
this._rgbaArr.length = 0;
|
||||
this._rotationArr.length = 0;
|
||||
this._alignedAxisArr.length = 0;
|
||||
this._fontIndexArr.length = 0;
|
||||
this._noOutlineArr.length = 0;
|
||||
this._outlineArr.length = 0;
|
||||
this._outlineColorArr.length = 0;
|
||||
|
||||
this._texCoordArr = [];
|
||||
this._vertexArr = [];
|
||||
this._positionArr = [];
|
||||
this._sizeArr = [];
|
||||
this._offsetArr = [];
|
||||
this._rgbaArr = [];
|
||||
this._rotationArr = [];
|
||||
this._alignedAxisArr = [];
|
||||
this._fontIndexArr = [];
|
||||
this._noOutlineArr = [];
|
||||
this._outlineArr = [];
|
||||
this._outlineColorArr = [];
|
||||
|
||||
this._removeBillboards();
|
||||
this._deleteBuffers();
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
_deleteBuffers() {
|
||||
var gl = this._renderer.handler.gl;
|
||||
gl.deleteBuffer(this._sizeBuffer);
|
||||
gl.deleteBuffer(this._fontIndexBuffer);
|
||||
gl.deleteBuffer(this._texCoordBuffer);
|
||||
gl.deleteBuffer(this._outlineBuffer);
|
||||
gl.deleteBuffer(this._noOutlineBuffer);
|
||||
gl.deleteBuffer(this._outlineColorBuffer);
|
||||
gl.deleteBuffer(this._positionBuffer);
|
||||
gl.deleteBuffer(this._sizeBuffer);
|
||||
gl.deleteBuffer(this._offsetBuffer);
|
||||
gl.deleteBuffer(this._rgbaBuffer);
|
||||
gl.deleteBuffer(this._rotationBuffer);
|
||||
gl.deleteBuffer(this._vertexBuffer);
|
||||
gl.deleteBuffer(this._texCoordBuffer);
|
||||
gl.deleteBuffer(this._alignedAxisBuffer);
|
||||
gl.deleteBuffer(this._pickingColorBuffer);
|
||||
|
||||
this._sizeBuffer = null;
|
||||
this._fontIndexBuffer = null;
|
||||
this._texCoordBuffer = null;
|
||||
this._outlineBuffer = null;
|
||||
this._outlineColorBuffer = null;
|
||||
this._positionBuffer = null;
|
||||
this._sizeBuffer = null;
|
||||
this._offsetBuffer = null;
|
||||
this._rgbaBuffer = null;
|
||||
this._rotationBuffer = null;
|
||||
this._vertexBuffer = null;
|
||||
this._texCoordBuffer = null;
|
||||
this._alignedAxisBuffer = null;
|
||||
this._pickingColorBuffer = null;
|
||||
|
||||
}
|
||||
|
||||
_addBillboardToArrays(label) {
|
||||
for (var i = 0; i < this._maxLetters; i++) {
|
||||
if (label._visibility) {
|
||||
BillboardHandler.concArr(this._vertexArr, [-0.5, 0.5, -0.5, -0.5, 0.5, -0.5, 0.5, -0.5, 0.5, 0.5, -0.5, 0.5]);
|
||||
} else {
|
||||
BillboardHandler.concArr(this._vertexArr, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||
}
|
||||
|
||||
BillboardHandler.concArr(this._texCoordArr, [0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0]);
|
||||
|
||||
var x = label._position.x, y = label._position.y, z = label._position.z, w = label._scale;
|
||||
BillboardHandler.concArr(this._positionArr, [x, y, z, w, x, y, z, w, x, y, z, w, x, y, z, w, x, y, z, w, x, y, z, w]);
|
||||
|
||||
x = label._size;
|
||||
BillboardHandler.concArr(this._sizeArr, [x, x, x, x, x, x]);
|
||||
|
||||
x = label._offset.x; y = label._offset.y; z = label._offset.z - 0.05;
|
||||
BillboardHandler.concArr(this._offsetArr, [x, y, z, x, y, z, x, y, z, x, y, z, x, y, z, x, y, z]);
|
||||
|
||||
x = label._color.x; y = label._color.y; z = label._color.z; w = label._color.w;
|
||||
BillboardHandler.concArr(this._rgbaArr, [x, y, z, w, x, y, z, w, x, y, z, w, x, y, z, w, x, y, z, w, x, y, z, w]);
|
||||
|
||||
x = label._rotation;
|
||||
BillboardHandler.concArr(this._rotationArr, [x, x, x, x, x, x]);
|
||||
|
||||
x = label._alignedAxis.x, y = label._alignedAxis.y, z = label._alignedAxis.z;
|
||||
BillboardHandler.concArr(this._alignedAxisArr, [x, y, z, x, y, z, x, y, z, x, y, z, x, y, z, x, y, z]);
|
||||
|
||||
x = label._fontIndex;
|
||||
BillboardHandler.concArr(this._fontIndexArr, [0, 0, 0, 0, 0, 0]);
|
||||
|
||||
x = 1.0 - label._outline, y = 0.0;
|
||||
BillboardHandler.concArr(this._outlineArr, [x, y, x, y, x, y, x, y, x, y, x, y]);
|
||||
|
||||
x = 0.75, y = 0.7;
|
||||
BillboardHandler.concArr(this._noOutlineArr, [x, y, x, y, x, y, x, y, x, y, x, y]);
|
||||
|
||||
x = label._outlineColor.x; y = label._outlineColor.y; z = label._outlineColor.z; w = label._outlineColor.w;
|
||||
BillboardHandler.concArr(this._outlineColorArr, [x, y, z, w, x, y, z, w, x, y, z, w, x, y, z, w, x, y, z, w, x, y, z, w]);
|
||||
|
||||
x = label._entity._pickingColor.x / 255, y = label._entity._pickingColor.y / 255, z = label._entity._pickingColor.z / 255;
|
||||
BillboardHandler.concArr(this._pickingColorArr, [x, y, z, x, y, z, x, y, z, x, y, z, x, y, z, x, y, z]);
|
||||
}
|
||||
};
|
||||
|
||||
_displayPASS() {
|
||||
var r = this._renderer;
|
||||
var h = r.handler;
|
||||
h.shaderPrograms.label.activate();
|
||||
var sh = h.shaderPrograms.label._program;
|
||||
var sha = sh.attributes,
|
||||
shu = sh.uniforms;
|
||||
|
||||
var gl = h.gl;
|
||||
|
||||
var ec = this._entityCollection;
|
||||
var rn = ec.renderNode;
|
||||
|
||||
gl.uniform1iv(shu.u_fontTextureArr._pName, rn.fontAtlas.samplerArr);
|
||||
|
||||
gl.uniformMatrix4fv(shu.viewMatrix._pName, false, r.activeCamera._viewMatrix._m);
|
||||
gl.uniformMatrix4fv(shu.projectionMatrix._pName, false, r.activeCamera._projectionMatrix._m);
|
||||
|
||||
gl.uniform3fv(shu.uCamPos._pName, r.activeCamera.eye.toVec());
|
||||
|
||||
gl.uniform3fv(shu.uScaleByDistance._pName, ec.scaleByDistance);
|
||||
|
||||
gl.uniform1f(shu.uOpacity._pName, ec._animatedOpacity);
|
||||
|
||||
gl.uniform2fv(shu.uFloatParams._pName, [rn._planetRadius2 || 0, r.activeCamera._tanViewAngle_hradOneByHeight]);
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this._texCoordBuffer);
|
||||
gl.vertexAttribPointer(sha.a_texCoord._pName, this._texCoordBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this._vertexBuffer);
|
||||
gl.vertexAttribPointer(sha.a_vertices._pName, this._vertexBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this._positionBuffer);
|
||||
gl.vertexAttribPointer(sha.a_positions._pName, this._positionBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this._sizeBuffer);
|
||||
gl.vertexAttribPointer(sha.a_size._pName, this._sizeBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this._offsetBuffer);
|
||||
gl.vertexAttribPointer(sha.a_offset._pName, this._offsetBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this._rotationBuffer);
|
||||
gl.vertexAttribPointer(sha.a_rotation._pName, this._rotationBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this._alignedAxisBuffer);
|
||||
gl.vertexAttribPointer(sha.a_alignedAxis._pName, this._alignedAxisBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this._fontIndexBuffer);
|
||||
gl.vertexAttribPointer(sha.a_fontIndex._pName, this._fontIndexBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
|
||||
//buffer
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this._outlineColorBuffer);
|
||||
gl.vertexAttribPointer(sha.a_rgba._pName, this._outlineColorBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this._outlineBuffer);
|
||||
gl.vertexAttribPointer(sha.a_bufferAA._pName, this._outlineBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
|
||||
gl.uniform1f(shu.uZ._pName, -2.0);
|
||||
gl.drawArrays(gl.TRIANGLES, 0, this._vertexBuffer.numItems);
|
||||
|
||||
//nobuffer
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this._rgbaBuffer);
|
||||
gl.vertexAttribPointer(sha.a_rgba._pName, this._rgbaBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this._noOutlineBuffer);
|
||||
gl.vertexAttribPointer(sha.a_bufferAA._pName, this._noOutlineBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
|
||||
gl.uniform1f(shu.uZ._pName, -10.0);
|
||||
gl.drawArrays(gl.TRIANGLES, 0, this._vertexBuffer.numItems);
|
||||
|
||||
}
|
||||
|
||||
_pickingPASS() {
|
||||
var r = this._renderer;
|
||||
var h = r.handler;
|
||||
h.shaderPrograms.labelPicking.activate();
|
||||
var sh = h.shaderPrograms.labelPicking._program;
|
||||
var sha = sh.attributes,
|
||||
shu = sh.uniforms;
|
||||
|
||||
var gl = h.gl;
|
||||
|
||||
gl.uniformMatrix4fv(shu.viewMatrix._pName, false, r.activeCamera._viewMatrix._m);
|
||||
gl.uniformMatrix4fv(shu.projectionMatrix._pName, false, r.activeCamera._projectionMatrix._m);
|
||||
|
||||
gl.uniform3fv(shu.uCamPos._pName, r.activeCamera.eye.toVec());
|
||||
|
||||
gl.uniform3fv(shu.uScaleByDistance._pName, this._entityCollection.scaleByDistance);
|
||||
|
||||
gl.uniform1f(shu.uOpacity._pName, this._entityCollection._animatedOpacity);
|
||||
|
||||
gl.uniform2fv(shu.uFloatParams._pName, [this._entityCollection.renderNode._planetRadius2 || 0, r.activeCamera._tanViewAngle_hradOneByHeight]);
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this._vertexBuffer);
|
||||
gl.vertexAttribPointer(sha.a_vertices._pName, this._vertexBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this._texCoordBuffer);
|
||||
gl.vertexAttribPointer(sha.a_texCoord._pName, this._texCoordBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this._positionBuffer);
|
||||
gl.vertexAttribPointer(sha.a_positions._pName, this._positionBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this._sizeBuffer);
|
||||
gl.vertexAttribPointer(sha.a_size._pName, this._sizeBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this._offsetBuffer);
|
||||
gl.vertexAttribPointer(sha.a_offset._pName, this._offsetBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this._rotationBuffer);
|
||||
gl.vertexAttribPointer(sha.a_rotation._pName, this._rotationBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this._alignedAxisBuffer);
|
||||
gl.vertexAttribPointer(sha.a_alignedAxis._pName, this._alignedAxisBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this._pickingColorBuffer);
|
||||
gl.vertexAttribPointer(sha.a_pickingColor._pName, this._pickingColorBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
|
||||
gl.drawArrays(gl.TRIANGLES, 0, this._vertexBuffer.numItems);
|
||||
}
|
||||
|
||||
_removeBillboard(label) {
|
||||
var li = label._handlerIndex;
|
||||
|
||||
this._billboards.splice(li, 1);
|
||||
|
||||
var ml = 24 * this._maxLetters;
|
||||
var i = li * ml;
|
||||
this._rgbaArr.splice(i, ml);
|
||||
this._outlineColorArr.splice(i, ml);
|
||||
this._texCoordArr.splice(i, ml);
|
||||
this._positionArr.splice(i, ml);
|
||||
|
||||
ml = 18 * this._maxLetters;
|
||||
i = li * ml;
|
||||
this._offsetArr.splice(i, ml);
|
||||
this._alignedAxisArr.splice(i, ml);
|
||||
this._pickingColorArr.splice(i, ml);
|
||||
|
||||
ml = 12 * this._maxLetters;
|
||||
i = li * ml;
|
||||
this._vertexArr.splice(i, ml);
|
||||
this._outlineArr.splice(i, ml);
|
||||
this._noOutlineArr.splice(i, ml);
|
||||
|
||||
ml = 6 * this._maxLetters;
|
||||
i = li * ml;
|
||||
this._sizeArr.splice(i, ml);
|
||||
this._rotationArr.splice(i, ml);
|
||||
this._fontIndexArr.splice(i, ml);
|
||||
|
||||
this.reindexBillbordsArray(li);
|
||||
this.refresh();
|
||||
|
||||
label._handlerIndex = -1;
|
||||
label._handler = null;
|
||||
//label._fontIndex = 0;
|
||||
//label._fontAtlas = null;
|
||||
};
|
||||
|
||||
setText(index, text, fontIndex, align) {
|
||||
|
||||
var fa = this._entityCollection.renderNode.fontAtlas.atlasesArr[fontIndex];
|
||||
|
||||
if (!fa) return;
|
||||
|
||||
var i = index * 24 * this._maxLetters;
|
||||
var a = this._texCoordArr;
|
||||
|
||||
var c = 0;
|
||||
|
||||
var j = i + c * 24;
|
||||
var n = fa.nodes[text[c]];
|
||||
var f = n ? n.emptySize : 0.0;
|
||||
var offset = f;
|
||||
|
||||
for (c = 0; c < text.length; c++) {
|
||||
var j = i + c * 24;
|
||||
var n = fa.nodes[text[c]] || fa.nodes[" "];
|
||||
var tc = n.texCoords;
|
||||
|
||||
a[j] = tc[0];
|
||||
a[j + 1] = tc[1];
|
||||
a[j + 2] = offset;
|
||||
a[j + 3] = 0.0;
|
||||
|
||||
a[j + 4] = tc[2];
|
||||
a[j + 5] = tc[3];
|
||||
a[j + 6] = offset;
|
||||
a[j + 7] = 0.0;
|
||||
|
||||
a[j + 8] = tc[4];
|
||||
a[j + 9] = tc[5];
|
||||
a[j + 10] = offset;
|
||||
a[j + 11] = 0.0;
|
||||
|
||||
a[j + 12] = tc[6];
|
||||
a[j + 13] = tc[7];
|
||||
a[j + 14] = offset;
|
||||
a[j + 15] = 0.0;
|
||||
|
||||
a[j + 16] = tc[8];
|
||||
a[j + 17] = tc[9];
|
||||
a[j + 18] = offset;
|
||||
a[j + 19] = 0.0;
|
||||
|
||||
a[j + 20] = tc[10];
|
||||
a[j + 21] = tc[11];
|
||||
a[j + 22] = offset;
|
||||
a[j + 23] = 0.0;
|
||||
|
||||
offset += n.emptySize;
|
||||
}
|
||||
|
||||
//49/512 - font atlas left border letter offset
|
||||
if (align == ALIGN.CENTER) {
|
||||
offset = (f + 49 / 512 - offset) * 0.5;
|
||||
for (c = 0; c < text.length; c++) {
|
||||
var j = i + c * 24;
|
||||
a[j + 3] = offset;
|
||||
a[j + 7] = offset;
|
||||
a[j + 11] = offset;
|
||||
a[j + 15] = offset;
|
||||
a[j + 19] = offset;
|
||||
a[j + 23] = offset;
|
||||
}
|
||||
} else if (align == ALIGN.LEFT) {
|
||||
offset = (f + 49 / 512 - offset);
|
||||
for (c = 0; c < text.length; c++) {
|
||||
var j = i + c * 24;
|
||||
a[j + 3] = offset;
|
||||
a[j + 7] = offset;
|
||||
a[j + 11] = offset;
|
||||
a[j + 15] = offset;
|
||||
a[j + 19] = offset;
|
||||
a[j + 23] = offset;
|
||||
}
|
||||
}
|
||||
|
||||
for (var c = c; c < this._maxLetters; c++) {
|
||||
var j = i + c * 24;
|
||||
a[j + 2] = -1.0;
|
||||
a[j + 6] = -1.0;
|
||||
a[j + 10] = -1.0;
|
||||
a[j + 14] = -1.0;
|
||||
a[j + 18] = -1.0;
|
||||
a[j + 17] = -1.0;
|
||||
}
|
||||
|
||||
this._changedBuffers[TEXCOORD_BUFFER] = true;
|
||||
}
|
||||
|
||||
setPositionArr(index, position) {
|
||||
var i = index * 24 * this._maxLetters;
|
||||
var a = this._positionArr, x = position.x, y = position.y, z = position.z;
|
||||
|
||||
for (var q = 0; q < this._maxLetters; q++) {
|
||||
var j = i + q * 24;
|
||||
a[j] = x;
|
||||
a[j + 1] = y;
|
||||
a[j + 2] = z;
|
||||
|
||||
a[j + 4] = x;
|
||||
a[j + 5] = y;
|
||||
a[j + 6] = z;
|
||||
|
||||
a[j + 8] = x;
|
||||
a[j + 9] = y;
|
||||
a[j + 10] = z;
|
||||
|
||||
a[j + 12] = x;
|
||||
a[j + 13] = y;
|
||||
a[j + 14] = z;
|
||||
|
||||
a[j + 16] = x;
|
||||
a[j + 17] = y;
|
||||
a[j + 18] = z;
|
||||
|
||||
a[j + 20] = x;
|
||||
a[j + 21] = y;
|
||||
a[j + 22] = z;
|
||||
}
|
||||
|
||||
this._changedBuffers[POSITION_BUFFER] = true;
|
||||
}
|
||||
|
||||
setScaleArr(index, scale) {
|
||||
|
||||
var i = index * 24 * this._maxLetters;
|
||||
var a = this._positionArr;
|
||||
for (var q = 0; q < this._maxLetters; q++) {
|
||||
var j = i + q * 24;
|
||||
a[j + 3] = scale;
|
||||
a[j + 7] = scale;
|
||||
a[j + 11] = scale;
|
||||
a[j + 15] = scale;
|
||||
a[j + 19] = scale;
|
||||
a[j + 23] = scale;
|
||||
}
|
||||
|
||||
this._changedBuffers[POSITION_BUFFER] = true;
|
||||
}
|
||||
|
||||
setPickingColorArr(index, color) {
|
||||
var i = index * 18 * this._maxLetters;
|
||||
var a = this._pickingColorArr, x = color.x / 255, y = color.y / 255, z = color.z / 255;
|
||||
|
||||
for (var q = 0; q < this._maxLetters; q++) {
|
||||
var j = i + q * 18;
|
||||
a[j] = x;
|
||||
a[j + 1] = y;
|
||||
a[j + 2] = z;
|
||||
|
||||
a[j + 3] = x;
|
||||
a[j + 4] = y;
|
||||
a[j + 5] = z;
|
||||
|
||||
a[j + 6] = x;
|
||||
a[j + 7] = y;
|
||||
a[j + 8] = z;
|
||||
|
||||
a[j + 9] = x;
|
||||
a[j + 10] = y;
|
||||
a[j + 11] = z;
|
||||
|
||||
a[j + 12] = x;
|
||||
a[j + 13] = y;
|
||||
a[j + 14] = z;
|
||||
|
||||
a[j + 15] = x;
|
||||
a[j + 16] = y;
|
||||
a[j + 17] = z;
|
||||
}
|
||||
|
||||
this._changedBuffers[PICKINGCOLOR_BUFFER] = true;
|
||||
}
|
||||
|
||||
setSizeArr(index, size) {
|
||||
|
||||
var i = index * 6 * this._maxLetters;
|
||||
var a = this._sizeArr;
|
||||
|
||||
for (var q = 0; q < this._maxLetters; q++) {
|
||||
var j = i + q * 6;
|
||||
a[j] = size;
|
||||
a[j + 1] = size;
|
||||
a[j + 2] = size;
|
||||
a[j + 3] = size;
|
||||
a[j + 4] = size;
|
||||
a[j + 5] = size;
|
||||
}
|
||||
|
||||
this._changedBuffers[SIZE_BUFFER] = true;
|
||||
}
|
||||
|
||||
setOffsetArr(index, offset) {
|
||||
|
||||
var i = index * 18 * this._maxLetters;
|
||||
var a = this._offsetArr, x = offset.x, y = offset.y, z = offset.z;
|
||||
|
||||
for (var q = 0; q < this._maxLetters; q++) {
|
||||
var j = i + q * 18;
|
||||
a[j] = x;
|
||||
a[j + 1] = y;
|
||||
a[j + 2] = z;
|
||||
|
||||
a[j + 3] = x;
|
||||
a[j + 4] = y;
|
||||
a[j + 5] = z;
|
||||
|
||||
a[j + 6] = x;
|
||||
a[j + 7] = y;
|
||||
a[j + 8] = z;
|
||||
|
||||
a[j + 9] = x;
|
||||
a[j + 10] = y;
|
||||
a[j + 11] = z;
|
||||
|
||||
a[j + 12] = x;
|
||||
a[j + 13] = y;
|
||||
a[j + 14] = z;
|
||||
|
||||
a[j + 15] = x;
|
||||
a[j + 16] = y;
|
||||
a[j + 17] = z;
|
||||
}
|
||||
|
||||
this._changedBuffers[OFFSET_BUFFER] = true;
|
||||
}
|
||||
|
||||
setRgbaArr(index, rgba) {
|
||||
var i = index * 24 * this._maxLetters;
|
||||
var a = this._rgbaArr, x = rgba.x, y = rgba.y, z = rgba.z, w = rgba.w;
|
||||
|
||||
for (var q = 0; q < this._maxLetters; q++) {
|
||||
var j = i + q * 24;
|
||||
|
||||
a[j] = x;
|
||||
a[j + 1] = y;
|
||||
a[j + 2] = z;
|
||||
a[j + 3] = w;
|
||||
|
||||
a[j + 4] = x;
|
||||
a[j + 5] = y;
|
||||
a[j + 6] = z;
|
||||
a[j + 7] = w;
|
||||
|
||||
a[j + 8] = x;
|
||||
a[j + 9] = y;
|
||||
a[j + 10] = z;
|
||||
a[j + 11] = w;
|
||||
|
||||
a[j + 12] = x;
|
||||
a[j + 13] = y;
|
||||
a[j + 14] = z;
|
||||
a[j + 15] = w;
|
||||
|
||||
a[j + 16] = x;
|
||||
a[j + 17] = y;
|
||||
a[j + 18] = z;
|
||||
a[j + 19] = w;
|
||||
|
||||
a[j + 20] = x;
|
||||
a[j + 21] = y;
|
||||
a[j + 22] = z;
|
||||
a[j + 23] = w;
|
||||
}
|
||||
|
||||
this._changedBuffers[RGBA_BUFFER] = true;
|
||||
}
|
||||
|
||||
setOutlineColorArr(index, rgba) {
|
||||
var i = index * 24 * this._maxLetters;
|
||||
var a = this._outlineColorArr, x = rgba.x, y = rgba.y, z = rgba.z, w = rgba.w;
|
||||
|
||||
for (var q = 0; q < this._maxLetters; q++) {
|
||||
var j = i + q * 24;
|
||||
|
||||
a[j] = x;
|
||||
a[j + 1] = y;
|
||||
a[j + 2] = z;
|
||||
a[j + 3] = w;
|
||||
|
||||
a[j + 4] = x;
|
||||
a[j + 5] = y;
|
||||
a[j + 6] = z;
|
||||
a[j + 7] = w;
|
||||
|
||||
a[j + 8] = x;
|
||||
a[j + 9] = y;
|
||||
a[j + 10] = z;
|
||||
a[j + 11] = w;
|
||||
|
||||
a[j + 12] = x;
|
||||
a[j + 13] = y;
|
||||
a[j + 14] = z;
|
||||
a[j + 15] = w;
|
||||
|
||||
a[j + 16] = x;
|
||||
a[j + 17] = y;
|
||||
a[j + 18] = z;
|
||||
a[j + 19] = w;
|
||||
|
||||
a[j + 20] = x;
|
||||
a[j + 21] = y;
|
||||
a[j + 22] = z;
|
||||
a[j + 23] = w;
|
||||
}
|
||||
|
||||
this._changedBuffers[OUTLINECOLOR_BUFFER] = true;
|
||||
}
|
||||
|
||||
setOutlineArr(index, outline) {
|
||||
var i = index * 12 * this._maxLetters;
|
||||
var a = this._outlineArr;
|
||||
|
||||
for (var q = 0; q < this._maxLetters; q++) {
|
||||
var j = i + q * 12;
|
||||
a[j] = outline;
|
||||
a[j + 2] = outline;
|
||||
a[j + 4] = outline;
|
||||
a[j + 6] = outline;
|
||||
a[j + 8] = outline;
|
||||
a[j + 10] = outline;
|
||||
}
|
||||
|
||||
this._changedBuffers[OUTLINE_BUFFER] = true;
|
||||
}
|
||||
|
||||
setRotationArr(index, rotation) {
|
||||
|
||||
var i = index * 6 * this._maxLetters;
|
||||
var a = this._rotationArr;
|
||||
|
||||
for (var q = 0; q < this._maxLetters; q++) {
|
||||
var j = i + q * 6;
|
||||
a[j] = rotation;
|
||||
a[j + 1] = rotation;
|
||||
a[j + 2] = rotation;
|
||||
a[j + 3] = rotation;
|
||||
a[j + 4] = rotation;
|
||||
a[j + 5] = rotation;
|
||||
}
|
||||
|
||||
this._changedBuffers[ROTATION_BUFFER] = true
|
||||
}
|
||||
|
||||
setVertexArr(index, vertexArr) {
|
||||
|
||||
var i = index * 12 * this._maxLetters;
|
||||
var a = this._vertexArr;
|
||||
|
||||
for (var q = 0; q < this._maxLetters; q++) {
|
||||
var j = i + q * 12;
|
||||
a[j] = vertexArr[0];
|
||||
a[j + 1] = vertexArr[1];
|
||||
a[j + 2] = vertexArr[2];
|
||||
|
||||
a[j + 3] = vertexArr[3];
|
||||
a[j + 4] = vertexArr[4];
|
||||
a[j + 5] = vertexArr[5];
|
||||
|
||||
a[j + 6] = vertexArr[6];
|
||||
a[j + 7] = vertexArr[7];
|
||||
a[j + 8] = vertexArr[8];
|
||||
|
||||
a[j + 9] = vertexArr[9];
|
||||
a[j + 10] = vertexArr[10];
|
||||
a[j + 11] = vertexArr[11];
|
||||
}
|
||||
|
||||
this._changedBuffers[VERTEX_BUFFER] = true;
|
||||
}
|
||||
|
||||
setAlignedAxisArr(index, alignedAxis) {
|
||||
var i = index * 18 * this._maxLetters;
|
||||
var a = this._alignedAxisArr, x = alignedAxis.x, y = alignedAxis.y, z = alignedAxis.z;
|
||||
|
||||
for (var q = 0; q < this._maxLetters; q++) {
|
||||
var j = i + q * 18;
|
||||
a[j] = x;
|
||||
a[j + 1] = y;
|
||||
a[j + 2] = z;
|
||||
|
||||
a[j + 3] = x;
|
||||
a[j + 4] = y;
|
||||
a[j + 5] = z;
|
||||
|
||||
a[j + 6] = x;
|
||||
a[j + 7] = y;
|
||||
a[j + 8] = z;
|
||||
|
||||
a[j + 9] = x;
|
||||
a[j + 10] = y;
|
||||
a[j + 11] = z;
|
||||
|
||||
a[j + 12] = x;
|
||||
a[j + 13] = y;
|
||||
a[j + 14] = z;
|
||||
|
||||
a[j + 15] = x;
|
||||
a[j + 16] = y;
|
||||
a[j + 17] = z;
|
||||
}
|
||||
|
||||
this._changedBuffers[ALIGNEDAXIS_BUFFER] = true;
|
||||
}
|
||||
|
||||
setFontIndexArr(index, fontIndex) {
|
||||
|
||||
var i = index * 6 * this._maxLetters;
|
||||
var a = this._fontIndexArr;
|
||||
|
||||
for (var q = 0; q < this._maxLetters; q++) {
|
||||
var j = i + q * 6;
|
||||
a[j] = fontIndex;
|
||||
a[j + 1] = fontIndex;
|
||||
a[j + 2] = fontIndex;
|
||||
a[j + 3] = fontIndex;
|
||||
a[j + 4] = fontIndex;
|
||||
a[j + 5] = fontIndex;
|
||||
}
|
||||
|
||||
this._changedBuffers[FONTINDEX_BUFFER] = true;
|
||||
}
|
||||
|
||||
createSizeBuffer() {
|
||||
var h = this._renderer.handler;
|
||||
h.gl.deleteBuffer(this._sizeBuffer);
|
||||
this._sizeBuffer = h.createArrayBuffer(new Float32Array(this._sizeArr), 1, this._sizeArr.length);
|
||||
}
|
||||
|
||||
createFontIndexBuffer() {
|
||||
var h = this._renderer.handler;
|
||||
h.gl.deleteBuffer(this._fontIndexBuffer);
|
||||
this._fontIndexBuffer = h.createArrayBuffer(new Float32Array(this._fontIndexArr), 1, this._fontIndexArr.length);
|
||||
}
|
||||
|
||||
createTexCoordBuffer() {
|
||||
var h = this._renderer.handler;
|
||||
h.gl.deleteBuffer(this._texCoordBuffer);
|
||||
this._texCoordBuffer = h.createArrayBuffer(new Float32Array(this._texCoordArr), 4, this._texCoordArr.length / 4);
|
||||
}
|
||||
|
||||
createOutlineBuffer() {
|
||||
var h = this._renderer.handler;
|
||||
|
||||
h.gl.deleteBuffer(this._outlineBuffer);
|
||||
this._outlineBuffer = h.createArrayBuffer(new Float32Array(this._outlineArr), 2, this._outlineArr.length / 2);
|
||||
|
||||
h.gl.deleteBuffer(this._noOutlineBuffer);
|
||||
this._noOutlineBuffer = h.createArrayBuffer(new Float32Array(this._noOutlineArr), 2, this._noOutlineArr.length / 2);
|
||||
}
|
||||
|
||||
createOutlineColorBuffer() {
|
||||
var h = this._renderer.handler;
|
||||
h.gl.deleteBuffer(this._outlineColorBuffer);
|
||||
this._outlineColorBuffer = h.createArrayBuffer(new Float32Array(this._outlineColorArr), 4, this._outlineColorArr.length / 4);
|
||||
}
|
||||
|
||||
setMaxLetters(c) {
|
||||
this._maxLetters = c;
|
||||
//...
|
||||
}
|
||||
|
||||
refreshTexCoordsArr() {
|
||||
//it is empty
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
export { LabelHandler };
|
||||
461
src/og/entity/PointCloud.js
Normal file
461
src/og/entity/PointCloud.js
Normal file
@ -0,0 +1,461 @@
|
||||
/**
|
||||
* @module og/entity/PointCloud
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { Vec3 } from '../math/Vec3.js';
|
||||
import { Vec4 } from '../math/Vec4.js';
|
||||
|
||||
const COORDINATES_BUFFER = 0;
|
||||
const COLOR_BUFFER = 1;
|
||||
const PICKING_COLOR_BUFFER = 2;
|
||||
|
||||
/**
|
||||
* PointCloud object.
|
||||
* @class
|
||||
* @param {*} [options] - Point cloud options:
|
||||
* @param {Array.<Array.<number,number,number,number,number,number,number,*>>} [options.points] - Points cartesian coordinates array,
|
||||
* where first three is cartesian coordinates, next fourth is a RGBA color, and last is an point properties.
|
||||
* @param {number} [options.pointSize] - Point screen size in pixels.
|
||||
* @param {number} [options.pickingDistance] - Point border picking size in screen pixels.
|
||||
* @parar {boolean} [options.visibility] - Point cloud visibility.
|
||||
* @example <caption>Creates point cloud with two ten pixel size points</caption>
|
||||
* new og.Entity({
|
||||
* pointCloud: {
|
||||
* pointSize: 10,
|
||||
* points: [
|
||||
* [0, 0, 0, 255, 255, 255, 255, { 'name': 'White point' }],
|
||||
* [100, 100, 0, 255, 0, 0, 255, { 'name': 'Red point' }]
|
||||
* ]
|
||||
* }
|
||||
* });
|
||||
*/
|
||||
class PointCloud {
|
||||
constructor(options) {
|
||||
|
||||
options = options || {};
|
||||
|
||||
/**
|
||||
* Object unic identifier.
|
||||
* @public
|
||||
* @readonly
|
||||
* @type {number}
|
||||
*/
|
||||
this.id = PointCloud._staticCounter++;
|
||||
|
||||
/**
|
||||
* Cloud visibility.
|
||||
* @public
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.visibility = (options.visibility != undefined ? options.visibility : true);
|
||||
|
||||
/**
|
||||
* Point screen size in pixels.
|
||||
* @public
|
||||
* @type {number}
|
||||
*/
|
||||
this.pointSize = options.pointSize || 3;
|
||||
|
||||
/**
|
||||
* Point picking border size in pixels.
|
||||
* @public
|
||||
* @type {number}
|
||||
*/
|
||||
this.pickingDistance = options.pickingDistance || 0;
|
||||
|
||||
/**
|
||||
* Parent collection render node.
|
||||
* @private
|
||||
* @type {og.scene.RenderNode}
|
||||
*/
|
||||
this._renderNode = null;
|
||||
|
||||
/**
|
||||
* Entity instance that holds this point cloud.
|
||||
* @private
|
||||
* @type {og.Entity}
|
||||
*/
|
||||
this._entity = null;
|
||||
|
||||
/**
|
||||
* Points properties.
|
||||
* @private
|
||||
* @type {Array.<*>}
|
||||
*/
|
||||
this._points = [];
|
||||
|
||||
/**
|
||||
* Coordinates array.
|
||||
* @private
|
||||
* @type {Array.<number>}
|
||||
*/
|
||||
this._coordinatesData = [];
|
||||
|
||||
/**
|
||||
* Color array.
|
||||
* @private
|
||||
* @type {Array.<number>}
|
||||
*/
|
||||
this._colorData = [];
|
||||
|
||||
/**
|
||||
* Picking color array.
|
||||
* @private
|
||||
* @type {Array.<number>}
|
||||
*/
|
||||
this._pickingColorData = [];
|
||||
|
||||
this._coordinatesBuffer = null;
|
||||
this._colorBuffer = null;
|
||||
this._pickingColorBuffer = null;
|
||||
|
||||
/**
|
||||
* Handler that stores and renders this object.
|
||||
* @private
|
||||
* @type {og.PointCloudHandler}
|
||||
*/
|
||||
this._handler = null;
|
||||
this._handlerIndex = -1;
|
||||
|
||||
this._buffersUpdateCallbacks = [];
|
||||
this._buffersUpdateCallbacks[COORDINATES_BUFFER] = this._createCoordinatesBuffer;
|
||||
this._buffersUpdateCallbacks[COLOR_BUFFER] = this._createColorBuffer;
|
||||
this._buffersUpdateCallbacks[PICKING_COLOR_BUFFER] = this._createPickingColorBuffer;
|
||||
|
||||
this._changedBuffers = new Array(this._buffersUpdateCallbacks.length);
|
||||
|
||||
this.setPoints(options.points);
|
||||
}
|
||||
|
||||
static get _staticCounter() {
|
||||
if (!this._counter && this._counter !== 0) {
|
||||
this._counter = 0;
|
||||
}
|
||||
return this._counter;
|
||||
}
|
||||
|
||||
static set _staticCounter(n) {
|
||||
this._counter = n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears point cloud data
|
||||
* @public
|
||||
*/
|
||||
clear() {
|
||||
|
||||
this._points.length = 0;
|
||||
this._points = [];
|
||||
|
||||
this._coordinatesData.length = 0;
|
||||
this._coordinatesData = [];
|
||||
|
||||
this._colorData.length = 0;
|
||||
this._colorData = [];
|
||||
|
||||
this._pickingColorData.length = 0;
|
||||
this._pickingColorData = [];
|
||||
|
||||
this._deleteBuffers();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set point cloud opacity.
|
||||
* @public
|
||||
* @param {number} opacity - Cloud opacity.
|
||||
*/
|
||||
setOpacity(opacity) {
|
||||
this.opacity = opacity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets cloud visibility.
|
||||
* @public
|
||||
* @param {number} visibility - Visibility flag.
|
||||
*/
|
||||
setVisibility(visibility) {
|
||||
this.visibility = visibility;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {boolean} Point cloud visibily.
|
||||
*/
|
||||
getVisibility() {
|
||||
return this.visibility;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign rendering scene node.
|
||||
* @public
|
||||
* @param {og.scene.RenderNode} renderNode - Assigned render node.
|
||||
*/
|
||||
setRenderNode(renderNode) {
|
||||
this._renderNode = renderNode;
|
||||
this._setPickingColors();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes from entity.
|
||||
* @public
|
||||
*/
|
||||
remove() {
|
||||
this._entity = null;
|
||||
this._handler && this._handler.remove(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds points to render.
|
||||
* @public
|
||||
* @param {Array.<Array<number,number,number,number,number,number,number,*>>} points - Point cloud array.
|
||||
* @example
|
||||
* var points = [[0, 0, 0, 255, 255, 255, 255, { 'name': 'White point' }], [100, 100, 0, 255, 0, 0, 255, { 'name': 'Red point' }]];
|
||||
*/
|
||||
setPoints(points) {
|
||||
this.clear();
|
||||
for (var i = 0; i < points.length; i++) {
|
||||
var pi = points[i];
|
||||
var pos = new Vec3(pi[0], pi[1], pi[2]),
|
||||
col = new Vec4(pi[3], pi[4], pi[5], (pi[6] == undefined ? 255.0 : pi[6]));
|
||||
this._coordinatesData.push(pos.x, pos.y, pos.z);
|
||||
this._colorData.push(col.x / 255.0, col.y / 255.0, col.z / 255.0, col.w / 255.0);
|
||||
var p = {
|
||||
'_pickingColor': new Vec3(),
|
||||
'_entityCollection': this._entity && this._entity._entityCollection,
|
||||
'index': i,
|
||||
'position': pos,
|
||||
'color': col,
|
||||
'pointCloud': this,
|
||||
'properties': pi[7] || {}
|
||||
};
|
||||
this._points.push(p);
|
||||
|
||||
if (this._renderNode) {
|
||||
this._renderNode.renderer.assignPickingColor(p);
|
||||
this._pickingColorData.push(p._pickingColor.x / 255.0, p._pickingColor.y / 255.0, p._pickingColor.z / 255.0, 1.0);
|
||||
}
|
||||
}
|
||||
|
||||
this._changedBuffers[COORDINATES_BUFFER] = true;
|
||||
this._changedBuffers[COLOR_BUFFER] = true;
|
||||
this._changedBuffers[PICKING_COLOR_BUFFER] = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @todo
|
||||
*/
|
||||
setPointPosition(index, x, y, z) {
|
||||
|
||||
//...
|
||||
|
||||
this._changedBuffers[COORDINATES_BUFFER] = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @todo
|
||||
*/
|
||||
setPointColor(index, r, g, b, a) {
|
||||
|
||||
//...
|
||||
|
||||
this._changedBuffers[COLOR_BUFFER] = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @todo
|
||||
*/
|
||||
addPoints(points) {
|
||||
|
||||
//...
|
||||
|
||||
this._changedBuffers[COORDINATES_BUFFER] = true;
|
||||
this._changedBuffers[COLOR_BUFFER] = true;
|
||||
this._changedBuffers[PICKING_COLOR_BUFFER] = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @todo
|
||||
*/
|
||||
addPoint(index, point) {
|
||||
|
||||
//...
|
||||
|
||||
this._changedBuffers[COORDINATES_BUFFER] = true;
|
||||
this._changedBuffers[COLOR_BUFFER] = true;
|
||||
this._changedBuffers[PICKING_COLOR_BUFFER] = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns specific point by index.
|
||||
* @public
|
||||
* @param {number} index - Point index.
|
||||
* @return {*} Specific point
|
||||
*/
|
||||
getPoint(index) {
|
||||
return this._points[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* @todo
|
||||
*/
|
||||
removePoint(index) {
|
||||
|
||||
//...
|
||||
|
||||
this._changedBuffers[COORDINATES_BUFFER] = true;
|
||||
this._changedBuffers[COLOR_BUFFER] = true;
|
||||
this._changedBuffers[PICKING_COLOR_BUFFER] = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @todo
|
||||
*/
|
||||
insertPoint(index, point) {
|
||||
|
||||
//...
|
||||
|
||||
this._changedBuffers[COORDINATES_BUFFER] = true;
|
||||
this._changedBuffers[COLOR_BUFFER] = true;
|
||||
this._changedBuffers[PICKING_COLOR_BUFFER] = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Each point iterator.
|
||||
* @public
|
||||
* @param {callback} callback
|
||||
*/
|
||||
each(callback) {
|
||||
var i = this._points.length;
|
||||
while (i--) {
|
||||
callback && callback(this._points[i]);
|
||||
}
|
||||
}
|
||||
|
||||
draw() {
|
||||
if (this.visibility && this._coordinatesData.length) {
|
||||
|
||||
this._update();
|
||||
|
||||
var rn = this._renderNode;
|
||||
var r = rn.renderer;
|
||||
var sh = r.handler.shaderPrograms.pointCloud;
|
||||
var p = sh._program;
|
||||
var gl = r.handler.gl,
|
||||
sha = p.attributes,
|
||||
shu = p.uniforms;
|
||||
|
||||
sh.activate();
|
||||
|
||||
gl.uniformMatrix4fv(shu.projectionViewMatrix._pName, false, r.activeCamera._projectionViewMatrix._m);
|
||||
|
||||
gl.uniform1f(shu.opacity._pName, this._handler._entityCollection._animatedOpacity);
|
||||
|
||||
gl.uniform1f(shu.pointSize._pName, this.pointSize);
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this._coordinatesBuffer);
|
||||
gl.vertexAttribPointer(sha.coordinates._pName, this._coordinatesBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this._colorBuffer);
|
||||
gl.vertexAttribPointer(sha.colors._pName, this._colorBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
|
||||
gl.drawArrays(gl.POINTS, 0, this._coordinatesBuffer.numItems);
|
||||
}
|
||||
}
|
||||
|
||||
drawPicking() {
|
||||
if (this.visibility && this._coordinatesData.length) {
|
||||
var rn = this._renderNode;
|
||||
var r = rn.renderer;
|
||||
var sh = r.handler.shaderPrograms.pointCloud;
|
||||
var p = sh._program;
|
||||
var gl = r.handler.gl,
|
||||
sha = p.attributes,
|
||||
shu = p.uniforms;
|
||||
|
||||
sh.activate();
|
||||
|
||||
gl.uniformMatrix4fv(shu.projectionViewMatrix._pName, false, r.activeCamera._projectionViewMatrix._m);
|
||||
|
||||
gl.uniform1f(shu.opacity._pName, this._handler._entityCollection._animatedOpacity);
|
||||
|
||||
gl.uniform1f(shu.pointSize._pName, this.pointSize + this.pickingDistance);
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this._coordinatesBuffer);
|
||||
gl.vertexAttribPointer(sha.coordinates._pName, this._coordinatesBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this._pickingColorBuffer);
|
||||
gl.vertexAttribPointer(sha.colors._pName, this._pickingColorBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
|
||||
gl.drawArrays(gl.POINTS, 0, this._coordinatesBuffer.numItems);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update gl buffers.
|
||||
* @private
|
||||
*/
|
||||
_update() {
|
||||
if (this._renderNode) {
|
||||
var i = this._changedBuffers.length;
|
||||
while (i--) {
|
||||
if (this._changedBuffers[i]) {
|
||||
this._buffersUpdateCallbacks[i].call(this);
|
||||
this._changedBuffers[i] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete buffers
|
||||
* @private
|
||||
*/
|
||||
_deleteBuffers() {
|
||||
if (this._renderNode) {
|
||||
var r = this._renderNode.renderer,
|
||||
gl = r.handler.gl;
|
||||
|
||||
gl.deleteBuffer(this._coordinatesBuffer);
|
||||
gl.deleteBuffer(this._colorBuffer);
|
||||
gl.deleteBuffer(this._pickingColorBuffer);
|
||||
}
|
||||
|
||||
this._coordinatesBuffer = null;
|
||||
this._colorBuffer = null;
|
||||
this._pickingColorBuffer = null;
|
||||
}
|
||||
|
||||
_createCoordinatesBuffer() {
|
||||
var h = this._renderNode.renderer.handler;
|
||||
h.gl.deleteBuffer(this._coordinatesBuffer);
|
||||
this._coordinatesBuffer = h.createArrayBuffer(new Float32Array(this._coordinatesData), 3, (this._coordinatesData.length) / 3);
|
||||
}
|
||||
|
||||
_createColorBuffer() {
|
||||
var h = this._renderNode.renderer.handler;
|
||||
h.gl.deleteBuffer(this._colorBuffer);
|
||||
this._colorBuffer = h.createArrayBuffer(new Float32Array(this._colorData), 4, (this._colorData.length) / 4);
|
||||
}
|
||||
|
||||
_createPickingColorBuffer() {
|
||||
var h = this._renderNode.renderer.handler;
|
||||
h.gl.deleteBuffer(this._pickingColorBuffer);
|
||||
this._pickingColorBuffer = h.createArrayBuffer(new Float32Array(this._pickingColorData), 4, (this._pickingColorData.length) / 4);
|
||||
}
|
||||
|
||||
_setPickingColors() {
|
||||
if (this._renderNode) {
|
||||
for (var i = 0; i < this._points.length; i++) {
|
||||
var p = this._points[i];
|
||||
p._entity = this._entity;
|
||||
p._entityCollection = this._entity._entityCollection;
|
||||
this._renderNode.renderer.assignPickingColor(p);
|
||||
this._pickingColorData.push(p._pickingColor.x / 255.0, p._pickingColor.y / 255.0, p._pickingColor.z / 255.0, 1.0);
|
||||
}
|
||||
this._changedBuffers[PICKING_COLOR_BUFFER] = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export { PointCloud };
|
||||
126
src/og/entity/PointCloudHandler.js
Normal file
126
src/og/entity/PointCloudHandler.js
Normal file
@ -0,0 +1,126 @@
|
||||
/**
|
||||
* @module og/entity/PointCloudHandler
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as shaders from '../shaders/pointCloud.js';
|
||||
|
||||
class PointCloudHandler {
|
||||
constructor(entityCollection) {
|
||||
|
||||
/**
|
||||
* Picking rendering option.
|
||||
* @public
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.pickingEnabled = true;
|
||||
|
||||
/**
|
||||
* Parent collection
|
||||
* @private
|
||||
* @type {og.EntityCollection}
|
||||
*/
|
||||
this._entityCollection = entityCollection;
|
||||
|
||||
/**
|
||||
* Renderer
|
||||
* @private
|
||||
* @type {og.Renderer}
|
||||
*/
|
||||
this._renderer = null;
|
||||
|
||||
/**
|
||||
* Point cloud array
|
||||
* @private
|
||||
* @type {Array.<og.PointCloud>}
|
||||
*/
|
||||
this._pointClouds = [];
|
||||
|
||||
this.__staticId = PointCloudHandler._staticCounter++;
|
||||
}
|
||||
|
||||
static get _staticCounter() {
|
||||
if (!this._counter && this._counter !== 0) {
|
||||
this._counter = 0;
|
||||
}
|
||||
return this._counter;
|
||||
}
|
||||
|
||||
static set _staticCounter(n) {
|
||||
this._counter = n;
|
||||
}
|
||||
|
||||
_initShaderProgram() {
|
||||
if (this._renderer.handler) {
|
||||
if (!this._renderer.handler.shaderPrograms.pointCloud) {
|
||||
this._renderer.handler.addShaderProgram(shaders.pointCloud());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setRenderNode(renderNode) {
|
||||
this._renderer = renderNode.renderer;
|
||||
this._initShaderProgram()
|
||||
for (var i = 0; i < this._pointClouds.length; i++) {
|
||||
this._pointClouds[i].setRenderNode(renderNode);
|
||||
}
|
||||
}
|
||||
|
||||
add(pointCloud) {
|
||||
if (pointCloud._handlerIndex == -1) {
|
||||
pointCloud._handler = this;
|
||||
pointCloud._handlerIndex = this._pointClouds.length;
|
||||
this._pointClouds.push(pointCloud);
|
||||
this._entityCollection && this._entityCollection.renderNode &&
|
||||
pointCloud.setRenderNode(this._entityCollection.renderNode);
|
||||
}
|
||||
}
|
||||
|
||||
remove(pointCloud) {
|
||||
var index = pointCloud._handlerIndex;
|
||||
if (index !== -1) {
|
||||
pointCloud._deleteBuffers();
|
||||
pointCloud._handlerIndex = -1;
|
||||
pointCloud._handler = null;
|
||||
this._pointClouds.splice(index, 1);
|
||||
this.reindexPointCloudArray(index);
|
||||
}
|
||||
}
|
||||
|
||||
reindexPointCloudArray(startIndex) {
|
||||
var pc = this._pointClouds;
|
||||
for (var i = startIndex; i < pc.length; i++) {
|
||||
pc[i]._handlerIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
draw() {
|
||||
var i = this._pointClouds.length;
|
||||
while (i--) {
|
||||
this._pointClouds[i].draw();
|
||||
}
|
||||
}
|
||||
|
||||
drawPicking() {
|
||||
if (this.pickingEnabled) {
|
||||
var i = this._pointClouds.length;
|
||||
while (i--) {
|
||||
this._pointClouds[i].drawPicking();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
clear() {
|
||||
var i = this._pointClouds.length;
|
||||
while (i--) {
|
||||
this._pointClouds[i]._deleteBuffers();
|
||||
this._pointClouds[i]._handler = null;
|
||||
this._pointClouds[i]._handlerIndex = -1;
|
||||
}
|
||||
this._pointClouds.length = 0;
|
||||
this._pointClouds = [];
|
||||
}
|
||||
};
|
||||
|
||||
export { PointCloudHandler };
|
||||
1132
src/og/entity/Polyline.js
Normal file
1132
src/og/entity/Polyline.js
Normal file
File diff suppressed because it is too large
Load Diff
95
src/og/entity/PolylineHandler.js
Normal file
95
src/og/entity/PolylineHandler.js
Normal file
@ -0,0 +1,95 @@
|
||||
/**
|
||||
* @module og/entity/PolylineHandler
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as shaders from '../shaders/polyline.js';
|
||||
|
||||
class PolylineHandler {
|
||||
constructor(entityCollection) {
|
||||
|
||||
this._entityCollection = entityCollection;
|
||||
|
||||
this._renderer = null;
|
||||
|
||||
this._polylines = [];
|
||||
|
||||
this.__staticId = PolylineHandler._staticCounter++;
|
||||
}
|
||||
|
||||
static get _staticCounter() {
|
||||
if (!this._counter && this._counter !== 0) {
|
||||
this._counter = 0;
|
||||
}
|
||||
return this._counter;
|
||||
}
|
||||
|
||||
static set _staticCounter(n) {
|
||||
this._counter = n;
|
||||
}
|
||||
|
||||
_initShaderProgram() {
|
||||
if (this._renderer.handler) {
|
||||
if (!this._renderer.handler.shaderPrograms.polyline) {
|
||||
this._renderer.handler.addShaderProgram(shaders.polyline(this._renderer.isMultiFramebufferCompatible()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setRenderNode(renderNode) {
|
||||
this._renderer = renderNode.renderer;
|
||||
this._initShaderProgram()
|
||||
for (var i = 0; i < this._polylines.length; i++) {
|
||||
this._polylines[i].setRenderNode(renderNode);
|
||||
}
|
||||
}
|
||||
|
||||
add(polyline) {
|
||||
if (polyline._handlerIndex == -1) {
|
||||
polyline._handler = this;
|
||||
polyline._handlerIndex = this._polylines.length;
|
||||
this._polylines.push(polyline);
|
||||
this._entityCollection && this._entityCollection.renderNode &&
|
||||
polyline.setRenderNode(this._entityCollection.renderNode);
|
||||
}
|
||||
}
|
||||
|
||||
remove(polyline) {
|
||||
var index = polyline._handlerIndex;
|
||||
if (index !== -1) {
|
||||
polyline._deleteBuffers();
|
||||
polyline._handlerIndex = -1;
|
||||
polyline._handler = null;
|
||||
this._polylines.splice(index, 1);
|
||||
this.reindexPolylineArray(index);
|
||||
}
|
||||
}
|
||||
|
||||
reindexPolylineArray(startIndex) {
|
||||
var ls = this._polylines;
|
||||
for (var i = startIndex; i < ls.length; i++) {
|
||||
ls[i]._handlerIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
draw() {
|
||||
var i = this._polylines.length;
|
||||
while (i--) {
|
||||
this._polylines[i].draw();
|
||||
}
|
||||
}
|
||||
|
||||
clear() {
|
||||
var i = this._polylines.length;
|
||||
while (i--) {
|
||||
this._polylines[i]._deleteBuffers();
|
||||
this._polylines[i]._handler = null;
|
||||
this._polylines[i]._handlerIndex = -1;
|
||||
}
|
||||
this._polylines.length = 0;
|
||||
this._polylines = [];
|
||||
}
|
||||
};
|
||||
|
||||
export { PolylineHandler };
|
||||
90
src/og/entity/ShapeHandler.js
Normal file
90
src/og/entity/ShapeHandler.js
Normal file
@ -0,0 +1,90 @@
|
||||
/**
|
||||
* @module og/entity/ShapeHandler
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as shaders from '../shaders/shape.js';
|
||||
|
||||
class ShapeHandler {
|
||||
constructor(entityCollection) {
|
||||
|
||||
/**
|
||||
* Picking rendering option.
|
||||
* @public
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.pickingEnabled = true;
|
||||
|
||||
this._entityCollection = entityCollection;
|
||||
|
||||
this._renderer = null;
|
||||
|
||||
this._shapes = [];
|
||||
|
||||
this.__staticId = ShapeHandler._staticCounter++;
|
||||
}
|
||||
|
||||
static get _staticCounter() {
|
||||
if (!this._counter && this._counter !== 0) {
|
||||
this._counter = 0;
|
||||
}
|
||||
return this._counter;
|
||||
}
|
||||
|
||||
static set _staticCounter(n) {
|
||||
this._counter = n;
|
||||
}
|
||||
|
||||
_initShaderProgram() {
|
||||
if (this._renderer.handler) {
|
||||
if (!this._renderer.handler.shaderPrograms.shape_nl) {
|
||||
this._renderer.handler.addShaderProgram(shaders.shape_nl());
|
||||
}
|
||||
if (!this._renderer.handler.shaderPrograms.shape_wl) {
|
||||
this._renderer.handler.addShaderProgram(shaders.shape_wl());
|
||||
}
|
||||
//if (!this._renderer.handler.shaderPrograms.shapePicking) {
|
||||
// this._renderer.handler.addShaderProgram(shaders.shapePicking());
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
setRenderNode(renderNode) {
|
||||
this._renderer = renderNode.renderer;
|
||||
this._initShaderProgram()
|
||||
for (var i = 0; i < this._shapes.length; i++) {
|
||||
this._shapes[i].setRenderNode(renderNode);
|
||||
}
|
||||
}
|
||||
|
||||
add(shape) {
|
||||
if (shape._handlerIndex == -1) {
|
||||
shape._handler = this;
|
||||
shape._handlerIndex = this._shapes.length;
|
||||
this._shapes.push(shape);
|
||||
this._entityCollection && this._entityCollection.renderNode && shape.setRenderNode(this._entityCollection.renderNode);
|
||||
}
|
||||
}
|
||||
|
||||
remove(shape) {
|
||||
//TODO
|
||||
}
|
||||
|
||||
draw() {
|
||||
var i = this._shapes.length;
|
||||
while (i--) {
|
||||
this._shapes[i].draw();
|
||||
}
|
||||
}
|
||||
|
||||
drawPicking() {
|
||||
//TODO
|
||||
}
|
||||
|
||||
clear() {
|
||||
//TODO
|
||||
}
|
||||
};
|
||||
|
||||
export { ShapeHandler };
|
||||
87
src/og/input/KeyboardHandler.js
Normal file
87
src/og/input/KeyboardHandler.js
Normal file
@ -0,0 +1,87 @@
|
||||
/**
|
||||
* @module og/input/KeyboardHandler
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const KeyboardHandler = function () {
|
||||
var _currentlyPressedKeys = {};
|
||||
var _pressedKeysCallbacks = {};
|
||||
var _charkeysCallbacks = {};
|
||||
var _that = this;
|
||||
var _anykeyCallback = null;
|
||||
var _event = null;
|
||||
|
||||
if (KeyboardHandler.prototype._instance) {
|
||||
return KeyboardHandler.prototype._instance;
|
||||
} else {
|
||||
KeyboardHandler.prototype._instance = this;
|
||||
|
||||
document.onkeydown = function (event) { _event = event; _that.handleKeyDown.call(_that) };
|
||||
document.onkeyup = function (event) { _event = event; _that.handleKeyUp.call(_that) };
|
||||
}
|
||||
|
||||
var _sortByPriority = function (a, b) {
|
||||
return a.priority < b.priority;
|
||||
};
|
||||
|
||||
this.addEvent = function (event, sender, callback, keyCode, priority) {
|
||||
if (priority === undefined) {
|
||||
priority = 1600;
|
||||
}
|
||||
switch (event) {
|
||||
case "keypress": {
|
||||
if (keyCode == null) {
|
||||
_anykeyCallback = { "callback": callback, "sender": sender || _that };
|
||||
} else {
|
||||
if (!_pressedKeysCallbacks[keyCode]) {
|
||||
_pressedKeysCallbacks[keyCode] = [];
|
||||
}
|
||||
_pressedKeysCallbacks[keyCode].push({ callback: callback, sender: sender, priority: priority });
|
||||
_pressedKeysCallbacks[keyCode].sort(_sortByPriority);
|
||||
}
|
||||
} break;
|
||||
case "charkeypress": {
|
||||
if (!_charkeysCallbacks[keyCode]) {
|
||||
_charkeysCallbacks[keyCode] = [];
|
||||
}
|
||||
_charkeysCallbacks[keyCode].push({ callback: callback, sender: sender, priority: priority });
|
||||
_charkeysCallbacks[keyCode].sort(_sortByPriority);
|
||||
} break;
|
||||
}
|
||||
};
|
||||
|
||||
this.isKeyPressed = function (keyCode) {
|
||||
return _currentlyPressedKeys[keyCode];
|
||||
};
|
||||
|
||||
this.handleKeyDown = function () {
|
||||
_anykeyCallback && _anykeyCallback.callback.call(_anykeyCallback.sender, _event);
|
||||
_currentlyPressedKeys[_event.keyCode] = true;
|
||||
for (var ch in _charkeysCallbacks) {
|
||||
if (String.fromCharCode(_event.keyCode) == String.fromCharCode(ch)) {
|
||||
var ccl = _charkeysCallbacks[ch];
|
||||
for (var i = 0; i < ccl.length; i++) {
|
||||
ccl[i].callback.call(ccl[i].sender, _event);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.handleKeyUp = function () {
|
||||
_currentlyPressedKeys[_event.keyCode] = false;
|
||||
};
|
||||
|
||||
this.handleEvents = function () {
|
||||
for (var pk in _pressedKeysCallbacks) {
|
||||
if (_currentlyPressedKeys[pk]) {
|
||||
var cpk = _pressedKeysCallbacks[pk];
|
||||
for (var i = 0; i < cpk.length; i++) {
|
||||
cpk[i].callback.call(cpk[i].sender, _event);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export { KeyboardHandler };
|
||||
58
src/og/input/MouseHandler.js
Normal file
58
src/og/input/MouseHandler.js
Normal file
@ -0,0 +1,58 @@
|
||||
/**
|
||||
* @module og/input/MouseHandler
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const MouseHandler = function (htmlObject) {
|
||||
this._htmlObject = htmlObject;
|
||||
};
|
||||
|
||||
MouseHandler.prototype.setEvent = function (event, sender, callback) {
|
||||
switch (event) {
|
||||
case "mousewheel": {
|
||||
this._htmlObject.addEventListener('mousewheel', function (evt) {
|
||||
var delta = evt.deltaY || evt.detail || evt.wheelDelta;
|
||||
if (evt.wheelDelta == undefined) {
|
||||
evt.wheelDelta = delta * (-120);
|
||||
}
|
||||
callback.call(sender, evt);
|
||||
evt.preventDefault();
|
||||
}, false);
|
||||
|
||||
this._htmlObject.addEventListener('wheel', function (evt) {
|
||||
var delta = evt.deltaY || evt.detail || evt.wheelDelta;
|
||||
if (evt.wheelDelta == undefined) {
|
||||
evt.wheelDelta = delta * (-120);
|
||||
}
|
||||
callback.call(sender, evt);
|
||||
evt.preventDefault();
|
||||
}, false);
|
||||
} break;
|
||||
case "mousedown": {
|
||||
this._htmlObject.addEventListener('mousedown', function (event) {
|
||||
callback.call(sender, event);
|
||||
});
|
||||
this._htmlObject.addEventListener('contextmenu', function (event) {
|
||||
event.preventDefault();
|
||||
return false;
|
||||
});
|
||||
} break;
|
||||
case "mouseup": {
|
||||
this._htmlObject.addEventListener('mouseup', function (event) {
|
||||
callback.call(sender, event);
|
||||
});
|
||||
} break;
|
||||
case "mousemove": {
|
||||
this._htmlObject.addEventListener('mousemove', function (event) {
|
||||
var rect = this.getBoundingClientRect();
|
||||
callback.call(sender, {
|
||||
'clientX': event.clientX - rect.left,
|
||||
'clientY': event.clientY - rect.top
|
||||
});
|
||||
});
|
||||
} break;
|
||||
}
|
||||
};
|
||||
|
||||
export { MouseHandler };
|
||||
57
src/og/input/TouchHandler.js
Normal file
57
src/og/input/TouchHandler.js
Normal file
@ -0,0 +1,57 @@
|
||||
/**
|
||||
* @module og/input/TouchHandler
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const TouchHandler = function (htmlObject) {
|
||||
this._htmlObject = htmlObject;
|
||||
};
|
||||
|
||||
TouchHandler.prototype.setEvent = function (event, sender, callback) {
|
||||
switch (event) {
|
||||
case "touchcancel": {
|
||||
this._htmlObject.addEventListener('touchcancel', function (event) {
|
||||
event.preventDefault();
|
||||
var rect = this.getBoundingClientRect();
|
||||
event.offsetLeft = rect.left;
|
||||
event.offsetTop = rect.top;
|
||||
callback.call(sender, event);
|
||||
event.preventDefault();
|
||||
});
|
||||
}
|
||||
break;
|
||||
case "touchstart": {
|
||||
this._htmlObject.addEventListener('touchstart', function (event) {
|
||||
event.preventDefault();
|
||||
var rect = this.getBoundingClientRect();
|
||||
event.offsetLeft = rect.left;
|
||||
event.offsetTop = rect.top;
|
||||
callback.call(sender, event);
|
||||
event.preventDefault();
|
||||
});
|
||||
} break;
|
||||
case "touchend": {
|
||||
this._htmlObject.addEventListener('touchend', function (event) {
|
||||
event.preventDefault();
|
||||
var rect = this.getBoundingClientRect();
|
||||
event.offsetLeft = rect.left;
|
||||
event.offsetTop = rect.top;
|
||||
callback.call(sender, event);
|
||||
event.preventDefault();
|
||||
});
|
||||
} break;
|
||||
case "touchmove": {
|
||||
this._htmlObject.addEventListener('touchmove', function (event) {
|
||||
event.preventDefault();
|
||||
var rect = this.getBoundingClientRect();
|
||||
event.offsetLeft = rect.left;
|
||||
event.offsetTop = rect.top;
|
||||
callback.call(sender, event);
|
||||
event.preventDefault();
|
||||
});
|
||||
} break;
|
||||
}
|
||||
};
|
||||
|
||||
export { TouchHandler };
|
||||
275
src/og/layer/BaseGeoImage.js
Normal file
275
src/og/layer/BaseGeoImage.js
Normal file
@ -0,0 +1,275 @@
|
||||
/**
|
||||
* @module og/layer/BaseGeoImage
|
||||
*/
|
||||
|
||||
'use sctrict';
|
||||
|
||||
import * as mercator from '../mercator.js';
|
||||
import { Extent } from '../Extent.js';
|
||||
import { Layer } from './Layer.js';
|
||||
import { LonLat } from '../LonLat.js';
|
||||
|
||||
/**
|
||||
* BaseGeoImage layer represents square imagery layer that could be an static image, or animated video or webgl buffer object displayed on the globe.
|
||||
* @class
|
||||
* @extends {og.layer.Layer}
|
||||
*/
|
||||
class BaseGeoImage extends Layer {
|
||||
constructor(name, options) {
|
||||
super(name, options);
|
||||
|
||||
this._projType = 0;
|
||||
|
||||
this._frameWidth = 256;
|
||||
this._frameHeight = 256;
|
||||
|
||||
this._sourceReady = false;
|
||||
this._sourceTexture = null;
|
||||
this._materialTexture = null;
|
||||
|
||||
this._gridBuffer = null;
|
||||
this._extentWgs84Params = null;
|
||||
|
||||
this._refreshFrame = true;
|
||||
this._frameCreated = false;
|
||||
this._sourceCreated = false;
|
||||
|
||||
this._animate = false;
|
||||
this._ready = false;
|
||||
this._creationProceeding = false;
|
||||
this._isRendering = false;
|
||||
|
||||
this._extentWgs84 = new Extent();
|
||||
this._cornersWgs84 = null;
|
||||
|
||||
this.rendering = null;
|
||||
|
||||
options.corners && this.setCorners(options.corners);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets corners coordinates.
|
||||
* @public
|
||||
* @return {Array.<og.LonLat,og.LonLat,og.LonLat,og.LonLat>}
|
||||
*/
|
||||
getCornersLonLat() {
|
||||
var c = this._cornersWgs84;
|
||||
return [new LonLat(c[0].lon, c[0].lat), new LonLat(c[1].lon, c[1].lat),
|
||||
new LonLat(c[2].lon, c[2].lat), new LonLat(c[3].lon, c[3].lat)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets corners coordinates.
|
||||
* @public
|
||||
* @return {Array.<Array<number,number,number>>}
|
||||
*/
|
||||
getCorners() {
|
||||
var c = this._cornersWgs84;
|
||||
return [[c[0].lon, c[0].lat], [c[1].lon, c[1].lat], [c[2].lon, c[2].lat], [c[3].lon, c[3].lat]];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets geoImage geographical corners coordinates.
|
||||
* @public
|
||||
* @param {Array.<Array.<number,number,number>>} corners - GeoImage corners coordinates. Where first coordinate
|
||||
* coincedents to the left top image corner, secont to the right top image corner, third to the right bottom
|
||||
* and fourth - left bottom image corner.
|
||||
*/
|
||||
setCorners(corners) {
|
||||
this.setCornersLonLat(LonLat.join(corners));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets geoImage geographical corners coordinates.
|
||||
* @public
|
||||
* @param {Array.<og.LonLat, og.LonLat, og.LonLat, og.LonLat>} corners - GeoImage corners coordinates. Where first coordinate
|
||||
* coincedents to the left top image corner, secont to the right top image corner, third to the right bottom
|
||||
* and fourth - left bottom image corner.
|
||||
*/
|
||||
setCornersLonLat(corners) {
|
||||
this._refreshFrame = true;
|
||||
this._cornersWgs84 = [corners[0].clone(), corners[1].clone(), corners[2].clone(), corners[3].clone()] || [0, 0, 0, 0];
|
||||
|
||||
for (var i = 0; i < this._cornersWgs84.length; i++) {
|
||||
if (this._cornersWgs84[i].lat >= 89.9)
|
||||
this._cornersWgs84[i].lat = 89.9;
|
||||
if (this._cornersWgs84[i].lat <= -89.9)
|
||||
this._cornersWgs84[i].lat = -89.9;
|
||||
}
|
||||
this._extent.setByCoordinates(this._cornersWgs84);
|
||||
|
||||
var me = this._extent;
|
||||
if (me.southWest.lat > mercator.MAX_LAT ||
|
||||
me.northEast.lat < mercator.MIN_LAT) {
|
||||
this._projType = 0;
|
||||
this.rendering = this._renderingProjType0;
|
||||
} else {
|
||||
this._projType = 1;
|
||||
this.rendering = this._renderingProjType1;
|
||||
}
|
||||
|
||||
if (this._ready && !this._creationProceeding) {
|
||||
this._planet._geoImageCreator.add(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates geoImage frame.
|
||||
* @protected
|
||||
*/
|
||||
_createFrame() {
|
||||
this._extentWgs84 = this._extent.clone();
|
||||
|
||||
this._cornersMerc = [this._cornersWgs84[0].forwardMercatorEPS01(), this._cornersWgs84[1].forwardMercatorEPS01(),
|
||||
this._cornersWgs84[2].forwardMercatorEPS01(), this._cornersWgs84[3].forwardMercatorEPS01()];
|
||||
|
||||
this._extentMerc = new Extent(this._extentWgs84.southWest.forwardMercatorEPS01(), this._extentWgs84.northEast.forwardMercatorEPS01());
|
||||
|
||||
if (this._projType == 0) {
|
||||
this._extentWgs84Params = [this._extentWgs84.southWest.lon, this._extentWgs84.southWest.lat, 2.0 / this._extentWgs84.getWidth(), 2.0 / this._extentWgs84.getHeight()];
|
||||
} else {
|
||||
this._extentMercParams = [this._extentMerc.southWest.lon, this._extentMerc.southWest.lat, 2.0 / this._extentMerc.getWidth(), 2.0 / this._extentMerc.getHeight()];
|
||||
}
|
||||
|
||||
//creates material frame textures
|
||||
if (this._planet) {
|
||||
var p = this._planet,
|
||||
h = p.renderer.handler,
|
||||
gl = h.gl;
|
||||
|
||||
gl.deleteTexture(this._materialTexture);
|
||||
this._materialTexture = h.createEmptyTexture_l(this._frameWidth, this._frameHeight);
|
||||
|
||||
this._gridBuffer = this._planet._geoImageCreator.createGridBuffer(this._cornersWgs84, this._projType);
|
||||
|
||||
this._refreshFrame = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @virtual
|
||||
* @param {og.planetSegment.Material} material - GeoImage material.
|
||||
*/
|
||||
abortMaterialLoading(material) {
|
||||
this._creationProceeding = false;
|
||||
material.isLoading = false;
|
||||
material.isReady = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear layer material.
|
||||
* @virtual
|
||||
*/
|
||||
clear() {
|
||||
var p = this._planet;
|
||||
|
||||
if (p) {
|
||||
var gl = p.renderer.handler.gl;
|
||||
this._creationProceeding && p._geoImageCreator.remove(this);
|
||||
p._clearLayerMaterial(this);
|
||||
|
||||
gl.deleteBuffer(this._gridBuffer);
|
||||
|
||||
gl.deleteTexture(this._sourceTexture);
|
||||
!this._materialTexture.default && gl.deleteTexture(this._materialTexture);
|
||||
}
|
||||
|
||||
this._sourceTexture = null;
|
||||
this._materialTexture = null;
|
||||
|
||||
this._gridBuffer = null;
|
||||
|
||||
this._refreshFrame = true;
|
||||
this._sourceCreated = false;
|
||||
|
||||
this._ready = false;
|
||||
this._creationProceeding = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets layer visibility.
|
||||
* @public
|
||||
* @virtual
|
||||
* @param {boolean} visibility - GeoImage visibility.
|
||||
*/
|
||||
setVisibility(visibility) {
|
||||
if (visibility != this._visibility) {
|
||||
this._visibility = visibility;
|
||||
if (this._isBaseLayer && visibility) {
|
||||
this._planet.setBaseLayer(this);
|
||||
}
|
||||
this._planet.updateVisibleLayers();
|
||||
this.events.dispatch(this.events.visibilitychange, this);
|
||||
|
||||
//remove from creator
|
||||
if (visibility)
|
||||
this._sourceReady && this._planet._geoImageCreator.add(this);
|
||||
else
|
||||
this._sourceReady && this._planet._geoImageCreator.remove(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @virtual
|
||||
* @protected
|
||||
* @param {og.planetSegment.Material} material - GeoImage material.
|
||||
*/
|
||||
clearMaterial(material) {
|
||||
//just clear material pointer not geoimage
|
||||
material.image = null;
|
||||
material.texture = null;
|
||||
material.isLoading = false;
|
||||
material.isReady = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @virtual
|
||||
* @protected
|
||||
* @param {og.planetSegment.Material} material - GeoImage material.
|
||||
*/
|
||||
applyMaterial(material) {
|
||||
|
||||
var segment = material.segment;
|
||||
|
||||
if (this._ready) {
|
||||
material.applyTexture(this._materialTexture);
|
||||
} else {
|
||||
material.texture = this._planet.transparentTexture;
|
||||
!this._creationProceeding && this.loadMaterial(material);
|
||||
}
|
||||
|
||||
if (this._projType === 0) {
|
||||
var v0s = this._extentWgs84;
|
||||
var v0t = segment._extent;
|
||||
} else {
|
||||
var v0s = this._extentMerc;
|
||||
var v0t = segment.getExtentMerc();
|
||||
}
|
||||
|
||||
var sSize_x = v0s.northEast.lon - v0s.southWest.lon;
|
||||
var sSize_y = v0s.northEast.lat - v0s.southWest.lat;
|
||||
var dV0s_x = (v0t.southWest.lon - v0s.southWest.lon) / sSize_x;
|
||||
var dV0s_y = (v0s.northEast.lat - v0t.northEast.lat) / sSize_y;
|
||||
var dSize_x = (v0t.northEast.lon - v0t.southWest.lon) / sSize_x;
|
||||
var dSize_y = (v0t.northEast.lat - v0t.southWest.lat) / sSize_y;
|
||||
return [dV0s_x, dV0s_y, dSize_x, dSize_y];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets frame width size in pixels.
|
||||
* @public
|
||||
*/
|
||||
getFrameWidth() {
|
||||
return this._frameWidth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets frame height size in pixels.
|
||||
* @public
|
||||
*/
|
||||
getFrameHeight() {
|
||||
return this._frameHeight;
|
||||
}
|
||||
};
|
||||
|
||||
export { BaseGeoImage };
|
||||
285
src/og/layer/CanvasTiles.js
Normal file
285
src/og/layer/CanvasTiles.js
Normal file
@ -0,0 +1,285 @@
|
||||
/**
|
||||
* @module og/layer/CanvasTiles
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as quadTree from '../quadTree/quadTree.js';
|
||||
import { ImageCanvas } from '../ImageCanvas.js';
|
||||
import { Layer } from './Layer.js';
|
||||
|
||||
/**
|
||||
* Maximum tiles per frame.
|
||||
* @const
|
||||
* @type {number}
|
||||
*/
|
||||
const MAX_REQUESTS = 7;
|
||||
|
||||
const EVENT_NAMES = [
|
||||
/**
|
||||
* Triggered when current tile image has loaded before rendereing.
|
||||
* @event og.layer.CanvasTiles#load
|
||||
*/
|
||||
"load",
|
||||
|
||||
/**
|
||||
* Triggered when all tiles have loaded or loading has stopped.
|
||||
* @event og.layer.CanvasTiles#loadend
|
||||
*/
|
||||
"loadend"
|
||||
];
|
||||
|
||||
/**
|
||||
* Layer used to rendering each tile as a separate canvas object.
|
||||
* @class
|
||||
* @extends {og.layer.Layer}
|
||||
* //TODO: make asynchronous handler.
|
||||
* @param {String} [name="noname"] - Layer name.
|
||||
* @param {Object} options:
|
||||
* @param {number} [options.opacity=1.0] - Layer opacity.
|
||||
* @param {Array.<number,number,number>} [options.transparentColor=[-1,-1,-1]] - RGB color that defines transparent color.
|
||||
* @param {number} [options.minZoom=0] - Minimal visibility zoom level.
|
||||
* @param {number} [options.maxZoom=0] - Maximal visibility zoom level.
|
||||
* @param {string} [options.attribution] - Layer attribution that displayed in the attribution area on the screen.
|
||||
* @param {boolean} [options.isBaseLayer=false] - Base layer flag.
|
||||
* @param {boolean} [options.visibility=true] - Layer visibility.
|
||||
* @param {og.layer.CanvasTiles~drawTileCallback} [options.drawTile] - Draw tile callback.
|
||||
* @fires og.layer.CanvasTiles#load
|
||||
* @fires og.layer.CanvasTiles#loadend
|
||||
*/
|
||||
class CanvasTiles extends Layer {
|
||||
constructor(name, options) {
|
||||
options = options || {};
|
||||
|
||||
super(name, options);
|
||||
|
||||
this.events.registerNames(EVENT_NAMES);
|
||||
|
||||
/**
|
||||
* Current creating tiles couter.
|
||||
* @protected
|
||||
* @type {number}
|
||||
*/
|
||||
this._counter = 0;
|
||||
|
||||
/**
|
||||
* Tile pending queue that waiting for create.
|
||||
* @protected
|
||||
* @type {Array.<og.planetSegment.Material>}
|
||||
*/
|
||||
this._pendingsQueue = []; //new og.QueueArray();
|
||||
|
||||
/**
|
||||
* Draw tile callback.
|
||||
* @type {og.layer.CanvasTiles~drawTileCallback}
|
||||
* @public
|
||||
*/
|
||||
this.drawTile = options.drawTile || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Abort loading tiles.
|
||||
* @public
|
||||
*/
|
||||
abortLoading() {
|
||||
var q = this._pendingsQueue;
|
||||
for (var i = q._shiftIndex + 1; i < q._popIndex + 1; i++) {
|
||||
if (q._array[i]) {
|
||||
this.abortMaterialLoading(q._array[i]);
|
||||
}
|
||||
}
|
||||
this._pendingsQueue = [];
|
||||
//this._pendingsQueue.clear();
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets layer visibility.
|
||||
* @public
|
||||
* @param {boolean} visibility - Layer visibility.
|
||||
*/
|
||||
setVisibility(visibility) {
|
||||
if (visibility != this._visibility) {
|
||||
this._visibility = visibility;
|
||||
if (this._isBaseLayer && visibility) {
|
||||
this._planet.setBaseLayer(this);
|
||||
} else if (!visibility) {
|
||||
this.abortLoading();
|
||||
}
|
||||
this._planet.updateVisibleLayers();
|
||||
this.events.dispatch(this.events.visibilitychange, this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start to load tile material.
|
||||
* @public
|
||||
* @virtual
|
||||
* @param {og.planetSegment.Material} mateial
|
||||
*/
|
||||
loadMaterial(material) {
|
||||
|
||||
var seg = material.segment;
|
||||
|
||||
if (this._isBaseLayer) {
|
||||
material.texture = seg._isNorth ? seg.planet.solidTextureOne : seg.planet.solidTextureTwo;
|
||||
} else {
|
||||
material.texture = seg.planet.transparentTexture;
|
||||
}
|
||||
|
||||
if (this._planet.layerLock.isFree()) {
|
||||
material.isReady = false;
|
||||
material.isLoading = true;
|
||||
if (CanvasTiles.__requestsCounter >= MAX_REQUESTS && this._counter) {
|
||||
this._pendingsQueue.push(material);
|
||||
} else {
|
||||
this._exec(material);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads material image and apply it to the planet segment.
|
||||
* @protected
|
||||
* @param {og.planetSegment.Material} material - Loads material image.
|
||||
*/
|
||||
_exec(material) {
|
||||
CanvasTiles.__requestsCounter++;
|
||||
this._counter++;
|
||||
var that = this;
|
||||
if (this.drawTile) {
|
||||
|
||||
/**
|
||||
* Tile custom draw function.
|
||||
* @callback og.layer.CanvasTiles~drawTileCallback
|
||||
* @param {og.planetSegment.Material} material
|
||||
* @param {applyCanvasCallback} applyCanvasCallback
|
||||
*/
|
||||
setTimeout(function () {
|
||||
var e = that.events.load;
|
||||
if (e.handlers.length) {
|
||||
that.events.dispatch(e, material);
|
||||
}
|
||||
that.drawTile(material,
|
||||
/**
|
||||
* Apply canvas.
|
||||
* @callback applyCanvasCallback
|
||||
* @param {Object} canvas
|
||||
*/
|
||||
function (canvas) {
|
||||
that._counter--;
|
||||
og.layer.CanvasTiles.__requestsCounter--;
|
||||
if (material.isLoading) {
|
||||
material.applyImage(canvas);
|
||||
}
|
||||
that._dequeueRequest();
|
||||
});
|
||||
}, 50);
|
||||
} else {
|
||||
material.textureNotExists();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Abort exact material loading.
|
||||
* @public
|
||||
* @param {og.planetSegment.Material} material - Segment material.
|
||||
*/
|
||||
abortMaterialLoading(material) {
|
||||
if (material.isLoading && material.image) {
|
||||
material.image.src = "";
|
||||
this._counter--;
|
||||
og.layer.CanvasTiles.__requestsCounter--;
|
||||
this._dequeueRequest();
|
||||
}
|
||||
material.isLoading = false;
|
||||
material.isReady = false;
|
||||
}
|
||||
|
||||
_dequeueRequest() {
|
||||
if (this._pendingsQueue.length) {
|
||||
if (CanvasTiles.__requestsCounter < MAX_REQUESTS) {
|
||||
var pmat;
|
||||
if (pmat = this._whilePendings())
|
||||
this._exec.call(this, pmat);
|
||||
}
|
||||
} else if (this._counter === 0) {
|
||||
this.events.dispatch(this.events.loadend);
|
||||
}
|
||||
}
|
||||
|
||||
_whilePendings() {
|
||||
while (this._pendingsQueue.length) {
|
||||
var pmat = this._pendingsQueue.pop();
|
||||
if (pmat.segment.node) {
|
||||
if (pmat.segment.ready && pmat.segment.node.getState() === quadTree.RENDERING) {
|
||||
return pmat;
|
||||
}
|
||||
pmat.isLoading = false;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
applyMaterial(material) {
|
||||
if (material.isReady) {
|
||||
return [0, 0, 1, 1];
|
||||
} else {
|
||||
|
||||
!material.isLoading && this.loadMaterial(material);
|
||||
|
||||
var segment = material.segment;
|
||||
var pn = segment.node,
|
||||
notEmpty = false;
|
||||
|
||||
var mId = this._id;
|
||||
var psegm = material;
|
||||
while (pn.parentNode) {
|
||||
if (psegm && psegm.isReady) {
|
||||
notEmpty = true;
|
||||
break;
|
||||
}
|
||||
pn = pn.parentNode;
|
||||
psegm = pn.segment.materials[mId];
|
||||
}
|
||||
|
||||
if (notEmpty) {
|
||||
material.appliedNodeId = pn.nodeId;
|
||||
material.texture = psegm.texture;
|
||||
var dZ2 = 1.0 / (2 << (segment.tileZoom - pn.segment.tileZoom - 1));
|
||||
return [
|
||||
segment.tileX * dZ2 - pn.segment.tileX,
|
||||
segment.tileY * dZ2 - pn.segment.tileY,
|
||||
dZ2,
|
||||
dZ2
|
||||
];
|
||||
} else {
|
||||
material.texture = segment.planet.transparentTexture;
|
||||
return [0, 0, 1, 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
clearMaterial(material) {
|
||||
if (material.isReady) {
|
||||
material.isReady = false;
|
||||
|
||||
!material.texture.default &&
|
||||
material.segment.handler.gl.deleteTexture(material.texture);
|
||||
|
||||
material.texture = null;
|
||||
}
|
||||
|
||||
this.abortMaterialLoading(material);
|
||||
|
||||
material.isLoading = false;
|
||||
material.textureExists = false;
|
||||
|
||||
if (material.image) {
|
||||
material.image.src = '';
|
||||
material.image = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export { CanvasTiles };
|
||||
214
src/og/layer/GeoImage.js
Normal file
214
src/og/layer/GeoImage.js
Normal file
@ -0,0 +1,214 @@
|
||||
/**
|
||||
* @module og/layer/GeoImage
|
||||
*/
|
||||
|
||||
'use sctrict';
|
||||
|
||||
import { BaseGeoImage } from './BaseGeoImage.js';
|
||||
|
||||
/**
|
||||
* Used to load and display a single image over specific corner coordinates on the globe, implements og.layer.BaseGeoImage interface.
|
||||
* @class
|
||||
* @extends {og.layer.BaseGeoImage}
|
||||
*/
|
||||
class GeoImage extends BaseGeoImage {
|
||||
constructor(name, options) {
|
||||
super(name, options);
|
||||
|
||||
/**
|
||||
* Image object.
|
||||
* @private
|
||||
* @type {Image}
|
||||
*/
|
||||
this._image = options.image || null;
|
||||
|
||||
/**
|
||||
* Image source url path.
|
||||
* @private
|
||||
* @type {String}
|
||||
*/
|
||||
this._src = options.src || null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets image source url path.
|
||||
* @public
|
||||
* @param {String} srs - Image url path.
|
||||
*/
|
||||
setSrc(src) {
|
||||
this._planet && this._planet._geoImageCreator.remove(this);
|
||||
this._src = src;
|
||||
this._sourceReady = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets image object.
|
||||
* @public
|
||||
* @param {Image} image - Image object.
|
||||
*/
|
||||
setImage(image) {
|
||||
this._planet && this._planet._geoImageCreator.remove(this);
|
||||
this._image = image;
|
||||
this._src = image.src;
|
||||
this._sourceReady = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates source gl texture.
|
||||
* @virtual
|
||||
* @protected
|
||||
*/
|
||||
_createSourceTexture() {
|
||||
if (!this._sourceCreated) {
|
||||
this._sourceTexture = this._planet.renderer.handler.createTexture_n(this._image);
|
||||
this._sourceCreated = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {Image} img
|
||||
*/
|
||||
_onLoad(img) {
|
||||
this._frameWidth = img.width;
|
||||
this._frameHeight = img.height;
|
||||
this._sourceReady = true;
|
||||
this._planet._geoImageCreator.add(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads planet segment material. In this case - GeoImage source image.
|
||||
* @virtual
|
||||
* @public
|
||||
* @param {og.planetSegment.Material} material - GeoImage planet material.
|
||||
*/
|
||||
loadMaterial(material) {
|
||||
material.isLoading = true;
|
||||
this._creationProceeding = true;
|
||||
if (!this._sourceReady && this._src) {
|
||||
if (this._image) {
|
||||
if (this._image.complete) {
|
||||
this._onLoad(this._image);
|
||||
} else if (this._image.src) {
|
||||
var that = this;
|
||||
this._image.addEventListener('load', function (e) {
|
||||
that._onLoad(this);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
var that = this;
|
||||
this._image = new Image();
|
||||
this._image.addEventListener('load', function (e) {
|
||||
that._onLoad(this);
|
||||
});
|
||||
this._image.src = this._src;
|
||||
}
|
||||
} else {
|
||||
this._planet._geoImageCreator.add(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @virtual
|
||||
* @param {og.planetSegment.Material} material - GeoImage material.
|
||||
*/
|
||||
abortMaterialLoading(material) {
|
||||
this._image && (this._image.src = '');
|
||||
this._creationProceeding = false;
|
||||
material.isLoading = false;
|
||||
material.isReady = false;
|
||||
}
|
||||
|
||||
_renderingProjType1() {
|
||||
var p = this._planet,
|
||||
h = p.renderer.handler,
|
||||
gl = h.gl,
|
||||
creator = p._geoImageCreator;
|
||||
|
||||
this._refreshFrame && this._createFrame();
|
||||
this._createSourceTexture();
|
||||
|
||||
var f = creator._framebuffer;
|
||||
f.setSize(this._frameWidth, this._frameHeight);
|
||||
f.activate();
|
||||
|
||||
h.shaderPrograms.geoImageTransform.activate();
|
||||
var sh = h.shaderPrograms.geoImageTransform._program;
|
||||
var sha = sh.attributes,
|
||||
shu = sh.uniforms;
|
||||
|
||||
var tr = this.transparentColor[0],
|
||||
tg = this.transparentColor[1],
|
||||
tb = this.transparentColor[2];
|
||||
|
||||
gl.disable(gl.CULL_FACE);
|
||||
|
||||
f.bindOutputTexture(this._materialTexture);
|
||||
gl.clearColor(tr, tg, tb, 0.0);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, creator._texCoordsBuffer);
|
||||
gl.vertexAttribPointer(sha.texCoords._pName, creator._texCoordsBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this._gridBuffer);
|
||||
gl.vertexAttribPointer(sha.corners._pName, this._gridBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
gl.uniform4fv(shu.extentParams._pName, this._extentMercParams);
|
||||
gl.activeTexture(gl.TEXTURE0);
|
||||
gl.bindTexture(gl.TEXTURE_2D, this._sourceTexture);
|
||||
gl.uniform1i(shu.sourceTexture._pName, 0);
|
||||
sh.drawIndexBuffer(gl.TRIANGLE_STRIP, creator._indexBuffer);
|
||||
f.deactivate();
|
||||
|
||||
gl.enable(gl.CULL_FACE);
|
||||
|
||||
this._ready = true;
|
||||
|
||||
this._creationProceeding = false;
|
||||
}
|
||||
|
||||
_renderingProjType0() {
|
||||
var p = this._planet,
|
||||
h = p.renderer.handler,
|
||||
gl = h.gl,
|
||||
creator = p._geoImageCreator;
|
||||
|
||||
this._refreshFrame && this._createFrame();
|
||||
this._createSourceTexture();
|
||||
|
||||
var f = creator._framebuffer;
|
||||
f.setSize(this._frameWidth, this._frameHeight);
|
||||
f.activate();
|
||||
|
||||
h.shaderPrograms.geoImageTransform.activate();
|
||||
var sh = h.shaderPrograms.geoImageTransform._program;
|
||||
var sha = sh.attributes,
|
||||
shu = sh.uniforms;
|
||||
|
||||
var tr = this.transparentColor[0],
|
||||
tg = this.transparentColor[1],
|
||||
tb = this.transparentColor[2];
|
||||
|
||||
gl.disable(gl.CULL_FACE);
|
||||
|
||||
f.bindOutputTexture(this._materialTexture);
|
||||
gl.clearColor(tr, tg, tb, 0.0);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, creator._texCoordsBuffer);
|
||||
gl.vertexAttribPointer(sha.texCoords._pName, creator._texCoordsBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this._gridBuffer);
|
||||
gl.vertexAttribPointer(sha.corners._pName, this._gridBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
gl.uniform4fv(shu.extentParams._pName, this._extentWgs84Params);
|
||||
gl.activeTexture(gl.TEXTURE0);
|
||||
gl.bindTexture(gl.TEXTURE_2D, this._sourceTexture);
|
||||
gl.uniform1i(shu.sourceTexture._pName, 0);
|
||||
sh.drawIndexBuffer(gl.TRIANGLE_STRIP, creator._indexBuffer);
|
||||
f.deactivate();
|
||||
|
||||
gl.enable(gl.CULL_FACE);
|
||||
|
||||
this._ready = true;
|
||||
|
||||
this._creationProceeding = false;
|
||||
}
|
||||
};
|
||||
|
||||
export { GeoImage };
|
||||
143
src/og/layer/GeoTexture2d.js
Normal file
143
src/og/layer/GeoTexture2d.js
Normal file
@ -0,0 +1,143 @@
|
||||
/**
|
||||
* @module og/layer/GeoTexture2d
|
||||
*/
|
||||
|
||||
'use sctrict';
|
||||
|
||||
import * as math from '../math.js';
|
||||
import { BaseGeoImage } from './BaseGeoImage.js';
|
||||
|
||||
|
||||
class GeoTexture2d extends BaseGeoImage {
|
||||
constructor(name, options) {
|
||||
super(name, options);
|
||||
|
||||
this._sourceTexture = options.texture || null;
|
||||
|
||||
if (options.texture) {
|
||||
this._sourceReady = true;
|
||||
this._sourceCreated = true;
|
||||
}
|
||||
|
||||
this._frameWidth = options.frameWidth ? math.nextHighestPowerOfTwo(options.frameWidth) : 256;
|
||||
this._frameHeight = options.frameHeight ? math.nextHighestPowerOfTwo(options.frameHeight) : 256;
|
||||
|
||||
this._animate = true;
|
||||
}
|
||||
|
||||
|
||||
loadMaterial(material) {
|
||||
this._planet._geoImageCreator.add(this);
|
||||
};
|
||||
|
||||
bindTexture(texture) {
|
||||
this._sourceReady = true;
|
||||
this._sourceCreated = true;
|
||||
this._sourceTexture = texture;
|
||||
}
|
||||
|
||||
setSize(width, height) {
|
||||
this._frameWidth = width;
|
||||
this._frameHeight = height;
|
||||
this._frameCreated = false;
|
||||
}
|
||||
|
||||
abortMaterialLoading(material) {
|
||||
this._creationProceeding = false;
|
||||
material.isLoading = false;
|
||||
material.isReady = false;
|
||||
}
|
||||
|
||||
_renderingProjType1() {
|
||||
var p = this._planet,
|
||||
h = p.renderer.handler,
|
||||
gl = h.gl,
|
||||
creator = p._geoImageCreator;
|
||||
|
||||
this._refreshFrame && this._createFrame();
|
||||
|
||||
var f = creator._framebuffer;
|
||||
f.setSize(this._frameWidth, this._frameHeight);
|
||||
f.activate();
|
||||
|
||||
h.shaderPrograms.geoImageTransform.activate();
|
||||
var sh = h.shaderPrograms.geoImageTransform._program;
|
||||
var sha = sh.attributes,
|
||||
shu = sh.uniforms;
|
||||
|
||||
var tr = this.transparentColor[0],
|
||||
tg = this.transparentColor[1],
|
||||
tb = this.transparentColor[2];
|
||||
|
||||
gl.disable(gl.CULL_FACE);
|
||||
|
||||
f.bindOutputTexture(this._materialTexture);
|
||||
gl.clearColor(tr, tg, tb, 0.0);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, creator._texCoordsBuffer);
|
||||
gl.vertexAttribPointer(sha.texCoords._pName, creator._texCoordsBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this._gridBuffer);
|
||||
gl.vertexAttribPointer(sha.corners._pName, this._gridBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
gl.uniform4fv(shu.extentParams._pName, this._extentMercParams);
|
||||
gl.activeTexture(gl.TEXTURE0);
|
||||
gl.bindTexture(gl.TEXTURE_2D, this._sourceTexture);
|
||||
gl.uniform1i(shu.sourceTexture._pName, 0);
|
||||
sh.drawIndexBuffer(gl.TRIANGLE_STRIP, creator._indexBuffer);
|
||||
f.deactivate();
|
||||
|
||||
gl.enable(gl.CULL_FACE);
|
||||
|
||||
this._ready = true;
|
||||
|
||||
this._creationProceeding = false;
|
||||
}
|
||||
|
||||
_renderingProjType0() {
|
||||
var p = this._planet,
|
||||
h = p.renderer.handler,
|
||||
gl = h.gl,
|
||||
creator = p._geoImageCreator;
|
||||
|
||||
var width = this._frameWidth,
|
||||
height = this._frameHeight;
|
||||
|
||||
this._refreshFrame && this._createFrame();
|
||||
|
||||
var f = creator._framebuffer;
|
||||
f.setSize(width, height);
|
||||
f.activate();
|
||||
|
||||
h.shaderPrograms.geoImageTransform.activate();
|
||||
var sh = h.shaderPrograms.geoImageTransform._program;
|
||||
var sha = sh.attributes,
|
||||
shu = sh.uniforms;
|
||||
|
||||
var tr = this.transparentColor[0],
|
||||
tg = this.transparentColor[1],
|
||||
tb = this.transparentColor[2];
|
||||
|
||||
gl.disable(gl.CULL_FACE);
|
||||
|
||||
f.bindOutputTexture(this._materialTexture);
|
||||
gl.clearColor(tr, tg, tb, 0.0);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, creator._texCoordsBuffer);
|
||||
gl.vertexAttribPointer(sha.texCoords._pName, creator._texCoordsBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this._gridBuffer);
|
||||
gl.vertexAttribPointer(sha.corners._pName, this._gridBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
gl.uniform4fv(shu.extentParams._pName, this._extentWgs84Params);
|
||||
gl.activeTexture(gl.TEXTURE0);
|
||||
gl.bindTexture(gl.TEXTURE_2D, this._sourceTexture);
|
||||
gl.uniform1i(shu.sourceTexture._pName, 0);
|
||||
sh.drawIndexBuffer(gl.TRIANGLE_STRIP, creator._indexBuffer);
|
||||
f.deactivate();
|
||||
|
||||
gl.enable(gl.CULL_FACE);
|
||||
|
||||
this._ready = true;
|
||||
|
||||
this._creationProceeding = false;
|
||||
}
|
||||
};
|
||||
|
||||
export { GeoTexture2d };
|
||||
287
src/og/layer/GeoVideo.js
Normal file
287
src/og/layer/GeoVideo.js
Normal file
@ -0,0 +1,287 @@
|
||||
/**
|
||||
* @module og/layer/GeoVideo
|
||||
*/
|
||||
|
||||
'use sctrict';
|
||||
|
||||
import { BaseGeoImage } from './BaseGeoImage.js';
|
||||
|
||||
/**
|
||||
* Used to load and display a video stream by specific corners coordinates on the globe, implements og.layer.BaseGeoImage interface.
|
||||
* @class
|
||||
* @extends {og.layer.BaseGeoImage}
|
||||
*/
|
||||
class GeoVideo extends BaseGeoImage {
|
||||
constructor(name, options) {
|
||||
super(name, options);
|
||||
|
||||
/**
|
||||
* @protected
|
||||
* @const
|
||||
* @type {Boolean}
|
||||
*/
|
||||
this._animate = true;
|
||||
|
||||
/**
|
||||
* HTML5 video element object.
|
||||
* @private
|
||||
* @type {Object}
|
||||
*/
|
||||
this._video = options.videoElement || null;
|
||||
|
||||
/**
|
||||
* VIdeo source url path.
|
||||
* @private
|
||||
* @type {String}
|
||||
*/
|
||||
this._src = options.src || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets video source url path.
|
||||
* @public
|
||||
* @param {String} srs - Video url path.
|
||||
*/
|
||||
setSrc(src) {
|
||||
this._planet && this._planet._geoImageCreator.remove(this);
|
||||
this._src = src;
|
||||
this._sourceReady = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets HTML5 video object.
|
||||
* @public
|
||||
* @param {Object} video - HTML5 video element object.
|
||||
*/
|
||||
setVideoElement(video) {
|
||||
this._planet && this._planet._geoImageCreator.remove(this);
|
||||
this._video = video;
|
||||
this._src = video.src;
|
||||
this._sourceReady = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets layer visibility.
|
||||
* @public
|
||||
* @param {boolean} visibility - Layer visibility.
|
||||
*/
|
||||
setVisibility(visibility) {
|
||||
if (visibility != this._visibility) {
|
||||
this._visibility = visibility;
|
||||
if (this._isBaseLayer && visibility) {
|
||||
this._planet.setBaseLayer(this);
|
||||
}
|
||||
this._planet.updateVisibleLayers();
|
||||
this.events.dispatch(this.events.visibilitychange, this);
|
||||
|
||||
//remove from creator
|
||||
if (visibility) {
|
||||
this._sourceReady && this._planet._geoImageCreator.add(this);
|
||||
this._video && this._video.play();
|
||||
} else {
|
||||
this._sourceReady && this._planet._geoImageCreator.remove(this);
|
||||
this._video && this._video.pause();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates or refresh source video GL texture.
|
||||
* @virtual
|
||||
* @protected
|
||||
*/
|
||||
_createSourceTexture() {
|
||||
if (!this._sourceCreated) {
|
||||
this._sourceTexture = this._planet.renderer.handler.createTexture_n(this._video);
|
||||
this._sourceCreated = true;
|
||||
} else {
|
||||
var gl = this._planet.renderer.handler.gl;
|
||||
gl.bindTexture(gl.TEXTURE_2D, this._sourceTexture);
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this._video);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_onCanPlay(video) {
|
||||
this._frameWidth = video.videoWidth;
|
||||
this._frameHeight = video.videoHeight;
|
||||
video.width = video.videoWidth;
|
||||
video.height = video.videoHeight;
|
||||
video.play();
|
||||
this._sourceReady = true;
|
||||
this._planet._geoImageCreator.add(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_onError(video) {
|
||||
var err = "unknown error";
|
||||
switch (video.error.code) {
|
||||
case 1: err = "video loading aborted"; break;
|
||||
case 2: err = "network loading error"; break;
|
||||
case 3: err = "video decoding failed / corrupted data or unsupported codec"; break;
|
||||
case 4: err = "video not supported"; break;
|
||||
};
|
||||
console.log("Error: " + err + " (errorcode=" + video.error.code + ")");
|
||||
};
|
||||
|
||||
/**
|
||||
* Loads planet segment material. In this case - GeoImage source video.
|
||||
* @virtual
|
||||
* @public
|
||||
* @param {og.planetSegment.Material} material - GeoImage planet material.
|
||||
*/
|
||||
loadMaterial(material) {
|
||||
material.isLoading = true;
|
||||
this._creationProceeding = true;
|
||||
if (!this._sourceReady && this._src) {
|
||||
if (this._video) {
|
||||
if (this._video.readyState === this._video.HAVE_ENOUGH_DATA) {
|
||||
this._onCanPlay(this._video);
|
||||
} else if (this._video.src) {
|
||||
var that = this;
|
||||
this._video.addEventListener('canplay', function (e) {
|
||||
that._onCanPlay(this);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
this._video = document.createElement('video');
|
||||
var that = this;
|
||||
this._video.addEventListener('canplay', function () {
|
||||
that._onCanPlay(this);
|
||||
});
|
||||
this._video.addEventListener('error', function () {
|
||||
that._onError(this);
|
||||
});
|
||||
}
|
||||
this._video.autoplay = true;
|
||||
this._video.loop = true;
|
||||
this._video.src = this._src;
|
||||
this._video.setAttribute("playsinline", "");
|
||||
this._video.setAttribute("webkit-playsinline", "");
|
||||
} else {
|
||||
this._planet._geoImageCreator.add(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @virtual
|
||||
* @param {og.planetSegment.Material} material - GeoImage material.
|
||||
*/
|
||||
abortMaterialLoading(material) {
|
||||
this._video && (this._video.src = '');
|
||||
this._creationProceeding = false;
|
||||
material.isLoading = false;
|
||||
material.isReady = false;
|
||||
}
|
||||
|
||||
_renderingProjType1() {
|
||||
var p = this._planet,
|
||||
h = p.renderer.handler,
|
||||
gl = h.gl,
|
||||
creator = p._geoImageCreator;
|
||||
|
||||
this._refreshFrame && this._createFrame();
|
||||
|
||||
if (this._sourceCreated) {
|
||||
var gl = this._planet.renderer.handler.gl;
|
||||
gl.bindTexture(gl.TEXTURE_2D, this._sourceTexture);
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this._video);
|
||||
} else {
|
||||
this._sourceTexture = this._planet.renderer.handler.createTexture_n(this._video);
|
||||
this._sourceCreated = true;
|
||||
}
|
||||
|
||||
var f = creator._framebuffer;
|
||||
f.setSize(this._frameWidth, this._frameHeight);
|
||||
f.activate();
|
||||
|
||||
h.shaderPrograms.geoImageTransform.activate();
|
||||
var sh = h.shaderPrograms.geoImageTransform._program;
|
||||
var sha = sh.attributes,
|
||||
shu = sh.uniforms;
|
||||
|
||||
var tr = this.transparentColor[0],
|
||||
tg = this.transparentColor[1],
|
||||
tb = this.transparentColor[2];
|
||||
|
||||
gl.disable(gl.CULL_FACE);
|
||||
|
||||
f.bindOutputTexture(this._materialTexture);
|
||||
gl.clearColor(tr, tg, tb, 0.0);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, creator._texCoordsBuffer);
|
||||
gl.vertexAttribPointer(sha.texCoords._pName, creator._texCoordsBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this._gridBuffer);
|
||||
gl.vertexAttribPointer(sha.corners._pName, this._gridBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
gl.uniform4fv(shu.extentParams._pName, this._extentMercParams);
|
||||
gl.activeTexture(gl.TEXTURE0);
|
||||
gl.bindTexture(gl.TEXTURE_2D, this._sourceTexture);
|
||||
gl.uniform1i(shu.sourceTexture._pName, 0);
|
||||
sh.drawIndexBuffer(gl.TRIANGLE_STRIP, creator._indexBuffer);
|
||||
f.deactivate();
|
||||
|
||||
gl.enable(gl.CULL_FACE);
|
||||
|
||||
this._ready = true;
|
||||
|
||||
this._creationProceeding = false;
|
||||
}
|
||||
|
||||
_renderingProjType0() {
|
||||
var p = this._planet,
|
||||
h = p.renderer.handler,
|
||||
gl = h.gl,
|
||||
creator = p._geoImageCreator;
|
||||
|
||||
this._refreshFrame && this._createFrame();
|
||||
if (!this._sourceCreated) {
|
||||
this._sourceTexture = this._planet.renderer.handler.createTexture_n(this._video);
|
||||
this._sourceCreated = true;
|
||||
} else {
|
||||
var gl = this._planet.renderer.handler.gl;
|
||||
gl.bindTexture(gl.TEXTURE_2D, this._sourceTexture);
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this._video);
|
||||
}
|
||||
|
||||
var f = creator._framebuffer;
|
||||
f.setSize(this._frameWidth, this._frameHeight);
|
||||
f.activate();
|
||||
|
||||
h.shaderPrograms.geoImageTransform.activate();
|
||||
var sh = h.shaderPrograms.geoImageTransform._program;
|
||||
var sha = sh.attributes,
|
||||
shu = sh.uniforms;
|
||||
|
||||
var tr = this.transparentColor[0],
|
||||
tg = this.transparentColor[1],
|
||||
tb = this.transparentColor[2];
|
||||
|
||||
gl.disable(gl.CULL_FACE);
|
||||
|
||||
f.bindOutputTexture(this._materialTexture);
|
||||
gl.clearColor(tr, tg, tb, 0.0);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, creator._texCoordsBuffer);
|
||||
gl.vertexAttribPointer(sha.texCoords._pName, creator._texCoordsBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this._gridBuffer);
|
||||
gl.vertexAttribPointer(sha.corners._pName, this._gridBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
gl.uniform4fv(shu.extentParams._pName, this._extentWgs84Params);
|
||||
gl.activeTexture(gl.TEXTURE0);
|
||||
gl.bindTexture(gl.TEXTURE_2D, this._sourceTexture);
|
||||
gl.uniform1i(shu.sourceTexture._pName, 0);
|
||||
sh.drawIndexBuffer(gl.TRIANGLE_STRIP, creator._indexBuffer);
|
||||
f.deactivate();
|
||||
|
||||
gl.enable(gl.CULL_FACE);
|
||||
|
||||
this._ready = true;
|
||||
|
||||
this._creationProceeding = false;
|
||||
}
|
||||
};
|
||||
|
||||
export { GeoVideo };
|
||||
649
src/og/layer/Layer.js
Normal file
649
src/og/layer/Layer.js
Normal file
@ -0,0 +1,649 @@
|
||||
/**
|
||||
* @module og/layer/Layer
|
||||
*/
|
||||
|
||||
'use sctrict';
|
||||
|
||||
import * as utils from '../utils/shared.js';
|
||||
import * as mercator from '../mercator.js';
|
||||
import { Events } from '../Events.js';
|
||||
import { Extent } from '../Extent.js';
|
||||
import { LonLat } from '../LonLat.js';
|
||||
import { Material } from './Material.js';
|
||||
import { Vec3 } from '../math/Vec3.js';
|
||||
|
||||
|
||||
/**
|
||||
* @classdesc
|
||||
* Base class; normally only used for creating subclasses and not instantiated in apps.
|
||||
* A visual representation of raster or vector map data well known as a layer.
|
||||
* @class
|
||||
* @param {String} [name="noname"] - Layer name.
|
||||
* @param {Object} [options] - Layer options:
|
||||
* @param {number} [options.opacity=1.0] - Layer opacity.
|
||||
* @param {Array.<number,number,number>} [options.transparentColor=[-1,-1,-1]] - RGB color that defines transparent color.
|
||||
* @param {number} [options.minZoom=0] - Minimal visibility zoom level.
|
||||
* @param {number} [options.maxZoom=0] - Maximal visibility zoom level.
|
||||
* @param {string} [options.attribution] - Layer attribution that displayed in the attribution area on the screen.
|
||||
* @param {boolean} [options.isBaseLayer=false] - This is a base layer.
|
||||
* @param {boolean} [options.visibility=true] - Layer visibility.
|
||||
* @param {og.Extent} [options.extent=[[-180.0, -90.0], [180.0, 90.0]]] - Visible extent.
|
||||
* @param {og.math.Vector3} [options.ambient=[0.1, 0.1, 0.21]] - Ambient RGB color.
|
||||
* @param {og.math.Vector3} [options.diffuse=[1.0, 1.0, 1.0]] - Diffuse RGB color.
|
||||
* @param {og.math.Vector3} [options.specular=[0.00025, 0.00015, 0.0001]] - Specular RGB color.
|
||||
* @param {Number} [options.shininess=100] - Shininess.
|
||||
*
|
||||
* @fires og.layer.Layer#visibilitychange
|
||||
* @fires og.layer.Layer#add
|
||||
* @fires og.layer.Layer#remove
|
||||
* @fires og.layer.Vector#mousemove
|
||||
* @fires og.layer.Vector#mouseenter
|
||||
* @fires og.layer.Vector#mouseleave
|
||||
* @fires og.layer.Vector#lclick
|
||||
* @fires og.layer.Vector#rclick
|
||||
* @fires og.layer.Vector#mclick
|
||||
* @fires og.layer.Vector#ldblclick
|
||||
* @fires og.layer.Vector#rdblclick
|
||||
* @fires og.layer.Vector#mdblclick
|
||||
* @fires og.layer.Vector#lup
|
||||
* @fires og.layer.Vector#rup
|
||||
* @fires og.layer.Vector#mup
|
||||
* @fires og.layer.Vector#ldown
|
||||
* @fires og.layer.Vector#rdown
|
||||
* @fires og.layer.Vector#mdown
|
||||
* @fires og.layer.Vector#lhold
|
||||
* @fires og.layer.Vector#rhold
|
||||
* @fires og.layer.Vector#mhold
|
||||
* @fires og.layer.Vector#mousewheel
|
||||
* @fires og.layer.Vector#touchmove
|
||||
* @fires og.layer.Vector#touchstart
|
||||
* @fires og.layer.Vector#touchend
|
||||
* @fires og.layer.Vector#doubletouch
|
||||
*/
|
||||
class Layer {
|
||||
constructor(name, options) {
|
||||
|
||||
options = options || {};
|
||||
|
||||
/**
|
||||
* Layer user name.
|
||||
* @public
|
||||
* @type {string}
|
||||
*/
|
||||
this.name = name || "noname";
|
||||
|
||||
this.displayInLayerSwitcher = options.displayInLayerSwitcher !== undefined ? options.displayInLayerSwitcher : true;
|
||||
|
||||
/**
|
||||
* Layer global opacity.
|
||||
* @public
|
||||
* @type {number}
|
||||
*/
|
||||
this.opacity = options.opacity || 1.0;
|
||||
|
||||
/**
|
||||
* Transparent RGB color mask.
|
||||
* @public
|
||||
* @type {Array.<number,number,number>}
|
||||
*/
|
||||
this.transparentColor = options.transparentColor || [-1, -1, -1];
|
||||
|
||||
/**
|
||||
* Minimal zoom level when layer is visibile.
|
||||
* @public
|
||||
* @type {number}
|
||||
*/
|
||||
this.minZoom = options.minZoom || 0;
|
||||
|
||||
/**
|
||||
* Maximal zoom level when layer is visibile.
|
||||
* @public
|
||||
* @type {number}
|
||||
*/
|
||||
this.maxZoom = options.maxZoom || 50;
|
||||
|
||||
/**
|
||||
* Layer light material parameters.
|
||||
* @public
|
||||
* @type {Object}
|
||||
*/
|
||||
this.ambient = utils.createColorRGB(options.ambient, new Vec3(0.1, 0.1, 0.21));
|
||||
this.diffuse = utils.createColorRGB(options.diffuse, new Vec3(1.0, 1.0, 1.0));
|
||||
this.specular = utils.createColorRGB(options.specular, new Vec3(0.00025, 0.00015, 0.0001));
|
||||
this.shininess = options.shininess || 100.0;
|
||||
|
||||
/**
|
||||
* Planet node.
|
||||
* @protected
|
||||
* @type {og.scene.Planet}
|
||||
*/
|
||||
this._planet = null;
|
||||
|
||||
/**
|
||||
* Unic identifier.
|
||||
* @protected
|
||||
* @type {number}
|
||||
*/
|
||||
this._id = Layer.__layersCounter++;
|
||||
|
||||
/**
|
||||
* Layer attribution.
|
||||
* @protected
|
||||
* @type {string}
|
||||
*/
|
||||
this._attribution = options.attribution || "";
|
||||
|
||||
/**
|
||||
* Layer z-index.
|
||||
* @protected
|
||||
* @type {number}
|
||||
*/
|
||||
this._zIndex = options.zIndex || 0;
|
||||
|
||||
/**
|
||||
* Base layer type flag.
|
||||
* @protected
|
||||
* @type {boolean}
|
||||
*/
|
||||
this._isBaseLayer = options.isBaseLayer || false;
|
||||
|
||||
/**
|
||||
* Layer visibility.
|
||||
* @protected
|
||||
* @type {boolean}
|
||||
*/
|
||||
this._visibility = options.visibility !== undefined ? options.visibility : true;
|
||||
|
||||
/**
|
||||
* Height over the ground.
|
||||
* @protected
|
||||
* @type {number}
|
||||
*/
|
||||
this._height = options.height || 0;
|
||||
|
||||
/**
|
||||
* Visible degrees extent.
|
||||
* @protected
|
||||
* @type {og.Extent}
|
||||
*/
|
||||
this._extent = null;
|
||||
|
||||
/**
|
||||
* Visible mercator extent.
|
||||
* @protected
|
||||
* @type {og.Extent}
|
||||
*/
|
||||
this._extentMerc = null;
|
||||
|
||||
//Setting the extent up
|
||||
this.setExtent(utils.createExtent(options.extent, new Extent(new LonLat(-180, -90), new LonLat(180, 90))));
|
||||
|
||||
/**
|
||||
* Layer picking color. Assign when added to the planet.
|
||||
* @protected
|
||||
* @type {og.math.Vector3}
|
||||
*/
|
||||
this._pickingColor = new Vec3();
|
||||
|
||||
/**
|
||||
* Events handler.
|
||||
* @public
|
||||
* @type {og.Events}
|
||||
*/
|
||||
this.events = new Events();
|
||||
this.events.registerNames(EVENT_NAMES);
|
||||
}
|
||||
|
||||
static getTileIndex(...arr) {
|
||||
return arr.join("_");
|
||||
}
|
||||
|
||||
static get __layersCounter() {
|
||||
if (!this.__lcounter && this.__lcounter !== 0) {
|
||||
this.__lcounter = 0;
|
||||
}
|
||||
return this.__lcounter;
|
||||
}
|
||||
|
||||
static set __layersCounter(n) {
|
||||
this.__lcounter = n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if a layer has imagery tiles.
|
||||
* @public
|
||||
* @virtual
|
||||
* @returns {boolean} - Imagery tiles flag.
|
||||
*/
|
||||
hasImageryTiles() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets layer identifier.
|
||||
* @public
|
||||
* @returns {string} - Layer object id.
|
||||
*/
|
||||
getID() {
|
||||
return this._id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares layers instances.
|
||||
* @public
|
||||
* @param {og.layer.Layer} layer - Layer instance to compare.
|
||||
* @returns {boolean} - Returns true if the layers is the same instance of the input.
|
||||
*/
|
||||
isEqual(layer) {
|
||||
return layer._id === this._id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign the planet.
|
||||
* @protected
|
||||
* @virtual
|
||||
* @param {og.scene.Planet} planet - Planet render node.
|
||||
*/
|
||||
_assignPlanet(planet) {
|
||||
planet.layers.push(this);
|
||||
this._planet = planet;
|
||||
this.events.on("visibilitychange", planet._onLayerVisibilityChanged, planet);
|
||||
if (this._isBaseLayer && this._visibility) {
|
||||
planet.setBaseLayer(this);
|
||||
}
|
||||
planet.events.dispatch(planet.events.layeradd, this);
|
||||
this.events.dispatch(this.events.add, planet);
|
||||
planet.updateVisibleLayers();
|
||||
this._bindPicking();
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign picking color to the layer.
|
||||
* @protected
|
||||
* @virtual
|
||||
*/
|
||||
_bindPicking() {
|
||||
this._planet && this._planet.renderer.assignPickingColor(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds layer to the planet.
|
||||
* @public
|
||||
* @param {og.scene.Planet} planet - Adds layer to the planet.
|
||||
*/
|
||||
addTo(planet) {
|
||||
this._assignPlanet(planet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes from planet.
|
||||
* @public
|
||||
* @returns {og.layer.Layer} -This layer.
|
||||
*/
|
||||
remove() {
|
||||
var p = this._planet;
|
||||
if (p) {
|
||||
var lid = this._id;
|
||||
for (var i = 0; i < p.layers.length; i++) {
|
||||
if (p.layers[i]._id === lid) {
|
||||
p.renderer.clearPickingColor(this);
|
||||
p.layers.splice(i, 1);
|
||||
p.updateVisibleLayers();
|
||||
this.clear();
|
||||
p.events.dispatch(p.events.layerremove, this);
|
||||
this.events.dispatch(this.events.remove, p);
|
||||
this._planet = null;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears layer material.
|
||||
* @virtual
|
||||
*/
|
||||
clear() {
|
||||
this._planet && this._planet._clearLayerMaterial(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets layer attribution text.
|
||||
* @public
|
||||
* @param {string} html - HTML code that represents layer attribution, it could be just a text.
|
||||
*/
|
||||
setAttribution(html) {
|
||||
this._attribution = html;
|
||||
this._planet.updateAttributionsList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets height over the ground.
|
||||
* @public
|
||||
* @param {number} height - Layer height.
|
||||
*/
|
||||
setHeight(height) {
|
||||
this._height = height;
|
||||
this._planet.updateVisibleLayers();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets layer height.
|
||||
* @public
|
||||
* @returns {number} -
|
||||
*/
|
||||
getHeight() {
|
||||
return this._height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets z-index.
|
||||
* @public
|
||||
* @param {number} zIndex - Layer z-index.
|
||||
*/
|
||||
setZIndex(zIndex) {
|
||||
this._zIndex = zIndex;
|
||||
this._planet.updateVisibleLayers();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets z-index.
|
||||
* @public
|
||||
* @returns {number} -
|
||||
*/
|
||||
getZIndex() {
|
||||
return this._zIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set zIndex to the maximal value depend on other layers on the planet.
|
||||
* @public
|
||||
*/
|
||||
bringToFront() {
|
||||
if (this._planet) {
|
||||
var vl = this._planet.visibleTileLayers;
|
||||
var l = vl[vl.length - 1];
|
||||
if (!l.isEqual(this)) {
|
||||
this.setZIndex(l.getZIndex() + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the layer is a base.
|
||||
* @public
|
||||
* @returns {boolean} - Base layer flag.
|
||||
*/
|
||||
isBaseLayer() {
|
||||
return this._isBaseLayer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets base layer type true.
|
||||
* @public
|
||||
* @param {boolean} flag - Base layer flag.
|
||||
*/
|
||||
setBaseLayer(flag) {
|
||||
this._isBaseLayer = flag;
|
||||
|
||||
if (this._planet && !flag && this.isEqual(this._planet.baseLayer)) {
|
||||
this._planet.baseLayer = null;
|
||||
}
|
||||
|
||||
this._planet.updateVisibleLayers();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets layer visibility.
|
||||
* @public
|
||||
* @virtual
|
||||
* @param {boolean} visibility - Layer visibility.
|
||||
*/
|
||||
setVisibility(visibility) {
|
||||
if (visibility !== this._visibility) {
|
||||
this._visibility = visibility;
|
||||
if (this._isBaseLayer && visibility) {
|
||||
this._planet.setBaseLayer(this);
|
||||
}
|
||||
this._planet.updateVisibleLayers();
|
||||
this.events.dispatch(this.events.visibilitychange, this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets layer visibility.
|
||||
* @public
|
||||
* @returns {boolean} - Layer visibility.
|
||||
*/
|
||||
getVisibility() {
|
||||
return this._visibility;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets visible geographical extent.
|
||||
* @public
|
||||
* @param {og.Extent} extent - Layer visible geographical extent.
|
||||
*/
|
||||
setExtent(extent) {
|
||||
var sw = extent.southWest.clone(),
|
||||
ne = extent.northEast.clone();
|
||||
if (sw.lat < mercator.MIN_LAT) {
|
||||
sw.lat = mercator.MIN_LAT;
|
||||
}
|
||||
if (ne.lat > mercator.MAX_LAT) {
|
||||
ne.lat = mercator.MAX_LAT;
|
||||
}
|
||||
this._extent = extent.clone();
|
||||
this._extentMerc = new Extent(sw.forwardMercator(), ne.forwardMercator());
|
||||
this._correctFullExtent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets layer extent.
|
||||
* @public
|
||||
* @return {og.Extent} - Layer geodetic extent.
|
||||
*/
|
||||
getExtent() {
|
||||
return this._extent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Special correction of the whole globe extent.
|
||||
* @protected
|
||||
*/
|
||||
_correctFullExtent() {
|
||||
//var e = this._extent,
|
||||
// em = this._extentMerc;
|
||||
//var ENLARGE_MERCATOR_LON = og.mercator.POLE + 50000;
|
||||
//var ENLARGE_MERCATOR_LAT = og.mercator.POLE + 50000;
|
||||
//if (e.northEast.lat === 90.0) {
|
||||
// em.northEast.lat = ENLARGE_MERCATOR_LAT;
|
||||
//}
|
||||
//if (e.northEast.lon === 180.0) {
|
||||
// em.northEast.lon = ENLARGE_MERCATOR_LON;
|
||||
//}
|
||||
//if (e.southWest.lat === -90.0) {
|
||||
// em.southWest.lat = -ENLARGE_MERCATOR_LAT;
|
||||
//}
|
||||
//if (e.southWest.lon === -180.0) {
|
||||
// em.southWest.lon = -ENLARGE_MERCATOR_LON;
|
||||
//}
|
||||
}
|
||||
|
||||
createMaterial(segment) {
|
||||
return new Material(segment, this);
|
||||
}
|
||||
};
|
||||
|
||||
const EVENT_NAMES = [
|
||||
/**
|
||||
* Triggered when layer visibilty chanched.
|
||||
* @event og.layer.Layer#visibilitychange
|
||||
*/
|
||||
"visibilitychange",
|
||||
|
||||
/**
|
||||
* Triggered when layer has added to the planet.
|
||||
* @event og.layer.Layer#add
|
||||
*/
|
||||
"add",
|
||||
|
||||
/**
|
||||
* Triggered when layer has removed from the planet.
|
||||
* @event og.layer.Layer#remove
|
||||
*/
|
||||
"remove",
|
||||
|
||||
/**
|
||||
* Triggered when mouse moves over the layer.
|
||||
* @event og.layer.Layer#mousemove
|
||||
*/
|
||||
"mousemove",
|
||||
|
||||
/**
|
||||
* Triggered when mouse has entered over the layer.
|
||||
* @event og.layer.Layer#mouseenter
|
||||
*/
|
||||
"mouseenter",
|
||||
|
||||
/**
|
||||
* Triggered when mouse leaves the layer.
|
||||
* @event og.layer.Layer#mouseenter
|
||||
*/
|
||||
"mouseleave",
|
||||
|
||||
/**
|
||||
* Mouse left button clicked.
|
||||
* @event og.layer.Layer#lclick
|
||||
*/
|
||||
"lclick",
|
||||
|
||||
/**
|
||||
* Mouse right button clicked.
|
||||
* @event og.layer.Layer#rclick
|
||||
*/
|
||||
"rclick",
|
||||
|
||||
/**
|
||||
* Mouse right button clicked.
|
||||
* @event og.layer.Layer#mclick
|
||||
*/
|
||||
"mclick",
|
||||
|
||||
/**
|
||||
* Mouse left button double click.
|
||||
* @event og.layer.Layer#ldblclick
|
||||
*/
|
||||
"ldblclick",
|
||||
|
||||
/**
|
||||
* Mouse right button double click.
|
||||
* @event og.layer.Layer#rdblclick
|
||||
*/
|
||||
"rdblclick",
|
||||
|
||||
/**
|
||||
* Mouse middle button double click.
|
||||
* @event og.layer.Layer#mdblclick
|
||||
*/
|
||||
"mdblclick",
|
||||
|
||||
/**
|
||||
* Mouse left button up(stop pressing).
|
||||
* @event og.layer.Layer#lup
|
||||
*/
|
||||
"lup",
|
||||
|
||||
/**
|
||||
* Mouse right button up(stop pressing).
|
||||
* @event og.layer.Layer#rup
|
||||
*/
|
||||
"rup",
|
||||
|
||||
/**
|
||||
* Mouse middle button up(stop pressing).
|
||||
* @event og.layer.Layer#mup
|
||||
*/
|
||||
"mup",
|
||||
|
||||
/**
|
||||
* Mouse left button is just pressed down(start pressing).
|
||||
* @event og.layer.Layer#ldown
|
||||
*/
|
||||
"ldown",
|
||||
|
||||
/**
|
||||
* Mouse right button is just pressed down(start pressing).
|
||||
* @event og.layer.Layer#rdown
|
||||
*/
|
||||
"rdown",
|
||||
|
||||
/**
|
||||
* Mouse middle button is just pressed down(start pressing).
|
||||
* @event og.layer.Layer#mdown
|
||||
*/
|
||||
"mdown",
|
||||
|
||||
/**
|
||||
* Mouse left button is pressing.
|
||||
* @event og.layer.Layer#lhold
|
||||
*/
|
||||
"lhold",
|
||||
|
||||
/**
|
||||
* Mouse right button is pressing.
|
||||
* @event og.layer.Layer#rhold
|
||||
*/
|
||||
"rhold",
|
||||
|
||||
/**
|
||||
* Mouse middle button is pressing.
|
||||
* @event og.layer.Layer#mhold
|
||||
*/
|
||||
"mhold",
|
||||
|
||||
/**
|
||||
* Mouse wheel is rotated.
|
||||
* @event og.layer.Layer#mousewheel
|
||||
*/
|
||||
"mousewheel",
|
||||
|
||||
/**
|
||||
* Triggered when touching moves over the layer.
|
||||
* @event og.layer.Layer#touchmove
|
||||
*/
|
||||
"touchmove",
|
||||
|
||||
/**
|
||||
* Triggered when layer begins to touch.
|
||||
* @event og.layer.Layer#touchstart
|
||||
*/
|
||||
"touchstart",
|
||||
|
||||
/**
|
||||
* Triggered when layer has finished touching.
|
||||
* @event og.layer.Layer#touchend
|
||||
*/
|
||||
"touchend",
|
||||
|
||||
/**
|
||||
* Triggered layer has double touched.
|
||||
* @event og.layer.Layer#doubletouch
|
||||
*/
|
||||
"doubletouch",
|
||||
|
||||
/**
|
||||
* Triggered when touching leaves layer borders.
|
||||
* @event og.layer.Layer#touchleave
|
||||
*/
|
||||
"touchleave",
|
||||
|
||||
/**
|
||||
* Triggered when touch enters over the layer.
|
||||
* @event og.layer.Layer#touchenter
|
||||
*/
|
||||
"touchenter"
|
||||
];
|
||||
|
||||
export { Layer };
|
||||
70
src/og/layer/Material.js
Normal file
70
src/og/layer/Material.js
Normal file
@ -0,0 +1,70 @@
|
||||
/**
|
||||
* @module og/layer/Material
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const Material = function (segment, layer) {
|
||||
this.segment = segment;
|
||||
this.layer = layer;
|
||||
this.isReady = false;
|
||||
this.isLoading = false;
|
||||
this.texture = null;
|
||||
this.pickingMask = null;
|
||||
this.image = null;
|
||||
this.textureExists = false;
|
||||
this.appliedNodeId = 0;
|
||||
|
||||
//vector data
|
||||
this._updateTexture = null;
|
||||
this._updatePickingMask = null;
|
||||
this.pickingReady = false;
|
||||
};
|
||||
|
||||
Material.prototype.assignLayer = function (layer) {
|
||||
this.layer = layer;
|
||||
};
|
||||
|
||||
Material.prototype.abortLoading = function () {
|
||||
this.layer.abortMaterialLoading(this);
|
||||
};
|
||||
|
||||
Material.prototype.applyImage = function (img) {
|
||||
if (this.segment.ready) {
|
||||
this._updateTexture = null;
|
||||
|
||||
this.image = img;
|
||||
this.texture = this.segment.handler.createTexture(img);
|
||||
this.appliedNodeId = this.segment.node.nodeId;
|
||||
this.isReady = true;
|
||||
this.pickingReady = true;
|
||||
this.textureExists = true;
|
||||
this.isLoading = false;
|
||||
}
|
||||
};
|
||||
|
||||
Material.prototype.applyTexture = function (texture, pickingMask) {
|
||||
|
||||
this.texture = texture;
|
||||
this._updateTexture = null;
|
||||
|
||||
this.pickingMask = pickingMask || null;
|
||||
this._updatePickingMask = null;
|
||||
|
||||
this.isReady = true;
|
||||
this.pickingReady = true;
|
||||
this.textureExists = true;
|
||||
this.isLoading = false;
|
||||
this.appliedNodeId = this.segment.node.nodeId;
|
||||
};
|
||||
|
||||
Material.prototype.textureNotExists = function () {
|
||||
this.isLoading = true;
|
||||
this.textureExists = false;
|
||||
};
|
||||
|
||||
Material.prototype.clear = function () {
|
||||
this.layer.clearMaterial(this);
|
||||
};
|
||||
|
||||
export { Material };
|
||||
814
src/og/layer/Vector.js
Normal file
814
src/og/layer/Vector.js
Normal file
@ -0,0 +1,814 @@
|
||||
/**
|
||||
* @module og/layer/Vector
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as math from '../math.js';
|
||||
import * as mercator from '../mercator.js';
|
||||
import * as quadTree from '../quadTree/quadTree.js';
|
||||
import { Entity } from '../entity/Entity.js';
|
||||
import { EntityCollection } from '../entity/EntityCollection.js';
|
||||
import { Extent } from '../Extent.js';
|
||||
import {
|
||||
EntityCollectionNode,
|
||||
EntityCollectionNodeWGS84
|
||||
} from '../quadTree/EntityCollectionNode.js';
|
||||
import { GeometryHandler } from '../entity/GeometryHandler.js';
|
||||
import { Layer } from './Layer.js';
|
||||
import { LonLat } from '../LonLat.js';
|
||||
import { QueueArray } from '../QueueArray.js';
|
||||
import { Vec3 } from '../math/Vec3.js';
|
||||
|
||||
/**
|
||||
* Creates entity instance array.
|
||||
* @static
|
||||
* @param {og.Entities[] || Object[]} entities - Entity array.
|
||||
* @returns {og.Entity[]} - Entity object array.
|
||||
*/
|
||||
function _entitiesConstructor(entities) {
|
||||
var res = [];
|
||||
for (var i = 0; i < entities.length; i++) {
|
||||
var ei = entities[i];
|
||||
if (ei instanceof Entity) {
|
||||
res.push(ei);
|
||||
} else {
|
||||
res.push(new Entity(ei));
|
||||
}
|
||||
}
|
||||
return res;
|
||||
};
|
||||
|
||||
/**
|
||||
* Vector layer represents alternative entities store. Used for geospatial data rendering like
|
||||
* points, lines, polygons, geometry objects etc.
|
||||
* @class
|
||||
* @extends {og.layer.Layer}
|
||||
* @param {string} [name="noname"] - Layer name.
|
||||
* @param {Object} [options] - Layer options:
|
||||
* @param {number} [options.minZoom=0] - Minimal visible zoom. 0 is default
|
||||
* @param {number} [options.maxZoom=50] - Maximal visible zoom. 50 is default.
|
||||
* @param {string} [options.attribution] - Layer attribution.
|
||||
* @param {string} [options.zIndex=0] - Layer Z-order index. 0 is default.
|
||||
* @param {boolean} [options.visibility=true] - Layer visibility. True is default.
|
||||
* @param {boolean} [options.isBaseLayer=false] - Layer base layer. False is default.
|
||||
* @param {Array.<og.Entity>} [options.entities] - Entities array.
|
||||
* @param {Array.<number,number,number>} [options.scaleByDistance] - Scale by distance parameters.
|
||||
* First index - near distance to the entity, after entity becomes full scale.
|
||||
* Second index - far distance to the entity, when entity becomes zero scale.
|
||||
* Third index - far distance to the entity, when entity becomes invisible.
|
||||
* @param {number} [options.nodeCapacity=30] - Maximum entities quantity in the tree node. Rendering optimization parameter. 30 is default.
|
||||
* @param {boolean} [options.async=true] - Asynchronous vector data handling before rendering. True for optimization huge data.
|
||||
* @param {boolean} [options.clampToGround = false] - Clamp vector data to the ground.
|
||||
* @param {boolean} [options.relativeToGround = false] - Place vector data relative to the ground relief.
|
||||
*
|
||||
* @fires og.layer.Vector#entitymove
|
||||
* @fires og.layer.Vector#draw
|
||||
* @fires og.layer.Vector#add
|
||||
* @fires og.layer.Vector#remove
|
||||
* @fires og.layer.Vector#entityadd
|
||||
* @fires og.layer.Vector#entityremove
|
||||
* @fires og.layer.Vector#visibilitychange
|
||||
*/
|
||||
class Vector extends Layer {
|
||||
constructor(name, options) {
|
||||
options = options || {};
|
||||
|
||||
super(name, options);
|
||||
|
||||
this.events.registerNames(EVENT_NAMES);
|
||||
|
||||
this.isVector = true;
|
||||
|
||||
/**
|
||||
* First index - near distance to the entity, after that entity becomes full scale.
|
||||
* Second index - far distance to the entity, when entity becomes zero scale.
|
||||
* Third index - far distance to the entity, when entity becomes invisible.
|
||||
* @public
|
||||
* @type {Array.<number,number,number>}
|
||||
*/
|
||||
this.scaleByDistance = options.scaleByDistance || [math.MAX32, math.MAX32, math.MAX32];
|
||||
|
||||
/**
|
||||
* Asynchronous data handling before rendering.
|
||||
* @public
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.async = options.async !== undefined ? options.async : true;
|
||||
|
||||
/**
|
||||
* Vector data clamp to ground flag.
|
||||
* @public
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.clampToGround = options.clampToGround || false;
|
||||
|
||||
/**
|
||||
* Sets vector data relative to the ground relief.
|
||||
* @public
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.relativeToGround = options.relativeToGround || false;
|
||||
|
||||
/**
|
||||
* Maximum entities quantity in the tree node.
|
||||
* @private
|
||||
*/
|
||||
this._nodeCapacity = options.nodeCapacity || 30;
|
||||
|
||||
this._pickingEnabled = options.pickingEnabled !== undefined ? options.pickingEnabled : true;
|
||||
|
||||
/**
|
||||
* Manimal tree node depth index.
|
||||
* @private
|
||||
*/
|
||||
this._minDepth = options.minDepth || 1;
|
||||
|
||||
/**
|
||||
* Stored entities.
|
||||
* @private
|
||||
*/
|
||||
this._entities = _entitiesConstructor(options.entities || []);
|
||||
|
||||
this._polylineEntityCollection = new EntityCollection({
|
||||
'pickingEnabled': this._pickingEnabled
|
||||
});
|
||||
this._bindEventsDefault(this._polylineEntityCollection);
|
||||
|
||||
this._geometryHandler = new GeometryHandler(this);
|
||||
|
||||
this._entityCollectionsTree = null;
|
||||
this._entityCollectionsTreeNorth = null;
|
||||
this._entityCollectionsTreeSouth = null;
|
||||
|
||||
this._renderingNodes = {};
|
||||
this._renderingNodesNorth = {};
|
||||
this._renderingNodesSouth = {};
|
||||
|
||||
this._counter = 0;
|
||||
this._deferredEntitiesPendingQueue = new QueueArray();
|
||||
|
||||
this._pendingsQueue = [];
|
||||
|
||||
/** Creates collections tree*/
|
||||
this.setEntities(this._entities);
|
||||
}
|
||||
|
||||
_bindPicking() {
|
||||
this._pickingColor.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds layer to the planet.
|
||||
* @public
|
||||
*/
|
||||
addTo(planet) {
|
||||
this._assignPlanet(planet);
|
||||
this._geometryHandler.assignHandler(planet.renderer.handler);
|
||||
this._polylineEntityCollection.addTo(planet, true);
|
||||
this.setEntities(this._entities);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if a layer has rasterized vector data like polygons.
|
||||
* @public
|
||||
* @virtual
|
||||
* @returns {boolean}
|
||||
*/
|
||||
hasImageryTiles() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns stored entities.
|
||||
* @public
|
||||
* @returns {Array.<og.Entity>}
|
||||
*/
|
||||
getEntities() {
|
||||
return [].concat(this._entities);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_fitExtent(entity) {
|
||||
var ee = entity.getExtent(),
|
||||
e = this._extent;
|
||||
if (ee.southWest.lon < e.southWest.lon) {
|
||||
e.southWest.lon = ee.southWest.lon;
|
||||
}
|
||||
if (ee.southWest.lat < e.southWest.lat) {
|
||||
e.southWest.lat = ee.southWest.lat;
|
||||
}
|
||||
if (ee.northEast.lon > e.northEast.lon) {
|
||||
e.northEast.lon = ee.northEast.lon;
|
||||
}
|
||||
if (ee.northEast.lat > e.northEast.lat) {
|
||||
e.northEast.lat = ee.northEast.lat;
|
||||
}
|
||||
this.setExtent(this._extent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds entity to the layer.
|
||||
* @public
|
||||
* @param {og.Entity} entity - Entity.
|
||||
* @param {boolean} [rightNow] - Entity insertion option. False is deafult.
|
||||
* @returns {og.layer.Vector} - Returns this layer.
|
||||
*/
|
||||
add(entity, rightNow) {
|
||||
if (!(entity._layer || entity._entityCollection)) {
|
||||
entity._layer = this;
|
||||
entity._layerIndex = this._entities.length;
|
||||
this._entities.push(entity);
|
||||
|
||||
|
||||
this._fitExtent(entity);
|
||||
|
||||
//
|
||||
//...pointCloud, shape, model etc.
|
||||
//
|
||||
|
||||
if (entity.polyline) {
|
||||
this._polylineEntityCollection.add(entity);
|
||||
}
|
||||
|
||||
if (entity.geometry) {
|
||||
if (this._planet) {
|
||||
this._planet.renderer.assignPickingColor(entity);
|
||||
this._geometryHandler.add(entity.geometry);
|
||||
}
|
||||
}
|
||||
|
||||
if (entity.billboard || entity.label) {
|
||||
if (this._planet) {
|
||||
if (!entity._lonlat) {
|
||||
entity._lonlat = this.layer._planet.ellipsoid.cartesianToLonLat(entity._cartesian);
|
||||
}
|
||||
|
||||
//poles trees
|
||||
if (entity._lonlat.lat > mercator.MAX_LAT) {
|
||||
this._entityCollectionsTreeNorth.insertEntity(entity, rightNow);
|
||||
} else if (entity._lonlat.lat < mercator.MIN_LAT) {
|
||||
this._entityCollectionsTreeSouth.insertEntity(entity, rightNow);
|
||||
} else {
|
||||
this._entityCollectionsTree.insertEntity(entity, rightNow);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.events.dispatch(this.events.entityadd, entity);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds entity array to the layer.
|
||||
* @public
|
||||
* @param {Array.<og.Entity>} entities - Entities array.
|
||||
* @param {boolean} [rightNow] - Entity insertion option. False is deafult.
|
||||
* @returns {og.layer.Vector} - Returns this layer.
|
||||
*/
|
||||
addEntities(entities, rightNow) {
|
||||
var i = entities.length;
|
||||
while (i--) {
|
||||
this.add(entities[i], rightNow);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove entity from layer.
|
||||
* TODO: memory leaks.
|
||||
* @public
|
||||
* @param {og.Entity} entity - Entity to remove.
|
||||
* @returns {og.layer.Vector} - Returns this layer.
|
||||
*/
|
||||
removeEntity(entity) {
|
||||
if (entity._layer && this.isEqual(entity._layer)) {
|
||||
this._entities.splice(entity._layerIndex, 1);
|
||||
this._reindexEntitiesArray(entity._layerIndex);
|
||||
entity._layer = null;
|
||||
entity._layerIndex = -1;
|
||||
|
||||
if (entity._entityCollection) {
|
||||
entity._entityCollection._removeEntitySilent(entity);
|
||||
var node = entity._nodePtr;
|
||||
while (node) {
|
||||
node.count--;
|
||||
node = node.parentNode;
|
||||
}
|
||||
if (entity._nodePtr && entity._nodePtr.count === 0 &&
|
||||
entity._nodePtr.deferredEntities.length === 0) {
|
||||
entity._nodePtr.entityCollection = null;
|
||||
//
|
||||
//...
|
||||
//
|
||||
}
|
||||
} else if (entity._nodePtr &&
|
||||
entity._nodePtr.deferredEntities.length) {
|
||||
var defEntities = entity._nodePtr.deferredEntities;
|
||||
var j = defEntities.length;
|
||||
while (j--) {
|
||||
if (defEntities[j].id === entity.id) {
|
||||
defEntities.splice(j, 1);
|
||||
var node = entity._nodePtr;
|
||||
while (node) {
|
||||
node.count--;
|
||||
node = node.parentNode;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (entity.geometry) {
|
||||
if (this._planet) {
|
||||
this._geometryHandler.remove(entity.geometry);
|
||||
this._planet.renderer.clearPickingColor(entity);
|
||||
}
|
||||
}
|
||||
|
||||
entity._nodePtr && (entity._nodePtr = null);
|
||||
this.events.dispatch(this.events.entityremove, entity);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set layer picking events active.
|
||||
* @public
|
||||
* @param {number} enable
|
||||
*/
|
||||
setPickingEnabled(enable) {
|
||||
this._pickingEnabled = enable;
|
||||
|
||||
this._polylineEntityCollection.setPickingEnabled(enable);
|
||||
|
||||
this._entityCollectionsTree.traverseTree(function (ec) {
|
||||
ec.setPickingEnabled(enable);
|
||||
});
|
||||
this._entityCollectionsTreeNorth.traverseTree(function (ec) {
|
||||
ec.setPickingEnabled(enable);
|
||||
});
|
||||
this._entityCollectionsTreeSouth.traverseTree(function (ec) {
|
||||
ec.setPickingEnabled(enable);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh collected entities indexes from startIndex entitytes collection array position.
|
||||
* @public
|
||||
* @param {number} startIndex - Entity array index.
|
||||
*/
|
||||
_reindexEntitiesArray(startIndex) {
|
||||
var e = this._entities;
|
||||
for (var i = startIndex; i < e.length; i++) {
|
||||
e[i]._layerIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes entities from layer.
|
||||
* @public
|
||||
* @param {Array.<og.Entity>} entities - Entity array.
|
||||
* @returns {og.layer.Vector} - Returns this layer.
|
||||
*/
|
||||
removeEntities(entities) {
|
||||
var i = entities.length;
|
||||
while (i--) {
|
||||
this.removeEntity(entities[i]);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets scale by distance parameters.
|
||||
* @public
|
||||
* @param {number} near - Full scale entity distance.
|
||||
* @param {number} far - Zerol scale entity distance.
|
||||
* @param {number} [farInvisible] - Entity visibility distance.
|
||||
*/
|
||||
setScaleByDistance(near, far, farInisible) {
|
||||
this.scaleByDistance[0] = near;
|
||||
this.scaleByDistance[1] = far;
|
||||
this.scaleByDistance[2] = farInisible || math.MAX32;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Clear the layer.
|
||||
* @public
|
||||
*/
|
||||
clear() {
|
||||
//TODO
|
||||
}
|
||||
|
||||
/**
|
||||
* Safety entities loop.
|
||||
* @public
|
||||
* @param {callback} callback - Entity callback.
|
||||
*/
|
||||
each(callback) {
|
||||
var e = this._entities;
|
||||
var i = e.length;
|
||||
while (i--) {
|
||||
callback(e[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes current entities from layer and adds new entities.
|
||||
* @public
|
||||
* @param {Array.<og.Entity>} entities - New entity array.
|
||||
*/
|
||||
setEntities(entities) {
|
||||
|
||||
this.clear();
|
||||
|
||||
var e = this._extent = new Extent(new LonLat(180, 90), new LonLat(-180, -90));
|
||||
|
||||
this._entities = new Array(entities.length);
|
||||
|
||||
var entitiesForTree = [];
|
||||
|
||||
for (var i = 0; i < entities.length; i++) {
|
||||
var ei = entities[i];
|
||||
|
||||
ei._layer = this;
|
||||
ei._layerIndex = i;
|
||||
|
||||
if (ei.polyline) {
|
||||
this._polylineEntityCollection.add(ei);
|
||||
} else if (ei.billboard || ei.label || ei.shape) {
|
||||
entitiesForTree.push(ei);
|
||||
}
|
||||
|
||||
if (ei.geometry) {
|
||||
if (this._planet) {
|
||||
this._planet.renderer.assignPickingColor(ei);
|
||||
this._geometryHandler.add(ei.geometry);
|
||||
}
|
||||
}
|
||||
|
||||
this._entities[i] = ei;
|
||||
|
||||
var ext = ei.getExtent();
|
||||
if (ext.northEast.lon > e.northEast.lon) e.northEast.lon = ext.northEast.lon;
|
||||
if (ext.northEast.lat > e.northEast.lat) e.northEast.lat = ext.northEast.lat;
|
||||
if (ext.southWest.lon < e.southWest.lon) e.southWest.lon = ext.southWest.lon;
|
||||
if (ext.southWest.lat < e.southWest.lat) e.southWest.lat = ext.southWest.lat;
|
||||
}
|
||||
|
||||
this._createEntityCollectionsTree(entitiesForTree);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
_createEntityCollectionsTree(entitiesForTree) {
|
||||
if (this._planet) {
|
||||
this._entityCollectionsTree = new EntityCollectionNode(this, quadTree.NW, null, 0,
|
||||
Extent.createFromArray([-20037508.34, -20037508.34, 20037508.34, 20037508.34]), this._planet, 0);
|
||||
this._entityCollectionsTreeNorth = new EntityCollectionNodeWGS84(this, quadTree.NW, null, 0,
|
||||
Extent.createFromArray([-180, mercator.MAX_LAT, 180, 90]), this._planet, 0);
|
||||
this._entityCollectionsTreeSouth = new EntityCollectionNodeWGS84(this, quadTree.NW, null, 0,
|
||||
Extent.createFromArray([-180, -90, 180, mercator.MIN_LAT]), this._planet, 0);
|
||||
|
||||
this._entityCollectionsTree.buildTree(entitiesForTree);
|
||||
this._entityCollectionsTreeNorth.buildTree(entitiesForTree);
|
||||
this._entityCollectionsTreeSouth.buildTree(entitiesForTree);
|
||||
}
|
||||
}
|
||||
|
||||
_bindEventsDefault(entityCollection) {
|
||||
var ve = this.events;
|
||||
entityCollection.events.on("entitymove", function (e) {
|
||||
ve.dispatch(ve.entitymove, e);
|
||||
});
|
||||
entityCollection.events.on("mousemove", function (e) {
|
||||
ve.dispatch(ve.mousemove, e);
|
||||
});
|
||||
entityCollection.events.on("mouseenter", function (e) {
|
||||
ve.dispatch(ve.mouseenter, e);
|
||||
});
|
||||
entityCollection.events.on("mouseleave", function (e) {
|
||||
ve.dispatch(ve.mouseleave, e);
|
||||
});
|
||||
entityCollection.events.on("lclick", function (e) {
|
||||
ve.dispatch(ve.lclick, e);
|
||||
});
|
||||
entityCollection.events.on("rclick", function (e) {
|
||||
ve.dispatch(ve.rclick, e);
|
||||
});
|
||||
entityCollection.events.on("mclick", function (e) {
|
||||
ve.dispatch(ve.mclick, e);
|
||||
});
|
||||
entityCollection.events.on("ldblclick", function (e) {
|
||||
ve.dispatch(ve.ldblclick, e);
|
||||
});
|
||||
entityCollection.events.on("rdblclick", function (e) {
|
||||
ve.dispatch(ve.rdblclick, e);
|
||||
});
|
||||
entityCollection.events.on("mdblclick", function (e) {
|
||||
ve.dispatch(ve.mdblclick, e);
|
||||
});
|
||||
entityCollection.events.on("lup", function (e) {
|
||||
ve.dispatch(ve.lup, e);
|
||||
});
|
||||
entityCollection.events.on("rup", function (e) {
|
||||
ve.dispatch(ve.rup, e);
|
||||
});
|
||||
entityCollection.events.on("mup", function (e) {
|
||||
ve.dispatch(ve.mup, e);
|
||||
});
|
||||
entityCollection.events.on("ldown", function (e) {
|
||||
ve.dispatch(ve.ldown, e);
|
||||
});
|
||||
entityCollection.events.on("rdown", function (e) {
|
||||
ve.dispatch(ve.rdown, e);
|
||||
});
|
||||
entityCollection.events.on("mdown", function (e) {
|
||||
ve.dispatch(ve.mdown, e);
|
||||
});
|
||||
entityCollection.events.on("lhold", function (e) {
|
||||
ve.dispatch(ve.lhold, e);
|
||||
});
|
||||
entityCollection.events.on("rhold", function (e) {
|
||||
ve.dispatch(ve.rhold, e);
|
||||
});
|
||||
entityCollection.events.on("mhold", function (e) {
|
||||
ve.dispatch(ve.mhold, e);
|
||||
});
|
||||
entityCollection.events.on("mousewheel", function (e) {
|
||||
ve.dispatch(ve.mousewheel, e);
|
||||
});
|
||||
entityCollection.events.on("touchmove", function (e) {
|
||||
ve.dispatch(ve.touchmove, e);
|
||||
});
|
||||
entityCollection.events.on("touchstart", function (e) {
|
||||
ve.dispatch(ve.touchstart, e);
|
||||
});
|
||||
entityCollection.events.on("touchend", function (e) {
|
||||
ve.dispatch(ve.touchend, e);
|
||||
});
|
||||
entityCollection.events.on("doubletouch", function (e) {
|
||||
ve.dispatch(ve.doubletouch, e);
|
||||
});
|
||||
entityCollection.events.on("touchleave", function (e) {
|
||||
ve.dispatch(ve.touchleave, e);
|
||||
});
|
||||
entityCollection.events.on("touchenter", function (e) {
|
||||
ve.dispatch(ve.touchenter, e);
|
||||
});
|
||||
}
|
||||
|
||||
_collectPolylineCollectionPASS(outArr) {
|
||||
outArr.push(this._polylineEntityCollection);
|
||||
if (this.clampToGround || this.relativeToGround) {
|
||||
var rtg = this.relativeToGround;
|
||||
|
||||
var nodes = this._planet._renderedNodes;
|
||||
var visibleExtent = this._planet.getViewExtent();
|
||||
var e = this._polylineEntityCollection._entities;
|
||||
var e_i = e.length;
|
||||
|
||||
while (e_i--) {
|
||||
var p = e[e_i].polyline;
|
||||
if (visibleExtent.overlaps(p._extent)) {
|
||||
var coords = p._pathLonLatMerc;
|
||||
var c_j = coords.length;
|
||||
while (c_j--) {
|
||||
var c_j_h = coords[c_j].length;
|
||||
while (c_j_h--) {
|
||||
var ll = coords[c_j][c_j_h];
|
||||
var n_k = nodes.length;
|
||||
while (n_k--) {
|
||||
var seg = nodes[n_k].segment;
|
||||
if (seg._extent.isInside(ll)) {
|
||||
var cart = p._path3v[c_j][c_j_h];
|
||||
var res = new Vec3();
|
||||
seg.getTerrainPoint(res, cart, ll);
|
||||
p.setPoint3v(res.addA(res.normal().scale(rtg && p.altitude + 1.0)), c_j_h, c_j, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
collectVisibleCollections(outArr) {
|
||||
var p = this._planet;
|
||||
|
||||
if (this.minZoom <= this._planet.maxCurrZoom && this.maxZoom >= p.maxCurrZoom) {
|
||||
|
||||
this._renderingNodes = {};
|
||||
this._renderingNodesNorth = {};
|
||||
this._renderingNodesSouth = {};
|
||||
|
||||
//Common collection first
|
||||
this._collectPolylineCollectionPASS(outArr);
|
||||
|
||||
//Merc nodes
|
||||
this._secondPASS = [];
|
||||
this._entityCollectionsTree.collectRenderCollectionsPASS1(p._visibleNodes, outArr);
|
||||
var i = this._secondPASS.length;
|
||||
while (i--) {
|
||||
this._secondPASS[i].collectRenderCollectionsPASS2(p._visibleNodes, outArr, this._secondPASS[i].nodeId);
|
||||
}
|
||||
|
||||
//North nodes
|
||||
this._secondPASS = [];
|
||||
this._entityCollectionsTreeNorth.collectRenderCollectionsPASS1(p._visibleNodesNorth, outArr);
|
||||
i = this._secondPASS.length;
|
||||
while (i--) {
|
||||
this._secondPASS[i].collectRenderCollectionsPASS2(p._visibleNodesNorth, outArr, this._secondPASS[i].nodeId);
|
||||
}
|
||||
|
||||
//South nodes
|
||||
this._secondPASS = [];
|
||||
this._entityCollectionsTreeSouth.collectRenderCollectionsPASS1(p._visibleNodesSouth, outArr);
|
||||
i = this._secondPASS.length;
|
||||
while (i--) {
|
||||
this._secondPASS[i].collectRenderCollectionsPASS2(p._visibleNodesSouth, outArr, this._secondPASS[i].nodeId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_queueDeferredNode(node) {
|
||||
if (this._visibility) {
|
||||
node._inTheQueue = true;
|
||||
if (this._counter >= 1) {
|
||||
this._deferredEntitiesPendingQueue.push(node);
|
||||
} else {
|
||||
this._execDeferredNode(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_execDeferredNode(node) {
|
||||
this._counter++;
|
||||
var that = this;
|
||||
setTimeout(function () {
|
||||
node.applyCollection();
|
||||
that._counter--;
|
||||
if (that._deferredEntitiesPendingQueue.length && that._counter < 1) {
|
||||
while (that._deferredEntitiesPendingQueue.length) {
|
||||
var n = that._deferredEntitiesPendingQueue.pop();
|
||||
n._inTheQueue = false;
|
||||
if (n.isVisible()) {
|
||||
that._execDeferredNode(node);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start to load tile material.
|
||||
* @public
|
||||
* @virtual
|
||||
* @param {og.Segment.Material} material - Current material.
|
||||
*/
|
||||
loadMaterial(material) {
|
||||
|
||||
var seg = material.segment;
|
||||
|
||||
if (this._isBaseLayer) {
|
||||
material.texture = seg._isNorth ? seg.planet.solidTextureOne : seg.planet.solidTextureTwo;
|
||||
} else {
|
||||
material.texture = seg.planet.transparentTexture;
|
||||
}
|
||||
|
||||
if (this._planet.layerLock.isFree()) {
|
||||
material.isReady = false;
|
||||
material.isLoading = true;
|
||||
this._planet._vectorTileCreator.add(material);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Abort exact material loading.
|
||||
* @public
|
||||
* @param {og.planetSegment.Material} material - Segment material.
|
||||
*/
|
||||
abortMaterialLoading(material) {
|
||||
material.isLoading = false;
|
||||
material.isReady = false;
|
||||
}
|
||||
|
||||
applyMaterial(material) {
|
||||
if (material.isReady) {
|
||||
return [0, 0, 1, 1];
|
||||
} else {
|
||||
|
||||
!material.isLoading && this.loadMaterial(material);
|
||||
|
||||
var segment = material.segment;
|
||||
var pn = segment.node,
|
||||
notEmpty = false;
|
||||
|
||||
var mId = this._id;
|
||||
var psegm = material;
|
||||
var i = 0;
|
||||
while (pn.parentNode && i < 2) {
|
||||
if (psegm && psegm.isReady) {
|
||||
notEmpty = true;
|
||||
break;
|
||||
}
|
||||
pn = pn.parentNode;
|
||||
psegm = pn.segment.materials[mId];
|
||||
}
|
||||
|
||||
if (notEmpty) {
|
||||
material.appliedNodeId = pn.nodeId;
|
||||
material.texture = psegm.texture;
|
||||
material.pickingMask = psegm.pickingMask;
|
||||
var dZ2 = 1.0 / (2 << (segment.tileZoom - pn.segment.tileZoom - 1));
|
||||
return [
|
||||
segment.tileX * dZ2 - pn.segment.tileX,
|
||||
segment.tileY * dZ2 - pn.segment.tileY,
|
||||
dZ2,
|
||||
dZ2
|
||||
];
|
||||
} else {
|
||||
if (material.textureExists && material._updateTexture) {
|
||||
material.texture = material._updateTexture;
|
||||
material.pickingMask = material._updatePickingMask;
|
||||
} else {
|
||||
material.texture = segment.planet.transparentTexture;
|
||||
material.pickingMask = segment.planet.transparentTexture;
|
||||
}
|
||||
return [0, 0, 1, 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
clearMaterial(material) {
|
||||
if (material.isReady) {
|
||||
var gl = material.segment.handler.gl;
|
||||
|
||||
material.isReady = false;
|
||||
material.pickingReady = false;
|
||||
|
||||
var t = material.texture;
|
||||
material.texture = null;
|
||||
t && !t.default && gl.deleteTexture(t);
|
||||
|
||||
t = material.pickingMask;
|
||||
material.pickingMask = null;
|
||||
t && !t.default && gl.deleteTexture(t);
|
||||
|
||||
t = material._updateTexture;
|
||||
material._updateTexture = null;
|
||||
t && !t.default && gl.deleteTexture(t);
|
||||
|
||||
t = material._updatePickingMask;
|
||||
material._updatePickingMask = null;
|
||||
t && !t.default && gl.deleteTexture(t);
|
||||
}
|
||||
|
||||
this.abortMaterialLoading(material);
|
||||
|
||||
material.isLoading = false;
|
||||
material.textureExists = false;
|
||||
}
|
||||
|
||||
update() {
|
||||
this._geometryHandler.update();
|
||||
this.collectVisibleCollections(this._planet._frustumEntityCollections);
|
||||
this.events.dispatch(this.events.draw, this);
|
||||
}
|
||||
};
|
||||
|
||||
const EVENT_NAMES = [
|
||||
/**
|
||||
* Triggered when entity has moved.
|
||||
* @event og.layer.Vector#draw
|
||||
*/
|
||||
"entitymove",
|
||||
|
||||
/**
|
||||
* Triggered when layer begin draw.
|
||||
* @event og.layer.Vector#draw
|
||||
*/
|
||||
"draw",
|
||||
|
||||
/**
|
||||
* Triggered when new entity added to the layer.
|
||||
* @event og.layer.Vector#entityadd
|
||||
*/
|
||||
"entityadd",
|
||||
|
||||
/**
|
||||
* Triggered when entity removes from the collection.
|
||||
* @event og.layer.Vector#entityremove
|
||||
*/
|
||||
"entityremove"
|
||||
];
|
||||
|
||||
export { Vector };
|
||||
171
src/og/layer/WMS.js
Normal file
171
src/og/layer/WMS.js
Normal file
@ -0,0 +1,171 @@
|
||||
/**
|
||||
* @module og/layer/WMS
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as mercator from '../mercator.js';
|
||||
import { Extent } from '../Extent.js';
|
||||
import { LonLat } from '../LonLat.js';
|
||||
import { XYZ } from './XYZ.js';
|
||||
|
||||
/**
|
||||
* Used to display WMS services as tile layers on the globe.
|
||||
* @class
|
||||
* @extends {og.layer.XYZ}
|
||||
* //TODO: WMS version, format, and custom srs cpecification.
|
||||
* @param {string} name - Layer name.
|
||||
* @param {Object} options - Options:
|
||||
* @param {number} [options.opacity=1.0] - Layer opacity.
|
||||
* @param {Array.<number,number,number>} [options.transparentColor=[-1,-1,-1]] - RGB color that defines transparent color.
|
||||
* @param {number} [options.minZoom=0] - Minimal visibility zoom level.
|
||||
* @param {number} [options.maxZoom=0] - Maximal visibility zoom level.
|
||||
* @param {string} [options.attribution] - Layer attribution that displayed in the attribution area on the screen.
|
||||
* @param {boolean} [options.isBaseLayer=false] - Base layer flag.
|
||||
* @param {boolean} [options.visibility=true] - Layer visibility.
|
||||
* @param {string} options.url - WMS url source.
|
||||
* @param {number} [options.width=256] - Tile width.
|
||||
* @param {number} [options.height=256] - Tile height.
|
||||
* @param {string} options.layers - WMS layers string.
|
||||
* @param {string} [options.version="1.1.1"] - WMS version.
|
||||
* @example:
|
||||
* new og.layer.WMS("USA States", {
|
||||
* isBaseLayer: false,
|
||||
* url: "http://openglobus.org/geoserver/",
|
||||
* layers: "topp:states",
|
||||
* opacity: 0.5,
|
||||
* zIndex: 50,
|
||||
* attribution: 'USA states - geoserver WMS example',
|
||||
* transparentColor: [1.0, 1.0, 1.0],
|
||||
* version: "1.1.1",
|
||||
* visibility: false }
|
||||
* );
|
||||
*
|
||||
* @fires og.layer.XYZ#load
|
||||
* @fires og.layer.XYZ#loadend
|
||||
*/
|
||||
class WMS extends XYZ {
|
||||
constructor(name, options) {
|
||||
super(name, options);
|
||||
|
||||
if (!options.extent) {
|
||||
this.setExtent(new Extent(new LonLat(-180.0, -90), new LonLat(180.0, 90)));
|
||||
}
|
||||
|
||||
/**
|
||||
* WMS layers string.
|
||||
* @public
|
||||
* @type {string}
|
||||
*/
|
||||
this.layers = options.layers;
|
||||
|
||||
/**
|
||||
* WMS tile width.
|
||||
* @public
|
||||
* @type {number}
|
||||
*/
|
||||
this.imageWidth = options.imageWidth || 256;
|
||||
|
||||
/**
|
||||
* WMS tile height.
|
||||
* @public
|
||||
* @type {number}
|
||||
*/
|
||||
this.imageHeight = options.imageHeight || 256;
|
||||
|
||||
this.setVersion(options.version);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Start to load tile material.
|
||||
* @public
|
||||
* @virtual
|
||||
* @param {og.planetSegment.Material} mateial
|
||||
*/
|
||||
loadMaterial(material) {
|
||||
var seg = material.segment;
|
||||
if (!material.isLoading) {
|
||||
if (this._planet.layerLock.isFree()) {
|
||||
material.isReady = false;
|
||||
material.isLoading = true;
|
||||
if (WMS.__requestsCounter >= WMS.MAX_REQUESTS && this.counter) {
|
||||
this.pendingsQueue.push(material);
|
||||
} else {
|
||||
this._exec(material);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates query url.
|
||||
* @protected
|
||||
* @virtual
|
||||
* @param {og.planetSegment.Segment}
|
||||
*/
|
||||
_createUrl(segment) {
|
||||
return this.url + "wms?" + "LAYERS=" + this.layers +
|
||||
"&FORMAT=image/jpeg&SERVICE=WMS&VERSION=" + this._version + "&REQUEST=GetMap" +
|
||||
"&SRS=" + segment._projection.code +
|
||||
"&BBOX=" + this._getBbox(segment) +
|
||||
"&WIDTH=" + this.imageWidth +
|
||||
"&HEIGHT=" + this.imageHeight;
|
||||
}
|
||||
|
||||
setVersion(version) {
|
||||
if (version) {
|
||||
this._version = version;
|
||||
} else {
|
||||
this._version = "1.1.1";
|
||||
}
|
||||
|
||||
if (this._version === "1.1.1") {
|
||||
this._getBbox = this._getBbox111;
|
||||
} else if (version === "1.3.0") {
|
||||
this._getBbox = this._getBbox130;
|
||||
} else {
|
||||
this._getBbox = this._getBbox111;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @return {string}
|
||||
*/
|
||||
_getBbox111(segment) {
|
||||
return segment._extent.getWest() + "," + segment._extent.getSouth() + "," + segment._extent.getEast() + "," + segment._extent.getNorth();
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @return {string}
|
||||
*/
|
||||
_getBbox130(segment) {
|
||||
return segment._extent.getSouth() + "," + segment._extent.getWest() + "," + segment._extent.getNorth() + "," + segment._extent.getEast();
|
||||
}
|
||||
|
||||
/**
|
||||
* @protected
|
||||
*/
|
||||
_correctFullExtent() {
|
||||
var e = this._extent,
|
||||
em = this._extentMerc;
|
||||
var ENLARGE_MERCATOR_LON = mercator.POLE + 50000;
|
||||
var ENLARGE_MERCATOR_LAT = mercator.POLE + 50000;
|
||||
if (e.northEast.lat === 90.0) {
|
||||
em.northEast.lat = ENLARGE_MERCATOR_LAT;
|
||||
}
|
||||
if (e.northEast.lon === 180.0) {
|
||||
em.northEast.lon = ENLARGE_MERCATOR_LON;
|
||||
}
|
||||
if (e.southWest.lat === -90.0) {
|
||||
em.southWest.lat = -ENLARGE_MERCATOR_LAT;
|
||||
}
|
||||
if (e.southWest.lon === -180.0) {
|
||||
em.southWest.lon = -ENLARGE_MERCATOR_LON;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export { WMS };
|
||||
412
src/og/layer/XYZ.js
Normal file
412
src/og/layer/XYZ.js
Normal file
@ -0,0 +1,412 @@
|
||||
/**
|
||||
* @module og/layer/XYZ
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as mercator from '../mercator.js';
|
||||
import * as quadTree from '../quadTree/quadTree.js';
|
||||
import { EPSG3857 } from '../proj/EPSG3857.js';
|
||||
import { Extent } from '../Extent.js';
|
||||
import { Layer } from './Layer.js';
|
||||
import { stringTemplate } from '../utils/shared.js';
|
||||
import { LonLat } from '../LonLat.js';
|
||||
import { QueueArray } from '../QueueArray.js';
|
||||
|
||||
/**
|
||||
* Represents an imagery tiles source provider.
|
||||
* @class
|
||||
* @extends {og.layer.Layer}
|
||||
* @param {string} name - Layer name.
|
||||
* @param {Object} options:
|
||||
* @param {number} [options.opacity=1.0] - Layer opacity.
|
||||
* @param {Array.<number,number,number>} [options.transparentColor=[-1,-1,-1]] - RGB color that defines transparent color.
|
||||
* @param {Array.<string>} [options.subdomains=['a','b','c']] - Subdomains of the tile service.
|
||||
* @param {number} [options.minZoom=0] - Minimal visibility zoom level.
|
||||
* @param {number} [options.maxZoom=0] - Maximal visibility zoom level.
|
||||
* @param {string} [options.attribution] - Layer attribution that displayed in the attribution area on the screen.
|
||||
* @param {boolean} [options.isBaseLayer=false] - Base layer flag.
|
||||
* @param {boolean} [options.visibility=true] - Layer visibility.
|
||||
* @param {string} [options.crossOrigin=true] - If true, all tiles will have their crossOrigin attribute set to ''.
|
||||
* @param {string} options.url - Tile url source template(see example below).
|
||||
* @param {og.layer.XYZ~_urlRewriteCallback} options.urlRewrite - Url rewrite function.
|
||||
* @fires og.layer.XYZ#load
|
||||
* @fires og.layer.XYZ#loadend
|
||||
*
|
||||
* @example <caption>Creates OpenStreetMap base tile layer</caption>
|
||||
* new og.layer.XYZ("OpenStreetMap", {
|
||||
* isBaseLayer: true,
|
||||
* url: "http://b.tile.openstreetmap.org/{z}/{x}/{y}.png",
|
||||
* visibility: true,
|
||||
* attribution: 'Data @ <a href="http://www.openstreetmap.org/">OpenStreetMap</a> contributors, <a href="http://www.openstreetmap.org/copyright">ODbL</a>'
|
||||
* });
|
||||
*/
|
||||
class XYZ extends Layer {
|
||||
constructor(name, options) {
|
||||
super(name, options);
|
||||
|
||||
this.events.registerNames(EVENT_NAMES);
|
||||
|
||||
if (!options.extent) {
|
||||
this.setExtent(new Extent(new LonLat(-180.0, mercator.MIN_LAT), new LonLat(180.0, mercator.MAX_LAT)));
|
||||
}
|
||||
|
||||
this.transparentColor = options.transparentColor || [-1, -1, -1];
|
||||
|
||||
/**
|
||||
* Tile url source template.
|
||||
* @public
|
||||
* @type {string}
|
||||
*/
|
||||
this.url = options.url || "";
|
||||
|
||||
/**
|
||||
* Current loading tiles couter.
|
||||
* @protected
|
||||
* @type {number}
|
||||
*/
|
||||
this._counter = 0;
|
||||
|
||||
/**
|
||||
* Tile pending queue that waiting for loading.
|
||||
* @protected
|
||||
* @type {Array.<og.planetSegment.Material>}
|
||||
*/
|
||||
this._pendingsQueue = new QueueArray();
|
||||
|
||||
/**
|
||||
* @protected
|
||||
*/
|
||||
this._s = options.subdomains || ['a', 'b', 'c'];
|
||||
|
||||
/**
|
||||
* @protected
|
||||
*/
|
||||
this._crossOrigin = options.crossOrigin === undefined ? '' : options.crossOrigin;
|
||||
|
||||
/**
|
||||
* Rewrites imagery tile url query.
|
||||
* @private
|
||||
* @callback og.layer.XYZ~_urlRewriteCallback
|
||||
* @param {og.planetSegment.Segment} segment - Segment to load.
|
||||
* @param {string} url - Created url.
|
||||
* @returns {string} - Url query string.
|
||||
*/
|
||||
this._urlRewriteCallback = options.urlRewrite || null;
|
||||
}
|
||||
|
||||
static get __requestsCounter() {
|
||||
return this.__reqcounter;
|
||||
}
|
||||
|
||||
static set __requestsCounter(v) {
|
||||
this.__reqcounter = v;
|
||||
}
|
||||
|
||||
/**
|
||||
* Maximum loading queries at one time.
|
||||
* @const
|
||||
* @type {number}
|
||||
*/
|
||||
static get MAX_REQUESTS() {
|
||||
return 7;
|
||||
}
|
||||
|
||||
/**
|
||||
* Abort loading tiles.
|
||||
* @public
|
||||
*/
|
||||
abortLoading() {
|
||||
var that = this;
|
||||
this._pendingsQueue.each(function (q) {
|
||||
q && that.abortMaterialLoading(q);
|
||||
});
|
||||
this._pendingsQueue.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets layer visibility.
|
||||
* @public
|
||||
* @param {boolean} visibility - Layer visibility.
|
||||
*/
|
||||
setVisibility(visibility) {
|
||||
if (visibility !== this._visibility) {
|
||||
this._visibility = visibility;
|
||||
if (this._isBaseLayer && visibility) {
|
||||
this._planet.setBaseLayer(this);
|
||||
} else if (!visibility) {
|
||||
this.abortLoading();
|
||||
}
|
||||
this._planet.updateVisibleLayers();
|
||||
this.events.dispatch(this.events.visibilitychange, this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets imagery tiles url source template.
|
||||
* @public
|
||||
* @param {string} url - Url template.
|
||||
* @example
|
||||
* http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png
|
||||
* where {z}, {x} and {y} - replaces by current tile values, {s} - random domen.
|
||||
*/
|
||||
setUrl(url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start to load tile material.
|
||||
* @public
|
||||
* @virtual
|
||||
* @param {og.planetSegment.Material} material - Loads current material.
|
||||
*/
|
||||
loadMaterial(material) {
|
||||
|
||||
var seg = material.segment;
|
||||
|
||||
if (this._isBaseLayer) {
|
||||
material.texture = seg._isNorth ? seg.planet.solidTextureOne : seg.planet.solidTextureTwo;
|
||||
} else {
|
||||
material.texture = seg.planet.transparentTexture;
|
||||
}
|
||||
|
||||
if (this._planet.layerLock.isFree()) {
|
||||
material.isReady = false;
|
||||
material.isLoading = true;
|
||||
if (material.segment._projection.id === EPSG3857.id) {
|
||||
if (XYZ.__requestsCounter >= XYZ.MAX_REQUESTS && this._counter) {
|
||||
this._pendingsQueue.push(material);
|
||||
} else {
|
||||
this._exec(material);
|
||||
}
|
||||
} else {
|
||||
material.textureNotExists();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates query url.
|
||||
* @protected
|
||||
* @virtual
|
||||
* @param {og.planetSegment.Segment} segment - Creates specific url for current segment.
|
||||
* @returns {String} - Returns url string.
|
||||
*/
|
||||
_createUrl(segment) {
|
||||
return stringTemplate(this.url, {
|
||||
"s": this._s[Math.floor(Math.random() * this._s.length)],
|
||||
"x": segment.tileX.toString(),
|
||||
"y": segment.tileY.toString(),
|
||||
"z": segment.tileZoom.toString()
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns actual url query string.
|
||||
* @protected
|
||||
* @param {og.planetSegment.Segment} segment - Segment that loads image data.
|
||||
* @returns {string} - Url string.
|
||||
*/
|
||||
_getHTTPRequestString(segment) {
|
||||
var url = this._createUrl(segment);
|
||||
return this._urlRewriteCallback ? this._urlRewriteCallback(segment, url) : url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets url rewrite callback, used for custom url rewriting for every tile laoding.
|
||||
* @public
|
||||
* @param {og.layer.XYZ~_urlRewriteCallback} ur - The callback that returns tile custom created url.
|
||||
*/
|
||||
setUrlRewriteCallback(ur) {
|
||||
this._urlRewriteCallback = ur;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads material image and apply it to the planet segment.
|
||||
* @protected
|
||||
* @param {og.planetSegment.Material} material - Loads material image.
|
||||
*/
|
||||
_exec(material) {
|
||||
XYZ.__requestsCounter++;
|
||||
this._counter++;
|
||||
|
||||
material.image = new Image();
|
||||
material.image.crossOrigin = this._crossOrigin;
|
||||
|
||||
var that = this;
|
||||
material.image.onload = function (evt) {
|
||||
that._counter--;
|
||||
XYZ.__requestsCounter--;
|
||||
|
||||
if (material.isLoading) {
|
||||
var e = that.events.load;
|
||||
if (e.handlers.length) {
|
||||
that.events.dispatch(e, material);
|
||||
}
|
||||
this.onerror = null;
|
||||
this.onload = null;
|
||||
material.applyImage(this);
|
||||
}
|
||||
that._dequeueRequest();
|
||||
};
|
||||
|
||||
material.image.onerror = function (evt) {
|
||||
that._counter--;
|
||||
XYZ.__requestsCounter--;
|
||||
this.onerror = null;
|
||||
this.onload = null;
|
||||
if (material.isLoading && material.image) {
|
||||
material.textureNotExists.call(material);
|
||||
}
|
||||
that._dequeueRequest();
|
||||
};
|
||||
|
||||
material.image.src = this._getHTTPRequestString(material.segment);
|
||||
}
|
||||
|
||||
/**
|
||||
* Abort exact material loading.
|
||||
* @public
|
||||
* @param {og.planetSegment.Material} material - Segment material.
|
||||
*/
|
||||
abortMaterialLoading(material) {
|
||||
if (material.isLoading && material.image) {
|
||||
material.image.src = "";
|
||||
material.image.__og_canceled = true;
|
||||
material.image = null;
|
||||
} else {
|
||||
this._dequeueRequest();
|
||||
}
|
||||
}
|
||||
|
||||
_dequeueRequest() {
|
||||
if (this._pendingsQueue.length) {
|
||||
if (XYZ.__requestsCounter < XYZ.MAX_REQUESTS) {
|
||||
var pmat = this._whilePendings();
|
||||
if (pmat)
|
||||
this._exec.call(this, pmat);
|
||||
}
|
||||
} else if (this._counter === 0) {
|
||||
this.events.dispatch(this.events.loadend);
|
||||
}
|
||||
}
|
||||
|
||||
_whilePendings() {
|
||||
while (this._pendingsQueue.length) {
|
||||
var pmat = this._pendingsQueue.pop();
|
||||
if (pmat.segment.node) {
|
||||
if (pmat.segment.ready && pmat.segment.node.getState() === quadTree.RENDERING) {
|
||||
return pmat;
|
||||
}
|
||||
pmat.isLoading = false;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
applyMaterial(material) {
|
||||
if (material.isReady) {
|
||||
return [0, 0, 1, 1];
|
||||
} else {
|
||||
|
||||
!material.isLoading && this.loadMaterial(material);
|
||||
|
||||
var segment = material.segment;
|
||||
var pn = segment.node,
|
||||
notEmpty = false;
|
||||
|
||||
var mId = this._id;
|
||||
var psegm = material;
|
||||
while (pn.parentNode) {
|
||||
if (psegm && psegm.isReady) {
|
||||
notEmpty = true;
|
||||
break;
|
||||
}
|
||||
pn = pn.parentNode;
|
||||
psegm = pn.segment.materials[mId];
|
||||
}
|
||||
|
||||
if (notEmpty) {
|
||||
material.appliedNodeId = pn.nodeId;
|
||||
material.texture = psegm.texture;
|
||||
var dZ2 = 1.0 / (2 << (segment.tileZoom - pn.segment.tileZoom - 1));
|
||||
return [
|
||||
segment.tileX * dZ2 - pn.segment.tileX,
|
||||
segment.tileY * dZ2 - pn.segment.tileY,
|
||||
dZ2,
|
||||
dZ2
|
||||
];
|
||||
} else {
|
||||
material.texture = segment.planet.transparentTexture;
|
||||
return [0, 0, 1, 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
clearMaterial(material) {
|
||||
if (material.isReady) {
|
||||
material.isReady = false;
|
||||
!material.texture.default &&
|
||||
material.segment.handler.gl.deleteTexture(material.texture);
|
||||
material.texture = null;
|
||||
} else {
|
||||
this.abortMaterialLoading(material);
|
||||
}
|
||||
|
||||
material.textureExists = false;
|
||||
|
||||
if (material.image) {
|
||||
material.image.onload = null;
|
||||
material.image.onerror = null;
|
||||
material.image.src = '';
|
||||
material.image = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @protected
|
||||
*/
|
||||
_correctFullExtent() {
|
||||
var e = this._extent,
|
||||
em = this._extentMerc;
|
||||
var ENLARGE_MERCATOR_LON = mercator.POLE + 50000;
|
||||
var ENLARGE_MERCATOR_LAT = mercator.POLE + 50000;
|
||||
if (e.northEast.lat === 90.0) {
|
||||
em.northEast.lat = ENLARGE_MERCATOR_LAT;
|
||||
}
|
||||
if (e.northEast.lon === 180.0) {
|
||||
em.northEast.lon = ENLARGE_MERCATOR_LON;
|
||||
}
|
||||
if (e.southWest.lat === -90.0) {
|
||||
em.southWest.lat = -ENLARGE_MERCATOR_LAT;
|
||||
}
|
||||
if (e.southWest.lon === -180.0) {
|
||||
em.southWest.lon = -ENLARGE_MERCATOR_LON;
|
||||
}
|
||||
|
||||
if (e.northEast.lat >= mercator.MAX_LAT) {
|
||||
e.northEast.lat = mercator.MAX_LAT;
|
||||
}
|
||||
|
||||
if (e.northEast.lat <= mercator.MIN_LAT) {
|
||||
e.northEast.lat = mercator.MIN_LAT;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
const EVENT_NAMES = [
|
||||
/**
|
||||
* Triggered when current tile image has loaded before rendereing.
|
||||
* @event og.layer.XYZ#load
|
||||
*/
|
||||
"load",
|
||||
|
||||
/**
|
||||
* Triggered when all tiles have loaded or loading has stopped.
|
||||
* @event og.layer.XYZ#loadend
|
||||
*/
|
||||
"loadend"
|
||||
];
|
||||
|
||||
export { XYZ };
|
||||
316
src/og/light/LightSource.js
Normal file
316
src/og/light/LightSource.js
Normal file
@ -0,0 +1,316 @@
|
||||
/**
|
||||
* @module og/light/LightSource
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { Vec3 } from '../math/Vec3.js';
|
||||
|
||||
/**
|
||||
* Represents basic light source.
|
||||
* @class
|
||||
* @param {string} [name] - Light source name.
|
||||
* @param {Object} [params] - Light parameters:
|
||||
* @param {og.math.Vector3} [params.position] - Light source position if it is a point light, otherwise it is a light direction vector.
|
||||
* @param {og.math.Vector3} [params.ambient] - Ambient RGB color.
|
||||
* @param {og.math.Vector3} [params.diffuse] - Diffuse RGB color.
|
||||
* @param {og.math.Vector3} [params.specular] - Specular RGB color.
|
||||
* @param {number} [params.shininess] - Specular shininess.
|
||||
*/
|
||||
class LightSource {
|
||||
static get _staticCounter() {
|
||||
if (!this._counter && this._counter !== 0) {
|
||||
this._counter = 0;
|
||||
}
|
||||
return this._counter;
|
||||
}
|
||||
|
||||
static set _staticCounter(n) {
|
||||
this._counter = n;
|
||||
}
|
||||
|
||||
constructor(name, params) {
|
||||
params = params || {};
|
||||
|
||||
/**
|
||||
* Light name.
|
||||
* @protected
|
||||
* @type {string}
|
||||
*/
|
||||
this._name = name || ("light_" + LightSource._staticCounter++);
|
||||
|
||||
/**
|
||||
* Render node where light is shines.
|
||||
* @protected
|
||||
* @type {og.scene.RenderNode}
|
||||
*/
|
||||
this._renderNode = null;
|
||||
|
||||
/**
|
||||
* Light position.
|
||||
* @protected
|
||||
* @type {og.math.Vector3}
|
||||
*/
|
||||
this._position = params.position || new Vec3();
|
||||
|
||||
/**
|
||||
* True if the light is directional.
|
||||
* @public
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.directional = params.derectional != undefined ? params.derectional : true;
|
||||
|
||||
/**
|
||||
* Ambient color.
|
||||
* @protected
|
||||
* @type {og.math.Vector3}
|
||||
*/
|
||||
this._ambient = params.ambient || new Vec3();
|
||||
|
||||
/**
|
||||
* Diffuse color.
|
||||
* @protected
|
||||
* @type {og.math.Vector3}
|
||||
*/
|
||||
this._diffuse = params.diffuse || new Vec3(0.8, 0.8, 0.8);
|
||||
|
||||
/**
|
||||
* Specular color.
|
||||
* @protected
|
||||
* @type {og.math.Vector3}
|
||||
*/
|
||||
this._specular = params.specular || new Vec3(0.18, 0.18, 0.18);
|
||||
|
||||
/**
|
||||
* Shininess.
|
||||
* @protected
|
||||
* @type {number}
|
||||
*/
|
||||
this._shininess = params.shininess != undefined ? params.shininess : 3.3;
|
||||
|
||||
/**
|
||||
* Light activity.
|
||||
* @protected
|
||||
* @type {boolean}
|
||||
*/
|
||||
this._active = true;
|
||||
|
||||
this._tempAmbient = this._ambient.clone();
|
||||
this._tempDiffuse = this._diffuse.clone();
|
||||
this._tempSpecular = this._specular.clone();
|
||||
this._tempShininess = this._shininess;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates clone of the current light object.
|
||||
* @todo: TODO
|
||||
* @public
|
||||
* @returns {og.LightSource}
|
||||
*/
|
||||
clone() {
|
||||
//TODO
|
||||
}
|
||||
|
||||
/**
|
||||
* Set light activity. If activity is false the light doesn't shine.
|
||||
* @public
|
||||
* @param {boolean} active - Light activity.
|
||||
*/
|
||||
setActive(active) {
|
||||
if (active && !this._active) {
|
||||
var rn = this._renderNode;
|
||||
if (rn) {
|
||||
var index = rn._lightsNames.indexOf(this._name);
|
||||
this._shininess = rn._lightsParamsf[index] = this._tempShininess;
|
||||
if (index != -1) {
|
||||
index *= 9;
|
||||
this._ambient.x = rn._lightsParamsv[index] = this._tempAmbient.x;
|
||||
this._ambient.y = rn._lightsParamsv[index + 1] = this._tempAmbient.y;
|
||||
this._ambient.z = rn._lightsParamsv[index + 2] = this._tempAmbient.z;
|
||||
this._diffuse.x = rn._lightsParamsv[index + 3] = this._tempDiffuse.x;
|
||||
this._diffuse.y = rn._lightsParamsv[index + 4] = this._tempDiffuse.y;
|
||||
this._diffuse.z = rn._lightsParamsv[index + 5] = this._tempDiffuse.z;
|
||||
this._specular.x = rn._lightsParamsv[index + 6] = this._tempSpecular.x;
|
||||
this._specular.y = rn._lightsParamsv[index + 7] = this._tempSpecular.y;
|
||||
this._specular.z = rn._lightsParamsv[index + 8] = this._tempSpecular.z;
|
||||
}
|
||||
}
|
||||
this._active = true;
|
||||
} else if (!active && this._active) {
|
||||
this._tempAmbient = this._ambient.clone();
|
||||
this._tempDiffuse = this._diffuse.clone();
|
||||
this._tempSpecular = this._specular.clone();
|
||||
this._tempShininess = this._shininess;
|
||||
this.setBlack();
|
||||
this._active = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets light activity.
|
||||
* @public
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isActive() {
|
||||
return this._active;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set light source position, or if it is a directional type sets light direction vector.
|
||||
* @public
|
||||
* @param {og.math.Vector3} position - Light position or direction vector.
|
||||
* @returns {og.LightSource}
|
||||
*/
|
||||
setPosition(position) {
|
||||
this._position.x = position.x;
|
||||
this._position.y = position.y;
|
||||
this._position.z = position.z;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns light source position, or if it is a directional type sets light direction vector.
|
||||
* @public
|
||||
* @returns {og.math.Vector3} - Light source position/direction.
|
||||
*/
|
||||
getPosition() {
|
||||
return this._position.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set ambient color.
|
||||
* @public
|
||||
* @param {og.math.Vector3} rgb - Ambient color.
|
||||
* @returns {og.LightSource}
|
||||
*/
|
||||
setAmbient(rgb) {
|
||||
this._ambient = rgb;
|
||||
var rn = this._renderNode;
|
||||
if (rn) {
|
||||
var index = 9 * rn._lightsNames.indexOf(this._name);
|
||||
if (index != -1) {
|
||||
rn._lightsParamsv[index] = rgb.x;
|
||||
rn._lightsParamsv[index + 1] = rgb.y;
|
||||
rn._lightsParamsv[index + 2] = rgb.z;
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set diffuse color.
|
||||
* @public
|
||||
* @param {og.math.Vector3} rgb - Diffuse color.
|
||||
* @returns {og.LightSource}
|
||||
*/
|
||||
setDiffuse(rgb) {
|
||||
this._diffuse = rgb;
|
||||
var rn = this._renderNode;
|
||||
if (rn) {
|
||||
var index = 9 * rn._lightsNames.indexOf(this._name);
|
||||
if (index != -1) {
|
||||
rn._lightsParamsv[index + 3] = rgb.x;
|
||||
rn._lightsParamsv[index + 4] = rgb.y;
|
||||
rn._lightsParamsv[index + 5] = rgb.z;
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set specular color.
|
||||
* @public
|
||||
* @param {og.math.Vector3} rgb - Specular color.
|
||||
* @returns {og.LightSource}
|
||||
*/
|
||||
setSpecular(rgb) {
|
||||
this._specular = rgb;
|
||||
var rn = this._renderNode;
|
||||
if (rn) {
|
||||
var index = 9 * rn._lightsNames.indexOf(this._name);
|
||||
if (index != -1) {
|
||||
rn._lightsParamsv[index + 6] = rgb.x;
|
||||
rn._lightsParamsv[index + 7] = rgb.y;
|
||||
rn._lightsParamsv[index + 8] = rgb.z;
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set material shininess.
|
||||
* @public
|
||||
* @param {number} shininess - Material shininess.
|
||||
* @returns {og.LightSource}
|
||||
*/
|
||||
setShininess(shininess) {
|
||||
this._shininess = shininess;
|
||||
var rn = this._renderNode;
|
||||
if (rn) {
|
||||
var index = rn._lightsNames.indexOf(this._name);
|
||||
if (index != -1) {
|
||||
rn._lightsParamsf[index] = shininess;
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets light to black.
|
||||
* @public
|
||||
* @returns {og.LightSource}
|
||||
*/
|
||||
setBlack() {
|
||||
this._ambient.clear();
|
||||
this._diffuse.clear();
|
||||
this._specular.clear();
|
||||
this._shininess = 0;
|
||||
var rn = this._renderNode;
|
||||
if (rn) {
|
||||
var index = 9 * rn._lightsNames.indexOf(this._name);
|
||||
if (index != -1) {
|
||||
rn._lightsParamsv[index] = rn._lightsParamsv[index + 1] = rn._lightsParamsv[index + 2] =
|
||||
rn._lightsParamsv[index + 3] = rn._lightsParamsv[index + 4] = rn._lightsParamsv[index + 5] =
|
||||
rn._lightsParamsv[index + 6] = rn._lightsParamsv[index + 7] = rn._lightsParamsv[index + 8] = 0;
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds current light to the render node scene.
|
||||
* @public
|
||||
* @param {og.scene.RenderNode} renderNode - Render node scene.
|
||||
* @returns {og.LightSource}
|
||||
*/
|
||||
addTo(renderNode) {
|
||||
this._renderNode = renderNode;
|
||||
renderNode._lights.push(this);
|
||||
renderNode._lightsNames.push(this._name);
|
||||
renderNode._lightsParamsf.push(this._shininess);
|
||||
renderNode._lightsParamsv.push.apply(renderNode._lightsParamsv, this._ambient.toVec());
|
||||
renderNode._lightsParamsv.push.apply(renderNode._lightsParamsv, this._diffuse.toVec());
|
||||
renderNode._lightsParamsv.push.apply(renderNode._lightsParamsv, this._specular.toVec());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes from render node scene.
|
||||
* @public
|
||||
*/
|
||||
remove() {
|
||||
var rn = this.renderNode;
|
||||
if (rn) {
|
||||
var li = rn.getLightById(this._name);
|
||||
if (li != -1) {
|
||||
rn._lights.splice(li, 1);
|
||||
rn._lightsNames.splice(li, 1);
|
||||
rn._lightsParamsf.splice(li, 1);
|
||||
rn._lightsParamsv.splice(li, 9);
|
||||
}
|
||||
}
|
||||
this._renderNode = null;
|
||||
}
|
||||
};
|
||||
|
||||
export { LightSource };
|
||||
32
src/og/math/Line2.js
Normal file
32
src/og/math/Line2.js
Normal file
@ -0,0 +1,32 @@
|
||||
/**
|
||||
* @module og/math/Line2
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { Vec2 } from './Vec2.js';
|
||||
|
||||
const Line2 = function (a, b, c) {
|
||||
this.a = a || 0;
|
||||
this.b = b || 0;
|
||||
this.c = c || 0;
|
||||
};
|
||||
|
||||
Line2.get = function (p0, p1) {
|
||||
return new Line2(p1.y - p0.y, p0.x - p1.x, p1.x * p0.y - p0.x * p1.y);
|
||||
};
|
||||
|
||||
Line2.getParallel = function (l, p) {
|
||||
return new Line2(l.a, l.b, -l.a * p.x - l.b * p.y);
|
||||
};
|
||||
|
||||
Line2.getIntersection = function (L0, L1) {
|
||||
var x = (L1.b * L0.c - L0.b * L1.c) / (L0.b * L1.a - L1.b * L0.a);
|
||||
return new Vec2(x, -(L0.c + L0.a * x) / L0.b);
|
||||
};
|
||||
|
||||
Line2.prototype.intersects = function (l) {
|
||||
return Line2.getIntersection(this, l);
|
||||
};
|
||||
|
||||
export { Line2 };
|
||||
66
src/og/math/Line3.js
Normal file
66
src/og/math/Line3.js
Normal file
@ -0,0 +1,66 @@
|
||||
/**
|
||||
* @module og/math/Line3
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { Vec3 } from './Vec3.js';
|
||||
|
||||
const Line3 = function (p0, p1) {
|
||||
this.p0 = (p0 ? p0.clone() : new Vec3());
|
||||
this.p1 = (p1 ? p1.clone() : new Vec3());
|
||||
};
|
||||
|
||||
Line3.prototype.getSphereIntersection = function (sphere) {
|
||||
var p0 = this.p0,
|
||||
p1 = this.p1;
|
||||
|
||||
var cx = sphere.x,
|
||||
cy = sphere.y,
|
||||
cz = sphere.z;
|
||||
|
||||
var px = p0.x,
|
||||
py = p0.y,
|
||||
pz = p0.z;
|
||||
|
||||
var vx = p1.x - px,
|
||||
vy = p1.y - py,
|
||||
vz = p1.z - pz;
|
||||
|
||||
var A = vx * vx + vy * vy + vz * vz,
|
||||
B = 2.0 * (px * vx + py * vy + pz * vz - vx * cx - vy * cy - vz * cz),
|
||||
C = px * px - 2 * px * cx + cx * cx + py * py - 2 * py * cy + cy * cy +
|
||||
pz * pz - 2 * pz * cz + cz * cz - sphere.radius * sphere.radius;
|
||||
var D = B * B - 4 * A * C;
|
||||
|
||||
if (D < 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var t1 = (-B - Math.Sqrt(D)) / (2.0 * A);
|
||||
|
||||
var solution1 = new Vec3(
|
||||
p0.x * (1 - t1) + t1 * p1.x,
|
||||
p0.y * (1 - t1) + t1 * p1.y,
|
||||
p0.z * (1 - t1) + t1 * p1.z);
|
||||
|
||||
if (D == 0) {
|
||||
return solution1;
|
||||
}
|
||||
|
||||
var t2 = (-B + Math.Sqrt(D)) / (2.0 * A);
|
||||
var solution2 = new Vec3(
|
||||
p0.x * (1 - t2) + t2 * p1.x,
|
||||
p0.y * (1 - t2) + t2 * p1.y,
|
||||
p0.z * (1 - t2) + t2 * p1.z);
|
||||
|
||||
// prefer a solution that's on the line segment itself
|
||||
if (Math.Abs(t1 - 0.5) < Math.abs(t2 - 0.5)) {
|
||||
//return new Point3D[] { solution1, solution2 };
|
||||
return [solution1, solution2];
|
||||
}
|
||||
|
||||
return [solution2, solution1];
|
||||
};
|
||||
|
||||
export { Line3 };
|
||||
88
src/og/math/Plane.js
Normal file
88
src/og/math/Plane.js
Normal file
@ -0,0 +1,88 @@
|
||||
/**
|
||||
* @module og/math/Plane
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as math from '../math.js';
|
||||
import { Vec3 } from './Vec3.js';
|
||||
|
||||
class Plane {
|
||||
constructor(p, n) {
|
||||
this.p = (p ? p.clone() : new Vec3());
|
||||
this.n = (n ? n.clone() : new Vec3());
|
||||
}
|
||||
|
||||
set(p, n) {
|
||||
this.p.copy(p);
|
||||
this.n.copy(n);
|
||||
}
|
||||
|
||||
getNormal() {
|
||||
return this.n;
|
||||
}
|
||||
|
||||
getIntersection(Pn1, Pn2, L) {
|
||||
|
||||
var u = Pn1.n.cross(Pn2.n);
|
||||
|
||||
var ax = (u.x >= 0 ? u.x : -u.x);
|
||||
var ay = (u.y >= 0 ? u.y : -u.y);
|
||||
var az = (u.z >= 0 ? u.z : -u.z);
|
||||
|
||||
// test if the two planes are parallel
|
||||
if ((ax + ay + az) < math.EPSILON5) { // Pn1 and Pn2 are near parallel
|
||||
// test if disjoint or coincide
|
||||
var v = Pn2.p.sub(Pn1.p);
|
||||
if (Pn1.n.dot(v) == 0) // Pn2.V0 lies in Pn1
|
||||
return 1; // Pn1 and Pn2 coincide
|
||||
else
|
||||
return 0; // Pn1 and Pn2 are disjoint
|
||||
}
|
||||
|
||||
// Pn1 and Pn2 intersect in a line
|
||||
// first determine max abs coordinate of cross product
|
||||
var maxc; // max coordinate
|
||||
if (ax > ay) {
|
||||
if (ax > az)
|
||||
maxc = 1;
|
||||
else maxc = 3;
|
||||
}
|
||||
else {
|
||||
if (ay > az)
|
||||
maxc = 2;
|
||||
else maxc = 3;
|
||||
}
|
||||
|
||||
// next, to get a point on the intersect line
|
||||
// zero the max coord, and solve for the other two
|
||||
var iP = new Vec3(); // intersect point
|
||||
|
||||
var d1, d2; // the constants in the 2 plane equations
|
||||
d1 = -Pn1.n.dot(Pn1.p); // note: could be pre-stored with plane
|
||||
d2 = -Pn2.n.dot(Pn2.p); // ditto
|
||||
|
||||
// select max coordinate
|
||||
if (maxc === 1) {
|
||||
// intersect with x=0
|
||||
iP.x = 0;
|
||||
iP.y = (d2 * Pn1.n.z - d1 * Pn2.n.z) / u.x;
|
||||
iP.z = (d1 * Pn2.n.y - d2 * Pn1.n.y) / u.x;
|
||||
} else if (maxc === 2) {
|
||||
// intersect with y=0
|
||||
iP.x = (d1 * Pn2.n.z - d2 * Pn1.n.z) / u.y;
|
||||
iP.y = 0;
|
||||
iP.z = (d2 * Pn1.n.x - d1 * Pn2.n.x) / u.y;
|
||||
} else if (maxc === 3) {
|
||||
// intersect with z=0
|
||||
iP.x = (d2 * Pn1.n.y - d1 * Pn2.n.y) / u.z;
|
||||
iP.y = (d1 * Pn2.n.x - d2 * Pn1.n.x) / u.z;
|
||||
iP.z = 0;
|
||||
}
|
||||
L.p0.copy(iP);
|
||||
L.p1.copy(iP.add(u));
|
||||
return 2;
|
||||
}
|
||||
};
|
||||
|
||||
export { Plane };
|
||||
230
src/og/math/Ray.js
Normal file
230
src/og/math/Ray.js
Normal file
@ -0,0 +1,230 @@
|
||||
/**
|
||||
* @module og/math/Ray
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as math from '../math.js';
|
||||
import { Vec3 } from './Vec3.js';
|
||||
|
||||
|
||||
/**
|
||||
* Represents a ray that extends infinitely from the provided origin in the provided direction.
|
||||
* @class
|
||||
* @param {og.math.Vec3} origin - The origin of the ray.
|
||||
* @param {og.math.Vec3} direction - The direction of the ray.
|
||||
*/
|
||||
const Ray = function (origin, direction) {
|
||||
|
||||
/**
|
||||
* The origin of the ray.
|
||||
* @public
|
||||
* @type {og.math.Vec3}
|
||||
*/
|
||||
this.origin = origin.clone();
|
||||
|
||||
/**
|
||||
* The direction of the ray.
|
||||
* @public
|
||||
* @type {og.math.Vec3}
|
||||
*/
|
||||
this.direction = direction.clone();
|
||||
};
|
||||
|
||||
/**
|
||||
* Ray object creator.
|
||||
* @function
|
||||
* @param {og.math.Vec3} origin - The origin of the ray.
|
||||
* @param {og.math.Vec3} direction - The direction of the ray.
|
||||
* @returns {og.math.Ray}
|
||||
*/
|
||||
export function ray(origin, direction) {
|
||||
return new Ray(origin, direction);
|
||||
};
|
||||
|
||||
/** @const */
|
||||
Ray.OUTSIDE = 0;
|
||||
/** @const */
|
||||
Ray.INSIDE = 1;
|
||||
/** @const */
|
||||
Ray.INPLANE = 2;
|
||||
/** @const */
|
||||
Ray.AWAY = 3;
|
||||
|
||||
/**
|
||||
* Sets a ray parameters.
|
||||
* @public
|
||||
* @param {og.math.Vec3} origin - The origin of the ray.
|
||||
* @param {og.math.Vec3} direction - The direction of the ray.
|
||||
* @returns {og.math.Ray}
|
||||
*/
|
||||
Ray.prototype.set = function (origin, direction) {
|
||||
this.origin = origin.clone();
|
||||
this.direction = direction.clone();
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Computes the point along the ray on the distance.
|
||||
* @public
|
||||
* @param {number} distance - Point distance.
|
||||
* @returns {og.math.Vec3}
|
||||
*/
|
||||
Ray.prototype.getPoint = function (distance) {
|
||||
return Vec3.add(this.origin, this.direction.scaleTo(distance));
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns ray hit a triange result.
|
||||
* @public
|
||||
* @param {og.math.Vec3} v0 - First triangle corner coordinate.
|
||||
* @param {og.math.Vec3} v1 - Second triangle corner coordinate.
|
||||
* @param {og.math.Vec3} v2 - Third triangle corner coordinate.
|
||||
* @param {og.math.Vec3} res - Hit point object pointer that stores hit result.
|
||||
* @returns {number} - Hit code, could 0 - og.math.Ray.OUTSIDE, 1 - og.math.Ray.INSIDE,
|
||||
* 2 - og.math.Ray.INPLANE and 3 - og.math.Ray.AWAY(ray goes away from triangle).
|
||||
*/
|
||||
Ray.prototype.hitTriangle = function (v0, v1, v2, res) {
|
||||
var state;
|
||||
var u = v1.sub(v0);
|
||||
var v = v2.sub(v0);
|
||||
var n = u.cross(v);
|
||||
|
||||
var w0 = this.origin.sub(v0);
|
||||
var a = -n.dot(w0);
|
||||
var b = n.dot(this.direction);
|
||||
|
||||
// ray is parallel to triangle plane
|
||||
if (Math.abs(b) < math.EPSILON10) {
|
||||
if (a === 0) {
|
||||
res.copy(this.origin);
|
||||
// ray lies in triangle plane
|
||||
return Ray.INPLANE;
|
||||
} else {
|
||||
// ray disjoint from plane
|
||||
return Ray.OUTSIDE;
|
||||
}
|
||||
}
|
||||
|
||||
var r = a / b;
|
||||
|
||||
// intersect point of ray and plane
|
||||
res.copy(this.origin.add(this.direction.scaleTo(r)));
|
||||
|
||||
// ray goes away from triangle
|
||||
if (r < 0.0)
|
||||
return Ray.AWAY;
|
||||
|
||||
// is res point inside the triangle?
|
||||
var uu = u.dot(u);
|
||||
var uv = u.dot(v);
|
||||
var vv = v.dot(v);
|
||||
var w = res.sub(v0);
|
||||
var wu = w.dot(u);
|
||||
var wv = w.dot(v);
|
||||
var D = uv * uv - uu * vv;
|
||||
|
||||
var s = (uv * wv - vv * wu) / D;
|
||||
if (s < 0.0 || s > 1.0)
|
||||
return Ray.OUTSIDE;
|
||||
|
||||
var t = (uv * wu - uu * wv) / D;
|
||||
if (t < 0.0 || (s + t) > 1.0)
|
||||
return Ray.OUTSIDE;
|
||||
|
||||
return Ray.INSIDE;
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets a ray hit a plane result. If the ray cross the plane returns 1 - og.math.Ray.INSIDE otherwise returns 0 - og.math.Ray.OUTSIDE.
|
||||
* @public
|
||||
* @param {og.math.Vec3} v0 - First plane point.
|
||||
* @param {og.math.Vec3} v1 - Second plane point.
|
||||
* @param {og.math.Vec3} v2 - Third plane point.
|
||||
* @param {og.math.Vec3} res - Hit point object pointer that stores hit result.
|
||||
* @returns {number}
|
||||
*/
|
||||
Ray.prototype.hitPlane = function (v0, v1, v2, res) {
|
||||
var u = Vec3.sub(v1, v0);
|
||||
var v = Vec3.sub(v2, v0);
|
||||
var n = u.cross(v);
|
||||
|
||||
var w0 = Vec3.sub(this.origin, v0);
|
||||
var a = -n.dot(w0);
|
||||
var b = n.dot(this.direction);
|
||||
|
||||
// ray is parallel to the plane
|
||||
if (Math.abs(b) < math.EPSILON10) {
|
||||
if (a === 0) {
|
||||
return Ray.OUTSIDE;
|
||||
}
|
||||
}
|
||||
|
||||
var r = a / b;
|
||||
|
||||
if (r < 0) {
|
||||
return Ray.OUTSIDE;
|
||||
}
|
||||
|
||||
var d = this.direction.scaleTo(r);
|
||||
|
||||
// intersect point of ray and plane
|
||||
res.x = this.origin.x + d.x;
|
||||
res.y = this.origin.y + d.y;
|
||||
res.z = this.origin.z + d.z;
|
||||
|
||||
return Ray.INSIDE;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a ray hit sphere coordiante. If there isn't hit returns null.
|
||||
* @public
|
||||
* @param {og.bv.Sphere} sphere - Sphere object.
|
||||
* @returns {og.math.Vec3}
|
||||
*/
|
||||
Ray.prototype.hitSphere = function (sphere) {
|
||||
var r = sphere.radius,
|
||||
c = sphere.center,
|
||||
o = this.origin,
|
||||
d = this.direction;
|
||||
var vpc = Vec3.sub(c, o);
|
||||
if (vpc.dot(d) < 0) {
|
||||
var l = vpc.length();
|
||||
if (l > r) {
|
||||
return null;
|
||||
} else if (l === r) {
|
||||
return o.clone();
|
||||
}
|
||||
var pc = c.projToRay(o, vpc);
|
||||
var lc = Vec3.sub(pc, c).length();
|
||||
var dist = Math.sqrt(r * r - lc * lc);
|
||||
var di1 = dist - Vec3.sub(pc, o).length();
|
||||
var intersection = Vec3.add(o, d.scaleTo(di1));
|
||||
return intersection;
|
||||
} else {
|
||||
var pc = c.projToRay(o, d);
|
||||
var cpcl = Vec3.sub(c, pc).length();
|
||||
if (cpcl > sphere.radius) {
|
||||
return null;
|
||||
} else {
|
||||
var dist = Math.sqrt(r * r - cpcl * cpcl);
|
||||
var di1;
|
||||
pc.subA(o);
|
||||
if (vpc.length() > r) {
|
||||
di1 = pc.length() - dist;
|
||||
} else {
|
||||
di1 = pc.length() + dist;
|
||||
}
|
||||
var intersection = Vec3.add(o, d.scaleTo(di1));
|
||||
return intersection;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ray.prototype.hitBox = function (box) {
|
||||
//
|
||||
//TODO
|
||||
//
|
||||
};
|
||||
|
||||
export { Ray };
|
||||
13
src/og/proj/EPSG3857.js
Normal file
13
src/og/proj/EPSG3857.js
Normal file
@ -0,0 +1,13 @@
|
||||
/**
|
||||
* @module og/proj/EPSG3857
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { Units, Proj } from './Proj.js';
|
||||
|
||||
/**
|
||||
* EPSG:3857 projection object.
|
||||
* @type {og.proj.Projection}
|
||||
*/
|
||||
export const EPSG3857 = new Proj({ code: "epsg:3857", units: Units.METERS });
|
||||
13
src/og/proj/EPSG4326.js
Normal file
13
src/og/proj/EPSG4326.js
Normal file
@ -0,0 +1,13 @@
|
||||
/**
|
||||
* @module og/proj/EPSG4326
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { Units, Proj } from './Proj.js';
|
||||
|
||||
/**
|
||||
* EPSG:4326 projection object.
|
||||
* @type {og.proj.Proj}
|
||||
*/
|
||||
export const EPSG4326 = new Proj({ code: "epsg:4326", units: Units.DEGREES });
|
||||
63
src/og/proj/Proj.js
Normal file
63
src/og/proj/Proj.js
Normal file
@ -0,0 +1,63 @@
|
||||
/**
|
||||
* @module og/proj/Proj
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Projection units: 'degrees', 'ft', 'm' or 'km'.
|
||||
* @enum {string}
|
||||
* @api
|
||||
*/
|
||||
export const Units = {
|
||||
"DEGREES": "degrees",
|
||||
"FEET": "ft",
|
||||
"METERS": "m",
|
||||
"KILOMETERS": "km"
|
||||
};
|
||||
|
||||
/**
|
||||
* Meters per unit lookup table.
|
||||
* @const
|
||||
* @type {Object.<og.proj.Units, number>}
|
||||
*/
|
||||
export const METERS_PER_UNIT = {};
|
||||
METERS_PER_UNIT[Units.FEET] = 0.3048;
|
||||
METERS_PER_UNIT[Units.METERS] = 1;
|
||||
METERS_PER_UNIT[Units.KILOMETERS] = 1000;
|
||||
|
||||
const Proj = function (options) {
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @type {string}
|
||||
*/
|
||||
this.code = options.code;
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @type {og.proj.Units}
|
||||
*/
|
||||
this.units = /** @type {Units} */ (options.units);
|
||||
|
||||
/**
|
||||
* Projection identifier, especially usefull for comparison.
|
||||
* @const
|
||||
* @type {integer}
|
||||
*/
|
||||
this.id = Proj._counter++;
|
||||
};
|
||||
|
||||
/**
|
||||
* Compare projections.
|
||||
* @public
|
||||
* @param {og.proj.Projection} proj - Projetion object.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
Proj.prototype.equal = function (proj) {
|
||||
return proj.id == this.id;
|
||||
};
|
||||
|
||||
Proj._counter = 0;
|
||||
|
||||
export { Proj };
|
||||
552
src/og/renderer/Renderer.js
Normal file
552
src/og/renderer/Renderer.js
Normal file
@ -0,0 +1,552 @@
|
||||
/**
|
||||
* @module og/renderer/Renderer
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { Camera } from '../camera/Camera.js';
|
||||
import { Framebuffer } from '../webgl/Framebuffer.js';
|
||||
import { MultiFramebuffer } from '../webgl/MultiFramebuffer.js';
|
||||
import { randomi } from '../math.js';
|
||||
import { RendererEvents } from './RendererEvents.js';
|
||||
import { Vec2 } from '../math/Vec2.js';
|
||||
import { Vec3 } from '../math/Vec3.js';
|
||||
import { cons } from '../cons.js';
|
||||
import { ShaderProgram } from '../webgl/ShaderProgram.js';
|
||||
import { types } from '../webgl/types.js';
|
||||
import { input } from '../input/input.js';
|
||||
|
||||
/**
|
||||
* Represents high level WebGL context interface that starts WebGL handler works real time.
|
||||
* @class
|
||||
* @param {og.webgl.Handler} handler - WebGL handler context.
|
||||
* @param {Object} [params] - Renderer parameters:
|
||||
* @fires og.RendererEvents#draw
|
||||
* @fires og.RendererEvents#resize
|
||||
* @fires og.RendererEvents#mousemove
|
||||
* @fires og.RendererEvents#mousestop
|
||||
* @fires og.RendererEvents#lclick
|
||||
* @fires og.RendererEvents#rclick
|
||||
* @fires og.RendererEvents#mclick
|
||||
* @fires og.RendererEvents#ldblclick
|
||||
* @fires og.RendererEvents#rdblclick
|
||||
* @fires og.RendererEvents#mdblclick
|
||||
* @fires og.RendererEvents#lup
|
||||
* @fires og.RendererEvents#rup
|
||||
* @fires og.RendererEvents#mup
|
||||
* @fires og.RendererEvents#ldown
|
||||
* @fires og.RendererEvents#rdown
|
||||
* @fires og.RendererEvents#mdown
|
||||
* @fires og.RendererEvents#lhold
|
||||
* @fires og.RendererEvents#rhold
|
||||
* @fires og.RendererEvents#mhold
|
||||
* @fires og.RendererEvents#mousewheel
|
||||
* @fires og.RendererEvents#touchstart
|
||||
* @fires og.RendererEvents#touchend
|
||||
* @fires og.RendererEvents#touchcancel
|
||||
* @fires og.RendererEvents#touchmove
|
||||
* @fires og.RendererEvents#doubletouch
|
||||
* @fires og.RendererEvents#touchleave
|
||||
* @fires og.RendererEvents#touchenter
|
||||
*/
|
||||
const Renderer = function (handler, params) {
|
||||
|
||||
params = params || {};
|
||||
|
||||
/**
|
||||
* Div element with WebGL canvas.
|
||||
* @public
|
||||
* @type {object}
|
||||
*/
|
||||
this.div = null;
|
||||
|
||||
/**
|
||||
* WebGL handler context.
|
||||
* @public
|
||||
* @type {og.webgl.Handler}
|
||||
*/
|
||||
this.handler = handler;
|
||||
|
||||
/**
|
||||
* Render nodes drawing queue.
|
||||
* @private
|
||||
* @type {Array.<og.scene.RenderNode>}
|
||||
*/
|
||||
this._renderNodesArr = [];
|
||||
|
||||
/**
|
||||
* Render nodes store for the comfortable access by the node name.
|
||||
* @public
|
||||
* @type {Object.<og.scene.RenderNode>}
|
||||
*/
|
||||
this.renderNodes = {};
|
||||
|
||||
/**
|
||||
* Cameras array.
|
||||
* @public
|
||||
* @type {Array.<og.Camera>}
|
||||
*/
|
||||
this.cameras = [];
|
||||
|
||||
/**
|
||||
* Current active camera.
|
||||
* @public
|
||||
* @type {og.Camera}
|
||||
*/
|
||||
this.activeCamera = null;
|
||||
|
||||
/**
|
||||
* Renderer events. Represents interface for setting events like mousemove, draw, keypress etc.
|
||||
* @public
|
||||
* @type {og.RendererEvents}
|
||||
*/
|
||||
this.events = new RendererEvents(this);
|
||||
|
||||
/**
|
||||
* OpenGlobus controls array.
|
||||
* @public
|
||||
* @type {Array.<og.control.Control>}
|
||||
*/
|
||||
this.controls = params.controls || [];
|
||||
|
||||
/**
|
||||
* Provides exchange between controls.
|
||||
* @public
|
||||
* @type {Object}
|
||||
*/
|
||||
this.controlsBag = {};
|
||||
|
||||
/**
|
||||
* Hash table for drawing objects.
|
||||
* @public
|
||||
* @type {Object}
|
||||
*/
|
||||
this.colorObjects = {};
|
||||
|
||||
/**
|
||||
* Color picking objects rendering queue.
|
||||
* @type {Array.<og.Renderer~pickingCallback>}
|
||||
*/
|
||||
this._pickingCallbacks = [];
|
||||
|
||||
/**
|
||||
* Picking objects(labels and billboards) framebuffer.
|
||||
* @public
|
||||
* @type {og.webgl.Framebuffer}
|
||||
*/
|
||||
this.pickingFramebuffer = null;
|
||||
|
||||
/**
|
||||
* Whole scene rendering framebuffer.
|
||||
* @public
|
||||
* @type {og.webgl.Framebuffer|og.webgl.MultiFramebuffer}
|
||||
*/
|
||||
this.sceneFramebuffer = null;
|
||||
|
||||
/**
|
||||
* Stores current picking rgb color.
|
||||
* @private
|
||||
* @type {Array.<number,number,number>}
|
||||
*/
|
||||
this._currPickingColor = [0, 0, 0];
|
||||
|
||||
/**
|
||||
* Stores previous picked rgb color.
|
||||
* @private
|
||||
* @type {Array.<number,number,number>}
|
||||
*/
|
||||
this._prevPickingColor = [0, 0, 0];
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
this._fnScreenFrame = null;
|
||||
|
||||
if (params.autoActivate) {
|
||||
this.initialize();
|
||||
this.start();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds picking rendering callback function.
|
||||
* @param {object} sender - Callback context.
|
||||
* @param {og.Renderer~pickingCallback} callback - Rendering callback.
|
||||
*/
|
||||
Renderer.prototype.addPickingCallback = function (sender, callback) {
|
||||
this._pickingCallbacks.push({ "callback": callback, "sender": sender });
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign picking color to the object.
|
||||
* @public
|
||||
* @param {Object} obj - Object that pressume to be picked.
|
||||
*/
|
||||
Renderer.prototype.assignPickingColor = function (obj) {
|
||||
if (!obj._pickingColor || obj._pickingColor.isZero()) {
|
||||
var r = 0, g = 0, b = 0;
|
||||
var str = "0_0_0";
|
||||
while (!(r || g || b) || this.colorObjects[str]) {
|
||||
r = randomi(1, 255);
|
||||
g = randomi(1, 255);
|
||||
b = randomi(1, 255);
|
||||
str = r + "_" + g + "_" + b;
|
||||
}
|
||||
|
||||
if (!obj._pickingColor)
|
||||
obj._pickingColor = new Vec3(r, g, b);
|
||||
else
|
||||
obj._pickingColor.set(r, g, b);
|
||||
|
||||
this.colorObjects[str] = obj;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes picking color from object.
|
||||
* @public
|
||||
* @param {Object} obj - Object to remove picking color.
|
||||
*/
|
||||
Renderer.prototype.clearPickingColor = function (obj) {
|
||||
if (!obj._pickingColor.isZero()) {
|
||||
var c = obj._pickingColor;
|
||||
if (!c.isZero()) {
|
||||
this.colorObjects[c.x + "_" + c.y + "_" + c.z] = null;
|
||||
delete this.colorObjects[c.x + "_" + c.y + "_" + c.z];
|
||||
c.x = c.y = c.z = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the client width.
|
||||
* @public
|
||||
* @returns {number} -
|
||||
*/
|
||||
Renderer.prototype.getWidth = function () {
|
||||
return this.handler.canvas.width;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the client height.
|
||||
* @public
|
||||
* @returns {number} -
|
||||
*/
|
||||
Renderer.prototype.getHeight = function () {
|
||||
return this.handler.canvas.height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get center of the screen
|
||||
* @public
|
||||
* @returns {og.math.Vec2} -
|
||||
*/
|
||||
Renderer.prototype.getCenter = function () {
|
||||
var cnv = this.handler.canvas;
|
||||
return new Vec2(Math.round(cnv.width * 0.5), Math.round(cnv.height * 0.5));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the given control to the renderer.
|
||||
* @param {og.control.BaseControl} control - Control.
|
||||
*/
|
||||
Renderer.prototype.addControl = function (control) {
|
||||
control.addTo(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the given controls array to the planet node.
|
||||
* @param {Array.<og.control.BaseControl>} cArr - Control array.
|
||||
*/
|
||||
Renderer.prototype.addControls = function (cArr) {
|
||||
for (var i = 0; i < cArr.length; i++) {
|
||||
cArr[i].addTo(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove control from the renderer.
|
||||
* @param {og.control.BaseControl} control - Control.
|
||||
* @return {og.control.BaseControl|undefined} -
|
||||
*/
|
||||
Renderer.prototype.removeControl = function (control) {
|
||||
for (var i = 0; i < this.controls.length; i++) {
|
||||
if (this.controls[i].isEqual(control)) {
|
||||
this.controls.splice(i, 1);
|
||||
control.remove();
|
||||
return control;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renderer initialization.
|
||||
* @public
|
||||
*/
|
||||
Renderer.prototype.initialize = function () {
|
||||
var that = this;
|
||||
|
||||
this.handler.setFrameCallback(function () {
|
||||
that.draw();
|
||||
});
|
||||
|
||||
this.activeCamera = new Camera(this, {
|
||||
'eye': new Vec3(0, 0, 0),
|
||||
'look': new Vec3(0, 0, -1),
|
||||
'up': new Vec3(0, 1, 0)
|
||||
});
|
||||
|
||||
this.events.initialize();
|
||||
|
||||
//Bind console key
|
||||
this.events.on("charkeypress", input.KEY_APOSTROPHE, function () {
|
||||
cons.setVisibility(!cons.getVisibility());
|
||||
});
|
||||
|
||||
this.handler.onCanvasResize = function (obj) {
|
||||
that.activeCamera.setAspectRatio(obj.clientWidth / obj.clientHeight);
|
||||
that.sceneFramebuffer.setSize(obj.clientWidth, obj.clientHeight);
|
||||
that.events.dispatch(that.events.resize, obj);
|
||||
};
|
||||
|
||||
this.pickingFramebuffer = new Framebuffer(this.handler, {
|
||||
'width': 640,
|
||||
'height': 480
|
||||
});
|
||||
this.pickingFramebuffer.init();
|
||||
|
||||
this.handler.addShaderProgram(new ShaderProgram("screenFrame", {
|
||||
uniforms: {
|
||||
texture: { type: types.SAMPLER2D }
|
||||
},
|
||||
attributes: {
|
||||
corners: { type: types.VEC3, enableArray: true },
|
||||
},
|
||||
vertexShader:
|
||||
'attribute vec2 corners;\
|
||||
\
|
||||
varying vec2 tc;\
|
||||
void main(void) {\
|
||||
gl_Position = vec4(corners, 0.0, 1.0);\
|
||||
tc = corners * 0.5 + 0.5;\
|
||||
}',
|
||||
fragmentShader:
|
||||
'precision highp float;\
|
||||
uniform sampler2D texture;\
|
||||
\
|
||||
varying vec2 tc;\
|
||||
\
|
||||
void main(void) {\
|
||||
gl_FragColor = texture2D( texture, tc );\
|
||||
}'
|
||||
}));
|
||||
|
||||
//Append multiframebuffer(WEBGL_draw_buffers) extension.
|
||||
this._drawBuffersExtension = this.handler.initializeExtension("WEBGL_draw_buffers");
|
||||
|
||||
if (this._drawBuffersExtension) {
|
||||
this.sceneFramebuffer = new MultiFramebuffer(this.handler, { size: 3 });
|
||||
this.sceneFramebuffer.init();
|
||||
|
||||
this._fnScreenFrame = this._multiframebufferScreenFrame;
|
||||
} else {
|
||||
this.sceneFramebuffer = new Framebuffer(this.handler);
|
||||
this.sceneFramebuffer.init();
|
||||
|
||||
this._fnScreenFrame = this._singleframebufferScreenFrame;
|
||||
}
|
||||
|
||||
this._screenFrameCornersBuffer = this.handler.createArrayBuffer(new Float32Array([1, 1, -1, 1, 1, -1, -1, -1]), 2, 4);
|
||||
|
||||
let _c = this.controls;
|
||||
this.controls = [];
|
||||
this.addControls(_c);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds render node to the renderer.
|
||||
* @public
|
||||
* @param {og.scene.RenderNode} renderNode - Render node.
|
||||
*/
|
||||
Renderer.prototype.addRenderNode = function (renderNode) {
|
||||
if (!this.renderNodes[renderNode.name]) {
|
||||
renderNode.assignRenderer(this);
|
||||
this._renderNodesArr.unshift(renderNode);
|
||||
this.renderNodes[renderNode.name] = renderNode;
|
||||
} else {
|
||||
cons.logWrn("og.Renderer(370) - node name: " + renderNode.name + " allready exists.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds render nodes array to the renderer.
|
||||
* @public
|
||||
* @param {Array.<og.scene.RenderNode>} nodesArr - Render nodes array.
|
||||
*/
|
||||
Renderer.prototype.addRenderNodes = function (nodesArr) {
|
||||
for (var i = 0; i < nodesArr.length; i++) {
|
||||
this.addRenderNode(nodesArr[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw nodes.
|
||||
* @public
|
||||
*/
|
||||
Renderer.prototype.draw = function () {
|
||||
|
||||
this.activeCamera.checkMoveEnd();
|
||||
|
||||
var e = this.events;
|
||||
e.handleEvents();
|
||||
e.dispatch(e.draw, this);
|
||||
|
||||
var sfb = this.sceneFramebuffer;
|
||||
sfb.activate();
|
||||
var h = this.handler;
|
||||
h.clearFrame();
|
||||
|
||||
h.gl.activeTexture(h.gl.TEXTURE0);
|
||||
h.gl.bindTexture(h.gl.TEXTURE_2D, h.transparentTexture);
|
||||
|
||||
//Rendering scene nodes
|
||||
var rn = this._renderNodesArr;
|
||||
var i = rn.length;
|
||||
while (i--) {
|
||||
rn[i].drawNode();
|
||||
}
|
||||
|
||||
sfb.deactivate();
|
||||
|
||||
//Rendering picking callbacks and refresh pickingColor
|
||||
this._drawPickingBuffer();
|
||||
|
||||
//Rendering on the screen
|
||||
this._fnScreenFrame();
|
||||
|
||||
e.mouseState.moving = false;
|
||||
e.touchState.moving = false;
|
||||
}
|
||||
|
||||
Renderer.prototype._multiframebufferScreenFrame = function () {
|
||||
var h = this.handler;
|
||||
var sh = h.shaderPrograms.screenFrame,
|
||||
p = sh._program,
|
||||
gl = h.gl;
|
||||
|
||||
gl.disable(gl.DEPTH_TEST);
|
||||
sh.activate();
|
||||
gl.activeTexture(gl.TEXTURE0);
|
||||
//MAYBE: Could be refactored with framebuf function like getTexture()
|
||||
gl.bindTexture(gl.TEXTURE_2D, this.sceneFramebuffer.textures[0]);
|
||||
//gl.bindTexture(gl.TEXTURE_2D, this.pickingFramebuffer.texture);
|
||||
gl.uniform1i(p.uniforms.texture._pName, 0);
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this._screenFrameCornersBuffer);
|
||||
gl.vertexAttribPointer(p.attributes.corners._pName, 2, gl.FLOAT, false, 0, 0);
|
||||
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
|
||||
gl.enable(gl.DEPTH_TEST);
|
||||
}
|
||||
|
||||
Renderer.prototype._singleframebufferScreenFrame = function () {
|
||||
var h = this.handler;
|
||||
var sh = h.shaderPrograms.screenFrame,
|
||||
p = sh._program,
|
||||
gl = h.gl;
|
||||
|
||||
gl.disable(gl.DEPTH_TEST);
|
||||
sh.activate();
|
||||
gl.activeTexture(gl.TEXTURE0);
|
||||
//gl.bindTexture(gl.TEXTURE_2D, this.pickingFramebuffer.texture);
|
||||
//gl.bindTexture(gl.TEXTURE_2D, globus.planet._heightPickingFramebuffer.texture);
|
||||
gl.bindTexture(gl.TEXTURE_2D, this.sceneFramebuffer.texture);
|
||||
gl.uniform1i(p.uniforms.texture._pName, 0);
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this._screenFrameCornersBuffer);
|
||||
gl.vertexAttribPointer(p.attributes.corners._pName, 2, gl.FLOAT, false, 0, 0);
|
||||
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
|
||||
gl.enable(gl.DEPTH_TEST);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns picking object by screen coordinates
|
||||
* @param {number} x - X position
|
||||
* @param {number} y - Y position
|
||||
* @return {Object} -
|
||||
*/
|
||||
Renderer.prototype.getPickingObject = function (x, y) {
|
||||
var cnv = this.renderer.handler.canvas;
|
||||
var c;
|
||||
if (this._drawBuffersExtension) {
|
||||
c = this.sceneFramebuffer.readPixel(x / cnv.width, (cnv.height - y) / cnv.height, 1);
|
||||
} else {
|
||||
c = this.sceneFramebuffer.readPixel(x / cnv.width, (cnv.height - y) / cnv.height);
|
||||
}
|
||||
return this.colorObjects[c[0] + "_" + c[1] + "_" + c[2]];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if 'WEBGL_draw_buffers' extension initialized.
|
||||
* @public
|
||||
* @returns {Boolean} -
|
||||
*/
|
||||
Renderer.prototype.isMultiFramebufferCompatible = function () {
|
||||
return (this._drawBuffersExtension ? true : false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw picking objects framebuffer.
|
||||
* @private
|
||||
*/
|
||||
Renderer.prototype._drawPickingBuffer = function () {
|
||||
this.pickingFramebuffer.activate();
|
||||
|
||||
var h = this.handler;
|
||||
var gl = h.gl;
|
||||
gl.clearColor(0.0, 0.0, 0.0, 1.0);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
||||
gl.disable(h.gl.BLEND);
|
||||
|
||||
var dp = this._pickingCallbacks;
|
||||
var i = dp.length;
|
||||
while (i--) {
|
||||
/**
|
||||
* This callback renders picking frame.
|
||||
* @callback og.Renderer~pickingCallback
|
||||
*/
|
||||
dp[i].callback.call(dp[i].sender);
|
||||
}
|
||||
|
||||
this.pickingFramebuffer.deactivate();
|
||||
|
||||
var ms = this.events.mouseState;
|
||||
var ts = this.events.touchState;
|
||||
|
||||
if (!(ms.leftButtonHold || ms.rightButtonHold || ms.middleButtonHold)) {
|
||||
this._prevPickingColor[0] = this._currPickingColor[0];
|
||||
this._prevPickingColor[1] = this._currPickingColor[1];
|
||||
this._prevPickingColor[2] = this._currPickingColor[2];
|
||||
|
||||
var pc;
|
||||
if (ts.x || ts.y) {
|
||||
pc = this.pickingFramebuffer.readPixel(ts.nx, 1.0 - ts.ny);
|
||||
if (!(pc[0] || pc[1] || pc[2]) && this._drawBuffersExtension)
|
||||
pc = this.sceneFramebuffer.readPixel(ts.nx, 1.0 - ts.ny, 1);
|
||||
} else {
|
||||
pc = this.pickingFramebuffer.readPixel(ms.nx, 1.0 - ms.ny);
|
||||
if (!(pc[0] || pc[1] || pc[2]) && this._drawBuffersExtension)
|
||||
pc = this.sceneFramebuffer.readPixel(ms.nx, 1.0 - ms.ny, 1);
|
||||
}
|
||||
this._currPickingColor = pc;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Function starts rendering.
|
||||
* @public
|
||||
*/
|
||||
Renderer.prototype.start = function () {
|
||||
this.handler.start();
|
||||
}
|
||||
|
||||
|
||||
export { Renderer };
|
||||
890
src/og/renderer/RendererEvents.js
Normal file
890
src/og/renderer/RendererEvents.js
Normal file
@ -0,0 +1,890 @@
|
||||
/**
|
||||
* @module og/renderer/RendererEvents
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { Events } from '../Events.js';
|
||||
import { input } from '../input/input.js';
|
||||
import { KeyboardHandler } from '../input/KeyboardHandler.js';
|
||||
import { MouseHandler } from '../input/MouseHandler.js';
|
||||
import { TouchHandler } from '../input/TouchHandler.js';
|
||||
import { Vec2 } from '../math/Vec2.js';
|
||||
import { Vec3 } from '../math/Vec3.js';
|
||||
|
||||
/**
|
||||
* Renderer events handler.
|
||||
* @class
|
||||
* @param {og.Renderer} renderer - Renderer object, events that works for.
|
||||
*/
|
||||
class RendererEvents extends Events {
|
||||
constructor(renderer) {
|
||||
|
||||
super(EVENT_NAMES);
|
||||
|
||||
/**
|
||||
* Assigned renderer.
|
||||
* @public
|
||||
* @type {og.Renderer}
|
||||
*/
|
||||
this.renderer = renderer;
|
||||
|
||||
/**
|
||||
* Low level touch events handler.
|
||||
* @private
|
||||
* @type {og.input.TouchHandler}
|
||||
*/
|
||||
this._touchHandler = new TouchHandler(renderer.handler.canvas);
|
||||
|
||||
/**
|
||||
* Low level mouse events handler.
|
||||
* @private
|
||||
* @type {og.input.MouseHandler}
|
||||
*/
|
||||
this._mouseHandler = new MouseHandler(renderer.handler.canvas);
|
||||
|
||||
/**
|
||||
* Low level keyboard events handler.
|
||||
* @private
|
||||
* @type {og.input.KeyboardHandler}
|
||||
*/
|
||||
this._keyboardHandler = new KeyboardHandler();
|
||||
|
||||
/**
|
||||
* Current mouse state.
|
||||
* @public
|
||||
* @enum {Object}
|
||||
*/
|
||||
this.mouseState = {
|
||||
/** Current mouse X position. */
|
||||
x: 0,
|
||||
/** Current mouse Y position. */
|
||||
y: 0,
|
||||
/** Current mouse X position from 0 to 1 */
|
||||
nx: 0,
|
||||
/** Current mouse Y position from 0 to 1 */
|
||||
ny: 0,
|
||||
/** Previous mouse X position. */
|
||||
prev_x: 0,
|
||||
/** Previous mouse Y position. */
|
||||
prev_y: 0,
|
||||
/** Screen mouse position world direction. */
|
||||
direction: new Vec3(),
|
||||
/** Left mouse button has stopped pushing down right now.*/
|
||||
leftButtonUp: false,
|
||||
/** Right mouse button has stopped pushing down right now.*/
|
||||
rightButtonUp: false,
|
||||
/** Middle mouse button has stopped pushing down right now.*/
|
||||
middleButtonUp: false,
|
||||
/** Left mouse button has pushed now.*/
|
||||
leftButtonDown: false,
|
||||
/** Right mouse button has pushed now.*/
|
||||
rightButtonDown: false,
|
||||
/** Middle mouse button has pushed now.*/
|
||||
middleButtonDown: false,
|
||||
/** Left mouse button is pushing.*/
|
||||
leftButtonHold: false,
|
||||
/** Right mouse button is pushing.*/
|
||||
rightButtonHold: false,
|
||||
/** Middle mouse button is pushing.*/
|
||||
middleButtonHold: false,
|
||||
/** Left mouse button has clicked twice now.*/
|
||||
leftButtonDoubleClick: false,
|
||||
/** Right mouse button has clicked twice now.*/
|
||||
rightButtonDoubleClick: false,
|
||||
/** Middle mouse button has clicked twice now.*/
|
||||
middleButtonDoubleClick: false,
|
||||
/** Left mouse button has clicked now. */
|
||||
leftButtonClick: false,
|
||||
/** Right mouse button has clicked now. */
|
||||
rightButtonClick: false,
|
||||
/** Middle mouse button has clicked now. */
|
||||
middleButtonClick: false,
|
||||
/** Mouse is moving now. */
|
||||
moving: false,
|
||||
/** Mouse has just stopped now. */
|
||||
justStopped: false,
|
||||
/** Mose double click delay response.*/
|
||||
doubleClickDelay: 300,
|
||||
/** Mouse wheel. */
|
||||
wheelDelta: 0,
|
||||
/** JavaScript mouse system event message. */
|
||||
sys: null,
|
||||
/** Current picking object. */
|
||||
pickingObject: null
|
||||
};
|
||||
|
||||
/**
|
||||
* Current touch state.
|
||||
* @public
|
||||
* @enum {Object}
|
||||
*/
|
||||
this.touchState = {
|
||||
/** Touching is moving now. */
|
||||
moving: false,
|
||||
/** Touch has ended right now.*/
|
||||
touchEnd: false,
|
||||
/** Touch has started right now.*/
|
||||
touchStart: false,
|
||||
/** Touch canceled.*/
|
||||
touchCancel: false,
|
||||
/** Touched twice.*/
|
||||
doubleTouch: false,
|
||||
/** Double touching responce delay.*/
|
||||
doubleTouchDelay: 550,
|
||||
/** Double touching responce radius in screen pixels.*/
|
||||
doubleTouchRadius: 10,
|
||||
/** Current touch X - coordinate. */
|
||||
x: 0,
|
||||
/** Current touch Y - coordinate. */
|
||||
y: 0,
|
||||
/** Current touch X - coordinate from 0 to 1 */
|
||||
nx: 0,
|
||||
/** Current touch Y - coordinate from 0 to 1 */
|
||||
ny: 0,
|
||||
/** Previous touch X coordinate. */
|
||||
prev_x: 0,
|
||||
/** Previous touch Y coordinate. */
|
||||
prev_y: 0,
|
||||
/** JavaScript touching system event message. */
|
||||
sys: null,
|
||||
/** Current touched(picking) object. */
|
||||
pickingObject: null
|
||||
};
|
||||
|
||||
this._dblTchCoords = new Vec2();
|
||||
this._oneTouchStart = false;
|
||||
this._dblTchBegins = 0;
|
||||
this._mousestopThread = null;
|
||||
this._ldblClkBegins = 0;
|
||||
this._rdblClkBegins = 0;
|
||||
this._mdblClkBegins = 0;
|
||||
this._lclickX = 0;
|
||||
this._lclickY = 0;
|
||||
this._rclickX = 0;
|
||||
this._rclickY = 0;
|
||||
this._mclickX = 0;
|
||||
this._mclickY = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used in render node frame.
|
||||
* @public
|
||||
*/
|
||||
handleEvents() {
|
||||
this.mouseState.direction = this.renderer.activeCamera.unproject(this.mouseState.x, this.mouseState.y);
|
||||
this.entityPickingEvents();
|
||||
this._keyboardHandler.handleEvents();
|
||||
this.handleMouseEvents();
|
||||
this.handleTouchEvents();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set render event callback.
|
||||
* @public
|
||||
* @param {string} name - Event name
|
||||
* @param {eventCallback} callback - Callback function
|
||||
* @param {number} [key] - Key code from og.input
|
||||
* @param {*} sender - Callback context
|
||||
* @param {number} [priority] - Event callback priority
|
||||
*/
|
||||
on(name, p0, p1, p2, p3) {
|
||||
if (name == "keypress" || name == "charkeypress") {
|
||||
this._keyboardHandler.addEvent(name, p2, p1, p0, p3);
|
||||
} else {
|
||||
super.on(name, p0, p1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check key is pressed.
|
||||
* @public
|
||||
* @param {number} keyCode - Key code
|
||||
* @return {boolean}
|
||||
*/
|
||||
isKeyPressed(keyCode) {
|
||||
return this._keyboardHandler.isKeyPressed(keyCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renderer events initialization.
|
||||
* @public
|
||||
*/
|
||||
initialize() {
|
||||
|
||||
this._mouseHandler.setEvent("mouseup", this, this.onMouseUp);
|
||||
this._mouseHandler.setEvent("mousemove", this, this.onMouseMove);
|
||||
this._mouseHandler.setEvent("mousedown", this, this.onMouseDown);
|
||||
this._mouseHandler.setEvent("mousewheel", this, this.onMouseWheel);
|
||||
|
||||
this._touchHandler.setEvent("touchstart", this, this.onTouchStart);
|
||||
this._touchHandler.setEvent("touchend", this, this.onTouchEnd);
|
||||
this._touchHandler.setEvent("touchcancel", this, this.onTouchCancel);
|
||||
this._touchHandler.setEvent("touchmove", this, this.onTouchMove);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
onMouseWheel(event) {
|
||||
this.mouseState.wheelDelta = event.wheelDelta;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
onMouseMove(event) {
|
||||
var ms = this.mouseState;
|
||||
ms.sys = event;
|
||||
|
||||
if (ms.x === event.clientX && ms.y === event.clientY) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._ldblClkBegins = 0;
|
||||
this._rdblClkBegins = 0;
|
||||
this._mdblClkBegins = 0;
|
||||
|
||||
ms.x = event.clientX;
|
||||
ms.y = event.clientY;
|
||||
|
||||
var cnv = this.renderer.handler.canvas;
|
||||
ms.nx = ms.x / cnv.width;
|
||||
ms.ny = ms.y / cnv.height;
|
||||
|
||||
ms.moving = true;
|
||||
|
||||
//dispatch stop mouse event
|
||||
clearTimeout(this._mousestopThread);
|
||||
var that = this;
|
||||
this._mousestopThread = setTimeout(function () {
|
||||
ms.justStopped = true;
|
||||
}, 100);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
onMouseDown(event) {
|
||||
if (event.button === input.MB_LEFT) {
|
||||
this._lclickX = event.clientX;
|
||||
this._lclickY = event.clientY;
|
||||
this.mouseState.sys = event;
|
||||
this.mouseState.leftButtonDown = true;
|
||||
} else if (event.button === input.MB_RIGHT) {
|
||||
this._rclickX = event.clientX;
|
||||
this._rclickY = event.clientY;
|
||||
this.mouseState.sys = event;
|
||||
this.mouseState.rightButtonDown = true;
|
||||
} else if (event.button === input.MB_MIDDLE) {
|
||||
this._mclickX = event.clientX;
|
||||
this._mclickY = event.clientY;
|
||||
this.mouseState.sys = event;
|
||||
this.mouseState.middleButtonDown = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
onMouseUp(event) {
|
||||
var ms = this.mouseState;
|
||||
ms.sys = event;
|
||||
if (event.button === input.MB_LEFT) {
|
||||
ms.leftButtonDown = false;
|
||||
ms.leftButtonUp = true;
|
||||
|
||||
if (this._lclickX === event.clientX &&
|
||||
this._lclickY === event.clientY) {
|
||||
|
||||
if (this._ldblClkBegins) {
|
||||
var deltatime = new Date().getTime() - this._ldblClkBegins;
|
||||
if (deltatime <= ms.doubleClickDelay) {
|
||||
ms.leftButtonDoubleClick = true;
|
||||
}
|
||||
this._ldblClkBegins = 0;
|
||||
} else {
|
||||
this._ldblClkBegins = new Date().getTime();
|
||||
}
|
||||
|
||||
ms.leftButtonClick = true;
|
||||
}
|
||||
|
||||
} else if (event.button === input.MB_RIGHT) {
|
||||
ms.rightButtonDown = false;
|
||||
ms.rightButtonUp = true;
|
||||
|
||||
if (this._rclickX === event.clientX &&
|
||||
this._rclickY === event.clientY) {
|
||||
|
||||
if (this._rdblClkBegins) {
|
||||
var deltatime = new Date().getTime() - this._rdblClkBegins;
|
||||
if (deltatime <= ms.doubleClickDelay) {
|
||||
ms.rightButtonDoubleClick = true;
|
||||
}
|
||||
this._rdblClkBegins = 0;
|
||||
} else {
|
||||
this._rdblClkBegins = new Date().getTime();
|
||||
}
|
||||
|
||||
ms.rightButtonClick = true;
|
||||
}
|
||||
} else if (event.button === input.MB_MIDDLE) {
|
||||
ms.middleButtonDown = false;
|
||||
ms.middleButtonUp = true;
|
||||
|
||||
if (this._mclickX === event.clientX &&
|
||||
this._mclickY === event.clientY) {
|
||||
|
||||
if (this._mdblClkBegins) {
|
||||
var deltatime = new Date().getTime() - this._mdblClkBegins;
|
||||
if (deltatime <= ms.doubleClickDelay) {
|
||||
ms.middleButtonDoubleClick = true;
|
||||
}
|
||||
this._mdblClkBegins = 0;
|
||||
} else {
|
||||
this._mdblClkBegins = new Date().getTime();
|
||||
}
|
||||
|
||||
ms.middleButtonClick = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
onTouchStart(event) {
|
||||
var ts = this.touchState;
|
||||
ts.sys = event;
|
||||
ts.x = event.touches.item(0).clientX - event.offsetLeft;
|
||||
ts.y = event.touches.item(0).clientY - event.offsetTop;
|
||||
var cnv = this.renderer.handler.canvas;
|
||||
ts.nx = ts.x / cnv.width;
|
||||
ts.ny = ts.y / cnv.height;
|
||||
ts.prev_x = ts.x;
|
||||
ts.prev_y = ts.y;
|
||||
ts.touchStart = true;
|
||||
|
||||
if (event.touches.length === 1) {
|
||||
this._dblTchCoords.x = ts.x;
|
||||
this._dblTchCoords.y = ts.y;
|
||||
this._oneTouchStart = true;
|
||||
} else {
|
||||
this._oneTouchStart = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
onTouchEnd(event) {
|
||||
var ts = this.touchState;
|
||||
ts.sys = event;
|
||||
ts.touchEnd = true;
|
||||
|
||||
if (event.touches.length === 0) {
|
||||
|
||||
ts.prev_x = ts.x;
|
||||
ts.prev_y = ts.y;
|
||||
|
||||
if (this._oneTouchStart) {
|
||||
|
||||
if (this._dblTchBegins) {
|
||||
var deltatime = new Date().getTime() - this._dblTchBegins;
|
||||
if (deltatime <= ts.doubleTouchDelay) {
|
||||
ts.doubleTouch = true;
|
||||
}
|
||||
this._dblTchBegins = 0;
|
||||
}
|
||||
this._dblTchBegins = new Date().getTime();
|
||||
this._oneTouchStart = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
onTouchCancel(event) {
|
||||
var ts = this.touchState;
|
||||
ts.sys = event;
|
||||
ts.touchCancel = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
onTouchMove(event) {
|
||||
var ts = this.touchState;
|
||||
ts.x = event.touches.item(0).clientX - event.offsetLeft;
|
||||
ts.y = event.touches.item(0).clientY - event.offsetTop;
|
||||
var cnv = this.renderer.handler.canvas;
|
||||
ts.nx = ts.x / cnv.width;
|
||||
ts.ny = ts.y / cnv.height;
|
||||
ts.sys = event;
|
||||
ts.moving = true;
|
||||
this._dblTchBegins = 0;
|
||||
this._oneTouchStart = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
entityPickingEvents() {
|
||||
var ts = this.touchState,
|
||||
ms = this.mouseState;
|
||||
|
||||
if (!(ms.leftButtonHold || ms.rightButtonHold || ms.middleButtonHold)) {
|
||||
|
||||
var r = this.renderer;
|
||||
|
||||
var o = r.colorObjects;
|
||||
|
||||
var c = r._currPickingColor,
|
||||
p = r._prevPickingColor;
|
||||
|
||||
ms.pickingObject = null;
|
||||
ts.pickingObject = null;
|
||||
|
||||
var co = o && o[c[0] + "_" + c[1] + "_" + c[2]];
|
||||
|
||||
ms.pickingObject = co;
|
||||
ts.pickingObject = co;
|
||||
|
||||
//object changed
|
||||
if (c[0] != p[0] || c[1] != p[1] || c[2] != p[2]) {
|
||||
//current black
|
||||
if (!(c[0] || c[1] || c[2])) {
|
||||
var po = o[p[0] + "_" + p[1] + "_" + p[2]];
|
||||
if (po) {
|
||||
//TODO: This is a reason to replace geometryHandler to an entityCollection...or maybe not
|
||||
var pe = po.events || po._entityCollection && po._entityCollection.events || po._layer.events;
|
||||
ms.pickingObject = po;
|
||||
pe.dispatch(pe.mouseleave, ms);
|
||||
ts.pickingObject = po;
|
||||
pe.dispatch(pe.touchleave, ts);
|
||||
}
|
||||
} else {
|
||||
//current not black
|
||||
|
||||
//previous not black
|
||||
if (p[0] || p[1] || p[2]) {
|
||||
var po = o[p[0] + "_" + p[1] + "_" + p[2]];
|
||||
if (po) {
|
||||
var pe = po.events || po._entityCollection && po._entityCollection.events || po._layer.events;
|
||||
ms.pickingObject = po;
|
||||
pe.dispatch(pe.mouseleave, ms);
|
||||
ts.pickingObject = po;
|
||||
pe.dispatch(pe.touchleave, ts);
|
||||
}
|
||||
}
|
||||
|
||||
if (co) {
|
||||
var ce = co.events || co._entityCollection && co._entityCollection.events || co._layer.events;
|
||||
ms.pickingObject = co;
|
||||
ce.dispatch(ce.mouseenter, ms);
|
||||
ts.pickingObject = co;
|
||||
ce.dispatch(ce.touchenter, ts);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
handleMouseEvents() {
|
||||
let ms = this.mouseState;
|
||||
let po = ms.pickingObject,
|
||||
pe = null;
|
||||
|
||||
if (ms.leftButtonClick) {
|
||||
if (po) {
|
||||
pe = po.events || po._entityCollection && po._entityCollection.events || po._layer.events;
|
||||
pe.dispatch(pe.lclick, ms);
|
||||
}
|
||||
this.dispatch(this.lclick, ms);
|
||||
ms.leftButtonClick = false;
|
||||
}
|
||||
|
||||
if (ms.rightButtonClick) {
|
||||
if (po) {
|
||||
pe = po.events || po._entityCollection && po._entityCollection.events || po._layer.events;
|
||||
pe.dispatch(pe.rclick, ms);
|
||||
}
|
||||
this.dispatch(this.rclick, ms);
|
||||
ms.rightButtonClick = false;
|
||||
}
|
||||
|
||||
if (ms.middleButtonClick) {
|
||||
if (po) {
|
||||
pe = po.events || po._entityCollection && po._entityCollection.events || po._layer.events;
|
||||
pe.dispatch(pe.mclick, ms);
|
||||
}
|
||||
this.dispatch(this.mclick, ms);
|
||||
ms.middleButtonClick = false;
|
||||
}
|
||||
|
||||
if (ms.leftButtonDown) {
|
||||
if (ms.leftButtonHold) {
|
||||
if (po) {
|
||||
pe = po.events || po._entityCollection && po._entityCollection.events || po._layer.events;
|
||||
pe.dispatch(pe.lhold, ms);
|
||||
}
|
||||
this.dispatch(this.lhold, ms);
|
||||
} else {
|
||||
ms.leftButtonHold = true;
|
||||
if (po) {
|
||||
pe = po.events || po._entityCollection && po._entityCollection.events || po._layer.events;
|
||||
pe.dispatch(pe.ldown, ms);
|
||||
}
|
||||
this.dispatch(this.ldown, ms);
|
||||
}
|
||||
}
|
||||
|
||||
if (ms.rightButtonDown) {
|
||||
if (ms.rightButtonHold) {
|
||||
if (po) {
|
||||
pe = po.events || po._entityCollection && po._entityCollection.events || po._layer.events;
|
||||
pe.dispatch(pe.rhold, ms);
|
||||
}
|
||||
this.dispatch(this.rhold, ms);
|
||||
} else {
|
||||
ms.rightButtonHold = true;
|
||||
if (po) {
|
||||
pe = po.events || po._entityCollection && po._entityCollection.events || po._layer.events;
|
||||
pe.dispatch(pe.rdown, ms);
|
||||
}
|
||||
this.dispatch(this.rdown, ms);
|
||||
}
|
||||
}
|
||||
|
||||
if (ms.middleButtonDown) {
|
||||
if (ms.middleButtonHold) {
|
||||
if (po) {
|
||||
pe = po.events || po._entityCollection && po._entityCollection.events || po._layer.events;
|
||||
pe.dispatch(pe.mhold, ms);
|
||||
}
|
||||
this.dispatch(this.mhold, ms);
|
||||
} else {
|
||||
ms.middleButtonHold = true;
|
||||
if (po) {
|
||||
pe = po.events || po._entityCollection && po._entityCollection.events || po._layer.events;
|
||||
pe.dispatch(pe.mdown, ms);
|
||||
}
|
||||
this.dispatch(this.mdown, ms);
|
||||
}
|
||||
}
|
||||
|
||||
if (ms.leftButtonUp) {
|
||||
if (po) {
|
||||
pe = po.events || po._entityCollection && po._entityCollection.events || po._layer.events;
|
||||
pe.dispatch(pe.lup, ms);
|
||||
}
|
||||
this.dispatch(this.lup, ms);
|
||||
ms.leftButtonUp = false;
|
||||
ms.leftButtonHold = false;
|
||||
}
|
||||
|
||||
if (ms.rightButtonUp) {
|
||||
if (po) {
|
||||
pe = po.events || po._entityCollection && po._entityCollection.events || po._layer.events;
|
||||
pe.dispatch(pe.rup, ms);
|
||||
}
|
||||
this.dispatch(this.rup, ms);
|
||||
ms.rightButtonUp = false;
|
||||
ms.rightButtonHold = false;
|
||||
}
|
||||
|
||||
if (ms.middleButtonUp) {
|
||||
if (po) {
|
||||
pe = po.events || po._entityCollection && po._entityCollection.events || po._layer.events;
|
||||
pe.dispatch(pe.mup, ms);
|
||||
}
|
||||
this.dispatch(this.mup, ms);
|
||||
ms.middleButtonUp = false;
|
||||
ms.middleButtonHold = false;
|
||||
}
|
||||
|
||||
if (ms.leftButtonDoubleClick) {
|
||||
if (po) {
|
||||
pe = po.events || po._entityCollection && po._entityCollection.events || po._layer.events;
|
||||
pe.dispatch(pe.ldblclick, ms);
|
||||
}
|
||||
this.dispatch(this.ldblclick, ms);
|
||||
ms.leftButtonDoubleClick = false;
|
||||
}
|
||||
|
||||
if (ms.rightButtonDoubleClick) {
|
||||
if (po) {
|
||||
pe = po.events || po._entityCollection && po._entityCollection.events || po._layer.events;
|
||||
pe.dispatch(pe.rdblclick, ms);
|
||||
}
|
||||
this.dispatch(this.rdblclick, ms);
|
||||
ms.rightButtonDoubleClick = false;
|
||||
}
|
||||
|
||||
if (ms.middleButtonDoubleClick) {
|
||||
if (po) {
|
||||
pe = po.events || po._entityCollection && po._entityCollection.events || po._layer.events;
|
||||
pe.dispatch(pe.mdblclick, ms);
|
||||
}
|
||||
this.dispatch(this.mdblclick, ms);
|
||||
ms.middleButtonDoubleClick = false;
|
||||
}
|
||||
|
||||
if (ms.wheelDelta) {
|
||||
if (po) {
|
||||
pe = po.events || po._entityCollection && po._entityCollection.events || po._layer.events;
|
||||
pe.dispatch(pe.mousewheel, ms);
|
||||
}
|
||||
this.dispatch(this.mousewheel, ms);
|
||||
ms.wheelDelta = 0;
|
||||
}
|
||||
|
||||
if (ms.moving) {
|
||||
if (po) {
|
||||
pe = po.events || po._entityCollection && po._entityCollection.events || po._layer.events;
|
||||
pe.dispatch(pe.mousemove, ms);
|
||||
}
|
||||
this.dispatch(this.mousemove, ms);
|
||||
ms.prev_x = ms.x;
|
||||
ms.prev_y = ms.y;
|
||||
}
|
||||
|
||||
if (ms.justStopped) {
|
||||
this.dispatch(this.mousestop, ms);
|
||||
ms.justStopped = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
handleTouchEvents() {
|
||||
var ts = this.touchState;
|
||||
|
||||
var tpo = ts.pickingObject,
|
||||
tpe = null;
|
||||
|
||||
if (ts.touchCancel) {
|
||||
this.dispatch(this.touchcancel, ts);
|
||||
ts.touchCancel = false;
|
||||
}
|
||||
|
||||
if (ts.touchStart) {
|
||||
var r = this.renderer;
|
||||
r._currPickingColor = (r._drawBuffersExtension ?
|
||||
r.pickingFramebuffer.readPixel(ts.nx, 1.0 - ts.ny, 1) :
|
||||
r.pickingFramebuffer.readPixel(ts.nx, 1.0 - ts.ny));
|
||||
var o = r.colorObjects;
|
||||
var c = r._currPickingColor;
|
||||
var co = o[c[0] + "_" + c[1] + "_" + c[2]];
|
||||
tpo = ts.pickingObject = co;
|
||||
if (tpo) {
|
||||
tpe = tpo.events || tpo._entityCollection && tpo._entityCollection.events || tpo._layer.events;
|
||||
tpe.dispatch(tpe.touchstart, ts);
|
||||
}
|
||||
this.dispatch(this.touchstart, ts);
|
||||
ts.touchStart = false;
|
||||
}
|
||||
|
||||
if (ts.doubleTouch) {
|
||||
if (tpo) {
|
||||
tpe = tpo.events || tpo._entityCollection && tpo._entityCollection.events || tpo._layer.events;
|
||||
tpe.dispatch(tpe.doubletouch, ts);
|
||||
}
|
||||
this.dispatch(this.doubletouch, ts);
|
||||
ts.doubleTouch = false;
|
||||
}
|
||||
|
||||
if (ts.touchEnd) {
|
||||
if (tpo) {
|
||||
tpe = tpo.events || tpo._entityCollection && tpo._entityCollection.events || tpo._layer.events;
|
||||
tpe.dispatch(tpe.touchend, ts);
|
||||
}
|
||||
this.dispatch(this.touchend, ts);
|
||||
ts.x = 0;
|
||||
ts.y = 0;
|
||||
ts.touchEnd = false;
|
||||
}
|
||||
|
||||
if (ts.moving) {
|
||||
if (tpo) {
|
||||
tpe = tpo.events || tpo._entityCollection && tpo._entityCollection.events || tpo._layer.events;
|
||||
tpe.dispatch(tpe.touchmove, ts);
|
||||
}
|
||||
this.dispatch(this.touchmove, ts);
|
||||
ts.prev_x = ts.x;
|
||||
ts.prev_y = ts.y;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const EVENT_NAMES = [
|
||||
/**
|
||||
* Triggered before scene frame is rendered(before render nodes).
|
||||
* @event og.RendererEvents#draw
|
||||
*/
|
||||
"draw",
|
||||
|
||||
/**
|
||||
* Triggered when screen is resized.
|
||||
* @event og.RendererEvents#resize
|
||||
*/
|
||||
"resize",
|
||||
|
||||
/**
|
||||
* Mouse is moving.
|
||||
* @event og.RendererEvents#mousemove
|
||||
*/
|
||||
"mousemove",
|
||||
|
||||
/**
|
||||
* Mouse is just stopped.
|
||||
* @event og.RendererEvents#mousestop
|
||||
*/
|
||||
"mousestop",
|
||||
|
||||
/**
|
||||
* Mouse left button clicked.
|
||||
* @event og.RendererEvents#lclick
|
||||
*/
|
||||
"lclick",
|
||||
|
||||
/**
|
||||
* Mouse right button clicked.
|
||||
* @event og.RendererEvents#rclick
|
||||
*/
|
||||
"rclick",
|
||||
|
||||
/**
|
||||
* Mouse middle button clicked.
|
||||
* @event og.RendererEvents#mclick
|
||||
*/
|
||||
"mclick",
|
||||
|
||||
/**
|
||||
* Mouse left button double click.
|
||||
* @event og.RendererEvents#ldblclick
|
||||
*/
|
||||
"ldblclick",
|
||||
|
||||
/**
|
||||
* Mouse right button double click.
|
||||
* @event og.RendererEvents#rdblclick
|
||||
*/
|
||||
"rdblclick",
|
||||
|
||||
/**
|
||||
* Mouse middle button double click.
|
||||
* @event og.RendererEvents#mdblclick
|
||||
*/
|
||||
"mdblclick",
|
||||
|
||||
/**
|
||||
* Mouse left button up(stop pressing).
|
||||
* @event og.RendererEvents#lup
|
||||
*/
|
||||
"lup",
|
||||
|
||||
/**
|
||||
* Mouse right button up(stop pressing).
|
||||
* @event og.RendererEvents#rup
|
||||
*/
|
||||
"rup",
|
||||
|
||||
/**
|
||||
* Mouse middle button up(stop pressing).
|
||||
* @event og.RendererEvents#mup
|
||||
*/
|
||||
"mup",
|
||||
|
||||
/**
|
||||
* Mouse left button is just pressed down(start pressing).
|
||||
* @event og.RendererEvents#ldown
|
||||
*/
|
||||
"ldown",
|
||||
|
||||
/**
|
||||
* Mouse right button is just pressed down(start pressing).
|
||||
* @event og.RendererEvents#rdown
|
||||
*/
|
||||
"rdown",
|
||||
|
||||
/**
|
||||
* Mouse middle button is just pressed down(start pressing).
|
||||
* @event og.RendererEvents#mdown
|
||||
*/
|
||||
"mdown",
|
||||
|
||||
/**
|
||||
* Mouse left button is pressing.
|
||||
* @event og.RendererEvents#lhold
|
||||
*/
|
||||
"lhold",
|
||||
|
||||
/**
|
||||
* Mouse right button is pressing.
|
||||
* @event og.RendererEvents#rhold
|
||||
*/
|
||||
"rhold",
|
||||
|
||||
/**
|
||||
* Mouse middle button is pressing.
|
||||
* @event og.RendererEvents#mhold
|
||||
*/
|
||||
"mhold",
|
||||
|
||||
/**
|
||||
* Mouse wheel is rotated.
|
||||
* @event og.RendererEvents#mousewheel
|
||||
*/
|
||||
"mousewheel",
|
||||
|
||||
/**
|
||||
* Triggered when touching starts.
|
||||
* @event og.RendererEvents#touchstart
|
||||
*/
|
||||
"touchstart",
|
||||
|
||||
/**
|
||||
* Triggered when touching ends.
|
||||
* @event og.RendererEvents#touchend
|
||||
*/
|
||||
"touchend",
|
||||
|
||||
/**
|
||||
* Triggered when touching cancel.
|
||||
* @event og.RendererEvents#touchcancel
|
||||
*/
|
||||
"touchcancel",
|
||||
|
||||
/**
|
||||
* Triggered when touch is move.
|
||||
* @event og.RendererEvents#touchmove
|
||||
*/
|
||||
"touchmove",
|
||||
|
||||
/**
|
||||
* Triggered when double touch.
|
||||
* @event og.RendererEvents#doubletouch
|
||||
*/
|
||||
"doubletouch",
|
||||
|
||||
/**
|
||||
* Triggered when touch leaves picked object.
|
||||
* @event og.RendererEvents#touchleave
|
||||
*/
|
||||
"touchleave",
|
||||
|
||||
/**
|
||||
* Triggered when touch enter picking object.
|
||||
* @event og.RendererEvents#touchenter
|
||||
*/
|
||||
"touchenter"
|
||||
];
|
||||
|
||||
export { RendererEvents };
|
||||
79
src/og/scene/Axes.js
Normal file
79
src/og/scene/Axes.js
Normal file
@ -0,0 +1,79 @@
|
||||
/**
|
||||
* @module og/scene/Axes
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { RenderNode } from './RenderNode.js';
|
||||
import { ShaderProgram } from '../webgl/ShaderProgram.js';
|
||||
import { types } from '../webgl/types.js';
|
||||
|
||||
class Axes extends RenderNode {
|
||||
constructor(size) {
|
||||
super("Axes");
|
||||
|
||||
this.size = size || 100;
|
||||
this.axesBuffer = null;
|
||||
this.axesColorBuffer = null;
|
||||
}
|
||||
|
||||
initialization() {
|
||||
this.createAxisBuffer(this.size);
|
||||
this.drawMode = this.renderer.handler.gl.LINES;
|
||||
this.renderer.handler.addShaderProgram(new ShaderProgram("axesShader", {
|
||||
uniforms: {
|
||||
projectionViewMatrix: { type: types.MAT4 }
|
||||
},
|
||||
attributes: {
|
||||
aVertexPosition: { type: types.VEC3, enableArray: true },
|
||||
aVertexColor: { type: types.VEC4, enableArray: true }
|
||||
},
|
||||
vertexShader:
|
||||
'attribute vec3 aVertexPosition;\
|
||||
attribute vec4 aVertexColor;\
|
||||
uniform mat4 projectionViewMatrix;\
|
||||
varying vec4 vColor;\
|
||||
void main(void) {\
|
||||
gl_Position = projectionViewMatrix * vec4(aVertexPosition, 1.0);\
|
||||
vColor = aVertexColor;\
|
||||
}',
|
||||
fragmentShader:
|
||||
'precision highp float;\
|
||||
varying vec4 vColor;\
|
||||
void main(void) {\
|
||||
gl_FragColor = vColor;\
|
||||
}'
|
||||
}));
|
||||
}
|
||||
|
||||
frame() {
|
||||
|
||||
this.renderer.handler.shaderPrograms.axesShader.activate().set({
|
||||
projectionViewMatrix: this.renderer.activeCamera._projectionViewMatrix._m,
|
||||
aVertexPosition: this.axisBuffer,
|
||||
aVertexColor: this.axisColorBuffer
|
||||
});
|
||||
|
||||
this.renderer.handler.shaderPrograms.axesShader.drawArray(this.drawMode, this.axisBuffer.numItems);
|
||||
}
|
||||
|
||||
createAxisBuffer(gridSize) {
|
||||
|
||||
var vertices = [
|
||||
0.0, 0.0, 0.0, gridSize - 1, 0.0, 0.0, // x - R
|
||||
0.0, 0.0, 0.0, 0.0, gridSize - 1, 0.0, // y - B
|
||||
0.0, 0.0, 0.0, 0.0, 0.0, gridSize - 1 // z - G
|
||||
];
|
||||
|
||||
var colors = [
|
||||
1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, // x - R
|
||||
0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, // y - B
|
||||
0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0 // z - G
|
||||
];
|
||||
|
||||
this.axisBuffer = this.renderer.handler.createArrayBuffer(new Float32Array(vertices), 3, 6);
|
||||
this.axisColorBuffer = this.renderer.handler.createArrayBuffer(new Float32Array(colors), 4, 6);
|
||||
}
|
||||
};
|
||||
|
||||
export { Axes };
|
||||
1521
src/og/scene/Planet.js
Normal file
1521
src/og/scene/Planet.js
Normal file
File diff suppressed because it is too large
Load Diff
378
src/og/scene/RenderNode.js
Normal file
378
src/og/scene/RenderNode.js
Normal file
@ -0,0 +1,378 @@
|
||||
/**
|
||||
* @module og/scene/RenderNode
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { BaseNode } from './BaseNode.js';
|
||||
import { Events } from '../Events.js';
|
||||
import { FontAtlas } from '../utils/FontAtlas.js';
|
||||
import { TextureAtlas } from '../utils/TextureAtlas.js';
|
||||
|
||||
/**
|
||||
* Render node is a logical part of a render mechanism. Represents scene rendering.
|
||||
* Forexample one scene node for rendering the Earth, another one for rendering the Moon, another node for rendering stars etc.
|
||||
* Each render node has own model view space defined with matrices(scale, rotation, translation, transformation).
|
||||
* There are collections of ligh sources, entities and so on in the node.
|
||||
* Access to the node is renderer.renderNodes["Earth"]
|
||||
* @class
|
||||
* @extends {og.scene.Node}
|
||||
* @param {string} name - Node name.
|
||||
*/
|
||||
class RenderNode extends BaseNode {
|
||||
constructor(name) {
|
||||
super(name);
|
||||
|
||||
/**
|
||||
* Renderer that calls frame() callback.
|
||||
* @public
|
||||
* @type {og.Renderer}
|
||||
*/
|
||||
this.renderer = null;
|
||||
|
||||
this.drawMode = null;
|
||||
|
||||
/** Show rendering.
|
||||
* @public
|
||||
*/
|
||||
this.show = true;
|
||||
|
||||
this._isActive = true;
|
||||
|
||||
/**
|
||||
* Lighting calculations.
|
||||
* @public
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.lightEnabled = false;
|
||||
|
||||
/**
|
||||
* Point light array.
|
||||
* @private
|
||||
* @type {Array.<og.LightSource>}
|
||||
*/
|
||||
this._lights = [];
|
||||
this._lightsTransformedPositions = [];
|
||||
this._lightsParamsv = [];
|
||||
this._lightsParamsf = [];
|
||||
this._lightsNames = [];
|
||||
|
||||
/**
|
||||
* Entity collection array.
|
||||
* @public
|
||||
* @type {Array.<og.EntityCollection>}
|
||||
*/
|
||||
this.entityCollections = [];
|
||||
|
||||
/**
|
||||
* Texture atlas for the billboards images. One atlas per node.
|
||||
* @protected
|
||||
* @type {og.utils.TextureAtlas}
|
||||
*/
|
||||
this.billboardsTextureAtlas = new TextureAtlas();
|
||||
|
||||
/**
|
||||
* Texture font atlas for the font families and styles. One atlas per node.
|
||||
* @public
|
||||
* @type {og.utils.FontAtlas}
|
||||
*/
|
||||
this.fontAtlas = new FontAtlas();
|
||||
|
||||
/**
|
||||
* Render node events.
|
||||
* @public
|
||||
* @type {og.Events}
|
||||
*/
|
||||
this.events = new Events();
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign render node with renderer.
|
||||
* @public
|
||||
* @param {og.Renderer} renderer - Redner node's renderer.
|
||||
*/
|
||||
assignRenderer(renderer) {
|
||||
this.renderer = renderer;
|
||||
this.billboardsTextureAtlas.assignHandler(renderer.handler);
|
||||
this.fontAtlas.assignHandler(renderer.handler);
|
||||
renderer.addPickingCallback(this, this._entityCollectionPickingCallback);
|
||||
|
||||
for (var i = 0; i < this.entityCollections.length; i++) {
|
||||
this.entityCollections[i].setRenderer(renderer);
|
||||
}
|
||||
|
||||
this.initialization && this.initialization();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds entity collection.
|
||||
* @public
|
||||
* @param {og.EntityCollection} entityCollection - Entity collection.
|
||||
* @param {boolean} [isHidden] - If it's true that this collection has specific rendering.
|
||||
* @returns {og.scene.RenderNode}
|
||||
*/
|
||||
addEntityCollection(entityCollection, isHidden) {
|
||||
entityCollection.addTo(this, isHidden);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes entity collection.
|
||||
* @public
|
||||
* @param {og.EntityCollection} entityCollection - Entity collection for remove.
|
||||
*/
|
||||
removeEntityCollection(entityCollection) {
|
||||
entityCollection.remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds point light source.
|
||||
* @public
|
||||
* @param {og.LightSource} light - Light source.
|
||||
* @returns {og.scene.RenderNode}
|
||||
*/
|
||||
addLight(light) {
|
||||
light.addTo(this);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets light object by its name.
|
||||
* @public
|
||||
* @param {string} name - Point light name.
|
||||
* @returns {og.LightSource}
|
||||
*/
|
||||
getLightByName(name) {
|
||||
var li = this._lightsNames.indexOf(name);
|
||||
return this._lights[li];
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes light source.
|
||||
* @public
|
||||
* @param {og.LightSource} light - Light source object.
|
||||
*/
|
||||
removeLight(light) {
|
||||
light.remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls render frame node's callback. Used in renderer.
|
||||
* @public
|
||||
*/
|
||||
drawNode() {
|
||||
this._isActive && this._drawNodes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets render node activity.
|
||||
* @public
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isActive() {
|
||||
return this._isActive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rendering activation.
|
||||
* @public
|
||||
* @param {boolean} isActive - Activation flag.
|
||||
*/
|
||||
setActive(isActive) {
|
||||
this._isActive = isActive;
|
||||
for (var i = 0; i < this.childNodes.length; i++) {
|
||||
this.childNodes[i].setActive(isActive);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
setDrawMode(mode) {
|
||||
this.drawMode = mode;
|
||||
for (var i = 0; i < this.childNodes.length; i++) {
|
||||
this.childNodes[i].setDrawMode(mode);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* IMPORTANT: This function have to be called manualy in each render node frame callback, before drawing scene geometry.
|
||||
* @public
|
||||
*/
|
||||
transformLights() {
|
||||
var r = this.renderer;
|
||||
for (var i = 0; i < this._lights.length; i++) {
|
||||
var ii = i * 4;
|
||||
var tp;
|
||||
if (this._lights[i].directional) {
|
||||
tp = r.activeCamera._normalMatrix.mulVec(this._lights[i]._position);
|
||||
this._lightsTransformedPositions[ii + 3] = 0;
|
||||
} else {
|
||||
tp = r.activeCamera._viewMatrix.mulVec3(this._lights[i]._position);
|
||||
this._lightsTransformedPositions[ii + 3] = 1;
|
||||
}
|
||||
this._lightsTransformedPositions[ii] = tp.x;
|
||||
this._lightsTransformedPositions[ii + 1] = tp.y;
|
||||
this._lightsTransformedPositions[ii + 2] = tp.z;
|
||||
}
|
||||
}
|
||||
|
||||
updateBillboardsTexCoords() {
|
||||
for (var i = 0; i < this.entityCollections.length; i++) {
|
||||
this.entityCollections[i].billboardHandler.refreshTexCoordsArr();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_drawNodes() {
|
||||
for (var i = 0; i < this.childNodes.length; i++) {
|
||||
if (this.childNodes[i]._isActive)
|
||||
this.childNodes[i]._drawNodes();
|
||||
}
|
||||
|
||||
if (this.show) {
|
||||
if (this.frame) {
|
||||
//this.lightEnabled && this.transformLights();
|
||||
this.frame();
|
||||
}
|
||||
this.drawEntityCollections(this.entityCollections);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
drawEntityCollections(ec) {
|
||||
if (ec.length) {
|
||||
var gl = this.renderer.handler.gl;
|
||||
|
||||
gl.enable(gl.BLEND);
|
||||
gl.blendEquation(gl.FUNC_ADD);
|
||||
gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE);
|
||||
gl.disable(gl.CULL_FACE);
|
||||
|
||||
//Z-buffer offset
|
||||
gl.enable(gl.POLYGON_OFFSET_FILL);
|
||||
gl.polygonOffset(0, -637000);
|
||||
|
||||
//billboards pass
|
||||
gl.activeTexture(gl.TEXTURE0);
|
||||
gl.bindTexture(gl.TEXTURE_2D, this.billboardsTextureAtlas.texture);
|
||||
|
||||
var i = ec.length;
|
||||
while (i--) {
|
||||
var eci = ec[i];
|
||||
if (eci._animatedOpacity) {
|
||||
//first begin draw event
|
||||
eci.events.dispatch(eci.events.draw, eci);
|
||||
eci.billboardHandler.draw();
|
||||
}
|
||||
}
|
||||
|
||||
//labels pass
|
||||
var fa = this.fontAtlas.atlasesArr;
|
||||
for (i = 0; i < fa.length; i++) {
|
||||
gl.activeTexture(gl.TEXTURE0 + i);
|
||||
gl.bindTexture(gl.TEXTURE_2D, fa[i].texture);
|
||||
}
|
||||
|
||||
i = ec.length;
|
||||
while (i--) {
|
||||
ec[i]._animatedOpacity && ec[i].labelHandler.draw();
|
||||
}
|
||||
|
||||
//polyline pass
|
||||
i = ec.length;
|
||||
while (i--) {
|
||||
ec[i]._animatedOpacity && ec[i].polylineHandler.draw();
|
||||
}
|
||||
|
||||
//Z-buffer offset
|
||||
gl.polygonOffset(0, 0);
|
||||
|
||||
gl.disable(gl.POLYGON_OFFSET_FILL);
|
||||
gl.enable(gl.CULL_FACE);
|
||||
|
||||
//shapes pass
|
||||
i = ec.length;
|
||||
while (i--) {
|
||||
var eci = ec[i];
|
||||
if (eci._animatedOpacity) {
|
||||
eci.shapeHandler.draw();
|
||||
//post draw event
|
||||
eci.events.dispatch(eci.events.drawend, eci);
|
||||
}
|
||||
}
|
||||
|
||||
//pointClouds pass
|
||||
i = ec.length;
|
||||
while (i--) {
|
||||
ec[i]._animatedOpacity && ec[i].pointCloudHandler.draw();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
drawPickingEntityCollections(ec) {
|
||||
if (ec.length) {
|
||||
|
||||
var gl = this.renderer.handler.gl;
|
||||
|
||||
gl.disable(gl.CULL_FACE);
|
||||
|
||||
//Z-buffer offset
|
||||
gl.enable(gl.POLYGON_OFFSET_FILL);
|
||||
gl.polygonOffset(0, -637000);
|
||||
|
||||
//billoard pass
|
||||
var i = ec.length;
|
||||
while (i--) {
|
||||
ec[i]._visibility && ec[i].billboardHandler.drawPicking();
|
||||
}
|
||||
|
||||
//label pass
|
||||
i = ec.length;
|
||||
while (i--) {
|
||||
ec[i]._visibility && ec[i].labelHandler.drawPicking();
|
||||
}
|
||||
|
||||
gl.polygonOffset(0, 0);
|
||||
|
||||
gl.disable(gl.POLYGON_OFFSET_FILL);
|
||||
gl.enable(gl.CULL_FACE);
|
||||
|
||||
////polylines pass
|
||||
//i = ec.length;
|
||||
//while (i--) {
|
||||
// ec[i]._visibility && ec[i].polylineHandler.drawPicking();
|
||||
//}
|
||||
|
||||
////shapes pass
|
||||
//i = ec.length;
|
||||
//while (i--) {
|
||||
// ec[i]._visibility && ec[i].shapeHandler.drawPicking();
|
||||
//}
|
||||
|
||||
////pointClouds pass
|
||||
//i = ec.length;
|
||||
//while (i--) {
|
||||
// ec[i]._visibility && ec[i].pointCloudHandler.drawPicking();
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Picking entity frame callback
|
||||
* @private
|
||||
*/
|
||||
_entityCollectionPickingCallback() {
|
||||
this.drawPickingEntityCollections(this.entityCollections);
|
||||
}
|
||||
};
|
||||
|
||||
export { RenderNode };
|
||||
110
src/og/scene/SkyBox.js
Normal file
110
src/og/scene/SkyBox.js
Normal file
@ -0,0 +1,110 @@
|
||||
/**
|
||||
* @module og/scene/SkyBox
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as shaders from '../shaderProgram/skybox.js';
|
||||
import { RenderNode } from './RenderNode.js';
|
||||
|
||||
const RESOURCES_URL = "";
|
||||
|
||||
class SkyBox extends RenderNode {
|
||||
constructor(params) {
|
||||
super("skybox");
|
||||
this.params = params;
|
||||
this.vertexPositionBuffer = null;
|
||||
this.texture = null;
|
||||
}
|
||||
|
||||
static createDefault(RESOURCES_URL) {
|
||||
return new SkyBox({
|
||||
"nx": RESOURCES_URL + "skybox/gal/_nx.jpg",
|
||||
"px": RESOURCES_URL + "skybox/gal/_px.jpg",
|
||||
"py": RESOURCES_URL + "skybox/gal/_py.jpg",
|
||||
"ny": RESOURCES_URL + "skybox/gal/_ny.jpg",
|
||||
"pz": RESOURCES_URL + "skybox/gal/_pz.jpg",
|
||||
"nz": RESOURCES_URL + "skybox/gal/_nz.jpg"
|
||||
});
|
||||
}
|
||||
|
||||
initialization() {
|
||||
this.renderer.handler.addShaderProgram(shaders.skybox(), true);
|
||||
this.texture = this.renderer.handler.loadCubeMapTexture(this.params);
|
||||
this._createBuffers();
|
||||
this.drawMode = this.renderer.handler.gl.TRIANGLES;
|
||||
}
|
||||
|
||||
frame() {
|
||||
var h = this.renderer.handler;
|
||||
var gl = h.gl;
|
||||
var cam = this.renderer.activeCamera;
|
||||
gl.disable(h.gl.DEPTH_TEST);
|
||||
|
||||
h.shaderPrograms.skybox.activate();
|
||||
sh = h.shaderPrograms.skybox._program;
|
||||
var shu = sh.uniforms;
|
||||
gl.uniformMatrix4fv(shu.projectionViewMatrix._pName, false, cam._projectionViewMatrix._m);
|
||||
gl.uniform3fv(shu.pos._pName, cam.eye.toVec());
|
||||
gl.activeTexture(gl.TEXTURE0);
|
||||
gl.bindTexture(gl.TEXTURE_CUBE_MAP, this.texture);
|
||||
gl.uniform1i(shu.uSampler._pName, 0);
|
||||
var buf = this.vertexPositionBuffer;
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
|
||||
gl.vertexAttribPointer(sh.attributes.aVertexPosition._pName, buf.itemSize, gl.FLOAT, false, 0, 0);
|
||||
|
||||
gl.drawArrays(this.drawMode, 0, buf.numItems);
|
||||
h.gl.enable(h.gl.DEPTH_TEST);
|
||||
}
|
||||
|
||||
_createBuffers() {
|
||||
|
||||
var vertices = new Float32Array([
|
||||
- 10000.0, 10000.0, - 10000.0,
|
||||
- 10000.0, - 10000.0, - 10000.0,
|
||||
10000.0, - 10000.0, - 10000.0,
|
||||
10000.0, - 10000.0, - 10000.0,
|
||||
10000.0, 10000.0, - 10000.0,
|
||||
- 10000.0, 10000.0, - 10000.0,
|
||||
|
||||
- 10000.0, - 10000.0, 10000.0,
|
||||
- 10000.0, - 10000.0, - 10000.0,
|
||||
- 10000.0, 10000.0, - 10000.0,
|
||||
- 10000.0, 10000.0, - 10000.0,
|
||||
- 10000.0, 10000.0, 10000.0,
|
||||
- 10000.0, - 10000.0, 10000.0,
|
||||
|
||||
10000.0, - 10000.0, - 10000.0,
|
||||
10000.0, - 10000.0, 10000.0,
|
||||
10000.0, 10000.0, 10000.0,
|
||||
10000.0, 10000.0, 10000.0,
|
||||
10000.0, 10000.0, - 10000.0,
|
||||
10000.0, - 10000.0, - 10000.0,
|
||||
|
||||
- 10000.0, - 10000.0, 10000.0,
|
||||
- 10000.0, 10000.0, 10000.0,
|
||||
10000.0, 10000.0, 10000.0,
|
||||
10000.0, 10000.0, 10000.0,
|
||||
10000.0, - 10000.0, 10000.0,
|
||||
- 10000.0, - 10000.0, 10000.0,
|
||||
|
||||
- 10000.0, 10000.0, - 10000.0,
|
||||
10000.0, 10000.0, - 10000.0,
|
||||
10000.0, 10000.0, 10000.0,
|
||||
10000.0, 10000.0, 10000.0,
|
||||
- 10000.0, 10000.0, 10000.0,
|
||||
- 10000.0, 10000.0, - 10000.0,
|
||||
|
||||
- 10000.0, - 10000.0, - 10000.0,
|
||||
- 10000.0, - 10000.0, 10000.0,
|
||||
10000.0, - 10000.0, - 10000.0,
|
||||
10000.0, - 10000.0, - 10000.0,
|
||||
- 10000.0, - 10000.0, 10000.0,
|
||||
10000.0, - 10000.0, 10000.0
|
||||
]);
|
||||
|
||||
this.vertexPositionBuffer = this.renderer.handler.createArrayBuffer(vertices, 3, vertices.length / 3);
|
||||
}
|
||||
};
|
||||
|
||||
export { SkyBox };
|
||||
430
src/og/shapes/BaseShape.js
Normal file
430
src/og/shapes/BaseShape.js
Normal file
@ -0,0 +1,430 @@
|
||||
goog.provide('og.shape.BaseShape');
|
||||
|
||||
goog.require('og.math.Vector3');
|
||||
goog.require('og.math.Quaternion');
|
||||
goog.require('og.math.Matrix4');
|
||||
|
||||
/**
|
||||
* Base geometry shape class.
|
||||
* @class
|
||||
* @param {Object} options - Shape parameters:
|
||||
* @param {og.math.Vector3} [options.position] - Shape position.
|
||||
* @param {og.math.Quaternion} [options.orientation] - Shape orientation(rotation).
|
||||
* @param {og.math.Vector3} [options.scale] - Scale vector.
|
||||
* @param {Array.<number,number,number,number>} [options.color] - Shape RGBA color.
|
||||
* @param {string} [options.src] - Texture image url source.
|
||||
* @param {boolean} [options.visibility] - Shape visibility.
|
||||
*/
|
||||
og.shape.BaseShape = function (options) {
|
||||
|
||||
options = options || {};
|
||||
|
||||
/**
|
||||
* Unic identifier.
|
||||
* @public
|
||||
* @readonly
|
||||
* @type {number}
|
||||
*/
|
||||
this.id = og.shape.BaseShape.__staticId++;
|
||||
|
||||
/**
|
||||
* Shape position.
|
||||
* @public
|
||||
* @type {og.math.Vector3}
|
||||
*/
|
||||
this.position = options.position || new og.math.Vector3();
|
||||
|
||||
/**
|
||||
* Shape orientation(rotation)
|
||||
* @public
|
||||
* @type {og.math.Quaternion}
|
||||
*/
|
||||
this.orientation = options.orientation || new og.math.Quaternion(0.0, 0.0, 0.0, 1.0);
|
||||
|
||||
/**
|
||||
* Scale.
|
||||
* @public
|
||||
* @type {og.math.Vector3}
|
||||
*/
|
||||
this.scale = options.scale || new og.math.Vector3(1.0, 1.0, 1.0);
|
||||
|
||||
/**
|
||||
* Shape RGBA color.
|
||||
* @public
|
||||
* @type {Array.<number,number,number,number>}
|
||||
*/
|
||||
this.color = options.color || [1.0, 1.0, 1.0, 1.0];
|
||||
|
||||
/**
|
||||
* Shape visibility.
|
||||
* @public
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.visibility = (options.visibility != undefined ? options.visibility : true);
|
||||
|
||||
/**
|
||||
* Image url source.
|
||||
* @protected
|
||||
* @type {string}
|
||||
*/
|
||||
this._src = options.src || null;
|
||||
|
||||
/**
|
||||
* Vertices position gl buffer.
|
||||
* @protected
|
||||
*/
|
||||
this._positionBuffer = null;
|
||||
|
||||
/**
|
||||
* Vertices normal gl buffer.
|
||||
* @protected
|
||||
*/
|
||||
this._normalBuffer = null;
|
||||
|
||||
/**
|
||||
* Vertices indexes gl buffer.
|
||||
* @protected
|
||||
*/
|
||||
this._indexBuffer = null;
|
||||
|
||||
/**
|
||||
* Vertex texture coordinates gl buffer.
|
||||
* @protected
|
||||
*/
|
||||
this._textureCoordBuffer = null;
|
||||
|
||||
/**
|
||||
* Vertex positions.
|
||||
* @protected
|
||||
* @type {Array.<number>}
|
||||
*/
|
||||
this._positionData = [];
|
||||
|
||||
/**
|
||||
* Vertex normals.
|
||||
* @protected
|
||||
* @type {Array.<number>}
|
||||
*/
|
||||
this._normalData = [];
|
||||
|
||||
/**
|
||||
* Vertex indeces.
|
||||
* @protected
|
||||
* @type {Array.<number>}
|
||||
*/
|
||||
this._indexData = [];
|
||||
|
||||
/**
|
||||
* Vertex texture coordinates.
|
||||
* @protected
|
||||
* @type {Array.<number>}
|
||||
*/
|
||||
this._textureCoordData = [];
|
||||
|
||||
/**
|
||||
* Scale matrix.
|
||||
* @protected
|
||||
* @type {og.math.Matrix4}
|
||||
*/
|
||||
this._mxScale = new og.math.Matrix4().setIdentity();
|
||||
|
||||
/**
|
||||
* Translation matrix.
|
||||
* @protected
|
||||
* @type {og.math.Matrix4}
|
||||
*/
|
||||
this._mxTranslation = new og.math.Matrix4().setIdentity();
|
||||
|
||||
/**
|
||||
* Model matrix.
|
||||
* @protected
|
||||
* @type {og.math.Matrix4}
|
||||
*/
|
||||
this._mxModel = new og.math.Matrix4().setIdentity();
|
||||
|
||||
/**
|
||||
* Gl texture pointer.
|
||||
* @protected
|
||||
*/
|
||||
this.texture = null;
|
||||
|
||||
/**
|
||||
* Assigned render node.
|
||||
* @protected
|
||||
* @type {og.scene.RenderNode}
|
||||
*/
|
||||
this._renderNode = null;
|
||||
|
||||
/**
|
||||
* Assigned picking color.
|
||||
* @protected
|
||||
* @type {Array.<number,number,number>}
|
||||
*/
|
||||
this._pickingColor = [0.0, 0.0, 0.0, 0.0];
|
||||
|
||||
/**
|
||||
* Entity instance that holds this shape.
|
||||
* @protected
|
||||
* @type {og.Entity}
|
||||
*/
|
||||
this._entity = null;
|
||||
|
||||
/**
|
||||
* Handler that stores and renders this shape object.
|
||||
* @protected
|
||||
* @type {og.ShapeHandler}
|
||||
*/
|
||||
this._handler = null;
|
||||
|
||||
/**
|
||||
* Shape handler array index.
|
||||
* @protected
|
||||
* @type {number}
|
||||
*/
|
||||
this._handlerIndex = -1;
|
||||
};
|
||||
|
||||
og.shape.BaseShape.__staticId = 0;
|
||||
|
||||
/**
|
||||
* Clear shape parameters.
|
||||
* @public
|
||||
*/
|
||||
og.shape.BaseShape.prototype.clear = function () {
|
||||
|
||||
this.position.set(0.0, 0.0, 0.0);
|
||||
this.orientation.set(0.0, 0.0, 0.0, 1.0);
|
||||
this.scale.set(1.0, 1.0, 1.0);
|
||||
|
||||
this._positionData.length = 0;
|
||||
this._normalData.length = 0;
|
||||
this._indexData.length = 0;
|
||||
|
||||
this._mxScale.setIdentity();
|
||||
this._mxTranslation.setIdentity();
|
||||
this._mxModel.setIdentity();
|
||||
|
||||
this._renderNode.handler.gl.deleteTexture(this.texture);
|
||||
this.texture = null;
|
||||
|
||||
this._deleteBuffers();
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets shape color.
|
||||
* @public
|
||||
* @param {Array.<number,number,number,number>} color - RGBA color values array.
|
||||
*/
|
||||
og.shape.BaseShape.prototype.setColor = function (color) {
|
||||
this.color[0] = color[0];
|
||||
this.color[1] = color[1];
|
||||
this.color[2] = color[2];
|
||||
this.color[3] = color[3];
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets shape color.
|
||||
* @public
|
||||
* @param {og.math.Vector4} color - RGBA color vector.
|
||||
*/
|
||||
og.shape.BaseShape.prototype.setColor4v = function (color) {
|
||||
this.color[0] = color.x;
|
||||
this.color[1] = color.y;
|
||||
this.color[2] = color.z;
|
||||
this.color[3] = color.w;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets shape opacity value.
|
||||
* @public
|
||||
* @param {number} opacity - Opacity value.
|
||||
*/
|
||||
og.shape.BaseShape.prototype.setOpacity = function (opacity) {
|
||||
this.color[3] = opacity;
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete gl buffers.
|
||||
* @protected
|
||||
*/
|
||||
og.shape.BaseShape.prototype._deleteBuffers = function () {
|
||||
var r = this._renderNode.renderer,
|
||||
gl = r.handler.gl;
|
||||
|
||||
gl.deleteBuffer(this._positionBuffer);
|
||||
gl.deleteBuffer(this._normalBuffer);
|
||||
gl.deleteBuffer(this._indexBuffer);
|
||||
|
||||
this._positionBuffer = null;
|
||||
this._normalBuffer = null;
|
||||
this._indexBuffer = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets shape visibility.
|
||||
* @public
|
||||
* @param {boolean} visibility - Visibility.
|
||||
*/
|
||||
og.shape.BaseShape.prototype.setVisibility = function (visibility) {
|
||||
this.visibility = visibility;
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets visibilty flag.
|
||||
* @public
|
||||
* @returns {boolean}
|
||||
*/
|
||||
og.shape.BaseShape.prototype.getVisibility = function () {
|
||||
return this.visibility;
|
||||
};
|
||||
|
||||
/**
|
||||
* Assign render node.
|
||||
* @public
|
||||
* @param {og.scene.RenderNode} renderNode - Render node to assign.
|
||||
*/
|
||||
og.shape.BaseShape.prototype.setRenderNode = function (renderNode) {
|
||||
this._renderNode = renderNode;
|
||||
this._createBuffers();
|
||||
if (this._src) {
|
||||
var img = new Image();
|
||||
var that = this;
|
||||
img.onload = function () {
|
||||
that.texture = renderNode.renderer.handler.createTexture(this);
|
||||
};
|
||||
img.src = this._src;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets shape position.
|
||||
* @public
|
||||
* @param {og.math.Vector3} position - Shape position.
|
||||
*/
|
||||
og.shape.BaseShape.prototype.setPosition3v = function (position) {
|
||||
this.position.copy(position);
|
||||
this._mxTranslation.translateToPosition(position);
|
||||
this.refresh();
|
||||
};
|
||||
|
||||
/**
|
||||
* Translate shape position to vector.
|
||||
* @public
|
||||
* @param {og.math.Vector3} vec - Translation vector.
|
||||
*/
|
||||
og.shape.BaseShape.prototype.translate3v = function (vec) {
|
||||
this.position.addA(vec);
|
||||
this._mxTranslation.translate(vec);
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets shape scale.
|
||||
* @param {og.math.Vector3} scale - Scale vector.
|
||||
*/
|
||||
og.shape.BaseShape.prototype.setScale3v = function (scale) {
|
||||
this.scale.copy(scale);
|
||||
this._mxScale.scale(scale);
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes shape from shape handler.
|
||||
* @public
|
||||
*/
|
||||
og.shape.BaseShape.prototype.remove = function () {
|
||||
this._entity = null;
|
||||
this._handler && this._handler.remove(this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Assign picking color.
|
||||
* @protected
|
||||
* @param {og.math.Vector3} color - Picking RGB color.
|
||||
*/
|
||||
og.shape.BaseShape.prototype.setPickingColor3v = function (color) {
|
||||
//...
|
||||
//TODO: check the renderer before
|
||||
//...
|
||||
this._pickingColor[0] = color.x / 255.0;
|
||||
this._pickingColor[1] = color.y / 255.0;
|
||||
this._pickingColor[2] = color.z / 255.0;
|
||||
this._pickingColor[3] = 1.0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates buffers.
|
||||
* @protected
|
||||
*/
|
||||
og.shape.BaseShape.prototype._createBuffers = function () {
|
||||
this._deleteBuffers();
|
||||
var r = this._renderNode.renderer;
|
||||
this._positionBuffer = r.handler.createArrayBuffer(new Float32Array(this._positionData), 3, this._positionData.length / 3);
|
||||
this._normalBuffer = r.handler.createArrayBuffer(new Float32Array(this._normalData), 3, this._normalData.length / 3);
|
||||
this._indexBuffer = r.handler.createElementArrayBuffer(new Uint16Array(this._indexData), 1, this._indexData.length);
|
||||
this._textureCoordBuffer = r.handler.createArrayBuffer(new Float32Array(this._textureCoordData), 2, this._textureCoordData.length / 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update model matrix.
|
||||
* @public
|
||||
*/
|
||||
og.shape.BaseShape.prototype.refresh = function () {
|
||||
this._mxModel = this._mxTranslation.mul(this.orientation.getMatrix4().mul(this._mxScale));
|
||||
};
|
||||
|
||||
/**
|
||||
* Shape rendering.
|
||||
* @public
|
||||
*/
|
||||
og.shape.BaseShape.prototype.draw = function () {
|
||||
if (this.visibility) {
|
||||
var rn = this._renderNode;
|
||||
var r = rn.renderer;
|
||||
|
||||
var sh, p,
|
||||
gl = r.handler.gl;
|
||||
|
||||
if (rn.lightEnabled) {
|
||||
sh = r.handler.shaderPrograms.shape_wl;
|
||||
p = sh._program;
|
||||
sha = p.attributes,
|
||||
shu = p.uniforms;
|
||||
|
||||
sh.activate();
|
||||
|
||||
gl.uniform4fv(shu.lightsPositions._pName, rn._lightsTransformedPositions);
|
||||
gl.uniform3fv(shu.lightsParamsv._pName, rn._lightsParamsv);
|
||||
gl.uniform1fv(shu.lightsParamsf._pName, rn._lightsParamsf);
|
||||
gl.uniformMatrix4fv(shu.projectionMatrix._pName, false, r.activeCamera._projectionMatrix._m);
|
||||
gl.uniformMatrix4fv(shu.viewMatrix._pName, false, r.activeCamera._viewMatrix._m);
|
||||
gl.uniformMatrix3fv(shu.normalMatrix._pName, false, r.activeCamera._normalMatrix._m);
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this._normalBuffer);
|
||||
gl.vertexAttribPointer(sha.aVertexNormal._pName, this._normalBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
} else {
|
||||
sh = r.handler.shaderPrograms.shape_nl;
|
||||
p = sh._program;
|
||||
sha = p.attributes,
|
||||
shu = p.uniforms;
|
||||
|
||||
sh.activate();
|
||||
|
||||
gl.uniformMatrix4fv(shu.projectionViewMatrix._pName, false, r.activeCamera._projectionViewMatrix._m);
|
||||
}
|
||||
|
||||
gl.uniform4fv(shu.uColor._pName, this.color);
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this._positionBuffer);
|
||||
gl.vertexAttribPointer(sha.aVertexPosition._pName, this._positionBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
gl.uniformMatrix4fv(shu.modelMatrix._pName, false, this._mxModel._m);
|
||||
|
||||
gl.activeTexture(gl.TEXTURE0);
|
||||
gl.bindTexture(gl.TEXTURE_2D, this.texture);
|
||||
gl.uniform1i(shu.uSampler._pName, 0);
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this._textureCoordBuffer);
|
||||
gl.vertexAttribPointer(sha.aTextureCoord._pName, this._textureCoordBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
|
||||
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._indexBuffer);
|
||||
gl.drawElements(r.handler.gl.TRIANGLES, this._indexBuffer.numItems, gl.UNSIGNED_SHORT, 0);
|
||||
}
|
||||
};
|
||||
161
src/og/shapes/Icosphere.js
Normal file
161
src/og/shapes/Icosphere.js
Normal file
@ -0,0 +1,161 @@
|
||||
goog.provide('og.shape.Icosphere');
|
||||
|
||||
goog.require('og.shape.BaseShape');
|
||||
|
||||
/**
|
||||
* @class
|
||||
* @extends {og.shape.BaseShape}
|
||||
* @param {Object} options - Icosphere parameters:
|
||||
* @param {og.math.Vector3} [options.position] - Icosphere position.
|
||||
* @param {og.math.Quaternion} [options.orientation] - Icosphere orientation(rotation).
|
||||
* @param {og.math.Vector3} [options.scale] - Scale vector.
|
||||
* @param {Array.<number,number,number,number>} [options.color] - Icosphere RGBA color.
|
||||
* @param {string} [options.src] - Texture image url source.
|
||||
* @param {boolean} [options.visibility] - Icosphere visibility.
|
||||
* @param {number} [options.size] - Icosphere radius.
|
||||
* @param {number} [options.level] - Icosphere complexity level.
|
||||
*/
|
||||
og.shape.Icosphere = function (options) {
|
||||
|
||||
goog.base(this, options);
|
||||
|
||||
/**
|
||||
* Icosphere radius.
|
||||
* @protected
|
||||
* @type {number}
|
||||
*/
|
||||
this._size = options.size || 1.0;
|
||||
|
||||
/**
|
||||
* Icosphere recursion level.
|
||||
* @protected
|
||||
* @type {number}
|
||||
*/
|
||||
this._level = options.level || 0;
|
||||
|
||||
this._index = 0;
|
||||
this._middlePointIndexCache = {};
|
||||
|
||||
this._createData();
|
||||
};
|
||||
|
||||
goog.inherits(og.shape.Icosphere, og.shape.BaseShape);
|
||||
|
||||
// add vertex to mesh, fix position to be on unit sphere, return index
|
||||
og.shape.Icosphere.prototype._addVertex = function (p) {
|
||||
var length = Math.sqrt(p[0] * p[0] + p[1] * p[1] + p[2] * p[2]);
|
||||
this._positionData.push(this._size * p[0] / length, this._size * p[1] / length, this._size * p[2] / length);
|
||||
return this._index++;
|
||||
};
|
||||
|
||||
// return index of point in the middle of p1 and p2
|
||||
og.shape.Icosphere.prototype._getMiddlePoint = function (p1, p2) {
|
||||
// first check if we have it already
|
||||
var firstIsSmaller = p1 < p2;
|
||||
var smallerIndex = firstIsSmaller ? p1 : p2;
|
||||
var greaterIndex = firstIsSmaller ? p2 : p1;
|
||||
|
||||
var key = smallerIndex + "_" + greaterIndex;
|
||||
|
||||
var ret = this._middlePointIndexCache[key];
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
var point1 = [this._positionData[p1 * 3], this._positionData[p1 * 3 + 1], this._positionData[p1 * 3 + 2]];
|
||||
var point2 = [this._positionData[p2 * 3], this._positionData[p2 * 3 + 1], this._positionData[p2 * 3 + 2]];
|
||||
var middle = [(point1[0] + point2[0]) / 2.0, (point1[1] + point2[1]) / 2.0, (point1[2] + point2[2]) / 2.0];
|
||||
|
||||
// add vertex makes sure point is on unit sphere
|
||||
var i = this._addVertex(middle);
|
||||
this._middlePointIndexCache[key] = i;
|
||||
return i;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create specific shape vertices data.
|
||||
* @protected
|
||||
* @virtual
|
||||
*/
|
||||
og.shape.Icosphere.prototype._createData = function () {
|
||||
|
||||
this._positionData = [];
|
||||
this._indexData = [];
|
||||
this._index = 0;
|
||||
this._middlePointIndexCache = {};
|
||||
|
||||
// create 12 vertices of a icosahedron
|
||||
var t = (1.0 + Math.sqrt(5.0)) / 2.0;
|
||||
|
||||
this._addVertex([-1, t, 0]);
|
||||
this._addVertex([1, t, 0]);
|
||||
this._addVertex([-1, -t, 0]);
|
||||
this._addVertex([1, -t, 0]);
|
||||
|
||||
this._addVertex([0, -1, t]);
|
||||
this._addVertex([0, 1, t]);
|
||||
this._addVertex([0, -1, -t]);
|
||||
this._addVertex([0, 1, -t]);
|
||||
|
||||
this._addVertex([t, 0, -1]);
|
||||
this._addVertex([t, 0, 1]);
|
||||
this._addVertex([-t, 0, -1]);
|
||||
this._addVertex([-t, 0, 1]);
|
||||
|
||||
// create 20 triangles of the icosahedron
|
||||
var faces = [];
|
||||
|
||||
// 5 faces around point 0
|
||||
faces.push([0, 11, 5]);
|
||||
faces.push([0, 5, 1]);
|
||||
faces.push([0, 1, 7]);
|
||||
faces.push([0, 7, 10]);
|
||||
faces.push([0, 10, 11]);
|
||||
|
||||
// 5 adjacent faces
|
||||
faces.push([1, 5, 9]);
|
||||
faces.push([5, 11, 4]);
|
||||
faces.push([11, 10, 2]);
|
||||
faces.push([10, 7, 6]);
|
||||
faces.push([7, 1, 8]);
|
||||
|
||||
// 5 faces around point 3
|
||||
faces.push([3, 9, 4]);
|
||||
faces.push([3, 4, 2]);
|
||||
faces.push([3, 2, 6]);
|
||||
faces.push([3, 6, 8]);
|
||||
faces.push([3, 8, 9]);
|
||||
|
||||
// 5 adjacent faces
|
||||
faces.push([4, 9, 5]);
|
||||
faces.push([2, 4, 11]);
|
||||
faces.push([6, 2, 10]);
|
||||
faces.push([8, 6, 7]);
|
||||
faces.push([9, 8, 1]);
|
||||
|
||||
|
||||
// refine triangles
|
||||
for (var i = 0; i < this._level; i++) {
|
||||
var faces2 = [];
|
||||
for (var j = 0; j < faces.length; j++) {
|
||||
var tri = faces[j];
|
||||
// replace triangle by 4 triangles
|
||||
var a = this._getMiddlePoint(tri[0], tri[1]);
|
||||
var b = this._getMiddlePoint(tri[1], tri[2]);
|
||||
var c = this._getMiddlePoint(tri[2], tri[0]);
|
||||
|
||||
faces2.push([tri[0], a, c]);
|
||||
faces2.push([tri[1], b, a]);
|
||||
faces2.push([tri[2], c, b]);
|
||||
faces2.push([a, b, c]);
|
||||
}
|
||||
faces = faces2;
|
||||
}
|
||||
|
||||
for (var i = 0; i < faces.length; i++) {
|
||||
var tri = faces[i];
|
||||
this._indexData.push(tri[0]);
|
||||
this._indexData.push(tri[1]);
|
||||
this._indexData.push(tri[2]);
|
||||
}
|
||||
};
|
||||
95
src/og/shapes/Sphere.js
Normal file
95
src/og/shapes/Sphere.js
Normal file
@ -0,0 +1,95 @@
|
||||
goog.provide('og.shape.Sphere');
|
||||
|
||||
goog.require('og.shape.BaseShape');
|
||||
|
||||
/**
|
||||
* @class
|
||||
* @extends {og.shape.BaseShape}
|
||||
* @param {Object} options - Sphere parameters:
|
||||
* @param {og.math.Vector3} [options.position] - Sphere position.
|
||||
* @param {og.math.Quaternion} [options.orientation] - Sphere orientation(rotation).
|
||||
* @param {og.math.Vector3} [options.scale] - Scale vector.
|
||||
* @param {Array.<number,number,number,number>} [options.color] - Sphere RGBA color.
|
||||
* @param {string} [options.src] - Texture image url source.
|
||||
* @param {boolean} [options.visibility] - Sphere visibility.
|
||||
* @param {number} [options.radius=100] - Sphere radius.
|
||||
* @param {number} [options.latBands=16] - Number of latitude bands.
|
||||
* @param {number} [options.lonBands=16] - Number of longitude bands.
|
||||
*/
|
||||
og.shape.Sphere = function (options) {
|
||||
|
||||
goog.base(this, options);
|
||||
|
||||
/**
|
||||
* Sphere radius.
|
||||
* @protected
|
||||
* @type {number}
|
||||
*/
|
||||
this._radius = options.radius || 100;
|
||||
|
||||
/**
|
||||
* Number of latitude bands.
|
||||
* @protected
|
||||
* @type {number}
|
||||
*/
|
||||
this._latBands = options.latBands || 16;
|
||||
|
||||
/**
|
||||
* Number of longitude bands.
|
||||
* @protected
|
||||
* @type {number}
|
||||
*/
|
||||
this._lonBands = options.lonBands || 16;
|
||||
|
||||
this._createData();
|
||||
};
|
||||
|
||||
goog.inherits(og.shape.Sphere, og.shape.BaseShape);
|
||||
|
||||
/**
|
||||
* Create specific shape vertices data.
|
||||
* @protected
|
||||
* @virtual
|
||||
*/
|
||||
og.shape.Sphere.prototype._createData = function () {
|
||||
|
||||
for (var latNumber = 0; latNumber <= this._latBands; latNumber++) {
|
||||
var theta = latNumber * Math.PI / this._latBands;
|
||||
var sinTheta = Math.sin(theta);
|
||||
var cosTheta = Math.cos(theta);
|
||||
|
||||
for (var longNumber = 0; longNumber <= this._lonBands; longNumber++) {
|
||||
var phi = longNumber * 2 * Math.PI / this._lonBands;
|
||||
var sinPhi = Math.sin(phi);
|
||||
var cosPhi = Math.cos(phi);
|
||||
var x = cosPhi * sinTheta;
|
||||
var y = cosTheta;
|
||||
var z = sinPhi * sinTheta;
|
||||
var u = 1 - (longNumber / this._lonBands);
|
||||
var v = latNumber / this._latBands;
|
||||
this._normalData.push(x);
|
||||
this._normalData.push(y);
|
||||
this._normalData.push(z);
|
||||
this._textureCoordData.push(u);
|
||||
this._textureCoordData.push(v);
|
||||
this._positionData.push(this._radius * x);
|
||||
this._positionData.push(this._radius * y);
|
||||
this._positionData.push(this._radius * z);
|
||||
}
|
||||
}
|
||||
|
||||
for (var latNumber = 0; latNumber < this._latBands; latNumber++) {
|
||||
for (var longNumber = 0; longNumber < this._lonBands; longNumber++) {
|
||||
var first = (latNumber * (this._lonBands + 1)) + longNumber;
|
||||
var second = first + this._lonBands + 1;
|
||||
|
||||
this._indexData.push(first);
|
||||
this._indexData.push(first + 1);
|
||||
this._indexData.push(second);
|
||||
|
||||
this._indexData.push(second);
|
||||
this._indexData.push(first + 1);
|
||||
this._indexData.push(second + 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
136
src/og/utils/FontAtlas.js
Normal file
136
src/og/utils/FontAtlas.js
Normal file
@ -0,0 +1,136 @@
|
||||
/**
|
||||
* @module og/utils/FontAtlas
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { TextureAtlas } from './TextureAtlas.js';
|
||||
import { ImageCanvas } from '../ImageCanvas.js';
|
||||
import { QueueArray } from '../QueueArray.js';
|
||||
import { FontDetector } from './FontDetector.js';
|
||||
import { SDFCreator } from './SDFCreator.js';
|
||||
|
||||
const tokens = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
|
||||
'а', 'б', 'в', 'г', 'д', 'е', 'ё', 'ж', 'з', 'и', 'к', 'л', 'м', 'н', 'о', 'п', 'р', 'с', 'т', 'у', 'ф', 'х', 'ц', 'ч', 'ш', 'щ', 'ь', 'э', 'ъ', 'ю', 'я', 'й',
|
||||
'А', 'Б', 'В', 'Г', 'Д', 'Е', 'Ё', 'Ж', 'З', 'И', 'К', 'Л', 'М', 'Н', 'О', 'П', 'Р', 'С', 'Т', 'У', 'Ф', 'Х', 'Ц', 'Ч', 'Ш', 'Щ', 'Ь', 'Э', 'Ъ', 'Ю', 'Я', 'Й',
|
||||
'1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
|
||||
'`', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '-', '=', '[', ']', '{', '}', '\\', '|', ';', ':', '"', ',', '.', '/', '<', '>', '?', ' ', ' ', "'"];
|
||||
|
||||
class FontAtlas {
|
||||
constructor() {
|
||||
this.atlasesArr = [];
|
||||
this.atlasIndexes = {};
|
||||
this.tokenImageSize = 64;
|
||||
this.samplerArr = [0];
|
||||
this._handler = null;
|
||||
this.defaultFace = "arial";
|
||||
|
||||
this._counter = 0;
|
||||
this._pendingsQueue = new QueueArray();
|
||||
this.fontDetector = new FontDetector();
|
||||
|
||||
this._sdfCreator = new SDFCreator(256, 256);
|
||||
}
|
||||
|
||||
assignHandler(handler) {
|
||||
this._handler = handler;
|
||||
}
|
||||
|
||||
getFontIndex(face, style, weight) {
|
||||
return this.atlasIndexes[this.getFullIndex(face, style, weight)];
|
||||
}
|
||||
|
||||
getFullIndex(face, style, weight) {
|
||||
face = face && face.trim().toLowerCase();
|
||||
if (!face || face && !this.fontDetector.detect(face)) {
|
||||
face = this.defaultFace;
|
||||
}
|
||||
return face + " " + ((style && style.toLowerCase()) || "normal") + " " + ((weight && weight.toLowerCase()) || "normal");
|
||||
}
|
||||
|
||||
createFont(face, style, weight) {
|
||||
var fontIndex = this.getFontIndex(face, style, weight);
|
||||
if (fontIndex == undefined) {
|
||||
var tis = this.tokenImageSize;
|
||||
var atlasSize = 1024;//og.math.nextHighestPowerOfTwo(Math.ceil(Math.sqrt(og.utils.FontAtlas.tokens.length)) / tis + (og.utils.FontAtlas.tokens.length - 1) * og.utils.TextureAtlas.BORDER_SIZE);
|
||||
var fontName = this.getFullIndex(face, style, weight);
|
||||
fontIndex = this.atlasIndexes[fontName] = this.atlasesArr.length;
|
||||
var atlas = new TextureAtlas(atlasSize, atlasSize);
|
||||
atlas.assignHandler(this._handler);
|
||||
atlas.borderSize = 6;
|
||||
this.samplerArr[this.atlasesArr.length] = this.atlasesArr.length;
|
||||
this.atlasesArr.push(atlas);
|
||||
atlas.canvas.fillColor("black");
|
||||
|
||||
var t = tokens;
|
||||
|
||||
var sdfSize = 512;
|
||||
var sdfCanvas = new ImageCanvas(sdfSize, sdfSize);
|
||||
var sc = this._sdfCreator;
|
||||
var pT = Math.round(sdfSize * 0.66);
|
||||
var tF = (style || "normal") + " " + (weight || "normal") + " " + pT + "px " + (face || this.defaultFace);
|
||||
|
||||
for (var i = 0; i < t.length; i++) {
|
||||
var ti = t[i];
|
||||
|
||||
sdfCanvas.fillColor("black");
|
||||
sdfCanvas.drawText(ti, 49, pT, tF, "white");
|
||||
var res = sc.createSDF(sdfCanvas._canvas, tis, tis);
|
||||
res.__nodeIndex = ti;
|
||||
var n = atlas.addImage(res, true);
|
||||
|
||||
var tokenWidth = sdfCanvas.getTextWidth(ti);
|
||||
n.emptySize = tokenWidth / sdfSize;
|
||||
}
|
||||
|
||||
atlas.createTexture();
|
||||
sdfCanvas.destroy();
|
||||
sdfCanvas = null;
|
||||
}
|
||||
|
||||
return fontIndex;
|
||||
}
|
||||
|
||||
createFontAsync(face, style, weight, callback) {
|
||||
var obj = { "face": face, "style": style, "weight": weight, "callback": callback };
|
||||
if (this._counter >= 1) {
|
||||
this._pendingsQueue.push(obj);
|
||||
} else {
|
||||
this._exec(obj);
|
||||
}
|
||||
}
|
||||
|
||||
_exec(obj) {
|
||||
this._counter++;
|
||||
var that = this;
|
||||
setTimeout(function () {
|
||||
var fontIndex = that.createFont(obj.face, obj.style, obj.weight);
|
||||
obj.callback(fontIndex);
|
||||
that._dequeueRequest();
|
||||
}, 0);
|
||||
}
|
||||
|
||||
_dequeueRequest() {
|
||||
this._counter--;
|
||||
if (this._pendingsQueue.length && this._counter < 1) {
|
||||
var obj;
|
||||
if (obj = this._whilePendings())
|
||||
this._exec(obj);
|
||||
}
|
||||
}
|
||||
|
||||
_whilePendings() {
|
||||
while (this._pendingsQueue.length) {
|
||||
var f = this._pendingsQueue.pop();
|
||||
var fontIndex = this.getFontIndex(f.face, f.style, f.weight);
|
||||
if (fontIndex != undefined) {
|
||||
f.callback(fontIndex);
|
||||
continue;
|
||||
}
|
||||
return f;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export { FontAtlas };
|
||||
50
src/og/utils/FontDetector.js
Normal file
50
src/og/utils/FontDetector.js
Normal file
@ -0,0 +1,50 @@
|
||||
/**
|
||||
* @module og/utils/FontDetector
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Font detector class.
|
||||
* @class
|
||||
*/
|
||||
class FontDetector {
|
||||
constructor() {
|
||||
var baseFonts = ['monospace', 'sans-serif', 'serif'];
|
||||
var testString = "mmmmmmmmmmlli";
|
||||
var testSize = '72px';
|
||||
var h = document.getElementsByTagName("body")[0];
|
||||
var s = document.createElement("span");
|
||||
s.style.fontSize = testSize;
|
||||
s.innerHTML = testString;
|
||||
var defaultWidth = {};
|
||||
var defaultHeight = {};
|
||||
for (var index in baseFonts) {
|
||||
s.style.fontFamily = baseFonts[index];
|
||||
h.appendChild(s);
|
||||
defaultWidth[baseFonts[index]] = s.offsetWidth;
|
||||
defaultHeight[baseFonts[index]] = s.offsetHeight;
|
||||
h.removeChild(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if font is known.
|
||||
* @public
|
||||
* @param {string} font - Font name.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
this.detect = function (font) {
|
||||
var detected = false;
|
||||
for (var index in baseFonts) {
|
||||
s.style.fontFamily = font + ',' + baseFonts[index];
|
||||
h.appendChild(s);
|
||||
var matched = (s.offsetWidth != defaultWidth[baseFonts[index]] || s.offsetHeight != defaultHeight[baseFonts[index]]);
|
||||
h.removeChild(s);
|
||||
detected = detected || matched;
|
||||
}
|
||||
return detected;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export { FontDetector };
|
||||
161
src/og/utils/GeoImageCreator.js
Normal file
161
src/og/utils/GeoImageCreator.js
Normal file
@ -0,0 +1,161 @@
|
||||
/**
|
||||
* @module og/utils/GeoImageCreator
|
||||
*/
|
||||
|
||||
'use sctrict';
|
||||
|
||||
import * as segmentHelper from '../segment/segmentHelper.js';
|
||||
import * as utils from '../utils/shared.js';
|
||||
import { Framebuffer } from '../webgl/Framebuffer.js';
|
||||
import { LonLat } from '../LonLat.js';
|
||||
import { ShaderProgram } from '../webgl/ShaderProgram.js';
|
||||
import { types } from '../webgl/types.js';
|
||||
|
||||
const GeoImageCreator = function (handler, maxFrames) {
|
||||
this._gridSize = 64;
|
||||
this._handler = handler;
|
||||
this._framebuffer = null;
|
||||
this._texCoordsBuffer = null;
|
||||
this._indexBuffer = null;
|
||||
this.MAX_FRAMES = maxFrames || 5;
|
||||
this._currentFrame = 0;
|
||||
this._queue = [];
|
||||
this._animate = [];
|
||||
this._initialize();
|
||||
};
|
||||
|
||||
GeoImageCreator.prototype._initialize = function () {
|
||||
this._initShaders();
|
||||
this._initBuffers();
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates geoImage corners coordinates grid buffer.
|
||||
* @public
|
||||
* @param{Array.<og.LonLat>} c - GeoImage corners coordinates.
|
||||
* @return{WebGLBuffer} Grid coordinates buffer.
|
||||
*/
|
||||
GeoImageCreator.prototype.createGridBuffer = function (c, toMerc) {
|
||||
var gs = this._gridSize;
|
||||
|
||||
var v03 = new LonLat((c[3].lon - c[0].lon) / gs, (c[3].lat - c[0].lat) / gs),
|
||||
v12 = new LonLat((c[2].lon - c[1].lon) / gs, (c[2].lat - c[1].lat) / gs),
|
||||
v01 = new LonLat((c[1].lon - c[0].lon) / gs, (c[1].lat - c[0].lat) / gs),
|
||||
v32 = new LonLat((c[2].lon - c[3].lon) / gs, (c[2].lat - c[3].lat) / gs);
|
||||
|
||||
var grid = new Float32Array((gs + 1) * (gs + 1) * 2);
|
||||
var k = 0;
|
||||
for (var i = 0; i <= gs; i++) {
|
||||
var P03i = new LonLat(c[0].lon + i * v03.lon, c[0].lat + i * v03.lat),
|
||||
P12i = new LonLat(c[1].lon + i * v12.lon, c[1].lat + i * v12.lat);
|
||||
for (var j = 0; j <= gs; j++) {
|
||||
var P01j = new LonLat(c[0].lon + j * v01.lon, c[0].lat + j * v01.lat),
|
||||
P32j = new LonLat(c[3].lon + j * v32.lon, c[3].lat + j * v32.lat);
|
||||
var xx = utils.getLinesIntersectionLonLat(P03i, P12i, P01j, P32j);
|
||||
grid[k++] = xx.lon;
|
||||
grid[k++] = xx.lat;
|
||||
}
|
||||
}
|
||||
|
||||
if (toMerc) {
|
||||
for (var i = 0; i < grid.length; i += 2) {
|
||||
var c = new LonLat(grid[i], grid[i + 1]).forwardMercator();
|
||||
grid[i] = c.lon;
|
||||
grid[i + 1] = c.lat;
|
||||
}
|
||||
}
|
||||
return this._handler.createArrayBuffer(grid, 2, grid.length / 2);
|
||||
};
|
||||
|
||||
GeoImageCreator.prototype.frame = function () {
|
||||
var i = this.MAX_FRAMES;
|
||||
while (i-- && this._queue.length) {
|
||||
var q = this._queue.shift();
|
||||
q._isRendering = false;
|
||||
q.rendering();
|
||||
}
|
||||
|
||||
i = this._animate.length;
|
||||
while (i--) {
|
||||
this._animate[i].rendering();
|
||||
}
|
||||
};
|
||||
|
||||
GeoImageCreator.prototype.add = function (geoImage) {
|
||||
if (!geoImage._isRendering) {
|
||||
geoImage._isRendering = true;
|
||||
if (geoImage._animate) {
|
||||
this._animate.push(geoImage);
|
||||
} else {
|
||||
this._queue.push(geoImage);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
GeoImageCreator.prototype.remove = function (geoImage) {
|
||||
if (geoImage._isRendering) {
|
||||
geoImage._creationProceeding = false;
|
||||
geoImage._isRendering = false;
|
||||
var arr;
|
||||
if (geoImage._animate) {
|
||||
arr = this._animate;
|
||||
} else {
|
||||
arr = this._queue;
|
||||
}
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
if (arr[i].isEqual(geoImage)) {
|
||||
arr.splice(i, 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
GeoImageCreator.prototype._initBuffers = function () {
|
||||
|
||||
this._framebuffer = new Framebuffer(this._handler, { width: 2, height: 2, useDepth: false });
|
||||
this._framebuffer.init();
|
||||
|
||||
this._framebufferMercProj = new Framebuffer(this._handler, { width: 2, height: 2, useDepth: false });
|
||||
this._framebufferMercProj.init();
|
||||
|
||||
var gs = this._gridSize;
|
||||
var gs1 = this._gridSize + 1;
|
||||
this._texCoordsBuffer = this._handler.createArrayBuffer(segmentHelper.textureCoordsTable[gs], 2, gs1 * gs1);
|
||||
|
||||
var indexes = segmentHelper.createSegmentIndexes(gs, [gs, gs, gs, gs]);
|
||||
this._indexBuffer = this._handler.createElementArrayBuffer(indexes, 1, indexes.length);
|
||||
|
||||
this._quadTexCoordsBuffer = this._handler.createArrayBuffer(new Float32Array([0, 1, 1, 1, 0, 0, 1, 0]), 2, 4);
|
||||
this._quadVertexBuffer = this._handler.createArrayBuffer(new Float32Array([-1, 1, 1, 1, -1, -1, 1, -1]), 2, 4);
|
||||
};
|
||||
|
||||
GeoImageCreator.prototype._initShaders = function () {
|
||||
this._handler.addShaderProgram(new ShaderProgram("geoImageTransform", {
|
||||
uniforms: {
|
||||
sourceTexture: { type: types.SAMPLER2D },
|
||||
extentParams: { type: types.VEC4 }
|
||||
},
|
||||
attributes: {
|
||||
corners: { type: types.VEC2, enableArray: true },
|
||||
texCoords: { type: types.VEC2, enableArray: true }
|
||||
},
|
||||
vertexShader: 'attribute vec2 corners; \
|
||||
attribute vec2 texCoords; \
|
||||
varying vec2 v_texCoords; \
|
||||
uniform vec4 extentParams; \
|
||||
void main() { \
|
||||
v_texCoords = texCoords; \
|
||||
gl_Position = vec4((-1.0 + (corners - extentParams.xy) * extentParams.zw) * vec2(1.0, -1.0), 0.0, 1.0); \
|
||||
}',
|
||||
fragmentShader:
|
||||
'precision highp float;\n\
|
||||
uniform sampler2D sourceTexture; \
|
||||
varying vec2 v_texCoords; \
|
||||
void main () { \
|
||||
gl_FragColor = texture2D(sourceTexture, v_texCoords); \
|
||||
}'
|
||||
}));
|
||||
};
|
||||
|
||||
export { GeoImageCreator };
|
||||
73
src/og/utils/ImageBitmapLoader.js
Normal file
73
src/og/utils/ImageBitmapLoader.js
Normal file
@ -0,0 +1,73 @@
|
||||
/**
|
||||
* @module og/utils/ImageBitmapLoader
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const NUM_WORKERS = 1;
|
||||
const MAX_REQUESTS = 12;
|
||||
|
||||
function program(maxRequests = 12) {
|
||||
return `var maxRequests = ${maxRequests};
|
||||
var _loading = 0;
|
||||
var _queue = [];
|
||||
|
||||
var processQueue = function() {
|
||||
if (_queue.length > 0 && _loading < maxRequests) {
|
||||
|
||||
var q = _queue.shift(),
|
||||
src = q.src,
|
||||
options = q.options || {};
|
||||
|
||||
_loading++;
|
||||
|
||||
return fetch(src, options).then((response) => {
|
||||
if (!response.ok) {
|
||||
throw Error("Unable to load '" + src + "'");
|
||||
}
|
||||
return response.blob();
|
||||
})
|
||||
.then(createImageBitmap)
|
||||
.then((imageBitmap) => {
|
||||
_loading--;
|
||||
self.postMessage({ 'ok': true, 'imageBitmap': imageBitmap, 'queue': _queue.length }, [imageBitmap]);
|
||||
})
|
||||
.then(processQueue)
|
||||
.catch((err) => {
|
||||
_loading--;
|
||||
self.postMessage({ 'ok': false, 'error': err.toString(), 'queue': _queue.length });
|
||||
processQueue();
|
||||
});
|
||||
}
|
||||
}
|
||||
self.onmessage = function (e) {
|
||||
var toEnqueue = e.data;
|
||||
if (_queue.indexOf(toEnqueue.src) < 0) {
|
||||
_queue.push(toEnqueue);
|
||||
processQueue();
|
||||
}
|
||||
}`;
|
||||
};
|
||||
|
||||
class ImageBitmapLoader {
|
||||
constructor(options) {
|
||||
options = options || {};
|
||||
|
||||
this._workers = new Array(options.numWorkers || NUM_WORKERS);
|
||||
this._counter = 0;
|
||||
|
||||
var p = new Blob([program(options.maxRequests || MAX_REQUESTS)], { type: 'application/javascript' });
|
||||
|
||||
for (var i = 0; i < this._workers.length; i++) {
|
||||
this._workers[i] = new Worker(URL.createObjectURL(p));
|
||||
}
|
||||
}
|
||||
|
||||
load(src, callback, options) {
|
||||
var _w = this._workers[this._counter++ % this._workers.length];
|
||||
_w.onmessage = (e) => callback && callback(e);
|
||||
_w.postMessage({ 'src': src, 'options': options });
|
||||
}
|
||||
};
|
||||
|
||||
export { ImageBitmapLoader };
|
||||
73
src/og/utils/ImagesCacheManager.js
Normal file
73
src/og/utils/ImagesCacheManager.js
Normal file
@ -0,0 +1,73 @@
|
||||
/**
|
||||
* @module og/utils/ImagesCacheManager
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { QueueArray } from '../QueueArray.js';
|
||||
|
||||
class ImagesCacheManager {
|
||||
constructor() {
|
||||
this.imagesCache = {};
|
||||
|
||||
this._counter = 0;
|
||||
this._pendingsQueue = new QueueArray();
|
||||
this._imageIndexCounter = 0;
|
||||
}
|
||||
|
||||
load(src, success) {
|
||||
if (this.imagesCache[src]) {
|
||||
success(this.imagesCache[src]);
|
||||
} else {
|
||||
var req = { "src": src, "success": success };
|
||||
if (this._counter >= 1) {
|
||||
this._pendingsQueue.push(req);
|
||||
} else {
|
||||
this._exec(req);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
_exec(req) {
|
||||
this._counter++;
|
||||
var that = this;
|
||||
|
||||
var img = new Image();
|
||||
img.crossOrigin = '';
|
||||
img.onload = function () {
|
||||
that.imagesCache[req.src] = img;
|
||||
this.__nodeIndex = that._imageIndexCounter++;
|
||||
req.success(this);
|
||||
that._dequeueRequest();
|
||||
};
|
||||
|
||||
img.onerror = function () {
|
||||
that._dequeueRequest();
|
||||
};
|
||||
|
||||
img.src = req.src;
|
||||
}
|
||||
|
||||
_dequeueRequest() {
|
||||
this._counter--;
|
||||
if (this._pendingsQueue.length && this._counter < 1) {
|
||||
while (this._pendingsQueue.length) {
|
||||
var req = this._pendingsQueue.pop();
|
||||
if (req) {
|
||||
if (this.imagesCache[req.src]) {
|
||||
if (this._counter <= 0)
|
||||
this._counter = 0;
|
||||
else
|
||||
this._counter--;
|
||||
req.success(this.imagesCache[req.src]);
|
||||
} else {
|
||||
this._exec(req);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export { ImagesCacheManager };
|
||||
308
src/og/utils/NormalMapCreator.js
Normal file
308
src/og/utils/NormalMapCreator.js
Normal file
@ -0,0 +1,308 @@
|
||||
/**
|
||||
* @module og/utils/NormalMapCreator
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as quadTree from '../quadTree/quadTree.js';
|
||||
import { Framebuffer } from '../webgl/Framebuffer.js';
|
||||
import { Lock } from '../Lock.js';
|
||||
import { ShaderProgram } from '../webgl/ShaderProgram.js';
|
||||
import { types } from '../webgl/types.js';
|
||||
import { QueueArray } from '../QueueArray.js';
|
||||
|
||||
const NormalMapCreator = function (planet, width, height, maxFrames) {
|
||||
this._planet = planet;
|
||||
this._handler = planet.renderer.handler;
|
||||
this._verticesBufferArray = [];
|
||||
this._indexBufferArray = [];
|
||||
this._positionBuffer = null;
|
||||
this._framebuffer = null;
|
||||
this._normalMapVerticesTexture = null;
|
||||
|
||||
this._width = width || 128;
|
||||
this._height = height || 128;
|
||||
|
||||
this.MAX_FRAMES = maxFrames || 5;
|
||||
this._currentFrame = 0;
|
||||
this._queue = new QueueArray(1024);
|
||||
|
||||
this._lock = new Lock();
|
||||
|
||||
this._init();
|
||||
};
|
||||
|
||||
NormalMapCreator.prototype._init = function () {
|
||||
|
||||
var isWebkit = false;//('WebkitAppearance' in document.documentElement.style) && !/^((?!chrome).)*safari/i.test(navigator.userAgent);
|
||||
|
||||
/*==================================================================================
|
||||
* http://www.sunsetlakesoftware.com/2013/10/21/optimizing-gaussian-blurs-mobile-gpu
|
||||
*=================================================================================*/
|
||||
var normalMapBlur = new ShaderProgram("normalMapBlur", {
|
||||
attributes: {
|
||||
a_position: { type: types.VEC2, enableArray: true }
|
||||
},
|
||||
uniforms: {
|
||||
s_texture: { type: types.SAMPLER2D }
|
||||
},
|
||||
vertexShader: "attribute vec2 a_position; \n\
|
||||
attribute vec2 a_texCoord; \n\
|
||||
\n\
|
||||
varying vec2 blurCoordinates[5]; \n\
|
||||
\n\
|
||||
void main() { \n\
|
||||
vec2 vt = a_position * 0.5 + 0.5;" +
|
||||
(isWebkit ? "vt.y = 1.0 - vt.y; " : " ") +
|
||||
"gl_Position = vec4(a_position, 0.0, 1.0); \n\
|
||||
blurCoordinates[0] = vt; \n\
|
||||
blurCoordinates[1] = vt + " + (1.0 / this._width * 1.407333) + ";" +
|
||||
"blurCoordinates[2] = vt - " + (1.0 / this._height * 1.407333) + ";" +
|
||||
"blurCoordinates[3] = vt + " + (1.0 / this._width * 3.294215) + ";" +
|
||||
"blurCoordinates[4] = vt - " + (1.0 / this._height * 3.294215) + ";" +
|
||||
"}",
|
||||
fragmentShader:
|
||||
"precision highp float;\n\
|
||||
uniform sampler2D s_texture; \n\
|
||||
\n\
|
||||
varying vec2 blurCoordinates[5]; \n\
|
||||
\n\
|
||||
void main() { \n\
|
||||
lowp vec4 sum = vec4(0.0); \n\
|
||||
if(blurCoordinates[0].x <= 0.01 || blurCoordinates[0].x >= 0.99 ||\n\
|
||||
blurCoordinates[0].y <= 0.01 || blurCoordinates[0].y >= 0.99){\n\
|
||||
sum = texture2D(s_texture, blurCoordinates[0]);\n\
|
||||
} else {\n\
|
||||
sum += texture2D(s_texture, blurCoordinates[0]) * 0.204164; \n\
|
||||
sum += texture2D(s_texture, blurCoordinates[1]) * 0.304005; \n\
|
||||
sum += texture2D(s_texture, blurCoordinates[2]) * 0.304005; \n\
|
||||
sum += texture2D(s_texture, blurCoordinates[3]) * 0.093913; \n\
|
||||
sum += texture2D(s_texture, blurCoordinates[4]) * 0.093913; \n\
|
||||
}\n\
|
||||
gl_FragColor = sum; \n\
|
||||
}"
|
||||
});
|
||||
|
||||
var normalMap = new ShaderProgram("normalMap", {
|
||||
attributes: {
|
||||
a_position: { type: types.VEC2, enableArray: true },
|
||||
a_normal: { type: types.VEC3, enableArray: true }
|
||||
},
|
||||
vertexShader: "attribute vec2 a_position; \
|
||||
attribute vec3 a_normal; \
|
||||
\
|
||||
varying vec3 v_color; \
|
||||
\
|
||||
void main() { \
|
||||
gl_PointSize = 1.0; \
|
||||
gl_Position = vec4(a_position, 0, 1); \
|
||||
v_color = normalize(a_normal) * 0.5 + 0.5; \
|
||||
}",
|
||||
fragmentShader:
|
||||
"precision highp float;\n\
|
||||
\
|
||||
varying vec3 v_color; \
|
||||
\
|
||||
void main () { \
|
||||
gl_FragColor = vec4(v_color, 1.0); \
|
||||
}"
|
||||
});
|
||||
|
||||
this._handler.addShaderProgram(normalMapBlur);
|
||||
this._handler.addShaderProgram(normalMap);
|
||||
|
||||
//create hidden handler buffer
|
||||
this._framebuffer = new Framebuffer(this._handler, {
|
||||
width: this._width,
|
||||
height: this._height,
|
||||
useDepth: false
|
||||
});
|
||||
|
||||
this._framebuffer.init();
|
||||
|
||||
this._normalMapVerticesTexture = this._handler.createEmptyTexture_l(this._width, this._height);
|
||||
|
||||
//create vertices hasharray for different grid size segments
|
||||
for (var p = 1; p <= 6; p++) {
|
||||
var gs = Math.pow(2, p);
|
||||
var gs2 = (gs / 2);
|
||||
var vertices = [];
|
||||
|
||||
for (var i = 0; i <= gs; i++) {
|
||||
for (var j = 0; j <= gs; j++) {
|
||||
vertices.push(-1 + j / gs2, -1 + i / gs2);
|
||||
}
|
||||
}
|
||||
|
||||
this._verticesBufferArray[gs] = this._handler.createArrayBuffer(new Float32Array(vertices), 2, vertices.length / 2);
|
||||
this._indexBufferArray[gs] = this._planet._indexesCache[gs][gs][gs][gs][gs].buffer;
|
||||
}
|
||||
|
||||
//create 2d screen square buffer
|
||||
var positions = new Float32Array([
|
||||
-1.0, -1.0,
|
||||
1.0, -1.0,
|
||||
-1.0, 1.0,
|
||||
1.0, 1.0]);
|
||||
|
||||
this._positionBuffer = this._handler.createArrayBuffer(positions, 2, positions.length / 2);
|
||||
};
|
||||
|
||||
NormalMapCreator.prototype._drawNormalMap = function (segment) {
|
||||
var normals = segment.normalMapNormals;
|
||||
if (segment.node && segment.node.getState() !== quadTree.NOTRENDERING
|
||||
&& normals && normals.length) {
|
||||
|
||||
segment._normalMapEdgeEqualize(quadTree.N, 0);
|
||||
segment._normalMapEdgeEqualize(quadTree.S, 1);
|
||||
segment._normalMapEdgeEqualize(quadTree.W, 0, true);
|
||||
segment._normalMapEdgeEqualize(quadTree.E, 1, true);
|
||||
|
||||
var outTexture = segment.normalMapTexturePtr;
|
||||
var size = normals.length / 3;
|
||||
var gridSize = Math.sqrt(size) - 1;
|
||||
|
||||
var h = this._handler;
|
||||
var gl = h.gl;
|
||||
|
||||
var _normalsBuffer = h.createArrayBuffer(normals, 3, size, gl.DYNAMIC_DRAW);
|
||||
|
||||
var f = this._framebuffer;
|
||||
var p = h.shaderPrograms.normalMap;
|
||||
var sha = p._program.attributes;
|
||||
|
||||
f.bindOutputTexture(this._normalMapVerticesTexture);
|
||||
|
||||
p.activate();
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this._verticesBufferArray[gridSize]);
|
||||
gl.vertexAttribPointer(sha.a_position._pName, this._verticesBufferArray[gridSize].itemSize, gl.FLOAT, false, 0, 0);
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, _normalsBuffer);
|
||||
gl.vertexAttribPointer(sha.a_normal._pName, _normalsBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
|
||||
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._indexBufferArray[gridSize]);
|
||||
gl.drawElements(gl.TRIANGLE_STRIP, this._indexBufferArray[gridSize].numItems, gl.UNSIGNED_SHORT, 0);
|
||||
|
||||
gl.deleteBuffer(_normalsBuffer);
|
||||
|
||||
//
|
||||
// blur pass
|
||||
//
|
||||
f.bindOutputTexture(outTexture);
|
||||
|
||||
p = h.shaderPrograms.normalMapBlur;
|
||||
|
||||
p.activate();
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this._positionBuffer);
|
||||
gl.vertexAttribPointer(p._program.attributes.a_position._pName, this._positionBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
gl.activeTexture(gl.TEXTURE0);
|
||||
gl.bindTexture(gl.TEXTURE_2D, this._normalMapVerticesTexture);
|
||||
gl.uniform1i(p._program.uniforms.s_texture._pName, 0);
|
||||
gl.drawArrays(gl.TRIANGLE_STRIP, 0, this._positionBuffer.numItems);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
NormalMapCreator.prototype.drawSingle = function (segment) {
|
||||
var h = this._handler,
|
||||
gl = h.gl;
|
||||
|
||||
this._framebuffer.activate();
|
||||
|
||||
gl.disable(gl.CULL_FACE);
|
||||
gl.disable(gl.DEPTH_TEST);
|
||||
|
||||
if (segment.terrainReady && this._drawNormalMap(segment)) {
|
||||
segment.normalMapReady = true;
|
||||
segment.normalMapTexture = segment.normalMapTexturePtr;
|
||||
segment.normalMapTextureBias[0] = 0;
|
||||
segment.normalMapTextureBias[1] = 0;
|
||||
segment.normalMapTextureBias[2] = 1;
|
||||
}
|
||||
segment._inTheQueue = false;
|
||||
|
||||
gl.disable(gl.BLEND);
|
||||
gl.enable(gl.DEPTH_TEST);
|
||||
gl.enable(gl.CULL_FACE);
|
||||
|
||||
this._framebuffer.deactivate();
|
||||
};
|
||||
|
||||
NormalMapCreator.prototype.frame = function () {
|
||||
|
||||
if (this._queue.length) {
|
||||
var h = this._handler,
|
||||
gl = h.gl;
|
||||
|
||||
this._framebuffer.activate();
|
||||
|
||||
gl.disable(gl.CULL_FACE);
|
||||
gl.disable(gl.DEPTH_TEST);
|
||||
|
||||
var deltaTime = 0,
|
||||
startTime = window.performance.now();
|
||||
|
||||
var width = this._width,
|
||||
height = this._height;
|
||||
|
||||
while (this._lock.isFree() && this._queue.length && deltaTime < 0.25) {
|
||||
var segment = this._queue.shift();
|
||||
if (segment.terrainReady && this._drawNormalMap(segment)) {
|
||||
segment.normalMapReady = true;
|
||||
segment.normalMapTexture = segment.normalMapTexturePtr;
|
||||
segment.normalMapTextureBias[0] = 0;
|
||||
segment.normalMapTextureBias[1] = 0;
|
||||
segment.normalMapTextureBias[2] = 1;
|
||||
}
|
||||
segment._inTheQueue = false;
|
||||
deltaTime = window.performance.now() - startTime;
|
||||
}
|
||||
|
||||
gl.disable(gl.BLEND);
|
||||
gl.enable(gl.DEPTH_TEST);
|
||||
gl.enable(gl.CULL_FACE);
|
||||
|
||||
this._framebuffer.deactivate();
|
||||
}
|
||||
};
|
||||
|
||||
NormalMapCreator.prototype.queue = function (segment) {
|
||||
segment._inTheQueue = true;
|
||||
this._queue.push(segment);
|
||||
};
|
||||
|
||||
NormalMapCreator.prototype.unshift = function (segment) {
|
||||
segment._inTheQueue = true;
|
||||
this._queue.unshift(segment);
|
||||
};
|
||||
|
||||
NormalMapCreator.prototype.remove = function (segment) {
|
||||
//...
|
||||
};
|
||||
|
||||
NormalMapCreator.prototype.clear = function () {
|
||||
while (this._queue.length) {
|
||||
var s = this._queue.pop();
|
||||
s._inTheQueue = false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Set activity off
|
||||
* @public
|
||||
*/
|
||||
NormalMapCreator.prototype.lock = function (key) {
|
||||
this._lock.lock(key);
|
||||
};
|
||||
|
||||
/**
|
||||
* Set activity on
|
||||
* @public
|
||||
*/
|
||||
NormalMapCreator.prototype.free = function (key) {
|
||||
this._lock.free(key);
|
||||
};
|
||||
|
||||
export { NormalMapCreator };
|
||||
289
src/og/utils/SDFCreator.js
Normal file
289
src/og/utils/SDFCreator.js
Normal file
@ -0,0 +1,289 @@
|
||||
/**
|
||||
* @module og/utils/SDFCreator
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { Framebuffer } from '../webgl/Framebuffer.js';
|
||||
import { Handler } from '../webgl/Handler.js';
|
||||
import { ShaderProgram } from '../webgl/ShaderProgram.js';
|
||||
import { types } from '../webgl/types.js';
|
||||
|
||||
class SDFCreator {
|
||||
constructor(width, height) {
|
||||
this._handler = null;
|
||||
this._framebuffer0 = null;
|
||||
this._framebuffer1 = null;
|
||||
this._framebuffer2 = null;
|
||||
this._vertexBuffer = null;
|
||||
|
||||
//default params
|
||||
this._width = width || 512;
|
||||
this._height = height || 512;
|
||||
var s = Math.max(this._width, this._height);
|
||||
this._outsideDistance = Math.round(80 * s / 512);
|
||||
this._insideDistance = Math.round(10 * s / 512);
|
||||
this._outsideMix = 0.710;
|
||||
this._insideMix = 0.679;
|
||||
this._sourceTexture = null;
|
||||
|
||||
this.initialize();
|
||||
}
|
||||
|
||||
initialize() {
|
||||
this._initHandler(this._width, this._height);
|
||||
this._initShaders();
|
||||
}
|
||||
|
||||
_initHandler(width, height) {
|
||||
|
||||
this._handler = new Handler(null, {
|
||||
width: width, height: height,
|
||||
context: { alpha: true, depth: false }
|
||||
});
|
||||
this._handler.initialize();
|
||||
this._handler.deactivateFaceCulling();
|
||||
this._handler.deactivateDepthTest();
|
||||
|
||||
this._vertexBuffer = this._handler.createArrayBuffer(new Float32Array([-1, -1, -1, 1, 1, -1, 1, 1]), 2, 4);
|
||||
|
||||
this._framebuffer0 = new Framebuffer(this._handler, { useDepth: false });
|
||||
this._framebuffer1 = new Framebuffer(this._handler, { useDepth: false });
|
||||
this._framebuffer2 = new Framebuffer(this._handler, { useDepth: false });
|
||||
|
||||
this._framebuffer0.init();
|
||||
this._framebuffer1.init();
|
||||
this._framebuffer2.init();
|
||||
}
|
||||
_initShaders() {
|
||||
var vfield = new ShaderProgram("vfield", {
|
||||
uniforms: {
|
||||
uTexSize: { type: types.VEC2 },
|
||||
uTex1: { type: types.SAMPLER2D },
|
||||
uDistance: { type: types.INT },
|
||||
uNeg: { type: types.VEC2 }
|
||||
},
|
||||
attributes: {
|
||||
aPos: { type: types.VEC2, enableArray: true }
|
||||
},
|
||||
vertexShader:
|
||||
"precision highp float;\
|
||||
attribute vec2 aPos;\
|
||||
uniform vec2 uTexSize;\
|
||||
varying vec2 TexCoord;\
|
||||
varying vec2 vTexSize;\
|
||||
void main() {\
|
||||
TexCoord = (aPos + 1.0) * 0.5;\
|
||||
TexCoord *= uTexSize;\
|
||||
vTexSize = uTexSize;\
|
||||
gl_Position.xy = aPos;\
|
||||
gl_Position.zw = vec2(0.0, 1.0);\
|
||||
}",
|
||||
fragmentShader:
|
||||
"precision highp float;\
|
||||
uniform sampler2D uTex1;\
|
||||
uniform int uDistance;\
|
||||
uniform vec2 uNeg;\
|
||||
varying vec2 TexCoord;\
|
||||
varying vec2 vTexSize;\
|
||||
const int maxDistance = " + this._outsideDistance + ";\
|
||||
void main() {\
|
||||
if ( uNeg.x - uNeg.y * texture2D(uTex1, TexCoord / vTexSize).r > 0.5 ) {\
|
||||
gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);\
|
||||
return;\
|
||||
}\
|
||||
for ( int i=1; i <= maxDistance; i++ ) {\
|
||||
if(i > uDistance) break;\
|
||||
if ( uNeg.x - uNeg.y * texture2D(uTex1, ( TexCoord + vec2(0.0, i) ) / vTexSize ).r > 0.5 ) {\
|
||||
gl_FragColor = vec4( vec3(float(i)/float(uDistance)), 1.0 );\
|
||||
return;\
|
||||
}\
|
||||
if ( uNeg.x - uNeg.y * texture2D(uTex1, ( TexCoord - vec2(0.0, i)) / vTexSize ).r > 0.5 ) {\
|
||||
gl_FragColor = vec4(vec3(float(i)/float(uDistance)), 1.0);\
|
||||
return;\
|
||||
}\
|
||||
}\
|
||||
gl_FragColor = vec4(1.0);\
|
||||
}"
|
||||
});
|
||||
|
||||
var hfield = new ShaderProgram("hfield", {
|
||||
uniforms: {
|
||||
uTexSize: { type: types.VEC2 },
|
||||
uTex1: { type: types.SAMPLER2D },
|
||||
uDistance: { type: types.INT }
|
||||
},
|
||||
attributes: {
|
||||
aPos: { type: types.VEC2, enableArray: true }
|
||||
},
|
||||
vertexShader:
|
||||
"precision highp float;\
|
||||
attribute vec2 aPos;\
|
||||
uniform vec2 uTexSize;\
|
||||
varying vec2 TexCoord;\
|
||||
varying vec2 vTexSize;\
|
||||
void main() {\n\
|
||||
TexCoord = (aPos + 1.0) * 0.5;\
|
||||
TexCoord *= uTexSize;\
|
||||
vTexSize = uTexSize;\
|
||||
gl_Position.xy = aPos;\
|
||||
gl_Position.zw = vec2(0.0, 1.0);\
|
||||
}",
|
||||
fragmentShader:
|
||||
"precision highp float;\
|
||||
uniform sampler2D uTex1;\
|
||||
uniform int uDistance;\
|
||||
varying vec2 TexCoord;\
|
||||
varying vec2 vTexSize;\
|
||||
const int maxDistance = " + this._outsideDistance + ";\
|
||||
float CalcC(float H, float V) {\
|
||||
return ( sqrt( H * H + V * V ) );\
|
||||
}\
|
||||
void main(){\
|
||||
float dist = CalcC( 0.0, texture2D( uTex1, TexCoord / vTexSize ).r );\
|
||||
for ( int i = 1; i <= maxDistance; i++ ) {\
|
||||
if(i > uDistance) break;\
|
||||
float H = float(i) / float(uDistance);\
|
||||
dist = min( dist, CalcC( H, texture2D( uTex1, ( TexCoord + vec2( float(i), 0.0) ) / vTexSize ).r ) );\
|
||||
dist = min( dist, CalcC( H, texture2D( uTex1, ( TexCoord - vec2( float(i), 0.0) ) / vTexSize ).r ) );\
|
||||
}\
|
||||
gl_FragColor = vec4(dist);\
|
||||
gl_FragColor.w = 1.0;\
|
||||
}"
|
||||
});
|
||||
|
||||
var sum = new ShaderProgram("sum", {
|
||||
uniforms: {
|
||||
outside: { type: types.SAMPLER2D },
|
||||
inside: { type: types.SAMPLER2D },
|
||||
source: { type: types.SAMPLER2D }
|
||||
},
|
||||
attributes: {
|
||||
aPos: { type: types.VEC2, enableArray: true }
|
||||
},
|
||||
vertexShader: "attribute vec2 aPos;\n\
|
||||
varying vec2 TexCoord;\n\
|
||||
void main(){\n\
|
||||
TexCoord = (aPos * vec2(1.0,-1.0) + 1.0) * 0.5;\n\
|
||||
gl_Position.xy = aPos;\n\
|
||||
gl_Position.zw = vec2(0.0, 1.0);\n\
|
||||
}",
|
||||
fragmentShader:
|
||||
"precision highp float;\n\
|
||||
uniform sampler2D outside;\n\
|
||||
uniform sampler2D inside;\n\
|
||||
uniform sampler2D source;\n\
|
||||
varying vec2 TexCoord;\n\
|
||||
void main(){\n\
|
||||
float o = texture2D(outside, TexCoord).r;\n\
|
||||
float i = 1.0 - texture2D(inside, TexCoord).r;\n\
|
||||
float s = texture2D(source, TexCoord).r;\n\
|
||||
gl_FragColor = vec4( vec3(1.0 - mix(i, o, step(0.5, s) * " + this._outsideMix + " + (1.0 - step(0.5, s)) * " + this._insideMix + " )), 1.0);\n\
|
||||
}"
|
||||
});
|
||||
this._handler.addShaderPrograms([vfield, hfield, sum]);
|
||||
};
|
||||
|
||||
setSize(width, height) {
|
||||
if (width != this._width || height != this._height) {
|
||||
this._width = width;
|
||||
this._height = height;
|
||||
this._handler.setSize(width, height);
|
||||
this._framebuffer0.setSize(width, height);
|
||||
this._framebuffer1.setSize(width, height);
|
||||
}
|
||||
}
|
||||
|
||||
createSDF(sourceCanvas, width, height) {
|
||||
|
||||
var h = this._handler,
|
||||
gl = h.gl;
|
||||
|
||||
h.setSize(this._width, this._height);
|
||||
|
||||
gl.deleteTexture(this._sourceTexture);
|
||||
|
||||
this._sourceTexture = h.createTexture_l(sourceCanvas);
|
||||
|
||||
h.shaderPrograms.vfield.activate();
|
||||
var sh = h.shaderPrograms.vfield._program;
|
||||
var sha = sh.attributes,
|
||||
shu = sh.uniforms;
|
||||
|
||||
gl.activeTexture(gl.TEXTURE0);
|
||||
gl.bindTexture(gl.TEXTURE_2D, this._sourceTexture);
|
||||
gl.uniform1i(shu.uTex1._pName, 0);
|
||||
gl.uniform2fv(shu.uTexSize._pName, [this._width, this._height]);
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this._vertexBuffer);
|
||||
gl.vertexAttribPointer(sha.aPos._pName, this._vertexBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
|
||||
//VERT
|
||||
this._framebuffer0.activate();
|
||||
gl.uniform1i(shu.uDistance._pName, this._outsideDistance);
|
||||
gl.uniform2fv(shu.uNeg._pName, [0.0, -1.0]);
|
||||
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
|
||||
this._framebuffer0.deactivate();
|
||||
|
||||
//NEG VERT
|
||||
this._framebuffer2.activate();
|
||||
gl.uniform2fv(shu.uNeg._pName, [1.0, 1.0]);
|
||||
gl.uniform1i(shu.uDistance._pName, this._insideDistance);
|
||||
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
|
||||
this._framebuffer2.deactivate();
|
||||
|
||||
h.shaderPrograms.hfield.activate();
|
||||
var sh = h.shaderPrograms.hfield._program;
|
||||
var sha = sh.attributes,
|
||||
shu = sh.uniforms;
|
||||
|
||||
gl.uniform2fv(shu.uTexSize._pName, [this._width, this._height]);
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this._vertexBuffer);
|
||||
gl.vertexAttribPointer(sha.aPos._pName, this._vertexBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
|
||||
//HORIZ
|
||||
this._framebuffer1.activate();
|
||||
gl.activeTexture(gl.TEXTURE0);
|
||||
gl.bindTexture(gl.TEXTURE_2D, this._framebuffer0.texture);
|
||||
gl.uniform1i(shu.uTex1._pName, 0);
|
||||
gl.uniform1i(shu.uDistance._pName, this._outsideDistance);
|
||||
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
|
||||
this._framebuffer1.deactivate();
|
||||
|
||||
//NEG HORIZ
|
||||
this._framebuffer0.activate();
|
||||
gl.activeTexture(gl.TEXTURE0);
|
||||
gl.bindTexture(gl.TEXTURE_2D, this._framebuffer2.texture);
|
||||
gl.uniform1i(shu.uTex1._pName, 0);
|
||||
gl.uniform1i(shu.uDistance._pName, this._insideDistance);
|
||||
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
|
||||
this._framebuffer0.deactivate();
|
||||
|
||||
h.setSize(width || this._width, height || this._height);
|
||||
|
||||
//SUM
|
||||
gl.clearColor(0.0, 0.0, 0.0, 0.0);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
||||
|
||||
h.shaderPrograms.sum.activate();
|
||||
var sh = h.shaderPrograms.sum._program;
|
||||
var sha = sh.attributes,
|
||||
shu = sh.uniforms;
|
||||
|
||||
gl.activeTexture(gl.TEXTURE0);
|
||||
gl.bindTexture(gl.TEXTURE_2D, this._framebuffer1.texture);
|
||||
gl.uniform1i(shu.outside._pName, 0);
|
||||
gl.activeTexture(gl.TEXTURE1);
|
||||
gl.bindTexture(gl.TEXTURE_2D, this._framebuffer0.texture);
|
||||
gl.uniform1i(shu.inside._pName, 1);
|
||||
gl.activeTexture(gl.TEXTURE2);
|
||||
gl.bindTexture(gl.TEXTURE_2D, this._sourceTexture);
|
||||
gl.uniform1i(shu.source._pName, 2);
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this._vertexBuffer);
|
||||
gl.vertexAttribPointer(sha.aPos._pName, this._vertexBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
return h.canvas;
|
||||
}
|
||||
};
|
||||
|
||||
export { SDFCreator };
|
||||
267
src/og/utils/TerrainWorker.js
Normal file
267
src/og/utils/TerrainWorker.js
Normal file
@ -0,0 +1,267 @@
|
||||
/**
|
||||
* @module og/utils/TerrainWorker
|
||||
*/
|
||||
|
||||
'use sctrict';
|
||||
|
||||
import { QueueArray } from '../QueueArray.js';
|
||||
|
||||
class TerrainWorker {
|
||||
constructor(numWorkers = 2) {
|
||||
this._workerQueue = new QueueArray(numWorkers);
|
||||
var elevationProgramm = new Blob([_programm], { type: 'application/javascript' });
|
||||
|
||||
for (let i = 0; i < numWorkers; i++) {
|
||||
this._workerQueue.push(new Worker(URL.createObjectURL(elevationProgramm)));
|
||||
}
|
||||
|
||||
this._pendingQueue = new QueueArray(512);
|
||||
}
|
||||
|
||||
make(segment, elevations) {
|
||||
|
||||
if (segment.ready && segment.terrainIsLoading) {
|
||||
|
||||
var _elevations = new Float32Array(elevations.length);
|
||||
_elevations.set(elevations);
|
||||
|
||||
if (this._workerQueue.length) {
|
||||
|
||||
var that = this;
|
||||
|
||||
var w = this._workerQueue.pop();
|
||||
|
||||
w.onmessage = function (e) {
|
||||
segment._terrainWorkerCallback(e.data);
|
||||
that._workerQueue.unshift(this);
|
||||
if (that._pendingQueue.length) {
|
||||
var p = that._pendingQueue.pop();
|
||||
that.make(p.segment, p.elevations)
|
||||
}
|
||||
};
|
||||
|
||||
w.postMessage({
|
||||
'elevations': _elevations,
|
||||
'this_plainVertices': segment.plainVertices,
|
||||
'this_plainNormals': segment.plainNormals,
|
||||
'this_normalMapVertices': segment.normalMapVertices,
|
||||
'this_normalMapNormals': segment.normalMapNormals,
|
||||
'heightFactor': segment.planet._heightFactor,
|
||||
'gridSize': segment.planet.terrain.gridSizeByZoom[segment.tileZoom]
|
||||
}, [
|
||||
_elevations.buffer,
|
||||
segment.plainVertices.buffer,
|
||||
segment.plainNormals.buffer,
|
||||
segment.normalMapVertices.buffer,
|
||||
segment.normalMapNormals.buffer
|
||||
]);
|
||||
} else {
|
||||
this._pendingQueue.push({ 'segment': segment, 'elevations': _elevations });
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const _programm =
|
||||
'self.onmessage = function (e) {\n\
|
||||
\n\
|
||||
var Vector3 = function(x, y, z) {\n\
|
||||
this.x = x;\n\
|
||||
this.y = y;\n\
|
||||
this.z = z;\n\
|
||||
};\n\
|
||||
Vector3.prototype.sub = function(v) {\n\
|
||||
return new Vector3(this.x - v.x, this.y - v.y, this.z - v.z);\n\
|
||||
};\n\
|
||||
Vector3.prototype.add = function(v) {\n\
|
||||
return new Vector3(this.x + v.x, this.y + v.y, this.z + v.z);\n\
|
||||
};\n\
|
||||
Vector3.prototype.cross = function(v) {\n\
|
||||
return new Vector3(\n\
|
||||
this.y * v.z - this.z * v.y,\n\
|
||||
this.z * v.x - this.x * v.z,\n\
|
||||
this.x * v.y - this.y * v.x\n\
|
||||
);\n\
|
||||
};\n\
|
||||
Vector3.prototype.normalize = function(v) {\n\
|
||||
var x = this.x, y = this.y, z = this.z;\n\
|
||||
var length = 1.0 / Math.sqrt(x * x + y * y + z * z);\n\
|
||||
this.x = x * length;\n\
|
||||
this.y = y * length;\n\
|
||||
this.z = z * length;\n\
|
||||
return this;\n\
|
||||
};\n\
|
||||
\n\
|
||||
var slice = function (t, h1, h0) {\n\
|
||||
return t * (h1 - h0);\n\
|
||||
};\n\
|
||||
\n\
|
||||
var elevations = e.data.elevations,\n\
|
||||
this_plainVertices = e.data.this_plainVertices,\n\
|
||||
this_plainNormals = e.data.this_plainNormals,\n\
|
||||
this_normalMapVertices = e.data.this_normalMapVertices,\n\
|
||||
this_normalMapNormals = e.data.this_normalMapNormals,\n\
|
||||
heightFactor = e.data.heightFactor,\n\
|
||||
//fileGridSize = e.data.fileGridSize,\n\
|
||||
gridSize = e.data.gridSize;\n\
|
||||
\n\
|
||||
var xmin = 549755748352, xmax = -549755748352, ymin = 549755748352, ymax = -549755748352, zmin = 549755748352, zmax = -549755748352;\n\
|
||||
\n\
|
||||
fileGridSize = Math.sqrt(elevations.length) - 1;\n\
|
||||
\n\
|
||||
var fileGridSize_one = fileGridSize + 1,\n\
|
||||
tgs = gridSize,\n\
|
||||
dg = fileGridSize / tgs,\n\
|
||||
gs = tgs + 1,\n\
|
||||
hf = heightFactor;\n\
|
||||
\n\
|
||||
var nmvInd = 0;\n\
|
||||
var vInd = 0;\n\
|
||||
\n\
|
||||
var terrainVertices = new Float32Array(gs * gs * 3);\n\
|
||||
var normalMapNormals = new Float32Array(fileGridSize_one * fileGridSize_one * 3);\n\
|
||||
var normalMapVertices = new Float32Array(fileGridSize_one * fileGridSize_one * 3);\n\
|
||||
\n\
|
||||
var nv = this_normalMapVertices,\n\
|
||||
nn = this_normalMapNormals;\n\
|
||||
\n\
|
||||
if (fileGridSize >= tgs) {\n\
|
||||
for (var i = 0; i < fileGridSize_one; i++) {\n\
|
||||
for (var j = 0; j < fileGridSize_one; j++) {\n\
|
||||
var hInd0 = i * fileGridSize_one + j;\n\
|
||||
var vInd0 = hInd0 * 3;\n\
|
||||
var h0 = hf * elevations[hInd0];\n\
|
||||
var v0 = new Vector3(nv[vInd0] + h0 * nn[vInd0], nv[vInd0 + 1] + h0 * nn[vInd0 + 1], nv[vInd0 + 2] + h0 * nn[vInd0 + 2]);\n\
|
||||
normalMapVertices[vInd0] = v0.x;\n\
|
||||
normalMapVertices[vInd0 + 1] = v0.y;\n\
|
||||
normalMapVertices[vInd0 + 2] = v0.z;\n\
|
||||
\n\
|
||||
if (i % dg == 0 && j % dg == 0) {\n\
|
||||
terrainVertices[vInd++] = v0.x;\n\
|
||||
terrainVertices[vInd++] = v0.y;\n\
|
||||
terrainVertices[vInd++] = v0.z;\n\
|
||||
\n\
|
||||
if (v0.x < xmin) xmin = v0.x; if (v0.x > xmax) xmax = v0.x;\n\
|
||||
if (v0.y < ymin) ymin = v0.y; if (v0.y > ymax) ymax = v0.y;\n\
|
||||
if (v0.z < zmin) zmin = v0.z; if (v0.z > zmax) zmax = v0.z;\n\
|
||||
}\n\
|
||||
\n\
|
||||
if (i != fileGridSize && j != fileGridSize) {\n\
|
||||
var hInd1 = i * fileGridSize_one + j + 1;\n\
|
||||
var vInd1 = hInd1 * 3;\n\
|
||||
var h1 = hf * elevations[hInd1];\n\
|
||||
var v1 = new Vector3(nv[vInd1] + h1 * nn[vInd1], nv[vInd1 + 1] + h1 * nn[vInd1 + 1], nv[vInd1 + 2] + h1 * nn[vInd1 + 2]);\n\
|
||||
normalMapVertices[vInd1] = v1.x;\n\
|
||||
normalMapVertices[vInd1 + 1] = v1.y;\n\
|
||||
normalMapVertices[vInd1 + 2] = v1.z;\n\
|
||||
\n\
|
||||
var hInd2 = (i + 1) * fileGridSize_one + j;\n\
|
||||
var vInd2 = hInd2 * 3;\n\
|
||||
var h2 = hf * elevations[hInd2];\n\
|
||||
var v2 = new Vector3(\n\
|
||||
nv[vInd2] + h2 * nn[vInd2],\n\
|
||||
nv[vInd2 + 1] + h2 * nn[vInd2 + 1],\n\
|
||||
nv[vInd2 + 2] + h2 * nn[vInd2 + 2]);\n\
|
||||
normalMapVertices[vInd2] = v2.x;\n\
|
||||
normalMapVertices[vInd2 + 1] = v2.y;\n\
|
||||
normalMapVertices[vInd2 + 2] = v2.z;\n\
|
||||
\n\
|
||||
var hInd3 = (i + 1) * fileGridSize_one + (j + 1);\n\
|
||||
var vInd3 = hInd3 * 3;\n\
|
||||
var h3 = hf * elevations[hInd3];\n\
|
||||
var v3 = new Vector3(nv[vInd3] + h3 * nn[vInd3], nv[vInd3 + 1] + h3 * nn[vInd3 + 1], nv[vInd3 + 2] + h3 * nn[vInd3 + 2]);\n\
|
||||
normalMapVertices[vInd3] = v3.x;\n\
|
||||
normalMapVertices[vInd3 + 1] = v3.y;\n\
|
||||
normalMapVertices[vInd3 + 2] = v3.z;\n\
|
||||
\n\
|
||||
var e10 = v1.sub(v0),\n\
|
||||
e20 = v2.sub(v0),\n\
|
||||
e30 = v3.sub(v0);\n\
|
||||
var sw = e20.cross(e30).normalize();\n\
|
||||
var ne = e30.cross(e10).normalize();\n\
|
||||
var n0 = ne.add(sw).normalize();\n\
|
||||
\n\
|
||||
normalMapNormals[vInd0] += n0.x;\n\
|
||||
normalMapNormals[vInd0 + 1] += n0.y;\n\
|
||||
normalMapNormals[vInd0 + 2] += n0.z;\n\
|
||||
\n\
|
||||
normalMapNormals[vInd1] += ne.x;\n\
|
||||
normalMapNormals[vInd1 + 1] += ne.y;\n\
|
||||
normalMapNormals[vInd1 + 2] += ne.z;\n\
|
||||
\n\
|
||||
normalMapNormals[vInd2] += sw.x;\n\
|
||||
normalMapNormals[vInd2 + 1] += sw.y;\n\
|
||||
normalMapNormals[vInd2 + 2] += sw.z;\n\
|
||||
\n\
|
||||
normalMapNormals[vInd3] += n0.x;\n\
|
||||
normalMapNormals[vInd3 + 1] += n0.y;\n\
|
||||
normalMapNormals[vInd3 + 2] += n0.z;\n\
|
||||
}\n\
|
||||
}\n\
|
||||
}\n\
|
||||
\n\
|
||||
} else {\n\
|
||||
\n\
|
||||
var plain_verts = this_plainVertices;\n\
|
||||
var plainNormals = this_plainNormals;\n\
|
||||
\n\
|
||||
var oneSize = tgs / fileGridSize;\n\
|
||||
var h, inside_i, inside_j, v_i, v_j;\n\
|
||||
\n\
|
||||
for (var i = 0; i < gs; i++) {\n\
|
||||
if (i == gs - 1) {\n\
|
||||
inside_i = oneSize;\n\
|
||||
v_i = Math.floor(i / oneSize) - 1;\n\
|
||||
} else {\n\
|
||||
inside_i = i % oneSize;\n\
|
||||
v_i = Math.floor(i / oneSize);\n\
|
||||
}\n\
|
||||
\n\
|
||||
for (var j = 0; j < gs; j++) {\n\
|
||||
if (j == gs - 1) {\n\
|
||||
inside_j = oneSize;\n\
|
||||
v_j = Math.floor(j / oneSize) - 1;\n\
|
||||
} else {\n\
|
||||
inside_j = j % oneSize;\n\
|
||||
v_j = Math.floor(j / oneSize);\n\
|
||||
}\n\
|
||||
\n\
|
||||
var hvlt = elevations[v_i * fileGridSize_one + v_j],\n\
|
||||
hvrt = elevations[v_i * fileGridSize_one + v_j + 1],\n\
|
||||
hvlb = elevations[(v_i + 1) * fileGridSize_one + v_j],\n\
|
||||
hvrb = elevations[(v_i + 1) * fileGridSize_one + v_j + 1];\n\
|
||||
\n\
|
||||
if (inside_i + inside_j < oneSize) {\n\
|
||||
h = hf * (hvlt + slice(inside_j / oneSize, hvrt, hvlt) + slice(inside_i / oneSize, hvlb, hvlt));\n\
|
||||
} else {\n\
|
||||
h = hf * (hvrb + slice((oneSize - inside_j) / oneSize, hvlb, hvrb) + slice((oneSize - inside_i) / oneSize, hvrt, hvrb));\n\
|
||||
}\n\
|
||||
\n\
|
||||
var x = plain_verts[vInd] + h * plainNormals[vInd],\n\
|
||||
y = plain_verts[vInd + 1] + h * plainNormals[vInd + 1],\n\
|
||||
z = plain_verts[vInd + 2] + h * plainNormals[vInd + 2];\n\
|
||||
\n\
|
||||
terrainVertices[vInd] = x;\n\
|
||||
terrainVertices[vInd + 1] = y;\n\
|
||||
terrainVertices[vInd + 2] = z;\n\
|
||||
\n\
|
||||
vInd += 3;\n\
|
||||
\n\
|
||||
if (x < xmin) xmin = x; if (x > xmax) xmax = x;\n\
|
||||
if (y < ymin) ymin = y; if (y > ymax) ymax = y;\n\
|
||||
if (z < zmin) zmin = z; if (z > zmax) zmax = z;\n\
|
||||
\n\
|
||||
}\n\
|
||||
}\n\
|
||||
\n\
|
||||
normalMapNormals = this_plainNormals;\n\
|
||||
}\n\
|
||||
self.postMessage({ \n\
|
||||
normalMapNormals: normalMapNormals,\n\
|
||||
normalMapVertices: normalMapVertices,\n\
|
||||
terrainVertices: terrainVertices,\n\
|
||||
bounds: [xmin, xmax, ymin, ymax, zmin, zmax],\n\
|
||||
}, [normalMapNormals.buffer, normalMapVertices.buffer, terrainVertices.buffer]);\n\
|
||||
}';
|
||||
|
||||
export { TerrainWorker };
|
||||
289
src/og/utils/TextureAtlas.js
Normal file
289
src/og/utils/TextureAtlas.js
Normal file
@ -0,0 +1,289 @@
|
||||
/**
|
||||
* @module og/utils/TextureAtlas
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { ImageCanvas } from '../ImageCanvas.js';
|
||||
import { Rectangle } from '../Rectangle.js';
|
||||
import { ImagesCacheManager } from './ImagesCacheManager.js';
|
||||
|
||||
|
||||
/**
|
||||
* Border beetween stored images.
|
||||
* @type {number}
|
||||
* @const
|
||||
*/
|
||||
const BORDER_SIZE = 4;
|
||||
|
||||
/**
|
||||
* Texture atlas stores images in one texture. Each image has texture
|
||||
* coordinates returned with node creation by addImage function.
|
||||
* @class
|
||||
* @param {number} [width] - Texture atlas width, if it hasn't 1024 default.
|
||||
* @param {number} [height] - Texture atlas height, if it hasn't 1024 default..
|
||||
*/
|
||||
class TextureAtlas {
|
||||
constructor(width, height) {
|
||||
|
||||
/**
|
||||
* Atlas nodes where input images store. It can be access by image.__nodeIndex.
|
||||
* @public
|
||||
* @type {Array.<og.utils.TextureAtlasNode >}
|
||||
*/
|
||||
this.nodes = [];
|
||||
|
||||
/**
|
||||
* Created gl texture.
|
||||
* @public
|
||||
*/
|
||||
this.texture = null;
|
||||
|
||||
/**
|
||||
* Atlas canvas.
|
||||
* @public
|
||||
* @type {canvas}
|
||||
*/
|
||||
this.canvas = new ImageCanvas(width || 1024, height || 1024);
|
||||
this.clearCanvas();
|
||||
|
||||
this._handler = null;
|
||||
this._images = [];
|
||||
this._btree = null;
|
||||
this._imagesCacheManager = new ImagesCacheManager();
|
||||
this.borderSize = 4;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns atlas javascript image object.
|
||||
* @public
|
||||
* @returns {Object}
|
||||
*/
|
||||
getImage() {
|
||||
return this.canvas.getImage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns canvas object.
|
||||
* @public
|
||||
* @retuns {Object}
|
||||
*/
|
||||
getCanvas() {
|
||||
return this.canvas._canvas;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear atlas with black.
|
||||
* @public
|
||||
*/
|
||||
clearCanvas() {
|
||||
this.canvas.fillEmpty("black");
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets openglobus gl handler that creates gl texture.
|
||||
* @public
|
||||
* @param {og.webgl.Handler} handler - WebGL handler.
|
||||
*/
|
||||
assignHandler(handler) {
|
||||
this._handler = handler;
|
||||
this.createTexture();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns image diagonal size.
|
||||
* @param {Object} image - JavaSript image object.
|
||||
* @returns {number}
|
||||
*/
|
||||
getDiagonal(image) {
|
||||
var w = image.atlasWidth || image.width,
|
||||
h = image.atlasHeight || image.height;
|
||||
return Math.sqrt(w * w + h * h);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds image to the atlas and returns creted node with texture coordinates of the stored image.
|
||||
* @public
|
||||
* @param {Object} image - Input javascript image object.
|
||||
* @param {boolean} [fastInsert] - If it's true atlas doesnt restore all images again
|
||||
* and store image in the curent atlas sheme.
|
||||
* @returns {og.utils.TextureAtlasNode}
|
||||
*/
|
||||
addImage(image, fastInsert) {
|
||||
|
||||
if (!(image.width && image.height)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._images.push(image);
|
||||
|
||||
this._makeAtlas(fastInsert);
|
||||
|
||||
return this.nodes[image.__nodeIndex];
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate texture coordianates and stores node.
|
||||
* @private
|
||||
*/
|
||||
_completeNode(nodes, node) {
|
||||
if (node) {
|
||||
var w = this.canvas.getWidth(),
|
||||
h = this.canvas.getHeight();
|
||||
var im = node.image;
|
||||
var r = node.rect;
|
||||
var bs = Math.round(this.borderSize * 0.5);
|
||||
this.canvas.drawImage(im, r.left + bs, r.top + bs, im.atlasWidth, im.atlasHeight);
|
||||
var tc = node.texCoords;
|
||||
|
||||
tc[0] = (r.left + bs) / w;
|
||||
tc[1] = (r.top + bs) / h;
|
||||
|
||||
tc[2] = (r.left + bs) / w;
|
||||
tc[3] = (r.bottom - bs) / h;
|
||||
|
||||
tc[4] = (r.right - bs) / w;
|
||||
tc[5] = (r.bottom - bs) / h;
|
||||
|
||||
tc[6] = (r.right - bs) / w;
|
||||
tc[7] = (r.bottom - bs) / h;
|
||||
|
||||
tc[8] = (r.right - bs) / w;
|
||||
tc[9] = (r.top + bs) / h;
|
||||
|
||||
tc[10] = (r.left + bs) / w;
|
||||
tc[11] = (r.top + bs) / h;
|
||||
|
||||
nodes[im.__nodeIndex] = node;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Main atlas making function.
|
||||
* @private
|
||||
* @param {boolean} [fastInsert] - If it's true atlas doesnt restore all images again
|
||||
* and store image in the curent atlas sheme.
|
||||
*/
|
||||
_makeAtlas(fastInsert) {
|
||||
|
||||
if (fastInsert && this._btree) {
|
||||
var im = this._images[this._images.length - 1];
|
||||
this._completeNode(this.nodes, this._btree.insert(im));
|
||||
} else {
|
||||
var im = this._images.slice(0);
|
||||
|
||||
im.sort(function (b, a) {
|
||||
return ((a.atlasWidth || a.width) - (b.atlasWidth || b.width)) || ((a.atlasHeight || a.height) - (b.atlasHeight || b.height));
|
||||
});
|
||||
|
||||
this._btree = new TextureAtlasNode(new Rectangle(0, 0, this.canvas.getWidth(), this.canvas.getHeight()));
|
||||
this._btree.atlas = this;
|
||||
|
||||
this.clearCanvas();
|
||||
|
||||
var newNodes = [];
|
||||
for (var i = 0; i < im.length; i++) {
|
||||
this._completeNode(newNodes, this._btree.insert(im[i]));
|
||||
}
|
||||
this.nodes = [];
|
||||
this.nodes = newNodes;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates atlas gl texture.
|
||||
* @public
|
||||
*/
|
||||
createTexture() {
|
||||
if (this._handler) {
|
||||
this._handler.gl.deleteTexture(this.texture);
|
||||
this.texture = this._handler.createTexture_l(this.canvas._canvas);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Image handler callback.
|
||||
* @callback Object~successCallback
|
||||
* @param {Image} img - Loaded image.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Asynchronous function that loads and creates image to the image cache, and call success callback when it's done.
|
||||
* @public
|
||||
* @param {string} src - Image object src string.
|
||||
* @param {Object~successCallback} success - The callback that handles the image loads done.
|
||||
*/
|
||||
loadImage(src, success) {
|
||||
this._imagesCacheManager.load(src, success);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Atlas binary tree node.
|
||||
* @class
|
||||
* @prarm {og.Rectangle} rect - Node image rectangle.
|
||||
*/
|
||||
class TextureAtlasNode {
|
||||
constructor(rect) {
|
||||
this.childNodes = null;
|
||||
this.image = null;
|
||||
this.rect = rect;
|
||||
this.texCoords = [];
|
||||
this.atlas = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This algorithm has got from here:
|
||||
* http://www.blackpawn.com/texts/lightmaps/default.html
|
||||
*/
|
||||
insert(img) {
|
||||
|
||||
if (this.childNodes) {
|
||||
|
||||
var newNode = this.childNodes[0].insert(img);
|
||||
|
||||
if (newNode != null)
|
||||
return newNode;
|
||||
|
||||
return this.childNodes[1].insert(img);
|
||||
|
||||
} else {
|
||||
|
||||
if (this.image != null)
|
||||
return null;
|
||||
|
||||
var rc = this.rect;
|
||||
var w = (img.atlasWidth || img.width) + this.atlas.borderSize;
|
||||
var h = (img.atlasHeight || img.height) + this.atlas.borderSize;
|
||||
|
||||
if (w > rc.getWidth() || h > rc.getHeight())
|
||||
return null;
|
||||
|
||||
if (rc.fit(w, h)) {
|
||||
this.image = img;
|
||||
return this;
|
||||
}
|
||||
|
||||
this.childNodes = new Array(2);
|
||||
this.childNodes[0] = new TextureAtlasNode();
|
||||
this.childNodes[0].atlas = this.atlas;
|
||||
this.childNodes[1] = new TextureAtlasNode();
|
||||
this.childNodes[1].atlas = this.atlas;
|
||||
|
||||
var dw = rc.getWidth() - w;
|
||||
var dh = rc.getHeight() - h;
|
||||
|
||||
if (dw > dh) {
|
||||
this.childNodes[0].rect = new Rectangle(rc.left, rc.top, rc.left + w, rc.bottom);
|
||||
this.childNodes[1].rect = new Rectangle(rc.left + w, rc.top, rc.right, rc.bottom);
|
||||
} else {
|
||||
this.childNodes[0].rect = new Rectangle(rc.left, rc.top, rc.right, rc.top + h);
|
||||
this.childNodes[1].rect = new Rectangle(rc.left, rc.top + h, rc.right, rc.bottom);
|
||||
}
|
||||
|
||||
return this.childNodes[0].insert(img);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export { TextureAtlas };
|
||||
349
src/og/utils/VectorTileCreator.js
Normal file
349
src/og/utils/VectorTileCreator.js
Normal file
@ -0,0 +1,349 @@
|
||||
/**
|
||||
* @module og/utils/VectorTileCreator
|
||||
*/
|
||||
|
||||
'use sctrict';
|
||||
|
||||
import * as quadTree from '../quadTree/quadTree.js';
|
||||
import { Framebuffer } from '../webgl/Framebuffer.js';
|
||||
import { ShaderProgram } from '../webgl/ShaderProgram.js';
|
||||
import { types } from '../webgl/types.js';
|
||||
|
||||
const VectorTileCreator = function (planet, maxFrames, width, height) {
|
||||
|
||||
this._width = width || 256;
|
||||
this._height = height || 256;
|
||||
this._handler = planet.renderer.handler;
|
||||
this._planet = planet;
|
||||
this._framebuffer = null;
|
||||
this.MAX_FRAMES = maxFrames || 5;
|
||||
this._currentFrame = 0;
|
||||
this._queue = [];
|
||||
this._initialize();
|
||||
};
|
||||
|
||||
VectorTileCreator.prototype._initialize = function () {
|
||||
|
||||
//Line
|
||||
if (!this._handler.shaderPrograms.vectorTileLineRasterization) {
|
||||
this._handler.addShaderProgram(new ShaderProgram("vectorTileLineRasterization", {
|
||||
uniforms: {
|
||||
'viewport': { type: types.VEC2 },
|
||||
'thicknessOutline': { type: types.FLOAT },
|
||||
'alpha': { type: types.FLOAT },
|
||||
'extentParams': { type: types.VEC4 }
|
||||
},
|
||||
attributes: {
|
||||
'prev': { type: types.VEC2 },
|
||||
'current': { type: types.VEC2 },
|
||||
'next': { type: types.VEC2 },
|
||||
'order': { type: types.FLOAT },
|
||||
'color': { type: types.VEC4 },
|
||||
'thickness': { type: types.FLOAT }
|
||||
},
|
||||
vertexShader: 'attribute vec2 prev;\
|
||||
attribute vec2 current;\
|
||||
attribute vec2 next;\
|
||||
attribute float order;\
|
||||
attribute float thickness;\
|
||||
attribute vec4 color;\
|
||||
uniform float thicknessOutline;\
|
||||
uniform vec2 viewport;\
|
||||
uniform vec4 extentParams;\
|
||||
varying vec4 vColor;\
|
||||
\
|
||||
vec2 proj(vec2 coordinates){\
|
||||
return vec2(-1.0 + (coordinates - extentParams.xy) * extentParams.zw) * vec2(1.0, -1.0);\
|
||||
}\
|
||||
\
|
||||
void main(){\
|
||||
vColor = color;\
|
||||
vec2 _next = next;\
|
||||
vec2 _prev = prev;\
|
||||
if(prev == current){\
|
||||
if(next == current){\
|
||||
_next = current + vec2(1.0, 0.0);\
|
||||
_prev = current - next;\
|
||||
}else{\
|
||||
_prev = current + normalize(current - next);\
|
||||
}\
|
||||
}\
|
||||
if(next == current){\
|
||||
_next = current + normalize(current - _prev);\
|
||||
}\
|
||||
\
|
||||
vec2 sNext = proj(_next),\
|
||||
sCurrent = proj(current),\
|
||||
sPrev = proj(_prev);\
|
||||
vec2 dirNext = normalize(sNext - sCurrent);\
|
||||
vec2 dirPrev = normalize(sPrev - sCurrent);\
|
||||
float dotNP = dot(dirNext, dirPrev);\
|
||||
\
|
||||
vec2 normalNext = normalize(vec2(-dirNext.y, dirNext.x));\
|
||||
vec2 normalPrev = normalize(vec2(dirPrev.y, -dirPrev.x));\
|
||||
vec2 d = (thickness + thicknessOutline) * 0.5 * sign(order) / viewport;\
|
||||
\
|
||||
vec2 m;\
|
||||
if(dotNP >= 0.99991){\
|
||||
m = sCurrent - normalPrev * d;\
|
||||
}else{\
|
||||
vec2 dir = normalPrev + normalNext;\
|
||||
m = sCurrent + dir * d / (dirNext.x * dir.y - dirNext.y * dir.x);\
|
||||
\
|
||||
if( dotNP > 0.5 && dot(dirNext + dirPrev, m - sCurrent) < 0.0 ){\
|
||||
float occw = order * sign(dirNext.x * dirPrev.y - dirNext.y * dirPrev.x);\
|
||||
if(occw == -1.0){\
|
||||
m = sCurrent + normalPrev * d;\
|
||||
}else if(occw == 1.0){\
|
||||
m = sCurrent + normalNext * d;\
|
||||
}else if(occw == -2.0){\
|
||||
m = sCurrent + normalNext * d;\
|
||||
}else if(occw == 2.0){\
|
||||
m = sCurrent + normalPrev * d;\
|
||||
}\
|
||||
}else if(distance(sCurrent, m) > min(distance(sCurrent, sNext), distance(sCurrent, sPrev))){\
|
||||
m = sCurrent + normalNext * d;\
|
||||
}\
|
||||
}\
|
||||
gl_Position = vec4(m.x, m.y, 0.0, 1.0);\
|
||||
}',
|
||||
fragmentShader: 'precision highp float;\
|
||||
uniform float alpha;\
|
||||
varying vec4 vColor;\
|
||||
void main() {\
|
||||
gl_FragColor = vec4(vColor.rgb, alpha * vColor.a);\
|
||||
}'
|
||||
}));
|
||||
}
|
||||
|
||||
//Polygon
|
||||
if (!this._handler.shaderPrograms.vectorTilePolygonRasterization) {
|
||||
this._handler.addShaderProgram(new ShaderProgram("vectorTilePolygonRasterization", {
|
||||
uniforms: {
|
||||
'extentParams': { type: types.VEC4 }
|
||||
},
|
||||
attributes: {
|
||||
'coordinates': { type: types.VEC2 },
|
||||
'colors': { type: types.VEC4 }
|
||||
},
|
||||
vertexShader: 'attribute vec2 coordinates; \
|
||||
attribute vec4 colors; \
|
||||
uniform vec4 extentParams; \
|
||||
varying vec4 color;\
|
||||
void main() { \
|
||||
color = colors;\
|
||||
gl_Position = vec4((-1.0 + (coordinates - extentParams.xy) * extentParams.zw) * vec2(1.0, -1.0), 0.0, 1.0); \
|
||||
}',
|
||||
fragmentShader: 'precision highp float;\
|
||||
varying vec4 color;\
|
||||
void main () { \
|
||||
gl_FragColor = color; \
|
||||
}'
|
||||
}));
|
||||
}
|
||||
|
||||
this._framebuffer = new Framebuffer(this._handler, {
|
||||
width: this._width,
|
||||
height: this._height,
|
||||
useDepth: false
|
||||
});
|
||||
this._framebuffer.init();
|
||||
};
|
||||
|
||||
VectorTileCreator.prototype.frame = function () {
|
||||
if (this._planet.layerLock.isFree() && this._queue.length) {
|
||||
var h = this._handler,
|
||||
gl = h.gl;
|
||||
|
||||
gl.disable(gl.CULL_FACE);
|
||||
gl.disable(gl.DEPTH_TEST);
|
||||
gl.enable(gl.BLEND);
|
||||
gl.blendEquationSeparate(gl.FUNC_ADD, gl.FUNC_ADD);
|
||||
gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
var hLine = h.shaderPrograms.vectorTileLineRasterization,
|
||||
hPoly = h.shaderPrograms.vectorTilePolygonRasterization;
|
||||
|
||||
var width, height;
|
||||
var pickingMask, texture;
|
||||
|
||||
var prevLayerId = -1;
|
||||
|
||||
var extentParams = new Array(4);
|
||||
|
||||
var f = this._framebuffer.activate();
|
||||
|
||||
var width2 = this._width * 2,
|
||||
height2 = this._height * 2;
|
||||
|
||||
var deltaTime = 0,
|
||||
startTime = window.performance.now();
|
||||
|
||||
while (this._planet.layerLock.isFree() && this._queue.length && deltaTime < 0.25) {
|
||||
var material = this._queue.shift();
|
||||
if (material.isLoading && material.segment.node.getState() === quadTree.RENDERING) {
|
||||
|
||||
if (material.segment.tileZoom <= 3) {
|
||||
width = width2;
|
||||
height = height2;
|
||||
} else {
|
||||
width = this._width;
|
||||
height = this._height;
|
||||
}
|
||||
|
||||
texture = material._updateTexture && material._updateTexture || h.createEmptyTexture_l(width, height);
|
||||
|
||||
f.setSize(width, height);
|
||||
|
||||
f.bindOutputTexture(texture);
|
||||
|
||||
gl.clearColor(1.0, 1.0, 1.0, 0.0);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
|
||||
var extent = material.segment.getExtentMerc();
|
||||
extentParams[0] = extent.southWest.lon;
|
||||
extentParams[1] = extent.southWest.lat;
|
||||
extentParams[2] = 2.0 / extent.getWidth();
|
||||
extentParams[3] = 2.0 / extent.getHeight();
|
||||
|
||||
hPoly.activate();
|
||||
var sh = hPoly._program;
|
||||
var sha = sh.attributes,
|
||||
shu = sh.uniforms;
|
||||
|
||||
var geomHandler = material.layer._geometryHandler;
|
||||
|
||||
//=========================================
|
||||
//Polygon rendering
|
||||
//=========================================
|
||||
gl.uniform4fv(shu.extentParams._pName, extentParams);
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, geomHandler._polyVerticesBufferMerc);
|
||||
gl.vertexAttribPointer(sha.coordinates._pName, geomHandler._polyVerticesBufferMerc.itemSize, gl.FLOAT, false, 0, 0);
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, geomHandler._polyColorsBuffer);
|
||||
gl.vertexAttribPointer(sha.colors._pName, geomHandler._polyColorsBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
|
||||
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, geomHandler._polyIndexesBuffer);
|
||||
|
||||
gl.drawElements(gl.TRIANGLES, geomHandler._polyIndexesBuffer.numItems, gl.UNSIGNED_INT, 0);
|
||||
|
||||
//Polygon picking PASS
|
||||
if (material.layer._pickingEnabled) {
|
||||
if (!material.pickingReady) {
|
||||
if (material._updatePickingMask) {
|
||||
pickingMask = material._updatePickingMask;
|
||||
} else {
|
||||
pickingMask = h.createEmptyTexture_n(width, height);
|
||||
}
|
||||
|
||||
f.bindOutputTexture(pickingMask);
|
||||
|
||||
gl.clearColor(0.0, 0.0, 0.0, 0.0);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, geomHandler._polyPickingColorsBuffer);
|
||||
gl.vertexAttribPointer(sha.colors._pName, geomHandler._polyPickingColorsBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
|
||||
gl.drawElements(gl.TRIANGLES, geomHandler._polyIndexesBuffer.numItems, gl.UNSIGNED_INT, 0);
|
||||
} else {
|
||||
pickingMask = material.pickingMask;
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================
|
||||
//Strokes and linestrings rendering
|
||||
//=========================================
|
||||
f.bindOutputTexture(texture);
|
||||
|
||||
hLine.activate();
|
||||
sh = hLine._program;
|
||||
sha = sh.attributes;
|
||||
shu = sh.uniforms;
|
||||
|
||||
gl.uniform2fv(shu.viewport._pName, [width, height]);
|
||||
|
||||
gl.uniform4fv(shu.extentParams._pName, extentParams);
|
||||
|
||||
//vertex
|
||||
var mb = geomHandler._lineVerticesBufferMerc;
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, mb);
|
||||
gl.vertexAttribPointer(sha.prev._pName, mb.itemSize, gl.FLOAT, false, 8, 0);
|
||||
gl.vertexAttribPointer(sha.current._pName, mb.itemSize, gl.FLOAT, false, 8, 32);
|
||||
gl.vertexAttribPointer(sha.next._pName, mb.itemSize, gl.FLOAT, false, 8, 64);
|
||||
|
||||
//order
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, geomHandler._lineOrdersBuffer);
|
||||
gl.vertexAttribPointer(sha.order._pName, geomHandler._lineOrdersBuffer.itemSize, gl.FLOAT, false, 4, 0);
|
||||
|
||||
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, geomHandler._lineIndexesBuffer);
|
||||
|
||||
//PASS - stroke
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, geomHandler._lineStrokesBuffer);
|
||||
gl.vertexAttribPointer(sha.thickness._pName, geomHandler._lineStrokesBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, geomHandler._lineStrokeColorsBuffer);
|
||||
gl.vertexAttribPointer(sha.color._pName, geomHandler._lineStrokeColorsBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
|
||||
//Antialias pass
|
||||
gl.uniform1f(shu.thicknessOutline._pName, 2);
|
||||
gl.uniform1f(shu.alpha._pName, 0.54);
|
||||
gl.drawElements(gl.TRIANGLE_STRIP, geomHandler._lineIndexesBuffer.numItems, gl.UNSIGNED_INT, 0);
|
||||
//
|
||||
//Aliased pass
|
||||
gl.uniform1f(shu.thicknessOutline._pName, 1);
|
||||
gl.uniform1f(shu.alpha._pName, 1.0);
|
||||
gl.drawElements(gl.TRIANGLE_STRIP, geomHandler._lineIndexesBuffer.numItems, gl.UNSIGNED_INT, 0);
|
||||
|
||||
//PASS - inside line
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, geomHandler._lineThicknessBuffer);
|
||||
gl.vertexAttribPointer(sha.thickness._pName, geomHandler._lineThicknessBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, geomHandler._lineColorsBuffer);
|
||||
gl.vertexAttribPointer(sha.color._pName, geomHandler._lineColorsBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
|
||||
//Antialias pass
|
||||
gl.uniform1f(shu.thicknessOutline._pName, 2);
|
||||
gl.uniform1f(shu.alpha._pName, 0.54);
|
||||
gl.drawElements(gl.TRIANGLE_STRIP, geomHandler._lineIndexesBuffer.numItems, gl.UNSIGNED_INT, 0);
|
||||
//
|
||||
//Aliased pass
|
||||
gl.uniform1f(shu.thicknessOutline._pName, 1);
|
||||
gl.uniform1f(shu.alpha._pName, 1.0);
|
||||
gl.drawElements(gl.TRIANGLE_STRIP, geomHandler._lineIndexesBuffer.numItems, gl.UNSIGNED_INT, 0);
|
||||
|
||||
if (material.layer._pickingEnabled && !material.pickingReady) {
|
||||
f.bindOutputTexture(pickingMask);
|
||||
gl.uniform1f(shu.thicknessOutline._pName, 8);
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, geomHandler._linePickingColorsBuffer);
|
||||
gl.vertexAttribPointer(sha.color._pName, geomHandler._linePickingColorsBuffer.itemSize, gl.FLOAT, false, 0, 0);
|
||||
gl.drawElements(gl.TRIANGLE_STRIP, geomHandler._lineIndexesBuffer.numItems, gl.UNSIGNED_INT, 0);
|
||||
}
|
||||
|
||||
material.applyTexture(texture, pickingMask);
|
||||
|
||||
} else {
|
||||
material.isLoading = false;
|
||||
}
|
||||
|
||||
deltaTime = window.performance.now() - startTime;
|
||||
prevLayerId = material.layer._id;
|
||||
}
|
||||
|
||||
gl.disable(gl.BLEND);
|
||||
gl.enable(gl.DEPTH_TEST);
|
||||
gl.enable(gl.CULL_FACE);
|
||||
|
||||
f.deactivate();
|
||||
}
|
||||
};
|
||||
|
||||
VectorTileCreator.prototype.add = function (material) {
|
||||
this._queue.push(material);
|
||||
};
|
||||
|
||||
VectorTileCreator.prototype.remove = function (material) {
|
||||
//...
|
||||
};
|
||||
|
||||
export { VectorTileCreator };
|
||||
251
src/og/webgl/Framebuffer.js
Normal file
251
src/og/webgl/Framebuffer.js
Normal file
@ -0,0 +1,251 @@
|
||||
/**
|
||||
* @module og/webgl/Framebuffer
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { ImageCanvas } from '../ImageCanvas.js';
|
||||
|
||||
/**
|
||||
* Class represents framebuffer.
|
||||
* @class
|
||||
* @param {og.webgl.Handler} handler - WebGL handler.
|
||||
* @param {number} [width] - Framebuffer width. Default is handler canvas width.
|
||||
* @param {number} [height] - Framebuffer height. Default is handler canvas height.
|
||||
*/
|
||||
const Framebuffer = function (handler, options) {
|
||||
options = options || {};
|
||||
|
||||
/**
|
||||
* WebGL handler.
|
||||
* @public
|
||||
* @type {og.webgl.Handler}
|
||||
*/
|
||||
this.handler = handler;
|
||||
|
||||
/**
|
||||
* Framebuffer object.
|
||||
* @private
|
||||
* @type {Object}
|
||||
*/
|
||||
this._fbo = null;
|
||||
|
||||
/**
|
||||
* Renderbuffer object.
|
||||
* @private
|
||||
* @type {Object}
|
||||
*/
|
||||
this._rbo = null;
|
||||
|
||||
/**
|
||||
* Framebuffer width.
|
||||
* @private
|
||||
* @type {number}
|
||||
*/
|
||||
this._width = options.width || handler.canvas.width;
|
||||
|
||||
/**
|
||||
* Framebuffer width.
|
||||
* @private
|
||||
* @type {number}
|
||||
*/
|
||||
this._height = options.height || handler.canvas.height;
|
||||
|
||||
this._useDepth = options.useDepth != undefined ? options.useDepth : true;
|
||||
|
||||
/**
|
||||
* Framebuffer activity.
|
||||
* @private
|
||||
* @type {boolean}
|
||||
*/
|
||||
this._active = false;
|
||||
|
||||
/**
|
||||
* Framebuffer texture.
|
||||
* @public
|
||||
* @type {number}
|
||||
*/
|
||||
this.texture = options.texture || null;
|
||||
};
|
||||
|
||||
Framebuffer.prototype.destroy = function () {
|
||||
var gl = this.handler.gl;
|
||||
gl.deleteTexture(this.texture);
|
||||
gl.deleteFramebuffer(this._fbo);
|
||||
gl.deleteRenderbuffer(this._rbo);
|
||||
|
||||
this.texture = null;
|
||||
this._rbo = null;
|
||||
this._fbo = null;
|
||||
|
||||
this._active = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Framebuffer initialization.
|
||||
* @private
|
||||
*/
|
||||
Framebuffer.prototype.init = function () {
|
||||
var gl = this.handler.gl;
|
||||
|
||||
this._fbo = gl.createFramebuffer();
|
||||
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, this._fbo);
|
||||
!this.texture && this.bindOutputTexture(this.handler.createEmptyTexture_l(this._width, this._height));
|
||||
|
||||
if (this._useDepth) {
|
||||
this._rbo = gl.createRenderbuffer();
|
||||
gl.bindRenderbuffer(gl.RENDERBUFFER, this._rbo);
|
||||
gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, this._width, this._height);
|
||||
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, this._rbo);
|
||||
gl.bindRenderbuffer(gl.RENDERBUFFER, null);
|
||||
}
|
||||
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind buffer texture.
|
||||
* @public
|
||||
* @param{Object} texture - Output texture.
|
||||
*/
|
||||
Framebuffer.prototype.bindOutputTexture = function (texture) {
|
||||
var gl = this.handler.gl;
|
||||
this.texture = texture;
|
||||
gl.bindTexture(gl.TEXTURE_2D, texture);
|
||||
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.texture, 0);
|
||||
gl.bindTexture(gl.TEXTURE_2D, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets framebuffer viewport size.
|
||||
* @public
|
||||
* @param {number} width - Framebuffer width.
|
||||
* @param {number} height - Framebuffer height.
|
||||
*/
|
||||
Framebuffer.prototype.setSize = function (width, height) {
|
||||
this._width = width;
|
||||
this._height = height;
|
||||
if (this._active) {
|
||||
this.handler.gl.viewport(0, 0, this._width, this._height);
|
||||
}
|
||||
if (this._useDepth) {
|
||||
this.destroy();
|
||||
this.init();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns framebuffer completed.
|
||||
* @public
|
||||
* @returns {boolean}
|
||||
*/
|
||||
Framebuffer.prototype.isComplete = function () {
|
||||
var gl = this.handler.gl;
|
||||
if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) == gl.FRAMEBUFFER_COMPLETE)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads all pixels(RGBA colors) from framebuffer.
|
||||
* @public
|
||||
* @returns {Array.<number>}
|
||||
*/
|
||||
Framebuffer.prototype.readAllPixels = function () {
|
||||
var gl = this.handler.gl;
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, this._fbo);
|
||||
var pixelValues = new Uint8Array(4 * this._width * this._height);
|
||||
gl.readPixels(0, 0, this._width, this._height, gl.RGBA, gl.UNSIGNED_BYTE, pixelValues);
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
||||
return pixelValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets pixel RBGA color from framebuffer by coordinates.
|
||||
* @public
|
||||
* @param {number} x - Normalized x - coordinate.
|
||||
* @param {number} y - Normalized y - coordinate.
|
||||
* @returns {Array.<number,number,number,number>}
|
||||
*/
|
||||
Framebuffer.prototype.readPixel = function (nx, ny) {
|
||||
var gl = this.handler.gl;
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, this._fbo);
|
||||
var pixelValues = new Uint8Array(4);
|
||||
gl.readPixels(nx * this._width, ny * this._height, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixelValues);
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
||||
return pixelValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate framebuffer frame to draw.
|
||||
* @public
|
||||
* @returns {og.webgl.Framebuffer} Returns Current framebuffer.
|
||||
*/
|
||||
Framebuffer.prototype.activate = function () {
|
||||
var gl = this.handler.gl;
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, this._fbo);
|
||||
gl.viewport(0, 0, this._width, this._height);
|
||||
this._active = true;
|
||||
var c = this.handler.framebufferStack.current().data;
|
||||
c && (c._active = false);
|
||||
this.handler.framebufferStack.push(this);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deactivate framebuffer frame.
|
||||
* @public
|
||||
*/
|
||||
Framebuffer.prototype.deactivate = function () {
|
||||
var h = this.handler,
|
||||
gl = h.gl;
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
||||
this._active = false;
|
||||
|
||||
var f = this.handler.framebufferStack.popPrev();
|
||||
|
||||
if (f) {
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, f._fbo);
|
||||
gl.viewport(0, 0, f._width, f._height);
|
||||
} else {
|
||||
gl.viewport(0, 0, h.canvas.width, h.canvas.height);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets JavaScript image object that framebuffer has drawn.
|
||||
* @public
|
||||
* @returns {Object}
|
||||
*/
|
||||
Framebuffer.prototype.getImage = function () {
|
||||
var data = this.readAllPixels();
|
||||
var imageCanvas = new ImageCanvas(this._width, this._height);
|
||||
imageCanvas.setData(data);
|
||||
return imageCanvas.getImage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Open dialog window with framebuffer image.
|
||||
* @public
|
||||
*/
|
||||
Framebuffer.prototype.openImage = function () {
|
||||
var img = this.getImage();
|
||||
var dataUrl = img.src;
|
||||
var windowContent = '<!DOCTYPE html>';
|
||||
windowContent += '<html>'
|
||||
windowContent += '<head><title>Print</title></head>';
|
||||
windowContent += '<body>'
|
||||
windowContent += '<img src="' + dataUrl + '">';
|
||||
windowContent += '</body>';
|
||||
windowContent += '</html>';
|
||||
var printWin = window.open('', '', 'width=' + img.width + 'px ,height=' + img.height + 'px');
|
||||
printWin.document.open();
|
||||
printWin.document.write(windowContent);
|
||||
printWin.document.close();
|
||||
printWin.focus();
|
||||
}
|
||||
|
||||
|
||||
export { Framebuffer };
|
||||
907
src/og/webgl/Handler.js
Normal file
907
src/og/webgl/Handler.js
Normal file
@ -0,0 +1,907 @@
|
||||
/**
|
||||
* @module og/webgl/Handler
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { cons } from '../cons.js';
|
||||
import { Clock } from '../Clock.js';
|
||||
import { ImageCanvas } from '../ImageCanvas.js';
|
||||
import { ShaderController } from './ShaderController.js';
|
||||
import { Stack } from '../Stack.js';
|
||||
import { Vec2 } from '../math/Vec2.js';
|
||||
|
||||
/**
|
||||
* Maximum texture image size.
|
||||
* @const
|
||||
* @type {number}
|
||||
*/
|
||||
const MAX_SIZE = 4096;
|
||||
|
||||
const vendorPrefixes = ["", "WEBKIT_", "MOZ_"];
|
||||
|
||||
/**
|
||||
* A WebGL handler for accessing low-level WebGL capabilities.
|
||||
* @class
|
||||
* @param {string} id - Canvas element id that WebGL handler assing with. If it's null
|
||||
* or undefined creates hidden canvas and handler bacomes hidden.
|
||||
* @param {Object} [params] - Handler options:
|
||||
* @param {number} [params.anisotropy] - Anisitropy filter degree. 8 is default.
|
||||
* @param {number} [params.width] - Hidden handler width. 256 is default.
|
||||
* @param {number} [params.height] - Hidden handler height. 256 is default.
|
||||
* @param {Object} [param.scontext] - Native WebGL context attributes. See https://www.khronos.org/registry/webgl/specs/latest/1.0/#WEBGLCONTEXTATTRIBUTES
|
||||
* @param {Array.<string>} [params.extensions] - Additional WebGL extension list. Available by default: OES_standard_derivatives, EXT_texture_filter_anisotropic.
|
||||
*/
|
||||
const Handler = function (id, params) {
|
||||
|
||||
/**
|
||||
* Application default timer.
|
||||
* @public
|
||||
* @type {og.Clock}
|
||||
*/
|
||||
this.defaultClock = new Clock();
|
||||
|
||||
/**
|
||||
* Custom timers.
|
||||
* @protected
|
||||
* @type{og.Clock[]}
|
||||
*/
|
||||
this._clocks = [];
|
||||
|
||||
/**
|
||||
* Draw frame time in milliseconds.
|
||||
* @public
|
||||
* @readonly
|
||||
* @type {number}
|
||||
*/
|
||||
this.deltaTime = 0;
|
||||
|
||||
/**
|
||||
* WebGL rendering canvas element.
|
||||
* @public
|
||||
* @type {Object}
|
||||
*/
|
||||
this.canvas = null;
|
||||
|
||||
/**
|
||||
* WebGL context.
|
||||
* @public
|
||||
* @type {Object}
|
||||
*/
|
||||
this.gl = null;
|
||||
|
||||
/**
|
||||
* Shader program controller list.
|
||||
* @public
|
||||
* @type {Object.<og.webgl.ShaderController>}
|
||||
*/
|
||||
this.shaderPrograms = {};
|
||||
|
||||
/**
|
||||
* Current active shader program controller.
|
||||
* @public
|
||||
* @type {og.webgl.ShaderController}
|
||||
*/
|
||||
this.activeShaderProgram = null;
|
||||
|
||||
/**
|
||||
* Handler parameters.
|
||||
* @private
|
||||
* @type {Object}
|
||||
*/
|
||||
this._params = params || {};
|
||||
this._params.anisotropy = this._params.anisotropy || 8;
|
||||
var w = this._params.width;
|
||||
if (w > MAX_SIZE) {
|
||||
w = MAX_SIZE;
|
||||
}
|
||||
this._params.width = w || 256;
|
||||
|
||||
var h = this._params.height;
|
||||
if (h > MAX_SIZE) {
|
||||
h = MAX_SIZE;
|
||||
}
|
||||
this._params.height = h || 256;
|
||||
this._params.context = this._params.context || {};
|
||||
this._params.extensions = this._params.extensions || [];
|
||||
this._oneByHeight = 1 / this._params.height;
|
||||
|
||||
/**
|
||||
* Current WebGL extensions. Becomes here after context initialization.
|
||||
* @public
|
||||
* @type {Object}
|
||||
*/
|
||||
this.extensions = {};
|
||||
|
||||
/**
|
||||
* HTML Canvas object id.
|
||||
* @private
|
||||
* @type {Object}
|
||||
*/
|
||||
this._id = id;
|
||||
|
||||
this._lastAnimationFrameTime = 0;
|
||||
|
||||
this._initialized = false;
|
||||
|
||||
/**
|
||||
* Animation frame function assigned from outside(Ex. from Renderer).
|
||||
* @private
|
||||
* @type {frameCallback}
|
||||
*/
|
||||
this._frameCallback = function () { };
|
||||
|
||||
this.transparentTexture = null;
|
||||
|
||||
this.framebufferStack = new Stack();
|
||||
|
||||
if (params.autoActivate) {
|
||||
this.initialize();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The return value is null if the extension is not supported, or an extension object otherwise.
|
||||
* @param {Object} gl - WebGl context pointer.
|
||||
* @param {String} name - Extension name.
|
||||
* @returns {Object}
|
||||
*/
|
||||
Handler.getExtension = function (gl, name) {
|
||||
var i, ext;
|
||||
for (i in vendorPrefixes) {
|
||||
ext = gl.getExtension(vendorPrefixes[i] + name);
|
||||
if (ext) {
|
||||
return ext;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a drawing context on the canvas, or null if the context identifier is not supported.
|
||||
* @param {Object} canvas - HTML canvas object.
|
||||
* @params {Object} [contextAttributes] - See canvas.getContext contextAttributes.
|
||||
* @returns {Object}
|
||||
*/
|
||||
Handler.getContext = function (canvas, contextAttributes) {
|
||||
var ctx;
|
||||
try {
|
||||
ctx = canvas.getContext("webgl", contextAttributes) ||
|
||||
canvas.getContext("experimental-webgl", contextAttributes);
|
||||
//ctx.canvas = canvas;
|
||||
}
|
||||
catch (ex) {
|
||||
cons.logErr("exception during the GL context initialization");
|
||||
}
|
||||
if (!ctx) {
|
||||
cons.logErr("could not initialise WebGL");
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets animation frame function.
|
||||
* @public
|
||||
* @param {callback} - Frame callback.
|
||||
*/
|
||||
Handler.prototype.setFrameCallback = function (callback) {
|
||||
callback && (this._frameCallback = callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates NEAREST filter texture.
|
||||
* @public
|
||||
* @param {Object} image - Image or Canvas object.
|
||||
* @returns {Object} - WebGL texture object.
|
||||
*/
|
||||
Handler.prototype.createTexture_n = function (image) {
|
||||
var gl = this.gl;
|
||||
var texture = gl.createTexture();
|
||||
gl.bindTexture(gl.TEXTURE_2D, texture);
|
||||
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
||||
gl.bindTexture(gl.TEXTURE_2D, null);
|
||||
return texture;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates Empty half float texture.
|
||||
* @public
|
||||
* @param {number} width - Empty texture width.
|
||||
* @param {number} height - Empty texture height.
|
||||
* @returns {Object} - WebGL half float texture object.
|
||||
*/
|
||||
Handler.prototype.createEmptyTexture_hf = function (width, height) {
|
||||
var gl = this.gl;
|
||||
var texture = gl.createTexture();
|
||||
gl.bindTexture(gl.TEXTURE_2D, texture);
|
||||
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.HALF_FLOAT_OES, null);
|
||||
//gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
||||
//gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
||||
//gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
||||
//gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
||||
gl.bindTexture(gl.TEXTURE_2D, null);
|
||||
return texture;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates Empty float texture.
|
||||
* @public
|
||||
* @param {number} width - Empty texture width.
|
||||
* @param {number} height - Empty texture height.
|
||||
* @returns {Object} - WebGL float texture object.
|
||||
*/
|
||||
Handler.prototype.createEmptyTexture_f = function (width, height) {
|
||||
var gl = this.gl;
|
||||
var texture = gl.createTexture();
|
||||
gl.bindTexture(gl.TEXTURE_2D, texture);
|
||||
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.FLOAT, null);
|
||||
//gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
||||
//gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
||||
//gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
||||
//gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
||||
gl.bindTexture(gl.TEXTURE_2D, null);
|
||||
return texture;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates Empty NEAREST filtered texture.
|
||||
* @public
|
||||
* @param {number} width - Empty texture width.
|
||||
* @param {number} height - Empty texture height.
|
||||
* @returns {Object} - WebGL texture object.
|
||||
*/
|
||||
Handler.prototype.createEmptyTexture_n = function (width, height) {
|
||||
var gl = this.gl;
|
||||
var texture = gl.createTexture();
|
||||
gl.bindTexture(gl.TEXTURE_2D, texture);
|
||||
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
||||
gl.bindTexture(gl.TEXTURE_2D, null);
|
||||
return texture;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates empty LINEAR filtered texture.
|
||||
* @public
|
||||
* @param {number} width - Empty texture width.
|
||||
* @param {number} height - Empty texture height.
|
||||
* @returns {Object} - WebGL texture object.
|
||||
*/
|
||||
Handler.prototype.createEmptyTexture_l = function (width, height) {
|
||||
var gl = this.gl;
|
||||
var texture = gl.createTexture();
|
||||
gl.bindTexture(gl.TEXTURE_2D, texture);
|
||||
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
||||
gl.bindTexture(gl.TEXTURE_2D, null);
|
||||
return texture;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates LINEAR filter texture.
|
||||
* @public
|
||||
* @param {Object} image - Image or Canvas object.
|
||||
* @returns {Object} - WebGL texture object.
|
||||
*/
|
||||
Handler.prototype.createTexture_l = function (image) {
|
||||
var gl = this.gl;
|
||||
var texture = gl.createTexture();
|
||||
gl.bindTexture(gl.TEXTURE_2D, texture);
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
||||
gl.bindTexture(gl.TEXTURE_2D, null);
|
||||
return texture;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates MIPMAP filter texture.
|
||||
* @public
|
||||
* @param {Object} image - Image or Canvas object.
|
||||
* @returns {Object} - WebGL texture object.
|
||||
*/
|
||||
Handler.prototype.createTexture_mm = function (image) {
|
||||
var gl = this.gl;
|
||||
var texture = gl.createTexture();
|
||||
gl.bindTexture(gl.TEXTURE_2D, texture);
|
||||
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
|
||||
gl.generateMipmap(gl.TEXTURE_2D);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
||||
gl.bindTexture(gl.TEXTURE_2D, null);
|
||||
return texture;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates ANISOTROPY filter texture.
|
||||
* @public
|
||||
* @param {Object} image - Image or Canvas object.
|
||||
* @returns {Object} - WebGL texture object.
|
||||
*/
|
||||
Handler.prototype.createTexture_a = function (image) {
|
||||
var gl = this.gl;
|
||||
var texture = gl.createTexture();
|
||||
gl.bindTexture(gl.TEXTURE_2D, texture);
|
||||
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
|
||||
gl.generateMipmap(gl.TEXTURE_2D);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
|
||||
gl.texParameterf(gl.TEXTURE_2D, this.extensions.EXT_texture_filter_anisotropic.TEXTURE_MAX_ANISOTROPY_EXT, this._params.anisotropy);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
||||
gl.bindTexture(gl.TEXTURE_2D, null);
|
||||
return texture;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates DEFAULT filter texture, ANISOTROPY is default.
|
||||
* @public
|
||||
* @param {Object} image - Image or Canvas object.
|
||||
* @returns {Object} - WebGL texture object.
|
||||
*/
|
||||
Handler.prototype.createTexture = function (image) {
|
||||
return this.createTexture_a(image)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates cube texture.
|
||||
* @public
|
||||
* @param {Object.<string>} params - Face image urls:
|
||||
* @param {string} params.px - Positive X or right image url.
|
||||
* @param {string} params.nx - Negative X or left image url.
|
||||
* @param {string} params.py - Positive Y or up image url.
|
||||
* @param {string} params.ny - Negative Y or bottom image url.
|
||||
* @param {string} params.pz - Positive Z or face image url.
|
||||
* @param {string} params.nz - Negative Z or back image url.
|
||||
* @returns {Object} - WebGL texture object.
|
||||
*/
|
||||
Handler.prototype.loadCubeMapTexture = function (params) {
|
||||
var gl = this.gl;
|
||||
var texture = gl.createTexture();
|
||||
gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture);
|
||||
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
||||
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
||||
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
||||
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
||||
|
||||
var faces = [[params.px, gl.TEXTURE_CUBE_MAP_POSITIVE_X],
|
||||
[params.nx, gl.TEXTURE_CUBE_MAP_NEGATIVE_X],
|
||||
[params.py, gl.TEXTURE_CUBE_MAP_POSITIVE_Y],
|
||||
[params.ny, gl.TEXTURE_CUBE_MAP_NEGATIVE_Y],
|
||||
[params.pz, gl.TEXTURE_CUBE_MAP_POSITIVE_Z],
|
||||
[params.nz, gl.TEXTURE_CUBE_MAP_NEGATIVE_Z]];
|
||||
|
||||
var imageCanvas = new ImageCanvas();
|
||||
imageCanvas.fillEmpty();
|
||||
var emptyImage = imageCanvas.getImage();
|
||||
|
||||
for (var i = 0; i < faces.length; i++) {
|
||||
var face = faces[i][1];
|
||||
gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture);
|
||||
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
|
||||
gl.texImage2D(face, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, emptyImage);
|
||||
}
|
||||
|
||||
for (var i = 0; i < faces.length; i++) {
|
||||
var face = faces[i][1];
|
||||
var image = new Image();
|
||||
image.crossOrigin = '';
|
||||
image.onload = function (texture, face, image) {
|
||||
return function () {
|
||||
gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture);
|
||||
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
|
||||
gl.texImage2D(face, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
|
||||
}
|
||||
}(texture, face, image);
|
||||
image.src = faces[i][0];
|
||||
}
|
||||
return texture;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds shader program to the handler.
|
||||
* @public
|
||||
* @param {og.shaderProgram.ShaderProgram} program - Shader program.
|
||||
* @param {boolean} [notActivate] - If it's true program will not compile.
|
||||
*/
|
||||
Handler.prototype.addShaderProgram = function (program, notActivate) {
|
||||
if (!this.shaderPrograms[program.name]) {
|
||||
var sc = new ShaderController(this, program);
|
||||
this.shaderPrograms[program.name] = sc;
|
||||
this._initShaderController(sc);
|
||||
if (notActivate)
|
||||
sc._activated = false;
|
||||
} else {
|
||||
!COMPILED && cons.logWrn("og.webgl.Handler:284 - shader program: '" + program.name + "' is allready exists.");
|
||||
}
|
||||
return program;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes shader program from handler.
|
||||
* @public
|
||||
* @param {String} program - Shader program name.
|
||||
*/
|
||||
Handler.prototype.removeShaderProgram = function (name) {
|
||||
this.shaderPrograms[name] && this.shaderPrograms[name].remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds shader programs to the handler.
|
||||
* @public
|
||||
* @param {Array.<og.shaderProgram.ShaderProgram>} programsArr - Shader program array.
|
||||
*/
|
||||
Handler.prototype.addShaderPrograms = function (programsArr) {
|
||||
for (var i = 0; i < programsArr.length; i++) {
|
||||
this.addShaderProgram(programsArr[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used in addShaderProgram
|
||||
* @private
|
||||
* @param {og.webgl.ShaderController}
|
||||
*/
|
||||
Handler.prototype._initShaderController = function (sc) {
|
||||
if (this._initialized) {
|
||||
sc.initialize();
|
||||
if (!this.activeShaderProgram) {
|
||||
this.activeShaderProgram = sc;
|
||||
sc.activate();
|
||||
} else {
|
||||
sc.deactivate();
|
||||
this.activeShaderProgram._program.enableAttribArrays();
|
||||
this.activeShaderProgram._program.use();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used in init function.
|
||||
* @private
|
||||
*/
|
||||
Handler.prototype._initShaderPrograms = function () {
|
||||
for (var p in this.shaderPrograms) {
|
||||
this._initShaderController(this.shaderPrograms[p]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize additional WebGL extensions.
|
||||
* @public
|
||||
* @param {string} extensionStr - Extension name.
|
||||
*/
|
||||
Handler.prototype.initializeExtension = function (extensionStr, showLog) {
|
||||
if (!(this.extensions && this.extensions[extensionStr])) {
|
||||
var ext = Handler.getExtension(this.gl, extensionStr);
|
||||
if (ext) {
|
||||
this.extensions[extensionStr] = ext;
|
||||
} else if (showLog) {
|
||||
!COMPILED && cons.logWrn("og.webgl.Handler: extension '" + extensionStr + "' doesn't initialize.");
|
||||
}
|
||||
}
|
||||
return this.extensions && this.extensions[extensionStr];
|
||||
}
|
||||
|
||||
/**
|
||||
* Main function that initialize handler.
|
||||
* @public
|
||||
*/
|
||||
Handler.prototype.initialize = function () {
|
||||
|
||||
if (this._id) {
|
||||
this.canvas = document.getElementById(this._id);
|
||||
} else {
|
||||
this.canvas = document.createElement("canvas");
|
||||
this.canvas.width = this._params.width;
|
||||
this.canvas.height = this._params.height;
|
||||
}
|
||||
|
||||
this.gl = Handler.getContext(this.canvas, this._params.context);
|
||||
this._initialized = true;
|
||||
|
||||
/** Sets deafult extensions */
|
||||
this._params.extensions.push("OES_standard_derivatives");
|
||||
this._params.extensions.push("EXT_texture_filter_anisotropic");
|
||||
this._params.extensions.push("OES_element_index_uint");
|
||||
|
||||
//this._params.extensions.push('OES_texture_half_float');
|
||||
//this._params.extensions.push('OES_texture_half_float_linear');
|
||||
|
||||
//this._params.extensions.push('OES_texture_float');
|
||||
//this._params.extensions.push('OES_texture_float_linear');
|
||||
|
||||
var i = this._params.extensions.length;
|
||||
while (i--) {
|
||||
this.initializeExtension(this._params.extensions[i], true);
|
||||
}
|
||||
|
||||
if (!this.extensions.EXT_texture_filter_anisotropic)
|
||||
this.createTexture = this.createTexture_mm;
|
||||
|
||||
/** Initilalize shaders and rendering parameters*/
|
||||
this._initShaderPrograms();
|
||||
this._setDefaults();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets default gl render parameters. Used in init function.
|
||||
* @private
|
||||
*/
|
||||
Handler.prototype._setDefaults = function () {
|
||||
this.activateDepthTest();
|
||||
this.setSize(this._params.width, this._params.height);
|
||||
this.gl.frontFace(this.gl.CCW);
|
||||
this.gl.cullFace(this.gl.BACK);
|
||||
this.activateFaceCulling();
|
||||
this.deactivateBlending();
|
||||
var that = this;
|
||||
this.createDefaultTexture({ color: "rgba(0,0,0,0.0)" }, function (t) {
|
||||
that.transparentTexture = t;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate depth test.
|
||||
* @public
|
||||
*/
|
||||
Handler.prototype.activateDepthTest = function () {
|
||||
this.gl.enable(this.gl.DEPTH_TEST);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deactivate depth test.
|
||||
* @public
|
||||
*/
|
||||
Handler.prototype.deactivateDepthTest = function () {
|
||||
this.gl.disable(this.gl.DEPTH_TEST);
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate face culling.
|
||||
* @public
|
||||
*/
|
||||
Handler.prototype.activateFaceCulling = function () {
|
||||
this.gl.enable(this.gl.CULL_FACE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deactivate face cullting.
|
||||
* @public
|
||||
*/
|
||||
Handler.prototype.deactivateFaceCulling = function () {
|
||||
this.gl.disable(this.gl.CULL_FACE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate blending.
|
||||
* @public
|
||||
*/
|
||||
Handler.prototype.activateBlending = function () {
|
||||
this.gl.enable(this.gl.BLEND);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deactivate blending.
|
||||
* @public
|
||||
*/
|
||||
Handler.prototype.deactivateBlending = function () {
|
||||
this.gl.disable(this.gl.BLEND);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates ARRAY buffer.
|
||||
* @public
|
||||
* @param {Array.<number>} array - Input array.
|
||||
* @param {number} itemSize - Array item size.
|
||||
* @param {number} numItems - Items quantity.
|
||||
* @param {number} [usage=STATIC_DRAW] - Parameter of the bufferData call can be one of STATIC_DRAW, DYNAMIC_DRAW, or STREAM_DRAW.
|
||||
* @return {Object}
|
||||
*/
|
||||
Handler.prototype.createArrayBuffer = function (array, itemSize, numItems, usage) {
|
||||
var buffer = this.gl.createBuffer();
|
||||
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, buffer);
|
||||
this.gl.bufferData(this.gl.ARRAY_BUFFER, array, usage || this.gl.STATIC_DRAW);
|
||||
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, null);
|
||||
buffer.itemSize = itemSize;
|
||||
buffer.numItems = numItems;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates ELEMENT ARRAY buffer.
|
||||
* @public
|
||||
* @param {Array.<number>} array - Input array.
|
||||
* @param {number} itemSize - Array item size.
|
||||
* @param {number} numItems - Items quantity.
|
||||
* @param {number} [usage=STATIC_DRAW] - Parameter of the bufferData call can be one of STATIC_DRAW, DYNAMIC_DRAW, or STREAM_DRAW.
|
||||
* @return {Object}
|
||||
*/
|
||||
Handler.prototype.createElementArrayBuffer = function (array, itemSize, numItems, usage) {
|
||||
var buffer = this.gl.createBuffer();
|
||||
this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, buffer);
|
||||
this.gl.bufferData(this.gl.ELEMENT_ARRAY_BUFFER, array, usage || this.gl.STATIC_DRAW);
|
||||
this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, null);
|
||||
buffer.itemSize = itemSize;
|
||||
buffer.numItems = numItems || array.length;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets handler canvas size.
|
||||
* @public
|
||||
* @param {number} width - Canvas width.
|
||||
* @param {number} height - Canvas height.
|
||||
*/
|
||||
Handler.prototype.setSize = function (w, h) {
|
||||
|
||||
if (w > MAX_SIZE) {
|
||||
w = MAX_SIZE;
|
||||
}
|
||||
|
||||
if (h > MAX_SIZE) {
|
||||
h = MAX_SIZE;
|
||||
}
|
||||
|
||||
this._params.width = w;
|
||||
this._params.height = h;
|
||||
this.canvas.width = w;
|
||||
this.canvas.height = h;
|
||||
this._oneByHeight = 1 / h;
|
||||
|
||||
this.gl && this.gl.viewport(0, 0, w, h);
|
||||
this.onCanvasResize && this.onCanvasResize(this.canvas);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns context screen width.
|
||||
* @public
|
||||
* @returns {number}
|
||||
*/
|
||||
Handler.prototype.getWidth = function () {
|
||||
return this.canvas.width;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns context screen height.
|
||||
* @public
|
||||
* @returns {number}
|
||||
*/
|
||||
Handler.prototype.getHeight = function () {
|
||||
return this.canvas.height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns canvas aspect ratio.
|
||||
* @public
|
||||
* @returns {number}
|
||||
*/
|
||||
Handler.prototype.getClientAspect = function () {
|
||||
return this.canvas.clientWidth / this.canvas.clientHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns screen center coordinates.
|
||||
* @public
|
||||
* @returns {number}
|
||||
*/
|
||||
Handler.prototype.getCenter = function () {
|
||||
var c = this.canvas;
|
||||
return new Vec2(Math.round(c.width * 0.5), Math.round(c.height * 0.5));
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw single frame.
|
||||
* @public
|
||||
* @param {number} now - Frame current time milliseconds.
|
||||
*/
|
||||
Handler.prototype.drawFrame = function () {
|
||||
|
||||
/** Calculate frame time */
|
||||
var now = new Date().getTime();
|
||||
this.deltaTime = now - this._lastAnimationFrameTime;
|
||||
this._lastAnimationFrameTime = now;
|
||||
|
||||
this.defaultClock._tick(this.deltaTime);
|
||||
|
||||
for (var i = 0; i < this._clocks.length; i++) {
|
||||
this._clocks[i]._tick(this.deltaTime);
|
||||
}
|
||||
|
||||
/** Canvas resize checking */
|
||||
var canvas = this.canvas;
|
||||
if (canvas.clientWidth !== canvas.width ||
|
||||
canvas.clientHeight !== canvas.height) {
|
||||
this.setSize(canvas.clientWidth, canvas.clientHeight);
|
||||
}
|
||||
|
||||
/** Draw frame */
|
||||
this._frameCallback();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clearing gl frame.
|
||||
* @public
|
||||
*/
|
||||
Handler.prototype.clearFrame = function () {
|
||||
var gl = this.gl;
|
||||
this.gl.clearColor(0.0, 0.0, 0.0, 1.0);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts animation loop.
|
||||
* @public
|
||||
*/
|
||||
Handler.prototype.start = function () {
|
||||
if (!this._requestAnimationFrameId && this._initialized) {
|
||||
var d = new Date();
|
||||
this._lastAnimationFrameTime = d.getTime();
|
||||
this.defaultClock.setDate(d);
|
||||
this._animationFrameCallback();
|
||||
}
|
||||
}
|
||||
|
||||
Handler.prototype.stop = function () {
|
||||
if (this._requestAnimationFrameId) {
|
||||
window.cancelAnimationFrame(this._requestAnimationFrameId);
|
||||
this._requestAnimationFrameId = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make animation.
|
||||
* @private
|
||||
*/
|
||||
Handler.prototype._animationFrameCallback = function () {
|
||||
this._requestAnimationFrameId = window.requestAnimationFrame(() => {
|
||||
this.drawFrame();
|
||||
this._animationFrameCallback();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @param
|
||||
*/
|
||||
Handler.prototype.createDefaultTexture = function (params, success) {
|
||||
var imgCnv;
|
||||
var texture;
|
||||
if (params && params.color) {
|
||||
imgCnv = new ImageCanvas(2, 2);
|
||||
imgCnv.fillColor(params.color);
|
||||
texture = this.createTexture_n(imgCnv._canvas);
|
||||
texture.default = true;
|
||||
success(texture);
|
||||
} else if (params && params.url) {
|
||||
var img = new Image();
|
||||
var that = this;
|
||||
img.onload = function () {
|
||||
texture = that.createTexture(this);
|
||||
texture.default = true;
|
||||
success(texture);
|
||||
};
|
||||
img.src = params.url;
|
||||
} else {
|
||||
imgCnv = new ImageCanvas(2, 2);
|
||||
imgCnv.fillColor("#C5C5C5");
|
||||
texture = this.createTexture_n(imgCnv._canvas);
|
||||
texture.default = true;
|
||||
success(texture);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
Handler.prototype.destroy = function () {
|
||||
|
||||
var gl = this.gl;
|
||||
|
||||
this.stop();
|
||||
|
||||
for (var p in this.shaderPrograms) {
|
||||
this.removeShaderProgram(p);
|
||||
}
|
||||
|
||||
gl.deleteTexture(this.transparentTexture);
|
||||
this.transparentTexture = null;
|
||||
|
||||
this.framebufferStack = null;
|
||||
this.framebufferStack = new Stack();
|
||||
|
||||
if (this.canvas.parentNode) {
|
||||
this.canvas.parentNode.removeChild(this.canvas);
|
||||
}
|
||||
this.canvas.width = 1;
|
||||
this.canvas.height = 1;
|
||||
this.canvas = null;
|
||||
|
||||
var numAttribs = gl.getParameter(gl.MAX_VERTEX_ATTRIBS);
|
||||
var tmp = gl.createBuffer();
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, tmp);
|
||||
for (var ii = 0; ii < numAttribs; ++ii) {
|
||||
gl.disableVertexAttribArray(ii);
|
||||
gl.vertexAttribPointer(ii, 4, gl.FLOAT, false, 0, 0);
|
||||
gl.vertexAttrib1f(ii, 0);
|
||||
}
|
||||
gl.deleteBuffer(tmp);
|
||||
|
||||
var numTextureUnits = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS);
|
||||
for (var ii = 0; ii < numTextureUnits; ++ii) {
|
||||
gl.activeTexture(gl.TEXTURE0 + ii);
|
||||
gl.bindTexture(gl.TEXTURE_CUBE_MAP, null);
|
||||
gl.bindTexture(gl.TEXTURE_2D, null);
|
||||
}
|
||||
|
||||
gl.activeTexture(gl.TEXTURE0);
|
||||
gl.useProgram(null);
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, null);
|
||||
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
||||
gl.bindRenderbuffer(gl.RENDERBUFFER, null);
|
||||
gl.disable(gl.BLEND);
|
||||
gl.disable(gl.CULL_FACE);
|
||||
gl.disable(gl.DEPTH_TEST);
|
||||
gl.disable(gl.DITHER);
|
||||
gl.disable(gl.SCISSOR_TEST);
|
||||
gl.blendColor(0, 0, 0, 0);
|
||||
gl.blendEquation(gl.FUNC_ADD);
|
||||
gl.blendFunc(gl.ONE, gl.ZERO);
|
||||
gl.clearColor(0, 0, 0, 0);
|
||||
gl.clearDepth(1);
|
||||
gl.clearStencil(-1);
|
||||
|
||||
this.gl = null;
|
||||
|
||||
this._initialized = false;
|
||||
}
|
||||
|
||||
Handler.prototype.addClock = function (clock) {
|
||||
if (!clock.__handler) {
|
||||
clock.__handler = this;
|
||||
this._clocks.push(clock);
|
||||
}
|
||||
}
|
||||
|
||||
Handler.prototype.addClocks = function (clockArr) {
|
||||
for (var i = 0; i < clockArr.length; i++) {
|
||||
this.addClock(clockArr[i]);
|
||||
}
|
||||
}
|
||||
|
||||
Handler.prototype.removeClock = function (clock) {
|
||||
if (clock.__handler) {
|
||||
var c = this._clocks;
|
||||
var i = c.length;
|
||||
while (i--) {
|
||||
if (c[i].equal(clock)) {
|
||||
clock.__handler = null;
|
||||
c.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { Handler };
|
||||
|
||||
268
src/og/webgl/MultiFramebuffer.js
Normal file
268
src/og/webgl/MultiFramebuffer.js
Normal file
@ -0,0 +1,268 @@
|
||||
/**
|
||||
* @module og/webgl/Framebuffer
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { ImageCanvas } from '../ImageCanvas.js';
|
||||
|
||||
/**
|
||||
* Class represents multiple render framebuffer.
|
||||
* @class
|
||||
* @param {og.webgl.Handler} handler - WebGL handler.
|
||||
*/
|
||||
const MultiFramebuffer = function (handler, options) {
|
||||
|
||||
options = options || {};
|
||||
|
||||
/**
|
||||
* WebGL handler.
|
||||
* @public
|
||||
* @type {og.webgl.Handler}
|
||||
*/
|
||||
this.handler = handler;
|
||||
|
||||
/**
|
||||
* Framebuffer object.
|
||||
* @private
|
||||
* @type {Object}
|
||||
*/
|
||||
this._fbo = null;
|
||||
|
||||
/**
|
||||
* Picking color framebuffers.
|
||||
* @private
|
||||
* @type {Object}
|
||||
*/
|
||||
this._pFbo = [];
|
||||
|
||||
/**
|
||||
* Render buffer object.
|
||||
* @private
|
||||
*/
|
||||
this._rbo = null;
|
||||
|
||||
this._size = options.size || 1;
|
||||
|
||||
/**
|
||||
* Framebuffer width.
|
||||
* @private
|
||||
* @type {number}
|
||||
*/
|
||||
this._width = options.width || 256;
|
||||
|
||||
/**
|
||||
* Framebuffer width.
|
||||
* @private
|
||||
* @type {number}
|
||||
*/
|
||||
this._height = options.height || 256;
|
||||
|
||||
/**
|
||||
* Buffer textures.
|
||||
* @public
|
||||
*/
|
||||
this.textures = [];
|
||||
|
||||
/**
|
||||
* Framebuffer activity.
|
||||
* @private
|
||||
* @type {boolean}
|
||||
*/
|
||||
this._active = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy framebuffer instance.
|
||||
* @public
|
||||
*/
|
||||
MultiFramebuffer.prototype.destroy = function () {
|
||||
var gl = this.handler.gl;
|
||||
gl.deleteFramebuffer(this._fbo);
|
||||
this._fbo = null;
|
||||
gl.deleteRenderbuffer(this._rbo);
|
||||
this._rbo = null;
|
||||
for (var i = 0; i < this._size; i++) {
|
||||
gl.deleteTexture(this.textures[i]);
|
||||
gl.deleteFramebuffer(this._pFbo[i]);
|
||||
}
|
||||
this.textures.length = 0;
|
||||
this.textures = [];
|
||||
this._pFbo.length = 0;
|
||||
this._pFbo = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Framebuffer initialization.
|
||||
* @virtual
|
||||
*/
|
||||
MultiFramebuffer.prototype.init = function () {
|
||||
var gl = this.handler.gl;
|
||||
var ext = this.handler.extensions.WEBGL_draw_buffers;
|
||||
|
||||
this._fbo = gl.createFramebuffer();
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, this._fbo);
|
||||
|
||||
var fragDataArr = [];
|
||||
for (var i = 0; i < this._size; i++) {
|
||||
fragDataArr[i] = ext.COLOR_ATTACHMENT0_WEBGL + i;
|
||||
}
|
||||
ext.drawBuffersWEBGL(fragDataArr);
|
||||
|
||||
for (var i = 0; i < this._size; i++) {
|
||||
this.textures[i] = this.handler.createEmptyTexture_l(this._width, this._height);
|
||||
gl.bindTexture(gl.TEXTURE_2D, this.textures[i]);
|
||||
gl.framebufferTexture2D(gl.FRAMEBUFFER, ext.COLOR_ATTACHMENT0_WEBGL + i, gl.TEXTURE_2D, this.textures[i], 0);
|
||||
gl.bindTexture(gl.TEXTURE_2D, null);
|
||||
fragDataArr[i] = ext.COLOR_ATTACHMENT0_WEBGL + i;
|
||||
}
|
||||
|
||||
this._rbo = gl.createRenderbuffer();
|
||||
gl.bindRenderbuffer(gl.RENDERBUFFER, this._rbo);
|
||||
gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, this._width, this._height);
|
||||
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, this._rbo);
|
||||
gl.bindRenderbuffer(gl.RENDERBUFFER, null);
|
||||
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
||||
|
||||
for (var i = 0; i < this._size; i++) {
|
||||
this._pFbo[i] = gl.createFramebuffer();
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, this._pFbo[i]);
|
||||
gl.bindTexture(gl.TEXTURE_2D, this.textures[i]);
|
||||
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.textures[i], 0);
|
||||
gl.bindTexture(gl.TEXTURE_2D, null);
|
||||
}
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets framebuffer size. Must be before the activate method.
|
||||
* @public
|
||||
* @param {number} width - Framebuffer width.
|
||||
* @param {number} height - Framebuffer height.
|
||||
*/
|
||||
MultiFramebuffer.prototype.setSize = function (width, height) {
|
||||
this._width = width;
|
||||
this._height = height;
|
||||
this.destroy();
|
||||
this.init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads all pixels(RGBA colors) from framebuffer.
|
||||
* @public
|
||||
* @returns {Array.<number>}
|
||||
*/
|
||||
MultiFramebuffer.prototype.readAllPixels = function (index) {
|
||||
var gl = this.handler.gl;
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, this._pFbo[index || 0]);
|
||||
var pixelValues = new Uint8Array(4 * this._width * this._height);
|
||||
gl.readPixels(0, 0, this._width, this._height, gl.RGBA, gl.UNSIGNED_BYTE, pixelValues);
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
||||
return pixelValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets pixel RBGA color from framebuffer by coordinates.
|
||||
* @public
|
||||
* @param {number} x - Normalized x - coordinate.
|
||||
* @param {number} y - Normalized y - coordinate.
|
||||
* @returns {Array.<number,number,number,number>}
|
||||
*/
|
||||
MultiFramebuffer.prototype.readPixel = function (nx, ny, index) {
|
||||
var gl = this.handler.gl;
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, this._pFbo[index || 0]);
|
||||
var pixelValues = new Uint8Array(4);
|
||||
gl.readPixels(nx * this._width, ny * this._height, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixelValues);
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
||||
return pixelValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns framebuffer completed.
|
||||
* @public
|
||||
* @returns {boolean}
|
||||
*/
|
||||
MultiFramebuffer.prototype.isComplete = function () {
|
||||
var gl = this.handler.gl;
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, this._fbo);
|
||||
if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) == gl.FRAMEBUFFER_COMPLETE) {
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
||||
return true;
|
||||
}
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate framebuffer frame to draw.
|
||||
* @public
|
||||
* @returns {og.webgl.MultiFramebuffer} Returns current framebuffer.
|
||||
*/
|
||||
MultiFramebuffer.prototype.activate = function () {
|
||||
var h = this.handler,
|
||||
gl = h.gl;
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, this._fbo);
|
||||
gl.viewport(0, 0, this._width, this._height);
|
||||
this._active = true;
|
||||
var c = this.handler.framebufferStack.current().data;
|
||||
c && (c._active = false);
|
||||
h.framebufferStack.push(this);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deactivate framebuffer frame.
|
||||
* @public
|
||||
*/
|
||||
MultiFramebuffer.prototype.deactivate = function () {
|
||||
var h = this.handler,
|
||||
gl = h.gl;
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
||||
gl.viewport(0, 0, h.canvas.width, h.canvas.height);
|
||||
this._active = false;
|
||||
|
||||
var f = h.framebufferStack.popPrev();
|
||||
if (f) {
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, f._fbo);
|
||||
gl.viewport(0, 0, f._width, f._height);
|
||||
} else {
|
||||
gl.viewport(0, 0, h.canvas.width, h.canvas.height);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets JavaScript image object that framebuffer has drawn.
|
||||
* @public
|
||||
* @returns {Object}
|
||||
*/
|
||||
MultiFramebuffer.prototype.getImage = function (index) {
|
||||
var data = this.readAllPixels(index);
|
||||
var imageCanvas = new ImageCanvas(this._width, this._height);
|
||||
imageCanvas.setData(data);
|
||||
return imageCanvas.getImage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Open dialog window with framebuffer image.
|
||||
* @public
|
||||
*/
|
||||
MultiFramebuffer.prototype.openImage = function (index) {
|
||||
var img = this.getImage(index);
|
||||
var dataUrl = img.src;
|
||||
var windowContent = '<!DOCTYPE html>';
|
||||
windowContent += '<html>'
|
||||
windowContent += '<head><title>Print</title></head>';
|
||||
windowContent += '<body>'
|
||||
windowContent += '<img src="' + dataUrl + '">';
|
||||
windowContent += '</body>';
|
||||
windowContent += '</html>';
|
||||
var printWin = window.open('', '', 'width=' + img.width + 'px ,height=' + img.height + 'px');
|
||||
printWin.document.open();
|
||||
printWin.document.write(windowContent);
|
||||
printWin.document.close();
|
||||
printWin.focus();
|
||||
}
|
||||
|
||||
export { MultiFramebuffer };
|
||||
142
src/og/webgl/ShaderController.js
Normal file
142
src/og/webgl/ShaderController.js
Normal file
@ -0,0 +1,142 @@
|
||||
/**
|
||||
* @module og/webgl/ShaderController
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* This is shader program controller that used by hadler object to access the shader
|
||||
* program capabilities, like switching program during the rendering.
|
||||
* Get access to the program from ...handler.shaderPrograms.<program name> etc.
|
||||
* @class
|
||||
* @param {og.webgl.Handler} handler - Handler.
|
||||
* @param {og.shaderProgram.ShaderProgram} shaderProgram - Shader program.
|
||||
*/
|
||||
const ShaderController = function (handler, shaderProgram) {
|
||||
|
||||
/**
|
||||
* Shader program.
|
||||
* @private
|
||||
* @type {og.shaderProgram.ShaderProgram}
|
||||
*/
|
||||
this._program = shaderProgram;
|
||||
|
||||
/**
|
||||
* Handler.
|
||||
* @private
|
||||
* @type {og.webgl.Handler}
|
||||
*/
|
||||
this._handler = handler;
|
||||
|
||||
/**
|
||||
* Program current frame activation flag.
|
||||
* @private
|
||||
* @type {boolean}
|
||||
*/
|
||||
this._activated = false;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Lazy create program call.
|
||||
* @public
|
||||
*/
|
||||
ShaderController.prototype.initialize = function () {
|
||||
this._program.createProgram(this._handler.gl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns controller's shader program.
|
||||
* @public
|
||||
* @return {og.shaderProgram.ShaderProgram}
|
||||
*/
|
||||
ShaderController.prototype.getProgram = function () {
|
||||
return this._program;
|
||||
}
|
||||
|
||||
/**
|
||||
* Activates current shader program.
|
||||
* @public
|
||||
*/
|
||||
ShaderController.prototype.activate = function () {
|
||||
if (!this._activated) {
|
||||
this._handler.activeShaderProgram.deactivate();
|
||||
this._handler.activeShaderProgram = this;
|
||||
var p = this._program;
|
||||
this._activated = true;
|
||||
p.enableAttribArrays();
|
||||
p.use();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove program from handler
|
||||
* @public
|
||||
*/
|
||||
ShaderController.prototype.remove = function () {
|
||||
var p = this._handler.shaderPrograms;
|
||||
if (p[this._program.name]) {
|
||||
if (this._activated) {
|
||||
this.deactivate();
|
||||
}
|
||||
this._program.delete();
|
||||
p[this._program.name] = null;
|
||||
delete p[this._program.name];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deactivate shader program. This is not necessary while activae function used.
|
||||
* @public
|
||||
*/
|
||||
ShaderController.prototype.deactivate = function () {
|
||||
this._program.disableAttribArrays();
|
||||
this._activated = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns program activity.
|
||||
* @public
|
||||
* @return {boolean}
|
||||
*/
|
||||
ShaderController.prototype.isActive = function () {
|
||||
return this._activated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets program uniforms and attributes values and return controller instance.
|
||||
* @public
|
||||
* @param {Object} - Object with variable name and value like { value: 12, someArray:[1,2,3], uSampler: texture,... }
|
||||
* @return {og.webgl.ShaderController}
|
||||
*/
|
||||
ShaderController.prototype.set = function (params) {
|
||||
this.activate();
|
||||
this._program.set(params);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw index buffer with this program.
|
||||
* @public
|
||||
* @param {number} mode - Gl draw mode
|
||||
* @param {}
|
||||
* @return {og.webgl.ShaderController} Returns current shader controller instance.
|
||||
*/
|
||||
ShaderController.prototype.drawIndexBuffer = function (mode, buffer) {
|
||||
this._program.drawIndexBuffer(mode, buffer);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls Gl drawArray function.
|
||||
* @param {number} - Gl draw mode.
|
||||
* @param {number} - draw items count.
|
||||
* @return {og.webgl.ShaderController} Returns current shader controller instance.
|
||||
*/
|
||||
ShaderController.prototype.drawArray = function (mode, numItems) {
|
||||
this._program.drawArray(mode, numItems);
|
||||
return this;
|
||||
}
|
||||
|
||||
export { ShaderController };
|
||||
Loading…
x
Reference in New Issue
Block a user