Merge branch 'master' into mc/1.12

This commit is contained in:
Blue (Lukas Rieger) 2020-01-15 21:32:21 +01:00
commit eac678669b
32 changed files with 1978 additions and 2503 deletions

View File

@ -14,7 +14,7 @@ jobs:
- name: Test with Gradle - name: Test with Gradle
run: ./gradlew test run: ./gradlew test
- name: Build with Gradle - name: Build with Gradle
run: ./gradlew shadowJar run: ./gradlew build
- uses: actions/upload-artifact@v1 - uses: actions/upload-artifact@v1
with: with:
name: artifact name: artifact

3
.gitignore vendored
View File

@ -20,6 +20,9 @@ bin/*
.project .project
*/.project */.project
node_modules/
package-lock.json
# exclude generated resource # exclude generated resource
BlueMapCore/src/main/resources/webroot.zip BlueMapCore/src/main/resources/webroot.zip
BlueMapCore/src/main/resources/resourceExtensions.zip BlueMapCore/src/main/resources/resourceExtensions.zip

View File

@ -1,3 +1,7 @@
plugins {
id 'com.moowork.node' version '1.3.1'
}
dependencies { dependencies {
compile 'com.google.guava:guava:21.0' compile 'com.google.guava:guava:21.0'
compile 'com.google.code.gson:gson:2.8.0' compile 'com.google.code.gson:gson:2.8.0'
@ -12,13 +16,42 @@ dependencies {
testCompile 'junit:junit:4.12' testCompile 'junit:junit:4.12'
} }
node {
version = '12.14.1'
download = true
}
task fixPackageLock() {
if (!file("./package-lock.json").exists()) {
file("./package-lock.json").text = ""
}
}
task cleanWebroot(type: Delete) {
delete 'build/generated/webroot/'
}
// Run WebPack build to generate resources into the generated resources
task webpackWebroot(type: NpmTask) {
args = ['run', 'build']
}
task zipWebroot(type: Zip) { task zipWebroot(type: Zip) {
from fileTree('src/main/webroot') from fileTree('build/generated/webroot/')
archiveName 'webroot.zip' archiveName 'webroot.zip'
destinationDir(file('src/main/resources/')) destinationDir(file('src/main/resources/'))
outputs.upToDateWhen { false } outputs.upToDateWhen { false }
} }
// removes tmp build directory, build project with webpack, zip contents for the shaded jar
task buildWebroot {
dependsOn 'fixPackageLock'
dependsOn 'npmInstall'
dependsOn 'cleanWebroot'
dependsOn 'webpackWebroot'
dependsOn 'zipWebroot'
}
task zipResourceExtensions(type: Zip) { task zipResourceExtensions(type: Zip) {
from fileTree('src/main/resourceExtensions') from fileTree('src/main/resourceExtensions')
archiveName 'resourceExtensions.zip' archiveName 'resourceExtensions.zip'
@ -27,5 +60,5 @@ task zipResourceExtensions(type: Zip) {
} }
//always update the zip before build //always update the zip before build
compileJava.dependsOn(zipWebroot) processResources.dependsOn(buildWebroot)
compileJava.dependsOn(zipResourceExtensions) processResources.dependsOn(zipResourceExtensions)

42
BlueMapCore/package.json Normal file
View File

@ -0,0 +1,42 @@
{
"name": "bluemap",
"description": "BlueMap is a tool that generates 3d-maps of your Minecraft worlds and displays them in your browser.",
"private": true,
"repository": {
"type": "git",
"url": "git+https://github.com/BlueMap-Minecraft/BlueMap.git"
},
"keywords": [
"minecraft"
],
"author": "Blue",
"license": "MIT",
"bugs": {
"url": "https://github.com/BlueMap-Minecraft/BlueMap/issues"
},
"homepage": "https://github.com/BlueMap-Minecraft/BlueMap#readme",
"dependencies": {
"jquery": "^3.4.1",
"three": "^0.94.0"
},
"devDependencies": {
"css-loader": "^3.4.2",
"fibers": "^4.0.2",
"file-loader": "^5.0.2",
"html-webpack-plugin": "^3.2.0",
"mini-css-extract-plugin": "^0.9.0",
"node-sass": "^4.13.0",
"sass": "^1.24.4",
"sass-loader": "^8.0.2",
"ts-loader": "^6.2.1",
"typescript": "^3.7.4",
"webpack": "^4.41.5",
"webpack-cli": "^3.3.10",
"webpack-dev-server": "^3.10.1"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack --mode production",
"start": "webpack-dev-server --watch --mode development"
}
}

View File

@ -1,22 +1,10 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<meta charset=utf-8> <meta charset="utf-8">
<title>BlueMap</title> <title>BlueMap</title>
<link rel="stylesheet" type="text/css" href="style/style.css?v=2">
<script src="js/libs/jquery.min.js"></script>
<script type="text/javascript" src="js/libs/three.min.js"></script>
<script type="text/javascript" src="js/libs/bluemap.js"></script>
</head> </head>
<body> <body>
<div id="map-container"></div> <div id="map-container"></div>
<script type="text/javascript" src="js/site.js"></script>
</body> </body>
</html> </html>

View File

@ -0,0 +1,483 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import $ from 'jquery';
import {
AmbientLight,
BackSide,
BufferGeometryLoader,
ClampToEdgeWrapping,
CubeGeometry,
DirectionalLight,
FileLoader,
FrontSide,
Mesh,
MeshBasicMaterial,
MeshLambertMaterial,
NormalBlending,
NearestFilter,
PerspectiveCamera,
Scene,
Texture,
TextureLoader,
VertexColors,
WebGLRenderer,
} from 'three';
import Compass from './modules/Compass.js';
import Info from './modules/Info.js';
import MapMenu from './modules/MapMenu.js';
import Position from './modules/Position.js';
import Settings from './modules/Settings.js';
import Controls from './Controls.js';
import TileManager from './TileManager.js';
import { stringToImage, pathFromCoords } from './utils.js';
import SKYBOX_NORTH from '../../assets/skybox/north.png';
import SKYBOX_SOUTH from '../../assets/skybox/south.png';
import SKYBOX_EAST from '../../assets/skybox/east.png';
import SKYBOX_WEST from '../../assets/skybox/west.png';
import SKYBOX_UP from '../../assets/skybox/up.png';
import SKYBOX_DOWN from '../../assets/skybox/down.png';
export default class BlueMap {
constructor(element, dataRoot) {
this.element = element;
this.dataRoot = dataRoot;
this.loadingNoticeElement = $('<div id="bluemap-loading" class="box">loading...</div>').appendTo($(this.element));
this.fileLoader = new FileLoader();
this.blobLoader = new FileLoader();
this.blobLoader.setResponseType('blob');
this.bufferGeometryLoader = new BufferGeometryLoader();
this.initStage();
this.locationHash = '';
this.controls = new Controls(this.camera, this.element, this.hiresScene);
this.loadSettings().then(async () => {
this.lowresTileManager = new TileManager(
this,
this.settings[this.map]['lowres']['viewDistance'],
this.loadLowresTile,
this.lowresScene,
this.settings[this.map]['lowres']['tileSize'],
{x: 0, z: 0}
);
this.hiresTileManager = new TileManager(
this,
this.settings[this.map]['hires']['viewDistance'],
this.loadHiresTile,
this.hiresScene,
this.settings[this.map]['hires']['tileSize'],
{x: 0, z: 0}
);
await this.loadHiresMaterial();
await this.loadLowresMaterial();
this.initModules();
this.start();
});
}
initModules() {
this.modules = {};
this.modules.compass = new Compass(this);
this.modules.position = new Position(this);
this.modules.mapMenu = new MapMenu(this);
this.modules.info = new Info(this);
this.modules.settings = new Settings(this);
}
changeMap(map) {
this.hiresTileManager.close();
this.lowresTileManager.close();
this.map = map;
this.controls.resetPosition();
this.lowresTileManager = new TileManager(
this,
this.settings[this.map]['lowres']['viewDistance'],
this.loadLowresTile,
this.lowresScene,
this.settings[this.map]['lowres']['tileSize'],
{x: 0, z: 0}
);
this.hiresTileManager = new TileManager(
this,
this.settings[this.map]['hires']['viewDistance'],
this.loadHiresTile,
this.hiresScene,
this.settings[this.map]['hires']['tileSize'],
{x: 0, z: 0}
);
this.lowresTileManager.update();
this.hiresTileManager.update();
document.dispatchEvent(new Event('bluemap-map-change'));
}
loadLocationHash() {
let hashVars = window.location.hash.substring(1).split(':');
if (hashVars.length >= 1){
if (this.settings[hashVars[0]] !== undefined && this.map !== hashVars[0]){
this.changeMap(hashVars[0]);
}
}
if (hashVars.length >= 3){
let x = parseInt(hashVars[1]);
let z = parseInt(hashVars[2]);
if (!isNaN(x) && !isNaN(z)){
this.controls.targetPosition.x = x + 0.5;
this.controls.targetPosition.z = z + 0.5;
}
}
if (hashVars.length >= 6){
let dir = parseFloat(hashVars[3]);
let dist = parseFloat(hashVars[4]);
let angle = parseFloat(hashVars[5]);
if (!isNaN(dir)) this.controls.targetDirection = dir;
if (!isNaN(dist)) this.controls.targetDistance = dist;
if (!isNaN(angle)) this.controls.targetAngle = angle;
this.controls.direction = this.controls.targetDirection;
this.controls.distance = this.controls.targetDistance;
this.controls.angle = this.controls.targetAngle;
this.controls.targetPosition.y = this.controls.minHeight;
this.controls.position.copy(this.controls.targetPosition);
}
if (hashVars.length >= 7){
let height = parseInt(hashVars[6]);
if (!isNaN(height)){
this.controls.minHeight = height;
this.controls.targetPosition.y = height;
this.controls.position.copy(this.controls.targetPosition);
}
}
}
start() {
this.loadingNoticeElement.remove();
this.loadLocationHash();
$(window).on('hashchange', () => {
if (this.locationHash === window.location.hash) return;
this.loadLocationHash();
});
this.update();
this.render();
this.lowresTileManager.update();
this.hiresTileManager.update();
}
update = () => {
setTimeout(this.update, 1000);
this.lowresTileManager.setPosition(this.controls.targetPosition);
this.hiresTileManager.setPosition(this.controls.targetPosition);
this.locationHash =
'#' + this.map
+ ':' + Math.floor(this.controls.targetPosition.x)
+ ':' + Math.floor(this.controls.targetPosition.z)
+ ':' + Math.round(this.controls.targetDirection * 100) / 100
+ ':' + Math.round(this.controls.targetDistance * 100) / 100
+ ':' + Math.ceil(this.controls.targetAngle * 100) / 100
+ ':' + Math.floor(this.controls.targetPosition.y);
history.replaceState(undefined, undefined, this.locationHash);
};
render = () => {
requestAnimationFrame(this.render);
if (this.controls.update()) this.updateFrame = true;
if (!this.updateFrame) return;
this.updateFrame = false;
document.dispatchEvent(new Event('bluemap-update-frame'));
this.skyboxCamera.rotation.copy(this.camera.rotation);
this.skyboxCamera.updateProjectionMatrix();
this.renderer.clear();
this.renderer.render(this.skyboxScene, this.skyboxCamera, this.renderer.getRenderTarget(), false);
this.renderer.clearDepth();
this.renderer.render(this.lowresScene, this.camera, this.renderer.getRenderTarget(), false);
if (this.camera.position.y < 400) {
this.renderer.clearDepth();
this.renderer.render(this.hiresScene, this.camera, this.renderer.getRenderTarget(), false);
}
}
handleContainerResize = () => {
this.camera.aspect = this.element.clientWidth / this.element.clientHeight;
this.camera.updateProjectionMatrix();
this.skyboxCamera.aspect = this.element.clientWidth / this.element.clientHeight;
this.skyboxCamera.updateProjectionMatrix();
this.renderer.setSize(this.element.clientWidth * this.quality, this.element.clientHeight * this.quality);
$(this.renderer.domElement)
.css('width', this.element.clientWidth)
.css('height', this.element.clientHeight);
this.updateFrame = true;
}
async loadSettings() {
return new Promise(resolve => {
this.fileLoader.load(this.dataRoot + 'settings.json', settings => {
this.settings = JSON.parse(settings);
this.maps = [];
for (let map in this.settings) {
if (this.settings.hasOwnProperty(map) && this.settings[map].enabled){
this.maps.push(map);
}
}
this.maps.sort((map1, map2) => {
var sort = this.settings[map1].ordinal - this.settings[map2].ordinal;
if (isNaN(sort)) return 0;
return sort;
});
this.map = this.maps[0];
resolve();
});
});
}
initStage() {
this.updateFrame = true;
this.quality = 1;
this.renderer = new WebGLRenderer({
alpha: true,
antialias: true,
sortObjects: false,
preserveDrawingBuffer: true,
logarithmicDepthBuffer: true,
});
this.renderer.autoClear = false;
this.camera = new PerspectiveCamera(75, this.element.scrollWidth / this.element.scrollHeight, 0.1, 10000);
this.camera.updateProjectionMatrix();
this.skyboxCamera = this.camera.clone();
this.skyboxCamera.updateProjectionMatrix();
this.skyboxScene = new Scene();
this.skyboxScene.ambient = new AmbientLight(0xffffff, 1);
this.skyboxScene.add(this.skyboxScene.ambient);
this.skyboxScene.add(this.createSkybox());
this.lowresScene = new Scene();
this.lowresScene.ambient = new AmbientLight(0xffffff, 0.6);
this.lowresScene.add(this.lowresScene.ambient);
this.lowresScene.sunLight = new DirectionalLight(0xccccbb, 0.7);
this.lowresScene.sunLight.position.set(1, 5, 3);
this.lowresScene.add(this.lowresScene.sunLight);
this.hiresScene = new Scene();
this.hiresScene.ambient = new AmbientLight(0xffffff, 1);
this.hiresScene.add(this.hiresScene.ambient);
this.hiresScene.sunLight = new DirectionalLight(0xccccbb, 0.2);
this.hiresScene.sunLight.position.set(1, 5, 3);
this.hiresScene.add(this.hiresScene.sunLight);
this.element.append(this.renderer.domElement);
this.handleContainerResize();
$(window).resize(this.handleContainerResize);
}
createSkybox() {
let geometry = new CubeGeometry(10, 10, 10);
let material = [
new MeshBasicMaterial({
map: new TextureLoader().load(SKYBOX_SOUTH),
side: BackSide
}),
new MeshBasicMaterial({
map: new TextureLoader().load(SKYBOX_NORTH),
side: BackSide
}),
new MeshBasicMaterial({
map: new TextureLoader().load(SKYBOX_UP),
side: BackSide
}),
new MeshBasicMaterial({
map: new TextureLoader().load(SKYBOX_DOWN),
side: BackSide
}),
new MeshBasicMaterial({
map: new TextureLoader().load(SKYBOX_EAST),
side: BackSide
}),
new MeshBasicMaterial({
map: new TextureLoader().load(SKYBOX_WEST),
side: BackSide
})
];
return new Mesh(geometry, material);
}
async loadHiresMaterial() {
return new Promise(resolve => {
this.fileLoader.load(this.dataRoot + 'textures.json', textures => {
textures = JSON.parse(textures);
let materials = [];
for (let i = 0; i < textures['textures'].length; i++) {
let t = textures['textures'][i];
let material = new MeshLambertMaterial({
transparent: t['transparent'],
alphaTest: 0.01,
depthWrite: true,
depthTest: true,
blending: NormalBlending,
vertexColors: VertexColors,
side: FrontSide,
wireframe: false
});
let texture = new Texture();
texture.image = stringToImage(t['texture']);
texture.premultiplyAlpha = false;
texture.generateMipmaps = false;
texture.magFilter = NearestFilter;
texture.minFilter = NearestFilter;
texture.wrapS = ClampToEdgeWrapping;
texture.wrapT = ClampToEdgeWrapping;
texture.flipY = false;
texture.needsUpdate = true;
texture.flatShading = true;
material.map = texture;
material.needsUpdate = true;
materials[i] = material;
}
this.hiresMaterial = materials;
resolve();
});
});
}
async loadLowresMaterial() {
this.lowresMaterial = new MeshLambertMaterial({
transparent: false,
depthWrite: true,
depthTest: true,
vertexColors: VertexColors,
side: FrontSide,
wireframe: false
});
}
async loadHiresTile(tileX, tileZ) {
let path = this.dataRoot + this.map + '/hires/';
path += pathFromCoords(tileX, tileZ);
path += '.json';
return new Promise((resolve, reject) => {
this.bufferGeometryLoader.load(path, geometry => {
let object = new Mesh(geometry, this.hiresMaterial);
let tileSize = this.settings[this.map]['hires']['tileSize'];
let translate = this.settings[this.map]['hires']['translate'];
let scale = this.settings[this.map]['hires']['scale'];
object.position.set(tileX * tileSize.x + translate.x, 0, tileZ * tileSize.z + translate.z);
object.scale.set(scale.x, 1, scale.z);
resolve(object);
}, () => {
}, reject);
});
}
async loadLowresTile(tileX, tileZ) {
let path = this.dataRoot + this.map + '/lowres/';
path += pathFromCoords(tileX, tileZ);
path += '.json';
return new Promise((reslove, reject) => {
this.bufferGeometryLoader.load(path, geometry => {
let object = new Mesh(geometry, this.lowresMaterial);
let tileSize = this.settings[this.map]['lowres']['tileSize'];
let translate = this.settings[this.map]['lowres']['translate'];
let scale = this.settings[this.map]['lowres']['scale'];
object.position.set(tileX * tileSize.x + translate.x, 0, tileZ * tileSize.z + translate.z);
object.scale.set(scale.x, 1, scale.z);
reslove(object);
}, () => {
}, reject);
})
}
// ###### UI ######
alert(content) {
let alertBox = $('#alert-box');
if (alertBox.length === 0){
alertBox = $('<div id="alert-box"></div>').appendTo(this.element);
}
let displayAlert = () => {
let alert = $(`<div class="alert box" style="display: none;"><div class="alert-close-button"></div>${content}</div>`).appendTo(alertBox);
alert.find('.alert-close-button').click(() => {
alert.fadeOut(200, () => alert.remove());
});
alert.fadeIn(200);
};
let oldAlerts = alertBox.find('.alert');
if (oldAlerts.length > 0){
alertBox.fadeOut(200, () => {
alertBox.html('');
alertBox.show();
displayAlert();
});
} else {
displayAlert();
}
}
}

View File

@ -0,0 +1,297 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the 'Software'), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Softwarevent.
*
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARevent.
*/
import $ from 'jquery';
import {
Euler,
Raycaster,
Vector2,
Vector3,
MOUSE
} from 'three';
import { Vector2_ZERO } from './utils.js';
export default class Controls {
static KEYS = {
LEFT: 37,
UP: 38,
RIGHT: 39,
DOWN: 40,
ORBIT: MOUSE.RIGHT,
MOVE: MOUSE.LEFT
};
static STATES = {
NONE: -1,
ORBIT: 0,
MOVE: 1,
};
/**
* targetHeightScene and cameraHeightScene are scenes of objects that are checked via raycasting for a height for the target and the camera
*/
constructor(camera, element, heightScene) {
this.settings = {
zoom: {
min: 10,
max: 2000,
speed: 1.5,
smooth: 0.2,
},
move: {
speed: 1.75,
smooth: 0.3,
smoothY: 0.075,
},
tilt: {
max: Math.PI / 2.1,
speed: 1.5,
smooth: 0.3,
},
rotate: {
speed: 1.5,
smooth: 0.3,
}
};
this.camera = camera;
this.element = element;
this.heightScene = heightScene;
this.minHeight = 0;
this.raycaster = new Raycaster();
this.rayDirection = new Vector3(0, -1, 0);
this.resetPosition();
this.mouse = new Vector2(0, 0);
this.lastMouse = new Vector2(0, 0);
this.deltaMouse = new Vector2(0, 0);
//variables used to calculate with (to prevent object creation every update)
this.orbitRot = new Euler(0, 0, 0, 'YXZ');
this.cameraPosDelta = new Vector3(0, 0, 0);
this.moveDelta = new Vector2(0, 0);
this.keyStates = {}
this.state = Controls.STATES.NONE;
let canvas = $(this.element).find('canvas').get(0);
window.addEventListener('contextmenu', event => {
event.preventDefault();
}, false);
canvas.addEventListener('mousedown', this.onMouseDown, false);
window.addEventListener('mousemove', this.onMouseMove, false);
window.addEventListener('mouseup', this.onMouseUp, false);
canvas.addEventListener('wheel', this.onMouseWheel, false);
window.addEventListener('keydown', this.onKeyDown, false);
window.addEventListener('keyup', this.onKeyUp, false);
this.camera.position.set(0, 1000, 0);
this.camera.lookAt(this.position);
this.camera.updateProjectionMatrix();
}
resetPosition() {
this.position = new Vector3(0, 70, 0);
this.targetPosition = new Vector3(0, 70, 0);
this.distance = 5000;
this.targetDistance = 1000;
this.direction = 0;
this.targetDirection = 0;
this.angle = 0;
this.targetAngle = 0;
}
update() {
this.updateMouseMoves();
let changed = false;
let zoomLerp = (this.distance - 100) / 200;
if (zoomLerp < 0) zoomLerp = 0;
if (zoomLerp > 1) zoomLerp = 1;
this.targetPosition.y = 300 * zoomLerp + this.minHeight * (1 - zoomLerp);
this.position.x += (this.targetPosition.x - this.position.x) * this.settings.move.smooth;
this.position.y += (this.targetPosition.y - this.position.y) * this.settings.move.smoothY;
this.position.z += (this.targetPosition.z - this.position.z) * this.settings.move.smooth;
this.distance += (this.targetDistance - this.distance) * this.settings.zoom.smooth;
let deltaDir = (this.targetDirection - this.direction) * this.settings.rotate.smooth;
this.direction += deltaDir;
changed = changed || Math.abs(deltaDir) > 0.001;
let max = Math.min(this.settings.tilt.max, this.settings.tilt.max - Math.pow(((this.distance - this.settings.zoom.min) / (this.settings.zoom.max - this.settings.zoom.min)) * Math.pow(this.settings.tilt.max, 4), 1/4));
if (this.targetAngle > max) this.targetAngle = max;
if (this.targetAngle < 0.01) this.targetAngle = 0.001;
let deltaAngle = (this.targetAngle - this.angle) * this.settings.tilt.smooth;
this.angle += deltaAngle;
changed = changed || Math.abs(deltaAngle) > 0.001;
let last = this.camera.position.x + this.camera.position.y + this.camera.position.z;
this.orbitRot.set(this.angle, this.direction, 0);
this.cameraPosDelta.set(0, this.distance, 0).applyEuler(this.orbitRot);
this.camera.position.set(this.position.x + this.cameraPosDelta.x, this.position.y + this.cameraPosDelta.y, this.position.z + this.cameraPosDelta.z);
let move = last - (this.camera.position.x + this.camera.position.y + this.camera.position.z);
changed = changed || Math.abs(move) > 0.001;
if (changed) {
this.camera.lookAt(this.position);
this.camera.updateProjectionMatrix();
this.updateHeights();
}
return changed;
}
updateHeights() {
//TODO: this can be performance-improved by only intersecting the correct tile?
let rayStart = new Vector3(this.targetPosition.x, 300, this.targetPosition.z);
this.raycaster.set(rayStart, this.rayDirection);
this.raycaster.near = 1;
this.raycaster.far = 300;
let intersects = this.raycaster.intersectObjects(this.heightScene.children);
if (intersects.length > 0){
this.minHeight = intersects[0].point.y;
//this.targetPosition.y = this.minHeight;
} else {
//this.targetPosition.y = 0;
}
rayStart.set(this.camera.position.x, 300, this.camera.position.z);
this.raycaster.set(rayStart, this.rayDirection);
intersects.length = 0;
intersects = this.raycaster.intersectObjects(this.heightScene.children);
if (intersects.length > 0){
if (intersects[0].point.y > this.minHeight){
this.minHeight = intersects[0].point.y;
}
}
};
updateMouseMoves = () => {
this.deltaMouse.set(this.lastMouse.x - this.mouse.x, this.lastMouse.y - this.mouse.y);
this.moveDelta.x = 0;
this.moveDelta.y = 0;
if (this.keyStates[Controls.KEYS.UP]){
this.moveDelta.y -= 20;
}
if (this.keyStates[Controls.KEYS.DOWN]){
this.moveDelta.y += 20;
}
if (this.keyStates[Controls.KEYS.LEFT]){
this.moveDelta.x -= 20;
}
if (this.keyStates[Controls.KEYS.RIGHT]){
this.moveDelta.x += 20;
}
if (this.state === Controls.STATES.MOVE) {
if (this.deltaMouse.x === 0 && this.deltaMouse.y === 0) return;
this.moveDelta.copy(this.deltaMouse);
}
if (this.moveDelta.x !== 0 || this.moveDelta.y !== 0) {
this.moveDelta.rotateAround(Vector2_ZERO, -this.direction);
this.targetPosition.set(
this.targetPosition.x + (this.moveDelta.x * this.distance / this.element.clientHeight * this.settings.move.speed),
this.targetPosition.y,
this.targetPosition.z + (this.moveDelta.y * this.distance / this.element.clientHeight * this.settings.move.speed)
);
}
if (this.state === Controls.STATES.ORBIT) {
this.targetDirection += (this.deltaMouse.x / this.element.clientHeight * Math.PI);
this.targetAngle += (this.deltaMouse.y / this.element.clientHeight * Math.PI);
}
this.lastMouse.copy(this.mouse);
};
onMouseWheel = event => {
if (event.deltaY > 0) {
this.targetDistance *= this.settings.zoom.speed;
} else if (event.deltaY < 0) {
this.targetDistance /= this.settings.zoom.speed;
}
if (this.targetDistance < this.settings.zoom.min) this.targetDistance = this.settings.zoom.min;
if (this.targetDistance > this.settings.zoom.max) this.targetDistance = this.settings.zoom.max;
}
onMouseMove = event => {
this.mouse.set(event.clientX, event.clientY);
if (this.state !== Controls.STATES.NONE){
event.preventDefault();
}
}
onMouseDown = event => {
if (this.state !== Controls.STATES.NONE) return;
switch (event.button) {
case Controls.KEYS.MOVE :
this.state = Controls.STATES.MOVE;
event.preventDefault();
break;
case Controls.KEYS.ORBIT :
this.state = Controls.STATES.ORBIT;
event.preventDefault();
break;
}
}
onMouseUp = event => {
if (this.state === Controls.STATES.NONE) return;
switch (event.button) {
case Controls.KEYS.MOVE :
if (this.state === Controls.STATES.MOVE) this.state = Controls.STATES.NONE;
break;
case Controls.KEYS.ORBIT :
if (this.state === Controls.STATES.ORBIT) this.state = Controls.STATES.NONE;
break;
}
}
onKeyDown = event => {
this.keyStates[event.keyCode] = true;
}
onKeyUp = e => {
this.keyStates[event.keyCode] = false;
}
}

View File

@ -0,0 +1,59 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
export default class Tile {
isLoading = false;
disposed = false;
model = null;
constructor(scene, x, z) {
this.scene = scene;
this.x = x;
this.z = z;
}
setModel(model) {
this.disposeModel();
if (model) {
this.model = model;
this.scene.add(model);
//console.log('Added tile:', this.x, this.z);
}
}
disposeModel() {
this.disposed = true;
if (this.model) {
this.scene.remove(this.model);
this.model.geometry.dispose();
delete this.model;
//console.log('Removed tile:', this.x, this.z);
}
}
}

View File

@ -0,0 +1,184 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import { Vector2 } from 'three';
import Tile from './Tile.js';
import { hashTile } from './utils.js';
export default class TileManager {
constructor(blueMap, viewDistance, tileLoader, scene, tileSize, position) {
this.blueMap = blueMap;
this.viewDistance = viewDistance;
this.tileLoader = tileLoader;
this.scene = scene;
this.tileSize = new Vector2(tileSize.x, tileSize.z);
this.tile = new Vector2(position.x, position.z);
this.lastTile = this.tile.clone();
this.closed = false;
this.currentlyLoading = 0;
this.updateTimeout = null;
this.tiles = {};
}
setPosition(center) {
this.tile.set(center.x, center.z).divide(this.tileSize).floor();
if (!this.tile.equals(this.lastTile) && !this.closed) {
this.update();
this.lastTile.copy(this.tile);
}
}
update() {
if (this.closed) return;
// free a loader so if there was an error loading a tile we don't get stuck forever with the blocked loading process
this.currentlyLoading--;
if (this.currentlyLoading < 0) this.currentlyLoading = 0;
this.removeFarTiles();
this.loadCloseTiles();
}
removeFarTiles() {
let keys = Object.keys(this.tiles);
for (let i = 0; i < keys.length; i++) {
if (!this.tiles.hasOwnProperty(keys[i])) continue;
let tile = this.tiles[keys[i]];
let vd = this.viewDistance;
if (
tile.x + vd < this.tile.x ||
tile.x - vd > this.tile.x ||
tile.z + vd < this.tile.y ||
tile.z - vd > this.tile.y
) {
tile.disposeModel();
delete this.tiles[keys[i]];
}
}
}
removeAllTiles() {
let keys = Object.keys(this.tiles);
for (let i = 0; i < keys.length; i++) {
if (!this.tiles.hasOwnProperty(keys[i])) continue;
let tile = this.tiles[keys[i]];
tile.disposeModel();
delete this.tiles[keys[i]];
}
}
close() {
this.closed = true;
this.removeAllTiles();
}
loadCloseTiles() {
if (this.closed) return;
if (this.currentlyLoading < 8) {
if (!this.loadNextTile()) return;
}
if (this.updateTimeout) clearTimeout(this.updateTimeout);
this.updateTimeout = setTimeout(() => this.loadCloseTiles(), 0);
}
loadNextTile() {
let x = 0;
let z = 0;
let d = 1;
let m = 1;
while (m < this.viewDistance * 2) {
while (2 * x * d < m) {
if (this.tryLoadTile(this.tile.x + x, this.tile.y + z)) return true;
x = x + d;
}
while (2 * z * d < m) {
if (this.tryLoadTile(this.tile.x + x, this.tile.y + z)) return true;
z = z + d;
}
d = -1 * d;
m = m + 1;
}
return false;
}
tryLoadTile(x, z) {
if (this.closed) return false;
let tileHash = hashTile(x, z);
let tile = this.tiles[tileHash];
if (tile !== undefined) return false;
tile = new Tile(this.scene, x, z);
tile.isLoading = true;
this.currentlyLoading++;
this.tiles[tileHash] = tile;
this.tileLoader.call(this.blueMap, x, z)
.then(model => {
tile.isLoading = false;
if (tile.disposed || this.closed) {
model.geometry.dispose();
tile.disposeModel();
delete this.tiles[tileHash];
return;
}
this.tiles[tileHash] = tile;
tile.setModel(model);
this.blueMap.updateFrame = true;
this.currentlyLoading--;
if (this.currentlyLoading < 0) this.currentlyLoading = 0;
}).catch(error => {
tile.isLoading = false;
tile.disposeModel();
this.currentlyLoading--;
//console.log("Failed to load tile: ", x, z);
});
return true;
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,53 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import $ from 'jquery';
import { getTopLeftElement } from './Module.js';
import COMPASS from '../../../assets/compass.svg';
export default class Compass {
constructor(blueMap) {
this.blueMap = blueMap;
$('#bluemap-compass').remove();
this.element = $(`<div id="bluemap-compass" class="button"><img id="bluemap-compass-needle" src="${COMPASS}" /></div>`).appendTo(getTopLeftElement(blueMap));
this.needle = $('#bluemap-compass-needle');
$(document).on('bluemap-update-frame', this.onBlueMapUpdateFrame);
$(this.element).click(this.onClick);
}
onBlueMapUpdateFrame = () => {
this.needle.css('transform', `rotate(${this.blueMap.controls.direction}rad)`);
}
onClick = () => {
this.blueMap.controls.targetDirection = 0;
this.blueMap.controls.direction = this.blueMap.controls.direction % (Math.PI * 2);
if (this.blueMap.controls.direction < -Math.PI) this.blueMap.controls.direction += Math.PI * 2;
if (this.blueMap.controls.direction > Math.PI) this.blueMap.controls.direction -= Math.PI * 2;
}
}

View File

@ -0,0 +1,49 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import $ from 'jquery';
import { getTopRightElement } from './Module.js';
export default class Info {
constructor(blueMap) {
this.blueMap = blueMap;
const parent = getTopRightElement(blueMap);
$('#bluemap-info').remove();
this.elementInfo = $('<div id="bluemap-info" class="button"></div>').appendTo(parent);
this.elementInfo.click(this.onClick);
}
onClick = () => {
this.blueMap.alert(
'<h1>Info</h1>' +
'Visit BlueMap on <a href="https://github.com/BlueMap-Minecraft">GitHub</a>!<br>' +
'BlueMap works best with <a href="https://www.google.com/chrome/">Chrome</a>.<br>' +
'<h2>Controls</h2>' +
'Leftclick-drag with your mouse or use the arrow-keys to navigate.<br>' +
'Rightclick-drag with your mouse to rotate your view.<br>' +
'Scroll to zoom.<br>'
);
}
}

View File

@ -0,0 +1,63 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import $ from 'jquery';
import { getTopLeftElement } from './Module.js';
export default class MapMenu {
constructor(blueMap) {
this.bluemap = blueMap;
const maps = this.bluemap.settings;
$('#bluemap-mapmenu').remove();
this.element = $(`<div id="bluemap-mapmenu" class="dropdown-container"><span class="selection">${maps[this.bluemap.map].name}</span></div>`).appendTo(getTopLeftElement(blueMap));
const dropdown = $('<div class="dropdown"></div>').appendTo(this.element);
this.maplist = $('<ul></ul>').appendTo(dropdown);
for (let mapId in maps) {
if (!maps.hasOwnProperty(mapId)) continue;
const map = maps[mapId];
if (!map.enabled) continue;
$(`<li map="${mapId}">${map.name}</li>`).appendTo(this.maplist);
}
this.maplist.find('li[map=' + this.bluemap.map + ']').hide();
this.maplist.find('li[map]').click(this.onMapClick);
$(document).on('bluemap-map-change', this.onBlueMapMapChange);
}
onMapClick = event => {
const map = $(event.target).attr('map');
this.bluemap.changeMap(map);
}
onBlueMapMapChange = () => {
this.maplist.find('li').show();
this.maplist.find('li[map=' + this.bluemap.map + ']').hide();
this.element.find('.selection').html(this.bluemap.settings[this.bluemap.map].name);
}
}

View File

@ -0,0 +1,47 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import $ from 'jquery';
// ###### Modules ######
export const getTopRightElement = blueMap => {
let element = $('#bluemap-topright');
if (element.length === 0){
element = $('<div id="bluemap-topright" class="box"></div>').appendTo(blueMap.element);
}
return element;
};
export const getTopLeftElement = blueMap => {
let element = $('#bluemap-topleft');
if (element.length === 0){
element = $('<div id="bluemap-topleft" class="box"></div>').appendTo(blueMap.element);
}
return element;
};

View File

@ -0,0 +1,47 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import $ from 'jquery';
import { getTopLeftElement } from './Module.js';
export default class Position {
constructor(blueMap) {
this.blueMap = blueMap;
const parent = getTopLeftElement(blueMap);
$('.bluemap-position').remove();
this.elementX = $('<div class="bluemap-position pos-x">0</div>').appendTo(parent);
//this.elementY = $('<div class="bluemap-position pos-y">0</div>').appendTo(parent);
this.elementZ = $('<div class="bluemap-position pos-z">0</div>').appendTo(parent);
$(document).on('bluemap-update-frame', this.onBlueMapUpdateFrame);
}
onBlueMapUpdateFrame = () => {
this.elementX.html(Math.floor(this.blueMap.controls.targetPosition.x));
//this.elementY.html(this.blueMap.controls.targetPosition.y === 0 ? '-' : Math.floor(this.blueMap.controls.targetPosition.y));
this.elementZ.html(Math.floor(this.blueMap.controls.targetPosition.z));
}
}

View File

@ -0,0 +1,122 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import $ from 'jquery';
import { Math as Math3 } from 'three';
import { getTopRightElement } from './Module.js';
import GEAR from '../../../assets/gear.svg';
export default class Settings {
constructor(blueMap) {
this.blueMap = blueMap;
const parent = getTopRightElement(blueMap);
$('#bluemap-settings').remove();
this.elementMenu = $('<div id="bluemap-settings-container" style="display: none"></div>').appendTo(parent);
this.elementSettings = $(`<div id="bluemap-settings" class="button"><img src="${GEAR}" /></div>`).appendTo(parent);
this.elementSettings.click(this.onSettingsClick);
/* Quality */
this.elementQuality = $(
'<div id="bluemap-settings-quality" class="dropdown-container"><span class="selection">Quality: <span>Normal</span></span><div class="dropdown"><ul>' +
'<li quality="2">High</li>' +
'<li quality="1" style="display: none">Normal</li>' +
'<li quality="0.75">Fast</li>' +
'</ul></div></div>'
).prependTo(this.elementMenu);
this.elementQuality.find('li[quality]').click(this.onQualityClick);
this.elementRenderDistance = $('<div id="bluemap-settings-render-distance" class="dropdown-container"></div>').prependTo(this.elementMenu);
this.init();
$(document).on('bluemap-map-change', this.init);
}
init = () => {
this.defaultHighRes = this.blueMap.hiresTileManager.viewDistance;
this.defaultLowRes = this.blueMap.lowresTileManager.viewDistance;
this.elementRenderDistance.html(
'<span class="selection">View Distance: <span>' + this.blueMap.hiresTileManager.viewDistance + '</span></span>' +
'<div class="dropdown">' +
'<input type="range" min="0" max="100" step="1" value="' + this.renderDistanceToPct(this.blueMap.hiresTileManager.viewDistance, this.defaultHighRes) + '" />' +
'</div>'
);
this.slider = this.elementRenderDistance.find('input');
this.slider.on('change input', this.onViewDistanceSlider);
};
onViewDistanceSlider = () => {
this.blueMap.hiresTileManager.viewDistance = this.pctToRenderDistance(parseFloat(this.slider.val()), this.defaultHighRes);
this.blueMap.lowresTileManager.viewDistance = this.pctToRenderDistance(parseFloat(this.slider.val()), this.defaultLowRes);
this.elementRenderDistance.find('.selection > span').html(Math.round(this.blueMap.hiresTileManager.viewDistance * 10) / 10);
this.blueMap.lowresTileManager.update();
this.blueMap.hiresTileManager.update();
};
onQualityClick = (event) => {
const target = event.target
const desc = $(target).html();
this.blueMap.quality = parseFloat($(target).attr('quality'));
this.elementQuality.find('li').show();
this.elementQuality.find(`li[quality="${this.blueMap.quality}"]`).hide();
this.elementQuality.find('.selection > span').html(desc);
this.blueMap.handleContainerResize();
};
onSettingsClick = () => {
if (this.elementMenu.css('display') === 'none'){
this.elementSettings.addClass('active');
} else {
this.elementSettings.removeClass('active');
}
this.elementMenu.animate({
width: 'toggle'
}, 200);
}
pctToRenderDistance(value, defaultValue) {
let max = defaultValue * 5;
if (max > 20) max = 20;
return Math3.mapLinear(value, 0, 100, 1, max);
}
renderDistanceToPct(value, defaultValue) {
let max = defaultValue * 5;
if (max > 20) max = 20;
return Math3.mapLinear(value, 1, max, 0, 100);
}
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,66 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the 'Software'), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import { Vector2, Vector3 } from 'three';
export const stringToImage = string => {
let image = document.createElementNS('http://www.w3.org/1999/xhtml', 'img');
image.src = string;
return image;
};
export const pathFromCoords = (x, z) => {
let path = 'x';
path += splitNumberToPath(x);
path += 'z';
path += splitNumberToPath(z);
path = path.substring(0, path.length - 1);
return path;
};
export const splitNumberToPath = num => {
let path = '';
if (num < 0) {
num = -num;
path += '-';
}
let s = num.toString();
for (let i = 0; i < s.length; i++) {
path += s.charAt(i) + '/';
}
return path;
};
export const hashTile = (x, z) => `x${x}z${z}`;
export const Vector2_ZERO = new Vector2(0, 0);
export const Vector3_ZERO = new Vector3(0, 0, 0);

View File

@ -1,6 +1,8 @@
// global variable to enable access through browser console import $ from 'jquery';
var blueMap; import BlueMap from './libs/BlueMap.js';
$(document).ready(function () { import '../style/style.scss';
blueMap = new BlueMap($("#map-container")[0], "data/");
$(document).ready(() => {
window.blueMap = new BlueMap($('#map-container')[0], 'data/');
}); });

View File

@ -0,0 +1,71 @@
#alert-box {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
align-content: center;
justify-content: center;
flex-wrap: wrap;
flex-direction: column;
pointer-events: none;
h1 {
font-size: 1.4rem;
font-weight: bold;
margin: 0;
padding: 15px;
text-align: center;
}
h2 {
font-size: 1.2rem;
font-weight: bold;
margin: 0;
padding: 15px 0 5px 0;
text-align: left;
}
a {
color: #333333;
text-decoration: underline;
&:hover {
color: #888888;
}
}
.alert {
position: relative;
pointer-events: all;
margin: 10px;
padding: 10px;
.alert-close-button {
/*position: absolute;
top: 5px;
right: 5px;
*/
margin: -10px -10px 0px 0px;
padding: 0 0 5px 5px;
float: right;
width: 15px;
height: 15px;
line-height: 15px;
font-weight: bold;
font-size: 15px;
color: #333333;
&::after {
content: 'x';
}
&:hover {
color: #dd3333;
}
}
}
}

View File

@ -0,0 +1,8 @@
#bluemap-compass {
width: 30px;
height: 30px;
&:hover #bluemap-compass-needle {
filter: invert(1);
}
}

View File

@ -0,0 +1,10 @@
#bluemap-info {
width: 30px;
height: 30px;
text-align: center;
&::after {
content: 'i';
font-weight: bold;
}
}

View File

@ -0,0 +1,7 @@
#bluemap-mapmenu {
width: 200px;
.selection, .dropdown li {
padding-left: 10px;
}
}

View File

@ -0,0 +1,41 @@
#bluemap-settings {
width: 30px;
height: 30px;
}
#bluemap-settings.active:not(:hover) {
background-color: #dddddd;
}
#bluemap-settings:hover > img {
filter: invert(1);
}
#bluemap-settings-container {
display: flex;
white-space: nowrap;
}
#bluemap-settings-quality {
width: 150px;
height: 30px;
}
#bluemap-settings-quality .selection, #bluemap-settings-quality .dropdown li {
padding-left: 10px;
}
#bluemap-settings-render-distance {
width: 180px;
}
#bluemap-settings-render-distance .selection {
padding-left: 10px;
}
#bluemap-settings-render-distance input {
width: calc(100% - 20px);
margin: 10px;
padding: 0;
}

View File

@ -1,290 +0,0 @@
html, body {
margin: 0;
padding: 0;
font-size: 15px;
line-height: 15px;
font-family: Verdana,Helvetica,Arial,sans-serif;
color: #333333;
background-color: #dddddd;
}
.box {
color: #333333;
background-color: white;
box-shadow: 0px 1px 4px 0px rgba(50, 50, 50, 0.8);
}
.button {
background-color: white;
cursor: pointer;
}
.button:hover {
background-color: #333333;
color: #dddddd;
}
.dropdown-container {
background-color: white;
position: relative;
transition: all 0.3s;
}
.dropdown-container:hover {
background-color: #dddddd;
}
.dropdown-container > .dropdown {
position: absolute;
background-color: white;
color: #333333;
width: 100%;
overflow: hidden;
transition: all 0.3s;
max-height: 0px;
}
.dropdown-container:hover > .dropdown {
max-height: 200px;
border-color: #ddddddFF;
}
.dropdown-container > .dropdown > ul {
margin: 0;
padding: 0;
list-style: none;
}
.dropdown-container > .dropdown > ul > li {
cursor: pointer;
}
.dropdown-container > .dropdown > ul > li:hover {
color: #dddddd;
background-color: #333333;
}
#alert-box {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
align-content: center;
justify-content: center;
flex-wrap: wrap;
flex-direction: column;
pointer-events: none;
}
#alert-box h1 {
font-size: 1.4rem;
font-weight: bold;
margin: 0;
padding: 15px;
text-align: center;
}
#alert-box h2 {
font-size: 1.2rem;
font-weight: bold;
margin: 0;
padding: 15px 0 5px 0;
text-align: left;
}
#alert-box a {
color: #333333;
text-decoration: underline;
}
#alert-box a:hover {
color: #888888;
}
#alert-box .alert {
position: relative;
pointer-events: all;
margin: 10px;
padding: 10px;
}
#alert-box .alert .alert-close-button {
/*position: absolute;
top: 5px;
right: 5px;
*/
margin: -10px -10px 0px 0px;
padding: 0 0 5px 5px;
float: right;
width: 15px;
height: 15px;
line-height: 15px;
font-weight: bold;
font-size: 15px;
color: #333333;
}
#alert-box .alert .alert-close-button::after {
content: 'x';
}
#alert-box .alert .alert-close-button:hover {
color: #dd3333;
}
#map-container {
position: absolute;
width: 100%;
height: 100%;
background-color: black;
overflow: hidden;
}
#map-container canvas {
width: 100%;
height: 100%;
}
#bluemap-loading {
position: absolute;
width: 200px;
line-height: 20px;
padding: 20px 0;
top: calc(50% - 31px);
left: calc(50% - 101px);
text-align: center;
}
#bluemap-topright {
position: absolute;
top: 10px;
right: 10px;
line-height: 30px;
display: flex;
}
#bluemap-topright > *:not(:last-child) {
border-right: solid 1px #dddddd;
}
#bluemap-topleft {
position: absolute;
top: 10px;
left: 10px;
line-height: 30px;
display: flex;
}
#bluemap-topleft > *:not(:last-child) {
border-right: solid 1px #dddddd;
}
#bluemap-mapmenu {
width: 200px;
}
#bluemap-mapmenu .selection, #bluemap-mapmenu .dropdown li {
padding-left: 10px;
}
#bluemap-compass {
width: 30px;
height: 30px;
}
#bluemap-compass:hover #bluemap-compass-needle {
filter: invert(1);
}
.bluemap-position {
position: relative;
width: 60px;
height: 30px;
padding: 0 5px 0 25px;
}
.bluemap-position::before {
position: absolute;
left: 7px;
color: #888888;
}
.bluemap-position.pos-x::before {
content: "x:";
}
.bluemap-position.pos-y::before {
content: "y:";
}
.bluemap-position.pos-z::before {
content: "z:";
}
#bluemap-settings {
width: 30px;
height: 30px;
}
#bluemap-settings.active:not(:hover) {
background-color: #dddddd;
}
#bluemap-settings:hover > img {
filter: invert(1);
}
#bluemap-settings-container {
display: flex;
white-space: nowrap;
}
#bluemap-settings-quality {
width: 150px;
height: 30px;
}
#bluemap-settings-quality .selection, #bluemap-settings-quality .dropdown li {
padding-left: 10px;
}
#bluemap-settings-render-distance {
width: 180px;
}
#bluemap-settings-render-distance .selection {
padding-left: 10px;
}
#bluemap-settings-render-distance input {
width: calc(100% - 20px);
margin: 10px;
padding: 0;
}
#bluemap-info {
width: 30px;
height: 30px;
text-align: center;
}
#bluemap-info::after {
content: 'i';
font-weight: bold;
}

