Hello Thaumaturge!
The fact is, the Panda3D window runs at a constant 60 FPS with plenty of headroom (4000+ FPS uncaped). At 60 FPS, I get consistent delta time between ~0.016 and ~0.017.
I tried:
newCamPos = camPos + (camTarget - camPos) * min(dt * camMovSpeed, 1)
in case dt * camMovSpeed ever goes above 1, but it did not change anything.
newCamPos = camPos + (camTarget - camPos) * camMovSpeed
with camMoveSpeed < 1, it did not help.
newPlayerPos = self.player.getPos() + movementVec * speed * 0.016
again, the issue is still there.
But when I tried removing delta time from both the newPlayerPos and newCamPos formula, I was suprised to see the issue fixed. I guess the issue is cause by dt? The annoying thing is that without delta time, movements become FPS dependant which I would like otherwise.
A thing I have started to notice too: the jitter seems to happen every X frame, the time interval between each micro-teleportation seems to stay constant.
Here is my full file for you to run the app:
from direct.showbase.ShowBase import ShowBase
from panda3d.core import WindowProperties
from panda3d.core import load_prc_file_data
from panda3d.core import Vec3
from panda3d.core import CardMaker, PNMImage, Texture
import random
from math import exp
# load_prc_file_data('', 'sync-video f\nshow-frame-rate-meter t')
# load_prc_file_data('','win-size 1280 720')
class Test(ShowBase):
def __init__(self, fStartDirect=True, windowType=None):
super().__init__(fStartDirect, windowType)
self.disable_mouse()
self.player = self.render.attachNewNode("player")
self.player.setPos(0, 0, 0)
self.model = self.loader.loadModel('teapot')
self.model.setScale(self.model.getScale()*0.1)
self.model.reparentTo(self.player)
props = WindowProperties()
props.setCursorHidden(True)
self.win.requestProperties(props)
self.accept("escape", self.userExit)
self.accept('raw-w', self.updateKeyMap, ['forward', True])
self.accept('raw-w-up', self.updateKeyMap, ['forward', False])
self.accept('raw-a', self.updateKeyMap, ['left', True])
self.accept('raw-a-up', self.updateKeyMap, ['left', False])
self.accept('raw-s', self.updateKeyMap, ['backward', True])
self.accept('raw-s-up', self.updateKeyMap, ['backward', False])
self.accept('raw-d', self.updateKeyMap, ['right', True])
self.accept('raw-d-up', self.updateKeyMap, ['right', False])
self.keyMap = {
"forward": False,
"backward": False,
"left": False,
"right": False,
}
self.camPivot = self.render.attach_new_node('camPivot')
self.camera.reparent_to(self.camPivot)
# self.camLens.setFov(10)
self.zoomLevel = 0
self.cameraOffset = Vec3(0, -5, 10)
self.generateGround()
self.taskMgr.add(self.update, 'update', sort=100)
def update(self, task):
dt = globalClock.getDt()
forward = Vec3(0, 1, 0)
right = Vec3(1, 0, 0)
speed = 10
movementVec = Vec3(0, 0, 0)
maxZoomOut = 5
if self.keyMap['forward']:
movementVec += forward
if self.keyMap['backward']:
movementVec -= forward
if self.keyMap['right']:
movementVec += right
if self.keyMap['left']:
movementVec -= right
movementVec.normalize()
newPlayerPos = self.player.getPos() + movementVec * speed * dt
self.player.setPos(newPlayerPos)
camMovSpeed = 10
camTarget = self.player.getPos() + self.cameraOffset
camPos = self.camPivot.getPos()
print(dt)
newCamPos = camPos + (camTarget - camPos) * camMovSpeed *dt
self.camera.lookAt(self.camera.getPos() - self.cameraOffset)
# self.camPivot.setPos(self.player.getPos() + self.cameraOffset) # Hard follow (works fine)
self.camPivot.setPos(newCamPos)
self.zoomLevel = maxZoomOut if movementVec.length() > 0 else 0
# self.updateFov(dt)
return task.cont
def updateFov(self, dt):
zoomInSpeed = 5
zoomOutSpeed = 1
fov = 10
targetFov = fov + self.zoomLevel
currentFov = self.camLens.getFov()[0]
if targetFov > currentFov:
smoothingSpeed = zoomOutSpeed
else:
smoothingSpeed = zoomInSpeed
currentFov += (targetFov - currentFov) * (1 - exp(-smoothingSpeed * dt))
# self.camLens.setFov(currentFov + (targetFov - currentFov) * min(zoomSpeed * dt, 1))
self.camLens.setFov(currentFov)
def generateGround(self):
size = 256 # texture resolution
img = PNMImage(size, size)
for x in range(size):
for y in range(size):
r = 0.25 + random.uniform(-0.05, 0.05)
g = 0.7 + random.uniform(-0.1, 0.1)
b = 0.25 + random.uniform(-0.05, 0.05)
r = min(max(r, 0), 1)
g = min(max(g, 0), 1)
b = min(max(b, 0), 1)
img.setXel(x, y, r, g, b)
texture = Texture("groundTexture")
texture.load(img)
texture.setWrapU(Texture.WM_repeat)
texture.setWrapV(Texture.WM_repeat)
cm = CardMaker("ground")
cm.setFrame(-50, 50, -50, 50)
cm.setUvRange((0, 0), (10, 10))
ground = self.render.attachNewNode(cm.generate())
ground.setP(-90)
ground.setZ(0)
ground.setTexture(texture)
def updateKeyMap(self, key, value):
self.keyMap[key] = value
app = Test()
app.run()
How could I make the programm run without jitters while conserving dt?
Edits:
typos
commented out prc file data loaders
update on dt