• Advertisement


  • Content count

  • Joined

  • Last visited

Community Reputation

1219 Excellent

About Cygon

  • Rank

Personal Information

  1. An interesting idea!   Do you have a way to deal with a 'Dongle' that has a non-trivial copy constructor? I'd imagine that with the code above, the Foo::Impl::mem field would be copied in a memcpy() / std::copy()-like manner without invoking the copy-constructor.   The solutions I can think of would be to implement the Impl class' copy constructor & assignment operator create new Impl instances instead or to add a memoryWipe() method to my classes that drops file handles, refcounted resources, etc. without properly destroying them. Both are ugly :)
  2. Unreal Engine 4

    I'm on the same path :)   You can find a good overview for the different concepts (blueprints, actors, components, game modes...) of Unreal Engine 4 here: Unreal Engine: Getting Started. Once you have a rough idea of how the engine is put together, you can follow this free book: Blueprints - Master the Art of Unreal Engine 4 Blueprints   The book is very specific (less a comprehensive explanation and more a "look over my shoulder while I make something"), but if you've already got some experience with other engines or know a bit about their design, you can probably connect the dots and figure out the general workings. While learning the workflow of an experienced at the same time.
  3. What if game engines were cars?

    Haha, love these kinds of comparisons :)   I would have taken proprietary engines from another angle, since they're so often tailored to a very specific usage. Like, a car with a single seat only fitting a specific driver who is expected to stick his feet through a hole into the engine compartment where he can direct the front wheels and control the fuel/air mixture with his toes to accelerate or slow down. Needs a picture of a tuned old french budget car :D
  4. Generalized Platformer AI Pathfinding

    Splendid!   I have written a platformer navigation system myself not so long ago and ended up with the same basic data structures. What differs is that I tried to solve the jump/drop problem algorithmically.   This has turned out to be one of the biggest issues - currently I'm setting up a 4-point spline from jumpoff to landing with an army of tweaks to figure out a realistic apex point depending on jump height, distance and velocity. And then there's the issue of whether that spline should be followed with linear speed, or slowing down upwards and speed up downwards...   Simply recording the developer's inputs used to jump or drop onto another platform is quite an idea and even allows for complicated jumps requiring air control. Thanks for sharing this!   I'm curious about one thing: how does the AI in Nomera figure out which node it is on (eg. at game start or after being throw back by a weapon)? A simple search for the closest node below the character? Collision volumes above the nodes?
  5. Banshee Engine Architecture - Introduction

    Yeah, I'm outta this discussion.   You falsely claim I set up a strawman when my point stands, with or without your std::vector, you dishonestly quote a single sentence from a website to make it say the opposite of what it actually did, you call me an "apologist" for just asking what riled you up against exceptions so much. It seems you consider this a war, not a discourse.   This has turned too toxic for me.   -   Regarding the original topic, this looks like an interesting engine, good work!   The general architecture appears to be inspired by Ogre a bit (the RenderSystem, RenderQueue, SceneManager and so on all look very familiar, down to the method names and coding conventions), but I welcome that, since at its core, Ogre is a very nice engine that only accumulated a lot of cruft over the years :)   Maybe you could add a bit more information about the hows and whys of the engine's architecture. It would be pretty interesting and provide something to learn from, apart from just providing a nice introduction into the engine.
  6. Banshee Engine Architecture - Introduction

    I don't know if it makes sense to continue this discussion. You seem far too worked up / polarized on this.   - Only you need a debugger. The "this application needs to be closed..." window you get when an application crashes (or the terminal output you get on Linux for that matter) contains a crash dump. When you user, artist, whatever sends that to you, you obtain the complete callstack from it. Often the sending process is automated with an error reporting wrapper.   - If you only add that code to 10 out of 10000 lines then you have an incomplete callstack. I don't see how that would help with debugging (you first post talks about being able to get a call stack).   - If you're using a fixed array you still have overhead. And a limit.   - A mutex is the wrong choice: you'd mix callbacks between different threads. If you want to use your stack in a multi-threaded application, you need a thread-local container.   - Yes, throw means "I've encountered an error I can't handle in this scope" thus it's jumping to higher scopes until one can deal with the error. And it can't be swiped under the carpet accidentally, letting the application continue in an invalid state. And cleanup is still handled locally (see RAII which you demonstrated yourself).   - Let me quote two more sentences of that punchline: The term “zero-cost exception” is a bit of misnomer: it refers only to the runtime cost when exceptions are not being thrown. The actual overhead in throwing an exception is quite high. Zero runtime cost unless an error occurs. Sounds good to me. The article underlines my points, by the way.   You can decide for yourself that you just don't like exceptions, I don't mind. But your arguments here are flawed and strongly opinionated. I think if you manage to take the emotion out of it, you'll find that your concept may have merit for C, but is on very weak footing in C++.
  7. Banshee Engine Architecture - Introduction

      I don't know what turned your opinion against exceptions that much, but they are, I believe not just in my opinion, the cleanest, safest and most readable way of handling errors.   Exceptions preserve the call stack, too. Any connected debugger can be told to break when an exception is thrown, or just when an exception is left unhandled. Getting the callstack when the application crashes at the end user is possible, too.   With exceptions, you also don't have to write tons of boilerplate code pushing and popping cleanup callbacks onto stacks. And you don't have the runtime overhead of a dynamically allocated container (you seem to be using std::vector). Plus you don't have to provide a thread local global "callback stack" to any and all of your engine's systems (creating a monolithic piece of code forcing anyone into this scheme of doing things).   Exceptions are extremely efficient, too. Google "zero cost exception handling" - the days when a try..catch block resulted in a setjmp() instruction are pretty long gone. In fact, you can't create anything remotely as efficient for cleaning up without getting down and dirty with stack frames and the instruction counter.
  8. Very nice!   Your use of the term 'delegate' is a little bit unusual (since they're not representing a method directly, but just the connection between an event and its subscriber). Maybe 'subscription' or 'forwarder' would also be good terms for this.   It would also be interesting to have a performance comparison. There's the by now well-known FastDelegate library (with limited portability) and the Impossibly Fast Delegate library which should be portable. Some community members have created signal/slot systems based on these, so it would be pretty interesting to see how this one holds up against that.   @Bearhugger: that is an interesting problem :)   I'd imagine that even having a flag in the class that is set by the destructor would still rely on the memory not having been reused by the time the running iteration gets to check it. The only safe way I can think of would be to register some local variables in an instance-wide notification list...   ...but then destruction could still happen while the event is executing the call into the subscriber. And the subscriber is likely to access the event publisher in the callback. Maybe it's best to require that subscribers don't destroy their event publishers from within the callback :P class Event { public: ~Event() { std::lock_guard<std::mutex> iterationLockScope(this->iterationLock); for(std::size_t index = 0; index < this->activeIterations.size(); ++index) { *this->activeIterations[index] = true; } } public: void Fire() { std::vector<Delegate> copiedSubscribers bool destroyed = false; // Still a race condition up to this point... :/ { std::lock_guard<std::mutex> iterationLockScope(this->iterationLock); ScopeGuard activeIterationScope = MakeGuard( this->activeIterations, std::vector<bool *>::erase, &destroyed ); copiedSubscribers.swap(std::vector<Delegate>(this->subscribers)); } for(std::size_t index = 0; index < copiedSubscribers.size(); ++index) { if(destroyed) { break; } copiedSubscribers[index](); } } private: std::vector<Delegate> subscribers; private: std::vector<bool *> activeIterations; private: std::mutex iterationLock; };
  9. Efficient Art Production: Theory and Practice

    @AndrewMaximov: Your advice is sound and emil is right, too: longer edges == lower performance (and, for extremely skinny triangles, even precision issues in certain algorithms)   Only the reason is slightly different. The "quads" in emil's article are blocks of 2x2 pixels by which graphics hardware steps through polygons 2-scanline-wise (allows for early rejection of entire blocks if obscured, helps align memory accesses and probably some other things). So if more screen space is touched by polygon borders, those quads have to be evaluated 2, 3 or even 4 times in the worst case.   Depending on the hardware's design, this also means a rectangle from (2, 2)-(10, 10) is usually rendered faster than one from (3, 3)-(11, 11) even though it's the same number of pixels because 19 more 2x2 pixel blocks ("quads") have to be processed.   But one can hardly optimize for such stuff, apart from maybe making edges as short as possible (which I find difficult, too, since one usually builds models from 4-sided polygons without knowing in which direction they'll be split). My own real world reason for subdividing cleanly is texturing - having extremely skinny triangles makes it hard to tweak texture coordinates since you can hardly see the difference.
  10. Efficient Art Production: Theory and Practice

      I do not believe this to be the case.   One important concept in hardware and software rasterizer design is "fill rules" or "rasterization rules," which include designing your rasterizer to never leave out a pixel and never draw a pixel twice along triangle edges.   This means that only the top and left facing edge pixels of a triangle are rendered. Thus, no matter how small the triangles, and no matter their topology, you'll simply never have a pixel that is drawn twice for two triangles with a shared edge.   Here are some references   http://fgiesen.wordpress.com/2011/07/06/a-trip-through-the-graphics-pipeline-2011-part-6/   http://graphics-software-engineer.blogspot.de/2012/04/rasterization-rules.html   http://msdn.microsoft.com/en-us/library/windows/desktop/cc627092%28v=vs.85%29.aspx
  11. What does your IDE look like?

    I'm using the built-in Visual Studio "Dark" Theme with colors from the Solarized Color Palette for syntax highlighting. The designer of that palette lists all kinds of color theory stuff that he took into account, all I know is that the colors contrast perfectly and that the brightness is very consistent.     I'm using the Lucida Console font because I find it more readable than serif fonts.   The width of the code view is set to show exactly 100 columns because I dislike horizontal scrolling, so I break my lines before that length (traditional 80x25 feels just a little bit short for modern code and 100x37 is the next larger terminal size).   EDIT: As requested by asvsfs, also providing the vssettings files: http://blog.nuclex-games.com/downloads/Solarized-VS2012.vssettings http://blog.nuclex-games.com/downloads/Solarized-VS2013.vssettings
  12. What do you consider to be the advantages of such an event distribution system compared to a signal/slot approach (like in libsigc++, Boost.Signals or sigslot)?   It seems to require a downcast (putting type safety into the hands of the user, any mistake would lead to a crash that is only detectable at runtime when the event gets fired). It also adds a map lookup to event processing that isn't present in the signal/slot design.   A possible advantage you could mention might be that a visual editor or scripting language binding could enumerate the available events (a feature easily implemented). Though of course one might still argue that decoupling such reflection abilities (by using a signal/slot system and an external reflection information provider class) would be truer to the SRP and allow seamless binding of third-party classes, too.     An unrelated note on the implementation:   Your CEventManager::TriggerEvent() deletes its argument, but the ownership transfer doesn't seem apparent from the outside. Or even required: a const ref would allow construction of the event arguments on the stack of the caller and dissolve any ownership concerns.
  • Advertisement