mirror of
https://github.com/BlueMap-Minecraft/BlueMap.git
synced 2024-11-22 10:35:16 +01:00
[breaking] Switch hires tile format to prbm (modified prwm)
This commit is contained in:
parent
3a1e723a51
commit
6e68a8f0e0
@ -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")
|
||||
|
@ -133,18 +133,7 @@ public HttpResponse handle(HttpRequest request) {
|
||||
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) {
|
||||
|
@ -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);
|
||||
|
||||
|
307
BlueMapCommon/webapp/src/js/map/hires/PRBMLoader.js
Normal file
307
BlueMapCommon/webapp/src/js/map/hires/PRBMLoader.js
Normal 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();
|
||||
}
|
||||
|
||||
}
|
@ -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 {
|
||||
|
@ -29,14 +29,14 @@
|
||||
|
||||
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 BlockModelView initialize(HiresTileModel hiresTile, int start) {
|
||||
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 BlockModelView transform(
|
||||
return this;
|
||||
}
|
||||
|
||||
public HiresTileModel getHiresTile() {
|
||||
public TileModel getHiresTile() {
|
||||
return hiresTile;
|
||||
}
|
||||
|
||||
|
@ -33,6 +33,7 @@
|
||||
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 void render(World world, Vector2i tile, TileMetaConsumer tileMetaConsumer
|
||||
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 void render(World world, Vector2i tile, TileMetaConsumer tileMetaConsumer
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -46,11 +46,11 @@ public HiresModelRenderer(ResourcePack resourcePack, TextureGallery textureGalle
|
||||
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());
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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<HiresTileModel> INSTANCE_POOL = new InstancePool<>(
|
||||
() -> new HiresTileModel(100),
|
||||
HiresTileModel::clear
|
||||
private static final InstancePool<TileModel> 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 int add(int count) {
|
||||
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 HiresTileModel setPositions(
|
||||
return this;
|
||||
}
|
||||
|
||||
public HiresTileModel setUvs(
|
||||
public TileModel setUvs(
|
||||
int face,
|
||||
float u1, float v1,
|
||||
float u2, float v2,
|
||||
@ -127,7 +119,7 @@ public HiresTileModel setUvs(
|
||||
return this;
|
||||
}
|
||||
|
||||
public HiresTileModel setAOs(
|
||||
public TileModel setAOs(
|
||||
int face,
|
||||
float ao1, float ao2, float ao3
|
||||
) {
|
||||
@ -140,7 +132,7 @@ public HiresTileModel setAOs(
|
||||
return this;
|
||||
}
|
||||
|
||||
public HiresTileModel setColor(
|
||||
public TileModel setColor(
|
||||
int face,
|
||||
float r, float g, float b
|
||||
){
|
||||
@ -153,22 +145,22 @@ public HiresTileModel setColor(
|
||||
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 HiresTileModel rotate(
|
||||
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 HiresTileModel rotate(
|
||||
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 HiresTileModel rotateByQuaternion(
|
||||
return this;
|
||||
}
|
||||
|
||||
public HiresTileModel scale(
|
||||
public TileModel scale(
|
||||
int start, int count,
|
||||
float sx, float sy, float sz
|
||||
) {
|
||||
@ -273,7 +265,7 @@ public HiresTileModel scale(
|
||||
return this;
|
||||
}
|
||||
|
||||
public HiresTileModel translate(
|
||||
public TileModel translate(
|
||||
int start, int count,
|
||||
float dx, float dy, float dz
|
||||
) {
|
||||
@ -290,7 +282,7 @@ public HiresTileModel translate(
|
||||
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 HiresTileModel transform(int start, int count, MatrixM3f t) {
|
||||
);
|
||||
}
|
||||
|
||||
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 HiresTileModel transform(
|
||||
);
|
||||
}
|
||||
|
||||
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 HiresTileModel transform(int start, int count, MatrixM4f t) {
|
||||
);
|
||||
}
|
||||
|
||||
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 HiresTileModel transform(
|
||||
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 @@ private void setCapacity(int capacity) {
|
||||
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 @@ private void swap(int face1, int face2) {
|
||||
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<HiresTileModel> instancePool() {
|
||||
public static InstancePool<TileModel> instancePool() {
|
||||
return INSTANCE_POOL;
|
||||
}
|
||||
|
@ -28,7 +28,7 @@
|
||||
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 @@ private boolean createElementFace(Direction faceDir, VectorM3f c0, VectorM3f c1,
|
||||
blockModel.initialize();
|
||||
blockModel.add(2);
|
||||
|
||||
HiresTileModel tileModel = blockModel.getHiresTile();
|
||||
TileModel tileModel = blockModel.getHiresTile();
|
||||
int face1 = blockModel.getStart();
|
||||
int face2 = face1 + 1;
|
||||
|
||||
|
@ -30,7 +30,7 @@
|
||||
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 @@ private void createElementFace(Element element, Direction faceDir, VectorM3f c0,
|
||||
blockModel.initialize();
|
||||
blockModel.add(2);
|
||||
|
||||
HiresTileModel tileModel = blockModel.getHiresTile();
|
||||
TileModel tileModel = blockModel.getHiresTile();
|
||||
int face1 = blockModel.getStart();
|
||||
int face2 = face1 + 1;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -72,14 +72,7 @@ public OutputStream writeMapTile(String mapId, int lod, Vector2i tile) throws IO
|
||||
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 Path getFilePath(String mapId, int lod, Vector2i tile){
|
||||
}
|
||||
|
||||
if (lod == 0) {
|
||||
return p.resolve(fileName + ".json" + hiresCompression.getFileSuffix());
|
||||
return p.resolve(fileName + ".prbm" + hiresCompression.getFileSuffix());
|
||||
} else {
|
||||
return p.resolve(fileName + ".png");
|
||||
}
|
||||
|
@ -29,7 +29,7 @@
|
||||
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;
|
||||
|
@ -32,7 +32,7 @@
|
||||
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;
|
||||
|
@ -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;
|
||||
|
@ -112,6 +112,14 @@ public static Key parse(String formatted, String defaultNamespace) {
|
||||
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.
|
||||
*/
|
||||
|
@ -16,6 +16,13 @@ public 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.
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package de.bluecolored.bluemap.core.util;
|
||||
package de.bluecolored.bluemap.core.util.stream;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
@ -1,4 +1,4 @@
|
||||
package de.bluecolored.bluemap.core.util;
|
||||
package de.bluecolored.bluemap.core.util.stream;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
@ -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;
|
@ -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;
|
Loading…
Reference in New Issue
Block a user