1. FOUNDRY
  2. News

FOUNDRY News

Dev Blog #30 | Foundry Fridays: Everything in Moderation

A First look at the Foundry Mod Kit

Today I wanted to talk about one of the ways that you’ll be able to create mods in Foundry. This talk will be mostly focused on how you can script new components and systems to enhance existing buildings with new functionality.

More specifically, we’ll be looking at taking one of my favorites: The Logistic Container



And turning it into: The Planter Box



Modest Beginnings

To make this new planter building, I don’t want to start from scratch. So the first thing I’m going to do is open the Foundry Asset Library and create a clone of the Logistic Container I:



This sets me up with a copy of all the files I need and after buying a cool set of plants from the asset store, I can quickly setup my new planter box prefab:



It’s probably worth mentioning at this point that I’m a programmer by trade so if you’re looking at this planter box and thinking “this doesn’t look too good”, you would be correct. That’s ok though because I wanted to make sure I went through all the steps of creating a mod myself, similar to how awesome people like you will be doing it at home.

Now on to the scripting!

One Component at a Time

The first thing I want to do is create some components to store all the data I’m going to need. If you’re familiar with Unity/MonoBehaviours, these are quite similar:

[expand]

// A component that represents a planter
// box which can have a plant growing in it
public class PlanterBox : IModComponent
{
// The plant growing in this planter box
[Save] public ulong plantId;
}

// A component that represents a plant that
// grows in a planter box
public class Plant : IModComponent
{
// How many sim ticks till the plant is fully grown
public int totalGrowthTimeInTicks = 100;

// What item template spawns this plant
public string itemTemplateId;

// How long has this plant been growing for
[Save] public int currentGrowthInTicks;
}


[/expand]

Once you've created the script for your components, we can go ahead and add them to our Planter Box prefab:



Now on to the systems!

Systematic Behaviour

Systems are how you can add new behaviour to Foundry. First let's start with our PlantSimSystem. The only thing this system does is slowly grow our plants over time:

[expand]

// A system which handles the growth of plants
//
// [AddSystemToGameSimulation] automatically starts
// this system when a game is started

[AddSystemToGameSimulation]
public class PlantSimSystem : SystemManager.System
{
// [LockstepTickRate(60)] means that this system is updated every
// 60 simulation ticks. It's an easy way to reduce the cost of your
// update.
[LockstepTickRate(60)]
public override void pstLockstep_tickUpdate(ulong tickId, DesyncHasher hasher)
{
// Here we iterate over all the Plant components
foreach(var kvp in Mods.Components.getAll())
{
// First we get the plant component
var plant = kvp.Value;

// Then we check to see if the plant is fully grown,
// and if so, then we skip the rest of the update.
bool isPlantFullyGrown = plant.currentGrowthInTicks >= plant.totalGrowthTimeInTicks;
if (isPlantFullyGrown) continue;

// And if we finally get to this point,
// then we increase how many ticks this
// plant has been growing for.
plant.currentGrowthInTicks++;
}
}
}

[/expand]

Next let's handle the PlanterBoxSimSystem. For this there are two things we need to take care of. We need to gather a mapping of item templates to plants so that we know what plant to spawn when we place an item into the logistic container and then we have to do the fun bit which is actually spawning a plant once we've found a valid seed in store:

[expand]

// A system which handles the spawning of plants
// when the appropriate item has been placed in
// storage.

