Archive for the ‘Eulora’ Category

Pretty Petty Particles (Notes on Graphics in Eulora, VI)

Friday, September 27th, 2019

As the latest client data hierarchy draft revealed a gaping hole regarding the exact use and requirements of "effects", I've spent those past days diving into the tediousness of "particle systems" - those are the core of CS effects since they are used to simulate rain, snow, explosions, fires, fountains etc. On the bright side, particle systems are actually explained in a bit more detail than other things in the CS manual, mainly because they have been apparently changed quite significantly so the manual's author felt the need to provide examples for using the new code. On the darker side, they are stuffed into the clunky system of various plugins + xml + factories + meshes + shaders + 1001 attributes to set for the smallest thing, so that 3 drops of rain get you sweating and swearing through the usual multiple layers of all-possible-things in slightly-different-ways.

To start with, trying to find a proper place where those particle systems belong in a sane hierarchy of CS items is rather dubious because particle systems are a combination of almost everything in there plus "emitters" and "effectors". And ~everything has to do the dance of factory+instance although each time pretty much everything gets re-set anyway1 so it all feels like an exercise in bureaucratic-coding, to make one's stomach heave anew. Anyway, the core components of particle systems are those:

  1. A non-moving mesh that is the "particle". Like all other non-moving meshes, this can naturally have one or several materials/textures and shaders (with the load of shader variables) but unlike all other non-moving meshes, the geometry is not something one sets explicitly via vertices and triangles and all that - instead, the geometry is fixed and set by the provided "plugin"2 for particle systems! Setting various parameters allows one to tweak this geometry to some extent but that's about it. According to the manual, the existing plugin simply makes all particles rectangles - you can set however their size and their orientation with respect to the camera.
  2. Any number of emitters. As the name suggests, the emitters create and launch about new particles according to another set of parameters. For the same particle system, one can in principle add any number of emitters but there has to be at least one emitter for anything to happen, of course. There are a few types of emitters that differ mainly in the shape that is covered by the launched particles: sphere, box, cylinder, cone.
  3. Any number of effectors. From my tests, those are not even mandatory. They provide some ways to influence the existing particles (usually their movement but supposedly any aspect of them; the manual says that effectors "put different kinds of effects" on particles, "be it physical or non-physical effects"). There are only three available types of effectors: force, lincolor (interpolates the colour of each particle between two values based on the remaining time to live) and forcefield.

Once a particle system is created, the way it works is relatively straightforward: the engine calls on every frame the update loop of each particle system that is visible to the user; the update loop deletes old particles, polls all emitters to emit new particles, polls all effectors to apply their effects and then performs the final transformations if/when such are required. Basically a particle system is designed as a sort of predefined show happening at some place in the world for some specified interval of time with rectangle-shaped actors only (but possibly various costumes via materials).

For practical tests, I've created from code several types of particle systems including "rain", "fountain" and "fire". While the general mechanics of it all seem to work fine and the parameters seem to do indeed what is expected, there is still some weird issue with the materials - all the stuff looks brown-ish, no matter what texture or material is loaded. Since otherwise a material is just a material and it's clearly loading fine for anything else, I quite suspect that the trouble here is the shader: although in principle any shader could be used, there is a specific particle_shader that is used but not really documented anywhere (ie there is the xml of course but that has some variables and possibly references and it is *one* area that I rather avoided to fully get into, so far). Added on top of all previous experience, I'd rather say that sooner or later I will still have to dig shaders too, what can I do. Anyways, at the moment I am mostly interested in having the mechanics in place and figuring out the parameters, so in this sense it's a success, here's some snow and rain falling down on naked girl, at times with erupting particles too:

Euloran Snow - it's more brown than white!

Euloran Snow - it's more brown than white!

Undisturbed euloran woman among erupting stuff and pouring rain.

Undisturbed euloran woman among erupting stuff and pouring rain.

Let it rain with brownish nothings.

Let it rain with brownish nothings.

Given the above, I'll mark the "particle systems (Air)" as explored for now and move on to procedural textures (Water) to see what further catralliard of parameters I find there too. The test of movement for effects (ie following a char or something) is still on the list too but I see it as more general really - since the whole particle system is simply a mesh, having it follow another mesh should be just a matter of giving its position as relative to that rather than absolute. At the moment I'm not sure where exactly would be the best place to do this but I think it requires a bit more consideration because it's possibly a more generally useful thing to have - some items/characters might simply follow others at some point/for some time, anyway.

  1. It would even be hard to keep track of what is same and what is different given how many darned setThis and setThat one calls. 

  2. The plugin's code can be found in CS/plugins/mesh/particles/object/. 

Eulora Client Data Hierarchy v2.0

Saturday, September 14th, 2019

This replaces the previous draft and is based on the new knowledge of client entrails of all sorts as acquired during those past 2 months. It is however still a draft in that it uncovers some new questions (at least new to me) and it requires anyway some feedback and discussion given its scope and crucial position in the SMG Communication Protocol. It's published as it is at this stage and by the promised deadline not because it's "complete and final" but mainly because I need to have this version exposed for further discussion in order to be able to advance it further.

While I was already trying in the previous draft to figure out more clearly the scope and nature of the desired hierarchy, this became clearer only after getting a better understanding of the various data the client uses and especially after further discussion in the SMG boardroom of the protocol specification. The previous observation that there are 3 types of information in there (structure, description, representation) is as correct now as it was then. But meanwhile, as I got to understand better all the different domains involved, the high-level view evolved to this:

  1. Game World
    1. Concrete objects
      1. Terrain
      2. Water
      3. Air
      4. Characters
      5. Structures/Items1.
    2. Abstract objects
      1. Time
      2. Self
      3. Sector
  2. Representation
    1. Graphical
      1. 3D
      2. 2D
      3. Skin/GUI
    2. Audio

Before going further and into the hairier details, it's worth stating the important distinction that the protocol specification makes between the two types of "resources" that the client can ask for:

  1. Files - relevant protocol messages 4.4 File Request and 4.3 File Transfer.
  2. Objects - relevant protocol messages 4.7 Object Request and 4.8 Object Info (ie "Transfer")

The Files are - from the protocol's point of view - "big" resources that never contain other resources. Files are referenced by their full name that works therefore exactly like an id (although a text-id rather than a numerical id).

The "objects" are - from the protocol's point of view - "small" resources of a potentially composite sort. This means specifically two things: any object will always fit into one single message; an object may contain references to any number of other resources (ie references to other objects and/or to any number of files). An object is given (via a 4.8 message) as a list of tuples (id:type). Each such tuple is the reference (the "id" part) to one "property" of the object, where the term property is quite generic: any object can be at some time or another a "property" of another object. Note also that "id" is not necessarily a numeric id - a filename (with full path included, where relevant) is effectively the "id" of the actual file.

The hierarchy starts with the single object that has no id (otherwise put it has the "null" value as id). Since all things have to start from somewhere, so does Eulora's language of knowledge - in the beginning, it is always "null". If you think of the hierarchy correctly as a tree, this object with "null" as id is the root from which there can be ultimately any number of levels - the hierarchy is *not* restricted on either direction (ie any node can have in principle any number of children and any path may be arbitrarely long). The leaf nodes will *always* have "Final" as given type in the tuple (id:type). The id in the tuple (id:type) of a leaf node is either a filename (ie the id of a file, one can think of this as value given by reference) or otherwise a value that is used directly as it is, as data (e.g. helmet.png:Final or 5:Final). All values given at a leaf node will be one of the following basic types:

