Wasserfälle weiter verveinert
This commit is contained in:
@@ -744,6 +744,11 @@ public class EditorApp extends Application {
|
||||
updateWaterfallPanel(input.selectedWaterfallInfo);
|
||||
}
|
||||
|
||||
if (input.refreshWorldTree) {
|
||||
input.refreshWorldTree = false;
|
||||
mapObjectsView.refresh();
|
||||
}
|
||||
|
||||
if (input.waterHeightChanged) {
|
||||
input.waterHeightChanged = false;
|
||||
updateWaterHeightDisplay(input.waterCurrentHeight);
|
||||
|
||||
@@ -376,6 +376,7 @@ public class SharedInput {
|
||||
public volatile boolean reloadPlacedModels = false;
|
||||
public volatile boolean reloadPlacedItems = false;
|
||||
public volatile boolean reloadPlacedOther = false; // Lichter, Emitter, Wasser, Bereiche, Zonen
|
||||
public volatile boolean refreshWorldTree = false; // Weltbaum (MapObjectsView) neu laden
|
||||
/** Nur Lichter + Emitter neu laden (kein Zonen-Reload), gesetzt von SceneObjectState. */
|
||||
public volatile boolean reloadLightsEmitters = false;
|
||||
/** Yaw in Grad: 0° = Süden (−Z), 90° = Westen (−X), ±180° = Norden (+Z). */
|
||||
|
||||
@@ -110,7 +110,8 @@ public class TerrainEditorState extends BaseAppState {
|
||||
private AreaState areaState;
|
||||
private LocationZoneState locationZoneState;
|
||||
private VoxelCliffEditorState voxelCliffEditorState;
|
||||
private RiverEditorState riverEditorState;
|
||||
private RiverEditorState riverEditorState;
|
||||
private WaterfallEditorState waterfallEditorState;
|
||||
private MapData loadedMapData;
|
||||
private Node axesGizmo;
|
||||
private boolean wireframeMode = false;
|
||||
@@ -390,7 +391,7 @@ public class TerrainEditorState extends BaseAppState {
|
||||
riverEditorState.setTerrain(terrain);
|
||||
}
|
||||
|
||||
WaterfallEditorState waterfallEditorState = app.getStateManager().getState(WaterfallEditorState.class);
|
||||
waterfallEditorState = app.getStateManager().getState(WaterfallEditorState.class);
|
||||
if (waterfallEditorState != null) {
|
||||
waterfallEditorState.setTerrain(terrain);
|
||||
}
|
||||
@@ -1314,8 +1315,9 @@ public class TerrainEditorState extends BaseAppState {
|
||||
try { if (soundAreaState != null) soundAreaState.loadAreas(de.blight.common.SoundAreaIO.load()); } catch (Exception ignored) {}
|
||||
try { if (areaState != null) areaState.loadAreas(de.blight.common.AreaIO.load()); } catch (Exception ignored) {}
|
||||
try { if (locationZoneState != null) locationZoneState.loadZones(de.blight.common.LocationZoneIO.load()); } catch (Exception ignored) {}
|
||||
try { if (riverEditorState != null) riverEditorState.loadPlacedRivers(de.blight.common.RiverIO.load()); } catch (Exception ignored) {}
|
||||
try { if (voxelCliffEditorState != null) voxelCliffEditorState.loadZones(de.blight.common.VoxelCliffZoneIO.load()); } catch (Exception ignored) {}
|
||||
try { if (riverEditorState != null) riverEditorState.loadPlacedRivers(de.blight.common.RiverIO.load()); } catch (Exception ignored) {}
|
||||
try { if (waterfallEditorState != null) waterfallEditorState.loadPlacedWaterfalls(de.blight.common.WaterfallIO.load()); } catch (Exception ignored) {}
|
||||
try { if (voxelCliffEditorState != null) voxelCliffEditorState.loadZones(de.blight.common.VoxelCliffZoneIO.load()); } catch (Exception ignored) {}
|
||||
}
|
||||
|
||||
if (input.saveRequested) {
|
||||
|
||||
@@ -83,6 +83,18 @@ public class WaterfallEditorState extends BaseAppState {
|
||||
|
||||
public void setTerrain(TerrainQuad t) { this.terrain = t; }
|
||||
|
||||
/** Wird von TerrainEditorState bei reloadPlacedOther (z.B. Löschen im Weltbaum) aufgerufen. */
|
||||
public void loadPlacedWaterfalls(java.util.List<PlacedWaterfall> list) {
|
||||
deselect();
|
||||
for (Node n : wfNodes) n.removeFromParent();
|
||||
wfNodes.clear();
|
||||
waterfalls.clear();
|
||||
for (PlacedWaterfall wf : list) {
|
||||
waterfalls.add(wf);
|
||||
wfNodes.add(buildWfNode(wf, false));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initialize(Application app) {
|
||||
this.app = (SimpleApplication) app;
|
||||
@@ -179,11 +191,18 @@ public class WaterfallEditorState extends BaseAppState {
|
||||
if (h >= 0) { startDrag(h); return; }
|
||||
}
|
||||
|
||||
// Raycast + See-Snapping
|
||||
// Obere Ecken (A=0, B=1): nur auf Terrain/Voxel + Snap auf Seekanten
|
||||
// Untere Ecken (C=2, D=3): nur auf Wasserflächen (Meer/Seen), kein Kanten-Snap
|
||||
boolean isBottomCorner = (mode == Mode.PLACING && placingCorners.size() >= 2);
|
||||
Ray ray = buildRay(jmeX, jmeY);
|
||||
Vector3f pt = raycastAll(ray);
|
||||
Vector3f snapped = snapToWaterVertex(jmeX, jmeY);
|
||||
if (snapped != null) pt = snapped;
|
||||
Vector3f pt;
|
||||
if (isBottomCorner) {
|
||||
pt = raycastAll(ray, true);
|
||||
} else {
|
||||
pt = raycastAll(ray, false);
|
||||
Vector3f snapped = snapToWaterVertex(jmeX, jmeY);
|
||||
if (snapped != null) pt = snapped;
|
||||
}
|
||||
if (pt == null) return;
|
||||
|
||||
if (mode == Mode.PLACING) {
|
||||
@@ -217,6 +236,7 @@ public class WaterfallEditorState extends BaseAppState {
|
||||
waterfalls.add(wf);
|
||||
wfNodes.add(buildWfNode(wf, false));
|
||||
save();
|
||||
input.refreshWorldTree = true;
|
||||
selectWf(waterfalls.size() - 1);
|
||||
}
|
||||
|
||||
@@ -240,8 +260,6 @@ public class WaterfallEditorState extends BaseAppState {
|
||||
mode = (idx >= 0) ? Mode.EDITING : Mode.IDLE;
|
||||
|
||||
if (idx >= 0 && idx < waterfalls.size()) {
|
||||
// wfNode ausblenden — Handles + selOutline übernehmen die Darstellung
|
||||
wfNodes.get(idx).setCullHint(Spatial.CullHint.Always);
|
||||
PlacedWaterfall wf = waterfalls.get(idx);
|
||||
liveCorners = new Vector3f[]{
|
||||
new Vector3f(wf.ax(), wf.ay(), wf.az()),
|
||||
@@ -256,7 +274,8 @@ public class WaterfallEditorState extends BaseAppState {
|
||||
}
|
||||
|
||||
private void deselect() {
|
||||
if (selectedIdx >= 0 && selectedIdx < wfNodes.size()) {
|
||||
// wfNode war nur während Drag verborgen → Inherit wiederherstellen falls nötig
|
||||
if (isDragging && selectedIdx >= 0 && selectedIdx < wfNodes.size()) {
|
||||
wfNodes.get(selectedIdx).setCullHint(Spatial.CullHint.Inherit);
|
||||
}
|
||||
selectedIdx = -1;
|
||||
@@ -280,15 +299,26 @@ public class WaterfallEditorState extends BaseAppState {
|
||||
private void startDrag(int h) {
|
||||
isDragging = true;
|
||||
dragHandle = h;
|
||||
// wfNode während Drag verbergen — live Positionen zeigt selOutline
|
||||
if (selectedIdx >= 0 && selectedIdx < wfNodes.size()) {
|
||||
wfNodes.get(selectedIdx).setCullHint(Spatial.CullHint.Always);
|
||||
}
|
||||
}
|
||||
|
||||
private void processDragMove(float screenX, float screenY) {
|
||||
if (!isDragging || selectedIdx < 0 || liveCorners == null) return;
|
||||
float jmeX = screenX * (float) input.viewportScaleX;
|
||||
float jmeY = cam.getHeight() - screenY * (float) input.viewportScaleY;
|
||||
Vector3f pt = raycastAll(buildRay(jmeX, jmeY));
|
||||
Vector3f snapped = snapToWaterVertex(jmeX, jmeY);
|
||||
if (snapped != null) pt = snapped;
|
||||
// Handles 2/3 (C/D) = untere Ecken → nur Wasser-Kollision, kein Kanten-Snap
|
||||
// Handles 0/1 (A/B) = obere Ecken → Terrain + Kanten-Snap, kein Wasser
|
||||
Vector3f pt;
|
||||
if (dragHandle >= 2) {
|
||||
pt = raycastAll(buildRay(jmeX, jmeY), true);
|
||||
} else {
|
||||
pt = raycastAll(buildRay(jmeX, jmeY), false);
|
||||
Vector3f snapped = snapToWaterVertex(jmeX, jmeY);
|
||||
if (snapped != null) pt = snapped;
|
||||
}
|
||||
if (pt == null) return;
|
||||
liveCorners[dragHandle].set(pt);
|
||||
rebuildHandlePositions();
|
||||
@@ -305,11 +335,10 @@ public class WaterfallEditorState extends BaseAppState {
|
||||
a.x, a.y, a.z, b.x, b.y, b.z, c.x, c.y, c.z, d.x, d.y, d.z,
|
||||
old.speed(), old.transparency(), old.colorR(), old.colorG(), old.colorB());
|
||||
waterfalls.set(selectedIdx, updated);
|
||||
// wfNode (verborgen) auf neue Positionen updaten, damit er beim Abwählen stimmt
|
||||
// wfNode mit neuen Positionen neu bauen und wieder anzeigen
|
||||
Node old2 = wfNodes.get(selectedIdx);
|
||||
old2.removeFromParent();
|
||||
Node fresh = buildWfNode(updated, false);
|
||||
fresh.setCullHint(Spatial.CullHint.Always); // bleibt verborgen, solange selektiert
|
||||
wfNodes.set(selectedIdx, fresh);
|
||||
save();
|
||||
publishSelection(selectedIdx);
|
||||
@@ -403,6 +432,10 @@ public class WaterfallEditorState extends BaseAppState {
|
||||
}
|
||||
|
||||
private Vector3f raycastAll(Ray ray) {
|
||||
return raycastAll(ray, false);
|
||||
}
|
||||
|
||||
private Vector3f raycastAll(Ray ray, boolean collisionWithWater) {
|
||||
Vector3f best = null;
|
||||
float bestDistSq = Float.MAX_VALUE;
|
||||
|
||||
@@ -430,13 +463,57 @@ public class WaterfallEditorState extends BaseAppState {
|
||||
Vector3f sp = smes.raycastGeometry(ray);
|
||||
if (sp != null) {
|
||||
float d = ray.origin.distanceSquared(sp);
|
||||
if (d < bestDistSq) { best = sp; }
|
||||
if (d < bestDistSq) { best = sp; bestDistSq = d; }
|
||||
}
|
||||
}
|
||||
|
||||
if (collisionWithWater) {
|
||||
// Meer bei Y=0
|
||||
Vector3f seaHit = rayHitsHorizontalPlane(ray, 0f);
|
||||
if (seaHit != null) {
|
||||
float d = ray.origin.distanceSquared(seaHit);
|
||||
if (d < bestDistSq) { best = seaHit; bestDistSq = d; }
|
||||
}
|
||||
|
||||
// Definierte Seen (PlacedWater)
|
||||
WaterBodyState wbs = getStateManager().getState(WaterBodyState.class);
|
||||
if (wbs != null) {
|
||||
for (PlacedWater body : wbs.getPlacedBodies()) {
|
||||
float[] xs = body.pointsX(), ys = body.pointsY(), zs = body.pointsZ();
|
||||
float avgY = 0f;
|
||||
for (float y : ys) avgY += y;
|
||||
avgY /= ys.length;
|
||||
Vector3f hit = rayHitsHorizontalPlane(ray, avgY);
|
||||
if (hit != null && pointInPolygon(hit.x, hit.z, xs, zs)) {
|
||||
float d = ray.origin.distanceSquared(hit);
|
||||
if (d < bestDistSq) { best = hit; bestDistSq = d; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return best;
|
||||
}
|
||||
|
||||
private static Vector3f rayHitsHorizontalPlane(Ray ray, float planeY) {
|
||||
if (Math.abs(ray.direction.y) < 1e-6f) return null;
|
||||
float t = (planeY - ray.origin.y) / ray.direction.y;
|
||||
if (t <= 0f) return null;
|
||||
return ray.origin.add(ray.direction.mult(t));
|
||||
}
|
||||
|
||||
private static boolean pointInPolygon(float px, float pz, float[] xs, float[] zs) {
|
||||
int n = xs.length;
|
||||
boolean inside = false;
|
||||
for (int i = 0, j = n - 1; i < n; j = i++) {
|
||||
if (((zs[i] > pz) != (zs[j] > pz)) &&
|
||||
(px < (xs[j] - xs[i]) * (pz - zs[i]) / (zs[j] - zs[i]) + xs[i])) {
|
||||
inside = !inside;
|
||||
}
|
||||
}
|
||||
return inside;
|
||||
}
|
||||
|
||||
// ── Visuals ───────────────────────────────────────────────────────────────
|
||||
|
||||
private int findNearestWf(Vector3f pos, float maxDist) {
|
||||
@@ -562,12 +639,7 @@ public class WaterfallEditorState extends BaseAppState {
|
||||
}
|
||||
|
||||
private void setAllCull(Spatial.CullHint hint) {
|
||||
for (int i = 0; i < wfNodes.size(); i++) {
|
||||
// Selektierter bleibt Always wenn hint=Always, sonst Inherit
|
||||
if (hint == Spatial.CullHint.Always || i != selectedIdx) {
|
||||
wfNodes.get(i).setCullHint(hint);
|
||||
}
|
||||
}
|
||||
for (Node n : wfNodes) n.setCullHint(hint);
|
||||
if (placingNode != null) placingNode.setCullHint(hint);
|
||||
for (Geometry g : handleGeos) g.setCullHint(hint);
|
||||
if (selOutline != null) selOutline.setCullHint(hint);
|
||||
@@ -708,7 +780,8 @@ public class WaterfallEditorState extends BaseAppState {
|
||||
Node fresh = buildWfNode(upd, false);
|
||||
wfNodes.add(i, fresh);
|
||||
if (isSelected) {
|
||||
fresh.setCullHint(Spatial.CullHint.Always);
|
||||
// wfNode bleibt sichtbar; nur bei aktivem Drag verbergen
|
||||
if (isDragging) fresh.setCullHint(Spatial.CullHint.Always);
|
||||
if (liveCorners != null) {
|
||||
liveCorners[0].set(upd.ax(), upd.ay(), upd.az());
|
||||
liveCorners[1].set(upd.bx(), upd.by(), upd.bz());
|
||||
|
||||
Reference in New Issue
Block a user