Thanks for the suggestion about modal operator… I looked at the documentation and set up this simple script to run a simulation of a cube bouncing back and forth on X axis. It uses a timer to keep things moving. The keypress event works great to abort the simulation.
import bpy
class ModalOperator(bpy.types.Operator):
bl_idname = "object.modal_operator"
bl_label = "Simple Modal Operator"
x_pos=0
x_limit=2
direction_forward=True
_timer=None
cube_object=None
def __init__(self):
print("Start")
def __del__(self):
print("End")
def cancel(self, context):
wm = context.window_manager
wm.event_timer_remove(self._timer)
def execute(self, context):
print(context.object.location.x)
wm = context.window_manager
self._timer = wm.event_timer_add(0.1, window=context.window)
wm.modal_handler_add(self)
return {'FINISHED'}
def modal(self, context, event):
if event.type in {'RIGHTMOUSE', 'ESC'}: # Cancel
self.cancel(context)
return {'CANCELLED'}
elif event.type == 'TIMER':
if self.direction_forward==True:
self.x_pos=self.x_pos+0.1
if self.x_pos>=self.x_limit:
self.direction_forward=False
else:
self.x_pos=self.x_pos-0.1
if self.x_pos<=-self.x_limit:
self.direction_forward=True
print(self.x_pos)
self.cube_object.location.x=self.x_pos
return {'RUNNING_MODAL'}
def invoke(self, context, event):
cube_name="simcube"
# find object by name to reuse if possible
self.cube_object = bpy.data.objects.get(cube_name)
if self.cube_object is None:
if self.cube_object==None:
bpy.ops.mesh.primitive_cube_add(size=2.0,
enter_editmode=False,
location=( self.x_pos, 0, 0))
self.cube_object=bpy.context.view_layer.objects.active
self.cube_object.name=cube_name
self.execute(context)
return {'RUNNING_MODAL'}
bpy.utils.register_class(ModalOperator)
# test call
bpy.ops.object.modal_operator('INVOKE_DEFAULT')
My problem is - the frame rate is limited by the timer…
How do I make it so it goes as fast as the processor can handle - not using a timer just poling for ‘ESC’ keypress event similar to fluid baking… The number of FPS when fluid baking is not limited by a timer interval, but limited by how fast your CPU can process the simulation…
Ideally I would be able to poll for keypress events while running a tight loop inside the execute function
Hi - thanks for the reply - this solution is still timer based. Speeding up the timer to 60fps won’t help because some frames will be need longer than 60fps and some will be less… It’s for baking a simulation which has a variable frame rate unlike a video game. Like a fluid simulation the first few frames will be fast because there isn’t much happening - and as the simulation progresses it will get way slower because the scene is much more complicated.
The objective is to maximize CPU utilization while showing some view port updates with the ability to abort.
In C++ UI programming - one way to do this would be to use a custom event ID and modal function would generate a new event each loop - event based rather than time based. Another way would be a separate thread(s) for the simulation and the worker thread sends update events to the main UI thread.
In this line dynamicsWorld->stepSimulation(1.f / 60.f, 10); you can see that you can pass a delta time in the physics system. Putting this in a straight loop, is the same as running the sim “offline”.
The modal operator in Blender in this case is independent of the physics system as you say, only doing a heartbeat update once per second just to see progress or control the running state as you mention.
However the physics simulator must run in a thread so the intense CPU loop won’t block the GUI. It has been mentioned that Threading is unsupported (it works great actually - but is only a matter to cause random crashes) so it means that alternatively you will have to choose Queues instead.