Usage of mdlMaterial_functions

Hi Folks,

Is there any body used mdlMaterial_functions in mdl programming.
Iam trying to use the material functions to assign or attach materails to the the building faces.
I found source code examples nowhere. Could you please any one share yours knowledge or any source codes ?.

Thanks in Advance

Nataraju

Parents
  • Nataraju:
    Is there any body used mdlMaterial_functions in MDL programming?

    You're right — there aren't any substantial examples.

    Getting and assigning materials is largely a matter of getting and chasing pointers. I'm not an expert in materials, so maybe someone else can chime in. Here's something to get you started:

    1. Get the material table for the current model
    2. Find the material you need
    3. Assign the material to a level or element
    
    MaterialNode    *pNode = NULL;
    DgnModelRefP	modelRef = ACTIVEMODEL;
    //Initialise materials for your MDL
    mdlMaterial_initialize ();
    mdlMaterial_getTable (&pNode, modelRef);
    if (NULL != pNode)
    {
       MaterialNode    *pCurrent = pNode;
       while (pCurrent)
       {
         //Do something with pCurrent
         pCurrent = pCurrent->next;
       }
       //	To Clean up the material Node when done use :
       mdlMaterial_deleteNode (&pNode, pNode, modelRef);
    }
    //Free materials for your MDL
    mdlMaterial_cleanup ();
    
    

    Regards, Jon Summers
    LA Solutions

     
    Regards, Jon Summers
    LA Solutions

Reply
  • Nataraju:
    Is there any body used mdlMaterial_functions in MDL programming?

    You're right — there aren't any substantial examples.

    Getting and assigning materials is largely a matter of getting and chasing pointers. I'm not an expert in materials, so maybe someone else can chime in. Here's something to get you started:

    1. Get the material table for the current model
    2. Find the material you need
    3. Assign the material to a level or element
    
    MaterialNode    *pNode = NULL;
    DgnModelRefP	modelRef = ACTIVEMODEL;
    //Initialise materials for your MDL
    mdlMaterial_initialize ();
    mdlMaterial_getTable (&pNode, modelRef);
    if (NULL != pNode)
    {
       MaterialNode    *pCurrent = pNode;
       while (pCurrent)
       {
         //Do something with pCurrent
         pCurrent = pCurrent->next;
       }
       //	To Clean up the material Node when done use :
       mdlMaterial_deleteNode (&pNode, pNode, modelRef);
    }
    //Free materials for your MDL
    mdlMaterial_cleanup ();
    
    

    Regards, Jon Summers
    LA Solutions

     
    Regards, Jon Summers
    LA Solutions

