在 Autodesk Revit Forge Design Automation 中创建带纹理的 material

Creating a material with texture in Autodesk Revit Forge Design Automation

我目前正在处理 Autodesk Forge Design Automation 云解决方案 中 运行 的一些 Revit API 代码。基本上,我正在尝试 创建一个 material 并通过以下代码向其附加纹理

 private void AddTexturePath(AssetProperty asset, string texturePath) {
  Asset connectedAsset = null;

  if (asset.NumberOfConnectedProperties == 0)
   asset.AddConnectedAsset("UnifiedBitmapSchema");

  connectedAsset = (Asset) asset.GetConnectedProperty(0);
  AssetPropertyString path = (AssetPropertyString) connectedAsset.FindByName(UnifiedBitmap.UnifiedbitmapBitmap);

  if (!path.IsValidValue(texturePath)) {
   File.Create("texture.png");
   texturePath = Path.GetFullPath("texture.png");
  }

  path.Value = texturePath;

 }

这实际上工作得很好,因为纹理路径的值:

path.Value = texturePath;

需要引用现有文件。我在Forge的云实例上没有这个文件,因为纹理名称的路径是用户在发送Workitem请求时指定的。

问题是这会将 material 的纹理路径设置为如下所示:

T:\Aces\Jobs\<workitem_id>\texture.png

这基本上是 Workitem 实例的工作文件夹。这个路径是没有用的,因为像这样的纹理路径的 material 需要在 Revit 中手动重新链接。

如果我能以某种方式将 material 纹理路径映射到一些用户友好的目录,如 "C:\Textures\texture.png" 并且 Forge 实例似乎有一个 "C:\" 驱动器存在(可能是某种 Windows 实例),但我的代码运行在低权限下,因此它无法在工作目录之外创建任何类型的 directories/files。

有人知道如何解决这个问题吗?任何帮助将不胜感激!

经过一整天的研究,我几乎得出了一个令人满意的解决方案。为清楚起见 - 我将参考 Autodesk Forge Design Automation API for Revit,简称为 "Forge".

基本上上面提供的代码是正确的。我没有找到在 Forge 实例上创建文件的任何可能方法,该文件位于不同于 Workitem 工作目录的目录中:

T:\Aces\Jobs\<workitem_id>\texture.png

有趣的是,Forge 实例上有一个 C:\ 驱动器,其中包含 Windows、Revit 和 .NET Framework 安装(因为 Forge 实例基本上是某种带有 Revit 的 Windows 实例安装)。可以枚举很多这样的目录,但是 none 我已经尝试过(而且我已经尝试了很多 - 大多数是最明显的,public 访问 Windows 目录例如 C:\Users\Public、C:\Program Files 等)允许创建目录或文件。这对应于 Forge 文档的“限制”区域中的说明:

Your application is run with low privileges, and will not be able to freely interact with the Windows OS :

  • Write access is typically restricted to the job’s working folder.
  • Registry access is mostly restricted, writing to the registry should be avoided.
  • Any sub-process will also be executed with low privileges.

因此,在尝试将“虚拟”纹理文件保存在 Forge C:\ 驱动器的某处后,我找到了另一个解决方案 - 纹理的纹理路径实际上无关紧要。 这是因为 Revit 提供了重新 link 纹理的替代方法。如果启动 Revit,您可以转到文件 -> 选项 -> 渲染,然后在“其他渲染外观路径”字段下,您可以指定本地计算机上的目录,Revit 可以使用该目录查找丢失的纹理。有了这些,您可以执行以下操作以完全控制在 Forge 上创建 materials:

  1. 将 Workitem 发送到 Forge,创建 materials。
  2. 在工作目录中使用正确的文件名创建虚拟纹理。
  3. 将虚拟纹理文件附加到 material。
  4. 输出生成的文件(.rvt 或 .rfa,取决于您在 Forge 上创建的内容)。
  5. 将所有纹理放入一个文件夹(或多个,这无关紧要)。
  6. 将带有纹理的目录添加到附加渲染外观路径。
  7. Revit 将成功地将所有纹理重新link到新路径。

