mirror of
https://github.com/BlueMap-Minecraft/BlueMap.git
synced 2024-11-24 03:25:17 +01:00
Add per-map view-settings and optimized flat-view only maps
This commit is contained in:
parent
f08c7946a0
commit
4d58cc25e3
@ -123,7 +123,6 @@ private static class Settings {
|
||||
|
||||
private boolean useCookies = true;
|
||||
|
||||
private boolean enableFreeFlight = true;
|
||||
private boolean defaultToFlatView = false;
|
||||
|
||||
private String startLocation = null;
|
||||
@ -150,7 +149,6 @@ private static class Settings {
|
||||
|
||||
public void setFrom(WebappConfig config) {
|
||||
this.useCookies = config.isUseCookies();
|
||||
this.enableFreeFlight = config.isEnableFreeFlight();
|
||||
this.defaultToFlatView = config.isDefaultToFlatView();
|
||||
this.startLocation = config.getStartLocation().orElse(null);
|
||||
this.resolutionDefault = config.getResolutionDefault();
|
||||
|
@ -75,7 +75,10 @@ public class MapConfig implements MapSettings {
|
||||
|
||||
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";
|
||||
|
||||
|
@ -42,7 +42,6 @@ public class WebappConfig {
|
||||
|
||||
private boolean useCookies = true;
|
||||
|
||||
private boolean enableFreeFlight = true;
|
||||
private boolean defaultToFlatView = false;
|
||||
|
||||
private String startLocation = null;
|
||||
@ -82,10 +81,6 @@ public boolean isUseCookies() {
|
||||
return useCookies;
|
||||
}
|
||||
|
||||
public boolean isEnableFreeFlight() {
|
||||
return enableFreeFlight;
|
||||
}
|
||||
|
||||
public boolean isDefaultToFlatView() {
|
||||
return defaultToFlatView;
|
||||
}
|
||||
|
@ -96,13 +96,28 @@ min-inhabited-time: 0
|
||||
# Default is 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.
|
||||
# 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 true will require a re-render of the map.
|
||||
# Default is true
|
||||
save-hires-layer: true
|
||||
enable-hires: true
|
||||
|
||||
# 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.
|
||||
|
@ -22,10 +22,6 @@ update-settings-file: true
|
||||
# Default is 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.
|
||||
# Default is false
|
||||
default-to-flat-view: false
|
||||
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="controls-switch">
|
||||
<SvgButton :active="isPerspectiveView" @action="setPerspectiveView" :title="$t('controls.perspective.tooltip')">
|
||||
<div class="controls-switch" v-if="showViewControls">
|
||||
<SvgButton v-if="mapViewer.map.perspectiveView" :active="isPerspectiveView" @action="setPerspectiveView" :title="$t('controls.perspective.tooltip')">
|
||||
<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
|
||||
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"/>
|
||||
</svg>
|
||||
</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">
|
||||
<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>
|
||||
</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">
|
||||
<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
|
||||
@ -40,6 +40,7 @@
|
||||
data() {
|
||||
return {
|
||||
controls: this.$bluemap.appState.controls,
|
||||
mapViewer: this.$bluemap.mapViewer.data
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@ -51,6 +52,10 @@
|
||||
},
|
||||
isFreeFlight() {
|
||||
return this.controls.state === "free";
|
||||
},
|
||||
showViewControls() {
|
||||
if (!this.mapViewer.map) return 0;
|
||||
return this.mapViewer.map.views.length > 1;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
@ -1,9 +1,9 @@
|
||||
<template>
|
||||
<div>
|
||||
<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 :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.perspectiveView" :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.flatView" :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.freeFlightView" :active="appState.controls.state === 'free'" @action="$bluemap.setFreeFlight(500)">{{$t('controls.freeFlight.button')}}</SimpleButton>
|
||||
</Group>
|
||||
|
||||
<Group :title="$t('lighting.title')">
|
||||
|
@ -59,7 +59,6 @@ export class BlueMapApp {
|
||||
/** @type {{
|
||||
* version: string,
|
||||
* useCookies: boolean,
|
||||
* enableFreeFlight: boolean,
|
||||
* defaultToFlatView: boolean,
|
||||
* resolutionDefault: number,
|
||||
* minZoomDistance: number,
|
||||
@ -96,7 +95,6 @@ export class BlueMapApp {
|
||||
mouseSensitivity: 1,
|
||||
showZoomButtons: true,
|
||||
invertMouse: false,
|
||||
enableFreeFlight: false,
|
||||
pauseTileLoading: false
|
||||
},
|
||||
menu: this.mainMenu,
|
||||
@ -139,7 +137,6 @@ export class BlueMapApp {
|
||||
await this.getSettings();
|
||||
this.mapControls.minDistance = this.settings.minZoomDistance;
|
||||
this.mapControls.maxDistance = this.settings.maxZoomDistance;
|
||||
this.appState.controls.enableFreeFlight = this.settings.enableFreeFlight;
|
||||
|
||||
// load 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)
|
||||
|
||||
if (resetCamera) this.resetCamera();
|
||||
if (resetCamera || !this.mapViewer.map.hasView(this.appState.controls.state))
|
||||
this.resetCamera();
|
||||
|
||||
this.updatePageAddress();
|
||||
|
||||
await Promise.all([
|
||||
@ -283,10 +282,18 @@ export class BlueMapApp {
|
||||
|
||||
controls.controls = this.mapControls;
|
||||
this.appState.controls.state = "perspective";
|
||||
if (this.settings.defaultToFlatView) {
|
||||
|
||||
if (this.settings.defaultToFlatView && map.hasView("flat")) {
|
||||
this.setFlatView();
|
||||
}
|
||||
|
||||
else if (!map.hasView("perspective")) {
|
||||
if (map.hasView("flat"))
|
||||
this.setFlatView();
|
||||
else
|
||||
this.setFreeFlight();
|
||||
}
|
||||
|
||||
this.updatePageAddress();
|
||||
}
|
||||
|
||||
@ -328,7 +335,6 @@ export class BlueMapApp {
|
||||
this.settings = {
|
||||
version: "?",
|
||||
useCookies: false,
|
||||
enableFreeFlight: true,
|
||||
defaultToFlatView: false,
|
||||
resolutionDefault: 1.0,
|
||||
minZoomDistance: 5,
|
||||
@ -451,6 +457,7 @@ export class BlueMapApp {
|
||||
|
||||
setPerspectiveView(transition = 0, minDistance = 5) {
|
||||
if (!this.mapViewer.map) return;
|
||||
if (!this.mapViewer.map.data.perspectiveView) return;
|
||||
if (this.viewAnimation) this.viewAnimation.cancel();
|
||||
|
||||
let cm = this.mapViewer.controlsManager;
|
||||
@ -488,6 +495,7 @@ export class BlueMapApp {
|
||||
|
||||
setFlatView(transition = 0, minDistance = 5) {
|
||||
if (!this.mapViewer.map) return;
|
||||
if (!this.mapViewer.map.data.flatView) return;
|
||||
if (this.viewAnimation) this.viewAnimation.cancel();
|
||||
|
||||
let cm = this.mapViewer.controlsManager;
|
||||
@ -521,7 +529,7 @@ export class BlueMapApp {
|
||||
|
||||
setFreeFlight(transition = 0, targetY = undefined) {
|
||||
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();
|
||||
|
||||
let cm = this.mapViewer.controlsManager;
|
||||
|
@ -78,7 +78,11 @@ export class Map {
|
||||
tileSize: {x: 32, z: 32},
|
||||
lodFactor: 5,
|
||||
lodCount: 3
|
||||
}
|
||||
},
|
||||
perspectiveView: false,
|
||||
flatView: false,
|
||||
freeFlightView: false,
|
||||
views: ["perspective", "flat", "free"]
|
||||
});
|
||||
|
||||
this.raycaster = new Raycaster();
|
||||
@ -198,6 +202,15 @@ export class Map {
|
||||
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");
|
||||
});
|
||||
}
|
||||
@ -472,6 +485,10 @@ export class Map {
|
||||
return false;
|
||||
}
|
||||
|
||||
hasView(view) {
|
||||
return this.data.views.some(v => v === view)
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this.unload();
|
||||
}
|
||||
|
@ -52,4 +52,22 @@ public interface MapSettings extends RenderSettings {
|
||||
|
||||
float getSkyLight();
|
||||
|
||||
boolean isEnablePerspectiveView();
|
||||
|
||||
boolean isEnableFlatView();
|
||||
|
||||
boolean isEnableFreeFlightView();
|
||||
|
||||
boolean isEnableHires();
|
||||
|
||||
@Override
|
||||
default boolean isSaveHiresLayer() {
|
||||
return isEnableHires();
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean isRenderTopOnly() {
|
||||
return !isEnableHires() || (!isEnablePerspectiveView() && !isEnableFreeFlightView());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -83,6 +83,11 @@ public JsonElement serialize(BmMap map, Type typeOfSrc, JsonSerializationContext
|
||||
root.addProperty("ambientLight", map.getMapSettings().getAmbientLight());
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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 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();
|
||||
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 (
|
||||
OutputStream out = storage.write(tile.getX(), tile.getY());
|
||||
PRBMWriter modelWriter = new PRBMWriter(out)
|
||||
|
@ -90,9 +90,6 @@ public void render(World world, Vector3i modelMin, Vector3i modelMax, TileModel
|
||||
//update topBlockLight
|
||||
topBlockLight = Math.max(topBlockLight, block.getBlockLightLevel() * (1 - columnColor.a));
|
||||
|
||||
// skip empty blocks
|
||||
if (blockModel.getSize() <= 0) continue;
|
||||
|
||||
// move block-model to correct position
|
||||
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());
|
||||
}
|
||||
|
||||
//if (blockColor.a > 0.999 && block.getProperties().isCulling()) break;
|
||||
if (renderSettings.isRenderTopOnly() && blockColor.a > 0.999 && block.getProperties().isCulling())
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -63,7 +63,7 @@ public PRBMWriter(OutputStream 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(HEADER_BITS); // format info - 1 byte
|
||||
write3byteValue(model.size * 3); // number of values - 3 bytes
|
||||
@ -85,7 +85,7 @@ public void close() throws IOException {
|
||||
out.close();
|
||||
}
|
||||
|
||||
private void writePositionArray(TileModel model) throws IOException {
|
||||
private void writePositionArray(ArrayTileModel model) throws IOException {
|
||||
float[] position = model.position;
|
||||
|
||||
writeString("position");
|
||||
@ -98,13 +98,13 @@ private void writePositionArray(TileModel model) throws IOException {
|
||||
|
||||
writePadding();
|
||||
|
||||
int posSize = model.size * TileModel.FI_POSITION;
|
||||
int posSize = model.size * ArrayTileModel.FI_POSITION;
|
||||
for (int i = 0; i < posSize; 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);
|
||||
float[] position = model.position;
|
||||
|
||||
@ -120,7 +120,7 @@ private void writeNormalArray(TileModel model) throws IOException {
|
||||
|
||||
int pi, i, j;
|
||||
for (i = 0; i < model.size; i++) {
|
||||
pi = i * TileModel.FI_POSITION;
|
||||
pi = i * ArrayTileModel.FI_POSITION;
|
||||
calculateSurfaceNormal(
|
||||
position[pi], position[pi + 1], position[pi + 2],
|
||||
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;
|
||||
|
||||
writeString("color");
|
||||
@ -149,7 +149,7 @@ private void writeColorArray(TileModel model) throws IOException {
|
||||
|
||||
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 (j = 0; j < 3; j++) {
|
||||
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;
|
||||
|
||||
writeString("uv");
|
||||
@ -172,13 +172,13 @@ private void writeUvArray(TileModel model) throws IOException {
|
||||
|
||||
writePadding();
|
||||
|
||||
int uvSize = model.size * TileModel.FI_UV;
|
||||
int uvSize = model.size * ArrayTileModel.FI_UV;
|
||||
for (int i = 0; i < uvSize; i++) {
|
||||
writeFloat(uv[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeAoArray(TileModel model) throws IOException {
|
||||
private void writeAoArray(ArrayTileModel model) throws IOException {
|
||||
float[] ao = model.ao;
|
||||
|
||||
writeString("ao");
|
||||
@ -191,13 +191,13 @@ private void writeAoArray(TileModel model) throws IOException {
|
||||
|
||||
writePadding();
|
||||
|
||||
int uvSize = model.size * TileModel.FI_AO;
|
||||
int uvSize = model.size * ArrayTileModel.FI_AO;
|
||||
for (int i = 0; i < uvSize; i++) {
|
||||
writeNormalizedUnsignedByteValue(ao[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeBlocklightArray(TileModel model) throws IOException {
|
||||
private void writeBlocklightArray(ArrayTileModel model) throws IOException {
|
||||
byte[] blocklight = model.blocklight;
|
||||
|
||||
writeString("blocklight");
|
||||
@ -210,7 +210,7 @@ private void writeBlocklightArray(TileModel model) throws IOException {
|
||||
|
||||
writePadding();
|
||||
|
||||
int blSize = model.size * TileModel.FI_BLOCKLIGHT;
|
||||
int blSize = model.size * ArrayTileModel.FI_BLOCKLIGHT;
|
||||
for (int i = 0; i < blSize; 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;
|
||||
|
||||
writeString("sunlight");
|
||||
@ -231,7 +231,7 @@ private void writeSunlightArray(TileModel model) throws IOException {
|
||||
|
||||
writePadding();
|
||||
|
||||
int slSize = model.size * TileModel.FI_SUNLIGHT;
|
||||
int slSize = model.size * ArrayTileModel.FI_SUNLIGHT;
|
||||
for (int i = 0; i < slSize; 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();
|
||||
|
||||
if (model.size > 0) {
|
||||
int[] materialIndex = model.materialIndex;
|
||||
|
||||
int miSize = model.size * TileModel.FI_MATERIAL_INDEX,
|
||||
int miSize = model.size * ArrayTileModel.FI_MATERIAL_INDEX,
|
||||
lastMaterial = materialIndex[0],
|
||||
material = lastMaterial, groupStart = 0;
|
||||
|
||||
|
@ -46,7 +46,7 @@ public interface RenderSettings {
|
||||
int getCaveDetectionOceanFloor();
|
||||
|
||||
/**
|
||||
* If blocklight should be used instead of sky light to detect "caves"
|
||||
* If blocklight should be used instead of skylight to detect "caves"
|
||||
*/
|
||||
boolean isCaveDetectionUsesBlockLight();
|
||||
|
||||
@ -125,4 +125,6 @@ default Predicate<Vector2i> getCellRenderBoundariesFilter(Grid grid, boolean all
|
||||
|
||||
boolean isSaveHiresLayer();
|
||||
|
||||
boolean isRenderTopOnly();
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
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 TileModel {
|
||||
private static final double GROW_MULTIPLIER = 1.5;
|
||||
public interface TileModel {
|
||||
|
||||
// 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 ;
|
||||
int size();
|
||||
|
||||
private static final InstancePool<TileModel> INSTANCE_POOL = new InstancePool<>(
|
||||
() -> new TileModel(100),
|
||||
TileModel::clear
|
||||
);
|
||||
int add(int count);
|
||||
|
||||
private int capacity;
|
||||
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(
|
||||
TileModel 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;
|
||||
}
|
||||
|
||||
public TileModel setUvs(
|
||||
TileModel 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;
|
||||
}
|
||||
|
||||
public TileModel setAOs(
|
||||
TileModel 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;
|
||||
}
|
||||
|
||||
public TileModel setColor(
|
||||
TileModel 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;
|
||||
TileModel setSunlight(int face, int sl);
|
||||
|
||||
return this;
|
||||
}
|
||||
TileModel setBlocklight(int face, int bl);
|
||||
|
||||
public TileModel setSunlight(int face, int sl) {
|
||||
sunlight[face * FI_SUNLIGHT] = (byte) sl;
|
||||
return this;
|
||||
}
|
||||
TileModel setMaterialIndex(int face, int m);
|
||||
|
||||
public TileModel setBlocklight(int face, int bl) {
|
||||
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(
|
||||
TileModel 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);
|
||||
}
|
||||
|
||||
public TileModel rotate(
|
||||
TileModel 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);
|
||||
}
|
||||
|
||||
public TileModel rotateByQuaternion(
|
||||
TileModel 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;
|
||||
}
|
||||
|
||||
public TileModel scale(
|
||||
TileModel 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;
|
||||
}
|
||||
|
||||
public TileModel translate(
|
||||
TileModel 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;
|
||||
}
|
||||
TileModel 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,
|
||||
t.m20, t.m21, t.m22
|
||||
);
|
||||
}
|
||||
|
||||
public TileModel transform(
|
||||
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 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) {
|
||||
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
|
||||
);
|
||||
}
|
||||
TileModel transform(int start, int count, MatrixM4f t);
|
||||
|
||||
public TileModel transform(
|
||||
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
|
||||
) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
TileModel reset(int size);
|
||||
|
||||
return this;
|
||||
}
|
||||
TileModel clear();
|
||||
|
||||
public TileModel reset(int size) {
|
||||
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;
|
||||
}
|
||||
void sort();
|
||||
|
||||
}
|
||||
|
@ -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() { }
|
||||
|
||||
}
|
@ -99,7 +99,7 @@ private void renderModel(BlockNeighborhood<?> block, BlockState blockState, Tile
|
||||
variantColor.set(0f, 0f, 0f, 0f, true);
|
||||
|
||||
blockRenderers.get(modelResource.getRenderer())
|
||||
.render(block, variant, blockModel.initialize(), blockColor);
|
||||
.render(block, variant, blockModel.initialize(), variantColor);
|
||||
|
||||
if (variantColor.a > blockColorOpacity)
|
||||
blockColorOpacity = variantColor.a;
|
||||
|
@ -203,7 +203,7 @@ private void createElementFace(Element element, Direction faceDir, VectorM3f c0,
|
||||
makeRotationRelative(faceRotationVector);
|
||||
|
||||
// face culling
|
||||
//if (faceRotationVector.y < 0.01) return;
|
||||
if (renderSettings.isRenderTopOnly() && faceRotationVector.y < 0.01) return;
|
||||
if (face.getCullface() != null) {
|
||||
ExtendedBlock<?> b = getRotationRelativeBlock(face.getCullface());
|
||||
BlockProperties p = b.getProperties();
|
||||
|
@ -5,7 +5,7 @@ junit = "5.8.2"
|
||||
spongegradle = "2.2.0"
|
||||
|
||||
[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" }
|
||||
brigadier = { module = "com.mojang:brigadier", version = "1.0.17" }
|
||||
bstats-bukkit = { module = "org.bstats:bstats-bukkit", version.ref = "bstats" }
|
||||
|
Loading…
Reference in New Issue
Block a user