The root of all the world (the object with "null" as id) is the object that the server sends (via a 4.6 World Bulletin message) in response to an Object Request (4.7) that contains an unknown/non-existent object id. The hierarchy as I managed to flesh it out so far is given as id:type with comments after > :

  1. Root > there is only ONE object of this type and that object has id "null"

    1. Top_Location_ID : Sector > current sector; player's character is somewhere in the subtree of this object (eg may be in a room that is in a house in this sector, perhaps).
    2. Self_Info_ID : Self > id of the abstract object, not the id of the player's concrete character object.
    3. Current_Date_ID : Date > euloran date
    4. Current_Time_ID : Time > euloran time
    5. Client_Skin_ID : ModSkin > graphical user interface resources
  2. Sector

    1. CurrentNameID : Name

      • Value (text): Final
    2. Terrain_ID : Terrain > the heightmap (+ material palette)
    3. Air_ID : Air > ambiental light and weather (?)
    4. (Light_ID : Light)* > NB: zero or more; those are purely sources of light (NOT ambiental light which is a property of Air), without physical representation other than the light itself.
    5. (Water_ID : Water)* > bodies of water
    6. (Character_ID : Character)* > anything that is active/can move;
    7. (Structure_ID : Structure/Item)* > anything that is passive/can't move and is not terrain/air/water/weather
  3. Self > this includes all the stuff that is specific to self (ie known only about own Character) as opposed to the generic info given via Character type (ie info available for all characters encountered, whether self or not)

    1. CurrentPlayer_ID : Character > player's actual character with data that everyone can access/see
    2. Current_Loc_ID : LocID > should the location ID be here? (otherwise in principle client can fully search the tree until it finds CurrentPlayerID as contained in some location)

      • Value (uint32): Final
    3. Date_Of_Birth_ID : DOB

      • Value (date) : Final
    4. Vitals_Profile_ID : VitalsProfile
    5. Skills_Profile_ID : SkillsProfile
    6. Inventory_ID : Inventory
    7. Equipment_ID : Equipment
  4. Inventory

    1. (Item_ID : Structure/Item)*
  5. Equipment > How is equipment different really from Inventory? I can't see any real reason to make it a separate type. "Equip" is an action and as such it will fail if the item doesn't "fit" the destination, be it because it's not equippable or whatever. Similarly, the "how it looks" is a matter of checking what is in some slots if one so wants. The only thing: the meaning of "positions" in inventory should be defined, at least for those that have a specific meaning beyond "in the bag".

    1. (ItemID : Structure/Item)*
  6. Date > this could be in principle simply unixtime number but that wouldn't match well/easily with custom year/month sizes (?) + presumably custom month names rather than numbers (?)

    1. Year_ID : Year
      • Value (text) : Final > should those be numbers?
    2. Month_ID : Month
      • Value (text) : Final > should those be numbers?
    3. Day_Of_Week_ID : DoW
      • Value (text) : Final > should those be numbers?
  7. Time

    1. Hour_ID : Hour
      • Value (uint8) : Final
    2. Minute_ID : Minute
      • Value (uint8) : Final
    3. Second_ID : Second
      • Value (uint8) : Final
  8. ModSkin > should this cover *also* layouts & windows & UI components or only strictly "art" resources? If it's the later, in principle it's simply a list of images (with at most width, height, alpha, transparency)??

    1. (UI_Art_ID : UI_Image | UI_Spritesheet) *
  9. UI_Image

    1. (Name_ID : Name)? > CS uses everywhere names for IDs and sprite sheets won't be fine with filename as name for each sprite; onth perhaps client should just generate its own "names" out of object id and/or filename if it needs them?

      • Value (text) : Final
    2. Size_2D_ID : Size

      • Value (size2d) : Final
    3. Transp_ID : Transparency

      • Value (rgb) : Final
    4. Alpha_ID : Alpha_Lvl

      • Value (float) : Final
    5. File_ID : Image > should all be .dds or .png or fixed format?

      • Ref (text) : Final
  10. UI_Spritesheet

    1. UI_Image_ID : UI_Image > for the sheet as a whole, it still has all the details
    2. (UI_Sprite)+
  11. UI_Sprite

    1. (Pos_ID : Position)? > for sprite sheets - position in parent image

      • Value (position) : Final
    2. (Name_ID : Name)? > CS uses everywhere names for IDs and sprite sheets won't be fine with filename as name for each sprite; onth perhaps client should just generate its own "names" out of object id and/or filename/whatever if it needs them?

      • Value (text) : Final
  12. VitalsProfile

    1. (VitalID : Vital)*
  13. Vital

    1. Name_ID : Name
      • Value (text) : Final
    2. Current_Value_ID : CurrentValue
      • Value (uint8) : Final > assuming that those are still given as % of max
  14. Skills_Profile

    1. (Skill_Group_ID : SkillGroup)*
  15. Skill_Group

    1. NameID : Name
      • Value (text): Final
    2. (Skill_ID : Skill)*
  16. Skill

    1. Name_ID : Name
      • Value (text): Final
    2. Rank_ID : Rank
      • Value (uint64): Final
    3. Practice_ID : Practice
      • Value (uint8) : Final

      > assuming given as %

    4. Knowledge_ID : Knowledge
      • Value (uint8) : Final

      > assuming given as %

  17. Character

    1. Name_ID : Name
      • Value (text): Final
    2. Sex_ID : Sex > is this given and in what form? is it known for anyone or just for self?
      • Value (uint8): Final
    3. Position_ID : Pos
      • Value (position): Final
    4. Rotation_ID : Rot
      • Value (rotation): Final
    5. Model_3D_ID : ModCal3D
    6. (Model_2D_ID : Mod2D)? > 0 or 1
    7. (Model_Audio_ID : ModAudio)? > 0 or 1
    8. (Model_Effects_ID : ModEffects)? > 0 or 1
  18. Structure/Item > the difference between structures and items is minimal to the point of inexistent (eg "structures" can't be picked up perhaps, but that's not a property - simply a response to an attempted action and as such of no interest/relevance here!; or structures will have stack count = 1 but that doesn't mean that they don't have stack count...)

    1. Name_ID : Name
      • Value (text): Final
    2. Stack_Count_ID : StackCount > this will just be 1 for houses / some cases.
      • Value (uint64): Final
    3. Quality_ID : Quality > this may be called "durability" for eg tools or houses but it's same thing anyway.
      • Value (uint64): Final
    4. Position_ID : Pos
      • Value (position): Final
    5. (Model_3D_ID : ModCal3D)? > ONE of the models 3d/2d should be present in general
    6. (Model_2D_ID : Mod2D)? > 0 or 1
    7. (Model_Audio_ID : ModAudio)? > 0 or 1
    8. (Model_Effects_ID : ModEffects)? > 0 or 1
  19. Terrain

    1. NameID : Name
      • Value (text): Final
    2. Position_ID : Pos
      • Value (position): Final
    3. Height_Map_ID : Heightmap > NB: while both materialmap and heightmap are images (and even of same type + same size, listing them as "IMAGE" type means that the role (i.e. which one is Heightmap and which one is MaterialMap should be defined somewhere else - would that be better/how?)
      • Ref (text): Final

      > filename, hence "Ref", currently "Island.png"

    4. Material_Map_ID : Materialmap
      • Ref (text): Final

      > filename, hence "Ref", currently "Island_alpha.png"

    5. (Material_ID : Material)* > material palette to use (eg sand, grass, stone)
  20. Air

    1. AmbientLightID : AmbientLight > all lights should be "dynamic" or at any rate the static/dynamic distinction is entirely client-side concern ; currently those values are given at different "times of day" + interpolated by client in between, but I don't see the *need* for hours in here; client can decide on its own if /when it wants to sample this and what/whether to interpolate or do anything else with the values it gets.
      • Value (rgb) : Final
    2. Weather_ID : Weather
  21. Water > is this needed as separate type? CS has 4 types of procedural textures: fire, water, dots, plasma
    > therefore "water" could simply be in principle an item/structure with the "water" texture applied - TO CHECK.

    1. Item_ID : Structure/Item
  22. Weather > CS uses "particle systems" for this sort of thing (i.e. actual snow/rain)
    > as such, those could be just part of the "sector" at some point + corresponding changes to light & sky, if any. TO CHECK
    > however, there can be perhaps the point of having this separate to set specific params such as emission rate, velocity etc. ?

    1. Particle_Syst_ID: Particle_System > if going this route, this is TBF
  23. ModCal3D > aka moving/character stuff

    1. BB_Size_ID : BoundingBox > this is used for collision detection

      • Value (size3d) : Final
    2. Skeleton_ID : Skeleton > this is MANDATORY; all moving stuff have to have a skeleton

      • Ref (text) : Final

      > this is a .csf file for now; should the format be a further level/property?

    3. (Animation_ID : ModAnim)* > a character may have any number of animations
    4. (Submesh_ID : ModMesh)* > parts/subparts of the model; NB: atm sockets is still not used/unclear!
    5. Scale_ID : Scale > intended scale of the model, this is a transformation to apply before use

      • Value (float) : Final
    6. Translation_ID : Trans > pre-transform to apply, translation; same as for scale - could be standard and therefore spec not needed?

      • Value (3xfloat): Final
    7. Rotation_ID : Trans > pre-transform, rotation; same std issue as above.

      • Value (4xfloat): Final > (x,y,z,angle)
    8. RotX_ID : RotX > IF the model should be pre-rotated along the x-axis; again, should be std and be done with. Default is true

      • Value (boolean): Final
    9. FlipTex_ID : FlipTex > whether v coordinates of texture should be flipped (i.e. whether 0,0 in texture space should be top left or bottom left); default is false

      • Value (boolean): Final
  24. ModAnim

    1. Name_ID : Name > name is used as an id by CS... eg "walk", "run", "jump"

      • Value (text) : Final
    2. File_ID : File > this is a .caf file for now; should format be a deeper level?

      • Ref (text) : Final
    3. Type_ID : TypeAnim

      • Value (uint8) : Final > idle, travel, cycle, action, none, style_cycle currently from CS&PS - WTF?? do we even want such a thing?
    4. Vel_ID : Velocity
    5. Time_ID : Interval > this is "interval for idle or override actions", in seconds

      • Value (2xfloat): Final
    6. Idle%_ID : Idle% > this is "probability (as %) of this action being the overriding action"

      • Value (float) : Final
  25. Velocity > this is for animations

    1. Base_ID : Base_Vel > speed of translation when animation is used on its own

      • Value (float) : Final
    2. MinVel_ID : Min_Vel

      • Value (float) : Final
    3. MaxVel_ID : Base_Vel > min and max are used by CS's internal "blender" to get what precise speed is required at one point or another

      • Value (float) : Final
  26. ModMesh

    1. MeshFile_ID : MeshFile > format is .cmf ; should format be a lower level?

      • Ref (text) : Final
    2. Size_ID : Size > is this actually mandatory/needed here? Can't quite yet tell.

      • Value (size3d) : Final
    3. Name_ID : Name > name of this part of the model, eg "Head", "Neck", "Left_Arm"

      • Value (text) : Final
    4. Material_ID : Material > CS allows several materials mixed on a mesh (though on occasion with this sort of thing it "allows" while at the same time complaininig if too many textures or too many this and too many that) but I'd stick to ONE material only.
  27. Mod3DGeneric > aka structure/item (non-moving)

    1. Material_ID : Material > while several materials can in principle be used, I'd rather stick to ONE material since *that* can combine several textures anyway.
    2. (Vertice_ID : Vertex)* > one vertex as a minimum

      • Value (position) : Final > this is given in model's own coordinate space
    3. (Triangle_ID : Triangle)* > each triangle is given as (i1,i2,i3) where i1-3 are indices of the vertices given previously

      • Value (3xuint64): Final
    4. (Normal_ID : Normal)* > there should be one normal vector given for *each* vertex

      • Value (3xfloat) : Final
    5. (Colour_ID : RGBA)* > there should be the colour given for *each* vertex

      • Value (4xfloat) : Final > this means r,g,b and alpha channel
    6. (Texture_Map_ID : TexMap)* > there should be mapping from each vertex to a texture point (ie u,w coords)

      • Value (2xuint) : Final > these are coordinates in the 2d, discrete space of the texture
  28. Mod2D

    1. Texture_ID : Texture > NB: this currently is "texture2d" but it's unclear if the distinction is worth much (ALL textures are anyway 2d images, ffs)
    2. Transparent_ID : rgb

      • Value (3xfloat) : Final
    3. Alpha_ID : Alpha > this is int in the current code, hm.

      • Value (int8) : Final
    4. Pos_ID : position

      • Value (3xfloat) : Final
    5. Size_ID : Size > height and width

      • Value (2xint) : Final
  29. Material

    1. Name_ID : Name > CS uses "names" in fact as IDs; so there can be several materials with same texture/file/data but different names...

      • Value (text) : Final
    2. Texture_ID : Texture > CS works with materials (it wraps any plain texture into a material anyway)
    3. (Shader_ID : Shader)* > a material CAN have several shaders applied.
    4. (ShaderVar_ID : ShaderVar)* > those are used *across* shaders to customise them, hence put here as separate, ugh; I think though that they belong more logically as a property of each Shader.
  30. Texture > CS supports: png, tga, bmp, jpg, dds, gif, mng, jng; common use given: png/tga for textures with alpha; jpg for those without; "best" = dds because it stores mipmaps directly + no need for additional steps (for a png there is uncompressing + mipmaps calc + recompressing to upload; a dds is directly uploaded to graphics hardware)
    > There are ALSO, "texture classes" but it's totally unclear if they bring anything useful or if they are anyway a server concern at all; from the manual (p. 331/644): texture class = collection of settings controlling how a texture is uploaded to the Graphics hardware (e.g. lookup, normalmap, nocompress) + "adding semantics".

    1. Name_ID : Name

      • Value (text) : Final
    2. Image_ID : TexImage > at this stage it's unclear if stuff like size should be given explicitly since it should rather be either extracted from the file itself (?) or default/predefined (?)

      • Ref (text) : Final
  31. Shader

    1. Name_ID : Name

      • Value (text) : Final
    2. ShaderTypeID : Name > ugh; those are again predefined CS stuff/available shader types...
    3. File_ID : File

      • Ref (text) : Final

      > filename containing the shader description; atm those are a bunch of xml files, full of dependencies & referencing one another + cs-specific.

  32. ShaderVar

    1. Name_ID : Name > NB: those names need to match what shaders (those xml files) know/expect, for anything to work...

      • Value (text) : Final
    2. Value_ID : Texture|Float|2xFloat|3xFloat > the value of the shader variable has one of those types

      • Value : Final
  33. Light > ALL lights are "dynamic" since they can always change/be added/removed etc.

    1. Name_ID : Name

      • Value (text) : Final
    2. CentreID : Centre

      • Value (3xfloat): Final
    3. ColourID : rgb

      • Value (3xfloat): Final
    4. AttenuationID : Attenuation > "type" of attenuation (?)

      • Value (uint8) : Final
    5. RadiusID : Radius

      • Value (uint16) : Final

As full of holes and possibly over-wordy and all that as the above draft finds itself, it was still quite the toil to extract. Nevertheless, extracted and written down it had to be as I couldn't quite move any further without this step. Now that it's pinned down, I can go through it and discuss the questions/issues that are still unclear throughout. There are also a few aspects that are currently still entirely missing (not that the rest is guaranteed to be complete/final), namely:

  1. Some meta resources - most notably authorship of art files - are still missing entirely. I'd rather discuss/iterate first those concrete (relatively) parts extracted from client-entrails before moving on and fixing the details of that, since it's more likely to be actually easier anyway as it's way more clearly and sanely defined to start with.
  2. ModAudio - the definition of this is still missing as I never even bothered yet to get the SoundPlugin to work at all. Perhaps it can be added/fleshed out later on and then clients that know it / want it will use it, while the rest will simply ignore it as an unknown/un-usable part.
  3. ModEffects - this is also still missing but unlike the audio part, this could be more readily investigated and fleshed out more, by figuring out how to load/create effects from code directly, similar to the previous work on characters for instance.
  4. Water - this requires an investigation into creating water bodies from the code directly; specifically: some practical experience using CS's procedural textures and some tests to see if/to what extent this could/couldn't be folded into structure/item
  5. Weather - this requires an investigation into CS's particle systems at the very least (for snow/rain/etc) and some more reflection + testing to check if it makes perhaps more sense to fold this into Air anyway.

  1. This is just one type, the name is given like this to make it clear that both "items" such as an axe or a flower and "structures" such as a house or a bridge are in here. Basically anything that is not terrain/water/air and can't move by itself is in here (if it can move by itself then it's in Characters)  

  2. NB: CS uses floats, with 1,1,1 for white and 0,0,0 for black. 

