Jump to content

How to create a timeline


Dad72
 Share

Recommended Posts

The drawing part is up to you.  I made my own, un-settable, always going forward TimelineControl class.  It is implemented using a scene level after render.  The purposes are:

  • to pause and resume at a system wide basis.
  • reduce error among the before renders of different meshes, if they each called BABYLON.Now themselves.
  • switch to fixed frame rates to generate marketing animation (mp4). not working & not going to be public, so just a hook.
  • handle tab switching / smooth resuming.

I have no need to arbitrary set a time.  This is a game platform / real time environment.  To go backwards in a automatic, scene level way would require a system which create an audit trail of everything ever changed.

My system s not yet public, so here is the source for this file:

/// <reference path="./BeforeRenderer.ts"/>
module QI {
    /**
     * This class is used to provide a way to render at a precise frame rate, as opposed to realtime,
     * as well as system level play - pause.
     */
    export class TimelineControl{
        // ======================================= Mode Control ======================================
        private static _afterRenderAssigned: boolean;
        private static _manualFrameRate : number;
        private static _isRealtime = true;
        private static _now = 0;
        private static _lastRun = 0;
        private static _frameID = 0; // useful for new in frame detection
        public  static CHANGED_TABS_THRESHOLD = 500; // msec
        
        private static _engine : BABYLON.Engine;  // only used for MP4 recordings
        public  static MP4Worker : Worker;
                
        /** called by POV.BeforeRenderer */
        public static initialize(scene : BABYLON.Scene){
            if (!TimelineControl._afterRenderAssigned){
                scene.registerAfterRender(TimelineControl._manualAdvanceAfterRender);     
                
                // built-in hooks for CocoonJS
                if (navigator.isCocoonJS){
                    Cocoon.App.on("activated" , TimelineControl.resumeSystem );
                    Cocoon.App.on("suspending", TimelineControl.pauseSystem  );
                }
                
                TimelineControl._engine = scene.getEngine();
                TimelineControl._afterRenderAssigned = true;
                var logMsg = "Queued Interpolation Timeline Control system initialized, version: " + BeforeRenderer.Version;
                BABYLON.Tools.Log(logMsg);      
            }            
        }
        
        public static change(isRealTime : boolean, rateIfManual = 24) : void {
            TimelineControl._isRealtime = isRealTime;
            TimelineControl._manualFrameRate = rateIfManual;
        }
        
        private static _manualAdvanceAfterRender() : void {
            if (!TimelineControl._systemPaused){
                // The system might not officially have been paused, rather browser tab switched & now switched back
                if (BABYLON.Tools.Now - TimelineControl._lastRun > TimelineControl.CHANGED_TABS_THRESHOLD) {
                    TimelineControl.resumeSystem();
                
                }else{
                    TimelineControl._frameID++;
                    if (TimelineControl._isRealtime){ 
                        TimelineControl._now = BABYLON.Tools.Now;
                        
                    } else {
                        TimelineControl._now += 1000 / TimelineControl._manualFrameRate; // add # of millis for exact advance
                    
                        if (TimelineControl.MP4Worker){
                            var screen = TimelineControl._engine.readPixels(0, 0, TimelineControl._engine.getRenderWidth(), TimelineControl._engine.getRenderHeight() );
                            // . . .
                        }
                    }
                }
            }
            TimelineControl._lastRun = BABYLON.Tools.Now;
        }
        
        public static sizeFor720 () : void { TimelineControl._sizeForRecording(1280,  720); }
        public static sizeFor1080() : void { TimelineControl._sizeForRecording(1920, 1080); }
        
        private static _sizeForRecording(width : number, height : number){
            TimelineControl._engine.setSize(width, height);
        }
        
        // =========================================== Gets ==========================================
        public static get manualFrameRate() : number  { return TimelineControl._manualFrameRate; }
        public static get isRealtime     () : boolean { return TimelineControl._isRealtime; }
        public static get Now            () : number  { return TimelineControl._now; }
        public static get FrameID         () : number  { return TimelineControl._frameID; }
        // =================================== SYSTEM play - pause ===================================
        // pause & resume statics
        private static _systemResumeTime = 0;
        private static _systemPaused = false;
        
