Updating to 10.1

Stardragon

15-03-2010 18:21:06

Hey there KungfooMasta :-) Just when you thought I'd gone for good, here I am. Mwahahaha, and all that :-D

After a year of hell I'm getting my coding groove on again. Time to finish off Return of the Sentinel, so updating all my libs. Came to update QG and after compiling it up, I get this when I try to compile my game:


1>UI.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: class QuickGUI::MouseCursor * __thiscall QuickGUI::GUIManager::getMouseCursor(void)" (__imp_?getMouseCursor@GUIManager@QuickGUI@@QAEPAVMouseCursor@2@XZ)
1>UI.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: void __thiscall QuickGUI::Root::setDefaultColor(class Ogre::ColourValue const &)" (__imp_?setDefaultColor@Root@QuickGUI@@QAEXABVColourValue@Ogre@@@Z)


Bearing in mind that I'm updating directly from *goes to check* QG 9.05 to 10.1 in one foul swoop I don't expect it to be without incident :-) (I'm also going from Ogre 1.6.3 to 1.7 at the same time, argh.)

Anyway... nice to be back with y'all. Any ideas about the above?

Edit
Actually, gawrsh. I cleaned my solution and wish I hadn't: my output box has just lit up like a pinball machine and what it boils down to is TILT. :? The one that comes up most is the Ogre/QuickGUI ColourValue clash, but it's also saying: 'label_verticalTextAlignment' : is not a member of 'QuickGUI::LabelDesc'. I'm also getting this: QuickGUI::GUIManager::getMouseCursor' : cannot access protected member declared in class 'QuickGUI::GUIManager

I've been glancing over the other posts on the first page and I can't help thinking that if QG isn't going to be renderer agnostic, why redefine Ogre's definitions? Hell, Ogre itself is currently driving me nuts by trying, it seems, to take over the STL: I can't just use vector<> now, since Ogre seems to bitch about it.

Grr.

kungfoomasta

15-03-2010 23:44:46

Hey Stardragon!

The linker errors are because those functions don't exist. You have to access and use the MouseCursor from the Sheet class now. The reason is because the MouseCursor is basically treated as a Sheet property. For example you might have a Sheet that doesn't need a mouse cursor (ie load screen), so you can set the mouse cursor not visible as a Sheet property, and whenever you set that Sheet the cursor will act accordingly. There are other cursor properties that belong to the Sheet, but I can't recall at the moment.

Regarding my own ColorValue, I tried to mask Ogre from any QuickGUI header files, and to do so I had to use a lot of forward declarations and create similar classes. However I never finished this task.. :)

I feel your pain on Ogre. What Ogre really needs is a plugin-type lib that supplies the definitions for commonly used data types like real, Vector2/3/4, ColourValue, common math functions, string convert and string util functions, etc. In my own engine I've had to write classes for all of these data types and for the most part copy/paste from Ogre. :?

Stardragon

16-03-2010 00:21:36

Thanks for that, KFM. :-) It's nice to see some things haven't changed - your kindly welcome, in particular.