View File

@ -0,0 +1,153 @@
@import "./modules/alertbox.scss";
@import "./modules/compass.scss";
@import "./modules/info.scss";
@import "./modules/mapmenu.scss";
@import "./modules/settings.scss";
html, body {
margin: 0;
padding: 0;
font-size: 15px;
line-height: 15px;
font-family: Verdana,Helvetica,Arial,sans-serif;
color: #333333;
background-color: #dddddd;
}
.box {
color: #333333;
background-color: white;
box-shadow: 0px 1px 4px 0px rgba(50, 50, 50, 0.8);
}
.button {
background-color: white;
cursor: pointer;
}
.button:hover {
background-color: #333333;
color: #dddddd;
}
.dropdown-container {
background-color: white;
position: relative;
transition: all 0.3s;
}
.dropdown-container:hover {
background-color: #dddddd;
}
.dropdown-container > .dropdown {
position: absolute;
background-color: white;
color: #333333;
width: 100%;
overflow: hidden;
transition: all 0.3s;
max-height: 0px;
}
.dropdown-container:hover > .dropdown {
max-height: 200px;
border-color: #ddddddFF;
}
.dropdown-container > .dropdown > ul {
margin: 0;
padding: 0;
list-style: none;
}
.dropdown-container > .dropdown > ul > li {
cursor: pointer;
}
.dropdown-container > .dropdown > ul > li:hover {
color: #dddddd;
background-color: #333333;
}
#map-container {
position: absolute;
width: 100%;
height: 100%;
background-color: black;
overflow: hidden;
}
#map-container canvas {
width: 100%;
height: 100%;
}
#bluemap-loading {
position: absolute;
width: 200px;
line-height: 20px;
padding: 20px 0;
top: calc(50% - 31px);
left: calc(50% - 101px);
text-align: center;
}
#bluemap-topright {
position: absolute;
top: 10px;
right: 10px;
line-height: 30px;
display: flex;
}
#bluemap-topright > *:not(:last-child) {
border-right: solid 1px #dddddd;
}
#bluemap-topleft {
position: absolute;
top: 10px;
left: 10px;
line-height: 30px;
display: flex;
}
#bluemap-topleft > *:not(:last-child) {
border-right: solid 1px #dddddd;
}
.bluemap-position {
position: relative;
width: 60px;
height: 30px;
padding: 0 5px 0 25px;
}
.bluemap-position::before {
position: absolute;
left: 7px;
color: #888888;
}
.bluemap-position.pos-x::before {
content: "x:";
}
.bluemap-position.pos-y::before {
content: "y:";
}
.bluemap-position.pos-z::before {
content: "z:";
}