A Summer's Summary and Next Steps in Eulora

Thursday, September 5th, 2019

Coming back from any holidays, no matter how short, means that I spend initially a few days simply with the task of picking up again each and every bit of work that I put on hold while I was away. Note that I didn't say "dropped for the holidays" but rather specifically put on hold in a prepared manner so that picking it up again is not a horrendous chore but rather an opportunity to re-load the context with a fresher mind. And this makes it also a good moment to review the work done over the past months and state the next steps just as I usually do anyway for my own benefit before setting to do any work. The only difference this time is that I'm publishing here this whole summary of work done + work in line to be done next, so that anyone interested can follow along. Why wouldn't I publish it anyway?

May was spent on two main pieces of work:

  1. A working initial1 implementation (both on client and on server side) of the SMG protocol parts that are already clearly defined, namely network communication, account request/creation, serpent keys request & management, file request & transfer. This part meant Ada implementation of the protocol but also wrestling the legacy Eulora client into using the network communication structures of SMG Comms as encapsulated in the standalone Ada lib called EuCore. It also included some server-side work so that the end result is proper communication: the client can now ask for a new Eulora account and the server will reply according to SMG Comms; the client can also use its already established Serpent keys to communicate with the server and the server will reply accordingly. As required part of this work, a broad mapping of the client and its dependencies had to be done as well and basic docs for the client were published.
  2. The initial design of client logic for communicating with the server i.e. moving up on to how to use the messages that the protocol (and the implementation at point 1 above) makes available. This involved more reading, thinking, discussing and structuring than coding. The concrete result is a clear client strategy for knowledge acquisition and a very basic initial skeleton of its implementation. However, as the *structure* of the knowledge to be acquired was not yet defined or even fully clear, the next steps (see June below) had to focus on clarifying that, before the implementation can be pushed any further.

