Am Lightning System gearbeitet
This commit is contained in:
@@ -1137,7 +1137,18 @@ public class EditorApp extends Application {
|
||||
viewMenu.getItems().addAll(resetCam, new SeparatorMenuItem(), viewTexture, viewWireframe,
|
||||
new SeparatorMenuItem(), viewTopology);
|
||||
|
||||
menuBar.getMenus().addAll(fileMenu, toolsMenu, viewMenu);
|
||||
Menu zeitMenu = new Menu("Zeit");
|
||||
ToggleGroup zeitGroup = new ToggleGroup();
|
||||
int[] hours = {0, 3, 6, 9, 12, 15, 18, 21};
|
||||
for (int h : hours) {
|
||||
RadioMenuItem item = new RadioMenuItem(String.format("%02d:00", h));
|
||||
item.setToggleGroup(zeitGroup);
|
||||
if (h == 12) { item.setSelected(true); }
|
||||
final float t = h / 24f;
|
||||
item.setOnAction(e -> input.pendingTimeOfDay = t);
|
||||
zeitMenu.getItems().add(item);
|
||||
}
|
||||
menuBar.getMenus().addAll(fileMenu, toolsMenu, viewMenu, zeitMenu);
|
||||
|
||||
ToolBar toolBar = new ToolBar();
|
||||
baseBtn = new ToggleButton("▲▼ Basis-Terrain");
|
||||
|
||||
@@ -309,6 +309,17 @@ public class JmeEditorApp extends SimpleApplication {
|
||||
input.camPitch = (float) Math.toDegrees(
|
||||
Math.asin(Math.max(-1f, Math.min(1f, dir.y))));
|
||||
|
||||
// Zeit-Menü: Tageszeit setzen
|
||||
float pendingTime = input.pendingTimeOfDay;
|
||||
if (!Float.isNaN(pendingTime)) {
|
||||
input.pendingTimeOfDay = Float.NaN;
|
||||
DayNightState dns = stateManager.getState(DayNightState.class);
|
||||
if (dns != null) {
|
||||
dns.getDayTime().setTimeOfDay(pendingTime);
|
||||
dns.getDayTime().setPaused(true);
|
||||
}
|
||||
}
|
||||
|
||||
if (jmeConsole == null) return;
|
||||
|
||||
// Toggle-Signal von JavaFX
|
||||
|
||||
@@ -359,6 +359,9 @@ public class SharedInput {
|
||||
/** JME setzt diesen Wert; JavaFX liest ihn (z. B. für Eingabesperre). */
|
||||
public volatile boolean consoleIsOpen = false;
|
||||
|
||||
/** JavaFX → JME3: Tageszeit setzen (0.0 = Mitternacht, 0.5 = Mittag, 1.0 = Mitternacht). NaN = kein Auftrag. */
|
||||
public volatile float pendingTimeOfDay = Float.NaN;
|
||||
|
||||
/** @deprecated Nur noch für Rückwärtskompatibilität – nicht mehr verwenden. */
|
||||
@Deprecated public volatile String pendingCommand = null;
|
||||
/** Status-/Fehlermeldungen von JME an JavaFX-Statusleiste. */
|
||||
|
||||
@@ -113,6 +113,11 @@ public class SceneObjectState extends BaseAppState {
|
||||
/** Kleiner Cache: modelPath → ModelMeta, damit nicht pro Frame geladen werden muss. */
|
||||
private final java.util.Map<String, de.blight.common.ModelMeta> metaCache = new java.util.HashMap<>();
|
||||
|
||||
// ── Beleuchtungs-Materialien (Tree/TreeLeaf/Fern) ────────────────────────
|
||||
private final List<Material> sceneLitMaterials = new ArrayList<>();
|
||||
private final List<Material> windMaterials = new ArrayList<>();
|
||||
private final List<Material> pbrMaterials = new ArrayList<>();
|
||||
|
||||
// ── Bett-Liegefläche ─────────────────────────────────────────────────────
|
||||
/** Visualisierungspfeil für die Liegefläche (1,8 m); wird nur gezeigt wenn Bett-Objekt gewählt. */
|
||||
private Node bedArrowNode = null;
|
||||
@@ -183,6 +188,9 @@ public class SceneObjectState extends BaseAppState {
|
||||
objects.clear();
|
||||
objNodes.clear();
|
||||
animClips.clear();
|
||||
sceneLitMaterials.clear();
|
||||
windMaterials.clear();
|
||||
pbrMaterials.clear();
|
||||
selectedIndices.clear();
|
||||
activeGizmo = -1;
|
||||
if (gizmoNode != null)
|
||||
@@ -222,6 +230,7 @@ public class SceneObjectState extends BaseAppState {
|
||||
|
||||
objNodes.add(node);
|
||||
objectRoot.attachChild(node);
|
||||
collectLitMaterials(node);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -310,6 +319,32 @@ public class SceneObjectState extends BaseAppState {
|
||||
updateGizmoVisibility();
|
||||
updateGizmoOrientation();
|
||||
|
||||
// Baum- und Farn-Shader mit Sonnen- und Windwerten versorgen; PBR-Emissive setzen
|
||||
if (!sceneLitMaterials.isEmpty() || !windMaterials.isEmpty() || !pbrMaterials.isEmpty()) {
|
||||
de.blight.game.state.DayNightState dns =
|
||||
getApplication().getStateManager().getState(de.blight.game.state.DayNightState.class);
|
||||
if (dns != null && dns.getSunLight() != null) {
|
||||
Vector3f lightDir = dns.getSunDirection().negate();
|
||||
com.jme3.math.ColorRGBA sc = dns.getSunLight().getColor();
|
||||
com.jme3.math.ColorRGBA ac = dns.getCustomAmbient();
|
||||
Vector3f sunVec = new Vector3f(sc.r, sc.g, sc.b);
|
||||
Vector3f ambVec = new Vector3f(ac.r, ac.g, ac.b);
|
||||
for (Material mat : sceneLitMaterials) {
|
||||
mat.setVector3("LightDir", lightDir);
|
||||
mat.setVector3("SunColor", sunVec);
|
||||
mat.setVector3("AmbientColor", ambVec);
|
||||
}
|
||||
for (Material mat : windMaterials) {
|
||||
mat.setVector3("LightDir", lightDir);
|
||||
mat.setVector3("SunColor", sunVec);
|
||||
mat.setVector3("AmbientColor", ambVec);
|
||||
}
|
||||
for (Material mat : pbrMaterials) {
|
||||
mat.setColor("Emissive", new com.jme3.math.ColorRGBA(ac.r, ac.g, ac.b, 1f));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Baum-Ordner-Platzierungsmodus
|
||||
String treeFolder = input.treeFolderPath;
|
||||
if (treeFolder != null && !treeFolder.equals(activeFolderPath)) {
|
||||
@@ -687,6 +722,7 @@ public class SceneObjectState extends BaseAppState {
|
||||
}
|
||||
objNodes.add(node);
|
||||
objectRoot.attachChild(node);
|
||||
collectLitMaterials(node);
|
||||
|
||||
selectObject(objects.size() - 1, false);
|
||||
input.objectJustPlaced = true;
|
||||
@@ -741,13 +777,73 @@ public class SceneObjectState extends BaseAppState {
|
||||
}
|
||||
}
|
||||
|
||||
private void convertUnshadedToPbr(Spatial spatial) {
|
||||
if (spatial instanceof Geometry geo) {
|
||||
Material mat = geo.getMaterial();
|
||||
if (mat == null) return;
|
||||
if (!"Unshaded".equals(mat.getMaterialDef().getName())) return;
|
||||
|
||||
com.jme3.material.MatParam colorMap = mat.getParam("ColorMap");
|
||||
com.jme3.material.MatParam color = mat.getParam("Color");
|
||||
com.jme3.material.RenderState origRS = mat.getAdditionalRenderState();
|
||||
|
||||
Material pbrMat = new Material(assets, "Common/MatDefs/Light/PBRLighting.j3md");
|
||||
pbrMat.setFloat("Metallic", 0.0f);
|
||||
pbrMat.setFloat("Roughness", 0.85f);
|
||||
|
||||
if (colorMap != null && colorMap.getValue() instanceof Texture t) {
|
||||
pbrMat.setTexture("BaseColorMap", t);
|
||||
} else if (color != null && color.getValue() instanceof ColorRGBA c) {
|
||||
pbrMat.setColor("BaseColor", new ColorRGBA(c.r, c.g, c.b, 1f));
|
||||
}
|
||||
|
||||
com.jme3.material.RenderState rs = pbrMat.getAdditionalRenderState();
|
||||
rs.setBlendMode(origRS.getBlendMode());
|
||||
rs.setFaceCullMode(origRS.getFaceCullMode());
|
||||
|
||||
geo.setMaterial(pbrMat);
|
||||
} else if (spatial instanceof Node node) {
|
||||
for (Spatial child : new java.util.ArrayList<>(node.getChildren())) {
|
||||
convertUnshadedToPbr(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void collectLitMaterials(Spatial spatial) {
|
||||
if (spatial instanceof Geometry geo) {
|
||||
Material mat = geo.getMaterial();
|
||||
if (mat == null) return;
|
||||
String name = mat.getMaterialDef().getName();
|
||||
if ("Tree".equals(name) || "TreeLeaf".equals(name)) {
|
||||
mat.setVector3("SunColor", new com.jme3.math.Vector3f(0.68f, 0.65f, 0.60f));
|
||||
mat.setVector3("AmbientColor", new com.jme3.math.Vector3f(0.08f, 0.10f, 0.16f));
|
||||
mat.setVector3("LightDir", new com.jme3.math.Vector3f(0f, -1f, 0f));
|
||||
sceneLitMaterials.add(mat);
|
||||
} else if ("Fern".equals(name)) {
|
||||
windMaterials.add(mat);
|
||||
} else if ("PBRLighting".equals(name)) {
|
||||
mat.setFloat("EmissivePower", 1f);
|
||||
mat.setFloat("EmissiveIntensity", 1f);
|
||||
mat.setColor("Emissive", new com.jme3.math.ColorRGBA(0.08f, 0.10f, 0.16f, 1f));
|
||||
pbrMaterials.add(mat);
|
||||
}
|
||||
} else if (spatial instanceof Node node) {
|
||||
for (Spatial child : node.getChildren()) {
|
||||
collectLitMaterials(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Node loadModelNode(String modelPath, float wx, float wy, float wz) {
|
||||
Node node = new Node("obj_" + objects.size());
|
||||
try {
|
||||
Spatial model = modelPath.startsWith("@")
|
||||
? createPrimitiveSpatial(modelPath.substring(1))
|
||||
: assets.loadModel(modelPath);
|
||||
if (!modelPath.startsWith("@")) stripControlsRecursive(model);
|
||||
if (!modelPath.startsWith("@")) {
|
||||
stripControlsRecursive(model);
|
||||
convertUnshadedToPbr(model);
|
||||
}
|
||||
if (modelPath.startsWith("@")) {
|
||||
Material mat = buildPrimitiveMaterial();
|
||||
if (model instanceof Geometry g) g.setMaterial(mat);
|
||||
@@ -785,12 +881,13 @@ public class SceneObjectState extends BaseAppState {
|
||||
boolean hasNmap = nmap != null && !nmap.isEmpty();
|
||||
if (hasTex || hasNmap) {
|
||||
try {
|
||||
Material mat = new Material(assets, "Common/MatDefs/Light/Lighting.j3md");
|
||||
mat.setBoolean("UseMaterialColors", false);
|
||||
Material mat = new Material(assets, "Common/MatDefs/Light/PBRLighting.j3md");
|
||||
mat.setFloat("Metallic", 0.0f);
|
||||
mat.setFloat("Roughness", 0.85f);
|
||||
if (hasTex) {
|
||||
Texture t = assets.loadTexture(tex);
|
||||
t.setWrap(Texture.WrapMode.Repeat);
|
||||
mat.setTexture("DiffuseMap", t);
|
||||
mat.setTexture("BaseColorMap", t);
|
||||
}
|
||||
if (hasNmap) {
|
||||
Texture n = assets.loadTexture(nmap);
|
||||
@@ -800,8 +897,10 @@ public class SceneObjectState extends BaseAppState {
|
||||
return mat;
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
Material mat = new Material(assets, "Common/MatDefs/Misc/Unshaded.j3md");
|
||||
mat.setColor("Color", new com.jme3.math.ColorRGBA(0.75f, 0.75f, 0.75f, 1f));
|
||||
Material mat = new Material(assets, "Common/MatDefs/Light/PBRLighting.j3md");
|
||||
mat.setFloat("Metallic", 0.0f);
|
||||
mat.setFloat("Roughness", 0.85f);
|
||||
mat.setColor("BaseColor", new com.jme3.math.ColorRGBA(0.75f, 0.75f, 0.75f, 1f));
|
||||
return mat;
|
||||
}
|
||||
|
||||
@@ -1461,7 +1560,9 @@ public class SceneObjectState extends BaseAppState {
|
||||
return;
|
||||
}
|
||||
} else if (hasTex) {
|
||||
mat = new Material(assets, "Common/MatDefs/Light/Lighting.j3md");
|
||||
mat = new Material(assets, "Common/MatDefs/Light/PBRLighting.j3md");
|
||||
mat.setFloat("Metallic", 0.0f);
|
||||
mat.setFloat("Roughness", 0.85f);
|
||||
} else {
|
||||
mat = new Material(assets, "Common/MatDefs/Misc/Unshaded.j3md");
|
||||
mat.setColor("Color", new ColorRGBA(0.75f, 0.75f, 0.75f, 1f));
|
||||
|
||||
@@ -96,6 +96,7 @@ public class TerrainEditorState extends BaseAppState {
|
||||
private final SharedInput input;
|
||||
|
||||
private TerrainQuad terrain;
|
||||
private com.jme3.material.Material terrainMat;
|
||||
private float[] cachedHeightMap; // Einmal geladen, danach manuell synchron gehalten
|
||||
private Geometry brushIndicator;
|
||||
private PlacedObjectState placedObjectState;
|
||||
@@ -157,6 +158,9 @@ public class TerrainEditorState extends BaseAppState {
|
||||
// ── Lifecycle ─────────────────────────────────────────────────────────────
|
||||
|
||||
private de.blight.game.state.DayNightState dayNightState;
|
||||
private com.jme3.light.LightProbe probe;
|
||||
private com.jme3.environment.EnvironmentCamera envCam;
|
||||
private int envCamFrames = 0;
|
||||
|
||||
@Override
|
||||
protected void initialize(Application app) {
|
||||
@@ -182,6 +186,16 @@ public class TerrainEditorState extends BaseAppState {
|
||||
dayNightState.getDayTime().setTimeOfDay(0.5f);
|
||||
dayNightState.setPaused(true);
|
||||
app.getStateManager().attach(dayNightState);
|
||||
|
||||
// LightProbe für PBR-IBL – ohne Probe ist envLightContribution immer 0 in JME3 3.9.0.
|
||||
// Probe backen wenn SkyControl den Himmel gerendert hat (ab Frame 30).
|
||||
probe = new com.jme3.light.LightProbe();
|
||||
probe.getArea().setRadius(8000f);
|
||||
rootNode.addLight(probe);
|
||||
envCam = new com.jme3.environment.EnvironmentCamera(256, new com.jme3.math.Vector3f(0f, 5f, 0f));
|
||||
app.getStateManager().attach(envCam);
|
||||
envCamFrames = 0;
|
||||
|
||||
}
|
||||
|
||||
private void loadCameraPrefs() {
|
||||
@@ -391,7 +405,7 @@ public class TerrainEditorState extends BaseAppState {
|
||||
tq.addControl(lod);
|
||||
|
||||
initSplatmap();
|
||||
tq.setMaterial(buildTerrainMaterial());
|
||||
tq.setMaterial(terrainMat = buildTerrainMaterial());
|
||||
return tq;
|
||||
}
|
||||
|
||||
@@ -594,7 +608,7 @@ public class TerrainEditorState extends BaseAppState {
|
||||
if (dayNightState != null && dayNightState.getSunLight() != null) {
|
||||
mat.setVector3("LightDir", dayNightState.getSunDirection().negate());
|
||||
com.jme3.math.ColorRGBA sc = dayNightState.getSunLight().getColor();
|
||||
com.jme3.math.ColorRGBA ac = dayNightState.getAmbientLight().getColor();
|
||||
com.jme3.math.ColorRGBA ac = dayNightState.getCustomAmbient();
|
||||
mat.setVector3("SunColor", new com.jme3.math.Vector3f(sc.r, sc.g, sc.b));
|
||||
mat.setVector3("AmbientColor", new com.jme3.math.Vector3f(ac.r, ac.g, ac.b));
|
||||
}
|
||||
@@ -925,6 +939,30 @@ public class TerrainEditorState extends BaseAppState {
|
||||
|
||||
@Override
|
||||
public void update(float tpf) {
|
||||
// LightProbe backen: nach 30 Frames hat SkyControl den Himmel vollständig gerendert.
|
||||
if (envCam != null) {
|
||||
envCamFrames++;
|
||||
if (envCamFrames == 30) {
|
||||
com.jme3.environment.LightProbeFactory.updateProbe(probe, envCam, rootNode,
|
||||
new com.jme3.environment.generation.JobProgressAdapter<com.jme3.light.LightProbe>() {
|
||||
@Override public void done(com.jme3.light.LightProbe result) {
|
||||
app.getStateManager().detach(envCam);
|
||||
envCam = null;
|
||||
log.info("[TerrainEditor] LightProbe fertig.");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Terrain-Shader mit DayNightState synchronisieren (reagiert auf Zeit-Menü-Änderungen)
|
||||
if (terrainMat != null && dayNightState != null && dayNightState.getSunLight() != null) {
|
||||
terrainMat.setVector3("LightDir", dayNightState.getSunDirection().negate());
|
||||
com.jme3.math.ColorRGBA sc = dayNightState.getSunLight().getColor();
|
||||
com.jme3.math.ColorRGBA ac = dayNightState.getCustomAmbient();
|
||||
terrainMat.setVector3("SunColor", new com.jme3.math.Vector3f(sc.r, sc.g, sc.b));
|
||||
terrainMat.setVector3("AmbientColor", new com.jme3.math.Vector3f(ac.r, ac.g, ac.b));
|
||||
}
|
||||
|
||||
float pgx = input.pendingGotoX;
|
||||
if (!Float.isNaN(pgx)) {
|
||||
input.pendingGotoX = Float.NaN;
|
||||
@@ -987,7 +1025,7 @@ public class TerrainEditorState extends BaseAppState {
|
||||
input.thirdNormalMapsChanged = false;
|
||||
input.normalMapOrderChanged = false;
|
||||
input.diffuseScalesChanged = false;
|
||||
terrain.setMaterial(buildTerrainMaterial());
|
||||
terrain.setMaterial(terrainMat = buildTerrainMaterial());
|
||||
}
|
||||
|
||||
if (input.reloadPlacedOther) {
|
||||
|
||||
@@ -219,6 +219,19 @@ public class VoxelEditorState extends BaseAppState {
|
||||
|
||||
@Override
|
||||
public void update(float tpf) {
|
||||
// Voxel-Shader mit DayNightState synchronisieren
|
||||
if (voxelMaterial != null) {
|
||||
de.blight.game.state.DayNightState dns =
|
||||
getApplication().getStateManager().getState(de.blight.game.state.DayNightState.class);
|
||||
if (dns != null && dns.getSunLight() != null) {
|
||||
voxelMaterial.setVector3("LightDir", dns.getSunDirection().negate());
|
||||
com.jme3.math.ColorRGBA sc = dns.getSunLight().getColor();
|
||||
com.jme3.math.ColorRGBA ac = dns.getCustomAmbient();
|
||||
voxelMaterial.setVector3("SunColor", new com.jme3.math.Vector3f(sc.r, sc.g, sc.b));
|
||||
voxelMaterial.setVector3("AmbientColor", new com.jme3.math.Vector3f(ac.r, ac.g, ac.b));
|
||||
}
|
||||
}
|
||||
|
||||
// LOD-Ergebnisse aus dem Hintergrund-Thread übernehmen
|
||||
Runnable r;
|
||||
while ((r = lodResultQueue.poll()) != null) r.run();
|
||||
|
||||
Reference in New Issue
Block a user