[AddSystemToGameSimulation]
public class PlanterBoxSimSystem : SystemManager.System
{
// onAddedToManager() is called once when the system is first created
// and added to the SystemManager
public override void onAddedToManager()
{
// On startup we create a map of ItemTemplates->Plant Prototypes
createItemTemplateToPlantPrototypeMap();
}

// create the ItemTemplate->Plant Prototype Map
void createItemTemplateToPlantPrototypeMap()
{
// Iterate all plant prototypes.
//
// A prototype is similar to a Unity GameObject.
// It has a list of components and you can use it
// to spawn entities in the simulation.
foreach (var plantPrototype in Mods.Prototypes.getAllPrototypesWithComponent().Values)
{
// get the Plant component
var plant = plantPrototype.getComponent();

// get the associated ItemTemplate
var itemTemplate = AssetManager.getAsset(plant.itemTemplateId);

// Map the item template to the specified plant prototype
plantPrototypes[itemTemplate] = plantPrototype;
}
}

// Update the simulation
//
// [LockstepTickRate(60)] tells the system manager to only update this system
// every 60 ticks.
[LockstepTickRate(60)]
public override void pstLockstep_tickUpdate(ulong tickId, DesyncHasher hasher)
{
// Iterate all PlanterBox components
foreach(var kvp in Mods.Components.getAll())
{
// Get the planter box component
var planterBox = kvp.Value;

// If the planter box already has a valid plant, then
// skip the rest of the update
if (EntityManager.isValid(planterBox.plantId)) continue;

// Get the planter box id
var planterBoxId = kvp.Key;

// Get a handle to the planter box inventory.
//
// Handles are used to interface with the native Foundry
// entities that exist in the native C++ plugin.
var planterBoxInventory = new StorageEntityHandle(planterBoxId).inventory;

// Iterate all items in the planter box inventory
foreach (var item in planterBoxInventory.items)
{
// If the item is not a plant, then skip it
if (!plantPrototypes.TryGetValue(item.template, out var plantPrototype)) continue;

// Remove the item from the inventory
planterBoxInventory.tryRemoveItem(item.template.id);

// Spawn a new plant from it's prototype
var plantId = Mods.Entities.spawn(plantPrototype.id);

// Have the planter box keep track of it's newly spawned plant
planterBox.plantId = plantId;

// Trigger an event that will be handled by the PlanterBoxRenderSystem
// or any other mods who want to know when a plant has been planted.
trigger(new OnEntityPlanted
{
plantId = plantId,
planterBoxId = planterBoxId
});

// If we've found a valid plant then we
// can exit out of this loop
break;
}
}
}

Dictionary plantPrototypes = new Dictionary();
}

[/expand]

Finally there's one last thing we're going to do which is to make a PlanterBoxRenderSystem which will spawn the plants once they get planted. For this we need to handle when the planter box is streamed in/out and when a new plant is planted. We're also going to scale up the plants as they grow.

I know this has already been quite technical up till this point and though I can't promise you that this is going to be any less technical, I can promise you that this is the last piece of code.

Promise.

[expand]

// A system which handles the rendering of plants when they're planted in a planter box

// [AddSystemToGameClient] automatically adds the system to the game except when
// the game is running in dedicated server mode.

[AddSystemToGameClient]
public class PlanterBoxRenderSystem : SystemManager.System
{
// Handle when the planter box GameObject is streamed in
[EventHandler]
public void handle(OnEntityStreamedIn evt)
{
// Keep track of the newly streamed in GameObject
streamedInPlanterBoxes[evt.entityId] = evt.cmp;

// Check to see if there's a plant we should be
// visualizing
trySpawnPlantVisualizer(evt.entityId);
}

// Handle when the planter box gets streamed out
[EventHandler]
public void handle(OnEntityStreamedOut evt)
{
// Remove the streamed out planter box.
streamedInPlanterBoxes.Remove(evt.entityId);

// Get the planter box component
var planterBox = Mods.Components.get(evt.entityId);

// Check to see if there's a plant GameObject that needs to be destroyed
if(plantVisualizers.TryGetValue(planterBox.plantId, out var plantVisualizer))
{
// If there is, then destroy it
GameObject.Destroy(plantVisualizer.gameObject);

// And remove the reference we had to it
plantVisualizers.Remove(evt.entityId);
}
}


// Handle the case where we've planted a new plant
[EventHandler]
public void handle(OnEntityPlanted evt)
{
// Spawn the plant visualizer if necessary
trySpawnPlantVisualizer(evt.planterBoxId);
}

// This handles spawning the plant GameObject.
// It will only be spawned if there's a valid plant
// and if the planter box is streamed in.
void trySpawnPlantVisualizer(ulong planterBoxId)
{
// If the planter box isn't streamed in, we can just skip the rest of this function
if (!streamedInPlanterBoxes.TryGetValue(planterBoxId, out var planterBoxGO)) return;

// Get the planter box component
var planterBox = Mods.Components.get(planterBoxId);

// If it does not have a plant then just skip the rest.
if (!EntityManager.isValid(planterBox.plantId)) return;

// Get the plant prefab to instantiate
var plantPrefab = Mods.Entities.getEntityPrototype(planterBox.plantId).moddedGameObject;

// Instantiate the prefab
var plantGO = GameObject.Instantiate(plantPrefab);

// Place the prefab at the plantLocator position of the planter box
plantGO.transform.position = planterBoxGO.plantLocator.transform.position;

// Keep track of our newly spawned plant.
plantVisualizers.Add(planterBox.plantId, plantGO.gameObject);
}

// Handle the updating of the plant scales
//
// [RenderTickRate(1.0f)] means that this function will only be updated once
// a second

[RenderTickRate(1.0f)]
public override void LateUpdate()
{
// Iterate all plant visualizers
foreach(var kvp in plantVisualizers)
{
// Get the plant id
var plantId = kvp.Key;

// Get the plant component
var plant = Mods.Components.get(plantId);

// Calculate the new plant scale
float plantScale = 0.1f + 0.9f * (float)plant.currentGrowthInTicks / (float)plant.totalGrowthTimeInTicks;

// Adjust the plant's scale
kvp.Value.transform.localScale = new Vector3(plantScale, plantScale, plantScale);
}
}

// PlanterBox id to GameObject
Dictionary streamedInPlanterBoxes = new Dictionary();

// Plant id to GameObject
Dictionary plantVisualizers = new Dictionary();
}


