Streamlining Indoor Environment Creation with PCG and Geometry Script in UE5 | Unreal Fest 2024
By Unreal Engine
Summary
## Key takeaways - **PCG & Geometry Script for Rapid Indoor Environment Creation**: Aardman Animations developed custom tools using Unreal Engine 5's PCG and Geometry Script frameworks to overcome challenges of high content requirements and short production cycles for indoor environments in 'Chicken Run: Eggstraction'. [00:11] - **Designer-led Tools for Iterative Level Building**: The team prioritized building designer-led tools that allowed for rapid iteration and the ability to discard content quickly, enabling them to adapt to evolving gameplay needs early in development. [01:30], [03:13] - **Hybrid PCG and Geometry Script Approach for Walls**: Linear wall segments are processed with PCG, while curved segments are handled by Geometry Script, allowing for specialized treatment and efficient asset generation. [10:25] - **Data-Driven Customization with Data Tables**: Data tables are crucial for managing biomes, styles, and mesh attributes, enabling artists to easily customize and add details to generated environments without directly modifying complex PCG graphs. [09:14], [09:34] - **Geometry Script for Complex Curved Walls**: Geometry Script proved essential for handling curved walls, allowing for non-destructive workflows, dynamic mesh manipulation, and efficient baking to static meshes, outperforming spline mesh components for this task. [25:43] - **Scriptable Tools for Efficient Editor Mode Functionality**: Scriptable tools provide an easy framework for creating custom editor modes, enabling rapid development of highly usable tools for tasks like placing doors or generating floors, significantly speeding up workflows. [38:31], [41:45]
Topics Covered
- Unreal Engine 5 tools: PCG, Geometry Script, Scriptable Tools.
- Unreal Engine's native tools enable rapid, flexible level design.
- Geometry Script: Precise mesh deformation for complex curved walls.
- PCG's attribute partition enables complex, physics-simulated clutter.
- Scriptable Tools: Quickly build usable custom editor modes.
Full Transcript
Right, welcome to Unreal Fest, I guess, for everyone in here.
I'm Jon.
I'm Arkadiusz.
We make games at Aardman, which surprise, surprise,
we actually make games.
And we're going to talk to you today about indoor environment
creation in PCG, geometry script, and a few other other tools.
You going to click for me?
Yes.
So, Ark and I first work together at another studio.
We built a fairly comprehensive procedural pipeline
to build huge outdoor environments in Houdini and UE4,
and it was quite natural for us to pick up this project together
to work on more PCG stuff and tooling.
We are not a tools team.
We're quite a small studio, so this is really
like a few sprints' work between us, but it was a lot of fun, and yeah.
So Aardman is-- hopefully some of you know, Aardman.
It is a historic Bristol-based, in the UK,
animation studio, well known for Shaun the Sheep, Chicken Run, Wallace
and Gromit, that sort of thing.
And yeah, as I mentioned, we have a games team now,
which we founded a few years back, and we also
have an interactive XR team.
Our ambitions as a studio are to bring some of--
I think our IP is very cultural touchstones,
and we're trying to modernize some of the IP for new audiences
and build new IP.
The studio previously built 11-11 Memories Retold a few years ago,
which is quite an indie-style title.
And we restructured the studio for new projects,
and we started this project in 2022 with Netflix.
Scaled up the team to manage that, and that sort of leads into this.
Our current project is isometric-like stealth action puzzler.
Follows straight on from Aardman's recent film,
Chicken Run, Dawn of the Nugget, which hopefully some of you
have seen.
It is mobile first, PC, console coming later.
But it's been designed from the ground
up, well, to manage both, if you believe that's possible.
That's a different debate.
Yep.
We started on 5.1, so without PCG.
And currently on 5.3, which we are upgrading soon.
But we have got a major milestone at the moment, so that's on hold.
The project heavily uses the gameplay ability system, many other UE5
frameworks.
So that would be lots to talk about.
But on this talk, we're going to focus
on actually building invite-like tools
to build our indoor environments.
And this is interesting.
Ark can talk about it in a sec because a lot of the ideal example
situations of PCG are not that sort of environment.
We have short development cycles for this project, short development
cycle.
We did not understand gameplay when we started building tools, which
obviously, is difficult. And do we understand gameplay yet?
I think so.
Yeah.
The game just was not fun at the beginning.
But yeah, we didn't understand gameplay,
so we wanted to build tools that were really flexible that we
could iterate really fast on.
And we would kill our darlings, so to speak.
So we could just throw away as much content as we wanted
and iterate quickly.
So we've got a little short.
This is really early footage.
[VIDEO PLAYBACK]
[MUSIC PLAYING]
[END PLAYBACK]
Cool thanks.
Thanks a lot.
[APPLAUSE]
So obviously, that says PC and console.
Pretend that doesn't say that for this.
We're not video editors, so that wasn't possible.
So we are not trying to suggest any specific algorithm--
did I say that right?
Practiced this so many times.
--or approaches.
This is more of a quick review of how we've rapidly built tools
for our team with these frameworks.
So we have PCG.
It is almost an exponential learning curve--
a lot of depth, highly flexible, scriptable, procedural content
framework.
I actually had to read that.
It is data driven and getting more so.
Then we have geometric script.
That is a geometric processing framework--
fast and extensible implementation for many general mesh operations
and manipulation.
So if any of you used modeling mode before, it's something similar.
I mean, it's basically automated version of that.
It's a pretty good way to find out what features you can do in there.
Yeah, and it's really good for baking.
Last thing we're going to talk about scriptable tools.
So it's like a simple tooling to build quick editor mode tools.
And basically, encapsulated the life cycle of our tools.
So it was just very easy user interface
for our designers and artists to just have everything in one place
to play around.
So I have-- we have--
Sort of just saying artist and designers just play around.
Is that what you're saying?
Yeah yeah yeah.
So we have a couple of crowd questions.
Any of you used PCG already?
Oh wow.
That's actually quite a lot.
And if you use the geometry script?
That's a bit less, but yes, still good.
And any of you tried scriptable tools?
OK, a couple of people.
We have some people.
That's cool.
Yeah, that's good.
So obviously, there are many different ways
to build tools in Unreal Engine 5.
Our requirements were that we speed up level building,
encourage iteration and art and design.
They really wanted to have production-ready assets
during white boxing, and we know how all that goes.
But actually, these tools--
both, I mean, PCG geometry script and scriptable edit tools,
they seem to actually fit the bill.
It was very nice to actually upgrade stuff on the fly,
and people would see, actually, these white boxing using
those production ready assets.
So all of this fit the bill.
What we didn't want to use were third party stuff.
It's also for mobile performance, as well.
Yeah yeah.
For mobile performance, as well, yeah.
They actually-- they are really good for that.
We didn't want to use third party stuff because it was-- like Houdini
because it's just hard to maintain.
And then if you go into C++ or blueprints to build from scratch,
it's just a lot of boilerplate.
In summary, it was just really hard-- it is really hard
to convince people to try new tools.
So when PCG was introduced for the first time,
there was this one-hour presentation, one-hour video by Epic,
and there was like this five-second clip of this building.
And we were like, yeah, we want this, although it was just five seconds.
It was a nice validation, actually.
We talked about using it, but we thought,
would this map well to what we were trying to do?
Or were we just trying to fit a round peg into a square hole?
And yeah, this validated it a little bit before we dug into it.
Yeah.
I mean and this is, as well, fun for us
because we both work on procedural gen together.
And Jon's academic background is in these areas, as well.
And So our whole approach to building rooms is based around splines.
Our actor blueprints, they contain its own spline and PCG
component in which the PCG component has biomes,
different styles for each side; some niche features like, let's say,
mirroring; some custom clutter on walls and bricks to break tiling;
and we will go, as well, through custom spline work we did.
Lastly passages.
Because although we make rooms, we want to connect them somehow.
And keep in mind, as already Jon mentioned,
we didn't upgrade to 5.4 yet.
So all of this you see is on 5.3.
Here we are going to give you an overview of our room builder tooling.
What is really important is that we are using data tables.
As we were expanding the PCG graph, we
didn't want artists to mangle too much.
So we decided initially to have a data table which
stores all the biomes and styles, and we actually really did like it.
So for artists to just jump in, fill a style,
add extra details to what they wanted to use, it was just amazing.
And then we have a blueprint that covers corners,
curved meshes using geometry script which we'll get back to as well,
and our PCG graph component.
And although this looks maybe too simple,
but I'll actually show you how it looks in editor.
So this is our wall PCG graph.
What we do, because our blueprint actor
has one spline, so we get that spline.
We use our custom spline sampler, which Jon will get back to you later.
And then we just filter out which segments of the spine
are linear or curved.
The reason is we just want to process the linear ones with PCG,
and the curved ones with geometry script.
After that, we do a little bit of filtering.
So we either put our passages or some custom meshes, even some hero assets,
if I'm correct.
Then after this, once we finish, what we
are left with are just where the points for the walls are.
So yeah, you specifically filtered out passages, as well, by this.
Yeah yeah yeah passages.
Yes, I did say that, yes.
So this is actually the main core logic of this blueprint
where we take our points, we give them an attribute of which mesh
it should use, which material it should use.
And then in the mesh spawner, we check these attributes,
and it will just use a template to just spawn them.
But the reason why we have it twice, it's because we get the same points.
But we want it to build on both sides different styles
to have control over that.
So as we separate this out, we just make a copy of these points,
and we just rotate them by 180 degrees.
That's literally it.
From this point, we started adding more custom features.
So in this case, we have mirroring.
And what we do in the data table, we have an attribute to say,
do we want to mirror this specific style?
And if we say yes, what we decided to do
is we would just literally scratching the surface of PCG blueprint
elements.
So they felt very overwhelming and complex.
But in all honesty, once you understand
that there is a function called point loop body, which
basically loops over each point, what we discovered
is that if you break the endpoint variable,
you can get something called metadata entry.
And what this is, this is actually the index
you see in PCG of every point.
So what we did, we took a modular.
So we just filter every second.
And we have a select node to just say either we don't flip it, or flip it.
Put it back to the PCG point and just return it.
After this, we have couple, as well, extra just-added features
which was requested [INAUDIBLE].
So for the input, for our crown logic, we take our wall points
and whatever we filtered out previously
for passages and custom meshes, and we first check with our data table.
Does this specific style use crowns?
Then we add the mesh.
And then, because we didn't want this specific thing to be double sided,
we actually choose from which style we want the crowns to be spawned.
After that, we just transform them a little bit higher
on top of the walls.
And again, same logic.
Use the string to just spawn the mesh.
Next section is actually spawning clutter on walls.
So again, we just check.
Do you want it for this side or the other side?
We then give them some custom offsets if they require.
Then if they were mirrored, we actually unmirror them.
And after this, we have this nice spacing logic.
So we can either go with select points node, which is built in,
which will randomly choose a selection of points.
You can, as well, give you attributes from your data sheet
to be referenced so you can control it.
Or you can do another custom blueprint.
So this one is, as well, doing the same thing.
We are getting our points.
We are getting our index.
And instead of filtering every second, like for mirroring,
we are making a new attribute which you can easily build in.
And then we are just defining-- we are selecting, is this true or not?
And what this does, if it's true, the point will be kept.
If it's false, the point will be discarded.
So after that, as well, our data table
can control for the certain style how many walls we want.
And the next section is our specific clutter.
So in this case, artists really wanted to just
have clutters on specific style.
So we are checking--
Wall clutter.
Wall clutter, yes.
So our logic basically checks if the point is
supposed to use this specific mesh.
If it's using this specific mesh, spawn this type of clutter.
If it's this specific mesh, spawn this type of clutter, and so on.
And last and least, we have bricks.
That's not the saying.
I know.
So we have bricks.
And for bricks, there is a node called
input data table, which is different to the new load data table.
But what this does, you can actually import a specific set of points.
So we made a data table with hand-placed points
of typical brickwork.
You say we.
I definitely did not do that.
Yeah, you did.
Shh.
And then what we actually do, we take our wall points,
and we copied those for each wall point.
After that, we again do a random selection.
Could be defined by data table.
Could be defined even per blueprint.
You can override this.
And then we do some scaling.
After that, there is this interesting system for static mesh spawners
where you can actually make a select type--
a PCG mesh select weighted by category.
And what this does that if you have a certain category that it expects,
it will look through the array and see which meshes should
be spawned for this type of array.
So in our case we have a manor garden style,
and then it will just spawn these four meshes.
If we would have a different style, it would look for a different index.
And that's it for this wall.
So here, we have examples of what we did.
So we have the two-sided style.
The first image, you can see it, so we can independently control
which style we want on the wall.
After that, we have our custom overrides
for any kind of custom mesh you want.
It just checks the wall and checks what should be spawned instead.
Next is mirroring.
And yeah, this is a specific style where we just
did one side of the wall and the next one
is just flipped so it looks nicer.
After that is clutter.
As well, could be manually overridden if an artist is unhappy.
They usually were unhappy with how it looked.
After that is crowns.
So as well, again, we just have these extra meshes on top
to add a little bit of style.
And last and least are bricks.
So as you can see, this is like initial pattern.
And then we filter out, and we can scale them.
And yeah, the brick styling, which is pretty cool, in my opinion.
I'll briefly talk about the spline sampler that Ark mentioned.
So what Ark said at the beginning is, our room building tool
is essentially based around splines.
But really, it's actually based around the discretization of splines.
So we are generating PCG samples along the spline,
and we are then tessellating meshes along that spline
to make it look like a contiguous sort of room or wall.
And there weren't really any samplers that fit the bill for us
easily out of the box, so we built our own.
This is actually very simple.
You extend UPCG settings, which is a class in the PCG
framework and plugin.
And you can set up custom params.
That's quite straightforward.
And essentially, we are overriding the F subdivision step
sampler, which is--
yeah, a class which extends F step sampler.
Within the step function, that's where most of our logic
is for doing this.
So essentially, the normal input for a subdivision sampler
is to take N points and sample that uniformly according
to some rules along the spline.
But we wanted to do it according to a mesh.
So whatever mesh we would feed into it, or set of meshes,
we would get an output.
So basically, instead of computing the subdivisions-- sorry,
instead of passing the subdivisions per segment as a parameter,
we compute that inside this function.
And then we just compute the out transform,
including the distance along the spline
and the scale based on the mesh that we input.
And then fixing up indexing, which is a little bit
different to the normal subdivision sampler.
So that gives us a really simple way to just
automatically tessellate any spline with the mesh that we put in.
What we also can do there, we have a slightly more accurate curvature
computation.
We can compute some other stuff in the step sampler,
as well, which is quite useful.
These were all written as attributes.
As Ark mentioned, PCG is similar to a pipeline
where you're reading and writing from attributes
all the way through that pipeline to allow
you to do filtering and different stuff along that PCG pipeline.
So having our custom sampler means we can compute new attributes
alongside that.
And you can see the gif here.
By the way, this talk is basically just gifs.
So if you like gifs, then that's great, as you've already noticed.
If you don't, then this--
Or "jifs."
--not the talk for you.
Yeah yeah.
Cool.
Oh OK.
So yeah, we needed passages for our rooms
because we just need to connect them somehow.
So these were our doors, double doors, vents, and even artists have--
yeah, it just--
we add windows to it so we can manually control where they are.
We're like, yeah, yeah, OK.
The simple solution was just replace the PCG wall sample with a passage.
However, this restricted design because they just
couldn't tweak a little bit the doors to the left, to the right.
Also, it's super fiddly.
Yeah, it is super fiddly.
So instead, we just said, yeah, just place the passage way you want.
And what we did is we insert a spline control
point either side of the passage.
And because of how our spline sample works,
it will auto compensate for the measures
around it, as you can see in the second gif.
Like right at the end of the second gif, which you
have to wait for the entire time.
That was really well done.
Yeah.
There we go.
So that's the different approaches, that gif.
Just to let you know, that's not the approach of fitting a passage.
Like you just click and it just fits the passage.
So that's the work in progress.
We thought we'd briefly talk about the fact we use custom splines, which
I'm sure most projects do.
So we built our custom spline component,
and that obviously has spline metadata, and spline metadata
details.
The custom spline we used for various different things
throughout the project, in this case, we just embed--
or most of the use cases for this we do some interpolation, but most of it
is embedding metadata in our spline control points.
As probably a lot of you know, there's
a lot of boilerplate to implement this
in Unreal, which is a bit fiddly, but it's quite straightforward.
This allows us to have custom corners per control point.
So these might be like turrets, or they might just be tiny little things
like in this gif here.
And yeah, this is great because it breaks up the thing,
and designers can control whether this appears on every corner or not.
And sort of manually control the offset.
It's also sided.
So as you move the wall around, it generates the offset
on the right side of the wall.
We also have, again, spline component visualizer,
which we use for various functions throughout the project.
This allows, as I'm sure, again, everyone knows,
this allows us to decouple functionality
from the spline component or add easily functionality
to the spline component.
An example of this is we--
because we've got these large rooms and large walls,
and obviously, people don't use the tools as we intended.
So instead of building small little cute rooms and stuff,
and then having that, they build enormous walls,
like hundreds and hundreds of meters long,
and then complain that the pivot's too far away, et cetera, et cetera.
So we just have a tool in the spline component visualizer that allows
us to split a spline, split a wall.
And it just generates two PCG graphs, regenerates
themselves, inserts control points, and changes the pivots, and stuff.
And just as a right click menu.
So that's super good for of functionality there.
So quickly, how we handle curved walls.
So Ark mentioned curved walls in the PCG graph.
Our original idea to solve this seemed like a super cool idea.
It was a bad idea.
Yeah, it was.
We were convinced that this was the right idea, as many of us
are on projects.
And the idea, I think it sounds sensible,
but the idea was basically to handle it all in PCG and materials.
So we would just treat a wall segment as a curved wall segment.
We would sample it in the PCG graph as before, but then in material,
curve that mesh locally, that instance locally.
Did you mention-- so all these are obviously instanced static meshes.
And we would curve the mesh locally and in the material
to bend it to the spline, essentially,
using custom primitive data which you can send in PCG.
So the problem with that was that we would-- well,
it was quite complicated to do in the material.
It's quite complicated to get it to follow a spline locally.
You had to send quite a lot of data.
Mass was fairly messy.
And also, this has actually turned out
to be quite a lot of performance overhead
to do that much vertex transformation on the GPU every frame.
So our interim solution was to filter out the curve points.
So we looked at the attribute sheet, as Ark mentioned, filter out
any points that don't have negative or positive curvature,
and throw them away pretty much.
And then insert a spline mesh, and that would follow it.
We could set up the materials basically.
And that looks fine.
Actually, it doesn't look fine because you can't flip elements.
We can't have custom materials.
It's problematic.
And if the curvature of the mesh is high, we get lots of errors
because the spline mesh won't handle that properly.
So our solution was to build this in geometry script.
So we used the same approach with the spline mesh.
So we filter out the points, throw that away.
And then we feed it, so Ark then parameterizes this geometry script
class we have.
So that will still tile this instance mesh as we want across the curve.
It'll handle backwards and forwards, and it will curve it properly
to the spline.
But also, we can do things like subdivision,
and decimation, and stuff to make it curve really well.
So a single draw call is sufficient.
We have the dynamic mesh actor, which I'll
talk about in a second, which allows like a non-destructive workflow
with the PCG graph.
So it handled everything really nicely,
and I'll just dig into that a little bit now.
So this is an example here of--
this is a PCG wall graph in the gif, and just modifying
the curvature of the spline.
And this is generating a geometry script curved wall
on the fly, which is baked down to a static mesh on the fly, as well,
but is fully reversible.
A bit like-- I don't know if anyone's played around with Lyra.
They do something called cold storage where they maintain the references
for the dynamic mesh and the static mesh
so you can flip back and forwards when you're moving windows around.
So geometry script is based on dynamic meshes, which,
as you can tell, you can guess what the difference is there
with the static mesh.
It is based-- like in blueprint, you will extend a dynamic generator,
dynamic mesh actor.
And normally, instead of then tying stuff into a construction script,
you'd use the Unreal editor on rebuild generated
mesh function, which just performantly handles that
rebuild at runtime in the editor.
There's some other functions in this class
to handle copying back and forth between static mesh actors
and showing progress in editor if it's a large rebuild.
But broadly speaking, this is the sort of environment
you'll be working in.
And I'm just going to briefly talk--
so the core approach to this is that we
sample the curve segment of the spline, spawn static meshes,
and then deform them locally to match the spline.
Then we bake this down and release all our assets.
And I think-- here we go.
So I'm just going to show you this blueprint, as well, just
very quickly.
So essentially, this is our function, the rebuild generated meshes.
We do a bunch of allocation of our dynamic meshes here.
And then first of all, we handle the front side
of the spline to handle our inside meshes or outside meshes, depending.
So as you can see, the act is quite simple.
We have a front spline and back spline.
Then most of the functionality is in this build
and aggregate mesh function here.
Inside this, we essentially copy from the static mesh
into our dynamic mesh object.
Then we apply uniform tessellation here.
So this allows us to, obviously, really increase
the density of the mesh, which allows us
to then deform the mesh accurately, and then simplify it afterwards.
These are just standard geometry script functions.
So then this is a custom function here.
So the mesh sample spline into transforms.
So there are some spline sampling functions here,
but again, we wrote our own for this.
This allows us to sample the spline according to the mesh
size in whatever access we specify.
And from that, we get out these frame times
along the spline, and a uniform scale for the spline, as well.
This also allows us to adjust for where the pivot is on the input mesh
depending on how the artist has decided to build it.
And essentially, then, we copy this template mesh,
which we've subdivided once.
So we only subdivide once in every process.
We copy this to our spawning mesh, what we're going to modify.
This is a bit of a hack here just to handle
the mirroring Ark showed on the PCG graph
like where inside castle walls are flipped every other thing.
Hack yes.
Yeah.
Which was a hack in that, as well.
And this is where we-- oh, sorry, this is the interesting function here
where we deform the mesh to follow the spline.
I'm not going to go into it here.
It's a bit too much to go into, but essentially, it's
some fairly trivial linear algebra to follow the spline
to deform the mesh locally, scale it in X, as well, which is our axis
for our wall pieces, and to deform it locally to follow the spline.
Pretty much similar to the function used in spline mesh component.
Then we append it to our aggregated target
mesh, which is another dynamic mesh.
We set up materials, and UVing, and recompute our normals here.
We do this once afterwards, so we get smooth normals, obviously,
across the curved segment.
And then, so basically, we do that for the front side of the mesh.
We then have a custom function to reverse the spline,
do it for the back side of the mesh with a different source static mesh.
Then we aggregate these two meshes.
We apply some planar simplification.
The reason why we use the planar simplification here--
there's a few simplification methods--
is this allows us to give an angle for control,
which means we can almost enforce the quality of how
we're using the curvature.
So it allows us to have a minimum quality
there for that, which is useful.
Then we release all our resources.
We have this function here.
We don't use it here.
Like Ark actually calls this from the PCG actor, which also owns the graph,
but this handles the baking to a static mesh-- so the auto
naming, adding to source control, et cetera, et cetera, computing
collision, stuff like that.
So that's then called, and the PCG graph actor, that manages the state,
like cold storage back and forth between the dynamic meshes stored
here and the static meshes output from this process.
So that is a quick run-through of what we're
doing in the curved mesh stuff.
And here is just a quick thing.
This was a gif, but it was so hard to follow I couldn't describe it.
So I actually converted it to images, which
is a bit of a breath of fresh air.
So you can see on the left--
I'll go through this pretty quickly.
On the left, we have the normal mesh if we don't do any subdivision
and decimation afterwards.
And you can see, it's an optimized mesh designed for mobile.
These are all instances, by the way.
Well, no, they're not anymore because it's baked down.
So this is a normal wireframe.
You can see subdivided we have this very high poly count,
but we match the curvature.
You can see the errors on the leftmost curved wall.
Like if we don't subdivide first, it looks rubbish.
And then we can decimate afterwards.
So these functions here are all part of geometry script, right?
Like this is not our novelty, but it's just really useful
to be able to do this in geometry script really easily,
and it's much more efficient and useful to us
than using the spline mesh.
Quick other algorithm we built. So we built something
to enable-- like we have lots of non-rectilinear rooms,
or all sorts of odd shapes.
We have curved rooms.
And because you're looking top-down, you
can see the extent of all the floors.
So we built this quick tool to allow you to generate floors in the level
very quickly.
This is a custom algorithm, so I won't go through it in much detail
because it's less relevant to this talk.
But it does use geometry script, so we thought
it'd be interesting to mention.
This allows really quick automatic generation of floors.
You basically click, it uses a geometric floodfill.
It builds a data structure that allows fast neighborhood lookups,
so you can do more interesting stuff with that data.
But essentially, we build a boundary for this,
generate a poly from the boundary.
Most of the complexity in this algorithm is handling edge cases.
As you can imagine, with really concave polys and stuff,
it's pretty nasty, especially when we're
light tracing out into the wall geometry
to figure out where the floor is.
But basically, this is not super clever.
It just allows artists and designers to quickly build floors for a level.
So this is all built around geometry script, as well.
And we generate a dynamic mesh from this poly,
which again, we can use this--
it's almost a cold storage method to go back and forwards
between the static mesh and our dynamic mesh.
Except actually, in this case, we throw the dynamic mesh
away to be a bit more optimal.
If you sample the floor, as you can imagine, there is a grid,
so it's not completely deterministic.
But if you sample the point at the same place,
it's completely deterministic when we regenerate this floor.
So that's all we store when we go back and forth between this.
So you can easily move a wall and regenerate this,
and it doesn't use the same footprint in your project.
This obviously bakes all these static meshes automatically.
You can see the naming when you build this,
it generates this and writes it to the project structure, and et cetera.
It does a uniform remesh on it.
I think you can see it in the last thing actually.
You can see when it builds this mesh we do a remesh of it, which
is pretty optimal for the project.
You can see when I just dragged this up.
Yeah, gifs, gifs, more gifs, have to wait.
There we go.
So it produces a fairly simple thing.
We could probably make that more simple.
We also have this space delimiter thing.
So using this neighborhood structure, we can detect gaps.
So if someone set a room up wrong, it gives them a giant instruction,
like a massive red arrow to show them where they've screwed up the room
basically.
We also generate a boundary spline from this.
And the reason that's interesting is we
use it for room clutter and other things down the line.
The reason why we don't just use--
yeah, the reason why we don't have that boundary spline
anyway is because, as we said, we thought people would just
build rooms, right?
But actually, most of the time they just add a little bit on.
They build a wall, and then it creates a room implicitly
from the connection to other rooms.
So building this boundary spline is really
useful for later clutter generation, which we can talk about now.
Yeah.
OK, so our room clutter is very artist-led
because they just wanted to place everything by hand.
So we said, yeah, go for it.
And as they were building, we've noticed
that there were a lot of rooms where they were stacking boxes
on top and stuff.
And we were like, you know, you can just use physics simulation for this.
And they were happy with it, but then, we've
decided to actually go deeper into PCG.
So at this point, I've discovered this three dimensionality
of attribute sheets.
And what is really cool about this, you
could create a data table where you have each of your mesh
are stored in a different row with some data.
And then what you can do, you can load that data table into PCG,
and then use this amazing node called attribute partition.
And what it will do, it will take all these-- if you sample by the mesh,
if you use the variable from the mesh,
it will split this attribute sheet, each point,
into an individual attribute sheets, each one containing one point.
I can see this somehow blowing your mind even talking about it.
I mean, I'm just-- it was really exciting.
It really did blow my mind.
But what is cool about this, you can actually then
go into subgraphs, which are, as well, really, really useful.
So there is a node called loop subgraphs.
So what this does, this will actually loop independently
per attribute sheet you feed to it.
So in the gif you see, we are actually having a room.
We are filtering some logic.
But then once we filter through all of the clutter,
we put them all together so they are bunched up overlapping.
After that, we use a prune node-- yeah, prune node--
to just make sure they don't overlap.
We spawn them as static meshes.
Then we run simulation and just take the output.
And yeah, artists were quite happy with this.
Yeah, we kind of went rogue on this one.
Because normally, we're just following
whatever-- like reviewing whatever tools are artists and designers want,
and then talking to them about it.
But this one, we were like, I think this would be useful,
and we wanted to try out some new stuff.
So obviously, our art director would probably
look at what you've generated here and cry.
But it's a good starting point.
I think this is also on our previous procedural project
that we worked on in our last company,
having a basis for artists and designers
to start from is sometimes part of the goal.
So generating enough for them that it's not scary to start on this level
is often really useful, and I think that fills that.
And you can easily build rules for different rooms.
So in our case, we could build clutter for storage rooms,
build for kitchen, and stuff like that.
So now we're going to give a quick talk about scriptable tools, which
interestingly, was the least known about thing in this room, I think.
It's very simple.
It is a framework for building really trivial custom editor modes,
basically.
And basically, it allows you to--
as a programmer, you can separate concerns sort of encapsulate stuff
differently.
So it's almost, you can very trivially
follow some sort of MVC pattern with your tooling.
So you can build your tool completely separately,
and then build your scriptable tool, and just
wrap functionality with it, which makes it really useful to build.
Its modal, so it has a pretty well-managed lifecycle,
though I will go into that in a bit.
It's amazing for building quick stuff.
You can see we built a few tools here as an example
to show you what's going on.
The three main classes that you'll end up using
are scriptable single click tool, scriptable click drag tool, and--
I'll have to read this one out-- editor scriptable interface tool
interactive tool property set.
That's a really long name.
Yeah.
I think this is proof that Epic don't review class names in their process.
So if anyone's ever wondering, I think we've proven that.
And yeah, feature-wise, you get life cycle support,
which you have to put some effort into; click, drag, hover.
It's largely event-driven, so it's really straightforward to use.
You can add gizmos to anything you like, property watchers.
So if you change data, you can rebuild automatically.
And you might be scratching your head wondering
what I'm actually talking about here, but I'll
show you the tool in the next slide.
Messaging help on screen; rendering-- simple, hard;
serialization through your property set; et cetera.
Actually, that property set thing you don't have to use,
but it's another way to separate your data from your tools.
So it's good to use it, I think.
So this is an example of using the tool.
So you can see the interface here.
So if you choose a tool on the left, bit like if you've used modeling--
if you've used any editor mode, really, you
choose your tool here and set it up.
And you can see on the left, I realize there's three gifs,
and there's a lot going on here.
So if you try and follow what I'm talking about.
So the left one is the wall builder, which you can
see in the scriptable editor tool.
So this is our wall builder tool--
room builder, sorry-- wrapped with a scriptable editor tool.
So you can see I've got gizmos on here
by default, which is probably stupid.
It makes it quite confusing to see what I'm doing,
but you can just toggle this on and off.
And when the gif restarts, I'll talk through it.
So you can see I'm just placing points here.
Yeah, now placing points here.
I can modify them in real time with the gizmos.
You see, I label the head and tail with message text to make it clearer.
And you can see also I've turned snapping on there.
So this is handled through a modifier,
through the control modifier.
And that allows snapping to tiles, which is super useful.
That's not part of scriptable tools, really, the snapping,
but the modifiers are.
Then that's a shift modifier to insert a point.
It's really flexible for what you're doing.
You can also-- and we've done stuff so you can just
add to the head or the tail of the spline.
It's a super useful interface for drawing splines.
Then we've got our passage tool here where--
so Ark mentioned the complexity of passages
and how they're a little bit tricky to set up.
So this tool just simplifies that.
So you just choose the door you want and just click anywhere on the level.
It just inserts it in a wall.
So it takes 10 seconds to put doors in the entire level, which
is super useful.
Then the floor tool.
Finally, you can see when the gif restarts, that I just click around,
generate all these polys for these floors, which this is obviously--
like the floors in this level are quite rectilinear-- in fact,
they're quite rectangular, I'd say.
So it's not a super good example, but it is dense and complicated,
and there's loads of geometry and stuff.
And then I just generate all those, bake it down instantly,
and then you can drag and drop materials into the scene
and completely build out all the floors in the scene super quickly.
And again, part of that utility is from how
good scriptable tools are just as a really quick tooling system.
This is just our learnings from using scriptable tools, which
I thought would be interesting to briefly talk through.
So modifiers don't actually work super well out of the box.
They work well in terms of when you're setting up
your blueprint scriptable function-- which again, is just
sort of a bunch of events.
Like you have a startup event--
it doesn't handle modifiers too well.
So you'll get modifiers like shift key control, Alt,
which is what it supports out of the box,
but they will only work on like your startup and your click events.
So if you're having a mouse hovering, which is probably a lot of the way
you will use the tool, that won't update your modifiers.
So I thought I'd just put this up here to give an example.
So if you extend this tool and just override the setup function,
then you can just bind events to some of the other events happening.
So you actually get real time modifiers.
Otherwise, if you're hovering, not clicking,
and you press shift or control, you won't get these updates.
So that's useful.
We added support for viewport grid snapping into all of our tools
and throughout the project.
So this is just this very simple function
that grabs that from the viewport.
So when the user's just changing the viewport,
you can just grab that value.
So whatever that value is will be used throughout our scriptable tools,
as well.
And then as you can see in the blueprint thing there,
we can just grab that value and snap to grid.
State saving is a little bit nuanced.
You have to do things in a certain order
to make sure that state saving works.
So you can serialize data between sessions in your property set,
but there's some nuance there, so that is a bit fiddly.
Complex watches don't work.
So you can drive property change events, basically,
and they don't work for complex types.
So you have to either roll your own.
So I assume if it's a basic type, you will get an event from that.
If it's not a complex type, that probably won't work.
Just bear that in mind.
Clean up very carefully.
So it is modal.
It does handle its life cycle, but there's
a shutdown event which you will have to manage stuff from.
It's very simple.
So you can get selected actor, as well, in a scriptable tool.
So you can actually load an actor in.
You can load your complex wall in and just edit it.
So obviously, if you're loading that in,
you don't want to roll back to no content,
so you need to handle that rollback properly, obviously.
Very simple to wrap stuff for.
Not that we found, anyway, there are no default delegates for when a PCG
graph is rebuilt. So if you're wrapping PCG stuff with this tool,
you need to write your own stuff, probably to-- we can find it,
anyway--
to handle when a graph is being regenerated.
So that is, obviously, a process.
You can't by default hook into that.
So yeah, extra bits.
We've built an editor widget to help us handle regeneration
because it was a little bit difficult to updating the PCG,
then regenerated the map per map.
One thing, as well, we did try to fix,
there was a small bug when you tried to do control z undo.
It would leave ghost meshes in PCG.
So as well, we try to fix that.
The other thing we've added were bushes, as well.
This was very niche for one specific map.
Somebody was asking to do kind of maze out of foliage.
So yeah, we just made these guide splines you can use for PCG.
And now, as you see in this maze, we will
have to change because you know the solution.
He's been excited about telling that joke.
I'm not sure why.
Yeah.
Last thing are tunnels.
And as well, this is the same idea with having
trade lines, handled by PCG, and then curved by geometry script.
Cool.
So just a couple of slides to wrap up on future work.
Do you want to go first?
Yeah so for PCG, we would like to have more of a global way of manage,
and regenerate, and test the PCG graphs in the project.
We wanted to do--
The reason for this, by the way, is if you build your PCG graph,
and you generate some stuff on a level,
and then you forget that level, don't work on it for three months,
and you've changed the graph a lot, that
won't be regenerated in that level.
And also, we don't know quite how to handle that of,
we don't just want to regenerate anything
because that's a QA nightmare to then regenerate everything and not go
and test it.
So it's a bit nuanced to us how to handle that, I think.
Yeah.
Another thing we really want to do is roof generation.
So currently, we could just use the flow builder, have a flat roof,
and just move it up.
But we would like to actually try to do some kind of more complex stuff,
and as well, build different ones for different styles if it's possible.
Yeah.
I think this is just out of scope of the project, really.
Because if we're building roofs for rectilinear buildings,
then it's reasonably straightforward.
As soon as you add curvature, it becomes even more tricky.
We just didn't have the time to work on this, really,
so that would be something we would like to do.
Sampling patterns.
So Ark mentioned we have this data table with samples for brickwork.
We'd like to use-- there is actually a plug-in called PCG geometry script
interop, which allows PCG to call some
of the sampling functions in the geometry script library.
We'd probably like to add to that to add
some more noise patterns and industrial patterns
to make wall clutter.
We currently don't handle passages on curved walls.
This is not because it doesn't work.
Obviously, this would actually be quite straightforward
carving into the mesh with a Boolean op or whatever in geometry script.
We just haven't done it.
We haven't had time.
And floor builder.
The artists want ways to explicitly define split room floors--
wasn't described very well.
So if you have some giant room, they want to be able to draw a boundary
and split that into two things.
So we'll probably do that.
Also, not auto-regeneration as such.
That's a misnomer, really.
But be able to click a button to regenerate it
based on what the room changes.
Hierarchical fog of war is something I'm not
going to go through because we don't have time, but there we go.
Oh yeah, the takeaways.
So it is great to build tools around these amazing frameworks.
So if you want to go through, consider
your sampling strategies, how you want to sample the data,
consider using subgraphs.
They are very, very powerful, and as well,
use data tables and data assets.
They really help you to do more versatile work.
That's got a lot more flexible in 5.4--
well, 5.3 and 5.4, utility of those.
That wasn't available, really, when we started.
Geometry script is super flexible.
Dynamic mesh is very useful.
I think there's real utility in non-mobile projects for using
dynamic meshes in release, as well.
I think someone told me the other day that you can use that with Lumen now.
And yeah, I think there's real utility there.
But also, like all the tools to bake stuff down,
it can be very efficient for mobile.
And just having the flexibility to do a lot of these mesh operations
in editor is super useful.
Scriptable tools, as you've seen, utility
is incredible for something that's so easy to build.
You can build these in like an hour or so,
and have a tool that is far more usable than the tool you had before.
And yeah, just to say, really, this has been continued development
through the project.
So the original thing was pretty bare bones.
You could see, I think, the life cycle from Ark's PCG graph.
As right at the beginning, we were both looking
at that to figure it out.
And then Ark's been working on that over the year,
and I guess his knowledge of that has changed a lot.
And also, the tooling for that with 5.3 and 5.4 has changed a lot.
But this is really just us building these tools throughout the year,
like just in our spare time, really.
Like moments here and there to try and build them out.
But yeah.
Anyway, thank you very much.
And that was the end of our--
Thank you.
[APPLAUSE]
Loading video analysis...