077 - Time For More Direction

July 26, 2020

I've been doing well with deploying something to the game everyday and haven't missed a day since we started a little over a month ago in 073.

Not all is rosy though.

I'm still struggling with making good progress on larger gameplay items.

I've been focused on the next thing from day to day, without having a concrete plan or direction guiding my work from week to week or month to month.

This makes it difficult to land big step function improvements on the gameplay experience.

My current approach to solving problem is to work on higher level plan and direction for the game.

I'll be refining that throughout the week so that by the end of the week I'll have a good sense of direction that can guide my development of larger features from week to week.

Customizing Renderables Foundation

This week I laid a foundation for being able to easily and flexibly customize aspects of how something is rendered, such as the skin color.

In our game data files it's been possible to set what materials a mesh uses for months.

But, if you wanted to be able to render the same mesh with two different materials you'd need to create another entry in the data file that copied almost all of the data with only the material changed.

This approach would not have scaled for more complex renderables that could be comprised of multiple meshes each with multiple possible materials.

This week I made some changes to our renderable definitions to make it easy to customize how entities are rendered without being duplicative.

Render customization The same meshes using different materials powered by the new renderable customization data structures.

To display an entity in the you need to give it a RenderableId.

Every RenderableId maps to a definition of what needs to be rendered into the 3d viewport.

Different entities can point to the same RenderableId, each transforming the final meshes using their own world position, rotation, scaling etc.

In the above screenshot, RenderableId::TutorOfNavigation is used to render the white character on the right.


I've introduced a RenderMeshCustomization struct and a few under data structures that we can use when defining the RenderableIdData for a RenderableId.

In the above screenshot we're using a MapSkinColor customization which maps a SkinColor:: enum variant to some material on the mesh.

Here's a lightly annotated look at some of the data structures in my renderable_ids_to_data.yml file that defines data for every RenderableId.

# The data for `RenderableId::TutorOfNavigation`
TutorOfNavigation:
  Absolute:
    renderable_ids:
    - id: Torso
    # Right now the "RenderableId::Head" is the head, legs, feet and heads..
    # I'll split the mesh up next time I work on the human model.
    - id: Head
    customizations:
      skin_color: White

Torso:
  Absolute:
    meshes:
    - mesh_name: Torso
      material:
        MapSkinColor:
          dark: BlackSkin
          white: WhiteSkin
          default_skin: BlackSkin
    - mesh_name: Arms
      material:
        MapSkinColor:
          dark: BlackSkin
          white: WhiteSkin
          default_skin: BlackSkin

Here's the code where we're selecting a material for a mesh to render.

// All material names are parsed from Blender and stored as `String`s.
// I'll eventually change them to be either an enum or a `u16`
// to avoid the unnecessary allocations.
pub fn material_name(&self, customizations: Option<RenderMeshCustomizations>) -> &String {
    match &self.material {
        MaterialSelector::Constant(material_name) => material_name,
        MaterialSelector::MapSkinColor(msc) => customizations
            .and_then(|c| c.skin_color())
            .and_then(|s| match s {
                SkinColor::Dark => msc.dark(),
                SkinColor::White => msc.white(),
            })
            .unwrap_or(&msc.default_skin),
    }
}

A RenderMeshCustomization can be merged with another RenderMeshCustomization in such a way that is a customization is defined inside one it will overwrite the other.

This will be the foundation for letting players select customizations for their character and having that override the defaults for things such as their skin or hair.

Skills Interface Foundation

I added a few data structures for storing skill levels and XP on the server side as well as a new data structure for storing skill information on the client side.

There's now an interface that displays this information.

Skills interface Will need polish, but the plumbing for display skills is in place.

Approach to Writing Quests

I Landed on a new system for quest writing.

Write the quest and the integration tests using placeholder dialogue that describes the gist of what is being said, then when everything fits together nicely I can replace the placeholders with real words.

Likely obvious to an experienced writer, but it sure wasn't to me.

Game Editor Progress

Made more progress on the Metal 3d API renderer, which is powers rendering for the work in progress cross-platform game editor on my MacOS machine.

I'd say that there is around 20 or so more hours of work to catch the MetalRenderer up to the WebGlRenderer, including time to refactor some of the rendering assumptions that were geared towards the WebGlRenderer that don't translate well to modern graphics APIs such as Metal.

Shadow test Next up I'll be getting the shadow render tests passing for the MetalRenderer.


I added a new shader to the MetalRenderer for vertex skinning of meshes.

Editor skinning I added skinning to the Metal 3D API renderer, so no more T-poses in the editor. Text and UI still don't render properly but I haven't looked into it yet.

Along the way I split out some of my code for managing allocations of GPU buffers into a platform agnostic re-usable set of traits and data structures.

It's fairly simplistic for now but will continue to evolve over the years as I run into more issues.

For example, this week I added an ItemAlignment::{SizeOfType, Constant(u64)} enum to control the alignment of entries in a buffer of data (such as a shader uniform objects). This was added because Metal expects offsets to be 256 bytes when using the constant address space to buffer objects.

Looking forward to learning more about GPU workload focused memory allocation over the coming years as I run into new problems and needs.

Other Notes / Progress

  • In 075 we moved from defining tiles as blocked to defining just the borders as blocked. This caused an issue where you could not interact with an entity that had all 4 sides of its tile blocked. So I've changed the system to allow marking both the tile as well as all four sides as blocked.

  • Fixed dual quaternion linear blending which fixed the gnarly artifacts when transitioning between two different animations.

  • In 071 I introduced the interactive sequence graph smoke tests. Added more of those this week.

Next Week

The most important thing to get done this week is my plan / design document for the direction of the game.

In the meantime I'll work on the TutorOfScience quest that I was supposed to write roughly 2 months ago. We live an we learn.


Cya next time!

- CFN