Start a new topic

getting switch nodes (children)?

How do I inventory a FLT file for switches, and export a file for each node child?

I only see docs (samples) for creating a switch from scratch.


I'm interested in building a script to process files external from Creator.


Am digging through the API docs.


Pointers gratefully accepted.


Thanks!


Here is something to get you started. You say you want to export a file for each child of the switch. I wonder if you really want a file for each "state" of the switch. It could be that for your switches, this is the same (if each switch mask has exactly one child ON and the rest OFF). But in general a switch mask may have more than one child ON. The code below finds all the switch nodes in your db and prints some info about each one. For each mask on the switch it prints out which children (plural) are ON and which are OFF for that mask. If you really want to know each child of the switch regardless of the mask states, that is easy enough to do using mgGetChild. We'll follow up with a separate script showing the export file part but this should get you going. Let us know if you have any questions.

 

# this function is called when the walk function finds a switch node in
# the hierarchy
def ProcessSwitch(switchNode):
	# get the node name
	name = mgGetName(switchNode)
	# get the number of masks defined for the switch
	numMasks = mgGetSwitchMaskCount(switchNode)
	on_off = { 0 : "on", 1 : "off" }
	# get the number of children for the switch node
	# each switch mask will have this many bits. 
	# each bit tells you whether or not "that" child is ON or OFF
	numChildren = mgCountChild(switchNode)
	print "switch name:",name,"numMasks:",numMasks,"numChildren:",numChildren

	# loop through the masks defined for the switch
	for maskNum in range(0, numMasks):
		print "mask:",maskNum
		# loop through the "bits" of the mask - again there will be one
		# bit per child. that bit will tell you if that child is ON/OFF
		for childNum in range(0, numChildren):
			b,on  = mgGetSwitchBit (switchNode, maskNum, childNum)
			print "   child",childNum,"is",on_off[on]

def PreCallback (db, parent, rec, i):
	code = mgGetCode(rec)
	if (code == fltSwitch):
		ProcessSwitch(rec)
	return MG_TRUE

# walk the database, looking for switch nodes
db = mgGetCurrentDb ()
mgWalk (db, PreCallback, None, None, 0)

 

Here is the typical output you'll see when you run this script on your db: 

Run Script started <PrintSwitch.py>...
switch name: sw1 numMasks: 3 numChildren: 6
mask: 0
   child 0 is off
   child 1 is off
   child 2 is on
   child 3 is on
   child 4 is on
   child 5 is on
mask: 1
   child 0 is on
   child 1 is on
   child 2 is off
   child 3 is off
   child 4 is on
   child 5 is on
mask: 2
   child 0 is on
   child 1 is on
   child 2 is on
   child 3 is on
   child 4 is off
   child 5 is off
Run Script finished

 

Thanks Steve,


Much appreciated.  I do in fact mean to write all children - I don't know of a better way to do it.


I am porting some files to Unity for a project we are doing here.  The workflow I have requires me to export FLT to OBJ.

As you know FLT files contain OpenFlight nodes which are not supported by the OBJ file format.  One of these is is/are switches - the source file contains several switch nodes, and I need the geometry they contain.  


I plan to get specific nodes from a configuration file that contains the switch index, effectively the child of the switch.  We haven't done anything with masks, some switches have them, but I think most do not.  So a config for child 0 would be the 1st child, 4 = the 5th child, etc.


I suppose I could have the script only grab the required child, and not do a global dump.  My thought was to process the source file once, and then choose the file/s I need from the output for any additional processing.


On a somewhat related note - you know the file export codes?  I have one for dxf that works:

 

mgExportDb (db, exportFileName, "D8A67204-C573-4EC5-B1E2-E1DC93EB78E9", None, None)

 

Is there one for OBJ..? The docs I am using (v16) indicates:  

 

mgExportDb exports the contents of the OpenFlight database db to a different file format on disk. The file format is specified by name formatName and version formatVersion. The name of the file created is specified by fileName. An argument list specifies a list of format specific parameters to control the export operation.

The in-memory representation of db is not affected by this function. In particular, the database db retains its original file name so subsequent calls to mgWriteDb will write out the original file name, not the name you specified to mgExportDb. Note that this behavior is different from that of mgSaveAsDb which changes the file name associated with the in-memory representation of db.

