Compare commits
2 Commits
03dcfb4187
...
528b2ea576
| Author | SHA1 | Date | |
|---|---|---|---|
| 528b2ea576 | |||
| 08e1df06d4 |
@@ -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
|
||||
|
||||
@@ -26,12 +26,15 @@ public class ThirdPersonCamera {
|
||||
private static final float MIN_VERTICAL_ANGLE = -0.12f; // Kamera kann bis Kniehöhe sinken
|
||||
private static final float MAX_VERTICAL_ANGLE = FastMath.HALF_PI - 0.1f;
|
||||
private static final float TARGET_HEIGHT = 1.0f;
|
||||
private static final float CAM_CLEARANCE = 0.3f; // Rückzugsabstand bei Terrain-Kollision
|
||||
|
||||
private final Camera cam;
|
||||
private final InputManager inputManager;
|
||||
private final KeyBindings keyBindings;
|
||||
|
||||
private Spatial target;
|
||||
/** Liefert den Trefferanteil (0..1) eines Strahls von from→to; 1 = kein Treffer. */
|
||||
private java.util.function.BiFunction<com.jme3.math.Vector3f, com.jme3.math.Vector3f, Float> raycastProvider;
|
||||
|
||||
private float yaw = 0f;
|
||||
private float pitch = 0.4f;
|
||||
@@ -59,6 +62,11 @@ public class ThirdPersonCamera {
|
||||
|
||||
public void setPaused(boolean paused) { this.paused = paused; }
|
||||
|
||||
/** Raycast-Anbieter: gibt Trefferanteil (0..1) zurück; 1 = freie Sichtlinie. */
|
||||
public void setRaycastProvider(java.util.function.BiFunction<com.jme3.math.Vector3f, com.jme3.math.Vector3f, Float> provider) {
|
||||
this.raycastProvider = provider;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
private void registerMappings() {
|
||||
@@ -111,6 +119,18 @@ public class ThirdPersonCamera {
|
||||
float z = distance * FastMath.cos(pitch) * FastMath.cos(yaw);
|
||||
|
||||
Vector3f camPos = pivot.add(x, y, z);
|
||||
|
||||
if (raycastProvider != null) {
|
||||
float fraction = raycastProvider.apply(pivot, camPos);
|
||||
if (fraction < 1f) {
|
||||
Vector3f dir = camPos.subtract(pivot);
|
||||
float rayLen = dir.length();
|
||||
dir.normalizeLocal();
|
||||
float safeLen = Math.max(MIN_DISTANCE * 0.5f, fraction * rayLen - CAM_CLEARANCE);
|
||||
camPos = pivot.add(dir.mult(safeLen));
|
||||
}
|
||||
}
|
||||
|
||||
cam.setLocation(camPos);
|
||||
cam.lookAt(pivot, Vector3f.UNIT_Y);
|
||||
}
|
||||
|
||||
@@ -318,6 +318,19 @@ public class WorldScene extends BaseAppState {
|
||||
|
||||
thirdPersonCam = new ThirdPersonCamera(app.getCamera(), app.getInputManager(), keyBindings);
|
||||
thirdPersonCam.setTarget(character);
|
||||
thirdPersonCam.setRaycastProvider((from, to) -> {
|
||||
com.jme3.bullet.PhysicsSpace ps =
|
||||
bulletAppState != null ? bulletAppState.getPhysicsSpace() : null;
|
||||
if (ps == null) return 1f;
|
||||
java.util.List<com.jme3.bullet.collision.PhysicsRayTestResult> hits = ps.rayTest(from, to);
|
||||
float best = 1f;
|
||||
for (com.jme3.bullet.collision.PhysicsRayTestResult hit : hits) {
|
||||
Object uo = hit.getCollisionObject().getUserObject();
|
||||
if (uo == physicsChar || uo == character) continue;
|
||||
if (hit.getHitFraction() < best) best = hit.getHitFraction();
|
||||
}
|
||||
return best;
|
||||
});
|
||||
|
||||
MainCharacter mc = findMainCharacter();
|
||||
if (mc != null) {
|
||||
|
||||
@@ -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<GrassTuft>[] 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<float[]> blades) {
|
||||
private static Mesh buildGrassMesh(List<float[]> 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);
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user