Start a new topic

Conditional formatting

Hi,


We have several airport sign models that need to be modified in order to work in our simulator.

The signs were created using the Creator's Sign Wizard, so the numbers and letters have a different base texture than the box itself.

Now I want to use this difference to assign a different material code and comment to the numbers and letters other then the box.


I was thinking this could be done with a script, but I don't have much experience with scripting.


With the help of the sample codes I came this far, but this code only changes all polygons.

Maybe someone can help me on my way ?

Cheers!


  

def MatchNodeType (db, parent, rec, userData):
	if (mgGetCode (rec) == userData):
		return MG_TRUE
	return MG_FALSE


db = mgGetCurrentDb()
code = fltPolygon
mgDeselectAll (db)
nodes = mgFind (db, MatchNodeType, code, 0)
mgSelectList (nodes)

num = mgGetRecListCount (nodes)
name = ddGetLabel(code)

message = "Selected %d %s%c" % (num,name,('\0' if num==0 else 's'))
mgSendMessage (MMSG_STATUS, message)
paramBlock = mgGetParamBlock ("Modify Attributes")
mgParamSet (paramBlock, "Attribute Code", fltComment)
mgParamSet (paramBlock,"String Value",'<AttributeList><MATERIAL NAME="FIBREGLASS"/></AttributeList>' )
mgParamSet (paramBlock, "Attribute Code", fltPolySmc)
mgParamSet (paramBlock, "Integer Value", 12)
mgParamSet (paramBlock, "Attribute Code", fltPolyMaterial)
mgParamSet (paramBlock, "Integer Value", 64)
mgExecute ("Modify Attributes", paramBlock)

  


 Hi, this can easily be done by a script and if that is the rout you want to take, we can look into that in more detail. But first, did you try the select similar tools? Here is a quick workflow:


make sure no polygons selected

in the texture palette, select the texture you want to get the polygons for

run the select similar by texture command  (select menu, or hit space bar and start typing the command name)

Now all the faces using that texture are selected, just open modify attr tool and change all the attributes at once

repeat for sign box etc...



Hi Chris,

Thanks for getting back to me. Unfortunately, I haven't had the chance to look further into the code yet. I've been working on another project to create an airport, and as a non-programmer, I tend to rely on trial and error when working with code.

I am aware of the option to select similar polygons through the selection menu, and that works well in many situations. However, when working with models from third-party sources, like signs, buildings, and 2D objects, these are often in separate files with their own specific configurations. For example, signs have different materials than buildings. So, to set the parameters for the polygons, I would need to open each individual model, apply the settings, and then close and save the model. This can become tedious and time-consuming, especially after working on it for a few hours.

As I looked at the code, I noticed the line "if (mgGetCode (rec) == userData):" and it got me thinking that if I could set the "rec" for user data to a group name or texture index, it would find the corresponding items and set their parameters. But that's beyond my knowledge of the API.

Ideally, I would like to have a code that looks for a template.flt file, and finds the specific items I want to work with. For example, if the Groupname is "Airside", then all polygons below that should be set to the same parameters as the template.flt file. Or, in the case of signs, if the texture index is 12, then the material index should be set to 32. But I guess that this also needs to fetch its material palette?.

I don't want to ask for too much, but if you could help me out with just the "if" statement that selects all polygons that have an index of x, I would really appreciate it.

Thanks,

Stanley


Hi, I did a quick modification to the script to show one way to do this. I decided to switch it to a walk function because you get to see every node as it walks through the hierarchy. The main reason for this was to be able to know if the current polygon is under a node name you care about. 