Actually, I think what Ogre needs is to come down off its self-created pedestal and stop trying to take over the world. It's good, sure, but it's bloated as all hell. Maybe I'm just too much of a throwback to 8-bit and 16-bit days but I look at the size of the OgreMain and support DLLs and my eyes water. And now Ogre's started to try to take over the STL... oh, please. :-(

Anyway, I'm here for QuickGUI. Rah. :-)

Calder

21-03-2010 20:04:38

I completely agree with you. Ogre's an amazing library, but there are a few things that just don't make sense. First, they still adamantly stick to the "we're just a graphics engine" line, holding their ground against the wave of integrated "game engines" that are popular now-a-days. That, IMHO, is the best thing about Ogre, and what's made it able to be such a high-quality graphics engine. However, there's a few design choices that are quite incongruous with that ideology:
  1. It's not thread-safe. This makes the whole issue of democratically treating it like "just another component" rather difficult.[/*:m]
  2. Not only is it not thread-safe, but it has to be run in the main thread. This doesn't seem like such a huge issue, but it's always hanging there at the back of my mind when I see the examples and other people's code: Ogre dominates the application loop by default.[/*:m]
  3. Most damningly, I think, is the way they present it in the tutorials. The solution to the above two problems is not buffering or controlled access, but subjugating all the other components of your application to Ogre's domineering will. It's the plight of the FrameListener. Rather than participating democratically with other components of your application, Ogre assumes that it will control the timing and structure of the startup, shutdown and everything in between. And the way the tutorials present it, this is in no ways overstepping the bounds of "just a graphics engine," all of the other myriads of components can just be updated once per "Ogre-frame".[/*:m][/list:u]

Stardragon

22-03-2010 11:20:28

That last reason is why I use root->renderOneFrame() :) I never use FrameListeners, or allow Ogre full control of anything. It's just another component, dammit! It can wait in the queue with the rest of them! :) To be fair, though, I can see something of what they're trying to do. I just don't like it. Maybe I'm a control freak :twisted:

As it happens, I did try to make Ogre run multithreaded once. I have to say it wasn't a happy experience. Though I also have to say it was in many ways something of an afterthought.

Calder

22-03-2010 12:16:17

Yeah, I have it chugging along in its own thread and a separate thread for each major component (audio, physics, game logic, networking). I've had to implement buffered wrappers for a few of the classes (Camera and Scenenode only right now), but it's not too bad. The only other thing that took a while to figure out was the main state loop needs to run in a spawned thread yet allow all of the initialization and destruction to occur in the main thread.

Stardragon

22-03-2010 13:10:30

Really?!... I confess I should love to see your code... :oops:

Calder

22-03-2010 15:20:46

It's open-source, so eat your heart out! :D (http://judgmentday.svn.sourceforge.net/ ... dgmentday/)

That's probably a bit overwhelming, but there's really only a few classes you'd be interested in:
  1. ThreadSafe (header and source)[/*:m]
  2. Map (header only)[/*:m]
  3. Vector (header only)[/*:m]
  4. BufferedObject (header and source)[/*:m]
  5. BufferedCamera (header and source)[/*:m]
  6. BufferedSceneNode (header and source)[/*:m][/list:u]

    and then of course the GraphicsSystem class has to call the update routines every frame:

    Vector<OGRE::BufferedObject*> BufferUpdateList;

    ...

    bool GraphicsSystem::frameStarted(FrameEvent const& evt)
    {
    // Run GraphicsHandler's updates
    mHandler->frameStarted(evt);
    // Update buffered object positions
    while (BufferUpdateList.begin() != BufferUpdateList.end())
    {
    BufferUpdateList[0]->updateBuffer();
    BufferUpdateList.erase(BufferUpdateList.begin());
    }
    return true;
    }


    There's also Doxygen documentation available in two flavors if you prefer looking at that: Black Box Edition and the Blood Guts and Gore Edition. Sorry if this is too much information or if it's just useless, but if you're still interested I'd recommend going through the classes slowly in the order I listed them. Questions/suggestions are always welcome! ;-)

Stardragon

22-03-2010 18:55:06

... holy g-g-g-guh, Batman!... :shock:

That really looks exceptionally nifty, and I've only had a browse through it. My approach would have been a lot less subtle and a lot less template-y, and therefore a bit crap :lol:

One thing I've been thinking about a lot is a design for an engine core. Something you can hang pretty much anything onto since it's almost completely agnostic, and you just pass prioritised messages around via the central switching box. So everything is pretty much entirely decoupled, except for the engine and its run-time clock. ;-) Not dissimilar in many ways to what you've got there.

Calder

22-03-2010 22:43:58

That would be really interesting, I can't say I understand exactly what you're talking about but, but a completely plugin-based engine core would be quite cool. It would also require long hours of design meditation to do right! :lol:

Stardragon

23-03-2010 00:01:09

*chuckle* Actually, what I'm talking about is in effect a CPU for a game engine.

It's effectively a fast delegate system with a priority list and a clock which ties into the system millisecond timer. It would run in its own thread and everything would be handled through it. You would register the different I/O systems - rendering, networking, keyboard/mouse, etc. - by simply telling it what function to call in order to trigger an update. The update occurs, it triggers messages - IRQs, if you like. Other modules can register to receive certain types of message for input, signalling, etc.

I've thought about this sort of thing for a while. It would take some fiddling to get right, you're quite correct, but it would allow you to decouple pretty much everything from everything else, leaving you with an entirely agnostic core for whatever game engine you wanted to build around it.

kungfoomasta

23-03-2010 18:57:35

That would be really interesting, I can't say I understand exactly what you're talking about but, but a completely plugin-based engine core would be quite cool. It would also require long hours of design meditation to do right! :lol:

Yes! I recently revamped my enging to be plugin based. It did take a ton of thought actually, but its very very awesome. Basically I have one "core" library, which is the engine, and everything else are plugins. The Core outlines interfaces for all the plugins, like GraphicsSystem, InputSystem, SoundSystem, NetworkSystem, etc. Since each plugin is based off an interface, they can interact with each other without needing to know any other details.

For example, here is my main function of my game:


void main()
{
// Path to bitmap image for splash screen
const std::string splashBitmapPath = ".\\resources\\rotm-splash.bmp";

Gaia::WinSplashScreen* ss = new Gaia::WinSplashScreen(splashBitmapPath,0,255,0);

Gaia::Core* engine = new Gaia::Core(ss);

engine->setGraphicsSystem(new Gaia::OgreGraphicsSystem::GraphicsSystem(ss->getWindowHandle(),800,600,"Plugins.cfg"));
engine->setSoundSystem(new Gaia::OgreOggSoundSystem::SoundSystem());
engine->setNetworkSystem(new Gaia::RakNetNetworkSystem::NetworkSystem());

Gaia::StateManager* stateManager = Gaia::StateManager::getSingletonPtr();

RotM::Play* playState = new RotM::Play();
stateManager->registerState(playState);
stateManager->enterState(playState);

engine->startMainLoop();

delete engine;
}

Stardragon

23-03-2010 21:32:13

Yes! I recently revamped my enging to be plugin based. It did take a ton of thought actually, but its very very awesome. Basically I have one "core" library, which is the engine, and everything else are plugins. The Core outlines interfaces for all the plugins, like GraphicsSystem, InputSystem, SoundSystem, NetworkSystem, etc. Since each plugin is based off an interface, they can interact with each other without needing to know any other details.

You know, that all looks rather funkeh :-) It's hard to tell from that code snippet alone how similar our trains of thought are. Pretty totally, probably :-) What I had in mind for mine was messaging, with classes registering themselves with the engine 'CPU', along with the types of messages they wanted to send and receive. Everything would be done through messaging, with telegraphs being sent to the 'CPU' and queued for processing, the engine snagging the message like a mailbag and returning straight away so the calling routine can return as well. And then everything is ordered by the 'CPU clock', which ties directly into the system's millisecond timer. Finally, every message has a priority level, with certain functions (rendering, input, network polling, etc.) having priority 0, which is the highest priority...

If I'm honest, I can't help but think of the faithful old Amiga, which did things in almost exactly the same way.

What would be extra funky is if everything ran in its own thread, so you could send out messages in parallel.

kungfoomasta

23-03-2010 22:39:12

with classes registering themselves with the engine 'CPU', along with the types of messages they wanted to send and receive.

Yah thats another thing I've done. Although Classes don't register what types of messages they send, only the messages they're interested in. Any "MessageHandler" can subscribe to messages, by type, or by type AND from a particular source. So basically the Core just puts out messages like a RenderOneFrame message, and anybody listening, ie the GraphicsSystem, should receive that message, and respond by calling renderOneFrame.

Now that you've read my code snippet, consider this:


void main()
{
// Path to bitmap image for splash screen
const std::string splashBitmapPath = ".\\resources\\rotm-splash.bmp";

Gaia::LinuxSplashScreen* ss = new Gaia::LinuxSplashScreen(splashBitmapPath,0,255,0);

Gaia::Core* engine = new Gaia::Core(ss);

engine->setGraphicsSystem(new Gaia::DX11GraphicsSystem::GraphicsSystem(ss->getWindowHandle(),800,600,"Plugins.cfg"));
engine->setSoundSystem(new Gaia::FMODSoundSystem::SoundSystem());
engine->setNetworkSystem(new Gaia::EnetNetworkSystem::NetworkSystem());

Gaia::StateManager* stateManager = Gaia::StateManager::getSingletonPtr();

RotM::Play* playState = new RotM::Play();
stateManager->registerState(playState);
stateManager->enterState(playState);

engine->startMainLoop();

delete engine;
}


The differences are small and big at the same time. 8)

[edit] By the way I agree with Calder above, he highlighted 3 really big points. Ogre claims to be just a rendering engine, but its really a massive beast, its hard to treat Ogre as just a component. My library Gaia has its own Vector2, Vector3, ColorValue, StringUtil lib, Math lib, StringConverter lib, etc.. and its all from Ogre, but I don't want my engine to depend on Ogre, nor are those data types/utility functions Ogre specific. Pretty sure everybody has copied the classes I've listed numerous times.. *cough Ogre needs to split these classes into a generic util lib*
[/edit]

Calder

23-03-2010 23:27:27

That message subscription system sounds really cool! If I'm not mistaken, you two are basically saying the exact same thing as with the exception of registering senders. Out of curiosity, have either of you ever used Objective-C? From the wiki page:
Objective-C is a reflective, object-oriented programming language, which adds Smalltalk-style messaging to the C programming language.
All (I think?) method calls are treated as messages: one object sends another a message telling it what method to execute and with what arguments, and the other decides how, when and even if to deal with the selector. Because method calls are essentially data packets to be passed around, they can just be forwarded to another object to handle instead.

BTW KFM, is ROTM going to be open-source? :P

Edit: Speaking of Objective-C, I was just forced by circumstances to write an equivalent for it's callInThread() function. There's sum really funky templating going on, but all in all I'm pretty satisfied with it. You can call pretty much any function with up to three arguments (could be arbitrarily large with some copy/pasting) and have it executed during Ogre's frameEnded sequence as either blocking or background. I'm considering re-writing our BufferedObject implementations with it to simplify things. Here's the code if anyone's interested: JDThreadUtils.cpp and JDThreadUtils.h

Stardragon

24-03-2010 01:12:49

Yah thats another thing I've done. Although Classes don't register what types of messages they send, only the messages they're interested in. Any "MessageHandler" can subscribe to messages, by type, or by type AND from a particular source. So basically the Core just puts out messages like a RenderOneFrame message, and anybody listening, ie the GraphicsSystem, should receive that message, and respond by calling renderOneFrame.

The reason I said that classes could register message types is in case they want to spend specific sorts. Say you have a standard list of message types, by which I mean the sort of data they'd ship around... so (just for example), type 0 is for input, type 1 is for network polling, type 2 indicates a filesystem message, type 3 could be for the renderer, and so on. Perhaps a class wants to add a type of message, for whatever reason. It asks for and is allocated a message type ID, and all messages of that type are bound to that module. Within each type, of course, you can subdivide to your heart's content.

I agree that subscribers should be able to filter by originators, too.

Now that you've read my code snippet, consider this:


void main()
{
// Path to bitmap image for splash screen
const std::string splashBitmapPath = ".\\resources\\rotm-splash.bmp";

Gaia::LinuxSplashScreen* ss = new Gaia::LinuxSplashScreen(splashBitmapPath,0,255,0);

Gaia::Core* engine = new Gaia::Core(ss);

engine->setGraphicsSystem(new Gaia::DX11GraphicsSystem::GraphicsSystem(ss->getWindowHandle(),800,600,"Plugins.cfg"));
engine->setSoundSystem(new Gaia::FMODSoundSystem::SoundSystem());
engine->setNetworkSystem(new Gaia::EnetNetworkSystem::NetworkSystem());

Gaia::StateManager* stateManager = Gaia::StateManager::getSingletonPtr();

RotM::Play* playState = new RotM::Play();
stateManager->registerState(playState);
stateManager->enterState(playState);

engine->startMainLoop();

delete engine;
}


The differences are small and big at the same time. 8)


I bow to your obvious greater-than-me-ness, but I take much egoboost from the fact that I'm thinking along the same lines as you ;-) Although I wouldn't do things quite the same way that you have. I'd make all the systems you have there just make a subscription to one of the zero priority messages. Maybe I'm misreading the code (it's late here), but the engine still is tied to the render system: it's not fully decoupled as it is with mine. With the 'CPU' I have in mind, there's absolutely no point of contact between the core and the modules which interface through it beyond the messages. It doesn't need to know anything that's going on around it, so it doesn't know.

[edit] By the way I agree with Calder above, he highlighted 3 really big points. Ogre claims to be just a rendering engine, but its really a massive beast, its hard to treat Ogre as just a component. My library Gaia has its own Vector2, Vector3, ColorValue, StringUtil lib, Math lib, StringConverter lib, etc.. and its all from Ogre, but I don't want my engine to depend on Ogre, nor are those data types/utility functions Ogre specific. Pretty sure everybody has copied the classes I've listed numerous times.. *cough Ogre needs to split these classes into a generic util lib*
[/edit]

Ogre isn't a component, sadly. Ogre is a real pain in the butt. :P

That message subscription system sounds really cool! If I'm not mistaken, you two are basically saying the exact same thing as with the exception of registering senders. Out of curiosity, have either of you ever used Objective-C? From the wiki page:

I've never used it, but I've read about it when bored and idly flicking through Wikipedia. I like the idea of it, with message passing, and I suppose it's unconsciously part of what I'm thinking of. That said, what would be very fun is if data could somehow just be left 'for collection', as it were... if each module were to run in its own thread (yeah, I know, as if) then it could just grab data from some central point and it would be possible to cut out the function passing entirely. Erlang does things in that sort of way, if I remember correctly.

Edit: Speaking of Objective-C, I was just forced by circumstances to write an equivalent for it's callInThread() function. There's sum really funky templating going on, but all in all I'm pretty satisfied with it. You can call pretty much any function with up to three arguments (could be arbitrarily large with some copy/pasting) and have it executed during Ogre's frameEnded sequence as either blocking or background. I'm considering re-writing our BufferedObject implementations with it to simplify things. Here's the code if anyone's interested: JDThreadUtils.cpp and JDThreadUtils.h

That really is some funky code. Made my eyes water and bug out on stalks. Templates are not something I'm particularly hot on, to be honest. I've only been at C++ for two or three years, there's a lot to learn. :-)

Calder

24-03-2010 17:25:42

How often would you envision having to screw with the central "CPU" class? Because one change to that header and you'd have to recompile you're entire project.

This is how I'm seeing things at a really basic level, correct me if I've completely misinterpreted this conversation: :D

Class CPU
Variable messageHandlerList
Function addMessageHandler (MessageType, MessageHandler)
Function removeMessageHandler (MessageType, MessageHandler)
Function pushMessage (Message)
Class Message
Variable type
Variable sentTime
Variable expirationTime
Class MessageHandler
Function handleMessage (MessageType, Message)


And the CPU class would loop through the process of taking the oldest message and calling handleMessage() on all the registered MessageHandlers. That way all message handling gets performed in the CPU's main thread and as long as the CPU's public member functions are thread-safe the whole system is.

What would be the best way to deal with return values?

Stardragon

24-03-2010 20:49:55

How often would you envision having to screw with the central "CPU" class? Because one change to that header and you'd have to recompile you're entire project.
Define 'screw around with'. That seems to be a technical term and I'd like to be sure I fully understand before I answer :P

This is how I'm seeing things at a really basic level, correct me if I've completely misinterpreted this conversation: :D

Class CPU
Variable messageHandlerList
Function addMessageHandler (MessageType, MessageHandler)
Function removeMessageHandler (MessageType, MessageHandler)
Function pushMessage (Message)
Class Message
Variable type
Variable sentTime
Variable expirationTime
Class MessageHandler
Function handleMessage (MessageType, Message)


And the CPU class would loop through the process of taking the oldest message and calling handleMessage() on all the registered MessageHandlers. That way all message handling gets performed in the CPU's main thread and as long as the CPU's public member functions are thread-safe the whole system is.

What would be the best way to deal with return values?


At a basic level, that looks about right, I think. I was thinking about KFM's Gaia engine, how it registers components, and had some ideas about that... based on the idea that you have some 'key' message types, they can register for the priority 0 calls for that type. That way the CPU remains agnostic.

Regarding return values, I'd probably do it so that you could telegraph directly to an originator, somehow. Not sure how, immediately, but that's the first thing that comes to mind. I mean, if you did it this way you'd need a standardised interface class, right? So you'd have a base class from which people can inherit to do stuffs. Which means that you can cast to that class type and blast the return data straight to the originating module.

Eh. Probably the wrong answer, but for whatever reason I'm really brain-dead tonight.

Calder

24-03-2010 21:19:11

Lol, "screw around with stuff" just meant change the header file. I guess the more generally applicable and agnostic this is, the less you'd have the change the header...

So the calling object would receive the return value at some indeterminate time in the future? Or would you call some functionReturned method that all MessageSenders must have? What if there were multiple MessageHandlers registered for the same message type? Would all of their return values be given?

Stardragon

24-03-2010 22:03:41

Lol, "screw around with stuff" just meant change the header file. I guess the more generally applicable and agnostic this is, the less you'd have the change the header...
That's the idea. The header doesn't get changed. And it's a shame, in a way, that you even have to #include that in everything else. Oh well... :P

So the calling object would receive the return value at some indeterminate time in the future? Or would you call some functionReturned method that all MessageSenders must have? What if there were multiple MessageHandlers registered for the same message type? Would all of their return values be given?
I have a nasty feeling I'm too tired and brain-dead to parse that properly, never mind give an answer :P

The calling object would receive the reply in the next clock cycle, or immediately (within the same clock cycle) if it absolutely had to be that way. And yes, if multiple handlers spawned messages, it would be up to the originating object to handle them all. The CPU is agnostic, after all: its sole function is to switch messages around the place. Though if the message doesn't need a return value, you'd just set that particular part of the telegraph to NULL, since it would be implemented as a void pointer - and the responsibility of the originating object to keep track of its own memory allocation.

Calder

24-03-2010 23:53:54

Get some sleep, jeez! :P

No reading this until the morning.
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...

So Objects (which everything would have to be), would all have a method receiveReturnValue() that could then store multiple return values from various functions to be popped and used at a later time? It would be interesting to see how well that would work out, but most of the time when you need a return value from something you need to use it in the line of code, which this wouldn't really handle...

Stardragon

25-03-2010 00:25:02

So Objects (which everything would have to be), would all have a method receiveReturnValue() that could then store multiple return values from various functions to be popped and used at a later time? It would be interesting to see how well that would work out, but most of the time when you need a return value from something you need to use it in the line of code, which this wouldn't really handle...

Sorry, couldn't resist reading :P Insomniac, here...

This is one of the trickier parts of the idea, I have to admit. I'm curious to know how KFM handles it in his Gaia engine. I think if a return value message was sent, the CPU would handle it differently to ordinary messages. It would have to do in order to be able to pass the data back properly to wherever it was being called from. But if you wanted to do things that way at times, there would be provision for that. I'm sure there's a way it could be done. The only trouble would be encoding and decoding the Telegrams.

Then again, would it be necessary?... the idea of having multiple return values isn't so crazy if you think of things only happening once per CPU cycle. Objects send out their messages, each of which has a target module and a priority level (don't forget priority levels!); modules send back the replies which are fielded to the source objects, which can then act on them. At the end of each CPU cycle the render hits and the system gets polled to update itself, triggering more messages. Effectively it turns the code into a large state machine type thing. There's nothing to say, of course, you can't unregister and re-register for things at different times.

kungfoomasta

25-03-2010 00:41:37

The only reason I require registration of a GraphicsSystem is for accessibility purposes. The Core will send out periodic messages of type "RenderOneFrame", and if anybody is subscribed to that message type (ie GraphicsSystem), it will handle that message (hopefully by rendering a frame). You could easily use the Core without any GraphicsSystem and there would be no problems.

All (I think?) method calls are treated as messages

I don't take this approach, my Messages are made up of 3 parts: type, source, data. The data is in the format of std::map<std::string,boost::any>. So people that send off messages can insert data, ie

myMessage.dataMap["name"] = mName;

And handlers receiving the message can use this data, associated with "name" property. I think this system is very flexible, multiple handlers can listen to the same types of messages and respond differently. They could modify the message and then send it somewhere else. I can disable handlers from receiving messages. Also my messageManager can process messages immediately, or queue them for later processing, which is most commonly used. This allows classes to queue themselves for deletion, etc. (Bullet killing itself when it reaches target, for example)

Stardragon the categorization of your messages sounds like overkill. For example this is what I currently have, defined by my Core library:


namespace Gaia
{
namespace DefaultMessageType
{
const std::string CaptureInput = "Gaia.CaptureInput";
const std::string DestroyCamera = "Gaia.DestroyCamera";
const std::string DestroyChildSceneNode = "Gaia.DestroyChildSceneNode";
const std::string DestroyMesh = "Gaia.DestroyMesh";
const std::string DestroyNetworkConnection = "Gaia.DestroyNetworkConnection";
const std::string DestroyPathMap = "Gaia.DestroyPathMap";
const std::string DestroyResourceGroup = "Gaia.DestroyResourceGroup";
const std::string DestroySceneNode = "Gaia.DestroySceneNode";
const std::string DestroyServer = "Gaia.DestroyServer";
const std::string DestroySound = "Gaia.DestroySound";
const std::string DestroyTerrain = "Gaia.DestroyTerrain";
const std::string DestroyViewport = "Gaia.DestroyViewport";
const std::string DestroyWindow = "Gaia.DestroyWindow";
const std::string EnterState = "Gaia.EnterState";
const std::string PollNetwork = "Gaia.PollNetwork";
const std::string RenderOneFrame = "Gaia.RenderOneFrame";
const std::string RotateModel = "Gaia.RotateModel";
const std::string ShapePositionChange = "Gaia.ShapePositionChange";
const std::string ShapeOrientationChange = "Gaia.ShapeOrientationChange";
const std::string ShutdownEngine = "Gaia.ShutdownEngine";
const std::string StepPhysics = "Gaia.StepPhysics";
const std::string StepLogic = "Gaia.StepLogic";
}
}


The game using Gaia, RotM, also has its own message types. (Its not going to be open source, but I'm sharing ideas right now :) )

Calder

25-03-2010 04:58:21

Woah, that data implementation is really cool!

A few questions though. Since all of your message types are statically defined in that one file, do you just put up with a crazy complete recompilation every time you need to add one? Second, what's calling the higher level ones like StepPhysics or RenderOneFrame? Does the engine go through a central loop where it steps logic, steps physics, renders a frame, etc... rinse and repeat? And finally, is it still single threaded?

Thanks for sharing the awesome code/thoughts! :D

Stardragon

25-03-2010 10:27:10

I feel like I should head back to the dribbly corner where the real coders keep the safety scissors, Elmer's glue and glitter...

Still, thanks indeed for the code snippets and feedback, KungFooMasta :-) I knew someone would have taken that approach but I didn't expect it to be you, nor for it to be as far along as your Gaia engine is :oops:

Maybe my approach is too much like overkill. I guess I'm just trying to break things apart even more. As far as I recall, vaguely, from the distant past when I did all this, a message is just passed by a function call... so if messages get passed around you end up with a whole raft of function calls, which then have to resolve themselves...

Oh, what do I know :P Pass the Elmer's glue, I'm hungry :P

Calder

25-03-2010 11:00:46

Lol, I've felt like that for this whole thread, but it's fun to push mental limits. I'm going be gone for five days, but I might try to sketch up the fundamental classes of an engine like this while I'm gone.

kungfoomasta

25-03-2010 17:56:23

My core library contains the following:
- Core class
- Data types
- Utility Functions
- Plugin Interfaces

When I modify the messages, I do have to recompile the core library, but its really small and easy to build. The core has a main loop that sends out messages like RenderOneFrame and StepPhysics. If anybody is subscribed to the messages they will respond, otherwise nothing happens. (Another great thing about these messages, sources will just send them out and not care what happens, there are no dependencies) My engine is single threaded. I'm not sure how worthwhile it is to plan/design/implement for MT when I haven't even finished my ST version. :lol:

Here is an example of a Plugin Interface, for my Window class:

/**
* The Window represents an OS window as well as a rendering context for a 3d scene. Every window has a scene that it
* represents; by default nothing will be shown because the scene has no viewports setup. The Scene is the class that
* gives control over content to be rendered within this window.
*/
class Window
{
public:

/**
* Gets the Input System used for getting Input from Hardware devices.
*/
virtual InputSystem* getInputSystem() = 0;
/**
* Returns the name of the window.
*/
virtual std::string getName() = 0;
/**
* Returns the window handle of the OS Window.
*/
virtual unsigned int getWindowHandle() = 0;

/**
* Returns the scene stored in this window.
*/
virtual Scene* getScene() = 0;

/**
* Sets the Input System used for getting Input from Hardware devices. The Window instance assumes ownership
* of the passed in object, and will clean it up on destruction.
*/
virtual void setInputSystem(InputSystem* s) = 0;

/**
* Window's position has changed.
*/
virtual void windowMoved() = 0;
/**
* Window's size has changed.
*/
virtual void windowResized() = 0;
/**
* Window is closing. (Only triggered if user pressed the [X] button)
*/
virtual bool windowClosing() = 0;
/**
* Window has been closed. (Only triggered if user pressed the [X] button)
*/
virtual void windowClosed() = 0;
/**
* Window has lost/gained focus.
*/
virtual void windowFocusChange() = 0;

protected:
};


The basic idea is that Gaia Core outlines all the interfaces, external libs will follow these interfaces and implement them, and libs can still interact with these classes without knowing how they're implemented.

Regardless of using Ogre, D3D9, GL, etc., in order to supply a working Graphics solution to Gaia Core, I'd have to implement the following classes, according to Gaia Core interfaces:
AnimationState, Camera, GraphicsSystem, Mesh, Renderable, ResourceGroup, Scene, SceneNode, Terrain, Viewport, Window. I imagine this list will grow, but basically it consists of fundamental classes that any graphics system should implement.

Calder

22-04-2010 19:08:00

Sorry I've been silent for the last three weeks, but I went ahead and tried writing what we were talking about. Here's the source code if youguys want to try it out: http://judgmentday.svn.sourceforge.net/ ... y/appcore/ From the three samples I've written and tested, it seems to work pretty well, but I'm sure there's as many bugs as there are lines of code just waiting to reveal themselves! Enjoy! ;-)

-Calder

Stardragon

04-05-2010 19:57:18

No problem!... I've been pretty busy myself.

And that... looks pretty darned good. Nothing I could have come up with would have been close. I feel really dumb now :-p

Calder

11-05-2010 03:24:45

Thanks, but I'm sure that's not the case! Anyway, there's still substantial room for improvement in the performance department of the CPU functions, although I've found the basic classes extremely useful already. The Condition class is especially nice to have around, it does a few nice things that I haven't seen any other implementations do.