Sandbox Logo
19 December 2020

December 2020

This month we mainly worked on UI, physics constraints, shaders, inventory, weapons and viewmodels.

I don't think parenting ever worked how you expected in GMod. It definitely wasn't as easy/transparent as it is in other engines. 

So I've taken extra steps to make it easy to use this time around.
You can view a bunch of the tests I was doing at the time over here.
To make content and game creation as flexible and powerful as possible, we are going to allow creators to make their own custom shaders and expose them in any way they want, may it be a compute shader, a postprocessing effect, a glowing flame for a knight's sword or any other kind of special effects.

We expect addon creators to compile these offline and include them much like they'd include texture or models in their addon packages. 

Improvements since Source 1

Shader workflow in Source 1 was a big mess, each shader having a lot of boilerplate that would need to be filled, you'd need to correctly manually bind each register bidirectionally between the render code and the shader code, even if you'd try to improve the description of the idea of what each register should be bound to, it was still a chore. 

Valve has improved this a lot in Source 2, adding a lot of near zero cost abstractions to make everyone's life easier but it still requires a lot of meddling around with external command line tools which makes parts of the workflow feel like a deja-vu, we've got much of that solved.

Making things easier

I wanted to do everything possible to make any kind of shader authoring as streamlined as possible, this includes any facilities on manipulating and reloading shaders, without ever touching anything besides Met and your favourite code editor.

I have reworked some of the code in the Source 2 engine to add support for loading and parsing shaders from our addon system, compiling them ingame, among other iterative improvements, and I made sure to add all those quality of life improvements to the editors.

All shader workflow is a closed loop now, from creation to distribution.

Save it in your editor and the changes you've previewed are instantly applied back in the game, if you are working on a new file that hasn't been compiled yet, it'll show on the shader list and compile itself when you load it at the first time.

You can compile shaders using a wildcard to batch compile a bunch at once too.


Oh yeah, Source 2 uses a new shader source format, on the core it's still HLSL code, only pre-processed into their syntax and exposing a lot of helpers.

It describes all functionality needed in the shader, encapsulating all shader programs into one tidy source, including parameters that you will set in the editor, and such readily appear there as a variable to be set per-material or dynamically in C# code.

A lot of how Valve has set up their shaders reminds me of how other game engines describe the idea of a base material to be expanded by their Material Editor, except describing such in code rather than in a node graph which is incredibly elegant.

For example I have toyed with it for building a test Vertex Shader for a blob mesh
You can expect the same readiness when constructing your shader expanding on PBR parameters for example, and this can be expanded infinitely, we're still evaluating how we can make it more accessible.

dynamic Expressions

Even though it's not ours I think it's be nice to mention the Dynamic Material Expression stuff in material editor and how you don't need to make a shader to do simple stuff like flashing a colour or dynamically modifying a parameter.

This is certainly something we're going to expand more upon in the future.

PostProcessing effects

I think one of the most immediate things people will use custom shaders for is for custom Post Processing Effects, and will be exposed as simple as possible to the player without losing it's power, instead of passing a complex list of parameters and commands, for most cases you only need to expose which material you want to render in a list of passes.

Post Processing effects can range from simple vignette effects to some crazy ideas you might find in something like Reshade, only better because you actually have control of everything.

The road from here

There's still a lot in the pipeline we can expose to players, and it should be done the smartest way possible.

Our rendering pipeline is malleable enough for doing any expansions on the long term without reengineering much of it and keeping things compatible.
In Source1 all of the constraints were kind of hidden from us. The only way we could properly access them was through entities. This meant that every constraint you added in GMod was actually an entity.

This time around, constraints can be added and accessed directly from the physics world. This makes the API nice and clean.

We use a builder design for the constraint API, here's an example:
You access the builder for the constraint you want to build, the builders have all the functions that can set all the properties needed before creation. Properties can also be set after the constraint has been created.

There are 7 main types of constraints but on the C# side we can create pseudo constraints using a base constraint type. A good example of this is making a hinge which uses a revolute with limited default values.

