mirror of
https://github.com/BlueMap-Minecraft/BlueMap.git
synced 2024-11-22 10:35:16 +01:00
Merge branch 'feature/marker' into mc/1.13
This commit is contained in:
commit
6acc75674c
3
.gitignore
vendored
3
.gitignore
vendored
@ -29,3 +29,6 @@ package-lock.json
|
||||
# exclude generated resource
|
||||
BlueMapCore/src/main/resources/webroot.zip
|
||||
BlueMapCore/src/main/resources/resourceExtensions.zip
|
||||
|
||||
#exclude-test-data
|
||||
data/test-render
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 1d7495dffd6d7c72e22a99888a277eb17de55d31
|
||||
Subproject commit 51ea1fe8d1e48eeeeb5e71af9e3d12c371214d89
|
@ -24,6 +24,11 @@
|
||||
*/
|
||||
package de.bluecolored.bluemap.common.api;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
@ -31,19 +36,23 @@
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
import de.bluecolored.bluemap.api.BlueMapAPI;
|
||||
import de.bluecolored.bluemap.api.renderer.BlueMapMap;
|
||||
import de.bluecolored.bluemap.api.renderer.BlueMapWorld;
|
||||
import de.bluecolored.bluemap.api.renderer.Renderer;
|
||||
import de.bluecolored.bluemap.common.MapType;
|
||||
import de.bluecolored.bluemap.common.api.marker.MarkerAPIImpl;
|
||||
import de.bluecolored.bluemap.common.plugin.Plugin;
|
||||
import de.bluecolored.bluemap.core.BlueMap;
|
||||
import de.bluecolored.bluemap.core.world.World;
|
||||
|
||||
public class BlueMapAPIImpl extends BlueMapAPI {
|
||||
|
||||
private static final String IMAGE_ROOT_PATH = "images";
|
||||
|
||||
public Plugin blueMap;
|
||||
public RendererImpl renderer;
|
||||
public RenderAPIImpl renderer;
|
||||
|
||||
public Map<UUID, BlueMapWorldImpl> worlds;
|
||||
public Map<String, BlueMapMapImpl> maps;
|
||||
@ -51,7 +60,7 @@ public class BlueMapAPIImpl extends BlueMapAPI {
|
||||
public BlueMapAPIImpl(Plugin blueMap) {
|
||||
this.blueMap = blueMap;
|
||||
|
||||
this.renderer = new RendererImpl(this, blueMap.getRenderManager());
|
||||
this.renderer = new RenderAPIImpl(this, blueMap.getRenderManager());
|
||||
|
||||
worlds = new HashMap<>();
|
||||
for (World world : blueMap.getWorlds()) {
|
||||
@ -67,10 +76,15 @@ public BlueMapAPIImpl(Plugin blueMap) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Renderer getRenderer() {
|
||||
public RenderAPIImpl getRenderAPI() {
|
||||
return renderer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MarkerAPIImpl getMarkerAPI() throws IOException {
|
||||
return new MarkerAPIImpl(this, blueMap.getMainConfig().getWebDataPath().resolve("markers.json").toFile());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<BlueMapMap> getMaps() {
|
||||
return Collections.unmodifiableCollection(maps.values());
|
||||
@ -81,6 +95,27 @@ public Collection<BlueMapWorld> getWorlds() {
|
||||
return Collections.unmodifiableCollection(worlds.values());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String createImage(BufferedImage image, String path) throws IOException {
|
||||
path = path.replaceAll("[^a-zA-Z_\\.\\-\\/]", "_");
|
||||
String separator = FileSystems.getDefault().getSeparator();
|
||||
|
||||
Path webRoot = blueMap.getMainConfig().getWebRoot().toAbsolutePath();
|
||||
Path webDataRoot = blueMap.getMainConfig().getWebDataPath().toAbsolutePath();
|
||||
|
||||
Path imagePath;
|
||||
if (webDataRoot.startsWith(webRoot)) {
|
||||
imagePath = webDataRoot.resolve(Paths.get(IMAGE_ROOT_PATH, path.replace("/", separator))).toAbsolutePath();
|
||||
} else {
|
||||
imagePath = webRoot.resolve("assets").resolve(Paths.get(IMAGE_ROOT_PATH, path.replace("/", separator))).toAbsolutePath();
|
||||
}
|
||||
|
||||
if (!ImageIO.write(image, "png", imagePath.toFile()))
|
||||
throw new IOException("The format 'png' is not supported!");
|
||||
|
||||
return webRoot.relativize(imagePath).toString().replace(separator, "/");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBlueMapVersion() {
|
||||
return BlueMap.VERSION;
|
||||
|
@ -30,15 +30,15 @@
|
||||
import com.flowpowered.math.vector.Vector3i;
|
||||
|
||||
import de.bluecolored.bluemap.api.renderer.BlueMapMap;
|
||||
import de.bluecolored.bluemap.api.renderer.Renderer;
|
||||
import de.bluecolored.bluemap.api.renderer.RenderAPI;
|
||||
import de.bluecolored.bluemap.common.RenderManager;
|
||||
|
||||
public class RendererImpl implements Renderer {
|
||||
public class RenderAPIImpl implements RenderAPI {
|
||||
|
||||
private BlueMapAPIImpl api;
|
||||
private RenderManager renderManager;
|
||||
|
||||
protected RendererImpl(BlueMapAPIImpl api, RenderManager renderManager) {
|
||||
protected RenderAPIImpl(BlueMapAPIImpl api, RenderManager renderManager) {
|
||||
this.api = api;
|
||||
this.renderManager = renderManager;
|
||||
}
|
@ -0,0 +1,163 @@
|
||||
/*
|
||||
* 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.common.api.marker;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import de.bluecolored.bluemap.api.marker.MarkerAPI;
|
||||
import de.bluecolored.bluemap.api.marker.MarkerSet;
|
||||
import de.bluecolored.bluemap.common.api.BlueMapAPIImpl;
|
||||
import de.bluecolored.bluemap.core.logger.Logger;
|
||||
import ninja.leaping.configurate.ConfigurationNode;
|
||||
import ninja.leaping.configurate.gson.GsonConfigurationLoader;
|
||||
|
||||
public class MarkerAPIImpl implements MarkerAPI {
|
||||
|
||||
private BlueMapAPIImpl api;
|
||||
private File markerFile;
|
||||
private Map<String, MarkerSetImpl> markerSets;
|
||||
private Set<String> removedMarkerSets;
|
||||
|
||||
public MarkerAPIImpl(BlueMapAPIImpl api, File markerFile) throws IOException {
|
||||
this.api = api;
|
||||
this.markerFile = markerFile;
|
||||
|
||||
this.markerSets = new ConcurrentHashMap<>();
|
||||
this.removedMarkerSets = Sets.newConcurrentHashSet();
|
||||
|
||||
load();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<MarkerSet> getMarkerSets() {
|
||||
return Collections.unmodifiableCollection(this.markerSets.values());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<MarkerSet> getMarkerSet(String id) {
|
||||
return Optional.ofNullable(this.markerSets.get(id));
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized MarkerSet createMarkerSet(String id) {
|
||||
MarkerSetImpl set = this.markerSets.get(id);
|
||||
|
||||
if (set == null) {
|
||||
set = new MarkerSetImpl(id);
|
||||
this.markerSets.put(id, set);
|
||||
}
|
||||
|
||||
return set;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean removeMarkerSet(String id) {
|
||||
if (this.markerSets.remove(id) != null) {
|
||||
this.removedMarkerSets.add(id);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void load() throws IOException {
|
||||
this.removedMarkerSets.clear();
|
||||
|
||||
if (!markerFile.exists()) {
|
||||
markerFile.getParentFile().mkdirs();
|
||||
markerFile.createNewFile();
|
||||
}
|
||||
|
||||
GsonConfigurationLoader loader = GsonConfigurationLoader.builder().setFile(markerFile).build();
|
||||
ConfigurationNode node = loader.load();
|
||||
|
||||
Set<String> externallyRemovedSets = new HashSet<>(markerSets.keySet());
|
||||
for (ConfigurationNode markerSetNode : node.getNode("markerSets").getChildrenList()) {
|
||||
String setId = markerSetNode.getNode("id").getString();
|
||||
if (setId == null) continue;
|
||||
|
||||
externallyRemovedSets.remove(setId);
|
||||
MarkerSetImpl set = markerSets.get(setId);
|
||||
|
||||
if (set == null) {
|
||||
set = new MarkerSetImpl(setId);
|
||||
}
|
||||
|
||||
try {
|
||||
set.load(api, markerSetNode);
|
||||
markerSets.put(setId, set);
|
||||
} catch (MarkerFileFormatException ex) {
|
||||
Logger.global.logDebug("Marker-API: Failed to load marker-set '" + setId + ": " + ex);
|
||||
}
|
||||
}
|
||||
|
||||
for (String setId : externallyRemovedSets) {
|
||||
markerSets.remove(setId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void save() throws IOException {
|
||||
if (!markerFile.exists()) {
|
||||
markerFile.getParentFile().mkdirs();
|
||||
markerFile.createNewFile();
|
||||
}
|
||||
|
||||
GsonConfigurationLoader loader = GsonConfigurationLoader.builder().setFile(markerFile).build();
|
||||
ConfigurationNode node = loader.load();
|
||||
|
||||
List<? extends ConfigurationNode> markerList = node.getNode("markerSets").getChildrenList();
|
||||
node.removeChild("markerSets");
|
||||
|
||||
Set<String> newMarkers = new HashSet<>(markerSets.keySet());
|
||||
for (ConfigurationNode markerSetNode : markerList) {
|
||||
String setId = markerSetNode.getNode("id").getString();
|
||||
if (setId == null) continue;
|
||||
if (removedMarkerSets.contains(setId)) continue;
|
||||
|
||||
newMarkers.remove(setId);
|
||||
MarkerSetImpl set = markerSets.get(setId);
|
||||
|
||||
if (set != null) set.save(markerSetNode, false);
|
||||
|
||||
node.getNode("markerSets").getAppendedNode().mergeValuesFrom(markerSetNode);
|
||||
}
|
||||
|
||||
removedMarkerSets.clear();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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.common.api.marker;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class MarkerFileFormatException extends IOException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public MarkerFileFormatException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public MarkerFileFormatException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public MarkerFileFormatException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,210 @@
|
||||
/*
|
||||
* 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.common.api.marker;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import com.flowpowered.math.vector.Vector3d;
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
import de.bluecolored.bluemap.api.BlueMapAPI;
|
||||
import de.bluecolored.bluemap.api.marker.Marker;
|
||||
import de.bluecolored.bluemap.api.renderer.BlueMapMap;
|
||||
import ninja.leaping.configurate.ConfigurationNode;
|
||||
|
||||
public abstract class MarkerImpl implements Marker {
|
||||
|
||||
private final String id;
|
||||
private BlueMapMap map;
|
||||
private Vector3d postition;
|
||||
private double minDistance, maxDistance;
|
||||
private String label, link;
|
||||
private boolean newTab;
|
||||
|
||||
private boolean hasUnsavedChanges;
|
||||
|
||||
public MarkerImpl(String id, BlueMapMap map, Vector3d position) {
|
||||
Preconditions.checkNotNull(id);
|
||||
Preconditions.checkNotNull(map);
|
||||
Preconditions.checkNotNull(position);
|
||||
|
||||
this.id = id;
|
||||
this.map = map;
|
||||
this.postition = position;
|
||||
this.minDistance = 0;
|
||||
this.maxDistance = 100000;
|
||||
this.label = id;
|
||||
this.link = null;
|
||||
this.newTab = true;
|
||||
|
||||
this.hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public abstract String getType();
|
||||
|
||||
@Override
|
||||
public BlueMapMap getMap() {
|
||||
return this.map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setMap(BlueMapMap map) {
|
||||
this.map = map;
|
||||
this.hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector3d getPosition() {
|
||||
return this.postition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setPosition(Vector3d position) {
|
||||
this.postition = position;
|
||||
this.hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getMinDistance() {
|
||||
return this.minDistance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setMinDistance(double minDistance) {
|
||||
this.minDistance = minDistance;
|
||||
this.hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getMaxDistance() {
|
||||
return this.maxDistance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setMaxDistance(double maxDistance) {
|
||||
this.maxDistance = maxDistance;
|
||||
this.hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLabel() {
|
||||
return this.label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setLabel(String label) {
|
||||
this.label = label;
|
||||
this.hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<String> getLink() {
|
||||
return Optional.ofNullable(this.link);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNewTab() {
|
||||
return this.newTab;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setLink(String link, boolean newTab) {
|
||||
this.link = link;
|
||||
this.newTab = newTab;
|
||||
this.hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void removeLink() {
|
||||
this.link = null;
|
||||
this.hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
public synchronized void load(BlueMapAPI api, ConfigurationNode markerNode) throws MarkerFileFormatException {
|
||||
this.hasUnsavedChanges = false;
|
||||
|
||||
//map
|
||||
String mapId = markerNode.getNode("map").getString();
|
||||
if (mapId == null) throw new MarkerFileFormatException("There is no map defined!");
|
||||
this.map = api.getMap(mapId).orElseThrow(() -> new MarkerFileFormatException("Could not resolve map with id: " + mapId));
|
||||
|
||||
//position
|
||||
this.postition = readPos(markerNode.getNode("position"));
|
||||
|
||||
//minmaxDistance
|
||||
this.minDistance = markerNode.getNode("minDistance").getDouble(0);
|
||||
this.maxDistance = markerNode.getNode("maxDistance").getDouble(100000);
|
||||
|
||||
//label
|
||||
this.label = markerNode.getNode("label").getString(this.id);
|
||||
|
||||
//link
|
||||
this.link = markerNode.getNode("link").getString();
|
||||
this.newTab = markerNode.getNode("newTab").getBoolean(true);
|
||||
}
|
||||
|
||||
public synchronized void save(ConfigurationNode markerNode, boolean force) {
|
||||
if (!force && !hasUnsavedChanges) return;
|
||||
|
||||
markerNode.getNode("id").setValue(this.id);
|
||||
markerNode.getNode("type").setValue(this.getType());
|
||||
markerNode.getNode("map").setValue(this.map.getId());
|
||||
writePos(markerNode.getNode("position"), this.postition);
|
||||
markerNode.getNode("minDistance").setValue(Math.round(this.minDistance * 1000d) / 1000d);
|
||||
markerNode.getNode("maxDistance").setValue(Math.round(this.maxDistance * 1000d) / 1000d);
|
||||
markerNode.getNode("label").setValue(this.label);
|
||||
markerNode.getNode("link").setValue(this.link);
|
||||
markerNode.getNode("newTab").setValue(this.newTab);
|
||||
|
||||
hasUnsavedChanges = false;
|
||||
}
|
||||
|
||||
private static Vector3d readPos(ConfigurationNode node) throws MarkerFileFormatException {
|
||||
ConfigurationNode nx, ny, nz;
|
||||
nx = node.getNode("x");
|
||||
ny = node.getNode("y");
|
||||
nz = node.getNode("z");
|
||||
|
||||
if (nx.isVirtual() || ny.isVirtual() || nz.isVirtual()) throw new MarkerFileFormatException("Failed to read position: One of the nodes x,y or z is missing!");
|
||||
|
||||
return new Vector3d(
|
||||
nx.getDouble(),
|
||||
ny.getDouble(),
|
||||
nz.getDouble()
|
||||
);
|
||||
}
|
||||
|
||||
private static void writePos(ConfigurationNode node, Vector3d pos) {
|
||||
node.getNode("x").setValue(Math.round(pos.getX() * 1000d) / 1000d);
|
||||
node.getNode("y").setValue(Math.round(pos.getY() * 1000d) / 1000d);
|
||||
node.getNode("z").setValue(Math.round(pos.getZ() * 1000d) / 1000d);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,232 @@
|
||||
/*
|
||||
* 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.common.api.marker;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import com.flowpowered.math.vector.Vector3d;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import de.bluecolored.bluemap.api.BlueMapAPI;
|
||||
import de.bluecolored.bluemap.api.marker.Marker;
|
||||
import de.bluecolored.bluemap.api.marker.MarkerSet;
|
||||
import de.bluecolored.bluemap.api.marker.Shape;
|
||||
import de.bluecolored.bluemap.api.renderer.BlueMapMap;
|
||||
import de.bluecolored.bluemap.core.logger.Logger;
|
||||
import ninja.leaping.configurate.ConfigurationNode;
|
||||
|
||||
public class MarkerSetImpl implements MarkerSet {
|
||||
|
||||
private final String id;
|
||||
private String label;
|
||||
private boolean toggleable;
|
||||
private boolean isDefaultHidden;
|
||||
private Map<String, MarkerImpl> markers;
|
||||
|
||||
private Set<String> removedMarkers;
|
||||
|
||||
private boolean hasUnsavedChanges;
|
||||
|
||||
public MarkerSetImpl(String id) {
|
||||
this.id = id;
|
||||
this.label = id;
|
||||
this.toggleable = true;
|
||||
this.isDefaultHidden = false;
|
||||
this.markers = new ConcurrentHashMap<>();
|
||||
|
||||
this.removedMarkers = Sets.newConcurrentHashSet();
|
||||
|
||||
this.hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLabel() {
|
||||
return this.label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setLabel(String label) {
|
||||
this.label = label;
|
||||
this.hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isToggleable() {
|
||||
return this.toggleable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setToggleable(boolean toggleable) {
|
||||
this.toggleable = toggleable;
|
||||
this.hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDefautHidden() {
|
||||
return this.isDefaultHidden;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setDefaultHidden(boolean defaultHide) {
|
||||
this.isDefaultHidden = defaultHide;
|
||||
this.hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Marker> getMarkers() {
|
||||
return Collections.unmodifiableCollection(markers.values());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Marker> getMarker(String id) {
|
||||
return Optional.ofNullable(markers.get(id));
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized POIMarkerImpl createPOIMarker(String id, BlueMapMap map, Vector3d position) {
|
||||
removeMarker(id);
|
||||
|
||||
POIMarkerImpl marker = new POIMarkerImpl(id, map, position);
|
||||
markers.put(id, marker);
|
||||
|
||||
return marker;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized ShapeMarkerImpl createShapeMarker(String id, BlueMapMap map, Vector3d position, Shape shape, float height) {
|
||||
removeMarker(id);
|
||||
|
||||
ShapeMarkerImpl marker = new ShapeMarkerImpl(id, map, position, shape, height);
|
||||
markers.put(id, marker);
|
||||
|
||||
return marker;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean removeMarker(String id) {
|
||||
if (markers.remove(id) != null) {
|
||||
removedMarkers.add(id);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public synchronized void load(BlueMapAPI api, ConfigurationNode node) throws MarkerFileFormatException {
|
||||
this.hasUnsavedChanges = false;
|
||||
this.removedMarkers.clear();
|
||||
|
||||
this.label = node.getNode("label").getString(id);
|
||||
this.toggleable = node.getNode("toggleable").getBoolean(true);
|
||||
this.isDefaultHidden = node.getNode("defaultHide").getBoolean(false);
|
||||
|
||||
BlueMapMap dummyMap = api.getMaps().iterator().next();
|
||||
Shape dummyShape = Shape.createRect(0d, 0d, 1d, 1d);
|
||||
Set<String> externallyRemovedMarkers = new HashSet<>(this.markers.keySet());
|
||||
|
||||
for (ConfigurationNode markerNode : node.getNode("marker").getChildrenList()) {
|
||||
String id = markerNode.getNode("id").getString();
|
||||
String type = markerNode.getNode("type").getString();
|
||||
|
||||
if (id == null || type == null) {
|
||||
Logger.global.logDebug("Marker-API: Failed to load a marker in the set '" + this.id + "': No id or type defined!");
|
||||
}
|
||||
|
||||
MarkerImpl marker = markers.get(id);
|
||||
externallyRemovedMarkers.remove(id);
|
||||
|
||||
if (marker == null || !marker.getType().equals(type)) {
|
||||
switch (type) {
|
||||
case POIMarkerImpl.MARKER_TYPE:
|
||||
marker = new POIMarkerImpl(id, dummyMap, Vector3d.ZERO);
|
||||
break;
|
||||
case ShapeMarkerImpl.MARKER_TYPE:
|
||||
marker = new ShapeMarkerImpl(id, dummyMap, Vector3d.ZERO, dummyShape, 0f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
marker.load(api, markerNode);
|
||||
markers.put(id, marker);
|
||||
} catch (MarkerFileFormatException ex) {
|
||||
Logger.global.logDebug("Marker-API: Failed to load marker '" + id + "' in the set '" + this.id + "': " + ex);
|
||||
}
|
||||
}
|
||||
|
||||
for (String id : externallyRemovedMarkers) {
|
||||
markers.remove(id);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void save(ConfigurationNode node, boolean force) {
|
||||
List<? extends ConfigurationNode> markerList = node.getNode("marker").getChildrenList();
|
||||
node.removeChild("marker");
|
||||
|
||||
Set<String> newMarkers = new HashSet<>(markers.keySet());
|
||||
for (ConfigurationNode markerNode : markerList) {
|
||||
String id = markerNode.getNode("id").getString();
|
||||
if (id == null) continue;
|
||||
if (removedMarkers.contains(id)) continue;
|
||||
|
||||
newMarkers.remove(id);
|
||||
MarkerImpl marker = markers.get(id);
|
||||
|
||||
if (marker != null) marker.save(markerNode, false);
|
||||
|
||||
node.getNode("marker").getAppendedNode().mergeValuesFrom(markerNode);
|
||||
}
|
||||
|
||||
for (String markerId : newMarkers) {
|
||||
MarkerImpl marker = markers.get(markerId);
|
||||
if (marker == null) continue;
|
||||
|
||||
marker.save(node.getNode("marker").getAppendedNode(), true);
|
||||
}
|
||||
|
||||
removedMarkers.clear();
|
||||
|
||||
if (!force && !hasUnsavedChanges) return;
|
||||
|
||||
node.getNode("id").setValue(this.id);
|
||||
node.getNode("label").setValue(this.label);
|
||||
node.getNode("toggleable").setValue(this.toggleable);
|
||||
node.getNode("defaultHide").setValue(this.isDefaultHidden);
|
||||
|
||||
this.hasUnsavedChanges = false;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,108 @@
|
||||
/*
|
||||
* 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.common.api.marker;
|
||||
|
||||
import com.flowpowered.math.vector.Vector2i;
|
||||
import com.flowpowered.math.vector.Vector3d;
|
||||
|
||||
import de.bluecolored.bluemap.api.BlueMapAPI;
|
||||
import de.bluecolored.bluemap.api.marker.POIMarker;
|
||||
import de.bluecolored.bluemap.api.renderer.BlueMapMap;
|
||||
import ninja.leaping.configurate.ConfigurationNode;
|
||||
|
||||
public class POIMarkerImpl extends MarkerImpl implements POIMarker {
|
||||
public static final String MARKER_TYPE = "poi";
|
||||
|
||||
private String iconAddress;
|
||||
private Vector2i anchor;
|
||||
|
||||
private boolean hasUnsavedChanges;
|
||||
|
||||
public POIMarkerImpl(String id, BlueMapMap map, Vector3d position) {
|
||||
super(id, map, position);
|
||||
|
||||
this.iconAddress = "assets/poi.svg";
|
||||
this.anchor = new Vector2i(25, 45);
|
||||
|
||||
this.hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return MARKER_TYPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIconAddress() {
|
||||
return iconAddress;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector2i getIconAnchor() {
|
||||
return anchor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setIcon(String iconAddress, Vector2i anchor) {
|
||||
this.iconAddress = iconAddress;
|
||||
this.anchor = anchor;
|
||||
this.hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void load(BlueMapAPI api, ConfigurationNode markerNode) throws MarkerFileFormatException {
|
||||
super.load(api, markerNode);
|
||||
|
||||
this.hasUnsavedChanges = false;
|
||||
|
||||
this.iconAddress = markerNode.getNode("icon").getString("assets/poi.svg");
|
||||
this.anchor = readAnchor(markerNode.getNode("iconAnchor"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void save(ConfigurationNode markerNode, boolean force) {
|
||||
super.save(markerNode, force);
|
||||
|
||||
if (!force && !hasUnsavedChanges) return;
|
||||
|
||||
markerNode.getNode("icon").setValue(this.iconAddress);
|
||||
writeAnchor(markerNode.getNode("iconAnchor"), this.anchor);
|
||||
|
||||
hasUnsavedChanges = false;
|
||||
}
|
||||
|
||||
private static Vector2i readAnchor(ConfigurationNode node) {
|
||||
return new Vector2i(
|
||||
node.getNode("x").getInt(0),
|
||||
node.getNode("y").getInt(0)
|
||||
);
|
||||
}
|
||||
|
||||
private static void writeAnchor(ConfigurationNode node, Vector2i anchor) {
|
||||
node.getNode("x").setValue(anchor.getX());
|
||||
node.getNode("y").setValue(anchor.getY());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,200 @@
|
||||
/*
|
||||
* 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.common.api.marker;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.util.List;
|
||||
|
||||
import com.flowpowered.math.vector.Vector2d;
|
||||
import com.flowpowered.math.vector.Vector3d;
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
import de.bluecolored.bluemap.api.BlueMapAPI;
|
||||
import de.bluecolored.bluemap.api.marker.Shape;
|
||||
import de.bluecolored.bluemap.api.marker.ShapeMarker;
|
||||
import de.bluecolored.bluemap.api.renderer.BlueMapMap;
|
||||
import ninja.leaping.configurate.ConfigurationNode;
|
||||
|
||||
public class ShapeMarkerImpl extends MarkerImpl implements ShapeMarker {
|
||||
public static final String MARKER_TYPE = "shape";
|
||||
|
||||
private Shape shape;
|
||||
private float height;
|
||||
private Color borderColor, fillColor;
|
||||
|
||||
private boolean hasUnsavedChanges;
|
||||
|
||||
public ShapeMarkerImpl(String id, BlueMapMap map, Vector3d position, Shape shape, float height) {
|
||||
super(id, map, position);
|
||||
|
||||
Preconditions.checkNotNull(shape);
|
||||
|
||||
this.shape = shape;
|
||||
this.height = height;
|
||||
|
||||
this.hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return MARKER_TYPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Shape getShape() {
|
||||
return this.shape;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getHeight() {
|
||||
return this.height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setShape(Shape shape, float height) {
|
||||
Preconditions.checkNotNull(shape);
|
||||
|
||||
this.shape = shape;
|
||||
this.height = height;
|
||||
this.hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Color getBorderColor() {
|
||||
return this.borderColor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setBorderColor(Color color) {
|
||||
Preconditions.checkNotNull(color);
|
||||
|
||||
this.borderColor = color;
|
||||
this.hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Color getFillColor() {
|
||||
return this.fillColor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setFillColor(Color color) {
|
||||
Preconditions.checkNotNull(color);
|
||||
|
||||
this.fillColor = color;
|
||||
this.hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(BlueMapAPI api, ConfigurationNode markerNode) throws MarkerFileFormatException {
|
||||
super.load(api, markerNode);
|
||||
|
||||
this.shape = readShape(markerNode.getNode("icon"));
|
||||
this.height = markerNode.getNode("height").getFloat(64);
|
||||
this.borderColor = readColor(markerNode.getNode("borderColor"));
|
||||
this.fillColor = readColor(markerNode.getNode("fillColor"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(ConfigurationNode markerNode, boolean force) {
|
||||
super.save(markerNode, force);
|
||||
|
||||
if (!force && !hasUnsavedChanges) return;
|
||||
|
||||
writeShape(markerNode.getNode("shape"), this.shape);
|
||||
markerNode.getNode("height").setValue(Math.round(height * 1000f) / 1000f);
|
||||
writeColor(markerNode.getNode("borderColor"), this.borderColor);
|
||||
writeColor(markerNode.getNode("fillColor"), this.fillColor);
|
||||
|
||||
hasUnsavedChanges = false;
|
||||
}
|
||||
|
||||
private Shape readShape(ConfigurationNode node) throws MarkerFileFormatException {
|
||||
List<? extends ConfigurationNode> posNodes = node.getChildrenList();
|
||||
|
||||
if (posNodes.size() < 3) throw new MarkerFileFormatException("Failed to read shape: point-list has fewer than 3 entries!");
|
||||
|
||||
Vector2d[] positions = new Vector2d[posNodes.size()];
|
||||
for (int i = 0; i < positions.length; i++) {
|
||||
positions[i] = readShapePos(posNodes.get(i));
|
||||
}
|
||||
|
||||
return new Shape(positions);
|
||||
}
|
||||
|
||||
private static Vector2d readShapePos(ConfigurationNode node) throws MarkerFileFormatException {
|
||||
ConfigurationNode nx, nz;
|
||||
nx = node.getNode("x");
|
||||
nz = node.getNode("z");
|
||||
|
||||
if (nx.isVirtual() || nz.isVirtual()) throw new MarkerFileFormatException("Failed to read shape position: Node x or z is not set!");
|
||||
|
||||
return new Vector2d(
|
||||
nx.getDouble(),
|
||||
nz.getDouble()
|
||||
);
|
||||
}
|
||||
|
||||
private static Color readColor(ConfigurationNode node) throws MarkerFileFormatException {
|
||||
ConfigurationNode nr, ng, nb, na;
|
||||
nr = node.getNode("r");
|
||||
ng = node.getNode("g");
|
||||
nb = node.getNode("b");
|
||||
na = node.getNode("a");
|
||||
|
||||
if (nr.isVirtual() || ng.isVirtual() || nb.isVirtual()) throw new MarkerFileFormatException("Failed to read color: Node r,g or b is not set!");
|
||||
|
||||
float alpha = na.getFloat(1);
|
||||
if (alpha < 0 || alpha > 1) throw new MarkerFileFormatException("Failed to read color: alpha value out of range (0-1)!");
|
||||
|
||||
try {
|
||||
return new Color(nr.getInt(), ng.getInt(), nb.getInt(), (int)(alpha * 255));
|
||||
} catch (IllegalArgumentException ex) {
|
||||
throw new MarkerFileFormatException("Failed to read color: " + ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeShape(ConfigurationNode node, Shape shape) {
|
||||
for (int i = 0; i < shape.getPointCount(); i++) {
|
||||
ConfigurationNode pointNode = node.getAppendedNode();
|
||||
Vector2d point = shape.getPoint(i);
|
||||
pointNode.getNode("x").setValue(Math.round(point.getX() * 1000d) / 1000d);
|
||||
pointNode.getNode("z").setValue(Math.round(point.getY() * 1000d) / 1000d);
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeColor(ConfigurationNode node, Color color) {
|
||||
int r = color.getRed();
|
||||
int g = color.getGreen();
|
||||
int b = color.getBlue();
|
||||
float a = color.getAlpha() / 255f;
|
||||
|
||||
node.getNode("r").setValue(r);
|
||||
node.getNode("g").setValue(g);
|
||||
node.getNode("b").setValue(b);
|
||||
node.getNode("a").setValue(a);
|
||||
}
|
||||
|
||||
}
|
@ -207,6 +207,11 @@ public synchronized void load() throws IOException, ParseResourceException {
|
||||
MapType mapType = new MapType(id, name, world, tileRenderer);
|
||||
maps.put(id, mapType);
|
||||
}
|
||||
if (maps.isEmpty()) {
|
||||
Logger.global.logWarning("There are no valid maps configured, please check your config! Disabling BlueMap...");
|
||||
unload();
|
||||
return;
|
||||
}
|
||||
|
||||
//initialize render manager
|
||||
renderManager = new RenderManager(config.getRenderThreadCount());
|
||||
@ -301,6 +306,7 @@ public synchronized void unload() {
|
||||
|
||||
//disable api
|
||||
if (api != null) api.unregister();
|
||||
api = null;
|
||||
|
||||
//unregister listeners
|
||||
serverInterface.unregisterAllListeners();
|
||||
|
15
BlueMapCore/src/main/webroot/assets/poi.svg
Normal file
15
BlueMapCore/src/main/webroot/assets/poi.svg
Normal file
@ -0,0 +1,15 @@
|
||||
<?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="50px" height="50px" viewBox="0 0 50 50" enable-background="new 0 0 50 50" xml:space="preserve">
|
||||
<g>
|
||||
<path fill="#FFFFFF" d="M37.468,17.123C37.468,29.596,25,45.346,25,45.346s-12.468-14.93-12.468-28.223
|
||||
c0-6.885,5.581-12.468,12.468-12.468C31.885,4.655,37.468,10.237,37.468,17.123z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path fill="#333333" d="M26.901,12.559c0.034,1.046-0.732,1.885-1.954,1.885c-1.083,0-1.85-0.838-1.85-1.885
|
||||
c0-1.082,0.804-1.92,1.918-1.92C26.169,10.639,26.901,11.478,26.901,12.559z M23.464,29.063l0.017-11.757h3.072l-0.018,11.757
|
||||
H23.464z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 919 B |
@ -42,6 +42,8 @@ import {
|
||||
Vector3,
|
||||
} from 'three';
|
||||
|
||||
import { CSS2DRenderer } from './hud/CSS2DRenderer';
|
||||
|
||||
import UI from './ui/UI.js';
|
||||
|
||||
import Controls from './Controls.js';
|
||||
@ -61,6 +63,7 @@ export default class BlueMap {
|
||||
constructor(element, dataRoot) {
|
||||
this.element = $('<div class="bluemap-container"></div>').appendTo(element)[0];
|
||||
this.dataRoot = dataRoot;
|
||||
this.locationHash = '';
|
||||
|
||||
this.hiresViewDistance = 160;
|
||||
this.lowresViewDistance = 3200;
|
||||
@ -79,18 +82,17 @@ export default class BlueMap {
|
||||
};
|
||||
this.debugInfo = false;
|
||||
|
||||
this.ui = new UI(this);
|
||||
|
||||
this.loadingNoticeElement = $('<div>loading...</div>').appendTo($(this.element));
|
||||
window.onerror = this.onLoadError;
|
||||
|
||||
this.fileLoader = new FileLoader();
|
||||
this.blobLoader = new FileLoader();
|
||||
this.blobLoader.setResponseType('blob');
|
||||
this.bufferGeometryLoader = new BufferGeometryLoader();
|
||||
|
||||
this.ui = new UI(this);
|
||||
|
||||
this.loadingNoticeElement = $('<div>loading...</div>').appendTo($(this.element));
|
||||
window.onerror = this.onLoadError;
|
||||
|
||||
this.initStage();
|
||||
this.locationHash = '';
|
||||
this.controls = new Controls(this.camera, this.element, this.hiresScene);
|
||||
|
||||
this.loadSettings().then(async () => {
|
||||
@ -100,16 +102,16 @@ export default class BlueMap {
|
||||
this.loadUserSettings();
|
||||
this.handleContainerResize();
|
||||
|
||||
this.changeMap(this.maps[0]);
|
||||
this.changeMap(this.maps[0], false);
|
||||
|
||||
this.ui.load();
|
||||
await this.ui.load();
|
||||
this.start();
|
||||
}).catch(error => {
|
||||
this.onLoadError(error.toString());
|
||||
});
|
||||
}
|
||||
|
||||
changeMap(map) {
|
||||
changeMap(map, loadTiles = true) {
|
||||
if (this.debugInfo) console.debug("changing map: ", map);
|
||||
|
||||
if (this.map === map) return;
|
||||
@ -156,13 +158,15 @@ export default class BlueMap {
|
||||
startPos
|
||||
);
|
||||
|
||||
this.lowresTileManager.update();
|
||||
this.hiresTileManager.update();
|
||||
if (loadTiles) {
|
||||
this.lowresTileManager.update();
|
||||
this.hiresTileManager.update();
|
||||
}
|
||||
|
||||
document.dispatchEvent(new Event('bluemap-map-change'));
|
||||
}
|
||||
|
||||
loadLocationHash() {
|
||||
loadLocationHash(smooth = false) {
|
||||
let hashVars = window.location.hash.substring(1).split(':');
|
||||
if (hashVars.length >= 1){
|
||||
if (this.settings.maps[hashVars[0]] !== undefined && this.map !== hashVars[0]){
|
||||
@ -184,18 +188,23 @@ export default class BlueMap {
|
||||
if (!isNaN(dir)) this.controls.targetDirection = dir;
|
||||
if (!isNaN(dist)) this.controls.targetDistance = dist;
|
||||
if (!isNaN(angle)) this.controls.targetAngle = angle;
|
||||
this.controls.direction = this.controls.targetDirection;
|
||||
this.controls.distance = this.controls.targetDistance;
|
||||
this.controls.angle = this.controls.targetAngle;
|
||||
this.controls.targetPosition.y = this.controls.minHeight;
|
||||
this.controls.position.copy(this.controls.targetPosition);
|
||||
if (!smooth) {
|
||||
this.controls.direction = this.controls.targetDirection;
|
||||
this.controls.distance = this.controls.targetDistance;
|
||||
this.controls.angle = this.controls.targetAngle;
|
||||
this.controls.targetPosition.y = this.controls.minHeight;
|
||||
this.controls.position.copy(this.controls.targetPosition);
|
||||
}
|
||||
}
|
||||
if (hashVars.length >= 7){
|
||||
let height = parseInt(hashVars[6]);
|
||||
if (!isNaN(height)){
|
||||
this.controls.minHeight = height;
|
||||
this.controls.targetPosition.y = height;
|
||||
this.controls.position.copy(this.controls.targetPosition);
|
||||
|
||||
if (!smooth) {
|
||||
this.controls.position.copy(this.controls.targetPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -207,7 +216,7 @@ export default class BlueMap {
|
||||
|
||||
$(window).on('hashchange', () => {
|
||||
if (this.locationHash === window.location.hash) return;
|
||||
this.loadLocationHash();
|
||||
this.loadLocationHash(true);
|
||||
});
|
||||
|
||||
this.update();
|
||||
@ -269,13 +278,16 @@ export default class BlueMap {
|
||||
this.skyboxCamera.updateProjectionMatrix();
|
||||
|
||||
this.renderer.clear();
|
||||
this.renderer.render(this.skyboxScene, this.skyboxCamera, this.renderer.getRenderTarget(), false);
|
||||
this.renderer.render(this.skyboxScene, this.skyboxCamera);
|
||||
this.renderer.clearDepth();
|
||||
this.renderer.render(this.lowresScene, this.camera);
|
||||
this.renderer.clearDepth();
|
||||
this.renderer.render(this.lowresScene, this.camera, this.renderer.getRenderTarget(), false);
|
||||
if (this.camera.position.y < 400) {
|
||||
this.renderer.clearDepth();
|
||||
this.renderer.render(this.hiresScene, this.camera, this.renderer.getRenderTarget(), false);
|
||||
this.renderer.render(this.hiresScene, this.camera);
|
||||
}
|
||||
this.renderer.render(this.shapeScene, this.camera);
|
||||
|
||||
this.hudRenderer.render(this.hudScene, this.camera);
|
||||
};
|
||||
|
||||
handleContainerResize = () => {
|
||||
@ -290,6 +302,8 @@ export default class BlueMap {
|
||||
.css('width', this.element.clientWidth)
|
||||
.css('height', this.element.clientHeight);
|
||||
|
||||
this.hudRenderer.setSize(this.element.clientWidth, this.element.clientHeight);
|
||||
|
||||
this.updateFrame = true;
|
||||
};
|
||||
|
||||
@ -322,12 +336,14 @@ export default class BlueMap {
|
||||
this.renderer = new WebGLRenderer({
|
||||
alpha: true,
|
||||
antialias: true,
|
||||
sortObjects: false,
|
||||
sortObjects: true,
|
||||
preserveDrawingBuffer: true,
|
||||
logarithmicDepthBuffer: true,
|
||||
logarithmicDepthBuffer: false,
|
||||
});
|
||||
this.renderer.autoClear = false;
|
||||
|
||||
this.hudRenderer = new CSS2DRenderer();
|
||||
|
||||
this.camera = new PerspectiveCamera(75, this.element.scrollWidth / this.element.scrollHeight, 0.1, 10000);
|
||||
this.camera.updateProjectionMatrix();
|
||||
|
||||
@ -340,7 +356,11 @@ export default class BlueMap {
|
||||
this.lowresScene = new Scene();
|
||||
this.hiresScene = new Scene();
|
||||
|
||||
this.shapeScene = new Scene();
|
||||
this.hudScene = new Scene();
|
||||
|
||||
$(this.renderer.domElement).addClass("map-canvas").appendTo(this.element);
|
||||
$(this.hudRenderer.domElement).addClass("map-canvas-hud").appendTo(this.element);
|
||||
this.handleContainerResize();
|
||||
|
||||
$(window).resize(this.handleContainerResize);
|
||||
|
190
BlueMapCore/src/main/webroot/js/libs/hud/CSS2DRenderer.js
Normal file
190
BlueMapCore/src/main/webroot/js/libs/hud/CSS2DRenderer.js
Normal file
@ -0,0 +1,190 @@
|
||||
/**
|
||||
* @author mrdoob / http://mrdoob.com/
|
||||
*/
|
||||
|
||||
import {
|
||||
Matrix4,
|
||||
Object3D,
|
||||
Vector3
|
||||
} from "three";
|
||||
|
||||
var CSS2DObject = function ( element ) {
|
||||
|
||||
Object3D.call( this );
|
||||
|
||||
this.element = element;
|
||||
this.element.style.position = 'absolute';
|
||||
|
||||
this.addEventListener( 'removed', function () {
|
||||
|
||||
this.traverse( function ( object ) {
|
||||
|
||||
if ( object.element instanceof Element && object.element.parentNode !== null ) {
|
||||
|
||||
object.element.parentNode.removeChild( object.element );
|
||||
|
||||
}
|
||||
|
||||
} );
|
||||
|
||||
} );
|
||||
|
||||
};
|
||||
|
||||
CSS2DObject.prototype = Object.create( Object3D.prototype );
|
||||
CSS2DObject.prototype.constructor = CSS2DObject;
|
||||
|
||||
//
|
||||
|
||||
var CSS2DRenderer = function () {
|
||||
|
||||
var _this = this;
|
||||
|
||||
var _width, _height;
|
||||
var _widthHalf, _heightHalf;
|
||||
|
||||
var vector = new Vector3();
|
||||
var viewMatrix = new Matrix4();
|
||||
var viewProjectionMatrix = new Matrix4();
|
||||
|
||||
var cache = {
|
||||
objects: new WeakMap()
|
||||
};
|
||||
|
||||
var domElement = document.createElement( 'div' );
|
||||
domElement.style.overflow = 'hidden';
|
||||
|
||||
this.domElement = domElement;
|
||||
|
||||
this.getSize = function () {
|
||||
|
||||
return {
|
||||
width: _width,
|
||||
height: _height
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
this.setSize = function ( width, height ) {
|
||||
|
||||
_width = width;
|
||||
_height = height;
|
||||
|
||||
_widthHalf = _width / 2;
|
||||
_heightHalf = _height / 2;
|
||||
|
||||
domElement.style.width = width + 'px';
|
||||
domElement.style.height = height + 'px';
|
||||
|
||||
};
|
||||
|
||||
var renderObject = function ( object, scene, camera ) {
|
||||
|
||||
if ( object instanceof CSS2DObject ) {
|
||||
|
||||
object.onBeforeRender( _this, scene, camera );
|
||||
|
||||
vector.setFromMatrixPosition( object.matrixWorld );
|
||||
vector.applyMatrix4( viewProjectionMatrix );
|
||||
|
||||
var element = object.element;
|
||||
var style = 'translate(-50%,-50%) translate(' + ( vector.x * _widthHalf + _widthHalf ) + 'px,' + ( - vector.y * _heightHalf + _heightHalf ) + 'px)';
|
||||
|
||||
element.style.WebkitTransform = style;
|
||||
element.style.MozTransform = style;
|
||||
element.style.oTransform = style;
|
||||
element.style.transform = style;
|
||||
|
||||
element.style.display = ( object.visible && vector.z >= - 1 && vector.z <= 1 ) ? '' : 'none';
|
||||
|
||||
var objectData = {
|
||||
distanceToCameraSquared: getDistanceToSquared( camera, object )
|
||||
};
|
||||
|
||||
cache.objects.set( object, objectData );
|
||||
|
||||
if ( element.parentNode !== domElement ) {
|
||||
|
||||
domElement.appendChild( element );
|
||||
|
||||
}
|
||||
|
||||
object.onAfterRender( _this, scene, camera );
|
||||
|
||||
}
|
||||
|
||||
for ( var i = 0, l = object.children.length; i < l; i ++ ) {
|
||||
|
||||
renderObject( object.children[ i ], scene, camera );
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
var getDistanceToSquared = function () {
|
||||
|
||||
var a = new Vector3();
|
||||
var b = new Vector3();
|
||||
|
||||
return function ( object1, object2 ) {
|
||||
|
||||
a.setFromMatrixPosition( object1.matrixWorld );
|
||||
b.setFromMatrixPosition( object2.matrixWorld );
|
||||
|
||||
return a.distanceToSquared( b );
|
||||
|
||||
};
|
||||
|
||||
}();
|
||||
|
||||
var filterAndFlatten = function ( scene ) {
|
||||
|
||||
var result = [];
|
||||
|
||||
scene.traverse( function ( object ) {
|
||||
|
||||
if ( object instanceof CSS2DObject ) result.push( object );
|
||||
|
||||
} );
|
||||
|
||||
return result;
|
||||
|
||||
};
|
||||
|
||||
var zOrder = function ( scene ) {
|
||||
|
||||
var sorted = filterAndFlatten( scene ).sort( function ( a, b ) {
|
||||
|
||||
var distanceA = cache.objects.get( a ).distanceToCameraSquared;
|
||||
var distanceB = cache.objects.get( b ).distanceToCameraSquared;
|
||||
|
||||
return distanceA - distanceB;
|
||||
|
||||
} );
|
||||
|
||||
var zMax = sorted.length;
|
||||
|
||||
for ( var i = 0, l = sorted.length; i < l; i ++ ) {
|
||||
|
||||
sorted[ i ].element.style.zIndex = zMax - i;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
this.render = function ( scene, camera ) {
|
||||
|
||||
if ( scene.autoUpdate === true ) scene.updateMatrixWorld();
|
||||
if ( camera.parent === null ) camera.updateMatrixWorld();
|
||||
|
||||
viewMatrix.copy( camera.matrixWorldInverse );
|
||||
viewProjectionMatrix.multiplyMatrices( camera.projectionMatrix, viewMatrix );
|
||||
|
||||
renderObject( scene, scene, camera );
|
||||
zOrder( scene );
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
export { CSS2DObject, CSS2DRenderer };
|
@ -6,35 +6,56 @@ import {
|
||||
Mesh,
|
||||
MeshBasicMaterial
|
||||
} from 'three';
|
||||
import {CSS2DObject} from './CSS2DRenderer';
|
||||
import {pathFromCoords} from "../utils";
|
||||
|
||||
export default class HudInfo {
|
||||
|
||||
constructor(blueMap, container){
|
||||
constructor(blueMap){
|
||||
this.blueMap = blueMap;
|
||||
this.container = container;
|
||||
|
||||
let blockMarkerGeo = new BoxBufferGeometry( 1, 1, 1 );
|
||||
let blockMarkerGeo = new BoxBufferGeometry( 1.01, 1.01, 1.01 );
|
||||
blockMarkerGeo.translate(0.5, 0.5, 0.5);
|
||||
this.blockMarker = new Mesh(blockMarkerGeo, new MeshBasicMaterial( {
|
||||
color: 0xffffff,
|
||||
opacity: 0.3,
|
||||
opacity: 0.5,
|
||||
depthWrite: false,
|
||||
depthTest: false,
|
||||
transparent: true
|
||||
transparent: true,
|
||||
} ));
|
||||
|
||||
this.rayPosition = new Vector2();
|
||||
this.raycaster = new Raycaster();
|
||||
|
||||
this.element = $(`
|
||||
<div class="hud-info" style="display: none">
|
||||
<div class="hud-info"><div class="bubble" style="display: none">
|
||||
<div class="content"></div>
|
||||
</div>
|
||||
`).appendTo(this.container);
|
||||
</div></div>
|
||||
`);
|
||||
this.bubble = this.element.find(".bubble");
|
||||
|
||||
this.hudElement = new CSS2DObject(this.element[0]);
|
||||
|
||||
$(document).on('bluemap-info-click', this.onShowInfo);
|
||||
$(window).on('mousedown wheel', this.onHideInfo);
|
||||
$(window).on('mousedown wheel touchstart', this.onHideInfo);
|
||||
}
|
||||
|
||||
showInfoBubble(content, x, y, z, onClose) {
|
||||
if (this.onClose){
|
||||
this.onClose();
|
||||
this.onClose = undefined;
|
||||
}
|
||||
|
||||
this.bubble.hide();
|
||||
this.bubble.find(".content").html(content);
|
||||
|
||||
this.hudElement.position.set(x, y, z);
|
||||
this.bubble.stop();
|
||||
this.blueMap.hudScene.add(this.hudElement);
|
||||
this.bubble.fadeIn(200);
|
||||
|
||||
this.onClose = onClose;
|
||||
|
||||
this.blueMap.updateFrame = true;
|
||||
}
|
||||
|
||||
onShowInfo = event => {
|
||||
@ -42,17 +63,27 @@ export default class HudInfo {
|
||||
this.rayPosition.y = - ( event.pos.y / this.blueMap.element.offsetHeight ) * 2 + 1;
|
||||
|
||||
this.raycaster.setFromCamera(this.rayPosition, this.blueMap.camera);
|
||||
|
||||
//check markers first
|
||||
let intersects = this.raycaster.intersectObjects( this.blueMap.shapeScene.children );
|
||||
console.log(intersects);
|
||||
if (intersects.length !== 0){
|
||||
try {
|
||||
intersects[0].object.userData.marker.onClick(intersects[0].point);
|
||||
} catch (ignore) {}
|
||||
return;
|
||||
}
|
||||
|
||||
//then show position info
|
||||
let hiresData = true;
|
||||
let intersects = this.raycaster.intersectObjects( this.blueMap.hiresScene.children );
|
||||
intersects = this.raycaster.intersectObjects( this.blueMap.hiresScene.children );
|
||||
if (intersects.length === 0){
|
||||
hiresData = false;
|
||||
intersects = this.raycaster.intersectObjects( this.blueMap.lowresScene.children );
|
||||
}
|
||||
|
||||
if (intersects.length > 0) {
|
||||
this.element.hide();
|
||||
let content = this.element.find(".content");
|
||||
content.html("");
|
||||
let content = $("<div></div>");
|
||||
|
||||
if (this.blueMap.debugInfo){
|
||||
console.debug("Tapped position data: ", intersects[0]);
|
||||
@ -129,28 +160,28 @@ export default class HudInfo {
|
||||
`).appendTo(content);
|
||||
}
|
||||
|
||||
//display the element
|
||||
this.element.css('left', `${event.pos.x}px`);
|
||||
this.element.css('top', `${event.pos.y}px`);
|
||||
if (event.pos.y < this.blueMap.element.offsetHeight / 3){
|
||||
this.element.addClass("below");
|
||||
} else {
|
||||
this.element.removeClass("below");
|
||||
}
|
||||
this.element.fadeIn(200);
|
||||
|
||||
//add block marker
|
||||
if (hiresData){
|
||||
this.blockMarker.position.set(block.x, block.y, block.z);
|
||||
this.blueMap.hiresScene.add(this.blockMarker);
|
||||
this.blueMap.updateFrame = true;
|
||||
this.blockMarker.needsUpdate = true;
|
||||
}
|
||||
|
||||
this.showInfoBubble(content.html(), block.x + 0.5, block.y + 1, block.z + 0.5);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
onHideInfo = event => {
|
||||
if (!this.element.is(':animated')) {
|
||||
this.element.fadeOut(200);
|
||||
if (!this.bubble.is(':animated')) {
|
||||
this.bubble.fadeOut(200, () => {
|
||||
this.blueMap.hudScene.remove(this.hudElement);
|
||||
|
||||
if (this.onClose){
|
||||
this.onClose();
|
||||
this.onClose = undefined;
|
||||
}
|
||||
});
|
||||
this.blueMap.hiresScene.remove(this.blockMarker);
|
||||
this.blueMap.updateFrame = true;
|
||||
}
|
39
BlueMapCore/src/main/webroot/js/libs/hud/Marker.js
Normal file
39
BlueMapCore/src/main/webroot/js/libs/hud/Marker.js
Normal file
@ -0,0 +1,39 @@
|
||||
import {Vector3} from "three";
|
||||
|
||||
export default class Marker {
|
||||
|
||||
constructor(blueMap, markerSet, markerData) {
|
||||
this.blueMap = blueMap;
|
||||
this.markerSet = markerSet;
|
||||
this.type = markerData.type;
|
||||
this.map = markerData.map;
|
||||
this.position = new Vector3(markerData.position.x, markerData.position.y, markerData.position.z);
|
||||
this.label = `<div class="marker-label">${markerData.label}</div>`;
|
||||
this.link = markerData.link;
|
||||
this.newTab = !!markerData.newTab;
|
||||
|
||||
this.visible = false;
|
||||
|
||||
this.minDistance = parseFloat(markerData.minDistance ? markerData.minDistance : 0);
|
||||
this.minDistanceSquared = this.minDistance * this.minDistance;
|
||||
this.maxDistance = parseFloat(markerData.maxDistance ? markerData.maxDistance : 100000);
|
||||
this.maxDistanceSquared = this.maxDistance * this.maxDistance;
|
||||
}
|
||||
|
||||
setVisible(visible) {
|
||||
this.visible = visible && this.blueMap.map === this.map;
|
||||
this.blueMap.updateFrame = true;
|
||||
}
|
||||
|
||||
updateRenderObject(object, scene, camera){
|
||||
if (this.visible) {
|
||||
//update visiblity
|
||||
let distanceSquared = this.position.distanceToSquared(camera.position);
|
||||
object.visible = distanceSquared <= this.maxDistanceSquared && distanceSquared >= this.minDistanceSquared;
|
||||
} else {
|
||||
object.visible = false;
|
||||
scene.remove(object);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
76
BlueMapCore/src/main/webroot/js/libs/hud/MarkerManager.js
Normal file
76
BlueMapCore/src/main/webroot/js/libs/hud/MarkerManager.js
Normal file
@ -0,0 +1,76 @@
|
||||
import MarkerSet from "./MarkerSet";
|
||||
import $ from "jquery";
|
||||
import ToggleButton from "../ui/ToggleButton";
|
||||
import Label from "../ui/Label";
|
||||
|
||||
export default class MarkerManager {
|
||||
|
||||
constructor(blueMap, ui) {
|
||||
this.blueMap = blueMap;
|
||||
this.ui = ui;
|
||||
|
||||
this.markerSets = [];
|
||||
|
||||
this.readyPromise =
|
||||
this.loadMarkerData()
|
||||
.then(this.loadMarkers);
|
||||
|
||||
$(document).on('bluemap-map-change', this.onBlueMapMapChange);
|
||||
}
|
||||
|
||||
loadMarkerData() {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.blueMap.fileLoader.load(this.blueMap.dataRoot + 'markers.json',
|
||||
markerData => {
|
||||
this.markerData = JSON.parse(markerData);
|
||||
resolve();
|
||||
},
|
||||
xhr => {},
|
||||
error => {
|
||||
reject();
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
loadMarkers = () => {
|
||||
this.markerData.markerSets.forEach(setData => {
|
||||
this.markerSets.push(new MarkerSet(this.blueMap, setData));
|
||||
});
|
||||
};
|
||||
|
||||
update(){
|
||||
this.markerSets.forEach(markerSet => {
|
||||
markerSet.update();
|
||||
});
|
||||
}
|
||||
|
||||
addMenuElements(menu){
|
||||
let addedLabel = false;
|
||||
this.markerSets.forEach(markerSet => {
|
||||
if (markerSet.toggleable) {
|
||||
if (!addedLabel){
|
||||
menu.addElement(new Label("marker:"));
|
||||
addedLabel = true;
|
||||
}
|
||||
|
||||
let menuElement = new ToggleButton(markerSet.label, !markerSet.defaultHide, button => {
|
||||
markerSet.visible = button.isSelected();
|
||||
markerSet.update();
|
||||
});
|
||||
|
||||
markerSet.visible = !markerSet.defaultHide;
|
||||
markerSet.update();
|
||||
|
||||
menu.addElement(menuElement);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onBlueMapMapChange = async () => {
|
||||
await this.readyPromise;
|
||||
|
||||
this.update();
|
||||
};
|
||||
|
||||
}
|
40
BlueMapCore/src/main/webroot/js/libs/hud/MarkerSet.js
Normal file
40
BlueMapCore/src/main/webroot/js/libs/hud/MarkerSet.js
Normal file
@ -0,0 +1,40 @@
|
||||
import POIMarker from "./POIMarker";
|
||||
import ShapeMarker from "./ShapeMarker";
|
||||
|
||||
export default class MarkerSet {
|
||||
|
||||
constructor(blueMap, setData) {
|
||||
this.blueMap = blueMap;
|
||||
this.id = setData.id;
|
||||
this.label = setData.label ? this.escapeHTML(setData.label) : this.id;
|
||||
this.toggleable = setData.toggleable !== undefined ? !!setData.toggleable : true;
|
||||
this.defaultHide = !!setData.defaultHide;
|
||||
this.marker = [];
|
||||
|
||||
this.visible = true;
|
||||
|
||||
if (Array.isArray(setData.marker)){
|
||||
setData.marker.forEach(markerData => {
|
||||
switch (markerData.type){
|
||||
case 'poi':
|
||||
this.marker.push(new POIMarker(this.blueMap, this, markerData));
|
||||
break;
|
||||
case 'shape':
|
||||
this.marker.push(new ShapeMarker(this.blueMap, this, markerData));
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
update() {
|
||||
this.marker.forEach(marker => {
|
||||
marker.setVisible(this.visible);
|
||||
});
|
||||
}
|
||||
|
||||
escapeHTML(text) {
|
||||
return text.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');
|
||||
}
|
||||
|
||||
}
|
55
BlueMapCore/src/main/webroot/js/libs/hud/POIMarker.js
Normal file
55
BlueMapCore/src/main/webroot/js/libs/hud/POIMarker.js
Normal file
@ -0,0 +1,55 @@
|
||||
import $ from 'jquery';
|
||||
import Marker from "./Marker";
|
||||
import {CSS2DObject} from "./CSS2DRenderer";
|
||||
import {Vector3} from "three";
|
||||
|
||||
import POI from "../../../assets/poi.svg";
|
||||
|
||||
export default class POIMarker extends Marker {
|
||||
|
||||
constructor(blueMap, markerSet, markerData) {
|
||||
super(blueMap, markerSet, markerData);
|
||||
|
||||
this.icon = markerData.icon ? markerData.icon : POI;
|
||||
this.iconAnchor = {
|
||||
x: markerData.iconAnchor.x,
|
||||
y: markerData.iconAnchor.y
|
||||
};
|
||||
}
|
||||
|
||||
setVisible(visible){
|
||||
super.setVisible(visible);
|
||||
|
||||
if (!this.renderObject){
|
||||
let iconElement = $(`<div class="marker-poi"><img src="${this.icon}" style="transform: translate(50%, 50%) translate(${-this.iconAnchor.x}px, ${-this.iconAnchor.y}px)"></div>`);
|
||||
iconElement.find("img").click(this.onClick);
|
||||
this.renderObject = new CSS2DObject(iconElement[0]);
|
||||
this.renderObject.position.copy(this.position);
|
||||
this.renderObject.onBeforeRender = (renderer, scene, camera) => this.updateRenderObject(this.renderObject, scene, camera);
|
||||
}
|
||||
|
||||
if (this.visible) {
|
||||
this.blueMap.hudScene.add(this.renderObject);
|
||||
} else {
|
||||
this.blueMap.hudScene.remove(this.renderObject);
|
||||
}
|
||||
}
|
||||
|
||||
onClick = () => {
|
||||
if (this.label) {
|
||||
this.setVisible(false);
|
||||
this.blueMap.ui.hudInfo.showInfoBubble(this.label, this.position.x, this.position.y, this.position.z, () => {
|
||||
this.setVisible(this.markerSet.visible);
|
||||
});
|
||||
}
|
||||
|
||||
if (this.link){
|
||||
if (this.newTab){
|
||||
window.open(this.link, '_blank');
|
||||
} else {
|
||||
location.href = this.link;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
106
BlueMapCore/src/main/webroot/js/libs/hud/ShapeMarker.js
Normal file
106
BlueMapCore/src/main/webroot/js/libs/hud/ShapeMarker.js
Normal file
@ -0,0 +1,106 @@
|
||||
import {
|
||||
Vector2,
|
||||
Shape,
|
||||
MeshBasicMaterial,
|
||||
Mesh,
|
||||
Line,
|
||||
LineBasicMaterial,
|
||||
BufferGeometry,
|
||||
ShapeBufferGeometry,
|
||||
DoubleSide
|
||||
} from 'three';
|
||||
import Marker from "./Marker";
|
||||
import $ from "jquery";
|
||||
|
||||
export default class ShapeMarker extends Marker {
|
||||
|
||||
constructor(blueMap, markerSet, markerData) {
|
||||
super(blueMap, markerSet, markerData);
|
||||
|
||||
let points = [];
|
||||
if (Array.isArray(markerData.shape)) {
|
||||
markerData.shape.forEach(point => {
|
||||
points.push(new Vector2(point.x, point.z));
|
||||
});
|
||||
}
|
||||
this.height = markerData.height ? markerData.height : 128;
|
||||
|
||||
this.fillColor = this.prepareColor(markerData.fillColor);
|
||||
this.borderColor = this.prepareColor(markerData.borderColor);
|
||||
|
||||
//fill
|
||||
let shape = new Shape(points);
|
||||
let fillGeo = new ShapeBufferGeometry(shape, 1);
|
||||
fillGeo.rotateX(Math.PI * 0.5);
|
||||
fillGeo.translate(0, this.height + 0.0072, 0);
|
||||
let fillMaterial = new MeshBasicMaterial({
|
||||
color: this.fillColor.rgb,
|
||||
opacity: this.fillColor.a,
|
||||
transparent: true,
|
||||
side: DoubleSide,
|
||||
});
|
||||
let fill = new Mesh( fillGeo, fillMaterial );
|
||||
|
||||
//border
|
||||
points.push(points[0]);
|
||||
let lineGeo = new BufferGeometry().setFromPoints(points);
|
||||
lineGeo.rotateX(Math.PI * 0.5);
|
||||
lineGeo.translate(0, this.height + 0.0072, 0);
|
||||
let lineMaterial = new LineBasicMaterial({
|
||||
color: this.borderColor.rgb,
|
||||
opacity: this.borderColor.a,
|
||||
transparent: true,
|
||||
depthTest: false,
|
||||
});
|
||||
let line = new Line( lineGeo, lineMaterial );
|
||||
|
||||
this.renderObject = fill;
|
||||
fill.add(line);
|
||||
|
||||
this.renderObject.userData = {
|
||||
marker: this,
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
setVisible(visible){
|
||||
super.setVisible(visible);
|
||||
|
||||
if (this.visible) {
|
||||
this.blueMap.shapeScene.add(this.renderObject);
|
||||
$(document).on('bluemap-update-frame', this.onRender);
|
||||
} else {
|
||||
this.blueMap.shapeScene.remove(this.renderObject);
|
||||
$(document).off('bluemap-update-frame', this.onRender);
|
||||
}
|
||||
}
|
||||
|
||||
onRender = () => {
|
||||
this.updateRenderObject(this.renderObject, this.blueMap.shapeScene, this.blueMap.camera);
|
||||
};
|
||||
|
||||
onClick = (clickPos) => {
|
||||
if (this.label) {
|
||||
this.blueMap.ui.hudInfo.showInfoBubble(this.label, clickPos.x, clickPos.y, clickPos.z);
|
||||
}
|
||||
|
||||
if (this.link){
|
||||
if (this.newTab){
|
||||
window.open(this.link, '_blank');
|
||||
} else {
|
||||
location.href = this.link;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
prepareColor(color){
|
||||
if (color.r === undefined) color.r = 0;
|
||||
if (color.g === undefined) color.g = 0;
|
||||
if (color.b === undefined) color.b = 0;
|
||||
if (color.a === undefined) color.a = 1;
|
||||
|
||||
color.rgb = (color.r << 16) + (color.g << 8) + (color.b);
|
||||
return color;
|
||||
}
|
||||
|
||||
}
|
@ -38,7 +38,8 @@ import ToggleButton from "./ToggleButton";
|
||||
import MapSelection from "./MapSeletion";
|
||||
|
||||
import NIGHT from '../../../assets/night.svg';
|
||||
import HudInfo from "../modules/HudInfo";
|
||||
import HudInfo from "../hud/HudInfo";
|
||||
import MarkerManager from "../hud/MarkerManager";
|
||||
|
||||
export default class UI {
|
||||
|
||||
@ -55,10 +56,11 @@ export default class UI {
|
||||
this.toolbar.element.appendTo(this.hud);
|
||||
|
||||
//modules
|
||||
this.hudInfo = new HudInfo(this.blueMap, this.element);
|
||||
this.hudInfo = new HudInfo(this.blueMap);
|
||||
this.markers = new MarkerManager(this.blueMap, this);
|
||||
}
|
||||
|
||||
load() {
|
||||
async load() {
|
||||
//elements
|
||||
let menuButton = new MenuButton(this.menu);
|
||||
let mapSelect = new MapSelection(this.blueMap);
|
||||
@ -108,7 +110,11 @@ export default class UI {
|
||||
|
||||
//menu
|
||||
this.menu.addElement(nightButton);
|
||||
this.menu.addElement(mobSpawnOverlay);
|
||||
//this.menu.addElement(mobSpawnOverlay);
|
||||
|
||||
await this.markers.readyPromise;
|
||||
this.markers.addMenuElements(this.menu);
|
||||
|
||||
this.menu.addElement(new Separator());
|
||||
this.menu.addElement(new Label('render quality:'));
|
||||
this.menu.addElement(quality);
|
||||
@ -119,7 +125,6 @@ export default class UI {
|
||||
this.menu.addElement(new Separator());
|
||||
this.menu.addElement(debugInfo);
|
||||
this.menu.update();
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,18 +1,15 @@
|
||||
.bluemap-container .ui .hud-info {
|
||||
position: absolute;
|
||||
.bluemap-container .hud-info {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
transform: translate(-50%, calc(-100% - 1rem));
|
||||
.bluemap-container .hud-info .bubble {
|
||||
transform: translate(0, calc(-50% - 0.5rem));
|
||||
|
||||
background-color: $normal_bg;
|
||||
filter: drop-shadow(1px 1px 3px #0008);
|
||||
|
||||
pointer-events: none;
|
||||
white-space: nowrap;
|
||||
|
||||
&.below {
|
||||
transform: translate(-50%, 1rem);
|
||||
}
|
||||
|
||||
.content {
|
||||
position: relative;
|
||||
|
||||
@ -67,23 +64,26 @@
|
||||
content: '';
|
||||
|
||||
position: absolute;
|
||||
bottom: -1rem;
|
||||
bottom: calc(-1rem + 1px);
|
||||
left: calc(50% - 0.5rem);
|
||||
|
||||
width: 0;
|
||||
height: 0;
|
||||
|
||||
z-index: 0;
|
||||
|
||||
border: solid 0.5rem;
|
||||
border-color: $normal_bg transparent transparent transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.below .content::after {
|
||||
top: -1rem;
|
||||
.bluemap-container .marker-poi {
|
||||
pointer-events: none;
|
||||
|
||||
border: solid 0.5rem;
|
||||
border-color: transparent transparent $normal_bg transparent;
|
||||
> * {
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
> img {
|
||||
filter: drop-shadow(1px 1px 3px #0008);
|
||||
}
|
||||
}
|
@ -31,19 +31,30 @@ html, body {
|
||||
|
||||
overflow: hidden;
|
||||
|
||||
> .map-canvas {
|
||||
> .map-canvas, .map-canvas-hud {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
> .map-canvas {
|
||||
background-color: #000;
|
||||
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
> .map-canvas-hud {
|
||||
pointer-events: none;
|
||||
|
||||
z-index: 10;
|
||||
|
||||
> * {
|
||||
pointer-events: auto;
|
||||
}
|
||||
}
|
||||
|
||||
> .ui {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
|
@ -6,75 +6,75 @@ const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||
const WEBROOT_PATH = path.resolve(__dirname, 'src/main/webroot')
|
||||
const BUILD_PATH = path.resolve(__dirname, 'build/generated/webroot')
|
||||
// folder with a generated world to render in the dev server
|
||||
const WORLD_DATA_PATH = path.resolve(__dirname, 'build/generated/world')
|
||||
const WORLD_DATA_PATH = path.resolve(__dirname, '../data/test-render')
|
||||
|
||||
module.exports = {
|
||||
mode: 'production',
|
||||
devtool: 'source-map',
|
||||
entry: {
|
||||
'bluemap': path.resolve(WEBROOT_PATH, 'js/site.js'),
|
||||
},
|
||||
output: {
|
||||
path: BUILD_PATH,
|
||||
filename: 'js/[name].js',
|
||||
},
|
||||
devServer: {
|
||||
contentBase: WORLD_DATA_PATH,
|
||||
compress: true,
|
||||
port: 8080,
|
||||
hot: true,
|
||||
host: '0.0.0.0'
|
||||
},
|
||||
plugins: [
|
||||
new MiniCssExtractPlugin({
|
||||
filename: 'style/[name].css?[hash]',
|
||||
}),
|
||||
new HtmlWebpackPlugin({
|
||||
template: path.resolve(WEBROOT_PATH, 'index.html'),
|
||||
hash: true,
|
||||
}),
|
||||
],
|
||||
resolve: {
|
||||
extensions: ['.js', '.css', '.scss'],
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
// Transpile JavaScript source files using TypeScript engine
|
||||
{
|
||||
test: /\.(js|ts)$/,
|
||||
include: /src/,
|
||||
use: 'ts-loader',
|
||||
},
|
||||
// Just import normal css files
|
||||
{
|
||||
test: /\.css$/,
|
||||
include: /src/,
|
||||
use: [
|
||||
{ loader: MiniCssExtractPlugin.loader },
|
||||
{ loader: 'css-loader' },
|
||||
],
|
||||
},
|
||||
// Converts scss files into css to use within custom elements
|
||||
{
|
||||
test: /\.scss$/,
|
||||
include: /src/,
|
||||
use: [
|
||||
{ loader: MiniCssExtractPlugin.loader },
|
||||
{ loader: 'css-loader' },
|
||||
{ loader: 'sass-loader' },
|
||||
],
|
||||
},
|
||||
// Load additional files
|
||||
{
|
||||
test: /\.(png|svg)(\?.*$|$)/,
|
||||
include: /src/,
|
||||
use: [
|
||||
{
|
||||
loader: 'file-loader',
|
||||
options: { name: 'assets/[name].[ext]?[hash]' },
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
mode: 'production',
|
||||
devtool: 'source-map',
|
||||
entry: {
|
||||
'bluemap': path.resolve(WEBROOT_PATH, 'js/site.js'),
|
||||
},
|
||||
output: {
|
||||
path: BUILD_PATH,
|
||||
filename: 'js/[name].js',
|
||||
},
|
||||
devServer: {
|
||||
contentBase: WORLD_DATA_PATH,
|
||||
compress: true,
|
||||
port: 8080,
|
||||
hot: true,
|
||||
host: '0.0.0.0'
|
||||
},
|
||||
plugins: [
|
||||
new MiniCssExtractPlugin({
|
||||
filename: 'style/[name].css?[hash]',
|
||||
}),
|
||||
new HtmlWebpackPlugin({
|
||||
template: path.resolve(WEBROOT_PATH, 'index.html'),
|
||||
hash: true,
|
||||
}),
|
||||
],
|
||||
resolve: {
|
||||
extensions: ['.js', '.css', '.scss'],
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
// Transpile JavaScript source files using TypeScript engine
|
||||
{
|
||||
test: /\.(js|ts)$/,
|
||||
include: /src/,
|
||||
use: 'ts-loader',
|
||||
},
|
||||
// Just import normal css files
|
||||
{
|
||||
test: /\.css$/,
|
||||
include: /src/,
|
||||
use: [
|
||||
{ loader: MiniCssExtractPlugin.loader },
|
||||
{ loader: 'css-loader' },
|
||||
],
|
||||
},
|
||||
// Converts scss files into css to use within custom elements
|
||||
{
|
||||
test: /\.scss$/,
|
||||
include: /src/,
|
||||
use: [
|
||||
{ loader: MiniCssExtractPlugin.loader },
|
||||
{ loader: 'css-loader' },
|
||||
{ loader: 'sass-loader' },
|
||||
],
|
||||
},
|
||||
// Load additional files
|
||||
{
|
||||
test: /\.(png|svg)(\?.*$|$)/,
|
||||
include: /src/,
|
||||
use: [
|
||||
{
|
||||
loader: 'file-loader',
|
||||
options: { name: 'assets/[name].[ext]?[hash]' },
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user