Friday 28 February 2020

Week 8 - Material ID Experimentation

I spent a couple of days trying to figure out how to solve the problem with the material IDs, since Houdini doesn't keep the Material IDs in a way, that Unreal can use them (This might be a problem specific to our workflow, 'obj' files get imported with Materials instead of Principled Shaders).

1 - Automating the process
Since we are currently working with about 50 Modular Kit pieces and 20 Material IDs it would be very tedious to manually set up the Materials. So I did a bit of testing.
All the pieces come with an attribute called 'shop_materialpath', which I can use to get the faces with different IDs.

Material ID List
Unfortunately I could not find out how to add material inputs for the HDA, or if that is even possible, so for now we use String Parameters.
I also couldn't figure out how to edit the parameters via Python, since the geometry of the nodes is read only and the python node doesn't own the geometry, because it is not connected to the geometry nodes.
However I can use Python to create nodes, so I created an Attribute Wrangle node in each Modular Kit Node, containing the necessary logic to assign the 'unreal_material' attribute + values.

I added Parameters for each Material ID, which get referenced by the attribute. This time I added the Parameters manually, but I could have automated this in a similar way with how I did the .json file evaluation. This would make it more flexible for using different kits, however due to time constraints I chose to do it manually for now.

I also locked the geometry files, so the HDA has no file dependencies.

2 - Unreal
So, to keep it short: It works.

Material IDs in Unreal




Now that I got this working I will move on to LODs and hopefully soon more Shader and Engine work.

Wednesday 26 February 2020

Week 6.2 and 7 - Finally Buildings

After about two not as productive weeks I finally completed the building construction. I had to go back and redo some bits, because I found out, that Unreal can't cook the Python Module nodes.

Looks like a building to me

1 - Roofs redone
Since I initially used Python to create the roof meshes I had to go back and redo the setup, but that turned out to be not that complicated. Instead of using a curve and having to pass the point positions into the coordinate parameter, I used VEX to create a polygon and add the points to it.


I used the following node setup to create the roof meshes and moved each into a separate obj node, so I could use them separately, e.g if I don't need one of them.
EDIT: I added an attribute wrangle node to add a material attribute.


2 - Ground Floor Complete
Since the ground Floor was done in a similar way to the mid floors I won't go into much detail about the process. I cleaned up the code for placing the wall tiles slightly and added a new section for adding unique tiles. I limited the number of unique tiles to 1 per building side, just to simplify things, but in theory it would be possible. I even tried it, but it got a bit buggy.
Here is the code for the unique tiles, nothing special.



This could and should probably be optimised.
Just for completeness sake I'll add the code for placing the wall tiles, but it is not any different from the middle floor section.

And finally the full ground floor section in action:


This is basically how far I got over the last couple of days. From here on I will focus on sorting the material IDs first and then move on to removing wall sections and creating LODs.
But again: It's a building !!!

Building Variation
And it even works in Unreal, that's progress.

Building Generator in UE4

Wednesday 12 February 2020

Week 5 and 6.1 - Sorting the Building Middle Floor Section

After thinking I had sorted the .json files  I found out the hard way this week, that once I converted my nodes into an HDA, my python code stopped working, so I guess this will go back onto my To-Do list.
On the bright sight I made quite a bit of progress with the building construction itself and got the middle floor section into a working state.

1 - Corners

First I went back into the Corner Node, cleaned up the code a bit and got rid of a bug, where depending on where point 0 was, it would not get assigned an instance attribute, because some of the angles where messed up. I fixed this by appending point 0 to the point array and working with that instead of always adding a special case for the last point of the curve.


This is of course only working for one floor, so I adapted it later, when working on the floor separation. Also I added a parameter for selecting the corner pieces.


2 - Wall Sections

Going back to the wall sections I basically got rid of all my previous code for them. As I stated in a previous blog post, we didn't want to have any scaled tiles, except plain wall tiles, where scaling doesn't matter.
However, since Litha added pillars to the modular kit and wanted to have parameters for 'Number of pillars' per building side and 'Wall Tiles in between Pillars' I had to change the way of how I calculated the tile positioning.
I ended up breaking this down into the following steps:

  1. Calculate the total length of the pillar + in between tiles section and check if it fits onto the building side
  2. Calculate the number of tiles that fit on each side of the pillar section (needs to be an even number)
  3. Fill the rest of the wall length with scaled plain wall tiles.
