Introduce shaders to improve rendering, add night and rework the ui

This commit is contained in:
Blue (Lukas Rieger) 2020-03-04 20:31:00 +01:00
parent a0ae675af1
commit ba2092b614
49 changed files with 1669 additions and 615 deletions

View File

@ -216,6 +216,11 @@ private void createElementFace(ExtendedModel model, Direction faceDir, Vector3f
float blockLight = bl.getBlockLightLevel();
float sunLight = bl.getSunLightLevel();
if (faceDir == Direction.UP) {
blockLight = block.getBlockLightLevel();
sunLight = block.getSunLightLevel();
}
f1.setC1(color);
f1.setC2(color);
f1.setC3(color);

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="30px" height="30px" viewBox="0 0 30 30" enable-background="new 0 0 30 30" xml:space="preserve">
<path fill="#333333" d="M25.004,15c0,0.807-0.75,1.461-1.676,1.461H6.671c-0.925,0-1.674-0.654-1.674-1.461l0,0
c0-0.807,0.749-1.461,1.674-1.461h16.657C24.254,13.539,25.004,14.193,25.004,15L25.004,15z"/>
<path fill="#333333" d="M25.004,20.706c0,0.807-0.75,1.461-1.676,1.461H6.671c-0.925,0-1.674-0.654-1.674-1.461l0,0
c0-0.807,0.749-1.461,1.674-1.461h16.657C24.254,19.245,25.004,19.899,25.004,20.706L25.004,20.706z"/>
<path fill="#333333" d="M25.004,9.294c0,0.806-0.75,1.46-1.676,1.46H6.671c-0.925,0-1.674-0.654-1.674-1.46l0,0
c0-0.807,0.749-1.461,1.674-1.461h16.657C24.254,7.833,25.004,8.487,25.004,9.294L25.004,9.294z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="30px" height="30px" viewBox="0 0 30 30" enable-background="new 0 0 30 30" xml:space="preserve">
<path fill="#333333" d="M17.011,19.722c-3.778-1.613-5.533-5.982-3.921-9.76c0.576-1.348,1.505-2.432,2.631-3.204
c-3.418-0.243-6.765,1.664-8.186,4.992c-1.792,4.197,0.159,9.053,4.356,10.844c3.504,1.496,7.462,0.377,9.717-2.476
C20.123,20.465,18.521,20.365,17.011,19.722z"/>
<circle fill="#333333" cx="5.123" cy="7.64" r="1.196"/>
<circle fill="#333333" cx="23.178" cy="5.249" r="1.195"/>
<circle fill="#333333" cx="20.412" cy="13.805" r="1.195"/>
<circle fill="#333333" cx="25.878" cy="23.654" r="1.195"/>
</svg>

After

Width:  |  Height:  |  Size: 975 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

View File

@ -24,31 +24,24 @@
*/
import $ from 'jquery';
import {
AmbientLight,
BackSide,
BufferGeometryLoader,
ClampToEdgeWrapping,
CubeGeometry,
DirectionalLight,
SphereGeometry,
FileLoader,
FrontSide,
Mesh,
MeshBasicMaterial,
NearestFilter,
NearestMipmapLinearFilter,
NearestMipMapLinearFilter,
PerspectiveCamera,
Scene,
ShaderMaterial,
Texture,
TextureLoader,
VertexColors,
WebGLRenderer, ShaderMaterial,
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 UI from './ui/UI.js';
import Controls from './Controls.js';
import TileManager from './TileManager.js';
@ -57,22 +50,29 @@ import HIRES_VERTEX_SHADER from './shaders/HiresVertexShader.js';
import HIRES_FRAGMENT_SHADER from './shaders/HiresFragmentShader.js';
import LOWRES_VERTEX_SHADER from './shaders/LowresVertexShader.js';
import LOWRES_FRAGMENT_SHADER from './shaders/LowresFragmentShader.js';
import SKY_VERTEX_SHADER from './shaders/SkyVertexShader.js';
import SKY_FRAGMENT_SHADER from './shaders/SkyFragmentShader.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.element = $('<div class="bluemap-container"></div>').appendTo(element)[0];
this.dataRoot = dataRoot;
this.loadingNoticeElement = $('<div id="bluemap-loading" class="box">loading...</div>').appendTo($(this.element));
this.hiresViewDistance = 160;
this.lowresViewDistance = 3200;
this.targetSunLightStrength = 1;
this.sunLightStrength = {
value: this.targetSunLightStrength
};
this.mobSpawnOverlay = {
value: false
};
this.ui = new UI(this);
this.loadingNoticeElement = $('<div>loading...</div>').appendTo($(this.element));
window.onerror = this.onLoadError;
this.fileLoader = new FileLoader();
@ -88,26 +88,18 @@ export default class BlueMap {
await this.loadHiresMaterial();
await this.loadLowresMaterial();
this.changeMap(this.map);
this.changeMap(this.maps[0]);
this.initModules();
this.ui.load();
this.start();
}).catch(error => {
this.onLoadError(error.toString());
console.error(error);
});
}
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) {
if (this.map === map) return;
if (this.hiresTileManager !== undefined) this.hiresTileManager.close();
if (this.lowresTileManager !== undefined) this.lowresTileManager.close();
@ -118,8 +110,6 @@ export default class BlueMap {
z: this.settings[this.map]["startPos"]["z"]
};
this.targetSunLightStrength = 1;
this.controls.setTileSize(this.settings[this.map]['hires']['tileSize']);
this.controls.resetPosition();
this.controls.targetPosition.set(startPos.x, this.controls.targetPosition.y, startPos.z);
@ -127,7 +117,7 @@ export default class BlueMap {
this.lowresTileManager = new TileManager(
this,
this.settings[this.map]['lowres']['viewDistance'],
this.lowresViewDistance,
this.loadLowresTile,
this.lowresScene,
this.settings[this.map]['lowres']['tileSize'],
@ -136,7 +126,7 @@ export default class BlueMap {
this.hiresTileManager = new TileManager(
this,
this.settings[this.map]['hires']['viewDistance'],
this.hiresViewDistance,
this.loadHiresTile,
this.hiresScene,
this.settings[this.map]['hires']['tileSize'],
@ -233,9 +223,9 @@ export default class BlueMap {
if (this.controls.update()) this.updateFrame = true;
//update lighting
let targetLight = 1;
if (this.camera.position.y < 400){
targetLight = this.targetSunLightStrength;
let targetLight = this.targetSunLightStrength;
if (this.camera.position.y > 400){
targetLight = Math.max(targetLight, 0.5);
}
if (Math.abs(targetLight - this.sunLightStrength.value) > 0.01) {
this.sunLightStrength.value += (targetLight - this.sunLightStrength.value) * 0.1;
@ -295,7 +285,6 @@ export default class BlueMap {
return sort;
});
this.map = this.maps[0];
resolve();
});
});
@ -305,11 +294,6 @@ export default class BlueMap {
this.updateFrame = true;
this.quality = 1;
this.targetSunLightStrength = 1;
this.sunLightStrength = {
value: this.targetSunLightStrength
};
this.renderer = new WebGLRenderer({
alpha: true,
antialias: true,
@ -331,40 +315,22 @@ export default class BlueMap {
this.lowresScene = new Scene();
this.hiresScene = new Scene();
this.element.append(this.renderer.domElement);
$(this.renderer.domElement).addClass("map-canvas").appendTo(this.element);
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
})
];
let geometry = new SphereGeometry(10, 10, 10);
let material = new ShaderMaterial({
uniforms: {
sunlightStrength: this.sunLightStrength
},
vertexShader: SKY_VERTEX_SHADER,
fragmentShader: SKY_FRAGMENT_SHADER,
side: BackSide
});
return new Mesh(geometry, material);
}
@ -385,7 +351,7 @@ export default class BlueMap {
texture.anisotropy = 1;
texture.generateMipmaps = opaque || transparent;
texture.magFilter = NearestFilter;
texture.minFilter = texture.generateMipmaps ? NearestMipmapLinearFilter : NearestFilter;
texture.minFilter = texture.generateMipmaps ? NearestMipMapLinearFilter : NearestFilter;
texture.wrapS = ClampToEdgeWrapping;
texture.wrapT = ClampToEdgeWrapping;
texture.flipY = false;
@ -397,7 +363,8 @@ export default class BlueMap {
type: 't',
value: texture
},
sunlightStrength: this.sunLightStrength
sunlightStrength: this.sunLightStrength,
mobSpawnOverlay: this.mobSpawnOverlay
};
let material = new ShaderMaterial({
@ -423,12 +390,10 @@ export default class BlueMap {
}
async loadLowresMaterial() {
let uniforms = {
sunlightStrength: this.sunLightStrength
};
this.lowresMaterial = new ShaderMaterial({
uniforms: uniforms,
uniforms: {
sunlightStrength: this.sunLightStrength
},
vertexShader: LOWRES_VERTEX_SHADER,
fragmentShader: LOWRES_FRAGMENT_SHADER,
transparent: false,
@ -486,7 +451,7 @@ export default class BlueMap {
this.loadingNoticeElement.remove();
this.toggleAlert(undefined, `
<div style="max-width: 500px">
<div style="max-width: 50rem">
<h1>Error</h1>
<p style="color: red; font-family: monospace">${message}</p>
</div>
@ -496,14 +461,14 @@ export default class BlueMap {
// ###### UI ######
toggleAlert(id, content) {
let alertBox = $('#alert-box');
let alertBox = $(this.element).find('.alert-box');
if (alertBox.length === 0){
alertBox = $('<div id="alert-box"></div>').appendTo(this.element);
alertBox = $('<div class="alert-box"></div>').appendTo(this.ui.hud);
}
let displayAlert = () => {
let alert = $(`<div class="alert box" data-alert-id="${id}" style="display: none;"><div class="alert-close-button"></div>${content}</div>`).appendTo(alertBox);
alert.find('.alert-close-button').click(() => {
let alert = $(`<div class="alert" data-alert-id="${id}" style="display: none;"><div class="close-button"></div>${content}</div>`).appendTo(alertBox);
alert.find('.close-button').click(() => {
alert.stop().fadeOut(200, () => alert.remove());
});
alert.stop().fadeIn(200);

View File

@ -32,10 +32,10 @@ import { hashTile } from './utils.js';
export default class TileManager {
constructor(blueMap, viewDistance, tileLoader, scene, tileSize, position) {
this.blueMap = blueMap;
this.viewDistance = viewDistance;
this.tileSize = new Vector2(tileSize.x, tileSize.z);
this.setViewDistance(viewDistance);
this.tileLoader = tileLoader;
this.scene = scene;
this.tileSize = new Vector2(tileSize.x, tileSize.z);
this.tile = new Vector2(0, 0);
this.tile.set(position.x, position.z).divide(this.tileSize).floor();
@ -48,6 +48,11 @@ export default class TileManager {
this.tiles = {};
}
setViewDistance(viewDistance){
this.viewDistanceX = viewDistance / this.tileSize.x;
this.viewDistanceZ = viewDistance / this.tileSize.y;
}
setPosition(center) {
this.tile.set(center.x, center.z).divide(this.tileSize).floor();
@ -75,16 +80,19 @@ export default class TileManager {
let tile = this.tiles[keys[i]];
let vd = this.viewDistance;
let vdx = this.viewDistanceX;
let vdz = this.viewDistanceZ;
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.x + vdx < this.tile.x ||
tile.x - vdx > this.tile.x ||
tile.z + vdz < this.tile.y ||
tile.z - vdz > this.tile.y
) {
tile.disposeModel();
delete this.tiles[keys[i]];
this.blueMap.updateFrame = true;
}
}
}
@ -98,6 +106,8 @@ export default class TileManager {
tile.disposeModel();
delete this.tiles[keys[i]];
}
this.blueMap.updateFrame = true;
}
close() {
@ -122,7 +132,7 @@ export default class TileManager {
let d = 1;
let m = 1;
while (m < this.viewDistance * 2) {
while (m < Math.max(this.viewDistanceX, this.viewDistanceZ) * 2 + 1) {
while (2 * x * d < m) {
if (this.tryLoadTile(this.tile.x + x, this.tile.y + z)) return true;
x = x + d;
@ -140,6 +150,8 @@ export default class TileManager {
tryLoadTile(x, z) {
if (this.closed) return false;
if (Math.abs(x - this.tile.x) > this.viewDistanceX) return false;
if (Math.abs(z - this.tile.z) > this.viewDistanceZ) return false;
let tileHash = hashTile(x, z);

View File

@ -24,24 +24,28 @@
*/
import $ from 'jquery';
import { getTopLeftElement } from './Module.js';
import Button from '../ui/Button.js';
import COMPASS from '../../../assets/compass.svg';
export default class Compass {
export default class Compass extends Button {
constructor(blueMap) {
super(undefined, undefined, COMPASS);
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);
}
createElement(){
let element = super.createElement();
element.click(this.onClick);
return element;
}
onBlueMapUpdateFrame = () => {
this.needle.css('transform', `rotate(${this.blueMap.controls.direction}rad)`);
this.elements.forEach(element => {
element.find("img").css('transform', `rotate(${this.blueMap.controls.direction}rad)`);
});
};
onClick = () => {

View File

@ -24,40 +24,39 @@
*/
import $ from 'jquery';
import { getTopLeftElement } from './Module.js';
import Dropdown from "../ui/Dropdown";
export default class MapMenu {
constructor(blueMap) {
this.bluemap = blueMap;
export default class MapSelection extends Dropdown {
constructor(bluemap) {
super(undefined);
super.onChange = this.onChangeMap;
this.bluemap = bluemap;
//add maps
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.addOption(mapId, map.name);
}
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);
createElement() {
let element = super.createElement();
element.addClass("map-selection");
return element;
}
onChangeMap = value => {
this.bluemap.changeMap(value);
};
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);
this.select(this.bluemap.map);
};
}

View File

@ -24,26 +24,34 @@
*/
import $ from 'jquery';
import { getTopRightElement } from './Module.js';
import ToggleButton from '../ui/ToggleButton.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);
import BURGER from '../../../assets/burger.svg';
export default class MenuButton extends ToggleButton {
constructor(menu) {
super(undefined, false, undefined, BURGER);
this.menu = menu;
this.menu.element.on('menu-close menu-open', this.updateMenuState);
}
onClick = () => {
this.blueMap.toggleAlert('bluemap-info',
'<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>'
);
createElement(){
let element = super.createElement();
element.click(this.onMenuClick);
return element;
}
updateMenuState = () => {
this.selected = this.menu.isOpen();
this.update();
};
onMenuClick = () => {
if (this.selected){
this.menu.open();
} else {
this.menu.close();
}
};
}

View File

@ -24,38 +24,34 @@
*/
import $ from 'jquery';
import { getTopLeftElement } from './Module.js';
import Element from "../ui/Element";
export default class Position {
constructor(blueMap) {
export default class Position extends Element {
constructor(blueMap, axis) {
super();
this.blueMap = blueMap;
this.axis = axis;
$('.bluemap-position').remove();
this.elements = [
this.createPositionElement('x'),
null,//this.elementY = this.createPositionElement('y');
this.createPositionElement('z'),
];
$(document).on('bluemap-update-frame', this.onBlueMapUpdateFrame);
$(document).on('bluemap-update-frame', this.update);
}
/** Creates the position display */
createPositionElement(type) {
const parent = getTopLeftElement(this.blueMap);
const element = $(`<div class="bluemap-position" data-pos="${type}"><input type="number" value="0" /></div>`)
.appendTo(parent)
.children()
.first();
element.on('input', this.onInput(type));
element.on('keydown', this.onKeyDown);
createElement(){
let element = super.createElement();
element.addClass("position");
element.attr("data-axis", this.axis);
let inputElement = $('<input type="number" value="0" />').appendTo(element);
inputElement.on('input', this.onInput);
inputElement.on('keydown', this.onKeyDown);
return element;
}
onInput = type => event => {
onInput = event => {
const value = Number(event.target.value);
if (!isNaN(value)) {
this.blueMap.controls.targetPosition[type] = value;
this.blueMap.controls.targetPosition[this.axis] = value;
this.update();
}
};
@ -63,14 +59,11 @@ export default class Position {
event.stopPropagation();
};
onBlueMapUpdateFrame = () => {
const { x, y, z } = this.blueMap.controls.targetPosition;
const values = [ z, y, x ];
for (let element of this.elements) {
const value = Math.floor(values.pop());
if (element) {
element.val(value);
}
}
update = () => {
const val = Math.floor(this.blueMap.controls.targetPosition[this.axis]);
this.elements.forEach(element => {
element.find("input").val(val);
});
};
}

View File

@ -1,122 +0,0 @@
/*
* 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 data-quality="2">High</li>' +
'<li data-quality="1" style="display: none">Normal</li>' +
'<li data-quality="0.75">Fast</li>' +
'</ul></div></div>'
).prependTo(this.elementMenu);
this.elementQuality.find('li[data-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('data-quality'));
this.elementQuality.find('li').show();
this.elementQuality.find(`li[data-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);
}
}

View File

@ -26,8 +26,10 @@
const HIRES_FRAGMENT_SHADER = `
uniform sampler2D texture;
uniform float sunlightStrength;
uniform bool mobSpawnOverlay;
varying vec3 vPosition;
varying vec3 vWorldPosition;
varying vec3 vNormal;
varying vec2 vUv;
varying vec3 vColor;
@ -35,12 +37,34 @@ varying float vAo;
varying float vSunlight;
varying float vBlocklight;
vec4 lerp(vec4 v1, vec4 v2, float amount){
return v1 * (1.0 - amount) + v2 * amount;
}
vec3 lerp(vec3 v1, vec3 v2, float amount){
return v1 * (1.0 - amount) + v2 * amount;
}
bool mobSpawnColor() {
if (vBlocklight < 7.1){
float cross1 = vUv.x - vUv.y;
float cross2 = vUv.x - (1.0 - vUv.y);
return cross1 < 0.05 && cross1 > -0.05 || cross2 < 0.05 && cross2 > -0.05;
}
return false;
}
void main() {
vec4 color = texture2D(texture, vUv);
if (color.a == 0.0) discard;
//apply vertex-color
color.rgb *= vColor;
//mob spawn overlay
if (mobSpawnOverlay && mobSpawnColor()){
color.rgb = lerp(vec3(1.0, 0.0, 0.0), color.rgb, 0.25);
}
//apply ao
color.rgb *= vAo;
@ -48,7 +72,7 @@ void main() {
//apply light
float light = max(vSunlight * sunlightStrength, vBlocklight);
color.rgb *= light / 15.0;
gl_FragColor = color;
}
`;

View File

@ -29,6 +29,7 @@ attribute float sunlight;
attribute float blocklight;
varying vec3 vPosition;
varying vec3 vWorldPosition;
varying vec3 vNormal;
varying vec2 vUv;
varying vec3 vColor;
@ -38,6 +39,7 @@ varying float vBlocklight;
void main() {
vPosition = position;
vWorldPosition = (vec4(position, 1) * modelMatrix).xyz;
vNormal = normal;
vUv = uv;
vColor = color;
@ -47,8 +49,9 @@ void main() {
gl_Position =
projectionMatrix *
modelViewMatrix *
vec4(position, 1);
viewMatrix *
modelMatrix *
vec4(position, 1);
}
`;

View File

@ -0,0 +1,42 @@
/*
* 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.
*/
const SKY_FRAGMENT_SHADER = `
uniform float sunlightStrength;
varying vec3 vPosition;
void main() {
vec4 dayColor = vec4(0.49, 0.67, 1.0, 1.0);
vec4 nightColor = vec4(0.0, 0.02, 0.05, 1.0);
vec4 color = dayColor * sunlightStrength + nightColor * (1.0 - sunlightStrength);
color.rgb *= (clamp(vPosition.y, -0.02, 0.02) + 0.02) * 25.0;
gl_FragColor = color;
}
`;
export default SKY_FRAGMENT_SHADER;

View File

@ -0,0 +1,39 @@
/*
* 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.
*/
const SKY_VERTEX_SHADER = `
varying vec3 vPosition;
void main() {
vPosition = position;
gl_Position =
projectionMatrix *
modelViewMatrix *
vec4(position, 1);
}
`;
export default SKY_VERTEX_SHADER;

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 Element from './Element.js';
export default class Button extends Element {
constructor(label, onClick, icon){
super();
this.label = label;
this.onClickListener = onClick;
this.icon = icon;
}
createElement() {
let element = super.createElement();
element.addClass("button");
element.click(this.onClickEvent);
if (this.label !== undefined) {
$(`<div class="label">${this.label}</div>`).appendTo(element);
}
if (this.icon !== undefined){
element.addClass("icon");
$(`<img src="${this.icon}" />`).appendTo(element);
}
return element;
}
onClickEvent = () => {
if (this.onClickListener !== undefined && this.onClickListener !== null) {
this.onClickListener(this);
}
}
}

View File

@ -0,0 +1,130 @@
/*
* 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 Element from './Element.js';
export default class Dropdown extends Element {
constructor(onChange, minWidth) {
super();
this.minWidth = minWidth;
this.value = null;
this.options = [];
this.onChange = onChange;
$(window).on('click', this.closeAll);
}
addOption(value, label, select) {
this.options.push({
value: value,
label: label,
select: select
});
if (this.value === null || select){
this.select(value);
}
}
createElement(){
let element = super.createElement();
element.addClass("dropdown");
let headerElement = $('<div class="header"></div>').appendTo(element);
let selectElement = $('<div class="select" style="display: none"></div>').appendTo(element);
headerElement.click(this.toggleEvent(element));
if (this.minWidth !== undefined){
this.element.addClass("sized");
this.element.css("min-width", this.minWidth);
}
this.options.forEach(option => {
let optionElement = $(`<div class="ui-element option" data-value="${option.value}">${option.label}</div>`).appendTo(selectElement);
optionElement.on('click', this.selectButtonEvent(option.value, optionElement));
if (this.value === option.value){
optionElement.addClass('selected');
headerElement.html('');
headerElement.append(optionElement.clone().off());
}
});
return element;
}
toggleEvent = element => event => {
let select = element.find(".select");
let open = select.css("display") !== "none";
this.closeAll();
if (!open) {
select.stop(true).slideDown(200);
element.addClass("open");
event.stopPropagation();
}
};
closeAll = () => {
this.elements.forEach(element => {
element.removeClass("open");
element.find(".select:not(:hidden)").stop(true).slideUp(200);
});
};
select = value => {
this.value = value;
this.elements.forEach(element => {
let selectElement = element.find(".select");
selectElement.find('.selected').removeClass('selected');
let option = selectElement.find(`.option[data-value='${value}']`);
option.addClass('selected');
let headerElement = element.find(".header");
headerElement.html('');
headerElement.append(option.clone().off());
});
};
selectButtonEvent = (value, option) => event => {
this.select(value);
//close
option.parents(".select").slideUp(200);
if (event !== undefined) event.stopPropagation();
if (this.onChange !== undefined && this.onChange !== null){
this.onChange(this.value, this);
}
};
}

View File

@ -24,24 +24,16 @@
*/
import $ from 'jquery';
// ###### Modules ######
export default class Element {
export const getTopRightElement = blueMap => {
let element = $('#bluemap-topright');
if (element.length === 0){
element = $('<div id="bluemap-topright" class="box"></div>').appendTo(blueMap.element);
constructor() {
this.elements = [];
}
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);
createElement() {
let newElement = $('<div class="ui-element"></div>');
this.elements.push(newElement);
return newElement;
}
return element;
};
}

View File

@ -0,0 +1,42 @@
/*
* 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 Element from './Element.js';
export default class Label extends Element {
constructor(label){
super();
this.label = label;
}
createElement() {
let element = super.createElement();
element.addClass("label");
element.html(this.label);
return element;
}
}

View File

@ -0,0 +1,71 @@
/*
* 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 Element from './Element.js';
export default class Menu {
constructor(){
this.element = $('<div class="menu closed"><h1>Menu</h1></div>');
this.content = $('<div class="content"></div>').appendTo(this.element);
this.closeButton = $('<div class="close-button"></div>').appendTo(this.element);
this.children = [];
this.closeButton.click(this.close);
}
addElement(element){
this.children.push(element);
}
update() {
this.content.html("");
this.children.forEach(child => {
this.content.append(child.createElement());
});
}
isOpen = () => {
return !this.element.hasClass('closed');
};
toggleOpen = () => {
this.element.toggleClass('closed');
};
open = () => {
this.element.removeClass('closed');
this.element.trigger('menu-open');
};
close = () => {
this.element.addClass('closed');
this.element.trigger('menu-close');
};
}

View File

@ -0,0 +1,58 @@
/*
* 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 Element from './Element.js';
export default class Separator extends Element {
constructor(greedy = false){
super();
this.greedy = greedy;
}
createElement() {
let element = super.createElement();
element.addClass("separator");
if (this.greedy) element.addClass("greedy");
return element;
}
isGreedy(){
return this.greedy;
}
setGreedy(greedy){
this.greedy = greedy;
this.elements.forEach(element => {
if (this.greedy) {
element.addClass("greedy");
} else {
element.removeClass("greedy");
}
});
}
}

View File

@ -0,0 +1,86 @@
/*
* 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 Element from './Element.js';
export default class Slider extends Element {
constructor(min = 0, max = 1, step = 0.01, value, onChange, minWidth){
super();
this.min = min;
this.max = max;
this.step = step;
if (value === undefined) value = min;
this.value = value;
this.onChangeListener = onChange;
this.minWidth = minWidth;
}
createElement() {
let element = super.createElement();
element.addClass("slider");
if (this.minWidth !== undefined){
element.addClass("sized");
element.css("min-width", this.minWidth);
}
let slider = $(`<input type="range" min="${this.min}" max="${this.max}" step="${this.step}" value="${this.value}">`).appendTo(element);
slider.on('input change', this.onChangeEvent(slider));
$(`<div class="label">-</div>`).appendTo(element);
this.update();
return element;
}
getValue() {
return this.value;
}
update(){
this.elements.forEach(element => {
let label = element.find(".label");
let slider = element.find("input");
slider.val(this.value);
label.html(Math.round(this.value * 100) / 100);
});
}
onChangeEvent = slider => () => {
this.value = slider.val();
this.update();
if (this.onChangeListener !== undefined && this.onChangeListener !== null) {
this.onChangeListener(this);
}
}
}

View File

@ -0,0 +1,74 @@
/*
* 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 Button from './Button.js';
export default class ToggleButton extends Button {
constructor(label, selected, onChange, icon){
super(label, undefined, icon);
this.selected = selected;
this.onChangeListener = onChange;
}
createElement() {
let element = super.createElement();
element.addClass("toggle-button");
if (this.selected) element.addClass("selected");
$('<div class="switch"></div>').appendTo(element);
element.click(this.onClick);
return element;
}
isSelected(){
return this.selected;
}
toggle(){
this.selected = !this.selected;
this.update();
}
update(){
this.elements.forEach(element => {
if (this.selected)
element.addClass("selected");
else
element.removeClass("selected");
});
}
onClick = () => {
this.toggle();
if (this.onChangeListener !== undefined && this.onChangeListener !== null){
this.onChangeListener(this);
}
};
}

View File

@ -0,0 +1,55 @@
/*
* 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 Element from './Element.js';
export default class Toolbar {
constructor(){
this.element = $('<div class="toolbar"></div>');
this.children = [];
}
addElement(element, hideOnMobile = false){
this.children.push({
element: element,
hideOnMobile: hideOnMobile
});
}
update() {
this.element.html("");
this.children.forEach(child => {
let element = child.element.createElement();
element.appendTo(this.element);
if (child.hideOnMobile){
element.addClass("mobile-hide");
}
});
}
}

View File

@ -0,0 +1,115 @@
/*
* 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 Toolbar from './Toolbar.js';
import Menu from './Menu.js';
import Dropdown from "./Dropdown";
import Separator from "./Separator";
import Label from "./Label";
import MenuButton from '../modules/MenuButton.js';
import Compass from "../modules/Compass";
import Position from "../modules/Position";
import Button from "./Button";
import Slider from "./Slider";
import ToggleButton from "./ToggleButton";
import MapSelection from "../modules/MapSeletion";
import NIGHT from '../../../assets/night.svg';
export default class UI {
constructor(blueMap) {
this.blueMap = blueMap;
this.element = $('<div class="ui"></div>').appendTo(this.blueMap.element);
this.menu = new Menu();
this.menu.element.appendTo(this.element);
this.hud = $('<div class="hud"></div>').appendTo(this.element);
this.toolbar = new Toolbar();
this.toolbar.element.appendTo(this.hud);
}
load() {
//elements
let menuButton = new MenuButton(this.menu);
let mapSelect = new MapSelection(this.blueMap);
let nightButton = new ToggleButton("night", blueMap.targetSunLightStrength < 1, button => {
this.blueMap.targetSunLightStrength = button.isSelected() ? 0.1 : 1;
}, NIGHT);
let posX = new Position(this.blueMap, 'x');
let posZ = new Position(this.blueMap, 'z');
let compass = new Compass(this.blueMap);
let mobSpawnOverlay = new ToggleButton("mob-spawnable overlay", blueMap.mobSpawnOverlay.value, button => {
this.blueMap.mobSpawnOverlay.value = button.isSelected();
this.blueMap.updateFrame = true;
});
let quality = new Dropdown(value => {
this.blueMap.quality = parseFloat(value);
this.blueMap.handleContainerResize();
});
quality.addOption("2", "high");
quality.addOption("1", "normal", true);
quality.addOption("0.5", "low");
let hiresSlider = new Slider(32, 480, 1, this.blueMap.hiresViewDistance, v => {
this.blueMap.hiresViewDistance = v.getValue();
this.blueMap.hiresTileManager.setViewDistance(this.blueMap.hiresViewDistance);
this.blueMap.hiresTileManager.update();
});
let lowresSlider = new Slider(480, 6400, 1, this.blueMap.lowresViewDistance, v => {
this.blueMap.lowresViewDistance = v.getValue();
this.blueMap.lowresTileManager.setViewDistance(this.blueMap.lowresViewDistance);
this.blueMap.lowresTileManager.update();
});
//toolbar
this.toolbar.addElement(menuButton);
this.toolbar.addElement(mapSelect);
this.toolbar.addElement(new Separator(), true);
this.toolbar.addElement(nightButton, true);
this.toolbar.addElement(new Separator(true));
this.toolbar.addElement(posX);
this.toolbar.addElement(posZ);
this.toolbar.addElement(compass);
this.toolbar.update();
//menu
this.menu.addElement(nightButton);
this.menu.addElement(mobSpawnOverlay);
this.menu.addElement(new Separator());
this.menu.addElement(new Label('render quality:'));
this.menu.addElement(quality);
this.menu.addElement(new Label('hires render-distance (blocks):'));
this.menu.addElement(hiresSlider);
this.menu.addElement(new Label('lowres render-distance (blocks):'));
this.menu.addElement(lowresSlider);
this.menu.update();
}
}

View File

@ -0,0 +1,14 @@
// colors
$normal_fg: #333;
$normal_bg: #fff;
$super_light_fg: #484848;
$super_light_bg: #ddd;
$light_fg: #666;
$light_bg: #aaa;
// breakpoints
$super-small-max: 500px;
$small-max: 800px;
$middle-max: 1200px;

View File

@ -1,67 +0,0 @@
#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 {
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

@ -1,13 +0,0 @@
#bluemap-compass {
width: 2rem;
height: 2rem;
#bluemap-compass-needle {
width: 100%;
height: 100%;
}
&:hover #bluemap-compass-needle {
filter: invert(1);
}
}

View File

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

View File

@ -1,8 +0,0 @@
#bluemap-mapmenu {
width: 15rem;
cursor: pointer;
.selection, .dropdown li {
padding-left: 10px;
}
}

View File

@ -1,32 +1,39 @@
.bluemap-position {
position: relative;
.bluemap-container .ui .ui-element.position {
flex-basis: 6rem;
flex-shrink: 1;
min-width: 4rem;
input {
width: 4rem;
height: 100%;
border: none;
outline: none;
background: transparent;
padding: 0 5px 0 25px;
font: inherit;
color: inherit;
display: flex;
// remove number spinner firefox
-moz-appearance:textfield;
height: 1rem;
// remove number spinner webkit
&::-webkit-inner-spin-button,
&::-webkit-outer-spin-button {
-webkit-appearance: none;
margin: 0;
> input {
width: calc(100% - 2rem);
height: 100%;
padding: 0 0.5rem 0 0;
font: inherit;
color: inherit;
background: transparent;
border: none;
outline: none;
// remove number spinner firefox
-moz-appearance:textfield;
// remove number spinner webkit
&::-webkit-inner-spin-button,
&::-webkit-outer-spin-button {
-webkit-appearance: none;
margin: 0;
}
}
}
&[data-pos]::before {
position: absolute;
left: 7px;
color: #888888;
content: attr(data-pos)':';
}
}
&[data-axis]::before {
padding: 0 0.2rem 0 0.5rem;
line-height: 2rem;
color: $light_fg;
content: attr(data-axis)':';
}
}

View File

@ -1,46 +0,0 @@
#bluemap-settings {
width: 2rem;
height: 2rem;
> img {
width: 100%;
height: 100%;
}
}
#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: 2rem;
}
#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,163 +1,105 @@
@import "./modules/alertbox.scss";
@import "./modules/compass.scss";
@import "./modules/info.scss";
@import "./modules/mapmenu.scss";
@import "./modules/position.scss";
@import "./modules/settings.scss";
@import "constants";
html, body {
margin: 0;
padding: 0;
font-size: 15px;
font-size: 16px;
line-height: 1rem;
font-family: Verdana,Helvetica,Arial,sans-serif;
color: #333333;
background-color: #dddddd;
color: $normal_fg;
background-color: $normal_bg;
@media (max-width: 900px) {
font-size: 17px;
@media (max-width: $small-max) {
font-size: 20px;
}
}
.box {
color: #333333;
background-color: white;
box-shadow: 0 1px 4px 0 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: background-color 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: 0;
}
.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;
}
.bluemap-container {
position: relative;
width: 100%;
height: 100%;
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: 2rem;
display: flex;
@media (max-width: 900px) {
display: none;
}
}
#bluemap-topright > *:not(:last-child) {
border-right: solid 1px #dddddd;
}
#bluemap-topleft {
position: absolute;
top: 10px;
left: 10px;
line-height: 2rem;
white-space: nowrap;
display: flex;
@media (max-width: 900px) {
> .map-canvas {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
>:last-child {
flex-grow: 1;
}
background-color: #000;
z-index: 0;
}
@media (max-width: 500px) {
flex-wrap: wrap;
> .ui {
display: flex;
align-items: stretch;
> :not(:first-child) {
flex-grow: 1;
position: relative;
width: 100%;
height: 100%;
color: $normal_fg;
filter: drop-shadow(1px 1px 3px #0008);
pointer-events: none;
z-index: 100;
> * {
pointer-events: auto;
}
>:last-child {
> .menu {
position: relative;
flex-shrink: 0;
filter: drop-shadow(1px 1px 3px #0008);
z-index: 200;
@media (max-width: $middle-max) {
position: absolute;
}
}
> .hud {
position: relative;
width: 100%;
border-top: solid 1px #dddddd;
pointer-events: none;
> * {
pointer-events: auto;
}
}
}
}
#bluemap-topleft > *:not(:last-child) {
border-right: solid 1px #dddddd;
}
@import "ui/ui";
@import "ui/element";
@import "ui/toolbar";
@import "ui/menu";
@import "ui/button";
@import "ui/slider";
@import "ui/togglebutton";
@import "ui/separator";
@import "ui/dropdown";
@import "ui/label";
@import "modules/position";

View File

@ -0,0 +1,38 @@
.bluemap-container .ui .ui-element.button {
cursor: pointer;
user-select: none;
> .label {
margin: 0 0.5rem;
}
&.icon {
flex-grow: 0;
> .label {
display: none;
}
}
&:hover {
background-color: $element_fg;
color: $element_bg;
> img {
filter: invert(1);
}
}
&:active {
background-color: $light_fg;
}
> img {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
}

View File

@ -0,0 +1,76 @@
.bluemap-container .ui .ui-element.dropdown {
padding: 0;
cursor: pointer;
overflow: visible;
user-select: none;
> .header {
height: 100%;
padding-right: 1rem;
&:hover {
background-color: $light_bg;
}
> .ui-element {
pointer-events: none;
background-color: transparent;
}
&::after {
position: absolute;
top: calc(50% - 0.2rem);
right: 0.5rem;
content: "";
width: 0;
height: 0;
border: solid;
border-width: 0.4rem 0.25rem 0.4rem 0.25rem;
border-color: $normal_fg transparent transparent transparent;
}
}
&.open > .header::after {
top: calc(50% - 0.6rem);
border-color: transparent transparent $normal_fg transparent;
}
> .select {
position: absolute;
top: calc(100% - 1px);
left: 0;
width: calc(100% - 2px);
overflow-x: hidden;
max-height: 300px;
overflow-y: auto;
border: solid 1px $light_bg;
z-index: 110;
> .option {
background-color: $super-light_bg;
&.selected {
background-color: $light_bg;
}
&:hover {
background-color: $normal_fg;
color: $normal_bg;
}
}
}
.option {
padding: 0 0.5rem;
}
}

View File

@ -0,0 +1,18 @@
$element_fg: $normal_fg;
$element_bg: $normal_bg;
.bluemap-container .ui .ui-element {
position: relative;
background-color: $element_bg;
color: $element_fg;
min-width: 2rem;
min-height: 2rem;
line-height: 2rem;
padding: 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}

View File

@ -0,0 +1,5 @@
.bluemap-container .ui .ui-element.label {
color: $light_fg;
}

View File

@ -0,0 +1,114 @@
$menu-width: 375px;
.bluemap-container .menu {
position: relative;
height: 100%;
width: $menu-width;
max-width: 100%;
background-color: $normal_bg;
overflow: hidden;
transition: width 0.2s;
&.closed {
width: 0;
}
@media (max-width: $menu-width) {
transition: opacity 0.2s;
&.closed {
opacity: 0;
width: $menu-width;
pointer-events: none;
}
}
> h1 {
position: absolute;
right: 0;
top: 0;
width: $menu-width;
height: 1.5rem;
line-height: 1.5rem;
margin: 0;
padding: 0.25rem;
text-align: center;
font-family: inherit;
font-size: 1.2rem;
font-weight: bold;
box-shadow: 0 0 5px #00000088;
z-index: 10;
@media (max-width: $menu-width) {
width: 100%;
}
}
> .close-button {
z-index: 20;
left: 0;
}
> .content {
position: absolute;
right: 0;
top: 2rem;
width: $menu-width;
height: calc(100% - 3rem);
padding-top: 0.5rem;
overflow-y: auto;
@media (max-width: $menu-width) {
width: 100%;
}
> .separator {
border-top: solid 1px $light_bg;
margin: 0.5rem 0;
}
> .label {
min-height: 0;
font-size: 0.8rem;
padding: 1rem 0.5rem 0.1rem 1rem;
line-height: 0.8rem;
}
// a little hacky to force not displaying any icon
> .toggle-button.icon {
> .label {
display: inline !important;
}
> img {
display: none;
}
> .switch {
display: block !important;
}
&.selected:not(:hover) {
background-color: $normal_bg !important;
color: $normal_fg !important;
}
&:hover {
background-color: $light_bg !important;
color: $normal_fg !important;
}
}
}
}

View File

@ -0,0 +1,10 @@
.bluemap-container .ui .ui-element.separator {
pointer-events: none;
min-width: 0;
min-height: 0;
padding: 0;
background-color: unset;
color: $element_fg;
}

View File

@ -0,0 +1,23 @@
.bluemap-container .ui .ui-element.slider {
display: flex;
align-content: stretch;
> input {
box-sizing: border-box;
width: 100%;
padding: 0;
margin: 0 0.5rem;
}
> .label {
margin: 0 0.5rem;
min-width: 4rem;
flex-grow: 0;
text-align: right;
}
}

View File

@ -0,0 +1,79 @@
.bluemap-container .ui .ui-element.toggle-button {
&:not(.icon) {
padding-right: 2.75rem;
}
&:hover,
&:active {
background-color: $light_bg;
color: $normal_fg;
}
&.icon {
&:hover,
&:active {
background-color: $light_fg;
color: $normal_bg;
}
&.selected {
background-color: $normal_fg;
color: $normal_bg;
> img {
filter: invert(1);
}
}
}
> .switch {
position: absolute;
right: 0.5rem;
top: 50%;
transform: translate(0, -50%);
height: 1rem;
width: 1.75rem;
border-radius: 1rem;
background-color: $light_fg;
transition: background-color 0.2s;
&::after {
position: absolute;
content: '';
top: 0;
left: 0;
height: 0.8rem;
width: 0.8rem;
margin: 0.1rem;
border-radius: 100%;
background-color: $light_bg;
transition: left 0.2s;
}
}
&.selected > .switch {
background-color: #008800;
&::after {
left: calc(100% - 1rem);
}
}
&.icon > .switch {
display: none;
}
}

View File

@ -0,0 +1,93 @@
.bluemap-container .ui .toolbar {
display: flex;
align-items: stretch;
//justify-content: center;
width: calc(100% - 20px);
margin: 10px;
pointer-events: none;
@media (max-width: $small-max) {
width: 100%;
margin: 0;
background-color: $super_light_bg;
> .mobile-hide {
display: none;
}
}
@media (max-width: $super-small-max) {
flex-wrap: wrap;
}
> * {
pointer-events: auto;
}
> .ui-element {
flex-shrink: 0;
@media (max-width: $small-max) {
border-top: solid 1px $light_bg;
margin-top: -1px;
}
@media (max-width: $super-small-max) {
flex-grow: 1;
}
}
> .ui-element:not(.separator) + .ui-element:not(.separator) {
border-left: solid 1px $light_bg;
margin-left: -1px;
}
> .ui-element.separator {
width: 10px;
flex-shrink: 0;
@media (max-width: $small-max) {
width: 0;
border-left: solid 1px $light_bg;
margin-left: -1px;
}
@media (max-width: $super-small-max) {
display: none;
}
}
> .ui-element.separator.greedy {
flex-grow: 1;
@media (max-width: $small-max) {
border-right: solid 1px $light_bg;
margin-right: -1px;
z-index: 101;
}
@media (max-width: $super-small-max) {
display: unset;
border-right: none;
margin-right: 0;
flex-grow: 0;
}
}
> .ui-element.dropdown {
flex-basis: 15rem;
flex-shrink: 1;
min-width: 10rem;
@media (max-width: $super-small-max) {
flex-basis: calc(100% - 2rem); //space for dropdown + menu button
}
}
}

View File

@ -0,0 +1,95 @@
.bluemap-container .ui {
position: relative;
h1, h2, h3, h4, h5, h6 {
font-size: 1rem;
font-weight: bold;
text-decoration: none;
text-align: left;
margin: 0;
padding: 0;
}
h1 {
font-size: 1.5rem;
}
h2 {
font-size: 1.3rem;
}
h3 {
font-size: 1.1rem;
}
p {
padding: 0;
margin: 0.5rem 0 0 0;
}
.close-button {
position: absolute;
top: 0;
right: 0;
width: 1.5rem;
height: 1.5rem;
margin: 0.25rem;
font-weight: bold;
&::after, &::before {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 0.8rem;
height: 0.2rem;
background-color: $light_fg;
}
&::before {
transform: translate(-50%, -50%) rotate(45deg);
}
&::after {
transform: translate(-50%, -50%) rotate(-45deg);
}
&:hover::after, &:hover::before {
background-color: darkred;
}
}
.alert-box {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: center;
pointer-events: none;
.alert {
position: relative;
pointer-events: all;
background-color: $normal_bg;
padding: 1rem;
margin: 1rem;
}
}
}

View File

@ -22,7 +22,8 @@ module.exports = {
contentBase: WORLD_DATA_PATH,
compress: true,
port: 8080,
hot: true
hot: true,
host: '0.0.0.0'
},
plugins: [
new MiniCssExtractPlugin({