June started with fleshing out the hierarchy of data in Eulora as an initial draft capturing the relevant knowledge so far. This helped clearly identify the big areas that required further detailed investigation, most notably the graphics part where neither the required building blocks nor the specific client approach were yet known in enough detail to allow any meaningful attempt to extract a relevant data hierarchy. So the rest of June was spent on figuring out the graphics part: acquiring a better theoretical understanding through study of the reference book for computer graphics2 and at the same time acquiring also a very practical knowledge of how things are done by first mapping broadly (and then ripping apart) the existing approach as it found itself spread and scattered through the legacy client, several plugins of the same and the several plugins more of CrystalSpace (CS) + the code of external libraries (cal3d especially). By the end of the month, the better understanding of the whole graphics part and the practical experience with it resulted in a working initial example of loading some specific type of graphics object and a clear structure of the notions involved. This structure doubles as the map to follow for the next steps towards making the client able to load everything on demand.

July focused entirely on graphics in Eulora, figuring out one by one and implementing a prototype loader for each of the sections in the map written at the end of June:

  1. Loading a static (incapable of movement) object given through its list of vertices, normals at vertices, colours at vertices, texture mappings at vertices and triangles that make its faces. This includes already loading factories and textures.
  2. Loading the landscape aka loading the terrain given through its heightmap and material palette. This includes also loading lights, materials and shaders. The whole was pursued only to the extent that the terrain and materials were indeed loaded as specified by the heightmap + material palette but without fully aiming to solve other potential issues such as the "mirror" effect that is more related to lighting and - possibly - some material or shader properties.
  3. Loading a character made of several parts that can in principle change on the fly. This part was pursued for now only to the extent that the character is loaded and shown + answers the player's control but without fully going into the details of equip/dequip, actions and the full extent of changing bits and parts.

August was a bit split, with the holidays in the middle:

  1. At the beginning of the month, I continued the work on graphics but moved from the 3D issues to 2D, namely the user interface with all the different icons and images and various bits that are sometimes known as the "skin" of a client. This required a deep dive into yet another plugin of the legacy client, the widgeting PAWS.
  2. At the end of the month, I started working on writing down the 2nd draft version of the client data hierarchy integrating the results of all the work on graphics + all the pondering and figuring out done throughout the month + the further discussions and clarifications in the SMG boardroom on the desired working of Eulora's communication protocol. This is still in the works currently but I plan to publish it for discussion as soon as I've got down all that I have so far and certainly no later than the end of next week (ie no later than 15th of September).

The current plan for September and beyond is this:

  1. Write and publish for discussion/revision the deeply revised (re-written really) draft of client data hierarchy. Deadline is 15th of September.
  2. Pending feedback and discussions, possibly dive again for more details if needed and/or revise further the data hierarchy, if/as required. This is a crucial part for moving forwards but it's also one that once fixed will be very costly to change.
  3. Work on the implementation of the client and server-side pieces of the SMG protocol that are still missing, as soon as they are set/clarified. At the moment, they are listed broadly and in this order: client-side data storage (aka cache, of which currently there is only a skeleton, pending better understanding of what the data is going to be exactly), object request/reply, client actions, full client logic for knowledge acquisition and storage, full client loading/use of local data cache, client and server management of own keys.

  1. to be refined/reviewed as other parts such as key storage and bootstrapping become clear 

  2. Foley, J.D., van Dam, A., Feiner, S.K. and Hughes, J.F., "Computer Graphics: Principles and Practice", 2nd ed., Addison-Wesley 

The Widgeting Paws of Pointerism (Notes on Graphics in Eulora, V)

Tuesday, August 6th, 2019

It took indeed more than a week and I'm rather surprised it didn't actually take even longer than two weeks to get *anything visible* out of it. It's all one strand like Stan's spittoon after all, so there's rarely just "one sip" that doesn't bring in the whole rot with it and I have to fight over and over the urge to just burn it all down and start a sane thing from scratch1. Nevertheless, a lot of deep breaths and assorted cussings later, here's a brief summary of the main achievements, in the order they were made:

  • some useless crap was removed and the whole thing made to work without (e.g. the doll with its dependence on a special sector in the map file);
  • the initial loading and "pre-caching" of meshes, textures, materials, maps and 2D stuff was excised (there are still the shaders remaining though as well as 1001 "preferences" in as many files in yet as many different places, sigh);
  • the actual handling of 2D icons and GUI images in general (e.g. buttons, borders, arrows, window decorations) aka the PAWS was dissected and exposed and roughly mapped as ugly as it is;
  • as recorded proof of breaking PAWS and because of its shameful lack of proper way to load images on actual demand, the whole thing has had a run with the stupidest "mouse pointer" available in there and otherwise with absolutely no 2D images at all for the very lulzy effect that it even looks better in some ways than with all of the various pieces of "art", here it is:
    That snot on the screen is the mouse pointer on the black background starting screen.

    That snot on the screen is the mouse pointer on the black background starting screen.

    PAWS's shame: the better-looking interface with no images whatsoever (clicking the invisible widgets still works!)

    PAWS's shame: the better-looking interface with no images whatsoever (clicking the invisible widgets still works!)

