Eulora's client uses the CrystalSpace (CS) engine for all its graphics needs and CS in turn uses the Cal3D library for animated objects in the world1. Some two years ago2 I wrote this stop-gap glue-and-string script to supposedly help artists and all those eager to contribute but held back by a lack of direct way to experiment and see their "art" coming alive in CS. Well, I wrote it and until the beginning of this month, those scripts plus some basic notions from introductory computer graphics courses taken way back during my uni years were about as much as I could say I actually knew regarding computer graphics.
As it seems quite conceivable by now that I'll end up having to get all the way to the bottom of the graphics pit too, I spent most of this month reading the graphics reference book3 and in parallel trying to make sense in a very practical way of the whole way in which Eulora's client handles all the graphics part from the Planeshift (PS) top layer down to CS and CAL3D. And while I don't yet have a fully working new pipeline for actually loading on the fly all and any sorts of graphics into Eulora's client, I have at least a hard-won better understanding of what goes on in there (despite the layers upon layers of obfuscation) as well as some bits and pieces that might conceivably even make it into such a pipeline in the end. So I'll jot down here my findings so far and those yet-moving ideas about it all, as it helps for sure to structure and unload a bit in preparation for the next stage of work. And it might even help - says ever optimistic me - to start perhaps a discussion with anyone who knows this better than I do so if that's you, kindly comment below and let me know your thoughts.
One difficulty with CS (and it seems to me with Computer Graphics in general really) is this persistent idea of accomodating all sorts of "alternative" ways of doing things that turn out in fact to be precisely the same thing only packaged differently so that nobody is left behind or something. So instead of having a clear structure of knowledge to acquire, one is supposedly left to be creative and find what "feels natural" or "comfortable" to them. Well, so much for actually learning anything there, welcome to lots of code that does the same thing, only it's harder to tell that upfront without sinking hours in it, to follow it all from one end to the other. Nevertheless, after all the reading and poking it and changing it and having my way with it, my current basic structure of CS4 notions of interest for client graphics is this (ordered from top down, as much as I could):
Sectors - those are the highest-level containers of "graphics" of any sort. A sector simply stands for a "location" that is effectively defined by nothing more than a name5 and two mixed characteristics: on one hand the visibility culler in use (so a computational matter) and on the other hand the ambient light (i.e. the light that affects everything in that sector in the same way at any position and as such a matter of aspect pure and simple).
CS supports two visibility cullers, namely Frustum and Dynamic (dynavis) with the default for the game's needs being the dynavis culler. So a first building block for the desired client pipeline is simply a "GetSector" function. Like all the rest of the "GetX" functions that I have in mind, this will retrieve any information it needs about the sector from EuCore / EuCache (via a CPP-to-Ada layer, as needed) and then either retrieves the required sector from the engine itself if it exists already or otherwise proceeds to creating it and loading it into the engine so that at the end of it, one way or another, the sector is known to exist. When information is missing/incomplete/unavailable, the GetSector method fails and lets the caller decide what to do (try again at later time, most likely). At this moment I have a prototype of this method as well as some similar prototypes for GetLight, GetTexture and GetMaterial - in other words, I can create from code without any xml and parametrised four main things: sectors, lights, textures and materials.
- Lights - those are sources of light in a sector beyond and in addition to any ambient light. They can be customised in all sorts of ways (e.g. intensity, colour, position, direction). Whether they are elements inside a sector or an environmental characteristic of a sector is unclear to me at this stage since they could easily be seen either way. From an implementation point of view however they are currently clearly elements inside a sector, NOT characteristics. This means among other things that one could in principle add/estinguish/modify lights as they please at any given time and I'd say this is how it should be, given that it's not inconceivable to be able to turn on a candle or any other light in one's own house for instance.
- Weather - the more general name for this would be environmental factors I suppose, but so far it's really at most "weather" and in practice mainly... fog. Supposedly one could add here all sorts of *effects* really so from an implementation point of view I'd even call this part "environmental effects" i.e. whatever effects manifest themselves in the sector as a whole and are specific to it. The fog itself so far appears to be a rather simple matter as it's essentially a limited modifier of ambiental light. I don't really know if this is just a limitation of CS or a more general approach and for that matter I can't for the life of me quite grasp yet exactly how do graphics-people decide whether some effect they want is rolled into "fog" or into "ambiental light" or into anything in there. This is an issue at all levels and so far it really looks like an individual preference than a choice, quite everywhere. In other words a matter of "am I more skilled in making it look just so by tinkering with this or by tinkering with that"?
Meshes - those are any and all elements in a sector that have what CS calls "geometry". This geometry concept is a bit fuzzy at times but as far as I managed to pin it down, it would seem to mean essentially that the object has a shape defined mainly as a set of "vertices" aka points in the 3D space that the object occupies. There are 3 main types of Meshes:
- Movement-capable entities
- Fixed entities
Each of the above types of meshes has its own specific requirements and even ways of defining itself, including its geometry (hence part of the fuzziness...). At least in the current setup and as a very brief overview, movement-capable entities are effectively CAL3D models defined as hierarchical structures of meshes with attached action representations (e.g. run/move/jump/fight animations) and skeleton-based geometry; fixed entities are CS's "generic mesh" with vertex and triangle-based geometry; terrain entities are CS's "terrain2" mesh type with attached terraformer for generating the geometry based on a heightmap. The details of each are much gnarlier than this list might suggest and I'll be teasing them out one by one as there is no way around that but at any rate, CS's approach seems to have been to make different xmls for each of them and all sorts of "loaders" on top of loaders and on top of plugins, services, parsers and whatnots. Then PS added its own loader on top of CS's pile of loaders. As you might imagine, this tends to rather obscure the precise way in which one can create those different meshes *without* xml.
- Factories - those are abstract concepts containing effectively the blueprint for creating identical meshes. The idea behind them - as far as I can tell - is that it's worth loading in memory what is needed for a mesh only once and then reusing it every time a new specific instance of that precise mesh is required. In practice I'm not sure that this is really worth much for anything other than snowflakes or raindrops really, since otherwise pretty much each instance of a mesh seems to have its own factory anyway. There is however the argument that the factory should contain only truly common structure (i.e. all players of one race have the same structure) and then leave the customisations to each instance. So keeping this in mind, here they are in the list, factories for meshes (of all sorts, though indeed, each of the three different mesh types has its own specific factory type on top of a generic type).
- Materials - as far as I can tell so far, a "material" is essentially any specific combination of one shader + a whole set of values for the shader's parameters (aka "shader variables" in CS terms). Note that the texture itself (or themselves as apparenlty one can use several although it's not clear to me WHY is that any better), if there is one, is effectively an argument passed on to the shader.
- Textures - those are the pretty picture that is lovingly painted on some/any surface that an object in the world may show to the player at some time or another. Essentially each object is but a set of surfaces linked together and each of those surfaces may be covered with one or another texture fixed with some spit so that it goes exactly over that bump or starting from that point to end up just-so. In principle, one could provide instead the way to calculate the texture at any point but that seems to be less frequently used in practice (CS claims to support it though, although I can't tell to what extent and with what results really).
- Shaders - this is where the fuziness increases to madness levels. On one hand, "shader" seems to really mean all sorts of things in different places, from yet another description of a surface to any code running on the GPU. There is obviously more reading that I need to do on this aspect but so far what I've got is that CS's "shaders" are really just another layer of encapsulation meant to be for types of surfaces. So if one has types of meshes encapsulated in factories, one also has types of surfaces encapsulated in shaders and is able therefore to apply some "surface type" to any given texture so that the same picture will look "cloth-like" or "stone-like" depending on the nature of the surface on which it is applied. This is about as clear as I can get it now and I can't say I like it much. On top of this, shaders in CS are yet another pile of layers and layers of xml with intricate dependencies on one another and on who-knows-what-else in the name of reusability6.
- Render Loops - those are (in usual graphics style as far as I can tell) simultaneously in parallel and on top of the "shaders" above. They are yet another encapsulation, this time of types of rendering + lighting objects. To make the difference between shaders and render loop abundantly clear, default render loops are of course provided by CS, as files in the /data/shaders directory. Admitedly I totally lack the expertise to say at this stage just why are BOTH shaders and renderloops needed as separate entities and/or why not either do with only one of them or otherwise merge them together in a single thing at least but hopefully one of my more knowledgeable readers clarifies this for me. Failing such clarification, I'll need to keep on reading and experimenting with it, of course, for as long as it takes, what else.
In addition to the list above, there would be also the easier part of interface "skin" aka the variious icons and decorations for windows, buttons, labels and whatnots. This part is also on the list to be made fully reliable on data received from EuCache but so far I haven't touched it yet (other than keeping an eye out for what I could do about it, whenever other bits and pieces make me go close to that part of the code that has anything to do with it, of course).
For a fresh image, the TestChar at least moved out of its cubic box and into a rather faceted sphere, in his quest for the promised infinite landscape. Lights got a bit better, shader variables were sorted so as to neither block nor directly crash CS anymore. Next on the list is fully figuring out just how to properly feed "geometry" to CS as what it is, namely several sets of numbers (vertices, texels, normals, triangles) rather than all those + a ton of xml on top. This is a required step in order to have the GetFactory prototype too for static meshes at least. Once that is done, the next step is to move on to factories for terrain and then for animated meshes. Until then though, here's Test-in-his-sphere:
For completeness, I should mention that CS has also its own native support for animated objects via 2 different sort of objects even but neither of those is as flexible and overall useful as CAL3D is. Therefore, I simply consider that animated object = CAL3D from now on. ↩
Already two years passed! And to think that I'm picking it up precisely from where I left it all this time ago, kind of boggles the mind. ↩
Foley, J.D., van Dam, A., Feiner, S.K. and Hughes, J.F., "Computer Graphics: Principles and Practice", 2nd ed., Addison-Wesley. Yes, I'm aware that there is a newer edition of this book, supposedly improved, better, shinier, more fashionable whatever have-you. I'm also aware that it's full of what can only be called advertising for Microsoft while being way lighter on what I'm interested in, namely precisely principles of computer graphics. So I'll stick with this edition, thank you. ↩
Note that there is no desire to make all Eulora somehow CS-dependent as such but at this stage, I have to at the very least support CS as it were so I'm aiming for using CS as a practical example of handling client graphics, not as a driver of standards or content. ↩
CS uses names aka text/strings as identifiers to the extent that it *requires* those for ~all the Find functions at all levels. It *does* have its own internal IDs and it even exposes them if one tries really, really hard to get to them but they are basically not very useful otherwise and moreover they can't even be set to match an external set of IDs (i.e. one can't create an item with a specified ID.) ↩
"Reusability" seems to mean that the original author can skip writing a few lines of code at the cost of nobody else being able to understand afterwards at all how the thing works unless they spend at least double the time that it would take them to write it from scratch anyway. ↩
Comments feed: RSS 2.0