[/expand]

And that's it! That's basically all the code necessary to have a plant, place it in a planter box and have it grow over time, all done inside of a mod and as a thank you for sticking around this far, here's a look at the PlanterBox mod working along side a Sprinkler mod that was made from one of the in game Pumps:

[previewyoutube][/previewyoutube]

Future Work

This is was just one of the ways that we're looking at making Foundry moddable. We're also working on Harmony support for those of you who want to hook into any C# functions we have. And for those of you who who are just looking to add/tweak/create crafting recipes/research/buildings, we're working on making it so you can do that by modifying data files, no programming necessary!

That's all I have for this Foundry Friday. Once again...

Thanks for listening!





Dev Blog #29 | Foundry Fridays: Underground Mining Rework

Hello everyone,

Today it’s my turn to write our FOUNDRY Friday blog post and I will cover the underground mining rework in detail. Our previous posts were more on the technical side, so this one will be about gameplay and presenting what’s coming once we release the game in Early Access on Steam.

[h3]Why the rework?[/h3]
Before I cover the new system I want to talk about the old version of underground mining and why I wanted this change and what I felt needs improvement. Previously we had what we called world layers, different voxel blocks depending on the depth. Regarding depth, they had a bit of a noise-based variation but roughly they were locked in place and filled the world along all cardinal directions. So wherever you dug down, you found the same voxel types (ores) at the same depths.


Some of those voxel layers could be mined with a rail mining depot, a machine that horizontally digs endlessly in one direction, excavating everything in its path. So the workflow for mining ore was to dig down at the right depth level and place as many rail miners as you needed. As they were digging forever, every railminer was an infinite source of ore.


Here are the key problems I identified:
  • Infinite ore without cost: Finite ore sources always felt wrong to me as it means you need to dismantle everything once the ore is gone, however having them be infinite isn’t right either, it is too simplistic.
  • No exploration for ore: All you had to do was remember the according depth level per ore and then dig down with an elevator. Ore was always guaranteed to be there. In a world where you can dig and explore, this feels like wasted potential.
  • Unwieldy system: The overall system felt cumbersome, we had different rail miner items to assign per depot, they needed a constant supply of new tracks, once they dug far enough they might block large areas of the world and many more not so ideal features. I just felt that this isn’t as polished as it could be.

[h3]The new Underground Mining[/h3]
In the new system we have ore veins randomly generated throughout the world. An ore vein is a large vertical shape made out of indestructible blocks. They range from the surface till far down into the ground.


