Hello @aimidi,
without wanting to be rude: Please do not go shopping for answers in other topics. As lined out in the Forum Guidelines we talk internally about every posting marked as 'As-as-Question' anyways, so you will always get the input of the whole team (with a flavor of whoever takes on a specific topic). This will also derail other topics, as your question was only related at best faintly to the other topic.
Since I am now already here:
My proposal was a GeDialog
with a timer and I have skteched up a very simple example below. The major hurdle for what you want to do is the fact that Cinema 4D has no event loop optimized for such thing, which is why you need your own, a timer.
What you should not do IMHO is hook into the standard messages of Cinema, as this will make it very hard to get a lag-free input, but I might be wrong, I have not tried. Due to the inaccuracy of clocked functions calls (be it GeDialog
or just some system clock) when going the 100 ms, i.e., 10 FPS threshold, you also must work with deltas and cannot 'just move around a camera'.
In my example I also did not deal with recording stuff into key-frames. I would not do that while receiving user inputs, but just store the raw data instead (the camera matrices) and then write the data once the 'record' session has ended. Finally, it would be better to implement the whole thing in C++ because performance is key here and move the input (keyboard, gamepad, etc) polling away from the main thread into its own thread and then only update the viewport from the main thread based on that data. You will need a third-party input device library for that as our input polling is bound to the main thread.
Finally, when writing the data, you will have to optimize/quantize it, as putting in 1000 keys for moving 1000 frames in a straight line is a bit non-optimal ^^
Cheers,
Ferdinand
The test file:City.c4d
The result:
The code:
import c4d
import time
class CameraControlDialog (c4d.gui.GeDialog):
"""
"""
# Translation velocity per second.
TRL_VELOCITY = 500
# Rotation velocity per second.
ROT_VELOCITY = 0.01
def CreateLayout(self):
"""
"""
self.SetTitle("Camera Control Dialog")
return True
def InitValues(self):
"""
"""
# Store the document, the render base draw (likely being the
# perspective one, you could also retrieve the active base draw
# instead) and the camera attached to it.
self._doc = doc
self._bd = doc.GetRenderBaseDraw()
self._camera = (self._bd.GetSceneCamera(doc) or
self._bd.GetEditorCamera(doc))
# Set the timer of the dialog to 40 ms, i.e., 25 FPS. We are
# stretching here the limits of what is possible, because
# GeDialog.Timer is not designed for such short intervals and we
# will have to anticipate a very large error in the actual call
# intervals.
self.SetTimer(40)
self._time = None
return True
@staticmethod
def PollKey(key):
"""
"""
res = c4d.BaseContainer()
if not c4d.gui.GetInputState(c4d.BFM_INPUT_KEYBOARD, ord(key), res):
return False
return res[c4d.BFM_INPUT_VALUE] == 1
def SetTraslationVelocity(self, mg, dt):
"""
"""
return mg.v3 * dt * self.TRL_VELOCITY
def SetRotationVelocity(self, mg, dt):
"""
"""
return mg *= c4d.utils.MatrixRotY(dt * self.ROT_VELOCITY)
def Timer(self, msg):
"""
"""
wKey, aKey, sKey, dKey = (self.PollKey("W"), self.PollKey("A"),
self.PollKey("S"), self.PollKey("D"))
# When no keys are pressed exit and clean out the time.
if sum((wKey, aKey, sKey, dKey)) == False:
self._lastTime = None
return None
# Initialize the time for the first frame a key is being pressed.
if self._lastTime is None:
self._lastTime = time.time()
return None
# The delta between now and the last evaluation
dt = time.time() - self._lastTime
mg = self._camera.GetMg()
if aKey:
mg = self.SetRotationVelocity(mg, dt)
elif dKey:
mg = self.SetRotationVelocity(mg, -dt)
if wKey:
mg.off += self.SetTraslationVelocity(mg, dt)
elif sKey:
mg.off += self.SetTraslationVelocity(mg, -dt)
self._lastTime = time.time()
self._camera.SetMg(mg)
c4d.DrawViews(c4d.DRAWFLAGS_ONLY_ACTIVE_VIEW)
if __name__ == '__main__':
global dialog
dialog = CameraControlDialog(doc)
dialog.Open(c4d.DLG_TYPE_ASYNC)