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
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:
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
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"); }
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:
Saxon Druce
Saxon: After setting ... Pass zero ... No need to call mdlLinkage_extractUsingDescr() or mdlLinkage_deleteUsingDescr(). Call mdlElmdscr_rewrite() ... A call to mdlMaterial_cleanup() ...
The material library is tortuous, so congrats. on navigating a path through the maze.
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
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.
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.
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.
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'.
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, ¶meterList, &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, ¶meterList, &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, ¶meterList, &table, ..., NULL)
mdlMaterial_loadTable(tableName, &paletteList, &materialList, ¶meterList, &table, ..., NULL)
Verify the status of API calls:
StatusInt status = mdlMaterial_loadTable(tableName, &paletteList, &materialList, ¶meterList, &table, ..., NULL) if (SUCCESS == status) { } else { switch (status) { case MDLERR_CANNOTOPENFILE: ... break; case MDLERR_INSFMEMORY: ... break; default: // Unknown error break; } }
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