To find such an ore vein, you can use our new ore scanning tool. Using the tool will start a scan ping which expands through the world, notifying you about all ore veins in the vicinity. This tool is also able to track regular ore patches for your drone miners. This part is still work in progress but we plan to let you configure what you’re looking for, at the moment it scans for everything near you.



Once you have located an ore vein, you need to dig to the mineable parts, they are also highlighted by the scanner. The amount of mineable parts is randomized, so some ore veins have more yield while others have less.
Additionally we introduced a new concept, called ”Mining hardness”. At the start of the game you can only dig down until you hit a certain rock layer which is too hard to mine. By progressing through research you will be able to dig down further, allowing you to access more mineable parts per ore vein.

Once you excavated a mineable part, you can place a machine very similar to our original rail mining depot. It will expand rails and start digging into the ore vein. On its way into the vein all the blocks will yield a large amount of ore. For me it was important to keep the digging process, and let the machine drill into the vein, showing some visual voxel changes and progress.

[previewyoutube][/previewyoutube]

After a certain distance it will hit what we call the ore vein core. Those blocks are indestructible again and don’t yield ore the same way as the previous ones, however your mining depots can continue mining as long as an extra item, a so-called mining fluid, is supplied. While in reality almost exclusively used for shale gas extraction, we are trying to emulate a process called “fracking”, extraction of resources by cracking up rock formation by pressure. The mining fluid isn’t supplied by the mining depot itself, but by a large unique (per vein) building that can be placed on top of the ore vein, the Fracking Tower. The Fracking Tower is a modular building and making it larger allows it to support a larger amount of mining depots.


Some ore veins are under water, causing the fracking tower to look like an oil rig.


Given its size and potential difficulty to place it on rough terrain we tried to make sure to take some actions making it easier. Most notably the struts are automatically built once the building is placed down, but I’m skipping the details for this post.


[h3]Conclusion[/h3]
Let’s have a look at the identified issues before and how they are solved:
  • Infinite ore without cost: Now you get the best of both worlds, they are finite at first and easy to use, and with some additional effort (building the Fracking Tower and supplying it with mining fluid) you can convert it into an infinite source of ore.
  • No exploration for ore: You need to explore the world and use your scanner to find ore veins, once you locate a vein the scanner helps you find the mineable parts. This requires a bit of exploration and digging up the right spots, but it’s straight-forward enough to not be frustrating. It requires more digging than before, to compensate we made sure that explosives are available before accessing the first ore vein.
  • Unwieldy system: The rail mining depot has been greatly simplified, gone are the rail miner items and the need to supply tracks. You need to supply a mining fluid once you hit the core, but instead of requiring to lay pipes far into the underground we made this more accessible with the Fracking Tower on top. We made sure all info texts make the system easy to understand and debug in case of issues, also we disallow invalid placement of any of the related buildings.

I’m sure it will need a little more polishing but looking at the points above I’m very confident that the new system is an improvement and I can’t wait to hear your thoughts and in-game-feedback once it’s on the experimental branch.

That’s it for today's FOUNDRY Friday post. See you in two weeks!
-mrmcduck

Dev Blog #28 | Foundry Fridays: Light at the End of the Tunnel

Light at the End of the Tunnel


Hey Founders, My name is Mark and I'm thrilled to continue our series of ‘Foundry Fridays’ developer diaries, where we - the team behind Foundry - get to pull back the curtain and give you a glimpse into our process and progress.

Each entry is a deep dive into a specific aspect of the game, ranging from art, design, user experience, and more. Today I’ll be speaking on my role as a shader specialist on the Foundry team, so you can expect today’s entry to be more on the technical side. If you enjoy these more technically focused posts, please let us know on our Discord channel, and we'll to create more in the future.

In this week's edition, we're shedding some “light” on an intricate aspect of our game - the rendering of our volumetric lighting. Volumetric ambient lighting creates realistically dark caves and crevices in Foundry, but we've hit a bit of a snag. An annoying blue fog shows up when our tunnels get too large. In order to explain how we tackled the blue fog issue, we need to give you a little backstory on how our game's lighting works.

Get ready to step out of the fog and into the light!



Where Does Light Come From?

