Jump to content

DanW58

Community Members
  • Posts

    417
  • Joined

  • Last visited

  • Days Won

    5

Everything posted by DanW58

  1. Another solution is to display (hot-key optional) stats in the opening screen, while the game is initializing, and the game does not start until all parties click OK. From then on, all you have is your memory of the stats. I suggest this as a solution because personally I would rather NOT see stats every time I click on a unit. That's information overload, to me. I would hate it, frankly. If you are a commander of a US Army platoon, do you see stats pop up every time you look at one of your soldiers? It doesn't add to immersiveness and belivability; it detracts from both. Clicking on a unit is intuitively associated with saying "yo!", to issue a command. Frankly, I would make the unit mouse-over and selection sprites far more subtle that they are presently, precisely to make the game feel more natural. First Century was a long time ago, and holographic overlays are still the stuff of SciFi even today. Okay, but how exactly does having stat numbers appear over units prevent brit slinger abuse?
  2. Exactly. And role is better depicted artistically than using words or numbers. Units with a lot of metal in the armor are visually and intuitively expected to take arrows with a grain of salt, but move more slowly. However, this requires proper depiction of metal, which takes a bit of science to understand how just by using diffuse and specular colors. Assets currently in game make very poor use of color to distinguish metals from non-metals. Improving the texturing would go a long way to make "stats" easier to visualize.
  3. Here's a mini-course in materials and optics: The diffuse texture represents the color of the material for the portion of light that reflects "diffusely" (randomly, sort of, in a photon by photon basis). The specular texture represents the color of the material for the portion of light that reflects "straight"... mirror-like. Some materials are entirely diffuse, or at least best modeled as such, such as dirt or cloth. Represent as colors in diffuse; black in specular. Some materials are almost entirely specular, such as highly polished metals. Represent as color in specular; black in diffuse. The FIRST RULE of scientific texturing is to never make the sum of diffuse and specular greater than white. Morover, never make any single channel add to more than 1.0 (or 255 if using char color notation). Doing so makes that pixel in the texture set indicate a material that reflects more light energy than it receives, which is not physically possible. Blender, unfortunately, entirely ignores this concern in its light/material pipeline. Blender's pipeline is NOT physics based, no matter how much they repeat it is. If you want realistic materials, you have to enforce the rules of optics yourself. And this is your first rule; make sure no pixel adds to more than white if you add diffuse and specular. They should never add to more than 0.9, 0.9, 0.9, really, as even mirrors have trouble reflecting more than 90%. So, something white in specular but black in diffuse looks like a mirror. Something white in diffuse but black in specular looks like white chalk powder. Something white in both looks like an ugly glowing thing from another universe. But there are many kinds of impossible colors that can ruin a game asset without adding to greater than white. A material that is red in diffuse but green in specular is a complete optical atrocity. No such material exists or could ever exist. From this point of view, the old texturers' trick of taking the diffuse texture and desaturating it to make a starting point for a specular texture is not entirely without mertit; it does prevent at least some atrocities. The problem is that everything ends up looking like plastic when you do that. Plastics behave precisely that way: diffuse color with gray specularity. Assets look infinitely better when you understand basic optics and make sure the materials are deliberately represented. I prefer to use separate textures for metallic and non-metallic areas, and later combine them. Metals: One thing that is fundamentally different for metals, besides their being very specular, is that their specularity has color. That is, if you look at a blue object reflected on a polished gold surface, it will look dark, because gold is yellow in specular, which doesn't reflect blue as well as red and green. The same is NOT true of paints and plastics, which reflect WITHOUT coloring their reflections. Another thing that is fundamentally different for metals is that their "diffuse" specularity, if any, is more like an over-saturated and dimmed version of their specular color. This is because light reflecting off a colored metal that reflects diffusely, randomly, implies that it bounces two or three times before coming out, probably by entering and exiting a micro-cavity on the metal surface, and at each reflection it becomes MORE colored by the metal. Thus, if your metal is gold, which in specular would be about 0.9, 0.9, 0.5, after a second bounce it becomes 0.81, 0.81, 0.25, that is, the square of the specular. And if it bounces a third time it will be 0.73, 0.73, 0.125, i.e. cubed. If we assume two and a half bounces average, we could use the average of the square and the cube, roughly 0.77, 0.77, 0.185. But probably less than 10% of the surface is pockmarked by microscopic cavities, so we can take a tenth of that figure, and compensate by reducing specular by 10%. So, our final physics modelling for gold's color is 0.077, 0.077, 0.0185 diffuse, and 0.81, 0.81, 0.45 specular. If you try it, you'll be surprised how remarkably golden that looks, even if in the diffuse texture it looks too dark to even see. Science delivers! Another thing that makes metallic reflections recognizable is that the angle does not matter; they reflect the same at any angle. Non-metals: Specular reflections from non-metals are a completely different animal. Here reflections are caused by photons being unable to refract into the material, which depends on the incident angle and the material's index of refraction (related to its dielectric constant), and following Fresnel law. If the photon reflects, it does so color-blindly. If it does not reflec, but enter the material, instead, then it may meet a particle of pigment and become colored, and eventually come out of the material in a diffuse way. This is how a typical car paint works. To represent car paint in a game you need a fresnel shader that will typically ignore your specular texture color, compute a variable intensity of specularity based on the angle of reflection, and make that specularity white; and then use 1 minus that specularity to modulate diffuse color. If you cannot use Fresnel, then you are relegated to the horrible last option of making specular color gray for paint, which will actually look like cheap plastic. What about things like plants, and human skin? Well, with shaders you can do wonderful things, like sub-surface scattering; but if you are limited to using standard OGL pipeline diffuse and specular, make plants green and human skin pink or brown to taste, and then make specular dark-grey. Plastic looks look better than metal for living things...
  4. A position is NOT a vector, mathematically. A position is a position whether there is a frame of reference or not, whereas a vector is a position relative to another position, possibly an "origin", in a specific frame of reference. The confusion is more than just semantics. A vector can be divided in half. A position is a point, which has no size, and therefore cannot be divided. A vector can be rotated. A position cannot. A vector can be added to a position to obtain another position; and a vector can be subtracted from a position; but a position cannot be subtracted from a vector; it makes no sense. And you might ask, "well, isn't it easier to just mix it all together?". Yes, it is; as it is also easier to make programming mistakes. By the same argument, a temperature and a distance are both quantities. Does that mean that we should be able to add them in a program? C++ is a huge evolution over C precisely because it made it possible for us to create our own types, and make it more difficult for conceptual mistakes to compile. That's why people should be trying to NOT mix concepts together when possible. To confuse positions and vectors is not too different from making every parameter a typedef of float. We've got a type system; let's use it. So let me repeat the question again, as now it is lost in the previous page: Could someone please help me understand this piece of code? I've been stuck for two days here. It's in the file source/simulation2/components/CCmpFootprint.cpp, the function PickSpawnPoint(), half way down, where it reads, if (m_Shape == CIRCLE) // Multiply the point by 2pi / 6*(distX+r) to get the angle. pos += u.Rotate(fixed::Pi() * (offsetPoints[r] + k) / (3 * (distX + r))).Multiply(halfSize.X + gap * r ); else { The problem is that I'm precisely immersed in making clear separations between what is a point and what is a vector and what is a unit-vector, etceteras. In my system, a point can be added to a vector, to get another point, for example; but a point cannot be added to another point. Much less can a point be multiplied, unless there is a way to end up with a "point cluster". But here in this code pos is referred to as a point, in comments, but implemented as a vector; and this comment says that we are going to "Multiply the point..." (literally! How does one "multiply a point"?) by what looks to me like an angle, sort of like dividing a cat's tail by a magic flavor, allegedly to obtain yet another angle.... So this "point" is some kind of number that multiplied by an angle yields yet another angle?!?! 2pi/6 (or pi/3) is an angle, alright, (60 degrees in radians); that much I know. The (distX+r) is as clear as mud even though I know r is some kind of "row number". And then this act of magical multiplication is implemented by operator+=(); yep... But the worst part is I can't figure out what pos is, or what it is supposed to be. A point or a vector? If I make it a CFixedVector3D<WORLD>, a dozen things break; and if I make it a CFixedPoint3D<WORLD> about two and a half dozen things break... Thanks in advance.
  5. This kind of problem is typical of games using the method whereby the scene is rendered to a depth texture from a light source, and then the depth info is transformed and compared with when rendering from the camera. "Depth buffer" is it called? Precision is a big issue with this method. There are offsets that can be tweaked, but ultimately you'll end up either with shadows that don't make it like they should, or light that doesn't make it that should have. I once saw a paper about a possible solution for this problem, but I no longer have the link; it involved transforming the scene BEFORE rendering the depth textures, then compensating for it numerically in the depth render, so as to end up with a depth buffer that roughly aligns with the view frustrum and better uses the depth bits. A more expedient solution is to make sure the assets don't have cases of self-shadowing that's cast too shortly (from too shallow a depth difference). In the case of the building shown a few posts back, making that ledge twice as high would probably cure the problem, if the problem is what I think it is.
  6. There's a greater, cultural problem, where games are concerned, that this is a part of. Just as in politics, where some fanatical and loud minority can steer politics in a direction that disadvantages the vast majority of people, in gaming culture there's also one typical loud minority that drives developers to ruin games for the rest of us. This minority is the dedicated, full time, competitive online gamers, who typically no longer care for the story or the art, or even the sound quality or the graphics anymore (presumably they cared at the beginning); once they become advanced and start getting an online reputation, all they come to care about is winning, and to win they need to know how the game works down to points of health removed by each single use of a weapon given so many points of defensive armor... Such people typically DEMAND stats and whatnot. But the thing for developers to keep in mind is that such players are NOT representative of most players. Most players would typically enjoy the scenery, enjoy the NOT knowing everything, and enjoy forgetting that they are playing a game in the first place. Realism is far more important than predictability to most of us. In the real world you don't look at a tag for most people you meet to read how likely are they to lie to you, for example. Why is it that in about half of the games that use such stats, they can be read directly in the interface? Who is being served by such openness? I think it is the loud minority of high adrenaline, competitive players that typically ruin games before they even release, if they can... winning also developer attention to the detriment of most people. The proof can be found right in this page, where someone answers the question why should the user know these stats by saying you are more likely to win if you have this knowledge. As I said, all they care about is winning. All other aspects of the game that would increase immersiveness are, to them, merely decorative.
  7. Look at many of the classic games. In Doom, health was represented by your face in the bottom bar; and the capabilities of the various monsters was for you to discover. In Descent it was obvious that kinetic munitions bypassed shields but had the problem of running out, whereas energy weapons never ran out but needed to get through shields; but you never had exact numbers of how much damage of what kind this does, etceteras; it was up to you to discover what worked. In Masters of Orion, battle stats used pretty advanced math, with gaussian probabilities and whatnot; but this was discovered and published by hackers; it was never even hinted at in-game. In Privateer, it was up to you to discover that the lowliest gun, the laser, was actually better than the top of the line plasma if you added all the extras. RPG's started this boring fashion of putting stats in-game, which is understandable to some extent, since you had to deal with advancing your character, which had so many points for this and so many for that; people got curiours how monsters compared. But the hell with RPG's! In Sim City it was up to you to discover that police stations became more and more corrupt over time, and that probably the only way to deal with it was to destroy them all and then start fresh. In Starcraft there were stats that one could read about online, but they were not present in the game itself. Games where all the rules are put on the table are the least immersive. Gamers speak of things like "to hit" ratios. If you go to a weapons show, do the developers show "to hit" numbers for their weapons? Stats are ludicrous simplifications, sometimes entirely absurd quantities; and the least you reveal about them the better.
  8. It's interesting that that exists, but it doesn't answer my question about what pos is. I think a clue is in the lines... // Scale and convert the perimeter coordinates of the point to its offset within the row. 236 int x = (offsetPos.Dot(u) * distX / halfSize.X).ToInt_RoundToNearest(); 237 int y = (offsetPos.Dot(v) * distY / halfSize.Y).ToInt_RoundToNearest(); I've no idea what that means, really, but I have a feeling that pos is really a vector; NOT a position; a vector at the center of the spawning building, that can be rotated to identify a spawning point, or something of the sort. But I need positive confirmation and technical details of how all of this works. Specially the lines I showed in my previous post, with the abominably confusing comment...
  9. Completely unrelated. Game immersiveness requires mystery and uncertainty. Development is a totally separate matter.
  10. I haven't read this entire discussion; just first half of the first page an second half of the last. I'm totally with wowgetoffyourcellphone that the user should NOT be able to see these stats. Why? Because it should be part of the game itself to discover them. What there could be and maybe should be is in-game descriptions in faded old script that allude to what different units are good at and what they aren't good at. Stats are a detail of implementation that should be hidden from the client, just as class variables should be. I had a similar discussion at a forum with the devs of another game, an RPG for cell phones, where I said to them they should take out the multiple status bars above and below the player. They had one for health, one for armor, another for energy... And they were thinking of adding a fourth one for gad knows what... I said to them you should get rid of ALL of these bars. Those are details of implementation. Depict the results. Health could be depicted by blood dripping. Armor by loss of chunks of it. Energy by the rate of attacks slowing down. Why are you showing these things as bars? But they disagreed with me, so I left. If you are given enough information about how the game works that you can figure out what will happen in every situation, then what's the point of playing it? A game should mirror the uncertainties of life.
  11. Thanks, this was my purpose in posting this; curious if there's any interest in the idea, as I will probably need some help making this happen. By the way, I have a background in C++ programming, in SSE Assembler, as well as in shader programming in GLSL. Additionally, I have a background as an artist; I used to work full time Blender modelling at one time, plus texturing. One area where I would need help is in any javascript needed; I never touched javascript with a 10-ft pole.
  12. Could someone help me understand this piece of code? I've been stuck for two days here. It's in the file source/simulation2/components/CCmpFootprint.cpp, the function PickSpawnPoint(), half way down, where it reads, if (m_Shape == CIRCLE) // Multiply the point by 2pi / 6*(distX+r) to get the angle. pos += u.Rotate(fixed::Pi() * (offsetPoints[r] + k) / (3 * (distX + r))).Multiply(halfSize.X + gap * r ); else { The problem is that I'm precisely immersed in making clear separations between what is a point and what is a vector and what is a unit-vector, etceteras. In my system, a point can be added to a vector, to get another point, for example; but a point cannot be added to another point. Much less can a point be multiplied, unless there is a way to end up with a "point cluster". But here in this code pos is referred to as a point, in comments, but implemented as a vector; and this comment says that we are going to "Multiply the point..." (literally! How does one "multiply a point"?) by what looks to me like an angle, sort of like dividing a cat's tail by a magic flavor, allegedly to obtain yet another angle.... So this "point" is some kind of number that multiplied by an angle yields yet another angle?!?! 2pi/6 (or pi/3) is an angle, alright, (60 degrees in radians); that much I know. The (distX+r) is as clear as mud even though I know r is some kind of "row number". And then this act of magical multiplication is implemented by operator+=(); yep... But the worst part is I can't figure out what pos is, or what it is supposed to be. A point or a vector? If I make it a CFixedVector3D<WORLD>, a dozen things break; and if I make it a CFixedPoint3D<WORLD> about two and a half dozen things break... Thanks in advance.
  13. I always say that profanity-phobics make profanity necessary. As long as there are people offended by it, there will be a need for people to provide the required offenses. But yes, consistency, or the lack of it, is a whole other story.
  14. There needs to be a new forum category for game development other than 0ad but using the Pyro engine. If the category exists and I missed it, my apollogies. I'm working on cleaning up the engine so that I can understand it, and it's a lot of work; but when I'm done, what I want to do next is to try and see if I could use it for a totally different (at least superficially) kind of game. I want to make a space game with this engine. No, not a first person shooter; it would be a strategy space game like good old Masters of Orion. And NOT a remake of MOO; something better. In MOO there were the gallactic map (if 50 star systems could be called a "galaxy", LOL), and a tactical screen for space battle (somewhere in planetary orbit, presumably). The gallactic map was not too sophisticated; a purely 2D thing; each fleet represented by a little arrowhead symbol. When enemy fleets meet, the game opens up a tactical display, where you get a chance to tell each ship which way to move and what to shoot at in the next turn. The ships of both fleets appear stationary on the screen at first. MOO was a turn-based game. So, once you are done giving instructions to your ships, the battle advances through one turn. Anyways, the ships could be implemented using regular units or special ones, such as sea-going ships. MOO2 was 2D, and this game could be 2.5D in the sense that, more or less, the space ships could be moving around on a virtual piece of glass, with a starry background seen through. In MOO2 it was a wonderful change, really, after hours of building your empire on the gallactic screen and planetary screens, to then change to a tactical environment and engage in space battle. At the end of the battle, usually there was a change to either bombard a planet to smitherines, or to land forces and take control of it. This part of the game was not too well developed in MOO: All you could do here was watch the ground battle. This new game could fix that and introduce something like Starcraft for planetary conquest. So it would be like 3 games in 1.
  15. Well, it depends on the type of setter. What I've done is BAD, admittedly; I simply wrote functions that expose the members directly, e.g., float & Xref() { return X; } That boils down to the same thing as making X public; it prevents the compiler from making its best optimizations. Note that through float& the variable can be read as well as written, so that some no-aliasing assumptions cannot be made. What I should have written is something like void setX( float const & f ){ X = f; } But then I would have to work hard for two months fixing code to call this function, as sometimes the code has "foo.X += 5;", which indeed reads and writes X. I intend to do it, but not just yet; I want to pick the low-hanging fruit for now. The first column in Perf output is total time spent? If 1.13% of the time is spent in isqrt64() vanilla, I would expect my code to spend half as much time, such that there should be about 0.6% performance gain, which is small, but not entirely insignificant. I have good news. This new approach is starting to bear fruit: Isn't that clearer and safer than, void AddNode(const CVector3D& pos, const CVector3D& rotation, fixed timePeriod); ? Now the problem I'm having is with source/simulation2/components/CCmpCinemaManager.cpp:282... The problem is, Cinema refers to the camera, doesn't it? But this is under simulation2 !!! EDIT: Another seeming inconsistency... In file Render.h, struct SDashedLine { /// Packed array of consecutive dashes' points. Use m_StartIndices to navigate it. std::vector< CVector2D > m_Points; ................. Why is this not CFixedVector2D ? Isn't this world ground coordinates? EDIT2: Never mind; I think I got it: Entities positions are given in CFixedVector2D, but these dash lines and stuff are not crucial to making games deterministic on input so they don't matter as much and float is used then. Ok, this kind of breaks my neat scheme of having a unique representation in each frame of reference. So, there's an entities representation and a non-entities representation in each FOR... --------------------------------------------------------------------------------------------------------------------------------------------------------
  16. Stan; I'm forced to repeat myself: A reference IS NOT A POINTER. I repeat: A reference IS NOT A POINTER. You are talking about accessing something by pointer as an "extra step", but I did not write getters and setters using pointers; I wrote them using references, and a reference IS NOT A POINTER. Let me elaborate: If we are talking about access to a variable in memory, that access ALWAYS needs an address, of course. The question is whether that address is embedded with the instruction (access "by reference"), or needs to be pulled from another variable containing it (access by pointer). To write a get function that gets a private variable by reference is IDENTICAL to making that variable public and accessing it "directly". In fact, a const int & getX() const is FASTER than using a public ".X" in the sense that it informs the compiler that the call will not be modifying X, which allows the compiler to make additional optimizations in the code, generally. Can a reference ever be a pointer? Yes, in some cases it may compile to something like pointer code, but rarely. Usually it will compile to NO CODE. But if you tell the compiler to count calls to the get by reference function, then you are FORCING the compiler to implement it as a function, and we have a case of observation affecting the result of an experiment, as in Quantum Mechanics. But if you don't believe me, indeed, ask wraitii; he knows what I'm talking about and can probably explain it to you more clearly. I have my differences with him in terms of taste: he hates to see .getX() functions all over the code; whereas I love them; but note that he never made any claims to get functions affecting performance. EDIT: Where get by reference functions DO compile to code is if you compile for debug. Then the compiler HAS to turn the get function into code that the debugger can enter into and exit from, otherwise it would look as if the function failed to compile or something; which is the truth in nodebug.
  17. Stan, the use of getters and setters DOES NOT impact performance negatively. This is a huge misconception. It is a compile-time, stylistic change. How does code read a public variable in a struct? It does so "by reference". So explicitly writing a function that gets at it "by reference" boils down to the same code. People often think that references are like pointers. They are NOT. When you use explicit referencing, in C++, you are basically telling the compiler, "do this however is best; I trust you". The concept of reference is intentionally vague, for this reason. The point is to get SMALLER AND FASTER code, by telling the compiler to optimize the heck out of it. And where you use a const ref, you are further informing the compiler that this call won't be modifying the thing, which allows the compiler to make wilder assumptions leading to even better performance improvements. In many cases, getters and setters produce NO CODE; and where they do, it is often for the better. Regarding sqrt(), if my patch did not improve performance, the only possible reason for it is that isqrt64() is not being used as much as we may have thought. I was 50-50 on whether the cacheing idea was going to make a difference; but my other change, namely using double precision sqrt() inside isqrt64(), for sure should have a measurable impact, otherwise. Re. "Looking forward to seeing it Promises Promises." I sense something I do not like. Re. "I will continue to help if I can but you might soon reach my limits." Please don't let me reach your limits, then. I appreciate all the help I got from you.
  18. AHHHHHHHHHHHHHHHHHHHHHH.... I get you now!!!! Alright, time for sleepy pies; but I think tomorrow is going to be a productive day. Many Thanks! EDIT: By the way, the changes I'm working on now are NOT to improve performance. They are striclty code refactoring that should have NO impact on performance, negative or positive. So, if you guys are interested in performance but not interested in refactoring, I'd recommend you incorporate that patch you just profiled, and NOT wait for my next one. My version will eventually get MUCH faster, as once there's clear divisions in the code it's easy to threadify it; but it's going to take a lot of changes to get there. And just to be clear, I'm sharing my changes with you in the spirit of GPL, because I intend to fork this engine, make my own game with it, and I don't have a server to put the code on, at the moment, and don't want you to be able to say I violated GPL. But whether you use my work or not it's your business. (I know I can put it on sourceforge; it takes work tho...)
  19. I think I know exactly why there's Fixed. As I mentioned before in some forum thread, I was once working on my own engine, and I had a moment of enlightenment, and saw that float's are not good as accumulators (e.g. counting votes), and not good for complex terrains, due to their absolute precision decreasing with the magnitude of the value they hold. Near the origin, floats can give you extraordinary fine detail; but if you go away from the origin a few miles in any direction, the float steps get coarser and coarser, from cm to dm, to meters to tens of meters. So I began to work on an engine that would use ints for the world. At 0.1 mm per step, 2 billion steps is 200 million mm, or 200,000 m, or 200 km (don't you love metric?); good enough for a whole city and suburbs, and a few cows, all at hair-width precision; same precision everywhere. Who needs float's? But that only applies to representing positions (points). Not that there's really anything wrong with expressing angles as integers, too. I mean, divide the circle into 4 billion (or rather 2^32) parts, and you have ultra-fine angular control, also of uniform precision, and naturally circular as numerics go (overflows just right). And who needs float's, again? So I REALLY have no complaints, EXCEPT the fact that the use of Fixed and Float for rotation seems inconsistent in the engine. THAT is what's driving me crazy: Sometimes it's floats, sometimes it's fixed, sometimes it's radians, and sometimes it's degrees, and there's often no way to know. If there seemed to be some "official" way to represent rotation, I would reflect it in my classes; I just don't know what it is. Same goes for speed; I find it is often float, even if it accumulates over fixed terrain... And there's no acceleration, no mass, no moment of inertia, no angular torque or acceleration... But that's my NEXT COMPLAINT And the macros are not mine; they were in the engine already; I just used them. Regarding the graph, the green line is my patch... so you're saying that my patch proved to be faster? I thought it was... I mean, I can feel it runs more smoothly. I used to have occasional freezes, or mixed frames, but since the changes I made, all that went away.
  20. Wow! I came here to lament and despair, but found a lot of curious replies. Optimus, I was starting to figure out the impact of SSE. Wish I would have joined earlier; I could have written all the SSE you needed and then some directly in Assembler, and bypass all the subversive interfaces. My experience was with with 3DNow!, rather, but same thing; --Intel just copied AMD, as always... R.e. the profiling, thanks!, but I don't understand anything; what's the conclusion? Did it speed up the engine or not? I can't tell from your words or your files or your graphs. I see that "comparison" graph, but it has two graphs, one for frequency, but doesn't look like a typical speaker response ;-), and the other is frame by frame graph... of what I have no idea. And there are red and green curves, but one says 0/frame - n = 1629 and the other 1/frame - n=5157, and I haven't the foggiest idea what that means. I need a Rosetta Stone... Anyways, I came to share my pain and yell for help understanding parts of the code. Just so you understand what I'm doing, first of all, these are my FOR's (Frames of Reference) declarations: First I declare a common ancestor for FOR's, not sure for what reason exactly, I'm not sure how to specify that a template param should be derived from class X... Then I declare my template functor for Transform, which will declare needed transforms, leaving only the work of implementing them. And I follow that by a whole list of FOR structs, full of useful typedefs. They follow a model space to screen pipeline approximately from top to bottom. Below that I have sugar typedefs, and vector and point declarations... ////////////////////////////////////////////////////////////// //// "Frames Of Reference" common abstract type: //// ////////////////////////////////////////////////////////////// struct FOR // --Frames Of Reference' common abstract type { protected: virtual ~FOR() = 0; }; ////////////////////////////////////////////////////////////// template< typename FOR_in_repr_t, typename FOR_out_repr_t > struct Transform { void operator()(FOR_in_repr_t const &, FOR_out_repr_t &) const; }; ////////////////////////////////////////////////////////////// /// Abstract Frames of Reference to be used as template params ////////////////////////////////////////////////////////////// // The first FOR is a null stub for the second to reference. struct ____NO____FOR : public FOR //The "NO" frame of reference { typedef ____NO____FOR FOR_t; // typedef ____NO____FOR otherFOR_t; typedef ____NO____FOR represent_t; // --none-- typedef ____NO____FOR other_repr_t; // --none-- typedef ____NO____FOR fwdXform_t; //NO transform typedef ____NO____FOR bakXform_t; //NO transform private: virtual ~____NO____FOR() = 0; //can't be instanced or inherited }; struct ModelSpaceFOR : public FOR //No need to id models; only one used at a time { typedef ModelSpaceFOR FOR_t; // typedef ____NO____FOR other_FOR_t; // preceded by nothing typedef CVector3D<FOR_t> represent_t; //float vec 3 typedef other_FOR_t::represent_t other_repr_t; //representation typedef Transform<other_repr_t,represent_t> fwdXform_t; //NO transform typedef Transform<represent_t,other_repr_t> bakXform_t; //NO transform private: virtual ~ModelSpaceFOR() = 0; }; struct WorldSpaceFOR : public FOR //This is the virtual world's 3D space { typedef WorldSpaceFOR FOR_t; // typedef ModelSpaceFOR other_FOR_t; // preceded by Models' space typedef CFixedVector3D<FOR_t> represent_t; //fixed-point vec 3 typedef other_FOR_t::represent_t other_repr_t; //representation typedef Transform<other_repr_t,represent_t> fwdXform_t; //unit movements typedef Transform<represent_t,other_repr_t> bakXform_t; //impact queries? private: virtual ~WorldSpaceFOR() = 0; }; struct WorldSpeedFOR : public FOR //This is virtual world velocity vectors for units { typedef WorldSpeedFOR FOR_t; // typedef ModelSpaceFOR other_FOR_t; // preceded by Models' space typedef CVector3D<FOR_t> represent_t; //fixed-point vec 3 typedef other_FOR_t::represent_t other_repr_t; //representation typedef Transform<other_repr_t,represent_t> fwdXform_t; //obstacles affecting speed typedef Transform<represent_t,other_repr_t> bakXform_t; //motion (speed accumulation) private: virtual ~WorldSpeedFOR() = 0; }; struct WorldRotatFOR : public FOR //This is virtual world spin vectors for units ///PROBABLY TO BE AXED { typedef WorldRotatFOR FOR_t; // typedef ModelSpaceFOR other_FOR_t; // preceded by Models' space typedef CVector3D<FOR_t> represent_t; //fixed-point vec 3 typedef other_FOR_t::represent_t other_repr_t; //representation typedef Transform<other_repr_t,represent_t> fwdXform_t; //NO transform typedef Transform<represent_t,other_repr_t> bakXform_t; //NO transform private: virtual ~WorldRotatFOR() = 0; }; struct Entty2SpaceFOR : public FOR //Same as world, but with no height info { typedef Entty2SpaceFOR FOR_t; // typedef WorldSpaceFOR other_FOR_t; // preceded by World space typedef CFixedVector2D<FOR_t> represent_t; //fixed-point vec 3 typedef other_FOR_t::represent_t other_repr_t; //representation typedef Transform<other_repr_t,represent_t> fwdXform_t; // X,Y <= X,..,Z typedef Transform<represent_t,other_repr_t> bakXform_t; // X,Y,Z <= X,H,Y private: virtual ~Entty2SpaceFOR() = 0; }; #if 0 struct EnttyOrientFOR : public FOR //Fwd and Up vectors; ... to become unit vectors { typedef EnttyOrientFOR FOR_t; // typedef WorldSpaceFOR other_FOR_t; // preceded by Models' space typedef CUnitVector2D represent_t; //fixed-point vec 3 typedef other_FOR_t::represent_t other_repr_t; //representation typedef Transform<other_repr_t,represent_t> fwdXform_t; //not sure how this works typedef Transform<represent_t,other_repr_t> bakXform_t; //not sure how this works private: virtual ~EnttyOrientFOR() = 0; }; #else struct EnttyOrientFOR : public FOR //Fwd and Up vectors; FixedVector2D for now... { typedef EnttyOrientFOR FOR_t; // typedef WorldSpaceFOR other_FOR_t; // preceded by Models' space typedef CFixedVector2D<FOR_t> represent_t; //fixed-point vec 3 typedef other_FOR_t::represent_t other_repr_t; //representation typedef Transform<other_repr_t,represent_t> fwdXform_t; //not sure how this works typedef Transform<represent_t,other_repr_t> bakXform_t; //not sure how this works private: virtual ~EnttyOrientFOR() = 0; }; #endif struct ViewFrustrFOR : public FOR //This is view frustrum space, CPU-GPU standard Xform { typedef ViewFrustrFOR FOR_t; // typedef WorldSpaceFOR other_FOR_t; // preceded by world space typedef CVector3D<FOR_t> represent_t; //float vec 3 typedef other_FOR_t::represent_t other_repr_t; //representation typedef Transform<other_repr_t,represent_t> fwdXform_t; //standard perspective matrix typedef Transform<represent_t,other_repr_t> bakXform_t; //inverse matrix for mouse ray queries private: virtual ~ViewFrustrFOR() = 0; }; struct MiniMapCoorFOR : public FOR //Mini-Map coordinate representation { typedef MiniMapCoorFOR FOR_t; // typedef WorldSpaceFOR other_FOR_t; // preceded by World typedef CFixedVector2D<FOR_t> represent_t; //fixed-point vec 2 typedef other_FOR_t::represent_t other_repr_t; //representation typedef Transform<other_repr_t,represent_t> fwdXform_t; //simple scaling from World X,Z typedef Transform<represent_t,other_repr_t> bakXform_t; //simple scaling to World X,Z private: virtual ~MiniMapCoorFOR() = 0; }; struct ScreenPerspFOR : public FOR //Not sure this is needed at all; probably Frustrum covers it { typedef ScreenPerspFOR FOR_t; // typedef ViewFrustrFOR other_FOR_t; typedef CFixedVector2D<FOR_t> represent_t; //fixed-point vec 2 typedef other_FOR_t::represent_t other_repr_t; //representation typedef Transform<other_repr_t,represent_t> fwdXform_t; //NO transform typedef Transform<represent_t,other_repr_t> bakXform_t; //NO transform private: virtual ~ScreenPerspFOR() = 0; }; struct ScrnOverlayFOR : public FOR //screen overlays, & maybe post-proc effects { typedef ScrnOverlayFOR FOR_t; // typedef MiniMapCoorFOR other_FOR_t; typedef CFixedVector2D<FOR_t> (or u16?) represent_t; //fixed-point vec 2 typedef other_FOR_t::represent_t other_repr_t; //representation typedef Transform<other_repr_t,represent_t> fwdXform_t; //NO transform typedef Transform<represent_t,other_repr_t> bakXform_t; //NO transform private: virtual ~ScrnOverlayFOR() = 0; }; ///sweet nicknames: typedef ____NO____FOR UNKN; //temp use to pacify compiler when FOR is hard to figure out typedef ModelSpaceFOR MODEL; typedef WorldSpaceFOR WORLD; typedef WorldSpeedFOR VELO; typedef WorldRotatFOR SPIN; //Probably not an FOR but a helper type like CEulerRadians typedef Entty2SpaceFOR ENTP; //for position, same as world (x,z) typedef EnttyOrientFOR ENTO; //for orientation, used in Fixed fwd & up unit vectors typedef EnttyOrientFOR UNIT; //for orientation, used in float fwd & up unit vectors typedef ViewFrustrFOR VIEW; typedef MiniMapCoorFOR MINI; typedef ScreenPerspFOR SCRN; typedef ScrnOverlayFOR OVRL; //convenient forward declarations: template< typename FOR > class CFixedVector2D; template< typename FOR > class CFixedPoint2D; template< typename FOR > class CVector2D; template< typename FOR > class CPoint2D; template< typename FOR > class CFixedVector3D; template< typename FOR > class CFixedPoint3D; template< typename FOR > class CVector3D; template< typename FOR > class CPoint3D; class CUnitVector2D; //This vector maintains unit-length automagically class CUnitVector3D; //This vector maintains unit-length automagically class CEulerDegrees; //float x,y,z class CEulerRadians; //float x,y,z (NOTE: All of the above is at the bottom of MathUtils.h, for now, as I don't know how to add new files to the build system.) Note that none of this "code bloat" is going to have negative consequences whether in code size or performance. They share a lot of code, and the compiler will merge them as much as it can; the FOR distinctions are compile-time only. Here's an example of two template transform implementations: (For now these are at the bottom of Vector3D.cpp.) //place these in the cpp files where they are most heavily called: template Transform::operator() const (CFixedPoint3D<WORLD> const & in, CFixedPoint2D<ENTP> & out) { out.X = in.X; out.Y = in.Z; } template Transform::operator() const (CFixedPoint2D<ENTP> const & in, CFixedPoint3D<WORLD> & out) { out.Y = mapheight_at(in.X, in.Y); //the name of the function is unsure, but it surely exists out.X = in.X; out.Z = in.Y; } So, the system basically declares all the needed transforms, and even specifies the input and output types; all I have to do then is copy-paste the code into the implementation, and then I can just push objects from one FOR to the other, and they go through the appropriate transformers. Anyone starting to like this? But then I get stuck on the stupidest problem... I have the following conversions: CCEulerDegrees::CCEulerDegrees( CEulerRadians const & r ) : X(RADTODEG(r.getX())), Y(RADTODEG(r.getY())), Z(RADTODEG(r.getZ())) {} CEulerRadians::CEulerRadians( CCEulerDegrees const & d ) : X(DEGTORAD(d.getX())), Y(DEGTORAD(d.getY())), Z(DEGTORAD(d.getZ())) {} Nothing too rocket-science here. These are three float classes that all they do is hold Euler angles. The problem is I don't know where in the code to use which. There was one place, in one file, where I knew what was what because there was code like the above to translate degrees to radians. But elsewhere in the code there's no such luck. There's often not even a comment to say if it's degrees or radians. Furthermore, in some places in the code, the representation is Vector3D (floats), while in other places it is FixedVector3D, but both are called "rotation". In file NUSpline.h there's a struct declaration with a rotation. If I make it a FixedVec, like, struct SplineData ///// this struct needs HEAVY refactoring... { // Should be fixed, because used in the simulation CFixedPoint3D<WORLD> Position; CVector3D<VELO> Velocity; // TODO: make rotation as other spline CFixedVector3D<UNKN> Rotation; //could be radians, degrees... why FixedVector? <<<<<<<<<<<<<<<< // Time interval to the previous node, should be 0 for the first node fixed Distance; }; I get an error in file CCmpCinemaManager.cpp, "cannot convert ‘CFixedVector3D<____NO____FOR>’ to ‘const CEulerDegrees&’"... [331] pathSpline.AddNode(node.Position, node.Rotation, node.Distance); And if I make it a float solution, namely, struct SplineData ///// this struct needs HEAVY refactoring... { // Should be fixed, because used in the simulation CFixedPoint3D<WORLD> Position; CVector3D<VELO> Velocity; // TODO: make rotation as other spline CEulerDegrees Rotation; //could be radians, degrees... why FixedVector? <<<<<<<<<<<<<<<<<<<<<<< // Time interval to the previous node, should be 0 for the first node fixed Distance; }; Then I get an error also in file CCmpCinemaManager.cpp, "cannot convert ‘float’ to ‘fixed’", but a different line: [282] serializer.NumberFixed_Unbounded("RotationX", nodes.Rotation.getX()); What makes navigating this part of the code particularly difficult is a lot of the functions are pure virtual; clicking on go to implementation does nothing. Too many hours resolving conflicts is having a toll on me...
  21. I came around looking for where to update the patch I submitted a few days ago, but could not find it for the life of me. I'm still working on this, though just for myself, (I may end up forking this), and I've finished a major milestone; namely, all the Vector2D, Vector3D, FixedVector2D and FixedVector3D classes are now fairly cleaned up. Well, there are still a few ugly things... The cacheing of sqrt in FixedVector3D remains, and works beautifully. The cacheing of sqrt in FixedVector2D is still there, but I'm not sure how much use it gets; I might remove it. In Vector3D I tried to do a similar cacheing, but the class is used structurally in many other classes, which changes their size, and in some cases it breaks their interfacing to other libraries that expect a fixed format. The use of alignas() has proven extremely detrimental, precisely in that it breaks structures that include the aligned structures. Everything I tried to do with alignas() I had to eventually undo. It is a directive from Hell. I also at least partially cleaned up the color classes. CColor I renamed it to RGBAcolor. I found cases where Vector4D was being used to carry color information, and I jumped to change interfaces to use RGBAcolor instead; but the use of Vector4D was going too deep, into shaders and uniforms and whatnot, and I got cold feet and reversed course. But so what I ended up doing instead is adding a conversion operator in RGBAcolor to Vector4D, which simplified a lot of the code elsewhere. Also, many places in the code had specially coded conversions from float to char color formats; I put getR8(), getG8(), getB8() and getA8() functions in both RGBcolor and RGBAcolor classes and managed to simplify a lot of code elsewhere. Getting back to vectors, not only am I cacheing the Length() results, but I also sped up the isqrt64() routine by using a double sqrt(), single instruction. It was tricky to get it to past the tests, but it works. It was just a matter of trimming overflows. The end result is that the gameplay feels smoother, but of course this is a subjective thing, and I have no idea how to use Profile2. I've no illusion anyone here is going to either look at my work or even try it for a minute, but just in case, I attach the diff, which is current (the last svn up I did was a 2 or 3 hours ago). What I said in my first post about making points and vectors separate classes, I've done the work of defining CFIxedPoint2D, CFIxedPoint3D, CPoint2D and CPoint3D. They are in the same files as where the vectors are declared, just below them. But I have not used them yet... That's what I'm about to do next. So, the diff is attached. Cheers! diff.txt
  22. Hi, I want to introduce this patch properly, first of all, and it's hard to do in IRC. I think it was Stan who mentioned to me in another forum thread, that one of the things needing fixing in the engine, performance-wise, is the fixed point (64-bit) square root, which is found int Sqrt.cpp, named isqrt64(). This is a hand-coded routine, which I saw no immediate way of improving. But what I thought of is that I could somehow reduce calls to it by cacheing results. 99% if not 100% of the use of this routine is courtesy of FixedVector2/3D objects' Length() fn, to obtain integer sqare root of ( x*x + y*y [+z*z] ). Having done my share of programming and debugging in the past, I'm familiar with the fact that gazillions of calls to functions in most programs are kind of redundant, with the same variable read gazillions of times, or variables being "updated" a million times to the same value they were holding already. There's an entire movement out there of people who believe in general cacheing of all results from all functions; they want languages to begin adopting their philosophy... I'm not subscribed to it, but it merits a thought or two. In the case of FixedVectors, I wildly speculate that some of them have their length() function called many times, while other vectors might never hear a single call. (Case in point, point2d is implemented using FixedVector2D under the hood; but points don't have much of a length, do they?). Speaking of points and vectors, my next patch I'm thinking of is having non-class binary functions for point and vector math such that, A point minus a point = a vector The length of a vector is a length The length of a point is a compiler error A point plus or minus a vector = a point A vector plus a point = a point A vector minus a point is a compiler error A point plus a point results in a compiler error (or in a point-group ;-) Negative a vector is a (flipped) vector Negative a point is a compiler error It should prevent coding errors in the future; --might it dig up some from the past? I implemented all of this for my own engine 20 years ago. But getting back to this patch, my educated guess is that many vectors' lengths will never be looked at, while some may be looked at ad-nauseum. So I decided to experimentally try to (only when needed) compute the length of a vector, if not already cached; otherwise return the cached value when computing the length, cache the result, in case it is needed again before the vector is modified whenever the vector is modified, invalidate the cached length (but don't compute it again unless or until it is needed again) Thus, I added a member to the class, L, also of type "fixed". It holds the length of the vector when it does; otherwise it reads zero, meaning that the length is not current. Now, you might object to this saying "using a specific value to mean something is bad programming!", or "Isn't a zero length a valid length?!". Yes, all of that is true, however, vectors of zero length are hopefully not numerous, and if we were to assume the worst, namely ALL our vectors are zero length, the penalty, in this code, is to have as many calls to isqrt64() as we do at present; certainly not capital punishment; and actually I test X Y and Z for being zero before calling the length computation. That is, if I need the length, and see that the cached length is zero, I do look at X, Y and Z, and if all are zero I do nothing. Secondly, having an extra bool to indicate currency of the length variable is a bit performance-wasteful in the case of a flyweight class like this. In a big cyberpunk boilerplate class, I'd consider it, of course. Note that this is effectively a cacheing, or "proxy", and as such it suffers from a minor design gottcha: The function Length() looks like a pure function, and should be const as far as the outside world is concerned; but, under the hood, the update of the cached length often happens inside this const function. The right and only way to solve this issue, as is true of proxies in general, is to make L mutable. This is the true reason "mutable" exists; so let's use it. The new code, compiles, links, passes the 351 tests, and plays nice and smooth like a charm, --at least at the Very Easy setting I used to have momentary freezes of half a second every 5 minutes or so; now I don't notice them anymore; but I don't know if that's due to other changes happening on svn. To find out if peformance is improved, I need to profile. I followed the instructions in the Wiki for Profiler2 and tried it. It looks pretty. Now I will need to know how to count calls to isqrt64() and show them on the screen, or how to compare performance overall using a prerecorded game. However, either way, the testing needs to play back a game by user input commands; NOT by results; as it needs to test pathfinding and AI, which are probably stripped back when a game is being re-played like a movie. I will need help with all this. For now, if anyone's interested in trying it, looking at it, and/or profiling it, here's my latest svn diff: https://pastebin.com/nZt94JjJ (File also attached below.) Note that it seems nothing I've done is conflicting with any changes on svn; it looks like there's nothing to resolve yet. In the course of making these changes, I fixed a couple of other things about these classes: Dot() and CrossProduct() functions in FixedVector3D were not const X, Y (and Z) members were public, in both classes... Privatized them, then dded getX(), getY()... const...; and non-const Xref(), Yref(), etc. for external manipulators. This latter change caused changes in many other files, where the members were being read or written directly. That's why the diff is so big. Note that some people believe that making members of a class public should make the compiled code run faster than having access functions. That's the exact opposite of the truth: The elephant in the room nobody talks about is that C++ code is generally far faster than any hand-coded C equivalent, and there's good reasons for this (which Soustrup eloquently explained in a recent interview): The code optimizers of today have grown so smart that most assembler coders have a hard time competing with them; but optimizations thrive in an atmosphere friendly to assumptions; and some of the most useful assumptions in compiler optimization are about what can and cannot change where and when. C++ tries to limit access to the minimum necessary, limit scopes to the minimum necessary, hide whatever can be hidden... In such an environment, crazy optimizations become possible. But if you subversively defeat the best gifts C++ has to offer, such as by exposing class members, you kill many optimization possibilities at the same time. These classes should already help optimized code run faster on the basis of having closed off public access to its members alone; never mind the isqrt64() issue. Plus, I fixed alignment issues with padding. Compilers don't touch alignment with a ten-foot pole; this is coder-responsibility. (At least it was so 20 and 15 years ago; I may be behind the curve; if so, my apologies in advance.) EDIT: Speaking of elephants, 0ad elephants have gone on strike. I can't get them to do any building or repair work for me, anymore. In fact, even the hammer icon is gone. I don't believe it has anything to do with my changes... Ether they joined a union, or were accidentally given wooly mammoth AI, or both... diff.txt
  23. Yeah, many thanks. Last night I hit a snag, though. These errors in ScriptTypes.h are triggered: #if MOZJS_MAJOR_VERSION != 78 #error Your compiler is trying to use an incorrect major version of the \ SpiderMonkey library. The only version that works is the one in the \ libraries/spidermonkey/ directory, and it will not work with a typical \ system-installed version. Make sure you have got all the right files and \ include paths. #endif #if MOZJS_MINOR_VERSION != 6 #error Your compiler is trying to use an untested minor version of the \ SpiderMonkey library. If you are a package maintainer, please make sure \ to check very carefully that this version does not change the behaviour \ of the code executed by SpiderMonkey. Different parts of the game (e.g. \ the multiplayer mode) rely on deterministic behaviour of the JavaScript \ engine. A simple way for testing this would be playing a network game \ with one player using the old version and one player using the new \ version. Another way for testing is running replays and comparing the \ final hash (check trac.wildfiregames.com/wiki/Debugging#Replaymode). \ For more information check this link: trac.wildfiregames.com/wiki/Debugging#Outofsync #endif No idea what to do. EDIT: never mind; problem solved in IRC
  24. You must be psychic, Loki; that is EXACTLY the one I'm trying to use. I installed it 2 or 3 months ago but never used it. Now, I managed to edit the Tools menu to svn up, update-workspaces, make, test and run. Everything works. The problem is I don't know how to add the code files to the project; can't find a menu to do it; not in File, not in Project. I tried importing the Visual Studio workspace, and nothing happened. I'm totally confused... EDIT: Which "premake" are you referring to? I found a folder "premake", and inside there's a premake4.lua and premake5.lua; no idea what that is...
×
×
  • Create New...