Merge branch 'mc/1.13'

This commit is contained in:
Blue (Lukas Rieger) 2020-03-08 20:57:34 +01:00
commit f7f5ca76b2
81 changed files with 2863 additions and 1249 deletions

View File

@ -3,6 +3,7 @@ metrics: true
renderThreadCount: -2
data: "bluemap"
webroot: "bluemap/web"
useCookies: true
webserver {
enabled: true
port: 8100

View File

@ -37,6 +37,9 @@ webroot: "bluemap/web"
# Default is "<webroot>/data"
#webdata: "path/to/data/folder"
# If the web-application should use cookies to save the configurations of a user.
useCookies: true
webserver {
# With this setting you can disable the integrated web-server.
# This is usefull if you want to only render the map-data for later use, or if you setup your own webserver.
@ -65,40 +68,43 @@ maps: [
{
# The id of this map
# Should only contain word-charactes: [a-zA-Z0-9_]
# Changing this value breaks your existing renders.
id: "world"
# The name of this map
# This defines the display name of this map, you can change this at any time
# This defines the display name of this map, you can change this at any time.
# Default is the id of this map
name: "World"
# The path to the save-folder of the world to render
# The path to the save-folder of the world to render.
world: "world"
# The position on the world where the map will be centered if you open it.
# You can change this at any time.
# This defaults to the world-spawn if you don't set it.
#startPos: [500, -820]
# The color of thy sky as a hex-color
# You can change this at any time.
# Default is "#7dabff"
skyColor: "#7dabff"
# Defines the ambient light-strength that every block is recieving, regardless of the sunlight/blocklight.
# 0 is no ambient light, 1 is fully lighted.
# You can change this at any time.
# Default is 0
ambientLight: 0
# If this is false, BlueMap tries to omit all blocks that are not visible from above-ground.
# More specific: Block-Faces that have a sunlight/skylight value of 0 are removed.
# This improves the performance of the map on slower devices by a lot, but might cause some blocks to disappear that should normally be visible.
# Changing this value requires a re-render of the map.
# Default is false
renderCaves: false
# AmbientOcclusion adds soft shadows into corners, which gives the map a much better look.
# This has only a small impact on render-time and has no impact on the web-performance of the map.
# The value defines the strength of the shading, a value of 0 disables ambientOcclusion.
# Default is 0.25
ambientOcclusion: 0.25
# Lighting uses the light-data in minecraft to shade each block-face.
# If this is enabled, caves and inside buildings without torches will be darker.
# The value defines the strength of the shading and a value of 0 disables lighting (every block will be fully lit).
# Default is 0.8
lighting: 0.8
# With the below values you can limit the map-render.
# This can be used to ignore the nethers ceiling or render only a certain part of a world.
# Changing this values might require a re-render of the map, already rendered tiles outside the limits will not be deleted.
# Default is no min or max value (= infinite bounds)
#minX: -4000
#maxX: 4000
@ -110,43 +116,17 @@ maps: [
# Using this, BlueMap pretends that every Block out of the defined render-bounds is AIR,
# this means you can see the blocks where the world is cut (instead of having a see-through/xray view).
# This has only an effect if you set some render-bounds above.
# Default is enabled
# Changing this value requires a re-render of the map.
# Default is true
renderEdges: true
# With this set to true, the generated files for this world are compressed using gzip to save A LOT of space.
# Files will be only 5% as big with compression!
# Note: If you are using NGINX or Apache to host your map, you can configure them to serve the compressed files directly.
# This is much better than disabling the compression.
# Changing this value requires a re-render of the map.
# Default is true
useCompression: true
# HIRES is the high-resolution render of the map. Where you see every block.
hires {
# Defines the size of one map-tile in blocks.
# If you change this value, the lowres values might need adjustment as well!
# Default is 32
tileSize: 32
# The View-Distance for hires tiles on the web-map (the value is the radius in tiles)
# Default is 4.5
viewDistance: 4.5
}
# LOWRES is the low-resolution render of the map. Thats the model that you see if you zoom far out to get an overview.
lowres {
# Defines resolution of the lowres model. E.g. If the hires.tileSize is 32, a value of 4 means that every 8*8 blocks will be summarized by one point on the lowres map.
# Calculation: 32 / 4 = 8
# You can only use values that result in an integer if you use the above calculation!
# Default is 4
pointsPerHiresTile: 4
# Defines the size of one lowres-map-tile in points.
# Default is 50
pointsPerLowresTile: 50
# The View-Distance for lowres tiles on the web-map (the value is the radius in tiles)
# Default is 7
viewDistance: 7
}
}
# Here another example for the End-Map
@ -154,23 +134,23 @@ maps: [
{
id: "end"
name: "End"
world: "world/DIM1"
world: "world_the_end"
# In the end is no sky-light, so we need to enable this or we won't see anything.
renderCaves: true
# Same here, we don't want a dark map. But not completely disabled, so we see the effect of e.g torches.
lighting: 0.4
# Same here, we don't want a dark map. But not completely lighted, so we see the effect of e.g torches.
ambientLight: 0.6
}
# Here another example for the Nether-Map
{
id: "nether"
name: "Nether"
world: "world/DIM-1"
world: "world_nether"
renderCaves: true
lighting: 0.6
ambientLight: 0.6
# We slice the whole world at y:90 so every block above 90 will be air.
# This way we don't render the nethers ceiling.

View File

@ -141,9 +141,10 @@ public class BlueMapCLI {
Logger.global.logInfo("Writing settings.json ...");
WebSettings webSettings = new WebSettings(config.getWebDataPath().resolve("settings.json").toFile());
webSettings.setAllEnabled(false);
webSettings.set(config.isUseCookies(), "useCookies");
webSettings.setAllMapsEnabled(false);
for (MapType map : maps.values()) {
webSettings.setEnabled(true, map.getId());
webSettings.setMapEnabled(true, map.getId());
webSettings.setFrom(map.getTileRenderer(), map.getId());
webSettings.setFrom(map.getWorld(), map.getId());
}

View File

@ -2,7 +2,8 @@ accept-download: false
metrics: true
renderThreadCount: 0
data: "."
webroot: "web"
webroot: "web"
useCookies: true
webserver {
enabled: false
port: 8100

View File

@ -37,6 +37,9 @@ webroot: "web"
# Default is "<webroot>/data"
#webdata: "path/to/data/folder"
# If the web-application should use cookies to save the configurations of a user.
useCookies: true
webserver {
# With this setting you can enable the integrated web-server.
# Default is disabled
@ -64,40 +67,43 @@ maps: [
{
# The id of this map
# Should only contain word-charactes: [a-zA-Z0-9_]
# Changing this value breaks your existing renders.
id: "world"
# The name of this map
# This defines the display name of this map, you can change this at any time
# This defines the display name of this map, you can change this at any time.
# Default is the id of this map
name: "World"
# The path to the save-folder of the world to render
# The path to the save-folder of the world to render.
world: "world"
# The position on the world where the map will be centered if you open it.
# You can change this at any time.
# This defaults to the world-spawn if you don't set it.
#startPos: [500, -820]
# The color of thy sky as a hex-color
# You can change this at any time.
# Default is "#7dabff"
skyColor: "#7dabff"
# Defines the ambient light-strength that every block is recieving, regardless of the sunlight/blocklight.
# 0 is no ambient light, 1 is fully lighted.
# You can change this at any time.
# Default is 0
ambientLight: 0
# If this is false, BlueMap tries to omit all blocks that are not visible from above-ground.
# More specific: Block-Faces that have a sunlight/skylight value of 0 are removed.
# This improves the performance of the map on slower devices by a lot, but might cause some blocks to disappear that should normally be visible.
# Changing this value requires a re-render of the map.
# Default is false
renderCaves: false
# AmbientOcclusion adds soft shadows into corners, which gives the map a much better look.
# This has only a small impact on render-time and has no impact on the web-performance of the map.
# The value defines the strength of the shading, a value of 0 disables ambientOcclusion.
# Default is 0.25
ambientOcclusion: 0.25
# Lighting uses the light-data in minecraft to shade each block-face.
# If this is enabled, caves and inside buildings without torches will be darker.
# The value defines the strength of the shading and a value of 0 disables lighting (every block will be fully lit).
# Default is 0.8
lighting: 0.8
# With the below values you can limit the map-render.
# This can be used to ignore the nethers ceiling or render only a certain part of a world.
# Changing this values might require a re-render of the map, already rendered tiles outside the limits will not be deleted.
# Default is no min or max value (= infinite bounds)
#minX: -4000
#maxX: 4000
@ -109,43 +115,17 @@ maps: [
# Using this, BlueMap pretends that every Block out of the defined render-bounds is AIR,
# this means you can see the blocks where the world is cut (instead of having a see-through/xray view).
# This has only an effect if you set some render-bounds above.
# Default is enabled
# Changing this value requires a re-render of the map.
# Default is true
renderEdges: true
# With this set to true, the generated files for this world are compressed using gzip to save A LOT of space.
# Files will be only 5% as big with compression!
# Note: If you are using NGINX or Apache to host your map, you can configure them to serve the compressed files directly.
# This is much better than disabling the compression.
# Changing this value requires a re-render of the map.
# Default is true
useCompression: true
# HIRES is the high-resolution render of the map. Where you see every block.
hires {
# Defines the size of one map-tile in blocks.
# If you change this value, the lowres values might need adjustment as well!
# Default is 32
tileSize: 32
# The View-Distance for hires tiles on the web-map (the value is the radius in tiles)
# Default is 4.5
viewDistance: 4.5
}
# LOWRES is the low-resolution render of the map. Thats the model that you see if you zoom far out to get an overview.
lowres {
# Defines resolution of the lowres model. E.g. If the hires.tileSize is 32, a value of 4 means that every 8*8 blocks will be summarized by one point on the lowres map.
# Calculation: 32 / 4 = 8
# You can only use values that result in an integer if you use the above calculation!
# Default is 4
pointsPerHiresTile: 4
# Defines the size of one lowres-map-tile in points.
# Default is 50
pointsPerLowresTile: 50
# The View-Distance for lowres tiles on the web-map (the value is the radius in tiles)
# Default is 7
viewDistance: 7
}
}
# Here another example for the End-Map
@ -158,8 +138,8 @@ maps: [
# In the end is no sky-light, so we need to enable this or we won't see anything.
renderCaves: true
# Same here, we don't want a dark map. But not completely disabled, so we see the effect of e.g torches.
lighting: 0.4
# Same here, we don't want a dark map. But not completely lighted, so we see the effect of e.g torches.
ambientLight: 0.6
}
# Here another example for the Nether-Map
@ -169,7 +149,7 @@ maps: [
world: "world/DIM-1"
renderCaves: true
lighting: 0.6
ambientLight: 0.6
# We slice the whole world at y:90 so every block above 90 will be air.
# This way we don't render the nethers ceiling.

View File

@ -246,9 +246,10 @@ public class Plugin {
}
WebSettings webSettings = new WebSettings(config.getWebDataPath().resolve("settings.json").toFile());
webSettings.setAllEnabled(false);
webSettings.set(config.isUseCookies(), "useCookies");
webSettings.setAllMapsEnabled(false);
for (MapType map : maps.values()) {
webSettings.setEnabled(true, map.getId());
webSettings.setMapEnabled(true, map.getId());
webSettings.setFrom(map.getTileRenderer(), map.getId());
webSettings.setFrom(map.getWorld(), map.getId());
}
@ -273,7 +274,7 @@ public class Plugin {
Thread.sleep(TimeUnit.MINUTES.toMillis(1));
while (true) {
if (serverInterface.isMetricsEnabled(config.isMetricsEnabled())) Metrics.sendReport("Sponge");
if (serverInterface.isMetricsEnabled(config.isMetricsEnabled())) Metrics.sendReport(this.implementationType);
Thread.sleep(TimeUnit.MINUTES.toMillis(30));
}
} catch (InterruptedException ex){

View File

@ -42,8 +42,6 @@ import de.bluecolored.bluemap.core.web.WebServerConfig;
import ninja.leaping.configurate.ConfigurationNode;
public class MainConfig implements WebServerConfig {
private String version;
private boolean downloadAccepted = false;
private boolean metricsEnabled = false;
@ -52,6 +50,7 @@ public class MainConfig implements WebServerConfig {
private int webserverPort = 8100;
private int webserverMaxConnections = 100;
private InetAddress webserverBindAdress = null;
private boolean useCookies;
private Path dataPath = Paths.get("data");
@ -92,6 +91,8 @@ public class MainConfig implements WebServerConfig {
else
webDataPath = webRoot.resolve("data");
useCookies = node.getNode("useCookies").getBoolean(true);
//webserver
loadWebConfig(node.getNode("webserver"));
@ -137,14 +138,24 @@ public class MainConfig implements WebServerConfig {
return file.toPath();
}
@Override
public Path getWebRoot() {
return webRoot;
}
public Path getDataPath() {
return dataPath;
}
public boolean isUseCookies() {
return useCookies;
}
public boolean isWebserverEnabled() {
return webserverEnabled;
}
public Path getWebDataPath() {
return webDataPath;
}
@ -163,15 +174,6 @@ public class MainConfig implements WebServerConfig {
public InetAddress getWebserverBindAdress() {
return webserverBindAdress;
}
@Override
public Path getWebRoot() {
return webRoot;
}
public String getVersion() {
return version;
}
public boolean isDownloadAccepted() {
return downloadAccepted;
@ -196,10 +198,10 @@ public class MainConfig implements WebServerConfig {
private String world;
private Vector2i startPos;
private int skyColor;
private float ambientLight;
private boolean renderCaves;
private float ambientOcclusion;
private float lighting;
private Vector3i min, max;
private boolean renderEdges;
@ -207,11 +209,9 @@ public class MainConfig implements WebServerConfig {
private boolean useGzip;
private int hiresTileSize;
private float hiresViewDistance;
private int lowresPointsPerHiresTile;
private int lowresPointsPerLowresTile;
private float lowresViewDistance;
private MapConfig(ConfigurationNode node) throws IOException {
this.id = node.getNode("id").getString("");
@ -224,9 +224,12 @@ public class MainConfig implements WebServerConfig {
if (!node.getNode("startPos").isVirtual()) this.startPos = ConfigUtils.readVector2i(node.getNode("startPos"));
if (!node.getNode("skyColor").isVirtual()) this.skyColor = ConfigUtils.readColorInt(node.getNode("skyColor"));
else this.skyColor = 0x7dabff;
this.ambientLight = node.getNode("ambientLight").getFloat(0f);
this.renderCaves = node.getNode("renderCaves").getBoolean(false);
this.ambientOcclusion = node.getNode("ambientOcclusion").getFloat(0.25f);
this.lighting = node.getNode("lighting").getFloat(0.8f);
int minX = node.getNode("minX").getInt(RenderSettings.super.getMin().getX());
int maxX = node.getNode("maxX").getInt(RenderSettings.super.getMax().getX());
@ -242,11 +245,9 @@ public class MainConfig implements WebServerConfig {
this.useGzip = node.getNode("useCompression").getBoolean(true);
this.hiresTileSize = node.getNode("hires", "tileSize").getInt(32);
this.hiresViewDistance = node.getNode("hires", "viewDistance").getFloat(4.5f);
this.lowresPointsPerHiresTile = node.getNode("lowres", "pointsPerHiresTile").getInt(4);
this.lowresPointsPerLowresTile = node.getNode("lowres", "pointsPerLowresTile").getInt(50);
this.lowresViewDistance = node.getNode("lowres", "viewDistance").getFloat(7f);
//check valid configuration values
double blocksPerPoint = (double) this.hiresTileSize / (double) this.lowresPointsPerHiresTile;
@ -268,29 +269,23 @@ public class MainConfig implements WebServerConfig {
public Vector2i getStartPos() {
return startPos;
}
public int getSkyColor() {
return skyColor;
}
public float getAmbientLight() {
return ambientLight;
}
public boolean isRenderCaves() {
return renderCaves;
}
@Override
public float getAmbientOcclusionStrenght() {
return ambientOcclusion;
}
@Override
public float getLightShadeMultiplier() {
return lighting;
}
public int getHiresTileSize() {
return hiresTileSize;
}
public float getHiresViewDistance() {
return hiresViewDistance;
}
public int getLowresPointsPerHiresTile() {
return lowresPointsPerHiresTile;
}
@ -299,10 +294,6 @@ public class MainConfig implements WebServerConfig {
return lowresPointsPerLowresTile;
}
public float getLowresViewDistance() {
return lowresViewDistance;
}
@Override
public boolean isExcludeFacesWithoutSunlight() {
return !isRenderCaves();

View File

@ -0,0 +1,120 @@
/*
* 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.
*/
package de.bluecolored.bluemap.core.model;
import com.flowpowered.math.vector.Vector2f;
import com.flowpowered.math.vector.Vector3f;
public class ExtendedFace extends Face {
private float ao1 = 1f, ao2 = 1f, ao3 = 1f; // ao
private float bl1 = 15f, bl2 = 15f, bl3 = 15f; // block-light
private float sl1 = 15f, sl2 = 15f, sl3 = 15f; // sun-light
public ExtendedFace(
Vector3f p1,
Vector3f p2,
Vector3f p3,
Vector2f uv1,
Vector2f uv2,
Vector2f uv3,
int materialIndex
) {
super(p1, p2, p3, uv1, uv2, uv3, materialIndex);
}
public float getAo1() {
return ao1;
}
public void setAo1(float ao1) {
this.ao1 = ao1;
}
public float getAo2() {
return ao2;
}
public void setAo2(float ao2) {
this.ao2 = ao2;
}
public float getAo3() {
return ao3;
}
public void setAo3(float ao3) {
this.ao3 = ao3;
}
public float getBl1() {
return bl1;
}
public void setBl1(float bl1) {
this.bl1 = bl1;
}
public float getBl2() {
return bl2;
}
public void setBl2(float bl2) {
this.bl2 = bl2;
}
public float getBl3() {
return bl3;
}
public void setBl3(float bl3) {
this.bl3 = bl3;
}
public float getSl1() {
return sl1;
}
public void setSl1(float sl1) {
this.sl1 = sl1;
}
public float getSl2() {
return sl2;
}
public void setSl2(float sl2) {
this.sl2 = sl2;
}
public float getSl3() {
return sl3;
}
public void setSl3(float sl3) {
this.sl3 = sl3;
}
}

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.
*/
package de.bluecolored.bluemap.core.model;
import de.bluecolored.bluemap.core.threejs.BufferAttribute;
import de.bluecolored.bluemap.core.threejs.BufferGeometry;
public class ExtendedModel extends Model<ExtendedFace> {
@Override
public BufferGeometry toBufferGeometry() {
BufferGeometry geo = super.toBufferGeometry();
int count = getFaces().size();
float[] ao = new float[count * 3];
float[] blockLight = new float[count * 3];
float[] sunLight = new float[count * 3];
for (int itemIndex = 0; itemIndex < count; itemIndex++){
ExtendedFace f = getFaces().get(itemIndex);
ao[itemIndex * 3 + 0] = f.getAo1();
ao[itemIndex * 3 + 1] = f.getAo2();
ao[itemIndex * 3 + 2] = f.getAo3();
blockLight[itemIndex * 3 + 0] = f.getBl1();
blockLight[itemIndex * 3 + 1] = f.getBl2();
blockLight[itemIndex * 3 + 2] = f.getBl3();
sunLight[itemIndex * 3 + 0] = f.getSl1();
sunLight[itemIndex * 3 + 1] = f.getSl2();
sunLight[itemIndex * 3 + 2] = f.getSl3();
}
geo.addAttribute("ao", new BufferAttribute(ao, 1));
geo.addAttribute("blocklight", new BufferAttribute(blockLight, 1));
geo.addAttribute("sunlight", new BufferAttribute(sunLight, 1));
return geo;
}
}

View File

@ -32,38 +32,38 @@ import com.flowpowered.math.vector.Vector3f;
import de.bluecolored.bluemap.core.util.MathUtils;
public class Face {
private Vector3f p1, p2, p3;
private Vector3f n1, n2, n3;
private Vector3f c1, c2, c3;
private Vector2f uv1, uv2, uv3;
private Vector3f p1, p2, p3; // points
private Vector3f n1, n2, n3; // normals
private Vector3f c1, c2, c3; // vertex-colors
private Vector2f uv1, uv2, uv3; // texture UV
private int materialIndex;
private boolean normalizedNormals;
public Face(Vector3f p1, Vector3f p2, Vector3f p3, Vector2f uv1, Vector2f uv2, Vector2f uv3, int materialIndex) {
this.p1 = p1;
this.p2 = p2;
this.p3 = p3;
this.uv1 = uv1;
this.uv2 = uv2;
this.uv3 = uv3;
this.materialIndex = materialIndex;
Vector3f faceNormal = getFaceNormal();
this.n1 = faceNormal;
this.n2 = faceNormal;
this.n3 = faceNormal;
this.normalizedNormals = true;
Vector3f color = Vector3f.ONE;
this.c1 = color;
this.c2 = color;
this.c3 = color;
}
public void rotate(Quaternionf rotation){
public void rotate(Quaternionf rotation) {
p1 = rotation.rotate(p1);
p2 = rotation.rotate(p2);
p3 = rotation.rotate(p3);
@ -72,25 +72,25 @@ public class Face {
n2 = rotation.rotate(n2);
n3 = rotation.rotate(n3);
}
public void transform(Matrix3f transformation){
public void transform(Matrix3f transformation) {
p1 = transformation.transform(p1);
p2 = transformation.transform(p2);
p3 = transformation.transform(p3);
n1 = transformation.transform(n1);
n2 = transformation.transform(n2);
n3 = transformation.transform(n3);
normalizedNormals = false;
}
public void translate(Vector3f translation){
public void translate(Vector3f translation) {
p1 = translation.add(p1);
p2 = translation.add(p2);
p3 = translation.add(p3);
}
public Vector3f getP1() {
return p1;
}
@ -201,18 +201,18 @@ public class Face {
this.materialIndex = materialIndex;
}
public Vector3f getFaceNormal(){
public Vector3f getFaceNormal() {
return MathUtils.getSurfaceNormal(p1, p2, p3);
}
private void normlizeNormals(){
private void normlizeNormals() {
if (normalizedNormals) return;
n1 = n1.normalize();
n2 = n2.normalize();
n3 = n3.normalize();
normalizedNormals = true;
}
}

View File

@ -25,7 +25,6 @@
package de.bluecolored.bluemap.core.model;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import com.flowpowered.math.imaginary.Quaternionf;
@ -34,10 +33,11 @@ import com.flowpowered.math.vector.Vector2f;
import com.flowpowered.math.vector.Vector3f;
import de.bluecolored.bluemap.core.threejs.BufferGeometry;
import de.bluecolored.bluemap.core.threejs.MaterialGroup;
public class Model {
public class Model<T extends Face> {
private List<Face> faces;
private List<T> faces;
public Model() {
this.faces = new ArrayList<>();
@ -47,19 +47,19 @@ public class Model {
* Merges the given Model into this model<br>
* Faces are not cloned: So changes to the faces of the previous model will mirror in this model, but adding and removing faces will not.
*/
public void merge(Model model){
public void merge(Model<T> model){
faces.addAll(model.getFaces());
}
public void addFace(Face face){
public void addFace(T face){
faces.add(face);
}
public void removeFace(Face face){
public void removeFace(T face){
faces.remove(face);
}
public Collection<Face> getFaces(){
public List<T> getFaces(){
return faces;
}
@ -70,13 +70,13 @@ public class Model {
}
public void transform(Matrix3f transformation){
for (Face f : faces){
for (T f : faces){
f.transform(transformation);
}
}
public void translate(Vector3f translation){
for (Face f : faces){
for (T f : faces){
f.translate(translation);
}
}
@ -89,7 +89,7 @@ public class Model {
//reorganize all faces into arrays and create material-groups
int count = faces.size();
List<BufferGeometry.MaterialGroup> groups = new ArrayList<>();
List<MaterialGroup> groups = new ArrayList<>();
int groupStart = 0;
int currentMaterialIndex = -1;
if (count > 0) currentMaterialIndex = faces.get(0).getMaterialIndex();
@ -100,10 +100,10 @@ public class Model {
float[] uv = new float[count * 2 * 3];
for (int itemIndex = 0; itemIndex < count; itemIndex++){
Face f = faces.get(itemIndex);
T f = faces.get(itemIndex);
if (currentMaterialIndex != f.getMaterialIndex()){
groups.add(new BufferGeometry.MaterialGroup(currentMaterialIndex, groupStart * 3, (itemIndex - groupStart) * 3));
groups.add(new MaterialGroup(currentMaterialIndex, groupStart * 3, (itemIndex - groupStart) * 3));
groupStart = itemIndex;
currentMaterialIndex = f.getMaterialIndex();
}
@ -124,24 +124,24 @@ public class Model {
addVector2fToArray( uv, f.getUv3(), (itemIndex * 3 + 2) * 2 );
}
groups.add(new BufferGeometry.MaterialGroup(currentMaterialIndex, groupStart * 3, (count - groupStart) * 3));
groups.add(new MaterialGroup(currentMaterialIndex, groupStart * 3, (count - groupStart) * 3));
return new BufferGeometry(
position,
normal,
color,
uv,
groups.toArray(new BufferGeometry.MaterialGroup[groups.size()])
groups.toArray(new MaterialGroup[groups.size()])
);
}
private void addVector3fToArray(float[] array, Vector3f v, int startIndex){
static void addVector3fToArray(float[] array, Vector3f v, int startIndex){
array[startIndex] = v.getX();
array[startIndex + 1] = v.getY();
array[startIndex + 2] = v.getZ();
}
private void addVector2fToArray(float[] array, Vector2f v, int startIndex){
static void addVector2fToArray(float[] array, Vector2f v, int startIndex){
array[startIndex] = v.getX();
array[startIndex + 1] = v.getY();
}

View File

@ -30,15 +30,6 @@ public interface RenderSettings {
static final Vector3i DEFAULT_MIN = Vector3i.from(Integer.MIN_VALUE);
static final Vector3i DEFAULT_MAX = Vector3i.from(Integer.MAX_VALUE);
/**
* The strenght of ao-shading calculated for each vertex.<br>
* A value of 0 turns off ao.<br>
* The value represents the amount that each occluding face subtracts of the light-multiplier. (There are at most 3 occluding faces)
*/
default float getAmbientOcclusionStrenght() {
return 0.25f;
}
/**
* Whether faces that have a sky-light-value of 0 will be rendered or not.
@ -46,14 +37,6 @@ public interface RenderSettings {
default boolean isExcludeFacesWithoutSunlight() {
return true;
}
/**
* A multiplier to how much faces are shaded due to their light value<br>
* This can be used to make sure blocks with a light value of 0 are not pitch black
*/
default float getLightShadeMultiplier() {
return 0.8f;
}
/**
* The minimum position of blocks to render
@ -87,9 +70,7 @@ public interface RenderSettings {
default RenderSettings copy() {
return new StaticRenderSettings(
getAmbientOcclusionStrenght(),
isExcludeFacesWithoutSunlight(),
getLightShadeMultiplier(),
getMin(),
getMax(),
isRenderEdges()

View File

@ -28,42 +28,26 @@ import com.flowpowered.math.vector.Vector3i;
public class StaticRenderSettings implements RenderSettings {
private float ambientOcclusion;
private boolean excludeFacesWithoutSunlight;
private float lightShade;
private Vector3i min, max;
private boolean renderEdges;
public StaticRenderSettings(
float ambientOcclusion,
boolean excludeFacesWithoutSunlight,
float ligheShade,
Vector3i min,
Vector3i max,
boolean renderEdges
) {
this.ambientOcclusion = ambientOcclusion;
this.excludeFacesWithoutSunlight = excludeFacesWithoutSunlight;
this.lightShade = ligheShade;
this.min = min;
this.max = max;
this.renderEdges = renderEdges;
}
@Override
public float getAmbientOcclusionStrenght() {
return ambientOcclusion;
}
@Override
public boolean isExcludeFacesWithoutSunlight() {
return excludeFacesWithoutSunlight;
}
@Override
public float getLightShadeMultiplier() {
return lightShade;
}
@Override
public Vector3i getMin() {

View File

@ -30,12 +30,12 @@ import com.flowpowered.math.vector.Vector2i;
import com.flowpowered.math.vector.Vector3i;
import com.flowpowered.math.vector.Vector4f;
import de.bluecolored.bluemap.core.model.Model;
import de.bluecolored.bluemap.core.model.ExtendedModel;
/**
* A model, containing additional information about the tile it represents
*/
public class HiresModel extends Model {
public class HiresModel extends ExtendedModel {
private UUID world;
private Vector2i tile;

View File

@ -28,7 +28,6 @@ import com.flowpowered.math.vector.Vector3f;
import com.flowpowered.math.vector.Vector3i;
import com.flowpowered.math.vector.Vector4f;
import de.bluecolored.bluemap.core.logger.Logger;
import de.bluecolored.bluemap.core.render.RenderSettings;
import de.bluecolored.bluemap.core.render.WorldTile;
import de.bluecolored.bluemap.core.render.hires.blockmodel.BlockStateModel;

View File

@ -26,13 +26,15 @@ package de.bluecolored.bluemap.core.render.hires.blockmodel;
import com.flowpowered.math.vector.Vector4f;
import de.bluecolored.bluemap.core.model.ExtendedFace;
import de.bluecolored.bluemap.core.model.ExtendedModel;
import de.bluecolored.bluemap.core.model.Model;
import de.bluecolored.bluemap.core.util.MathUtils;
/**
* A model with some extra information about the BlockState it represents
*/
public class BlockStateModel extends Model {
public class BlockStateModel extends ExtendedModel {
private Vector4f mapColor;
@ -45,7 +47,7 @@ public class BlockStateModel extends Model {
}
@Override
public void merge(Model model) {
public void merge(Model<ExtendedFace> model) {
super.merge(model);
if (model instanceof BlockStateModel){

View File

@ -32,8 +32,8 @@ import com.flowpowered.math.vector.Vector3f;
import com.flowpowered.math.vector.Vector4f;
import com.google.common.collect.Sets;
import de.bluecolored.bluemap.core.model.Face;
import de.bluecolored.bluemap.core.model.Model;
import de.bluecolored.bluemap.core.model.ExtendedFace;
import de.bluecolored.bluemap.core.model.ExtendedModel;
import de.bluecolored.bluemap.core.render.RenderSettings;
import de.bluecolored.bluemap.core.resourcepack.BlockColorCalculator;
import de.bluecolored.bluemap.core.resourcepack.BlockModelResource;
@ -190,7 +190,7 @@ public class LiquidModelBuilder {
return 0;
}
private void createElementFace(Model model, Direction faceDir, Vector3f c0, Vector3f c1, Vector3f c2, Vector3f c3, Vector3f color, int textureId) {
private void createElementFace(ExtendedModel model, Direction faceDir, Vector3f c0, Vector3f c1, Vector3f c2, Vector3f c3, Vector3f color, int textureId) {
//face culling
Block bl = block.getRelativeBlock(faceDir);
@ -206,33 +206,42 @@ public class LiquidModelBuilder {
uvs[2] = new Vector2f(uv.getZ(), uv.getY());
uvs[3] = new Vector2f(uv.getX(), uv.getY());
Face f1 = new Face(c0, c1, c2, uvs[0], uvs[1], uvs[2], textureId);
Face f2 = new Face(c0, c2, c3, uvs[0], uvs[2], uvs[3], textureId);
ExtendedFace f1 = new ExtendedFace(c0, c1, c2, uvs[0], uvs[1], uvs[2], textureId);
ExtendedFace f2 = new ExtendedFace(c0, c2, c3, uvs[0], uvs[2], uvs[3], textureId);
//move face in a tiny bit to avoid z-fighting with waterlogged blocks
f1.translate(faceDir.opposite().toVector().toFloat().mul(0.01));
f2.translate(faceDir.opposite().toVector().toFloat().mul(0.01));
// move face in a tiny bit to avoid z-fighting with waterlogged blocks (doesn't work because it is rounded back when storing the model later)
//f1.translate(faceDir.opposite().toVector().toFloat().mul(0.01));
//f2.translate(faceDir.opposite().toVector().toFloat().mul(0.01));
float light = 1f;
if (renderSettings.getLightShadeMultiplier() > 0) {
light = 0f;
for (Direction d : Direction.values()){
Block b = block.getRelativeBlock(d.toVector());
float l = (float) (Math.max(b.getBlockLightLevel(), b.getSunLightLevel()) / 15f) * renderSettings.getLightShadeMultiplier() + (1 - renderSettings.getLightShadeMultiplier());
if (l > light) light = l;
}
float blockLight = bl.getBlockLightLevel();
float sunLight = bl.getSunLightLevel();
if (faceDir == Direction.UP) {
blockLight = block.getBlockLightLevel();
sunLight = block.getSunLightLevel();
}
color = color.mul(light);
f1.setC1(color);
f1.setC2(color);
f1.setC3(color);
f2.setC1(color);
f2.setC2(color);
f2.setC3(color);
f1.setBl1(blockLight);
f1.setBl2(blockLight);
f1.setBl3(blockLight);
f2.setBl1(blockLight);
f2.setBl2(blockLight);
f2.setBl3(blockLight);
f1.setSl1(sunLight);
f1.setSl2(sunLight);
f1.setSl3(sunLight);
f2.setSl1(sunLight);
f2.setSl2(sunLight);
f2.setSl3(sunLight);
//add the face
model.addFace(f1);
model.addFace(f2);

View File

@ -33,7 +33,7 @@ import com.flowpowered.math.vector.Vector3f;
import com.flowpowered.math.vector.Vector3i;
import com.flowpowered.math.vector.Vector4f;
import de.bluecolored.bluemap.core.model.Face;
import de.bluecolored.bluemap.core.model.ExtendedFace;
import de.bluecolored.bluemap.core.render.RenderSettings;
import de.bluecolored.bluemap.core.resourcepack.BlockColorCalculator;
import de.bluecolored.bluemap.core.resourcepack.BlockModelResource;
@ -154,18 +154,12 @@ public class ResourceModelBuilder {
//light calculation
Block facedBlockNeighbor = getRotationRelativeBlock(modelResource.getRotation(), faceDir);
float skyLight = facedBlockNeighbor.getPassedSunLight();
float sunLight = facedBlockNeighbor.getPassedSunLight();
//filter out faces that are not skylighted
if (skyLight == 0f && renderSettings.isExcludeFacesWithoutSunlight()) return;
//filter out faces that are not sunlighted
if (sunLight == 0f && renderSettings.isExcludeFacesWithoutSunlight()) return;
float light = 1f;
if (renderSettings.getLightShadeMultiplier() > 0) {
float blockLight = facedBlockNeighbor.getPassedBlockLight();
light = (Math.max(skyLight, blockLight) / 15f) * renderSettings.getLightShadeMultiplier() + (1 - renderSettings.getLightShadeMultiplier());
if (light > 1) light = 1;
if (light < 0) light = 0;
}
float blockLight = facedBlockNeighbor.getPassedBlockLight();
//UV
Vector4f uv = face.getUv().toFloat().div(16);
@ -195,41 +189,51 @@ public class ResourceModelBuilder {
Texture texture = face.getTexture();
int textureId = texture.getId();
Face f1 = new Face(c0, c1, c2, uvs[0], uvs[1], uvs[2], textureId);
Face f2 = new Face(c0, c2, c3, uvs[0], uvs[2], uvs[3], textureId);
//calculate ao
double ao0 = 1d, ao1 = 1d, ao2 = 1d, ao3 = 1d;
if (renderSettings.getAmbientOcclusionStrenght() > 0f && modelResource.getModel().isAmbientOcclusion()){
ao0 = testAo(modelResource.getRotation(), c0, faceDir);
ao1 = testAo(modelResource.getRotation(), c1, faceDir);
ao2 = testAo(modelResource.getRotation(), c2, faceDir);
ao3 = testAo(modelResource.getRotation(), c3, faceDir);
}
ExtendedFace f1 = new ExtendedFace(c0, c1, c2, uvs[0], uvs[1], uvs[2], textureId);
ExtendedFace f2 = new ExtendedFace(c0, c2, c3, uvs[0], uvs[2], uvs[3], textureId);
//tint the face
Vector3f color = Vector3f.ONE;
if (face.isTinted()){
color = tintColor.getValue();
}
color = color.mul(light);
Vector3f aoColor;
f1.setC1(color);
f1.setC2(color);
f1.setC3(color);
f2.setC1(color);
f2.setC2(color);
f2.setC3(color);
aoColor = color.mul(ao0);
f1.setC1(aoColor);
f2.setC1(aoColor);
f1.setBl1(blockLight);
f1.setBl2(blockLight);
f1.setBl3(blockLight);
f2.setBl1(blockLight);
f2.setBl2(blockLight);
f2.setBl3(blockLight);
aoColor = color.mul(ao1);
f1.setC2(aoColor);
f1.setSl1(sunLight);
f1.setSl2(sunLight);
f1.setSl3(sunLight);
f2.setSl1(sunLight);
f2.setSl2(sunLight);
f2.setSl3(sunLight);
aoColor = color.mul(ao2);
f1.setC3(aoColor);
f2.setC2(aoColor);
//calculate ao
float ao0 = 1f, ao1 = 1f, ao2 = 1f, ao3 = 1f;
if (modelResource.getModel().isAmbientOcclusion()){
ao0 = testAo(modelResource.getRotation(), c0, faceDir);
ao1 = testAo(modelResource.getRotation(), c1, faceDir);
ao2 = testAo(modelResource.getRotation(), c2, faceDir);
ao3 = testAo(modelResource.getRotation(), c3, faceDir);
}
aoColor = color.mul(ao3);
f2.setC3(aoColor);
f1.setAo1(ao0);
f1.setAo2(ao1);
f1.setAo3(ao2);
f2.setAo1(ao0);
f2.setAo2(ao2);
f2.setAo3(ao3);
//add the face
model.addFace(f1);
@ -275,7 +279,7 @@ public class ResourceModelBuilder {
return dir;
}
private double testAo(Vector2f modelRotation, Vector3f vertex, Direction dir){
private float testAo(Vector2f modelRotation, Vector3f vertex, Direction dir){
int occluding = 0;
int x = 0;
@ -322,7 +326,7 @@ public class ResourceModelBuilder {
if (occluding > 3)
occluding = 3;
return Math.max(0.0, Math.min(1.0 - ((double) occluding * renderSettings.getAmbientOcclusionStrenght()), 1.0));
return Math.max(0f, Math.min(1f - occluding * 0.25f, 1f));
}
private Vector2f[] rotateUVInner(Vector2f[] uv, int angle){

View File

@ -140,35 +140,39 @@ public class LowresModel {
Map<Vector2i, LowresPoint> points = changes;
changes = new HashMap<>();
int vertexCount = model.position.length / 3;
float[] position = model.attributes.get("position").values();
float[] color = model.attributes.get("color").values();
float[] normal = model.attributes.get("normal").values();
int vertexCount = Math.floorDiv(position.length, 3);
for (int i = 0; i < vertexCount; i++){
int j = i * 3;
int px = Math.round(model.position[j + 0]);
int pz = Math.round(model.position[j + 2]);
int px = Math.round(position[j + 0]);
int pz = Math.round(position[j + 2]);
Vector2i p = new Vector2i(px, pz);
LowresPoint lrp = points.get(p);
if (lrp == null) continue;
model.position[j + 1] = lrp.height;
position[j + 1] = lrp.height;
model.color[j + 0] = lrp.color.getX();
model.color[j + 1] = lrp.color.getY();
model.color[j + 2] = lrp.color.getZ();
color[j + 0] = lrp.color.getX();
color[j + 1] = lrp.color.getY();
color[j + 2] = lrp.color.getZ();
//recalculate normals
int f = Math.floorDiv(i, 3) * 3 * 3;
Vector3f p1 = new Vector3f(model.position[f + 0], model.position[f + 1], model.position[f + 2]);
Vector3f p2 = new Vector3f(model.position[f + 3], model.position[f + 4], model.position[f + 5]);
Vector3f p3 = new Vector3f(model.position[f + 6], model.position[f + 7], model.position[f + 8]);
Vector3f p1 = new Vector3f(position[f + 0], position[f + 1], position[f + 2]);
Vector3f p2 = new Vector3f(position[f + 3], position[f + 4], position[f + 5]);
Vector3f p3 = new Vector3f(position[f + 6], position[f + 7], position[f + 8]);
Vector3f n = MathUtils.getSurfaceNormal(p1, p2, p3);
model.normal[f + 0] = n.getX(); model.normal[f + 1] = n.getY(); model.normal[f + 2] = n.getZ();
model.normal[f + 3] = n.getX(); model.normal[f + 4] = n.getY(); model.normal[f + 5] = n.getZ();
model.normal[f + 6] = n.getX(); model.normal[f + 7] = n.getY(); model.normal[f + 8] = n.getZ();
normal[f + 0] = n.getX(); normal[f + 1] = n.getY(); normal[f + 2] = n.getZ();
normal[f + 3] = n.getX(); normal[f + 4] = n.getY(); normal[f + 5] = n.getZ();
normal[f + 6] = n.getX(); normal[f + 7] = n.getY(); normal[f + 8] = n.getZ();
}
}
}

View File

@ -76,7 +76,7 @@ public class LowresModelManager {
/**
* Renders all points from the given highres-model onto the lowres-grid
*/
public void render(HiresModel hiresModel) throws IOException {
public void render(HiresModel hiresModel) {
Vector3i min = hiresModel.getBlockMin();
Vector3i max = hiresModel.getBlockMax();
Vector3i size = max.sub(min).add(Vector3i.ONE);
@ -140,7 +140,7 @@ public class LowresModelManager {
/**
* Updates a point on the lowresmodel-grid
*/
public void update(UUID world, Vector2i point, float height, Vector3f color) throws IOException {
public void update(UUID world, Vector2i point, float height, Vector3f color) {
Vector2i tile = pointToTile(point);
Vector2i relPoint = getPointRelativeToTile(tile, point);
LowresModel model = getModel(world, tile);
@ -175,7 +175,7 @@ public class LowresModelManager {
return FileUtils.coordsToFile(fileRoot, tile, "json" + (useGzip ? ".gz" : ""));
}
private LowresModel getModel(UUID world, Vector2i tile) throws IOException {
private LowresModel getModel(UUID world, Vector2i tile) {
File modelFile = getFile(tile, useGzip);
CachedModel model = models.get(modelFile);
@ -184,24 +184,19 @@ public class LowresModelManager {
synchronized (this) {
model = models.get(modelFile);
if (model == null){
if (modelFile.exists()){
InputStream is = new FileInputStream(modelFile);
if (useGzip) is = new GZIPInputStream(is);
try {
try (FileInputStream fis = new FileInputStream(modelFile)) {
InputStream is = fis;
if (useGzip) is = new GZIPInputStream(is);
String json = IOUtils.toString(is, StandardCharsets.UTF_8);
try {
model = new CachedModel(world, tile, BufferGeometry.fromJson(json));
} catch (IllegalArgumentException | IOException ex){
Logger.global.logError("Failed to load lowres model: " + modelFile, ex);
//gridFile.renameTo(gridFile.toPath().getParent().resolve(gridFile.getName() + ".broken").toFile());
modelFile.delete();
}
} finally {
is.close();
model = new CachedModel(world, tile, BufferGeometry.fromJson(json));
} catch (IllegalArgumentException | IOException ex){
Logger.global.logError("Failed to load lowres model: " + modelFile, ex);
modelFile.delete();
}
}
if (model == null){

View File

@ -0,0 +1,148 @@
/*
* 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.
*/
/**
*
*/
package de.bluecolored.bluemap.core.threejs;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import com.flowpowered.math.GenericMath;
import com.google.common.base.Preconditions;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
/**
* Represents a ThreeJS BufferAttribute
*/
public class BufferAttribute {
private int itemSize;
private boolean normalized;
private float[] values;
/**
* Creates a new {@link BufferAttribute} with the defined item-size
*/
public BufferAttribute(float[] values, int itemSize) {
Preconditions.checkArgument(values.length % itemSize == 0, "The length of the values-array is not a multiple of the item-size!");
this.values = values;
this.itemSize = itemSize;
this.normalized = false;
}
/**
* Creates a new {@link BufferAttribute} with the defined item-size and the
* defined threejs "normalized" attribute
*/
public BufferAttribute(float[] values, int itemSize, boolean normalized) {
Preconditions.checkArgument(values.length % itemSize == 0, "The length of the values-array is not a multiple of the item-size!");
this.values = values;
this.itemSize = itemSize;
this.normalized = normalized;
}
public void writeJson(JsonWriter json) throws IOException {
json.beginObject();
json.name("type").value("Float32Array");
json.name("itemSize").value(itemSize);
json.name("normalized").value(normalized);
json.name("array").beginArray();
for (int i = 0; i < values.length; i++) {
// rounding and remove ".0" to save string space
double d = GenericMath.round(values[i], 4);
if (d == (int) d) json.value((int) d);
else json.value(d);
}
json.endArray();
json.endObject();
}
public int getItemSize() {
return this.itemSize;
}
public boolean isNormalized() {
return this.normalized;
}
public int getValueCount() {
return this.values.length;
}
public int getItemCount() {
return Math.floorDiv(getValueCount(), getItemSize());
}
public float[] values() {
return values;
}
public static BufferAttribute readJson(JsonReader json) throws IOException {
List<Float> list = new ArrayList<>(1000);
int itemSize = 1;
boolean normalized = false;
json.beginObject(); //root
while (json.hasNext()){
String name = json.nextName();
if(name.equals("array")){
json.beginArray(); //array
while (json.hasNext()){
list.add(new Float(json.nextDouble()));
}
json.endArray(); //array
}
else if (name.equals("itemSize")) {
itemSize = json.nextInt();
}
else if (name.equals("normalized")) {
normalized = json.nextBoolean();
}
else json.skipValue();
}
json.endObject(); //root
//collect values in array
float[] values = new float[list.size()];
for (int i = 0; i < values.length; i++) {
values[i] = list.get(i);
}
return new BufferAttribute(values, itemSize, normalized);
}
}

View File

@ -28,10 +28,12 @@ import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;
import com.flowpowered.math.GenericMath;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.stream.JsonReader;
@ -39,274 +41,169 @@ import com.google.gson.stream.JsonWriter;
public class BufferGeometry {
public final float[] position, normal, color, uv;
public final MaterialGroup[] groups;
public Map<String, BufferAttribute> attributes;
public MaterialGroup[] groups;
private BufferGeometry() {}
public BufferGeometry(float[] position, float[] normal, float[] color, float[] uv, MaterialGroup[] groups) {
this.position = position;
this.normal = normal;
this.color = color;
this.uv = uv;
this.attributes = new HashMap<>();
addAttribute("position", new BufferAttribute(position, 3));
addAttribute("normal", new BufferAttribute(normal, 3));
addAttribute("color", new BufferAttribute(color, 3));
addAttribute("uv", new BufferAttribute(uv, 2));
this.groups = groups;
}
public int getFaceCount(){
return Math.floorDiv(position.length, 3);
public void addAttribute(String name, BufferAttribute attribute) {
this.attributes.put(name, attribute);
}
public boolean isValid() {
int faceCount = getFaceCount();
for (BufferAttribute attribute : attributes.values()) {
if (attribute.getItemCount() != faceCount) return false;
}
return true;
}
public int getFaceCount() {
if (attributes.isEmpty()) return 0;
BufferAttribute attribute = attributes.values().iterator().next();
return attribute.getItemCount();
}
public String toJson() {
try {
StringWriter sw = new StringWriter();
Gson gson = new GsonBuilder().create();
JsonWriter json = gson.newJsonWriter(sw);
json.beginObject();
//set special values
json.beginObject(); // main-object
// set special values
json.name("type").value("BufferGeometry");
json.name("uuid").value(UUID.randomUUID().toString().toUpperCase());
json.name("data").beginObject();
json.name("attributes").beginObject();
json.name("position");
floatArray2Json(json, position, 3, false);
json.name("normal");
floatArray2Json(json, normal, 3, true);
json.name("color");
floatArray2Json(json, color, 3, false);
json.name("uv");
floatArray2Json(json, uv, 2, false);
json.endObject(); //attributes
json.name("groups").beginArray();
//write groups into json
for (BufferGeometry.MaterialGroup g : groups){
json.name("data").beginObject(); // data
json.name("attributes").beginObject(); // attributes
for (Entry<String, BufferAttribute> entry : attributes.entrySet()) {
json.name(entry.getKey());
entry.getValue().writeJson(json);
}
json.endObject(); // attributes
json.name("groups").beginArray(); // groups
// write groups into json
for (MaterialGroup g : groups) {
json.beginObject();
json.name("materialIndex").value(g.getMaterialIndex());
json.name("start").value(g.getStart());
json.name("count").value(g.getCount());
json.endObject();
}
json.endArray(); //groups
json.endObject(); //data
json.endObject(); //main-object
//save and return
json.endArray(); // groups
json.endObject(); // data
json.endObject(); // main-object
// save and return
json.flush();
return sw.toString();
} catch (IOException e){
//since we are using a StringWriter there should never be an IO exception thrown
} catch (IOException e) {
// since we are using a StringWriter there should never be an IO exception
// thrown
throw new RuntimeException(e);
}
}
public static BufferGeometry fromJson(String jsonString) throws IOException {
Gson gson = new GsonBuilder().create();
JsonReader json = gson.newJsonReader(new StringReader(jsonString));
List<Float> positionList = new ArrayList<>(300);
List<Float> normalList = new ArrayList<>(300);
List<Float> colorList = new ArrayList<>(300);
List<Float> uvList = new ArrayList<>(200);
List<MaterialGroup> groups = new ArrayList<>(10);
json.beginObject(); //root
while (json.hasNext()){
Map<String, BufferAttribute> attributes = new HashMap<>();
json.beginObject(); // root
while (json.hasNext()) {
String name1 = json.nextName();
if(name1.equals("data")){
json.beginObject(); //data
while (json.hasNext()){
if (name1.equals("data")) {
json.beginObject(); // data
while (json.hasNext()) {
String name2 = json.nextName();
if(name2.equals("attributes")){
json.beginObject(); //attributes
while (json.hasNext()){
if (name2.equals("attributes")) {
json.beginObject(); // attributes
while (json.hasNext()) {
String name3 = json.nextName();
if(name3.equals("position")){
json2FloatList(json, positionList);
}
else if(name3.equals("normal")){
json2FloatList(json, normalList);
}
else if(name3.equals("color")){
json2FloatList(json, colorList);
}
else if(name3.equals("uv")){
json2FloatList(json, uvList);
}
else json.skipValue();
attributes.put(name3, BufferAttribute.readJson(json));
}
json.endObject(); //attributes
json.endObject(); // attributes
}
else if (name2.equals("groups")){
json.beginArray(); //groups
while (json.hasNext()){
else if (name2.equals("groups")) {
json.beginArray(); // groups
while (json.hasNext()) {
MaterialGroup group = new MaterialGroup(0, 0, 0);
json.beginObject(); //group
while (json.hasNext()){
json.beginObject(); // group
while (json.hasNext()) {
String name3 = json.nextName();
if(name3.equals("materialIndex")){
if (name3.equals("materialIndex")) {
group.setMaterialIndex(json.nextInt());
}
else if(name3.equals("start")){
else if (name3.equals("start")) {
group.setStart(json.nextInt());
}
else if(name3.equals("count")){
else if (name3.equals("count")) {
group.setCount(json.nextInt());
}
else json.skipValue();
}
json.endObject(); //group
json.endObject(); // group
groups.add(group);
}
json.endArray(); //groups
json.endArray(); // groups
}
else json.skipValue();
}
json.endObject();//data
json.endObject();// data
}
else json.skipValue();
}
json.endObject(); //root
//check if this is a valid BufferGeometry
int faceCount = Math.floorDiv(positionList.size(), 3);
if (positionList.size() != faceCount * 3) throw new IllegalArgumentException("Wrong count of positions! (Got " + positionList.size() + " but expected " + (faceCount * 3) + ")");
if (normalList.size() != faceCount * 3) throw new IllegalArgumentException("Wrong count of normals! (Got " + normalList.size() + " but expected " + (faceCount * 3) + ")");
if (colorList.size() != faceCount * 3) throw new IllegalArgumentException("Wrong count of colors! (Got " + colorList.size() + " but expected " + (faceCount * 3) + ")");
if (uvList.size() != faceCount * 2) throw new IllegalArgumentException("Wrong count of uvs! (Got " + uvList.size() + " but expected " + (faceCount * 2) + ")");
json.endObject(); // root
groups.sort((g1, g2) -> (int) Math.signum(g1.getStart() - g2.getStart()));
int nextGroup = 0;
for (MaterialGroup g : groups){
if(g.getStart() != nextGroup) throw new IllegalArgumentException("Group did not start at correct index! (Got " + g.getStart() + " but expected " + nextGroup + ")");
if(g.getCount() < 0) throw new IllegalArgumentException("Group has a negative count! (" + g.getCount() + ")");
for (MaterialGroup g : groups) {
if (g.getStart() != nextGroup)
throw new IllegalArgumentException("Group did not start at correct index! (Got " + g.getStart() + " but expected " + nextGroup + ")");
if (g.getCount() < 0) throw new IllegalArgumentException("Group has a negative count! (" + g.getCount() + ")");
nextGroup += g.getCount();
}
BufferGeometry bufferGeometry = new BufferGeometry();
bufferGeometry.attributes = attributes;
bufferGeometry.groups = groups.toArray(new MaterialGroup[groups.size()]);
//collect values in arrays
float[] position = new float[positionList.size()];
for (int i = 0; i < position.length; i++) {
position[i] = positionList.get(i);
}
float[] normal = new float[normalList.size()];
for (int i = 0; i < normal.length; i++) {
normal[i] = normalList.get(i);
}
float[] color = new float[colorList.size()];
for (int i = 0; i < color.length; i++) {
color[i] = colorList.get(i);
}
float[] uv = new float[uvList.size()];
for (int i = 0; i < uv.length; i++) {
uv[i] = uvList.get(i);
}
return new BufferGeometry(position, normal, color, uv,
groups.toArray(new MaterialGroup[groups.size()])
);
return bufferGeometry;
}
private static void json2FloatList(JsonReader json, List<Float> list) throws IOException {
json.beginObject(); //root
while (json.hasNext()){
String name = json.nextName();
if(name.equals("array")){
json.beginArray(); //array
while (json.hasNext()){
list.add(new Float(json.nextDouble()));
}
json.endArray(); //array
}
else json.skipValue();
}
json.endObject(); //root
}
private static void floatArray2Json(JsonWriter json, float[] array, int itemSize, boolean normalized) throws IOException {
json.beginObject();
json.name("type").value("Float32Array");
json.name("itemSize").value(itemSize);
json.name("normalized").value(normalized);
json.name("array").beginArray();
for (int i = 0; i < array.length; i++){
//rounding and remove ".0" to save string space
double d = GenericMath.round(array[i], 4);
if (d == (int) d) json.value((int) d);
else json.value(d);
}
json.endArray();
json.endObject();
}
public static class MaterialGroup {
private int materialIndex;
private int start;
private int count;
public MaterialGroup(int materialIndex, int start, int count) {
this.materialIndex = materialIndex;
this.start = start;
this.count = count;
}
public int getMaterialIndex() {
return materialIndex;
}
public int getStart() {
return start;
}
public int getCount() {
return count;
}
public void setMaterialIndex(int materialIndex) {
this.materialIndex = materialIndex;
}
public void setStart(int start) {
this.start = start;
}
public void setCount(int count) {
this.count = count;
}
}
}

View File

@ -0,0 +1,64 @@
/*
* 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.
*/
/**
*
*/
package de.bluecolored.bluemap.core.threejs;
public class MaterialGroup {
private int materialIndex;
private int start;
private int count;
public MaterialGroup(int materialIndex, int start, int count) {
this.materialIndex = materialIndex;
this.start = start;
this.count = count;
}
public int getMaterialIndex() {
return materialIndex;
}
public int getStart() {
return start;
}
public int getCount() {
return count;
}
public void setMaterialIndex(int materialIndex) {
this.materialIndex = materialIndex;
}
public void setStart(int start) {
this.start = start;
}
public void setCount(int count) {
this.count = count;
}
}

View File

@ -38,8 +38,8 @@ public class ModelUtils {
/**
* Creates a plane-grid with alternating face-rotations.
*/
public static Model makeGrid(Vector2i gridSize){
Model m = new Model();
public static Model<Face> makeGrid(Vector2i gridSize){
Model<Face> m = new Model<Face>();
float y = 0;

View File

@ -30,9 +30,11 @@ import java.util.Collection;
import java.util.stream.Collectors;
import com.flowpowered.math.vector.Vector2i;
import com.flowpowered.math.vector.Vector3f;
import de.bluecolored.bluemap.core.config.MainConfig.MapConfig;
import de.bluecolored.bluemap.core.render.TileRenderer;
import de.bluecolored.bluemap.core.util.MathUtils;
import de.bluecolored.bluemap.core.world.World;
import ninja.leaping.configurate.ConfigurationNode;
import ninja.leaping.configurate.gson.GsonConfigurationLoader;
@ -94,17 +96,17 @@ public class WebSettings {
}
public Collection<String> getMapIds() {
return rootNode.getChildrenMap().keySet().stream().map(o -> o.toString()).collect(Collectors.toSet());
return rootNode.getNode("maps").getChildrenMap().keySet().stream().map(o -> o.toString()).collect(Collectors.toSet());
}
public void setAllEnabled(boolean enabled) {
for (ConfigurationNode mapNode : rootNode.getChildrenMap().values()) {
public void setAllMapsEnabled(boolean enabled) {
for (ConfigurationNode mapNode : rootNode.getNode("maps").getChildrenMap().values()) {
mapNode.getNode("enabled").setValue(enabled);
}
}
public void setEnabled(boolean enabled, String mapId) {
set(enabled, mapId, "enabled");
public void setMapEnabled(boolean enabled, String mapId) {
set(enabled, "maps", mapId, "enabled");
}
public void setFrom(TileRenderer tileRenderer, String mapId) {
@ -113,56 +115,60 @@ public class WebSettings {
Vector2i lowresTileSize = tileRenderer.getLowresModelManager().getTileSize();
Vector2i lowresPointsPerHiresTile = tileRenderer.getLowresModelManager().getPointsPerHiresTile();
set(hiresTileSize.getX(), mapId, "hires", "tileSize", "x");
set(hiresTileSize.getY(), mapId, "hires", "tileSize", "z");
set(1, mapId, "hires", "scale", "x");
set(1, mapId, "hires", "scale", "z");
set(gridOrigin.getX(), mapId, "hires", "translate", "x");
set(gridOrigin.getY(), mapId, "hires", "translate", "z");
set(hiresTileSize.getX(), "maps", mapId, "hires", "tileSize", "x");
set(hiresTileSize.getY(), "maps", mapId, "hires", "tileSize", "z");
set(1, "maps", mapId, "hires", "scale", "x");
set(1, "maps", mapId, "hires", "scale", "z");
set(gridOrigin.getX(), "maps", mapId, "hires", "translate", "x");
set(gridOrigin.getY(), "maps", mapId, "hires", "translate", "z");
Vector2i pointSize = hiresTileSize.div(lowresPointsPerHiresTile);
Vector2i tileSize = pointSize.mul(lowresTileSize);
set(tileSize.getX(), mapId, "lowres", "tileSize", "x");
set(tileSize.getY(), mapId, "lowres", "tileSize", "z");
set(pointSize.getX(), mapId, "lowres", "scale", "x");
set(pointSize.getY(), mapId, "lowres", "scale", "z");
set(pointSize.getX() / 2, mapId, "lowres", "translate", "x");
set(pointSize.getY() / 2, mapId, "lowres", "translate", "z");
set(tileSize.getX(), "maps", mapId, "lowres", "tileSize", "x");
set(tileSize.getY(), "maps", mapId, "lowres", "tileSize", "z");
set(pointSize.getX(), "maps", mapId, "lowres", "scale", "x");
set(pointSize.getY(), "maps", mapId, "lowres", "scale", "z");
set(pointSize.getX() / 2, "maps", mapId, "lowres", "translate", "x");
set(pointSize.getY() / 2, "maps", mapId, "lowres", "translate", "z");
}
public void setFrom(World world, String mapId) {
set(world.getSpawnPoint().getX(), mapId, "startPos", "x");
set(world.getSpawnPoint().getZ(), mapId, "startPos", "z");
set(world.getSpawnPoint().getX(), "maps", mapId, "startPos", "x");
set(world.getSpawnPoint().getZ(), "maps", mapId, "startPos", "z");
}
public void setFrom(MapConfig mapConfig, String mapId) {
Vector2i startPos = mapConfig.getStartPos();
if (startPos != null) {
set(startPos.getX(), mapId, "startPos", "x");
set(startPos.getY(), mapId, "startPos", "z");
set(startPos.getX(), "maps", mapId, "startPos", "x");
set(startPos.getY(), "maps", mapId, "startPos", "z");
}
set(mapConfig.getLowresViewDistance(), mapId, "lowres", "viewDistance");
set(mapConfig.getHiresViewDistance(), mapId, "hires", "viewDistance");
Vector3f skyColor = MathUtils.color3FromInt(mapConfig.getSkyColor());
set(skyColor.getX(), "maps", mapId, "skyColor", "r");
set(skyColor.getY(), "maps", mapId, "skyColor", "g");
set(skyColor.getZ(), "maps", mapId, "skyColor", "b");
set(mapConfig.getAmbientLight(), "maps", mapId, "ambientLight");
setName(mapConfig.getName(), mapId);
}
public void setOrdinal(int ordinal, String mapId) {
set(ordinal, mapId, "ordinal");
set(ordinal, "maps", mapId, "ordinal");
}
public int getOrdinal(String mapId) {
return getInt(mapId, "ordinal");
return getInt("maps", mapId, "ordinal");
}
public void setName(String name, String mapId) {
set(name, mapId, "name");
set(name, "maps", mapId, "name");
}
public String getName(String mapId) {
return getString(mapId, "name");
return getString("maps", mapId, "name");
}
}

View File

@ -65,11 +65,11 @@ public class Block {
return pos;
}
public double getSunLightLevel() {
public float getSunLightLevel() {
return lightData.getSkyLight();
}
public double getBlockLightLevel() {
public float getBlockLightLevel() {
return lightData.getBlockLight();
}
@ -108,15 +108,15 @@ public class Block {
}
private void calculateLight() {
sunLight = (float) getSunLightLevel();
blockLight = (float) getBlockLightLevel();
sunLight = getSunLightLevel();
blockLight = getBlockLightLevel();
if (blockLight > 0 || sunLight > 0) return;
for (Direction direction : Direction.values()) {
Block neighbor = getRelativeBlock(direction);
sunLight = (float) Math.max(neighbor.getSunLightLevel(), sunLight);
blockLight = (float) Math.max(neighbor.getBlockLightLevel(), blockLight);
sunLight = Math.max(neighbor.getSunLightLevel(), sunLight);
blockLight = Math.max(neighbor.getBlockLightLevel(), blockLight);
}
}

View File

@ -0,0 +1,5 @@
{
"variants": {
"": { "model": "block/bubble_column" }
}
}

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,52 +24,63 @@
*/
import $ from 'jquery';
import {
AmbientLight,
BackSide,
BufferGeometryLoader,
ClampToEdgeWrapping,
CubeGeometry,
DirectionalLight,
SphereGeometry,
FileLoader,
FrontSide,
Mesh,
MeshBasicMaterial,
MeshLambertMaterial,
NormalBlending,
NearestFilter,
NearestMipmapLinearFilter,
NearestMipMapLinearFilter,
PerspectiveCamera,
Scene,
ShaderMaterial,
Texture,
TextureLoader,
VertexColors,
WebGLRenderer,
Vector3,
} 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';
import { stringToImage, pathFromCoords } from './utils.js';
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 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';
import { stringToImage, pathFromCoords } from './utils.js';
import {getCookie, setCookie} from "./utils";
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.ambientLight = {
value: 0
};
this.skyColor = {
value: new Vector3(0, 0, 0)
};
this.ui = new UI(this);
this.loadingNoticeElement = $('<div>loading...</div>').appendTo($(this.element));
window.onerror = this.onLoadError;
this.fileLoader = new FileLoader();
@ -85,56 +96,58 @@ export default class BlueMap {
await this.loadHiresMaterial();
await this.loadLowresMaterial();
this.changeMap(this.map);
this.loadUserSettings();
this.handleContainerResize();
this.initModules();
this.changeMap(this.maps[0]);
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();
this.map = map;
let startPos = {
x: this.settings[this.map]["startPos"]["x"],
z: this.settings[this.map]["startPos"]["z"]
x: this.settings.maps[this.map]["startPos"]["x"],
z: this.settings.maps[this.map]["startPos"]["z"]
};
this.controls.setTileSize(this.settings[this.map]['hires']['tileSize']);
this.ambientLight.value = this.settings.maps[this.map]["ambientLight"];
this.skyColor.value.set(
this.settings.maps[this.map]["skyColor"].r,
this.settings.maps[this.map]["skyColor"].g,
this.settings.maps[this.map]["skyColor"].b
);
this.controls.setTileSize(this.settings.maps[this.map]['hires']['tileSize']);
this.controls.resetPosition();
this.controls.targetPosition.set(startPos.x, this.controls.targetPosition.y, startPos.z);
this.controls.position.copy(this.controls.targetPosition);
this.lowresTileManager = new TileManager(
this,
this.settings[this.map]['lowres']['viewDistance'],
this.lowresViewDistance,
this.loadLowresTile,
this.lowresScene,
this.settings[this.map]['lowres']['tileSize'],
this.settings.maps[this.map]['lowres']['tileSize'],
startPos
);
this.hiresTileManager = new TileManager(
this,
this.settings[this.map]['hires']['viewDistance'],
this.hiresViewDistance,
this.loadHiresTile,
this.hiresScene,
this.settings[this.map]['hires']['tileSize'],
this.settings.maps[this.map]['hires']['tileSize'],
startPos
);
@ -202,6 +215,8 @@ export default class BlueMap {
update = () => {
setTimeout(this.update, 1000);
this.saveUserSettings();
this.lowresTileManager.setPosition(this.controls.targetPosition);
if (this.camera.position.y < 400) {
this.hiresTileManager.setPosition(this.controls.targetPosition);
@ -224,13 +239,27 @@ export default class BlueMap {
render = () => {
requestAnimationFrame(this.render);
//update controls
if (this.controls.update()) this.updateFrame = true;
//update lighting
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;
this.updateFrame = true;
}
//don't render if nothing has changed
if (!this.updateFrame) return;
this.updateFrame = false;
//render event
document.dispatchEvent(new Event('bluemap-update-frame'));
//render
this.skyboxCamera.rotation.copy(this.camera.rotation);
this.skyboxCamera.updateProjectionMatrix();
@ -264,19 +293,18 @@ export default class BlueMap {
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){
for (let map in this.settings.maps) {
if (this.settings["maps"].hasOwnProperty(map) && this.settings.maps[map].enabled){
this.maps.push(map);
}
}
this.maps.sort((map1, map2) => {
var sort = this.settings[map1].ordinal - this.settings[map2].ordinal;
var sort = this.settings.maps[map1].ordinal - this.settings.maps[map2].ordinal;
if (isNaN(sort)) return 0;
return sort;
});
this.map = this.maps[0];
resolve();
});
});
@ -302,58 +330,65 @@ export default class BlueMap {
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.renderer.domElement).addClass("map-canvas").appendTo(this.element);
this.handleContainerResize();
$(window).resize(this.handleContainerResize);
}
loadUserSettings(){
if (!this.settings["useCookies"]) return;
this.mobSpawnOverlay.value = this.loadUserSetting("mobSpawnOverlay", this.mobSpawnOverlay.value);
this.targetSunLightStrength = this.loadUserSetting("sunLightStrength", this.targetSunLightStrength);
this.quality = this.loadUserSetting("renderQuality", this.quality);
this.hiresViewDistance = this.loadUserSetting("hiresViewDistance", this.hiresViewDistance);
this.lowresViewDistance = this.loadUserSetting("lowresViewDistance", this.lowresViewDistance);
}
saveUserSettings(){
if (!this.settings["useCookies"]) return;
if (this.savedUserSettings === undefined) this.savedUserSettings = {};
this.saveUserSetting("mobSpawnOverlay", this.mobSpawnOverlay.value);
this.saveUserSetting("sunLightStrength", this.targetSunLightStrength);
this.saveUserSetting("renderQuality", this.quality);
this.saveUserSetting("hiresViewDistance", this.hiresViewDistance);
this.saveUserSetting("lowresViewDistance", this.lowresViewDistance);
}
loadUserSetting(key, defaultValue){
let value = getCookie("bluemap-" + key);
if (value === undefined) return defaultValue;
return value;
}
saveUserSetting(key, value){
if (this.savedUserSettings[key] !== value){
this.savedUserSettings[key] = value;
setCookie("bluemap-" + key, value);
}
}
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,
ambientLight: this.ambientLight,
skyColor: this.skyColor,
},
vertexShader: SKY_VERTEX_SHADER,
fragmentShader: SKY_FRAGMENT_SHADER,
side: BackSide
});
return new Mesh(geometry, material);
}
@ -367,16 +402,6 @@ export default class BlueMap {
let opaque = t['color'][3] === 1;
let transparent = t['transparent'];
let material = new MeshLambertMaterial({
transparent: transparent,
alphaTest: transparent ? 0 : (opaque ? 1 : 0.01),
depthWrite: true,
depthTest: true,
blending: NormalBlending,
vertexColors: VertexColors,
side: FrontSide,
wireframe: false
});
let texture = new Texture();
texture.image = stringToImage(t['texture']);
@ -384,16 +409,36 @@ 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;
texture.flatShading = true;
texture.needsUpdate = true;
material.map = texture;
material.needsUpdate = true;
let uniforms = {
texture: {
type: 't',
value: texture
},
sunlightStrength: this.sunLightStrength,
mobSpawnOverlay: this.mobSpawnOverlay,
ambientLight: this.ambientLight,
};
let material = new ShaderMaterial({
uniforms: uniforms,
vertexShader: HIRES_VERTEX_SHADER,
fragmentShader: HIRES_FRAGMENT_SHADER,
transparent: transparent,
depthWrite: true,
depthTest: true,
vertexColors: VertexColors,
side: FrontSide,
wireframe: false,
});
material.needsUpdate = true;
materials[i] = material;
}
@ -404,7 +449,13 @@ export default class BlueMap {
}
async loadLowresMaterial() {
this.lowresMaterial = new MeshLambertMaterial({
this.lowresMaterial = new ShaderMaterial({
uniforms: {
sunlightStrength: this.sunLightStrength,
ambientLight: this.ambientLight,
},
vertexShader: LOWRES_VERTEX_SHADER,
fragmentShader: LOWRES_FRAGMENT_SHADER,
transparent: false,
depthWrite: true,
depthTest: true,
@ -423,9 +474,9 @@ export default class BlueMap {
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'];
let tileSize = this.settings.maps[this.map]['hires']['tileSize'];
let translate = this.settings.maps[this.map]['hires']['translate'];
let scale = this.settings.maps[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);
@ -444,9 +495,9 @@ export default class BlueMap {
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'];
let tileSize = this.settings.maps[this.map]['lowres']['tileSize'];
let translate = this.settings.maps[this.map]['lowres']['translate'];
let scale = this.settings.maps[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);
@ -460,24 +511,22 @@ 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>
`);
};
// ###### 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);
@ -506,4 +555,5 @@ export default class BlueMap {
displayAlert();
}
}

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,26 +24,38 @@
*/
import $ from 'jquery';
import { getTopRightElement } from './Module.js';
import Dropdown from "../ui/Dropdown";
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);
export default class MapSelection extends Dropdown {
constructor(bluemap) {
super(undefined);
super.onChange = this.onChangeMap;
this.bluemap = bluemap;
//add maps
const maps = this.bluemap.settings.maps;
for (let mapId of this.bluemap.maps) {
const map = maps[mapId];
if (!map.enabled) continue;
this.addOption(mapId, map.name);
}
$(document).on('bluemap-map-change', this.onBlueMapMapChange);
}
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.addClass("map-selection");
return element;
}
onChangeMap = value => {
this.bluemap.changeMap(value);
};
onBlueMapMapChange = () => {
this.select(this.bluemap.map);
};
}

View File

@ -24,40 +24,34 @@
*/
import $ from 'jquery';
import { getTopLeftElement } from './Module.js';
import ToggleButton from '../ui/ToggleButton.js';
export default class MapMenu {
constructor(blueMap) {
this.bluemap = blueMap;
const maps = this.bluemap.settings;
import BURGER from '../../../assets/burger.svg';
$('#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));
export default class MenuButton extends ToggleButton {
constructor(menu) {
super(undefined, false, undefined, BURGER);
this.menu = menu;
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);
this.menu.element.on('menu-close menu-open', this.updateMenuState);
}
onMapClick = event => {
const map = $(event.target).attr('map');
this.bluemap.changeMap(map);
createElement(){
let element = super.createElement();
element.click(this.onMenuClick);
return element;
}
updateMenuState = () => {
this.selected = this.menu.isOpen();
this.update();
};
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);
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

@ -0,0 +1,81 @@
/*
* 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 HIRES_FRAGMENT_SHADER = `
uniform sampler2D texture;
uniform float sunlightStrength;
uniform float ambientLight;
uniform bool mobSpawnOverlay;
varying vec3 vPosition;
varying vec3 vWorldPosition;
varying vec3 vNormal;
varying vec2 vUv;
varying vec3 vColor;
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;
//apply light
float light = max(vSunlight * sunlightStrength, vBlocklight);
color.rgb *= max(light / 15.0, ambientLight);
gl_FragColor = color;
}
`;
export default HIRES_FRAGMENT_SHADER;

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.
*/
const HIRES_VERTEX_SHADER = `
attribute float ao;
attribute float sunlight;
attribute float blocklight;
varying vec3 vPosition;
varying vec3 vWorldPosition;
varying vec3 vNormal;
varying vec2 vUv;
varying vec3 vColor;
varying float vAo;
varying float vSunlight;
varying float vBlocklight;
void main() {
vPosition = position;
vWorldPosition = (vec4(position, 1) * modelMatrix).xyz;
vNormal = normal;
vUv = uv;
vColor = color;
vAo = ao;
vSunlight = sunlight;
vBlocklight = blocklight;
gl_Position =
projectionMatrix *
viewMatrix *
modelMatrix *
vec4(position, 1);
}
`;
export default HIRES_VERTEX_SHADER;

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.
*/
const LOWRES_FRAGMENT_SHADER = `
uniform float sunlightStrength;
uniform float ambientLight;
varying vec3 vPosition;
varying vec3 vNormal;
varying vec2 vUv;
varying vec3 vColor;
void main() {
vec4 color = vec4(vColor, 1.0);
float diff = sqrt(max(dot(vNormal, vec3(0.3637, 0.7274, 0.5819)), 0.0)) * 0.4 + 0.6;
color *= diff;
color *= max(sunlightStrength, ambientLight);
gl_FragColor = color;
}
`;
export default LOWRES_FRAGMENT_SHADER;

View File

@ -0,0 +1,45 @@
/*
* 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 LOWRES_VERTEX_SHADER = `
varying vec3 vPosition;
varying vec3 vNormal;
varying vec2 vUv;
varying vec3 vColor;
void main() {
vPosition = position;
vNormal = normal;
vUv = uv;
vColor = color;
gl_Position =
projectionMatrix *
modelViewMatrix *
vec4(position, 1);
}
`;
export default LOWRES_VERTEX_SHADER;

View File

@ -0,0 +1,41 @@
/*
* 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;
uniform float ambientLight;
uniform vec3 skyColor;
varying vec3 vPosition;
void main() {
vec4 color = vec4(skyColor * max(sunlightStrength, ambientLight), 1.0);
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
this.closeAll();
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", this.blueMap.quality === 2);
quality.addOption("1", "normal", this.blueMap.quality === 1);
quality.addOption("0.5", "low", this.blueMap.quality === 0.5);
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

@ -62,5 +62,44 @@ export const splitNumberToPath = num => {
export const hashTile = (x, z) => `x${x}z${z}`;
/**
* Adapted from https://www.w3schools.com/js/js_cookies.asp
*/
export const setCookie = (key, value, days = 360) => {
value = JSON.stringify(value);
let expireDate = new Date();
expireDate.setTime(expireDate.getTime() + days * 24 * 60 * 60 * 1000);
document.cookie = key + "=" + value + ";" + "expires=" + expireDate.toUTCString();
};
/**
* Adapted from https://www.w3schools.com/js/js_cookies.asp
*/
export const getCookie = key => {
let cookieString = decodeURIComponent(document.cookie);
let cookies = cookieString.split(';');
for(let i = 0; i < cookies.length; i++) {
let cookie = cookies[i];
while (cookie.charAt(0) === ' ') {
cookie = cookie.substring(1);
}
if (cookie.indexOf(key + "=") === 0) {
let value = cookie.substring(key.length + 1, cookie.length);
try {
value = JSON.parse(value);
} catch (e) {}
return value;
}
}
return undefined;
};
export const Vector2_ZERO = new Vector2(0, 0);
export const Vector3_ZERO = new Vector3(0, 0, 0);

View File

@ -0,0 +1,18 @@
// colors
$normal_fg: #333;
$normal_bg: #fff;
$label_fg: #666;
$hover_fg: $normal_fg;
$hover_bg: #aaa;
$active_fg: $normal_bg;
$active_bg: $normal_fg;
$line_color: #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: $label_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,103 @@
@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";
@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";
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%;
pointer-events: none;
z-index: 100;
> * {
pointer-events: auto;
}
>:last-child {
> .menu {
position: relative;
flex-shrink: 0;
z-index: 200;
filter: drop-shadow(1px 1px 3px #0008);
@media (max-width: $middle-max) {
position: absolute;
}
}
> .hud {
position: relative;
width: 100%;
border-top: solid 1px #dddddd;
pointer-events: none;
filter: drop-shadow(1px 1px 3px #0008);
> * {
pointer-events: auto;
}
}
}
}
#bluemap-topleft > *:not(:last-child) {
border-right: solid 1px #dddddd;
}

View File

@ -0,0 +1,39 @@
.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: $hover_bg;
color: $hover_fg;
}
&:active {
background-color: $active_bg;
color: $active_fg;
> img {
filter: invert(1);
}
}
> img {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
}

View File

@ -0,0 +1,86 @@
.bluemap-container .ui .ui-element.dropdown {
padding: 0;
cursor: pointer;
overflow: visible;
user-select: none;
> .header {
height: 100%;
padding-right: 1rem;
&:hover {
background-color: $hover_bg;
color: $hover_fg;
&::after {
border-color: $hover_fg transparent transparent transparent;
}
}
> .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 {
background-color: $hover_bg;
color: $hover_fg;
&::after {
top: calc(50% - 0.6rem);
border-color: transparent transparent $hover_fg transparent;
}
}
> .select {
position: absolute;
top: 100%;
left: 0;
width: 100%;
overflow-x: hidden;
max-height: 300px;
overflow-y: auto;
z-index: 110;
> .option {
background-color: $normal_bg;
color: $normal_fg;
&:hover {
background-color: $hover_bg;
color: $hover_fg;
}
&.selected {
background-color: $active_bg;
color: $active_fg;
}
}
}
.option {
padding: 0 0.5rem;
}
}

View File

@ -0,0 +1,12 @@
.bluemap-container .ui .ui-element {
position: relative;
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: $label_fg;
}

View File

@ -0,0 +1,127 @@
$menu-width: 375px;
.bluemap-container .ui .menu {
position: relative;
height: 100%;
width: $menu-width;
max-width: 100%;
background-color: $normal_bg;
color: $normal_fg;
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: 2.5rem;
line-height: 2.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: 3rem;
width: calc(375px - 1rem);
height: calc(100% - 3rem);
padding: 0.5rem;
overflow-y: auto;
@media (max-width: $menu-width) {
width: calc(100% - 1rem);
}
> .separator {
position: relative;
left: -0.5rem;
width: calc(100% + 1rem);
border-top: solid 1px $line_color;
margin: 0.5rem 0;
}
> .label {
min-height: 0;
font-size: 0.8rem;
padding: 1rem 0.5rem 0.1rem 1rem;
line-height: 0.8rem;
}
> .dropdown > .select {
border: solid 1px $line_color;
border-top: none;
width: calc(100% - 2px);
}
// a little hacky to force not displaying any icon
> .toggle-button.icon {
> .label {
display: inline;
}
> img {
display: none;
}
> .switch {
display: block;
}
&.selected:not(:hover) {
background-color: $normal_bg;
color: $normal_fg;
}
&:hover {
background-color: $hover_bg;
color: $hover_fg;
}
}
}
}

View File

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

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,76 @@
.bluemap-container .ui .ui-element.toggle-button {
&:not(.icon) {
padding-right: 2.75rem;
}
&:active {
background-color: $hover_bg;
color: $hover_fg;
> img {
filter: invert(0);
}
}
&.icon {
&.selected {
background-color: $active_bg;
color: $active_fg;
> 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: $normal_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: $normal_bg;
transition: left 0.2s;
}
}
&.selected > .switch {
background-color: #008800;
&::after {
left: calc(100% - 1rem);
}
}
&.icon > .switch {
display: none;
}
}

View File

@ -0,0 +1,96 @@
.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: $normal_bg;
> .mobile-hide {
display: none;
}
}
@media (max-width: $super-small-max) {
flex-wrap: wrap;
}
> * {
pointer-events: auto;
}
> .ui-element {
flex-shrink: 0;
background-color: $normal_bg;
color: $normal_fg;
@media (max-width: $small-max) {
border-top: solid 1px $line_color;
margin-top: -1px;
}
@media (max-width: $super-small-max) {
flex-grow: 1;
}
}
> .ui-element:not(.separator) + .ui-element:not(.separator) {
border-left: solid 1px $line_color;
margin-left: -1px;
}
> .ui-element.separator {
width: 10px;
flex-shrink: 0;
@media (max-width: $small-max) {
width: 0;
border-left: solid 1px $line_color;
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 $line_color;
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,96 @@
.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: $normal_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;
padding: 1rem;
margin: 1rem;
background-color: $normal_bg;
color: $normal_fg;
pointer-events: all;
}
}
}

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({

View File

@ -3,6 +3,7 @@ metrics: false
renderThreadCount: -2
data: "bluemap"
webroot: "bluemap/web"
useCookies: true
webserver {
enabled: true
port: 8100

View File

@ -32,6 +32,9 @@ webroot: "bluemap/web"
# Default is "<webroot>/data"
#webdata: "path/to/data/folder"
# If the web-application should use cookies to save the configurations of a user.
useCookies: true
webserver {
# With this setting you can disable the integrated web-server.
# This is usefull if you want to only render the map-data for later use, or if you setup your own webserver.
@ -60,40 +63,43 @@ maps: [
{
# The id of this map
# Should only contain word-charactes: [a-zA-Z0-9_]
# Changing this value breaks your existing renders.
id: "world"
# The name of this map
# This defines the display name of this map, you can change this at any time
# This defines the display name of this map, you can change this at any time.
# Default is the id of this map
name: "World"
# The path to the save-folder of the world to render
# The path to the save-folder of the world to render.
world: "world"
# The position on the world where the map will be centered if you open it.
# You can change this at any time.
# This defaults to the world-spawn if you don't set it.
#startPos: [500, -820]
# The color of thy sky as a hex-color
# You can change this at any time.
# Default is "#7dabff"
skyColor: "#7dabff"
# Defines the ambient light-strength that every block is recieving, regardless of the sunlight/blocklight.
# 0 is no ambient light, 1 is fully lighted.
# You can change this at any time.
# Default is 0
ambientLight: 0
# If this is false, BlueMap tries to omit all blocks that are not visible from above-ground.
# More specific: Block-Faces that have a sunlight/skylight value of 0 are removed.
# This improves the performance of the map on slower devices by a lot, but might cause some blocks to disappear that should normally be visible.
# Changing this value requires a re-render of the map.
# Default is false
renderCaves: false
# AmbientOcclusion adds soft shadows into corners, which gives the map a much better look.
# This has only a small impact on render-time and has no impact on the web-performance of the map.
# The value defines the strength of the shading, a value of 0 disables ambientOcclusion.
# Default is 0.25
ambientOcclusion: 0.25
# Lighting uses the light-data in minecraft to shade each block-face.
# If this is enabled, caves and inside buildings without torches will be darker.
# The value defines the strength of the shading and a value of 0 disables lighting (every block will be fully lit).
# Default is 0.8
lighting: 0.8
# With the below values you can limit the map-render.
# This can be used to ignore the nethers ceiling or render only a certain part of a world.
# Changing this values might require a re-render of the map, already rendered tiles outside the limits will not be deleted.
# Default is no min or max value (= infinite bounds)
#minX: -4000
#maxX: 4000
@ -105,43 +111,17 @@ maps: [
# Using this, BlueMap pretends that every Block out of the defined render-bounds is AIR,
# this means you can see the blocks where the world is cut (instead of having a see-through/xray view).
# This has only an effect if you set some render-bounds above.
# Default is enabled
# Changing this value requires a re-render of the map.
# Default is true
renderEdges: true
# With this set to true, the generated files for this world are compressed using gzip to save A LOT of space.
# Files will be only 5% as big with compression!
# Note: If you are using NGINX or Apache to host your map, you can configure them to serve the compressed files directly.
# This is much better than disabling the compression.
# Changing this value requires a re-render of the map.
# Default is true
useCompression: true
# HIRES is the high-resolution render of the map. Where you see every block.
hires {
# Defines the size of one map-tile in blocks.
# If you change this value, the lowres values might need adjustment as well!
# Default is 32
tileSize: 32
# The View-Distance for hires tiles on the web-map (the value is the radius in tiles)
# Default is 4.5
viewDistance: 4.5
}
# LOWRES is the low-resolution render of the map. Thats the model that you see if you zoom far out to get an overview.
lowres {
# Defines resolution of the lowres model. E.g. If the hires.tileSize is 32, a value of 4 means that every 8*8 blocks will be summarized by one point on the lowres map.
# Calculation: 32 / 4 = 8
# You can only use values that result in an integer if you use the above calculation!
# Default is 4
pointsPerHiresTile: 4
# Defines the size of one lowres-map-tile in points.
# Default is 50
pointsPerLowresTile: 50
# The View-Distance for lowres tiles on the web-map (the value is the radius in tiles)
# Default is 7
viewDistance: 7
}
}
# Here another example for the End-Map
@ -154,8 +134,8 @@ maps: [
# In the end is no sky-light, so we need to enable this or we won't see anything.
renderCaves: true
# Same here, we don't want a dark map. But not completely disabled, so we see the effect of e.g torches.
lighting: 0.4
# Same here, we don't want a dark map. But not completely lighted, so we see the effect of e.g torches.
ambientLight: 0.6
}
# Here another example for the Nether-Map
@ -165,7 +145,7 @@ maps: [
world: "world/DIM-1"
renderCaves: true
lighting: 0.6
ambientLight: 0.6
# We slice the whole world at y:90 so every block above 90 will be air.
# This way we don't render the nethers ceiling.