An der Texturierung der Welt gearbeitet
This commit is contained in:
@@ -3833,6 +3833,25 @@ public class EditorApp extends Application {
|
|||||||
toolTitle.setStyle("-fx-font-weight: bold; -fx-font-size: 13; -fx-text-fill: #111111;");
|
toolTitle.setStyle("-fx-font-weight: bold; -fx-font-size: 13; -fx-text-fill: #111111;");
|
||||||
panel.getChildren().addAll(toolTitle, new Separator());
|
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()) {
|
for (ChoiceToolParameter param : tool.getChoiceParameters()) {
|
||||||
Label nameLabel = new Label(param.getName());
|
Label nameLabel = new Label(param.getName());
|
||||||
nameLabel.setStyle("-fx-text-fill: #111111;");
|
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<Double> 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)
|
// Textur-Picker (nur beim GrassTool)
|
||||||
if (tool instanceof GrassTool) {
|
if (tool instanceof GrassTool) {
|
||||||
javafx.scene.Node grassUI = buildGrassTextureUI();
|
javafx.scene.Node grassUI = buildGrassTextureUI();
|
||||||
@@ -4790,6 +4769,147 @@ public class EditorApp extends Application {
|
|||||||
return box;
|
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<Double> 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
|
* 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,
|
* per ↑/↓ ändern. Slots ohne Normal-Map sind ausgegraut; ein Budget-Hinweis zeigt,
|
||||||
|
|||||||
@@ -557,6 +557,12 @@ public class SculptedMeshEditorState extends BaseAppState {
|
|||||||
bakedMat.setVector3("AmbientColor", ambientColor);
|
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 ────────────────────────────────────────────────
|
// ── Normalen & Mesh-Update ────────────────────────────────────────────────
|
||||||
|
|
||||||
private static void recomputeNormals(float[] positions, float[] normals,
|
private static void recomputeNormals(float[] positions, float[] normals,
|
||||||
|
|||||||
@@ -1042,11 +1042,11 @@ public class TerrainEditorState extends BaseAppState {
|
|||||||
|
|
||||||
// Terrain-Material neu aufbauen wenn Texturen (Slots 1-12), Normal-Maps
|
// Terrain-Material neu aufbauen wenn Texturen (Slots 1-12), Normal-Maps
|
||||||
// oder UV-Scales geändert wurden
|
// oder UV-Scales geändert wurden
|
||||||
if (input.terrainTexturesChanged || input.terrainNormalMapsChanged
|
boolean needsFullRebuild = input.terrainTexturesChanged || input.terrainNormalMapsChanged
|
||||||
|| input.upperTexturesChanged || input.upperNormalMapsChanged
|
|| input.upperTexturesChanged || input.upperNormalMapsChanged
|
||||||
|| input.thirdTexturesChanged || input.thirdNormalMapsChanged
|
|| input.thirdTexturesChanged || input.thirdNormalMapsChanged
|
||||||
|| input.normalMapOrderChanged
|
|| input.normalMapOrderChanged;
|
||||||
|| input.diffuseScalesChanged) {
|
if (needsFullRebuild || input.diffuseScalesChanged) {
|
||||||
input.terrainTexturesChanged = false;
|
input.terrainTexturesChanged = false;
|
||||||
input.terrainNormalMapsChanged = false;
|
input.terrainNormalMapsChanged = false;
|
||||||
input.upperTexturesChanged = false;
|
input.upperTexturesChanged = false;
|
||||||
@@ -1055,7 +1055,17 @@ public class TerrainEditorState extends BaseAppState {
|
|||||||
input.thirdNormalMapsChanged = false;
|
input.thirdNormalMapsChanged = false;
|
||||||
input.normalMapOrderChanged = false;
|
input.normalMapOrderChanged = false;
|
||||||
input.diffuseScalesChanged = 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) {
|
if (input.reloadLightsEmitters) {
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
# x y z r g b intensity radius
|
# x y z r g b intensity radius
|
||||||
|
-12.11008 2.23313 -1313.13879 1.00000 1.00000 1.00000 1.00000 20.00000
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user