Rewrite lowres tiles with mutiple LODs

This commit is contained in:
Lukas Rieger (Blue) 2022-08-07 20:32:55 +02:00
parent f50a86d5e1
commit 81574c5176
No known key found for this signature in database
GPG Key ID: 2D09EC5ED2687FF2
32 changed files with 562 additions and 1984 deletions

View File

@ -45,7 +45,7 @@ import java.util.regex.Pattern;
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 class MapStorageRequestHandler implements HttpRequestHandler {
// 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();

View File

@ -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 class BmMap {
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 class BmMap {
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 class BmMap {
}
public synchronized void save() {
lowresModelManager.save();
lowresTileManager.save();
saveRenderState();
saveMarkerState();
@ -234,8 +232,8 @@ public class BmMap {
return hiresModelManager;
}
public LowresModelManager getLowresModelManager() {
return lowresModelManager;
public LowresTileManager getLowresTileManager() {
return lowresTileManager;
}
public Map<String, MarkerSet> getMarkerSets() {

View File

@ -28,6 +28,7 @@ import com.flowpowered.math.vector.Vector2i;
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 class HiresModelManager {
/**
* 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) {

View File

@ -27,6 +27,7 @@ package de.bluecolored.bluemap.core.map.hires;
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 class HiresModelRenderer {
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 class HiresModelRenderer {
}
}
tileMeta.setHeight(x, z, maxHeight);
tileMeta.setColor(x, z, columnColor);
lowresTileManager.set(x, z, columnColor, maxHeight);
}
}
return tileMeta;
}
}

View File

@ -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;
}
}

View File

@ -28,6 +28,7 @@ import com.flowpowered.math.TrigMath;
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.OutputStream;
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 @@ public class HiresTileModel {
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 @@ public class HiresTileModel {
target.set((float) p1x, (float) p1y, (float) p1z);
}
public static InstancePool<HiresTileModel> instancePool() {
return INSTANCE_POOL;
}
}

View File

@ -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;
}
}
}

View File

@ -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();
}
}
}

View File

@ -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");
}
}
}

View File

@ -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);
}
}

View File

@ -28,10 +28,10 @@ import com.flowpowered.math.vector.Vector2i;
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.Collection;
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 class MCAWorld implements World {
@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 @@ public class MCAWorld implements World {
@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 class MCAWorld implements World {
@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 @@ public class MCAWorld implements World {
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);
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -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);
}
}

View File

@ -229,7 +229,7 @@ public class BlockColorCalculatorFactory {
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);
}

View File

@ -58,7 +58,9 @@ public class ColorAdapter extends TypeAdapter<Color> {
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;

View File

@ -26,8 +26,9 @@ public class MapSettingsSerializer implements JsonSerializer<BmMap> {
// 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 class MapSettingsSerializer implements JsonSerializer<BmMap> {
root.add("hires", hires);
// lowres
/*
Vector2i pointSize = hiresTileSize.div(lowresPointsPerHiresTile);
Vector2i tileSize = pointSize.mul(lowresTileSize);
@ -44,6 +46,7 @@ public class MapSettingsSerializer implements JsonSerializer<BmMap> {
lowres.add("scale", context.serialize(pointSize));
lowres.add("translate", context.serialize(pointSize.div(2)));
root.add("lowres", lowres);
*/
// startPos
Vector2i startPos = map.getMapSettings().getStartPos()

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -42,16 +42,16 @@ import java.util.Optional;
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 class FileStorage extends Storage {
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 class FileStorage extends Storage {
}
@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 class FileStorage extends Storage {
}
@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 class FileStorage extends Storage {
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 class FileStorage extends Storage {
}
@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 class FileStorage extends Storage {
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 class FileStorage extends Storage {
}
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) {

View File

@ -54,14 +54,11 @@ import java.util.concurrent.CompletionException;
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 class SQLStorage extends Storage {
//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 class SQLStorage extends Storage {
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 class SQLStorage extends Storage {
}
@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 class SQLStorage extends Storage {
"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 class SQLStorage extends Storage {
}
@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 class SQLStorage extends Storage {
}
@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 class SQLStorage extends Storage {
"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 class SQLStorage extends Storage {
}
// 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 class SQLStorage extends Storage {
");"
);
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 class SQLStorage extends Storage {
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 class SQLStorage extends Storage {
"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 @@ public class SQLStorage extends Storage {
}
}
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 @@ public class SQLStorage extends Storage {
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());
}

View File

@ -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 class ConfigUtils {
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;
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -51,17 +51,26 @@ public class 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 class 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 class 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 class Color {
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;

View File

@ -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();

View File

@ -68,38 +68,99 @@ public class Grid {
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);