Sichtweite und Nebel hinzugefügt
This commit is contained in:
22
blight-assets/src/main/resources/MatDefs/BlightFog.j3md
Normal file
22
blight-assets/src/main/resources/MatDefs/BlightFog.j3md
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
MaterialDef BlightFog {
|
||||||
|
|
||||||
|
MaterialParameters {
|
||||||
|
Texture2D Texture
|
||||||
|
Texture2D DepthTexture
|
||||||
|
Int NumSamples
|
||||||
|
Int NumSamplesDepth
|
||||||
|
|
||||||
|
Color FogColor : 0.75 0.80 0.88 1.0
|
||||||
|
Float FogDensity : 0.40
|
||||||
|
Float FogDistance : 600.0
|
||||||
|
// Wird jeden Frame aus der echten Kamera gesetzt (near, far)
|
||||||
|
Vector2 FrustumNearFar : 1.0 1000.0
|
||||||
|
}
|
||||||
|
|
||||||
|
Technique {
|
||||||
|
VertexShader GLSL150: Common/MatDefs/Post/Post15.vert
|
||||||
|
FragmentShader GLSL150: Shaders/BlightFog.frag
|
||||||
|
|
||||||
|
WorldParameters {}
|
||||||
|
}
|
||||||
|
}
|
||||||
37
blight-assets/src/main/resources/Shaders/BlightFog.frag
Normal file
37
blight-assets/src/main/resources/Shaders/BlightFog.frag
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
#import "Common/ShaderLib/GLSLCompat.glsllib"
|
||||||
|
|
||||||
|
uniform sampler2D m_Texture;
|
||||||
|
uniform sampler2D m_DepthTexture;
|
||||||
|
uniform vec4 m_FogColor;
|
||||||
|
uniform float m_FogDensity;
|
||||||
|
uniform float m_FogDistance;
|
||||||
|
// Kamera-Frustum (near, far) — wird jeden Frame aus der echten Kamera gesetzt
|
||||||
|
uniform vec2 m_FrustumNearFar;
|
||||||
|
|
||||||
|
in vec2 texCoord;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec4 sceneColor = texture2D(m_Texture, texCoord);
|
||||||
|
float rawDepth = texture2D(m_DepthTexture, texCoord).r;
|
||||||
|
|
||||||
|
// Sky-Pixel (Depth-Buffer = 1.0) nicht nebeln
|
||||||
|
if (rawDepth >= 1.0) {
|
||||||
|
gl_FragColor = sceneColor;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
float near = m_FrustumNearFar.x;
|
||||||
|
float far = m_FrustumNearFar.y;
|
||||||
|
|
||||||
|
// Depth-Buffer [0,1] → echte Weltdistanz (Meter)
|
||||||
|
// Herleitung: z_view = near*far / (far - rawDepth*(far-near))
|
||||||
|
float linearDist = (near * far) / (far - rawDepth * (far - near));
|
||||||
|
|
||||||
|
// Normiert auf FogDistance; d=1 entspricht genau der eingestellten Nebel-Distanz
|
||||||
|
float d = linearDist / m_FogDistance;
|
||||||
|
|
||||||
|
// Exponential-squared fog (gleiche Formel wie JME FogFilter, korrekte Tiefe)
|
||||||
|
float fogFactor = clamp(exp2(-m_FogDensity * m_FogDensity * d * d * 1.4426950408), 0.0, 1.0);
|
||||||
|
|
||||||
|
gl_FragColor = mix(m_FogColor, sceneColor, fogFactor);
|
||||||
|
}
|
||||||
@@ -1616,7 +1616,7 @@ public class TerrainEditorState extends BaseAppState {
|
|||||||
|
|
||||||
// Geschwindigkeit skaliert linear mit Abstand zum Terrain (näher = langsamer)
|
// Geschwindigkeit skaliert linear mit Abstand zum Terrain (näher = langsamer)
|
||||||
float terrainDist = terrainDistBelow();
|
float terrainDist = terrainDistBelow();
|
||||||
float speed = FastMath.clamp(terrainDist, 5f, CAM_SPEED) * tpf;
|
float speed = FastMath.clamp(terrainDist, 5f, CAM_SPEED) * tpf * (input.shiftHeld ? 5f : 1f);
|
||||||
|
|
||||||
if (input.camMode == SharedInput.CAM_FREEFLY) {
|
if (input.camMode == SharedInput.CAM_FREEFLY) {
|
||||||
Vector3f fwd = cam.getDirection().clone();
|
Vector3f fwd = cam.getDirection().clone();
|
||||||
|
|||||||
@@ -203,7 +203,7 @@ public class BlightGame extends SimpleApplication {
|
|||||||
|
|
||||||
// WorldScene erst vorbereiten, aber nicht aktivieren (onEnable lädt die Welt)
|
// WorldScene erst vorbereiten, aber nicht aktivieren (onEnable lädt die Welt)
|
||||||
status("Initialisiere Welt...");
|
status("Initialisiere Welt...");
|
||||||
worldScene = new WorldScene(keyBindings);
|
worldScene = new WorldScene(keyBindings, graphicsSettings);
|
||||||
worldScene.setEnabled(false);
|
worldScene.setEnabled(false);
|
||||||
stateManager.attach(worldScene);
|
stateManager.attach(worldScene);
|
||||||
|
|
||||||
@@ -268,6 +268,13 @@ public class BlightGame extends SimpleApplication {
|
|||||||
});
|
});
|
||||||
stateManager.attach(console);
|
stateManager.attach(console);
|
||||||
|
|
||||||
|
// ── Debug: Nebel-Toggle (F7) ─────────────────────────────────────────────
|
||||||
|
inputManager.addMapping("DebugFog", new KeyTrigger(KeyInput.KEY_F7));
|
||||||
|
inputManager.addListener((ActionListener) (name, isPressed, tpf) -> {
|
||||||
|
if (!isPressed) return;
|
||||||
|
worldScene.toggleDebugFog();
|
||||||
|
}, "DebugFog");
|
||||||
|
|
||||||
// ── Debug: Lighting-Toggle (F8) ──────────────────────────────────────────
|
// ── Debug: Lighting-Toggle (F8) ──────────────────────────────────────────
|
||||||
inputManager.addMapping("DebugNoLight", new KeyTrigger(KeyInput.KEY_F8));
|
inputManager.addMapping("DebugNoLight", new KeyTrigger(KeyInput.KEY_F8));
|
||||||
inputManager.addListener((ActionListener) (name, isPressed, tpf) -> {
|
inputManager.addListener((ActionListener) (name, isPressed, tpf) -> {
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import com.jme3.font.BitmapText;
|
|||||||
import com.jme3.math.ColorRGBA;
|
import com.jme3.math.ColorRGBA;
|
||||||
import com.jme3.scene.Node;
|
import com.jme3.scene.Node;
|
||||||
import com.jme3.system.AppSettings;
|
import com.jme3.system.AppSettings;
|
||||||
|
import de.blight.game.state.TerrainChunkState;
|
||||||
|
import de.blight.game.state.WeatherState;
|
||||||
import de.blight.lang.TextResolver;
|
import de.blight.lang.TextResolver;
|
||||||
|
|
||||||
public class GraphicsScreen extends MenuScreen {
|
public class GraphicsScreen extends MenuScreen {
|
||||||
@@ -13,11 +15,12 @@ public class GraphicsScreen extends MenuScreen {
|
|||||||
};
|
};
|
||||||
private static final int[] SAMPLES = {0, 2, 4, 8};
|
private static final int[] SAMPLES = {0, 2, 4, 8};
|
||||||
|
|
||||||
private static final int ROW_RES = 0;
|
private static final int ROW_RES = 0;
|
||||||
private static final int ROW_FULL = 1;
|
private static final int ROW_FULL = 1;
|
||||||
private static final int ROW_VSYNC = 2;
|
private static final int ROW_VSYNC = 2;
|
||||||
private static final int ROW_AA = 3;
|
private static final int ROW_AA = 3;
|
||||||
private static final int ROW_COUNT = 4;
|
private static final int ROW_VIEWDIST = 4;
|
||||||
|
private static final int ROW_COUNT = 5;
|
||||||
|
|
||||||
private final GraphicsSettings live;
|
private final GraphicsSettings live;
|
||||||
private GraphicsSettings edit;
|
private GraphicsSettings edit;
|
||||||
@@ -42,9 +45,10 @@ public class GraphicsScreen extends MenuScreen {
|
|||||||
protected void onEnableExtras() {
|
protected void onEnableExtras() {
|
||||||
edit = new GraphicsSettings();
|
edit = new GraphicsSettings();
|
||||||
edit.width = live.width; edit.height = live.height;
|
edit.width = live.width; edit.height = live.height;
|
||||||
edit.fullscreen = live.fullscreen;
|
edit.fullscreen = live.fullscreen;
|
||||||
edit.vsync = live.vsync;
|
edit.vsync = live.vsync;
|
||||||
edit.samples = live.samples;
|
edit.samples = live.samples;
|
||||||
|
edit.viewDistance = live.viewDistance;
|
||||||
|
|
||||||
resIdx = 0;
|
resIdx = 0;
|
||||||
for (int i = 0; i < RESOLUTIONS.length; i++) {
|
for (int i = 0; i < RESOLUTIONS.length; i++) {
|
||||||
@@ -66,7 +70,8 @@ public class GraphicsScreen extends MenuScreen {
|
|||||||
"menu.graphics.row.resolution",
|
"menu.graphics.row.resolution",
|
||||||
"menu.graphics.row.fullscreen",
|
"menu.graphics.row.fullscreen",
|
||||||
"menu.graphics.row.vsync",
|
"menu.graphics.row.vsync",
|
||||||
"menu.graphics.row.aa"
|
"menu.graphics.row.aa",
|
||||||
|
"menu.graphics.row.viewdist"
|
||||||
};
|
};
|
||||||
|
|
||||||
float lblX = CONT_X + 30f;
|
float lblX = CONT_X + 30f;
|
||||||
@@ -110,11 +115,16 @@ public class GraphicsScreen extends MenuScreen {
|
|||||||
|
|
||||||
private void refreshText(int row) {
|
private void refreshText(int row) {
|
||||||
String val = switch (row) {
|
String val = switch (row) {
|
||||||
case ROW_RES -> RESOLUTIONS[resIdx][0] + "x" + RESOLUTIONS[resIdx][1];
|
case ROW_RES -> RESOLUTIONS[resIdx][0] + "x" + RESOLUTIONS[resIdx][1];
|
||||||
case ROW_FULL -> edit.fullscreen ? t("menu.graphics.val.on") : t("menu.graphics.val.off");
|
case ROW_FULL -> edit.fullscreen ? t("menu.graphics.val.on") : t("menu.graphics.val.off");
|
||||||
case ROW_VSYNC -> edit.vsync ? t("menu.graphics.val.on") : t("menu.graphics.val.off");
|
case ROW_VSYNC -> edit.vsync ? t("menu.graphics.val.on") : t("menu.graphics.val.off");
|
||||||
case ROW_AA -> SAMPLES[samplesIdx] == 0
|
case ROW_AA -> SAMPLES[samplesIdx] == 0
|
||||||
? t("menu.graphics.val.off") : SAMPLES[samplesIdx] + "x MSAA";
|
? t("menu.graphics.val.off") : SAMPLES[samplesIdx] + "x MSAA";
|
||||||
|
case ROW_VIEWDIST -> switch (edit.viewDistance) {
|
||||||
|
case SHORT -> t("menu.graphics.val.viewdist.short");
|
||||||
|
case NORMAL -> t("menu.graphics.val.viewdist.normal");
|
||||||
|
case FAR -> t("menu.graphics.val.viewdist.far");
|
||||||
|
};
|
||||||
default -> "";
|
default -> "";
|
||||||
};
|
};
|
||||||
BitmapText vt = valTexts[row];
|
BitmapText vt = valTexts[row];
|
||||||
@@ -140,27 +150,47 @@ public class GraphicsScreen extends MenuScreen {
|
|||||||
samplesIdx = (samplesIdx + dir + SAMPLES.length) % SAMPLES.length;
|
samplesIdx = (samplesIdx + dir + SAMPLES.length) % SAMPLES.length;
|
||||||
edit.samples = SAMPLES[samplesIdx];
|
edit.samples = SAMPLES[samplesIdx];
|
||||||
break;
|
break;
|
||||||
|
case ROW_VIEWDIST:
|
||||||
|
GraphicsSettings.ViewDistance[] vds = GraphicsSettings.ViewDistance.values();
|
||||||
|
edit.viewDistance = vds[(edit.viewDistance.ordinal() + dir + vds.length) % vds.length];
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
refreshText(row);
|
refreshText(row);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void applyAndSave() {
|
private void applyAndSave() {
|
||||||
live.width = edit.width; live.height = edit.height;
|
boolean needsRestart = live.width != edit.width
|
||||||
live.fullscreen = edit.fullscreen;
|
|| live.height != edit.height
|
||||||
live.vsync = edit.vsync;
|
|| live.fullscreen != edit.fullscreen
|
||||||
live.samples = edit.samples;
|
|| live.vsync != edit.vsync
|
||||||
|
|| live.samples != edit.samples;
|
||||||
|
|
||||||
|
live.width = edit.width;
|
||||||
|
live.height = edit.height;
|
||||||
|
live.fullscreen = edit.fullscreen;
|
||||||
|
live.vsync = edit.vsync;
|
||||||
|
live.samples = edit.samples;
|
||||||
|
live.viewDistance = edit.viewDistance;
|
||||||
GraphicsStore.save(live);
|
GraphicsStore.save(live);
|
||||||
|
|
||||||
AppSettings s = app.getContext().getSettings();
|
WeatherState ws = getApplication().getStateManager().getState(WeatherState.class);
|
||||||
s.setResolution(live.width, live.height);
|
if (ws != null) ws.setViewDistanceFactor(live.viewDistance.fogFactor);
|
||||||
s.setFullscreen(live.fullscreen);
|
|
||||||
s.setBitsPerPixel(32);
|
TerrainChunkState tcs = getApplication().getStateManager().getState(TerrainChunkState.class);
|
||||||
s.setVSync(live.vsync);
|
if (tcs != null) tcs.setLodRanges(live.viewDistance.lod0Range, live.viewDistance.lod1Range);
|
||||||
s.setSamples(live.samples);
|
|
||||||
app.setSettings(s);
|
|
||||||
|
|
||||||
close();
|
close();
|
||||||
app.restart();
|
|
||||||
|
if (needsRestart) {
|
||||||
|
AppSettings s = app.getContext().getSettings();
|
||||||
|
s.setResolution(live.width, live.height);
|
||||||
|
s.setFullscreen(live.fullscreen);
|
||||||
|
s.setBitsPerPixel(32);
|
||||||
|
s.setVSync(live.vsync);
|
||||||
|
s.setSamples(live.samples);
|
||||||
|
app.setSettings(s);
|
||||||
|
app.restart();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void close() {
|
private void close() {
|
||||||
|
|||||||
@@ -1,9 +1,26 @@
|
|||||||
package de.blight.game.config;
|
package de.blight.game.config;
|
||||||
|
|
||||||
public class GraphicsSettings {
|
public class GraphicsSettings {
|
||||||
public int width = 1280;
|
public int width = 1280;
|
||||||
public int height = 720;
|
public int height = 720;
|
||||||
public boolean fullscreen = false;
|
public boolean fullscreen = false;
|
||||||
public boolean vsync = false;
|
public boolean vsync = false;
|
||||||
public int samples = 4;
|
public int samples = 4;
|
||||||
|
public ViewDistance viewDistance = ViewDistance.NORMAL;
|
||||||
|
|
||||||
|
public enum ViewDistance {
|
||||||
|
SHORT (0.55f, 0, 2),
|
||||||
|
NORMAL(1.00f, 1, 3),
|
||||||
|
FAR (1.60f, 2, 5);
|
||||||
|
|
||||||
|
public final float fogFactor;
|
||||||
|
public final int lod0Range;
|
||||||
|
public final int lod1Range;
|
||||||
|
|
||||||
|
ViewDistance(float fogFactor, int lod0Range, int lod1Range) {
|
||||||
|
this.fogFactor = fogFactor;
|
||||||
|
this.lod0Range = lod0Range;
|
||||||
|
this.lod1Range = lod1Range;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,96 @@
|
|||||||
|
package de.blight.game.post;
|
||||||
|
|
||||||
|
import com.jme3.asset.AssetManager;
|
||||||
|
import com.jme3.material.Material;
|
||||||
|
import com.jme3.math.ColorRGBA;
|
||||||
|
import com.jme3.math.Vector2f;
|
||||||
|
import com.jme3.post.Filter;
|
||||||
|
import com.jme3.renderer.Camera;
|
||||||
|
import com.jme3.renderer.RenderManager;
|
||||||
|
import com.jme3.renderer.ViewPort;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Nebelfilter mit korrekter Tiefen-Linearisierung.
|
||||||
|
*
|
||||||
|
* JME3s eingebauter FogFilter setzt FrustumNearFar=[1, fogDistance], was bei
|
||||||
|
* camera.far=4000m dazu führt, dass alle Objekte jenseits von fogDistance
|
||||||
|
* praktisch den gleichen Nebelwert bekommen (NDC-z clustert um 1.0).
|
||||||
|
* Dieser Filter liest jeden Frame near/far aus der echten Kamera und rechnet
|
||||||
|
* korrekt zurück auf die tatsächliche Weltdistanz.
|
||||||
|
*/
|
||||||
|
public class BlightFogFilter extends Filter {
|
||||||
|
|
||||||
|
private float fogDensity = 0.40f;
|
||||||
|
private float fogDistance = 600f;
|
||||||
|
private ColorRGBA fogColor = new ColorRGBA(0.75f, 0.80f, 0.88f, 1f);
|
||||||
|
private Camera camera;
|
||||||
|
|
||||||
|
public BlightFogFilter() {
|
||||||
|
super("BlightFog");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Lifecycle ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isRequiresDepthTexture() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void initFilter(AssetManager manager, RenderManager rm,
|
||||||
|
ViewPort vp, int w, int h) {
|
||||||
|
camera = vp.getCamera();
|
||||||
|
material = new Material(manager, "MatDefs/BlightFog.j3md");
|
||||||
|
material.setColor("FogColor", fogColor.clone());
|
||||||
|
material.setFloat("FogDensity", fogDensity);
|
||||||
|
material.setFloat("FogDistance", fogDistance);
|
||||||
|
syncFrustum();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void preFrame(float tpf) {
|
||||||
|
syncFrustum();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Material getMaterial() {
|
||||||
|
return material;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Setter (gleiche API wie FogFilter) ───────────────────────────────────
|
||||||
|
|
||||||
|
public void setFogDensity(float density) {
|
||||||
|
this.fogDensity = density;
|
||||||
|
if (material != null) {
|
||||||
|
material.setFloat("FogDensity", density);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFogDistance(float distance) {
|
||||||
|
this.fogDistance = distance;
|
||||||
|
if (material != null) {
|
||||||
|
material.setFloat("FogDistance", distance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFogColor(ColorRGBA color) {
|
||||||
|
this.fogColor = color;
|
||||||
|
if (material != null) {
|
||||||
|
material.setColor("FogColor", color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getFogDensity() { return fogDensity; }
|
||||||
|
public float getFogDistance() { return fogDistance; }
|
||||||
|
public ColorRGBA getFogColor() { return fogColor; }
|
||||||
|
|
||||||
|
// ── Intern ────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
private void syncFrustum() {
|
||||||
|
if (camera == null || material == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
material.setVector2("FrustumNearFar",
|
||||||
|
new Vector2f(camera.getFrustumNear(), camera.getFrustumFar()));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -33,11 +33,12 @@ import de.blight.common.model.GameCharacter;
|
|||||||
import de.blight.common.model.MainCharacter;
|
import de.blight.common.model.MainCharacter;
|
||||||
import de.blight.game.BlightGame;
|
import de.blight.game.BlightGame;
|
||||||
import de.blight.game.animation.AnimationLibrary;
|
import de.blight.game.animation.AnimationLibrary;
|
||||||
|
import de.blight.game.config.GraphicsSettings;
|
||||||
import de.blight.game.config.KeyBindings;
|
import de.blight.game.config.KeyBindings;
|
||||||
import de.blight.game.control.PlayerInputControl;
|
import de.blight.game.control.PlayerInputControl;
|
||||||
import de.blight.game.control.ThirdPersonCamera;
|
import de.blight.game.control.ThirdPersonCamera;
|
||||||
import com.jme3.post.FilterPostProcessor;
|
import com.jme3.post.FilterPostProcessor;
|
||||||
import com.jme3.post.filters.FogFilter;
|
import de.blight.game.post.BlightFogFilter;
|
||||||
import com.jme3.water.WaterFilter;
|
import com.jme3.water.WaterFilter;
|
||||||
import de.blight.game.state.DynamicWaterFilter;
|
import de.blight.game.state.DynamicWaterFilter;
|
||||||
import de.blight.game.state.WaterInteractionState;
|
import de.blight.game.state.WaterInteractionState;
|
||||||
@@ -78,10 +79,12 @@ public class WorldScene extends BaseAppState {
|
|||||||
private BulletAppState bulletAppState;
|
private BulletAppState bulletAppState;
|
||||||
private MapData loadedMapData;
|
private MapData loadedMapData;
|
||||||
private FilterPostProcessor sharedFPP;
|
private FilterPostProcessor sharedFPP;
|
||||||
|
private BlightFogFilter fogFilter;
|
||||||
private TerrainChunkState terrainChunkState;
|
private TerrainChunkState terrainChunkState;
|
||||||
private Material terrainMaterial;
|
private Material terrainMaterial;
|
||||||
|
|
||||||
private final KeyBindings keyBindings;
|
private final KeyBindings keyBindings;
|
||||||
|
private final GraphicsSettings graphicsSettings;
|
||||||
private ThirdPersonCamera thirdPersonCam;
|
private ThirdPersonCamera thirdPersonCam;
|
||||||
private PlayerInputControl playerInput;
|
private PlayerInputControl playerInput;
|
||||||
private InventoryState inventoryState;
|
private InventoryState inventoryState;
|
||||||
@@ -101,8 +104,9 @@ public class WorldScene extends BaseAppState {
|
|||||||
private de.blight.game.state.DrownState drownState;
|
private de.blight.game.state.DrownState drownState;
|
||||||
private WaterInteractionState waterInteractionState;
|
private WaterInteractionState waterInteractionState;
|
||||||
|
|
||||||
public WorldScene(KeyBindings keyBindings) {
|
public WorldScene(KeyBindings keyBindings, GraphicsSettings graphicsSettings) {
|
||||||
this.keyBindings = keyBindings;
|
this.keyBindings = keyBindings;
|
||||||
|
this.graphicsSettings = graphicsSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
public InventoryState getInventoryState() { return inventoryState; }
|
public InventoryState getInventoryState() { return inventoryState; }
|
||||||
@@ -246,7 +250,9 @@ public class WorldScene extends BaseAppState {
|
|||||||
|
|
||||||
BlightGame.status("Lade Welt-Objekte...");
|
BlightGame.status("Lade Welt-Objekte...");
|
||||||
app.getStateManager().attach(new RiverState());
|
app.getStateManager().attach(new RiverState());
|
||||||
app.getStateManager().attach(new WorldObjectsState());
|
WorldObjectsState worldObjects = new WorldObjectsState();
|
||||||
|
worldObjects.setLodFactor(graphicsSettings.viewDistance.fogFactor);
|
||||||
|
app.getStateManager().attach(worldObjects);
|
||||||
app.getStateManager().attach(new WorldLightState(sharedFPP));
|
app.getStateManager().attach(new WorldLightState(sharedFPP));
|
||||||
app.getStateManager().attach(new StoneWorldState());
|
app.getStateManager().attach(new StoneWorldState());
|
||||||
|
|
||||||
@@ -466,6 +472,13 @@ public class WorldScene extends BaseAppState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void toggleDebugFog() {
|
||||||
|
if (fogFilter == null) return;
|
||||||
|
boolean nowEnabled = !fogFilter.isEnabled();
|
||||||
|
fogFilter.setEnabled(nowEnabled);
|
||||||
|
log.info("[Debug] Nebel {}", nowEnabled ? "AN" : "AUS");
|
||||||
|
}
|
||||||
|
|
||||||
@Override protected void cleanup(Application app) {
|
@Override protected void cleanup(Application app) {
|
||||||
}
|
}
|
||||||
@Override protected void onDisable() {}
|
@Override protected void onDisable() {}
|
||||||
@@ -712,13 +725,14 @@ public class WorldScene extends BaseAppState {
|
|||||||
WeatherState weather = new WeatherState();
|
WeatherState weather = new WeatherState();
|
||||||
weather.setWaterFilter(waterFilter);
|
weather.setWaterFilter(waterFilter);
|
||||||
|
|
||||||
FogFilter fogFilter = new FogFilter();
|
fogFilter = new BlightFogFilter();
|
||||||
fogFilter.setFogColor(new ColorRGBA(0.75f, 0.80f, 0.88f, 1f));
|
fogFilter.setFogColor(new ColorRGBA(0.75f, 0.80f, 0.88f, 1f));
|
||||||
fogFilter.setFogDensity(0.0f);
|
fogFilter.setFogDensity(0.40f);
|
||||||
fogFilter.setFogDistance(600f);
|
fogFilter.setFogDistance(600f);
|
||||||
fpp.addFilter(fogFilter);
|
fpp.addFilter(fogFilter);
|
||||||
|
|
||||||
weather.setFogFilter(fogFilter);
|
weather.setFogFilter(fogFilter);
|
||||||
|
weather.setViewDistanceFactor(graphicsSettings.viewDistance.fogFactor);
|
||||||
weather.setSkyControl(dayNight.getSkyControl());
|
weather.setSkyControl(dayNight.getSkyControl());
|
||||||
app.getStateManager().attach(weather);
|
app.getStateManager().attach(weather);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -775,6 +789,9 @@ public class WorldScene extends BaseAppState {
|
|||||||
terrainMaterial = buildTerrainMaterial(loadedMapData);
|
terrainMaterial = buildTerrainMaterial(loadedMapData);
|
||||||
|
|
||||||
terrainChunkState = new TerrainChunkState(bulletAppState, terrainMaterial, loadedMapData);
|
terrainChunkState = new TerrainChunkState(bulletAppState, terrainMaterial, loadedMapData);
|
||||||
|
terrainChunkState.setLodRanges(
|
||||||
|
graphicsSettings.viewDistance.lod0Range,
|
||||||
|
graphicsSettings.viewDistance.lod1Range);
|
||||||
// Höhen vorab laden, damit getHeightAt() bereits hier (vor initialize()) korrekte Werte liefert
|
// Höhen vorab laden, damit getHeightAt() bereits hier (vor initialize()) korrekte Werte liefert
|
||||||
terrainChunkState.loadChunkHeights();
|
terrainChunkState.loadChunkHeights();
|
||||||
app.getStateManager().attach(terrainChunkState);
|
app.getStateManager().attach(terrainChunkState);
|
||||||
|
|||||||
@@ -10,48 +10,59 @@ import com.jme3.material.Material;
|
|||||||
import com.jme3.material.RenderState;
|
import com.jme3.material.RenderState;
|
||||||
import com.jme3.math.ColorRGBA;
|
import com.jme3.math.ColorRGBA;
|
||||||
import com.jme3.math.FastMath;
|
import com.jme3.math.FastMath;
|
||||||
|
import com.jme3.math.Quaternion;
|
||||||
import com.jme3.math.Vector3f;
|
import com.jme3.math.Vector3f;
|
||||||
import com.jme3.renderer.Camera;
|
import com.jme3.renderer.Camera;
|
||||||
import com.jme3.renderer.queue.RenderQueue;
|
import com.jme3.renderer.queue.RenderQueue;
|
||||||
import com.jme3.scene.Geometry;
|
import com.jme3.scene.Geometry;
|
||||||
|
import com.jme3.scene.Mesh;
|
||||||
import com.jme3.scene.Node;
|
import com.jme3.scene.Node;
|
||||||
import com.jme3.scene.Spatial;
|
import com.jme3.scene.VertexBuffer;
|
||||||
import com.jme3.scene.shape.Quad;
|
import com.jme3.util.BufferUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Kompass unten links. Zeigt N/O/S/W-Labels, die um das Kompasszentrum
|
* Kompass unten links – runde rotierende Rose, identisch mit dem Editor-Kompass.
|
||||||
* rotieren wenn sich die Kamera dreht. Eine feste Markierung oben
|
*
|
||||||
* zeigt immer die aktuelle Blickrichtung an.
|
* Die Rose dreht sich mit dem Kamera-Yaw; die Buchstaben konter-rotieren,
|
||||||
|
* damit sie immer aufrecht bleiben. Ein festes goldenes Dreieck oben zeigt
|
||||||
|
* die aktuelle Blickrichtung an.
|
||||||
*/
|
*/
|
||||||
public class CompassHudState extends BaseAppState {
|
public class CompassHudState extends BaseAppState {
|
||||||
|
|
||||||
// ── Layout ────────────────────────────────────────────────────────────────
|
// ── Layout ────────────────────────────────────────────────────────────────
|
||||||
private static final float SIZE = 76f; // Kompass-Durchmesser (Pixel)
|
private static final float SIZE = 100f; // Canvas-Größe (px)
|
||||||
private static final float RADIUS = 26f; // Label-Radius vom Mittelpunkt
|
private static final float RADIUS = 47f; // Kreis-Außenradius
|
||||||
private static final float MARGIN = 16f; // Abstand zum Bildschirmrand
|
private static final float LABEL_R = 30f; // Beschriftungsradius vom Mittelpunkt
|
||||||
private static final float LABEL_Z = 4f;
|
private static final float MARGIN = 16f; // Abstand zum Bildschirmrand
|
||||||
private static final float DOT_SZ = 6f; // Vorwärts-Marker
|
|
||||||
|
|
||||||
private static final ColorRGBA COL_BG = new ColorRGBA(0.06f, 0.06f, 0.10f, 0.88f);
|
// ── Farben (matching EditorApp.drawCompass) ───────────────────────────────
|
||||||
private static final ColorRGBA COL_BORDER = new ColorRGBA(0.30f, 0.30f, 0.44f, 1.00f);
|
private static final ColorRGBA COL_BG = new ColorRGBA(0.047f, 0.047f, 0.094f, 0.82f);
|
||||||
private static final ColorRGBA COL_N = new ColorRGBA(0.90f, 0.20f, 0.20f, 1.00f);
|
private static final ColorRGBA COL_BORDER = new ColorRGBA(0.392f, 0.392f, 0.627f, 0.85f);
|
||||||
private static final ColorRGBA COL_CARD = new ColorRGBA(0.75f, 0.75f, 0.78f, 1.00f);
|
private static final ColorRGBA COL_TICK_MJ = new ColorRGBA(0.627f, 0.627f, 0.784f, 0.65f);
|
||||||
private static final ColorRGBA COL_MARKER = new ColorRGBA(1.00f, 0.90f, 0.20f, 1.00f);
|
private static final ColorRGBA COL_TICK_MN = new ColorRGBA(0.627f, 0.627f, 0.784f, 0.45f);
|
||||||
|
private static final ColorRGBA COL_N = new ColorRGBA(1.00f, 0.314f, 0.314f, 1.00f);
|
||||||
|
private static final ColorRGBA COL_CARD = new ColorRGBA(0.824f, 0.824f, 0.902f, 1.00f);
|
||||||
|
private static final ColorRGBA COL_MARKER = new ColorRGBA(1.00f, 0.863f, 0.235f, 0.95f);
|
||||||
|
private static final ColorRGBA COL_CENTER = new ColorRGBA(0.784f, 0.784f, 0.941f, 0.85f);
|
||||||
|
|
||||||
// ── Himmelsrichtungen: Name, Winkel-Offset von Nord (Rad) ─────────────────
|
// Kompasswinkel (Grad von Nord CW): N=0, O=90, S=180, W=270
|
||||||
private static final String[] LABELS = {"N", "O", "S", "W"};
|
private static final String[] LABELS = {"N", "O", "S", "W"};
|
||||||
private static final float[] OFFSETS = {0f, FastMath.HALF_PI, FastMath.PI, -FastMath.HALF_PI};
|
private static final float[] LABEL_DEG = {0f, 90f, 180f, 270f};
|
||||||
|
|
||||||
private SimpleApplication app;
|
private SimpleApplication app;
|
||||||
private Camera cam;
|
private Camera cam;
|
||||||
private AssetManager assets;
|
private AssetManager assets;
|
||||||
private BitmapFont font;
|
private BitmapFont font;
|
||||||
private Node compassNode;
|
|
||||||
private float cx, cy; // Mittelpunkt (Bildschirmkoordinaten)
|
|
||||||
|
|
||||||
private final BitmapText[] dirLabels = new BitmapText[4];
|
private Node compassNode;
|
||||||
|
private Node roseNode;
|
||||||
|
|
||||||
|
private final Node[] labelNodes = new Node[4];
|
||||||
|
private final Quaternion roseRot = new Quaternion();
|
||||||
|
private final Quaternion labelRot = new Quaternion();
|
||||||
|
|
||||||
// ── Lifecycle ─────────────────────────────────────────────────────────────
|
// ── Lifecycle ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void initialize(Application application) {
|
protected void initialize(Application application) {
|
||||||
app = (SimpleApplication) application;
|
app = (SimpleApplication) application;
|
||||||
@@ -70,6 +81,7 @@ public class CompassHudState extends BaseAppState {
|
|||||||
if (compassNode != null) {
|
if (compassNode != null) {
|
||||||
app.getGuiNode().detachChild(compassNode);
|
app.getGuiNode().detachChild(compassNode);
|
||||||
compassNode = null;
|
compassNode = null;
|
||||||
|
roseNode = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,80 +89,173 @@ public class CompassHudState extends BaseAppState {
|
|||||||
protected void cleanup(Application application) {}
|
protected void cleanup(Application application) {}
|
||||||
|
|
||||||
// ── Update ────────────────────────────────────────────────────────────────
|
// ── Update ────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void update(float tpf) {
|
public void update(float tpf) {
|
||||||
if (compassNode == null) { return; }
|
if (compassNode == null) { return; }
|
||||||
|
|
||||||
Vector3f dir = cam.getDirection();
|
Vector3f dir = cam.getDirection();
|
||||||
// Yaw vom Weltursprung: 0 = Blick nach +Z (Nord), π/2 = Osten (+X)
|
// yaw=0 = Blick nach +Z (Nord); positiv = Osten
|
||||||
float yaw = FastMath.atan2(dir.x, dir.z);
|
float yaw = FastMath.atan2(dir.x, dir.z);
|
||||||
|
|
||||||
for (int i = 0; i < 4; i++) {
|
// Rose dreht sich so, dass Nord immer in der echten Nord-Richtung liegt
|
||||||
// Winkel des Labels im Kompass-Raum (von Oben CW = von +Y Achse)
|
roseRot.fromAngleAxis(-yaw, Vector3f.UNIT_Z);
|
||||||
float a = -yaw + OFFSETS[i];
|
roseNode.setLocalRotation(roseRot);
|
||||||
float lx = cx + RADIUS * FastMath.sin(a);
|
|
||||||
float ly = cy + RADIUS * FastMath.cos(a);
|
// Buchstaben konter-rotieren → bleiben immer aufrecht
|
||||||
BitmapText lbl = dirLabels[i];
|
labelRot.fromAngleAxis(yaw, Vector3f.UNIT_Z);
|
||||||
lbl.setLocalTranslation(lx - lbl.getLineWidth() * 0.5f, ly + lbl.getLineHeight() * 0.4f, LABEL_Z);
|
for (Node ln : labelNodes) {
|
||||||
|
ln.setLocalRotation(labelRot);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Aufbau ───────────────────────────────────────────────────────────────
|
// ── Aufbau ────────────────────────────────────────────────────────────────
|
||||||
private void buildCompass() {
|
|
||||||
float sw = cam.getWidth();
|
|
||||||
float sh = cam.getHeight();
|
|
||||||
|
|
||||||
// Kompass unten links
|
private void buildCompass() {
|
||||||
float ox = MARGIN;
|
|
||||||
// Direkt über der Hotbar: Hotbar.MARGIN_BOT + SLOT_SIZE + Gap
|
|
||||||
float oy = HotbarState.MARGIN_BOT + HotbarState.SLOT_SIZE + 8f;
|
float oy = HotbarState.MARGIN_BOT + HotbarState.SLOT_SIZE + 8f;
|
||||||
cx = ox + SIZE / 2f;
|
float cx = MARGIN + SIZE / 2f;
|
||||||
cy = oy + SIZE / 2f;
|
float cy = oy + SIZE / 2f;
|
||||||
|
|
||||||
compassNode = new Node("compass");
|
compassNode = new Node("compass");
|
||||||
|
compassNode.setLocalTranslation(cx, cy, 0f);
|
||||||
|
|
||||||
// Rahmen
|
// Rahmen (etwas größerer Kreis) + Hintergrund
|
||||||
compassNode.attachChild(makeQuad(ox, oy, SIZE, SIZE, COL_BORDER, 1f));
|
compassNode.attachChild(makeCircle(RADIUS + 1.5f, 64, COL_BORDER, 1f));
|
||||||
// Hintergrund
|
compassNode.attachChild(makeCircle(RADIUS, 64, COL_BG, 2f));
|
||||||
compassNode.attachChild(makeQuad(ox + 1, oy + 1, SIZE - 2, SIZE - 2, COL_BG, 2f));
|
|
||||||
|
|
||||||
// Vorwärts-Marker: kleine goldene Raute oben in der Mitte
|
// Rotierende Rose
|
||||||
compassNode.attachChild(makeQuad(cx - DOT_SZ * 0.5f, cy + RADIUS - DOT_SZ * 0.5f,
|
roseNode = new Node("rose");
|
||||||
DOT_SZ, DOT_SZ, COL_MARKER, 3f));
|
compassNode.attachChild(roseNode);
|
||||||
|
|
||||||
// Himmelsrichtungs-Labels (Positionen werden in update() gesetzt)
|
// 8 Tick-Striche (major = Hauptrichtungen, minor = Zwischenrichtungen)
|
||||||
for (int i = 0; i < 4; i++) {
|
for (int i = 0; i < 8; i++) {
|
||||||
ColorRGBA col = i == 0 ? COL_N : COL_CARD;
|
float deg = i * 45f;
|
||||||
BitmapText lbl = txt(LABELS[i], 13, col);
|
boolean maj = (i % 2 == 0);
|
||||||
lbl.setLocalTranslation(cx, cy, LABEL_Z);
|
float innerR = RADIUS - (maj ? 9f : 5f);
|
||||||
dirLabels[i] = lbl;
|
roseNode.attachChild(makeTickQuad(deg, innerR, maj ? COL_TICK_MJ : COL_TICK_MN, 3f));
|
||||||
compassNode.attachChild(lbl);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Himmelsrichtungs-Beschriftungen
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
float ca = LABEL_DEG[i] * FastMath.DEG_TO_RAD;
|
||||||
|
float lx = FastMath.sin(ca) * LABEL_R;
|
||||||
|
float ly = FastMath.cos(ca) * LABEL_R;
|
||||||
|
|
||||||
|
BitmapText lbl = makeBitmapText(LABELS[i], i == 0 ? 14 : 12, i == 0 ? COL_N : COL_CARD);
|
||||||
|
// Buchstabe am Label-Ursprung zentrieren
|
||||||
|
lbl.setLocalTranslation(-lbl.getLineWidth() * 0.5f, lbl.getLineHeight() * 0.4f, 0f);
|
||||||
|
|
||||||
|
Node ln = new Node("lbl_" + LABELS[i]);
|
||||||
|
ln.setLocalTranslation(lx, ly, 4f);
|
||||||
|
ln.attachChild(lbl);
|
||||||
|
labelNodes[i] = ln;
|
||||||
|
roseNode.attachChild(ln);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fixer Richtungs-Zeiger: gelbes Dreieck oben (nicht in roseNode)
|
||||||
|
compassNode.attachChild(makeTriangle(COL_MARKER, 5f));
|
||||||
|
|
||||||
|
// Mittelpunkt-Punkt
|
||||||
|
compassNode.attachChild(makeCircle(2.5f, 16, COL_CENTER, 6f));
|
||||||
|
|
||||||
app.getGuiNode().attachChild(compassNode);
|
app.getGuiNode().attachChild(compassNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Hilfsmethoden ────────────────────────────────────────────────────────
|
// ── Geometry-Helfer ───────────────────────────────────────────────────────
|
||||||
private Geometry makeQuad(float x, float y, float w, float h, ColorRGBA col, float z) {
|
|
||||||
Geometry g = new Geometry("q", new Quad(w, h));
|
/** Gefüllter Kreis (Triangle-Fan) zentriert bei (0,0). */
|
||||||
|
private Geometry makeCircle(float r, int segs, ColorRGBA col, float z) {
|
||||||
|
float[] verts = new float[(segs + 2) * 3];
|
||||||
|
// Mittelpunkt
|
||||||
|
verts[0] = 0f; verts[1] = 0f; verts[2] = 0f;
|
||||||
|
for (int i = 0; i <= segs; i++) {
|
||||||
|
float a = FastMath.TWO_PI * i / segs;
|
||||||
|
int base = (i + 1) * 3;
|
||||||
|
verts[base] = FastMath.cos(a) * r;
|
||||||
|
verts[base + 1] = FastMath.sin(a) * r;
|
||||||
|
verts[base + 2] = 0f;
|
||||||
|
}
|
||||||
|
int[] idx = new int[segs * 3];
|
||||||
|
for (int i = 0; i < segs; i++) {
|
||||||
|
idx[i * 3] = 0;
|
||||||
|
idx[i * 3 + 1] = i + 1;
|
||||||
|
idx[i * 3 + 2] = i + 2;
|
||||||
|
}
|
||||||
|
Geometry g = new Geometry("circle", buildMesh(verts, idx));
|
||||||
|
g.setMaterial(makeMat(col));
|
||||||
|
g.setLocalTranslation(0f, 0f, z);
|
||||||
|
g.setQueueBucket(col.a < 1f ? RenderQueue.Bucket.Transparent : RenderQueue.Bucket.Gui);
|
||||||
|
return g;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tick-Strich als dünnes Quad entlang der radialen Richtung.
|
||||||
|
* ca_deg: Kompasswinkel (0=Nord, 90=Ost, CW).
|
||||||
|
*/
|
||||||
|
private Geometry makeTickQuad(float ca_deg, float innerR, ColorRGBA col, float z) {
|
||||||
|
float ca = ca_deg * FastMath.DEG_TO_RAD;
|
||||||
|
float sx = FastMath.sin(ca); // Richtung entlang des Radius
|
||||||
|
float cy_ = FastMath.cos(ca);
|
||||||
|
float px = -cy_ * 0.75f; // Halbe Breite (senkrecht zur Radiale)
|
||||||
|
float py = sx * 0.75f;
|
||||||
|
|
||||||
|
float[] v = {
|
||||||
|
sx * innerR + px, cy_ * innerR + py, z, // v0 links innen
|
||||||
|
sx * innerR - px, cy_ * innerR - py, z, // v1 rechts innen
|
||||||
|
sx * RADIUS + px, cy_ * RADIUS + py, z, // v2 links außen
|
||||||
|
sx * RADIUS - px, cy_ * RADIUS - py, z // v3 rechts außen
|
||||||
|
};
|
||||||
|
int[] idx = {0, 1, 2, 1, 3, 2};
|
||||||
|
|
||||||
|
Geometry g = new Geometry("tick", buildMesh(v, idx));
|
||||||
|
g.setMaterial(makeMat(col));
|
||||||
|
g.setQueueBucket(RenderQueue.Bucket.Transparent);
|
||||||
|
return g;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Festes gelbes Dreieck – zeigt immer nach oben (Blickrichtung).
|
||||||
|
* Spitze bei (0, RADIUS−5), Basis bei (±5, RADIUS−16).
|
||||||
|
*/
|
||||||
|
private Geometry makeTriangle(ColorRGBA col, float z) {
|
||||||
|
float tipY = RADIUS - 5f;
|
||||||
|
float baseY = RADIUS - 16f;
|
||||||
|
float[] v = {
|
||||||
|
0f, tipY, 0f, // Spitze
|
||||||
|
-5f, baseY, 0f, // Basis links
|
||||||
|
5f, baseY, 0f // Basis rechts
|
||||||
|
};
|
||||||
|
// CCW von +Z-Seite: (0,high)→(-5,low)→(5,low) ✓
|
||||||
|
int[] idx = {0, 1, 2};
|
||||||
|
Geometry g = new Geometry("triangle", buildMesh(v, idx));
|
||||||
|
g.setMaterial(makeMat(col));
|
||||||
|
g.setLocalTranslation(0f, 0f, z);
|
||||||
|
g.setQueueBucket(RenderQueue.Bucket.Transparent);
|
||||||
|
return g;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Mesh buildMesh(float[] verts, int[] idx) {
|
||||||
|
Mesh m = new Mesh();
|
||||||
|
m.setBuffer(VertexBuffer.Type.Position, 3, BufferUtils.createFloatBuffer(verts));
|
||||||
|
m.setBuffer(VertexBuffer.Type.Index, 3, BufferUtils.createIntBuffer(idx));
|
||||||
|
m.updateBound();
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Material makeMat(ColorRGBA col) {
|
||||||
Material m = new Material(assets, "Common/MatDefs/Misc/Unshaded.j3md");
|
Material m = new Material(assets, "Common/MatDefs/Misc/Unshaded.j3md");
|
||||||
m.setColor("Color", col.clone());
|
m.setColor("Color", col.clone());
|
||||||
if (col.a < 1f) {
|
if (col.a < 1f) {
|
||||||
m.getAdditionalRenderState().setBlendMode(RenderState.BlendMode.Alpha);
|
m.getAdditionalRenderState().setBlendMode(RenderState.BlendMode.Alpha);
|
||||||
g.setQueueBucket(RenderQueue.Bucket.Transparent);
|
|
||||||
} else {
|
|
||||||
g.setQueueBucket(RenderQueue.Bucket.Gui);
|
|
||||||
}
|
}
|
||||||
g.setMaterial(m);
|
return m;
|
||||||
g.setLocalTranslation(x, y, z);
|
|
||||||
return g;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private BitmapText txt(String s, int size, ColorRGBA col) {
|
private BitmapText makeBitmapText(String text, int size, ColorRGBA col) {
|
||||||
BitmapText t = new BitmapText(font);
|
BitmapText t = new BitmapText(font);
|
||||||
t.setSize(size);
|
t.setSize(size);
|
||||||
t.setColor(col);
|
t.setColor(col.clone());
|
||||||
t.setText(s);
|
t.setText(text);
|
||||||
t.setQueueBucket(RenderQueue.Bucket.Gui);
|
t.setQueueBucket(RenderQueue.Bucket.Gui);
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
package de.blight.game.state;
|
package de.blight.game.state;
|
||||||
|
|
||||||
import com.jme3.asset.AssetManager;
|
import com.jme3.asset.AssetManager;
|
||||||
|
import com.jme3.math.Vector3f;
|
||||||
import com.jme3.renderer.Camera;
|
import com.jme3.renderer.Camera;
|
||||||
import com.jme3.renderer.RenderManager;
|
import com.jme3.renderer.RenderManager;
|
||||||
import com.jme3.renderer.ViewPort;
|
import com.jme3.renderer.ViewPort;
|
||||||
import com.jme3.scene.Node;
|
import com.jme3.scene.Node;
|
||||||
import com.jme3.scene.Spatial;
|
import com.jme3.scene.Spatial;
|
||||||
import com.jme3.scene.control.AbstractControl;
|
import com.jme3.scene.control.AbstractControl;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wechselt zwischen LOD-Stufen und blendet Objekte ab einer Sichtweite aus.
|
* Wechselt zwischen LOD-Stufen und blendet Objekte ab einer Sichtweite aus.
|
||||||
@@ -26,6 +29,8 @@ import com.jme3.scene.control.AbstractControl;
|
|||||||
*/
|
*/
|
||||||
public class ModelLodControl extends AbstractControl {
|
public class ModelLodControl extends AbstractControl {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(ModelLodControl.class);
|
||||||
|
|
||||||
private final Camera cam;
|
private final Camera cam;
|
||||||
private final float lod1DistSq;
|
private final float lod1DistSq;
|
||||||
private final float lod2DistSq;
|
private final float lod2DistSq;
|
||||||
@@ -93,9 +98,19 @@ public class ModelLodControl extends AbstractControl {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void controlUpdate(float tpf) {
|
protected void controlUpdate(float tpf) {
|
||||||
float dx = cam.getLocation().x - spatial.getWorldTranslation().x;
|
// getWorldTranslation() liefert auf dem ersten Frame (0,0,0) solange updateGeometricState()
|
||||||
float dy = cam.getLocation().y - spatial.getWorldTranslation().y;
|
// noch nicht gelaufen ist. Deshalb: wenn Translation noch (0,0,0) ist und der spatial
|
||||||
float dz = cam.getLocation().z - spatial.getWorldTranslation().z;
|
// eine LocalTranslation hat, die verschieden ist, überspringen wir diesen Frame.
|
||||||
|
Vector3f wt = spatial.getWorldTranslation();
|
||||||
|
Vector3f lt = spatial.getLocalTranslation();
|
||||||
|
if (wt.x == 0f && wt.y == 0f && wt.z == 0f
|
||||||
|
&& (lt.x != 0f || lt.y != 0f || lt.z != 0f)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
float dx = cam.getLocation().x - wt.x;
|
||||||
|
float dy = cam.getLocation().y - wt.y;
|
||||||
|
float dz = cam.getLocation().z - wt.z;
|
||||||
float distSq = dx*dx + dy*dy + dz*dz;
|
float distSq = dx*dx + dy*dy + dz*dz;
|
||||||
|
|
||||||
boolean embedded = embLod0 != null;
|
boolean embedded = embLod0 != null;
|
||||||
@@ -114,6 +129,9 @@ public class ModelLodControl extends AbstractControl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (targetSlot == currentSlot) return;
|
if (targetSlot == currentSlot) return;
|
||||||
|
log.debug("[LOD] {} Slot {} → {} (dist={}m)",
|
||||||
|
spatial.getName(), currentSlot, targetSlot,
|
||||||
|
String.format("%.1f", Math.sqrt(distSq)));
|
||||||
currentSlot = targetSlot;
|
currentSlot = targetSlot;
|
||||||
|
|
||||||
if (targetSlot == -1) {
|
if (targetSlot == -1) {
|
||||||
|
|||||||
@@ -72,9 +72,19 @@ public class TerrainChunkState extends BaseAppState {
|
|||||||
private final RigidBodyControl[] physics = new RigidBodyControl[TOTAL];
|
private final RigidBodyControl[] physics = new RigidBodyControl[TOTAL];
|
||||||
private final List<ChunkListener> listeners = new ArrayList<>();
|
private final List<ChunkListener> listeners = new ArrayList<>();
|
||||||
|
|
||||||
|
private int lod0Range = ChunkTerrainIO.LOD0_RANGE;
|
||||||
|
private int lod1Range = ChunkTerrainIO.LOD1_RANGE;
|
||||||
|
|
||||||
private int lastPlayerCx = Integer.MIN_VALUE;
|
private int lastPlayerCx = Integer.MIN_VALUE;
|
||||||
private int lastPlayerCz = Integer.MIN_VALUE;
|
private int lastPlayerCz = Integer.MIN_VALUE;
|
||||||
|
|
||||||
|
public void setLodRanges(int lod0, int lod1) {
|
||||||
|
this.lod0Range = lod0;
|
||||||
|
this.lod1Range = lod1;
|
||||||
|
lastPlayerCx = Integer.MIN_VALUE;
|
||||||
|
lastPlayerCz = Integer.MIN_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
/** Beim nächsten update() diese Position statt Kamera-Position verwenden (einmalig). */
|
/** Beim nächsten update() diese Position statt Kamera-Position verwenden (einmalig). */
|
||||||
private float spawnHintX = Float.NaN;
|
private float spawnHintX = Float.NaN;
|
||||||
private float spawnHintZ = Float.NaN;
|
private float spawnHintZ = Float.NaN;
|
||||||
@@ -184,7 +194,7 @@ public class TerrainChunkState extends BaseAppState {
|
|||||||
for (int cx = 0; cx < N; cx++) {
|
for (int cx = 0; cx < N; cx++) {
|
||||||
int ci = ChunkTerrainIO.chunkIndex(cx, cz);
|
int ci = ChunkTerrainIO.chunkIndex(cx, cz);
|
||||||
int dist = ChunkTerrainIO.chebyshev(cx, cz, pcx, pcz);
|
int dist = ChunkTerrainIO.chebyshev(cx, cz, pcx, pcz);
|
||||||
targetLod[ci] = ChunkTerrainIO.lodForDistance(dist);
|
targetLod[ci] = dist <= lod0Range ? 0 : dist <= lod1Range ? 1 : 2;
|
||||||
dirty[ci] = targetLod[ci] != chunkLod[ci];
|
dirty[ci] = targetLod[ci] != chunkLod[ci];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import com.jme3.math.ColorRGBA;
|
|||||||
import com.jme3.math.FastMath;
|
import com.jme3.math.FastMath;
|
||||||
import com.jme3.math.Vector2f;
|
import com.jme3.math.Vector2f;
|
||||||
import com.jme3.math.Vector3f;
|
import com.jme3.math.Vector3f;
|
||||||
import com.jme3.post.filters.FogFilter;
|
import de.blight.game.post.BlightFogFilter;
|
||||||
import com.jme3.water.WaterFilter;
|
import com.jme3.water.WaterFilter;
|
||||||
import jme3utilities.sky.SkyControl;
|
import jme3utilities.sky.SkyControl;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@@ -16,37 +16,40 @@ public class WeatherState extends BaseAppState {
|
|||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(WeatherState.class);
|
private static final Logger log = LoggerFactory.getLogger(WeatherState.class);
|
||||||
|
|
||||||
public enum Weather { SUNNY, CLOUDY, OVERCAST, STORM }
|
public enum Weather { SUNNY, CLOUDY, OVERCAST, STORM, FOG }
|
||||||
|
|
||||||
// ── Per-weather targets (Reihenfolge: SUNNY, CLOUDY, OVERCAST, STORM) ─────
|
// ── Per-weather targets (Reihenfolge: SUNNY, CLOUDY, OVERCAST, STORM, FOG) ─
|
||||||
|
|
||||||
private static final float[] FOG_DENSITY = { 0.00f, 0.30f, 0.65f, 0.90f };
|
private static final float[] FOG_DENSITY = { 0.40f, 0.55f, 0.75f, 0.90f, 0.88f };
|
||||||
private static final float[] FOG_DISTANCE = { 600f, 350f, 140f, 50f };
|
private static final float[] FOG_DISTANCE = { 600f, 350f, 140f, 50f, 80f };
|
||||||
private static final float[] WIND_SPEED = { 4f, 14f, 26f, 55f };
|
private static final float[] WIND_SPEED = { 4f, 14f, 26f, 55f, 3f };
|
||||||
private static final float[] WAVE_SPEED = { 0.5f, 1.0f, 1.5f, 3.2f };
|
private static final float[] WAVE_SPEED = { 0.5f, 1.0f, 1.5f, 3.2f, 0.4f };
|
||||||
private static final float[] WAVE_AMP = { 0.3f, 0.5f, 0.8f, 1.8f };
|
private static final float[] WAVE_AMP = { 0.3f, 0.5f, 0.8f, 1.8f, 0.2f };
|
||||||
private static final float[] WAVE_SCALE = { 0.008f, 0.007f, 0.006f, 0.005f};
|
private static final float[] WAVE_SCALE = { 0.008f, 0.007f, 0.006f, 0.005f, 0.009f};
|
||||||
private static final float[] WATER_TRANS = { 0.15f, 0.10f, 0.07f, 0.02f };
|
private static final float[] WATER_TRANS = { 0.15f, 0.10f, 0.07f, 0.02f, 0.12f };
|
||||||
private static final float[] FOAM_INTENSITY= { 0.0f, 0.20f, 0.45f, 0.90f };
|
private static final float[] FOAM_INTENSITY= { 0.0f, 0.20f, 0.45f, 0.90f, 0.0f };
|
||||||
private static final float[] CLOUD_OPACITY = { 0.0f, 0.40f, 0.80f, 1.00f };
|
private static final float[] CLOUD_OPACITY = { 0.0f, 0.40f, 0.80f, 1.00f, 0.95f };
|
||||||
|
|
||||||
private static final ColorRGBA[] FOG_COLOR = {
|
private static final ColorRGBA[] FOG_COLOR = {
|
||||||
new ColorRGBA(0.75f, 0.80f, 0.88f, 1f),
|
new ColorRGBA(0.75f, 0.80f, 0.88f, 1f),
|
||||||
new ColorRGBA(0.62f, 0.65f, 0.70f, 1f),
|
new ColorRGBA(0.62f, 0.65f, 0.70f, 1f),
|
||||||
new ColorRGBA(0.42f, 0.43f, 0.46f, 1f),
|
new ColorRGBA(0.42f, 0.43f, 0.46f, 1f),
|
||||||
new ColorRGBA(0.18f, 0.19f, 0.21f, 1f),
|
new ColorRGBA(0.18f, 0.19f, 0.21f, 1f),
|
||||||
|
new ColorRGBA(0.70f, 0.72f, 0.75f, 1f),
|
||||||
};
|
};
|
||||||
private static final ColorRGBA[] WATER_COLOR = {
|
private static final ColorRGBA[] WATER_COLOR = {
|
||||||
new ColorRGBA(0.05f, 0.25f, 0.55f, 1f),
|
new ColorRGBA(0.05f, 0.25f, 0.55f, 1f),
|
||||||
new ColorRGBA(0.04f, 0.18f, 0.42f, 1f),
|
new ColorRGBA(0.04f, 0.18f, 0.42f, 1f),
|
||||||
new ColorRGBA(0.03f, 0.12f, 0.28f, 1f),
|
new ColorRGBA(0.03f, 0.12f, 0.28f, 1f),
|
||||||
new ColorRGBA(0.02f, 0.06f, 0.14f, 1f),
|
new ColorRGBA(0.02f, 0.06f, 0.14f, 1f),
|
||||||
|
new ColorRGBA(0.04f, 0.20f, 0.45f, 1f),
|
||||||
};
|
};
|
||||||
private static final ColorRGBA[] DEEP_WATER_COLOR = {
|
private static final ColorRGBA[] DEEP_WATER_COLOR = {
|
||||||
new ColorRGBA(0.02f, 0.12f, 0.30f, 1f),
|
new ColorRGBA(0.02f, 0.12f, 0.30f, 1f),
|
||||||
new ColorRGBA(0.01f, 0.08f, 0.20f, 1f),
|
new ColorRGBA(0.01f, 0.08f, 0.20f, 1f),
|
||||||
new ColorRGBA(0.01f, 0.04f, 0.12f, 1f),
|
new ColorRGBA(0.01f, 0.04f, 0.12f, 1f),
|
||||||
new ColorRGBA(0.00f, 0.02f, 0.06f, 1f),
|
new ColorRGBA(0.00f, 0.02f, 0.06f, 1f),
|
||||||
|
new ColorRGBA(0.01f, 0.09f, 0.23f, 1f),
|
||||||
};
|
};
|
||||||
|
|
||||||
// ── State ────────────────────────────────────────────────────────────────
|
// ── State ────────────────────────────────────────────────────────────────
|
||||||
@@ -54,7 +57,7 @@ public class WeatherState extends BaseAppState {
|
|||||||
private Weather active = Weather.SUNNY;
|
private Weather active = Weather.SUNNY;
|
||||||
private float changeTimer = 120f;
|
private float changeTimer = 120f;
|
||||||
|
|
||||||
private float fogDensity = 0f;
|
private float fogDensity = 0.40f; // Startwert = SUNNY-Ziel, kein langsames Fade-in
|
||||||
private float fogDistance = 600f;
|
private float fogDistance = 600f;
|
||||||
private float windSpeed = 4f;
|
private float windSpeed = 4f;
|
||||||
private float waveSpeed = 0.5f;
|
private float waveSpeed = 0.5f;
|
||||||
@@ -69,13 +72,19 @@ public class WeatherState extends BaseAppState {
|
|||||||
private final ColorRGBA waterColor = new ColorRGBA(0.05f, 0.25f, 0.55f, 1f);
|
private final ColorRGBA waterColor = new ColorRGBA(0.05f, 0.25f, 0.55f, 1f);
|
||||||
private final ColorRGBA deepWaterColor= new ColorRGBA(0.02f, 0.12f, 0.30f, 1f);
|
private final ColorRGBA deepWaterColor= new ColorRGBA(0.02f, 0.12f, 0.30f, 1f);
|
||||||
|
|
||||||
|
// ── Sichtweiten-Faktor (aus Grafikeinstellungen) ──────────────────────────
|
||||||
|
|
||||||
|
private float viewDistanceFactor = 1.0f;
|
||||||
|
|
||||||
|
public void setViewDistanceFactor(float f) { this.viewDistanceFactor = f; }
|
||||||
|
|
||||||
// ── Externe Referenzen ────────────────────────────────────────────────────
|
// ── Externe Referenzen ────────────────────────────────────────────────────
|
||||||
|
|
||||||
private FogFilter fogFilter;
|
private BlightFogFilter fogFilter;
|
||||||
private WaterFilter waterFilter;
|
private WaterFilter waterFilter;
|
||||||
private SkyControl skyControl;
|
private SkyControl skyControl;
|
||||||
|
|
||||||
public void setFogFilter(FogFilter f) { this.fogFilter = f; }
|
public void setFogFilter(BlightFogFilter f) { this.fogFilter = f; }
|
||||||
public void setWaterFilter(WaterFilter f) { this.waterFilter = f; }
|
public void setWaterFilter(WaterFilter f) { this.waterFilter = f; }
|
||||||
public void setSkyControl(SkyControl sc) { this.skyControl = sc; }
|
public void setSkyControl(SkyControl sc) { this.skyControl = sc; }
|
||||||
|
|
||||||
@@ -114,8 +123,8 @@ public class WeatherState extends BaseAppState {
|
|||||||
|
|
||||||
int i = active.ordinal();
|
int i = active.ordinal();
|
||||||
|
|
||||||
fogDensity = approach(fogDensity, FOG_DENSITY[i], tpf * 0.025f);
|
fogDensity = approach(fogDensity, FOG_DENSITY[i], tpf * 0.025f);
|
||||||
fogDistance = approach(fogDistance, FOG_DISTANCE[i], tpf * 0.025f);
|
fogDistance = approach(fogDistance, FOG_DISTANCE[i] * viewDistanceFactor, tpf * 0.025f);
|
||||||
windSpeed = approach(windSpeed, WIND_SPEED[i], tpf * 0.040f);
|
windSpeed = approach(windSpeed, WIND_SPEED[i], tpf * 0.040f);
|
||||||
waveSpeed = approach(waveSpeed, WAVE_SPEED[i], tpf * 0.030f);
|
waveSpeed = approach(waveSpeed, WAVE_SPEED[i], tpf * 0.030f);
|
||||||
waveAmp = approach(waveAmp, WAVE_AMP[i], tpf * 0.025f);
|
waveAmp = approach(waveAmp, WAVE_AMP[i], tpf * 0.025f);
|
||||||
@@ -156,10 +165,11 @@ public class WeatherState extends BaseAppState {
|
|||||||
|
|
||||||
private void pickNextWeather() {
|
private void pickNextWeather() {
|
||||||
float r = FastMath.nextRandomFloat();
|
float r = FastMath.nextRandomFloat();
|
||||||
Weather next = r < 0.40f ? Weather.SUNNY
|
Weather next = r < 0.38f ? Weather.SUNNY
|
||||||
: r < 0.70f ? Weather.CLOUDY
|
: r < 0.65f ? Weather.CLOUDY
|
||||||
: r < 0.88f ? Weather.OVERCAST
|
: r < 0.82f ? Weather.OVERCAST
|
||||||
: Weather.STORM;
|
: r < 0.92f ? Weather.STORM
|
||||||
|
: Weather.FOG;
|
||||||
windAngleTgt = FastMath.nextRandomFloat() * FastMath.TWO_PI;
|
windAngleTgt = FastMath.nextRandomFloat() * FastMath.TWO_PI;
|
||||||
if (next != active) {
|
if (next != active) {
|
||||||
active = next;
|
active = next;
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import com.jme3.material.RenderState;
|
|||||||
import com.jme3.math.*;
|
import com.jme3.math.*;
|
||||||
import com.jme3.renderer.queue.RenderQueue;
|
import com.jme3.renderer.queue.RenderQueue;
|
||||||
import com.jme3.scene.*;
|
import com.jme3.scene.*;
|
||||||
|
import com.jme3.scene.control.BillboardControl;
|
||||||
import com.jme3.scene.shape.*;
|
import com.jme3.scene.shape.*;
|
||||||
import com.jme3.texture.Texture;
|
import com.jme3.texture.Texture;
|
||||||
import de.blight.common.PlacedModel;
|
import de.blight.common.PlacedModel;
|
||||||
@@ -38,6 +39,9 @@ public class WorldObjectsState extends BaseAppState {
|
|||||||
private final List<Material> windMaterials = new ArrayList<>();
|
private final List<Material> windMaterials = new ArrayList<>();
|
||||||
private final List<Material> pbrMaterials = new ArrayList<>();
|
private final List<Material> pbrMaterials = new ArrayList<>();
|
||||||
private float debugTimer = 0f;
|
private float debugTimer = 0f;
|
||||||
|
private float lodFactor = 1.0f;
|
||||||
|
|
||||||
|
public void setLodFactor(float f) { this.lodFactor = f; }
|
||||||
|
|
||||||
/** RigidBodyControl pro Interactable-ID, damit Kollision während Animationen deaktiviert werden kann. */
|
/** RigidBodyControl pro Interactable-ID, damit Kollision während Animationen deaktiviert werden kann. */
|
||||||
private final Map<String, RigidBodyControl> interactableRbcs = new HashMap<>();
|
private final Map<String, RigidBodyControl> interactableRbcs = new HashMap<>();
|
||||||
@@ -192,22 +196,41 @@ public class WorldObjectsState extends BaseAppState {
|
|||||||
|
|
||||||
// LOD / Distance-Culling
|
// LOD / Distance-Culling
|
||||||
if (m.cullDistance() > 0f) {
|
if (m.cullDistance() > 0f) {
|
||||||
// Embedded-LOD: j3o enthält bereits lod0/lod1/lod2 Child-Nodes (TreeGenerator).
|
|
||||||
// TreeLodControl im j3o ist editor-only und wird im Spiel nicht deserialisiert —
|
|
||||||
// wir übernehmen die LOD-Steuerung selbst, direkt am j3o-Root-Node.
|
|
||||||
if (spatial instanceof Node treeNode) {
|
if (spatial instanceof Node treeNode) {
|
||||||
Spatial lod0 = treeNode.getChild("lod0");
|
Spatial lod0 = treeNode.getChild("lod0");
|
||||||
Spatial lod1 = treeNode.getChild("lod1");
|
Spatial lod1 = treeNode.getChild("lod1");
|
||||||
Spatial lod2 = treeNode.getChild("lod2");
|
Spatial lod2 = treeNode.getChild("lod2");
|
||||||
|
|
||||||
|
// Fall A: j3o hat explizite lod0/lod1/lod2 Child-Nodes.
|
||||||
if (lod0 != null) {
|
if (lod0 != null) {
|
||||||
lod0.setCullHint(Spatial.CullHint.Inherit);
|
lod0.setCullHint(Spatial.CullHint.Inherit);
|
||||||
if (lod1 != null) lod1.setCullHint(Spatial.CullHint.Always);
|
if (lod1 != null) lod1.setCullHint(Spatial.CullHint.Always);
|
||||||
if (lod2 != null) lod2.setCullHint(Spatial.CullHint.Always);
|
if (lod2 != null) lod2.setCullHint(Spatial.CullHint.Always);
|
||||||
treeNode.addControl(new ModelLodControl(
|
treeNode.addControl(new ModelLodControl(
|
||||||
app.getCamera(), lod0, lod1, lod2,
|
app.getCamera(), lod0, lod1, lod2,
|
||||||
m.lod1Distance(), m.lod2Distance(), m.cullDistance()));
|
m.lod1Distance() * lodFactor, m.lod2Distance() * lodFactor, m.cullDistance() * lodFactor));
|
||||||
return treeNode;
|
return treeNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fall B: j3o hat nur "lod2" als Kind — Root ist lod0, lod2 ist das vereinfachte Mesh.
|
||||||
|
// lod2 aus dem Root ausgliedern, damit beide unabhängig ein/ausgeblendet werden können.
|
||||||
|
if (lod2 != null) {
|
||||||
|
treeNode.detachChild(lod2);
|
||||||
|
Node lodRoot = new Node("lodRoot_" + path);
|
||||||
|
lodRoot.attachChild(treeNode); // index 0 = lod0 (volle Qualität)
|
||||||
|
lodRoot.attachChild(lod2); // index 1 = lod2 (Impostor-Karte)
|
||||||
|
treeNode.setCullHint(Spatial.CullHint.Inherit);
|
||||||
|
lod2.setCullHint(Spatial.CullHint.Always);
|
||||||
|
// Billboard: Impostor-Karte dreht sich immer zur Kamera,
|
||||||
|
// unabhängig von der Baum-Rotation im BLO.
|
||||||
|
BillboardControl bc = new BillboardControl();
|
||||||
|
bc.setAlignment(BillboardControl.Alignment.Camera);
|
||||||
|
lod2.addControl(bc);
|
||||||
|
lodRoot.addControl(new ModelLodControl(
|
||||||
|
app.getCamera(), treeNode, null, lod2,
|
||||||
|
m.lod1Distance() * lodFactor, m.lod2Distance() * lodFactor, m.cullDistance() * lodFactor));
|
||||||
|
return lodRoot;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback: externe LOD-Dateien (lod1Path / lod2Path)
|
// Fallback: externe LOD-Dateien (lod1Path / lod2Path)
|
||||||
@@ -216,7 +239,7 @@ public class WorldObjectsState extends BaseAppState {
|
|||||||
ModelLodControl ctrl = new ModelLodControl(
|
ModelLodControl ctrl = new ModelLodControl(
|
||||||
assets, app.getCamera(),
|
assets, app.getCamera(),
|
||||||
m.lod1Path(), m.lod2Path(),
|
m.lod1Path(), m.lod2Path(),
|
||||||
m.lod1Distance(), m.lod2Distance(), m.cullDistance());
|
m.lod1Distance() * lodFactor, m.lod2Distance() * lodFactor, m.cullDistance() * lodFactor);
|
||||||
ctrl.setLodLoadCallback(lod -> {
|
ctrl.setLodLoadCallback(lod -> {
|
||||||
convertUnshadedToPbr(lod);
|
convertUnshadedToPbr(lod);
|
||||||
collectSceneLitMaterials(lod);
|
collectSceneLitMaterials(lod);
|
||||||
|
|||||||
@@ -48,8 +48,12 @@ menu.graphics.row.resolution=Auflösung
|
|||||||
menu.graphics.row.fullscreen=Vollbild
|
menu.graphics.row.fullscreen=Vollbild
|
||||||
menu.graphics.row.vsync=VSync
|
menu.graphics.row.vsync=VSync
|
||||||
menu.graphics.row.aa=Kantenglättung
|
menu.graphics.row.aa=Kantenglättung
|
||||||
|
menu.graphics.row.viewdist=Sichtweite
|
||||||
menu.graphics.val.on=An
|
menu.graphics.val.on=An
|
||||||
menu.graphics.val.off=Aus
|
menu.graphics.val.off=Aus
|
||||||
|
menu.graphics.val.viewdist.short=Kurz
|
||||||
|
menu.graphics.val.viewdist.normal=Normal
|
||||||
|
menu.graphics.val.viewdist.far=Weit
|
||||||
menu.graphics.btn.apply=Übernehmen
|
menu.graphics.btn.apply=Übernehmen
|
||||||
menu.graphics.btn.cancel=Abbrechen
|
menu.graphics.btn.cancel=Abbrechen
|
||||||
|
|
||||||
|
|||||||
@@ -51,8 +51,12 @@ menu.graphics.row.resolution=Resolution
|
|||||||
menu.graphics.row.fullscreen=Fullscreen
|
menu.graphics.row.fullscreen=Fullscreen
|
||||||
menu.graphics.row.vsync=VSync
|
menu.graphics.row.vsync=VSync
|
||||||
menu.graphics.row.aa=Anti-aliasing
|
menu.graphics.row.aa=Anti-aliasing
|
||||||
|
menu.graphics.row.viewdist=View Distance
|
||||||
menu.graphics.val.on=On
|
menu.graphics.val.on=On
|
||||||
menu.graphics.val.off=Off
|
menu.graphics.val.off=Off
|
||||||
|
menu.graphics.val.viewdist.short=Short
|
||||||
|
menu.graphics.val.viewdist.normal=Normal
|
||||||
|
menu.graphics.val.viewdist.far=Far
|
||||||
menu.graphics.btn.apply=Apply
|
menu.graphics.btn.apply=Apply
|
||||||
menu.graphics.btn.cancel=Cancel
|
menu.graphics.btn.cancel=Cancel
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
Reference in New Issue
Block a user