我希望有人会觉得这有用!

此外,根据 Jeremy 的要求,我 post 一个代码示例,用于使用 Revit API(在 C# 中)创建带有纹理的 material 和修改 Revit 中不同的外观属性:

private void SetAppearanceParameters(Document project, Material mat, MaterialData data) {
    using(Transaction setParameters = new Transaction(project, "Set material parameters")) {
        setParameters.Start();

        AppearanceAssetElement genericAsset = new FilteredElementCollector(project)
                .OfClass(typeof(AppearanceAssetElement))
                .ToElements()
                .Cast < AppearanceAssetElement > ().Where(i = >i.Name.Contains("Generic"))
                .FirstOrDefault();

        AppearanceAssetElement newAsset = genericAsset.Duplicate(data.Name);
        mat.AppearanceAssetId = newAsset.Id;

        using(AppearanceAssetEditScope editAsset = new AppearanceAssetEditScope(project)) {
            Asset editableAsset = editAsset.Start(newAsset.Id);
            AssetProperty assetProperty = editableAsset["generic_diffuse"];

            SetColor(editableAsset, data.MaterialAppearance.Color);
            SetGlossiness(editableAsset, data.MaterialAppearance.Gloss);
            SetReflectivity(editableAsset, data.MaterialAppearance.Reflectivity);
            SetTransparency(editableAsset, data.MaterialAppearance.Transparency);

            if (data.MaterialAppearance.Texture != null && data.MaterialAppearance.Texture.Length != 0) 
            AddTexturePath(assetProperty, $@"C:\{data.MaterialIdentity.Manufacturer}\textures\{data.MaterialAppearance.Texture}");

            editAsset.Commit(true);
        }
        setParameters.Commit();
    }
}

private void SetTransparency(Asset editableAsset, int transparency) {
    AssetPropertyDouble genericTransparency = editableAsset["generic_transparency"] as AssetPropertyDouble;
    genericTransparency.Value = Convert.ToDouble(transparency);
}

private void SetReflectivity(Asset editableAsset, int reflectivity) {
    AssetPropertyDouble genericReflectivityZero = (AssetPropertyDouble) editableAsset["generic_reflectivity_at_0deg"];
    genericReflectivityZero.Value = Convert.ToDouble(reflectivity) / 100;

    AssetPropertyDouble genericReflectivityAngle = (AssetPropertyDouble) editableAsset["generic_reflectivity_at_90deg"];
    genericReflectivityAngle.Value = Convert.ToDouble(reflectivity) / 100;
}

private void SetGlossiness(Asset editableAsset, int gloss) {
    AssetPropertyDouble glossProperty = (AssetPropertyDouble) editableAsset["generic_glossiness"];
    glossProperty.Value = Convert.ToDouble(gloss) / 100;
}

private void SetColor(Asset editableAsset, int[] color) {
    AssetPropertyDoubleArray4d genericDiffuseColor = (AssetPropertyDoubleArray4d) editableAsset["generic_diffuse"];
    Color newColor = new Color((byte) color[0], (byte) color[1], (byte) color[2]);
    genericDiffuseColor.SetValueAsColor(newColor);
}

private void AddTexturePath(AssetProperty asset, string texturePath) {
    Asset connectedAsset = null;
    if (asset.NumberOfConnectedProperties == 0) asset.AddConnectedAsset("UnifiedBitmapSchema");

    connectedAsset = (Asset) asset.GetConnectedProperty(0);
    AssetProperty prop = connectedAsset.FindByName(UnifiedBitmap.UnifiedbitmapBitmap);
    AssetPropertyString path = (AssetPropertyString) connectedAsset.FindByName(UnifiedBitmap.UnifiedbitmapBitmap);

    string fileName = Path.GetFileName(texturePath);
    File.Create(fileName);
    texturePath = Path.GetFullPath(fileName);

    path.Value = texturePath;
}