public static UdsTexture FromNode(AssetImportContext ctx, XmlNode node)
        {
            UdsTexture tex = new UdsTexture();

            /*
             * <Texture name="concrete_cast-in-place_formwork_wood_boards_bump_0" texturemode="0" texturefilter="3" textureaddressx="0" textureaddressy="0" rgbcurve="-1.000000"
             *          srgb="0" file="rac_basic_sample_project_local_copy-3DView-UE4_Assets/concrete.cast-in-place.formwork.wood.boards.bump.jpg">
             *   <Hash value="c99e25a6f94199ce085a6e78e56639f2"/>
             * </Texture>
             */

            tex.name     = node.Attributes["name"].Value;
            tex.filePath = node.Attributes["file"].Value;

            if (Regex.Match(tex.filePath, @"\.ies$", RegexOptions.IgnoreCase).Success)
            {
                ctx.LogImportWarning(String.Format("Texture Reference \"{0}\" to IES light profile \"{1}\" cannot be resolved: IES light profile import is not implemented.", tex.name, tex.filePath));
                return(null);
            }


            tex.fullyQualifiedPath = Path.Combine(Path.GetDirectoryName(ctx.assetPath), tex.filePath);

            var texAssetObj = AssetDatabase.LoadAssetAtPath(tex.fullyQualifiedPath, typeof(Texture));

            if (texAssetObj != null)
            {
                tex.assetRef = (Texture)texAssetObj;
                var texImporterObj = AssetImporter.GetAtPath(tex.fullyQualifiedPath); // load import settings for possible later adjustment once we know what this will be used for
                if (texImporterObj != null)
                {
                    tex.importer = (TextureImporter)texImporterObj;
                }
            }

            if (tex.assetRef == null || tex.importer == null)
            {
                ctx.LogImportError(String.Format("UdsTexture::FromNode: Asset does not exist at path \"{0}\"", tex.fullyQualifiedPath));
            }

            return(tex);
        }
    public override void OnImportAsset(AssetImportContext ctx)
    {
        XmlDocument doc = new XmlDocument();
        {
            String wholeDoc = File.ReadAllText(ctx.assetPath);
            // Datasmith doesn't properly escape & in texture filename XML elements, so we need to do that before we can pass the document to XmlDocument or it will throw a parse error.
            doc.LoadXml(System.Text.RegularExpressions.Regex.Replace(wholeDoc, @"&([^;]{8}?)", "&amp;$1"));
        }

        XmlElement rootElement = doc.DocumentElement;

        Debug.Assert(rootElement.Name == "DatasmithUnrealScene");

        staticMeshElements = new Dictionary <string, UdsStaticMesh>();
        materialElements   = new Dictionary <string, UdsMaterial>();
        textureElements    = new Dictionary <string, UdsTexture>();
        actorMetadata      = new Dictionary <String, Dictionary <String, String> >();

        // Populate actor metadata dictionaries
        foreach (XmlNode metadataNode in rootElement.SelectNodes("child::MetaData"))
        {
            var m = Regex.Match(metadataNode.Attributes["reference"].Value, @"Actor\.(.*)$");
            if (!m.Success)
            {
                continue;
            }
            String actorId = m.Groups[1].Value;

            Dictionary <String, String> metadata = new Dictionary <string, string>();
            foreach (XmlNode kvpNode in metadataNode.SelectNodes("child::KeyValueProperty"))
            {
                metadata.Add(kvpNode.Attributes["name"].Value, kvpNode.Attributes["val"].Value);
            }
            actorMetadata.Add(actorId, metadata);
        }

        // Import textures
        foreach (XmlNode node in rootElement.SelectNodes("child::Texture"))
        {
            UdsTexture tex = UdsTexture.FromNode(ctx, node);
            if (tex != null)
            {
                textureElements.Add(tex.name, tex);
            }
        }

        // Import materials
        foreach (XmlNode node in rootElement.SelectNodes("child::MasterMaterial"))
        {
            UdsMaterial m = UdsMaterial.FromNode(ctx, node, textureElements);
            materialElements.Add(m.name, m);
        }

        // Import StaticMesh nodes and crossreference materials
        foreach (XmlNode node in rootElement.SelectNodes("child::StaticMesh"))
        {
            UdsStaticMesh m = UdsStaticMesh.FromNode(ctx, node);

            for (int materialIdx = 0; materialIdx < m.materialNames.Count; ++materialIdx)
            {
                UdsMaterial mat;
                if (!materialElements.TryGetValue(m.materialNames[materialIdx], out mat))
                {
                    ctx.LogImportError(String.Format("Can't resolve Material ref \"{0}\"", m.materialNames[materialIdx]));
                }
                m.materialRefs.Add(mat);
            }

            staticMeshElements.Add(m.name, m);
        }


        GameObject sceneRoot = new GameObject("datasmithRoot");

        sceneRoot.transform.rotation = Quaternion.Euler(90.0f, 0.0f, 0.0f); // Convert Revit's Z-up orientation to Unity's Y-up orientation
        ctx.AddObjectToAsset("datasmithRoot", sceneRoot);
        ctx.SetMainObject(sceneRoot);

        ImportActorChildren(ctx, sceneRoot, rootElement);

        // cleanup (TODO not sure if this is required / what the lifecycle of this object looks like)
        staticMeshElements = null;
        materialElements   = null;
        textureElements    = null;
        actorMetadata      = null;
    }