Archicad C++ API
About Archicad add-on development using the C++ API.

Replacing Surface Materials based on MaterialName.

KirthikaSrinivasan-L
Participant

Hi! I'm looking into ways to replace Surface Material (API_MaterialID), based on the name of the Material (after downloading the Material into the project). If I already know that my project has a material A applied to a window/door/wall/slab, and I want to download and import a material say B into the Project and replace all the instances of A in the project, with B, I'm unsure how to replace the instances. 

I tried getting the Attribute Indices of A and B and replacing B's 

Attr.header.typeID, Attr.header.index, Attr.material and Attr.material.texture.fileLoc values with A's corresponding values and deleting the A Attribute fully. But this doesn't seem to work. 

Please let me know if there is any other way to make this replacement work, on surface material level.

 

Edit: Let me rename the elements as plain A and B, as I think A1 and B1 is creating some confusions. 

Thank you in Advance.

 

 

API_AttributeIndex FindSurfaceMaterialIndex(const char* MaterialName)
{
	API_AttrTypeID	typeID;
	GSErrCode		err = NoError;
	API_AttributeIndex test;

	for (typeID = API_FirstAttributeID; typeID <= API_LastAttributeID; ((Int32&)typeID)++) {
		GS::Array<API_Attribute> attributes;
		err = ACAPI_Attribute_GetAttributesByType(typeID, attributes);
		if (err != NoError) {
			DBPrintf("Error in ACAPI_Attribute_GetAttributesByType", err);
			continue;
		}

		if (typeID == API_MaterialID)
		{
			short count = 1;
			for (API_Attribute& attrib : attributes) {
				char guidStr[64];
				APIGuid2GSGuid(attrib.header.guid).ConvertToString(guidStr);

				if (strcmp(attrib.header.name, MaterialName) == 0) {  // Match found
					test = attrib.header.index;
					return test;
				}
				count++;  //index loc for reference
			}
		}

	}
	return ACAPI_CreateAttributeIndex(0);
}

// Function to replace a surface material
GSErrCode	ReplaceSurfaceMaterial(const char* oldMaterialName, const char* newMaterialName) {
	API_AttributeIndex oldAttribute = FindSurfaceMaterialIndex(oldMaterialName);
	API_AttributeIndex newAttribute = FindSurfaceMaterialIndex(newMaterialName);

	API_Attribute oldAttr, newAttr;
	BNZeroMemory(&oldAttr, sizeof(API_Attribute));
	BNZeroMemory(&newAttr, sizeof(API_Attribute));

	oldAttr.header.typeID = API_MaterialID;
	oldAttr.header.index = oldAttribute;

	newAttr.header.typeID = API_MaterialID;
	newAttr.header.index = newAttribute;

	if (ACAPI_Attribute_Get(&oldAttr) != NoError) {
		return 0;
	}

	// Copy all properties from the new material to the old material
	newAttr.material = oldAttr.material;  
	
	newAttr.material.texture.fileLoc = oldAttr.material.texture.fileLoc;

	char guidStr[64];
	APIGuid2GSGuid(newAttr.header.guid).ConvertToString(guidStr);

	DBPrintf("\n");
	DBPrintf("  [%3s]   {%s}  \"%s\"", newAttr.header.index.ToUniString().ToCStr().Get(), guidStr, newAttr.header.name);

	GSErrCode err2 = ACAPI_Attribute_Delete(oldAttr.header);

	newAttr.header.index = oldAttr.header.index;

	GSErrCode err = ACAPI_Attribute_Modify(&newAttr, nullptr);
	if (err != NoError) {
		return err;
	}

	if (ACAPI_Attribute_Get(&newAttr) != NoError) {
		return 0;
	}

	return NoError;
}

 

 

 

 

4 REPLIES 4
Lingwisyer
Guru

The API sub can be found here for future reference.

 

@Barry Kelly or someone can move this thread for you when they see it.

AC22-28 AUS 3110Help Those Help You - Add a Signature
Self-taught, bend it till it breaksCreating a Thread
Win11 | i9 10850K | 64GB | RX6600 Win11 | R5 2600 | 16GB | GTX1660
Lingwisyer
Guru

I am no C++ coder, but are you replacing the information within each material with that of your desired one? Would you not just completely replace the material;

  1. parse material library for "const char", write result to A1 to An
  2. find index of A1 to An
  3. store string Prior to "const char" for A1 to An
  4. store string After "const char" for A1 to An
  5. delete A1 to An from material library
  6. duplicate B1 => B2 to Bn
  7. replace the index of B2 to Bn with the stored values from A1 to An?
  8. ammend name B2 to Bn with Prior "const char" After

Or is that just really memory inefficient?

Or maybe that is what you have done, I am no C++ coder...

AC22-28 AUS 3110Help Those Help You - Add a Signature
Self-taught, bend it till it breaksCreating a Thread
Win11 | i9 10850K | 64GB | RX6600 Win11 | R5 2600 | 16GB | GTX1660
LChen
Graphisoft
Graphisoft

Hi, Your code shows me that you deleted the oldAttr, then you try to use the oldAttr index to modify...

newAttr.header.index = oldAttr.header.index;
ACAPI_Attribute_Modify(&newAttr,...

If you'd modify a material attribute, the material should not be removed.
HTH.

KirthikaSrinivasan-L
Participant

@LChen 

Thank you for your reply!

 

That doesn't fix the problem. The replacement of the Surface Material still doesn't seem to work.

 

For example, I want the Surface Material used in the API_WindowID and API_DoorID to be overwritten with a new Surface Material, based on the Material Name.

 

Please let me know how to do this. 

 

Didn't find the answer?

Check other topics in this Forum

Back to Forum

Read the latest accepted solutions!

Accepted Solutions

Start a new conversation!