diff --git a/blight-editor/src/main/java/de/blight/editor/state/PlacedObjectState.java b/blight-editor/src/main/java/de/blight/editor/state/PlacedObjectState.java index 6fe23d3..d99d3b6 100644 --- a/blight-editor/src/main/java/de/blight/editor/state/PlacedObjectState.java +++ b/blight-editor/src/main/java/de/blight/editor/state/PlacedObjectState.java @@ -147,6 +147,33 @@ public class PlacedObjectState extends BaseAppState { grassNode = new Node("grassNode"); ((SimpleApplication) app).getRootNode().attachChild(grassNode); applyAllSlotMaterials(); + initAspectRatios(); + } + + private void initAspectRatios() { + String[] allPaths = new String[8]; + allPaths[0] = input.grassTexturePath != null ? input.grassTexturePath : ""; + String[] slots = input.grassTextureSlots; + for (int i = 0; i < 7; i++) { + allPaths[i + 1] = (i < slots.length && slots[i] != null) ? slots[i] : ""; + } + float[] ars = input.grassAspectRatios.clone(); + for (int s = 0; s < 8; s++) { + String p = allPaths[s]; + if (p == null || p.isEmpty()) continue; + try { + com.jme3.texture.Texture tex = assetManager.loadTexture( + new com.jme3.asset.TextureKey(p, false)); + if (tex != null && tex.getImage() != null) { + int w = tex.getImage().getWidth(); + int h = tex.getImage().getHeight(); + if (w > 0 && h > 0) ars[s] = (float) w / h; + } + } catch (Exception e) { + log.debug("[PlacedObjectState] Aspect-Ratio nicht lesbar für Slot {}: {}", s, e.getMessage()); + } + } + input.grassAspectRatios = ars; } @Override diff --git a/blight-game/src/main/java/de/blight/game/state/GrassState.java b/blight-game/src/main/java/de/blight/game/state/GrassState.java index 2be3720..7bf282c 100644 --- a/blight-game/src/main/java/de/blight/game/state/GrassState.java +++ b/blight-game/src/main/java/de/blight/game/state/GrassState.java @@ -36,18 +36,18 @@ public class GrassState extends BaseAppState private static final Logger log = LoggerFactory.getLogger(GrassState.class); - private static final int TERRAIN_HALF = 2048; - private static final int CHUNK_SIZE = 128; - private static final int CHUNKS_PER_AXIS = (TERRAIN_HALF * 2) / CHUNK_SIZE; // 32 - private static final int CHUNK_COUNT = CHUNKS_PER_AXIS * CHUNKS_PER_AXIS; // 1024 + private static final int CHUNK_SIZE = de.blight.common.ChunkTerrainIO.CHUNK_SIZE; // 128 + private static final int TERRAIN_HALF = de.blight.common.ChunkTerrainIO.WORLD_SIZE / 2; // 1024 + private static final int CHUNKS_PER_AXIS = de.blight.common.ChunkTerrainIO.CHUNKS_PER_AXIS; // 16 + private static final int CHUNK_COUNT = de.blight.common.ChunkTerrainIO.CHUNK_COUNT; // 256 private static final int BLADES_PER_TUFT = 4; private static final float TUFT_SPREAD = 0.5f; - private static final float BLADE_WIDTH = 0.18f; private static final int INIT_PER_FRAME = 4; private final TerrainChunkState terrainChunkState; - private Node grassNode; + private Node grassNode; + private float[] slotAspectRatios = new float[8]; @SuppressWarnings({"unchecked", "rawtypes"}) private final List[] chunkTufts = new List[CHUNK_COUNT]; @@ -69,8 +69,9 @@ public class GrassState extends BaseAppState for (int i = 0; i < CHUNK_COUNT; i++) chunkTufts[i] = new ArrayList<>(); + GrassTuftIO.GrassData data = null; try { - GrassTuftIO.GrassData data = GrassTuftIO.load(); + data = GrassTuftIO.load(); if (data != null) { initSlotMaterials(app.getAssetManager(), data.slotPaths()); for (GrassTuft t : data.tufts()) { @@ -85,6 +86,28 @@ public class GrassState extends BaseAppState if (slotMaterials.isEmpty()) { slotMaterials.put(0, buildGrassMat(app.getAssetManager(), "")); } + initAspectRatios(app.getAssetManager(), + data != null ? data.slotPaths() : null); + } + + private void initAspectRatios(AssetManager assets, String[] slotPaths) { + java.util.Arrays.fill(slotAspectRatios, 1f); + if (slotPaths == null) return; + for (int s = 0; s < Math.min(slotAspectRatios.length, slotPaths.length); s++) { + String p = slotPaths[s]; + if (p == null || p.isEmpty()) continue; + try { + com.jme3.texture.Texture tex = assets.loadTexture( + new com.jme3.asset.TextureKey(p, false)); + if (tex != null && tex.getImage() != null) { + int w = tex.getImage().getWidth(); + int h = tex.getImage().getHeight(); + if (w > 0 && h > 0) slotAspectRatios[s] = (float) w / h; + } + } catch (Exception e) { + log.debug("[GrassState] Aspect-Ratio nicht lesbar Slot {}: {}", s, e.getMessage()); + } + } } @Override @@ -228,8 +251,10 @@ public class GrassState extends BaseAppState if (entry.getValue().isEmpty()) continue; Material mat = getSlotMaterial(entry.getKey()); if (mat == null) continue; - Geometry geo = new Geometry("grass_" + idx + "_s" + entry.getKey(), - buildGrassMesh(entry.getValue())); + int slot = entry.getKey(); + float ar = (slot < slotAspectRatios.length) ? slotAspectRatios[slot] : 1f; + Geometry geo = new Geometry("grass_" + idx + "_s" + slot, + buildGrassMesh(entry.getValue(), ar)); geo.setMaterial(mat); node.attachChild(geo); } @@ -288,7 +313,7 @@ public class GrassState extends BaseAppState // ── Mesh: Kreuz-Quad mit UV ─────────────────────────────────────────────── - private static Mesh buildGrassMesh(List blades) { + private static Mesh buildGrassMesh(List blades, float ar) { int n = blades.size(); FloatBuffer pos = BufferUtils.createFloatBuffer(n * 8 * 3); FloatBuffer uv = BufferUtils.createFloatBuffer(n * 8 * 2); @@ -298,7 +323,7 @@ public class GrassState extends BaseAppState int vi = 0; for (float[] blade : blades) { float x = blade[0], y = blade[1], z = blade[2], h = blade[3]; - float w = Math.max(0.05f, h * BLADE_WIDTH); + float w = Math.max(0.05f, h * ar * 0.5f); pos.put(x-w).put(y ).put(z); uv.put(0).put(0); nrm.put(0).put(1).put(0); pos.put(x+w).put(y ).put(z); uv.put(1).put(0); nrm.put(0).put(1).put(0); diff --git a/blight-map/src/main/map/blight_grass_vertex.blgv b/blight-map/src/main/map/blight_grass_vertex.blgv index 0155c6e..c3748c1 100644 Binary files a/blight-map/src/main/map/blight_grass_vertex.blgv and b/blight-map/src/main/map/blight_grass_vertex.blgv differ