Add cache-hashes and more tweaks

This commit is contained in:
Blue (Lukas Rieger) 2021-03-18 21:05:12 +01:00
parent fd74e320cc
commit 3a7b8aef59
No known key found for this signature in database
GPG Key ID: 904C4995F9E1F800
19 changed files with 277 additions and 152 deletions

View File

@ -27,7 +27,7 @@ import {Map} from "./map/Map";
import {SkyboxScene} from "./skybox/SkyboxScene";
import {ControlsManager} from "./controls/ControlsManager";
import Stats from "./util/Stats";
import {alert, dispatchEvent, elementOffset, htmlToElement} from "./util/Utils";
import {alert, dispatchEvent, elementOffset, generateCacheHash, htmlToElement} from "./util/Utils";
import {TileManager} from "./map/TileManager";
import {HIRES_VERTEX_SHADER} from "./map/hires/HiresVertexShader";
import {HIRES_FRAGMENT_SHADER} from "./map/hires/HiresFragmentShader";
@ -73,6 +73,8 @@ export class MapViewer {
loadedLowresViewDistance: 2000,
}
this.tileCacheHash = generateCacheHash();
this.stats = new Stats();
this.stats.hide();
@ -87,7 +89,7 @@ export class MapViewer {
this.renderer.uniforms = this.data.uniforms;
// CSS2D renderer
this.css2dRenderer = new CSS2DRenderer();
this.css2dRenderer = new CSS2DRenderer(this.events);
this.skyboxScene = new SkyboxScene(this.data.uniforms);
@ -310,7 +312,7 @@ export class MapViewer {
this.map = map;
if (this.map && this.map.isMap) {
return map.load(HIRES_VERTEX_SHADER, HIRES_FRAGMENT_SHADER, LOWRES_VERTEX_SHADER, LOWRES_FRAGMENT_SHADER, this.data.uniforms)
return map.load(HIRES_VERTEX_SHADER, HIRES_FRAGMENT_SHADER, LOWRES_VERTEX_SHADER, LOWRES_FRAGMENT_SHADER, this.data.uniforms, this.tileCacheHash)
.then(() => {
for (let texture of this.map.loadedTextures){
this.renderer.initTexture(texture);
@ -356,6 +358,16 @@ export class MapViewer {
this.map.loadMapArea(this.data.loadedCenter.x, this.data.loadedCenter.y, this.data.loadedHiresViewDistance, this.data.loadedLowresViewDistance);
}
clearTileCache(newTileCacheHash) {
if (!newTileCacheHash) newTileCacheHash = generateCacheHash();
this.tileCacheHash = newTileCacheHash;
if (this.map) {
this.map.lowresTileManager.tileLoader.tileCacheHash = this.tileCacheHash;
this.map.hiresTileManager.tileLoader.tileCacheHash = this.tileCacheHash;
}
}
/**
* @returns {number}
*/

View File

@ -41,14 +41,18 @@ export class FreeFlightControls {
this.target = target;
this.manager = null;
this.data = {
};
this.hammer = new Manager(this.target);
this.initializeHammer();
this.keyMove = new KeyMoveControls(this.target, 0.5, 0.1);
this.keyHeight = new KeyHeightControls(this.target, 0.5, 0.2);
this.mouseRotate = new MouseRotateControls(this.target, 0.002, -0.003, -0.002, 0.5);
this.mouseAngle = new MouseAngleControls(this.target, 0.002, -0.003, -0.002, 0.5);
this.touchPan = new TouchPanControls(this.hammer, 0.005, 0.15);
this.mouseRotate = new MouseRotateControls(this.target, 1.5, -2, -1.5, 0.5);
this.mouseAngle = new MouseAngleControls(this.target, 1.5, -2, -1.5, 0.5);
this.touchPan = new TouchPanControls(this.target, this.hammer, 5, 0.15);
this.started = false;
@ -74,24 +78,6 @@ export class FreeFlightControls {
this.target.addEventListener("mousedown", this.onMouseDown);
this.target.addEventListener("mouseup", this.onMouseUp);
window.addEventListener("wheel", this.onWheel, {passive: true});
let startOrtho = this.manager.ortho;
let startDistance = this.manager.distance;
let startAngle = this.manager.angle;
let startY = this.manager.position.y;
let targetAngle = Math.PI / 2;
animate(progress => {
let smoothProgress = EasingFunctions.easeInOutQuad(progress);
this.manager.ortho = MathUtils.lerp(startOrtho, 0, progress);
this.manager.distance = MathUtils.lerp(startDistance, 0, smoothProgress);
this.manager.angle = MathUtils.lerp(startAngle, targetAngle, smoothProgress);
this.manager.position.y = MathUtils.lerp(startY, this.animationTargetHeight, smoothProgress);
}, 500, () => {
this.started = true;
});
}
stop() {
@ -105,8 +91,6 @@ export class FreeFlightControls {
this.target.removeEventListener("mousedown", this.onMouseDown);
this.target.removeEventListener("mouseup", this.onMouseUp);
window.removeEventListener("wheel", this.onWheel);
this.started = false;
}
/**
@ -114,11 +98,6 @@ export class FreeFlightControls {
* @param map {Map}
*/
update(delta, map) {
if (!this.started) {
this.animationTargetHeight = map.terrainHeightAt(this.manager.position.x, this.manager.position.z) + 10;
return;
}
this.keyMove.update(delta, map);
this.keyHeight.update(delta, map);
this.mouseRotate.update(delta, map);
@ -126,6 +105,8 @@ export class FreeFlightControls {
this.touchPan.update(delta, map);
this.manager.angle = MathUtils.clamp(this.manager.angle, 0, Math.PI);
this.manager.distance = 0;
this.manager.ortho = 0;
}
initializeHammer() {
@ -145,7 +126,10 @@ export class FreeFlightControls {
if (Math.abs(this.clickStart.x - evt.x) > 5) return;
if (Math.abs(this.clickStart.y - evt.y) > 5) return;
this.target.requestPointerLock();
document.body.requestFullscreen()
.finally(() => {
this.target.requestPointerLock();
});
}
onWheel = evt => {

View File

@ -46,6 +46,9 @@ export class MouseAngleControls {
this.speedRight = speedRight;
this.speedCapture = speedCapture;
this.stiffness = stiffness;
this.pixelToSpeedMultiplier = 0;
this.updatePixelToSpeedMultiplier();
}
/**
@ -57,12 +60,16 @@ export class MouseAngleControls {
this.target.addEventListener("mousedown", this.onMouseDown);
window.addEventListener("mousemove", this.onMouseMove);
window.addEventListener("mouseup", this.onMouseUp);
window.addEventListener("resize", this.updatePixelToSpeedMultiplier);
}
stop() {
this.target.removeEventListener("mousedown", this.onMouseDown);
window.removeEventListener("mousemove", this.onMouseMove);
window.removeEventListener("mouseup", this.onMouseUp);
window.removeEventListener("resize", this.updatePixelToSpeedMultiplier);
}
/**
@ -103,14 +110,14 @@ export class MouseAngleControls {
*/
onMouseMove = evt => {
if (document.pointerLockElement) {
this.deltaAngle += evt.movementY * this.speedCapture;
this.deltaAngle += evt.movementY * this.speedCapture * this.pixelToSpeedMultiplier;
}
else if(this.moving){
if (evt.buttons === 1) {
this.deltaAngle += (evt.y - this.lastY) * this.speedLeft;
this.deltaAngle += (evt.y - this.lastY) * this.speedLeft * this.pixelToSpeedMultiplier;
} else {
this.deltaAngle += (evt.y - this.lastY) * this.speedRight;
this.deltaAngle += (evt.y - this.lastY) * this.speedRight * this.pixelToSpeedMultiplier;
}
}
@ -125,4 +132,8 @@ export class MouseAngleControls {
this.moving = false;
}
updatePixelToSpeedMultiplier = () => {
this.pixelToSpeedMultiplier = 1 / this.target.clientHeight;
}
}

View File

@ -28,7 +28,7 @@ import {MathUtils} from "three";
export class MouseRotateControls {
/**
* @param target {EventTarget}
* @param target {Element}
* @param speedLeft {number}
* @param speedRight {number}
* @param speedCapture {number}
@ -46,6 +46,9 @@ export class MouseRotateControls {
this.speedRight = speedRight;
this.speedCapture = speedCapture;
this.stiffness = stiffness;
this.pixelToSpeedMultiplier = 0;
this.updatePixelToSpeedMultiplier();
}
/**
@ -57,12 +60,16 @@ export class MouseRotateControls {
this.target.addEventListener("mousedown", this.onMouseDown);
window.addEventListener("mousemove", this.onMouseMove);
window.addEventListener("mouseup", this.onMouseUp);
window.addEventListener("resize", this.updatePixelToSpeedMultiplier);
}
stop() {
this.target.removeEventListener("mousedown", this.onMouseDown);
window.removeEventListener("mousemove", this.onMouseMove);
window.removeEventListener("mouseup", this.onMouseUp);
window.removeEventListener("resize", this.updatePixelToSpeedMultiplier);
}
/**
@ -103,14 +110,14 @@ export class MouseRotateControls {
*/
onMouseMove = evt => {
if (document.pointerLockElement) {
this.deltaRotation -= evt.movementX * this.speedCapture;
this.deltaRotation -= evt.movementX * this.speedCapture * this.pixelToSpeedMultiplier;
}
else if(this.moving){
if (evt.buttons === 1) {
this.deltaRotation -= (evt.x - this.lastX) * this.speedLeft;
this.deltaRotation -= (evt.x - this.lastX) * this.speedLeft * this.pixelToSpeedMultiplier;
} else {
this.deltaRotation -= (evt.x - this.lastX) * this.speedRight;
this.deltaRotation -= (evt.x - this.lastX) * this.speedRight * this.pixelToSpeedMultiplier;
}
}
@ -125,4 +132,8 @@ export class MouseRotateControls {
this.moving = false;
}
updatePixelToSpeedMultiplier = () => {
this.pixelToSpeedMultiplier = (1 / this.target.clientWidth) * (this.target.clientWidth / this.target.clientHeight);
}
}

View File

@ -30,11 +30,13 @@ export class TouchPanControls {
static tempVec2_1 = new Vector2();
/**
* @param target {Element}
* @param hammer {Manager}
* @param speed {number}
* @param stiffness {number}
*/
constructor(hammer, speed, stiffness) {
constructor(target, hammer, speed, stiffness) {
this.target = target;
this.hammer = hammer;
this.manager = null;
@ -44,6 +46,10 @@ export class TouchPanControls {
this.speed = speed;
this.stiffness = stiffness;
this.pixelToSpeedMultiplierX = 0;
this.pixelToSpeedMultiplierY = 0;
this.updatePixelToSpeedMultiplier();
}
/**
@ -56,6 +62,8 @@ export class TouchPanControls {
this.hammer.on("movemove", this.onTouchMove);
this.hammer.on("moveend", this.onTouchUp);
this.hammer.on("movecancel", this.onTouchUp);
window.addEventListener("resize", this.updatePixelToSpeedMultiplier);
}
stop() {
@ -63,6 +71,8 @@ export class TouchPanControls {
this.hammer.off("movemove", this.onTouchMove);
this.hammer.off("moveend", this.onTouchUp);
this.hammer.off("movecancel", this.onTouchUp);
window.removeEventListener("resize", this.updatePixelToSpeedMultiplier);
}
/**
@ -75,8 +85,8 @@ export class TouchPanControls {
let smoothing = this.stiffness / (16.666 / delta);
smoothing = MathUtils.clamp(smoothing, 0, 1);
this.manager.rotation += this.deltaPosition.x * this.speed * this.stiffness;
this.manager.angle -= this.deltaPosition.y * this.speed * this.stiffness;
this.manager.rotation += this.deltaPosition.x * this.speed * this.pixelToSpeedMultiplierX * this.stiffness;
this.manager.angle -= this.deltaPosition.y * this.speed * this.pixelToSpeedMultiplierY * this.stiffness;
this.deltaPosition.multiplyScalar(1 - smoothing);
if (this.deltaPosition.lengthSq() < 0.0001) {
@ -126,4 +136,9 @@ export class TouchPanControls {
this.moving = false;
}
updatePixelToSpeedMultiplier = () => {
this.pixelToSpeedMultiplierX = (1 / this.target.clientWidth) * (this.target.clientWidth / this.target.clientHeight);
this.pixelToSpeedMultiplierY = 1 / this.target.clientHeight;
}
}

View File

@ -27,9 +27,9 @@ import {MouseMoveControls} from "./mouse/MouseMoveControls";
import {MouseZoomControls} from "./mouse/MouseZoomControls";
import {MouseRotateControls} from "./mouse/MouseRotateControls";
import {MouseAngleControls} from "./mouse/MouseAngleControls";
import {MathUtils, Vector2} from "three";
import {MathUtils, Vector2, Vector3} from "three";
import {Manager, Pan, Pinch, Rotate, Tap, DIRECTION_ALL, DIRECTION_VERTICAL} from "hammerjs";
import {animate, EasingFunctions, softClamp} from "../../util/Utils";
import {softClamp} from "../../util/Utils";
import {MapHeightControls} from "./MapHeightControls";
import {KeyMoveControls} from "./keyboard/KeyMoveControls";
import {KeyAngleControls} from "./keyboard/KeyAngleControls";
@ -39,29 +39,34 @@ import {TouchMoveControls} from "./touch/TouchMoveControls";
import {TouchRotateControls} from "./touch/TouchRotateControls";
import {TouchAngleControls} from "./touch/TouchAngleControls";
import {TouchZoomControls} from "./touch/TouchZoomControls";
import {PlayerMarker} from "../../markers/PlayerMarker";
const HALF_PI = Math.PI * 0.5;
export class MapControls {
static _beforeMoveTemp = new Vector3();
/**
* @param rootElement {EventTarget}
* @param rootElement {Element}
*/
constructor(rootElement) {
this.rootElement = rootElement;
this.data = {
followingPlayer: null
};
/** @type {ControlsManager} */
this.manager = null;
this.started = false;
this.hammer = new Manager(this.rootElement);
this.initializeHammer();
//controls
this.mouseMove = new MouseMoveControls(this.rootElement, 0.002,0.3);
this.mouseRotate = new MouseRotateControls(this.rootElement, 0.004, 0.3);
this.mouseAngle = new MouseAngleControls(this.rootElement, 0.004, 0.3);
this.mouseMove = new MouseMoveControls(this.rootElement, 1.5,0.3);
this.mouseRotate = new MouseRotateControls(this.rootElement, 6, 0.3);
this.mouseAngle = new MouseAngleControls(this.rootElement, 3, 0.3);
this.mouseZoom = new MouseZoomControls(this.rootElement, 1, 0.2);
this.keyMove = new KeyMoveControls(this.rootElement, 0.025, 0.2);
@ -69,14 +74,15 @@ export class MapControls {
this.keyAngle = new KeyAngleControls(this.rootElement, 0.04, 0.15);
this.keyZoom = new KeyZoomControls(this.rootElement, 0.2, 0.15);
this.touchMove = new TouchMoveControls(this.hammer, 0.002,0.3);
this.touchMove = new TouchMoveControls(this.rootElement, this.hammer, 1.5,0.3);
this.touchRotate = new TouchRotateControls(this.hammer, 0.0174533, 0.3);
this.touchAngle = new TouchAngleControls(this.hammer, 0.01, 0.3);
this.touchAngle = new TouchAngleControls(this.rootElement, this.hammer, 3, 0.3);
this.touchZoom = new TouchZoomControls(this.hammer);
this.mapHeight = new MapHeightControls(0.2, 0.1);
this.animationTargetHeight = 0;
this.lastTap = -1;
this.lastTapCenter = null;
}
/**
@ -104,27 +110,11 @@ export class MapControls {
this.touchZoom.start(manager);
this.mapHeight.start(manager);
let startOrtho = this.manager.ortho;
let startDistance = this.manager.distance;
let startAngle = this.manager.angle;
let startY = this.manager.position.y;
let targetDistance = MathUtils.clamp(this.manager.distance, 100, 10000);
let targetAngle = Math.min(startAngle, this.getMaxPerspectiveAngleForDistance(targetDistance));
animate(progress => {
let smoothProgress = EasingFunctions.easeInOutQuad(progress);
this.manager.ortho = MathUtils.lerp(startOrtho, 0, progress);
this.manager.distance = MathUtils.lerp(startDistance, targetDistance, smoothProgress);
this.manager.angle = MathUtils.lerp(startAngle, targetAngle, smoothProgress);
this.manager.position.y = MathUtils.lerp(startY, this.animationTargetHeight, smoothProgress);
}, 500, () => this.started = true);
}
stop() {
this.stopFollowingPlayerMarker();
this.rootElement.removeEventListener("contextmenu", this.onContextMenu);
this.hammer.off("tap", this.onTap);
@ -144,8 +134,6 @@ export class MapControls {
this.touchZoom.stop();
this.mapHeight.stop();
this.started = false;
}
/**
@ -153,18 +141,27 @@ export class MapControls {
* @param map {Map}
*/
update(delta, map) {
if (!this.started){
this.mapHeight.updateHeights(delta, map);
this.animationTargetHeight = this.mapHeight.getSuggestedHeight();
return;
this.manager.position.y = 0; // reset target y position
// move
MapControls._beforeMoveTemp.copy(this.manager.position);
this.mouseMove.update(delta, map);
this.keyMove.update(delta, map);
this.touchMove.update(delta, map);
// if moved, stop following the marker and give back control
if (this.data.followingPlayer && !MapControls._beforeMoveTemp.equals(this.manager.position)) {
this.stopFollowingPlayerMarker();
}
// move and zoom
this.mouseMove.update(delta, map);
// follow player marker
if (this.data.followingPlayer) {
this.manager.position.copy(this.data.followingPlayer.position);
}
// zoom
this.mouseZoom.update(delta, map);
this.keyMove.update(delta, map);
this.keyZoom.update(delta, map);
this.touchMove.update(delta, map);
this.touchZoom.update(delta, map);
this.manager.distance = softClamp(this.manager.distance, 5, 10000, 0.8);
@ -189,7 +186,6 @@ export class MapControls {
// target height
if (this.manager.ortho === 0 || this.manager.angle === 0) {
this.manager.position.y = 0;
this.mapHeight.maxAngle = maxAngleForZoom;
this.mapHeight.update(delta, map);
}
@ -211,38 +207,6 @@ export class MapControls {
return MathUtils.clamp((1 - Math.pow(Math.max(distance - 5, 0.001) / 500, 0.5)) * HALF_PI,0, HALF_PI)
}
setPerspectiveView() {
this.reset();
let startOrtho = this.manager.ortho;
let startAngle = this.manager.angle;
let targetAngle = Math.min(startAngle, this.getMaxPerspectiveAngleForDistance(this.manager.distance));
animate(progress => {
let smoothProgress = EasingFunctions.easeInOutQuad(progress);
this.manager.ortho = MathUtils.lerp(startOrtho, 0, progress);
this.manager.angle = MathUtils.lerp(startAngle, targetAngle, smoothProgress);
}, 500);
}
setOrthographicView(targetRotation = 0, targetAngle = 0) {
this.reset();
let startOrtho = this.manager.ortho;
let startAngle = this.manager.angle;
let startRotation = this.manager.rotation;
animate(progress => {
let smoothProgress = EasingFunctions.easeInOutQuad(progress);
this.manager.ortho = MathUtils.lerp(startOrtho, 1, progress);
this.manager.angle = MathUtils.lerp(startAngle, targetAngle, smoothProgress);
this.manager.rotation = MathUtils.lerp(startRotation, targetRotation, smoothProgress);
}, 500);
}
initializeHammer() {
let touchTap = new Tap({ event: 'tap', pointers: 1, taps: 1, threshold: 5 });
let touchMove = new Pan({ event: 'move', pointers: 1, direction: DIRECTION_ALL, threshold: 0 });
@ -264,12 +228,36 @@ export class MapControls {
this.hammer.add(touchZoom);
}
/**
* @param marker {object}
*/
followPlayerMarker(marker) {
if (marker.isPlayerMarker) marker = marker.data;
this.data.followingPlayer = marker;
}
stopFollowingPlayerMarker() {
this.data.followingPlayer = null;
}
onContextMenu = evt => {
evt.preventDefault();
}
onTap = evt => {
this.manager.handleMapInteraction(new Vector2(evt.center.x, evt.center.y));
let doubleTap = false;
let center = new Vector2(evt.center.x, evt.center.y);
let now = Date.now();
if (this.lastTap > 0 && this.lastTapCenter && now - this.lastTap < 500 && this.lastTapCenter.distanceTo(center) < 5){
doubleTap = true;
this.lastTap = -1;
} else {
this.lastTap = now;
this.lastTapCenter = center;
}
this.manager.handleMapInteraction(new Vector2(evt.center.x, evt.center.y), {doubleTap: doubleTap});
}
}

View File

@ -42,6 +42,9 @@ export class MouseAngleControls {
this.speed = speed;
this.stiffness = stiffness;
this.pixelToSpeedMultiplierY = 0;
this.updatePixelToSpeedMultiplier();
}
/**
@ -53,12 +56,16 @@ export class MouseAngleControls {
this.target.addEventListener("mousedown", this.onMouseDown);
window.addEventListener("mousemove", this.onMouseMove);
window.addEventListener("mouseup", this.onMouseUp);
window.addEventListener("resize", this.updatePixelToSpeedMultiplier);
}
stop() {
this.target.removeEventListener("mousedown", this.onMouseDown);
window.removeEventListener("mousemove", this.onMouseMove);
window.removeEventListener("mouseup", this.onMouseUp);
window.removeEventListener("resize", this.updatePixelToSpeedMultiplier);
}
/**
@ -71,7 +78,7 @@ export class MouseAngleControls {
let smoothing = this.stiffness / (16.666 / delta);
smoothing = MathUtils.clamp(smoothing, 0, 1);
this.manager.angle += this.deltaAngle * smoothing * this.speed;
this.manager.angle += this.deltaAngle * smoothing * this.speed * this.pixelToSpeedMultiplierY;
this.deltaAngle *= 1 - smoothing;
if (Math.abs(this.deltaAngle) < 0.0001) {
@ -116,4 +123,8 @@ export class MouseAngleControls {
this.moving = false;
}
updatePixelToSpeedMultiplier = () => {
this.pixelToSpeedMultiplierY = 1 / this.target.clientHeight;
}
}

View File

@ -31,7 +31,7 @@ export class MouseMoveControls {
static tempVec2_1 = new Vector2();
/**
* @param target {EventTarget}
* @param target {Element}
* @param speed {number}
* @param stiffness {number}
*/
@ -45,6 +45,10 @@ export class MouseMoveControls {
this.speed = speed;
this.stiffness = stiffness;
this.pixelToSpeedMultiplierX = 0;
this.pixelToSpeedMultiplierY = 0;
this.updatePixelToSpeedMultiplier();
}
/**
@ -56,12 +60,16 @@ export class MouseMoveControls {
this.target.addEventListener("mousedown", this.onMouseDown);
window.addEventListener("mousemove", this.onMouseMove);
window.addEventListener("mouseup", this.onMouseUp);
window.addEventListener("resize", this.updatePixelToSpeedMultiplier);
}
stop() {
this.target.removeEventListener("mousedown", this.onMouseDown);
window.removeEventListener("mousemove", this.onMouseMove);
window.removeEventListener("mouseup", this.onMouseUp);
window.removeEventListener("resize", this.updatePixelToSpeedMultiplier);
}
/**
@ -77,8 +85,8 @@ export class MouseMoveControls {
let directionDelta = MouseMoveControls.tempVec2_1.copy(this.deltaPosition);
directionDelta.rotateAround(VEC2_ZERO, this.manager.rotation);
this.manager.position.x += directionDelta.x * smoothing * this.manager.distance * this.speed;
this.manager.position.z += directionDelta.y * smoothing * this.manager.distance * this.speed;
this.manager.position.x += directionDelta.x * smoothing * this.manager.distance * this.speed * this.pixelToSpeedMultiplierX;
this.manager.position.z += directionDelta.y * smoothing * this.manager.distance * this.speed * this.pixelToSpeedMultiplierY;
this.deltaPosition.multiplyScalar(1 - smoothing);
if (this.deltaPosition.lengthSq() < 0.0001) {
@ -124,4 +132,9 @@ export class MouseMoveControls {
if (evt.button === 0) this.moving = false;
}
updatePixelToSpeedMultiplier = () => {
this.pixelToSpeedMultiplierX = (1 / this.target.clientWidth) * (this.target.clientWidth / this.target.clientHeight);
this.pixelToSpeedMultiplierY = 1 / this.target.clientHeight;
}
}

View File

@ -42,6 +42,9 @@ export class MouseRotateControls {
this.speed = speed;
this.stiffness = stiffness;
this.pixelToSpeedMultiplierX = 0;
this.updatePixelToSpeedMultiplier();
}
/**
@ -53,12 +56,16 @@ export class MouseRotateControls {
this.target.addEventListener("mousedown", this.onMouseDown);
window.addEventListener("mousemove", this.onMouseMove);
window.addEventListener("mouseup", this.onMouseUp);
window.addEventListener("resize", this.updatePixelToSpeedMultiplier);
}
stop() {
this.target.removeEventListener("mousedown", this.onMouseDown);
window.removeEventListener("mousemove", this.onMouseMove);
window.removeEventListener("mouseup", this.onMouseUp);
window.removeEventListener("resize", this.updatePixelToSpeedMultiplier);
}
/**
@ -71,7 +78,7 @@ export class MouseRotateControls {
let smoothing = this.stiffness / (16.666 / delta);
smoothing = MathUtils.clamp(smoothing, 0, 1);
this.manager.rotation += this.deltaRotation * smoothing * this.speed;
this.manager.rotation += this.deltaRotation * smoothing * this.speed * this.pixelToSpeedMultiplierX;
this.deltaRotation *= 1 - smoothing;
if (Math.abs(this.deltaRotation) < 0.0001) {
@ -116,4 +123,8 @@ export class MouseRotateControls {
this.moving = false;
}
updatePixelToSpeedMultiplier = () => {
this.pixelToSpeedMultiplierX = (1 / this.target.clientWidth); //* (this.target.clientWidth / this.target.clientHeight);
}
}

View File

@ -28,11 +28,13 @@ import {MathUtils} from "three";
export class TouchAngleControls {
/**
* @param target {Element}
* @param hammer {Manager}
* @param speed {number}
* @param stiffness {number}
*/
constructor(hammer, speed, stiffness) {
constructor(target, hammer, speed, stiffness) {
this.target = target;
this.hammer = hammer;
this.manager = null;
@ -42,6 +44,9 @@ export class TouchAngleControls {
this.speed = speed;
this.stiffness = stiffness;
this.pixelToSpeedMultiplierY = 0;
this.updatePixelToSpeedMultiplier();
}
/**
@ -54,6 +59,8 @@ export class TouchAngleControls {
this.hammer.on("tiltmove", this.onTouchMove);
this.hammer.on("tiltend", this.onTouchUp);
this.hammer.on("tiltcancel", this.onTouchUp);
window.addEventListener("resize", this.updatePixelToSpeedMultiplier);
}
stop() {
@ -61,6 +68,8 @@ export class TouchAngleControls {
this.hammer.off("tiltmove", this.onTouchMove);
this.hammer.off("tiltend", this.onTouchUp);
this.hammer.off("tiltcancel", this.onTouchUp);
window.removeEventListener("resize", this.updatePixelToSpeedMultiplier);
}
/**
@ -73,7 +82,7 @@ export class TouchAngleControls {
let smoothing = this.stiffness / (16.666 / delta);
smoothing = MathUtils.clamp(smoothing, 0, 1);
this.manager.angle += this.deltaAngle * smoothing * this.speed;
this.manager.angle += this.deltaAngle * smoothing * this.speed * this.pixelToSpeedMultiplierY;
this.deltaAngle *= 1 - smoothing;
if (Math.abs(this.deltaAngle) < 0.0001) {
@ -115,4 +124,8 @@ export class TouchAngleControls {
this.moving = false;
}
updatePixelToSpeedMultiplier = () => {
this.pixelToSpeedMultiplierY = 1 / this.target.clientHeight;
}
}

View File

@ -31,11 +31,13 @@ export class TouchMoveControls {
static tempVec2_1 = new Vector2();
/**
* @param target {Element}
* @param hammer {Manager}
* @param speed {number}
* @param stiffness {number}
*/
constructor(hammer, speed, stiffness) {
constructor(target, hammer, speed, stiffness) {
this.target = target;
this.hammer = hammer;
this.manager = null;
@ -45,6 +47,10 @@ export class TouchMoveControls {
this.speed = speed;
this.stiffness = stiffness;
this.pixelToSpeedMultiplierX = 0;
this.pixelToSpeedMultiplierY = 0;
this.updatePixelToSpeedMultiplier();
}
/**
@ -57,6 +63,8 @@ export class TouchMoveControls {
this.hammer.on("movemove", this.onTouchMove);
this.hammer.on("moveend", this.onTouchUp);
this.hammer.on("movecancel", this.onTouchUp);
window.addEventListener("resize", this.updatePixelToSpeedMultiplier);
}
stop() {
@ -64,6 +72,8 @@ export class TouchMoveControls {
this.hammer.off("movemove", this.onTouchMove);
this.hammer.off("moveend", this.onTouchUp);
this.hammer.off("movecancel", this.onTouchUp);
window.removeEventListener("resize", this.updatePixelToSpeedMultiplier);
}
/**
@ -79,8 +89,8 @@ export class TouchMoveControls {
let directionDelta = TouchMoveControls.tempVec2_1.copy(this.deltaPosition);
directionDelta.rotateAround(VEC2_ZERO, this.manager.rotation);
this.manager.position.x += directionDelta.x * smoothing * this.manager.distance * this.speed;
this.manager.position.z += directionDelta.y * smoothing * this.manager.distance * this.speed;
this.manager.position.x += directionDelta.x * smoothing * this.manager.distance * this.speed * this.pixelToSpeedMultiplierX;
this.manager.position.z += directionDelta.y * smoothing * this.manager.distance * this.speed * this.pixelToSpeedMultiplierY;
this.deltaPosition.multiplyScalar(1 - smoothing);
if (this.deltaPosition.lengthSq() < 0.0001) {
@ -130,4 +140,9 @@ export class TouchMoveControls {
this.moving = false;
}
updatePixelToSpeedMultiplier = () => {
this.pixelToSpeedMultiplierX = (1 / this.target.clientWidth) * (this.target.clientWidth / this.target.clientHeight);
this.pixelToSpeedMultiplierY = 1 / this.target.clientHeight;
}
}

View File

@ -27,7 +27,7 @@ import {
FileLoader, FrontSide, NearestFilter, NearestMipMapLinearFilter, Raycaster,
Scene, ShaderMaterial, Texture, Vector2, Vector3, VertexColors
} from "three";
import {alert, dispatchEvent, hashTile, stringToImage} from "../util/Utils";
import {alert, dispatchEvent, generateCacheHash, hashTile, stringToImage} from "../util/Utils";
import {TileManager} from "./TileManager";
import {TileLoader} from "./TileLoader";
import {MarkerFileManager} from "../markers/MarkerFileManager";
@ -42,7 +42,7 @@ export class Map {
* @param texturesUrl {string}
* @param events {EventTarget}
*/
constructor(id, dataUrl, settingsUrl, texturesUrl, events = null) {
constructor(id, dataUrl, settingsUrl, texturesUrl, events = null) {
Object.defineProperty( this, 'isMap', { value: true } );
this.events = events;
@ -91,9 +91,10 @@ export class Map {
* @param lowresVertexShader {string}
* @param lowresFragmentShader {string}
* @param uniforms {object}
* @param tileCacheHash {number}
* @returns {Promise<void>}
*/
load(hiresVertexShader, hiresFragmentShader, lowresVertexShader, lowresFragmentShader, uniforms) {
load(hiresVertexShader, hiresFragmentShader, lowresVertexShader, lowresFragmentShader, uniforms, tileCacheHash = 0) {
this.unload()
let settingsPromise = this.loadSettings();
@ -108,8 +109,8 @@ export class Map {
this.hiresMaterial = this.createHiresMaterial(hiresVertexShader, hiresFragmentShader, uniforms, textures);
this.hiresTileManager = new TileManager(new Scene(), new TileLoader(`${this.data.dataUrl}hires/`, this.hiresMaterial, this.data.hires), this.onTileLoad("hires"), this.onTileUnload("hires"), this.events);
this.lowresTileManager = new TileManager(new Scene(), new TileLoader(`${this.data.dataUrl}lowres/`, this.lowresMaterial, this.data.lowres), this.onTileLoad("lowres"), this.onTileUnload("lowres"), this.events);
this.hiresTileManager = new TileManager(new Scene(), new TileLoader(`${this.data.dataUrl}hires/`, this.hiresMaterial, this.data.hires, tileCacheHash), this.onTileLoad("hires"), this.onTileUnload("hires"), this.events);
this.lowresTileManager = new TileManager(new Scene(), new TileLoader(`${this.data.dataUrl}lowres/`, this.lowresMaterial, this.data.lowres, tileCacheHash), this.onTileLoad("lowres"), this.onTileUnload("lowres"), this.events);
this.hiresTileManager.scene.autoUpdate = false;
this.lowresTileManager.scene.autoUpdate = false;
@ -201,7 +202,7 @@ export class Map {
let loader = new FileLoader();
loader.setResponseType("json");
loader.load(this.data.settingsUrl,
loader.load(this.data.settingsUrl + "?" + generateCacheHash(),
settings => {
if (settings.maps && settings.maps[this.data.id]) {
resolve(settings.maps[this.data.id]);
@ -225,7 +226,7 @@ export class Map {
let loader = new FileLoader();
loader.setResponseType("json");
loader.load(this.data.texturesUrl,
loader.load(this.data.texturesUrl + "?" + generateCacheHash(),
resolve,
() => {},
() => reject(`Failed to load the textures.json for map: ${this.data.id}`)

View File

@ -31,13 +31,14 @@ export class TileLoader {
* @param tilePath {string}
* @param material {THREE.Material | THREE.Material[]}
* @param tileSettings {{
* tileSize: {x: number, z: number},
* scale: {x: number, z: number},
* tileSize: {x: number, z: number},
* scale: {x: number, z: number},
* translate: {x: number, z: number}
* }}
* @param tileCacheHash {number}
* @param layer {number}
*/
constructor(tilePath, material, tileSettings, layer = 0) {
constructor(tilePath, material, tileSettings, tileCacheHash = 0, layer = 0) {
Object.defineProperty( this, 'isTileLoader', { value: true } );
this.tilePath = tilePath;
@ -46,6 +47,8 @@ export class TileLoader {
this.layer = layer;
this.tileCacheHash = tileCacheHash;
this.fileLoader = new FileLoader();
this.fileLoader.setResponseType('json');
@ -53,8 +56,10 @@ export class TileLoader {
}
load = (tileX, tileZ) => {
let tileUrl = this.tilePath + pathFromCoords(tileX, tileZ) + '.json';
return new Promise((resolve, reject) => {
this.fileLoader.load(this.tilePath + pathFromCoords(tileX, tileZ) + '.json',
this.fileLoader.load(tileUrl + '?' + this.tileCacheHash,
geometryJson => {
if (!geometryJson.type || geometryJson.type !== 'BufferGeometry') reject({status: "empty"});
@ -69,7 +74,7 @@ export class TileLoader {
object.position.set(tileX * tileSize.x + translate.x, 0, tileZ * tileSize.z + translate.z);
object.scale.set(scale.x, 1, scale.z);
object.userData.tileUrl = this.tilePath + pathFromCoords(tileX, tileZ) + '.json';
object.userData.tileUrl = tileUrl;
object.updateMatrixWorld(true);

View File

@ -24,7 +24,7 @@
*/
import {FileLoader, Scene} from "three";
import {MarkerSet} from "./MarkerSet";
import {alert} from "../util/Utils";
import {alert, generateCacheHash} from "../util/Utils";
/**
* A manager for loading and updating markers from a file
@ -170,7 +170,7 @@ export class MarkerManager {
return new Promise((resolve, reject) => {
let loader = new FileLoader();
loader.setResponseType("json");
loader.load(this.fileUrl,
loader.load(this.fileUrl + "?" + generateCacheHash(),
markerFileData => {
if (!markerFileData) reject(`Failed to parse '${this.fileUrl}'!`);
else resolve(markerFileData);

View File

@ -41,6 +41,8 @@ export class ObjectMarker extends Marker {
this.data.detail = null;
this.data.link = null;
this.data.newTab = true;
this.lastClick = -1;
}
onClick(event) {
@ -50,6 +52,8 @@ export class ObjectMarker extends Marker {
pos.sub(this.position);
}
if (event.data.doubleTap) return false;
if (this.data.detail || this.data.label) {
let popup = new LabelPopup(this.data.detail || this.data.label);
popup.position.copy(pos);

View File

@ -40,6 +40,8 @@ export class PlayerMarker extends Marker {
this.data.playerUuid = playerUuid;
this.data.name = playerUuid;
this.data.world = "?";
this.elementObject = new CSS2DObject(htmlToElement(`
<div id="bm-marker-${this.data.id}" class="bm-marker-${this.data.type}">
<img src="assets/playerheads/${this.data.playerUuid}.png" alt="playerhead" draggable="false">
@ -135,6 +137,9 @@ export class PlayerMarker extends Marker {
if (this.playerNameElement.innerHTML !== name)
this.playerNameElement.innerHTML = name;
// update world
this.data.world = markerData.world || "?";
}
dispose() {

View File

@ -43,11 +43,13 @@ export class PoiMarker extends HtmlMarker {
}
onClick(event) {
if (event.data.doubleTap) return false;
if (this.highlight || !this.data.label) return true;
this.highlight = true;
let eventHandler = evt => {
if (evt.path.includes(this.element)) return;
if (evt.composedPath().includes(this.element)) return;
this.highlight = false;

View File

@ -9,7 +9,7 @@ import {
Object3D, Vector2,
Vector3
} from "three";
import {htmlToElement} from "./Utils";
import {dispatchEvent, htmlToElement} from "./Utils";
var CSS2DObject = function ( element ) {
@ -24,6 +24,8 @@ var CSS2DObject = function ( element ) {
this.anchor = new Vector2();
this.events = null;
this.addEventListener( 'removed', function () {
this.traverse( function ( object ) {
@ -38,18 +40,33 @@ var CSS2DObject = function ( element ) {
} );
this.element.addEventListener("click", event => {
if (this.onClick(event)) {
let lastClick = -1;
let handleClick = event => {
let doubleTap = false;
let now = Date.now();
if (now - lastClick < 500){
doubleTap = true;
}
lastClick = now;
let data = {doubleTap: doubleTap};
if (this.onClick( {event: event, data: data} )) {
event.preventDefault();
event.stopPropagation();
} else {
// fire event
dispatchEvent(this.events, "bluemapMapInteraction", {
data: data,
object: this,
});
}
});
this.element.addEventListener("touch", event => {
if (this.onClick(event)) {
event.preventDefault();
event.stopPropagation();
}
});
}
this.element.addEventListener("click", handleClick);
this.element.addEventListener("touch", handleClick);
};
@ -58,7 +75,7 @@ CSS2DObject.prototype.constructor = CSS2DObject;
//
var CSS2DRenderer = function () {
var CSS2DRenderer = function (events = null) {
var _this = this;
@ -78,6 +95,8 @@ var CSS2DRenderer = function () {
this.domElement = domElement;
this.events = events;
this.getSize = function () {
return {
@ -104,6 +123,8 @@ var CSS2DRenderer = function () {
if ( object instanceof CSS2DObject ) {
object.events = _this.events;
object.onBeforeRender( _this, scene, camera );
vector.setFromMatrixPosition( object.matrixWorld );

View File

@ -89,6 +89,9 @@ const splitNumberToPath = num => {
*/
export const hashTile = (x, z) => `x${x}z${z}`;
export const generateCacheHash = () => {
return Math.round(Math.random() * 1000000);
}
/**
* Dispatches an event to the element of this map-viewer
@ -184,7 +187,7 @@ export const animate = function (animationFrame, durationMs = 1000, postAnimatio
this.lastFrame = time;
}
let progress = MathUtils.clamp((time - this.animationStart) / durationMs, 0, 1);
let progress = durationMs === 0 ? 1 : MathUtils.clamp((time - this.animationStart) / durationMs, 0, 1);
let deltaTime = time - this.lastFrame;
animationFrame(progress, deltaTime);