From 6e68a8f0e06cdc63e164490bab6c19d1866c55f0 Mon Sep 17 00:00:00 2001 From: "Lukas Rieger (Blue)" Date: Fri, 23 Feb 2024 17:32:07 +0100 Subject: [PATCH] [breaking] Switch hires tile format to prbm (modified prwm) --- BlueMapCommon/build.gradle.kts | 4 +- .../common/web/MapStorageRequestHandler.java | 13 +- BlueMapCommon/webapp/src/js/map/TileLoader.js | 21 +- .../webapp/src/js/map/hires/PRBMLoader.js | 307 +++++++++++++++++ BlueMapCore/build.gradle.kts | 8 +- .../core/map/hires/BlockModelView.java | 10 +- .../core/map/hires/HiresModelManager.java | 23 +- .../core/map/hires/HiresModelRenderer.java | 4 +- .../bluemap/core/map/hires/PRBMWriter.java | 317 ++++++++++++++++++ .../{HiresTileModel.java => TileModel.java} | 314 ++--------------- .../hires/blockmodel/LiquidModelBuilder.java | 4 +- .../blockmodel/ResourceModelBuilder.java | 4 +- .../core/storage/CompressedInputStream.java | 2 +- .../core/storage/file/FileStorage.java | 11 +- .../core/storage/sql/PostgreSQLStorage.java | 2 +- .../bluemap/core/storage/sql/SQLStorage.java | 2 +- .../bluemap/core/util/FileHelper.java | 2 + .../de/bluecolored/bluemap/core/util/Key.java | 8 + .../bluemap/core/util/Registry.java | 7 + .../util/stream/CountingOutputStream.java | 49 +++ .../{ => stream}/DelegateInputStream.java | 2 +- .../{ => stream}/DelegateOutputStream.java | 2 +- .../util/{ => stream}/OnCloseInputStream.java | 2 +- .../{ => stream}/OnCloseOutputStream.java | 2 +- 24 files changed, 769 insertions(+), 351 deletions(-) create mode 100644 BlueMapCommon/webapp/src/js/map/hires/PRBMLoader.js create mode 100644 BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/PRBMWriter.java rename BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/{HiresTileModel.java => TileModel.java} (59%) create mode 100644 BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/stream/CountingOutputStream.java rename BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/{ => stream}/DelegateInputStream.java (97%) rename BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/{ => stream}/DelegateOutputStream.java (94%) rename BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/{ => stream}/OnCloseInputStream.java (97%) rename BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/{ => stream}/OnCloseOutputStream.java (97%) diff --git a/BlueMapCommon/build.gradle.kts b/BlueMapCommon/build.gradle.kts index ad5b5dec..86130a6c 100644 --- a/BlueMapCommon/build.gradle.kts +++ b/BlueMapCommon/build.gradle.kts @@ -33,9 +33,9 @@ dependencies { api ("de.bluecolored.bluemap.core:BlueMapCore") compileOnly ("org.jetbrains:annotations:16.0.2") - compileOnly ("org.projectlombok:lombok:1.18.28") + compileOnly ("org.projectlombok:lombok:1.18.30") - annotationProcessor ("org.projectlombok:lombok:1.18.28") + annotationProcessor ("org.projectlombok:lombok:1.18.30") testImplementation ("org.junit.jupiter:junit-jupiter:5.8.2") testRuntimeOnly ("org.junit.jupiter:junit-jupiter-engine:5.8.2") diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/MapStorageRequestHandler.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/MapStorageRequestHandler.java index 1793d325..1047451e 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/MapStorageRequestHandler.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/MapStorageRequestHandler.java @@ -133,18 +133,7 @@ public class MapStorageRequestHandler implements HttpRequestHandler { return new HttpResponse(HttpStatusCode.INTERNAL_SERVER_ERROR); } - if (path.endsWith(".png")) { - return new HttpResponse(HttpStatusCode.NO_CONTENT); - } - - if (path.endsWith(".json")) { - HttpResponse response = new HttpResponse(HttpStatusCode.OK); - response.addHeader("Content-Type", "application/json"); - response.setData("{}"); - return response; - } - - return new HttpResponse(HttpStatusCode.NOT_FOUND); + return new HttpResponse(HttpStatusCode.NO_CONTENT); } private String calculateETag(String path, TileInfo tileInfo) { diff --git a/BlueMapCommon/webapp/src/js/map/TileLoader.js b/BlueMapCommon/webapp/src/js/map/TileLoader.js index 8d2b756e..cc23d7e3 100644 --- a/BlueMapCommon/webapp/src/js/map/TileLoader.js +++ b/BlueMapCommon/webapp/src/js/map/TileLoader.js @@ -23,13 +23,14 @@ * THE SOFTWARE. */ import {pathFromCoords} from "../util/Utils"; -import {BufferGeometryLoader, FileLoader, Mesh} from "three"; +import {BufferGeometryLoader, FileLoader, Mesh, Material} from "three"; +import {PRBMLoader} from "./hires/PRBMLoader"; export class TileLoader { /** * @param tilePath {string} - * @param material {THREE.Material | THREE.Material[]} + * @param material {Material | Material[]} * @param tileSettings {{ * tileSize: {x: number, z: number}, * scale: {x: number, z: number}, @@ -50,23 +51,17 @@ export class TileLoader { this.loadBlocker = loadBlocker; this.fileLoader = new FileLoader(); - this.fileLoader.setResponseType('json'); + this.fileLoader.setResponseType('arraybuffer'); - this.bufferGeometryLoader = new BufferGeometryLoader(); + this.bufferGeometryLoader = new PRBMLoader(); } load = (tileX, tileZ, cancelCheck = () => false) => { - let tileUrl = this.tilePath + pathFromCoords(tileX, tileZ) + '.json'; + let tileUrl = this.tilePath + pathFromCoords(tileX, tileZ) + '.prbm'; - //await this.loadBlocker(); return new Promise((resolve, reject) => { this.fileLoader.load(tileUrl + '?' + this.tileCacheHash, - async json => { - let geometryJson = json.tileGeometry || {}; - if (!geometryJson.type || geometryJson.type !== 'BufferGeometry'){ - reject({status: "empty"}); - return; - } + async data => { await this.loadBlocker(); if (cancelCheck()){ @@ -74,7 +69,7 @@ export class TileLoader { return; } - let geometry = this.bufferGeometryLoader.parse(geometryJson); + let geometry = this.bufferGeometryLoader.parse(data); let object = new Mesh(geometry, this.material); diff --git a/BlueMapCommon/webapp/src/js/map/hires/PRBMLoader.js b/BlueMapCommon/webapp/src/js/map/hires/PRBMLoader.js new file mode 100644 index 00000000..d3808561 --- /dev/null +++ b/BlueMapCommon/webapp/src/js/map/hires/PRBMLoader.js @@ -0,0 +1,307 @@ +/* + * This file is part of BlueMap, licensed under the MIT License (MIT). + * + * Copyright (c) Blue (Lukas Rieger) + * Copyright (c) Kevin Chapelier + * 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. + * + * Adapted version of PRWM by Kevin Chapelier + * See https://github.com/kchapelier/PRWM for more informations about this file format + */ + +import { + DefaultLoadingManager, + BufferGeometry, + BufferAttribute, + FloatType +} from "three" + +"use strict"; + +let bigEndianPlatform = null; + +/** + * Check if the endianness of the platform is big-endian (most significant bit first) + * @returns {boolean} True if big-endian, false if little-endian + */ +function isBigEndianPlatform() { + if ( bigEndianPlatform === null ) { + let buffer = new ArrayBuffer( 2 ), + uint8Array = new Uint8Array( buffer ), + uint16Array = new Uint16Array( buffer ); + + uint8Array[ 0 ] = 0xAA; // set first byte + uint8Array[ 1 ] = 0xBB; // set second byte + bigEndianPlatform = ( uint16Array[ 0 ] === 0xAABB ); + } + + return bigEndianPlatform; +} + +// match the values defined in the spec to the TypedArray types +let InvertedEncodingTypes = [ + null, + Float32Array, + null, + Int8Array, + Int16Array, + null, + Int32Array, + Uint8Array, + Uint16Array, + null, + Uint32Array +]; + +// define the method to use on a DataView, corresponding the TypedArray type +let getMethods = { + Uint16Array: 'getUint16', + Uint32Array: 'getUint32', + Int16Array: 'getInt16', + Int32Array: 'getInt32', + Float32Array: 'getFloat32', + Float64Array: 'getFloat64' +}; + +function copyFromBuffer( sourceArrayBuffer, viewType, position, length, fromBigEndian ) { + let bytesPerElement = viewType.BYTES_PER_ELEMENT, + result; + + if ( fromBigEndian === isBigEndianPlatform() || bytesPerElement === 1 ) { + result = new viewType( sourceArrayBuffer, position, length ); + } else { + console.debug("PRWM file has opposite encoding, loading will be slow..."); + + let readView = new DataView( sourceArrayBuffer, position, length * bytesPerElement ), + getMethod = getMethods[ viewType.name ], + littleEndian = ! fromBigEndian, + i = 0; + + result = new viewType( length ); + + for ( ; i < length; i ++ ) { + result[ i ] = readView[ getMethod ]( i * bytesPerElement, littleEndian ); + } + } + + return result; +} + +/** + * @param buffer {ArrayBuffer} + * @param offset {number} + */ +function decodePrwm( buffer, offset ) { + offset = offset || 0; + + let array = new Uint8Array( buffer, offset ), + version = array[ 0 ], + flags = array[ 1 ], + indexedGeometry = !! ( flags >> 7 & 0x01 ), + indicesType = flags >> 6 & 0x01, + bigEndian = ( flags >> 5 & 0x01 ) === 1, + attributesNumber = flags & 0x1F, + valuesNumber = 0, + indicesNumber = 0; + + if ( bigEndian ) { + valuesNumber = ( array[ 2 ] << 16 ) + ( array[ 3 ] << 8 ) + array[ 4 ]; + indicesNumber = ( array[ 5 ] << 16 ) + ( array[ 6 ] << 8 ) + array[ 7 ]; + } else { + valuesNumber = array[ 2 ] + ( array[ 3 ] << 8 ) + ( array[ 4 ] << 16 ); + indicesNumber = array[ 5 ] + ( array[ 6 ] << 8 ) + ( array[ 7 ] << 16 ); + } + + /** PRELIMINARY CHECKS **/ + + if ( offset / 4 % 1 !== 0 ) { + throw new Error( 'PRWM decoder: Offset should be a multiple of 4, received ' + offset ); + } + + if ( version === 0 ) { + throw new Error( 'PRWM decoder: Invalid format version: 0' ); + } else if ( version !== 1 ) { + throw new Error( 'PRWM decoder: Unsupported format version: ' + version ); + } + + if ( ! indexedGeometry ) { + if ( indicesType !== 0 ) { + throw new Error( 'PRWM decoder: Indices type must be set to 0 for non-indexed geometries' ); + } else if ( indicesNumber !== 0 ) { + throw new Error( 'PRWM decoder: Number of indices must be set to 0 for non-indexed geometries' ); + } + } + + /** PARSING **/ + + let pos = 8; + + let attributes = {}, + attributeName, + char, + attributeType, + cardinality, + encodingType, + normalized, + arrayType, + values, + indices, + groups, + next, + i; + + for ( i = 0; i < attributesNumber; i ++ ) { + attributeName = ''; + + while ( pos < array.length ) { + char = array[ pos ]; + pos ++; + + if ( char === 0 ) { + break; + } else { + attributeName += String.fromCharCode( char ); + } + } + + flags = array[ pos ]; + + attributeType = flags >> 7 & 0x01; + normalized = flags >> 6 & 0x01; + cardinality = ( flags >> 4 & 0x03 ) + 1; + encodingType = flags & 0x0F; + arrayType = InvertedEncodingTypes[ encodingType ]; + + pos ++; + + // padding to next multiple of 4 + pos = Math.ceil( pos / 4 ) * 4; + + values = copyFromBuffer( buffer, arrayType, pos + offset, cardinality * valuesNumber, bigEndian ); + + pos += arrayType.BYTES_PER_ELEMENT * cardinality * valuesNumber; + + attributes[ attributeName ] = { + type: attributeType, + cardinality: cardinality, + values: values, + normalized: normalized === 1 + }; + } + + indices = null; + if ( indexedGeometry ) { + pos = Math.ceil( pos / 4 ) * 4; + indices = copyFromBuffer( + buffer, + indicesType === 1 ? Uint32Array : Uint16Array, + pos + offset, + indicesNumber, + bigEndian + ); + } + + // read groups + groups = []; + pos = Math.ceil( pos / 4 ) * 4; + while ( pos < array.length ) { + next = read4ByteInt(array, pos); + if (next === -1) { + pos += 4; + break; + } + groups.push({ + materialIndex: next, + start: read4ByteInt(array, pos + 4), + count: read4ByteInt(array, pos + 8) + }); + pos += 12; + } + + return { + version: version, + attributes: attributes, + indices: indices, + groups: groups + }; +} + +function read4ByteInt(array, pos) { + return array[pos] | + array[pos + 1] << 8 | + array[pos + 2] << 16 | + array[pos + 3] << 24; +} + +export class PRBMLoader { + + constructor ( manager ) { + this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; + } + + load ( url, onLoad, onProgress, onError ) { + let scope = this; + + url = url.replace( /\*/g, isBigEndianPlatform() ? 'be' : 'le' ); + + let loader = new FileLoader( scope.manager ); + loader.setPath( scope.path ); + loader.setResponseType( 'arraybuffer' ); + + loader.load( url, function ( arrayBuffer ) { + onLoad( scope.parse( arrayBuffer ) ); + }, onProgress, onError ); + } + + setPath ( value ) { + this.path = value; + return this; + } + + parse ( arrayBuffer, offset ) { + let data = decodePrwm( arrayBuffer, offset ), + attributesKey = Object.keys( data.attributes ), + bufferGeometry = new BufferGeometry(), + attribute, + bufferAttribute, + i; + + for ( i = 0; i < attributesKey.length; i ++ ) { + attribute = data.attributes[ attributesKey[ i ] ]; + bufferAttribute = new BufferAttribute( attribute.values, attribute.cardinality, attribute.normalized ); + bufferAttribute.gpuType = FloatType; + bufferGeometry.setAttribute( attributesKey[ i ], bufferAttribute ); + } + + if ( data.indices !== null ) { + bufferGeometry.setIndex( new BufferAttribute( data.indices, 1 ) ); + } + + bufferGeometry.groups = data.groups; + + return bufferGeometry; + } + + isBigEndianPlatform () { + return isBigEndianPlatform(); + } + +} \ No newline at end of file diff --git a/BlueMapCore/build.gradle.kts b/BlueMapCore/build.gradle.kts index da0d6399..a8a72074 100644 --- a/BlueMapCore/build.gradle.kts +++ b/BlueMapCore/build.gradle.kts @@ -74,14 +74,14 @@ dependencies { api ("de.bluecolored.bluemap.api:BlueMapAPI") compileOnly ("org.jetbrains:annotations:23.0.0") - compileOnly ("org.projectlombok:lombok:1.18.28") + compileOnly ("org.projectlombok:lombok:1.18.30") - annotationProcessor ("org.projectlombok:lombok:1.18.28") + annotationProcessor ("org.projectlombok:lombok:1.18.30") testImplementation ("org.junit.jupiter:junit-jupiter:5.8.2") testRuntimeOnly ("org.junit.jupiter:junit-jupiter-engine:5.8.2") - testCompileOnly ("org.projectlombok:lombok:1.18.28") - testAnnotationProcessor ("org.projectlombok:lombok:1.18.28") + testCompileOnly ("org.projectlombok:lombok:1.18.30") + testAnnotationProcessor ("org.projectlombok:lombok:1.18.30") } spotless { diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/BlockModelView.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/BlockModelView.java index 4fb572b5..8fff9aaa 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/BlockModelView.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/BlockModelView.java @@ -29,14 +29,14 @@ import de.bluecolored.bluemap.core.util.math.MatrixM4f; public class BlockModelView { - private HiresTileModel hiresTile; + private TileModel hiresTile; private int start, size; - public BlockModelView(HiresTileModel hiresTile) { + public BlockModelView(TileModel hiresTile) { initialize(hiresTile); } - public BlockModelView initialize(HiresTileModel hiresTile, int start) { + public BlockModelView initialize(TileModel hiresTile, int start) { this.hiresTile = hiresTile; this.start = start; this.size = hiresTile.size() - start; @@ -44,7 +44,7 @@ public class BlockModelView { return this; } - public BlockModelView initialize(HiresTileModel hiresTile) { + public BlockModelView initialize(TileModel hiresTile) { this.hiresTile = hiresTile; this.start = hiresTile.size(); this.size = 0; @@ -138,7 +138,7 @@ public class BlockModelView { return this; } - public HiresTileModel getHiresTile() { + public TileModel getHiresTile() { return hiresTile; } diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/HiresModelManager.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/HiresModelManager.java index 822a0d4f..9dbb3dff 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/HiresModelManager.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/HiresModelManager.java @@ -33,6 +33,7 @@ import de.bluecolored.bluemap.core.resources.resourcepack.ResourcePack; import de.bluecolored.bluemap.core.storage.Storage; import de.bluecolored.bluemap.core.util.Grid; import de.bluecolored.bluemap.core.world.World; +import lombok.Getter; import java.io.IOException; import java.io.OutputStream; @@ -41,6 +42,8 @@ public class HiresModelManager { private final Storage.TileStorage storage; private final HiresModelRenderer renderer; + + @Getter private final Grid tileGrid; public HiresModelManager(Storage.TileStorage storage, ResourcePack resourcePack, TextureGallery textureGallery, RenderSettings renderSettings, Grid tileGrid) { @@ -64,7 +67,7 @@ public class HiresModelManager { 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.instancePool().claimInstance(); + TileModel model = TileModel.instancePool().claimInstance(); renderer.render(world, modelMin, modelMax, model, tileMetaConsumer); @@ -73,22 +76,18 @@ public class HiresModelManager { save(model, tile); } - HiresTileModel.instancePool().recycleInstance(model); + TileModel.instancePool().recycleInstance(model); } - private void save(final HiresTileModel model, Vector2i tile) { - try (OutputStream os = storage.write(tile)) { - model.writeBufferGeometryJson(os); + private void save(final TileModel model, Vector2i tile) { + try ( + OutputStream out = storage.write(tile); + PRBMWriter modelWriter = new PRBMWriter(out) + ) { + modelWriter.write(model); } catch (IOException e){ Logger.global.logError("Failed to save hires model: " + tile, e); } } - /** - * Returns the tile-grid - */ - public Grid getTileGrid() { - return tileGrid; - } - } diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/HiresModelRenderer.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/HiresModelRenderer.java index fbf0707b..a8106c6c 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/HiresModelRenderer.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/HiresModelRenderer.java @@ -46,11 +46,11 @@ public class HiresModelRenderer { this.renderSettings = renderSettings; } - public void render(World world, Vector3i modelMin, Vector3i modelMax, HiresTileModel model) { + public void render(World world, Vector3i modelMin, Vector3i modelMax, TileModel model) { render(world, modelMin, modelMax, model, (x, z, c, h, l) -> {}); } - public void render(World world, Vector3i modelMin, Vector3i modelMax, HiresTileModel model, TileMetaConsumer tileMetaConsumer) { + public void render(World world, Vector3i modelMin, Vector3i modelMax, TileModel model, TileMetaConsumer tileMetaConsumer) { Vector3i min = modelMin.max(renderSettings.getMinPos()); Vector3i max = modelMax.min(renderSettings.getMaxPos()); Vector3i modelAnchor = new Vector3i(modelMin.getX(), 0, modelMin.getZ()); diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/PRBMWriter.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/PRBMWriter.java new file mode 100644 index 00000000..8556cc62 --- /dev/null +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/PRBMWriter.java @@ -0,0 +1,317 @@ +package de.bluecolored.bluemap.core.map.hires; + +import de.bluecolored.bluemap.core.util.math.VectorM3f; +import de.bluecolored.bluemap.core.util.stream.CountingOutputStream; + +import java.io.Closeable; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; + +@SuppressWarnings("unused") +public class PRBMWriter implements Closeable { + + private static final int FORMAT_VERSION = 1; + private static final int HEADER_BITS = 0b0_0_0_00111; // indexed (no) _ indices-type (-) _ endianness (little) _ attribute-nr (7) + + private static final int ATTRIBUTE_TYPE_FLOAT = 0; + private static final int ATTRIBUTE_TYPE_INTEGER = 1 << 7; + + private static final int ATTRIBUTE_NOT_NORMALIZED = 0; + private static final int ATTRIBUTE_NORMALIZED = 1 << 6; + + private static final int ATTRIBUTE_CARDINALITY_SCALAR = 0; + private static final int ATTRIBUTE_CARDINALITY_2D_VEC = 1 << 4; + private static final int ATTRIBUTE_CARDINALITY_3D_VEC = 2 << 4; + private static final int ATTRIBUTE_CARDINALITY_4D_VEC = 3 << 4; + + private static final int ATTRIBUTE_ENCODING_SIGNED_32BIT_FLOAT = 1; + private static final int ATTRIBUTE_ENCODING_SIGNED_8BIT_INT = 3; + private static final int ATTRIBUTE_ENCODING_SIGNED_16BIT_INT = 4; + private static final int ATTRIBUTE_ENCODING_SIGNED_32BIT_INT = 6; + private static final int ATTRIBUTE_ENCODING_UNSIGNED_8BIT_INT = 7; + private static final int ATTRIBUTE_ENCODING_UNSIGNED_16BIT_INT = 8; + private static final int ATTRIBUTE_ENCODING_UNSIGNED_32BIT_INT = 10; + + private final CountingOutputStream out; + + public PRBMWriter(OutputStream out) { + this.out = new CountingOutputStream(out); + } + + public void write(TileModel model) throws IOException { + out.write(FORMAT_VERSION); // version - 1 byte + out.write(HEADER_BITS); // format info - 1 byte + write3byteValue(model.size * 3); // number of values - 3 bytes + write3byteValue(0); // number of indices (0 for non-indexed) - 3 bytes + + writePositionArray(model); + writeNormalArray(model); + writeColorArray(model); + writeUvArray(model); + writeAoArray(model); + writeBlocklightArray(model); + writeSunlightArray(model); + + writeMaterialGroups(model); + } + + @Override + public void close() throws IOException { + out.close(); + } + + private void writePositionArray(TileModel model) throws IOException { + float[] position = model.position; + + writeString("position"); + out.write( + ATTRIBUTE_TYPE_FLOAT | + ATTRIBUTE_NOT_NORMALIZED | + ATTRIBUTE_CARDINALITY_3D_VEC | + ATTRIBUTE_ENCODING_SIGNED_32BIT_FLOAT + ); + + writePadding(); + + int posSize = model.size * TileModel.FI_POSITION; + for (int i = 0; i < posSize; i++) { + writeFloat(position[i]); + } + } + + private void writeNormalArray(TileModel model) throws IOException { + VectorM3f normal = new VectorM3f(0, 0, 0); + float[] position = model.position; + + writeString("normal"); + out.write( + ATTRIBUTE_TYPE_FLOAT | + ATTRIBUTE_NOT_NORMALIZED | + ATTRIBUTE_CARDINALITY_3D_VEC | + ATTRIBUTE_ENCODING_SIGNED_32BIT_FLOAT + ); + + writePadding(); + + int pi, i, j; + for (i = 0; i < model.size; i++) { + pi = i * TileModel.FI_POSITION; + calculateSurfaceNormal( + position[pi], position[pi + 1], position[pi + 2], + position[pi + 3], position[pi + 4], position[pi + 5], + position[pi + 6], position[pi + 7], position[pi + 8], + normal + ); + + for (j = 0; j < 3; j++) { // all 3 points + writeFloat(normal.x); + writeFloat(normal.y); + writeFloat(normal.z); + } + } + } + + private void writeColorArray(TileModel model) throws IOException { + float[] color = model.color; + + writeString("color"); + out.write( + ATTRIBUTE_TYPE_FLOAT | + ATTRIBUTE_NORMALIZED | + ATTRIBUTE_CARDINALITY_3D_VEC | + ATTRIBUTE_ENCODING_UNSIGNED_8BIT_INT + ); + + writePadding(); + + int colorSize = model.size * TileModel.FI_COLOR, i, j; + for (i = 0; i < colorSize; i += 3) { + for (j = 0; j < 3; j++) { + writeNormalizedUnsignedByteValue(color[i]); + writeNormalizedUnsignedByteValue(color[i + 1]); + writeNormalizedUnsignedByteValue(color[i + 2]); + } + } + } + + private void writeUvArray(TileModel model) throws IOException { + float[] uv = model.uv; + + writeString("uv"); + out.write( + ATTRIBUTE_TYPE_FLOAT | + ATTRIBUTE_NOT_NORMALIZED | + ATTRIBUTE_CARDINALITY_2D_VEC | + ATTRIBUTE_ENCODING_SIGNED_32BIT_FLOAT + ); + + writePadding(); + + int uvSize = model.size * TileModel.FI_UV; + for (int i = 0; i < uvSize; i++) { + writeFloat(uv[i]); + } + } + + private void writeAoArray(TileModel model) throws IOException { + float[] ao = model.ao; + + writeString("ao"); + out.write( + ATTRIBUTE_TYPE_FLOAT | + ATTRIBUTE_NORMALIZED | + ATTRIBUTE_CARDINALITY_SCALAR | + ATTRIBUTE_ENCODING_UNSIGNED_8BIT_INT + ); + + writePadding(); + + int uvSize = model.size * TileModel.FI_AO; + for (int i = 0; i < uvSize; i++) { + writeNormalizedUnsignedByteValue(ao[i]); + } + } + + private void writeBlocklightArray(TileModel model) throws IOException { + byte[] blocklight = model.blocklight; + + writeString("blocklight"); + out.write( + ATTRIBUTE_TYPE_FLOAT | + ATTRIBUTE_NOT_NORMALIZED | + ATTRIBUTE_CARDINALITY_SCALAR | + ATTRIBUTE_ENCODING_SIGNED_8BIT_INT + ); + + writePadding(); + + int blSize = model.size * TileModel.FI_BLOCKLIGHT; + for (int i = 0; i < blSize; i++) { + out.write(blocklight[i]); + out.write(blocklight[i]); + out.write(blocklight[i]); + } + } + + private void writeSunlightArray(TileModel model) throws IOException { + byte[] sunlight = model.sunlight; + + writeString("sunlight"); + out.write( + ATTRIBUTE_TYPE_FLOAT | + ATTRIBUTE_NOT_NORMALIZED | + ATTRIBUTE_CARDINALITY_SCALAR | + ATTRIBUTE_ENCODING_SIGNED_8BIT_INT + ); + + writePadding(); + + int slSize = model.size * TileModel.FI_SUNLIGHT; + for (int i = 0; i < slSize; i++) { + out.write(sunlight[i]); + out.write(sunlight[i]); + out.write(sunlight[i]); + } + } + + private void writeMaterialGroups(TileModel model) throws IOException { + + writePadding(); + + if (model.size > 0) { + int[] materialIndex = model.materialIndex; + + int miSize = model.size * TileModel.FI_MATERIAL_INDEX, + lastMaterial = materialIndex[0], + material = lastMaterial, groupStart = 0; + + write4byteValue(material); + write4byteValue(0); + + for (int i = 1; i < miSize; i++) { + material = materialIndex[i]; + + if (material != lastMaterial) { + write4byteValue((i - groupStart) * 3); + + groupStart = i; + + write4byteValue(material); + write4byteValue(groupStart * 3); + } + + lastMaterial = material; + } + + write4byteValue((miSize - groupStart) * 3); + + } + + write4byteValue(-1); + + } + + private void writePadding() throws IOException { + int paddingBytes = (int) (-out.getCount() & 0x3); + for (int i = 0; i < paddingBytes; i++) { + out.write(0); + } + } + + private void write2byteValue(int value) throws IOException { + if (value > 0xFFFF) throw new IOException("Value too high: " + value); + out.write(value & 0xFF); + out.write((value >> 8) & 0xFF); + } + + private void write3byteValue(int value) throws IOException { + if (value > 0xFFFFFF) throw new IOException("Value too high: " + value); + out.write(value & 0xFF); + out.write((value >> 8) & 0xFF); + out.write((value >> 16) & 0xFF); + } + + private void write4byteValue(int value) throws IOException { + out.write(value & 0xFF); + out.write((value >> 8) & 0xFF); + out.write((value >> 16) & 0xFF); + out.write((value >> 24) & 0xFF); + } + + private void writeFloat(float value) throws IOException { + write4byteValue(Float.floatToIntBits(value)); + } + + private void writeNormalizedUnsignedByteValue(float value) throws IOException { + int normalized = (int) (value * 0xFF); + out.write(normalized & 0xFF); + } + + private void writeString(String value) throws IOException { + out.write(value.getBytes(StandardCharsets.US_ASCII)); + out.write(0); + } + + private void calculateSurfaceNormal( + float p1x, float p1y, float p1z, + float p2x, float p2y, float p2z, + float p3x, float p3y, float p3z, + VectorM3f target + ){ + 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; + + float length = (float) Math.sqrt(p1x * p1x + p1y * p1y + p1z * p1z); + p1x /= length; + p1y /= length; + p1z /= length; + + target.set(p1x, p1y, p1z); + } + +} diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/HiresTileModel.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/TileModel.java similarity index 59% rename from BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/HiresTileModel.java rename to BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/TileModel.java index 6b8bb14c..e51c084a 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/HiresTileModel.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/TileModel.java @@ -25,27 +25,16 @@ package de.bluecolored.bluemap.core.map.hires; 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.MergeSort; import de.bluecolored.bluemap.core.util.math.MatrixM3f; import de.bluecolored.bluemap.core.util.math.MatrixM4f; -import de.bluecolored.bluemap.core.util.math.VectorM3f; -import java.io.BufferedWriter; -import java.io.IOException; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.nio.charset.StandardCharsets; -import java.util.UUID; - -public class HiresTileModel { +public class TileModel { private static final double GROW_MULTIPLIER = 1.5; // attributes per-vertex * per-face - private static final int + static final int FI_POSITION = 3 * 3, FI_UV = 2 * 3, FI_AO = 3, @@ -54,20 +43,23 @@ public class HiresTileModel { FI_BLOCKLIGHT = 1 , FI_MATERIAL_INDEX = 1 ; - private static final InstancePool INSTANCE_POOL = new InstancePool<>( - () -> new HiresTileModel(100), - HiresTileModel::clear + private static final InstancePool INSTANCE_POOL = new InstancePool<>( + () -> new TileModel(100), + TileModel::clear ); private int capacity; - private int size; + int size; - private float[] position; - private float[] color, uv, ao; - private byte[] sunlight, blocklight; - private int[] materialIndex, materialIndexSort, materialIndexSortSupport; + float[] position; + float[] color, uv, ao; + byte[] sunlight, blocklight; + int[] materialIndex, materialIndexSort, materialIndexSortSupport; - public HiresTileModel(int initialCapacity) { + float[] indexedPosition; + int[] positionIndex; + + public TileModel(int initialCapacity) { if (initialCapacity < 0) throw new IllegalArgumentException("initialCapacity is negative"); setCapacity(initialCapacity); clear(); @@ -84,7 +76,7 @@ public class HiresTileModel { return start; } - public HiresTileModel setPositions( + public TileModel setPositions( int face, float x1, float y1, float z1, float x2, float y2, float z2, @@ -107,7 +99,7 @@ public class HiresTileModel { return this; } - public HiresTileModel setUvs( + public TileModel setUvs( int face, float u1, float v1, float u2, float v2, @@ -127,7 +119,7 @@ public class HiresTileModel { return this; } - public HiresTileModel setAOs( + public TileModel setAOs( int face, float ao1, float ao2, float ao3 ) { @@ -140,7 +132,7 @@ public class HiresTileModel { return this; } - public HiresTileModel setColor( + public TileModel setColor( int face, float r, float g, float b ){ @@ -153,22 +145,22 @@ public class HiresTileModel { return this; } - public HiresTileModel setSunlight(int face, int sl) { + public TileModel setSunlight(int face, int sl) { sunlight[face * FI_SUNLIGHT] = (byte) sl; return this; } - public HiresTileModel setBlocklight(int face, int bl) { + public TileModel setBlocklight(int face, int bl) { blocklight[face * FI_BLOCKLIGHT] = (byte) bl; return this; } - public HiresTileModel setMaterialIndex(int face, int m) { + public TileModel setMaterialIndex(int face, int m) { materialIndex[face * FI_MATERIAL_INDEX] = m; return this; } - public HiresTileModel rotate( + public TileModel rotate( int start, int count, float angle, float axisX, float axisY, float axisZ ) { @@ -193,7 +185,7 @@ public class HiresTileModel { return rotateByQuaternion(start, count, qx, qy, qz, qw); } - public HiresTileModel rotate( + public TileModel rotate( int start, int count, float pitch, float yaw, float roll ) { @@ -228,7 +220,7 @@ public class HiresTileModel { return rotateByQuaternion(start, count, qx, qy, qz, qw); } - public HiresTileModel rotateByQuaternion( + public TileModel rotateByQuaternion( int start, int count, double qx, double qy, double qz, double qw ) { @@ -256,7 +248,7 @@ public class HiresTileModel { return this; } - public HiresTileModel scale( + public TileModel scale( int start, int count, float sx, float sy, float sz ) { @@ -273,7 +265,7 @@ public class HiresTileModel { return this; } - public HiresTileModel translate( + public TileModel translate( int start, int count, float dx, float dy, float dz ) { @@ -290,7 +282,7 @@ public class HiresTileModel { return this; } - public HiresTileModel transform(int start, int count, MatrixM3f t) { + public TileModel transform(int start, int count, MatrixM3f t) { return transform(start, count, t.m00, t.m01, t.m02, t.m10, t.m11, t.m12, @@ -298,7 +290,7 @@ public class HiresTileModel { ); } - public HiresTileModel transform( + public TileModel transform( int start, int count, float m00, float m01, float m02, float m10, float m11, float m12, @@ -312,7 +304,7 @@ public class HiresTileModel { ); } - public HiresTileModel transform(int start, int count, MatrixM4f t) { + public TileModel transform(int start, int count, MatrixM4f t) { return transform(start, count, t.m00, t.m01, t.m02, t.m03, t.m10, t.m11, t.m12, t.m13, @@ -321,7 +313,7 @@ public class HiresTileModel { ); } - public HiresTileModel transform( + public TileModel transform( int start, int count, float m00, float m01, float m02, float m03, float m10, float m11, float m12, float m13, @@ -346,12 +338,12 @@ public class HiresTileModel { return this; } - public HiresTileModel reset(int size) { + public TileModel reset(int size) { this.size = size; return this; } - public HiresTileModel clear() { + public TileModel clear() { this.size = 0; return this; } @@ -394,225 +386,6 @@ public class HiresTileModel { materialIndexSortSupport = new int [materialIndex.length]; } - public void writeBufferGeometryJson(OutputStream out) throws IOException { - Gson gson = new GsonBuilder().create(); - JsonWriter json = gson.newJsonWriter(new BufferedWriter(new OutputStreamWriter(out, StandardCharsets.UTF_8), 81920)); - - json.beginObject(); // main-object - json.name("tileGeometry").beginObject(); // tile-geometry-object - - // set special values - json.name("type").value("BufferGeometry"); - json.name("uuid").value(UUID.randomUUID().toString().toUpperCase()); - - json.name("data").beginObject(); // data - json.name("attributes").beginObject(); // attributes - - writePositionArray(json); - writeNormalArray(json); - writeColorArray(json); - writeUvArray(json); - writeAoArray(json); - writeBlocklightArray(json); - writeSunlightArray(json); - - json.endObject(); // attributes - - writeMaterialGroups(json); - - json.endObject(); // data - json.endObject(); // tile-geometry-object - json.endObject(); // main-object - - // save and return - json.flush(); - } - - private void writePositionArray(JsonWriter json) throws IOException { - json.name("position"); - json.beginObject(); - - json.name("type").value("Float32Array"); - json.name("itemSize").value(3); - json.name("normalized").value(false); - - json.name("array").beginArray(); - int posSize = size * FI_POSITION; - for (int i = 0; i < posSize; i++) { - writeRounded(json, position[i]); - } - json.endArray(); - json.endObject(); - } - - private void writeNormalArray(JsonWriter json) throws IOException { - VectorM3f normal = new VectorM3f(0, 0, 0); - - json.name("normal"); - json.beginObject(); - - json.name("type").value("Float32Array"); - json.name("itemSize").value(3); - json.name("normalized").value(false); - - json.name("array").beginArray(); - - int pi, i, j; - for (i = 0; i < size; i++) { - pi = i * FI_POSITION; - calculateSurfaceNormal( - position[pi ], position[pi + 1], position[pi + 2], - position[pi + 3], position[pi + 4], position[pi + 5], - position[pi + 6], position[pi + 7], position[pi + 8], - normal - ); - - for (j = 0; j < 3; j++) { // all 3 points - writeRounded(json, normal.x); - writeRounded(json, normal.y); - writeRounded(json, normal.z); - } - } - json.endArray(); - json.endObject(); - } - - private void writeColorArray(JsonWriter json) throws IOException { - json.name("color"); - json.beginObject(); - - json.name("type").value("Float32Array"); - json.name("itemSize").value(3); - json.name("normalized").value(false); - - json.name("array").beginArray(); - int colorSize = size * FI_COLOR, i, j; - for (i = 0; i < colorSize; i += 3) { - for (j = 0; j < 3; j++) { - writeRounded(json, color[i]); - writeRounded(json, color[i + 1]); - writeRounded(json, color[i + 2]); - } - } - json.endArray(); - json.endObject(); - } - - private void writeUvArray(JsonWriter json) throws IOException { - json.name("uv"); - json.beginObject(); - - json.name("type").value("Float32Array"); - json.name("itemSize").value(2); - json.name("normalized").value(false); - - json.name("array").beginArray(); - int uvSize = size * FI_UV; - for (int i = 0; i < uvSize; i++) { - writeRounded(json, uv[i]); - } - json.endArray(); - json.endObject(); - } - - private void writeAoArray(JsonWriter json) throws IOException { - json.name("ao"); - json.beginObject(); - - json.name("type").value("Float32Array"); - json.name("itemSize").value(1); - json.name("normalized").value(false); - - json.name("array").beginArray(); - int aoSize = size * FI_AO; - for (int i = 0; i < aoSize; i++) { - writeRounded(json, ao[i]); - } - json.endArray(); - json.endObject(); - } - - private void writeBlocklightArray(JsonWriter json) throws IOException { - json.name("blocklight"); - json.beginObject(); - - json.name("type").value("Float32Array"); - json.name("itemSize").value(1); - json.name("normalized").value(false); - - json.name("array").beginArray(); - int blSize = size * FI_BLOCKLIGHT; - for (int i = 0; i < blSize; i++) { - json.value(blocklight[i]); - json.value(blocklight[i]); - json.value(blocklight[i]); - } - json.endArray(); - json.endObject(); - } - - private void writeSunlightArray(JsonWriter json) throws IOException { - json.name("sunlight"); - json.beginObject(); - - json.name("type").value("Float32Array"); - json.name("itemSize").value(1); - json.name("normalized").value(false); - - json.name("array").beginArray(); - int blSize = size * FI_SUNLIGHT; - for (int i = 0; i < blSize; i++) { - json.value(sunlight[i]); - json.value(sunlight[i]); - json.value(sunlight[i]); - } - json.endArray(); - json.endObject(); - } - - private void writeMaterialGroups(JsonWriter json) throws IOException { - json.name("groups").beginArray(); // groups - - if (size > 0) { - - int miSize = size * FI_MATERIAL_INDEX, lastMaterial = materialIndex[0], material = lastMaterial, groupStart = 0; - - json.beginObject(); - json.name("materialIndex").value(material); - json.name("start").value(0); - - for (int i = 1; i < miSize; i++) { - material = materialIndex[i]; - - if (material != lastMaterial) { - json.name("count").value((i - groupStart) * 3L); - json.endObject(); - - groupStart = i; - - json.beginObject(); - json.name("materialIndex").value(material); - json.name("start").value(groupStart * 3L); - } - - lastMaterial = material; - } - - json.name("count").value((miSize - groupStart) * 3L); - json.endObject(); - - } - - json.endArray(); // groups - } - - private void writeRounded(JsonWriter json, double value) throws IOException { - // rounding and remove ".0" to save string space - double d = Math.round(value * 10000d) / 10000d; - if (d == (long) d) json.value((long) d); - else json.value(d); - } - public void sort() { if (size <= 1) return; // nothing to sort @@ -700,28 +473,7 @@ public class HiresTileModel { materialIndex[face2] = vi; } - private static void calculateSurfaceNormal( - double p1x, double p1y, double p1z, - double p2x, double p2y, double p2z, - double p3x, double p3y, double p3z, - VectorM3f target - ){ - 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.set((float) p1x, (float) p1y, (float) p1z); - } - - public static InstancePool instancePool() { + public static InstancePool instancePool() { return INSTANCE_POOL; } diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/blockmodel/LiquidModelBuilder.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/blockmodel/LiquidModelBuilder.java index fa827088..1b915f36 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/blockmodel/LiquidModelBuilder.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/blockmodel/LiquidModelBuilder.java @@ -28,7 +28,7 @@ import com.flowpowered.math.TrigMath; import com.flowpowered.math.vector.Vector3i; import de.bluecolored.bluemap.core.map.TextureGallery; import de.bluecolored.bluemap.core.map.hires.BlockModelView; -import de.bluecolored.bluemap.core.map.hires.HiresTileModel; +import de.bluecolored.bluemap.core.map.hires.TileModel; import de.bluecolored.bluemap.core.map.hires.RenderSettings; import de.bluecolored.bluemap.core.resources.BlockColorCalculatorFactory; import de.bluecolored.bluemap.core.resources.ResourcePath; @@ -249,7 +249,7 @@ public class LiquidModelBuilder { blockModel.initialize(); blockModel.add(2); - HiresTileModel tileModel = blockModel.getHiresTile(); + TileModel tileModel = blockModel.getHiresTile(); int face1 = blockModel.getStart(); int face2 = face1 + 1; diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/blockmodel/ResourceModelBuilder.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/blockmodel/ResourceModelBuilder.java index ecfefd2b..fe57b72d 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/blockmodel/ResourceModelBuilder.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/blockmodel/ResourceModelBuilder.java @@ -30,7 +30,7 @@ import com.flowpowered.math.vector.Vector3i; import com.flowpowered.math.vector.Vector4f; import de.bluecolored.bluemap.core.map.TextureGallery; import de.bluecolored.bluemap.core.map.hires.BlockModelView; -import de.bluecolored.bluemap.core.map.hires.HiresTileModel; +import de.bluecolored.bluemap.core.map.hires.TileModel; import de.bluecolored.bluemap.core.map.hires.RenderSettings; import de.bluecolored.bluemap.core.resources.BlockColorCalculatorFactory; import de.bluecolored.bluemap.core.resources.ResourcePath; @@ -205,7 +205,7 @@ public class ResourceModelBuilder { blockModel.initialize(); blockModel.add(2); - HiresTileModel tileModel = blockModel.getHiresTile(); + TileModel tileModel = blockModel.getHiresTile(); int face1 = blockModel.getStart(); int face2 = face1 + 1; diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/CompressedInputStream.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/CompressedInputStream.java index a033ab53..b14b2745 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/CompressedInputStream.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/CompressedInputStream.java @@ -24,7 +24,7 @@ */ package de.bluecolored.bluemap.core.storage; -import de.bluecolored.bluemap.core.util.DelegateInputStream; +import de.bluecolored.bluemap.core.util.stream.DelegateInputStream; import java.io.IOException; import java.io.InputStream; diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/file/FileStorage.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/file/FileStorage.java index 926eaac6..4b5a30bc 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/file/FileStorage.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/file/FileStorage.java @@ -72,14 +72,7 @@ public class FileStorage extends Storage { Path file = getFilePath(mapId, lod, tile); OutputStream os = FileHelper.createFilepartOutputStream(file); - try { - os = new BufferedOutputStream(compression.compress(os)); - } catch (IOException ex) { - os.close(); - throw ex; - } - - return os; + return new BufferedOutputStream(compression.compress(os)); } @Override @@ -240,7 +233,7 @@ public class FileStorage extends Storage { } if (lod == 0) { - return p.resolve(fileName + ".json" + hiresCompression.getFileSuffix()); + return p.resolve(fileName + ".prbm" + hiresCompression.getFileSuffix()); } else { return p.resolve(fileName + ".png"); } diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/sql/PostgreSQLStorage.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/sql/PostgreSQLStorage.java index 1256e9f3..0dde7a7d 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/sql/PostgreSQLStorage.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/sql/PostgreSQLStorage.java @@ -29,7 +29,7 @@ import de.bluecolored.bluemap.core.storage.CompressedInputStream; import de.bluecolored.bluemap.core.storage.Compression; import de.bluecolored.bluemap.core.storage.sql.dialect.Dialect; import de.bluecolored.bluemap.core.storage.sql.dialect.PostgresDialect; -import de.bluecolored.bluemap.core.util.OnCloseOutputStream; +import de.bluecolored.bluemap.core.util.stream.OnCloseOutputStream; import java.io.*; import java.net.MalformedURLException; diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/sql/SQLStorage.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/sql/SQLStorage.java index d6aabc3c..405ff4e9 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/sql/SQLStorage.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/sql/SQLStorage.java @@ -32,7 +32,7 @@ import de.bluecolored.bluemap.core.logger.Logger; import de.bluecolored.bluemap.core.storage.*; import de.bluecolored.bluemap.core.storage.sql.dialect.DialectType; import de.bluecolored.bluemap.core.storage.sql.dialect.Dialect; -import de.bluecolored.bluemap.core.util.OnCloseOutputStream; +import de.bluecolored.bluemap.core.util.stream.OnCloseOutputStream; import org.apache.commons.dbcp2.*; import org.apache.commons.pool2.ObjectPool; import org.apache.commons.pool2.impl.GenericObjectPool; diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/FileHelper.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/FileHelper.java index 0ce497ce..08d3b467 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/FileHelper.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/FileHelper.java @@ -24,6 +24,8 @@ */ package de.bluecolored.bluemap.core.util; +import de.bluecolored.bluemap.core.util.stream.OnCloseOutputStream; + import java.io.FileNotFoundException; import java.io.IOException; import java.io.OutputStream; diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/Key.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/Key.java index a988fecf..cd8db23c 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/Key.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/Key.java @@ -112,6 +112,14 @@ public class Key implements Keyed { return new Key(namespace, value); } + public static Key minecraft(String value) { + return new Key(MINECRAFT_NAMESPACE, value); + } + + public static Key bluemap(String value) { + return new Key(BLUEMAP_NAMESPACE, value); + } + /** * Using our own function instead of {@link String#intern()} since the ConcurrentHashMap is much faster. */ diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/Registry.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/Registry.java index d1805592..6e422b3a 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/Registry.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/Registry.java @@ -16,6 +16,13 @@ public class Registry { this.entries = new ConcurrentHashMap<>(); } + @SafeVarargs + public Registry(T... defaultEntires) { + this(); + for (T entry : defaultEntires) + register(entry); + } + /** * Registers a new entry, only if there is no entry with the same key registered already. * Does nothing otherwise. diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/stream/CountingOutputStream.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/stream/CountingOutputStream.java new file mode 100644 index 00000000..3ff50b3f --- /dev/null +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/stream/CountingOutputStream.java @@ -0,0 +1,49 @@ +package de.bluecolored.bluemap.core.util.stream; + +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; +import java.io.OutputStream; + +public class CountingOutputStream extends DelegateOutputStream { + + private long count; + + public CountingOutputStream(OutputStream out) { + this(out, 0); + } + + public CountingOutputStream(OutputStream out, int initialCount) { + super(out); + this.count = initialCount; + } + + @Override + public void write(int b) throws IOException { + out.write(b); + count ++; + } + + @Override + public void write(byte @NotNull [] b) throws IOException { + out.write(b); + count += b.length; + } + + @Override + public void write(byte @NotNull [] b, int off, int len) throws IOException { + out.write(b, off, len); + count += len; + } + + public long getCount() { + return count; + } + + @Override + public void close() throws IOException { + count = 0; + super.close(); + } + +} diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/DelegateInputStream.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/stream/DelegateInputStream.java similarity index 97% rename from BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/DelegateInputStream.java rename to BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/stream/DelegateInputStream.java index 70bfb4dd..3506d08f 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/DelegateInputStream.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/stream/DelegateInputStream.java @@ -1,4 +1,4 @@ -package de.bluecolored.bluemap.core.util; +package de.bluecolored.bluemap.core.util.stream; import org.jetbrains.annotations.NotNull; diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/DelegateOutputStream.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/stream/DelegateOutputStream.java similarity index 94% rename from BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/DelegateOutputStream.java rename to BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/stream/DelegateOutputStream.java index c90a4070..3b8e9259 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/DelegateOutputStream.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/stream/DelegateOutputStream.java @@ -1,4 +1,4 @@ -package de.bluecolored.bluemap.core.util; +package de.bluecolored.bluemap.core.util.stream; import org.jetbrains.annotations.NotNull; diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/OnCloseInputStream.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/stream/OnCloseInputStream.java similarity index 97% rename from BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/OnCloseInputStream.java rename to BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/stream/OnCloseInputStream.java index d5916144..d6c11cf1 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/OnCloseInputStream.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/stream/OnCloseInputStream.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package de.bluecolored.bluemap.core.util; +package de.bluecolored.bluemap.core.util.stream; import java.io.IOException; import java.io.InputStream; diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/OnCloseOutputStream.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/stream/OnCloseOutputStream.java similarity index 97% rename from BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/OnCloseOutputStream.java rename to BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/stream/OnCloseOutputStream.java index f1851fab..916d4509 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/OnCloseOutputStream.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/stream/OnCloseOutputStream.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package de.bluecolored.bluemap.core.util; +package de.bluecolored.bluemap.core.util.stream; import java.io.IOException; import java.io.OutputStream;