Implement player-markers
This commit is contained in:
parent
31c159d9ae
commit
a4ee07aa5c
|
@ -7,7 +7,7 @@
|
||||||
<link rel="shortcut icon" href="favicon.png">
|
<link rel="shortcut icon" href="favicon.png">
|
||||||
</head>
|
</head>
|
||||||
<body style="margin: 0; padding: 0;">
|
<body style="margin: 0; padding: 0;">
|
||||||
<div id="map-container" style="position: absolute; width: 96%; height: 90%; margin: 2%;"></div>
|
<div id="map-container" style="position: absolute; width: 100%; height: 100%;"></div>
|
||||||
|
|
||||||
<script type="text/javascript" src="js/three.js"></script>
|
<script type="text/javascript" src="js/three.js"></script>
|
||||||
<script type="text/javascript" src="js/hammer.js"></script>
|
<script type="text/javascript" src="js/hammer.js"></script>
|
||||||
|
@ -22,13 +22,21 @@
|
||||||
// load map
|
// load map
|
||||||
let maps = [];
|
let maps = [];
|
||||||
let markerManager = null;
|
let markerManager = null;
|
||||||
|
let playerManager = null;
|
||||||
BlueMap.loadMaps("data/", bluemap.events).then(loadedMaps => {
|
BlueMap.loadMaps("data/", bluemap.events).then(loadedMaps => {
|
||||||
maps = loadedMaps;
|
maps = loadedMaps;
|
||||||
bluemap.setMap(maps[0]);
|
|
||||||
|
|
||||||
markerManager = maps[0].createMarkerFileManager(bluemap.markerScene);
|
markerManager = new BlueMap.MarkerFileManager(bluemap.markerScene, "data/markers.json", maps[0].id, bluemap.events);
|
||||||
markerManager.update();
|
markerManager.update();
|
||||||
markerManager.setAutoUpdateInterval(1000 * 10);
|
markerManager.setAutoUpdateInterval(1000 * 10);
|
||||||
|
|
||||||
|
playerManager = new BlueMap.PlayerMarkerManager(bluemap.markerScene, "live/players", "", bluemap.events);
|
||||||
|
playerManager.update();
|
||||||
|
playerManager.setAutoUpdateInterval(1000);
|
||||||
|
|
||||||
|
bluemap.setMap(maps[0]).then(() => {
|
||||||
|
playerManager.worldId = maps[0].world;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
{"players":[{"uuid":"c61be8fb-0a66-4411-aae5-0c67bb4808d4","name":"TBlueF","world":"118a8ec2-ccb1-4e84-be88-1ff568e413ca","position":{"x":-130.62644738873905,"y":72.53260088333643,"z":138.27086962845027}}]}
|
{"players":[{"uuid":"c61be8fb-0a66-4411-aae5-0c67bb4808d4","name":"TBlueF","world":"118a8ec2-ccb1-4e84-be88-1ff568e413ca","position":{"x":130.62644738873905,"y":82.53260088333643,"z":-138.27086962845027}}]}
|
|
@ -3,6 +3,8 @@ import {Map} from "./map/Map";
|
||||||
|
|
||||||
export { MapViewer } from "./MapViewer";
|
export { MapViewer } from "./MapViewer";
|
||||||
export * from "./util/Utils";
|
export * from "./util/Utils";
|
||||||
|
export { MarkerFileManager } from "./markers/MarkerFileManager";
|
||||||
|
export { PlayerMarkerManager } from "./markers/PlayerMarkerManager";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads and returns a promise with an array of Maps loaded from that root-path.<br>
|
* Loads and returns a promise with an array of Maps loaded from that root-path.<br>
|
||||||
|
|
|
@ -287,6 +287,7 @@ export class MapViewer {
|
||||||
/**
|
/**
|
||||||
* Changes / Sets the map that will be loaded and displayed
|
* Changes / Sets the map that will be loaded and displayed
|
||||||
* @param map {Map}
|
* @param map {Map}
|
||||||
|
* @returns Promise<void>
|
||||||
*/
|
*/
|
||||||
setMap(map = null) {
|
setMap(map = null) {
|
||||||
if (this.map && this.map.isMap) this.map.unload();
|
if (this.map && this.map.isMap) this.map.unload();
|
||||||
|
|
|
@ -1,116 +0,0 @@
|
||||||
import {alert} from "../util/Utils";
|
|
||||||
import {FileLoader} from "three";
|
|
||||||
|
|
||||||
export class PlayerMarkerManager {
|
|
||||||
|
|
||||||
constructor(mapViewer, events = null, livePlayerUrl = "/live/players") {
|
|
||||||
this.mapViewer = mapViewer;
|
|
||||||
this.events = events ? events : mapViewer.events;
|
|
||||||
this.playerUrl = livePlayerUrl;
|
|
||||||
|
|
||||||
this._playerData = {players:[]};
|
|
||||||
|
|
||||||
this.events.addEventListener("bluemapMapChanged", this.onMapChange);
|
|
||||||
}
|
|
||||||
|
|
||||||
update(){
|
|
||||||
return this.loadPlayerData()
|
|
||||||
.then(playerData => {
|
|
||||||
if (playerData && Array.isArray(playerData.players)) {
|
|
||||||
this._playerData = playerData;
|
|
||||||
this.updateMarkers();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(reason => {
|
|
||||||
alert(this.events, reason, "warning");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
updateMarkers() {
|
|
||||||
if (!this.mapViewer.map || !this.mapViewer.map.isLoaded) return;
|
|
||||||
let markerset = this.getMarkerset();
|
|
||||||
|
|
||||||
// map of uuid to playerdata
|
|
||||||
let playerMap = {};
|
|
||||||
this._playerData.players.forEach(player => {
|
|
||||||
if (!player.uuid) return;
|
|
||||||
|
|
||||||
// fill defaults
|
|
||||||
player = {
|
|
||||||
name: player.uuid,
|
|
||||||
world: "",
|
|
||||||
position: {},
|
|
||||||
rotation: {},
|
|
||||||
...player
|
|
||||||
};
|
|
||||||
|
|
||||||
player.position = {
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
z: 0,
|
|
||||||
...player.position
|
|
||||||
};
|
|
||||||
|
|
||||||
player.rotation = {
|
|
||||||
yaw: 0,
|
|
||||||
pitch: 0,
|
|
||||||
roll: 0,
|
|
||||||
...player.rotation
|
|
||||||
};
|
|
||||||
|
|
||||||
playerMap[player.uuid] = player;
|
|
||||||
});
|
|
||||||
|
|
||||||
//update existing markers
|
|
||||||
markerset.marker.forEach(marker => {
|
|
||||||
if (!marker.isPlayerMarker) return;
|
|
||||||
if (!playerMap[marker.playerUuid]) return;
|
|
||||||
let player = playerMap[marker.playerUuid];
|
|
||||||
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
getMarkerset() {
|
|
||||||
//init markerset
|
|
||||||
let markerset = this.mapViewer.map.markerManager.markerSets["bm-live-players"];
|
|
||||||
if (!markerset || !markerset.isMarkerSet){
|
|
||||||
markerset = this.mapViewer.map.markerManager.createMarkerSet("bm-live-players");
|
|
||||||
}
|
|
||||||
|
|
||||||
return markerset;
|
|
||||||
}
|
|
||||||
|
|
||||||
onMapChange = () => {
|
|
||||||
this.updateMarkers();
|
|
||||||
}
|
|
||||||
|
|
||||||
dispose() {
|
|
||||||
this.events.removeEventListener("bluemapMapChanged", this.onMapChange);
|
|
||||||
|
|
||||||
this._playerData = {players:[]};
|
|
||||||
this.updateMarkers();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads the playerdata
|
|
||||||
* @returns {Promise<Object>}
|
|
||||||
*/
|
|
||||||
loadPlayerData() {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
//alert(this.events, `Loading players from '${this.playerUrl}'...`, "fine");
|
|
||||||
|
|
||||||
let loader = new FileLoader();
|
|
||||||
loader.setResponseType("json");
|
|
||||||
loader.load(this.playerUrl,
|
|
||||||
playerData => {
|
|
||||||
if (!playerData) reject(`Failed to parse '${this.playerUrl}'!`);
|
|
||||||
else resolve(playerData);
|
|
||||||
},
|
|
||||||
() => {},
|
|
||||||
() => reject(`Failed to load '${this.playerUrl}'!`)
|
|
||||||
)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -7,6 +7,7 @@ import {alert, dispatchEvent, hashTile, stringToImage} from "../util/Utils";
|
||||||
import {TileManager} from "./TileManager";
|
import {TileManager} from "./TileManager";
|
||||||
import {TileLoader} from "./TileLoader";
|
import {TileLoader} from "./TileLoader";
|
||||||
import {MarkerFileManager} from "../markers/MarkerFileManager";
|
import {MarkerFileManager} from "../markers/MarkerFileManager";
|
||||||
|
import {PlayerMarkerManager} from "../markers/PlayerMarkerManager";
|
||||||
|
|
||||||
export class Map {
|
export class Map {
|
||||||
|
|
||||||
|
@ -334,15 +335,6 @@ export class Map {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a MarkerFileManager that is loading and updating the markers for this map.
|
|
||||||
* @param markerScene {Scene} - The scene to which all markers will be added
|
|
||||||
* @returns {MarkerFileManager}
|
|
||||||
*/
|
|
||||||
createMarkerFileManager(markerScene) {
|
|
||||||
return new MarkerFileManager(markerScene, this.dataUrl + "../markers.json", this.id, this.events);
|
|
||||||
}
|
|
||||||
|
|
||||||
dispose() {
|
dispose() {
|
||||||
this.unload();
|
this.unload();
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ export class HtmlMarker extends Marker {
|
||||||
this.fadeDistanceMax = Number.MAX_VALUE;
|
this.fadeDistanceMax = Number.MAX_VALUE;
|
||||||
|
|
||||||
this.addEventListener( 'removed', () => {
|
this.addEventListener( 'removed', () => {
|
||||||
this.element.parentNode.removeChild(this.element);
|
if (this.element.parentNode) this.element.parentNode.removeChild(this.element);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.add(this.elementObject);
|
this.add(this.elementObject);
|
||||||
|
|
|
@ -35,12 +35,7 @@ export class Marker extends Object3D {
|
||||||
* @returns {number} - opacity between 0 and 1
|
* @returns {number} - opacity between 0 and 1
|
||||||
*/
|
*/
|
||||||
static calculateDistanceOpacity(position, camera, fadeDistanceMin, fadeDistanceMax) {
|
static calculateDistanceOpacity(position, camera, fadeDistanceMin, fadeDistanceMax) {
|
||||||
//calculate "orthographic distance" to marker
|
let distance = Marker.calculateDistanceToCameraPlane(position, camera);
|
||||||
Marker._posRelativeToCamera.subVectors(position, camera.position);
|
|
||||||
camera.getWorldDirection(Marker._cameraDirection);
|
|
||||||
let distance = Marker._posRelativeToCamera.dot(Marker._cameraDirection);
|
|
||||||
|
|
||||||
//calculate opacity based on (min/max)distance
|
|
||||||
let minDelta = (distance - fadeDistanceMin) / fadeDistanceMin;
|
let minDelta = (distance - fadeDistanceMin) / fadeDistanceMin;
|
||||||
let maxDelta = (distance - fadeDistanceMax) / (fadeDistanceMax * 0.5);
|
let maxDelta = (distance - fadeDistanceMax) / (fadeDistanceMax * 0.5);
|
||||||
return Math.min(
|
return Math.min(
|
||||||
|
@ -49,4 +44,15 @@ export class Marker extends Object3D {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param position {Vector3}
|
||||||
|
* @param camera {THREE.Camera}
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
static calculateDistanceToCameraPlane (position, camera) {
|
||||||
|
Marker._posRelativeToCamera.subVectors(position, camera.position);
|
||||||
|
camera.getWorldDirection(Marker._cameraDirection);
|
||||||
|
return Marker._posRelativeToCamera.dot(Marker._cameraDirection);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
import {FileLoader, Scene} from "three";
|
import {Scene} from "three";
|
||||||
import {MarkerSet} from "./MarkerSet";
|
import {MarkerSet} from "./MarkerSet";
|
||||||
import {ShapeMarker} from "./ShapeMarker";
|
import {ShapeMarker} from "./ShapeMarker";
|
||||||
import {alert} from "../util/Utils";
|
import {alert} from "../util/Utils";
|
||||||
|
@ -6,11 +6,12 @@ import {ExtrudeMarker} from "./ExtrudeMarker";
|
||||||
import {LineMarker} from "./LineMarker";
|
import {LineMarker} from "./LineMarker";
|
||||||
import {HtmlMarker} from "./HtmlMarker";
|
import {HtmlMarker} from "./HtmlMarker";
|
||||||
import {PoiMarker} from "./PoiMarker";
|
import {PoiMarker} from "./PoiMarker";
|
||||||
|
import {MarkerManager} from "./MarkerManager";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A manager for loading and updating markers from a markers.json file
|
* A manager for loading and updating markers from a markers.json file
|
||||||
*/
|
*/
|
||||||
export class MarkerFileManager {
|
export class MarkerFileManager extends MarkerManager {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @constructor
|
* @constructor
|
||||||
|
@ -20,120 +21,12 @@ export class MarkerFileManager {
|
||||||
* @param events {EventTarget}
|
* @param events {EventTarget}
|
||||||
*/
|
*/
|
||||||
constructor(markerScene, fileUrl, mapId, events = null) {
|
constructor(markerScene, fileUrl, mapId, events = null) {
|
||||||
|
super(markerScene, fileUrl, events);
|
||||||
Object.defineProperty(this, 'isMarkerFileManager', {value: true});
|
Object.defineProperty(this, 'isMarkerFileManager', {value: true});
|
||||||
|
|
||||||
this.markerScene = markerScene;
|
|
||||||
this.fileUrl = fileUrl;
|
|
||||||
this.mapId = mapId;
|
this.mapId = mapId;
|
||||||
this.events = events;
|
|
||||||
|
|
||||||
/** @type {Map<string, MarkerSet>} */
|
|
||||||
this.markerSets = new Map();
|
|
||||||
/** @type {Map<string, Marker>} */
|
|
||||||
this.markers = new Map();
|
|
||||||
|
|
||||||
/** @type {NodeJS.Timeout} */
|
|
||||||
this._updateInterval = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the automatic-update frequency, setting this to 0 or negative disables automatic updates (default).
|
|
||||||
* This is better than using setInterval() on update() because this will wait for the update to finish before requesting the next update.
|
|
||||||
* @param ms - interval in milliseconds
|
|
||||||
*/
|
|
||||||
setAutoUpdateInterval(ms) {
|
|
||||||
if (this._updateInterval) clearInterval(this._updateInterval);
|
|
||||||
if (ms > 0) {
|
|
||||||
let autoUpdate = () => {
|
|
||||||
this.update().finally(() => {
|
|
||||||
this._updateInterval = setTimeout(autoUpdate, ms);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
this._updateInterval = setTimeout(autoUpdate, ms);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads the marker-file and updates all managed markers.
|
|
||||||
* @returns {Promise<object>} - A promise completing when the markers finished updating
|
|
||||||
*/
|
|
||||||
update() {
|
|
||||||
return this.loadMarkerFile()
|
|
||||||
.then(markerFileData => this.updateFromData(markerFileData))
|
|
||||||
.catch(error => {
|
|
||||||
alert(this.events, error, "error");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stops automatic-updates and disposes all markersets and markers managed by this manager
|
|
||||||
*/
|
|
||||||
dispose() {
|
|
||||||
this.setAutoUpdateInterval(0);
|
|
||||||
this.markerSets.forEach(markerSet => markerSet.dispose());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
* Adds a MarkerSet to this Manager, removing any existing markerSet with this id first.
|
|
||||||
* @param markerSet {MarkerSet}
|
|
||||||
*/
|
|
||||||
addMarkerSet(markerSet) {
|
|
||||||
this.removeMarkerSet(markerSet.markerSetId);
|
|
||||||
|
|
||||||
this.markerSets.set(markerSet.markerSetId, markerSet);
|
|
||||||
this.markerScene.add(markerSet)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
* Removes a MarkerSet from this Manager
|
|
||||||
* @param setId {string} - The id of the MarkerSet
|
|
||||||
*/
|
|
||||||
removeMarkerSet(setId) {
|
|
||||||
let markerSet = this.markerSets.get(setId);
|
|
||||||
|
|
||||||
if (markerSet) {
|
|
||||||
this.markerScene.remove(markerSet);
|
|
||||||
this.markerSets.delete(setId);
|
|
||||||
markerSet.dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
* Adds a marker to this manager
|
|
||||||
* @param markerSet {MarkerSet}
|
|
||||||
* @param marker {Marker}
|
|
||||||
*/
|
|
||||||
addMarker(markerSet, marker) {
|
|
||||||
this.removeMarker(marker.markerId);
|
|
||||||
|
|
||||||
this.markers.set(marker.markerId, marker);
|
|
||||||
markerSet.add(marker);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
* Removes a marker from this manager
|
|
||||||
* @param markerId {string}
|
|
||||||
*/
|
|
||||||
removeMarker(markerId) {
|
|
||||||
let marker = this.markers.get(markerId);
|
|
||||||
|
|
||||||
if (marker) {
|
|
||||||
if (marker.parent) marker.parent.remove(marker);
|
|
||||||
this.markers.delete(markerId);
|
|
||||||
marker.dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
* Updates all managed markers using the provided data.
|
|
||||||
* @param markerData {object} - The data object, usually parsed json from a markers.json
|
|
||||||
*/
|
|
||||||
updateFromData(markerData) {
|
updateFromData(markerData) {
|
||||||
if (!Array.isArray(markerData.markerSets)) return;
|
if (!Array.isArray(markerData.markerSets)) return;
|
||||||
let updatedMarkerSets = new Set();
|
let updatedMarkerSets = new Set();
|
||||||
|
@ -144,7 +37,7 @@ export class MarkerFileManager {
|
||||||
let markerSet = this.updateMarkerSetFromData(markerSetData);
|
let markerSet = this.updateMarkerSetFromData(markerSetData);
|
||||||
updatedMarkerSets.add(markerSet);
|
updatedMarkerSets.add(markerSet);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
alert(this.events, "Failed to parse markerset-data: " + err, "fine");
|
alert(this.events, err, "fine");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -189,7 +82,7 @@ export class MarkerFileManager {
|
||||||
let marker = this.updateMarkerFromData(markerSet, markerData);
|
let marker = this.updateMarkerFromData(markerSet, markerData);
|
||||||
updatedMarkers.add(marker);
|
updatedMarkers.add(marker);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
alert(this.events, "Failed to parse marker-data: " + err, "fine");
|
alert(this.events, err, "fine");
|
||||||
console.debug(err);
|
console.debug(err);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -241,24 +134,4 @@ export class MarkerFileManager {
|
||||||
return marker;
|
return marker;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
* Loads the marker file
|
|
||||||
* @returns {Promise<Object>} - A promise completing with the parsed json object from the loaded file
|
|
||||||
*/
|
|
||||||
loadMarkerFile() {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
let loader = new FileLoader();
|
|
||||||
loader.setResponseType("json");
|
|
||||||
loader.load(this.fileUrl,
|
|
||||||
markerFileData => {
|
|
||||||
if (!markerFileData) reject(`Failed to parse '${this.fileUrl}'!`);
|
|
||||||
else resolve(markerFileData);
|
|
||||||
},
|
|
||||||
() => {},
|
|
||||||
() => reject(`Failed to load '${this.fileUrl}'!`)
|
|
||||||
)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -0,0 +1,158 @@
|
||||||
|
import {FileLoader, Scene} from "three";
|
||||||
|
import {MarkerSet} from "./MarkerSet";
|
||||||
|
import {alert} from "../util/Utils";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A manager for loading and updating markers from a file
|
||||||
|
*/
|
||||||
|
export class MarkerManager {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @constructor
|
||||||
|
* @param markerScene {Scene} - The scene to which all markers will be added
|
||||||
|
* @param fileUrl {string} - The marker file from which this manager updates its markers
|
||||||
|
* @param events {EventTarget}
|
||||||
|
*/
|
||||||
|
constructor(markerScene, fileUrl, events = null) {
|
||||||
|
Object.defineProperty(this, 'isMarkerManager', {value: true});
|
||||||
|
|
||||||
|
this.markerScene = markerScene;
|
||||||
|
this.fileUrl = fileUrl;
|
||||||
|
this.events = events;
|
||||||
|
|
||||||
|
/** @type {Map<string, MarkerSet>} */
|
||||||
|
this.markerSets = new Map();
|
||||||
|
/** @type {Map<string, Marker>} */
|
||||||
|
this.markers = new Map();
|
||||||
|
|
||||||
|
/** @type {NodeJS.Timeout} */
|
||||||
|
this._updateInterval = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the automatic-update frequency, setting this to 0 or negative disables automatic updates (default).
|
||||||
|
* This is better than using setInterval() on update() because this will wait for the update to finish before requesting the next update.
|
||||||
|
* @param ms - interval in milliseconds
|
||||||
|
*/
|
||||||
|
setAutoUpdateInterval(ms) {
|
||||||
|
if (this._updateInterval) clearInterval(this._updateInterval);
|
||||||
|
if (ms > 0) {
|
||||||
|
let autoUpdate = () => {
|
||||||
|
this.update().finally(() => {
|
||||||
|
this._updateInterval = setTimeout(autoUpdate, ms);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
this._updateInterval = setTimeout(autoUpdate, ms);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the marker-file and updates all managed markers.
|
||||||
|
* @returns {Promise<object>} - A promise completing when the markers finished updating
|
||||||
|
*/
|
||||||
|
update() {
|
||||||
|
return this.loadMarkerFile()
|
||||||
|
.then(markerFileData => this.updateFromData(markerFileData))
|
||||||
|
.catch(error => {
|
||||||
|
alert(this.events, error, "warning");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stops automatic-updates and disposes all markersets and markers managed by this manager
|
||||||
|
*/
|
||||||
|
dispose() {
|
||||||
|
this.setAutoUpdateInterval(0);
|
||||||
|
this.markerSets.forEach(markerSet => markerSet.dispose());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all markers managed by this marker-manager
|
||||||
|
*/
|
||||||
|
clear() {
|
||||||
|
this.markerSets.forEach(markerSet => this.removeMarkerSet(markerSet.markerSetId));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @protected
|
||||||
|
* Adds a MarkerSet to this Manager, removing any existing markerSet with this id first.
|
||||||
|
* @param markerSet {MarkerSet}
|
||||||
|
*/
|
||||||
|
addMarkerSet(markerSet) {
|
||||||
|
this.removeMarkerSet(markerSet.markerSetId);
|
||||||
|
|
||||||
|
this.markerSets.set(markerSet.markerSetId, markerSet);
|
||||||
|
this.markerScene.add(markerSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @protected
|
||||||
|
* Removes a MarkerSet from this Manager
|
||||||
|
* @param setId {string} - The id of the MarkerSet
|
||||||
|
*/
|
||||||
|
removeMarkerSet(setId) {
|
||||||
|
let markerSet = this.markerSets.get(setId);
|
||||||
|
|
||||||
|
if (markerSet) {
|
||||||
|
this.markerScene.remove(markerSet);
|
||||||
|
this.markerSets.delete(setId);
|
||||||
|
markerSet.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @protected
|
||||||
|
* Adds a marker to this manager
|
||||||
|
* @param markerSet {MarkerSet}
|
||||||
|
* @param marker {Marker}
|
||||||
|
*/
|
||||||
|
addMarker(markerSet, marker) {
|
||||||
|
this.removeMarker(marker.markerId);
|
||||||
|
|
||||||
|
this.markers.set(marker.markerId, marker);
|
||||||
|
markerSet.add(marker);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @protected
|
||||||
|
* Removes a marker from this manager
|
||||||
|
* @param markerId {string}
|
||||||
|
*/
|
||||||
|
removeMarker(markerId) {
|
||||||
|
let marker = this.markers.get(markerId);
|
||||||
|
|
||||||
|
if (marker) {
|
||||||
|
if (marker.parent) marker.parent.remove(marker);
|
||||||
|
this.markers.delete(markerId);
|
||||||
|
marker.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates all managed markers using the provided data.
|
||||||
|
* @param markerData {object} - The data object, usually parsed json from a markers.json
|
||||||
|
*/
|
||||||
|
updateFromData(markerData) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* Loads the marker file
|
||||||
|
* @returns {Promise<Object>} - A promise completing with the parsed json object from the loaded file
|
||||||
|
*/
|
||||||
|
loadMarkerFile() {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let loader = new FileLoader();
|
||||||
|
loader.setResponseType("json");
|
||||||
|
loader.load(this.fileUrl,
|
||||||
|
markerFileData => {
|
||||||
|
if (!markerFileData) reject(`Failed to parse '${this.fileUrl}'!`);
|
||||||
|
else resolve(markerFileData);
|
||||||
|
},
|
||||||
|
() => {},
|
||||||
|
() => reject(`Failed to load '${this.fileUrl}'!`)
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,111 @@
|
||||||
|
import {Marker} from "./Marker";
|
||||||
|
import {CSS2DObject} from "../util/CSS2DRenderer";
|
||||||
|
import {animate, EasingFunctions, htmlToElement} from "../util/Utils";
|
||||||
|
import {Vector3} from "three";
|
||||||
|
|
||||||
|
export class PlayerMarker extends Marker {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param markerId {string}
|
||||||
|
* @param playerUuid {string}
|
||||||
|
*/
|
||||||
|
constructor(markerId, playerUuid) {
|
||||||
|
super(markerId);
|
||||||
|
Object.defineProperty(this, 'isPlayerMarker', {value: true});
|
||||||
|
this.markerType = "player";
|
||||||
|
|
||||||
|
this.playerUuid = playerUuid;
|
||||||
|
|
||||||
|
this.elementObject = new CSS2DObject(htmlToElement(`
|
||||||
|
<div id="bm-marker-${this.markerId}" class="bm-marker-${this.markerType}">
|
||||||
|
<img src="assets/playerheads/${this.playerUuid}.png" alt="playerhead" draggable="false">
|
||||||
|
<div class="bm-player-name"></div>
|
||||||
|
</div>
|
||||||
|
`));
|
||||||
|
this.elementObject.onBeforeRender = (renderer, scene, camera) => this.onBeforeRender(renderer, scene, camera);
|
||||||
|
|
||||||
|
this.playerHeadElement = this.element.getElementsByTagName("img")[0];
|
||||||
|
this.playerNameElement = this.element.getElementsByTagName("div")[0];
|
||||||
|
|
||||||
|
this.addEventListener( 'removed', () => {
|
||||||
|
if (this.element.parentNode) this.element.parentNode.removeChild(this.element);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.playerHeadElement.addEventListener('error', () => {
|
||||||
|
this.playerHeadElement.src = "assets/playerheads/steve.png";
|
||||||
|
}, {once: true});
|
||||||
|
|
||||||
|
this.add(this.elementObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {Element}
|
||||||
|
*/
|
||||||
|
get element() {
|
||||||
|
return this.elementObject.element;
|
||||||
|
}
|
||||||
|
|
||||||
|
onBeforeRender(renderer, scene, camera) {
|
||||||
|
this.element.setAttribute("distance-data", Marker.calculateDistanceToCameraPlane(this.position, camera).toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef PlayerLike {{
|
||||||
|
* uuid: string,
|
||||||
|
* name: string,
|
||||||
|
* world: string,
|
||||||
|
* position: {x: number, y: number, z: number},
|
||||||
|
* rotation: {yaw: number, pitch: number, roll: number}
|
||||||
|
* }}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param markerData {PlayerLike}
|
||||||
|
*/
|
||||||
|
updateFromData(markerData) {
|
||||||
|
|
||||||
|
// animate position update
|
||||||
|
let pos = markerData.position || {};
|
||||||
|
if (!this.position.x && !this.position.y && !this.position.z) {
|
||||||
|
this.position.set(
|
||||||
|
pos.x || 0,
|
||||||
|
pos.y || 0,
|
||||||
|
pos.z || 0
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
let startPos = {
|
||||||
|
x: this.position.x,
|
||||||
|
y: this.position.y,
|
||||||
|
z: this.position.z,
|
||||||
|
}
|
||||||
|
let deltaPos = {
|
||||||
|
x: (pos.x || 0) - startPos.x,
|
||||||
|
y: (pos.y || 0) - startPos.y,
|
||||||
|
z: (pos.z || 0) - startPos.z,
|
||||||
|
}
|
||||||
|
if (deltaPos.x || deltaPos.y || deltaPos.z) {
|
||||||
|
animate(progress => {
|
||||||
|
let ease = EasingFunctions.easeInOutCubic(progress);
|
||||||
|
this.position.set(
|
||||||
|
startPos.x + deltaPos.x * ease || 0,
|
||||||
|
startPos.y + deltaPos.y * ease || 0,
|
||||||
|
startPos.z + deltaPos.z * ease || 0
|
||||||
|
);
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// update name
|
||||||
|
let name = markerData.name || this.playerUuid;
|
||||||
|
if (this.playerNameElement.innerHTML !== name)
|
||||||
|
this.playerNameElement.innerHTML = name;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
dispose() {
|
||||||
|
super.dispose();
|
||||||
|
|
||||||
|
if (this.element.parentNode) this.element.parentNode.removeChild(this.element);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,101 @@
|
||||||
|
import {MarkerManager} from "./MarkerManager";
|
||||||
|
import {alert} from "../util/Utils";
|
||||||
|
import {MarkerSet} from "./MarkerSet";
|
||||||
|
import {PlayerMarker} from "./PlayerMarker";
|
||||||
|
|
||||||
|
export class PlayerMarkerManager extends MarkerManager {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @constructor
|
||||||
|
* @param markerScene {THREE.Scene} - The scene to which all markers will be added
|
||||||
|
* @param playerDataUrl {string} - The marker file from which this manager updates its markers
|
||||||
|
* @param worldId {string} - The worldId of the world for which the markers should be loaded
|
||||||
|
* @param events {EventTarget}
|
||||||
|
*/
|
||||||
|
constructor(markerScene, playerDataUrl, worldId, events = null) {
|
||||||
|
super(markerScene, playerDataUrl, events);
|
||||||
|
Object.defineProperty(this, 'isPlayerMarkerManager', {value: true});
|
||||||
|
|
||||||
|
this.worldId = worldId;
|
||||||
|
|
||||||
|
this.getPlayerMarkerSet();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param markerData {{players: PlayerLike[]}}
|
||||||
|
*/
|
||||||
|
updateFromData(markerData) {
|
||||||
|
|
||||||
|
/** @type Set<Marker> */
|
||||||
|
let updatedPlayerMarkers = new Set();
|
||||||
|
|
||||||
|
// update
|
||||||
|
if (Array.isArray(markerData.players)) {
|
||||||
|
markerData.players.forEach(playerData => {
|
||||||
|
try {
|
||||||
|
let playerMarker = this.updatePlayerMarkerFromData(playerData);
|
||||||
|
updatedPlayerMarkers.add(playerMarker);
|
||||||
|
} catch (err) {
|
||||||
|
alert(this.events, err, "fine");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove
|
||||||
|
this.markers.forEach((playerMarker, markerId) => {
|
||||||
|
if (!updatedPlayerMarkers.has(playerMarker)) {
|
||||||
|
this.removeMarker(markerId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @param markerData {PlayerLike}
|
||||||
|
* @returns PlayerMarker
|
||||||
|
*/
|
||||||
|
updatePlayerMarkerFromData(markerData) {
|
||||||
|
let playerUuid = markerData.uuid;
|
||||||
|
if (!playerUuid) throw new Error("player-data has no uuid!");
|
||||||
|
let markerId = "bm-player-" + playerUuid;
|
||||||
|
|
||||||
|
/** @type PlayerMarker */
|
||||||
|
let marker = this.markers.get(markerId);
|
||||||
|
|
||||||
|
let markerSet = this.getPlayerMarkerSet();
|
||||||
|
|
||||||
|
// create new if not existent of wrong type
|
||||||
|
if (!marker || !marker.isPlayerMarker) {
|
||||||
|
marker = new PlayerMarker(markerId, playerUuid);
|
||||||
|
this.addMarker(markerSet, marker);
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure marker is in the correct MarkerSet
|
||||||
|
if (marker.parent !== markerSet) markerSet.add(marker);
|
||||||
|
|
||||||
|
// update
|
||||||
|
marker.updateFromData(markerData);
|
||||||
|
|
||||||
|
// hide if wrong world
|
||||||
|
marker.visible = markerData.world === this.worldId;
|
||||||
|
|
||||||
|
return marker;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @returns {MarkerSet}
|
||||||
|
*/
|
||||||
|
getPlayerMarkerSet() {
|
||||||
|
let playerMarkerSet = this.markerSets.get("bm-players");
|
||||||
|
|
||||||
|
if (!playerMarkerSet) {
|
||||||
|
playerMarkerSet = new MarkerSet("bm-players");
|
||||||
|
this.addMarkerSet(playerMarkerSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
return playerMarkerSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -102,7 +102,7 @@ var CSS2DRenderer = function () {
|
||||||
element.style.oTransform = style;
|
element.style.oTransform = style;
|
||||||
element.style.transform = style;
|
element.style.transform = style;
|
||||||
|
|
||||||
element.style.display = ( parentVisible && object.visible && vector.z >= - 1 && vector.z <= 1 && element.style.opacity > 0 ) ? '' : 'none';
|
element.style.display = ( parentVisible && object.visible && vector.z >= - 1 && vector.z <= 1 && element.style.opacity !== "0" ) ? '' : 'none';
|
||||||
|
|
||||||
var objectData = {
|
var objectData = {
|
||||||
distanceToCameraSquared: getDistanceToSquared( camera, object )
|
distanceToCameraSquared: getDistanceToSquared( camera, object )
|
||||||
|
|
|
@ -84,7 +84,7 @@ export const dispatchEvent = (element, event, detail = {}) => {
|
||||||
* - warning
|
* - warning
|
||||||
* - error
|
* - error
|
||||||
* @param element {EventTarget} the element on that the event is dispatched
|
* @param element {EventTarget} the element on that the event is dispatched
|
||||||
* @param message {string}
|
* @param message {object}
|
||||||
* @param level {string}
|
* @param level {string}
|
||||||
*/
|
*/
|
||||||
export const alert = (element, message, level = "info") => {
|
export const alert = (element, message, level = "info") => {
|
||||||
|
@ -176,6 +176,53 @@ export const animate = function (animationFrame, durationMs = 1000, postAnimatio
|
||||||
return animation;
|
return animation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Source: https://gist.github.com/gre/1650294
|
||||||
|
* @type {{
|
||||||
|
* easeOutCubic: (function(number): number),
|
||||||
|
* linear: (function(number): number),
|
||||||
|
* easeOutQuint: (function(number): number),
|
||||||
|
* easeInQuart: (function(number): number),
|
||||||
|
* easeInOutQuint: (function(number): number),
|
||||||
|
* easeInQuad: (function(number): number),
|
||||||
|
* easeOutQuart: (function(number): number),
|
||||||
|
* easeInCubic: (function(number): number),
|
||||||
|
* easeInQuint: (function(number): number),
|
||||||
|
* easeOutQuad: (function(number): number),
|
||||||
|
* easeInOutQuad: (function(number): number),
|
||||||
|
* easeInOutCubic: (function(number): number),
|
||||||
|
* easeInOutQuart: (function(number): number)
|
||||||
|
* }}
|
||||||
|
*/
|
||||||
|
export const EasingFunctions = {
|
||||||
|
// no easing, no acceleration
|
||||||
|
linear: t => t,
|
||||||
|
// accelerating from zero velocity
|
||||||
|
easeInQuad: t => t*t,
|
||||||
|
// decelerating to zero velocity
|
||||||
|
easeOutQuad: t => t*(2-t),
|
||||||
|
// acceleration until halfway, then deceleration
|
||||||
|
easeInOutQuad: t => t<.5 ? 2*t*t : -1+(4-2*t)*t,
|
||||||
|
// accelerating from zero velocity
|
||||||
|
easeInCubic: t => t*t*t,
|
||||||
|
// decelerating to zero velocity
|
||||||
|
easeOutCubic: t => (--t)*t*t+1,
|
||||||
|
// acceleration until halfway, then deceleration
|
||||||
|
easeInOutCubic: t => t<.5 ? 4*t*t*t : (t-1)*(2*t-2)*(2*t-2)+1,
|
||||||
|
// accelerating from zero velocity
|
||||||
|
easeInQuart: t => t*t*t*t,
|
||||||
|
// decelerating to zero velocity
|
||||||
|
easeOutQuart: t => 1-(--t)*t*t*t,
|
||||||
|
// acceleration until halfway, then deceleration
|
||||||
|
easeInOutQuart: t => t<.5 ? 8*t*t*t*t : 1-8*(--t)*t*t*t,
|
||||||
|
// accelerating from zero velocity
|
||||||
|
easeInQuint: t => t*t*t*t*t,
|
||||||
|
// decelerating to zero velocity
|
||||||
|
easeOutQuint: t => 1+(--t)*t*t*t*t,
|
||||||
|
// acceleration until halfway, then deceleration
|
||||||
|
easeInOutQuint: t => t<.5 ? 16*t*t*t*t*t : 1+16*(--t)*t*t*t*t
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the offset position of an element
|
* Returns the offset position of an element
|
||||||
*
|
*
|
||||||
|
|
Loading…
Reference in New Issue