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.

node = hou.pwd()
import json
# Add code to modify contained geometries.
# Use drop down menu to select examples.
def onClick():
filepath = node.parm('import_file').eval()
kitNodes = []
kitNodeNames = []
newNodes = []
importNode = hou.node('/obj')
#check existing kit files
parentNode = node.parent()
children = parentNode.children()
for c in children:
t = str(c.type().name())
if(t == 'geo'):
kitNodes.append(c)
kitNodeNames.append(str(c))
#read data and import
with open(filepath) as myfile:
data = myfile.read()
kit = json.loads(data)
for obj in kit:
if(obj['name'] in kitNodeNames):
#if file already exists, reload mesh
hou.parm('../' + obj['name'] + '/' + obj['name'] + '/file').set(obj['path'])
hou.parm('../' + obj['name'] + '/' + obj['name'] + '/reload').pressButton()
print(obj['name'] + ' reloaded')
else:
#import file and move into subnet
hou.hipFile.importFBX(obj['path'], unlock_geometry = True, unlock_deformations = True, import_into_object_subnet = False, convert_into_y_up_coordinate_system = True)
n = hou.node('/obj/'+obj['name'])
n.setDisplayFlag(False)
print(obj['name'] + ' imported')
newNodes.append(n)
hou.copyNodesTo(newNodes, parentNode)
for n in newNodes:
n.destroy()
node = hou.pwd()
# Add code to modify contained geometries.
# Use drop down menu to select examples.
parentNode = node.parent()
children = parentNode.children()
cPaths = ''
for c in children:
if(str(c.type().name()) == 'geo'):
cPaths += str(c.path()) + ' '
#print(hou.node(c.path()))
n = hou.node('../Import_FBX_Files')
n.parm('kit_pieces').set(cPaths)


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.

import json
node = hou.pwd()
# Add code to modify contents of geo.
# Use drop down menu to select examples.
def onClick():
parentNode = hou.node('/obj/UE4_Building_Generator/')
parentParms = parentNode.parms()
storageNode = hou.node('/obj/UE4_Building_Generator/Instance/kit_storage')
storageParms = storageNode.parms()
for p in parentParms:
if ('_kit' in p.name()):
if str(p.eval()) == '':
print('Kit not in use')
kitName = str(p.name())
for param in storageParms:
if(kitName in str(param.name())):
print (param.name(), ' not used')
storageNode.parm(str(param.name())).set('')
elif str(p.eval()) != '' :
filepath = p.eval()
kitName = p.name()
#read data from json files
with open (filepath) as file:
data = file.read()
kit = json.loads(data)
types = []
newParmNames = []
allTypeKits = {}
#filter types
for obj in kit:
if (str(obj['type']) in types) == False :
types.append(str(obj['type']))
#sort kit pieces by type
for t in types:
#create new Parameter Names list
parmName = kitName + '_' + t
newParmNames.append(parmName)
print(parmName)
for t in types:
typeKit = ''
for obj in kit:
#check if geometry file exists
n = hou.node('/obj/UE4_Building_Generator/'+str(obj['name']))
if(n != None):
if (str(obj['type']) == t):
typeKit += str(n.path()) + ' '
#SHOULD I IMPORT MISSING FBX FILE and add to pieceList?
else:
print('Geometry for ' + str(obj['name'] + ' has not been imported.'))
keyName = kitName + '_' + t
allTypeKits.update({keyName : typeKit})
#print('All Type Kits: ' + str(allTypeKits))
print('New Param Names: ' + str(newParmNames))
for newParm in newParmNames:
p = storageNode.parm(newParm)
if(p == None):
print('Create param: ' + newParm)
ptg = storageNode.parmTemplateGroup()
strParm = hou.StringParmTemplate(newParm, newParm, 1, string_type = hou.stringParmType.NodeReferenceList)
ptg.append(strParm)
storageNode.setParmTemplateGroup(ptg)
storageNode.parm(newParm).set(allTypeKits[newParm])
else:
print('New value for: ' + newParm)
storageNode.parm(str(newParm)).set(allTypeKits[newParm])
#if parameter not in use: set to ''
for param in storageParms:
if(kitName in str(param.name())):
if ((str(param.name()) not in newParmNames) == True):
print (param.name(), ' not used')
storageNode.parm(str(param.name())).set('')
print('\n')


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.

1 comment:

  1. I'm a big fan of list comprehension in python, maybe you would like it too..?
    https://www.pythonforbeginners.com/basics/list-comprehensions-in-python

    As an example, we can replace this:

    kitNodes = []
    for c in children:
    t = str(c.type().name())
    if(t == 'geo'):
    kitNodes.append(c)

    with this:

    kitNodes = [c for c in children if str(c.type().name()) == 'geo']


    Also, I am wondering what is the reason for populating a null node with those parameters? Do you have a loop importing those FBXs into your houdini graph, and can that work in some other way? perhaps using a detail attribute which is a list of fbx paths? of course, there may be a perfectly legitimate reason for doing it this way!

    ReplyDelete