Performance Tweaks
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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). */
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user