public static List <TextureBundle> GetTextureBundles(Document doc, out List <string> paths)
        {
            if (_bundleCache.ContainsKey(doc.PathName))
            {
                paths = _texturePathCache[doc.PathName];
                return(_bundleCache[doc.PathName]);
            }
            _texturePathCache.Add(doc.PathName, new List <string>());

            // Find materials
            FilteredElementCollector fec = new FilteredElementCollector(doc).OfClass(typeof(Material));

            // Convert materials to bundles
            List <TextureBundle> bundles = new List <TextureBundle>();

            foreach (var m in fec.Cast <Material>())
            {
                try
                {
                    var bundle = new TextureBundle(m);

                    ElementId appearanceAssetId = m.AppearanceAssetId;
                    AppearanceAssetElement appearanceAssetElem
                        = doc.GetElement(appearanceAssetId)
                          as AppearanceAssetElement;

                    if (appearanceAssetElem == null)
                    {
                        continue;
                    }

                    Asset asset = appearanceAssetElem
                                  .GetRenderingAsset();

                    if (asset == null)
                    {
                        continue;
                    }

                    for (int assetIdx = 0; assetIdx < asset.Size; assetIdx++)
                    {
                        AssetProperty aProperty = asset[assetIdx];
                        if (aProperty.NumberOfConnectedProperties < 1)
                        {
                            continue;
                        }

                        Asset connectedAsset = aProperty
                                               .GetConnectedProperty(0) as Asset;

                        // See if there is a path associated.
#if REVIT2018 || REVIT2019 || REVIT2020
                        // This line is 2018.1 & up because of the
                        // property reference to UnifiedBitmap
                        // .UnifiedbitmapBitmap.  In earlier versions,
                        // you can still reference the string name
                        // instead: "unifiedbitmap_Bitmap"
                        AssetPropertyString path = connectedAsset[
                            UnifiedBitmap.UnifiedbitmapBitmap]
                                                   as AssetPropertyString;
#else
                        AssetPropertyString path =
                            connectedAsset["unifiedbitmap_Bitmap"] as AssetPropertyString;
#endif
                        // If there is no asset path, nothing to pursue (Empty field)
                        if (path == null || String.IsNullOrEmpty(path.Value))
                        {
                            continue;
                        }

                        // See what kind of texture it is.
                        if (TryGetTextureTypeFromAssetName(connectedAsset.Name, out var t))
                        {
                            // This will be a relative path to the
                            // built -in materials folder, addiitonal
                            // render appearance folder, or an
                            // absolute path.
                            string assetName = Path.GetFileNameWithoutExtension(path.Value);

                            // Ensure that we have a valid texture path.
                            if (RevitTextures.ContainsKey(assetName))
                            {
                                bundle.TexturePaths.Add(t, new SafenedFilename(RevitTextures[assetName]));
                            }
                            else
                            {
                                //Logger.LogError(
                                //    $"Found asset outisde of Revit material lib: {path.Value}. Could not add to export"
                                //    );
                            }
                        }
                    }

                    // Return the bundle we created.
                    bundles.Add(bundle);
                }
                catch (Exception e)
                {
                    //Logger.LogException("Error in bundle creation: ", e);
                }
            }

            var bundleList = bundles.Where(b => b != null).ToList();
            _bundleCache.Add(doc.PathName, bundleList);
            paths = _texturePathCache[doc.PathName];

            return(bundleList);
        }
        public static Assimp.Material ConvertToAssimpMaterial(TextureBundle bundle, Document doc)
        {
            // Create new material with base props
            // from the Revit material
            var newmat = new Assimp.Material()
            {
                Opacity      = bundle.Material.GetOpacity(),
                Reflectivity = 0f,
                Name         = bundle.Material.Name,
                ColorDiffuse = bundle.Material.ToColor4D()
            };

            // Extract base properties from revit material
            ElementId appearanceAssetId            = bundle.Material.AppearanceAssetId;
            AppearanceAssetElement appearanceAsset = doc.GetElement(appearanceAssetId) as AppearanceAssetElement;
            Asset renderingAsset = appearanceAsset.GetRenderingAsset();
            RenderAppearanceDescriptor rad
                = new RenderAppearanceDescriptor(renderingAsset);
            PropertyDescriptorCollection collection          = rad.GetProperties();
            List <PropertyDescriptor>    orderableCollection = new List <PropertyDescriptor>(collection.Count);

            List <string> allPropNames = orderableCollection.Select(f => f.Name).ToList();

            foreach (PropertyDescriptor descr in collection)
            {
                orderableCollection.Add(descr);
                switch (descr.Name)
                {
                    #region Notes

                    // The commented out properties aren't in use yet,
                    // but do work with revit materials as expected.

                    //case "texture_UScale":
                    //    var uScale = renderingAsset["texture_UScale"] as AssetPropertyDouble;
                    //    break;
                    //case "texture_VScale":
                    //    break;
                    //case "texture_UOffset":
                    //    break;
                    //case "texture_VOffset":
                    //    break;
                    //case "texture_RealWorldScaleX":
                    //    var xScale = renderingAsset["texture_RealWorldScaleX"] as AssetPropertyDistance;
                    //    break;
                    //case "texture_RealWorldScaleY":
                    //    break;

                    #endregion

                case "generic_diffuse":
                    var prop = renderingAsset.GetAssetProperty <AssetPropertyDoubleArray4d>("generic_diffuse");
                    newmat.ColorDiffuse = ColorFromAssetDoubleArray4d(prop);
                    break;

                case "glazing_reflectance":
                    // This is glass, so we should reduce the transparency.
                    var refl = renderingAsset.GetAssetProperty <AssetPropertyDouble>("glazing_reflectance");
                    if (refl == null)
                    {
                        var reflFloat = renderingAsset.GetAssetProperty <AssetPropertyFloat>("glazing_reflectance");
                        newmat.Reflectivity = reflFloat?.Value ?? 0f;
                    }
                    else
                    {
                        newmat.Reflectivity = (float)refl.Value;
                    }
                    newmat.Opacity = Math.Abs(0f - newmat.Reflectivity);
                    break;

                case "common_Tint_color":
                    // Tint shouldn't be used if generic diffuse is set
                    if (
                        renderingAsset.GetAssetProperty <AssetPropertyDoubleArray4d>("generic_diffuse") != null
                        )
                    {
                        continue;
                    }
                    var tintProp = renderingAsset.GetAssetProperty <AssetPropertyDoubleArray4d>("common_Tint_color");
                    newmat.ColorDiffuse = ColorFromAssetDoubleArray4d(tintProp);
                    break;

                default:
                    break;
                }
            }

            // Set textures
            foreach (var tx in bundle.TexturePaths)
            {
                // Get the filename
                var txFileName = tx.Value.SafeFileName;
                if (tx.Key == RevitTextureType.Color)
                {
                    newmat.TextureDiffuse = new TextureSlot(
                        $"Textures/{txFileName}",
                        TextureType.Diffuse,
                        0,    // Texture index in the material
                        TextureMapping.Box,
                        0,    //
                        0.5f, // Blend mode
                        TextureOperation.Add,
                        TextureWrapMode.Clamp,
                        TextureWrapMode.Clamp,
                        0 // Flags,
                        );
                }
                else if (tx.Key == RevitTextureType.Bump)
                {
                    newmat.TextureHeight = new TextureSlot(
                        $"Textures/{txFileName}",
                        TextureType.Diffuse,
                        0,    // Texture index in the material
                        TextureMapping.Box,
                        0,    //
                        0.5f, // Blend mode
                        TextureOperation.Add,
                        TextureWrapMode.Clamp,
                        TextureWrapMode.Clamp,
                        0 // Flags,
                        );
                }
            }
            return(newmat);
        }