Children
  • Now, when there are new types of materials, I'm not sure, whether these functions will work.

    I found this on old forum (discussion.bentley.com):

    KIBODY* sphereP;
    MSElementDescr* edP;
    ULong elmFilePos;

    // create a new element
    mdlKISolid_beginCurrTrans(MASTERFILE);
    mdlKISolid_makeSphere(&sphereP, radius);
    mdlKISolid_bodyToElement(&edP, sphereP, TRUE, -1, 0, MASTERFILE);
    mdlKISolid_freeBody(sphereP);
    mdlKISolid_endCurrTrans();

    // Write it to the design file
    elmFilePos = mdlElmdscr_add(edP);

    // create a material attachment linkage
    {
    MaterialLinkageData data;
    LinkageHeader linkHdr;
    memset(&linkHdr, 0, sizeof(linkHdr));
    linkHdr.primaryID = LINKAGEID_Material;
    linkHdr.user = 1;
    strcpy(data.name, "Blue");
    data.nameSize = strlen(data.name);
    printf("appendUsingDescr\n");
    if (mdlLinkage_extractUsingDescr (&data, edP, LINKAGEID_Material, 0, NULL, NULL, NULL, FALSE))
    mdlLinkage_deleteUsingDescr (&edP, LINKAGEID_Material, 0, NULL, NULL, NULL, FALSE);
    if (mdlLinkage_appendUsingDescr(&edP, &linkHdr, &data,
    LINKAGEID_Material, NULL, FALSE) == SUCCESS)
    printf("success\n");
    else
    printf("no success\n");
    printf("end\n");
    }

     

  • I was able to attach a material to an element descriptor using the code posted by Dan Paul, with the following changes:

    1. After setting linkHdr.user, call mdlLinkage_setWords(&linkHdr, sizeof(MaterialLinkageData) / sizeof(short)).
    2. Pass zero instead of LINKAGEID_Material to mdlLinkage_appendUsingDescr(), otherwise it returns an error of MDLERR_DATADEFNOTFOUND.
    3. No need to call mdlLinkage_extractUsingDescr() or mdlLinkage_deleteUsingDescr().
    4. Call mdlElmdscr_rewrite() after the call to mdlLinkage_appendUsingDescr(), or move the call to mdlElmdscr_add() after mdlLinkage_appendUsingDescr().
    5. If the material being used is not already applied to another object in the scene, the material doesn't appear in the view until either the material editor is opened, or if it is already open, until it is refreshed. A call to mdlMaterial_cleanup() seemed to fix this.

    Saxon Druce

     

  • Saxon:
    1. After setting ...
    2. Pass zero ...
    3. No need to call mdlLinkage_extractUsingDescr() or mdlLinkage_deleteUsingDescr().
    4. Call mdlElmdscr_rewrite() ...
    5. A call to mdlMaterial_cleanup() ...

    The material library is tortuous, so congrats. on navigating a path through the maze.

    1.  
    2.  
    3. No need to call mdlLinkage_extractUsingDescr() or mdlLinkage_deleteUsingDescr().
      They are there simply to illustrate how to remove a material if you want to replace an existing material with another. In this example you have a new element, so those calls don't do anything.
    4. Call mdlElmdscr_rewrite() ....
      You have to update the element in the file for your change to be persistent.
    5. A call to mdlMaterial_cleanup() ....
      Your material functions should be bracketted by mdlMaterial_initialize/mdlMaterial_cleanup. If writing C++, then encapsulate those calls in a class.

    Resource Acquisition Is Initialisation

    Put matched calls from the MDL API into a C++ struct or class constructor/destructor. That way, you can't forget the second of the matched pair:

    
    //	Class definition/implementation in MaterialAPI.h
    #include <material.fdf>
    struct CMaterialAPI
    {
        	CMaterialAPI	()	{	mdlMaterial_initialize ();	};
        	~CMaterialAPI	()	{	mdlMaterial_cleanup ();		};
    };
    //	Usage in myApp.cpp
    #include "MaterialAPI.h"
    {
         CMaterialAPI material_raii;	//	Calls mdlMaterial_initialize
          ... your code that uses mdlMaterial_xxx
    } //	Destructor called when CMaterialAPI goes out of scope: calls mdlMaterial_cleanup
    
    

    Regards, Jon Summers
    LA Solutions

     
    Regards, Jon Summers
    LA Solutions

  • Saxon,

    Thanks for  sharing your experience.

    One thing: if you like to use the rewrite function, you definitely should use one of the mdlLinkage_delete... functions, otherwise you will end up with an element that has more than one material linkages.

    If you need to extract the data before, is another question, but I think an app should react when a material was previously attached.

    If you only work on newly build elements, there's really no need  to test and/or delete.

    Michael



  • Hi Jon and Michael,

    3/4: Yes, in both the sample and in my code the material is being applied to a new element, so the mdlLinkage_extract() / mdlLinkage_delete() isn't necessary for that reason. Probably moving mdlElmdscr_add() to the end instead of using mdlElmdscr_rewrite() is the better solution.

    5: I'm using C# - so could use the equivalent IDisposable/using. Curiously, calling mdlMaterial_initialize() doesn't seem to make a difference.

    Now that I'm able to attach a material to an element, the next step is loading/creating/editing materials.

    I've tried the following 'find' approach to load an existing material (visible in the material editor) named 'test1':

    mdlMaterial_initialize()
    mdlMaterial_load(...)
    MaterialProperties* material = mdlMaterial_create()

    mdlMaterial_find(material, ..., 0, 0, "test1")
    // or
    mdlMaterial_findAny(material, ..., 0, 0, "test1")
    // or
    mdlMaterial_findByLevelName(material, ..., NULL, 0, "test1")
    mdlMaterial_free(material)
    mdlMaterial_cleanup()

     

    And I've also tried the following 'table' approach to loop through all materials to find 'test1':

    mdlMaterial_initialize()
    mdlMaterial_load(...)
    MaterialNode* node;
    mdlMaterial_getTable(&node, ...)
    while (node)
    {
        // check if node->materialP->name is "test1"

        node = node->next
    }
    mdlMaterial_freeTable(...)
    mdlMaterial_cleanup()

     

    mdlMaterial_find() always seems to return MDLERR_NOMATCH.

    mdlMaterial_findAny(), mdlMaterial_findByLevelName() and the table approach only seem to find the material if it is currently assigned to a level/colour (regardless of whether any elements exist of that level/colour).

    If the material isn't assigned to a level/colour, but if an element has the material attached to it when the model is first loaded, then the material isn't found by either of the above approaches. However, if I attach the material to an element (either manually using the material editor, or using mdlLinkage_appendUsingDescr() as described yesterday), then the next run through either of the above approaches successfully finds the test1 material. But then second (and subsequent) runs of the above code don't find the test1 material again.

    Is there something I'm missing?

    Thanks,
    Saxon Druce

  • Well, you're well and truly inside the material API maze!

    saxon:

    I'm using C# - so could use the equivalent IDisposable/using.

    MicroStation has its own internal memory management model. The material API performs a lot of allocation for you: I would hesitate to mix that with C# garbage model.

    saxon:

    Curiously, calling mdlMaterial_initialize() doesn't seem to make a difference.

    It may not have made a difference so far, but I'm sure it's there for a purpose. Since it appears not to make a difference, there's no harm in leaving it there.

    Regards, Jon Summers
    LA Solutions

     
    Regards, Jon Summers
    LA Solutions

  • Hi Jon,

    Jon Summers:

    MicroStation has its own internal memory management model. The material API performs a lot of allocation for you: I would hesitate to mix that with C# garbage model.

    C# garbage collection won't touch the unmanaged objects from MicroStation - you need to free them yourself like in C++. So I could use disposable wrapper objects or try/finally, or a combination of the two, for example:

    internal class MaterialAPI : IDisposable
    {
        internal MaterialAPI() { mdlMaterial_initialize(); }
        internal void Dispose() { mdlMaterial_cleanup(); }
    }

    ...

    using (MaterialAPI materialAPI = new MaterialAPI())
    {
        IntPtr material = mdlMaterial_create();
        try
        {
            mdlMaterial_findAny(material, ..., 0, 0, "test1");

            ...
        }
        finally
        {
            mdlMaterial_free(material);
        }
    }

     

    Jon Summers:

    It may not have made a difference so far, but I'm sure it's there for a purpose. Since it appears not to make a difference, there's no harm in leaving it there.

    Agreed, best to call it just to be 'safe'.

    Saxon Druce

  • Hi all,

    For now I am working around the issue of not being able to find existing materials (I have just assigned the material to a level / colour so that I can find it). So next I am trying to modify and save a material.

    This is the code I've tried, to change the blue component of a material's colour from 0.0 to 1.0:

    mdlMaterial_initialize()

    // load table
    char tableName[MAX_TableNameLength]
    StringList* paletteList
    StringList* materialList
    StringList* parameterList
    MaterialNode* table
    mdlMaterial_loadTable(tableName, &paletteList, &materialList, &parameterList, &table, ..., NULL)
    // loop through, to set blue to 1.0
    MaterialNode* node
    node = table
    while (node)
    {
        if (node->materialP->name is "test1")
        {
            node->materialP->color.blue = 1.0
            break
        }
        node = node->next
    }
    // save material table with modifications (?)
    mdlMaterial_saveTable(tableName, table, paletteList, parameterList)

    // done with table
    mdlMaterial_freeTable(...)

    // reload table to check if the modification has been saved
    mdlMaterial_loadTable(tableName, &paletteList, &materialList, &parameterList, &table, ..., NULL)
    node = table
    while (node)
    {
        if (node->materialP->name is "test1")
        {
            // node->materialP->color.blue has been set back to 0.0 here
            break
        }
        node = node->next
    }
    mdlMaterial_freeTable(...)

    mdlMaterial_cleanup()

     

    In the above example, the second iteration over the material nodes shows that the value hasn't been updated (and it also appears unchanged in the material editor).

    What is the correct approach to save changes to materials?

    Thanks again,
    Saxon Druce

  • saxon:
    mdlMaterial_loadTable(tableName, &paletteList, &materialList, &parameterList, &table, ..., NULL)

    Verify the status of API calls:

    
    StatusInt status = mdlMaterial_loadTable(tableName, &paletteList, &materialList, &parameterList, &table, ..., NULL)
    if (SUCCESS == status) { } else {    switch (status)    {     case MDLERR_CANNOTOPENFILE:     ...     break;     case MDLERR_INSFMEMORY:     ...     break;     default:     // Unknown error     break;    } }

    Regards, Jon Summers
    LA Solutions

     
    Regards, Jon Summers
    LA Solutions

  • Saxon.

    if you would save the palette instead of the table you could succeed (see mdlMaterial_savePalette). AFAIK the table contains only the list used palettes and assignments.

    HTH Michael