        /** system could be paused at a higher up without notification; just by stop calling beforeRender() */
        public static get isSystemPaused() : boolean { return TimelineControl._systemPaused; }
        public static pauseSystem() : void { TimelineControl._systemPaused = true; }        
        
        public static resumeSystem() : void {
            TimelineControl._systemPaused = false;
            TimelineControl._systemResumeTime = TimelineControl.Now;
        }
        public static get SystemResumeTime() : number { return TimelineControl._systemResumeTime; }
    }
}

 

Link to comment
Share on other sites

Not as single file. I fished it out of the file from the eclipse add-in compiler:

var QI;
(function (QI) {
    /**
     * This class is used to provide a way to render at a precise frame rate, as opposed to realtime,
     * as well as system level play - pause.
     */
    var TimelineControl = (function () {
        function TimelineControl() {
        }
        /** called by POV.BeforeRenderer */
        TimelineControl.initialize = function (scene) {
            if (!TimelineControl._afterRenderAssigned) {
                scene.registerAfterRender(TimelineControl._manualAdvanceAfterRender);
                // built-in hooks for CocoonJS
                if (navigator.isCocoonJS) {
                    Cocoon.App.on("activated", TimelineControl.resumeSystem);
                    Cocoon.App.on("suspending", TimelineControl.pauseSystem);
                }
                TimelineControl._engine = scene.getEngine();
                TimelineControl._afterRenderAssigned = true;
                var logMsg = "Queued Interpolation Timeline Control system initialized, version: " + QI.BeforeRenderer.Version;
                BABYLON.Tools.Log(logMsg);
            }
        };
        TimelineControl.change = function (isRealTime, rateIfManual) {
            if (rateIfManual === void 0) { rateIfManual = 24; }
            TimelineControl._isRealtime = isRealTime;
            TimelineControl._manualFrameRate = rateIfManual;
        };
        TimelineControl._manualAdvanceAfterRender = function () {
            if (!TimelineControl._systemPaused) {
                // The system might not officially have been paused, rather browser tab switched & now switched back
                if (BABYLON.Tools.Now - TimelineControl._lastRun > TimelineControl.CHANGED_TABS_THRESHOLD) {
                    TimelineControl.resumeSystem();
                }
                else {
                    TimelineControl._frameID++;
                    if (TimelineControl._isRealtime) {
                        TimelineControl._now = BABYLON.Tools.Now;
                    }
                    else {
                        TimelineControl._now += 1000 / TimelineControl._manualFrameRate; // add # of millis for exact advance
                        if (TimelineControl.MP4Worker) {
                            var screen = TimelineControl._engine.readPixels(0, 0, TimelineControl._engine.getRenderWidth(), TimelineControl._engine.getRenderHeight());
                        }
                    }
                }
            }
            TimelineControl._lastRun = BABYLON.Tools.Now;
        };
        TimelineControl.sizeFor720 = function () { TimelineControl._sizeForRecording(1280, 720); };
        TimelineControl.sizeFor1080 = function () { TimelineControl._sizeForRecording(1920, 1080); };
        TimelineControl._sizeForRecording = function (width, height) {
            TimelineControl._engine.setSize(width, height);
        };
        Object.defineProperty(TimelineControl, "manualFrameRate", {
            // =========================================== Gets ==========================================
            get: function () { return TimelineControl._manualFrameRate; },
            enumerable: true,
            configurable: true
        });
        Object.defineProperty(TimelineControl, "isRealtime", {
            get: function () { return TimelineControl._isRealtime; },
            enumerable: true,
            configurable: true
        });
        Object.defineProperty(TimelineControl, "Now", {
            get: function () { return TimelineControl._now; },
            enumerable: true,
            configurable: true
        });
        Object.defineProperty(TimelineControl, "FrameID", {
            get: function () { return TimelineControl._frameID; },
            enumerable: true,
            configurable: true
        });
        Object.defineProperty(TimelineControl, "isSystemPaused", {
            /** system could be paused at a higher up without notification; just by stop calling beforeRender() */
            get: function () { return TimelineControl._systemPaused; },
            enumerable: true,
            configurable: true
        });
        TimelineControl.pauseSystem = function () { TimelineControl._systemPaused = true; };
        TimelineControl.resumeSystem = function () {
            console.log("system resumed");
            TimelineControl._systemPaused = false;
            TimelineControl._systemResumeTime = TimelineControl.Now;
        };
        Object.defineProperty(TimelineControl, "SystemResumeTime", {
            get: function () { return TimelineControl._systemResumeTime; },
            enumerable: true,
            configurable: true
        });
        TimelineControl._isRealtime = true;
        TimelineControl._now = 0;
        TimelineControl._lastRun = 0;
        TimelineControl._frameID = 0; // useful for new in frame detection
        TimelineControl.CHANGED_TABS_THRESHOLD = 500; // msec
        // =================================== SYSTEM play - pause ===================================
        // pause & resume statics
        TimelineControl._systemResumeTime = 0;
        TimelineControl._systemPaused = false;
        return TimelineControl;
    })();
    QI.TimelineControl = TimelineControl;
})(QI || (QI = {}));

