Lukáš kohoulu3 Kohout
brought up an interesting and important issue in
the Revit API discussion forum thread
on changing material texture path with EditScope
that
I wanted to highlight last year. Here it is, finally:
ChangeRenderingTexturePath
method I am trying to set material texture path using following code (Revit 2018):
public void ChangeRenderingTexturePath( Material mat, Document doc, string texturePath ) { AppearanceAssetElement appearanceAsset = (AppearanceAssetElement) doc.GetElement( mat.AppearanceAssetId ); Asset renderingAsset = appearanceAsset .GetRenderingAsset(); int size = renderingAsset.Size; for( int assetIdx = 0; assetIdx < size; assetIdx++ ) { AssetProperty aProperty = renderingAsset[assetIdx]; if( aProperty.NumberOfConnectedProperties < 1 ) continue; Asset connectedAsset = aProperty.GetConnectedProperty( 0 ) as Asset; if( connectedAsset.Name == "UnifiedBitmapSchema" ) { AssetPropertyString path = connectedAsset.FindByName( UnifiedBitmap.UnifiedbitmapBitmap ) as AssetPropertyString; using( AppearanceAssetEditScope editScope = new AppearanceAssetEditScope( doc ) ) { Asset editableAsset = editScope.Start( mat.AppearanceAssetId ); // Exception thrown, asset is read only, // need to use editScope path.Value = texturePath; editScope.Commit( true ); } } } }
I know that editScope accepts only ElementId (specifically AppearanceElementAsset.Id), but with this I cannot get to edit the texture path that is saved in RenderingAsset
of the AppearanceAsset
.
Do anyone know any way to get around this?
Finally, we can create materials with API, but without textures? Does not make sense.
I am using the latest Revit 2018 API with the asset editing functionality included.
I am able to duplicate and edit AppearanceAsset
.
I have searched The Building Coder and read both the relevant posts:
The way I obtain the current texture path is taken directly from the latter.
Problem with the texture path as I see it is, that it is not directly contained in the AppearanceAsset
but in the connected RenderingAsset
. And to that I cannot get through the EditScope
. RenderingAsset
has no ElementId
and cannot be used in the AppearanceAssetEditScope.Start
function.
Exception message:
An exception of type 'Autodesk.Revit.Exceptions.InvalidOperationException' occurred in RevitAPI.dll but was not handled in user code. Additional information: The asset property is not editable. Asset can be edited only in an edit scope.
Problem is that the returned asset from AppearanceAssetEditScope.Start
does not contain the desired UnifiedBitmapSchema
property. That property is contained only in the RenderingAsset
.
The only editable asset inside the edit scope is the one returned from AppearanceAssetEditScope.Start
, and those connected assets which it directly returns.
I think if you reverse your code and get the UnifiedBitmapSchema
from the returned asset from Start
, you will be able to change it.
I'd recommend starting from the code snippet which shows how to do this, available from the help documentation on GetSingleConnectedAsset
.
Thanks Jeremy. Finally, I got it to work. It was quite simple, actually, in the end.
There is just one small change from the code snippet I posted at first.
Here is the (MaterialTexturePath.zip archive)[zip/lk_MaterialTexturePath.zip] containing the entire sample Visual Studio solution with detailed guide as well as a sample project and texture to demonstrate.
I hope that someone finds this useful.
Here is the final code:
void ChangeRenderingTexturePath( Document doc ) { // As there is only one material in the sample // project, we can use FilteredElementCollector // and grab the first result Material mat = new FilteredElementCollector( doc ) .OfClass( typeof( Material ) ) .FirstElement() as Material; // Fixed path for new texture // Texture included in sample files string texturePath = Path.Combine( Environment.GetFolderPath( Environment.SpecialFolder.Desktop ), "new_texture.png" ); using( Transaction t = new Transaction( doc ) ) { t.Start( "Changing material texture path" ); using( AppearanceAssetEditScope editScope = new AppearanceAssetEditScope( doc ) ) { Asset editableAsset = editScope.Start( mat.AppearanceAssetId ); // Getting the correct AssetProperty #if VERSION2018 AssetProperty assetProperty = editableAsset["generic_diffuse"]; #elif VERSION2019 AssetProperty assetProperty = editableAsset.FindByName("generic_diffuse"); #endif Asset connectedAsset = assetProperty .GetConnectedProperty( 0 ) as Asset; // Getting the right connected Asset if( connectedAsset.Name == "UnifiedBitmapSchema" ) { AssetPropertyString path = connectedAsset.FindByName( UnifiedBitmap.UnifiedbitmapBitmap ) as AssetPropertyString; if( path.IsValidValue( texturePath ) ) path.Value = texturePath; } editScope.Commit( true ); } TaskDialog.Show( "Material texture path", "Material texture path changed to:\n" + texturePath ); t.Commit(); t.Dispose(); } }
Here are the steps to reproduce the solution:
Goal:
That is quite simple with the new Revit 2018.1 API and the sample code provided.
Furthermore, for some materials, I want to change the texture as well, and that was bit of a problem.
Solution:
AssetProperty
from the asset returned from AppearanceAssetEditScope.Start
.ConnectedProperty
as Asset
.UnifiedBitmap.UnifiedbitmapBitmap
AssetPropertyString
.Guide to reproduce the solution:
Here are screen snapshots illustrating some of the steps from my test run:
Store the sample texture file on desktop:
Open the sample model and observe the instructions:
The sample model contains one single sample material:
Click the sample command in its custom ribbon panel:
The add-in displays a message on completion:
The sample material texture is updated:
Many thanks to Lukáš for his research, putting together and documenting this sample so well!