Tuesday, 2 September 2008

Hordes


Nothing important today, just been sounding out the pitfalls and quicksand that abound in OpenGL. This time it's so-called 'pseudo-instancing', providing more or less the equivalent of DX9's SetStreamSourceFreq-style instancing for geometry. I'm testing it with the anemone mesh, because I still have yet to get around to authoring more models. I'm lazy and emacs is more friendly than Blender.

Technical sorta-details: a position/scale and quaternion rotation are stuffed in a couple of spare texture coordinates using OpenGL's immediate mode, on a per instance basis. It's a bit of extra mucking about but seems viable in terms of performance. A per-instance colour should round out the system for maximum variation at a small overhead.

I think I'll be using this a lot for semi-dynamic clutter - rocks, vegetation, refuse and the like. Static objects will probably benefit more from being baked into world space in large batches, assuming memory is not a concern, but smaller things may need to be moved or animated.

6 comments:

Jotaf said...

That's creepy!!

Looks like I won't be playing your game after all ;P

Jotaf said...

Sorry for the double post. Concerning your entry "Functional Update Musings", I struggled with the same issue myself.

It seems to me that the only viable choice is event-based simulation. Implicit there is the fact that these are not arbitrary events, but rather timed events (much different from reactive programming), which are scheduled in an ordered list of functor/monad instances (from my experience functors in C++ are more than enough to defer a function call).

A simulation like this is pretty easy to make and a good programing exercise.

Now, relating this approach to your problematic examples. If you want to update all creatures at once, schedule that event, and when it happens it should automatically re-schedule itself. A World->World transformation. Alternatively, each creature has a separate Update event.

Updates don't need to be frequent. Most of the transformations that need frequent updating are done in another way, described below.

Progressive damage is handled as a "countdown to death": calculate the point when this countdown results in a meaningful event, such as death or a low health warning, and schedule that. This is like an edge detector (in some ways).

The trouble is usually in the availability of up-to-date information. Since inside an event you need to know a creature's HP *now*, not just when it dies, there are two approaches to this: the simplest is to have Update lower HP by the appropriate amount given the current condition, certain that it won't trigger a death since that's handled as an event. Unfortunately, events triggered between two updates won't have up-to-date information.

The other, more "functional" way, is for a getHP() function to make its own update on the last set HP given elapsed time since that update and the applied conditions. This is more accurate but "doesn't feel right" to most people because of the incurred overhead. However this is rarely the case.

These are ramblings on their own, but there's a whole field on this. I just wanted to give you an overview of the approach and a bit of my own experience :)

Snut said...

Looks like I won't be playing your game after all ;P

Ack, my playerbase halved in a single stroke! I'll replace them with something less creepy, honest...

On to the problems with semi-functional world updates:

Progressive damage is handled as a "countdown to death": calculate the point when this countdown results in a meaningful event, such as death or a low health warning, and schedule that. This is like an edge detector (in some ways).

I think in general I'm aligning myself with the event-based approach too. Especially with regards 'edge detectors'; they're just not a good fit with the rest of the scheme, but events certainly are and it's not hard to map the one onto the other in most cases.

I think on the whole, I'll stick with functions of time integrated between events rather than a fixed update step. The addition of events caused by those functions of time, inserted into the event stream appropriately, makes them a lot more tractable, and they nicely encapsulate some general time-dependant thingies.

The other, more "functional" way, is for a getHP() function to make its own update on the last set HP given elapsed time since that update and the applied conditions.

I think I'll use this approach too, for the most simple and ubiquitous of environmental conditions. Right now I'm not worried about perceived overhead - if I were optimising at this kind of scale I'd be writing in C++. It's kind of liberating to choose to do things the 'correct' way, although maybe after profiling I'll need to come back to these areas and hack them into shape.

It is a very large area, and huge amounts to cover. It's a lot more fun trying to do it this way than the old imperative approach though.

Thank you for the insights, and sorry again for not spotting the comments sooner!

Do you have a blog for such things by the way? Always interested to read how others are approaching these thorny problems.

orillian said...

Leave them, just as long as it doesn't become a tentacle rape anime RL. :P

I'm really enjoying this blog of yours, it's a lot of fun to see someone approaching the RL theme with a bit of tech, I've been saying for years that Graphics are not BAD for RL's.

O.

Snut said...

Leave them, just as long as it doesn't become a tentacle rape anime RL. :P

::shudder:: Never fear, this is not about to become GhastlyRL...

Everyone loves some Lovecraftian squirming with their games, though.

Thanks for the comment, it has been fun writing the blog as well. A useful place to drop ideas, and it helps to look back and see progress of a sort, but I'm very glad other people enjoy it too.

There's nothing wrong with combining RLs and graphics. There probably is something wrong with the lack of game in my game, as opposed to screenshot-worthy stuff. Hopefully this too will improve in time...

Jotaf said...

Sorry for the delay -- I had a paper due today. Hectic couple of weeks, as you might imagine :)

I've been most successful at applying these techniques to my main game projects, which are in C++. But when I've been banging my head against a problem for enough time, I can just cheat and do it the old procedural way! The theory we've discussed *sounds* right, and I've applied it to C++ (call it a proof-of-concept if you will), it's just that translating it to a pure functional context is not that simple (for me at least). Still, it beats trying to hammer an imperative update loop into a functional language...

I don't really have a blog, sorry :) I switch topics so often that you would only see Haskell+Games again in a couple of months! But don't take me wrong, I do spend a lot of time discussing and exchanging ideas, if not online, then with colleagues and friends. I'd be happy to talk about this, you can send me a message to: jotaf ninety-eight at hotmail com.