Add per-map view-settings and optimized flat-view only maps

This commit is contained in:
Lukas Rieger (Blue) 2024-11-17 15:09:26 +01:00
parent f08c7946a0
commit 4d58cc25e3
No known key found for this signature in database
GPG Key ID: AA33883B1BBA03E6
21 changed files with 812 additions and 480 deletions

View File

@ -123,7 +123,6 @@ private static class Settings {
private boolean useCookies = true; private boolean useCookies = true;
private boolean enableFreeFlight = true;
private boolean defaultToFlatView = false; private boolean defaultToFlatView = false;
private String startLocation = null; private String startLocation = null;
@ -150,7 +149,6 @@ private static class Settings {
public void setFrom(WebappConfig config) { public void setFrom(WebappConfig config) {
this.useCookies = config.isUseCookies(); this.useCookies = config.isUseCookies();
this.enableFreeFlight = config.isEnableFreeFlight();
this.defaultToFlatView = config.isDefaultToFlatView(); this.defaultToFlatView = config.isDefaultToFlatView();
this.startLocation = config.getStartLocation().orElse(null); this.startLocation = config.getStartLocation().orElse(null);
this.resolutionDefault = config.getResolutionDefault(); this.resolutionDefault = config.getResolutionDefault();

View File

@ -75,7 +75,10 @@ public class MapConfig implements MapSettings {
private boolean renderEdges = true; private boolean renderEdges = true;
private boolean saveHiresLayer = true; private boolean enablePerspectiveView = true;
private boolean enableFlatView = true;
private boolean enableFreeFlightView = true;
private boolean enableHires = true;
private String storage = "file"; private String storage = "file";

View File

@ -42,7 +42,6 @@ public class WebappConfig {
private boolean useCookies = true; private boolean useCookies = true;
private boolean enableFreeFlight = true;
private boolean defaultToFlatView = false; private boolean defaultToFlatView = false;
private String startLocation = null; private String startLocation = null;
@ -82,10 +81,6 @@ public boolean isUseCookies() {
return useCookies; return useCookies;
} }
public boolean isEnableFreeFlight() {
return enableFreeFlight;
}
public boolean isDefaultToFlatView() { public boolean isDefaultToFlatView() {
return defaultToFlatView; return defaultToFlatView;
} }

View File

@ -96,13 +96,28 @@ min-inhabited-time: 0
# Default is true # Default is true
render-edges: true render-edges: true
# Whether the hires-layer will be saved to the storage. # Whether the perspective view will be enabled for this map.
# Changing this to true requires a re-render of the map, only if the hires-layer is enabled and free-flight view is disabled.
# Default is true
enable-perspective-view: true
# Whether the flat (isometric, top-down) view will be enabled for this map.
# Having only flat-view enabled while disabling free-flight and perspective will speed up the render and reduce the maps storage-size.
# Default is true
enable-flat-view: true
# Whether the free-flight view will be enabled for this map.
# Changing this to true requires a re-render of the map, only if the hires-layer is enabled and perspective view is disabled.
# Default is true
enable-free-flight-view: true
# Whether the hires-layer will be enabled.
# Disabling this will speed up rendering and reduce the size of the map-files a lot. # Disabling this will speed up rendering and reduce the size of the map-files a lot.
# But you will not be able to see the full 3d-models if you zoom in on the map. # But you will not be able to see the full 3d-models if you zoom in on the map.
# Changing this to false will not remove any existing tiles, existing tiles just won't get updated anymore. # Changing this to false will not remove any existing tiles, existing tiles just won't get updated anymore.
# Changing this to true will require a re-render of the map. # Changing this to true will require a re-render of the map.
# Default is true # Default is true
save-hires-layer: true enable-hires: true
# This defines the storage-config that will be used to save this map. # This defines the storage-config that will be used to save this map.
# You can find your storage configs next to this config file in the 'storages'-folder. # You can find your storage configs next to this config file in the 'storages'-folder.

View File

@ -22,10 +22,6 @@ update-settings-file: true
# Default is true # Default is true
use-cookies: true use-cookies: true
# If the free-flight-mode in the web-application is enabled or not.
# Default is true
enable-free-flight: true
# If the webapp will default to flat-view instead of perspective-view. # If the webapp will default to flat-view instead of perspective-view.
# Default is false # Default is false
default-to-flat-view: false default-to-flat-view: false

View File

@ -1,6 +1,6 @@
<template> <template>
<div class="controls-switch"> <div class="controls-switch" v-if="showViewControls">
<SvgButton :active="isPerspectiveView" @action="setPerspectiveView" :title="$t('controls.perspective.tooltip')"> <SvgButton v-if="mapViewer.map.perspectiveView" :active="isPerspectiveView" @action="setPerspectiveView" :title="$t('controls.perspective.tooltip')">
<svg viewBox="0 0 30 30"> <svg viewBox="0 0 30 30">
<path d="M19.475,10.574c-0.166-0.021-0.337-0.036-0.51-0.045c-0.174-0.009-0.35-0.013-0.525-0.011 <path d="M19.475,10.574c-0.166-0.021-0.337-0.036-0.51-0.045c-0.174-0.009-0.35-0.013-0.525-0.011
c-0.176,0.002-0.353,0.01-0.526,0.024c-0.175,0.015-0.347,0.036-0.515,0.063l-13.39,2.189 c-0.176,0.002-0.353,0.01-0.526,0.024c-0.175,0.015-0.347,0.036-0.515,0.063l-13.39,2.189
@ -13,12 +13,12 @@
c-0.116-0.051-0.243-0.097-0.381-0.138c-0.137-0.041-0.283-0.078-0.438-0.108C19.803,10.621,19.641,10.595,19.475,10.574"/> c-0.116-0.051-0.243-0.097-0.381-0.138c-0.137-0.041-0.283-0.078-0.438-0.108C19.803,10.621,19.641,10.595,19.475,10.574"/>
</svg> </svg>
</SvgButton> </SvgButton>
<SvgButton :active="isFlatView" @action="setFlatView" :title="$t('controls.flatView.tooltip')"> <SvgButton v-if="mapViewer.map.flatView" :active="isFlatView" @action="setFlatView" :title="$t('controls.flatView.tooltip')">
<svg viewBox="0 0 30 30"> <svg viewBox="0 0 30 30">
<path d="M22.371,4.158c1.65,0,3,1.35,3,3v15.684c0,1.65-1.35,3-3,3H7.629c-1.65,0-3-1.35-3-3V7.158c0-1.65,1.35-3,3-3H22.371z"/> <path d="M22.371,4.158c1.65,0,3,1.35,3,3v15.684c0,1.65-1.35,3-3,3H7.629c-1.65,0-3-1.35-3-3V7.158c0-1.65,1.35-3,3-3H22.371z"/>
</svg> </svg>
</SvgButton> </SvgButton>
<SvgButton v-if="controls.enableFreeFlight" :active="isFreeFlight" @action="setFreeFlight" :title="$t('controls.freeFlight.tooltip')"> <SvgButton v-if="mapViewer.map.freeFlightView" :active="isFreeFlight" @action="setFreeFlight" :title="$t('controls.freeFlight.tooltip')">
<svg viewBox="0 0 30 30"> <svg viewBox="0 0 30 30">
<path d="M21.927,11.253c-0.256-0.487-0.915-0.885-1.465-0.885h-2.004c-0.55,0-0.726-0.356-0.39-0.792c0,0,0.698-0.905,0.698-2.041 <path d="M21.927,11.253c-0.256-0.487-0.915-0.885-1.465-0.885h-2.004c-0.55,0-0.726-0.356-0.39-0.792c0,0,0.698-0.905,0.698-2.041
c0-2.08-1.687-3.767-3.767-3.767s-3.767,1.687-3.767,3.767c0,1.136,0.698,2.041,0.698,2.041c0.336,0.436,0.161,0.794-0.389,0.797 c0-2.08-1.687-3.767-3.767-3.767s-3.767,1.687-3.767,3.767c0,1.136,0.698,2.041,0.698,2.041c0.336,0.436,0.161,0.794-0.389,0.797
@ -40,6 +40,7 @@
data() { data() {
return { return {
controls: this.$bluemap.appState.controls, controls: this.$bluemap.appState.controls,
mapViewer: this.$bluemap.mapViewer.data
} }
}, },
computed: { computed: {
@ -51,6 +52,10 @@
}, },
isFreeFlight() { isFreeFlight() {
return this.controls.state === "free"; return this.controls.state === "free";
},
showViewControls() {
if (!this.mapViewer.map) return 0;
return this.mapViewer.map.views.length > 1;
} }
}, },
methods: { methods: {

View File

@ -1,9 +1,9 @@
<template> <template>
<div> <div>
<Group :title="$t('controls.title')"> <Group :title="$t('controls.title')">
<SimpleButton :active="appState.controls.state === 'perspective'" @action="$bluemap.setPerspectiveView(500, appState.controls.state === 'free' ? 100 : 0)">{{$t('controls.perspective.button')}}</SimpleButton> <SimpleButton v-if="mapViewer.map.perspectiveView" :active="appState.controls.state === 'perspective'" @action="$bluemap.setPerspectiveView(500, appState.controls.state === 'free' ? 100 : 0)">{{$t('controls.perspective.button')}}</SimpleButton>
<SimpleButton :active="appState.controls.state === 'flat'" @action="$bluemap.setFlatView(500, appState.controls.state === 'free' ? 100 : 0)">{{$t('controls.flatView.button')}}</SimpleButton> <SimpleButton v-if="mapViewer.map.flatView" :active="appState.controls.state === 'flat'" @action="$bluemap.setFlatView(500, appState.controls.state === 'free' ? 100 : 0)">{{$t('controls.flatView.button')}}</SimpleButton>
<SimpleButton v-if="appState.controls.enableFreeFlight" :active="appState.controls.state === 'free'" @action="$bluemap.setFreeFlight(500)">{{$t('controls.freeFlight.button')}}</SimpleButton> <SimpleButton v-if="mapViewer.map.freeFlightView" :active="appState.controls.state === 'free'" @action="$bluemap.setFreeFlight(500)">{{$t('controls.freeFlight.button')}}</SimpleButton>
</Group> </Group>
<Group :title="$t('lighting.title')"> <Group :title="$t('lighting.title')">

View File

@ -59,7 +59,6 @@ export class BlueMapApp {
/** @type {{ /** @type {{
* version: string, * version: string,
* useCookies: boolean, * useCookies: boolean,
* enableFreeFlight: boolean,
* defaultToFlatView: boolean, * defaultToFlatView: boolean,
* resolutionDefault: number, * resolutionDefault: number,
* minZoomDistance: number, * minZoomDistance: number,
@ -96,7 +95,6 @@ export class BlueMapApp {
mouseSensitivity: 1, mouseSensitivity: 1,
showZoomButtons: true, showZoomButtons: true,
invertMouse: false, invertMouse: false,
enableFreeFlight: false,
pauseTileLoading: false pauseTileLoading: false
}, },
menu: this.mainMenu, menu: this.mainMenu,
@ -139,7 +137,6 @@ export class BlueMapApp {
await this.getSettings(); await this.getSettings();
this.mapControls.minDistance = this.settings.minZoomDistance; this.mapControls.minDistance = this.settings.minZoomDistance;
this.mapControls.maxDistance = this.settings.maxZoomDistance; this.mapControls.maxDistance = this.settings.maxZoomDistance;
this.appState.controls.enableFreeFlight = this.settings.enableFreeFlight;
// load settings-styles // load settings-styles
if (this.settings.styles) for (let styleUrl of this.settings.styles) { if (this.settings.styles) for (let styleUrl of this.settings.styles) {
@ -259,7 +256,9 @@ export class BlueMapApp {
await this.mapViewer.switchMap(map) await this.mapViewer.switchMap(map)
if (resetCamera) this.resetCamera(); if (resetCamera || !this.mapViewer.map.hasView(this.appState.controls.state))
this.resetCamera();
this.updatePageAddress(); this.updatePageAddress();
await Promise.all([ await Promise.all([
@ -283,10 +282,18 @@ export class BlueMapApp {
controls.controls = this.mapControls; controls.controls = this.mapControls;
this.appState.controls.state = "perspective"; this.appState.controls.state = "perspective";
if (this.settings.defaultToFlatView) {
if (this.settings.defaultToFlatView && map.hasView("flat")) {
this.setFlatView(); this.setFlatView();
} }
else if (!map.hasView("perspective")) {
if (map.hasView("flat"))
this.setFlatView();
else
this.setFreeFlight();
}
this.updatePageAddress(); this.updatePageAddress();
} }
@ -328,7 +335,6 @@ export class BlueMapApp {
this.settings = { this.settings = {
version: "?", version: "?",
useCookies: false, useCookies: false,
enableFreeFlight: true,
defaultToFlatView: false, defaultToFlatView: false,
resolutionDefault: 1.0, resolutionDefault: 1.0,
minZoomDistance: 5, minZoomDistance: 5,
@ -451,6 +457,7 @@ export class BlueMapApp {
setPerspectiveView(transition = 0, minDistance = 5) { setPerspectiveView(transition = 0, minDistance = 5) {
if (!this.mapViewer.map) return; if (!this.mapViewer.map) return;
if (!this.mapViewer.map.data.perspectiveView) return;
if (this.viewAnimation) this.viewAnimation.cancel(); if (this.viewAnimation) this.viewAnimation.cancel();
let cm = this.mapViewer.controlsManager; let cm = this.mapViewer.controlsManager;
@ -488,6 +495,7 @@ export class BlueMapApp {
setFlatView(transition = 0, minDistance = 5) { setFlatView(transition = 0, minDistance = 5) {
if (!this.mapViewer.map) return; if (!this.mapViewer.map) return;
if (!this.mapViewer.map.data.flatView) return;
if (this.viewAnimation) this.viewAnimation.cancel(); if (this.viewAnimation) this.viewAnimation.cancel();
let cm = this.mapViewer.controlsManager; let cm = this.mapViewer.controlsManager;
@ -521,7 +529,7 @@ export class BlueMapApp {
setFreeFlight(transition = 0, targetY = undefined) { setFreeFlight(transition = 0, targetY = undefined) {
if (!this.mapViewer.map) return; if (!this.mapViewer.map) return;
if (!this.settings.enableFreeFlight) return this.setPerspectiveView(transition); if (!this.mapViewer.map.data.freeFlightView) return;
if (this.viewAnimation) this.viewAnimation.cancel(); if (this.viewAnimation) this.viewAnimation.cancel();
let cm = this.mapViewer.controlsManager; let cm = this.mapViewer.controlsManager;

View File

@ -78,7 +78,11 @@ export class Map {
tileSize: {x: 32, z: 32}, tileSize: {x: 32, z: 32},
lodFactor: 5, lodFactor: 5,
lodCount: 3 lodCount: 3
} },
perspectiveView: false,
flatView: false,
freeFlightView: false,
views: ["perspective", "flat", "free"]
}); });
this.raycaster = new Raycaster(); this.raycaster = new Raycaster();
@ -198,6 +202,15 @@ export class Map {
lodCount: worldSettings.lowres.lodCount !== undefined ? worldSettings.lowres.lodCount : this.data.lowres.lodCount lodCount: worldSettings.lowres.lodCount !== undefined ? worldSettings.lowres.lodCount : this.data.lowres.lodCount
}; };
this.data.perspectiveView = worldSettings.perspectiveView !== undefined ? worldSettings.perspectiveView : this.data.perspectiveView;
this.data.flatView = worldSettings.flatView !== undefined ? worldSettings.flatView : this.data.flatView;
this.data.freeFlightView = worldSettings.freeFlightView !== undefined ? worldSettings.freeFlightView : this.data.freeFlightView;
this.data.views = [];
if (this.data.perspectiveView) this.data.views.push("perspective");
if (this.data.flatView) this.data.views.push("flat");
if (this.data.freeFlightView) this.data.views.push("free");
alert(this.events, `Settings for map '${this.data.id}' loaded.`, "fine"); alert(this.events, `Settings for map '${this.data.id}' loaded.`, "fine");
}); });
} }
@ -472,6 +485,10 @@ export class Map {
return false; return false;
} }
hasView(view) {
return this.data.views.some(v => v === view)
}
dispose() { dispose() {
this.unload(); this.unload();
} }

View File

@ -52,4 +52,22 @@ public interface MapSettings extends RenderSettings {
float getSkyLight(); float getSkyLight();
boolean isEnablePerspectiveView();
boolean isEnableFlatView();
boolean isEnableFreeFlightView();
boolean isEnableHires();
@Override
default boolean isSaveHiresLayer() {
return isEnableHires();
}
@Override
default boolean isRenderTopOnly() {
return !isEnableHires() || (!isEnablePerspectiveView() && !isEnableFreeFlightView());
}
} }

View File

@ -83,6 +83,11 @@ public JsonElement serialize(BmMap map, Type typeOfSrc, JsonSerializationContext
root.addProperty("ambientLight", map.getMapSettings().getAmbientLight()); root.addProperty("ambientLight", map.getMapSettings().getAmbientLight());
root.addProperty("skyLight", map.getMapSettings().getSkyLight()); root.addProperty("skyLight", map.getMapSettings().getSkyLight());
// view settings
root.addProperty("perspectiveView", map.getMapSettings().isEnablePerspectiveView());
root.addProperty("flatView", map.getMapSettings().isEnableFlatView());
root.addProperty("freeFlightView", map.getMapSettings().isEnableFreeFlightView());
return root; return root;
} }

View File

@ -0,0 +1,498 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.core.map.hires;
import com.flowpowered.math.TrigMath;
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;
public class ArrayTileModel implements TileModel {
private static final double GROW_MULTIPLIER = 1.5;
private static final InstancePool<ArrayTileModel> INSTANCE_POOL = new InstancePool<>(
() -> new ArrayTileModel(100),
ArrayTileModel::clear
);
// attributes per-vertex * per-face
static final int
FI_POSITION = 3 * 3,
FI_UV = 2 * 3,
FI_AO = 3,
FI_COLOR = 3 ,
FI_SUNLIGHT = 1 ,
FI_BLOCKLIGHT = 1 ,
FI_MATERIAL_INDEX = 1 ;
private int capacity;
int size;
float[] position;
float[] color, uv, ao;
byte[] sunlight, blocklight;
int[] materialIndex, materialIndexSort, materialIndexSortSupport;
public ArrayTileModel(int initialCapacity) {
if (initialCapacity < 0) throw new IllegalArgumentException("initialCapacity is negative");
setCapacity(initialCapacity);
clear();
}
@Override
public int size() {
return size;
}
@Override
public int add(int count) {
ensureCapacity(count);
int start = this.size;
this.size += count;
return start;
}
@Override
public ArrayTileModel setPositions(
int face,
float x1, float y1, float z1,
float x2, float y2, float z2,
float x3, float y3, float z3
){
int index = face * FI_POSITION;
position[index ] = x1;
position[index + 1] = y1;
position[index + 2] = z1;
position[index + 3 ] = x2;
position[index + 3 + 1] = y2;
position[index + 3 + 2] = z2;
position[index + 6 ] = x3;
position[index + 6 + 1] = y3;
position[index + 6 + 2] = z3;
return this;
}
@Override
public ArrayTileModel setUvs(
int face,
float u1, float v1,
float u2, float v2,
float u3, float v3
){
int index = face * FI_UV;
uv[index ] = u1;
uv[index + 1] = v1;
uv[index + 2 ] = u2;
uv[index + 2 + 1] = v2;
uv[index + 4 ] = u3;
uv[index + 4 + 1] = v3;
return this;
}
@Override
public ArrayTileModel setAOs(
int face,
float ao1, float ao2, float ao3
) {
int index = face * FI_AO;
ao[index ] = ao1;
ao[index + 1] = ao2;
ao[index + 2] = ao3;
return this;
}
@Override
public ArrayTileModel setColor(
int face,
float r, float g, float b
){
int index = face * FI_COLOR;
color[index ] = r;
color[index + 1] = g;
color[index + 2] = b;
return this;
}
@Override
public ArrayTileModel setSunlight(int face, int sl) {
sunlight[face * FI_SUNLIGHT] = (byte) sl;
return this;
}
@Override
public ArrayTileModel setBlocklight(int face, int bl) {
blocklight[face * FI_BLOCKLIGHT] = (byte) bl;
return this;
}
@Override
public ArrayTileModel setMaterialIndex(int face, int m) {
materialIndex[face * FI_MATERIAL_INDEX] = m;
return this;
}
@Override
public ArrayTileModel rotate(
int start, int count,
float angle, float axisX, float axisY, float axisZ
) {
// create quaternion
double halfAngle = Math.toRadians(angle) * 0.5;
double q = TrigMath.sin(halfAngle) / Math.sqrt(axisX * axisX + axisY * axisY + axisZ * axisZ);
double //quaternion
qx = axisX * q,
qy = axisY * q,
qz = axisZ * q,
qw = TrigMath.cos(halfAngle),
qLength = Math.sqrt(qx * qx + qy * qy + qz * qz + qw * qw);
// normalize quaternion
qx /= qLength;
qy /= qLength;
qz /= qLength;
qw /= qLength;
return rotateByQuaternion(start, count, qx, qy, qz, qw);
}
@Override
public ArrayTileModel rotate(
int start, int count,
float pitch, float yaw, float roll
) {
double
halfYaw = Math.toRadians(yaw) * 0.5,
qy1 = TrigMath.sin(halfYaw),
qw1 = TrigMath.cos(halfYaw),
halfPitch = Math.toRadians(pitch) * 0.5,
qx2 = TrigMath.sin(halfPitch),
qw2 = TrigMath.cos(halfPitch),
halfRoll = Math.toRadians(roll) * 0.5,
qz3 = TrigMath.sin(halfRoll),
qw3 = TrigMath.cos(halfRoll);
// multiply 1 with 2
double
qxA = qw1 * qx2,
qyA = qy1 * qw2,
qzA = - qy1 * qx2,
qwA = qw1 * qw2;
// multiply with 3
double
qx = qxA * qw3 + qyA * qz3,
qy = qyA * qw3 - qxA * qz3,
qz = qwA * qz3 + qzA * qw3,
qw = qwA * qw3 - qzA * qz3;
return rotateByQuaternion(start, count, qx, qy, qz, qw);
}
@Override
public ArrayTileModel rotateByQuaternion(
int start, int count,
double qx, double qy, double qz, double qw
) {
double x, y, z, px, py, pz, pw;
int end = start + count, index;
for (int face = start; face < end; face++) {
for (int i = 0; i < 3; i++) {
index = face * FI_POSITION + i * 3;
x = position[index];
y = position[index + 1];
z = position[index + 2];
px = qw * x + qy * z - qz * y;
py = qw * y + qz * x - qx * z;
pz = qw * z + qx * y - qy * x;
pw = -qx * x - qy * y - qz * z;
position[index] = (float) (pw * -qx + px * qw - py * qz + pz * qy);
position[index + 1] = (float) (pw * -qy + py * qw - pz * qx + px * qz);
position[index + 2] = (float) (pw * -qz + pz * qw - px * qy + py * qx);
}
}
return this;
}
@Override
public ArrayTileModel scale(
int start, int count,
float sx, float sy, float sz
) {
int end = start + count, index;
for (int face = start; face < end; face++) {
for (int i = 0; i < 3; i++) {
index = face * FI_POSITION + i * 3;
position[index ] *= sx;
position[index + 1] *= sy;
position[index + 2] *= sz;
}
}
return this;
}
@Override
public ArrayTileModel translate(
int start, int count,
float dx, float dy, float dz
) {
int end = start + count, index;
for (int face = start; face < end; face++) {
for (int i = 0; i < 3; i++) {
index = face * FI_POSITION + i * 3;
position[index ] += dx;
position[index + 1] += dy;
position[index + 2] += dz;
}
}
return this;
}
@Override
public ArrayTileModel transform(int start, int count, MatrixM3f t) {
return transform(start, count,
t.m00, t.m01, t.m02,
t.m10, t.m11, t.m12,
t.m20, t.m21, t.m22
);
}
@Override
public ArrayTileModel transform(
int start, int count,
float m00, float m01, float m02,
float m10, float m11, float m12,
float m20, float m21, float m22
) {
return transform(start, count,
m00, m01, m02, 0,
m10, m11, m12, 0,
m20, m21, m22, 0,
0, 0, 0, 1
);
}
@Override
public ArrayTileModel 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,
t.m20, t.m21, t.m22, t.m23,
t.m30, t.m31, t.m32, t.m33
);
}
@Override
public ArrayTileModel transform(
int start, int count,
float m00, float m01, float m02, float m03,
float m10, float m11, float m12, float m13,
float m20, float m21, float m22, float m23,
float m30, float m31, float m32, float m33
) {
int end = start + count, index;
float x, y, z;
for (int face = start; face < end; face++) {
for (int i = 0; i < 3; i++) {
index = face * FI_POSITION + i * 3;
x = position[index ];
y = position[index + 1];
z = position[index + 2];
position[index ] = m00 * x + m01 * y + m02 * z + m03;
position[index + 1] = m10 * x + m11 * y + m12 * z + m13;
position[index + 2] = m20 * x + m21 * y + m22 * z + m23;
}
}
return this;
}
@Override
public ArrayTileModel reset(int size) {
this.size = size;
return this;
}
@Override
public ArrayTileModel clear() {
this.size = 0;
return this;
}
private void ensureCapacity(int count) {
if (size + count > capacity){
float[] _position = position;
float[] _color = color, _uv = uv, _ao = ao;
byte[] _sunlight = sunlight, _blocklight = blocklight;
int[] _materialIndex = materialIndex;
int newCapacity = (int) (capacity * GROW_MULTIPLIER) + count;
setCapacity(newCapacity);
System.arraycopy(_position, 0, position, 0, size * FI_POSITION);
System.arraycopy(_uv, 0, uv, 0, size * FI_UV);
System.arraycopy(_ao, 0, ao, 0, size * FI_AO);
System.arraycopy(_color, 0, color, 0, size * FI_COLOR);
System.arraycopy(_sunlight, 0, sunlight, 0, size * FI_SUNLIGHT);
System.arraycopy(_blocklight, 0, blocklight, 0, size * FI_BLOCKLIGHT);
System.arraycopy(_materialIndex, 0, materialIndex, 0, size * FI_MATERIAL_INDEX);
}
}
private void setCapacity(int capacity) {
this.capacity = capacity;
// attributes capacity * per-vertex * per-face
position = new float [capacity * FI_POSITION];
uv = new float [capacity * FI_UV];
ao = new float [capacity * FI_AO];
color = new float [capacity * FI_COLOR];
sunlight = new byte [capacity * FI_SUNLIGHT];
blocklight = new byte [capacity * FI_BLOCKLIGHT];
materialIndex = new int [capacity * FI_MATERIAL_INDEX];
materialIndexSort = new int[materialIndex.length];
materialIndexSortSupport = new int [materialIndex.length];
}
@Override
public void sort() {
if (size <= 1) return; // nothing to sort
// initialize material-index-sort
for (int i = 0; i < size; i++) {
materialIndexSort[i] = i;
materialIndexSortSupport[i] = i;
}
// sort
MergeSort.mergeSortInt(materialIndexSort, 0, size, this::compareMaterialIndex, materialIndexSortSupport);
// move
int s, c;
for (int i = 0; i < size; i++) {
s = materialIndexSort[i]; c = 0;
while (s < i) {
s = materialIndexSort[s];
// should never happen, just making absolutely sure this can't get stuck in an endless loop
if (c++ > size) throw new IllegalStateException();
}
swap(i, s);
}
}
private int compareMaterialIndex(int i1, int i2) {
return Integer.compare(materialIndex[i1], materialIndex[i2]);
}
private void swap(int face1, int face2) {
int i, if1, if2, vi;
float vf;
byte vb;
//swap positions
if1 = face1 * FI_POSITION;
if2 = face2 * FI_POSITION;
for (i = 0; i < FI_POSITION; i++){
vf = position[if1 + i];
position[if1 + i] = position[if2 + i];
position[if2 + i] = vf;
}
//swap uv
if1 = face1 * FI_UV;
if2 = face2 * FI_UV;
for (i = 0; i < FI_UV; i++){
vf = uv[if1 + i];
uv[if1 + i] = uv[if2 + i];
uv[if2 + i] = vf;
}
//swap ao
if1 = face1 * FI_AO;
if2 = face2 * FI_AO;
for (i = 0; i < FI_AO; i++){
vf = ao[if1 + i];
ao[if1 + i] = ao[if2 + i];
ao[if2 + i] = vf;
}
//swap color
if1 = face1 * FI_COLOR;
if2 = face2 * FI_COLOR;
for (i = 0; i < FI_COLOR; i++){
vf = color[if1 + i];
color[if1 + i] = color[if2 + i];
color[if2 + i] = vf;
}
//swap sunlight (assuming FI_SUNLIGHT = 1)
vb = sunlight[face1];
sunlight[face1] = sunlight[face2];
sunlight[face2] = vb;
//swap blocklight (assuming FI_BLOCKLIGHT = 1)
vb = blocklight[face1];
blocklight[face1] = blocklight[face2];
blocklight[face2] = vb;
//swap material-index (assuming FI_MATERIAL_INDEX = 1)
vi = materialIndex[face1];
materialIndex[face1] = materialIndex[face2];
materialIndex[face2] = vi;
}
static InstancePool<ArrayTileModel> instancePool() {
return INSTANCE_POOL;
}
}

View File

@ -68,16 +68,19 @@ public void render(World world, Vector2i tile, TileMetaConsumer tileMetaConsumer
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());
TileModel model = TileModel.instancePool().claimInstance(); if (save) {
ArrayTileModel model = ArrayTileModel.instancePool().claimInstance();
renderer.render(world, modelMin, modelMax, model, tileMetaConsumer); renderer.render(world, modelMin, modelMax, model, tileMetaConsumer);
if (save){
model.sort(); model.sort();
save(model, tile); save(model, tile);
ArrayTileModel.instancePool().recycleInstance(model);
} else {
renderer.render(world, modelMin, modelMax, VoidTileModel.INSTANCE, tileMetaConsumer);
} }
TileModel.instancePool().recycleInstance(model);
} }
/** /**
@ -97,7 +100,7 @@ public void unrender(Vector2i tile, TileMetaConsumer tileMetaConsumer) {
); );
} }
private void save(final TileModel model, Vector2i tile) { private void save(final ArrayTileModel model, Vector2i tile) {
try ( try (
OutputStream out = storage.write(tile.getX(), tile.getY()); OutputStream out = storage.write(tile.getX(), tile.getY());
PRBMWriter modelWriter = new PRBMWriter(out) PRBMWriter modelWriter = new PRBMWriter(out)

View File

@ -90,9 +90,6 @@ public void render(World world, Vector3i modelMin, Vector3i modelMax, TileModel
//update topBlockLight //update topBlockLight
topBlockLight = Math.max(topBlockLight, block.getBlockLightLevel() * (1 - columnColor.a)); topBlockLight = Math.max(topBlockLight, block.getBlockLightLevel() * (1 - columnColor.a));
// skip empty blocks
if (blockModel.getSize() <= 0) continue;
// move block-model to correct position // move block-model to correct position
blockModel.translate(x - modelAnchor.getX(), y - modelAnchor.getY(), z - modelAnchor.getZ()); blockModel.translate(x - modelAnchor.getX(), y - modelAnchor.getY(), z - modelAnchor.getZ());
@ -102,7 +99,8 @@ public void render(World world, Vector3i modelMin, Vector3i modelMax, TileModel
columnColor.underlay(blockColor.premultiplied()); columnColor.underlay(blockColor.premultiplied());
} }
//if (blockColor.a > 0.999 && block.getProperties().isCulling()) break; if (renderSettings.isRenderTopOnly() && blockColor.a > 0.999 && block.getProperties().isCulling())
break;
} }
} }

View File

@ -63,7 +63,7 @@ public PRBMWriter(OutputStream out) {
this.out = new CountingOutputStream(out); this.out = new CountingOutputStream(out);
} }
public void write(TileModel model) throws IOException { public void write(ArrayTileModel model) throws IOException {
out.write(FORMAT_VERSION); // version - 1 byte out.write(FORMAT_VERSION); // version - 1 byte
out.write(HEADER_BITS); // format info - 1 byte out.write(HEADER_BITS); // format info - 1 byte
write3byteValue(model.size * 3); // number of values - 3 bytes write3byteValue(model.size * 3); // number of values - 3 bytes
@ -85,7 +85,7 @@ public void close() throws IOException {
out.close(); out.close();
} }
private void writePositionArray(TileModel model) throws IOException { private void writePositionArray(ArrayTileModel model) throws IOException {
float[] position = model.position; float[] position = model.position;
writeString("position"); writeString("position");
@ -98,13 +98,13 @@ private void writePositionArray(TileModel model) throws IOException {
writePadding(); writePadding();
int posSize = model.size * TileModel.FI_POSITION; int posSize = model.size * ArrayTileModel.FI_POSITION;
for (int i = 0; i < posSize; i++) { for (int i = 0; i < posSize; i++) {
writeFloat(position[i]); writeFloat(position[i]);
} }
} }
private void writeNormalArray(TileModel model) throws IOException { private void writeNormalArray(ArrayTileModel model) throws IOException {
VectorM3f normal = new VectorM3f(0, 0, 0); VectorM3f normal = new VectorM3f(0, 0, 0);
float[] position = model.position; float[] position = model.position;
@ -120,7 +120,7 @@ private void writeNormalArray(TileModel model) throws IOException {
int pi, i, j; int pi, i, j;
for (i = 0; i < model.size; i++) { for (i = 0; i < model.size; i++) {
pi = i * TileModel.FI_POSITION; pi = i * ArrayTileModel.FI_POSITION;
calculateSurfaceNormal( calculateSurfaceNormal(
position[pi], position[pi + 1], position[pi + 2], position[pi], position[pi + 1], position[pi + 2],
position[pi + 3], position[pi + 4], position[pi + 5], position[pi + 3], position[pi + 4], position[pi + 5],
@ -136,7 +136,7 @@ private void writeNormalArray(TileModel model) throws IOException {
} }
} }
private void writeColorArray(TileModel model) throws IOException { private void writeColorArray(ArrayTileModel model) throws IOException {
float[] color = model.color; float[] color = model.color;
writeString("color"); writeString("color");
@ -149,7 +149,7 @@ private void writeColorArray(TileModel model) throws IOException {
writePadding(); writePadding();
int colorSize = model.size * TileModel.FI_COLOR, i, j; int colorSize = model.size * ArrayTileModel.FI_COLOR, i, j;
for (i = 0; i < colorSize; i += 3) { for (i = 0; i < colorSize; i += 3) {
for (j = 0; j < 3; j++) { for (j = 0; j < 3; j++) {
writeNormalizedUnsignedByteValue(color[i]); writeNormalizedUnsignedByteValue(color[i]);
@ -159,7 +159,7 @@ private void writeColorArray(TileModel model) throws IOException {
} }
} }
private void writeUvArray(TileModel model) throws IOException { private void writeUvArray(ArrayTileModel model) throws IOException {
float[] uv = model.uv; float[] uv = model.uv;
writeString("uv"); writeString("uv");
@ -172,13 +172,13 @@ private void writeUvArray(TileModel model) throws IOException {
writePadding(); writePadding();
int uvSize = model.size * TileModel.FI_UV; int uvSize = model.size * ArrayTileModel.FI_UV;
for (int i = 0; i < uvSize; i++) { for (int i = 0; i < uvSize; i++) {
writeFloat(uv[i]); writeFloat(uv[i]);
} }
} }
private void writeAoArray(TileModel model) throws IOException { private void writeAoArray(ArrayTileModel model) throws IOException {
float[] ao = model.ao; float[] ao = model.ao;
writeString("ao"); writeString("ao");
@ -191,13 +191,13 @@ private void writeAoArray(TileModel model) throws IOException {
writePadding(); writePadding();
int uvSize = model.size * TileModel.FI_AO; int uvSize = model.size * ArrayTileModel.FI_AO;
for (int i = 0; i < uvSize; i++) { for (int i = 0; i < uvSize; i++) {
writeNormalizedUnsignedByteValue(ao[i]); writeNormalizedUnsignedByteValue(ao[i]);
} }
} }
private void writeBlocklightArray(TileModel model) throws IOException { private void writeBlocklightArray(ArrayTileModel model) throws IOException {
byte[] blocklight = model.blocklight; byte[] blocklight = model.blocklight;
writeString("blocklight"); writeString("blocklight");
@ -210,7 +210,7 @@ private void writeBlocklightArray(TileModel model) throws IOException {
writePadding(); writePadding();
int blSize = model.size * TileModel.FI_BLOCKLIGHT; int blSize = model.size * ArrayTileModel.FI_BLOCKLIGHT;
for (int i = 0; i < blSize; i++) { for (int i = 0; i < blSize; i++) {
out.write(blocklight[i]); out.write(blocklight[i]);
out.write(blocklight[i]); out.write(blocklight[i]);
@ -218,7 +218,7 @@ private void writeBlocklightArray(TileModel model) throws IOException {
} }
} }
private void writeSunlightArray(TileModel model) throws IOException { private void writeSunlightArray(ArrayTileModel model) throws IOException {
byte[] sunlight = model.sunlight; byte[] sunlight = model.sunlight;
writeString("sunlight"); writeString("sunlight");
@ -231,7 +231,7 @@ private void writeSunlightArray(TileModel model) throws IOException {
writePadding(); writePadding();
int slSize = model.size * TileModel.FI_SUNLIGHT; int slSize = model.size * ArrayTileModel.FI_SUNLIGHT;
for (int i = 0; i < slSize; i++) { for (int i = 0; i < slSize; i++) {
out.write(sunlight[i]); out.write(sunlight[i]);
out.write(sunlight[i]); out.write(sunlight[i]);
@ -239,14 +239,14 @@ private void writeSunlightArray(TileModel model) throws IOException {
} }
} }
private void writeMaterialGroups(TileModel model) throws IOException { private void writeMaterialGroups(ArrayTileModel model) throws IOException {
writePadding(); writePadding();
if (model.size > 0) { if (model.size > 0) {
int[] materialIndex = model.materialIndex; int[] materialIndex = model.materialIndex;
int miSize = model.size * TileModel.FI_MATERIAL_INDEX, int miSize = model.size * ArrayTileModel.FI_MATERIAL_INDEX,
lastMaterial = materialIndex[0], lastMaterial = materialIndex[0],
material = lastMaterial, groupStart = 0; material = lastMaterial, groupStart = 0;

View File

@ -125,4 +125,6 @@ default Predicate<Vector2i> getCellRenderBoundariesFilter(Grid grid, boolean all
boolean isSaveHiresLayer(); boolean isSaveHiresLayer();
boolean isRenderTopOnly();
} }

View File

@ -1,480 +1,92 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.core.map.hires; package de.bluecolored.bluemap.core.map.hires;
import com.flowpowered.math.TrigMath;
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.MatrixM3f;
import de.bluecolored.bluemap.core.util.math.MatrixM4f; import de.bluecolored.bluemap.core.util.math.MatrixM4f;
public class TileModel { public interface TileModel {
private static final double GROW_MULTIPLIER = 1.5;
// attributes per-vertex * per-face int size();
static final int
FI_POSITION = 3 * 3,
FI_UV = 2 * 3,
FI_AO = 3,
FI_COLOR = 3 ,
FI_SUNLIGHT = 1 ,
FI_BLOCKLIGHT = 1 ,
FI_MATERIAL_INDEX = 1 ;
private static final InstancePool<TileModel> INSTANCE_POOL = new InstancePool<>( int add(int count);
() -> new TileModel(100),
TileModel::clear
);
private int capacity; TileModel setPositions(
int size;
float[] position;
float[] color, uv, ao;
byte[] sunlight, blocklight;
int[] materialIndex, materialIndexSort, materialIndexSortSupport;
float[] indexedPosition;
int[] positionIndex;
public TileModel(int initialCapacity) {
if (initialCapacity < 0) throw new IllegalArgumentException("initialCapacity is negative");
setCapacity(initialCapacity);
clear();
}
public int size() {
return size;
}
public int add(int count) {
ensureCapacity(count);
int start = this.size;
this.size += count;
return start;
}
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,
float x3, float y3, float z3 float x3, float y3, float z3
){ );
int index = face * FI_POSITION;
position[index ] = x1; TileModel setUvs(
position[index + 1] = y1;
position[index + 2] = z1;
position[index + 3 ] = x2;
position[index + 3 + 1] = y2;
position[index + 3 + 2] = z2;
position[index + 6 ] = x3;
position[index + 6 + 1] = y3;
position[index + 6 + 2] = z3;
return this;
}
public TileModel setUvs(
int face, int face,
float u1, float v1, float u1, float v1,
float u2, float v2, float u2, float v2,
float u3, float v3 float u3, float v3
){ );
int index = face * FI_UV;
uv[index ] = u1; TileModel setAOs(
uv[index + 1] = v1;
uv[index + 2 ] = u2;
uv[index + 2 + 1] = v2;
uv[index + 4 ] = u3;
uv[index + 4 + 1] = v3;
return this;
}
public TileModel setAOs(
int face, int face,
float ao1, float ao2, float ao3 float ao1, float ao2, float ao3
) { );
int index = face * FI_AO;
ao[index ] = ao1; TileModel setColor(
ao[index + 1] = ao2;
ao[index + 2] = ao3;
return this;
}
public TileModel setColor(
int face, int face,
float r, float g, float b float r, float g, float b
){ );
int index = face * FI_COLOR;
color[index ] = r; TileModel setSunlight(int face, int sl);
color[index + 1] = g;
color[index + 2] = b;
return this; TileModel setBlocklight(int face, int bl);
}
public TileModel setSunlight(int face, int sl) { TileModel setMaterialIndex(int face, int m);
sunlight[face * FI_SUNLIGHT] = (byte) sl;
return this;
}
public TileModel setBlocklight(int face, int bl) { TileModel rotate(
blocklight[face * FI_BLOCKLIGHT] = (byte) bl;
return this;
}
public TileModel setMaterialIndex(int face, int m) {
materialIndex[face * FI_MATERIAL_INDEX] = m;
return this;
}
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
) { );
// create quaternion TileModel rotate(
double halfAngle = Math.toRadians(angle) * 0.5;
double q = TrigMath.sin(halfAngle) / Math.sqrt(axisX * axisX + axisY * axisY + axisZ * axisZ);
double //quaternion
qx = axisX * q,
qy = axisY * q,
qz = axisZ * q,
qw = TrigMath.cos(halfAngle),
qLength = Math.sqrt(qx * qx + qy * qy + qz * qz + qw * qw);
// normalize quaternion
qx /= qLength;
qy /= qLength;
qz /= qLength;
qw /= qLength;
return rotateByQuaternion(start, count, qx, qy, qz, qw);
}
public TileModel rotate(
int start, int count, int start, int count,
float pitch, float yaw, float roll float pitch, float yaw, float roll
) { );
double TileModel rotateByQuaternion(
halfYaw = Math.toRadians(yaw) * 0.5,
qy1 = TrigMath.sin(halfYaw),
qw1 = TrigMath.cos(halfYaw),
halfPitch = Math.toRadians(pitch) * 0.5,
qx2 = TrigMath.sin(halfPitch),
qw2 = TrigMath.cos(halfPitch),
halfRoll = Math.toRadians(roll) * 0.5,
qz3 = TrigMath.sin(halfRoll),
qw3 = TrigMath.cos(halfRoll);
// multiply 1 with 2
double
qxA = qw1 * qx2,
qyA = qy1 * qw2,
qzA = - qy1 * qx2,
qwA = qw1 * qw2;
// multiply with 3
double
qx = qxA * qw3 + qyA * qz3,
qy = qyA * qw3 - qxA * qz3,
qz = qwA * qz3 + qzA * qw3,
qw = qwA * qw3 - qzA * qz3;
return rotateByQuaternion(start, count, qx, qy, qz, qw);
}
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
) { );
double x, y, z, px, py, pz, pw;
int end = start + count, index;
for (int face = start; face < end; face++) {
for (int i = 0; i < 3; i++) {
index = face * FI_POSITION + i * 3;
x = position[index]; TileModel scale(
y = position[index + 1];
z = position[index + 2];
px = qw * x + qy * z - qz * y;
py = qw * y + qz * x - qx * z;
pz = qw * z + qx * y - qy * x;
pw = -qx * x - qy * y - qz * z;
position[index] = (float) (pw * -qx + px * qw - py * qz + pz * qy);
position[index + 1] = (float) (pw * -qy + py * qw - pz * qx + px * qz);
position[index + 2] = (float) (pw * -qz + pz * qw - px * qy + py * qx);
}
}
return this;
}
public TileModel scale(
int start, int count, int start, int count,
float sx, float sy, float sz float sx, float sy, float sz
) { );
int end = start + count, index;
for (int face = start; face < end; face++) {
for (int i = 0; i < 3; i++) {
index = face * FI_POSITION + i * 3;
position[index ] *= sx;
position[index + 1] *= sy;
position[index + 2] *= sz;
}
}
return this; TileModel translate(
}
public TileModel translate(
int start, int count, int start, int count,
float dx, float dy, float dz float dx, float dy, float dz
) {
int end = start + count, index;
for (int face = start; face < end; face++) {
for (int i = 0; i < 3; i++) {
index = face * FI_POSITION + i * 3;
position[index ] += dx;
position[index + 1] += dy;
position[index + 2] += dz;
}
}
return this;
}
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,
t.m20, t.m21, t.m22
); );
}
public TileModel transform( TileModel transform(int start, int count, MatrixM3f t);
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,
float m20, float m21, float m22 float m20, float m21, float m22
) {
return transform(start, count,
m00, m01, m02, 0,
m10, m11, m12, 0,
m20, m21, m22, 0,
0, 0, 0, 1
); );
}
public TileModel transform(int start, int count, MatrixM4f t) { 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,
t.m20, t.m21, t.m22, t.m23,
t.m30, t.m31, t.m32, t.m33
);
}
public TileModel transform( 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,
float m20, float m21, float m22, float m23, float m20, float m21, float m22, float m23,
float m30, float m31, float m32, float m33 float m30, float m31, float m32, float m33
) { );
int end = start + count, index;
float x, y, z;
for (int face = start; face < end; face++) {
for (int i = 0; i < 3; i++) {
index = face * FI_POSITION + i * 3;
x = position[index ];
y = position[index + 1];
z = position[index + 2];
position[index ] = m00 * x + m01 * y + m02 * z + m03; TileModel reset(int size);
position[index + 1] = m10 * x + m11 * y + m12 * z + m13;
position[index + 2] = m20 * x + m21 * y + m22 * z + m23;
}
}
return this; TileModel clear();
}
public TileModel reset(int size) { void sort();
this.size = size;
return this;
}
public TileModel clear() {
this.size = 0;
return this;
}
private void ensureCapacity(int count) {
if (size + count > capacity){
float[] _position = position;
float[] _color = color, _uv = uv, _ao = ao;
byte[] _sunlight = sunlight, _blocklight = blocklight;
int[] _materialIndex = materialIndex;
int newCapacity = (int) (capacity * GROW_MULTIPLIER) + count;
setCapacity(newCapacity);
System.arraycopy(_position, 0, position, 0, size * FI_POSITION);
System.arraycopy(_uv, 0, uv, 0, size * FI_UV);
System.arraycopy(_ao, 0, ao, 0, size * FI_AO);
System.arraycopy(_color, 0, color, 0, size * FI_COLOR);
System.arraycopy(_sunlight, 0, sunlight, 0, size * FI_SUNLIGHT);
System.arraycopy(_blocklight, 0, blocklight, 0, size * FI_BLOCKLIGHT);
System.arraycopy(_materialIndex, 0, materialIndex, 0, size * FI_MATERIAL_INDEX);
}
}
private void setCapacity(int capacity) {
this.capacity = capacity;
// attributes capacity * per-vertex * per-face
position = new float [capacity * FI_POSITION];
uv = new float [capacity * FI_UV];
ao = new float [capacity * FI_AO];
color = new float [capacity * FI_COLOR];
sunlight = new byte [capacity * FI_SUNLIGHT];
blocklight = new byte [capacity * FI_BLOCKLIGHT];
materialIndex = new int [capacity * FI_MATERIAL_INDEX];
materialIndexSort = new int[materialIndex.length];
materialIndexSortSupport = new int [materialIndex.length];
}
public void sort() {
if (size <= 1) return; // nothing to sort
// initialize material-index-sort
for (int i = 0; i < size; i++) {
materialIndexSort[i] = i;
materialIndexSortSupport[i] = i;
}
// sort
MergeSort.mergeSortInt(materialIndexSort, 0, size, this::compareMaterialIndex, materialIndexSortSupport);
// move
int s, c;
for (int i = 0; i < size; i++) {
s = materialIndexSort[i]; c = 0;
while (s < i) {
s = materialIndexSort[s];
// should never happen, just making absolutely sure this can't get stuck in an endless loop
if (c++ > size) throw new IllegalStateException();
}
swap(i, s);
}
}
private int compareMaterialIndex(int i1, int i2) {
return Integer.compare(materialIndex[i1], materialIndex[i2]);
}
private void swap(int face1, int face2) {
int i, if1, if2, vi;
float vf;
byte vb;
//swap positions
if1 = face1 * FI_POSITION;
if2 = face2 * FI_POSITION;
for (i = 0; i < FI_POSITION; i++){
vf = position[if1 + i];
position[if1 + i] = position[if2 + i];
position[if2 + i] = vf;
}
//swap uv
if1 = face1 * FI_UV;
if2 = face2 * FI_UV;
for (i = 0; i < FI_UV; i++){
vf = uv[if1 + i];
uv[if1 + i] = uv[if2 + i];
uv[if2 + i] = vf;
}
//swap ao
if1 = face1 * FI_AO;
if2 = face2 * FI_AO;
for (i = 0; i < FI_AO; i++){
vf = ao[if1 + i];
ao[if1 + i] = ao[if2 + i];
ao[if2 + i] = vf;
}
//swap color
if1 = face1 * FI_COLOR;
if2 = face2 * FI_COLOR;
for (i = 0; i < FI_COLOR; i++){
vf = color[if1 + i];
color[if1 + i] = color[if2 + i];
color[if2 + i] = vf;
}
//swap sunlight (assuming FI_SUNLIGHT = 1)
vb = sunlight[face1];
sunlight[face1] = sunlight[face2];
sunlight[face2] = vb;
//swap blocklight (assuming FI_BLOCKLIGHT = 1)
vb = blocklight[face1];
blocklight[face1] = blocklight[face2];
blocklight[face2] = vb;
//swap material-index (assuming FI_MATERIAL_INDEX = 1)
vi = materialIndex[face1];
materialIndex[face1] = materialIndex[face2];
materialIndex[face2] = vi;
}
public static InstancePool<TileModel> instancePool() {
return INSTANCE_POOL;
}
} }

View File

@ -0,0 +1,159 @@
package de.bluecolored.bluemap.core.map.hires;
import de.bluecolored.bluemap.core.util.math.MatrixM3f;
import de.bluecolored.bluemap.core.util.math.MatrixM4f;
/**
* An empty tile-model discarding any actions
*/
public class VoidTileModel implements TileModel {
public static final TileModel INSTANCE = new VoidTileModel();
@Override
public int size() {
return 0;
}
@Override
public int add(int count) {
return 0;
}
@Override
public TileModel setPositions(
int face,
float x1, float y1, float z1,
float x2, float y2, float z2,
float x3, float y3, float z3
) {
return this;
}
@Override
public TileModel setUvs(
int face,
float u1, float v1,
float u2, float v2,
float u3, float v3
) {
return this;
}
@Override
public TileModel setAOs(
int face,
float ao1, float ao2, float ao3
) {
return this;
}
@Override
public TileModel setColor(
int face,
float r, float g, float b
) {
return this;
}
@Override
public TileModel setSunlight(int face, int sl) {
return this;
}
@Override
public TileModel setBlocklight(int face, int bl) {
return this;
}
@Override
public TileModel setMaterialIndex(int face, int m) {
return this;
}
@Override
public TileModel rotate(
int start, int count,
float angle,
float axisX, float axisY, float axisZ
) {
return this;
}
@Override
public TileModel rotate(
int start, int count,
float pitch, float yaw, float roll
) {
return this;
}
@Override
public TileModel rotateByQuaternion(
int start, int count,
double qx, double qy, double qz, double qw
) {
return this;
}
@Override
public TileModel scale(
int start, int count,
float sx, float sy, float sz
) {
return this;
}
@Override
public TileModel translate(
int start, int count,
float dx, float dy, float dz
) {
return this;
}
@Override
public TileModel transform(int start, int count, MatrixM3f t) {
return this;
}
@Override
public TileModel transform(
int start, int count,
float m00, float m01, float m02,
float m10, float m11, float m12,
float m20, float m21, float m22
) {
return this;
}
@Override
public TileModel transform(int start, int count, MatrixM4f t) {
return this;
}
@Override
public TileModel transform(
int start, int count,
float m00, float m01, float m02, float m03,
float m10, float m11, float m12, float m13,
float m20, float m21, float m22, float m23,
float m30, float m31, float m32, float m33
) {
return this;
}
@Override
public TileModel reset(int size) {
return this;
}
@Override
public TileModel clear() {
return this;
}
@Override
public void sort() { }
}

View File

@ -99,7 +99,7 @@ private void renderModel(BlockNeighborhood<?> block, BlockState blockState, Tile
variantColor.set(0f, 0f, 0f, 0f, true); variantColor.set(0f, 0f, 0f, 0f, true);
blockRenderers.get(modelResource.getRenderer()) blockRenderers.get(modelResource.getRenderer())
.render(block, variant, blockModel.initialize(), blockColor); .render(block, variant, blockModel.initialize(), variantColor);
if (variantColor.a > blockColorOpacity) if (variantColor.a > blockColorOpacity)
blockColorOpacity = variantColor.a; blockColorOpacity = variantColor.a;

View File

@ -203,7 +203,7 @@ private void createElementFace(Element element, Direction faceDir, VectorM3f c0,
makeRotationRelative(faceRotationVector); makeRotationRelative(faceRotationVector);
// face culling // face culling
//if (faceRotationVector.y < 0.01) return; if (renderSettings.isRenderTopOnly() && faceRotationVector.y < 0.01) return;
if (face.getCullface() != null) { if (face.getCullface() != null) {
ExtendedBlock<?> b = getRotationRelativeBlock(face.getCullface()); ExtendedBlock<?> b = getRotationRelativeBlock(face.getCullface());
BlockProperties p = b.getProperties(); BlockProperties p = b.getProperties();

View File

@ -5,7 +5,7 @@ junit = "5.8.2"
spongegradle = "2.2.0" spongegradle = "2.2.0"
[libraries] [libraries]
aircompressor = { module = "io.airlift:aircompressor", version = "0.24" } aircompressor = { module = "io.airlift:aircompressor", version = "0.27" }
bluenbt = { module = "de.bluecolored.bluenbt:BlueNBT", version = "3.0.1" } bluenbt = { module = "de.bluecolored.bluenbt:BlueNBT", version = "3.0.1" }
brigadier = { module = "com.mojang:brigadier", version = "1.0.17" } brigadier = { module = "com.mojang:brigadier", version = "1.0.17" }
bstats-bukkit = { module = "org.bstats:bstats-bukkit", version.ref = "bstats" } bstats-bukkit = { module = "org.bstats:bstats-bukkit", version.ref = "bstats" }