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
run: ./gradlew test
- name: Build with Gradle
run: ./gradlew shadowJar
run: ./gradlew build
- uses: actions/upload-artifact@v1
with:
name: artifact

5
.gitignore vendored
View File

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

View File

@ -1,24 +1,57 @@
plugins {
id 'com.moowork.node' version '1.3.1'
}
dependencies {
compile 'com.google.guava:guava:21.0'
compile 'com.google.code.gson:gson:2.8.0'
compile 'org.apache.commons:commons-lang3:3.5'
compile group: 'commons-io', name: 'commons-io', version: '2.6'
compile 'com.flowpowered:flow-math:1.0.3'
compile 'ninja.leaping.configurate:configurate-hocon:3.3'
compile 'ninja.leaping.configurate:configurate-gson:3.3'
compile 'ninja.leaping.configurate:configurate-yaml:3.3'
compile 'com.github.Querz:NBT:4.0'
testCompile 'junit:junit:4.12'
compile 'com.google.guava:guava:21.0'
compile 'com.google.code.gson:gson:2.8.0'
compile 'org.apache.commons:commons-lang3:3.5'
compile group: 'commons-io', name: 'commons-io', version: '2.6'
compile 'com.flowpowered:flow-math:1.0.3'
compile 'ninja.leaping.configurate:configurate-hocon:3.3'
compile 'ninja.leaping.configurate:configurate-gson:3.3'
compile 'ninja.leaping.configurate:configurate-yaml:3.3'
compile 'com.github.Querz:NBT:4.0'
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) {
from fileTree('src/main/webroot')
from fileTree('build/generated/webroot/')
archiveName 'webroot.zip'
destinationDir(file('src/main/resources/'))
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) {
from fileTree('src/main/resourceExtensions')
archiveName 'resourceExtensions.zip'
@ -27,5 +60,5 @@ task zipResourceExtensions(type: Zip) {
}
//always update the zip before build
compileJava.dependsOn(zipWebroot)
compileJava.dependsOn(zipResourceExtensions)
processResources.dependsOn(buildWebroot)
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

@ -34,38 +34,38 @@
public class TileRenderer {
private HiresModelManager hiresModelManager;
private LowresModelManager lowresModelManager;
public TileRenderer(HiresModelManager hiresModelManager, LowresModelManager lowresModelManager) {
this.hiresModelManager = hiresModelManager;
this.lowresModelManager = lowresModelManager;
}
/**
* Renders the provided WorldTile (only) if the world is generated
* @throws IOException If an IO-Exception occurs during the render
*/
public void render(WorldTile tile) throws IOException {
//check if the region is generated before rendering, don't render if it's not generated
//check if the region is generated before rendering, don't render if it's not generated
AABB area = hiresModelManager.getTileRegion(tile);
if (!tile.getWorld().isAreaGenerated(area)) return;
HiresModel hiresModel = hiresModelManager.render(tile);
lowresModelManager.render(hiresModel);
}
/**
* Saves changes to disk
*/
public void save(){
lowresModelManager.save();
}
public HiresModelManager getHiresModelManager() {
return hiresModelManager;
}
public LowresModelManager getLowresModelManager() {
return lowresModelManager;
}
}

View File

@ -1,22 +1,10 @@
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8>
<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>
<body>
<div id="map-container"></div>
<script type="text/javascript" src="js/site.js"></script>
</body>
<head>
<meta charset="utf-8">
<title>BlueMap</title>
</head>
<body>
<div id="map-container"></div>
</body>
</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
var blueMap;
import $ from 'jquery';
import BlueMap from './libs/BlueMap.js';
$(document).ready(function () {
blueMap = new BlueMap($("#map-container")[0], "data/");
import '../style/style.scss';
$(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

@ -1,6 +1,6 @@
![title-banner](https://bluecolored.de/paste/bluemap-title.jpg)
BlueMap is a tool that generates 3d-maps of your Minecraft worlds and displays them in your browser. Take a look at [this demo](https://bluecolored.de/bluemap). It is really easy to set up - almost plug-and-play - if you use the integrated web-server (optional).
BlueMap is a tool that generates 3d-maps of your Minecraft worlds and displays them in your browser. Take a look at [this demo](https://bluecolored.de/bluemap). It is really easy to set up - almost plug-and-play - if you use the integrated web-server (optional).
The Sponge-Plugin automatically updates your map as soon as something changes in your world, as well as rendering newly generated terrain and managing the render-tasks.
@ -15,21 +15,21 @@ Easy:
`git clone https://github.com/BlueMap-Minecraft/BlueMap.git`
### 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`
### Issues / Suggestions
You found a bug, have another issue or a suggestion? Please create an issue [here](https://github.com/BlueMap-Minecraft/BlueMap/issues)!
### Contributing
You are welcome to contribute!
You are welcome to contribute!
Just create a pull request with your changes :)
## Using the CLI
BlueMap can be used on the command-line, to render your Minecraft-Worlds *(Currently supported versions: 1.12 - 1.14)*.
Use `java -jar bluemap.jar` and BlueMap will generate a default config in the current working directory. You then can configure your maps and even the webserver as you wish. Then, re-run the command and BlueMap will render all the configured maps for you and start the webserver if you turned it on in the config.
To only run the webserver, just don't define any maps in the config.
To only run the webserver, just don't define any maps in the config.
You can use `-c <config-file>` on the command-line to define a different configuration-file.
@ -39,7 +39,7 @@ BlueMap is mostly plug-and-play. Just install it like every other Sponge-Plugin
**Before BlueMap can render anything,** it needs one more thing: resources! To render all the block-models, BlueMap makes use of the default minecraft-resources. Since they are property of mojang i can not include them in the plugin. Fortunately BlueMap can download them from mojangs servers for you, but you need to explicitly agree to this in the config! Simply change the `accept-download: false` setting to `accept-download: true`, and run the `/bluemap reload` command.
After downloading the resources, BlueMap will start updating the configured worlds. To render the whole world for a start, you can use this command `/bluemap render [world]`.
After downloading the resources, BlueMap will start updating the configured worlds. To render the whole world for a start, you can use this command `/bluemap render [world]`.
Then, head over to `http://<your-server-ip>:8100/` and you should see your map! *(If there is only black, you might have to wait a little until BlueMap has rendered enough of the map. You can also try to zoom in: the hires-models are saved first)*

View File

@ -6,7 +6,7 @@ plugins {
allprojects {
repositories {
mavenCentral()
maven {
maven {
url 'https://jitpack.io'
}
maven {
@ -21,7 +21,7 @@ allprojects {
compileJava.options.compilerArgs.add '-parameters'
compileTestJava.options.compilerArgs.add '-parameters'
apply plugin: 'java'
apply plugin: 'com.github.johnrengelman.shadow'
}
@ -31,14 +31,14 @@ dependencies {
compile project(':BlueMapSponge')
}
shadowJar {
assemble.dependsOn shadowJar {
baseName = 'BlueMap'
version = null
classifier = null
}
jar {
jar {
manifest {
attributes 'Main-Class' : "de.bluecolored.bluemap.cli.BlueMapCLI"
}
}
}