Jump to content
  • Advertisement


  • Content Count

  • Joined

  • Last visited

Community Reputation

2555 Excellent

About Trienco

  • Rank
  1. Nobody mentioning the bug in printf (and how it probably shouldn't be used in the first place)?   %ld = long, but the variable is int. This will work great, until you build that code as a 64bit build on Unix, where a long is 64bit and int is 32bit. Most compilers should even warn about this.   Either use %d or (safer) use cout instead of printf.
  2. Trienco

    NonCopyable everywhere ?

    The main reason is usually doing things like having "new" in the constructor and "delete" in the destructor. Any accidental temporary copy would typically cause a double delete eventually (or worse, leave your original with an invalid pointer for a long time, only crashing much later and being a real pain to debug).   One solution is explicitly deleting all problem operations (assignment, copy construction, etc.)   A typically better solution is to properly express your ownerships by using the correct smart pointer. In the above case, use a unique_ptr instead of a raw pointer and suddenly code that would accidentally (or intentionally) copy or assign won't compile. Problem solved without messing about with deleting default functions. If data should be shared, use a shared_ptr and data is automatically deleted at the proper point. In both cases, you don't need to worry about manually deleting anything.   Just making literally everything noncopyable for no good reason would simply look like unreflected cargo cult programming (yes, it's a thing and scarily common).
  3. That's exactly the same problem, except you apparently changed from static linking to dynamic linking. All your changed build option did was to move the error from compile time to run time. SFML is trying to call a function that is missing in your version of the standard library. The first step when you get linker errors about stuff being undefined is to copy paste the error (or at least the symbol it complains about) into a search engine (unless you already know which library or source file contains the missing code). All results are the same: you need at least GCC 4.9. From the fact you HAVE SFML libraries and the error you see now, I would assume you didn't build SFML yourself and just grabbed some pre-built binaries. Those were built with a newer version of GCC and the simplified issue is: it's not compatible with yours (technically the problem is more with the different standard library versions).   Since you're on Windows anyway and struggling with very common compiler/linker errors: why are you adding the extra pain and complexity of using an IDE that uses MinGW instead of some Visual Studio Express or Community version? Dealing with the extra issues from potentially outdated GNU ports for Windows is not something I'd do without a very good reason.
  4. Just quickly going over it:   Yes, when you move (especially along local axes), your orientation is relevant.   You won't get anywhere with just your cameras position. A direction means you have a "from" and a "to". To get a view vector from your camera position, you ALSO need the position of whatever you want to look at. If you use a rotation matrix to store the camera orientation, your view vector has nothing to do with the position and is typically the "forward" part (ie. the third column of your rotation matrix).   I think one problem might be that you don't know what to do with those values you are trying to compute. So the basic question before all others is: how do you intend to set up your view? Will you add the view matrix to the transformation stack, specify it separately through an API call or do you plan to use some helper function like "lookAt"?   Uhm, yes. East should be west. That was me being directionally challenged.   If you don't plan to write your own shaders, the w coordinate shouldn't be something to worry about.   Matrices are a bit painful to display (there's also lots of arbitrary decisions that can be made by the API, engine, or yourself). Usually, the matrix is stored as an array of 16 values, the first four being the first column, the next four being the second, etc. The "mathematical" representation would look something like this (with r=right, u=up, f=forward, p=position):   r.x | u.x | f.x | p.x r.y | u.y | f.y | p.y r.z | u.z | f.z | p.z 0   | 0   | 0   | 1   Of course you might use completely different conventions. In a top-down game, the y-axis might be forward and z would be up.   If movement is going the wrong way, try moving along different axes. Chances are you're simply not looking at the object from the direction you think are. With a right handed coordinate system, "forward" is -z, but the default is the identity matrix (so your camera wouldn't be facing "into" the screen by default, but out of it).   From a camera perspective, things are always moving and turning the other way. If you move a camera up, it looks like the world is moving down, if you turn it left, it looks the world is spinning to the right, etc. Of course it would be absurd to manually apply that to every single object, but depending on your API/framework, that's what happens automatically. In DX you set the view matrix and the default shader will always multiply it with the objects transformation matrix (ObjectMat * ViewMat).   Well, how would you rotate without a vector (or in this context: axis)? Rotating "left" or "clockwise" without an axis is meaningless. It might seem to make sense to you, because you assume a context where you deal with a standing person or vehicle. The axis you rotate around is the one thing not affected by the rotation (except for your position). If you picture sticking a pencil into your head so that when you turn it always points in the same direction, you will soon realize you're rotating around your "up", aka local y-axis, aka (0,1,0)... (or "down", if you flip the axis and invert the angle it's the exact same thing).   What might help a lot in visualizing things is to grab any 3D modelling software of your choice. Most of them will always display the objects local coordinate system as three color coded arrows sticking out from it. If you get used to thinking of everything in your scene to have these arrows and how each arrow is one column of the transformation matrix, things should become a lot more intuitive. Just think of the camera as one more invisible object and don't worry about the whole "inverse matrix" while modifying or placing it. Treat it as a separate step at the end of everything else.
  5. You seem to be confused about the difference between points and vectors and what's supposed to be what (or your naming is just really misleading). (0,0,0) is NOT a useful vector. It has zero length. It has no real direction. It can't be rotated and nothing can be rotated around it. If it's your camera position, it is the one thing that has no business being involved in any of your rotations, because an objects position is irrelevant and separate from its orientation.   Generally, when people are talking about a "view vector", they mean the direction your camera is pointing at (also typically normalized to be unit length).   With all the 0s in your code, I'd expect either that absolutely nothing happens or that the result is completely unpredictable and undefined (try normalizing a zero length vector).   Your first step should be to get an understanding of what you're trying to do and visualize it.   Step one: Forget you ever heard the word "quaternion". Seriously, if you're a beginner and still trying to wrap your head around rotations and how to get a grip on the camera, 4-dimensional complex numbers should never ever enter the equation at any point. For your task, they offer zero benefit, are most likely less efficient due to all the conversion between matrices and quats and will just add a pointless extra layer of confusion and complexity. Suggesting quats for a simple FPS camera is like telling someone who struggles with a ray-triangle test "you must haz Plücker coords, them's the only way.. you won't understand the underlying math (I didn't), so just copy/paste some code, then go forth and spread the gospel of the one and only true and almighty Plücker" (yes, that's basically the impression I get with everybody immediately telling people to use quats whenever a topic remotely involves rotations).   They are neat and very useful in some cases, but they should still be considered a specialized tool. You wouldn't use a power drill to get a nail into the wall, just because both jobs involve putting a longish piece of metal into a hole.   Step two: Understand transformation matrices. They aren't mysterious black boxes that magically make graphics happen. In fact, they are by far the easiest representation to visualize an objects orientation in your head (easier than quats and definitely easier than Euler angles). The three columns are literally nothing but your objects right/up/forward vectors. Creating any orientation that only involves 90° (or even 45°) steps is easy enough to not require a single bit of calculation (not that this would ever be useful in practice, just making a point).   Case in point, rotating 90° to the left means "right" is now pointing "north", "up" is still.. uhm.. "skywards" and "forward" is pointing "east" and your position didn't change (let's assume at the origin), ie. the columns are (0,0,-1,0), (0,1,0,0), (-1,0,0,0), (0,0,0,1). If the fourth coordinate (w) confuses you, don't worry about the math and just pretend w=0 means "direction" and w=1 means "position". There are generally no other values (at least not in places you care about anytime soon).   Step three: There is no camera. A camera is just a nice concept to help picturing stuff in your mind. When it comes down to the math, you just move everything the opposite way of your imaginary camera. Still, just treat that imaginary camera like any other object in your scene until you set the view matrix as the opposite of it. What is used to apply the opposite of a transformation? The inverse matrix. What's neat about a typical camera matrix? No scaling or shearing, so there's a simple and efficient way to invert it (though for now, just use whatever inverse function your math library is offering).   Step four: Multiplication order. To not get hopelessly confused or just stubbornly following a "you must always multiply matrices THIS way"-mantra, understand the difference between R*M and M*R. Say R is a rotation around the x-axis (1,0,0). Typically, matrix multiplications are applies right to left. If you used the deprecated OpenGL functions, "rotate" would result in R*M and apply the rotation AFTER all previous transformations (using the local coordinate system, which leads to confusing claims about OpenGL applying transformations in "reverse order" by people insisting on thinking in global terms).   Doing M*R means you apply R before other transformations, back when your objects axes were still identical to the worlds axes. Meaning: it rotates around the global x-axis. R*M on the other hand applies it last, so the x-axis you rotate around is whatever is "right" after all the other transformations. I prefer thinking in local coordinates, because unless you're writing an editor or deal with external forces in your physics simulation, you don't care about what's "north" or "west", you care about what's "forward" and what's "right" from your objects point of view. If you want to strafe to the side, you don't want to do pointless calculations to figure out what "right" is. It's right there in the first column of your objects transformation matrix. You also don't care, you simply apply a translation along (1,0,0) by doing T*M (and not the other way around). Later you will get rid of wasteful matrix multiplications for a simple translation, but understanding first, making it work second and making it efficient once you know what you're doing.     A few simple kinds of "disembodied" cameras:   FPS: -Store a position vector -Store two angles for left/right and up/down -Limit up/down to values -Create camera transformation: M = rotateAroundX(up/down) * rotateAroundY(left/right) * translation -Invert, orthonormalize and set as view matrix (you can do the inversion implicitly like Norman Barrows by inverting all signs and the multiplication order, but the idea is picture what's going on, not to optimize).   Third person: -Store two angles for left/right and up/down -Store a distance to the observed object -Limit up/down to values -Create camera transformation: M = translateBackwards(distance) * rotateAroundX(up/down) * rotateAroundY(left/right) * translation(position of observed object)     -"Backwards" depends on your coordinate system. with a right handed system most likely (0,0,1).. I prefer left handed, since positive z being forward is more intuitive -Invert, orthonormalize and set as view matrix   Free camera (think flight simulator): -Store the whole matrix as one (4x4) -Add transformations depending on input each frame (do NOT reset the matrix at any point)      -Do it in local terms (ie. forward is translation(0,0,-1) * M, pitch (up/down) is rotation(1,0,0, angle) * M, etc.)      -Order of multiple transformations not relevant within frames. While the results are different, changes between frames should be very small and it won't matter much. -Orthonormalize, invert and set as view matrix   Note that the "normalize" part comes first in this last case. Accumulating all the transformations will add more and more floating point issues and screw up the matrix over time, so it should be "repaired" from time to time (fix axes to be orthogonal to each other and normalize the them to be unit length... don't touch the last column, which is the translation/position).   Those are not be the most efficient, fancy or flexible methods, but they should involve the least effort and are the most trivial ways to achieve a trivial goal that I can think of. Also, if there is one part of your engine where performance usually doesn't matter, it's the camera that is only set up once every frame anyway.
  6. Typical FPS style cameras are one of the few cases where storing two angles is perfectly fine and sufficient (and quaternions are likely to be pointless overkill that turn something simple and trivial to visualize into completely abstract math). As long as "typical" means "ground based", so usually without rolling and up/down rotations being limited to 90°.   All you need to keep in mind is that every rotation will also affect the axes that are used by all subsequent rotations. So for an FPS style camera, you always apply your "left/right" rotation first. Otherwise, if you do the "up/down" first, your "up-axis" for the second rotation would be screwed up, plus, your first rotation would be around the global "right", rather than your current local one.   The only issue you can run into is that depending on the API you use and the multiplication order in which you apply transformations can have an impact on which transformation happens "first". On top of that, your own perception of what happens first depends on whether you look at your object from a global or local perspective. For example, if you insist that "right" is always the global x-axis, transformations will appear to happen in reverse order, while considering "right" to be the objects own current x-axis will usually mean they happen in the order you apply them in (I also find the latter to be more useful and less confusing than the "global perspective").   A very appropriate definition of computer graphics I remember from some book was something like "computer graphics is the art of making an even number of sign errors".
  7.   Why is the second preferred?   I suspect it was supposed to be (f & uf) != 0, since the other version is definitely not equivalent. It prevents the implicit cast from unsigned to bool and all the potential compiler warnings that come with it (in VS it's usually something about runtime performance).
  8. Well, you can skip worrying about types if you just use overloads. In your second code snippet, you somewhat pointlessly explicitly specify a template parameter, when the compiler could just as well figure it out automatically. Also, if the only template parameter is the type of the variable you want to write to, there is no need for templates in the first place (though it can make for a useful default).   void getOpt(const char* key, int* value, int min, int max) { parse as int } void getOpt(const char* key, int* value, int min, int max) { parse as float }   Personally, I'd add one more layer to only overload some "parse as X" function and keep the rest a template. Then you can always just call   template<typename T> void getOpt(const char* key, T* value, T min, T max) {}   getOpt("x", obj->x, ..); getOpt("y", obj->y, ..);   Which is also the first step to introduce black metaprogramming magic that would allow for things like for (each element in config file)     getOpt(elementName, object);   This would automatically assign the value of an element of name X to the member of object that is also named X and automatically use the overload for the type of that member. As a bonus, for any types you didn't define overloads for, you could automatically recurse into the object (and config file element) to handle nested constructs.   While that level of automatism can be nice and convenient, it's also a major pain to debug if things go wrong. See this thread as an example of "just because you can, doesn't necessarily mean you should".
  9.   Based on the admittedly random assumption of a 32bit build. Indexing the words instead of storing pointers means you can get away with a 16bit index, rather than a 32bit pointer (but obviously only if 16bit are enough, hence the 64k limit). Of course, if we're talking 64bit pointers, then a 32bit index would still be an improvement.   On the other hand, if the system does need to deal with a huge number of words, the approach as such is probably not a good idea in the first place. As I said, it would be my first instinct quick and dirty way to just get it done, without much regard to resource usage.
  10. It also depends on how many queries you expect during one session. If this is the whole point or a major feature of the software, starting to physically search through files on your drive would appear to be a big no-go and the more interesting question would be: what kind of data structure is a good compromise between fast look ups and memory usage.   A quick and dirty approach that is heavy on memory:   map<string, set<string> > table;   words = table[first_tag]; for each additional tag {     words = intersection(words, table[additional_tag]); } return words;   The very first step should probably be a set<string*>, so every word is only kept in memory once with the set only storing pointers to them (or storing them in a vector and having only indices in the set, though that only makes sense if you expect fewer than 64k words).
  11. Trienco

    The fruits of laziness

    I'd be too worried about my sanity if I ever dived into it that far. Usually, when I need to save the preprocessor output just to figure out where the problem is, that's a good time to start looking for a way out of the rabbit hole.
  12. Trienco

    The fruits of laziness

    Sometimes you just get very tired of writing endless tons of boiler plate code. Case in point, importing/exporting nested data hierarchies. Due to reasons, fields aren't explicitly looked for, since they might have been "inherited" through referenced sub-objects and will only be partially overwritten.   So after staring at endless screens full of if-else-if-else-if string comparisons for each element name, you develop a desire to automate this kind of mess.   Enter the scary depths of boost::fusion and the black magic that can invoked by macros and template meta-programming.   So, this now automagically "just works":   DEFINE_ENUM(MyEnum,     (ALPHA)     (BETA))   BOOST_FUSION_DEFINE_STRUCT((test), TestStructNested,     (float, myFloat)     (unsigned, noSign)     (MyEnum, greekLetter))   BOOST_FUSION_DEFINE_STRUCT((test), TestStruct,     (bool, yesNo)     (std::string, someString)     (test::TestStructNested, nested))     int main() {     test::TestStruct myStruct;       pugi::xml_document doc;     doc.load_file("in.xml");     fillStructFromXml(doc.first_child(), myStruct);         pugi::xml_document newDoc;     addStructToXml(newDoc.append_child("root"), myStruct);     newDoc.save_file("out.xml");       return 0; }     And yet, despite the luxury of being able to add new fields and structs and enums without writing a single line of code to actually read/write them, the real horror is what makes it work and the bad feeling about unleashing it unto mankind.   Having functions to convert enums from/to strings isn't too uncommon, so some people have already been unfortunate enough to see things like this:   #define ADAPT_ENUM_VALUE_TO_STRING(R, ENUM, ENUM_VALUE)\     case ENUM::ENUM_VALUE: return BOOST_PP_STRINGIZE(ENUM_VALUE); break;   #define ADAPT_ENUM_VALUE_FROM_STRING(R, ENUM, ENUM_VALUE)\     else if (!strcmp(str, BOOST_PP_STRINGIZE(ENUM_VALUE))) v = ENUM::ENUM_VALUE;   #define ADAPT_ENUM(ENUM, ENUM_VALUES)\     inline const char* toString(const ENUM& value) {\         switch (value) {\             BOOST_PP_SEQ_FOR_EACH(ADAPT_ENUM_VALUE_TO_STRING, ENUM, ENUM_VALUES)\             default: throw std::runtime_error(std::string("invalid value for " BOOST_PP_STRINGIZE(ENUM) ": ") + std::to_string((unsigned)value));\         }\     }\     inline void fromString(const char* str, ENUM& v) {\         if (!str) throw std::runtime_error("String can't be null");\         BOOST_PP_SEQ_FOR_EACH(ADAPT_ENUM_VALUE_FROM_STRING, ENUM, ENUM_VALUES)\         else throw std::runtime_error(std::string("invalid " BOOST_PP_STRINGIZE(ENUM) " value: ") + str);\     }   #define VALUE(R, _, ENUM_VALUE) ENUM_VALUE,   #define DEFINE_ENUM(ENUM, ENUM_VALUES)\     enum class ENUM {BOOST_PP_SEQ_FOR_EACH(VALUE, _, ENUM_VALUES)};\     ADAPT_ENUM(ENUM, ENUM_VALUES)   But the real "beauty" is the code that iterates over the adapted struct members until the member name matches the xml element name and recursively delves into nested structs...   template<class XmlNodeType, class MemberType> typename std::enable_if<std::is_class<MemberType>::value, void>::type inline fromElement(const XmlNodeType& node, MemberType& member) {     for (const auto& child : getNodeChildren(node))         setMemberFromXml(child, member); }   template<class XmlNodeType, class MemberType> typename std::enable_if<std::is_enum<MemberType>::value, void>::type inline fromElement(const XmlNodeType& node, MemberType& member) {     fromString(getNodeText(node), member); }   template<class XmlNodeType, class ValueType> typename std::enable_if<std::is_integral<ValueType>::value, void>::type inline fromElement(const XmlNodeType& node, ValueType& value) {     value = static_cast<ValueType>(strtol(getNodeText(node), nullptr, 0)); }   template<class XmlNodeType> inline void fromElement(const XmlNodeType& node, std::string& value) {     value = getNodeText(node); } //Bunch of additional overloads       template<class XmlNodeType> struct SetMemberByName {     SetMemberByName(const XmlNodeType& node) : xmlNode(node) {}       template<typename Iter, typename End>     void doIt(const Iter& it, const End& end, boost::mpl::false_)     {         const char* memberName = boost::fusion::extension::struct_member_name<Iter::seq_type, Iter::index::value>::call();           if (!strcmp(getNodeName(xmlNode), memberName))         {             fromElement(xmlNode, *it);             return;         }           doIt(boost::fusion::next(it), end,             boost::fusion::result_of::equal_to<typename boost::fusion::result_of::next<Iter>::type, End>());     }       template<typename Iter, typename End>     void doIt(const Iter&, const End&, boost::mpl::true_) {}       const XmlNodeType& xmlNode; };   template<class XmlNodeType, class StructType> void setMemberFromXml(const XmlNodeType& node, StructType& obj) {     using namespace boost;       SetMemberByName<XmlNodeType>(node).doIt(         fusion::begin(obj),         fusion::end(obj),         fusion::result_of::equal_to<         typename fusion::result_of::begin<StructType>::type,         typename fusion::result_of::end<StructType>::type>()); }   template<class XmlNodeType, class StructType> void fillStructFromXml(const XmlNodeType& root, StructType& obj) {     fromElement(root, obj); }   I still can't tell if that represents reaching a new level of C++ or entering a new circle of hell. The mere thought of what must be going on in those boost headers is pure nightmare fuel.
  13. Trienco

    C++ most vexing parse

      It's also not the same thing, will fail if Foo isn't copy-constructable and depending on the complexity of Foo might be horribly inefficient. Though at least the latter will most likely get optimized away.   Example: struct Bar {};   struct Foo {     Foo(const Bar& b) : x(new Something) {}     ~Foo() { delete x; }       //No copy/assignment allowed, because it would be a major bug     Foo(const Foo&) = delete;     Foo& operator=(const Foo&) = delete; };     Foo y{ Bar() }; //Compiles Foo x = Foo(Bar()); //Doesn't compile (trying to use deleted copy constructor)
  14. Trienco

    How to switch turn in a card game?

    It's always a little worrying when conditional logic is thrown at simple "number problems". While I agree it's easier to understand in "for beginners", it also teaches bad habits (yes, I consider needless branching as bad style).   Plus, in a more generic game with 2-8 players, you'd be completely screwed with any kind of "if it was player x's turn, it is now player y's turn" logic.   List<Player> players; int activePlayer = 0;   while (playing) {     //do stuff for players[activePlayer]       //next player, modulo (%) will ensure wrapping back to 0 after the last player     activePlayer = ++activePlayer % players.Count(); }   And if you prefer more readable code, keep in mind that pretty much every time you feel the need to comment a piece of code, it should probably be made a function named very much like what your comment is saying:   int nextPlayer() {      activePlayer = ++activePlayer % players.Count(); }   ...   while (...) {     ...       activePlayer = nextPlayer(); }
  15. Trienco

    Am I doing it all wrong?

    Programming isn't about memorizing code. It's about understanding your tools and the ability to use them to figure out a solution to a problem. The code isn't "what" you're doing, it's "how" you're doing it. What you generally want to learn is the "what", because the "how" will come naturally if you know your tools.   In practice, you will usually solve each problem once and later go back to reuse or copy from your previous solution. Though it can make sense to force yourself to write it again and again a few times in some cases, until a particularly common algorithm (say quick sort, A*, etc.) isn't just that "mysterious chunk of code I wrote years ago and don't remember how it works".    Copying from tutorials was probably not the point of writing them. Not only because it can be embarrassing to have code that calls "MyFunc" (yes, I've seen that kind of thing in production code), but because the point of that code is to illustrate and focus on a particular point or technique. That means there's usually little to no error handling and room for optimization, because that would obfuscate and distract from the actual subject matter. Also, unless you plan to build your whole program around a tutorial, it will rarely be something you can just drop into the rest of your code without plenty of adjustments. And then there's always the copyright question and what kind of use is permitted by the tutorial code's license.   So I'd move on to the next project. Learning and experience will happen naturally, unless you plan to wipe your memory or drink yourself silly after each programming session. While doing that, you will probably revisit your last project a lot anyway. For practice, resist the urge to copy/paste, if you just want to get it done, reuse away (just try not to copy comments or names that are nonsensical in the context of the new game).
  • Advertisement

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!