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}?)", "&$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; }
public static UdsMaterial FromNode(AssetImportContext ctx, XmlNode node, Dictionary <String, UdsTexture> textureElements) { Debug.Assert(node.Name == "MasterMaterial"); UdsMaterial m = new UdsMaterial(); m.assetRef = new Material(Shader.Find("Standard")); m.assetRef.name = node.Attributes["label"].Value; m.name = node.Attributes["name"].Value; /* * <KeyValueProperty name="DiffuseColor" type="Color" val="(R=0.784314,G=0.619608,B=0.243137,A=1.000000)"/> * <KeyValueProperty name="DiffuseMapFading" type="Float" val="0.350000"/> // mix between DiffuseColor and DiffuseMap? * <KeyValueProperty name="DiffuseMap" type="Texture" val="woods_and_plastics_finish_carpentry_wood_paneling_1_0"/> * <KeyValueProperty name="DiffuseMap_UVOffsetX" type="Float" val="0.000000"/> * <KeyValueProperty name="DiffuseMap_UVOffsetY" type="Float" val="0.000000"/> * <KeyValueProperty name="DiffuseMap_UVScaleX" type="Float" val="0.169333"/> * <KeyValueProperty name="DiffuseMap_UVScaleY" type="Float" val="0.169333"/> * <KeyValueProperty name="DiffuseMap_UVWAngle" type="Float" val="0.000000"/> * <KeyValueProperty name="TintEnabled" type="Bool" val="True"/> * <KeyValueProperty name="TintColor" type="Color" val="(R=0.172549,G=0.172549,B=0.172549,A=1.000000)"/> * <KeyValueProperty name="SelfIlluminationLuminance" type="Float" val="0.000000"/> * <KeyValueProperty name="SelfIlluminationFilter" type="Color" val="(R=1.000000,G=1.000000,B=1.000000,A=1.000000)"/> * <KeyValueProperty name="SelfIlluminationMapEnable" type="Bool" val="False"/> * <KeyValueProperty name="BumpAmount" type="Float" val="9.310000"/> * <KeyValueProperty name="BumpMap" type="Texture" val="woods_and_plastics_finish_carpentry_wood_paneling_1_bump_0"/> * <KeyValueProperty name="BumpMap_UVOffsetX" type="Float" val="0.000000"/> * <KeyValueProperty name="BumpMap_UVOffsetY" type="Float" val="0.000000"/> * <KeyValueProperty name="BumpMap_UVScaleX" type="Float" val="0.169333"/> * <KeyValueProperty name="BumpMap_UVScaleY" type="Float" val="0.169333"/> * <KeyValueProperty name="BumpMap_UVWAngle" type="Float" val="0.000000"/> * <KeyValueProperty name="IsMetal" type="Bool" val="False"/> * <KeyValueProperty name="Glossiness" type="Float" val="0.000000"/> * * if (Type == 2) { * <KeyValueProperty name="Transparency" type="Float" val="0.850000"/> * <KeyValueProperty name="TransparencyMapFading" type="Float" val="0.000000"/> * <KeyValueProperty name="RefractionIndex" type="Float" val="1.010000"/> * } * */ foreach (XmlNode kvpNode in node.ChildNodes) { if (kvpNode.Name != "KeyValueProperty") { continue; } String keyName = kvpNode.Attributes["name"].Value; if (keyName == "DiffuseColor") { Color kvpColor = ParseKVPColor(kvpNode); m.assetRef.color = new Color(kvpColor.r, kvpColor.g, kvpColor.b, m.assetRef.color.a); // Alpha channel is preserved since we set it from the "Transparency" key } else if (keyName == "DiffuseMap") { Debug.Assert(kvpNode.Attributes["type"].Value == "Texture"); UdsTexture texRef; textureElements.TryGetValue(kvpNode.Attributes["val"].Value, out texRef); if (texRef == null) { ctx.LogImportError(String.Format("Missing diffuse texref \"{0}\" while assembling material node \"{1}\"", kvpNode.Attributes["val"].Value, m.name)); } else { Vector2 uvOffset, uvScale; FillUVOffsetScaleParameters(node, "DiffuseMap", out uvOffset, out uvScale); m.assetRef.SetTexture("_MainTex", texRef.assetRef); m.assetRef.SetTextureOffset("_MainTex", uvOffset); m.assetRef.SetTextureScale("_MainTex", uvScale); } } else if (keyName == "BumpMap") { Debug.Assert(kvpNode.Attributes["type"].Value == "Texture"); UdsTexture texRef; textureElements.TryGetValue(kvpNode.Attributes["val"].Value, out texRef); if (texRef == null) { ctx.LogImportError(String.Format("Missing bump texref \"{0}\" while assembling material node \"{1}\"", kvpNode.Attributes["val"].Value, m.name)); } else { Vector2 uvOffset, uvScale; FillUVOffsetScaleParameters(node, "BumpMap", out uvOffset, out uvScale); if (!texRef.importer.convertToNormalmap) { // Update importer config for this texture to convert it to a normal map texRef.importer.textureType = TextureImporterType.NormalMap; texRef.importer.convertToNormalmap = true; texRef.importer.SaveAndReimport(); } m.assetRef.SetTexture("_BumpMap", texRef.assetRef); m.assetRef.SetTextureOffset("_BumpMap", uvOffset); m.assetRef.SetTextureScale("_BumpMap", uvScale); m.assetRef.EnableKeyword("_NORMALMAP"); // ref: https://docs.unity3d.com/Manual/materials-scripting-standard-shader.html } } else if (keyName == "BumpAmount") { m.assetRef.SetFloat("_BumpScale", Math.Min(Single.Parse(kvpNode.Attributes["val"].Value), 1.0f)); } else if (keyName == "Transparency") { float transparency = Single.Parse(kvpNode.Attributes["val"].Value); if (transparency > 0.0f) { // Set up the material for the "Transparent" transparency mode. // This code is borrowed from StandardShaderGUI.cs:365 in the Unity 2018.4.20 builtin shaders package m.assetRef.SetInt("_Mode", 3); // StandardShader.BlendMode enum: {Opaque=0, Cutout=1, Fade=2, Transparent=3} m.assetRef.SetOverrideTag("RenderType", "Transparent"); m.assetRef.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One); m.assetRef.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha); m.assetRef.SetInt("_ZWrite", 0); m.assetRef.DisableKeyword("_ALPHATEST_ON"); m.assetRef.DisableKeyword("_ALPHABLEND_ON"); m.assetRef.EnableKeyword("_ALPHAPREMULTIPLY_ON"); m.assetRef.renderQueue = (int)UnityEngine.Rendering.RenderQueue.Transparent; // End borrowed setup code Color c = m.assetRef.color; c.a = 1.0f - transparency; m.assetRef.color = c; } } else if (keyName == "IsMetal") { m.assetRef.SetFloat("_Metallic", Boolean.Parse(kvpNode.Attributes["val"].Value) ? 1.0f : 0.0f); } else if (keyName == "Glossiness") { m.assetRef.SetFloat("_Glossiness", Single.Parse(kvpNode.Attributes["val"].Value)); } else if (keyName == "SelfIlluminationLuminance") { float emissiveLuma = Single.Parse(kvpNode.Attributes["val"].Value); if (emissiveLuma > 0.0f) { // Emissive surfaces seem to mostly be used for light fixture glass. Those components also have lights attached, // so we don't need the emissive component to contribute to global illumination. m.assetRef.EnableKeyword("_EMISSION"); m.assetRef.globalIlluminationFlags = MaterialGlobalIlluminationFlags.None; m.assetRef.SetColor("_EmissionColor", new Color(emissiveLuma, emissiveLuma, emissiveLuma, emissiveLuma)); } } } // Kvp node loop ctx.AddObjectToAsset(m.name, m.assetRef); return(m); }