Performance Tweaks

This commit is contained in:
2026-09-10 20:54:36 +02:00
parent 52801b5ddf
commit 1b509c3a52
4 changed files with 85 additions and 57 deletions

View File

@@ -120,6 +120,15 @@ public class PlayerInputControl {
/** Autopilot: wenn gesetzt, geht der Charakter automatisch in diese (normalisierte) Richtung. */
private Vector3f autopilotDir = null;
// Pre-allokierte Scratch-Objekte — keine Heap-Allokation pro Frame in update()
private final Vector3f scratchCamDir = new Vector3f();
private final Vector3f scratchCamLeft = new Vector3f();
private final Vector3f scratchMoveDir = new Vector3f();
private final Vector3f scratchWalk = new Vector3f();
private final Quaternion scratchRot = new Quaternion();
private final Quaternion scratchCurRot = new Quaternion();
private final Vector3f scratchTransl = new Vector3f();
private final ActionListener actionListener = (name, isPressed, tpf) -> {
if (paused) return;
switch (name) {
@@ -477,7 +486,8 @@ public class PlayerInputControl {
float t = Math.min(1f, animOffsetElapsed / AnimPlayer.BLEND_DURATION);
animOffsetCurrent.set(animOffsetFrom);
animOffsetCurrent.interpolateLocal(animOffsetTarget, t);
visual.setLocalTranslation(visualBaseTranslation.clone().addLocal(animOffsetCurrent));
scratchTransl.set(visualBaseTranslation).addLocal(animOffsetCurrent);
visual.setLocalTranslation(scratchTransl);
}
if (inputsBlocked) return;
@@ -513,19 +523,17 @@ public class PlayerInputControl {
turnRemaining -= tpf;
physicsChar.setWalkDirection(Vector3f.ZERO);
if (visual != null && turnDir != null) {
Quaternion targetRot = new Quaternion();
targetRot.lookAt(turnDir, Vector3f.UNIT_Y);
Quaternion current = visual.getLocalRotation().clone();
current.slerp(targetRot, ROTATE_SPEED * tpf);
visual.setLocalRotation(current);
scratchRot.lookAt(turnDir, Vector3f.UNIT_Y);
scratchCurRot.set(visual.getLocalRotation());
scratchCurRot.slerp(scratchRot, ROTATE_SPEED * tpf);
visual.setLocalRotation(scratchCurRot);
}
if (turnRemaining <= 0f) {
turnActive = false;
// Zielrotation exakt einrasten
if (visual != null && turnDir != null) {
Quaternion snap = new Quaternion();
snap.lookAt(turnDir, Vector3f.UNIT_Y);
visual.setLocalRotation(snap);
scratchRot.lookAt(turnDir, Vector3f.UNIT_Y);
visual.setLocalRotation(scratchRot);
}
Runnable cb = turnCallback;
turnCallback = null;
@@ -557,13 +565,12 @@ public class PlayerInputControl {
// Autopilot: Charakter läuft automatisch in eine vorgegebene Richtung (WALK-Animation)
if (autopilotDir != null) {
physicsChar.setWalkDirection(autopilotDir.mult(MOVE_SPEED * WALK_MULT));
physicsChar.setWalkDirection(scratchWalk.set(autopilotDir).multLocal(MOVE_SPEED * WALK_MULT));
if (visual != null) {
Quaternion targetRot = new Quaternion();
targetRot.lookAt(autopilotDir, Vector3f.UNIT_Y);
Quaternion current = visual.getLocalRotation().clone();
current.slerp(targetRot, ROTATE_SPEED * tpf);
visual.setLocalRotation(current);
scratchRot.lookAt(autopilotDir, Vector3f.UNIT_Y);
scratchCurRot.set(visual.getLocalRotation());
scratchCurRot.slerp(scratchRot, ROTATE_SPEED * tpf);
visual.setLocalRotation(scratchCurRot);
}
if (AnimationAction.WALK != currentAnim) {
playAction(AnimationAction.WALK);
@@ -573,16 +580,16 @@ public class PlayerInputControl {
}
// Normale Spielereingabe (WASD)
Vector3f camDir = cam.getDirection().clone().setY(0).normalizeLocal();
Vector3f camLeft = cam.getLeft().clone().setY(0).normalizeLocal();
cam.getDirection(scratchCamDir).setY(0).normalizeLocal();
cam.getLeft(scratchCamLeft).setY(0).normalizeLocal();
Vector3f moveDir = new Vector3f();
if (forward) moveDir.addLocal(camDir);
if (backward) moveDir.subtractLocal(camDir);
if (left) moveDir.addLocal(camLeft);
if (right) moveDir.subtractLocal(camLeft);
scratchMoveDir.set(0f, 0f, 0f);
if (forward) scratchMoveDir.addLocal(scratchCamDir);
if (backward) scratchMoveDir.subtractLocal(scratchCamDir);
if (left) scratchMoveDir.addLocal(scratchCamLeft);
if (right) scratchMoveDir.subtractLocal(scratchCamLeft);
boolean moving = moveDir.lengthSquared() > 0.001f;
boolean moving = scratchMoveDir.lengthSquared() > 0.001f;
// Wassertiefe bestimmen – einmal pro Frame, auch in pollFootsteps() genutzt
Vector3f charPos = physicsChar.getPhysicsLocation();
@@ -590,18 +597,17 @@ public class PlayerInputControl {
boolean inDeepWater = currentWaterDepth > WATER_WADING_DEPTH;
if (moving) {
moveDir.normalizeLocal();
scratchMoveDir.normalizeLocal();
float speed = (inDeepWater || walk) ? MOVE_SPEED * WALK_MULT
: sprint ? MOVE_SPEED * SPRINT_MULT
: MOVE_SPEED;
physicsChar.setWalkDirection(moveDir.mult(speed));
physicsChar.setWalkDirection(scratchWalk.set(scratchMoveDir).multLocal(speed));
if (visual != null) {
Quaternion targetRot = new Quaternion();
targetRot.lookAt(moveDir, Vector3f.UNIT_Y);
Quaternion current = visual.getLocalRotation().clone();
current.slerp(targetRot, ROTATE_SPEED * tpf);
visual.setLocalRotation(current);
scratchRot.lookAt(scratchMoveDir, Vector3f.UNIT_Y);
scratchCurRot.set(visual.getLocalRotation());
scratchCurRot.slerp(scratchRot, ROTATE_SPEED * tpf);
visual.setLocalRotation(scratchCurRot);
}
} else {
physicsChar.setWalkDirection(Vector3f.ZERO);

View File

@@ -48,6 +48,11 @@ public class ThirdPersonCamera {
private float introDuration;
private float introTimer;
// Pre-allokierte Scratch-Vektoren — keine Heap-Allokation pro Frame
private final Vector3f scratchPivot = new Vector3f();
private final Vector3f scratchCamPos = new Vector3f();
private final Vector3f scratchDir = new Vector3f();
public ThirdPersonCamera(Camera cam, InputManager inputManager, KeyBindings keyBindings) {
this.cam = cam;
@@ -111,28 +116,28 @@ public class ThirdPersonCamera {
if (target == null || paused) return;
Vector3f pivot = target.getWorldTranslation().add(0, TARGET_HEIGHT, 0);
scratchPivot.set(target.getWorldTranslation()).addLocal(0f, TARGET_HEIGHT, 0f);
// Sphärische Koordinaten → kartesisch
float x = distance * FastMath.cos(pitch) * FastMath.sin(yaw);
float y = distance * FastMath.sin(pitch);
float z = distance * FastMath.cos(pitch) * FastMath.cos(yaw);
Vector3f camPos = pivot.add(x, y, z);
scratchCamPos.set(scratchPivot).addLocal(x, y, z);
if (raycastProvider != null) {
float fraction = raycastProvider.apply(pivot, camPos);
float fraction = raycastProvider.apply(scratchPivot, scratchCamPos);
if (fraction < 1f) {
Vector3f dir = camPos.subtract(pivot);
float rayLen = dir.length();
dir.normalizeLocal();
scratchDir.set(scratchCamPos).subtractLocal(scratchPivot);
float rayLen = scratchDir.length();
scratchDir.normalizeLocal();
float safeLen = Math.max(MIN_DISTANCE * 0.5f, fraction * rayLen - CAM_CLEARANCE);
camPos = pivot.add(dir.mult(safeLen));
scratchCamPos.set(scratchPivot).addLocal(scratchDir.multLocal(safeLen));
}
}
cam.setLocation(camPos);
cam.lookAt(pivot, Vector3f.UNIT_Y);
cam.setLocation(scratchCamPos);
cam.lookAt(scratchPivot, Vector3f.UNIT_Y);
}
/** Aktueller Yaw-Winkel (für CharacterControl nutzbar). */

View File

@@ -108,6 +108,13 @@ public class WorldScene extends BaseAppState {
private de.blight.game.state.DrownState drownState;
private WaterInteractionState waterInteractionState;
// Pre-allokierte Scratch-Objekte — keine Heap-Allokation pro Frame in update()
private final Vector3f scratchLightDir = new Vector3f();
private final Vector3f scratchSunColor = new Vector3f();
private final Vector3f scratchAmbColor = new Vector3f();
private final ColorRGBA scratchEmissive = new ColorRGBA();
private final Vector3f scratchSunPos = new Vector3f();
public WorldScene(KeyBindings keyBindings, GraphicsSettings graphicsSettings) {
this.keyBindings = keyBindings;
this.graphicsSettings = graphicsSettings;
@@ -466,23 +473,25 @@ public class WorldScene extends BaseAppState {
musicSystem.setDaytime(dayNight.getSunElevation() >= 0f);
if (terrainMaterial != null && dayNight != null && dayNight.getSunLight() != null) {
terrainMaterial.setVector3("LightDir", dayNight.getSunDirection().negate());
scratchLightDir.set(dayNight.getSunDirection()).negateLocal();
terrainMaterial.setVector3("LightDir", scratchLightDir);
ColorRGBA sc = dayNight.getSunLight().getColor();
ColorRGBA ac = dayNight.getCustomAmbient();
terrainMaterial.setVector3("SunColor", new Vector3f(sc.r, sc.g, sc.b));
terrainMaterial.setVector3("AmbientColor", new Vector3f(ac.r, ac.g, ac.b));
terrainMaterial.setVector3("SunColor", scratchSunColor.set(sc.r, sc.g, sc.b));
terrainMaterial.setVector3("AmbientColor", scratchAmbColor.set(ac.r, ac.g, ac.b));
de.blight.game.state.RainState rs = app.getStateManager().getState(de.blight.game.state.RainState.class);
if (rs != null) terrainMaterial.setFloat("Wetness", rs.getWetness());
// Charakter-PBR: Emissive = customAmbient (IBL-Fallback ohne fertige LightProbe)
scratchEmissive.set(ac.r, ac.g, ac.b, 1f);
for (Material mat : characterPbrMaterials) {
mat.setColor("Emissive", new ColorRGBA(ac.r, ac.g, ac.b, 1f));
mat.setColor("Emissive", scratchEmissive);
}
if (lightScatterFilter != null) {
Vector3f sunDir = dayNight.getSunDirection();
// Weltposition der Sonne: von der Kamera aus weit entfernt entgegen der Lichtrichtung
Vector3f sunWorld = app.getCamera().getLocation().add(sunDir.negate().mult(10000f));
lightScatterFilter.setLightPosition(sunWorld);
scratchSunPos.set(sunDir).negateLocal().multLocal(10000f).addLocal(app.getCamera().getLocation());
lightScatterFilter.setLightPosition(scratchSunPos);
// Deaktivieren wenn Sonne hinter der Kamera (gleiche Richtung wie Kamera schaut)
lightScatterFilter.setEnabled(sunDir.dot(app.getCamera().getDirection()) < 0.2f);
}

View File

@@ -147,6 +147,12 @@ public class WorldNpcsState extends BaseAppState {
private float revertTimer = 0f;
private float revertDelay = 0f;
// Pre-allokierte Scratch-Objekte — keine Heap-Allokation in hot paths
private final List<SpawnedNpc> toRemoveBuf = new ArrayList<>();
private final Set<String> spawnedIdsBuf = new HashSet<>();
private final Quaternion scratchQ = new Quaternion();
private final com.jme3.math.ColorRGBA scratchEmissive = new com.jme3.math.ColorRGBA();
// ── Konstruktor ───────────────────────────────────────────────────────────
public WorldNpcsState(KeyBindings keyBindings, CharacterControl physicsChar,
@@ -244,14 +250,14 @@ public class WorldNpcsState extends BaseAppState {
Vector3f playerPos = physicsChar.getPhysicsLocation();
// Bestehende NPCs prüfen: Position aktualisieren oder entladen
List<SpawnedNpc> toRemove = new ArrayList<>();
toRemoveBuf.clear();
for (SpawnedNpc s : spawned) {
Vector3f pos = resolveRoutinePosition(s.npc(), currentHour);
float dist = pos != null ? dist2d(playerPos, pos) : Float.MAX_VALUE;
if (pos == null || dist > UNLOAD_RADIUS) {
s.visual().removeFromParent();
toRemove.add(s);
toRemoveBuf.add(s);
if (rotNpc == s) rotNpc = null;
if (revertNpc == s) revertNpc = null;
} else {
@@ -260,27 +266,28 @@ public class WorldNpcsState extends BaseAppState {
playNpcAction(s, activityActionFor(s.npc(), currentHour));
}
}
spawned.removeAll(toRemove);
spawned.removeAll(toRemoveBuf);
// PBR-Emissive der verbleibenden NPCs mit aktuellem Ambient aktualisieren
if (dayNight != null) {
com.jme3.math.ColorRGBA ac = dayNight.getCustomAmbient();
scratchEmissive.set(ac.r, ac.g, ac.b, 1f);
for (SpawnedNpc s : spawned) {
for (Material mat : s.pbrMaterials) {
mat.setColor("Emissive", new com.jme3.math.ColorRGBA(ac.r, ac.g, ac.b, 1f));
mat.setColor("Emissive", scratchEmissive);
}
}
}
// IDs der bereits gespawnten NPCs für schnelle Prüfung
Set<String> spawnedIds = new HashSet<>();
spawnedIdsBuf.clear();
for (SpawnedNpc s : spawned) {
spawnedIds.add(s.npc().getCharacterId());
spawnedIdsBuf.add(s.npc().getCharacterId());
}
// Neue NPCs einladen wenn in Reichweite
for (NPC npc : allNpcs) {
if (spawnedIds.contains(npc.getCharacterId())) continue;
if (spawnedIdsBuf.contains(npc.getCharacterId())) continue;
Vector3f pos = resolveRoutinePosition(npc, currentHour);
if (pos == null) continue;
if (dist2d(playerPos, pos) > LOAD_RADIUS) continue;
@@ -292,8 +299,9 @@ public class WorldNpcsState extends BaseAppState {
de.blight.game.scene.WorldScene.collectPbrMaterials(vis, s.pbrMaterials);
if (dayNight != null && !s.pbrMaterials.isEmpty()) {
com.jme3.math.ColorRGBA ac = dayNight.getCustomAmbient();
scratchEmissive.set(ac.r, ac.g, ac.b, 1f);
for (Material mat : s.pbrMaterials) {
mat.setColor("Emissive", new com.jme3.math.ColorRGBA(ac.r, ac.g, ac.b, 1f));
mat.setColor("Emissive", scratchEmissive);
}
}
spawned.add(s);
@@ -482,8 +490,8 @@ public class WorldNpcsState extends BaseAppState {
if (rotNpc == null) return;
rotElapsed += tpf;
float p = FastMath.clamp(rotElapsed / ROT_DURATION, 0f, 1f);
Quaternion q = new Quaternion().slerp(rotFrom, rotTo, p);
rotNpc.visual().setLocalRotation(q);
scratchQ.slerp(rotFrom, rotTo, p);
rotNpc.visual().setLocalRotation(scratchQ);
if (p >= 1f) rotNpc = null;
}
@@ -492,8 +500,8 @@ public class WorldNpcsState extends BaseAppState {
revertTimer += tpf;
if (revertTimer < revertDelay) return;
float p = FastMath.clamp((revertTimer - revertDelay) / REVERT_DURATION, 0f, 1f);
Quaternion q = new Quaternion().slerp(revertFrom, revertTo, p);
revertNpc.visual().setLocalRotation(q);
scratchQ.slerp(revertFrom, revertTo, p);
revertNpc.visual().setLocalRotation(scratchQ);
if (p >= 1f) revertNpc = null;
}