[breaking] Switch hires tile format to prbm (modified prwm)

This commit is contained in:
Lukas Rieger (Blue) 2024-02-23 17:32:07 +01:00
parent 3a1e723a51
commit 6e68a8f0e0
No known key found for this signature in database
GPG Key ID: AA33883B1BBA03E6
24 changed files with 769 additions and 351 deletions

View File

@ -33,9 +33,9 @@ dependencies {
api ("de.bluecolored.bluemap.core:BlueMapCore") api ("de.bluecolored.bluemap.core:BlueMapCore")
compileOnly ("org.jetbrains:annotations:16.0.2") 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") testImplementation ("org.junit.jupiter:junit-jupiter:5.8.2")
testRuntimeOnly ("org.junit.jupiter:junit-jupiter-engine:5.8.2") testRuntimeOnly ("org.junit.jupiter:junit-jupiter-engine:5.8.2")

View File

@ -133,18 +133,7 @@ public class MapStorageRequestHandler implements HttpRequestHandler {
return new HttpResponse(HttpStatusCode.INTERNAL_SERVER_ERROR); return new HttpResponse(HttpStatusCode.INTERNAL_SERVER_ERROR);
} }
if (path.endsWith(".png")) { return new HttpResponse(HttpStatusCode.NO_CONTENT);
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);
} }
private String calculateETag(String path, TileInfo tileInfo) { private String calculateETag(String path, TileInfo tileInfo) {

View File

@ -23,13 +23,14 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
import {pathFromCoords} from "../util/Utils"; 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 { export class TileLoader {
/** /**
* @param tilePath {string} * @param tilePath {string}
* @param material {THREE.Material | THREE.Material[]} * @param material {Material | Material[]}
* @param tileSettings {{ * @param tileSettings {{
* tileSize: {x: number, z: number}, * tileSize: {x: number, z: number},
* scale: {x: number, z: number}, * scale: {x: number, z: number},
@ -50,23 +51,17 @@ export class TileLoader {
this.loadBlocker = loadBlocker; this.loadBlocker = loadBlocker;
this.fileLoader = new FileLoader(); 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) => { 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) => { return new Promise((resolve, reject) => {
this.fileLoader.load(tileUrl + '?' + this.tileCacheHash, this.fileLoader.load(tileUrl + '?' + this.tileCacheHash,
async json => { async data => {
let geometryJson = json.tileGeometry || {};
if (!geometryJson.type || geometryJson.type !== 'BufferGeometry'){
reject({status: "empty"});
return;
}
await this.loadBlocker(); await this.loadBlocker();
if (cancelCheck()){ if (cancelCheck()){
@ -74,7 +69,7 @@ export class TileLoader {
return; return;
} }
let geometry = this.bufferGeometryLoader.parse(geometryJson); let geometry = this.bufferGeometryLoader.parse(data);
let object = new Mesh(geometry, this.material); let object = new Mesh(geometry, this.material);

View File

@ -0,0 +1,307 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) Kevin Chapelier <https://github.com/kchapelier>
* 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();
}
}

View File

@ -74,14 +74,14 @@ dependencies {
api ("de.bluecolored.bluemap.api:BlueMapAPI") api ("de.bluecolored.bluemap.api:BlueMapAPI")
compileOnly ("org.jetbrains:annotations:23.0.0") 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") testImplementation ("org.junit.jupiter:junit-jupiter:5.8.2")
testRuntimeOnly ("org.junit.jupiter:junit-jupiter-engine:5.8.2") testRuntimeOnly ("org.junit.jupiter:junit-jupiter-engine:5.8.2")
testCompileOnly ("org.projectlombok:lombok:1.18.28") testCompileOnly ("org.projectlombok:lombok:1.18.30")
testAnnotationProcessor ("org.projectlombok:lombok:1.18.28") testAnnotationProcessor ("org.projectlombok:lombok:1.18.30")
} }
spotless { spotless {

View File

@ -29,14 +29,14 @@ import de.bluecolored.bluemap.core.util.math.MatrixM4f;
public class BlockModelView { public class BlockModelView {
private HiresTileModel hiresTile; private TileModel hiresTile;
private int start, size; private int start, size;
public BlockModelView(HiresTileModel hiresTile) { public BlockModelView(TileModel hiresTile) {
initialize(hiresTile); initialize(hiresTile);
} }
public BlockModelView initialize(HiresTileModel hiresTile, int start) { public BlockModelView initialize(TileModel hiresTile, int start) {
this.hiresTile = hiresTile; this.hiresTile = hiresTile;
this.start = start; this.start = start;
this.size = hiresTile.size() - start; this.size = hiresTile.size() - start;
@ -44,7 +44,7 @@ public class BlockModelView {
return this; return this;
} }
public BlockModelView initialize(HiresTileModel hiresTile) { public BlockModelView initialize(TileModel hiresTile) {
this.hiresTile = hiresTile; this.hiresTile = hiresTile;
this.start = hiresTile.size(); this.start = hiresTile.size();
this.size = 0; this.size = 0;
@ -138,7 +138,7 @@ public class BlockModelView {
return this; return this;
} }
public HiresTileModel getHiresTile() { public TileModel getHiresTile() {
return hiresTile; return hiresTile;
} }

View File

@ -33,6 +33,7 @@ import de.bluecolored.bluemap.core.resources.resourcepack.ResourcePack;
import de.bluecolored.bluemap.core.storage.Storage; import de.bluecolored.bluemap.core.storage.Storage;
import de.bluecolored.bluemap.core.util.Grid; import de.bluecolored.bluemap.core.util.Grid;
import de.bluecolored.bluemap.core.world.World; import de.bluecolored.bluemap.core.world.World;
import lombok.Getter;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
@ -41,6 +42,8 @@ public class HiresModelManager {
private final Storage.TileStorage storage; private final Storage.TileStorage storage;
private final HiresModelRenderer renderer; private final HiresModelRenderer renderer;
@Getter
private final Grid tileGrid; private final Grid tileGrid;
public HiresModelManager(Storage.TileStorage storage, ResourcePack resourcePack, TextureGallery textureGallery, RenderSettings renderSettings, 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 modelMin = new Vector3i(tileMin.getX(), Integer.MIN_VALUE, tileMin.getY());
Vector3i modelMax = new Vector3i(tileMax.getX(), Integer.MAX_VALUE, tileMax.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); renderer.render(world, modelMin, modelMax, model, tileMetaConsumer);
@ -73,22 +76,18 @@ public class HiresModelManager {
save(model, tile); save(model, tile);
} }
HiresTileModel.instancePool().recycleInstance(model); TileModel.instancePool().recycleInstance(model);
} }
private void save(final HiresTileModel model, Vector2i tile) { private void save(final TileModel model, Vector2i tile) {
try (OutputStream os = storage.write(tile)) { try (
model.writeBufferGeometryJson(os); OutputStream out = storage.write(tile);
PRBMWriter modelWriter = new PRBMWriter(out)
) {
modelWriter.write(model);
} catch (IOException e){ } catch (IOException e){
Logger.global.logError("Failed to save hires model: " + tile, e); Logger.global.logError("Failed to save hires model: " + tile, e);
} }
} }
/**
* Returns the tile-grid
*/
public Grid getTileGrid() {
return tileGrid;
}
} }

View File

@ -46,11 +46,11 @@ public class HiresModelRenderer {
this.renderSettings = renderSettings; 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) -> {}); 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 min = modelMin.max(renderSettings.getMinPos());
Vector3i max = modelMax.min(renderSettings.getMaxPos()); Vector3i max = modelMax.min(renderSettings.getMaxPos());
Vector3i modelAnchor = new Vector3i(modelMin.getX(), 0, modelMin.getZ()); Vector3i modelAnchor = new Vector3i(modelMin.getX(), 0, modelMin.getZ());

View File

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

View File

@ -25,27 +25,16 @@
package de.bluecolored.bluemap.core.map.hires; package de.bluecolored.bluemap.core.map.hires;
import com.flowpowered.math.TrigMath; 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.InstancePool;
import de.bluecolored.bluemap.core.util.MergeSort; import de.bluecolored.bluemap.core.util.MergeSort;
import de.bluecolored.bluemap.core.util.math.MatrixM3f; import de.bluecolored.bluemap.core.util.math.MatrixM3f;
import de.bluecolored.bluemap.core.util.math.MatrixM4f; import de.bluecolored.bluemap.core.util.math.MatrixM4f;
import de.bluecolored.bluemap.core.util.math.VectorM3f;
import java.io.BufferedWriter; public class TileModel {
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.util.UUID;
public class HiresTileModel {
private static final double GROW_MULTIPLIER = 1.5; private static final double GROW_MULTIPLIER = 1.5;
// attributes per-vertex * per-face // attributes per-vertex * per-face
private static final int static final int
FI_POSITION = 3 * 3, FI_POSITION = 3 * 3,
FI_UV = 2 * 3, FI_UV = 2 * 3,
FI_AO = 3, FI_AO = 3,
@ -54,20 +43,23 @@ public class HiresTileModel {
FI_BLOCKLIGHT = 1 , FI_BLOCKLIGHT = 1 ,
FI_MATERIAL_INDEX = 1 ; FI_MATERIAL_INDEX = 1 ;
private static final InstancePool<HiresTileModel> INSTANCE_POOL = new InstancePool<>( private static final InstancePool<TileModel> INSTANCE_POOL = new InstancePool<>(
() -> new HiresTileModel(100), () -> new TileModel(100),
HiresTileModel::clear TileModel::clear
); );
private int capacity; private int capacity;
private int size; int size;
private float[] position; float[] position;
private float[] color, uv, ao; float[] color, uv, ao;
private byte[] sunlight, blocklight; byte[] sunlight, blocklight;
private int[] materialIndex, materialIndexSort, materialIndexSortSupport; 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"); if (initialCapacity < 0) throw new IllegalArgumentException("initialCapacity is negative");
setCapacity(initialCapacity); setCapacity(initialCapacity);
clear(); clear();
@ -84,7 +76,7 @@ public class HiresTileModel {
return start; return start;
} }
public HiresTileModel setPositions( public TileModel setPositions(
int face, int face,
float x1, float y1, float z1, float x1, float y1, float z1,
float x2, float y2, float z2, float x2, float y2, float z2,
@ -107,7 +99,7 @@ public class HiresTileModel {
return this; return this;
} }
public HiresTileModel setUvs( public TileModel setUvs(
int face, int face,
float u1, float v1, float u1, float v1,
float u2, float v2, float u2, float v2,
@ -127,7 +119,7 @@ public class HiresTileModel {
return this; return this;
} }
public HiresTileModel setAOs( public TileModel setAOs(
int face, int face,
float ao1, float ao2, float ao3 float ao1, float ao2, float ao3
) { ) {
@ -140,7 +132,7 @@ public class HiresTileModel {
return this; return this;
} }
public HiresTileModel setColor( public TileModel setColor(
int face, int face,
float r, float g, float b float r, float g, float b
){ ){
@ -153,22 +145,22 @@ public class HiresTileModel {
return this; return this;
} }
public HiresTileModel setSunlight(int face, int sl) { public TileModel setSunlight(int face, int sl) {
sunlight[face * FI_SUNLIGHT] = (byte) sl; sunlight[face * FI_SUNLIGHT] = (byte) sl;
return this; return this;
} }
public HiresTileModel setBlocklight(int face, int bl) { public TileModel setBlocklight(int face, int bl) {
blocklight[face * FI_BLOCKLIGHT] = (byte) bl; blocklight[face * FI_BLOCKLIGHT] = (byte) bl;
return this; return this;
} }
public HiresTileModel setMaterialIndex(int face, int m) { public TileModel setMaterialIndex(int face, int m) {
materialIndex[face * FI_MATERIAL_INDEX] = m; materialIndex[face * FI_MATERIAL_INDEX] = m;
return this; return this;
} }
public HiresTileModel rotate( public TileModel rotate(
int start, int count, int start, int count,
float angle, float axisX, float axisY, float axisZ float angle, float axisX, float axisY, float axisZ
) { ) {
@ -193,7 +185,7 @@ public class HiresTileModel {
return rotateByQuaternion(start, count, qx, qy, qz, qw); return rotateByQuaternion(start, count, qx, qy, qz, qw);
} }
public HiresTileModel rotate( public TileModel rotate(
int start, int count, int start, int count,
float pitch, float yaw, float roll float pitch, float yaw, float roll
) { ) {
@ -228,7 +220,7 @@ public class HiresTileModel {
return rotateByQuaternion(start, count, qx, qy, qz, qw); return rotateByQuaternion(start, count, qx, qy, qz, qw);
} }
public HiresTileModel rotateByQuaternion( public TileModel rotateByQuaternion(
int start, int count, int start, int count,
double qx, double qy, double qz, double qw double qx, double qy, double qz, double qw
) { ) {
@ -256,7 +248,7 @@ public class HiresTileModel {
return this; return this;
} }
public HiresTileModel scale( public TileModel scale(
int start, int count, int start, int count,
float sx, float sy, float sz float sx, float sy, float sz
) { ) {
@ -273,7 +265,7 @@ public class HiresTileModel {
return this; return this;
} }
public HiresTileModel translate( public TileModel translate(
int start, int count, int start, int count,
float dx, float dy, float dz float dx, float dy, float dz
) { ) {
@ -290,7 +282,7 @@ public class HiresTileModel {
return this; return this;
} }
public HiresTileModel transform(int start, int count, MatrixM3f t) { public TileModel transform(int start, int count, MatrixM3f t) {
return transform(start, count, return transform(start, count,
t.m00, t.m01, t.m02, t.m00, t.m01, t.m02,
t.m10, t.m11, t.m12, t.m10, t.m11, t.m12,
@ -298,7 +290,7 @@ public class HiresTileModel {
); );
} }
public HiresTileModel transform( public TileModel transform(
int start, int count, int start, int count,
float m00, float m01, float m02, float m00, float m01, float m02,
float m10, float m11, float m12, 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, return transform(start, count,
t.m00, t.m01, t.m02, t.m03, t.m00, t.m01, t.m02, t.m03,
t.m10, t.m11, t.m12, t.m13, 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, int start, int count,
float m00, float m01, float m02, float m03, float m00, float m01, float m02, float m03,
float m10, float m11, float m12, float m13, float m10, float m11, float m12, float m13,
@ -346,12 +338,12 @@ public class HiresTileModel {
return this; return this;
} }
public HiresTileModel reset(int size) { public TileModel reset(int size) {
this.size = size; this.size = size;
return this; return this;
} }
public HiresTileModel clear() { public TileModel clear() {
this.size = 0; this.size = 0;
return this; return this;
} }
@ -394,225 +386,6 @@ public class HiresTileModel {
materialIndexSortSupport = new int [materialIndex.length]; 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() { public void sort() {
if (size <= 1) return; // nothing to sort if (size <= 1) return; // nothing to sort
@ -700,28 +473,7 @@ public class HiresTileModel {
materialIndex[face2] = vi; materialIndex[face2] = vi;
} }
private static void calculateSurfaceNormal( public static InstancePool<TileModel> instancePool() {
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<HiresTileModel> instancePool() {
return INSTANCE_POOL; return INSTANCE_POOL;
} }

View File

@ -28,7 +28,7 @@ import com.flowpowered.math.TrigMath;
import com.flowpowered.math.vector.Vector3i; import com.flowpowered.math.vector.Vector3i;
import de.bluecolored.bluemap.core.map.TextureGallery; import de.bluecolored.bluemap.core.map.TextureGallery;
import de.bluecolored.bluemap.core.map.hires.BlockModelView; 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.map.hires.RenderSettings;
import de.bluecolored.bluemap.core.resources.BlockColorCalculatorFactory; import de.bluecolored.bluemap.core.resources.BlockColorCalculatorFactory;
import de.bluecolored.bluemap.core.resources.ResourcePath; import de.bluecolored.bluemap.core.resources.ResourcePath;
@ -249,7 +249,7 @@ public class LiquidModelBuilder {
blockModel.initialize(); blockModel.initialize();
blockModel.add(2); blockModel.add(2);
HiresTileModel tileModel = blockModel.getHiresTile(); TileModel tileModel = blockModel.getHiresTile();
int face1 = blockModel.getStart(); int face1 = blockModel.getStart();
int face2 = face1 + 1; int face2 = face1 + 1;

View File

@ -30,7 +30,7 @@ import com.flowpowered.math.vector.Vector3i;
import com.flowpowered.math.vector.Vector4f; import com.flowpowered.math.vector.Vector4f;
import de.bluecolored.bluemap.core.map.TextureGallery; import de.bluecolored.bluemap.core.map.TextureGallery;
import de.bluecolored.bluemap.core.map.hires.BlockModelView; 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.map.hires.RenderSettings;
import de.bluecolored.bluemap.core.resources.BlockColorCalculatorFactory; import de.bluecolored.bluemap.core.resources.BlockColorCalculatorFactory;
import de.bluecolored.bluemap.core.resources.ResourcePath; import de.bluecolored.bluemap.core.resources.ResourcePath;
@ -205,7 +205,7 @@ public class ResourceModelBuilder {
blockModel.initialize(); blockModel.initialize();
blockModel.add(2); blockModel.add(2);
HiresTileModel tileModel = blockModel.getHiresTile(); TileModel tileModel = blockModel.getHiresTile();
int face1 = blockModel.getStart(); int face1 = blockModel.getStart();
int face2 = face1 + 1; int face2 = face1 + 1;

View File

@ -24,7 +24,7 @@
*/ */
package de.bluecolored.bluemap.core.storage; 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.IOException;
import java.io.InputStream; import java.io.InputStream;

View File

@ -72,14 +72,7 @@ public class FileStorage extends Storage {
Path file = getFilePath(mapId, lod, tile); Path file = getFilePath(mapId, lod, tile);
OutputStream os = FileHelper.createFilepartOutputStream(file); OutputStream os = FileHelper.createFilepartOutputStream(file);
try { return new BufferedOutputStream(compression.compress(os));
os = new BufferedOutputStream(compression.compress(os));
} catch (IOException ex) {
os.close();
throw ex;
}
return os;
} }
@Override @Override
@ -240,7 +233,7 @@ public class FileStorage extends Storage {
} }
if (lod == 0) { if (lod == 0) {
return p.resolve(fileName + ".json" + hiresCompression.getFileSuffix()); return p.resolve(fileName + ".prbm" + hiresCompression.getFileSuffix());
} else { } else {
return p.resolve(fileName + ".png"); return p.resolve(fileName + ".png");
} }

View File

@ -29,7 +29,7 @@ import de.bluecolored.bluemap.core.storage.CompressedInputStream;
import de.bluecolored.bluemap.core.storage.Compression; import de.bluecolored.bluemap.core.storage.Compression;
import de.bluecolored.bluemap.core.storage.sql.dialect.Dialect; import de.bluecolored.bluemap.core.storage.sql.dialect.Dialect;
import de.bluecolored.bluemap.core.storage.sql.dialect.PostgresDialect; 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.io.*;
import java.net.MalformedURLException; import java.net.MalformedURLException;

View File

@ -32,7 +32,7 @@ import de.bluecolored.bluemap.core.logger.Logger;
import de.bluecolored.bluemap.core.storage.*; import de.bluecolored.bluemap.core.storage.*;
import de.bluecolored.bluemap.core.storage.sql.dialect.DialectType; import de.bluecolored.bluemap.core.storage.sql.dialect.DialectType;
import de.bluecolored.bluemap.core.storage.sql.dialect.Dialect; 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.dbcp2.*;
import org.apache.commons.pool2.ObjectPool; import org.apache.commons.pool2.ObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPool; import org.apache.commons.pool2.impl.GenericObjectPool;

View File

@ -24,6 +24,8 @@
*/ */
package de.bluecolored.bluemap.core.util; package de.bluecolored.bluemap.core.util;
import de.bluecolored.bluemap.core.util.stream.OnCloseOutputStream;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;

View File

@ -112,6 +112,14 @@ public class Key implements Keyed {
return new Key(namespace, value); 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. * Using our own function instead of {@link String#intern()} since the ConcurrentHashMap is much faster.
*/ */

View File

@ -16,6 +16,13 @@ public class Registry<T extends Keyed> {
this.entries = new ConcurrentHashMap<>(); 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. * Registers a new entry, only if there is no entry with the same key registered already.
* Does nothing otherwise. * Does nothing otherwise.

View File

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

View File

@ -1,4 +1,4 @@
package de.bluecolored.bluemap.core.util; package de.bluecolored.bluemap.core.util.stream;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;

View File

@ -1,4 +1,4 @@
package de.bluecolored.bluemap.core.util; package de.bluecolored.bluemap.core.util.stream;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;

View File

@ -22,7 +22,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
package de.bluecolored.bluemap.core.util; package de.bluecolored.bluemap.core.util.stream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;

View File

@ -22,7 +22,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
package de.bluecolored.bluemap.core.util; package de.bluecolored.bluemap.core.util.stream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;