Part 2 of: https://geofflester.wordpress.com/2016/02/07/factory-pt-1/
I had to split this post up, so I want to get this out of the way:
You’re going to see a lot of ugly in the post. #Procedural #Placeholder ugly
This post is mostly about early pipeline setup in Houdini Python, and UE4 c++.
For testing purposes, I made 4 instances of #procedural plants using l-systems:
When I say “made”, I mean ripped from my Shangri-La tribute scene, just heavily modified:
Like I mention in that post, if you want to learn lots about Houdini, go buy tutorials from Rohan Dalvi.
He has some free ones you can have a run through, but the floating islands series is just fantastic, so just buy it😛
These plants I exported as FBX, imported into UE4, and gave them a flat vertex colour material, ’cause I ain’t gonna bother with unwrapping placeholder stuff:
The placeholder meshes are 4000 triangles each.
Amusingly, when I first brought them in, I hadn’t bothered checking the density, and they were 80 000 + triangles, and the frame rate was at a horrible 25 fps😛
Houdini –> UE4
So, the 4 unique plants are in UE4. Yay!
But, I want to place thousands of them. It would be smart to use the in-built vegetation tools in UE4, but my purpose behind this post is to find some nice generic ways to get placement data from Houdini to UE4, something that I’ve been planning to do in my old Half Life scene for ages.
So I’m going to used Instanced Static Meshes
Generating the placements
For now, I’ve gone with a very simple method of placing vegetation: around the edges of my puddles.
It will do for sake of example. So here’s the puddle and vegetation masks in Houdini (vegetation mask on the left, puddle mask on the right):
A couple of layers of noise, and a fit range applied to vertex colours.
I then just scatter a bunch of points on the mask on the left, and then copy flowers onto them, creating a range of random scales and rotations:
The node network for that looks like this:
Not shown here, off to the left, is all the flower setup stuff.
I’ll leave that alone for now, since I don’t know if I’ll be keeping any of that
The right hand side is the scattering, which can be summarized as:
- Read ground plane
- Subdivide and cache out a the super high poly plane
- Move colour into Vertex data (because I use UVs in the next part, although I don’t really have to do it this way)
- Read the brick texture as a mask (more on that below)
- Move mask back to Point data
- Scatter points on the mask
- Add ID, Rotation and Scale data to each point
- Flip YZ axis to match UE4 (could probably do this in Houdini prefs instead)
- Python all the things out (more on that later)
I mentioned quickly that I read the brick mask as a texture in the above section.
I wanted the plants to mostly grow out of cracks, so I multiplied the mask by the inverted height of the bricks, clamped to a range, using a Point VOP:
And here’s the network, but I won’t explain that node for node, it’s just a bunch of clamps and fits which I eyeballed until it did what I wanted:
Python all the things out, huh?
Python and I have a special relationship.
It’s my favourite language to use when there aren’t other languages available.
Anyway… I’ve gone with dumping my instance data to XML.
More on that decision later.
Now for some horrible hackyness:
node = hou.pwd()
from lxml import etree as ET
geo = node.geometry()
root = ET.Element("ObjectInstances")
for point in geo.points():
pos = point.position()
scale = hou.Point.attribValue(point, 'Scale')
rotation = hou.Point.attribValue(point, 'Rotation')
scatterID = "Flower" + repr(hou.Point.attribValue(point, 'ScatterID')+1)
PosString = repr(pos) + ", " + repr(pos) + ", " + repr(pos)
RotString = repr(rotation)
ScaleString = repr(scale) + ", " + repr(scale) + ", " + repr(scale)
# Do the export
tree = ET.ElementTree(root)
tree.write("D:/MyDocuments/Unreal Projects/Warehouse/Content/Scenes/HoudiniVegetationPlacement.xml", pretty_print=True)
NOTE: Not sure if it will post this way, but in Preview the tabbing seems to be screwed up, no matter what I do. Luckily, programming languages have block start and end syntax, so this would never be a prob… Oh. Python. Right.
Also, all hail the ugly hard coded path right at the end there
(Trust me, I’ll dump that into the interface for the node or something, would I lie to you?)
Very simply, this code exports an XML element for each Point.
I’m being very lazy for now, and only exporting Y rotation. I’ll probably fix that later.
This pumps out an XML file that looks like this:
<Flower1 Location=“-236.48265075683594, -51.096923828125, -0.755022406578064“ Rotation=“(0.0, 230.97622680664062, 0.0)“ Scale=“0.6577988862991333, 0.6577988862991333, 0.6577988862991333“/>
Reading the XML in UE4
In the spirit of slapping things together, I decided to make a plugin that would read the XML file, and then add all the instances to my InstancedStaticMesh components.
First up, I put 4 StaticMeshActors in the scene, and in place I gave them an InstancedStaticMesh component. I could have done this in a Blueprint, but I try to keep Blueprints to a minimum if I don’t actually need them:
As stated, I’m a hack, so the StaticMeshActor needs to be named Flower<1..4>, because the code matches the name to what it finds in the XML.
The magic button
I should really implement my code as a either a specialized type of Data Table, or perhaps some sort of new thing called an XMLInstancedStaticMesh, or… Something else clever.
Instead, I made a Magic Button(tm):
XML Object Loader. Probably should have put a cat picture on that, in retrospect.
Brief overview of code
I’m not going to post the full code here for a bunch of reasons, including just that it is pretty unexciting, but the basic outline of it is:
- Click the button
- The plugin gets all InstancedStaticMeshComponents in the scene
- Get a list of all of the Parent Actors for those components, and their labels
- Process the XML file, and for each Element:
- Check if the element matches a name found in step 3
- If the Actor name hasn’t already been visited, clear the instances on the InstancedStaticMesh component, and mark it as visited
- Get the position, rotation and scale from the XML element, and add a new instance to the InstancedStaticMesh with that data
And that’s it! I had a bit of messing around, with originally doing Euler –> Quaternion conversion in Houdini instead of C++, and also not realizing that the rotations were in radians, but all in all it only took a hour or two to throw together, in the current very hacky form
Some useful snippets
The FastXML library in UE4 is great, made life easy:
I just needed to create a new class inheriting from the IFastXmlCallback interface, and implement the Process<x> functions.
I’d create a new instance in ProcessElement, then fill in the actual data in ProcessAttribute.
Adding an instance to an InstancedStaticMeshComponent is as easy as:
And then, in shortened form, updating the instance data:
One last dirty detail…
That’s about it for the code side of things.
One thing I didn’t mention earlier: In Houdini, I’m using the placement of the plants to generate out the dirt map mask so I can blend in details around their roots:
So when I export out my ground plane, I am putting the Puddles mask into the blue channel of the vertex colours, and the Dirt mask into the red channel of the vertex mask
Still to come (for vegetation)
So I need to:
- Make the actual flowers I want
- Make the roots/dirt/mossy texture that gets blended in under the plants
- Build more stuff
Why not data tables
I’m all about XML.
But a sensible, less code-y way to do this would be to save all your instance data from Houdini into CSV format, bring it in to UE4 as a data table, then use a Construction Script in a blueprint to iterate over the data and add instances to an Instanced Static Mesh.
I like XML as a data format, so I decided it would be more fun to use XML.
Why not Houdini Engine
That’s a good question…
- I want to explore similar workflows with Modo replicators at some point, and I should be able to re-use the c++/Python stuff for that
- Who knows what other DCC tools I’ll want to export instances out of
- It’s nice to jump into code every now and then. Keeps me honest.
- I don’t own it currently, and I’ve spent my software budget on Houdini Indie and Modo 901 already :)
If you have any questions, feel free to dump them in the comments, I hurried through this one a little since it’s at a half way point without great results to show off yet!