The path that led in the end to the above started with me looking - as initially planned - at how the character (as moving sprite) is handled or rather mishandled, consider: there is the CEL layer that supposedly provides on top of CS the sort of structures needed by games to store and manage the various entities and objects; PS however "uses" it in principle but in practice it still mainly does its own thing anyway (and by now I strongly suspect that this pattern is simply because nobody ever spends the time to understand what they use); specifically, the PS way is to have a pscelclient class that supposedly manages game entities on client side (shouldn't CEL have been enough for that, you ask? most probably yes but that'd have taken someone to figure out first exactly how it works and who has time for that!!); but then there is *also* a pscharapp class that handles the appearance of the character (apparently that's too low stuff for "management" to handle) and there is also a psclientchar that moves about equipped items because this is yet neither management nor appearance but absolutely something separate - though you'll be hard pressed to notice much separation in the code for all this theoretical separation; and to top it all, the inventory and doll views that precisely reflect character's appearance and equipped items are actually the domain of something else entirely - the PAWS; and the PAWS (which is supposedly so separate as to bother to be a plugin by itself) in fact messes about with pretty much anything and everything from the engine to shaders and textures and 2D images (which are still loaded as... textures, but those are textures-this not textures-that) and xml-defined maps and hardcoded paths to all sorts of files it expects - nay, demands - to be present and just-so and at-start but oh-so-configurable. At which point it became clear that the PAWS mess is indeed such a large part of the whole mess that I'll simply have to get to the bottom of it just in order to move further at all.

Looking at PAWS more closely revealed that it's meant to be a GUI system. This initially rather puzzled me because there IS in fact already another GUI system in there, one that PS uses/relies on as a dependency, namely CEGUI. Then again, the PS style is to "use" something by simply sitting on top of it and mushrooming a lot of managers with hardcoded paths and expectations that they know the whole possible world upfront so there should be no surprise at PAWS, which is precisely this sort of thing - it mushrooms widgets and pointers rather than something-else and pointers, that's about all the difference from all the other PS masterpieces.

Masterpiece aside, what IS this GUI system PAWS? As far as I can tell, it's a lot of widgets so well organised that there are no less than 3 methods "FindWidget" because the 1st one (defined in pawswidget) checks the current widget and its children only, the 2nd one (in pawsscript) checks also the parent of the current widget while yet the 3rd one (in pawsmanager because yes, of course there are all sorts of managers) simply delegates it all to "mainwidget" aka a sort of calling the 1st one from the top of the tree (but no, they couldn't possibly notice that it's meant to be a tree and there are well-known algorithms for searches in a tree). Anyways, the fun part with widgets is the way in which they are defined really as each widget has an .xml file (in data/gui) that supposedly defines appearance only (though more concretely it's about which elements it contains and on what position) and a .cpp file (in client/gui usually) that supposedly defines behaviour. The funny bit however is that both of those, for all their pretense of clear separation (they even are in totally different dirs, yes?), quite directly reference both one another's bits and pieces AND global, all-of-a-sudden-made-hardcoded parts: the appearance references by name other widgets (that it uses for specific roles and in specific places so it's not just a name really) and "resources" aka pre-loaded images; the behaviour code in turn has to reference various components of course. And in all this, apparently nobody noticed that before you can talk of behaviour and of appearance, there has to be somewhere a *structure and nature* of the thing that actually is the core and therefore the driver of everything else... Then again, what structure and nature and such silly notions, when the whole thing is bent on defining anything and everything by its current, temporary position or at best some role it might fulfill for a while2.

The widgeting PAWS further has all sorts of notions regarding appearances: there are "skins" and "styles" and "resources" and they are all both hardcoded and expected to be fully known upfront. The Great Paws knows not only all the world that is but also all the world that could possibly be (the world as it defines it is all nothing but labels anyway so it makes sense in a way). And it enforces this by whining and even wagging its finger at the user if it ever finds that a file with what it *knows* is the "right" name is missing from its expected place, quite literally giving orders to the user, as the message spit is this:

"ART ERROR: PawsTextureManager loaded the image %s which was missing from the imagelist.xml and was loaded on demand. Add the image there!"

I'll add to the above: and blow your nose and stand up straight and cut your nails shorter! Imagine that, being such a shitty user that the poor, poor PAWS had to actually load an image on demand (how degrading!!), you nasty user, you! And note that this "on demand" is anyway the sort of "you demand what I'm willing to do or no dice" not any sort of actual demand: it only means it deigned to look (because it had to use that image) in the *same place* it looked initially (when it tried to load everything that there should ever be) just because you are the sort of shit that failed to put the image there when it looked the first time, you know? What sort of user does such a thing to a poor piece of code, you tormenter, you, shame on you!!!

Before you get your hopes up that there is at least some sort of "loading on demand", bash those hopes one over the head: for one thing this "on demand" still relies on the predefined list of what there *can* ever be so that if you request a "resource" it doesn't know about than tough luck, it won't try to look for it, it's not that stupid to do something useful; for another thing, there is no way to actually call this on demand: it's just a way of doing rigidly the same thing but expecting a different result because meanwhile the world should have better come to its senses and fit the expectations already, what.

If the above sadness is not enough, add to it this significant bit: PAWS considers itself a plugin and as such entirely separated from the actual client and therefore unable to use whatever the rest of the client knows. Sure, in practice this separation "works" by means of the main client class simply passing pointers to PAWS on creation - why use CPP if not for pointerism, after all. Moreover, the added benefit of this separation is that PAWS also gets therefore to have its own set of repeated XML parsing of all sorts since it can't use the already-repeated XML parsing that the client anyway suffers from. And finally, it makes somehow perfect sense to "separate" the main client from the GUI system seeing how they both use the same graphics engine anyway and moreover the main client loads some images as textures while PAWS loads...well, some images as textures, yes. But they are not the same "class" of textures, so that's significant separation, see?

Besides deep red, I tell you what I further see: since I have no intention to pass even more pointers on or otherwise to force this "plugin" nonsense on everything just for the very specialness of PAWS, it's way more likely that the paws will be clipped more to size and therefore will take their place as yet another of the many classes inside the main client, possibly together with all the rest of very-special-plugins if need be (of course the dependencies are not that simple). I'm still pondering this, mainly because the idea is to touch as little as possible the code rather than as much as it takes to clean it (mainly because clean it won't be anyway). But one way or another, I will have to find some reasonable way to bring PAWS in line with the rest and have it go and load resources from wherever (and whenever) it is told to. This might even require more discussion in S.MG's boardroom but at the moment and as a result of all the adventure above, I have at least a much clearer idea as to what the 2D part is all about so there is therefore a point to even start the discussion from.

  1. While it might *seem* faster that way, it's not, nor does it really make sense if one calmly considers everything involved. Urges like this are simply focused on getting rid faster of the current set of problems and do not care nor stop one second to consider the alternative set of problems that are bought by such "solution". So yeah, urges make very poor guides, how surprising. 

  2. And no matter how much I try to *not* see it this way, it always is the case that the closer one gets to the "how things look" and therefore in this case to graphics, the more confusion and more monkeying there is. 

Naked Models (Notes on Graphics in Eulora, IV)

Wednesday, July 24th, 2019

What's the first change you'd do to an empty-headed character such as Testy? Why, chop some bits off, change his sex and get him naked at least, even transparent at that, since he's pretty much just as tedious and boring as all children dolls 1. Of course the first attempts failed to produce anything visible because it turns out that using Cal3D sprites in Eulora's client goes through several layers of inmisdirection: Crystal Space (CS) wraps the Cal3D code in its own SprCal3D plugins and factories and whatnots; on top of that, there is also the rather elusive Cel (Crystal Entity Layer) that is meant to be a game-specific part of CS keeping track of game entities; and messing within and across all is Planeshift's (PS) own GEM concoction interlaced with delayed loaders and Dead-Reckonings2 and cloning of materials (why? no idea yet) and surprising, undocumented transformations. Still, after a while of hacking through all the above, I could at least get some parts of Testy showing... if lying down rather than upright:

Spot the bottom in the grass!

Spot the bottom in the grass!

The prostrate position makes at least sense in that "movement" also looks a bit like some swimming look-alike: parting the legs and advancing sort of thing. Then again, it turns out that using a different model (e.g. Cally) makes it even more interesting - the model is upright when waiting but it dives straight back to the horizontal as part of moving and it furthermore cares not one bit for whatever movement animation one loads. So there's a clue for another day - current movement needs a good bashing over the head and a deep dive to figure out what's all this nonsense. But until then, here's naked Cally who borrowed Testy's skin for a spell:

What bits is she missing?

What bits is she missing?

Anyway, since position of the model is always a matter of applying one transformation or another, I went ahead and applied the obvious transformation, 90 degrees and upsy daisy on her feet:

Upsy-Daisy with muddy skin!

Upsy-Daisy with muddy skin!

And funnily enough, once the 90 degrees rotation is applied to the factory (i.e. presumably to ALL entities built out of that factory) the Testy model remains upright even during movement without any perceived change. On the other hand, the Cally model is upright without the 90 degrees rotation but nevertheless dives down for movement and pops back up as soon as movement is done. So it would seem that movement has its own assumptions that it doesn't bother to make public, how lovely. Seeing how it's most probably to do with the model's initial position or similar, I might even need to end up again with the full environment, Blender and all installed to get to the bottom of it and probably see exactly what or how the exporter is also broken and specific. Such joys to come but for the moment they are not yet here since the more pressing need is first of all to disentangle some more the PS+CEL+CS+CAL3D mess regarding animated entities so as to actually be able to *use* the walk/fight/move animations rather than just "add" them and moreover to be able to add and remove on the fly as many and as diverse entities as wanted, beyond the "main character" guy. And all this is getting deeper into PS swamps so it's likely to take more than a week indeed - ideally by then I'll be able to cut off at least some parts of the existing code (precaching and preloading especially) and lighten the client start a bit if nothing more.

  1. Changing graphics is this exercise in tediousness and pointlessness, it eerily reminds me of playing with dolls, just one step lower than that since those are just images of dolls, fancy that. And playing with dolls was utterly boring even when I was 5! It's like an endurance test along the lines of watching paint dry. Tinker with this and tinker with that, all for "the looks" of it and can't even satisfyingly throw it at anything to hear it break. 

  2. Not kidding, although it's way less exciting than the name they had the chance to land on: it's mainly looking to see if your character falls down and breaks their neck. 

The Mirror Land (Notes on Graphics for Eulora, III)

Wednesday, July 17th, 2019

Getting some actual landscape as in getting the height right turns out to be only half (the reasonably well-working half) of the terrain task: once I had in place at least the generic mechanism for creating factories and meshes, the terrain factory required only a few iterations, false starts and mandatory dives into CS's entrails to get working and save Testy from his usual fall into the sky-sphere:
Terrain generated from heightmaps only (no mix of materials, only base material).

After setting up the lighting a bit better too, it looks even more reasonable (next to the crater that is currently empty, yes):
Base terrain from heightmap.

The truly frustrating bit turns out to be the painting of the landscape in the desired materials. For the relatively easy part, the Terrain2 construct of CrystalSpace provides a "terraformer" that can be fed a "material palette" (i.e. a set of materials to use) and an indexed image - as a result, the terrain will have a reasonable mixture of the materials depending on heights and an ok fading of the materials into one another where they meet so it doesn't look that terrible, especially once all normals are in place too so that lights stand a chance to work:
Terrain from height map *and* material palette with 3 materials: grass, sand and rock.

The trouble however is that so far I haven't quite managed to get to the bottom of turning down the reflective mirror-like surface of this landscape! In theory, materials have those 2 light-reflecting properties, namely the extent to which they are "diffuse" or "specular" (i.e. "shiny"). Still, for all my futzing around with those for all three materials (sand, grass, rock), the observed effects are pretty much non-existent. So much for the theory and welcome to this graphics thing of tinkering. To see clearly what I mean, here's a rather surreal rendering with the ambient light turned on to higher levels so it's more obvious (note that the effect is precisely the same even with low ambient light - it's just harder to realise what it is exactly; similarly and as expected, one can mask the effect to the point that "it's fine" by turning on the fog button - it makes sense since it dulls the ambient light, I'd say):

On the positive side, the futzing with lights and whatnots is really something of less concern for me at this moment since I'm not that interested in obtaining an image *just so* - my goal is in figuring out *what* sort of things one can set, what do they do and how to set them while the game is running. In this sense, at least the basics of the terrain seem to be in place for now: heights are read from a heightmap file, the materials are indeed used according to heights, the rest is for another day. I suspect though that in usual tradition, the only real solution here is to dig in the end in that shaders pit as well since it's most probably a shader thing: the terrain object uses a different, terrain-specific shader and for some reason the defaults for that end up producing more of a mirror than a soil, at least given *everything else* (such is graphics, everything and anything can be the culprit). Note that I specifically used and set precisely the same parameters for materials as they are in the client currently in use - so it's most probably something else/somewhere else that I'm not setting just right for now.

The next step now is to figure out animated meshes too, hence Cal3d objects. So in the next episode, it's Testy himself that will change and get created directly from code. Once I have at least one way to do this too, I can go back a bit and look again into integrating them into (or extracting them out of...) the rest of the PS infrastructure. Then again, before doing that, I might still need to dive in and figure out the shaders in more detail to extract some way of specifying them without xml and directly from code too, perhaps. That promises though to be a lot of figuring out and additional mess so depending on whether it can or not wait a bit longer, it might not be exactly next in line - basically I have lots of work competing for my attention, a sort of inverted looking-for-work, it's the... (lots and lots of) work looking for me!

The Rocky Moon (Notes on Graphics for Eulora, II)

Monday, July 8th, 2019

My client-side Eulora task set last week is completed and as a result, the Testy character gets now to see something more than just fog in his Sphere-of-all-beginnings, look:

Testy under a Wavy-Blue Moon, in the Sky-Sphere of all beginnings.

Testy under a Wavy-Blue Moon, in the Sky-Sphere of all beginnings.

It turns out that CS can indeed eat - though not very willingly1 - "geometry" for fixed/static entities (aka "generic meshes") even without any xml in sight2. For static aka non-moving entities, there are 5 sets of numbers required:

  1. Vertex coordinates
  2. Normals at vertices
  3. Colours at vertices
  4. Texture mappings at vertices
  5. Triangles

The main trouble with the above was the exact "how" and "where" to actually set them so CS understands what they are. It turns out that the most obvious "those are the vertices and those are the normals etc" approach is "obsolete" and replaced apparently by having to specify a render buffer for each set of numbers. Of course it's still the same arrays of numbers either way, but with render buffers you need to also specifiy which *type* of render buffer you have in mind (i.e. what are those values for) and in pure CS style this is specified by giving the... name. Name that is afterwards internally translated into a numerical id of course but nevermind since it's supposedly as easy as it can get to have to remember that the name is exactly "position" and not "positions" nor "vertices" nor anything else3). So easy in fact that I ended up having the list of "buffer names" at hand all the time, great help indeed.

Before moving on to some basic descriptions of those 5 sets of numbers, it's worth noting that they are just the most usual 5 sets really: there are plenty others that one can specify though it's unclear exactly when and precisely why would one do. The easiest example concerns texture coordinates: one can specify as many as 4 different texture mappings. And why stop at 4 if one went all the way there, why not 5 or 6 or 7 or 1000? No idea. Anyway, for the basic descriptions:

Vertex coordinates are exactly what the name says: the 3D coordinates of vertices that define the entity's shape in space. Those are all relative to the position at which the entity is drawn at any given time, of course, since they are internal to the entity and unaware of anything else in the larger world.

Normals at vertices are perpendiculars on the surface of the entity at each of the vertices defined earlier. Those are quite important for lighting (together with the orientation of each triangle surface defined further on).

Colours at vertices specify the colour of the object at each vertex and allow therefore colouring of the full object through interpolation for instance. While in principle this can be skipped when a texture is given, the "skip" simply means default colour (black) so it's a "skip" only in the sense that it's less obvious it exists.

