|
Note
|
This text was written a long time ago. After ~15-20 years since writing this, it is now 2026, and my (Michalis) opinion on this topic is:
-
Somewhat the same. I would like to do some things differently, e.g. maybe TimeSensor just should not have startTime/stopTime, only start / stop events with bool input.
-
… But the priority of the problem described here is much smaller than I imagined.
So I still think, in 2026, that using SFTime for some things in X3D is an issue. Current state not only means we have huge time values where we don’t have to, e.g. when advancing TimeSensor time (though this is somewhat solved by double precision, though you don’t want to do this in shaders). But also this time definition doesn’t allow time pausing / slowing-down, which is possible in non-multi-player experiences, it’s even an obvious feature in single-player games. The fact that to do this "obvious feature" we need to actually break from X3D standard (our time is not real time) is weird.
That being said, we have shown in our engine that this issue can be mostly hidden inside. The X3D time processing is hidden in TCastleScene animation processing, and most external code doesn’t care, it doesn’t make engine API really worse. Your game code doesn’t access any weird time value that is "like real-life since 1970 but with game pauses".
So → this is a problem, but not a critical one.
|
In short, for single-player games, the current idea of time origin ("January 1, 1970") in X3D is uncomfortable. Castle Game Engine complies with X3D standard in this regard anyway, although you can change it by using our extension NavigationInfo.timeOriginAtLoad.
What’s the problem? X3D have an idea that time stored in SFTime corresponds to a real-world time. More precisely, it’s the number of seconds since 00:00:00 GMT January 1, 1970. This affects time-dependent nodes behavior, like TimeSensor and MovieTexture, and timestamps generated by events.
For experiences where "game time" is unrelated to real-life time, this is a bad idea. Any general-purpose X3D browser must honor it in some way, not necessarily by using real-world time, but at least by setting initial time to some very large value. Reason: otherwise many animations in X3D files start playing immediately after file is loaded, and X3D authors don’t expect this. Default field values are designed such that a default time-dependent node (with default loop = FALSE) should play one cycle from time 0 to the end of it’s cycle. If a browser starts with real-world time value, this is a very large time value, larger than usual cycle interval, so node will not play at all.
So X3D authors learned to expect that actually "default values for time-dependent nodes mean that node doesn’t play when file is loaded".
Why this state is bad in my opinion?
-
The main problem is that honoring this rule literally would prevent user from pausing the animation. If you continuously supply time values as real-world time, there’s no way to just "pause" the animation. It may not be rendered for some time, but real-world time is always ticking. That’s why it’s called "real" world time after all.
That’s why Castle Game Engine and Castle Model Viewer don’t really supply real-world time. Although initial X3D time is taken from real-world time, it’s not guaranteed to be synchronized with real-world time. As soon as you pause the animation, or open some modal window, time pass is paused, and X3D world time is no longer synchronized with real-world time. This way you can "pause" the animation, which is a very useful feature in our opinion.
-
Another trouble is that X3D authors cannot easily synchronize starting of the animation with loading of the file. startTime = 0 is useless, as "0" means "January 1, 1970". For constantly looping animations (loop = TRUE, rest of the fields as default) this is also a problem, as you have no idea in what stage of the animation you are when loading the file.
And the default outputs of TimeSensor.elapsedTime and TimeSensor.time are incredibly large values. Which means you have to be careful when operating on them. Passing these large values to shaders is usually a bad idea, since they will be rounded to something useless.
Making some "welcome" animation requires you to use tricks to route some sensor like ProximitySensor (positioned to include default viewpoint) to time-dependent node. The trick looks a little ugly, like this:
DEF MyProximitySensor ProximitySensor { size 10000000 10000000 10000000 } # some size that is in practice infinite
DEF MyTimeSensor TimeSensor { loop TRUE }
ROUTE MyProximitySensor.enterTime TO MyTimeSensor.startTime
ROUTE MyTimeSensor.elapsedTime TO ... # this starts from zero and grows
It allows to simply write:
NavigationInfo { timeOriginAtLoad TRUE }
DEF MyTimeSensor TimeSensor { loop TRUE }
ROUTE MyTimeSensor.time TO ... # this starts from zero and grows
When using our engine to develop your own games, you can simply start X3D time from 0.0 (by TCastleSceneCore.ResetTime(0.0)), or to any other value you want. For example, setting it to some large but determined value, like exactly a million, allows you to work correctly with standard animations and at the same time you’re able to express startTime relative to loading time.
-
Large time values are not nice to show to the user. It’s strange to average user to see time value like 1220626229.13 immediately after opening the file. And manual input of such time values is difficult. This is a pity, as sometimes I really have to ask or show X3D time for user: for example when recording the X3D animation (Castle Model Viewer can record animation to the movie, or as a precalculated animation), and for things like Logger node output timestamps.
To remedy this at least a little, Castle Model Viewer displays time as World time: load time + %f = %f for standard X3D files (that do not use timeOriginAtLoad = TRUE). This way user sees also the simpler time (since load).
-
A minor problem is also that user doesn’t expect different behavior of X3D world depending on the real-world time at which it is loaded. True, it opens some interesting possibilities (X3D world may adjust to real-world day/night state for example), but also some nightmarish scenarios ("X3D world crashes with segfault but only when opened ~5 minutues after the midnight" — now imagine you have to debug this :) ).
More sane definition of "time origin" would seem to be "for single-user games, time origin 0.0 is equivalent to the time when browser finished initialization and presented X3D world to the user, starting X3D sensors listening and events processing". (For multi-player games over the network, real-world time or some other server time may be more appropriate indeed.) Actually this is exactly done when our extension NavigationInfo.timeOriginAtLoad = TRUE. This also means that time-dependent node with all fields set as default plays exactly once when the model is loaded — which is actually quite sensible default behavior for me. (You can always set for example startTime = -1 and stopTime = -0.5 to prevent node from playing.)