The script works like this:

  • specify a rec code (fltPolygon etc...)
  • specify a node name (Building, Sign...)
  • the script will select all nodes of type code found under a node with the specified name
  • At this point you can run the modify attributes code. (I didn't change or look at your mod attr code)

Let me know if you have any questions or problems with it.

 

class params:
   pass
   
params.code = fltPolygon
params.parentName = 'Building'
params.inValidParent = False

def SelectNodesMatchingParams (db, parent, rec, params):
   if params.parentName == mgGetName(rec):
      params.inValidParent = True
      
   if mgGetCode(rec) == params.code and params.inValidParent:
      mgSelectOne(rec)
   return MG_TRUE
   
def SelectNodesPost (db, parent, rec, params):
   if params.parentName == mgGetName(rec):
      params.inValidParent = False
   return MG_TRUE
   
   
db = mgGetCurrentDb()
mgDeselectAll (db)
mgWalk(db, SelectNodesMatchingParams, SelectNodesPost, params, 0)


paramBlock = mgGetParamBlock ("Modify Attributes")
mgParamSet (paramBlock, "Attribute Code", fltComment)
mgParamSet (paramBlock,"String Value",'<AttributeList><MATERIAL NAME="FIBREGLASS"/></AttributeList>' )
mgParamSet (paramBlock, "Attribute Code", fltPolySmc)
mgParamSet (paramBlock, "Integer Value", 12)
mgParamSet (paramBlock, "Attribute Code", fltPolyMaterial)
mgParamSet (paramBlock, "Integer Value", 64)
mgExecute ("Modify Attributes", paramBlock)

 

Dear Chris, first and foremost, thank you for modifying the script.


This is a great step in the direction of standardizing our models with the "batch run script" editor and no longer having to make adjustments later on due to errors in the compiler that generates the final runtime files for our simulator.

I had to make some modifications to the script because it only adjusted the last line of the script, so I added the code "mgExecute("Modify Attributes", paramBlock)" after every command to adjust a parameter. After this, all parameters were adjusted.  

paramBlock = mgGetParamBlock ("Modify Attributes")
mgParamSet (paramBlock, "Attribute Code", fltComment)
mgParamSet (paramBlock,"String Value",'<AttributeList><MATERIAL NAME="FIBREGLASS"/></AttributeList>' )
mgExecute ("Modify Attributes", paramBlock)
mgParamSet (paramBlock, "Attribute Code", fltPolySmc)
mgParamSet (paramBlock, "Integer Value", 12)
mgExecute ("Modify Attributes", paramBlock)
mgParamSet (paramBlock, "Attribute Code", fltPolyMaterial)
mgParamSet (paramBlock, "Integer Value", 66)
mgExecute ("Modify Attributes", paramBlock)

 

 If you have time, I still have two questions:


1. Can you make the script use a specific (saved) material palette and/or texture mapping palette? That way, I can make all my files use the same material palette and in some cases, the same texture mapping.

2. In some cases, the models have a material that is related to their texture only. For example, a glass texture has a different material than a roof. From what I understand of the script, it looks at the parent name and in this case, that's a group name, so I don't think I can modify it to look at the texture index.


In any case, thank you for the effort! We are already very happy with this as a small department.


Kind regards, Stanley

1. Can you make the script use a specific (saved) material palette and/or texture mapping palette? That way, I can make all my files use the same material palette and in some cases, the same texture mapping.


Can you provide more specific information about this? Details of a before and after would be helpful. (start with model using default material palette and no polygons referencing materials, end with polygons with x texture now using material y etc...)


2. In some cases, the models have a material that is related to their texture only. For example, a glass texture has a different material than a roof. From what I understand of the script, it looks at the parent name and in this case, that's a group name, so I don't think I can modify it to look at the texture index.


Using parent is just an example. You can actually do pretty much anything you can imagine using the walk function. For for instance, you can test for the texture first and make a decision based on that test, then if the test fails, revert to the name based behavior.

If I create a 3D sign model (which I generate with the sign wizard), then all of the faces of that model have a value of -1 when it comes to the material attributes index. 


To make that model more useful in the simulator, I need to change a few things. 


1. I have a material palette with some settings and labels, so my first step is to add that material palette to the model using "Load material palette". 

2. Once that is done, I select all of the faces that have a value of Base Texture Attribute = 6 (box_black.rgb), then I open the properties of those polygons (ctrl+=) and give them a value of '65' for the Material Attribute Index in the input field. 

The remaining faces get a value of 66 for the material index. 


The material that they are assigned determines whether they will light up later in the simulator when it gets dark, but I'm not sure if only the property (Ambient, Diffuse, Specular, Emissive) of the material is taken into account or if the label of the name is also included in the lighting of the model. 


Additionally, all of the polygons will have a line of code added in the comment field, which determines what material they are made of. After that, the models will receive a Level of Detail (LOD) and they will be ready for use. But I think that a part of the code should reference the material palette that I added at some point.


I had looked into what the walk function does and asked chat GPT for advice, but according to the information I found, the walk function only works on the parent-level. So I thought that I couldn't use that function for the faces.


If everything goes well, I will have more time later this week and I will then sit down and try to change the settings of the polygons when they have a certain texture index, with the walk function. So ill be back, lol


image


Thanks, greetings!

Today I have been working on the mgwalk function, for a while.


However, I believe that my programming skills may not be sufficient to complete this task.

I have browsed through the Open flight API reference and, as far as I understand, the mgwalk function navigates through the db structure and its nodes and can execute some commands.

But I am unable to find how to read the texture attribute index from a face and then trigger a command.

In another topic on this forum, the Openflight API Developers Guide was mentioned as a reference but I am unable to locate it. There is a link to this guide under the help button of the Openflight script editor, but it is grayed out.


I did find the mgGettextureIndex and mgGetTextureName functions, but they refer to the texturepalette.


If it come to how to add a material palette using code, I am completely clueless.


If you have any advice and/or suggestions it would be highly appreciated! But I also do not mean to keep you up too much.


Kind regards,

Stanley

Hi Stanley,


Here is a quick script to show how you can modify faces in the walk function. I'll explain the key parts in smaller snips below

  

signBaseMaterialIdx = 65
signFaceMaterialIdx = 64
signBackTextureIdx = 6
signFrontTextureIdx = 2
materialMap = {signBackTextureIdx : signBaseMaterialIdx, 
               signFrontTextureIdx: signFaceMaterialIdx}

def VisitNodeDbPre (db, parent, rec, data): 
   if mgGetCode(rec) == fltPolygon:
      ok, code, texidx = mgGetAttList(rec, fltPolyTexture)
      if texidx in materialMap:
         mgSetAttList(rec, fltPolyMaterial, materialMap[texidx])
   return MG_TRUE

db = mgGetCurrentDb ()
mgWalk (db, VisitNodeDbPre, None, None, 0)

  

First the script defines a mapping that contains the knowledge needed for the modification

Basically we need to associate all of the texture indices we are looking for, with all the material indices we want to add to face that have that texure. This code is pure python, no OpenFlight API functions used. It can be done many ways, but I just defined a dictionary I called materialMap. to this dictionary I just added key values where the key is the texture index, and the value is the material index that should be associated with that texture.

 

signBaseMaterialIdx = 65
signFaceMaterialIdx = 64
signBackTextureIdx = 6
signFrontTextureIdx = 2
materialMap = {signBackTextureIdx : signBaseMaterialIdx, 
               signFrontTextureIdx: signFaceMaterialIdx}

 


Next we have our walk function. This function is called for potentially every node in the database, so you can just check to see if the current node (rec) is the one you are looking to modify, and if it is, do the modification. (just don't delete anything in the pre-walk function as it will mess up the walk.)


In this case, we just check to see if the current rec is a polygon, and if so we get the texure on layer 0 from it. If the texture is in our map we set up earlier, then we use the map to find the material associated and finally apply that material to the rec.

 

def VisitNodeDbPre (db, parent, rec, data): 
   if mgGetCode(rec) == fltPolygon:
      ok, code, texidx = mgGetAttList(rec, fltPolyTexture)
      if texidx in materialMap:
         mgSetAttList(rec, fltPolyMaterial, materialMap[texidx])
   return MG_TRUE

 

 Finally, we use the api to get the current db. This is how it is done when running inside of Creator's script editor and when running using the batch run script tool. If running standalone, you would instead open a db and would also need to init the api. Check out the api docs on how to run scripts standalone.


After we have the current db, we simply call walk and specify the walk function to do the work. The parameters to mgWalk have a few parameters that control how the walk should function. If you need to visit vertices, or visit only nodes that are visible, you can look into the parameters. In this situation, there is no user data we need the walk to have, and we want all nodes to be visited so we just pass zeros for these last parameters.

db = mgGetCurrentDb ()
mgWalk (db, VisitNodeDbPre, None, None, 0)

 

Hopefully this gives you the info you need so you can use your own texture indices and material indices to get your files set up.

 

I have browsed through the Open flight API reference and, as far as I understand, the mgwalk function navigates through the db structure and its nodes and can execute some commands.

 So the mgwalk lets you builds a callback function. This callback will be called for every node in the db. Basically you create a function and pass that function to mgWalk. Then mgWalk calls that function for every node in the database. This gives you the option to do basically anything you want to each node. (just avoid detach and delete in the prewalk). You can get attributes of the node, you can look at children, parents, find the textures applied to the node, select it, duplicate it... almost anything you want. 

Dear Chris, thank you for the code and for taking the time to explain how it works. 


Although I now understand what is happening and it seems simple and straightforward, I would never have thought of it myself. 


With this piece of code and the use of the group name as a trigger, we can simplify and speed up our workflow. 

Also, if I now want to expand an existing database, for example by drawing an extra piece of taxiway, I can now run the script afterwards and I'm done. 

Previously, I had to always check the group settings of those polygons and then adjust them and make sure I didn't miss any. 


I understand that this forum is not a question and answer service for code making, but I still see room for improvement in our workflow. Do you think that in future API releases, the number of example scripts will also be expanded? 


In my previous message, I also mentioned that I couldn't find the developer's guide, but I have now found it :)


Thank you again and have a nice weekend!

Login to post a comment