Here is some of the before render which does the pause checking.  Basically the rest uses TimelineControl.Now, instead.  Individual mesh instances can also be paused,

/**
 * beforeRender() registered to this._mesh.  Public for sub-classing in MORPH Module.
 */
public _incrementallyMove() : void {
    // test for active instance pausing, either instance of entire system
    if (this._instancePaused || TimelineControl.isSystemPaused){
        if (this._currentStepInSeries) this._currentStepInSeries.pause();
        return;
    }
            
    // system active resume test
    if (this._lastResumeTime < TimelineControl.SystemResumeTime){
        this._lastResumeTime = TimelineControl.SystemResumeTime;
        this.resumeInstancePlay(); // does nothing when this._currentStepInSeries === null
    }
    ...
}

// ================================== INSTANCE play - pause ==================================
private _lastResumeTime = 0; // for passive detection of game pause
private _instancePaused = false;
        
public isInstancePaused() : boolean { return this._instancePaused; }
public pauseInstance(){ this._instancePaused = true; }       
        
public resumeInstancePlay() : void {
    this._lastResumeTime = TimelineControl.Now; 
    this._instancePaused = false;
    // cause Event in progress to calibrate for smooth resume
    if (this._currentStepInSeries !== null) this._currentStepInSeries.resumePlay();
}

 

Link to comment
Share on other sites

Hi Dad72,

 

If you are planning on implimenting this into the CastorGUI extension (which I really like and am using a great deal now,) I would want to use my own image files to create my own style of a graphic timeline. So if this is the case, then providing a function which I can format and insert my own style timeline would be fantastic. However, if you're not looking at implimenting this into the CastorGUI extension currently, please ignore this suggestion. However having the ability to create not only a timeline, but a control which I could use for a timeline, video control, etc. would be a great addition to CastorGUI. I'm building a video timeline control currently, and it's not fun at all. :wacko:

Cheers,

DB

Link to comment
Share on other sites

hi,

In fact I realized it for another project I built editor to easily create small animation 3D movie. I did not think to include it in CastorGUI. But I'm not totally against the idea of doing it, but when I have something functional, not before.

 

Link to comment
Share on other sites

Hi,

Perhaps it's not necessary to include in the CastorGUI extension, as I can use your GUIslider to create most everything I might need to build controls for video, timelines, etc. But I thought I would ask if you might be adapting something more for CastorGUI and thought to mention that in any GUI I build, I prefer not to be forced to use preset images and styles to my GUIs.

Again, thanks for CastorGUI and all of the tools you have built for the BJS community.

Cheers,

DB

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...