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

  • Saxon,

    I believe this should read:

    // create list for materials to save
    StringList* outputMaterialList =
        mdlStringList_create(0, mdlStringList_numInfoFields(materialList))

    Other than that, yes, it works!

    Cheers,

    Saxon Druce said:

    The following code combines approaches 1 and 3, loading the materials from the material table into an external palette file, and from there saving back into the local palette:

    mdlMaterial_initialize()

    // load all materials
    StringList* materialList
    mdlMaterial_loadTable(NULL, NULL, &materialList, NULL, NULL, ..., NULL)

    // create list for materials to save
    StringList* outputMaterialList =
        mdlStringList_create(0, mdlStringList_size(materialList))

    char* name
    long* infoFields

    // copy the materials in TestMaterials.pal into the output material list,
    // and set blue component of test1 material's colour to 1.0
    for (long i = 0; i < mdlStringList_size(materialList); i++)
    {
        mdlStringList_getMember(&name, &infoFields, materialList, i)
        MaterialProperties* material = (MaterialProperties*)infoFields[1]
        if (material->paletteName is "$(_DGNFILE)\\TestMaterials.pal")
        {
            if (name is "test1")
                material->color.blue = 1.0

            long index
            mdlStringList_insertMember(&index, outputMaterialList, -1, 1)
            mdlStringList_setMember(outputMaterialList, index, name, infoFields)
        }
    }

    // save the palette to an external temporary file
    mdlMaterial_savePalette(
        "c:\\temp\\test.pal",
        outputMaterialList)

    // free the original materials
    for (long i = 0; i < mdlStringList_size(materialList); i++)
    {
        mdlStringList_getMember(&name, &infoFields, materialList, i)
        MaterialProperties* material = (MaterialProperties*)infoFields[1]
        mdlMaterial_free(material)
    }
    mdlStringList_destroy(materialList)

    // free the output material list
    mdlStringList_destroy(outputMaterialList)

    // free the table
    mdlMaterial_freeTable(...)

    // load temporary external palette
    StringList* materialList = mdlStringList_create(0, 3)
    char outPaletteName[MAX_PaletteName]
    mdlMaterial_loadPalette(materialList, outPaletteName, "c:\\temp\\test.pal")

    // save as internal palette
    mdlMaterial_savePalette(
        "$(_DGNLIB)|c:\\path\\dgnfile.dgn|$(_DGNFILE)\\TestMaterials.pal",
        materialList)

    // free the materials
    for (long i = 0; i < mdlStringList_size(materialList); i++)
    {
        mdlStringList_getMember(&name, &infoFields, materialList, i)
        MaterialProperties* material = (MaterialProperties*)infoFields[1]
        mdlMaterial_free(material)
    }
    mdlStringList_destroy(materialList)

    mdlMaterial_cleanup()

     

    This approach works... but seems fairly convoluted.

    Is there an easier way?

    Thanks again,
    Saxon Druce

    Cheers,

    /Chris Z.

  • Hi Matt...

    BTW: How far are you with Luxology rendering hook? Did you have a time for it?

  • Saxon,

    It is not possible to reliably save internal materials using the public API. You may be able to get close, but the functions that you're calling are sufficiently state-dependent that it's very likely that you'll corrupt or destroy your existing internal material entries.

    We are aware that the public API for materials is far from ideal and are in the process of a complete rewrite of this system for the next release of MicroStation. The new system should make the kind of operations you're looking to perform fairly trivial.

    In the mean time, I will contact you offline to see if we can work something out with the current API.

    Thanks,
    Matt



  • Hi Jon,

    I think the $(_DGNLIB) and $(_DGNFILE) are just markers which are used to indicate an internal palette file, not variables which are expanded - the string I pass to mdlMaterial_savePalette() is not a real file path. Also I think the | character is just a delimiter, rather than an OR operation. I got the idea from this comment in material.h:

    #define MAX_PaletteName MAXFILELENGTH*2 + 32
    // palette name formated like "type|contents" ex. "$(_DGNLIB)|pathtodgnlib|palettename

     

    Replacing $(_DGNLIB) with its actual path, eg:

    mdlMaterial_savePalette(
        "C:\\ProgramData\\...\\interfacename.dgnlib|c:\\path\\dgnfile.dgn|$(_DGNFILE)\\TestMaterials.pal",
        outputMaterialList)

    Causes mdlMaterial_savePalette() to return MDLERR_CANNOTOPENFILE.

    Replacing $(_DGNFILE) with its actual path, eg:

    mdlMaterial_savePalette(
        "$(_DGNLIB)|c:\\path\\dgnfile.dgn|c:\\path\\dgnfile.dgn\\TestMaterials.pal",
        outputMaterialList)

    Produces a combination of strange behaviours:

    1. When saving a palette loaded by mdlMaterial_loadPalette(), the palette is successfully created / updated. However, the palette name gets converted to lower case (eg appears in the material editor as testmaterials instead of TestMaterials).
    2. After using mdlMaterial_loadPalette(external file), mdlMaterial_savePalette(internal file) then mdlMaterial_loadTable(), the palette name is now reported as c:\path\dgnfile.dgn\testmaterials.pal, instead of the value of $(_DGNFILE)\TestMaterials.pal which is reported for materials created manually using the material editor.
    3. When using approach 3 from yesterday's post (call mdlMaterial_loadTable() then extract materials from the desired palette into another list), mdlMaterial_savePalette() now returns SUCCESS (whereas yesterday's code returns MDLERR_CANNOTOPENFILE). However it doesn't actually succeed - the palette is deleted from the material editor instead of being updated.

    I don't think it is doing any translation of the config variables. For example if I replace the path to the dgn with $(_DGNFILE):

    mdlMaterial_savePalette(
        "$(_DGNLIB)|$(_DGNFILE)|$(_DGNFILE)\\TestMaterials.pal",
        outputMaterialList)

    Then mdlMaterial_savePalette() returns ERROR.

    Finally, if I follow the comment in material.h and use the path to the dgnlib instead of the dgn:

    mdlMaterial_savePalette(
        "$(_DGNLIB)|C:\\ProgramData\\...\\interfacename.dgnlib|$(_DGNFILE)\\TestMaterials.pal",
        outputMaterialList)

    Then the palette ends up being saved in the dgnlib, not in the active dgn where I want it.

    Saxon Druce

     

  • saxon:

    Previously I tried using this as the palette file name when calling mdlMaterial_savePalette():

    $(_DGNFILE)\TestMaterials.pal

    If that's a literal path then I'm astonished. In my experience, CfgVars have to be expanded to a fully-qualified file path before passing the string to an MDL functions that deals with files. Are the materials API file functions smarter than the rest?

    saxon:
    
    $(_DGNLIB)|c:\path\dgnfile.dgn|$(_DGNFILE)\TestMaterials.pal 
    // save the palette
    mdlMaterial_savePalette ("$(_DGNLIB)|c:\\path\\dgnfile.dgn|$(_DGNFILE)\\TestMaterials.pal", materialList)

    And that's even more astonishing! Can the API really parse a file path from three different expressions ORed together?

    Regards, Jon Summers
    LA Solutions

     
    Regards, Jon Summers
    LA Solutions

  • Hi all,

    Previously I tried using this as the palette file name when calling mdlMaterial_savePalette(), which is the palette name returned by mdlMaterial_loadTable(), in order to save an internal / local palette:

    $(_DGNFILE)\TestMaterials.pal

    This always returns MDLERR_CANNOTOPENFILE. I have now found that mdlMaterial_savePalette() can (sometimes) successfully save an internal palette by using this as the file name (including the path to the active dgn file):

    $(_DGNLIB)|c:\path\dgnfile.dgn|$(_DGNFILE)\TestMaterials.pal

    However, it only seems to work when saving a material list which has been loaded using mdlMaterial_loadPalette(), although mdlMaterial_loadPalette() itself still fails with ERROR (ie 0x8000) when using this file name.

    So for example the following code to load an external palette file, modify the colour of the test1 material, then save as an internal palette works:

    mdlMaterial_initialize()

    // load external palette
    StringList* materialList = mdlStringList_create(0, 3)
    char outPaletteName[MAX_PaletteName]
    mdlMaterial_loadPalette(materialList, outPaletteName, "c:\\temp\\test.pal")

    char* name
    long* infoFields

    // set blue component of test1 material's colour to 1.0
    for (long i = 0; i < mdlStringList_size(materialList); i++)
    {
        mdlStringList_getMember(&name, &infoFields, materialList, i)
        if (name is "test1")
        {
            MaterialProperties* material = (MaterialProperties*)infoFields[1]
            material->color.blue = 1.0
            break
        }
    }

    // save the palette
    mdlMaterial_savePalette(
        "$(_DGNLIB)|c:\\path\\dgnfile.dgn|$(_DGNFILE)\\TestMaterials.pal",
        materialList)

    // free the materials
    for (long i = 0; i < mdlStringList_size(materialList); i++)
    {
        mdlStringList_getMember(&name, &infoFields, materialList, i)
        MaterialProperties* material = (MaterialProperties*)infoFields[1]
        mdlMaterial_free(material)
    }
    mdlStringList_destroy(materialList)

    mdlMaterial_cleanup()

     

    The following code tries to load the list of materials from the material table and then save them, but the call to mdlMaterial_savePalette() fails:

    mdlMaterial_initialize()

    // load all materials
    StringList* materialList
    mdlMaterial_loadTable(NULL, NULL, &materialList, NULL, NULL, ..., NULL)

    char* name
    long* infoFields

    // set blue component of test1 material's colour to 1.0
    for (long i = 0; i < mdlStringList_size(materialList); i++)
    {
        mdlStringList_getMember(&name, &infoFields, materialList, i)
        if (name is "test1")
        {
            MaterialProperties* material = (MaterialProperties*)infoFields[1]
            material->color.blue = 1.0
            break
        }
    }

    // save the palette (fails with MDLERR_CANNOTOPENFILE)
    mdlMaterial_savePalette(
        "$(_DGNLIB)|c:\\path\\dgnfile.dgn|$(_DGNFILE)\\TestMaterials.pal",
        materialList)

    // free the materials
    for (long i = 0; i < mdlStringList_size(materialList); i++)
    {
        mdlStringList_getMember(&name, &infoFields, materialList, i)
        MaterialProperties* material = (MaterialProperties*)infoFields[1]
        mdlMaterial_free(material)
    }
    mdlStringList_destroy(materialList)

    // free the table
    mdlMaterial_freeTable(...)

    mdlMaterial_cleanup()

     

    The above code wouldn't be expected to work properly if there are multiple palettes, as it tries to save all materials into the TestMaterials palette.

    The following code overcomes this by building a list containing just the materials from the original TestMaterials palette. However it also fails when calling mdlMaterial_savePalette().

    mdlMaterial_initialize()

    // load all materials
    StringList* materialList
    mdlMaterial_loadTable(NULL, NULL, &materialList, NULL, NULL, ..., NULL)

    // create list for materials to save
    StringList* outputMaterialList =
        mdlStringList_create(0, mdlStringList_size(materialList))

    char* name
    long* infoFields

    // copy the materials in TestMaterials.pal into the output material list,
    // and set blue component of test1 material's colour to 1.0
    for (long i = 0; i < mdlStringList_size(materialList); i++)
    {
        mdlStringList_getMember(&name, &infoFields, materialList, i)
        MaterialProperties* material = (MaterialProperties*)infoFields[1]
        if (material->paletteName is "$(_DGNFILE)\\TestMaterials.pal")
        {
            if (name is "test1")
                material->color.blue = 1.0

            long index
            mdlStringList_insertMember(&index, outputMaterialList, -1, 1)
            mdlStringList_setMember(outputMaterialList, index, name, infoFields)
        }
    }

    // save the palette (fails with MDLERR_CANNOTOPENFILE)
    mdlMaterial_savePalette(
        "$(_DGNLIB)|c:\\path\\dgnfile.dgn|$(_DGNFILE)\\TestMaterials.pal",
        outputMaterialList)

    // free the original materials
    for (long i = 0; i < mdlStringList_size(materialList); i++)
    {
        mdlStringList_getMember(&name, &infoFields, materialList, i)
        MaterialProperties* material = (MaterialProperties*)infoFields[1]
        mdlMaterial_free(material)
    }
    mdlStringList_destroy(materialList)

    // free the output material list
    mdlStringList_destroy(outputMaterialList)

    // free the table
    mdlMaterial_freeTable(...)

    mdlMaterial_cleanup()

     

    The following code combines approaches 1 and 3, loading the materials from the material table into an external palette file, and from there saving back into the local palette:

    mdlMaterial_initialize()

    // load all materials
    StringList* materialList
    mdlMaterial_loadTable(NULL, NULL, &materialList, NULL, NULL, ..., NULL)

    // create list for materials to save
    StringList* outputMaterialList =
        mdlStringList_create(0, mdlStringList_size(materialList))

    char* name
    long* infoFields

    // copy the materials in TestMaterials.pal into the output material list,
    // and set blue component of test1 material's colour to 1.0
    for (long i = 0; i < mdlStringList_size(materialList); i++)
    {
        mdlStringList_getMember(&name, &infoFields, materialList, i)
        MaterialProperties* material = (MaterialProperties*)infoFields[1]
        if (material->paletteName is "$(_DGNFILE)\\TestMaterials.pal")
        {
            if (name is "test1")
                material->color.blue = 1.0

            long index
            mdlStringList_insertMember(&index, outputMaterialList, -1, 1)
            mdlStringList_setMember(outputMaterialList, index, name, infoFields)
        }
    }

    // save the palette to an external temporary file
    mdlMaterial_savePalette(
        "c:\\temp\\test.pal",
        outputMaterialList)

    // free the original materials
    for (long i = 0; i < mdlStringList_size(materialList); i++)
    {
        mdlStringList_getMember(&name, &infoFields, materialList, i)
        MaterialProperties* material = (MaterialProperties*)infoFields[1]
        mdlMaterial_free(material)
    }
    mdlStringList_destroy(materialList)

    // free the output material list
    mdlStringList_destroy(outputMaterialList)

    // free the table
    mdlMaterial_freeTable(...)

    // load temporary external palette
    StringList* materialList = mdlStringList_create(0, 3)
    char outPaletteName[MAX_PaletteName]
    mdlMaterial_loadPalette(materialList, outPaletteName, "c:\\temp\\test.pal")

    // save as internal palette
    mdlMaterial_savePalette(
        "$(_DGNLIB)|c:\\path\\dgnfile.dgn|$(_DGNFILE)\\TestMaterials.pal",
        materialList)

    // free the materials
    for (long i = 0; i < mdlStringList_size(materialList); i++)
    {
        mdlStringList_getMember(&name, &infoFields, materialList, i)
        MaterialProperties* material = (MaterialProperties*)infoFields[1]
        mdlMaterial_free(material)
    }
    mdlStringList_destroy(materialList)

    mdlMaterial_cleanup()

     

    This approach works... but seems fairly convoluted.

    Is there an easier way?

    Thanks again,
    Saxon Druce

  • Thanks Jon!

    That's cleared up a few things for me. It looks like my previous example for finding materials may have a memory leak. Here's a modified version in case anyone tries to use this code in the future:

    mdlMaterial_initialize()
    StringList* materialList
    mdlMaterial_loadTable(NULL, NULL, &materialList, NULL, NULL, ..., NULL)
    for (long i = 0; i < mdlStringList_size(materialList); i++)
    {
        char* name
        long* infoFields
        mdlStringList_getMember(&name, &infoFields, materialList, i)
        MaterialProperties* material = (MaterialProperties*)infoFields[1]
        if (name is "test1")
        {
            // process material here
        }
        mdlMaterial_free(material)
    }
    mdlStringList_destroy(materialList)
    mdlMaterial_freeTable(...)
    mdlMaterial_cleanup()

     

    It looks like you also used a constant for the number of info fields in the string list, when calling mdlMaterial_loadPalette():

    enum StringListInfo
    {
        nInfoFields = 2,
    };

     

    I found 2 was sufficient to read the palette (since the material is at offset 1), but 3 was required if resaving the palette (or else MicroStation would crash).

    Your code covers pretty much everything related to reading material info, thanks. However I'm still at a loss for how to edit and save materials. Can anyone else provide any tips?

    Thanks,
    Saxon Druce

  • saxon:

    Jon, I noticed you posted back in 2005 that you had successfully used mdlMaterial_loadPalette:

    Now that you mention it, it turns out I know rather more about the materials API than I thought. Attached are a handful of C++ classes that extract material information and write it as XML. There are notes scattered through the files that may, or may not, be helpful.

    Regards, Jon Summers
    LA Solutions

     
    Regards, Jon Summers
    LA Solutions

    materials.zip
  • Thanks Michael.

    MichaelStark:

    if you would save the palette instead of the table you could succeed (see mdlMaterial_savePalette).

    Do you know what filename I should use when calling mdlMaterial_savePalette(), to save an internal / local palette?

    My palette (in the material editor) is named 'TestMaterials'. The paletteList argument returned by mdlMaterial_loadTable(), and the paletteName member of the materials returned by my previous sample code, contain a palette name of '$(_DGNFILE)\TestMaterials.pal'. However if I pass '$(_DGNFILE)\TestMaterials.pal' to mdlMaterial_savePalette(), it returns a MDLERR_CANNOTOPENFILE error.

    Calling mdlMaterial_savePalette() with an external file name such as 'c:\temp\test.pal' seems to work fine.

    MichaelStark:

    AFAIK the table contains only the list used palettes and assignments.

    Thanks for that bit of info - that solved the problem I had last week with finding unassigned materials!

    So this is the code which successfully finds a material:

    mdlMaterial_initialize()
    StringList* materialList
    mdlMaterial_loadTable(NULL, NULL, &materialList, NULL, NULL, ..., NULL)
    for (long i = 0; i < mdlStringList_size(materialList); i++)
    {
        char* name
        long* infoFields
        mdlStringList_getMember(&name, &infoFields, materialList, i)
        if (name is "test1")
        {
            MaterialProperties* material = (MaterialProperties*)infoFields[1]
            // process material here
            break
        }
    }
    mdlMaterial_freeTable(...)
    mdlMaterial_cleanup()

     

    Curiously, the material pointers in the materialList are different to the ones in the material table. Previously I was modifying the colour of a material within the material table, but this has no effect when saving the palette. Instead I had to modify the colour of the material in the material list, and then pass that material list to mdlMaterial_savePalette().

    I also experimented with mdlMaterial_loadPalette(). This works fine with an external palette such as 'c:\temp\test.pal' which I had previously saved, but fails returning ERROR (ie 0x8000) when using the internal name of '$(_DGNFILE)\TestMaterials.pal'.

    This is the code I used to load a palette:

    StringList* materialList = mdlStringList_create(0, 3)
    char outPaletteName[MAX_PaletteName]
    mdlMaterial_loadPalette(materialList, outPaletteName, "c:\\temp\\test.pal")

     

    The value '3' for the number of info fields was based on the number of info fields in the material list returned by mdlMaterial_loadTable(). Less than 3 would succeed at loading the palette, but then crash if I tried to resave the loaded palette. Is there a more 'formal' specification of how to allocate the string list passed to mdlMaterial_loadPalette()?

    Jon, I noticed you posted back in 2005 that you had successfully used mdlMaterial_loadPalette():

    http://discussion.bentley.com/cgi-bin/dnewsweb.exe?cmd=article&group=bentley.microstation.v8.mdl&item=12730&utag=

    Did you also use 3 info fields back then?

    Thanks again,
    Saxon Druce

     

  • Hi Jon, 

    Jon Summers:

    Verify the status of API calls:

    Yes I do - I just removed the error checking (and the C# marshalling) from the post to make the code easier to follow.

    Saxon Druce