@sofia_feist,
I went in and tried my suggestion.
It does work. here's the code in VB (where RDB = Revit.DB and RDV =Revit.DB.Visual):
'Forum Issue resolution
Public Sub ReplaceEmptyAppearanceAssets(thisDoc As RDB.Document)
Dim printStr As String = "Invalid Assets: " & vbCrLf
'collect document materials
Dim matCollector As RDB.FilteredElementCollector = New RDB.FilteredElementCollector(thisDoc)
Dim allMats As IList(Of RDB.Material) = matCollector.OfClass(GetType(RDB.Material)).OfType(Of RDB.Material).ToList
'collect document appearance Asset Elements
Dim assetCollector As New RDB.FilteredElementCollector(thisDoc)
Dim allAppearanceAssets As IList(Of RDB.AppearanceAssetElement) = assetCollector.OfClass(GetType(RDB.AppearanceAssetElement)) _
.OfType(Of RDB.AppearanceAssetElement).ToList
'create a list of asset names in use
Dim currentNames As New List(Of String)
For Each appAsset As RDB.AppearanceAssetElement In allAppearanceAssets
If currentNames.Contains(appAsset.Name) = False Then
currentNames.Add(appAsset.Name)
End If
Next
'prep for creating Asset if required
'this takes a while on first run to expand the default library,
'could be moved to seperate function and called if neede
Dim assetList As List(Of RDV.Asset) = thisDoc.Application.GetAssets(RDV.AssetType.Appearance)
Dim genericAsset As RDV.Asset = Nothing
'really shouldn't start a transaction unless modification is needed...
Using thisTrans As New RDB.Transaction(thisDoc, "Create new material")
thisTrans.Start()
Dim nameStr As String = ""
'parse materials looking for invalid Appearance Assets
Dim thisAssetElem As RDB.AppearanceAssetElement
For Each thisMat As RDB.Material In allMats
If thisMat.AppearanceAssetId <> RDB.ElementId.InvalidElementId Then
Dim renderAssetElem As RDB.AppearanceAssetElement = TryCast(thisDoc.GetElement(thisMat.AppearanceAssetId), RDB.AppearanceAssetElement)
If renderAssetElem IsNot Nothing Then
'Check to see if it's fully defined
'from API help
'AppearanceAssetElement.GetRenderingAsset
'The retrieved Asset may be empty if it is loaded from material library without any modification.
'In this case, you can use Application.GetAssets(AssetType.Appearance) to load all preset appearance assets,
'and retrieve the asset by its name.
Dim thisAsset As RDV.Asset = renderAssetElem.GetRenderingAsset()
If thisAsset.Size = 0 Then
printStr += "Invalid Asset Size in Material: " & thisMat.Name & " - Asset: " & renderAssetElem.Name & vbCrLf
genericAsset = assetList.FirstOrDefault(Function(eachAsset) eachAsset.Name = thisAsset.Name)
'We could read the default properties directly from this genericAsset or
'create new as duplicate
'the following would be a seperate function due to replication
nameStr = renderAssetElem.Name
Dim numb As Integer = 1
Dim testStr As String = nameStr
Do While currentNames.Contains(testStr) = True
testStr = nameStr & "_" & numb.ToString()
numb = numb + 1
Loop
nameStr = testStr
If genericAsset IsNot Nothing Then
printStr += " : Duplicating Asset :" & nameStr & vbCrLf
Dim newAssetElem As RDB.AppearanceAssetElement = RDB.AppearanceAssetElement.Create(thisDoc, nameStr, genericAsset)
thisMat.AppearanceAssetId = newAssetElem.Id
currentNames.Add(nameStr)
Else
printStr += " !! Could not aquire Asset !!" & vbCrLf
End If
End If
Else
printStr += "Cannot aquire Asset from Material: " & thisMat.Name & vbCrLf
End If
Else
'from API help
'Material.AppearanceAssetId
'The id of the AppearanceAssetElement, or InvalidElementId if the material does not have an associated appearance asset.
'This is the id to the element that contains visual material information used for rendering.
'In some cases where the material is created without setting up custom render appearance properties
'(for example, when the material is created via an import, or when it is created by the API),
'this property will be InvalidElementId. In that situation the standard material properties
'such as Color and Transparency will dictate the appearance of the material during rendering.
printStr += "Invalid Asset ID in Material: " & thisMat.Name & vbCrLf
'create new from existing generic
'the following would be a seperate function due to replication
nameStr = thisMat.Name
Dim numb As Integer = 1
Dim testStr As String = nameStr
Do While currentNames.Contains(testStr) = True
testStr = nameStr & "_" & numb.ToString()
numb = numb + 1
Loop
nameStr = testStr
genericAsset = assetList.FirstOrDefault(Function(eachAsset) eachAsset.FindByName(RDV.Generic.GenericDiffuse) IsNot Nothing)
If genericAsset IsNot Nothing Then
printStr += " : Creating Asset :" & nameStr & vbCrLf
Dim newAssetElem As RDB.AppearanceAssetElement = RDB.AppearanceAssetElement.Create(thisDoc, nameStr, genericAsset)
thisMat.AppearanceAssetId = newAssetElem.Id
currentNames.Add(nameStr)
Else
printStr += " !! Could not aquire Asset from App !!" & vbCrLf
End If
End If
Next
thisTrans.Commit()
End Using
RUI.TaskDialog.Show("Material Info", printStr)
End Sub
You will also notice a couple of comments taken from the API help that directly answer your original question of "Why is... empty..." and further to those that do not have a valid AppearanceAssetId
Running this code on your posted file found many materials with no Appearance Asset Element as well as many Assets with size=0. It creates valid Assets in both cases.
This was a good exercise, thanks for the question and I hope this helps you in some way.
-G
Gary J. Orr
GaryOrrMBI (MBI Companies 2014-Current)
aka (past user names):
Gary_J_Orr (GOMO Stuff 2008-2014);
OrrG (Forum Studio 2005-2008);
Gary J. Orr (LHB Inc 2002-2005);
Orr, Gary J. (Gossen Livingston 1997-2002)