VRML / X3D time origin considered uncomfortable

In short, for single-player games, the current idea of time origin ("January 1, 1970") in VRML / X3D is uncomfortable. Castle Game Engine complies with VRML/X3D standard in this regard anyway, although you can change it by using our extension KambiNavigationInfo.timeOriginAtLoad. Also the (deprecated now) TCastlePrecalculatedAnimation animations will always start playing from their begin-time, usually 0.0.

What's the problem? VRML/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.

As far as I'm concerned, this is a bad idea. Any general-purpose VRML/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 VRML/X3D files start playing immediately after file is loaded, and VRML/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 VRML/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?

  1. 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 view3dscene don't really supply real-world time. Although initial VRML/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 VRML/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.

  2. Another trouble is that VRML/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

    That's why view3dscene allows VRML/X3D author to change VRML/X3D time origin by KambiNavigationInfo.timeOriginAtLoad. This allows you to use startTime = 0 predictably. Also, user has menu item "Animation -> Rewind to the Beginning", for testing.

    It allows to simply write:

    KambiNavigationInfo { timeOriginAtLoad TRUE }
    DEF MyTimeSensor TimeSensor { loop TRUE }
    ROUTE MyTimeSensor.time TO ... # this starts from zero and grows

    When using my engine to develop your own games, you can simply start VRML/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.

  3. 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 VRML/X3D time for user: for example when recording the VRML/X3D animation (view3dscene 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, view3dscene displays time as World time: load time + %f = %f for standard VRML/X3D files (that do not use timeOriginAtLoad = TRUE). This way user sees also the simpler time (since load).

  4. A minor problem is also that user doesn't expect different behavior of VRML/X3D world depending on the real-world time at which it is loaded. True, it opens some interesting possibilities (VRML/X3D world may adjust to real-world day/night state for example), but also some nightmarish scenarios ("VRML/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 VRML/X3D world to the user, starting VRML/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 KambiNavigationInfo.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.)