Note: If invoked from within the modeler environment, this function will fail if either db or the named file is open in an active window.

The only valid format name is MEFN_OPENFLIGHT.

 

Thanks!



And here is a script that should be close to what you want. Let us know if you have any questions or need clarification on any of this code.

 

# temp files for copying palettes between existing and new dbs created
# change these for your folder structure
COLORPALETTE = "d:/share/colorpalette.color"
TEXTUREPLETTE = "d:/share/texturepalette.txt"
MATERIALPALETTE = "d:/share/materialpalette.materials"

# this is where you want new files to be saved. {0} is
# replaced by 1,2,3,... etc for each switch mask set saved
# change this (but leave {0} somewhere in pattern)
NEWFILEROOT = "d:/share/switchmask_{0}.flt"

def CopyColorPalette(dstDb, srcDb):
	# copy the color palette from srcDb to dstDb
	mgWriteColorPalette(srcDb, COLORPALETTE)
	mgReadColorPalette(dstDb, COLORPALETTE)

def CopyTexturePalette(dstDb, srcDb):
	# copy the texture palette from srcDb to dstDb
	mgWriteTexturePalette(srcDb, TEXTUREPLETTE)
	mgReadTexturePalette(dstDb, TEXTUREPLETTE)

def CopyMaterialPalette(dstDb, srcDb):
	# copy the material palette from srcDb to dstDb
	mgWriteMaterialFile(srcDb, MATERIALPALETTE)
	mgReadMaterialFile(dstDb, MATERIALPALETTE)

def CopyPalettes(dstDb, srcDb):
	# copy color/texture/material palettes from scrDb to dstDb
	# if you need other palettes there are more read/write palette
	# functions in the API (light point, light source, texture mapping, etc)
	CopyColorPalette(dstDb, srcDb)
	CopyTexturePalette(dstDb, srcDb)
	CopyMaterialPalette(dstDb, srcDb)

# this function saves a new db file containing the nodes in the recList
# presumably these are the nodes that are ON in a switch mask
def SaveNode(fileName, srcDb, recList):
	# overwrite file if it already exists
	mgSetNewOverwriteFlag(MG_TRUE)
	# make the new db
	newDb = mgNewDb(fileName)
	if (newDb):
		# new dbs always have db-g1-g2
		# we'll attach new nodes under g2 which is child of g1 which is child of db
		g2 = mgGetChild(mgGetChild(newDb))
		for rec in recList:
			# for each node in the recList...
			# make a copy in the newDb
			newRec = mgDuplicateToDb(rec, newDb)
			# attach it to g2 in newDb
			mgAttach(g2, newRec)
		# copy color/texture/material palettes from original db to newDb
		CopyPalettes(newDb, srcDb)
		# write and close
		mgWriteDb(newDb)
		mgCloseDb(newDb)
		print fileName,"created and saved"

# this function is called when the walk function finds a switch node in
# the hierarchy
def ProcessSwitch(switchNode):
	# get the switch node name
	name = mgGetName(switchNode)
	# get the number of masks defined for the switch
	numMasks = mgGetSwitchMaskCount(switchNode)
	on_off = { 0 : "on", 1 : "off" }
	# get the number of children for the switch node
	# each switch mask will have this many bits. 
	# each bit tells you whether or not "that" child is ON or OFF
	numChildren = mgCountChild(switchNode)
	print "switch name:",name,"numMasks:",numMasks,"numChildren:",numChildren
	fileNum = 1

	# loop through the masks defined for the switch
	for maskNum in range(0, numMasks):
		print "mask:",maskNum
		# build a list of nodes that are ON for this mask
		recList = list() 
		# loop through the "bits" of the mask - again there will be one
		# bit per child. that bit will tell you if that child is ON/OFF
		for childNum in range(0, numChildren):
			b,on  = mgGetSwitchBit (switchNode, maskNum, childNum)
			print "   child",childNum,"is",on_off[on]
			child = mgGetChildNth(switchNode, childNum+1)	# first child is 1, not 0 !!
			# add this child to the list if it is ON in this mask
			if (on): recList.append(child)
		if (len(recList) > 0):
			# if there are any children ON in this mask, save them to a new file
			# make the new file name, it is the root file name with {0} replaced by 1,2,3, etc
			fileName = NEWFILEROOT.format(fileNum)
			fileNum = fileNum + 1		# ready for next new file name
			db = mgRec2Db(switchNode)
			# save the nodes in this list (recList) to this fileName
			SaveNode(fileName, db, recList)