A game like Foundry is rendered with something called “deferred” lighting. In a deferred rendering project all geometry is written to a series of graphics buffers (GBuffers) that contain information like, albedo (color), specular, glossyness and normal. After all objects are drawn we run a single pass over the buffers to calculate the direct lighting information for each pixel. This creates nice highlights and directional lighting information that we are used to in modern games. But it doesn’t accurately simulate how lighting works; for that we need something called “bounce” (or ambient) lighting.



Volumetric Bounce Lighting

When a light ray hits a surface it reflects and scatters around the area. This bounce is what fills a real room with light when you turn on a simple lamp. The deferred step I mentioned before does not handle any of these “bounces” and results in areas not directly lit by lights being pitch black. If you wanted to render all these bounces you would be doing something called ray tracing- a feature that is prohibitively costly performance-wise.
Normally games fill in a best guess for the bounce lighting with an ambient term, or even by baking the bounces into something called a lightmap texture. But Foundry is a fully dynamic world so this just won’t work. We instead create our own approximation of a volumetric ambient lightmap that updates in real time as the world changes. This structure is computed on the CPU and then the volumetric texture is shared with the GPU. We can simply use this value per-voxel to mask an ambient term to ensure caves are properly darkened while keeping our outdoor scenes nice and bright.

Fixing the Blue

If you went deep into our itch.io demo you would know that large caves had a sky colored fog in the distance. This is because, although the lighting system knew about our volumetric ambient data- the fog did not!


To solve this, when we compute the fog value at a given location we mask it by the volumetric lighting. This helps keep the darkness “dark”. But when you make a truly MASSIVE cave it still breaks because cameras have something called a “clip distance”, a distance away from the camera where no more content can be rendered. At this distance we no longer have any cave walls to sample the ambient lightmap at- just a big void. The solution here was to take the ray projected by the camera to that pixel and “clamp” its length by the max draw distance and sample the lightmap at that location. Then if this value is fully bright, we draw the regular sky/clouds/fog, and if it’s fully dark we render black… and voila! We have massive (dark) caves:


Obviously this system is more complicated and has many more parts than what we spoke about here- but this was a particular improvement that was always on my list to fix, and I think it improves the experience for those players hoping to spend their time in an underground voxel factory.

Until next time Founders.
-Mark

Dev Blog #27 | Foundry Fridays: Adventures in Compute Rendering

Welcome to ‘Foundry Fridays’

Hello! My name is Yog(Cheerio) and I have the honor of writing the first ‘Foundry Fridays’ dev diary, a place for different members of the Foundry team to share what they’ve been working on.

Each post will dive into a different area of the game, be that art, design, ux etc. As I tend to do a lot of technical work on Foundry, today’s post is going to be fairly technical. Let me know on our discord if you guys like these types of posts and I’ll try to do more of them in the future.

For today’s post, we’re going to talk about one of the systems I’ve been working on to help optimize our CPU usage.

[h2]What problem are we trying to solve?[/h2]
Foundry is an online, deterministic, procedurally generated, voxel based, infinite world where players build massive factories that grow to be hundreds of thousands of objects that need to be rendered to the screen. Basically a full bingo card of game development challenges.

One of the big costs in rendering so many objects is gathering all the objects that need to be rendered, determining which objects are visible and then grouping similar objects together in batches and then sending all of that data to the GPU.

Since Foundry is made in Unity, most of this work is done on the CPU. On top of that, most of this happens on the same thread as other game systems such as physics/particles/animation/audio/factorysimulation/ui etc.

That being said, if you’re willing to get your hands dirty, Unity gives you the tools to move a lot of this work on to the gpu using what we’re going to call Compute Based Rendering.

[h2]Compute Based Rendering[/h2]
Compute based rendering allows us to move a lot of the rendering work that would typically be done on the cpu, to the gpu. Not only does this free up our cpu, it turns out this kind of massively parallelizable work is what GPUs are really good at, meaning we can draw even more objects then before.

[h2]Baby Steps[/h2]
Before I joined Foundry, I did a lot of rendering/optimization work on games like Oxygen Not Included and Dead Rising but this was going to be my first time working with compute shaders so I wanted to start with something fairly simple to prove that this idea would actually work.