14
BlueMapCore/tsconfig.json Normal file
View File

@ -0,0 +1,14 @@
{
"compilerOptions": {
"sourceMap": true,
"module": "esnext",
"target": "es6",
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"moduleResolution": "node",
"strict": true,
"esModuleInterop": true,
"allowJs": true
},
"include": [ "src" ]
}

View File

@ -0,0 +1,79 @@
const path = require('path')
const fs = require('fs')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const WEBROOT_PATH = path.resolve(__dirname, 'src/main/webroot')
const BUILD_PATH = path.resolve(__dirname, 'build/generated/webroot')
// folder with a generated world to render in the dev server
const WORLD_DATA_PATH = path.resolve(__dirname, 'build/generated/world')
module.exports = {
mode: 'production',
devtool: 'source-map',
entry: {
'bluemap': path.resolve(WEBROOT_PATH, 'js/site.js'),
},
output: {
path: BUILD_PATH,
filename: 'js/[name].js',
},
devServer: {
contentBase: WORLD_DATA_PATH,
compress: true,
port: 8080,
hot: true,
},
plugins: [
new MiniCssExtractPlugin({
filename: 'style/[name].css?[hash]',
}),
new HtmlWebpackPlugin({
template: path.resolve(WEBROOT_PATH, 'index.html'),
hash: true,
}),
],
resolve: {
extensions: ['.js', '.css', '.scss'],
},
module: {
rules: [
// Transpile JavaScript source files using TypeScript engine
{
test: /\.(js|ts)$/,
include: /src/,
use: 'ts-loader',
},
// Just import normal css files
{
test: /\.css$/,
include: /src/,
use: [
{ loader: MiniCssExtractPlugin.loader },
{ loader: 'css-loader' },
],
},
// Converts scss files into css to use within custom elements
{
test: /\.scss$/,
include: /src/,
use: [
{ loader: MiniCssExtractPlugin.loader },
{ loader: 'css-loader' },
{ loader: 'sass-loader' },
],
},
// Load additional files
{
test: /\.(png|svg)(\?.*$|$)/,
include: /src/,
use: [
{
loader: 'file-loader',
options: { name: 'assets/[name].[ext]?[hash]' },
},
],
},
],
},
}

View File

@ -15,7 +15,7 @@ Easy:
`git clone https://github.com/BlueMap-Minecraft/BlueMap.git` `git clone https://github.com/BlueMap-Minecraft/BlueMap.git`
### Build ### Build
In order to build BlueMap you simply need to run the `./gradlew shadowJar` command. In order to build BlueMap you simply need to run the `./gradlew build` command.
You can find the compiled JAR file in `./build/libs` You can find the compiled JAR file in `./build/libs`
### Issues / Suggestions ### Issues / Suggestions

View File

@ -31,7 +31,7 @@ dependencies {
compile project(':BlueMapSponge') compile project(':BlueMapSponge')
} }
shadowJar { assemble.dependsOn shadowJar {
baseName = 'BlueMap' baseName = 'BlueMap'
version = null version = null
classifier = null classifier = null