Texture mappings at vertices - those are tuples (u,v) that specify a texture point (2D because textures are 2D images) that is to be mapped precisely to each vertex. Just like vertex coordinates provide fixed points for the shape of the engine that is otherwise interpolated at non-specified points, the texture mappings provide fixed points for the "painting" of the shape with a given texture.

Triangles define the surface of the entity as approximated through triangular shapes. All surfaces of static shapes in CS are approximated through triangles and the only difference between curved and flat surfaces is essentially in the number of triangles required so that your eye is fooled enough so you like rather than dislike the lie. This is after all the whole computer graphics in a shell: a lot of effort spent to lie to you the way you like it.

Triangles in CS are given as triplets (A,B,C) where the values are simply indices of the vertices previously defined. So the simplest cube will have for instance 8 vertices for its 8 corners as well as 2*6=12 triangles for its 6 faces since each face, being a square, needs 2 triangles to approximate. And those 12 triangles mean 36 values in total since each triangle needs the indices of all its 3 vertices. Moreover, the order in which the vertices are specified matters as it gives the orientation of the approximated surface4 and that in turn is crucial for calculating lighting (and ultimately for figuring out whether you get to even see what is on that surface at all). Since it's not intuitive perhaps, it's worth noting the basic fact that a surface has 2 sides and the "painting" of a texture is strictly on one side only. So if you mess up the surface's orientation, you can easily do the equivalent of dyeing a garment on the inside without any colour whatsoever showing when you wear it unless you turn it first inside-out.

As you might have guessed from the above example with the most basic cube if from nothing else, even very simple entities require a lot of values tediously and boringly specified - hence quite the bread and butter of automation5, yes. For now and for testing purposes, I simply extracted the numbers of interest from the xml description of the moon object in game and then I used them as default values for an object created directly from code. And lo' and behold, Testy got a moon to stare at and the moon even got to paint itself stony when I'm bored of its usual wavy blue:

Stony Moon and Testy in the Sky-Sphere.

Stony Moon and Testy in the Sky-Sphere.

The next biggish step further in this line is to figure out how to generate terrain from just a heightmap and then how to create an animated (hence, Cal3D) entity from code rather than through the sterile xml-packaging. Looking even further from there, monstrous xml-shaders await in the same direction but in other directions there is still a lot of work to do on teaching the PS code to use the EuCore library and to discover as a result that the world is way larger than what fits in its inherited, well-worn and time-proven, traditional xml-bible.

  1. A bit like those finding out that they can actually eat food that was never packaged in plastic or washed in chlorine or even re-heated. 

  2. At least not in geometry-sight. In a bit wider perspective, all shaders so far are still balls of xml that will probably have to be untangled sooner rather than later. 

  3. If you give a non-existent name, the buffer is simply ignored since supposedly you can define your own buffers too so it's not an error. Combined with the wonderful way in which everything influences everything else in graphics, you get - if you are lucky - to puzzle over some unexpected visual effect that might even seem at first to have nothing at all to do with the names of the render buffers at all (maybe you messed up a few numbers in that long list, you know? 

  4. By convention, vertices are given clockwise as read when facing the surface 

  5. Automation can be here exporters from supported tools such as Blender and generators of known shapes/patterns. 

Notes on Graphics for Eulora

Sunday, June 30th, 2019

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):

  1. 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.

  2. 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.
  3. 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"?
  4. 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:

    1. Movement-capable entities
    2. Fixed entities
    3. Terrain

    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.

  5. 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).
  6. 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.
  7. 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).
  8. 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.
  9. 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:

  1. 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. 

  2. 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. 

  3. 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. 

  4. 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. 

  5. 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.)  

  6. "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. 

Eulora Client Hierarchy of Data Draft

Tuesday, June 11th, 2019

This is a very first draft to help with the current work on enabling Eulora's GUI client to make use of new information as it becomes available from the server rather than expecting everything to be fully known upfront (as it currently does, laugh you not!).

Eulora's data is one big hierarchy with 3 main types of information: structure, description, representation.

  • 1. Structure (i.e. what entities are inside what other entities)
    Structure is always a tree of entities where each entity is given through ID (unsigned 32 bits) + Position (x,y,z,rx,ry,rz):

    The Root of the world will ALWAYS contain:

    • an *abstract object* with meta-info:
      • ID of "self" (SelfID)
      • ID of current location (aka "sector" or room or world or whatever else it is) (LocID)
      • List of Skills: list of (skillID, skillName, skillCategory, skillRank, skillXP, skillKnowledge) (?)
      • Game Date and Time (?) --- is this a meta-matter or is it a location-property?
    • the *top-most ID* (aka topmost entity); NB: this is not always the same as LocID since character may move into a location inside another location, presumably.
    • from top-most ID down, EACH node will have zero or more contained entities (hence, child nodes given through their unique ID and concrete position within their parent).
    • NB: each node can also have in addition to its structure-children (hence entities), any relevant number of description-children (see 2 below) or representation-children (see 3 below).

    • 2. Description of what entities themselves "are" through their relevant properties

      ANY structure-node may further have, alongside its children, one or several of the following properties (given as a tuple property,value):

      • name
      • description (??)
      • (- equipment slots (list of (slotID, entityID) ?) -- are those any different from "contained"? I can't see a truly significant difference.)
      • stack count
      • quality
      • HP (hitpoints)
      • BP (bloodpoints)
      • SP (stamina points)
      • MP (mana points)
      • SpP (spirit points)
      • MSP (mana spirit points)
      • weight
      • bulk
      • weight limit (max weight)
      • bulk limit (max bulk)
    • 3. Available representation of entities (graphics, sounds, effects, maps etc)

      NB: in principle there can easily be a structure + properties split at this level too since a graphical representation may have its own structure (e.g. map defined as a hierarchy of contained terrain + geometry meshes that have in turn different submeshes with corresponding textures/materials). This seems rather unseemly though and it's unclear if there really is a need for a hierarchy of graphical detail that is anything OTHER than hierarchy of world entities to start with. So perhaps this can be flattened so that representation of entities means just that: a list of relevant graphical characteristics of ONE entity.

      ANY structure node may further have, alongside its entity-children and its properties, one or several of the following representations:

      • Mod2D (aka 2D model; at its most basic, the filename of icon to show for this; tbd)
      • Mod3D (aka 3D model; at its most basic, spec for mesh including material/texture and sockets if any; tbd) ; NB: sockets allow graphical representation of contained (specifically "equipped" or "component") entities and therefore they should probably be identified by "position" so that the entity "child" on that position will then give the actual representation shown to the user (?).
      • ModAnimations tbd
      • ModMap (aka map for this entity; at its most basic heightmap + size (or is this fixed?); possibly also a list of materials to use + corresponding density)
      • ModWeather (unclear yet if this is any different from sounds+effects really)
      • ModSounds tbd
      • ModEffects tbd (unclear yet if this is substantially different from animations+sounds)
      • ModSkin tbd (stuff like window borders and button colours)

      ANYWAY: GUI may request whatever it wants from local cache, regardless of cache's own internal representation (just as cache may store data in any form, regardless of smg's own description of hierarchy).

    TO DO: flesh out in more detail each possible representation, for GUI to ask & work with + get it to actually work outside of/inside the intricate mess of ps paws, widgets, managers and whatnots maggots.

Eulora's Client Core: The Dedicated Requester

Monday, May 27th, 2019

A crucial requirement of Eulora's new client is to actively request from the server ANY data that it may find itself missing at any point in time. At first glance, this seemed to me simply a matter of providing request services1 from Eulora's new Ada-based core and then adjusting the existing C/CPP code of the legacy client to make use of those services. This rather optimistic idea is of course plain wrong: "adjusting the existing C/CPP code" in this context is similar to saying that one "adjusts" a sheep to use the library - while it can certainly be done for various definitions of "done" and the sheep may indeed use the library one way or another, it's at best a huge waste (of time, of resources, even possibly of steak) for everyone involved and no matter how one looks at it.

