diff --git a/blight-assets/src/main/resources/quests/blight_wuehler.quest b/blight-assets/src/main/resources/quests/blight_wuehler.quest index 62153a3..dffc94e 100644 --- a/blight-assets/src/main/resources/quests/blight_wuehler.quest +++ b/blight-assets/src/main/resources/quests/blight_wuehler.quest @@ -11,4 +11,4 @@ "id": "quest.blight_wuehler.success" }, "type": "INTERACT" -} +} \ No newline at end of file diff --git a/blight-assets/src/main/resources/quests/kuehles_bier.quest b/blight-assets/src/main/resources/quests/kuehles_bier.quest index b643a79..4fbf073 100644 --- a/blight-assets/src/main/resources/quests/kuehles_bier.quest +++ b/blight-assets/src/main/resources/quests/kuehles_bier.quest @@ -12,4 +12,4 @@ "id": "quest.kuehles_bier.success" }, "type": "ITEM" -} +} \ No newline at end of file diff --git a/blight-assets/src/main/resources/quests/magnetiterz.quest b/blight-assets/src/main/resources/quests/magnetiterz.quest index 2acfce3..e980ee8 100644 --- a/blight-assets/src/main/resources/quests/magnetiterz.quest +++ b/blight-assets/src/main/resources/quests/magnetiterz.quest @@ -12,4 +12,4 @@ "id": "quest.magnetiterz.success" }, "type": "ITEM" -} +} \ No newline at end of file diff --git a/blight-editor/src/main/java/de/blight/editor/EditorApp.java b/blight-editor/src/main/java/de/blight/editor/EditorApp.java index 0db2862..7abecb0 100644 --- a/blight-editor/src/main/java/de/blight/editor/EditorApp.java +++ b/blight-editor/src/main/java/de/blight/editor/EditorApp.java @@ -3833,6 +3833,25 @@ public class EditorApp extends Application { toolTitle.setStyle("-fx-font-weight: bold; -fx-font-size: 13; -fx-text-fill: #111111;"); panel.getChildren().addAll(toolTitle, new Separator()); + // ── TextureTool: Radius/Stärke oben, dann 4×3-Grid + aktiver Slot ────── + if (tool instanceof de.blight.editor.tool.TextureTool tt) { + for (ToolParameter p : tt.getParameters()) { + Label name = new Label(p.getName()); + name.setStyle("-fx-text-fill: #111111;"); + Slider slider = new Slider(p.getMin(), p.getMax(), p.getValue()); + slider.setShowTickMarks(true); + slider.setShowTickLabels(true); + slider.setMajorTickUnit((p.getMax() - p.getMin()) / 2); + slider.valueProperty().addListener((obs, oldV, newV) -> p.setValue(newV.doubleValue())); + panel.getChildren().add(new VBox(3, name, withField(slider, "%.3f"))); + } + panel.getChildren().add(new Separator()); + javafx.scene.Node texUI = buildTextureChoiceWithActiveSlotUI(tt.textureIndex); + VBox.setVgrow(texUI, Priority.ALWAYS); + panel.getChildren().add(texUI); + return; + } + for (ChoiceToolParameter param : tool.getChoiceParameters()) { Label nameLabel = new Label(param.getName()); nameLabel.setStyle("-fx-text-fill: #111111;"); @@ -3948,46 +3967,6 @@ public class EditorApp extends Application { } } - // Textur-Slot-Konfigurator (nur beim TextureTool) - if (tool instanceof de.blight.editor.tool.TextureTool) { - // Globaler UV-Scale-Spinner (ändert alle 12 Slots gleichzeitig) - float curScale = (input.diffuseScales != null) ? input.diffuseScales[0] : 8f; - javafx.scene.control.Spinner globalScaleSpin = - new javafx.scene.control.Spinner<>(0.5, 1024.0, curScale, 0.5); - globalScaleSpin.setEditable(true); - globalScaleSpin.setMaxWidth(Double.MAX_VALUE); - globalScaleSpin.setTooltip(new Tooltip("UV-Scale in Metern — gilt für alle Slots gleichzeitig")); - globalScaleSpin.valueProperty().addListener((ob, ov, nv) -> { - if (nv == null) return; - float v = nv.floatValue(); - java.util.Arrays.fill(input.diffuseScales, v); - input.diffuseScalesChanged = true; - }); - Label scaleLbl = new Label("UV-Scale alle Slots (m):"); - scaleLbl.setStyle("-fx-font-size: 11; -fx-text-fill: #333;"); - - // Slot-Gruppen in ScrollPane damit die Werkzeugleiste nicht überquillt - VBox slots = new VBox(5, - buildTextureSlotsUI(false), - new javafx.scene.control.Separator(), - buildTextureSlotsUI(true), - new javafx.scene.control.Separator(), - buildThirdTextureSlotsUI()); - slots.setPadding(new javafx.geometry.Insets(4)); - ScrollPane slotsScroll = new ScrollPane(slots); - slotsScroll.setFitToWidth(true); - slotsScroll.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER); - VBox.setVgrow(slotsScroll, Priority.ALWAYS); - - panel.getChildren().addAll( - new javafx.scene.control.Separator(), - scaleLbl, globalScaleSpin, - new javafx.scene.control.Separator(), - slotsScroll, - new javafx.scene.control.Separator(), - buildVoxelSplatSectionUI()); - } - // Textur-Picker (nur beim GrassTool) if (tool instanceof GrassTool) { javafx.scene.Node grassUI = buildGrassTextureUI(); @@ -4790,6 +4769,147 @@ public class EditorApp extends Application { return box; } + private String texPathForSlot(int globalIdx) { + if (globalIdx < 4) return input.terrainTexturePaths[globalIdx]; + else if (globalIdx < 8) return input.upperTexturePaths[globalIdx - 4]; + else return input.thirdTexturePaths[globalIdx - 8]; + } + + private void setTexPathForSlot(int globalIdx, String path) { + if (globalIdx < 4) { + input.terrainTexturePaths[globalIdx] = path; + input.terrainTexturesChanged = true; + } else if (globalIdx < 8) { + input.upperTexturePaths[globalIdx - 4] = path; + input.upperTexturesChanged = true; + } else { + input.thirdTexturePaths[globalIdx - 8] = path; + input.thirdTexturesChanged = true; + } + } + + /** 4×3-Textur-Grid + darunter UV-Scale und Textur-Button für den aktiven Slot. */ + private javafx.scene.Node buildTextureChoiceWithActiveSlotUI(ChoiceToolParameter param) { + VBox outer = new VBox(6); + + // Aktiver-Slot-Bereich (unten) — wird bei Slot-Wechsel neu befüllt + VBox activeBox = new VBox(4); + activeBox.setPadding(new Insets(4, 0, 0, 0)); + + Runnable[] refresh = { null }; + refresh[0] = () -> { + activeBox.getChildren().clear(); + int sel = param.getSelectedIndex(); + String path = texPathForSlot(sel); + boolean hasTexture = path != null && !path.isEmpty(); + int group = sel < 4 ? 0 : sel < 8 ? 1 : 2; + int slotInGrp = sel % 4; + + Label scaleLbl = new Label("UV-Scale Slot " + (sel + 1) + " (m):"); + scaleLbl.setStyle("-fx-font-size: 11; -fx-text-fill: #333;"); + float curScale = (input.diffuseScales != null && sel < input.diffuseScales.length) + ? input.diffuseScales[sel] : 8f; + javafx.scene.control.Spinner scaleSpin = + new javafx.scene.control.Spinner<>(0.5, 1024.0, curScale, 0.5); + scaleSpin.setEditable(true); + scaleSpin.setMaxWidth(Double.MAX_VALUE); + scaleSpin.setDisable(!hasTexture); + scaleSpin.valueProperty().addListener((ob, ov, nv) -> { + if (nv == null) return; + input.diffuseScales[sel] = nv.floatValue(); + input.diffuseScalesChanged = true; + }); + + Button chooseBtn = new Button("Textur ändern..."); + chooseBtn.setMaxWidth(Double.MAX_VALUE); + chooseBtn.setDisable(!hasTexture); + chooseBtn.setOnAction(ev -> { + TextureChooser chooser = new TextureChooser(ASSET_ROOT, true); + if (primaryStage != null) chooser.initOwner(primaryStage); + chooser.showAndWait().ifPresent(p -> { + setTexPathForSlot(sel, p); + applyMetaToSlot(p, slotInGrp, group); + param.setSelectedIndex(sel); + showToolParameters(toolPanel, input.activeTool); + }); + }); + + activeBox.getChildren().addAll(new Separator(), scaleLbl, scaleSpin, chooseBtn); + }; + + // 4×3 Grid (wie buildTextureChoiceUI, aber mit Refresh-Hook) + String[] defaultsTerrain = { + "Textures/Terrain/splat/grass.jpg", "Textures/Terrain/Rock2/rock.jpg", + "Textures/Terrain/splat/dirt.jpg", "" + }; + String[] allPaths = new String[12]; + String[] allLabels = new String[12]; + for (int i = 0; i < 4; i++) { + String p = input.terrainTexturePaths[i]; + allPaths[i] = (p != null && !p.isEmpty()) ? p : defaultsTerrain[i]; + allLabels[i] = "S" + (i + 1) + " " + labelFromPath(allPaths[i]); + } + for (int i = 0; i < 4; i++) { + String p = input.upperTexturePaths[i]; + allPaths[4 + i] = (p != null && !p.isEmpty()) ? p : ""; + allLabels[4 + i] = "S" + (i + 5) + " " + labelFromPath(allPaths[4 + i]); + } + for (int i = 0; i < 4; i++) { + String p = input.thirdTexturePaths[i]; + allPaths[8 + i] = (p != null && !p.isEmpty()) ? p : ""; + allLabels[8 + i] = "S" + (i + 9) + " " + labelFromPath(allPaths[8 + i]); + } + + ToggleGroup tg = new ToggleGroup(); + javafx.scene.layout.TilePane tile = new javafx.scene.layout.TilePane(); + tile.setHgap(4); tile.setVgap(4); tile.setPrefColumns(4); + + for (int j = 0; j < 12; j++) { + final int idx = j; + String path = allPaths[j]; + ToggleButton btn = new ToggleButton(); + btn.setToggleGroup(tg); + btn.setPrefSize(56, 76); + btn.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE); + btn.setTooltip(new Tooltip("Slot " + (j + 1) + ": " + (path.isEmpty() ? "(leer)" : path))); + VBox content = new VBox(2); + content.setAlignment(Pos.CENTER); + if (!path.isEmpty()) { + String imgUrl = resolveImageUrl(path); + if (imgUrl != null) { + Image img = new Image(imgUrl, 44, 44, true, true); + ImageView iv = new ImageView(img.isError() ? null : img); + iv.setFitWidth(44); iv.setFitHeight(44); + content.getChildren().add(img.isError() ? emptySlotPlaceholder(44) : iv); + } else { + content.getChildren().add(emptySlotPlaceholder(44)); + } + } else { + content.getChildren().add(emptySlotPlaceholder(44)); + } + Label lbl = new Label(allLabels[j]); + lbl.setStyle("-fx-font-size: 9; -fx-text-fill: #222;"); + lbl.setMaxWidth(54); + content.getChildren().add(lbl); + btn.setGraphic(content); + boolean initially = (j == param.getSelectedIndex()); + btn.setSelected(initially); + applyModeButtonStyle(btn, initially); + btn.selectedProperty().addListener((obs, was, isNow) -> { + applyModeButtonStyle(btn, isNow); + if (isNow) { param.setSelectedIndex(idx); refresh[0].run(); } + }); + tile.getChildren().add(btn); + } + tg.selectedToggleProperty().addListener((obs, oldT, newT) -> { + if (newT == null) tg.selectToggle(oldT); + }); + + refresh[0].run(); + outer.getChildren().addAll(tile, activeBox); + return outer; + } + /** * Normal-Map-Prioritätsliste: zeigt alle 12 Slots, lässt den Nutzer die Reihenfolge * per ↑/↓ ändern. Slots ohne Normal-Map sind ausgegraut; ein Budget-Hinweis zeigt, diff --git a/blight-editor/src/main/java/de/blight/editor/state/SculptedMeshEditorState.java b/blight-editor/src/main/java/de/blight/editor/state/SculptedMeshEditorState.java index 9e7bdfc..d183829 100644 --- a/blight-editor/src/main/java/de/blight/editor/state/SculptedMeshEditorState.java +++ b/blight-editor/src/main/java/de/blight/editor/state/SculptedMeshEditorState.java @@ -557,6 +557,12 @@ public class SculptedMeshEditorState extends BaseAppState { bakedMat.setVector3("AmbientColor", ambientColor); } + public void applyDiffuseScales(float[] scales) { + if (bakedMat == null) return; + bakedMat.setParam("DiffuseScales", + com.jme3.shader.VarType.FloatArray, scales.clone()); + } + // ── Normalen & Mesh-Update ──────────────────────────────────────────────── private static void recomputeNormals(float[] positions, float[] normals, diff --git a/blight-editor/src/main/java/de/blight/editor/state/TerrainEditorState.java b/blight-editor/src/main/java/de/blight/editor/state/TerrainEditorState.java index e8bbcf1..414e05e 100644 --- a/blight-editor/src/main/java/de/blight/editor/state/TerrainEditorState.java +++ b/blight-editor/src/main/java/de/blight/editor/state/TerrainEditorState.java @@ -1042,11 +1042,11 @@ public class TerrainEditorState extends BaseAppState { // Terrain-Material neu aufbauen wenn Texturen (Slots 1-12), Normal-Maps // oder UV-Scales geändert wurden - if (input.terrainTexturesChanged || input.terrainNormalMapsChanged + boolean needsFullRebuild = input.terrainTexturesChanged || input.terrainNormalMapsChanged || input.upperTexturesChanged || input.upperNormalMapsChanged || input.thirdTexturesChanged || input.thirdNormalMapsChanged - || input.normalMapOrderChanged - || input.diffuseScalesChanged) { + || input.normalMapOrderChanged; + if (needsFullRebuild || input.diffuseScalesChanged) { input.terrainTexturesChanged = false; input.terrainNormalMapsChanged = false; input.upperTexturesChanged = false; @@ -1055,7 +1055,17 @@ public class TerrainEditorState extends BaseAppState { input.thirdNormalMapsChanged = false; input.normalMapOrderChanged = false; input.diffuseScalesChanged = false; - terrain.setMaterial(terrainMat = buildTerrainMaterial()); + if (needsFullRebuild) { + terrain.setMaterial(terrainMat = buildTerrainMaterial()); + } else if (terrainMat != null) { + // Nur UV-Scales geändert → bestehendes Material direkt aktualisieren, + // kein teurer TextureArray-Rebuild nötig + terrainMat.setParam("DiffuseScales", + com.jme3.shader.VarType.FloatArray, input.diffuseScales.clone()); + } + // BakedTerrain-Material (gebackene Voxel) ebenfalls aktualisieren + SculptedMeshEditorState smes = getStateManager().getState(SculptedMeshEditorState.class); + if (smes != null) smes.applyDiffuseScales(input.diffuseScales); } if (input.reloadLightsEmitters) { diff --git a/blight-map/src/main/map/blight_lights.bll b/blight-map/src/main/map/blight_lights.bll index 36a87bd..0601f0a 100644 --- a/blight-map/src/main/map/blight_lights.bll +++ b/blight-map/src/main/map/blight_lights.bll @@ -1 +1,2 @@ # x y z r g b intensity radius +-12.11008 2.23313 -1313.13879 1.00000 1.00000 1.00000 1.00000 20.00000 diff --git a/blight-map/src/main/map/blight_map.blm b/blight-map/src/main/map/blight_map.blm index 9dfbbde..a297ee8 100644 Binary files a/blight-map/src/main/map/blight_map.blm and b/blight-map/src/main/map/blight_map.blm differ diff --git a/blight-map/src/main/map/blight_voxel_splat.bin b/blight-map/src/main/map/blight_voxel_splat.bin index 4c79678..243b1d6 100644 Binary files a/blight-map/src/main/map/blight_voxel_splat.bin and b/blight-map/src/main/map/blight_voxel_splat.bin differ