def PreCallback (db, parent, rec, i):
	code = mgGetCode(rec)
	if (code == fltSwitch):
		# this node is a switch node, process it
		ProcessSwitch(rec)
	return MG_TRUE

# walk the database, looking for switch nodes
db = mgGetCurrentDb ()
mgWalk (db, PreCallback, None, None, 0)

 

Shawn, looks like our replies crossed in the night.

Note that mgExportDb (in Creator 16) only works for non-current versions of OpenFlight. It won't export to OBJ or any other non-OpenFlight format. It was in Creator 17 that mgExportDb was changed to support non-OpenFlight formats. In Creator 16 you'll have to export (save) to OpenFlight (like my script does) and then export in Creator those OpenFlight files to OBJ.

Shawn, here is an updated version of the last script. It has two switch "processing" functions:

ProcessSwitchMasks - this function (formerly ProcessSwitch in previous script) writes out a new db per mask of the switch

ProcessSwitchChildren - this function (new) writes out a new db per child of the switch

I left both functions in the script but changed the Walk Function (PreCallback) to call the latter. If you ever wanted to change it, you could make it call the former.

# temp files for copying palettes between existing and new dbs created
# change these for your folder structure
COLORPALETTE = "d:/share/colorpalette.color"
TEXTUREPLETTE = "d:/share/texturepalette.txt"
MATERIALPALETTE = "d:/share/materialpalette.materials"

# this is where you want new files to be saved. {0} is
# replaced by 1,2,3,... etc for each switch mask set saved
# change this (but leave {0} somewhere in pattern)
NEWFILEROOT = "d:/share/switchmask_{0}.flt"

def CopyColorPalette(dstDb, srcDb):
	# copy the color palette from srcDb to dstDb
	mgWriteColorPalette(srcDb, COLORPALETTE)
	mgReadColorPalette(dstDb, COLORPALETTE)

def CopyTexturePalette(dstDb, srcDb):
	# copy the texture palette from srcDb to dstDb
	mgWriteTexturePalette(srcDb, TEXTUREPLETTE)
	mgReadTexturePalette(dstDb, TEXTUREPLETTE)

def CopyMaterialPalette(dstDb, srcDb):
	# copy the material palette from srcDb to dstDb
	mgWriteMaterialFile(srcDb, MATERIALPALETTE)
	mgReadMaterialFile(dstDb, MATERIALPALETTE)

def CopyPalettes(dstDb, srcDb):
	# copy color/texture/material palettes from scrDb to dstDb
	# if you need other palettes there are more read/write palette
	# functions in the API (light point, light source, texture mapping, etc)
	CopyColorPalette(dstDb, srcDb)
	CopyTexturePalette(dstDb, srcDb)
	CopyMaterialPalette(dstDb, srcDb)

# this function saves a new db file containing the nodes in the recList
# presumably these are the nodes that are ON in a switch mask
def SaveNode(fileName, srcDb, recList):
	# overwrite file if it already exists
	mgSetNewOverwriteFlag(MG_TRUE)
	# make the new db
	newDb = mgNewDb(fileName)
	if (newDb):
		# new dbs always have db-g1-g2
		# we'll attach new nodes under g2 which is child of g1 which is child of db
		g2 = mgGetChild(mgGetChild(newDb))
		for rec in recList:
			# for each node in the recList...
			# make a copy in the newDb
			newRec = mgDuplicateToDb(rec, newDb)
			# attach it to g2 in newDb
			mgAttach(g2, newRec)
		# copy color/texture/material palettes from original db to newDb
		CopyPalettes(newDb, srcDb)
		# write and close
		mgWriteDb(newDb)
		mgCloseDb(newDb)
		print fileName,"created and saved"