My first step on my compute rendering journey was to take one of the awesome new grass tufts made by our new art director (Jason) and make myself a test scene in Unity. The next thing I did was to make it so that every frame, all objects that get streamed in have their position/material info uploaded to one big buffer on the gpu. That combined with learning how to use Unity’s Graphics.DrawMeshInstancedIndirect api was enough for me to get something on screen and see if this experiment was going to be worth it.

[h2]First Results[/h2]
My first test was to compare the performance of drawing 250,000 patches of grass using traditional GameObjects to my new compute based rendering solution. As you can see by the image above, my test scene’s framerate went from 7fps to 197 fps. So far, this is looking very promising.

What’s even more exciting is that even after adding things like visibility tests(frustum culling), the cost on the gpu to iterate through all the patches of grass and copy them to a separate compute buffer for rendering is under 0.05ms which is incredibly cheap.

At this point I was quite optimistic however it was now time to try out something a little more complicated…

[h2]Not the Flop(s) I was looking for[/h2]
For my next test, I wanted to try rendering an object with a hierarchy(an object made up of multiple parts). The new pillars in Foundry are made up of a base model, a middle piece and a top piece. It took a little bit of work to handle uploading the hierarchies to the gpu but once that was taken care of, I was ready to gaze upon my vast sea of super optimized pillars only to instead be greeted by a number that brought sadness to my heart:

6fps.

Even though this was six times faster than Unity’s default rendering, I was hoping I would at least hit a smooth 30fps with the new system. I spent some time profiling both my code and Unity’s rendering and after a little digging I finally found the issue. The model was just a lot more detailed than the piece of grass I used in my previous test.

I then went and purchased an LOD generator from the asset store(PolyFew), generated some lods and went to work adding LOD support to my rendering system. Once this was all working, it was time for another test. I crossed my fingers and was met with a much better number which brought joy to my heart:


227fps! Basically the entire problem was that the gpu was struggling drawing so many vertices. By adding LOD support to my system, we can now reduce both the cpu and gpu cost of drawing so many objects.

And at last, we have finally come to the grand finale for today’s post:

[h2]Conveyors @ 160fps[/h2]
The final test I want to talk about today is the conveyor test. The conveyor is the most common object players will place in Foundry. In a large base, a player can easily place tens of thousands of them so it’s quite important that these be rendered as efficiently as possible.

I didn’t actually need to add any new features to my system so without further ado, here are the results we’ve all been waiting for:

After all that, 250,000 conveyors went from rendering at 3fps to 164fps which I am very happy with. This isn’t as big a win as with the grass pieces but that mostly has to do with an individual conveyor piece actually being more expensive to draw than a single piece of grass. Knowing this, there are a few more steps we can take to further improve conveyor rendering such as optimizing the shader/mesh.

[h2]Final Thoughts[/h2]
So far it looks like spending time investigating compute based rendering is going to be a win. There’s still more work to be done to support some of the more complex buildings such as the animated ones but every time we convert one of our machines to use this new rendering system, we should see a similar jump in performance.

There’s also a lot of room for performance improvements to the system such as occlusion culling, tight shadow frustum culling and better sorting. All of which should help us see an even bigger jump in performance.

[h2]Till Next Time![/h2]
I hope you enjoyed our first Foundry Friday. We will be posting a new one every second Friday up until early access. If you have any questions, please hit us up on our discord.



Thanks for listening!

Foundry Status Update

Hello everyone!

Lots of amazing things happened during the past six months, so we wanted to give you an update about FOUNDRY and what is happening behind the scenes.

We have partnered up with a yet unannounced publisher, a move that allowed us to scale up our studio and increase the production quality of our game significantly. We were able to hire and increase our team size to about ten people. This allows us to work on more different features and most importantly spend more time polishing them.

It makes us excited to see that many of you are eagerly waiting for more information about our game, therefore we’re announcing that we will be posting a development update every second Friday, starting in four days. Those will cover a specific part of the game and give some detailed insight, the first one will be about some rendering performance improvements that are currently under development.

In case you want to follow our development even closer you can join our Discord where we show work in progress and more informal messages (like this message from Foundry's creator MrMcDuck) to our community.

See you soon,
the FOUNDRY-Team