You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

寻求Revit转SVF/GLTF开源代码及Forge Viewer离线查看方案

Revit转SVF/GLTF用于Forge Viewer离线查看的开源方案

一、Revit转SVF的开源实现思路

SVF是Autodesk专属格式,没有完全开源的直接导出代码,但可以基于Revit API提取模型核心数据,参考SVF结构规范组织成兼容格式,以下是核心代码片段(C#,基于Revit API):

using Autodesk.Revit.DB;
using System.Text.Json;
using System.IO;

public void ExportToSVFStructure(Document doc, string outputPath)
{
    // 创建输出目录
    Directory.CreateDirectory(outputPath);
    var manifestContent = new System.Text.StringBuilder("[");
    string geometryPath = Path.Combine(outputPath, "geometry.bin");

    // 遍历可见元素
    var collector = new FilteredElementCollector(doc)
        .WhereElementIsNotElementType()
        .WhereElementIsViewIndependent();

    foreach (Element elem in collector)
    {
        var geoElem = elem.get_Geometry(new Options());
        if (geoObj is Mesh mesh)
        {
            // 写入几何二进制数据
            using (var writer = new BinaryWriter(File.Open(geometryPath, FileMode.Append)))
            {
                // 写入顶点数据
                foreach (XYZ vertex in mesh.Vertices)
                {
                    writer.Write((float)vertex.X);
                    writer.Write((float)vertex.Y);
                    writer.Write((float)vertex.Z);
                }
                // 写入三角索引
                foreach (int idx in mesh.GetTriangles())
                {
                    writer.Write((ushort)idx);
                }
            }
        }

        // 写入元素元数据到清单
        var metadata = new
        {
            id = elem.Id.IntegerValue,
            name = elem.Name,
            category = elem.Category?.Name
        };
        manifestContent.Append(JsonSerializer.Serialize(metadata) + ",");
    }

    // 完善并保存清单文件
    manifestContent.Remove(manifestContent.Length - 1, 1).Append("]");
    File.WriteAllText(Path.Combine(outputPath, "manifest.json"), manifestContent.ToString());
}

注:上述代码仅实现基础数据提取,完整SVF格式需补充材质、纹理、层级关系等细节,可通过解析Forge导出的SVF文件参考完整结构规范。

二、Revit转GLTF并兼容Forge Viewer的无报错代码

如果选择GLTF,需确保导出文件符合glTF 2.0规范,且适配Forge Viewer的加载逻辑,以下是基于Revit API和glTF-SDK的完整实现:

using Autodesk.Revit.DB;
using glTFLoader.Schema;
using System.Numerics;
using System.Text.Json;
using System.IO;

public void ExportToGLTF(Document doc, string outputPath)
{
    var gltf = new Gltf
    {
        Accessors = new List<Accessor>(),
        Buffers = new List<Buffer>(),
        BufferViews = new List<BufferView>(),
        Meshes = new List<Mesh>(),
        Nodes = new List<Node>(),
        Scenes = new List<Scene> { new Scene { Nodes = new List<int> { 0 } } }
    };

    using (var ms = new MemoryStream())
    {
        var collector = new FilteredElementCollector(doc)
            .WhereElementIsNotElementType()
            .WhereElementIsViewIndependent();

        int nodeIdx = 0;
        foreach (Element elem in collector)
        {
            var geoElem = elem.get_Geometry(new Options { DetailLevel = ViewDetailLevel.Fine });
            Mesh mesh = null;
            foreach (var geoObj in geoElem)
            {
                if (geoObj is Solid solid) mesh = solid.Triangulate();
                else if (geoObj is Mesh m) mesh = m;
                if (mesh != null) break;
            }
            if (mesh == null) continue;

            // 写入顶点数据
            long vertexOffset = ms.Position;
            foreach (XYZ vertex in mesh.Vertices)
            {
                ms.Write(BitConverter.GetBytes((float)vertex.X));
                ms.Write(BitConverter.GetBytes((float)vertex.Y));
                ms.Write(BitConverter.GetBytes((float)vertex.Z));
            }

            // 写入索引数据
            long indexOffset = ms.Position;
            foreach (int idx in mesh.GetTriangles())
            {
                ms.Write(BitConverter.GetBytes((ushort)idx));
            }

            // 创建顶点Accessor
            gltf.Accessors.Add(new Accessor
            {
                BufferView = gltf.BufferViews.Count,
                ComponentType = Accessor.ComponentType.UNSIGNED_SHORT,
                Count = mesh.Vertices.Count,
                Type = Accessor.Type.VEC3,
                Min = new List<float> { mesh.GetBoundingBox().Min.X, mesh.GetBoundingBox().Min.Y, mesh.GetBoundingBox().Min.Z },
                Max = new List<float> { mesh.GetBoundingBox().Max.X, mesh.GetBoundingBox().Max.Y, mesh.GetBoundingBox().Max.Z }
            });

            // 创建索引Accessor
            gltf.Accessors.Add(new Accessor
            {
                BufferView = gltf.BufferViews.Count + 1,
                ComponentType = Accessor.ComponentType.UNSIGNED_SHORT,
                Count = mesh.NumTriangles * 3,
                Type = Accessor.Type.SCALAR
            });

            // 创建顶点BufferView
            gltf.BufferViews.Add(new BufferView
            {
                Buffer = 0,
                ByteOffset = (int)vertexOffset,
                ByteLength = mesh.Vertices.Count * 12,
                Target = BufferView.Target.ARRAY_BUFFER
            });

            // 创建索引BufferView
            gltf.BufferViews.Add(new BufferView
            {
                Buffer = 0,
                ByteOffset = (int)indexOffset,
                ByteLength = mesh.NumTriangles * 3 * 2,
                Target = BufferView.Target.ELEMENT_ARRAY_BUFFER
            });

            // 创建Mesh和Node
            gltf.Meshes.Add(new Mesh
            {
                Primitives = new List<MeshPrimitive>
                {
                    new MeshPrimitive
                    {
                        Attributes = new Dictionary<string, int> { { "POSITION", gltf.Accessors.Count - 2 } },
                        Indices = gltf.Accessors.Count - 1
                    }
                },
                Name = elem.Name
            });

            gltf.Nodes.Add(new Node
            {
                Mesh = gltf.Meshes.Count - 1,
                Name = elem.Name,
                Matrix = GetTransformMatrix(elem.GetTransform())
            });

            gltf.Scenes[0].Nodes.Add(nodeIdx);
            nodeIdx++;
        }

        // 写入主Buffer
        gltf.Buffers.Add(new Buffer
        {
            ByteLength = (int)ms.Length,
            Uri = $"data:application/octet-stream;base64,{Convert.ToBase64String(ms.ToArray())}"
        });

        // 保存GLTF文件
        File.WriteAllText(outputPath, JsonSerializer.Serialize(gltf, new JsonSerializerOptions { WriteIndented = true }));
    }
}

// 转换Revit Transform为glTF矩阵
private float[] GetTransformMatrix(Transform transform)
{
    var matrix = Matrix4x4.CreateTranslation(new Vector3((float)transform.Origin.X, (float)transform.Origin.Y, (float)transform.Origin.Z))
        * Matrix4x4.CreateFromQuaternion(transform.Basis.ToQuaternion());
    return new float[]
    {
        matrix.M11, matrix.M12, matrix.M13, matrix.M14,
        matrix.M21, matrix.M22, matrix.M23, matrix.M24,
        matrix.M31, matrix.M32, matrix.M33, matrix.M34,
        matrix.M41, matrix.M42, matrix.M43, matrix.M44
    };
}

加载注意事项:

  1. 先通过NuGet安装glTF-SDK包确保格式合规
  2. Forge Viewer加载时需指定单位匹配Revit:
viewer.loadModel('./your-model.gltf', { applyScaling: 'm' });

内容的提问来源于stack exchange,提问作者oğuzhan kömcü

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 16:15:41