# this function is called when the walk function finds a switch node in
# the hierarchy and you want to process the "masks" of the switch
def ProcessSwitchMasks(switchNode):
	# get the switch node name
	name = mgGetName(switchNode)
	# get the number of masks defined for the switch
	numMasks = mgGetSwitchMaskCount(switchNode)
	on_off = { 0 : "on", 1 : "off" }
	# get the number of children for the switch node
	# each switch mask will have this many bits. 
	# each bit tells you whether or not "that" child is ON or OFF
	numChildren = mgCountChild(switchNode)
	print "switch name:",name,"numMasks:",numMasks,"numChildren:",numChildren
	fileNum = 1

	# loop through the masks defined for the switch
	for maskNum in range(0, numMasks):
		print "mask:",maskNum
		# build a list of nodes that are ON for this mask
		recList = list() 
		# loop through the "bits" of the mask - again there will be one
		# bit per child. that bit will tell you if that child is ON/OFF
		for childNum in range(0, numChildren):
			b,on  = mgGetSwitchBit (switchNode, maskNum, childNum)
			print "   child",childNum,"is",on_off[on]
			child = mgGetChildNth(switchNode, childNum+1)	# first child is 1, not 0 !!
			# add this child to the list if it is ON in this mask
			if (on): recList.append(child)
		if (len(recList) > 0):
			# if there are any children ON in this mask, save them to a new file
			# make the new file name, it is the root file name with {0} replaced by 1,2,3, etc
			fileName = NEWFILEROOT.format(fileNum)
			fileNum = fileNum + 1		# ready for next new file name
			db = mgRec2Db(switchNode)
			# save the nodes in this list (recList) to this fileName
			SaveNode(fileName, db, recList)

# this function is called when the walk function finds a switch node in
# the hierarchy and you want to process the children of the switch
def ProcessSwitchChildren(switchNode):
	# get the switch node name
	name = mgGetName(switchNode)
	numChildren = mgCountChild(switchNode)
	print "switch name:",name,"numChildren:",numChildren
	fileNum = 1

	# loop through the children under the switch
	for childNum in range(0, numChildren):
		# build a list of nodes (will just contain one node - the current child)
		recList = list() 
		child = mgGetChildNth(switchNode, childNum+1)	# first child is 1, not 0 !!
		recList.append(child)
		if (len(recList) > 0):
			# if there are any children ON in this mask, save them to a new file
			# make the new file name, it is the root file name with {0} replaced by 1,2,3, etc
			fileName = NEWFILEROOT.format(fileNum)
			fileNum = fileNum + 1		# ready for next new file name
			db = mgRec2Db(switchNode)
			# save the nodes in this list (recList) to this fileName
			SaveNode(fileName, db, recList)

def PreCallback (db, parent, rec, i):
	code = mgGetCode(rec)
	if (code == fltSwitch):
		# this node is a switch node, process it
		ProcessSwitchChildren(rec)
	return MG_TRUE

# walk the database, looking for switch nodes
db = mgGetCurrentDb ()
mgWalk (db, PreCallback, None, None, 0)

 

Steve,


You are awesome, thanks!  This seems to work as advertised (I killed the session early just because things were taking a while, but files are present!!).


I'm driving Creator 18 but am using the API refs from v16 - thanks for clarifying.

Ok, got it. Time to get the OF API 18 refs ;-)
SInce you're using CR 18, you can use mgExportDb to write other (non-OpenFlight) formats. Go to Script>OpenFlight Script Editor and do Help>On Creator Script to get the format keys for the different formats. Also you could use the Creator Script Snippet Wizard (also off the OpenFlight Script Editor) to paste the mgExportDb snippet for the format you choose there.
Have fun scripting!!

Downloading now, thanks!


I appreciate the tips.  Scripting does not come naturally, but with practice I hope to be able to at least roll around in the docs and snippets ;)

If you have some extra time (and have not done so yet) I might also recommend checking out the OpenFlight API Developer Guides. These documents give you a frame of reference for the OpenFlight API, the OpenFlight Scene Graph and how it is structured. OpenFlight Script, after all, is merely a Python wrapper to this API. Perhaps the concepts in the Developer Guides might help you to better understand how to structure and implement your scripts.

Login to post a comment