Sorry, it's a bit long story but I hope you can help me.
First of all: I have an entity class called SceneObject. The SceneObject has components (eg. RenderableMesh, Camera) and transformation data (position, rotation, scale). When I transform an object it is queued for update. So the world matrix and other world-space related components (eg. Bounds) are not calculated immediately. This is an optimization because it's overkill to recalculate everything when a transformation data is changed. And the update should be done in a recursive-way because the parent's data must be up-to-date. So I ended up using a "transformed" queue.
So let's talk about the update sequence which causes the problems for me:
fire input events call PrePhysics (scripts) update animation (frame, bone matrices) begin fixed steps update physics end fixed steps calculate object matrices (world, worldInv) call PostPhysics (scripts) calculate object matrices again (world, worldInv) update cameras (view, proj)
- First the input events are fired to the handlers. This way a script can eg. start to move an object (add force to a rigidbody or set the position directly, skipping the physics engine).
- PrePhysics() is called before the fixed update step. This is a good place to update everything which is not related to the input but eg. moves the object which means it uses the physics system. But everything else can be handled here, audio, counters, etc.
- After that, update the animation system: advance time, handle animation tracks and modify bones - and updates their matrices.
- Then the physics step is coming at a fixed rate. The collisions are handled here.
- Now update the object's transform data: world matrix, world inverse matrix, world-space bounding box and so on. This is the place where the "transformed" queue is processed. Of course when the queue is processed, it is cleared as well.
- Execute PostPhysics(): here the already transformed objects are in a valid, up-to-date state.
- Because in the PostPhysics() the objects can be transformed, the dirty objects have to be re-updated.
- Finally update the camera data, like view matrix, projection matrix, view frustum.
- Update order of scripts: a single PrePhysics() is called before the physics engine step. The execution order is undefined. However if a script requires a data from another script but want to use the physics engine as well, that's a problem. This probably can be solved by using multiple queues and set up dependencies.
- Every object has two types of transform data: local and absolute. Let's see an example: I set the local position of the object A. Suppose that the object A is a child of object B. If I query the absolute position of object A, it gives me the up-to-date, valid data, because it's calculated at the time of the call. However the matrices, bounds, etc. are not updated yet. (pseudo-code at the end of the post because I cannot format it here properly).
- The same goes for the camera-related data. The view matrix is not updated until the end of the current frame, so the view matrix is always out of date.
- What if an animation depends on post-physics status? Eg. if the player pressed the "W" key to move forward but the physics engine does not allow it. The animation should be the "Idle" and not the "Run". I don't know if there is any point of having the animation update before the physics engine step.
- If an object is collided with another object, the physics system fires an event (like Unity's or UE4's events). This is intended to be used for stopping animations, change material properties and so on. But if the script moves the object here, doesn't it generate an "infinite loop"? The object tried to move forward, collided with something, event fired, then tries to move again.
- In short: the components/systems depend on the others.
- Should I use dirty-flagged calculations? This way when an object's eg. world matrix is requested, it is calculated at the time of the call instead of a pre-defined time. However this way I cannot use const functions so if I have a constant reference of the object, I cannot get the up-to-date world matrix anyway.
Slightly off-topic question:
- How UE4 handle this problem? It has a single Event Tick.
Pseudo-code of the problem #2 and #3
Object B; Object A = child of B; A.SetLocalPosition(...); A.GetAbsolutePosition(); // valid, up-to-date A.GetWorld(); // invalid, previous-frame data A.GetBounds(); // invalid, previous-frame data Camera.GetView(); // invalid, previous-frame data
Edit (Temp Solution):
For now I've added a public function for each component which has/had this problem. I'm not sure if I should solve this problem globally or use this dirty-hack.
So what if a script sets the local position and after that line of code, it needs the up-to-date world matrix? An "Update" function can now be called. This function will find the topmost node that is transformed and start the recursion from that node, and updates the world matrices. If the Update is not called but the GetWorld, then the previos Update result is returned.
The same mechanic is implemented for the BoundComponent and the CameraComponent. So in short: a force-update can be requested by the user as well as by the component manager.
This seems to be a dirty-hack for me, but it probably solves some problems.