Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Background model loading #107

Open
rpavlik opened this issue Jan 31, 2013 · 2 comments
Open

Background model loading #107

rpavlik opened this issue Jan 31, 2013 · 2 comments
Assignees

Comments

@rpavlik
Copy link
Member

rpavlik commented Jan 31, 2013

(Happy to help on this one)

There are two approaches, as far as I can tell, but you can conveniently use the same C++ code to do both. Here are the two lua functions:

This first one requires that the caller be in a frame action, but blocks that particular action while the spinning takes place until loading is complete.

hugemodel = function(fn)
    local modelLoader = vrjLua.ModelLoader(fn)
    -- Attach your spinny thing to some convenient place here - note that you'd have to do
    -- this arbitrarily (RelativeTo.Room or something)
    while not modelLoader.finished do
        Actions.waitForRedraw()
    end
    -- remove your spinny here
    return modelLoader.node
end

The second one does a swap behind the scenes: it returns a group node immediately, which initially has the spinner attached. It launches its own frame action to watch the model loader and replaces the spinner with the model when it's done.

backgroundModel = function(fn)
    local modelLoader = vrjLua.ModelLoader(fn)
    local temp = osg.Group()
    local mySpinner = CreateSpinner()
    temp:addChild(mySpinner)
    Actions.createFrameAction(
        function()
            while not modelLoader.finished do
                Actions.waitForRedraw()
            end
            temp:removeChild(mySpinner)
            temp:addChild(modelLoader.node)
        end
    )
    return temp
end

Interface for vrjLua.ModelLoaderThread:

This would be a C++ class that starts a thread (vpr::Thread - pass a functor) and has two main members: an osg node and a bool (might be redundant?). The object owns the thread, and has appropriate thread-safety around the two members. The thread would load the file passing ReaderWriter::Options that result in the complete, blocking loading of the model - making this the thread that blocks instead of the draw thread. Upon completion, the thread would pass the node back to the model loader, set the flag (which might be redundant - a non-null pointer might work, although the load method returns a null pointer in case of failure), and exits.

@ghost ghost assigned lpberg Jan 31, 2013
@rpavlik
Copy link
Member Author

rpavlik commented Jan 31, 2013

@rpavlik
Copy link
Member Author

rpavlik commented Jan 31, 2013

This is one rough approximation of how the modelloader might look. Not 100% sure that the semaphore trick I do is valid - plain old mutexes and a flag is probably safer.

class ModelLoader;

namespace {
    class ModelLoadFunctor {
        public:
            ModelLoadFunctor(std::string const& fn, ModelLoader & l) : _fn(fn), _loader(l) {}

            void operator()() {
                osg::ref_ptr<osg::Object> obj = osgDB::readObjectFile( bla bla bla make it fully load here)
                if (obj) {
                    l.reportSuccess(obj);
                } else {
                    l.reportFailure();
                }
            }

        private:
            std::string _fn;
            ModelLoader & l;
    };
} // end of namespace


class ModelLoader {
    public:
        ModelLoader(std::string const& fn)
            : _objProtection(0) // start with resource "unavailable", aka initially held by the functor 
            , _obj() {
            // Spawn and run thread
            thread.reset(new vpr::Thread(ModelLoadFunctor(fn, *this)));
        }

        /// bind this one
        bool isFinished() {
            vpr::Guard<vpr::Semaphore> myGuard(_objProtection, false /*don't block */);
            // If we got it, then the thread is done.
            return myGuard.locked();
        }

        /// bind this one too
        osg::ref_ptr<osg::Object> getObject() {
            vpr::Guard<vpr::Semaphore> myGuard(_objProtection, false /*don't block */);
            // If we got it, then the thread is done.
            if (myGuard.locked()) {
                return _obj;
            }

            // return null pointer if thread isn't done
            return osg::ref_ptr<osg::Object>();     
        }
    private:
        friend class ModelLoadFunctor;

        void reportSuccess(osg::ref_ptr<osg::Object> o) {
            _obj = o;
            _objProtection.release();
        }
        void reportFailure() {
            _objProtection.release();
        }
        vpr::Semaphore _objProtection; 
        osg::ref_ptr<osg::Object> _obj;
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants