mirror of
https://github.com/BlueMap-Minecraft/BlueMapWeb.git
synced 2025-02-19 19:31:41 +01:00
Implement player-markers
This commit is contained in:
parent
31c159d9ae
commit
a4ee07aa5c
@ -7,7 +7,7 @@
|
||||
<link rel="shortcut icon" href="favicon.png">
|
||||
</head>
|
||||
<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/hammer.js"></script>
|
||||
@ -22,13 +22,21 @@
|
||||
// load map
|
||||
let maps = [];
|
||||
let markerManager = null;
|
||||
let playerManager = null;
|
||||
BlueMap.loadMaps("data/", bluemap.events).then(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.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>
|
||||
</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 * 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>
|
||||
|
@ -287,6 +287,7 @@ export class MapViewer {
|
||||
/**
|
||||
* Changes / Sets the map that will be loaded and displayed
|
||||
* @param map {Map}
|
||||
* @returns Promise<void>
|
||||
*/
|
||||
setMap(map = null) {
|
||||
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 {TileLoader} from "./TileLoader";
|
||||
import {MarkerFileManager} from "../markers/MarkerFileManager";
|
||||
import {PlayerMarkerManager} from "../markers/PlayerMarkerManager";
|
||||
|
||||
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() {
|
||||
this.unload();
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ export class HtmlMarker extends Marker {
|
||||
this.fadeDistanceMax = Number.MAX_VALUE;
|
||||
|
||||
this.addEventListener( 'removed', () => {
|
||||
this.element.parentNode.removeChild(this.element);
|
||||
if (this.element.parentNode) this.element.parentNode.removeChild(this.element);
|
||||
});
|
||||
|
||||
this.add(this.elementObject);
|
||||
|
@ -35,12 +35,7 @@ export class Marker extends Object3D {
|
||||
* @returns {number} - opacity between 0 and 1
|
||||
*/
|
||||
static calculateDistanceOpacity(position, camera, fadeDistanceMin, fadeDistanceMax) {
|
||||
//calculate "orthographic distance" to marker
|
||||
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 distance = Marker.calculateDistanceToCameraPlane(position, camera);
|
||||
let minDelta = (distance - fadeDistanceMin) / fadeDistanceMin;
|
||||
let maxDelta = (distance - fadeDistanceMax) / (fadeDistanceMax * 0.5);
|
||||
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 {ShapeMarker} from "./ShapeMarker";
|
||||
import {alert} from "../util/Utils";
|
||||
@ -6,11 +6,12 @@ import {ExtrudeMarker} from "./ExtrudeMarker";
|
||||
import {LineMarker} from "./LineMarker";
|
||||
import {HtmlMarker} from "./HtmlMarker";
|
||||
import {PoiMarker} from "./PoiMarker";
|
||||
import {MarkerManager} from "./MarkerManager";
|
||||
|
||||
/**
|
||||
* A manager for loading and updating markers from a markers.json file
|
||||
*/
|
||||
export class MarkerFileManager {
|
||||
export class MarkerFileManager extends MarkerManager {
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
@ -20,120 +21,12 @@ export class MarkerFileManager {
|
||||
* @param events {EventTarget}
|
||||
*/
|
||||
constructor(markerScene, fileUrl, mapId, events = null) {
|
||||
super(markerScene, fileUrl, events);
|
||||
Object.defineProperty(this, 'isMarkerFileManager', {value: true});
|
||||
|
||||
this.markerScene = markerScene;
|
||||
this.fileUrl = fileUrl;
|
||||
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) {
|
||||
if (!Array.isArray(markerData.markerSets)) return;
|
||||
let updatedMarkerSets = new Set();
|
||||
@ -144,7 +37,7 @@ export class MarkerFileManager {
|
||||
let markerSet = this.updateMarkerSetFromData(markerSetData);
|
||||
updatedMarkerSets.add(markerSet);
|
||||
} 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);
|
||||
updatedMarkers.add(marker);
|
||||
} catch (err) {
|
||||
alert(this.events, "Failed to parse marker-data: " + err, "fine");
|
||||
alert(this.events, err, "fine");
|
||||
console.debug(err);
|
||||
}
|
||||
});
|
||||
@ -241,24 +134,4 @@ export class MarkerFileManager {
|
||||
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}'!`)
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
}
|
158
src/markers/MarkerManager.js
Normal file
158
src/markers/MarkerManager.js
Normal file
@ -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}'!`)
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
}
|
111
src/markers/PlayerMarker.js
Normal file
111
src/markers/PlayerMarker.js
Normal file
@ -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);
|
||||
}
|
||||
|
||||
}
|
101
src/markers/PlayerMarkerManager.js
Normal file
101
src/markers/PlayerMarkerManager.js
Normal file
@ -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.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 = {
|
||||
distanceToCameraSquared: getDistanceToSquared( camera, object )
|
||||
|
@ -84,7 +84,7 @@ export const dispatchEvent = (element, event, detail = {}) => {
|
||||
* - warning
|
||||
* - error
|
||||
* @param element {EventTarget} the element on that the event is dispatched
|
||||
* @param message {string}
|
||||
* @param message {object}
|
||||
* @param level {string}
|
||||
*/
|
||||
export const alert = (element, message, level = "info") => {
|
||||
@ -176,6 +176,53 @@ export const animate = function (animationFrame, durationMs = 1000, postAnimatio
|
||||
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
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user