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
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
/Chris Z.
Hi Matt...
BTW: How far are you with Luxology rendering hook? Did you have a time for it?
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:
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
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)
$(_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
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:
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()
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:
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.
Thanks Michael.
MichaelStark: if you would save the palette instead of the table you could succeed (see mdlMaterial_savePalette).
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?
Jon Summers: Verify the status of API calls:
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.