mirror of
https://github.com/BlueMap-Minecraft/BlueMap.git
synced 2024-11-14 22:55:15 +01:00
Rewrite lowres tiles with mutiple LODs
This commit is contained in:
parent
f50a86d5e1
commit
81574c5176
@ -45,7 +45,7 @@
|
||||
|
||||
public class MapStorageRequestHandler implements HttpRequestHandler {
|
||||
|
||||
private static final Pattern TILE_PATTERN = Pattern.compile("([^/]+)/x(-?[\\d/]+)z(-?[\\d/]+).*");
|
||||
private static final Pattern TILE_PATTERN = Pattern.compile("tiles/([\\d/]+)/x(-?[\\d/]+)z(-?[\\d/]+).*");
|
||||
|
||||
private final String mapId;
|
||||
private final Storage mapStorage;
|
||||
@ -74,11 +74,10 @@ public HttpResponse handle(HttpRequest request) {
|
||||
// provide map-tiles
|
||||
Matcher tileMatcher = TILE_PATTERN.matcher(path);
|
||||
if (tileMatcher.matches()) {
|
||||
String tileTypeId = tileMatcher.group(1);
|
||||
TileType tileType = TileType.forTypeId(tileTypeId);
|
||||
int lod = Integer.parseInt(tileMatcher.group(1));
|
||||
int x = Integer.parseInt(tileMatcher.group(2).replace("/", ""));
|
||||
int z = Integer.parseInt(tileMatcher.group(3).replace("/", ""));
|
||||
Optional<TileData> optTileData = mapStorage.readMapTileData(mapId, tileType, new Vector2i(x, z));
|
||||
Optional<TileData> optTileData = mapStorage.readMapTileData(mapId, lod, new Vector2i(x, z));
|
||||
|
||||
if (optTileData.isPresent()) {
|
||||
TileData tileData = optTileData.get();
|
||||
|
@ -25,19 +25,17 @@
|
||||
package de.bluecolored.bluemap.core.map;
|
||||
|
||||
import com.flowpowered.math.vector.Vector2i;
|
||||
import de.bluecolored.bluemap.api.debug.DebugDump;
|
||||
import de.bluecolored.bluemap.api.gson.MarkerGson;
|
||||
import de.bluecolored.bluemap.api.markers.MarkerSet;
|
||||
import de.bluecolored.bluemap.api.debug.DebugDump;
|
||||
import de.bluecolored.bluemap.core.logger.Logger;
|
||||
import de.bluecolored.bluemap.core.map.hires.HiresModelManager;
|
||||
import de.bluecolored.bluemap.core.map.hires.HiresTileMeta;
|
||||
import de.bluecolored.bluemap.core.map.lowres.LowresModelManager;
|
||||
import de.bluecolored.bluemap.core.map.lowres.LowresTileManager;
|
||||
import de.bluecolored.bluemap.core.resources.adapter.ResourcesGson;
|
||||
import de.bluecolored.bluemap.core.resources.resourcepack.ResourcePack;
|
||||
import de.bluecolored.bluemap.core.storage.CompressedInputStream;
|
||||
import de.bluecolored.bluemap.core.storage.MetaType;
|
||||
import de.bluecolored.bluemap.core.storage.Storage;
|
||||
import de.bluecolored.bluemap.core.storage.TileType;
|
||||
import de.bluecolored.bluemap.core.world.Grid;
|
||||
import de.bluecolored.bluemap.core.world.World;
|
||||
|
||||
@ -63,7 +61,7 @@ public class BmMap {
|
||||
private final TextureGallery textureGallery;
|
||||
|
||||
private final HiresModelManager hiresModelManager;
|
||||
private final LowresModelManager lowresModelManager;
|
||||
private final LowresTileManager lowresTileManager;
|
||||
|
||||
private final ConcurrentHashMap<String, MarkerSet> markerSets;
|
||||
|
||||
@ -89,17 +87,18 @@ public BmMap(String id, String name, String worldId, World world, Storage storag
|
||||
saveTextureGallery();
|
||||
|
||||
this.hiresModelManager = new HiresModelManager(
|
||||
storage.tileStorage(id, TileType.HIRES),
|
||||
storage.tileStorage(id, 0),
|
||||
this.resourcePack,
|
||||
this.textureGallery,
|
||||
settings,
|
||||
new Grid(settings.getHiresTileSize(), 2)
|
||||
);
|
||||
|
||||
this.lowresModelManager = new LowresModelManager(
|
||||
storage.tileStorage(id, TileType.LOWRES),
|
||||
new Vector2i(settings.getLowresPointsPerLowresTile(), settings.getLowresPointsPerLowresTile()),
|
||||
new Vector2i(settings.getLowresPointsPerHiresTile(), settings.getLowresPointsPerHiresTile())
|
||||
this.lowresTileManager = new LowresTileManager(
|
||||
storage.mapStorage(id),
|
||||
new Grid(new Vector2i(500, 500)),
|
||||
3,
|
||||
5
|
||||
);
|
||||
|
||||
this.tileFilter = t -> true;
|
||||
@ -117,8 +116,7 @@ public void renderTile(Vector2i tile) {
|
||||
|
||||
long start = System.nanoTime();
|
||||
|
||||
HiresTileMeta tileMeta = hiresModelManager.render(world, tile);
|
||||
lowresModelManager.render(tileMeta);
|
||||
hiresModelManager.render(world, tile, lowresTileManager);
|
||||
|
||||
long end = System.nanoTime();
|
||||
long delta = end - start;
|
||||
@ -128,7 +126,7 @@ public void renderTile(Vector2i tile) {
|
||||
}
|
||||
|
||||
public synchronized void save() {
|
||||
lowresModelManager.save();
|
||||
lowresTileManager.save();
|
||||
saveRenderState();
|
||||
saveMarkerState();
|
||||
|
||||
@ -234,8 +232,8 @@ public HiresModelManager getHiresModelManager() {
|
||||
return hiresModelManager;
|
||||
}
|
||||
|
||||
public LowresModelManager getLowresModelManager() {
|
||||
return lowresModelManager;
|
||||
public LowresTileManager getLowresTileManager() {
|
||||
return lowresTileManager;
|
||||
}
|
||||
|
||||
public Map<String, MarkerSet> getMarkerSets() {
|
||||
|
@ -28,6 +28,7 @@
|
||||
import com.flowpowered.math.vector.Vector3i;
|
||||
import de.bluecolored.bluemap.core.logger.Logger;
|
||||
import de.bluecolored.bluemap.core.map.TextureGallery;
|
||||
import de.bluecolored.bluemap.core.map.lowres.LowresTileManager;
|
||||
import de.bluecolored.bluemap.core.resources.resourcepack.ResourcePack;
|
||||
import de.bluecolored.bluemap.core.storage.Storage;
|
||||
import de.bluecolored.bluemap.core.world.Grid;
|
||||
@ -56,21 +57,19 @@ public HiresModelManager(Storage.TileStorage storage, HiresModelRenderer rendere
|
||||
/**
|
||||
* Renders the given world tile with the provided render-settings
|
||||
*/
|
||||
public HiresTileMeta render(World world, Vector2i tile) {
|
||||
public void render(World world, Vector2i tile, LowresTileManager lowresTileManager) {
|
||||
Vector2i tileMin = tileGrid.getCellMin(tile);
|
||||
Vector2i tileMax = tileGrid.getCellMax(tile);
|
||||
|
||||
Vector3i modelMin = new Vector3i(tileMin.getX(), Integer.MIN_VALUE, tileMin.getY());
|
||||
Vector3i modelMax = new Vector3i(tileMax.getX(), Integer.MAX_VALUE, tileMax.getY());
|
||||
|
||||
HiresTileModel model = HiresTileModel.claimInstance();
|
||||
HiresTileModel model = HiresTileModel.instancePool().claimInstance();
|
||||
|
||||
HiresTileMeta tileMeta = renderer.render(world, modelMin, modelMax, model);
|
||||
renderer.render(world, modelMin, modelMax, model, lowresTileManager);
|
||||
save(model, tile);
|
||||
|
||||
HiresTileModel.recycleInstance(model);
|
||||
|
||||
return tileMeta;
|
||||
HiresTileModel.instancePool().recycleInstance(model);
|
||||
}
|
||||
|
||||
private void save(final HiresTileModel model, Vector2i tile) {
|
||||
|
@ -27,6 +27,7 @@
|
||||
import com.flowpowered.math.vector.Vector3i;
|
||||
import de.bluecolored.bluemap.core.map.TextureGallery;
|
||||
import de.bluecolored.bluemap.core.map.hires.blockmodel.BlockStateModelFactory;
|
||||
import de.bluecolored.bluemap.core.map.lowres.LowresTileManager;
|
||||
import de.bluecolored.bluemap.core.resources.resourcepack.ResourcePack;
|
||||
import de.bluecolored.bluemap.core.util.math.Color;
|
||||
import de.bluecolored.bluemap.core.world.BlockNeighborhood;
|
||||
@ -44,13 +45,11 @@ public HiresModelRenderer(ResourcePack resourcePack, TextureGallery textureGalle
|
||||
this.renderSettings = renderSettings;
|
||||
}
|
||||
|
||||
public HiresTileMeta render(World world, Vector3i modelMin, Vector3i modelMax, HiresTileModel model) {
|
||||
public void render(World world, Vector3i modelMin, Vector3i modelMax, HiresTileModel model, LowresTileManager lowresTileManager) {
|
||||
Vector3i min = modelMin.max(renderSettings.getMinPos());
|
||||
Vector3i max = modelMax.min(renderSettings.getMaxPos());
|
||||
Vector3i modelAnchor = new Vector3i(modelMin.getX(), 0, modelMin.getZ());
|
||||
|
||||
HiresTileMeta tileMeta = new HiresTileMeta(modelMin.getX(), modelMin.getZ(), modelMax.getX(), modelMax.getZ()); //TODO: recycle tilemeta instances?
|
||||
|
||||
// create new for each tile-render since the factory is not threadsafe
|
||||
BlockStateModelFactory modelFactory = new BlockStateModelFactory(resourcePack, textureGallery, renderSettings);
|
||||
|
||||
@ -93,12 +92,8 @@ public HiresTileMeta render(World world, Vector3i modelMin, Vector3i modelMax, H
|
||||
}
|
||||
}
|
||||
|
||||
tileMeta.setHeight(x, z, maxHeight);
|
||||
tileMeta.setColor(x, z, columnColor);
|
||||
|
||||
lowresTileManager.set(x, z, columnColor, maxHeight);
|
||||
}
|
||||
}
|
||||
|
||||
return tileMeta;
|
||||
}
|
||||
}
|
||||
|
@ -1,106 +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.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.map.hires;
|
||||
|
||||
import de.bluecolored.bluemap.core.util.math.Color;
|
||||
|
||||
public class HiresTileMeta {
|
||||
|
||||
private final int[] heights;
|
||||
private final float[] colors;
|
||||
|
||||
private final int minX, minZ, maxX, maxZ, sizeX, sizeZ;
|
||||
|
||||
public HiresTileMeta(int minX, int minZ, int maxX, int maxZ) {
|
||||
this.minX = minX;
|
||||
this.minZ = minZ;
|
||||
|
||||
this.maxX = maxX;
|
||||
this.maxZ = maxZ;
|
||||
|
||||
this.sizeX = maxX - minX + 1;
|
||||
this.sizeZ = maxZ - minZ + 1;
|
||||
|
||||
this.heights = new int[sizeX * sizeZ];
|
||||
this.colors = new float[sizeX * sizeZ * 4];
|
||||
}
|
||||
|
||||
public void setHeight(int x, int z, int height) {
|
||||
heights[(x - minX) * sizeZ + (z - minZ)] = height;
|
||||
}
|
||||
|
||||
public int getHeight(int x, int z) {
|
||||
return heights[(x - minX) * sizeZ + (z - minZ)];
|
||||
}
|
||||
|
||||
public void setColor(int x, int z, Color color) {
|
||||
if (!color.premultiplied) throw new IllegalArgumentException("Color should be premultiplied!");
|
||||
setColor(x, z, color.r, color.g, color.b, color.a);
|
||||
}
|
||||
|
||||
private void setColor(int x, int z, float r, float g, float b, float a) {
|
||||
int index = ((x - minX) * sizeZ + (z - minZ)) * 4;
|
||||
colors[index ] = r;
|
||||
colors[index + 1] = g;
|
||||
colors[index + 2] = b;
|
||||
colors[index + 3] = a;
|
||||
}
|
||||
|
||||
public Color getColor(int x, int z, Color target) {
|
||||
int index = ((x - minX) * sizeZ + (z - minZ)) * 4;
|
||||
return target.set(
|
||||
colors[index ],
|
||||
colors[index + 1],
|
||||
colors[index + 2],
|
||||
colors[index + 3],
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
public int getMinX() {
|
||||
return minX;
|
||||
}
|
||||
|
||||
public int getMinZ() {
|
||||
return minZ;
|
||||
}
|
||||
|
||||
public int getMaxX() {
|
||||
return maxX;
|
||||
}
|
||||
|
||||
public int getMaxZ() {
|
||||
return maxZ;
|
||||
}
|
||||
|
||||
public int getSizeX() {
|
||||
return sizeX;
|
||||
}
|
||||
|
||||
public int getSizeZ() {
|
||||
return sizeZ;
|
||||
}
|
||||
|
||||
}
|
@ -28,6 +28,7 @@
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
import de.bluecolored.bluemap.core.util.InstancePool;
|
||||
import de.bluecolored.bluemap.core.util.math.MatrixM3f;
|
||||
import de.bluecolored.bluemap.core.util.math.MatrixM4f;
|
||||
import de.bluecolored.bluemap.core.util.math.VectorM3f;
|
||||
@ -38,7 +39,6 @@
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
public class HiresTileModel {
|
||||
private static final double GROW_MULTIPLIER = 1.5;
|
||||
@ -53,7 +53,10 @@ public class HiresTileModel {
|
||||
FI_BLOCKLIGHT = 1 ,
|
||||
FI_MATERIAL_INDEX = 1 ;
|
||||
|
||||
private static final ConcurrentLinkedQueue<HiresTileModel> INSTANCE_POOL = new ConcurrentLinkedQueue<>();
|
||||
private static final InstancePool<HiresTileModel> INSTANCE_POOL = new InstancePool<>(
|
||||
() -> new HiresTileModel(100),
|
||||
HiresTileModel::clear
|
||||
);
|
||||
|
||||
private int capacity;
|
||||
private int size;
|
||||
@ -694,21 +697,6 @@ private void swap(int face1, int face2) {
|
||||
materialIndex[face2] = vi;
|
||||
}
|
||||
|
||||
public static HiresTileModel claimInstance() {
|
||||
HiresTileModel instance = INSTANCE_POOL.poll();
|
||||
if (instance != null) {
|
||||
instance.clear();
|
||||
} else {
|
||||
instance = new HiresTileModel(100);
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
public static void recycleInstance(HiresTileModel instance) {
|
||||
instance.clear();
|
||||
INSTANCE_POOL.offer(instance);
|
||||
}
|
||||
|
||||
private static void calculateSurfaceNormal(
|
||||
double p1x, double p1y, double p1z,
|
||||
double p2x, double p2y, double p2z,
|
||||
@ -730,4 +718,8 @@ private static void calculateSurfaceNormal(
|
||||
target.set((float) p1x, (float) p1y, (float) p1z);
|
||||
}
|
||||
|
||||
public static InstancePool<HiresTileModel> instancePool() {
|
||||
return INSTANCE_POOL;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,168 +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.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.map.lowres;
|
||||
|
||||
import com.flowpowered.math.vector.Vector2i;
|
||||
import com.flowpowered.math.vector.Vector3f;
|
||||
import de.bluecolored.bluemap.core.storage.Storage;
|
||||
import de.bluecolored.bluemap.core.threejs.BufferGeometry;
|
||||
import de.bluecolored.bluemap.core.util.MathUtils;
|
||||
import de.bluecolored.bluemap.core.util.ModelUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class LowresModel {
|
||||
|
||||
private final BufferGeometry model;
|
||||
private Map<Vector2i, LowresPoint> changes;
|
||||
|
||||
private boolean hasUnsavedChanges;
|
||||
|
||||
private final Object
|
||||
fileLock = new Object(),
|
||||
modelLock = new Object();
|
||||
|
||||
public LowresModel(Vector2i gridSize) {
|
||||
this(
|
||||
ModelUtils.makeGrid(gridSize).toBufferGeometry()
|
||||
);
|
||||
}
|
||||
|
||||
public LowresModel(BufferGeometry model) {
|
||||
this.model = model;
|
||||
|
||||
this.changes = new ConcurrentHashMap<>();
|
||||
|
||||
this.hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for all vertices at that point on the grid-model and change the height and color.<br>
|
||||
* <br>
|
||||
* <i>
|
||||
* Implementation note:<br>
|
||||
* The vertex x, z -coords are rounded, so we can compare them using == without worrying about floating point rounding differences.<br>
|
||||
* </i>
|
||||
*/
|
||||
public void update(Vector2i point, float height, Vector3f color){
|
||||
changes.put(point, new LowresPoint(height, color));
|
||||
this.hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves this model to its file
|
||||
* @param force if this is false, the model is only saved if it has any changes
|
||||
*/
|
||||
public void save(Storage.TileStorage storage, Vector2i tile, boolean force) throws IOException {
|
||||
if (!force && !hasUnsavedChanges) return;
|
||||
this.hasUnsavedChanges = false;
|
||||
|
||||
flush();
|
||||
|
||||
String json;
|
||||
synchronized (modelLock) {
|
||||
json = model.toJson();
|
||||
}
|
||||
|
||||
synchronized (fileLock) {
|
||||
try (
|
||||
PrintWriter pw = new PrintWriter(
|
||||
new OutputStreamWriter(storage.write(tile), StandardCharsets.UTF_8))
|
||||
){
|
||||
pw.print(json);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void flush(){
|
||||
if (changes.isEmpty()) return;
|
||||
|
||||
synchronized (modelLock) {
|
||||
if (changes.isEmpty()) return;
|
||||
|
||||
Map<Vector2i, LowresPoint> points = changes;
|
||||
changes = new ConcurrentHashMap<>();
|
||||
|
||||
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(position[j ]);
|
||||
int pz = Math.round(position[j + 2]);
|
||||
|
||||
Vector2i p = new Vector2i(px, pz);
|
||||
|
||||
LowresPoint lrp = points.get(p);
|
||||
if (lrp == null) continue;
|
||||
|
||||
position[j + 1] = lrp.height;
|
||||
|
||||
color[j ] = 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(position[f ], 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);
|
||||
|
||||
normal[f ] = 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public BufferGeometry getBufferGeometry(){
|
||||
flush();
|
||||
return model;
|
||||
}
|
||||
|
||||
/**
|
||||
* a point on this lowres-model-grid
|
||||
*/
|
||||
public static class LowresPoint {
|
||||
private final float height;
|
||||
private final Vector3f color;
|
||||
|
||||
public LowresPoint(float height, Vector3f color) {
|
||||
this.height = height;
|
||||
this.color = color;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,293 +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.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.map.lowres;
|
||||
|
||||
import com.flowpowered.math.vector.Vector2i;
|
||||
import com.flowpowered.math.vector.Vector3f;
|
||||
import de.bluecolored.bluemap.core.logger.Logger;
|
||||
import de.bluecolored.bluemap.core.map.hires.HiresTileMeta;
|
||||
import de.bluecolored.bluemap.core.storage.CompressedInputStream;
|
||||
import de.bluecolored.bluemap.core.storage.Storage;
|
||||
import de.bluecolored.bluemap.core.threejs.BufferGeometry;
|
||||
import de.bluecolored.bluemap.core.util.math.Color;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class LowresModelManager {
|
||||
|
||||
private final Storage.TileStorage storage;
|
||||
private final Vector2i pointsPerLowresTile;
|
||||
private final Vector2i pointsPerHiresTile;
|
||||
|
||||
private final Map<Vector2i, CachedModel> models;
|
||||
|
||||
public LowresModelManager(Storage.TileStorage storage, Vector2i pointsPerLowresTile, Vector2i pointsPerHiresTile) {
|
||||
this.storage = storage;
|
||||
|
||||
this.pointsPerLowresTile = pointsPerLowresTile;
|
||||
this.pointsPerHiresTile = pointsPerHiresTile;
|
||||
|
||||
models = new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders all points from the given hires-model onto the lowres-grid
|
||||
*/
|
||||
public void render(HiresTileMeta tileMeta) {
|
||||
Vector2i blocksPerPoint = new Vector2i(
|
||||
tileMeta.getSizeX() / pointsPerHiresTile.getX(),
|
||||
tileMeta.getSizeZ() / pointsPerHiresTile.getY()
|
||||
);
|
||||
|
||||
Vector2i pointMin = new Vector2i(
|
||||
Math.floorDiv(tileMeta.getMinX(), blocksPerPoint.getX()),
|
||||
Math.floorDiv(tileMeta.getMinZ(), blocksPerPoint.getY())
|
||||
);
|
||||
|
||||
Color
|
||||
pointColor = new Color(),
|
||||
columnColor = new Color();
|
||||
|
||||
for (int tx = 0; tx < pointsPerHiresTile.getX(); tx++){
|
||||
for (int tz = 0; tz < pointsPerHiresTile.getY(); tz++){
|
||||
|
||||
double height = 0;
|
||||
pointColor.set(0, 0, 0, 0, true);
|
||||
|
||||
for (int x = 0; x < blocksPerPoint.getX(); x++){
|
||||
for (int z = 0; z < blocksPerPoint.getY(); z++){
|
||||
|
||||
int rx = tx * blocksPerPoint.getX() + x + tileMeta.getMinX();
|
||||
int rz = tz * blocksPerPoint.getY() + z + tileMeta.getMinZ();
|
||||
height += tileMeta.getHeight(rx, rz);
|
||||
|
||||
tileMeta.getColor(rx, rz, columnColor).premultiplied();
|
||||
pointColor.add(columnColor);
|
||||
}
|
||||
}
|
||||
|
||||
pointColor.flatten().straight();
|
||||
|
||||
int count = blocksPerPoint.getX() * blocksPerPoint.getY();
|
||||
height /= count;
|
||||
|
||||
update(pointMin.getX() + tx, pointMin.getY() + tz, (float) height, pointColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves all unsaved changes to the models to disk
|
||||
*/
|
||||
public synchronized void save(){
|
||||
for (Entry<Vector2i, CachedModel> entry : models.entrySet()){
|
||||
saveModel(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
tidyUpModelCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a point on the lowres-model-grid
|
||||
*/
|
||||
public void update(int px, int pz, float height, Color color) {
|
||||
if (color.premultiplied) throw new IllegalArgumentException("Color can not be premultiplied!");
|
||||
|
||||
Vector2i point = new Vector2i(px, pz);
|
||||
Vector3f colorV = new Vector3f(color.r, color.g, color.b);
|
||||
|
||||
Vector2i tile = pointToTile(point);
|
||||
Vector2i relPoint = getPointRelativeToTile(tile, point);
|
||||
LowresModel model = getModel(tile);
|
||||
model.update(relPoint, height, colorV);
|
||||
|
||||
if (relPoint.getX() == 0){
|
||||
Vector2i tile2 = tile.add(-1, 0);
|
||||
Vector2i relPoint2 = getPointRelativeToTile(tile2, point);
|
||||
LowresModel model2 = getModel(tile2);
|
||||
model2.update(relPoint2, height, colorV);
|
||||
}
|
||||
|
||||
if (relPoint.getY() == 0){
|
||||
Vector2i tile2 = tile.add(0, -1);
|
||||
Vector2i relPoint2 = getPointRelativeToTile(tile2, point);
|
||||
LowresModel model2 = getModel(tile2);
|
||||
model2.update(relPoint2, height, colorV);
|
||||
}
|
||||
|
||||
if (relPoint.getX() == 0 && relPoint.getY() == 0){
|
||||
Vector2i tile2 = tile.add(-1, -1);
|
||||
Vector2i relPoint2 = getPointRelativeToTile(tile2, point);
|
||||
LowresModel model2 = getModel(tile2);
|
||||
model2.update(relPoint2, height, colorV);
|
||||
}
|
||||
}
|
||||
|
||||
private LowresModel getModel(Vector2i tile) {
|
||||
|
||||
CachedModel model = models.get(tile);
|
||||
|
||||
if (model == null){
|
||||
synchronized (this) {
|
||||
model = models.get(tile);
|
||||
if (model == null){
|
||||
|
||||
try {
|
||||
Optional<CompressedInputStream> optIs = storage.read(tile);
|
||||
if (optIs.isPresent()){
|
||||
try (InputStream is = optIs.get().decompress()) {
|
||||
String json = IOUtils.toString(is, StandardCharsets.UTF_8);
|
||||
|
||||
model = new CachedModel(BufferGeometry.fromJson(json));
|
||||
}
|
||||
}
|
||||
} catch (IllegalArgumentException | IOException ex){
|
||||
Logger.global.logWarning("Failed to load lowres model '" + tile + "': " + ex);
|
||||
|
||||
try {
|
||||
storage.delete(tile);
|
||||
} catch (IOException ex2) {
|
||||
Logger.global.logError("Failed to delete lowres-file: " + tile, ex2);
|
||||
}
|
||||
}
|
||||
|
||||
if (model == null){
|
||||
model = new CachedModel(pointsPerLowresTile);
|
||||
}
|
||||
|
||||
models.put(tile, model);
|
||||
|
||||
tidyUpModelCache();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
/**
|
||||
* This Method tidies up the model cache:<br>
|
||||
* it saves all modified models that have not been saved for 2 minutes and<br>
|
||||
* saves and removes the oldest models from the cache until the cache size is 10 or less.<br>
|
||||
* <br>
|
||||
* This method gets automatically called if the cache grows, but if you want to ensure model will be saved after 2 minutes, you could e.g call this method every second.<br>
|
||||
*/
|
||||
public synchronized void tidyUpModelCache() {
|
||||
List<Entry<Vector2i, CachedModel>> entries = new ArrayList<>(models.size());
|
||||
entries.addAll(models.entrySet());
|
||||
entries.sort((e1, e2) -> (int) Math.signum(e1.getValue().cacheTime - e2.getValue().cacheTime));
|
||||
|
||||
int size = entries.size();
|
||||
for (Entry<Vector2i, CachedModel> e : entries) {
|
||||
if (size > 10) {
|
||||
saveAndRemoveModel(e.getKey(), e.getValue());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (e.getValue().getCacheTime() > 120000) {
|
||||
saveModel(e.getKey(), e.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void saveAndRemoveModel(Vector2i tile, CachedModel model) {
|
||||
models.remove(tile);
|
||||
try {
|
||||
model.save(storage, tile,false);
|
||||
//logger.logDebug("Saved and unloaded lowres tile: " + model.getTile());
|
||||
} catch (IOException ex) {
|
||||
Logger.global.logError("Failed to save and unload lowres-model: " + tile, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void saveModel(Vector2i tile, CachedModel model) {
|
||||
try {
|
||||
model.save(storage, tile, false);
|
||||
//logger.logDebug("Saved lowres tile: " + model.getTile());
|
||||
} catch (IOException ex) {
|
||||
Logger.global.logError("Failed to save lowres-model: " + tile, ex);
|
||||
}
|
||||
|
||||
model.resetCacheTime();
|
||||
}
|
||||
|
||||
private Vector2i pointToTile(Vector2i point){
|
||||
return new Vector2i(
|
||||
Math.floorDiv(point.getX(), pointsPerLowresTile.getX()),
|
||||
Math.floorDiv(point.getY(), pointsPerLowresTile.getY())
|
||||
);
|
||||
}
|
||||
|
||||
private Vector2i getPointRelativeToTile(Vector2i tile, Vector2i point){
|
||||
return new Vector2i(
|
||||
point.getX() - tile.getX() * pointsPerLowresTile.getX(),
|
||||
point.getY() - tile.getY() * pointsPerLowresTile.getY()
|
||||
);
|
||||
}
|
||||
|
||||
public Vector2i getTileSize() {
|
||||
return pointsPerLowresTile;
|
||||
}
|
||||
|
||||
public Vector2i getPointsPerHiresTile() {
|
||||
return pointsPerHiresTile;
|
||||
}
|
||||
|
||||
private static class CachedModel extends LowresModel {
|
||||
|
||||
private long cacheTime;
|
||||
|
||||
public CachedModel(BufferGeometry model) {
|
||||
super(model);
|
||||
|
||||
cacheTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public CachedModel(Vector2i gridSize) {
|
||||
super(gridSize);
|
||||
|
||||
cacheTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public long getCacheTime() {
|
||||
return System.currentTimeMillis() - cacheTime;
|
||||
}
|
||||
|
||||
public void resetCacheTime() {
|
||||
cacheTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
package de.bluecolored.bluemap.core.map.lowres;
|
||||
|
||||
import com.flowpowered.math.vector.Vector2i;
|
||||
import de.bluecolored.bluemap.core.util.math.Color;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
public class LowresTile {
|
||||
|
||||
public static final int HEIGHT_UNDEFINED = Integer.MIN_VALUE;
|
||||
|
||||
private final BufferedImage texture;
|
||||
private final Vector2i size;
|
||||
|
||||
private volatile boolean closed = false;
|
||||
|
||||
public LowresTile(Vector2i tileSize) {
|
||||
this.size = tileSize.add(1, 1); // add 1 for seamless edges
|
||||
this.texture = new BufferedImage(this.size.getX(), this.size.getY() * 2, BufferedImage.TYPE_INT_ARGB);
|
||||
}
|
||||
|
||||
public LowresTile(Vector2i tileSize, InputStream in) throws IOException {
|
||||
this.size = tileSize.add(1, 1); // add 1 for seamless edges
|
||||
this.texture = ImageIO.read(in);
|
||||
|
||||
if (this.texture.getWidth() != this.size.getX() || this.texture.getHeight() != this.size.getY() * 2) {
|
||||
throw new IOException("Size of tile does not match");
|
||||
}
|
||||
}
|
||||
|
||||
public void set(int x, int z, Color color, int height) throws TileClosedException {
|
||||
if (closed) throw new TileClosedException();
|
||||
texture.setRGB(x, z, color.straight().getInt());
|
||||
texture.setRGB(x, size.getY() + z, (height & 0x00FFFFFF) | 0xFF000000);
|
||||
}
|
||||
|
||||
public Color getColor(int x, int z, Color target) {
|
||||
return target.set(texture.getRGB(x, z));
|
||||
}
|
||||
|
||||
public int getHeight(int x, int z) {
|
||||
int height = texture.getRGB(x, size.getY() + z) & 0x00FFFFFF;
|
||||
if (height > 0x00800000)
|
||||
return height | 0xFF000000;
|
||||
return height;
|
||||
}
|
||||
|
||||
public void save(OutputStream out) throws IOException {
|
||||
ImageIO.write(texture, "png", out);
|
||||
}
|
||||
|
||||
public void close() {
|
||||
closed = true;
|
||||
}
|
||||
|
||||
public static class TileClosedException extends Exception {
|
||||
public TileClosedException() {
|
||||
super("Tile is closed");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,185 @@
|
||||
package de.bluecolored.bluemap.core.map.lowres;
|
||||
|
||||
import com.flowpowered.math.vector.Vector2i;
|
||||
import com.github.benmanes.caffeine.cache.*;
|
||||
import de.bluecolored.bluemap.core.BlueMap;
|
||||
import de.bluecolored.bluemap.core.logger.Logger;
|
||||
import de.bluecolored.bluemap.core.storage.Storage;
|
||||
import de.bluecolored.bluemap.core.util.Vector2iCache;
|
||||
import de.bluecolored.bluemap.core.util.math.Color;
|
||||
import de.bluecolored.bluemap.core.world.Grid;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class LowresTileManager {
|
||||
|
||||
private final Storage.MapStorage mapStorage;
|
||||
|
||||
private final Grid tileGrid;
|
||||
private final int lodFactor, lodCount;
|
||||
|
||||
private final Vector2iCache vector2iCache;
|
||||
private final List<LoadingCache<Vector2i, LowresTile>> tileCaches;
|
||||
|
||||
public LowresTileManager(Storage.MapStorage mapStorage, Grid tileGrid, int lodCount, int lodFactor) {
|
||||
this.mapStorage = mapStorage;
|
||||
|
||||
this.tileGrid = tileGrid;
|
||||
this.lodFactor = lodFactor;
|
||||
this.lodCount = lodCount;
|
||||
|
||||
this.vector2iCache = new Vector2iCache();
|
||||
List<LoadingCache<Vector2i, LowresTile>> tileCacheList = new ArrayList<>();
|
||||
for (int i = 0; i < lodCount; i++) {
|
||||
int lod = i + 1;
|
||||
tileCacheList.add(Caffeine.newBuilder()
|
||||
.executor(BlueMap.THREAD_POOL)
|
||||
.scheduler(Scheduler.systemScheduler())
|
||||
.expireAfterAccess(10, TimeUnit.SECONDS)
|
||||
.expireAfterWrite(5, TimeUnit.MINUTES)
|
||||
.writer(new CacheWriter<Vector2i, LowresTile>() {
|
||||
|
||||
@Override
|
||||
public void write(@NonNull Vector2i key, @NonNull LowresTile value) {}
|
||||
|
||||
@Override
|
||||
public void delete(@NonNull Vector2i key, @Nullable LowresTile value, @NonNull RemovalCause cause) {
|
||||
if (value != null)
|
||||
saveTile(key, lod, value, cause);
|
||||
}
|
||||
|
||||
})
|
||||
.build(key -> loadTile(key, lod))
|
||||
);
|
||||
}
|
||||
this.tileCaches = Collections.unmodifiableList(tileCacheList);
|
||||
}
|
||||
|
||||
private LowresTile loadTile(Vector2i tilePos, int lod) {
|
||||
try (InputStream in = mapStorage.read(lod, tilePos).orElse(null)) {
|
||||
if (in == null)
|
||||
return new LowresTile(tileGrid.getGridSize());
|
||||
return new LowresTile(tileGrid.getGridSize(), in);
|
||||
} catch (IOException e) {
|
||||
Logger.global.logError("Failed to load tile " + tilePos + " (lod: " + lod + ")", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private void saveTile(Vector2i tilePos, int lod, LowresTile tile, RemovalCause removalCause) {
|
||||
// close the tile so it can't be edited anymore
|
||||
tile.close();
|
||||
|
||||
// save the tile
|
||||
try (OutputStream out = mapStorage.write(lod, tilePos)) {
|
||||
tile.save(out);
|
||||
} catch (IOException e) {
|
||||
Logger.global.logError("Failed to save tile " + tilePos + " (lod: " + lod + ")", e);
|
||||
}
|
||||
|
||||
if (lod >= lodCount) return;
|
||||
|
||||
// write to next LOD (prepare for the most confusing grid-math you will ever see)
|
||||
Color averageColor = new Color();
|
||||
int averageHeight;
|
||||
int count;
|
||||
|
||||
Color color = new Color();
|
||||
|
||||
int nextLodTileX = Math.floorDiv(tilePos.getX(), lodFactor);
|
||||
int nextLodTileY = Math.floorDiv(tilePos.getY(), lodFactor);
|
||||
Vector2i groupCount = new Vector2i(
|
||||
Math.floorDiv(tileGrid.getGridSize().getX(), lodFactor),
|
||||
Math.floorDiv(tileGrid.getGridSize().getY(), lodFactor)
|
||||
);
|
||||
|
||||
for (int gX = 0; gX < groupCount.getX(); gX++) {
|
||||
for (int gY = 0; gY < groupCount.getY(); gY++) {
|
||||
averageColor.set(0, 0, 0, 0, true);
|
||||
averageHeight = 0;
|
||||
count = 0;
|
||||
for (int x = 0; x < lodFactor; x++) {
|
||||
for (int y = 0; y < lodFactor; y++) {
|
||||
count++;
|
||||
averageColor.add(tile.getColor(gX * lodFactor + x, gY * lodFactor + y, color).premultiplied());
|
||||
averageHeight += tile.getHeight(gX * lodFactor + x, gY * lodFactor + y);
|
||||
}
|
||||
}
|
||||
averageColor.div(count);
|
||||
averageHeight /= count;
|
||||
|
||||
set(
|
||||
nextLodTileX,
|
||||
nextLodTileY,
|
||||
lod + 1,
|
||||
Math.floorMod(tilePos.getX(), lodFactor) * groupCount.getX() + gX,
|
||||
Math.floorMod(tilePos.getY(), lodFactor) * groupCount.getY() + gY,
|
||||
averageColor,
|
||||
averageHeight
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void save() {
|
||||
for (LoadingCache<Vector2i, LowresTile> cache : this.tileCaches) {
|
||||
cache.invalidateAll();
|
||||
cache.cleanUp();
|
||||
}
|
||||
}
|
||||
|
||||
public LowresTile getTile(int x, int z, int lod) {
|
||||
return tileCaches.get(lod - 1).get(vector2iCache.get(x, z));
|
||||
}
|
||||
|
||||
public void set(int x, int z, Color color, int height) {
|
||||
int cellX = tileGrid.getCellX(x);
|
||||
int cellZ = tileGrid.getCellY(z);
|
||||
int localX = tileGrid.getLocalX(x);
|
||||
int localZ = tileGrid.getLocalY(z);
|
||||
set(cellX, cellZ, 1, localX, localZ, color, height);
|
||||
}
|
||||
|
||||
private void set(int cellX, int cellZ, int lod, int pixelX, int pixelZ, Color color, int height) {
|
||||
|
||||
int tries = 0;
|
||||
LowresTile.TileClosedException closedException;
|
||||
do {
|
||||
tries ++;
|
||||
closedException = null;
|
||||
try {
|
||||
getTile(cellX, cellZ, lod)
|
||||
.set(pixelX, pixelZ, color, height);
|
||||
|
||||
// for seamless edges
|
||||
if (pixelX == 0) {
|
||||
getTile(cellX - 1, cellZ, lod)
|
||||
.set(tileGrid.getGridSize().getX(), pixelZ, color, height);
|
||||
}
|
||||
|
||||
if (pixelZ == 0) {
|
||||
getTile(cellX, cellZ - 1, lod)
|
||||
.set(pixelX, tileGrid.getGridSize().getY(), color, height);
|
||||
}
|
||||
|
||||
if (pixelX == 0 && pixelZ == 0) {
|
||||
getTile(cellX - 1, cellZ - 1, lod)
|
||||
.set(tileGrid.getGridSize().getX(), tileGrid.getGridSize().getY(), color, height);
|
||||
}
|
||||
} catch (LowresTile.TileClosedException ex) {
|
||||
closedException = ex;
|
||||
}
|
||||
} while (closedException != null && tries < 10);
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -28,10 +28,10 @@
|
||||
import com.flowpowered.math.vector.Vector3i;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import com.github.benmanes.caffeine.cache.LoadingCache;
|
||||
import de.bluecolored.bluemap.core.BlueMap;
|
||||
import de.bluecolored.bluemap.api.debug.DebugDump;
|
||||
import de.bluecolored.bluemap.core.debug.StateDumper;
|
||||
import de.bluecolored.bluemap.core.BlueMap;
|
||||
import de.bluecolored.bluemap.core.logger.Logger;
|
||||
import de.bluecolored.bluemap.core.util.Vector2iCache;
|
||||
import de.bluecolored.bluemap.core.world.Grid;
|
||||
import de.bluecolored.bluemap.core.world.World;
|
||||
import net.querz.nbt.CompoundTag;
|
||||
@ -47,7 +47,6 @@
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
@DebugDump
|
||||
public class MCAWorld implements World {
|
||||
@ -55,6 +54,8 @@ public class MCAWorld implements World {
|
||||
private static final Grid CHUNK_GRID = new Grid(16);
|
||||
private static final Grid REGION_GRID = new Grid(32).multiply(CHUNK_GRID);
|
||||
|
||||
private static final Vector2iCache VECTOR_2_I_CACHE = new Vector2iCache();
|
||||
|
||||
private final Path worldFolder;
|
||||
|
||||
private final String name;
|
||||
@ -107,7 +108,7 @@ public MCAChunk getChunkAtBlock(int x, int y, int z) {
|
||||
|
||||
@Override
|
||||
public MCAChunk getChunk(int x, int z) {
|
||||
return getChunk(vec2i(x, z));
|
||||
return getChunk(VECTOR_2_I_CACHE.get(x, z));
|
||||
}
|
||||
|
||||
private MCAChunk getChunk(Vector2i pos) {
|
||||
@ -116,7 +117,7 @@ private MCAChunk getChunk(Vector2i pos) {
|
||||
|
||||
@Override
|
||||
public MCARegion getRegion(int x, int z) {
|
||||
return getRegion(vec2i(x, z));
|
||||
return getRegion(VECTOR_2_I_CACHE.get(x, z));
|
||||
}
|
||||
|
||||
private MCARegion getRegion(Vector2i pos) {
|
||||
@ -193,7 +194,7 @@ public void invalidateChunkCache() {
|
||||
|
||||
@Override
|
||||
public void invalidateChunkCache(int x, int z) {
|
||||
chunkCache.invalidate(vec2i(x, z));
|
||||
chunkCache.invalidate(VECTOR_2_I_CACHE.get(x, z));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -290,62 +291,4 @@ private static Path resolveLevelFile(Path worldFolder) throws IOException {
|
||||
return levelFile;
|
||||
}
|
||||
|
||||
private static final int VEC_2I_CACHE_SIZE = 0x4000;
|
||||
private static final int VEC_2I_CACHE_MASK = VEC_2I_CACHE_SIZE - 1;
|
||||
private static final Vector2i[] VEC_2I_CACHE = new Vector2i[VEC_2I_CACHE_SIZE];
|
||||
private static final HitMissMetric HIT_MISS_METRIC = new HitMissMetric();
|
||||
static {
|
||||
StateDumper.global().register(HIT_MISS_METRIC);
|
||||
}
|
||||
|
||||
private static Vector2i vec2i(int x, int y) {
|
||||
int cacheIndex = (x * 1456 ^ y * 948375892) & VEC_2I_CACHE_MASK;
|
||||
Vector2i possibleMatch = VEC_2I_CACHE[cacheIndex];
|
||||
|
||||
if (possibleMatch != null && possibleMatch.getX() == x && possibleMatch.getY() == y) {
|
||||
HIT_MISS_METRIC.hit();
|
||||
return possibleMatch;
|
||||
}
|
||||
|
||||
HIT_MISS_METRIC.miss();
|
||||
return VEC_2I_CACHE[cacheIndex] = new Vector2i(x, y);
|
||||
}
|
||||
|
||||
@DebugDump
|
||||
private static class HitMissMetric {
|
||||
|
||||
private final AtomicLong
|
||||
hits = new AtomicLong(),
|
||||
misses = new AtomicLong();
|
||||
|
||||
public void hit() {
|
||||
hits.incrementAndGet();
|
||||
}
|
||||
|
||||
public void miss() {
|
||||
misses.incrementAndGet();
|
||||
}
|
||||
|
||||
public long getHits() {
|
||||
return hits.get();
|
||||
}
|
||||
|
||||
public long getMisses() {
|
||||
return misses.get();
|
||||
}
|
||||
|
||||
@DebugDump
|
||||
public long getSum() {
|
||||
return hits.get() + misses.get();
|
||||
}
|
||||
|
||||
@DebugDump
|
||||
public double getHitRate() {
|
||||
long hits = getHits();
|
||||
long misses = getMisses();
|
||||
return (double) hits / (hits + misses);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,121 +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.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.model;
|
||||
|
||||
import com.flowpowered.math.vector.Vector2f;
|
||||
import com.flowpowered.math.vector.Vector3f;
|
||||
|
||||
@Deprecated
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
@ -1,64 +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.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.model;
|
||||
|
||||
import de.bluecolored.bluemap.core.threejs.BufferAttribute;
|
||||
import de.bluecolored.bluemap.core.threejs.BufferGeometry;
|
||||
|
||||
@Deprecated
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
@ -1,241 +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.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.model;
|
||||
|
||||
import com.flowpowered.math.imaginary.Quaternionf;
|
||||
import com.flowpowered.math.matrix.Matrix3f;
|
||||
import com.flowpowered.math.vector.Vector2f;
|
||||
import com.flowpowered.math.vector.Vector3f;
|
||||
|
||||
@Deprecated
|
||||
public class Face {
|
||||
|
||||
private final VectorM3f p1, p2, p3; // points
|
||||
private final VectorM3f 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 = new VectorM3f(p1);
|
||||
this.p2 = new VectorM3f(p2);
|
||||
this.p3 = new VectorM3f(p3);
|
||||
|
||||
this.uv1 = uv1;
|
||||
this.uv2 = uv2;
|
||||
this.uv3 = uv3;
|
||||
|
||||
this.materialIndex = materialIndex;
|
||||
|
||||
this.n1 = calculateSurfaceNormal(new VectorM3f(0, 0, 0));
|
||||
this.n2 = new VectorM3f(n1);
|
||||
this.n3 = new VectorM3f(n1);
|
||||
this.normalizedNormals = true;
|
||||
|
||||
Vector3f color = Vector3f.ONE;
|
||||
this.c1 = color;
|
||||
this.c2 = color;
|
||||
this.c3 = color;
|
||||
}
|
||||
|
||||
public void rotate(Quaternionf rotation) {
|
||||
p1.rotate(rotation);
|
||||
p2.rotate(rotation);
|
||||
p3.rotate(rotation);
|
||||
|
||||
n1.rotate(rotation);
|
||||
n2.rotate(rotation);
|
||||
n3.rotate(rotation);
|
||||
}
|
||||
|
||||
public void transform(Matrix3f transformation) {
|
||||
MatrixM3f mtransform = new MatrixM3f(transformation);
|
||||
|
||||
p1.transform(mtransform);
|
||||
p2.transform(mtransform);
|
||||
p3.transform(mtransform);
|
||||
|
||||
n1.transform(mtransform);
|
||||
n2.transform(mtransform);
|
||||
n3.transform(mtransform);
|
||||
|
||||
normalizedNormals = false;
|
||||
}
|
||||
|
||||
public void translate(Vector3f translation) {
|
||||
p1.add(translation);
|
||||
p2.add(translation);
|
||||
p3.add(translation);
|
||||
}
|
||||
|
||||
public Vector3f getP1() {
|
||||
return p1.toVector3f();
|
||||
}
|
||||
|
||||
public void setP1(Vector3f p1) {
|
||||
this.p1.set(p1);
|
||||
}
|
||||
|
||||
public Vector3f getP2() {
|
||||
return p2.toVector3f();
|
||||
}
|
||||
|
||||
public void setP2(Vector3f p2) {
|
||||
this.p2.set(p2);
|
||||
}
|
||||
|
||||
public Vector3f getP3() {
|
||||
return p3.toVector3f();
|
||||
}
|
||||
|
||||
public void setP3(Vector3f p3) {
|
||||
this.p3.set(p3);
|
||||
}
|
||||
|
||||
public Vector3f getN1() {
|
||||
normalizeNormals();
|
||||
return n1.toVector3f();
|
||||
}
|
||||
|
||||
public void setN1(Vector3f n1) {
|
||||
this.n1.set(n1);
|
||||
normalizedNormals = false;
|
||||
}
|
||||
|
||||
public Vector3f getN2() {
|
||||
normalizeNormals();
|
||||
return n2.toVector3f();
|
||||
}
|
||||
|
||||
public void setN2(Vector3f n2) {
|
||||
this.n2.set(n2);
|
||||
normalizedNormals = false;
|
||||
}
|
||||
|
||||
public Vector3f getN3() {
|
||||
normalizeNormals();
|
||||
return n3.toVector3f();
|
||||
}
|
||||
|
||||
public void setN3(Vector3f n3) {
|
||||
this.n3.set(n3);
|
||||
normalizedNormals = false;
|
||||
}
|
||||
|
||||
public Vector3f getC1() {
|
||||
return c1;
|
||||
}
|
||||
|
||||
public void setC1(Vector3f c1) {
|
||||
this.c1 = c1;
|
||||
}
|
||||
|
||||
public Vector3f getC2() {
|
||||
return c2;
|
||||
}
|
||||
|
||||
public void setC2(Vector3f c2) {
|
||||
this.c2 = c2;
|
||||
}
|
||||
|
||||
public Vector3f getC3() {
|
||||
return c3;
|
||||
}
|
||||
|
||||
public void setC3(Vector3f c3) {
|
||||
this.c3 = c3;
|
||||
}
|
||||
|
||||
public Vector2f getUv1() {
|
||||
return uv1;
|
||||
}
|
||||
|
||||
public void setUv1(Vector2f uv1) {
|
||||
this.uv1 = uv1;
|
||||
}
|
||||
|
||||
public Vector2f getUv2() {
|
||||
return uv2;
|
||||
}
|
||||
|
||||
public void setUv2(Vector2f uv2) {
|
||||
this.uv2 = uv2;
|
||||
}
|
||||
|
||||
public Vector2f getUv3() {
|
||||
return uv3;
|
||||
}
|
||||
|
||||
public void setUv3(Vector2f uv3) {
|
||||
this.uv3 = uv3;
|
||||
}
|
||||
|
||||
public int getMaterialIndex() {
|
||||
return materialIndex;
|
||||
}
|
||||
|
||||
public void setMaterialIndex(int materialIndex) {
|
||||
this.materialIndex = materialIndex;
|
||||
}
|
||||
|
||||
private void normalizeNormals() {
|
||||
if (normalizedNormals) return;
|
||||
|
||||
n1.normalize();
|
||||
n2.normalize();
|
||||
n3.normalize();
|
||||
|
||||
normalizedNormals = true;
|
||||
}
|
||||
|
||||
private VectorM3f calculateSurfaceNormal(
|
||||
VectorM3f target
|
||||
){
|
||||
double
|
||||
p1x = p1.x, p1y = p1.y, p1z = p1.z,
|
||||
p2x = p2.x, p2y = p2.y, p2z = p2.z,
|
||||
p3x = p3.x, p3y = p3.y, p3z = p3.z;
|
||||
|
||||
p2x -= p1x; p2y -= p1y; p2z -= p1z;
|
||||
p3x -= p1x; p3y -= p1y; p3z -= p1z;
|
||||
|
||||
p1x = p2y * p3z - p2z * p3y;
|
||||
p1y = p2z * p3x - p2x * p3z;
|
||||
p1z = p2x * p3y - p2y * p3x;
|
||||
|
||||
double length = Math.sqrt(p1x * p1x + p1y * p1y + p1z * p1z);
|
||||
p1x /= length;
|
||||
p1y /= length;
|
||||
p1z /= length;
|
||||
|
||||
target.x = (float) p1x;
|
||||
target.y = (float) p1y;
|
||||
target.z = (float) p1z;
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
}
|
@ -1,48 +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.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.model;
|
||||
|
||||
import com.flowpowered.math.matrix.Matrix3f;
|
||||
|
||||
@Deprecated
|
||||
public class MatrixM3f {
|
||||
|
||||
public float m00, m01, m02;
|
||||
public float m10, m11, m12;
|
||||
public float m20, m21, m22;
|
||||
|
||||
public MatrixM3f(Matrix3f v) {
|
||||
this.m00 = v.get(0, 0);
|
||||
this.m01 = v.get(0, 1);
|
||||
this.m02 = v.get(0, 2);
|
||||
this.m10 = v.get(1, 0);
|
||||
this.m11 = v.get(1, 1);
|
||||
this.m12 = v.get(1, 2);
|
||||
this.m20 = v.get(2, 0);
|
||||
this.m21 = v.get(2, 1);
|
||||
this.m22 = v.get(2, 2);
|
||||
}
|
||||
|
||||
}
|
@ -1,149 +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.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.model;
|
||||
|
||||
import com.flowpowered.math.imaginary.Quaternionf;
|
||||
import com.flowpowered.math.matrix.Matrix3f;
|
||||
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;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Deprecated
|
||||
public class Model<T extends Face> {
|
||||
|
||||
private List<T> faces;
|
||||
|
||||
public Model() {
|
||||
this.faces = new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<T> model){
|
||||
faces.addAll(model.getFaces());
|
||||
}
|
||||
|
||||
public void addFace(T face){
|
||||
faces.add(face);
|
||||
}
|
||||
|
||||
public void removeFace(T face){
|
||||
faces.remove(face);
|
||||
}
|
||||
|
||||
public List<T> getFaces(){
|
||||
return faces;
|
||||
}
|
||||
|
||||
public void rotate(Quaternionf rotation){
|
||||
for (Face f : faces){
|
||||
f.rotate(rotation);
|
||||
}
|
||||
}
|
||||
|
||||
public void transform(Matrix3f transformation){
|
||||
for (T f : faces){
|
||||
f.transform(transformation);
|
||||
}
|
||||
}
|
||||
|
||||
public void translate(Vector3f translation){
|
||||
for (T f : faces){
|
||||
f.translate(translation);
|
||||
}
|
||||
}
|
||||
|
||||
public BufferGeometry toBufferGeometry() {
|
||||
|
||||
//sort faces by material index
|
||||
faces.sort((f1, f2) -> (int) Math.signum(f1.getMaterialIndex() - f2.getMaterialIndex()));
|
||||
|
||||
//reorganize all faces into arrays and create material-groups
|
||||
int count = faces.size();
|
||||
|
||||
List<MaterialGroup> groups = new ArrayList<>();
|
||||
int groupStart = 0;
|
||||
int currentMaterialIndex = -1;
|
||||
if (count > 0) currentMaterialIndex = faces.get(0).getMaterialIndex();
|
||||
|
||||
float[] position = new float[count * 3 * 3];
|
||||
float[] normal = new float[count * 3 * 3];
|
||||
float[] color = new float[count * 3 * 3];
|
||||
float[] uv = new float[count * 2 * 3];
|
||||
|
||||
for (int itemIndex = 0; itemIndex < count; itemIndex++){
|
||||
T f = faces.get(itemIndex);
|
||||
|
||||
if (currentMaterialIndex != f.getMaterialIndex()){
|
||||
groups.add(new MaterialGroup(currentMaterialIndex, groupStart * 3, (itemIndex - groupStart) * 3));
|
||||
groupStart = itemIndex;
|
||||
currentMaterialIndex = f.getMaterialIndex();
|
||||
}
|
||||
|
||||
addVector3fToArray( position, f.getP1(), (itemIndex * 3 + 0) * 3 );
|
||||
addVector3fToArray( normal, f.getN1(), (itemIndex * 3 + 0) * 3 );
|
||||
addVector3fToArray( color, f.getC1(), (itemIndex * 3 + 0) * 3 );
|
||||
addVector2fToArray( uv, f.getUv1(), (itemIndex * 3 + 0) * 2 );
|
||||
|
||||
addVector3fToArray( position, f.getP2(), (itemIndex * 3 + 1) * 3 );
|
||||
addVector3fToArray( normal, f.getN2(), (itemIndex * 3 + 1) * 3 );
|
||||
addVector3fToArray( color, f.getC2(), (itemIndex * 3 + 1) * 3 );
|
||||
addVector2fToArray( uv, f.getUv2(), (itemIndex * 3 + 1) * 2 );
|
||||
|
||||
addVector3fToArray( position, f.getP3(), (itemIndex * 3 + 2) * 3 );
|
||||
addVector3fToArray( normal, f.getN3(), (itemIndex * 3 + 2) * 3 );
|
||||
addVector3fToArray( color, f.getC3(), (itemIndex * 3 + 2) * 3 );
|
||||
addVector2fToArray( uv, f.getUv3(), (itemIndex * 3 + 2) * 2 );
|
||||
}
|
||||
|
||||
groups.add(new MaterialGroup(currentMaterialIndex, groupStart * 3, (count - groupStart) * 3));
|
||||
|
||||
return new BufferGeometry(
|
||||
position,
|
||||
normal,
|
||||
color,
|
||||
uv,
|
||||
groups.toArray(new MaterialGroup[groups.size()])
|
||||
);
|
||||
}
|
||||
|
||||
static void addVector3fToArray(float[] array, Vector3f v, int startIndex){
|
||||
array[startIndex] = v.getX();
|
||||
array[startIndex + 1] = v.getY();
|
||||
array[startIndex + 2] = v.getZ();
|
||||
}
|
||||
|
||||
static void addVector2fToArray(float[] array, Vector2f v, int startIndex){
|
||||
array[startIndex] = v.getX();
|
||||
array[startIndex + 1] = v.getY();
|
||||
}
|
||||
|
||||
}
|
@ -1,121 +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.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.model;
|
||||
|
||||
import com.flowpowered.math.GenericMath;
|
||||
import com.flowpowered.math.imaginary.Quaternionf;
|
||||
import com.flowpowered.math.vector.Vector3f;
|
||||
|
||||
@Deprecated
|
||||
public class VectorM3f {
|
||||
|
||||
public float x, y, z;
|
||||
|
||||
public VectorM3f(float x, float y, float z) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
public VectorM3f(VectorM3f v) {
|
||||
this.x = v.x;
|
||||
this.y = v.y;
|
||||
this.z = v.z;
|
||||
}
|
||||
|
||||
public VectorM3f(Vector3f v) {
|
||||
this.x = v.getX();
|
||||
this.y = v.getY();
|
||||
this.z = v.getZ();
|
||||
}
|
||||
|
||||
public void set(Vector3f v) {
|
||||
this.x = v.getX();
|
||||
this.y = v.getY();
|
||||
this.z = v.getZ();
|
||||
}
|
||||
|
||||
public void add(VectorM3f translation) {
|
||||
this.x += translation.x;
|
||||
this.y += translation.y;
|
||||
this.z += translation.z;
|
||||
}
|
||||
|
||||
public void add(Vector3f translation) {
|
||||
this.x += translation.getX();
|
||||
this.y += translation.getY();
|
||||
this.z += translation.getZ();
|
||||
}
|
||||
|
||||
public void rotate(Quaternionf rotation) {
|
||||
final float length = rotation.length();
|
||||
if (Math.abs(length) < GenericMath.FLT_EPSILON) {
|
||||
throw new ArithmeticException("Cannot rotate by the zero quaternion");
|
||||
}
|
||||
final float nx = rotation.getX() / length;
|
||||
final float ny = rotation.getY() / length;
|
||||
final float nz = rotation.getZ() / length;
|
||||
final float nw = rotation.getW() / length;
|
||||
final float px = nw * x + ny * z - nz * y;
|
||||
final float py = nw * y + nz * x - nx * z;
|
||||
final float pz = nw * z + nx * y - ny * x;
|
||||
final float pw = -nx * x - ny * y - nz * z;
|
||||
|
||||
this.x = pw * -nx + px * nw - py * nz + pz * ny;
|
||||
this.y = pw * -ny + py * nw - pz * nx + px * nz;
|
||||
this.z = pw * -nz + pz * nw - px * ny + py * nx;
|
||||
}
|
||||
|
||||
public void transform(MatrixM3f t) {
|
||||
float lx = x, ly = y;
|
||||
this.x = t.m00 * lx + t.m01 * ly + t.m02 * z;
|
||||
this.y = t.m10 * lx + t.m11 * ly + t.m12 * z;
|
||||
this.z = t.m20 * lx + t.m21 * ly + t.m22 * z;
|
||||
}
|
||||
|
||||
public void normalize() {
|
||||
final float length = length();
|
||||
if (Math.abs(length) < GenericMath.FLT_EPSILON) {
|
||||
throw new ArithmeticException("Cannot normalize the zero vector");
|
||||
}
|
||||
|
||||
x /= length;
|
||||
y /= length;
|
||||
z /= length;
|
||||
}
|
||||
|
||||
public float length() {
|
||||
return (float) Math.sqrt(lengthSquared());
|
||||
}
|
||||
|
||||
public float lengthSquared() {
|
||||
return x * x + y * y + z * z;
|
||||
}
|
||||
|
||||
public Vector3f toVector3f() {
|
||||
return new Vector3f(this.x, this.y, this.z);
|
||||
}
|
||||
|
||||
}
|
@ -229,7 +229,7 @@ private void getColorFromMap(Biome biome, int[] colorMap, int defaultColor, Colo
|
||||
int y = (int) ((1.0 - humidity) * 255.0);
|
||||
|
||||
int index = y << 8 | x;
|
||||
int color = index >= colorMap.length ? defaultColor : colorMap[index];
|
||||
int color = (index >= colorMap.length ? defaultColor : colorMap[index]) | 0xFF000000;
|
||||
|
||||
target.set(color);
|
||||
}
|
||||
|
@ -58,7 +58,9 @@ public Color read(JsonReader in) throws IOException {
|
||||
value.set(ConfigUtils.parseColorFromString(in.nextString()));
|
||||
break;
|
||||
case NUMBER:
|
||||
value.set(in.nextInt());
|
||||
int color = in.nextInt();
|
||||
if ((color & 0xFF000000) == 0) color = color | 0xFF000000; // assume full alpha if not specified
|
||||
value.set(color);
|
||||
break;
|
||||
case NULL:
|
||||
break;
|
||||
|
@ -26,8 +26,9 @@ public JsonElement serialize(BmMap map, Type typeOfSrc, JsonSerializationContext
|
||||
// hires
|
||||
Vector2i hiresTileSize = map.getHiresModelManager().getTileGrid().getGridSize();
|
||||
Vector2i gridOrigin = map.getHiresModelManager().getTileGrid().getOffset();
|
||||
Vector2i lowresTileSize = map.getLowresModelManager().getTileSize();
|
||||
Vector2i lowresPointsPerHiresTile = map.getLowresModelManager().getPointsPerHiresTile();
|
||||
//Vector2i lowresTileSize = map.getLowresModelManager().getTileSize();
|
||||
//Vector2i lowresPointsPerHiresTile = map.getLowresModelManager().getPointsPerHiresTile();
|
||||
//TODO
|
||||
|
||||
JsonObject hires = new JsonObject();
|
||||
hires.add("tileSize", context.serialize(hiresTileSize));
|
||||
@ -36,6 +37,7 @@ public JsonElement serialize(BmMap map, Type typeOfSrc, JsonSerializationContext
|
||||
root.add("hires", hires);
|
||||
|
||||
// lowres
|
||||
/*
|
||||
Vector2i pointSize = hiresTileSize.div(lowresPointsPerHiresTile);
|
||||
Vector2i tileSize = pointSize.mul(lowresTileSize);
|
||||
|
||||
@ -44,6 +46,7 @@ public JsonElement serialize(BmMap map, Type typeOfSrc, JsonSerializationContext
|
||||
lowres.add("scale", context.serialize(pointSize));
|
||||
lowres.add("translate", context.serialize(pointSize.div(2)));
|
||||
root.add("lowres", lowres);
|
||||
*/
|
||||
|
||||
// startPos
|
||||
Vector2i startPos = map.getMapSettings().getStartPos()
|
||||
|
@ -35,13 +35,13 @@ public abstract class Storage implements Closeable {
|
||||
|
||||
public abstract void initialize() throws IOException;
|
||||
|
||||
public abstract OutputStream writeMapTile(String mapId, TileType tileType, Vector2i tile) throws IOException;
|
||||
public abstract OutputStream writeMapTile(String mapId, int lod, Vector2i tile) throws IOException;
|
||||
|
||||
public abstract Optional<CompressedInputStream> readMapTile(String mapId, TileType tileType, Vector2i tile) throws IOException;
|
||||
public abstract Optional<CompressedInputStream> readMapTile(String mapId, int lod, Vector2i tile) throws IOException;
|
||||
|
||||
public abstract Optional<TileData> readMapTileData(String mapId, TileType tileType, Vector2i tile) throws IOException;
|
||||
public abstract Optional<TileData> readMapTileData(String mapId, int lod, Vector2i tile) throws IOException;
|
||||
|
||||
public abstract void deleteMapTile(String mapId, TileType tileType, Vector2i tile) throws IOException;
|
||||
public abstract void deleteMapTile(String mapId, int lod, Vector2i tile) throws IOException;
|
||||
|
||||
public abstract OutputStream writeMeta(String mapId, MetaType metaType) throws IOException;
|
||||
|
||||
@ -51,30 +51,56 @@ public abstract class Storage implements Closeable {
|
||||
|
||||
public abstract void purgeMap(String mapId) throws IOException;
|
||||
|
||||
public TileStorage tileStorage(final String mapId, final TileType tileType) {
|
||||
return new TileStorage(mapId, tileType);
|
||||
public MapStorage mapStorage(final String mapId) {
|
||||
return new MapStorage(mapId);
|
||||
}
|
||||
|
||||
public TileStorage tileStorage(final String mapId, final int lod) {
|
||||
return new TileStorage(mapId, lod);
|
||||
}
|
||||
|
||||
public class MapStorage {
|
||||
|
||||
private final String mapId;
|
||||
|
||||
private MapStorage(String mapId) {
|
||||
this.mapId = mapId;
|
||||
}
|
||||
|
||||
public OutputStream write(int lod, Vector2i tile) throws IOException {
|
||||
return writeMapTile(mapId, lod, tile);
|
||||
}
|
||||
|
||||
public Optional<CompressedInputStream> read(int lod, Vector2i tile) throws IOException {
|
||||
return readMapTile(mapId, lod, tile);
|
||||
}
|
||||
|
||||
public void delete(int lod, Vector2i tile) throws IOException {
|
||||
deleteMapTile(mapId, lod, tile);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class TileStorage {
|
||||
|
||||
private final String mapId;
|
||||
private final TileType tileType;
|
||||
private final int lod;
|
||||
|
||||
private TileStorage(String mapId, TileType tileType) {
|
||||
private TileStorage(String mapId, int lod) {
|
||||
this.mapId = mapId;
|
||||
this.tileType = tileType;
|
||||
this.lod = lod;
|
||||
}
|
||||
|
||||
public OutputStream write(Vector2i tile) throws IOException {
|
||||
return writeMapTile(mapId, tileType, tile);
|
||||
return writeMapTile(mapId, lod, tile);
|
||||
}
|
||||
|
||||
public Optional<CompressedInputStream> read(Vector2i tile) throws IOException {
|
||||
return readMapTile(mapId, tileType, tile);
|
||||
return readMapTile(mapId, lod, tile);
|
||||
}
|
||||
|
||||
public void delete(Vector2i tile) throws IOException {
|
||||
deleteMapTile(mapId, tileType, tile);
|
||||
deleteMapTile(mapId, lod, tile);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,52 +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.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.storage;
|
||||
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
public enum TileType {
|
||||
|
||||
HIRES ("hires"),
|
||||
LOWRES ("lowres");
|
||||
|
||||
private final String typeId;
|
||||
|
||||
TileType(String typeId) {
|
||||
this.typeId = typeId;
|
||||
}
|
||||
|
||||
public String getTypeId() {
|
||||
return typeId;
|
||||
}
|
||||
|
||||
public static TileType forTypeId(String id) {
|
||||
for (TileType type : values()) {
|
||||
if (type.typeId.equals(id)) return type;
|
||||
}
|
||||
|
||||
throw new NoSuchElementException("There is no TileType with id: " + id);
|
||||
}
|
||||
|
||||
}
|
@ -42,16 +42,16 @@
|
||||
public class FileStorage extends Storage {
|
||||
|
||||
private final Path root;
|
||||
private final Compression compression;
|
||||
private final Compression hiresCompression;
|
||||
|
||||
public FileStorage(FileStorageSettings config) {
|
||||
this.root = config.getRoot();
|
||||
this.compression = config.getCompression();
|
||||
this.hiresCompression = config.getCompression();
|
||||
}
|
||||
|
||||
public FileStorage(Path root, Compression compression) {
|
||||
this.root = root;
|
||||
this.compression = compression;
|
||||
this.hiresCompression = compression;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -61,8 +61,9 @@ public void initialize() {}
|
||||
public void close() throws IOException {}
|
||||
|
||||
@Override
|
||||
public OutputStream writeMapTile(String mapId, TileType tileType, Vector2i tile) throws IOException {
|
||||
Path file = getFilePath(mapId, tileType, tile);
|
||||
public OutputStream writeMapTile(String mapId, int lod, Vector2i tile) throws IOException {
|
||||
Compression compression = lod == 0 ? this.hiresCompression : Compression.NONE;
|
||||
Path file = getFilePath(mapId, lod, tile);
|
||||
|
||||
OutputStream os = AtomicFileHelper.createFilepartOutputStream(file);
|
||||
os = new BufferedOutputStream(os);
|
||||
@ -78,8 +79,9 @@ public OutputStream writeMapTile(String mapId, TileType tileType, Vector2i tile)
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<CompressedInputStream> readMapTile(String mapId, TileType tileType, Vector2i tile) throws IOException {
|
||||
Path file = getFilePath(mapId, tileType, tile);
|
||||
public Optional<CompressedInputStream> readMapTile(String mapId, int lod, Vector2i tile) throws IOException {
|
||||
Compression compression = lod == 0 ? this.hiresCompression : Compression.NONE;
|
||||
Path file = getFilePath(mapId, lod, tile);
|
||||
|
||||
if (!Files.exists(file)) return Optional.empty();
|
||||
|
||||
@ -90,8 +92,9 @@ public Optional<CompressedInputStream> readMapTile(String mapId, TileType tileTy
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<TileData> readMapTileData(String mapId, TileType tileType, Vector2i tile) throws IOException {
|
||||
Path file = getFilePath(mapId, tileType, tile);
|
||||
public Optional<TileData> readMapTileData(String mapId, int lod, Vector2i tile) throws IOException {
|
||||
Compression compression = lod == 0 ? this.hiresCompression : Compression.NONE;
|
||||
Path file = getFilePath(mapId, lod, tile);
|
||||
|
||||
if (!Files.exists(file)) return Optional.empty();
|
||||
|
||||
@ -101,7 +104,7 @@ public Optional<TileData> readMapTileData(String mapId, TileType tileType, Vecto
|
||||
return Optional.of(new TileData() {
|
||||
@Override
|
||||
public CompressedInputStream readMapTile() throws IOException {
|
||||
return FileStorage.this.readMapTile(mapId, tileType, tile)
|
||||
return FileStorage.this.readMapTile(mapId, lod, tile)
|
||||
.orElseThrow(() -> new IOException("Tile no longer present!"));
|
||||
}
|
||||
|
||||
@ -123,8 +126,8 @@ public long getLastModified() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteMapTile(String mapId, TileType tileType, Vector2i tile) throws IOException {
|
||||
Path file = getFilePath(mapId, tileType, tile);
|
||||
public void deleteMapTile(String mapId, int lod, Vector2i tile) throws IOException {
|
||||
Path file = getFilePath(mapId, lod, tile);
|
||||
Files.deleteIfExists(file);
|
||||
}
|
||||
|
||||
@ -161,7 +164,7 @@ public void purgeMap(String mapId) throws IOException {
|
||||
Files.walkFileTree(getFilePath(mapId), DeletingPathVisitor.INSTANCE);
|
||||
}
|
||||
|
||||
public Path getFilePath(String mapId, TileType tileType, Vector2i tile){
|
||||
public Path getFilePath(String mapId, int lod, Vector2i tile){
|
||||
String path = "x" + tile.getX() + "z" + tile.getY();
|
||||
char[] cs = path.toCharArray();
|
||||
List<String> folders = new ArrayList<>();
|
||||
@ -175,12 +178,16 @@ public Path getFilePath(String mapId, TileType tileType, Vector2i tile){
|
||||
}
|
||||
String fileName = folders.remove(folders.size() - 1);
|
||||
|
||||
Path p = getFilePath(mapId).resolve(tileType.getTypeId());
|
||||
Path p = getFilePath(mapId).resolve("tiles").resolve(Integer.toString(lod));
|
||||
for (String s : folders){
|
||||
p = p.resolve(s);
|
||||
}
|
||||
|
||||
return p.resolve(fileName + ".json" + compression.getFileSuffix());
|
||||
if (lod == 0) {
|
||||
return p.resolve(fileName + ".json" + hiresCompression.getFileSuffix());
|
||||
} else {
|
||||
return p.resolve(fileName + ".png");
|
||||
}
|
||||
}
|
||||
|
||||
public Path getFilePath(String mapId) {
|
||||
|
@ -54,14 +54,11 @@
|
||||
public class SQLStorage extends Storage {
|
||||
|
||||
private final DataSource dataSource;
|
||||
private final Compression compression;
|
||||
private final Compression hiresCompression;
|
||||
|
||||
private final LoadingCache<String, Integer> mapFKs = Caffeine.newBuilder()
|
||||
.executor(BlueMap.THREAD_POOL)
|
||||
.build(this::loadMapFK);
|
||||
private final LoadingCache<TileType, Integer> mapTileTypeFKs = Caffeine.newBuilder()
|
||||
.executor(BlueMap.THREAD_POOL)
|
||||
.build(this::loadMapTileTypeFK);
|
||||
private final LoadingCache<Compression, Integer> mapTileCompressionFKs = Caffeine.newBuilder()
|
||||
.executor(BlueMap.THREAD_POOL)
|
||||
.build(this::loadMapTileCompressionFK);
|
||||
@ -98,25 +95,26 @@ public SQLStorage(SQLStorageSettings config) throws MalformedURLException, SQLDr
|
||||
//throw new ConfigurationException("The driver-class does not exist. Check your sql-storage-config!", ex);
|
||||
}
|
||||
|
||||
this.compression = config.getCompression();
|
||||
this.hiresCompression = config.getCompression();
|
||||
}
|
||||
|
||||
public SQLStorage(String dbUrl, String user, String password, Compression compression) {
|
||||
this.dataSource = createDataSource(dbUrl, user, password);
|
||||
this.compression = compression;
|
||||
this.hiresCompression = compression;
|
||||
}
|
||||
|
||||
public SQLStorage(DataSource dataSource, Compression compression) {
|
||||
this.dataSource = dataSource;
|
||||
this.compression = compression;
|
||||
this.hiresCompression = compression;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputStream writeMapTile(String mapId, TileType tileType, Vector2i tile) throws IOException {
|
||||
public OutputStream writeMapTile(String mapId, int lod, Vector2i tile) throws IOException {
|
||||
Compression compression = lod == 0 ? this.hiresCompression : Compression.NONE;
|
||||
|
||||
ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
|
||||
return new WrappedOutputStream(compression.compress(byteOut), () -> {
|
||||
int mapFK = getMapFK(mapId);
|
||||
int tileTypeFK = getMapTileTypeFK(tileType);
|
||||
int tileCompressionFK = getMapTileCompressionFK(compression);
|
||||
|
||||
recoveringConnection(connection -> {
|
||||
@ -128,10 +126,10 @@ public OutputStream writeMapTile(String mapId, TileType tileType, Vector2i tile)
|
||||
|
||||
executeUpdate(connection,
|
||||
//language=SQL
|
||||
"REPLACE INTO `bluemap_map_tile` (`map`, `type`, `x`, `z`, `compression`, `data`) " +
|
||||
"REPLACE INTO `bluemap_map_tile` (`map`, `lod`, `x`, `z`, `compression`, `data`) " +
|
||||
"VALUES (?, ?, ?, ?, ?, ?)",
|
||||
mapFK,
|
||||
tileTypeFK,
|
||||
lod,
|
||||
tile.getX(),
|
||||
tile.getY(),
|
||||
tileCompressionFK,
|
||||
@ -145,7 +143,9 @@ public OutputStream writeMapTile(String mapId, TileType tileType, Vector2i tile)
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<CompressedInputStream> readMapTile(String mapId, TileType tileType, Vector2i tile) throws IOException {
|
||||
public Optional<CompressedInputStream> readMapTile(String mapId, int lod, Vector2i tile) throws IOException {
|
||||
Compression compression = lod == 0 ? this.hiresCompression : Compression.NONE;
|
||||
|
||||
try {
|
||||
byte[] data = recoveringConnection(connection -> {
|
||||
ResultSet result = executeQuery(connection,
|
||||
@ -154,17 +154,15 @@ public Optional<CompressedInputStream> readMapTile(String mapId, TileType tileTy
|
||||
"FROM `bluemap_map_tile` t " +
|
||||
" INNER JOIN `bluemap_map` m " +
|
||||
" ON t.`map` = m.`id` " +
|
||||
" INNER JOIN `bluemap_map_tile_type` u " +
|
||||
" ON t.`type` = u.`id` " +
|
||||
" INNER JOIN `bluemap_map_tile_compression` c " +
|
||||
" ON t.`compression` = c.`id` " +
|
||||
"WHERE m.`map_id` = ? " +
|
||||
"AND u.`type` = ? " +
|
||||
"AND t.`lod` = ? " +
|
||||
"AND t.`x` = ? " +
|
||||
"AND t.`z` = ? " +
|
||||
"AND c.`compression` = ?",
|
||||
mapId,
|
||||
tileType.getTypeId(),
|
||||
lod,
|
||||
tile.getX(),
|
||||
tile.getY(),
|
||||
compression.getTypeId()
|
||||
@ -186,40 +184,39 @@ public Optional<CompressedInputStream> readMapTile(String mapId, TileType tileTy
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<TileData> readMapTileData(final String mapId, final TileType tileType, final Vector2i tile) throws IOException {
|
||||
public Optional<TileData> readMapTileData(final String mapId, int lod, final Vector2i tile) throws IOException {
|
||||
Compression compression = lod == 0 ? this.hiresCompression : Compression.NONE;
|
||||
|
||||
try {
|
||||
TileData tileData = recoveringConnection(connection -> {
|
||||
ResultSet result = executeQuery(connection,
|
||||
//language=SQL
|
||||
"SELECT c.`compression`, t.`changed`, LENGTH(t.`data`) as 'size' " +
|
||||
"SELECT t.`changed`, LENGTH(t.`data`) as 'size' " +
|
||||
"FROM `bluemap_map_tile` t " +
|
||||
" INNER JOIN `bluemap_map` m " +
|
||||
" ON t.`map` = m.`id` " +
|
||||
" INNER JOIN `bluemap_map_tile_type` u " +
|
||||
" ON t.`type` = u.`id` " +
|
||||
" INNER JOIN `bluemap_map_tile_compression` c " +
|
||||
" ON t.`compression` = c.`id` " +
|
||||
"WHERE m.`map_id` = ? " +
|
||||
"AND u.`type` = ? " +
|
||||
"AND t.`lod` = ? " +
|
||||
"AND t.`x` = ? " +
|
||||
"AND t.`z` = ? " +
|
||||
"AND c.`compression` = ?",
|
||||
mapId,
|
||||
tileType.getTypeId(),
|
||||
lod,
|
||||
tile.getX(),
|
||||
tile.getY(),
|
||||
compression.getTypeId()
|
||||
);
|
||||
|
||||
if (result.next()) {
|
||||
final Compression compression = Compression.forTypeId(result.getString("compression"));
|
||||
final long lastModified = result.getTimestamp("changed").getTime();
|
||||
final long size = result.getLong("size");
|
||||
|
||||
return new TileData() {
|
||||
@Override
|
||||
public CompressedInputStream readMapTile() throws IOException {
|
||||
return SQLStorage.this.readMapTile(mapId, tileType, tile)
|
||||
return SQLStorage.this.readMapTile(mapId, lod, tile)
|
||||
.orElseThrow(() -> new IOException("Tile no longer present!"));
|
||||
}
|
||||
|
||||
@ -250,7 +247,7 @@ public long getLastModified() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteMapTile(String mapId, TileType tileType, Vector2i tile) throws IOException {
|
||||
public void deleteMapTile(String mapId, int lod, Vector2i tile) throws IOException {
|
||||
try {
|
||||
recoveringConnection(connection ->
|
||||
executeUpdate(connection,
|
||||
@ -259,14 +256,12 @@ public void deleteMapTile(String mapId, TileType tileType, Vector2i tile) throws
|
||||
"FROM `bluemap_map_tile` t " +
|
||||
" INNER JOIN `bluemap_map` m " +
|
||||
" ON t.`map` = m.`id` " +
|
||||
" INNER JOIN `bluemap_map_tile_type` u " +
|
||||
" ON t.`type` = u.`id` " +
|
||||
"WHERE m.`map_id` = ? " +
|
||||
"AND u.`type` = ? " +
|
||||
"AND t.`lod` = ? " +
|
||||
"AND t.`x` = ? " +
|
||||
"AND t.`z` = ?",
|
||||
mapId,
|
||||
tileType.getTypeId(),
|
||||
lod,
|
||||
tile.getX(),
|
||||
tile.getY()
|
||||
), 2);
|
||||
@ -423,7 +418,7 @@ public void initialize() throws IOException {
|
||||
}
|
||||
|
||||
// validate schema version
|
||||
if (schemaVersion < 0 || schemaVersion > 1)
|
||||
if (schemaVersion < 0 || schemaVersion > 2)
|
||||
throw new IOException("Unknown schema-version: " + schemaVersion);
|
||||
|
||||
// update schema to current version
|
||||
@ -441,15 +436,6 @@ public void initialize() throws IOException {
|
||||
");"
|
||||
);
|
||||
|
||||
connection.createStatement().executeUpdate(
|
||||
"CREATE TABLE `bluemap_map_tile_type` (" +
|
||||
"`id` SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT," +
|
||||
"`type` VARCHAR(255) NOT NULL," +
|
||||
"PRIMARY KEY (`id`)," +
|
||||
"UNIQUE INDEX `type` (`type`)" +
|
||||
");"
|
||||
);
|
||||
|
||||
connection.createStatement().executeUpdate(
|
||||
"CREATE TABLE `bluemap_map_tile_compression` (" +
|
||||
"`id` SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT," +
|
||||
@ -471,15 +457,14 @@ public void initialize() throws IOException {
|
||||
connection.createStatement().executeUpdate(
|
||||
"CREATE TABLE `bluemap_map_tile` (" +
|
||||
"`map` SMALLINT UNSIGNED NOT NULL," +
|
||||
"`type` SMALLINT UNSIGNED NOT NULL," +
|
||||
"`lod` SMALLINT UNSIGNED NOT NULL," +
|
||||
"`x` INT NOT NULL," +
|
||||
"`z` INT NOT NULL," +
|
||||
"`compression` SMALLINT UNSIGNED NOT NULL," +
|
||||
"`changed` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP," +
|
||||
"`data` LONGBLOB NOT NULL," +
|
||||
"PRIMARY KEY (`map`, `type`, `x`, `z`)," +
|
||||
"PRIMARY KEY (`map`, `lod`, `x`, `z`)," +
|
||||
"CONSTRAINT `fk_bluemap_map_tile_map` FOREIGN KEY (`map`) REFERENCES `bluemap_map` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT," +
|
||||
"CONSTRAINT `fk_bluemap_map_tile_type` FOREIGN KEY (`type`) REFERENCES `bluemap_map_tile_type` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT," +
|
||||
"CONSTRAINT `fk_bluemap_map_tile_compression` FOREIGN KEY (`compression`) REFERENCES `bluemap_map_tile_compression` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT" +
|
||||
");"
|
||||
);
|
||||
@ -489,12 +474,17 @@ public void initialize() throws IOException {
|
||||
"UPDATE `bluemap_storage_meta` " +
|
||||
"SET `value` = ? " +
|
||||
"WHERE `key` = ?",
|
||||
"1", "schema_version"
|
||||
"2", "schema_version"
|
||||
);
|
||||
}, 2);
|
||||
|
||||
//schemaVersion = 1;
|
||||
schemaVersion = 2;
|
||||
}
|
||||
|
||||
if (schemaVersion == 1)
|
||||
throw new IOException("Outdated database schema: " + schemaVersion +
|
||||
" (Cannot automatically update, reset your database and reload bluemap to fix this)");
|
||||
|
||||
} catch (SQLException ex) {
|
||||
throw new IOException(ex);
|
||||
}
|
||||
@ -585,19 +575,6 @@ private int getMapFK(String mapId) throws SQLException {
|
||||
}
|
||||
}
|
||||
|
||||
private int getMapTileTypeFK(TileType tileType) throws SQLException {
|
||||
try {
|
||||
return Objects.requireNonNull(mapTileTypeFKs.get(tileType));
|
||||
} catch (CompletionException ex) {
|
||||
Throwable cause = ex.getCause();
|
||||
|
||||
if (cause instanceof SQLException)
|
||||
throw (SQLException) cause;
|
||||
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
private int getMapTileCompressionFK(Compression compression) throws SQLException {
|
||||
try {
|
||||
return Objects.requireNonNull(mapTileCompressionFKs.get(compression));
|
||||
@ -615,10 +592,6 @@ private int loadMapFK(String mapId) throws SQLException, IOException {
|
||||
return lookupFK("bluemap_map", "id", "map_id", mapId);
|
||||
}
|
||||
|
||||
private int loadMapTileTypeFK(TileType mapTileType) throws SQLException, IOException {
|
||||
return lookupFK("bluemap_map_tile_type", "id", "type", mapTileType.getTypeId());
|
||||
}
|
||||
|
||||
private int loadMapTileCompressionFK(Compression compression) throws SQLException, IOException {
|
||||
return lookupFK("bluemap_map_tile_compression", "id", "compression", compression.getTypeId());
|
||||
}
|
||||
|
@ -24,130 +24,10 @@
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.util;
|
||||
|
||||
import com.flowpowered.math.vector.*;
|
||||
import org.spongepowered.configurate.ConfigurationNode;
|
||||
import org.spongepowered.configurate.NodePath;
|
||||
import org.spongepowered.configurate.serialize.SerializationException;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ConfigUtils {
|
||||
|
||||
private ConfigUtils(){}
|
||||
|
||||
public static Vector2i readVector2i(ConfigurationNode vectorNode){
|
||||
if (vectorNode.isList()){
|
||||
List<? extends ConfigurationNode> list = vectorNode.childrenList();
|
||||
return new Vector2i(
|
||||
list.get(0).getInt(),
|
||||
list.get(1).getInt()
|
||||
);
|
||||
}
|
||||
|
||||
return new Vector2i(
|
||||
vectorNode.node("x").getInt(),
|
||||
vectorNode.node("y").getInt()
|
||||
);
|
||||
}
|
||||
|
||||
public static Vector3i readVector3i(ConfigurationNode vectorNode){
|
||||
if (vectorNode.isList()){
|
||||
List<? extends ConfigurationNode> list = vectorNode.childrenList();
|
||||
return new Vector3i(
|
||||
list.get(0).getInt(),
|
||||
list.get(1).getInt(),
|
||||
list.get(2).getInt()
|
||||
);
|
||||
}
|
||||
|
||||
return new Vector3i(
|
||||
vectorNode.node("x").getInt(),
|
||||
vectorNode.node("y").getInt(),
|
||||
vectorNode.node("z").getInt()
|
||||
);
|
||||
}
|
||||
|
||||
public static Vector3f readVector3f(ConfigurationNode vectorNode){
|
||||
if (vectorNode.isList()){
|
||||
List<? extends ConfigurationNode> list = vectorNode.childrenList();
|
||||
return new Vector3f(
|
||||
list.get(0).getFloat(),
|
||||
list.get(1).getFloat(),
|
||||
list.get(2).getFloat()
|
||||
);
|
||||
}
|
||||
|
||||
return new Vector3f(
|
||||
vectorNode.node("x").getFloat(),
|
||||
vectorNode.node("y").getFloat(),
|
||||
vectorNode.node("z").getFloat()
|
||||
);
|
||||
}
|
||||
|
||||
public static Vector4i readVector4i(ConfigurationNode vectorNode){
|
||||
if (vectorNode.isList()){
|
||||
List<? extends ConfigurationNode> list = vectorNode.childrenList();
|
||||
return new Vector4i(
|
||||
list.get(0).getInt(),
|
||||
list.get(1).getInt(),
|
||||
list.get(2).getInt(),
|
||||
list.get(3).getInt()
|
||||
);
|
||||
}
|
||||
|
||||
return new Vector4i(
|
||||
vectorNode.node("x").getInt(),
|
||||
vectorNode.node("y").getInt(),
|
||||
vectorNode.node("z").getInt(),
|
||||
vectorNode.node("w").getInt()
|
||||
);
|
||||
}
|
||||
|
||||
public static Vector4f readVector4f(ConfigurationNode vectorNode){
|
||||
if (vectorNode.isList()){
|
||||
List<? extends ConfigurationNode> list = vectorNode.childrenList();
|
||||
return new Vector4f(
|
||||
list.get(0).getFloat(),
|
||||
list.get(1).getFloat(),
|
||||
list.get(2).getFloat(),
|
||||
list.get(3).getFloat()
|
||||
);
|
||||
}
|
||||
|
||||
return new Vector4f(
|
||||
vectorNode.node("x").getFloat(),
|
||||
vectorNode.node("y").getFloat(),
|
||||
vectorNode.node("z").getFloat(),
|
||||
vectorNode.node("w").getFloat()
|
||||
);
|
||||
}
|
||||
|
||||
public static void writeVector4f(ConfigurationNode vectorNode, Vector4f v) throws SerializationException {
|
||||
vectorNode.appendListNode().set(v.getX());
|
||||
vectorNode.appendListNode().set(v.getY());
|
||||
vectorNode.appendListNode().set(v.getZ());
|
||||
vectorNode.appendListNode().set(v.getW());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a color-integer. The value can be a normal integer, an integer in String-Format, or a string in hexadecimal format prefixed with # (css-style: e.g. #f16 becomes #ff1166).
|
||||
* @param node The Configuration Node with the value
|
||||
* @return The parsed Integer
|
||||
* @throws NumberFormatException If the value is not formatted correctly or if there is no value present.
|
||||
*/
|
||||
public static int readColorInt(ConfigurationNode node) throws NumberFormatException {
|
||||
Object value = node.raw();
|
||||
|
||||
if (value == null) throw new NumberFormatException("No value!");
|
||||
|
||||
if (value instanceof Number) {
|
||||
return ((Number) value).intValue();
|
||||
}
|
||||
|
||||
String val = value.toString();
|
||||
return parseColorFromString(val);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a color-integer. The value can be an integer in String-Format or a string in hexadecimal format prefixed with # (css-style: e.g. #f16 becomes #ff1166).
|
||||
* @param val The String to parse
|
||||
@ -165,16 +45,9 @@ public static int parseColorFromString(String val) {
|
||||
return Integer.parseUnsignedInt(val, 16);
|
||||
}
|
||||
|
||||
return Integer.parseInt(val);
|
||||
}
|
||||
|
||||
public static String nodePathToString(ConfigurationNode node) {
|
||||
NodePath keys = node.path();
|
||||
String[] stringKeys = new String[keys.size()];
|
||||
for (int i = 0; i < keys.size(); i++) {
|
||||
stringKeys[i] = keys.get(i).toString();
|
||||
}
|
||||
return String.join(".", stringKeys);
|
||||
int color = Integer.parseInt(val);
|
||||
if ((color & 0xFF000000) == 0) color |= 0xFF000000; // assume full alpha if not present
|
||||
return color;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,37 @@
|
||||
package de.bluecolored.bluemap.core.util;
|
||||
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class InstancePool<T> {
|
||||
|
||||
private final Supplier<T> creator;
|
||||
private final Function<T, T> recycler;
|
||||
private final ConcurrentLinkedQueue<T> pool = new ConcurrentLinkedQueue<>();
|
||||
|
||||
public InstancePool(Supplier<T> creator) {
|
||||
this.creator = creator;
|
||||
this.recycler = t -> t;
|
||||
}
|
||||
|
||||
public InstancePool(Supplier<T> creator, Function<T, T> recycler) {
|
||||
this.creator = creator;
|
||||
this.recycler = recycler;
|
||||
}
|
||||
|
||||
public T claimInstance() {
|
||||
T instance = pool.poll();
|
||||
if (instance == null) {
|
||||
instance = creator.get();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
public void recycleInstance(T instance) {
|
||||
instance = recycler.apply(instance);
|
||||
if (instance != null)
|
||||
pool.offer(instance);
|
||||
}
|
||||
|
||||
}
|
@ -1,175 +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.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.util;
|
||||
|
||||
import com.flowpowered.math.vector.Vector3d;
|
||||
import com.flowpowered.math.vector.Vector3f;
|
||||
import com.flowpowered.math.vector.Vector3i;
|
||||
import com.flowpowered.math.vector.Vector4f;
|
||||
import de.bluecolored.bluemap.core.util.math.VectorM3f;
|
||||
|
||||
@Deprecated //TODO
|
||||
public class MathUtils {
|
||||
|
||||
private MathUtils() {}
|
||||
|
||||
/**
|
||||
* Calculates the surface-normal of a plane spanned between three vectors.
|
||||
* @param p1 The first vector
|
||||
* @param p2 The second vector
|
||||
* @param p3 The third vector
|
||||
* @return The calculated normal
|
||||
*/
|
||||
public static Vector3d getSurfaceNormal(Vector3d p1, Vector3d p2, Vector3d p3){
|
||||
Vector3d u = p2.sub(p1);
|
||||
Vector3d v = p3.sub(p1);
|
||||
|
||||
double nX = u.getY() * v.getZ() - u.getZ() * v.getY();
|
||||
double nY = u.getZ() * v.getX() - u.getX() * v.getZ();
|
||||
double nZ = u.getX() * v.getY() - u.getY() * v.getX();
|
||||
|
||||
return new Vector3d(nX, nY, nZ);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the surface-normal of a plane spanned between three vectors.
|
||||
* @param p1 The first vector
|
||||
* @param p2 The second vector
|
||||
* @param p3 The third vector
|
||||
* @return The calculated normal
|
||||
*/
|
||||
public static Vector3f getSurfaceNormal(Vector3f p1, Vector3f p2, Vector3f p3) {
|
||||
Vector3f u = p2.sub(p1);
|
||||
Vector3f v = p3.sub(p1);
|
||||
|
||||
float nX = u.getY() * v.getZ() - u.getZ() * v.getY();
|
||||
float nY = u.getZ() * v.getX() - u.getX() * v.getZ();
|
||||
float nZ = u.getX() * v.getY() - u.getY() * v.getX();
|
||||
|
||||
Vector3f n = new Vector3f(nX, nY, nZ);
|
||||
n = n.normalize();
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Hashes the provided position to a random float between 0 and 1.<br>
|
||||
* <br>
|
||||
* <i>(Implementation adapted from https://github.com/SpongePowered/SpongeAPI/blob/ecd761a70219e467dea47a09fc310e8238e9911f/src/main/java/org/spongepowered/api/extra/skylands/SkylandsUtil.java)</i>
|
||||
*
|
||||
* @param pos The position to hash
|
||||
* @param seed A seed for the hashing
|
||||
* @return The hashed value between 0 and 1
|
||||
*/
|
||||
public static float hashToFloat(Vector3i pos, long seed) {
|
||||
return hashToFloat(pos.getX(), pos.getY(), pos.getZ(), seed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hashes the provided position to a random float between 0 and 1.<br>
|
||||
* <br>
|
||||
* <i>(Implementation adapted from https://github.com/SpongePowered/SpongeAPI/blob/ecd761a70219e467dea47a09fc310e8238e9911f/src/main/java/org/spongepowered/api/extra/skylands/SkylandsUtil.java)</i>
|
||||
*
|
||||
* @param x The x component of the position
|
||||
* @param y The y component of the position
|
||||
* @param z The z component of the position
|
||||
* @param seed A seed for the hashing
|
||||
* @return The hashed value between 0 and 1
|
||||
*/
|
||||
public static float hashToFloat(int x, int y, int z, long seed) {
|
||||
final long hash = x * 73428767 ^ y * 9122569 ^ z * 4382893 ^ seed * 457;
|
||||
return (hash * (hash + 456149) & 0x00ffffff) / (float) 0x01000000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Blends two colors, taking into account the alpha component
|
||||
* @param top The top color
|
||||
* @param bottom The bottom color
|
||||
* @return The merged color
|
||||
*/
|
||||
public static Vector4f blendColors(Vector4f top, Vector4f bottom){
|
||||
if (top.getW() > 0 && bottom.getW() > 0){
|
||||
float a = 1 - (1 - top.getW()) * (1 - bottom.getW());
|
||||
float r = (top.getX() * top.getW() / a) + (bottom.getX() * bottom.getW() * (1 - top.getW()) / a);
|
||||
float g = (top.getY() * top.getW() / a) + (bottom.getY() * bottom.getW() * (1 - top.getW()) / a);
|
||||
float b = (top.getZ() * top.getW() / a) + (bottom.getZ() * bottom.getW() * (1 - top.getW()) / a);
|
||||
return new Vector4f(r, g, b, a);
|
||||
} else if (bottom.getW() > 0) {
|
||||
return bottom;
|
||||
} else {
|
||||
return top;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overlays two colors, taking into account the alpha component
|
||||
* @param top The top color
|
||||
* @param bottom The bottom color
|
||||
* @return The merged color
|
||||
*/
|
||||
public static Vector4f overlayColors(Vector4f top, Vector4f bottom){
|
||||
if (top.getW() > 0 && bottom.getW() > 0){
|
||||
float p = (1 - top.getW()) * bottom.getW();
|
||||
float a = p + top.getW();
|
||||
float r = (p * bottom.getX() + top.getW() * top.getX()) / a;
|
||||
float g = (p * bottom.getY() + top.getW() * top.getY()) / a;
|
||||
float b = (p * bottom.getZ() + top.getW() * top.getZ()) / a;
|
||||
return new Vector4f(r, g, b, a);
|
||||
} else if (bottom.getW() > 0) {
|
||||
return bottom;
|
||||
} else {
|
||||
return top;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link Vector3f} representing the color from the integer
|
||||
* @param color The color-int
|
||||
* @return The color-Vector
|
||||
*/
|
||||
public static Vector3f color3FromInt(int color){
|
||||
return new Vector3f(
|
||||
(color >> 16) & 0xFF,
|
||||
(color >> 8) & 0xFF,
|
||||
color & 0xFF
|
||||
).div(0xFF);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link Vector4f} representing the color from the integer
|
||||
* @param color The color-int
|
||||
* @return The color-Vector
|
||||
*/
|
||||
public static Vector4f color4FromInt(int color){
|
||||
return new Vector4f(
|
||||
(color >> 16) & 0xFF,
|
||||
(color >> 8) & 0xFF,
|
||||
color & 0xFF,
|
||||
(color >> 24) & 0xFF
|
||||
).div(0xFF);
|
||||
}
|
||||
|
||||
}
|
@ -1,91 +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.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.util;
|
||||
|
||||
import com.flowpowered.math.vector.Vector2f;
|
||||
import com.flowpowered.math.vector.Vector2i;
|
||||
import com.flowpowered.math.vector.Vector3f;
|
||||
|
||||
import de.bluecolored.bluemap.core.model.Face;
|
||||
import de.bluecolored.bluemap.core.model.Model;
|
||||
|
||||
@Deprecated //TODO
|
||||
public class ModelUtils {
|
||||
|
||||
private ModelUtils() {}
|
||||
|
||||
/**
|
||||
* Creates a plane-grid with alternating face-rotations.
|
||||
*/
|
||||
public static Model<Face> makeGrid(Vector2i gridSize){
|
||||
Model<Face> m = new Model<Face>();
|
||||
|
||||
float y = 0;
|
||||
|
||||
for (int x = 0; x < gridSize.getX(); x++){
|
||||
for (int z = 0; z < gridSize.getY(); z++){
|
||||
|
||||
Vector3f[] p = new Vector3f[]{
|
||||
new Vector3f(x , y, z + 1),
|
||||
new Vector3f(x + 1, y, z + 1),
|
||||
new Vector3f(x + 1, y, z ),
|
||||
new Vector3f(x , y, z ),
|
||||
};
|
||||
|
||||
Vector2f[] uv = new Vector2f[]{
|
||||
new Vector2f(0, 1),
|
||||
new Vector2f(1, 1),
|
||||
new Vector2f(1, 0),
|
||||
new Vector2f(0, 0),
|
||||
};
|
||||
|
||||
Face f1, f2;
|
||||
if (x % 2 == z % 2){
|
||||
f1 = new Face(p[0], p[1], p[2], uv[0], uv[1], uv[2], -1);
|
||||
f2 = new Face(p[0], p[2], p[3], uv[0], uv[2], uv[3], -1);
|
||||
} else {
|
||||
f1 = new Face(p[0], p[1], p[3], uv[0], uv[1], uv[3], -1);
|
||||
f2 = new Face(p[1], p[2], p[3], uv[1], uv[2], uv[3], -1);
|
||||
}
|
||||
|
||||
Vector3f color = Vector3f.ZERO;
|
||||
|
||||
f1.setC1(color);
|
||||
f1.setC2(color);
|
||||
f1.setC3(color);
|
||||
|
||||
f2.setC1(color);
|
||||
f2.setC2(color);
|
||||
f2.setC3(color);
|
||||
|
||||
m.addFace(f1);
|
||||
m.addFace(f2);
|
||||
}
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package de.bluecolored.bluemap.core.util;
|
||||
|
||||
import com.flowpowered.math.vector.Vector2i;
|
||||
|
||||
public class Vector2iCache {
|
||||
|
||||
private static final int CACHE_SIZE = 0x4000;
|
||||
private static final int CACHE_MASK = CACHE_SIZE - 1;
|
||||
private final Vector2i[] cache;
|
||||
|
||||
public Vector2iCache() {
|
||||
this.cache = new Vector2i[CACHE_SIZE];
|
||||
}
|
||||
|
||||
public Vector2i get(int x, int y) {
|
||||
int cacheIndex = (x * 1456 ^ y * 948375892) & CACHE_MASK;
|
||||
Vector2i possibleMatch = cache[cacheIndex];
|
||||
|
||||
if (possibleMatch != null && possibleMatch.getX() == x && possibleMatch.getY() == y) {
|
||||
return possibleMatch;
|
||||
}
|
||||
return cache[cacheIndex] = new Vector2i(x, y);
|
||||
}
|
||||
|
||||
}
|
@ -51,17 +51,26 @@ public Color set(Color color) {
|
||||
}
|
||||
|
||||
public Color set(int color) {
|
||||
return set(color, false);
|
||||
}
|
||||
|
||||
public Color set(int color, boolean premultiplied) {
|
||||
this.r = ((color >> 16) & 0xFF) / 255f;
|
||||
this.g = ((color >> 8) & 0xFF) / 255f;
|
||||
this.b = (color & 0xFF) / 255f;
|
||||
this.a = ((color >> 24) & 0xFF) / 255f;
|
||||
this.premultiplied = false;
|
||||
|
||||
if (this.a == 0) this.a = 1f; // if the given integer does not define an alpha, we assume 1f
|
||||
|
||||
this.premultiplied = premultiplied;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getInt() {
|
||||
int r = (int) (this.r * 255) & 0xFF;
|
||||
int g = (int) (this.g * 255) & 0xFF;
|
||||
int b = (int) (this.b * 255) & 0xFF;
|
||||
int a = (int) (this.a * 255) & 0xFF;
|
||||
return (a << 24) | (r << 16) | (g << 8) | b;
|
||||
}
|
||||
|
||||
public Color add(Color color) {
|
||||
if (color.a < 1f && !color.premultiplied){
|
||||
throw new IllegalArgumentException("Can only add premultiplied colors with alpha!");
|
||||
@ -77,6 +86,18 @@ public Color add(Color color) {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Color div(int divisor) {
|
||||
premultiplied();
|
||||
|
||||
float p = 1f / divisor;
|
||||
this.r *= p;
|
||||
this.g *= p;
|
||||
this.b *= p;
|
||||
this.a *= p;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Color multiply(Color color) {
|
||||
if (color.premultiplied) premultiplied();
|
||||
else straight();
|
||||
@ -106,7 +127,7 @@ public Color overlay(Color color) {
|
||||
public Color flatten() {
|
||||
if (this.a == 1f) return this;
|
||||
|
||||
if (premultiplied) {
|
||||
if (premultiplied && this.a > 0f) {
|
||||
float m = 1f / this.a;
|
||||
this.r *= m;
|
||||
this.g *= m;
|
||||
@ -130,10 +151,12 @@ public Color premultiplied() {
|
||||
|
||||
public Color straight() {
|
||||
if (premultiplied) {
|
||||
float m = 1f / this.a;
|
||||
this.r *= m;
|
||||
this.g *= m;
|
||||
this.b *= m;
|
||||
if (this.a > 0f) {
|
||||
float m = 1f / this.a;
|
||||
this.r *= m;
|
||||
this.g *= m;
|
||||
this.b *= m;
|
||||
}
|
||||
this.premultiplied = false;
|
||||
}
|
||||
return this;
|
||||
|
@ -35,7 +35,7 @@ public class Biome extends Key {
|
||||
|
||||
private float humidity = 0.5f;
|
||||
private float temp = 0.5f;
|
||||
private final Color waterColor = new Color().set(4159204).premultiplied();
|
||||
private final Color waterColor = new Color().set(4159204 | 0xFF000000).premultiplied();
|
||||
|
||||
private final Color overlayFoliageColor = new Color().premultiplied();
|
||||
private final Color overlayGrassColor = new Color().premultiplied();
|
||||
|
@ -68,38 +68,99 @@ public Vector2i getOffset() {
|
||||
return offset;
|
||||
}
|
||||
|
||||
public int getCellX(int posX) {
|
||||
return Math.floorDiv(posX - offset.getX(), gridSize.getX());
|
||||
}
|
||||
|
||||
public int getCellY(int posY) {
|
||||
return Math.floorDiv(posY - offset.getY(), gridSize.getY());
|
||||
}
|
||||
|
||||
public Vector2i getCell(Vector2i pos) {
|
||||
return new Vector2i(
|
||||
Math.floorDiv(pos.getX() - offset.getX(), gridSize.getX()),
|
||||
Math.floorDiv(pos.getY() - offset.getY(), gridSize.getY())
|
||||
getCellX(pos.getX()),
|
||||
getCellY(pos.getY())
|
||||
);
|
||||
}
|
||||
|
||||
public int getLocalX(int posX) {
|
||||
return Math.floorMod(posX - offset.getX(), gridSize.getX());
|
||||
}
|
||||
|
||||
public int getLocalY(int posY) {
|
||||
return Math.floorMod(posY - offset.getY(), gridSize.getY());
|
||||
}
|
||||
|
||||
public Vector2i getLocal(Vector2i pos) {
|
||||
return new Vector2i(
|
||||
getLocalX(pos.getX()),
|
||||
getLocalY(pos.getY())
|
||||
);
|
||||
}
|
||||
|
||||
public int getCellMinX(int cellX) {
|
||||
return cellX * gridSize.getX() + offset.getX();
|
||||
}
|
||||
|
||||
public int getCellMinY(int cellY) {
|
||||
return cellY * gridSize.getY() + offset.getY();
|
||||
}
|
||||
|
||||
public Vector2i getCellMin(Vector2i cell) {
|
||||
return new Vector2i(
|
||||
cell.getX() * gridSize.getX() + offset.getX(),
|
||||
cell.getY() * gridSize.getY() + offset.getY()
|
||||
getCellMinX(cell.getX()),
|
||||
getCellMinY(cell.getY())
|
||||
);
|
||||
}
|
||||
|
||||
public int getCellMaxX(int cellX) {
|
||||
return (cellX + 1) * gridSize.getX() + offset.getX() - 1;
|
||||
}
|
||||
|
||||
public int getCellMaxY(int cellY) {
|
||||
return (cellY + 1) * gridSize.getY() + offset.getY() - 1;
|
||||
}
|
||||
|
||||
public Vector2i getCellMax(Vector2i cell) {
|
||||
return new Vector2i(
|
||||
(cell.getX() + 1) * gridSize.getX() + offset.getX() - 1,
|
||||
(cell.getY() + 1) * gridSize.getY() + offset.getY() - 1
|
||||
getCellMaxX(cell.getX()),
|
||||
getCellMaxY(cell.getY())
|
||||
);
|
||||
}
|
||||
|
||||
public int getCellMinX(int cellX, Grid targetGrid) {
|
||||
return targetGrid.getCellX(getCellMinX(cellX));
|
||||
}
|
||||
|
||||
public int getCellMinY(int cellY, Grid targetGrid) {
|
||||
return targetGrid.getCellY(getCellMinY(cellY));
|
||||
}
|
||||
|
||||
public Vector2i getCellMin(Vector2i cell, Grid targetGrid) {
|
||||
return targetGrid.getCell(getCellMin(cell));
|
||||
return new Vector2i(
|
||||
getCellMinX(cell.getX(), targetGrid),
|
||||
getCellMinY(cell.getY(), targetGrid)
|
||||
);
|
||||
}
|
||||
|
||||
public int getCellMaxX(int cellX, Grid targetGrid) {
|
||||
return targetGrid.getCellX(getCellMaxX(cellX));
|
||||
}
|
||||
|
||||
public int getCellMaxY(int cellY, Grid targetGrid) {
|
||||
return targetGrid.getCellY(getCellMaxY(cellY));
|
||||
}
|
||||
|
||||
public Vector2i getCellMax(Vector2i cell, Grid targetGrid) {
|
||||
return targetGrid.getCell(getCellMax(cell));
|
||||
return new Vector2i(
|
||||
getCellMaxX(cell.getX(), targetGrid),
|
||||
getCellMaxY(cell.getY(), targetGrid)
|
||||
);
|
||||
}
|
||||
|
||||
public Collection<Vector2i> getIntersecting(Vector2i cell, Grid targetGrid) {
|
||||
Vector2i min = getCellMin(cell, targetGrid);
|
||||
Vector2i max = getCellMin(cell, targetGrid);
|
||||
Vector2i max = getCellMax(cell, targetGrid);
|
||||
|
||||
if (min.equals(max)) return Collections.singleton(min);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user