Even leaving aside for a moment the trouble with "adjusting" the legacy tangle in any direction, the more important issue here is that this requirement is not as much a functional requirement as a non-functional, quality of service requirement: whichever part of the client provides data services, it should better be dedicated to the task and do whatever it takes to get it done instead of "providing" it only fair-weather style - if it's easy, there you are and if it's not easy then it's your problem really. In other words and in marked contrast to the very democratic "best"-effort-you-can-do-anything-anytime existing C/CPP code2, the new code should have a clearly defined task and then either complete it or die trying over and over again, taking full responsibility for the process involved, not just for some specific detail conveniently chosen nor - as an excuse for not delivering - for the outcomes that are not fully under its control3.

Considering therefore "active and dedicated request" as a quality of data service on Eulora's client side, it follows that its place is rather close to the data cache mentioned previously and at any rate inside the new Client Core since it's certainly not some additional part of client logic nor some bit of user interface. However, I'm reluctant to make it the responsibility of the cache itself since the cache is a passive structure that focuses on *storing* data and *providing access* to it. Mixing passive data storage with active data acquisition doesn't make much sense to me and even seems ill-advised for Eulora's client given the competing requirements: on one hand passive, immediate-response local data storage and on the other hand active, possibly-delayed and world-facing (i.e. communicating with the server) data acquisition. So I'd rather avoid this passive-active construction and have instead the two as separate entities: a EuCache dedicated to storing and retrieving on demand *any* data; a Requester dedicated to acquiring *any* data that is demanded of it. Note that the definition of "acquiring" here has nothing to do with the means through which the Requester actually gets this data (specifically nothing to do with the exact messages sent/received/exchanged with the server). Acquiring some data means simply that the required piece of data becomes available in the local cache aka EuCache. So the Requester will keep requesting this data from the server through whatever means it knows until either the data arrives and becomes available from EuCache or otherwise the whole client kills itself for lack of server4 and therefore of any possibility of playing the game.

Specifically, the Requester will be implemented in EuCore (and therefore in Ada) as a protected object exposing only a few procedures that are essentially notifications: some are notifications of demands for some specific piece of data (either an Object or a File really since those are the only 2 overall types of game-data that one can request from the server); the others are notifications of data being received or of timeout interval having elapsed (in other words a notification of failure to receive data). Note that the demand for an "Object" effectively means a demand of its properties and those might be indeed anything at all but each and every one of them will be either directly a value or otherwise an ID of another Object or of a File. All notifications (including those demanding data) are always accepted by the Requester but not necessarily immediately acted upon. Clients of the Requester do NOT control the actual requests to the server or messages exchanged and are not even concerned with those at all - the production of actual requests, their content and their timing are entirely the job of the Requester and under its sole control. Implementation-wise, the Requester will simply keep queues of requested Objects/Files and will then proceed as soon as it can to pack a request for as many of them as possible; this request will then be posted to the server and the Requester will set a timer to the timeout value so that in the worst case it is at least notified that nothing happened; when/if any data is received or when this timer expires, the Requester will check in EuCache to see what items (if any) of those requested are now available; any items that have become available will be discarded from the watchlist of the Requester (i.e. the demands for them are considered completed) and a new request may be created and posted to the server for any items that are still in demand but not yet available. Note that even in the event of a timeout, a "repeated" request to the server may not be identical to the previous request since the list of demanded data possibly changed in the interval.

One potentially iffy point for the Requester is its need to be notified of any incoming data. At the moment I don't see any real way around this, short of making the Requester poll at set times the EuCache and checking if any data of interest has meanwhile arrived. I don't really like this polling approach here because it's rather wasteful without good reason: any incoming data is indeed received and processed by another unit that is also on the same level with the Requester, namely the Consumer (the part that processes messages from the inbound queue). So the Consumer will have to notify the Requester when new data is received. While several Consumers may be active at the same time (at least one for Serpent and one for RSA messages) this is not a problem at all since the Requester is anyway a protected object i.e. thread-safe. Note also that even if (some of) the consumers fail to notify the Requester of some incoming data, the whole thing will still work if only slower than it could: the timeout timer will wake up the Requester and the check of data will happen there at any rate. In other words, the Requester is capable of reacting to events if notified of them but not dependent on those notifications to do its job correctly.

Given its rather complex task, the Requester is currently on the top conceptual layer of EuCore, making use of quite a lot of other units from lower levels. Currently, the main relevant units on this top level are the following:

  • Data Cache aka EuCache - this is a passive, thread-safe entity responsible for storing all and any data given to it and retrieve or delete it on demand. As such, it *owns* the specific format in which data is stored5 and it simply exposes functions and procedures for storing, retrieving, deleting and checking for data.
  • Communication Link aka Comm_Link - this is a passive, thread-safe entity responsible for persistent storage and updating of communication details, most notably RSA and Serpent keys for inbound and outbound communications as well as a message counter. This is effectively a specialized cache - where EuCache is for game data, Comm_Link is for communication protocol data. The requirements (including use contexts) and specifics of the two seem to me sufficiently different to keep them separate at least for now.
  • Consumers of incoming messages (RSA and Serpent) - those are separate, active tasks, responsible for processing incoming messages. Consumers own and get to define what "processing" means exactly but their role is to extract the data contained in the messages received and make it available for use by Eulora's client. In practice this means currently that Consumers will pass any data received on to EuCache for storage and will notify the Requester of any data receipt.
  • Requester - this is an active, thread-safe entity responsible for acquiring data that is in demand. It owns the process of data acquisition from the server and it accepts any demands of data specified by some identifier. While it guarantees that all demands will be served at some point in time as long as the whole program is running, it does not (and can not) guarantee when or if the corresponding data becomes available. It can't even guarantee that there IS any corresponding data and so ALL it can do is to guarantee that it will try with the same dedication for each and every bit of data to acquire it. Anyone demanding data can of course pester the Requester with more demands or give up or decide for themselves for how long they can or will wait.

And now that the main idea and overall design of this whole thing is at least quite clear to me, there remains of course the small bit of actually implementing it in full (rather than the current skeleton I already made) and sorting out the various implementation-level troubles that will appear as they always do, in all sorts of details. Relatively easy work compared to what follows it inevitably, namely teaching that C/CPP sheep to use the EuCore library...

  1. Mainly picking, packing and encrypting everything in the right format + sending it through to the correct place. 

  2. Seriously, think of it: existing client code is this event-driven thing where *anyone* can subscribe to any event and then "do" anything at any time provided of course that the any and anything are in fact very narrowly defined and set in stone even to the level of names of various art files (it has to be a zoneinfo.xml inside this and that and put exactly there, sort of thing). If this narrowing of "do" was not a price high enough to pay for such code "liberty", there is also the added reality of a huge tangle that does precious little indeed since everyone ends up calling anything from anywhere and no piece of code is really and truly responsible for anything bigger than a few variables here and there. And at the end of the day how could any code even be responsible for anything since it can't *own* any process by itself (shared! event-driven!) and it has to be passive, mainly reacting to some events or at most... signalling through events of its own but never able to rely on anyone giving a fig about its signalling! So there it is, code - like anything you do - is more of a mirror than you might think. And "teaching people to code" has way more layers than "teach them Java" and certainly more issues than current "courses" even seem to be able to imagine. 

  3. And now that it's clearly stated like this, tell me first just how many people you know who actually handle their own work like that? And just what bit of "programming language" you think one needs to teach so that you get programmers to actually design their code this way? 

  4. This "lack of server" is defined in practice as a number of successive timeouts on requests sent to the server, where the specific threshold value is chosen by the user via a config file. 

  5. Currently game objects are stored in memory in Hashmaps aka associative arrays while art/files are stored directly on disk. Note however that any of this can be changed without touching anything outside EuCache as it's nobody else's business.