We agreed to have the pillar section in the centre of the building side for now. Of course it would be possible to add the option of offsetting the section, but  we'll have to see, if it is necessary.

The code bit for this looks like this, again this version only working for one floor, but I will show the multiple floor version later:


I originally also thought about somehow calculating the maximum amount of pillars, with the given number of in between pieces, but for now I left it to just fill the wall with tiles, if the specified pillar amount doesn't fit.
I also, similar to the corner pieces, added Parameters to switch through the window frame, window, and pillar meshes.


3 - Floor and Roof Separation

After having figured out the basics for the corners and walls, I moved on to implementing multiple floors, including the option to either add separation pieces to every floor, or to add them only to specific floors.
For the top floor it automatically uses the roof separation tiles, instead of the floor separation ones.

When adding the corners to each row of the building, I also added an attribute to the points, depending on whether it was a wall, floor separation or roof separation row, so I could add the corresponding tiles, when adding the wall sections.

Here are the updated code bits for corners and walls.




I also brought the HDA into Unreal and it seems to be working alright, but we really need to figure out LODs, otherwise we sacrifice performance, where it is not necessary.


4 - Roof Mesh

Since the roof at that point was just a hole and might have caused issues with lighting (leaving aside, that in a game the rooftop might be visible), I needed to create a mesh from the footprint curve, which I had to do in a separate geometry node. Since Mike explained the use and advantages of detail attributes to me, I thought I could store the point positions of the curve in a detail attribute and use it to recreate the curve. I had some trouble figuring out how to get the attribute reference work with the coordinate parameter of the new curve, so I just used python, modified the attribute to fit the pattern of the coordinates and it ended up working.


After using a divide, poly-extrude and unwrap node, I ended up with a pretty usable roof mesh. I also stored the height of the mid floor section in a detail attribute and used it to offset it.

Complete Middle Floor Section

5 - To Do

  1. Fix Import and Read .json file python code
  2. Add Ground Floor Section
  3. Figure out Material IDs !!
  4. LODs
  5. Collision

Monday 3 February 2020

Week 4 - Json Files Part 2

After spending the majority of time in week 3 working on the json file generator tool, I decided that I had to go back into Houdini and get things moving in there. So week 4 was basically implementing the use of the .json files in the building generator.

1 - Importing .FBX Files

EDIT: Turns out there is some issue with the FBX import, when creating Digital Assets for Unreal, as the axis of the meshed don't get updated in UE4. It works, when the .FBX files get imported with the geometry option. I might see, if I can later figure out another solution, but at the moment I need to move on.

First I worked on automating the import process, by using a .json file containing all the .FBX files of the modular kit. I used Python to read the file, import of reimport from the specified path, depending on whether a geometry node of the same name already exists or not. I then stored all the node information in a parameter, so other nodes can access it. I also had to copy those geometry nodes into the subnet, that contains all the nodes for the Digital Asset and delete the other ones. At the moment this is done in two python nodes, however I will probably move them into one later.



2 - Evaluating Kit Files

After finishing the import bit I now had to figure out, how to work with the kit .json files. I created a null node for storing the parameters. This looks like this:

Kit info storage
Essentially the code checks, if there is a file in the kit file slots and depending on that either sets the corresponding parameters to '' or evaluates the .json file (I might add a check for file type to make sure it is a json file.)
If it evaluates the .json file, it checks for piece types in the file, e.g. corners, walls, etc., and filters the kit pieces based on their type and saves them into parameters. It also checks, if the geometry file for each piece exists and currently gives a warning, if it doesn't. I might change it, so that the missing files do get imported, or just get rid of the import section and just import the pieces on evaluating the kit files, might make more sense and gets rid of an extra button click. For the parameters it either creates a new parameter, if it doesn't exist, yet or overwrites it's value, if it exists. Again, if a parameter is not needed, the value is set to ''. I originally tried deleting those parameters, but ran into some problems, so I decided to solve it this way.



I made a small video, showing how it currently works.



Again, all the code bits will probably need some optimisation, but I need to move on to the actual building generator bit and clean up the code later. It kinda feels like I did not get a lot done last week, a lot of my current work involves hours of digging through documentations and planning, but now that I have all the necessary modular kit organisation  parts, I will go back to what I have already made of the building generator and  continue it.