You may be thinking, is the weld constraint wobbly like jelly or stiff and solid like you've always dreamed it would be?

The weld joint isn't perfectly rigid so should only be used as a locked constraint.

For real welding we need to attach entities and merge their collision shapes, this is already looking promising.

Weld joints still have some cool uses, they can be used to make a physgun!

It's important to me that we don't make assumptions about which games will be made with s&box. I shouldn't hardcode a HL2 style slot and bucket inventory system and expect every game to use it.

So I looked at how the inventory system worked in Source 1 and broke it down into a generic system that can be easily adapted into a bunch of different scenarios.

Every entity has a Inventory member, which is null by default.
If it's not null, when the entity gets a new child, or a child is removed, it tells the inventory about it.
So now whichever class you set as your inventory is in charge of this logic. I've coded a simple inventory class that I'm guessing will meet 90% of people's needs, but all that code is addon side, so you can look at it, derive from it or just make your own class.

The inventory system is responsible for two primary things.. keeping a list of entities in the player's inventory and handling the active entity. The Active entity is the one which is in your hands, the one that is selected.

In GMod the weapon system was based on CBaseEntity in the engine. In s&box we don't even use the native classes. In s&box a weapon is just an entity with a bunch of interfaces implemented.

The weapon system is actually stupidly simple. It's based on the ActiveEntity system.

This function is called during the player's tick. If you have an ActiveChild and it's an IPlayerControllable, it'll also tick.

So on your gun entity you can do stuff like this.
And just for fun, here's the box shooter code

I really struggled with viewmodels. It seems overly complicated to me that the viewmodels are serverside, and they always exist, and we're changing the model when we change guns, but they're predicted and only ever seen by us.

So after a few days of trying to make it make sense, I made them totally clientside. My reasoning is that they should behave like a HUD, like they're drawn on the screen.

This has made things a billion times simpler.. and although there are things that I know we'll have to work around, it works exactly how I'd expect it to.

Please excuse the fact that you can see the shadow of the third person gun on the viewmodel.

Here's some code stuff.
There's nothing magic or hidden about BaseViewModel, it exists in addon code. The property EnableViewmodelRendering can be set on any model and it basically marks it as a viewmodel - which means it'll only render if you're in first person and looking through one of its parents. It'll also render in the viewmodel path which stops it clipping through the walls.

The only thing really special about BaseViewModel is that I wired it up to get a call when we set the camera position, so it can position itself at the player's eyes.
Remember that blog where I talked about how great Panorama was and how much I loved it. Well this month I ripped Panorana out and replaced it with a homebrew solution written in c#.


I know myself. I know that I would have eventually done this. It's easier and better to rewrite it in c# than to fix the stuff that annoys me in c++.

WHAT'S changed

The layout system is now based on yoga, so it's all flex based. Panorama has non standard layout system, which is kind of flexxy, but it's not as powerful/complete as proper flexboxing. (Anyone that asks for grid support can get fucked imo)

The CSS transitions in Panorama are wired backwards, so if you have a transition set on a :hover it uses that transition when you're not hovering. There's a bunch of small stuff like this that on their own are nothing, but make you stumble when you're working with it, and they all start to add up.

Things like pointer-events and cursor work.

In general the improvement is to make everything more in line with how you'd expect real stylesheets to behave. We can add some special magic to the sheets, like sounds and different rendering modes, but generally if you're using plain css it should work like you'd expect.

Forced Binds

Doing this has had the obvious benefit of forcing us to bind a bunch of stuff to render and get/control input. The more stuff we move to the addon realm the more stuff gets bound.. which means more functionality for modding.
So lots of pretty low level foundational stuff this month. I'm really happy with our velocity. It feels like every stride forward we make also increases our speed by 20%. 

Everything we implement makes implementing a bunch of other stuff a million times easier.

My primary goal over the next month is to get it playable. It's playable right now but there's some real rough edges that I need to round off. Once it's playable by us internally we can evaluate whether it's worth starting to give access out to some of you guys.