├── AddinBundle ├── Installer │ ├── logo.bmp │ ├── cesium.ico │ └── sidelogo.bmp ├── BundleDebug.bat ├── BundleRelease.bat ├── RemoveBundleFromRevit.bat ├── CopyBundleToRevit.bat ├── CesiumIonRevitAddin.bundle │ └── PackageContents.xml └── installer.iss ├── CesiumIonRevitAddin ├── Images │ ├── logo.png │ ├── cesium.ico │ └── FontAwesome │ │ ├── circle-question.png │ │ ├── file-export-solid.png │ │ ├── book-open-reader-solid.png │ │ ├── cloud-arrow-up-solid.png │ │ ├── right-to-bracket-solid.png │ │ ├── right-from-bracket-solid.png │ │ └── attribution.txt ├── gltf │ ├── GltfExtensionSchema.cs │ ├── GltfExtensions.cs │ ├── GltfImage.cs │ ├── GltfAttribute.cs │ ├── GltfScene.cs │ ├── GltfTexture.cs │ ├── GltfBuffer.cs │ ├── GltfMesh.cs │ ├── GltfVersion.cs │ ├── GltfMeshPrimitive.cs │ ├── KHRTextureTransform.cs │ ├── GltfSampler.cs │ ├── GltfPbr.cs │ ├── GltfTextureInfo.cs │ ├── GltfMaterial.cs │ ├── GltfNode.cs │ ├── GltfBufferView.cs │ ├── GltfBinaryData.cs │ ├── GltfAccessor.cs │ ├── ExtStructuralMetadata.cs │ └── Gltf.cs ├── Model │ ├── GeometryDataObject.cs │ ├── PointIntObject.cs │ └── VertexLookupIntObject.cs ├── Transform │ └── ModelRotation.cs ├── Utils │ ├── Collectors.cs │ ├── GeometryUtils.cs │ ├── MaterialUtils.cs │ ├── TilerExportUtils.cs │ ├── Util.cs │ ├── GltfBinaryDataUtils.cs │ ├── IonExportUtils.cs │ └── GLTFExportUtils.cs ├── CesiumIonRevit.addin ├── CesiumIonRevitAddin.shproj ├── Export │ ├── BufferConfig.cs │ ├── FileExport.cs │ ├── BinFile.cs │ ├── GltfJson.cs │ └── ParameterValue.cs ├── Preferences.cs ├── Logger.cs ├── IndexedDictionary.cs ├── Forms │ ├── IonConnectDialog.cs │ ├── IonUploadDialog.cs │ ├── ExportDialog.cs │ └── IonUploadDialog.Designer.cs └── CesiumIonRevitAddin.projitems ├── Documentation ├── Resources │ ├── Cesium_Logo_Color.jpg │ ├── SnowdonRevitIon.png │ ├── Tutorial_Connect.png │ └── cesium-unreal-metadata.png ├── ReleaseGuide │ └── README.md └── DeveloperGuide │ └── README.md ├── GenerateHelp ├── GenerateHelp.csproj └── Program.cs ├── CesiumIonRevitAddin_2022 ├── packages.config ├── Properties │ └── AssemblyInfo.cs └── CesiumIonRevitAddin_2022.csproj ├── CesiumIonRevitAddin_2023 ├── packages.config ├── Properties │ └── AssemblyInfo.cs └── CesiumIonRevitAddin_2023.csproj ├── CesiumIonRevitAddin_2024 ├── packages.config ├── Properties │ └── AssemblyInfo.cs └── CesiumIonRevitAddin_2024.csproj ├── CHANGES.md ├── ThirdParty.json ├── CesiumIonRevitAddin_2025 ├── Properties │ └── AssemblyInfo.cs └── CesiumIonRevitAddin_2025.csproj ├── CesiumIonRevitAddin_2026 ├── Properties │ └── AssemblyInfo.cs └── CesiumIonRevitAddin_2026.csproj ├── .gitignore ├── Build └── CommonBuild.targets ├── CesiumIonRevitAddin.sln ├── README.md ├── .editorconfig └── LICENSE /AddinBundle/Installer/logo.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CesiumGS/cesium-ion-revit-add-in/HEAD/AddinBundle/Installer/logo.bmp -------------------------------------------------------------------------------- /AddinBundle/Installer/cesium.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CesiumGS/cesium-ion-revit-add-in/HEAD/AddinBundle/Installer/cesium.ico -------------------------------------------------------------------------------- /AddinBundle/Installer/sidelogo.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CesiumGS/cesium-ion-revit-add-in/HEAD/AddinBundle/Installer/sidelogo.bmp -------------------------------------------------------------------------------- /CesiumIonRevitAddin/Images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CesiumGS/cesium-ion-revit-add-in/HEAD/CesiumIonRevitAddin/Images/logo.png -------------------------------------------------------------------------------- /CesiumIonRevitAddin/Images/cesium.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CesiumGS/cesium-ion-revit-add-in/HEAD/CesiumIonRevitAddin/Images/cesium.ico -------------------------------------------------------------------------------- /Documentation/Resources/Cesium_Logo_Color.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CesiumGS/cesium-ion-revit-add-in/HEAD/Documentation/Resources/Cesium_Logo_Color.jpg -------------------------------------------------------------------------------- /Documentation/Resources/SnowdonRevitIon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CesiumGS/cesium-ion-revit-add-in/HEAD/Documentation/Resources/SnowdonRevitIon.png -------------------------------------------------------------------------------- /Documentation/Resources/Tutorial_Connect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CesiumGS/cesium-ion-revit-add-in/HEAD/Documentation/Resources/Tutorial_Connect.png -------------------------------------------------------------------------------- /Documentation/Resources/cesium-unreal-metadata.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CesiumGS/cesium-ion-revit-add-in/HEAD/Documentation/Resources/cesium-unreal-metadata.png -------------------------------------------------------------------------------- /CesiumIonRevitAddin/Images/FontAwesome/circle-question.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CesiumGS/cesium-ion-revit-add-in/HEAD/CesiumIonRevitAddin/Images/FontAwesome/circle-question.png -------------------------------------------------------------------------------- /CesiumIonRevitAddin/Images/FontAwesome/file-export-solid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CesiumGS/cesium-ion-revit-add-in/HEAD/CesiumIonRevitAddin/Images/FontAwesome/file-export-solid.png -------------------------------------------------------------------------------- /CesiumIonRevitAddin/gltf/GltfExtensionSchema.cs: -------------------------------------------------------------------------------- 1 | namespace CesiumIonRevitAddin.Gltf 2 | { 3 | // Base class 4 | internal class GltfExtensionSchema 5 | { 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /CesiumIonRevitAddin/Images/FontAwesome/book-open-reader-solid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CesiumGS/cesium-ion-revit-add-in/HEAD/CesiumIonRevitAddin/Images/FontAwesome/book-open-reader-solid.png -------------------------------------------------------------------------------- /CesiumIonRevitAddin/Images/FontAwesome/cloud-arrow-up-solid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CesiumGS/cesium-ion-revit-add-in/HEAD/CesiumIonRevitAddin/Images/FontAwesome/cloud-arrow-up-solid.png -------------------------------------------------------------------------------- /CesiumIonRevitAddin/Images/FontAwesome/right-to-bracket-solid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CesiumGS/cesium-ion-revit-add-in/HEAD/CesiumIonRevitAddin/Images/FontAwesome/right-to-bracket-solid.png -------------------------------------------------------------------------------- /CesiumIonRevitAddin/Images/FontAwesome/right-from-bracket-solid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CesiumGS/cesium-ion-revit-add-in/HEAD/CesiumIonRevitAddin/Images/FontAwesome/right-from-bracket-solid.png -------------------------------------------------------------------------------- /CesiumIonRevitAddin/gltf/GltfExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace CesiumIonRevitAddin.Gltf 2 | { 3 | internal class GltfExtensions 4 | { 5 | public ExtStructuralMetadata EXT_structural_metadata = null; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /CesiumIonRevitAddin/Images/FontAwesome/attribution.txt: -------------------------------------------------------------------------------- 1 | The SVG icons in this directory are from the "Font Awesome" project. 2 | They are published under the CC BY 4.0 License. See 3 | https://fontawesome.com/license/free for further information. -------------------------------------------------------------------------------- /CesiumIonRevitAddin/gltf/GltfImage.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace CesiumIonRevitAddin.Gltf 4 | { 5 | internal class GltfImage 6 | { 7 | [JsonProperty("uri")] 8 | public string Uri { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /CesiumIonRevitAddin/gltf/GltfAttribute.cs: -------------------------------------------------------------------------------- 1 | namespace CesiumIonRevitAddin.Gltf 2 | { 3 | internal class GltfAttribute 4 | { 5 | public int POSITION { get; set; } 6 | public int? NORMAL { get; set; } 7 | public int? TEXCOORD_0 { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /CesiumIonRevitAddin/gltf/GltfScene.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System.Collections.Generic; 3 | 4 | namespace CesiumIonRevitAddin.Gltf 5 | { 6 | internal class GltfScene 7 | { 8 | [JsonProperty("nodes")] 9 | public List Nodes { get; set; } = new List(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /CesiumIonRevitAddin/gltf/GltfTexture.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace CesiumIonRevitAddin.Gltf 4 | { 5 | internal class GltfTexture 6 | { 7 | [JsonProperty("source")] 8 | public int Source { get; set; } 9 | 10 | [JsonProperty("sampler")] 11 | public int Sampler { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /CesiumIonRevitAddin/gltf/GltfBuffer.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace CesiumIonRevitAddin.Gltf 4 | { 5 | internal class GltfBuffer 6 | { 7 | [JsonProperty("uri")] 8 | public string Uri { get; set; } 9 | 10 | [JsonProperty("byteLength")] 11 | public ulong ByteLength { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /CesiumIonRevitAddin/gltf/GltfMesh.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System.Collections.Generic; 3 | 4 | namespace CesiumIonRevitAddin.Gltf 5 | { 6 | internal class GltfMesh 7 | { 8 | [JsonProperty("name")] 9 | public string Name { get; set; } 10 | 11 | [JsonProperty("primitives")] 12 | public List Primitives { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /GenerateHelp/GenerateHelp.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /CesiumIonRevitAddin/gltf/GltfVersion.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace CesiumIonRevitAddin.Gltf 4 | { 5 | internal class GltfVersion 6 | { 7 | public string version = "2.0"; 8 | public string generator = "Cesium ion for Autodesk Revit"; 9 | public string copyright = "Copyright 2024 Cesium GS, Inc."; 10 | public Dictionary extras = new Dictionary(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /CesiumIonRevitAddin/Model/GeometryDataObject.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace CesiumIonRevitAddin.Model 4 | { 5 | internal class GeometryDataObject 6 | { 7 | public List Vertices { get; set; } = new List(); 8 | public List Faces { get; set; } = new List(); 9 | public List Normals { get; set; } = new List(); 10 | public List TexCoords { get; set; } = new List(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /CesiumIonRevitAddin/gltf/GltfMeshPrimitive.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace CesiumIonRevitAddin.Gltf 4 | { 5 | internal class GltfMeshPrimitive 6 | { 7 | [JsonProperty("attributes")] 8 | public GltfAttribute Attributes { get; set; } = new GltfAttribute(); 9 | 10 | [JsonProperty("indices")] 11 | public int Indices { get; set; } 12 | 13 | [JsonProperty("material")] 14 | public int? Material { get; set; } = null; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /CesiumIonRevitAddin_2022/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /CesiumIonRevitAddin_2023/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /CesiumIonRevitAddin/gltf/KHRTextureTransform.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace CesiumIonRevitAddin.Gltf 4 | { 5 | internal class KhrTextureTransform 6 | { 7 | [JsonProperty("offset")] 8 | public double[] Offset { get; set; } = new double[] { 0, 0 }; 9 | [JsonProperty("rotation")] 10 | public double? Rotation { get; set; } 11 | [JsonProperty("scale")] 12 | public double[] Scale { get; set; } = new double[] { 1.0, 1.0 }; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /CesiumIonRevitAddin_2024/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /CesiumIonRevitAddin/Transform/ModelRotation.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace CesiumIonRevitAddin.Transform 4 | { 5 | internal static class ModelRotation 6 | { 7 | public static List Get(bool flipAxis) 8 | { 9 | if (flipAxis) 10 | { 11 | return new List { 0.7071, 0, 0, -0.7071 }; 12 | } 13 | else 14 | { 15 | return new List { 0, 0, 0, 0 }; 16 | } 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /CesiumIonRevitAddin/gltf/GltfSampler.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace CesiumIonRevitAddin.Gltf 4 | { 5 | internal class GltfSampler 6 | { 7 | [JsonProperty("magFilter")] 8 | public int? MagFilter { get; set; } = 9729; // LINEAR 9 | 10 | [JsonProperty("minFilter")] 11 | public int? MinFilter { get; set; } = 9987; // LINEAR 12 | 13 | [JsonProperty("wrapS")] 14 | public int WrapS { get; set; } = 10497; // REPEAT 15 | 16 | [JsonProperty("wrapT")] 17 | public int WrapT { get; set; } = 10497; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /AddinBundle/BundleDebug.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal 3 | 4 | rem Set the base directory to the location of this script 5 | set "baseDir=%~dp0" 6 | set "parentDir=%baseDir%.." 7 | set "contentsFolder=%baseDir%CesiumIonRevitAddin.bundle\Contents" 8 | set "versions=2022 2023 2024 2025 2026" 9 | 10 | rem Loop through each version number 11 | for %%v in (%versions%) do ( 12 | rem Correct the path to reference the parent directory of the script location 13 | robocopy "%parentDir%\CesiumIonRevitAddin_%%v\bin\Debug" "%contentsFolder%\%%v" /IS /IT /MIR /E /R:3 /W:5 /NFL 14 | echo Bundling complete for version %%v! 15 | ) 16 | 17 | pause 18 | endlocal 19 | -------------------------------------------------------------------------------- /AddinBundle/BundleRelease.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal 3 | 4 | rem Set the base directory to the location of this script 5 | set "baseDir=%~dp0" 6 | set "parentDir=%baseDir%.." 7 | set "contentsFolder=%baseDir%CesiumIonRevitAddin.bundle\Contents" 8 | set "versions=2022 2023 2024 2025 2026" 9 | 10 | rem Loop through each version number 11 | for %%v in (%versions%) do ( 12 | rem Correct the path to reference the parent directory of the script location 13 | robocopy "%parentDir%\CesiumIonRevitAddin_%%v\bin\Release" "%contentsFolder%\%%v" /IS /IT /MIR /E /R:3 /W:5 /NFL 14 | echo Bundling complete for version %%v! 15 | ) 16 | 17 | pause 18 | endlocal 19 | -------------------------------------------------------------------------------- /CesiumIonRevitAddin/gltf/GltfPbr.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System.Collections.Generic; 3 | 4 | namespace CesiumIonRevitAddin.Gltf 5 | { 6 | internal class GltfPbr 7 | { 8 | [JsonProperty("baseColorFactor")] 9 | public List BaseColorFactor { get; set; } 10 | 11 | [JsonProperty("metallicFactor")] 12 | public float MetallicFactor { get; set; } 13 | 14 | [JsonProperty("roughnessFactor")] 15 | public float RoughnessFactor { get; set; } 16 | 17 | [JsonProperty("baseColorTexture")] 18 | public GltfTextureInfo BaseColorTexture { get; set; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /CesiumIonRevitAddin/Utils/Collectors.cs: -------------------------------------------------------------------------------- 1 | using Autodesk.Revit.DB; 2 | using System.Linq; 3 | 4 | namespace CesiumIonRevitAddin.Utils 5 | { 6 | internal class Collectors 7 | { 8 | public static Material GetRandomMaterial(Document document) 9 | { 10 | using (var collector = new FilteredElementCollector(document)) 11 | { 12 | return collector.OfCategory(BuiltInCategory.OST_Materials) 13 | .WhereElementIsNotElementType() 14 | .ToElements() 15 | .Cast() 16 | .FirstOrDefault(); 17 | } 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /CesiumIonRevitAddin/gltf/GltfTextureInfo.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.Collections.Generic; 4 | 5 | namespace CesiumIonRevitAddin.Gltf 6 | { 7 | internal class GltfTextureInfo 8 | { 9 | [JsonProperty("index")] 10 | public int Index { get; set; } // Index to the texture in the glTF's "textures" array 11 | 12 | [JsonProperty("texCoord")] 13 | public int TexCoord { get; set; } = 0; // Default to using the first set of UV coordinates, typically base color 14 | 15 | [JsonProperty("extensions")] 16 | public Dictionary Extensions { get; set; } = new Dictionary(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /AddinBundle/RemoveBundleFromRevit.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | :: Check if the script is running with administrative privileges 3 | net session >nul 2>nul 4 | if %errorlevel% neq 0 ( 5 | echo This script requires administrator privileges. 6 | echo. 7 | echo Relaunching as Administrator... 8 | echo. 9 | :: Relaunch the script with admin privileges using PowerShell 10 | powershell -Command "Start-Process cmd -ArgumentList '/c, %~s0' -Verb runAs" 11 | exit /b 12 | ) 13 | 14 | setlocal enabledelayedexpansion 15 | 16 | set "folder=C:\ProgramData\Autodesk\ApplicationPlugins\CesiumIonRevitAddin.bundle" 17 | 18 | if exist "%folder%" ( 19 | echo Deleting: %folder% 20 | 21 | rmdir /s /q "%folder%" 22 | 23 | ) else ( 24 | echo %folder% does not exist 25 | ) 26 | 27 | pause 28 | endlocal -------------------------------------------------------------------------------- /CesiumIonRevitAddin/gltf/GltfMaterial.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace CesiumIonRevitAddin.Gltf 4 | { 5 | internal class GltfMaterial 6 | { 7 | [JsonProperty("alphaMode")] 8 | public string AlphaMode { get; set; } 9 | 10 | [JsonProperty("alphaCutoff")] 11 | public float? AlphaCutoff { get; set; } 12 | 13 | [JsonProperty("name")] 14 | public string Name { get; set; } 15 | 16 | [JsonProperty("pbrMetallicRoughness")] 17 | public GltfPbr PbrMetallicRoughness { get; set; } 18 | 19 | [JsonProperty("doubleSided")] 20 | public bool DoubleSided { get; set; } 21 | 22 | [JsonProperty("extensions")] 23 | public GltfExtensions Extensions { get; set; } = null; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /CesiumIonRevitAddin/CesiumIonRevit.addin: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Cesium ion for Autodesk Revit 5 | .\CesiumIonRevitAddin\CesiumIonRevitAddin.dll 6 | CesiumIonRevitAddin.ExternalApplication 7 | Easily export Revit designs to Cesium ion using Cesium ion for Autodesk Revit, which transforms your models into 3D Tiles for immersive visualizations and interactive geospatial experiences. 8 | AlwaysVisible 9 | 502fe383-2648-4e98-adf8-5e6047f9dc35 10 | CESIUM 11 | Cesium, www.cesium.com 12 | 13 | 14 | -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ### v1.3.0 - 2025-10-01 4 | 5 | * Add support Revit 2026 6 | * Fix export issues for some elements in Revit 2026 7 | * Fix incorrect transforms for some exported subcomponent elements 8 | 9 | ### v1.2.0 - 2025-05-01 10 | 11 | * Add support for an elements unique ID, level and room metadata 12 | * Remove unused root node project level metadata 13 | * Fix crash when exporting with textures enabled and metadata disabled 14 | 15 | ### v1.1.0 - 2024-02-07 16 | 17 | * Export optimization: 2-10x speed increase, ~40% memory usage reduction, reduced upload size (Revit 2023+) 18 | * Additional human-readable metadata added 19 | * Client-side EPSG code UI validation 20 | * Improved surface normal calculation 21 | * Additional GPU instances detected 22 | * Additional minor enhancements and bug fixes 23 | 24 | ### v1.0.0 - 2024-12-02 25 | 26 | * The initial release of Cesium ion for Autodesk Revit 27 | -------------------------------------------------------------------------------- /AddinBundle/CopyBundleToRevit.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | :: Check if the script is running with administrative privileges 3 | net session >nul 2>nul 4 | if %errorlevel% neq 0 ( 5 | echo This script requires administrator privileges. 6 | echo. 7 | echo Relaunching as Administrator... 8 | echo. 9 | :: Relaunch the script with admin privileges using PowerShell 10 | powershell -Command "Start-Process cmd -ArgumentList '/c, %~s0' -Verb runAs" 11 | exit /b 12 | ) 13 | 14 | setlocal 15 | 16 | rem Set the source folder to the path of the script directory 17 | set "sourceFolder=%~dp0CesiumIonRevitAddin.bundle" 18 | set "destinationFolder=C:\ProgramData\Autodesk\ApplicationPlugins\CesiumIonRevitAddin.bundle" 19 | 20 | rem Use robocopy to mirror the source folder to the destination 21 | robocopy "%sourceFolder%" "%destinationFolder%" /MIR /R:3 /W:5 /NFL 22 | 23 | echo Mirroring complete from %sourceFolder% to %destinationFolder%. 24 | 25 | pause 26 | endlocal 27 | -------------------------------------------------------------------------------- /CesiumIonRevitAddin/Model/PointIntObject.cs: -------------------------------------------------------------------------------- 1 | using Autodesk.Revit.DB; 2 | using System; 3 | 4 | namespace CesiumIonRevitAddin.Model 5 | { 6 | internal class PointIntObject : IComparable 7 | { 8 | public PointIntObject(XYZ p) 9 | { 10 | this.X = p.X; 11 | this.Y = p.Y; 12 | this.Z = p.Z; 13 | } 14 | 15 | public double X { get; set; } 16 | public double Y { get; set; } 17 | public double Z { get; set; } 18 | 19 | public int CompareTo(PointIntObject a) 20 | { 21 | double d = this.X - a.X; 22 | if (d == 0) 23 | { 24 | d = this.Y - a.Y; 25 | if (d == 0) 26 | { 27 | d = this.Z - a.Z; 28 | } 29 | } 30 | 31 | return (d == 0) ? 0 : ((d > 0) ? 1 : -1); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /CesiumIonRevitAddin/gltf/GltfNode.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System.Collections.Generic; 3 | 4 | namespace CesiumIonRevitAddin.Gltf 5 | { 6 | internal class GltfNode 7 | { 8 | [JsonProperty("name")] 9 | public string Name { get; set; } 10 | [JsonProperty("mesh")] 11 | public int? Mesh { get; set; } = null; 12 | 13 | [JsonProperty("matrix")] 14 | public List Matrix { get; set; } 15 | 16 | [JsonProperty("rotation")] 17 | public List Rotation { get; set; } 18 | [JsonProperty("scale")] 19 | public List Scale { get; set; } 20 | [JsonProperty("translation")] 21 | public List Translation { get; set; } 22 | 23 | [JsonProperty("children")] 24 | public List Children { get; set; } 25 | [JsonProperty("extensions")] 26 | public GltfExtensions Extensions { get; set; } = null; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /CesiumIonRevitAddin/gltf/GltfBufferView.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace CesiumIonRevitAddin.Gltf 4 | { 5 | internal class GltfBufferView 6 | { 7 | [JsonProperty("buffer")] 8 | public int Buffer { get; set; } 9 | 10 | [JsonProperty("byteOffset")] 11 | public ulong ByteOffset { get; set; } 12 | 13 | [JsonProperty("byteLength")] 14 | public ulong ByteLength { get; set; } 15 | 16 | /// 17 | [JsonProperty("target")] 18 | public Targets Target { get; set; } 19 | 20 | [JsonProperty("name")] 21 | public string Name { get; set; } 22 | 23 | public GltfBufferView(int buffer, ulong byteOffset, ulong byteLength, Targets target, string name) 24 | { 25 | Buffer = buffer; 26 | ByteOffset = byteOffset; 27 | ByteLength = byteLength; 28 | Target = target; 29 | Name = name; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /ThirdParty.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "Newtonsoft.Json", 4 | "license": ["MIT"], 5 | "version": "13.0.3", 6 | "url": "https://github.com/JamesNK/Newtonsoft.Json" 7 | }, 8 | { 9 | "name": "AWSSDK.Core", 10 | "license": ["Apache 2.0"], 11 | "version": "3.7.400.34", 12 | "url": "https://github.com/aws/aws-sdk-net/" 13 | }, 14 | { 15 | "name": "AWSSDK.S3", 16 | "license": ["Apache 2.0"], 17 | "version": "3.7.404.6", 18 | "url": "https://github.com/aws/aws-sdk-net/" 19 | }, 20 | { 21 | "name": "AWSSDK.SecurityToken", 22 | "license": ["Apache 2.0"], 23 | "version": "3.7.400.34", 24 | "url": "https://github.com/aws/aws-sdk-net/" 25 | }, 26 | { 27 | "name": "Revit_All_Main_Versions_API_x64", 28 | "license": ["Proprietary"], 29 | "version": "Latest", 30 | "url": "https://www.nuget.org/packages/Revit_All_Main_Versions_API_x64" 31 | }, 32 | { 33 | "name": "Revit glTF exporter", 34 | "license": ["MIT"], 35 | "version": "Latest", 36 | "url": "https://github.com/EverseDevelopment/revit-glTF-exporter" 37 | } 38 | ] -------------------------------------------------------------------------------- /CesiumIonRevitAddin/Model/VertexLookupIntObject.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace CesiumIonRevitAddin.Model 4 | { 5 | internal class VertexLookupIntObject : Dictionary 6 | { 7 | public int AddVertex(PointIntObject p) 8 | { 9 | if (this.ContainsKey(p)) 10 | { 11 | return this[p]; 12 | } 13 | else 14 | { 15 | int index = this.Count; 16 | this[p] = index; 17 | return index; 18 | } 19 | } 20 | 21 | /// 22 | /// Define equality for integer-based PointInt. 23 | /// 24 | public class PointIntEqualityComparer : IEqualityComparer 25 | { 26 | public bool Equals(PointIntObject p, PointIntObject q) => p.CompareTo(q) == 0; 27 | 28 | public int GetHashCode(PointIntObject p) => string.Concat(p.X.ToString(), ",", p.Y.ToString(), ",", p.Z.ToString()).GetHashCode(); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /CesiumIonRevitAddin/CesiumIonRevitAddin.shproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1dd3ffa0-ad97-4e51-8fab-9a065c71ac64 5 | 14.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /CesiumIonRevitAddin/gltf/GltfBinaryData.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System.Collections.Generic; 3 | 4 | namespace CesiumIonRevitAddin.Gltf 5 | { 6 | internal class GltfBinaryData 7 | { 8 | [JsonProperty("name")] 9 | public string Name { get; set; } 10 | 11 | [JsonProperty("vertexBuffer")] 12 | public List VertexBuffer { get; set; } = new List(); 13 | 14 | [JsonProperty("indexBuffer")] 15 | public List IndexBuffer { get; set; } = new List(); 16 | 17 | [JsonProperty("normalBuffer")] 18 | public List NormalBuffer { get; set; } = new List(); 19 | 20 | [JsonProperty("texcoordsBuffer")] 21 | public List TexCoordBuffer { get; set; } = new List(); 22 | 23 | [JsonProperty("vertexAccessorIndex")] 24 | public int VertexAccessorIndex { get; set; } 25 | 26 | [JsonProperty("indexAccessorIndex")] 27 | public int IndexAccessorIndex { get; set; } 28 | 29 | [JsonProperty("normalsAccessorIndex")] 30 | public int NormalsAccessorIndex { get; set; } 31 | 32 | [JsonProperty("texcoordsAccessorIndex")] 33 | public int TexCoordAccessorIndex { get; set; } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /CesiumIonRevitAddin/Export/BufferConfig.cs: -------------------------------------------------------------------------------- 1 | using CesiumIonRevitAddin.Gltf; 2 | using System.Collections.Generic; 3 | 4 | namespace CesiumIonRevitAddin.Export 5 | { 6 | internal static class BufferConfig 7 | { 8 | public static void Run(List bufferViews, List buffers, string bufferUri) 9 | { 10 | ulong bytePosition = 0; 11 | int currentBuffer = 0; 12 | 13 | foreach (var bufferView in bufferViews) 14 | 15 | { 16 | if (bufferView.Buffer == 0) 17 | { 18 | bytePosition += bufferView.ByteLength; 19 | continue; 20 | } 21 | 22 | if (bufferView.Buffer != currentBuffer) 23 | { 24 | bufferView.Buffer = 0; 25 | bufferView.ByteOffset = bytePosition; 26 | bytePosition += bufferView.ByteLength; 27 | } 28 | } 29 | 30 | var buffer = new GltfBuffer 31 | { 32 | Uri = bufferUri, 33 | ByteLength = bytePosition 34 | }; 35 | buffers.Clear(); 36 | buffers.Add(buffer); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /CesiumIonRevitAddin/gltf/GltfAccessor.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System.Collections.Generic; 3 | 4 | namespace CesiumIonRevitAddin.Gltf 5 | { 6 | internal class GltfAccessor 7 | { 8 | public GltfAccessor(int bufferView, int byteOffset, ComponentType componentType, int count, string type, List max, List min, string name) 9 | { 10 | BufferView = bufferView; 11 | ByteOffset = byteOffset; 12 | ComponentType = componentType; 13 | Count = count; 14 | Type = type; 15 | Max = max; 16 | Min = min; 17 | Name = name; 18 | } 19 | 20 | [JsonProperty("bufferView")] 21 | public int BufferView { get; set; } 22 | [JsonProperty("byteOffset")] 23 | public int ByteOffset { get; set; } 24 | [JsonProperty("componentType")] 25 | public ComponentType ComponentType { get; set; } 26 | [JsonProperty("count")] 27 | public int Count { get; set; } 28 | [JsonProperty("type")] 29 | public string Type { get; set; } 30 | [JsonProperty("max")] 31 | public List Max { get; set; } 32 | [JsonProperty("min")] 33 | public List Min { get; set; } 34 | [JsonProperty("name")] 35 | public string Name { get; set; } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /CesiumIonRevitAddin/Export/FileExport.cs: -------------------------------------------------------------------------------- 1 | using CesiumIonRevitAddin.Export; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | 5 | namespace CesiumIonRevitAddin.Gltf 6 | { 7 | internal static class FileExport 8 | { 9 | public static void Run( 10 | Preferences preferences, 11 | List bufferViews, 12 | List buffers, 13 | List binaryData, 14 | List scenes, 15 | IndexedDictionary nodes, 16 | IndexedDictionary meshes, 17 | IndexedDictionary materials, 18 | List accessors, 19 | List extensionsUsed, 20 | List extensionsRequired, 21 | Dictionary extensions, 22 | GltfVersion asset, 23 | IndexedDictionary images, 24 | IndexedDictionary textures, 25 | IndexedDictionary samplers) 26 | { 27 | BufferConfig.Run(bufferViews, buffers, Path.GetFileName(preferences.BinPath)); 28 | BinFile.Create(preferences.BinPath, binaryData, preferences.Normals, false); 29 | 30 | string gltfJson = GltfJson.Get(scenes, nodes.List, meshes.List, materials.List, 31 | buffers, bufferViews, accessors, extensionsUsed, extensionsRequired, extensions, preferences, asset, images.List, textures.List, samplers.List); 32 | 33 | File.WriteAllText(preferences.GltfPath, gltfJson); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /CesiumIonRevitAddin/Export/BinFile.cs: -------------------------------------------------------------------------------- 1 | using CesiumIonRevitAddin.Gltf; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | 5 | namespace CesiumIonRevitAddin.Export 6 | { 7 | internal static class BinFile 8 | { 9 | public static void Create(string filename, List binaryFileData, bool exportNormals, bool exportBatchId) 10 | { 11 | using (FileStream f = File.Create(filename)) 12 | { 13 | using (var writer = new BinaryWriter(new BufferedStream(f), System.Text.Encoding.Default)) 14 | { 15 | foreach (GltfBinaryData binaryData in binaryFileData) 16 | { 17 | for (int i = 0; i < binaryData.VertexBuffer.Count; i++) 18 | { 19 | writer.Write(binaryData.VertexBuffer[i]); 20 | } 21 | 22 | if (exportNormals) 23 | { 24 | for (int i = 0; i < binaryData.NormalBuffer.Count; i++) 25 | { 26 | writer.Write(binaryData.NormalBuffer[i]); 27 | } 28 | } 29 | 30 | for (int i = 0; i < binaryData.TexCoordBuffer.Count; i++) 31 | { 32 | writer.Write(binaryData.TexCoordBuffer[i]); 33 | } 34 | 35 | for (int i = 0; i < binaryData.IndexBuffer.Count; i++) 36 | { 37 | writer.Write(binaryData.IndexBuffer[i]); 38 | } 39 | } 40 | 41 | writer.Flush(); 42 | } 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /CesiumIonRevitAddin/gltf/ExtStructuralMetadata.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.Collections.Generic; 4 | 5 | namespace CesiumIonRevitAddin.Gltf 6 | { 7 | // appended to nodes 8 | internal class ExtStructuralMetadata 9 | { 10 | [JsonProperty("class")] 11 | public String Class; 12 | 13 | [JsonProperty("properties")] 14 | Dictionary properties; 15 | 16 | // Adds a property to the properties dictionary. Will append a number if the property already exists. 17 | public string AddProperty(String key, ParameterValue parameterValue) 18 | { 19 | properties = properties ?? new Dictionary(); 20 | string addedKey = key; 21 | 22 | if (properties.ContainsKey(addedKey)) 23 | { 24 | int i = 1; 25 | while (properties.ContainsKey(addedKey + i)) 26 | { 27 | i++; 28 | } 29 | addedKey = key + i; 30 | } 31 | properties.Add(addedKey, parameterValue); 32 | if (addedKey != key) Logger.Instance.Log("Parameter " + key + " had a name collision, is now " + addedKey); 33 | return addedKey; 34 | } 35 | 36 | public bool HasProperty(String key) 37 | { 38 | if (properties == null) return false; 39 | 40 | return properties.ContainsKey(key); 41 | } 42 | 43 | public ParameterValue? GetPropertyValue(String propertyname) 44 | { 45 | if (properties == null) return null; 46 | 47 | if (properties.TryGetValue(propertyname, out ParameterValue value)) 48 | { 49 | return value; 50 | } 51 | return null; 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /CesiumIonRevitAddin/gltf/Gltf.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace CesiumIonRevitAddin.Gltf 4 | { 5 | /// 6 | /// Magic numbers to differentiate scalar and vector array buffers 7 | /// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#buffers-and-buffer-views. 8 | /// 9 | public enum Targets 10 | { 11 | ARRAY_BUFFER = 34962, // signals vertex data 12 | ELEMENT_ARRAY_BUFFER = 34963, // signals index or face data 13 | } 14 | 15 | /// 16 | /// Magic numbers to differentiate array buffer component types 17 | /// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#accessor-element-size. 18 | /// 19 | public enum ComponentType 20 | { 21 | BYTE = 5120, 22 | UNSIGNED_BYTE = 5121, 23 | SHORT = 5122, 24 | UNSIGNED_SHORT = 5123, 25 | UNSIGNED_INT = 5125, 26 | FLOAT = 5126, 27 | } 28 | 29 | internal struct Gltf 30 | { 31 | public List extensionsUsed; 32 | public List extensionsRequired; 33 | public GltfVersion asset; 34 | public List scenes; 35 | public List nodes; 36 | public List meshes; 37 | public List buffers; 38 | public List bufferViews; 39 | public List accessors; 40 | public List materials; 41 | public List textures; 42 | public List images; 43 | public List samplers; 44 | public Dictionary extensions; 45 | 46 | public Gltf(Dictionary extensions) : this() 47 | { 48 | this.extensions = extensions; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /CesiumIonRevitAddin_2022/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | [assembly: AssemblyTitle("Cesium ion for Autodesk Revit 2022")] 8 | [assembly: AssemblyDescription("Easily export Revit designs to Cesium ion using Cesium ion for Autodesk Revit, which transforms your models into 3D Tiles for immersive visualizations and interactive geospatial experiences.")] 9 | 10 | #if DEBUG 11 | [assembly: AssemblyConfiguration("Debug")] 12 | #else 13 | [assembly: AssemblyConfiguration("Release")] 14 | #endif 15 | 16 | [assembly: AssemblyCompany("Cesium GS, Inc.")] 17 | [assembly: AssemblyProduct("Cesium ion for Autodesk Revit")] 18 | [assembly: AssemblyCopyright("Copyright 2024 Cesium GS, Inc.")] 19 | [assembly: AssemblyTrademark("")] 20 | [assembly: AssemblyCulture("")] 21 | 22 | // Setting ComVisible to false makes the types in this assembly not visible 23 | // to COM components. If you need to access a type in this assembly from 24 | // COM, set the ComVisible attribute to true on that type. 25 | [assembly: ComVisible(false)] 26 | 27 | // The following GUID is for the ID of the typelib if this project is exposed to COM 28 | [assembly: Guid("b0ed72c2-cd9a-4e86-a091-fa328b2f542c")] 29 | 30 | // Version information for an assembly consists of the following four values: 31 | // 32 | // Major Version 33 | // Minor Version 34 | // Build Number 35 | // Revision 36 | // 37 | // You can specify all the values or you can default the Build and Revision Numbers 38 | // by using the '*' as shown below: 39 | // [assembly: AssemblyVersion("1.0.*")] 40 | [assembly: AssemblyVersion("1.3.0")] 41 | [assembly: AssemblyFileVersion("1.3.0")] 42 | [assembly: AssemblyInformationalVersion("1.3.0")] -------------------------------------------------------------------------------- /CesiumIonRevitAddin_2023/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | [assembly: AssemblyTitle("Cesium ion for Autodesk Revit 2023")] 8 | [assembly: AssemblyDescription("Easily export Revit designs to Cesium ion using Cesium ion for Autodesk Revit, which transforms your models into 3D Tiles for immersive visualizations and interactive geospatial experiences.")] 9 | 10 | #if DEBUG 11 | [assembly: AssemblyConfiguration("Debug")] 12 | #else 13 | [assembly: AssemblyConfiguration("Release")] 14 | #endif 15 | 16 | [assembly: AssemblyCompany("Cesium GS, Inc.")] 17 | [assembly: AssemblyProduct("Cesium ion for Autodesk Revit")] 18 | [assembly: AssemblyCopyright("Copyright 2024 Cesium GS, Inc.")] 19 | [assembly: AssemblyTrademark("")] 20 | [assembly: AssemblyCulture("")] 21 | 22 | // Setting ComVisible to false makes the types in this assembly not visible 23 | // to COM components. If you need to access a type in this assembly from 24 | // COM, set the ComVisible attribute to true on that type. 25 | [assembly: ComVisible(false)] 26 | 27 | // The following GUID is for the ID of the typelib if this project is exposed to COM 28 | [assembly: Guid("b0ed72c2-cd9a-4e86-a091-fa328b2f542c")] 29 | 30 | // Version information for an assembly consists of the following four values: 31 | // 32 | // Major Version 33 | // Minor Version 34 | // Build Number 35 | // Revision 36 | // 37 | // You can specify all the values or you can default the Build and Revision Numbers 38 | // by using the '*' as shown below: 39 | // [assembly: AssemblyVersion("1.0.*")] 40 | [assembly: AssemblyVersion("1.3.0")] 41 | [assembly: AssemblyFileVersion("1.3.0")] 42 | [assembly: AssemblyInformationalVersion("1.3.0")] -------------------------------------------------------------------------------- /CesiumIonRevitAddin_2024/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | [assembly: AssemblyTitle("Cesium ion for Autodesk Revit 2024")] 8 | [assembly: AssemblyDescription("Easily export Revit designs to Cesium ion using Cesium ion for Autodesk Revit, which transforms your models into 3D Tiles for immersive visualizations and interactive geospatial experiences.")] 9 | 10 | #if DEBUG 11 | [assembly: AssemblyConfiguration("Debug")] 12 | #else 13 | [assembly: AssemblyConfiguration("Release")] 14 | #endif 15 | 16 | [assembly: AssemblyCompany("Cesium GS, Inc.")] 17 | [assembly: AssemblyProduct("Cesium ion for Autodesk Revit")] 18 | [assembly: AssemblyCopyright("Copyright 2024 Cesium GS, Inc.")] 19 | [assembly: AssemblyTrademark("")] 20 | [assembly: AssemblyCulture("")] 21 | 22 | // Setting ComVisible to false makes the types in this assembly not visible 23 | // to COM components. If you need to access a type in this assembly from 24 | // COM, set the ComVisible attribute to true on that type. 25 | [assembly: ComVisible(false)] 26 | 27 | // The following GUID is for the ID of the typelib if this project is exposed to COM 28 | [assembly: Guid("b0ed72c2-cd9a-4e86-a091-fa328b2f542c")] 29 | 30 | // Version information for an assembly consists of the following four values: 31 | // 32 | // Major Version 33 | // Minor Version 34 | // Build Number 35 | // Revision 36 | // 37 | // You can specify all the values or you can default the Build and Revision Numbers 38 | // by using the '*' as shown below: 39 | // [assembly: AssemblyVersion("1.0.*")] 40 | [assembly: AssemblyVersion("1.3.0")] 41 | [assembly: AssemblyFileVersion("1.3.0")] 42 | [assembly: AssemblyInformationalVersion("1.3.0")] 43 | -------------------------------------------------------------------------------- /CesiumIonRevitAddin_2025/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | using System.Runtime.Versioning; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Cesium ion for Autodesk Revit 2025")] 9 | [assembly: AssemblyDescription("Easily export Revit designs to Cesium ion using Cesium ion for Autodesk Revit, which transforms your models into 3D Tiles for immersive visualizations and interactive geospatial experiences.")] 10 | 11 | #if DEBUG 12 | [assembly: AssemblyConfiguration("Debug")] 13 | #else 14 | [assembly: AssemblyConfiguration("Release")] 15 | #endif 16 | 17 | [assembly: AssemblyCompany("Cesium GS, Inc.")] 18 | [assembly: AssemblyProduct("Cesium ion for Autodesk Revit")] 19 | [assembly: AssemblyCopyright("Copyright 2024 Cesium GS, Inc.")] 20 | [assembly: AssemblyTrademark("")] 21 | [assembly: AssemblyCulture("")] 22 | 23 | // Setting ComVisible to false makes the types in this assembly not visible 24 | // to COM components. If you need to access a type in this assembly from 25 | // COM, set the ComVisible attribute to true on that type. 26 | [assembly: ComVisible(false)] 27 | 28 | // The following GUID is for the ID of the typelib if this project is exposed to COM 29 | [assembly: Guid("b0ed72c2-cd9a-4e86-a091-fa328b2f542c")] 30 | 31 | // Version information for an assembly consists of the following four values: 32 | // 33 | // Major Version 34 | // Minor Version 35 | // Build Number 36 | // Revision 37 | // 38 | // You can specify all the values or you can default the Build and Revision Numbers 39 | // by using the '*' as shown below: 40 | // [assembly: AssemblyVersion("1.0.*")] 41 | [assembly: AssemblyVersion("1.3.0")] 42 | [assembly: AssemblyFileVersion("1.3.0")] 43 | [assembly: AssemblyInformationalVersion("1.3.0")] 44 | [assembly: SupportedOSPlatform("windows")] -------------------------------------------------------------------------------- /CesiumIonRevitAddin_2026/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | using System.Runtime.Versioning; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Cesium ion for Autodesk Revit 2026")] 9 | [assembly: AssemblyDescription("Easily export Revit designs to Cesium ion using Cesium ion for Autodesk Revit, which transforms your models into 3D Tiles for immersive visualizations and interactive geospatial experiences.")] 10 | 11 | #if DEBUG 12 | [assembly: AssemblyConfiguration("Debug")] 13 | #else 14 | [assembly: AssemblyConfiguration("Release")] 15 | #endif 16 | 17 | [assembly: AssemblyCompany("Cesium GS, Inc.")] 18 | [assembly: AssemblyProduct("Cesium ion for Autodesk Revit")] 19 | [assembly: AssemblyCopyright("Copyright 2026 Cesium GS, Inc.")] 20 | [assembly: AssemblyTrademark("")] 21 | [assembly: AssemblyCulture("")] 22 | 23 | // Setting ComVisible to false makes the types in this assembly not visible 24 | // to COM components. If you need to access a type in this assembly from 25 | // COM, set the ComVisible attribute to true on that type. 26 | [assembly: ComVisible(false)] 27 | 28 | // The following GUID is for the ID of the typelib if this project is exposed to COM 29 | [assembly: Guid("b0ed72c2-cd9a-4e86-a091-fa328b2f542c")] 30 | 31 | // Version information for an assembly consists of the following four values: 32 | // 33 | // Major Version 34 | // Minor Version 35 | // Build Number 36 | // Revision 37 | // 38 | // You can specify all the values or you can default the Build and Revision Numbers 39 | // by using the '*' as shown below: 40 | // [assembly: AssemblyVersion("1.0.*")] 41 | [assembly: AssemblyVersion("1.3.0")] 42 | [assembly: AssemblyFileVersion("1.3.0")] 43 | [assembly: AssemblyInformationalVersion("1.3.0")] 44 | [assembly: SupportedOSPlatform("windows")] -------------------------------------------------------------------------------- /AddinBundle/CesiumIonRevitAddin.bundle/PackageContents.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /CesiumIonRevitAddin_2025/CesiumIonRevitAddin_2025.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net8.0-windows 4 | Library 5 | CesiumIonRevitAddin 6 | CesiumIonRevitAddin 7 | false 8 | true 9 | true 10 | true 11 | false 12 | 13 | 14 | 15 | 16 | $(DefineConstants);REVIT2025 17 | 18 | 19 | $(DefineConstants);REVIT2025 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /CesiumIonRevitAddin_2026/CesiumIonRevitAddin_2026.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net8.0-windows 4 | Library 5 | CesiumIonRevitAddin 6 | CesiumIonRevitAddin 7 | false 8 | true 9 | true 10 | true 11 | false 12 | 13 | 14 | 15 | 16 | $(DefineConstants);REVIT2026 17 | 18 | 19 | $(DefineConstants);REVIT2026 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /CesiumIonRevitAddin/Export/GltfJson.cs: -------------------------------------------------------------------------------- 1 | using CesiumIonRevitAddin.Gltf; 2 | using Newtonsoft.Json; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | 6 | namespace CesiumIonRevitAddin.Export 7 | { 8 | internal static class GltfJson 9 | { 10 | public static string Get( 11 | List scenes, 12 | List nodes, 13 | List meshes, 14 | List materials, 15 | List buffers, 16 | List bufferViews, 17 | List accessors, 18 | List extensionsUsed, 19 | List extensionsRequired, 20 | Dictionary extensions, 21 | Preferences preferences, 22 | GltfVersion asset, 23 | List images, 24 | List textures, 25 | List samplers) 26 | { 27 | // Package the properties into a serializable container 28 | var model = new CesiumIonRevitAddin.Gltf.Gltf 29 | { 30 | extensionsUsed = extensionsUsed, 31 | extensionsRequired = extensionsRequired, 32 | asset = asset, 33 | scenes = scenes, 34 | nodes = nodes, 35 | meshes = meshes, 36 | buffers = buffers, 37 | bufferViews = bufferViews, 38 | accessors = accessors 39 | }; 40 | 41 | if (images.Count > 0) 42 | { 43 | model.images = images; 44 | model.textures = textures; 45 | model.samplers = samplers; 46 | } 47 | 48 | if (materials.Count > 0) 49 | { 50 | model.materials = materials; 51 | } 52 | 53 | model.extensions = extensions; 54 | 55 | var settings = new JsonSerializerSettings 56 | { 57 | NullValueHandling = NullValueHandling.Ignore, 58 | Formatting = Formatting.Indented 59 | }; 60 | string serializedModel = JsonConvert.SerializeObject(model, settings); 61 | 62 | return serializedModel; 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.rsuser 6 | *.suo 7 | *.user 8 | *.userosscache 9 | *.sln.docstates 10 | 11 | # User-specific files (MonoDevelop/Xamarin Studio) 12 | *.userprefs 13 | 14 | # Build results 15 | [Dd]ebug/ 16 | [Dd]ebugPublic/ 17 | [Rr]elease/ 18 | [Rr]eleases/ 19 | x64/ 20 | x86/ 21 | [Aa][Rr][Mm]/ 22 | [Aa][Rr][Mm]64/ 23 | bld/ 24 | [Bb]in/ 25 | [Oo]bj/ 26 | [Ll]og/ 27 | 28 | # Visual Studio 2015/2017/2019/2022 cache/options directory 29 | .vs/ 30 | # Uncomment if you have tasks that create the project's static files in wwwroot 31 | #wwwroot/ 32 | 33 | # MSTest test Results 34 | [Tt]est[Rr]esult*/ 35 | [Bb]uild[Ll]og.* 36 | 37 | # NUnit 38 | *.VisualState.xml 39 | TestResult.xml 40 | nunit-*.xml 41 | 42 | # .NET Core 43 | project.lock.json 44 | project.fragment.lock.json 45 | artifacts/ 46 | 47 | # .NET Core SDK-based projects 48 | project.assets.json 49 | project.nuget.cache 50 | global.json 51 | *.dll 52 | *.exe 53 | *.pdb 54 | *.cache 55 | *.nupkg 56 | *.vsconfig 57 | # Including nuget symbol packages 58 | *.snupkg 59 | # Including project.assets.json 60 | project.assets.json 61 | 62 | # Package Parts 63 | *.csproj.nuget.dgspec.json 64 | *.csproj.nuget.g.props 65 | *.csproj.nuget.g.targets 66 | *.csproj.nuget.targets 67 | *.csproj.nuget.props 68 | 69 | # PowerShell Modules 70 | PSModules/ 71 | 72 | # Windows image file caches 73 | Thumbs.db 74 | ehthumbs.db 75 | 76 | # Folder config file 77 | Desktop.ini 78 | 79 | # Recycle Bin used on file shares 80 | $RECYCLE.BIN/ 81 | 82 | # Mac desktop service store files 83 | .DS_Store 84 | 85 | # ASP.NET Scaffolding 86 | ScaffoldingReadMe.txt 87 | 88 | # Rider 89 | .idea/ 90 | 91 | # User-specific files 92 | *.suo 93 | *.user 94 | *.userosscache 95 | *.sln.docstates 96 | 97 | # Mono Auto Generated Files 98 | mono_crash.* 99 | 100 | # Avoid ignoring DLLs and EXEs if you are building libraries 101 | # However, uncomment if necessary 102 | #*.dll 103 | #*.exe 104 | 105 | # Files built by Visual Studio 106 | *_i.c 107 | *_p.c 108 | *_h.h 109 | *_i.h 110 | *_P.h 111 | *_I.c 112 | [Rr]esources/ 113 | [Rr]esources.Designer.cs 114 | [Rr]esources.resx 115 | Settings.Designer.cs 116 | Settings.settings 117 | 118 | # ReSharper 119 | _ReSharper.*/ 120 | *.[Rr]e[Ss]harper 121 | *.DotSettings.user 122 | packages/* 123 | 124 | # Bundle Folder 125 | AddinBundle/CesiumIonRevitAddin.bundle/Contents/* 126 | 127 | # Documentation 128 | !/documentation/* -------------------------------------------------------------------------------- /CesiumIonRevitAddin/Utils/GeometryUtils.cs: -------------------------------------------------------------------------------- 1 | using Autodesk.Revit.DB; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | 6 | namespace CesiumIonRevitAddin.Utils 7 | { 8 | internal class GeometryUtils 9 | { 10 | public static List GetMeshes(Document document, Element element) 11 | { 12 | GeometryElement geometryElement = GetGeometryElement(document, element); 13 | 14 | List meshes = new List(); 15 | foreach (GeometryInstance geoObject in geometryElement.OfType()) 16 | { 17 | foreach (var mesh in geoObject.GetSymbolGeometry().OfType()) 18 | { 19 | meshes.Add(mesh); 20 | } 21 | } 22 | 23 | return meshes; 24 | } 25 | 26 | public static GeometryElement GetGeometryElement(Document document, Element element) 27 | { 28 | GeometryElement result; 29 | try 30 | { 31 | Options opt = new Options 32 | { 33 | ComputeReferences = true, 34 | View = document.ActiveView, 35 | }; 36 | result = element.get_Geometry(opt); 37 | } 38 | catch 39 | { 40 | Options opt = new Options 41 | { 42 | ComputeReferences = true, 43 | }; 44 | result = element.get_Geometry(opt); 45 | } 46 | 47 | return result; 48 | } 49 | 50 | public static XYZ GetNormal(MeshTriangle triangle) 51 | { 52 | var vertex0 = triangle.get_Vertex(0); 53 | var side1 = triangle.get_Vertex(1) - vertex0; 54 | var side2 = triangle.get_Vertex(2) - vertex0; 55 | var normal = side1.CrossProduct(side2); 56 | return normal.Normalize(); 57 | } 58 | 59 | public static XYZ GetProjectOffset(Document doc) 60 | { 61 | ProjectLocation currentLocation = doc.ActiveProjectLocation; 62 | ProjectPosition projectPosition = currentLocation.GetProjectPosition(new XYZ(0, 0, 0)); // Get the shared coordinates for 0,0,0 63 | return new XYZ(projectPosition.EastWest, projectPosition.NorthSouth, projectPosition.Elevation); 64 | } 65 | 66 | public static double GetProjectTrueNorth(Document doc) 67 | { 68 | ProjectLocation currentLocation = doc.ActiveProjectLocation; 69 | ProjectPosition projectPosition = currentLocation.GetProjectPosition(new XYZ(0, 0, 0)); // Get the shared coordinates for 0,0,0 70 | return projectPosition.Angle * (180.0 / Math.PI); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /CesiumIonRevitAddin/Preferences.cs: -------------------------------------------------------------------------------- 1 | using CesiumIonRevitAddin.Utils; 2 | using Newtonsoft.Json; 3 | using System; 4 | using System.IO; 5 | 6 | namespace CesiumIonRevitAddin 7 | { 8 | public class Preferences 9 | { 10 | public bool Materials { get; set; } = true; 11 | public bool Textures { get; set; } = true; 12 | public bool Normals { get; set; } = true; 13 | public bool Links { get; set; } = false; 14 | public bool Levels { get; set; } 15 | public bool Properties { get; set; } 16 | public bool RelocateTo0 { get; set; } 17 | public bool FlipAxis { get; set; } = true; 18 | public bool SymbolicInstancing { get; set; } = true; 19 | public bool IonInstancing { get; set; } = false; 20 | public bool TrueNorth { get; set; } = true; 21 | public bool SharedCoordinates { get; set; } = true; 22 | public string EpsgCode { get; set; } = ""; 23 | public int MaxTextureSize { get; set; } = 2048; 24 | #if DEBUG 25 | public bool KeepGltf { get; } = true; 26 | #else 27 | public bool KeepGltf { get; } = false; 28 | #endif 29 | public bool Export3DTilesDB { get; } = true; 30 | public bool ExportMetadata { get; set; } = true; 31 | public string OutputPath { get; set; } = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + "\\tileset.3dtiles"; 32 | public string OutputDirectory => Path.GetDirectoryName(OutputPath); 33 | public string OutputFilename => Path.GetFileName(OutputPath); 34 | public string TempDirectory => Path.Combine(OutputDirectory, Path.GetFileNameWithoutExtension(OutputPath) + "_temp"); 35 | public string JsonPath => Path.Combine(TempDirectory, "tileset.json"); 36 | public string BinPath => Path.Combine(TempDirectory, "tileset.bin"); 37 | public string GltfPath => Path.Combine(TempDirectory, "tileset.gltf"); 38 | public string Temp3DTilesPath => Path.Combine(TempDirectory, OutputFilename); 39 | public bool ionExport { get; set; } = false; 40 | public bool VerboseLogging { get; } = false; 41 | 42 | public string ToJson() => JsonConvert.SerializeObject(this, Formatting.Indented); 43 | 44 | public static Preferences FromJson(string json) => JsonConvert.DeserializeObject(json); 45 | 46 | public void SaveToFile(string filePath) 47 | { 48 | Directory.CreateDirectory(Path.GetDirectoryName(filePath)); 49 | File.WriteAllText(filePath, ToJson()); 50 | } 51 | 52 | public static Preferences LoadFromFile(string filePath) => FromJson(File.ReadAllText(filePath)); 53 | 54 | public static string GetPreferencesFolder() => Path.Combine(Util.GetAddinUserDataFolder(), "preferences"); 55 | 56 | public static string GetPreferencesPathForProject(string projectPath) => Path.Combine(GetPreferencesFolder(), Path.GetFileNameWithoutExtension(projectPath) + ".json"); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Documentation/ReleaseGuide/README.md: -------------------------------------------------------------------------------- 1 | # Releasing a new version of Cesium ion for Autodesk Revit 2 | 3 | This is the process we follow when releasing a new version of Cesium ion for Autodesk Revit on GitHub and the Autodesk app store. 4 | 5 | Inno Setup is required for generating installers. It can be downloaded [here](https://jrsoftware.org/isdl.php). 6 | 7 | 1. Cesium ion for Autodesk Revit does not currently use CI to produce a build, so releases are currently generated on developers' systems. It is crucial you work with a clean copy of the main branch. If in doubt, run `git reset --hard` and `git clean -x -d -f` prior to starting. 8 | 2. Update `AppVersion` and `FriendlyVersion` in [PackageContents.xml](/AddinBundle/CesiumIonRevitAddin.bundle/PackageContents.xml), along with updating each `Version` entry for each add-in listed in the file. 9 | 3. Update `AssemblyVersion`, `AssemblyFileVersion` and `AssemblyInformationVersion` in the following files: 10 | 11 | - [/CesiumIonRevitAddin_2022/Properties/AssemblyInfo.cs](/CesiumIonRevitAddin_2022/Properties/AssemblyInfo.cs) 12 | - [/CesiumIonRevitAddin_2023/Properties/AssemblyInfo.cs](/CesiumIonRevitAddin_2023/Properties/AssemblyInfo.cs) 13 | - [/CesiumIonRevitAddin_2024/Properties/AssemblyInfo.cs](/CesiumIonRevitAddin_2024/Properties/AssemblyInfo.cs) 14 | - [/CesiumIonRevitAddin_2025/Properties/AssemblyInfo.cs](/CesiumIonRevitAddin_2025/Properties/AssemblyInfo.cs) 15 | - [/CesiumIonRevitAddin_2026/Properties/AssemblyInfo.cs](/CesiumIonRevitAddin_2026/Properties/AssemblyInfo.cs) 16 | 17 | 4. Update `MyAppVersion` in [installer.iss](/AddinBundle/installer.iss 18 | ) 19 | 5. Update [`CHANGES.md`](../../CHANGES.md). 20 | 6. Create a branch, e.g. `git checkout -b release-0.0.0`. 21 | 7. Commit the changes, e.g. `git commit -am "0.0.0 release"`. 22 | 8. Push the commit, e.g. `git push origin release-0.0.0`. 23 | 9. Open a PR and merge the branch with "Rebase and merge". 24 | 10. Tag the release, e.g. `git tag -a v0.0.0 -m "0.0.0 release"`. 25 | 11. Push the tag, e.g. `git push origin v0.0.0`. 26 | 12. Generate a new Release bundle by following **Creating a Bundle** in the [Developer Guide](/Documentation/DeveloperGuide/README.md) 27 | 13. In the bundle, digitally sign `CesiumIonRevitAddin.dll` for each version of Revit. 28 | 14. Open [installer.iss](/AddinBundle/installer.iss) in Inno Setup. 29 | 15. Press CTRL+F9 to compile a new installer. The installer will be located in the [/AddinBundle/Output](/AddinBundle/Output) folder. 30 | 16. Digitally sign the installer. 31 | 17. Run the installer and verify the new add-in is available to Revit. 32 | 18. Confirm the installed bundle contains digitally signed .dlls by opening `%appdata%\Autodesk\ApplicationPlugins\CesiumIonRevitAddin.bundle\Contents\2023\CesiumIonRevitAddin` and checking each `CesiumIonRevitAddin.dll` contains a digital signature. 33 | 19. Create a new release on GitHub: https://github.com/CesiumGS/cesium-ion-revit-add-in/releases/new. 34 | * Choose the new tag. 35 | * Copy the changelog into the description. Follow the format used in previous releases. 36 | * Upload the installer generated by Inno Setup. 37 | 20. Submit the installer to the Autodesk App Store for approval. 38 | -------------------------------------------------------------------------------- /CesiumIonRevitAddin/Logger.cs: -------------------------------------------------------------------------------- 1 | using CesiumIonRevitAddin.Model; 2 | using CesiumIonRevitAddin.Utils; 3 | using System; 4 | using System.IO; 5 | using System.Linq; 6 | 7 | namespace CesiumIonRevitAddin 8 | { 9 | internal class Logger 10 | { 11 | private static Logger instance = null; 12 | private static readonly object LogMutex = new object(); 13 | private readonly StreamWriter logFile; 14 | 15 | public static bool Enabled { get; set; } = true; 16 | 17 | private Logger() 18 | { 19 | string timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss"); 20 | string addinLogFolderPath = Path.Combine(Util.GetAddinUserDataFolder(), "Logs"); 21 | Directory.CreateDirectory(addinLogFolderPath); 22 | string logFilePath = Path.Combine(addinLogFolderPath, $"CesiumIonRevitAddinLog_{timestamp}.txt"); 23 | logFile = new StreamWriter(logFilePath, false) { AutoFlush = true }; 24 | } 25 | 26 | public static Logger Instance 27 | { 28 | get 29 | { 30 | if (instance == null) 31 | { 32 | lock (LogMutex) 33 | { 34 | if (instance == null) 35 | { 36 | instance = new Logger(); 37 | } 38 | } 39 | } 40 | return instance; 41 | } 42 | } 43 | 44 | public void Log(string message) 45 | { 46 | if (!Enabled) 47 | { 48 | return; 49 | } 50 | 51 | lock (LogMutex) 52 | { 53 | DateTime now = DateTime.Now; 54 | var timeStr = now.ToString("yyyy-MM-dd HH:mm:ss.fff"); 55 | logFile.WriteLine($"{timeStr}: {message}"); 56 | } 57 | } 58 | 59 | public void Log(GeometryDataObject geometryDataObject, bool printVertices = false) 60 | { 61 | 62 | double sumX = 0, sumY = 0, sumZ = 0; 63 | int pointCount = geometryDataObject.Vertices.Count / 3; 64 | 65 | for (int i = 0; i < geometryDataObject.Vertices.Count; i += 3) 66 | { 67 | sumX += geometryDataObject.Vertices[i]; 68 | sumY += geometryDataObject.Vertices[i + 1]; 69 | sumZ += geometryDataObject.Vertices[i + 2]; 70 | } 71 | 72 | double centroidX = sumX / pointCount; 73 | double centroidY = sumY / pointCount; 74 | double centroidZ = sumZ / pointCount; 75 | 76 | string formattedList = ""; 77 | if (printVertices) 78 | { 79 | formattedList = string.Join(", ", geometryDataObject.Vertices.Select(d => d.ToString("F2"))); 80 | } 81 | lock (LogMutex) 82 | { 83 | DateTime now = DateTime.Now; 84 | var timeStr = now.ToString("yyyy-MM-dd HH:mm:ss.fff"); 85 | logFile.WriteLine($"{timeStr}: Centroid: <{centroidX:F2}, {centroidY:F2}, {centroidZ:F2}>"); 86 | if (printVertices) 87 | { 88 | logFile.WriteLine($"{timeStr}: {formattedList}"); 89 | } 90 | } 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /AddinBundle/installer.iss: -------------------------------------------------------------------------------- 1 | ; Script generated by the Inno Setup Script Wizard. 2 | 3 | #define MyAppName "Cesium ion for Autodesk Revit" 4 | #define MyAppVersion "1.3.0" 5 | #define MyAppPublisher "Cesium GS, Inc." 6 | #define MyAppURL "https://www.cesium.com/" 7 | #define AutodeskGUID "{588139E0-5F8C-4817-B5D8-B4DAFB8954E7}" 8 | 9 | [Setup] 10 | AppId={{7549ECAF-53CD-428E-9576-A827C16D43A7} 11 | AppName={#MyAppName} 12 | AppVersion={#MyAppVersion} 13 | AppVerName={#MyAppName} {#MyAppVersion} 14 | AppPublisher={#MyAppPublisher} 15 | AppPublisherURL={#MyAppURL} 16 | AppSupportURL={#MyAppURL} 17 | AppUpdatesURL={#MyAppURL} 18 | VersionInfoVersion={#MyAppVersion} 19 | DefaultDirName={userappdata}\Autodesk\ApplicationPlugins\CesiumIonRevitAddin.bundle 20 | DisableDirPage=yes 21 | DisableProgramGroupPage=yes 22 | PrivilegesRequired=lowest 23 | OutputBaseFilename={#MyAppName} {#MyAppVersion} 24 | Compression=lzma 25 | SolidCompression=yes 26 | WizardStyle=modern 27 | SetupIconFile=.\Installer\cesium.ico 28 | UninstallDisplayIcon={app}\unins000.exe 29 | WizardImageFile=.\Installer\sidelogo.bmp 30 | WizardSmallImageFile=.\Installer\logo.bmp 31 | WizardImageAlphaFormat=premultiplied 32 | 33 | [Languages] 34 | Name: "english"; MessagesFile: "compiler:Default.isl" 35 | 36 | [Files] 37 | ; Copies all files and subdirectories from the source to the destination 38 | Source: ".\CesiumIonRevitAddin.bundle\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs 39 | 40 | [InstallDelete] 41 | ; Remove the previous add-in 42 | Type: filesandordirs; Name: "{app}" 43 | 44 | [Code] 45 | function OriginalAddinInstalled: boolean; 46 | var 47 | Path1Exists: Boolean; 48 | Path2Exists: Boolean; 49 | begin 50 | // Check both 64-bit and 32-bit uninstall locations for the original Autodesk installer 51 | Path1Exists := RegKeyExists(HKEY_LOCAL_MACHINE, 'SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{#AutodeskGUID}'); 52 | Path2Exists := RegKeyExists(HKEY_LOCAL_MACHINE, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{#AutodeskGUID}'); 53 | Result := Path1Exists or Path2Exists; 54 | end; 55 | 56 | function PrepareToInstall(var NeedsRestart: Boolean): String; 57 | var 58 | addinExists: Boolean; 59 | ResultCode: Integer; 60 | begin 61 | // This checks for an existing installation of the Autodesk installer from version 1.0.0, and if found prompts the user to uninstall first. 62 | // This is to avoid having both Autodesk and Cesium installers simultaneously present on the users system 63 | addinExists := OriginalAddinInstalled; 64 | if addinExists then 65 | begin 66 | if MsgBox('An existing installation of Cesium ion for Autodesk Revit 1.0.0 must be uninstalled first. Do you want to uninstall it now?', mbConfirmation, MB_YESNO) = IDYES then 67 | begin 68 | ShellExec('', 'msiexec.exe', '/X {#AutodeskGUID} /passive', '', SW_SHOWNORMAL, ewWaitUntilTerminated, ResultCode); 69 | Log(SysErrorMessage(ResultCode)); 70 | if ResultCode = 0 then 71 | begin 72 | MsgBox('Setup successfully uninstalled Cesium ion for Autodesk Revit 1.0.0. Click OK to continue with installation.', mbError, MB_OK); 73 | end 74 | else 75 | begin 76 | Result := 'Setup could not uninstall the existing installation of Cesium ion for Autodesk Revit 1.0.0. Please try uninstalling it manually.'; 77 | end; 78 | end 79 | else 80 | begin 81 | Result := 'The existing installation of Cesium ion for Autodesk Revit 1.0.0 must be uninstalled first.'; 82 | end; 83 | end 84 | end; -------------------------------------------------------------------------------- /CesiumIonRevitAddin/IndexedDictionary.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace CesiumIonRevitAddin.Gltf 5 | { 6 | internal class IndexedDictionary 7 | { 8 | public List List { get; } = new List(); 9 | public string CurrentKey { get; private set; } 10 | 11 | public bool Contains(string uuid) 12 | { 13 | return dict.ContainsKey(uuid); 14 | } 15 | public int CurrentIndex 16 | { 17 | get { return dict[CurrentKey]; } 18 | } 19 | 20 | public T CurrentItem => this.List[this.dict[this.CurrentKey]]; 21 | 22 | public T GetElement(string uuid) 23 | { 24 | int index = GetIndexFromUUID(uuid); 25 | return this.List[index]; 26 | } 27 | 28 | public Dictionary Dict 29 | { 30 | get 31 | { 32 | output.Clear(); 33 | foreach (var kvp in this.dict) 34 | { 35 | output.Add(kvp.Key, this.List[kvp.Value]); 36 | } 37 | 38 | return output; 39 | } 40 | } 41 | 42 | public void Reset() 43 | { 44 | dict.Clear(); 45 | this.List.Clear(); 46 | Dict.Clear(); 47 | CurrentKey = string.Empty; 48 | } 49 | 50 | public bool AddOrUpdateCurrent(string uuid, T elem) 51 | { 52 | if (!dict.ContainsKey(uuid)) 53 | { 54 | this.List.Add(elem); 55 | dict.Add(uuid, this.List.Count - 1); 56 | CurrentKey = uuid; 57 | return true; 58 | } 59 | 60 | CurrentKey = uuid; 61 | return false; 62 | } 63 | 64 | public int GetIndexFromUuid(string uuid) 65 | { 66 | if (dict.TryGetValue(uuid, out int index)) 67 | { 68 | return index; 69 | } 70 | 71 | throw new KeyNotFoundException($"Specified item with UUID '{uuid}' could not be found."); 72 | } 73 | 74 | public bool AddOrUpdateCurrentMaterial(string uuid, T elem, bool doubleSided) 75 | { 76 | if (!dict.ContainsKey(uuid)) 77 | { 78 | List.Add(elem); 79 | dict.Add(uuid, List.Count - 1); 80 | CurrentKey = uuid; 81 | return true; 82 | } 83 | 84 | CurrentKey = uuid; 85 | 86 | if (GetElement(uuid) is GltfMaterial mat) 87 | { 88 | mat.DoubleSided = doubleSided; 89 | } 90 | 91 | return false; 92 | } 93 | 94 | private readonly Dictionary dict = new Dictionary(); 95 | private readonly Dictionary output = new Dictionary(); 96 | 97 | private int GetIndexFromUUID(string uuid) 98 | { 99 | try 100 | { 101 | return dict[uuid]; // ignore Intellisense 102 | } 103 | catch (KeyNotFoundException) 104 | { 105 | Logger.Instance.Log("Specified item could not be found in IndexedDictionary: " + uuid); 106 | } 107 | 108 | catch (Exception ex) 109 | { 110 | Logger.Instance.Log("Error getting the specified item " + uuid + " in IndexedDictionary: " + ex.Message); 111 | } 112 | 113 | return -1; 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /CesiumIonRevitAddin/Export/ParameterValue.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | 4 | [JsonConverter(typeof(ParameterValueConverter))] 5 | public struct ParameterValue 6 | { 7 | public int? IntegerValue { get; set; } 8 | public double? DoubleValue { get; set; } 9 | public string StringValue { get; set; } 10 | public long? LongValue { get; set; } 11 | 12 | public static implicit operator ParameterValue(int value) => new ParameterValue { IntegerValue = value }; 13 | public static implicit operator ParameterValue(double value) => new ParameterValue { DoubleValue = value }; 14 | public static implicit operator ParameterValue(long value) => new ParameterValue { LongValue = value }; 15 | public static implicit operator ParameterValue(string value) => new ParameterValue { StringValue = value }; 16 | #if REVIT2022 || REVIT2023 17 | public static implicit operator ParameterValue(Autodesk.Revit.DB.ElementId value) => new ParameterValue { LongValue = (System.Int64) value.IntegerValue }; 18 | #else 19 | public static implicit operator ParameterValue(Autodesk.Revit.DB.ElementId value) => new ParameterValue { LongValue = value.Value }; 20 | #endif 21 | 22 | public Type GetStoredType() 23 | { 24 | if (IntegerValue.HasValue) 25 | return typeof(int); 26 | if (DoubleValue.HasValue) 27 | return typeof(double); 28 | if (LongValue.HasValue) 29 | return typeof(long); 30 | if (!string.IsNullOrEmpty(StringValue)) 31 | return typeof(string); 32 | return null; 33 | } 34 | 35 | public bool Equals(ParameterValue other) 36 | { 37 | return Nullable.Equals(IntegerValue, other.IntegerValue) 38 | && Nullable.Equals(DoubleValue, other.DoubleValue) 39 | && string.Equals(StringValue, other.StringValue) 40 | && Nullable.Equals(LongValue, other.LongValue); 41 | } 42 | 43 | public override bool Equals(object obj) 44 | { 45 | return obj is ParameterValue other && Equals(other); 46 | } 47 | 48 | public override int GetHashCode() 49 | { 50 | unchecked 51 | { 52 | int hash = 17; 53 | hash = hash * 23 + IntegerValue.GetHashCode(); 54 | hash = hash * 23 + DoubleValue.GetHashCode(); 55 | hash = hash * 23 + (StringValue != null ? StringValue.GetHashCode() : 0); 56 | hash = hash * 23 + LongValue.GetHashCode(); 57 | return hash; 58 | } 59 | } 60 | 61 | public static bool operator ==(ParameterValue left, ParameterValue right) => left.Equals(right); 62 | public static bool operator !=(ParameterValue left, ParameterValue right) => !left.Equals(right); 63 | } 64 | 65 | public class ParameterValueConverter : JsonConverter 66 | { 67 | public override void WriteJson(JsonWriter writer, ParameterValue value, JsonSerializer serializer) 68 | { 69 | if (value.IntegerValue.HasValue) 70 | { 71 | writer.WriteValue(value.IntegerValue.Value); 72 | } 73 | else if (value.DoubleValue.HasValue) 74 | { 75 | writer.WriteValue(value.DoubleValue.Value); 76 | } 77 | else if (value.LongValue.HasValue) 78 | { 79 | writer.WriteValue(value.LongValue.Value); 80 | } 81 | else if (!string.IsNullOrEmpty(value.StringValue)) 82 | { 83 | writer.WriteValue(value.StringValue); 84 | } 85 | else 86 | { 87 | writer.WriteNull(); 88 | } 89 | } 90 | 91 | public override ParameterValue ReadJson(JsonReader reader, Type objectType, ParameterValue existingValue, bool hasExistingValue, JsonSerializer serializer) 92 | { 93 | throw new NotImplementedException("Deserialization is not supported for ParameterValue."); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /CesiumIonRevitAddin/Forms/IonConnectDialog.cs: -------------------------------------------------------------------------------- 1 | using CesiumIonRevitAddin.CesiumIonClient; 2 | using System; 3 | using System.Net.Sockets; 4 | using System.Net; 5 | using System.Windows.Forms; 6 | using Autodesk.Revit.UI; 7 | using System.Threading; 8 | 9 | namespace CesiumIonRevitAddin.Forms 10 | { 11 | public partial class IonConnectDialog : Form 12 | { 13 | private CancellationTokenSource cts; 14 | private int port; 15 | 16 | public IonConnectDialog() 17 | { 18 | InitializeComponent(); 19 | setTextFieldsEnabled(selfHostedRadioBtn.Checked); 20 | 21 | // Use a randomly available port for the redirect URI 22 | TcpListener portListener = new TcpListener(IPAddress.Loopback, 0); 23 | portListener.Start(); 24 | port = ((IPEndPoint)portListener.LocalEndpoint).Port; 25 | portListener.Stop(); 26 | } 27 | 28 | private async void connectBtn_Click(object sender, EventArgs e) 29 | { 30 | // Standard Cesium ion OAuth2 parameters 31 | string ionServerUrl = "https://ion.cesium.com/"; 32 | string apiServerUrl = "https://api.cesium.com/"; 33 | string responseType = "code"; 34 | string clientID = "847"; 35 | string redirectUrl = "http://127.0.0.1/cesium-ion-revit-addin/oauth2/callback"; 36 | string scope = "assets:write"; 37 | 38 | if (selfHostedRadioBtn.Checked) 39 | { 40 | ionServerUrl = ionUrlText.Text; 41 | apiServerUrl = apiUrlText.Text; 42 | clientID = clientIDText.Text; 43 | redirectUrl = redirectUrlText.Text; 44 | } 45 | 46 | UriBuilder uriBuilder = new UriBuilder(new Uri(redirectUrl)) 47 | { 48 | Port = port 49 | }; 50 | redirectUrl = uriBuilder.Uri.ToString(); 51 | 52 | // Cancel the existing connection if another is started 53 | if (cts != null) 54 | cts.Cancel(); 55 | 56 | cts = new CancellationTokenSource(); 57 | 58 | ConnectionResult result = await Connection.ConnectToIon(ionServerUrl, apiServerUrl, responseType, clientID, redirectUrl, scope, cts.Token); 59 | 60 | if (result.Status == ConnectionStatus.Success) 61 | { 62 | Autodesk.Revit.UI.TaskDialog.Show("Connected", result.Message); 63 | this.Close(); 64 | } 65 | else if (result.Status == ConnectionStatus.Failure) 66 | { 67 | Autodesk.Revit.UI.TaskDialog.Show("Connection Failed", result.Message); 68 | } 69 | else if (result.Status == ConnectionStatus.Cancelled) 70 | { 71 | // Do nothing 72 | } 73 | 74 | 75 | } 76 | 77 | private void selfHostedRadioBtn_CheckedChanged(object sender, EventArgs e) 78 | { 79 | setTextFieldsEnabled(selfHostedRadioBtn.Checked); 80 | } 81 | 82 | private void setTextFieldsEnabled(bool enabled) 83 | { 84 | ionUrlText.Enabled = enabled; 85 | apiUrlText.Enabled = enabled; 86 | clientIDText.Enabled = enabled; 87 | redirectUrlText.Enabled = enabled; 88 | } 89 | 90 | private void cancelBtn_Click(object sender, EventArgs e) 91 | { 92 | this.Close(); 93 | } 94 | 95 | private void IonConnectDialog_FormClosing(object sender, FormClosingEventArgs e) 96 | { 97 | // Cancel any existing connection task that hasn't completed 98 | if (cts != null) 99 | cts.Cancel(); 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /CesiumIonRevitAddin/Utils/MaterialUtils.cs: -------------------------------------------------------------------------------- 1 | using Autodesk.Revit.DB; 2 | using CesiumIonRevitAddin.Gltf; 3 | using System; 4 | using System.Drawing; 5 | using System.Drawing.Drawing2D; 6 | using System.Drawing.Imaging; 7 | 8 | namespace CesiumIonRevitAddin.Utils 9 | { 10 | internal class MaterialUtils 11 | { 12 | public static Material GetMeshMaterial(Document document, Mesh mesh) 13 | { 14 | ElementId materialId = mesh.MaterialElementId; 15 | if (materialId != null) 16 | { 17 | return document.GetElement(materialId) as Material; 18 | } 19 | else 20 | { 21 | return null; 22 | } 23 | } 24 | 25 | public static void SetMaterial(Document doc, Preferences preferences, Mesh mesh, IndexedDictionary materials, bool doubleSided) 26 | { 27 | Material material = MaterialUtils.GetMeshMaterial(doc, mesh); 28 | 29 | if (preferences.Materials) 30 | { 31 | if (material == null) 32 | { 33 | material = Collectors.GetRandomMaterial(doc); 34 | } 35 | 36 | GltfMaterial gltfMaterial = GltfExportUtils.GetGLTFMaterial(materials.List, material, doubleSided); 37 | 38 | materials.AddOrUpdateCurrentMaterial(material.UniqueId, gltfMaterial, doubleSided); 39 | } 40 | } 41 | 42 | public static void SaveDownsampledTexture(string inputPath, string outputPath, int maxWidth, int maxHeight) 43 | { 44 | using (var sourceImage = Image.FromFile(inputPath)) 45 | { 46 | int originalWidth = sourceImage.Width; 47 | int originalHeight = sourceImage.Height; 48 | 49 | // Check if downsampling is needed 50 | if (originalWidth <= maxWidth && originalHeight <= maxHeight) 51 | { 52 | // If the image is smaller or equal to the target size, save it as is 53 | sourceImage.Save(outputPath); 54 | return; 55 | } 56 | 57 | // Calculate the new dimensions while maintaining aspect ratio 58 | float widthRatio = (float)maxWidth / originalWidth; 59 | float heightRatio = (float)maxHeight / originalHeight; 60 | float ratio = Math.Min(widthRatio, heightRatio); 61 | 62 | int newWidth = (int)(originalWidth * ratio); 63 | int newHeight = (int)(originalHeight * ratio); 64 | 65 | // Create a new bitmap for the downsampled image 66 | var destRect = new System.Drawing.Rectangle(0, 0, newWidth, newHeight); 67 | var destImage = new Bitmap(newWidth, newHeight); 68 | 69 | // Use high-quality settings for downsampling 70 | using (var graphics = Graphics.FromImage(destImage)) 71 | { 72 | graphics.CompositingMode = CompositingMode.SourceCopy; 73 | graphics.CompositingQuality = CompositingQuality.HighQuality; 74 | graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; 75 | graphics.SmoothingMode = SmoothingMode.HighQuality; 76 | graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; 77 | 78 | using (var wrapMode = new ImageAttributes()) 79 | { 80 | wrapMode.SetWrapMode(WrapMode.TileFlipXY); 81 | graphics.DrawImage(sourceImage, destRect, 0, 0, originalWidth, originalHeight, GraphicsUnit.Pixel, wrapMode); 82 | } 83 | } 84 | 85 | // Save the downsampled image in the original format 86 | destImage.Save(outputPath, sourceImage.RawFormat); 87 | } 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /CesiumIonRevitAddin/Forms/IonUploadDialog.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using System.Windows.Forms; 4 | using CesiumIonRevitAddin.CesiumIonClient; 5 | using Autodesk.Revit.UI; 6 | 7 | 8 | namespace CesiumIonRevitAddin.Forms 9 | { 10 | public partial class IonUploadDialog : Form 11 | { 12 | string assetUrl; 13 | readonly string zipPath; 14 | readonly string assetName; 15 | readonly string assetDesc; 16 | readonly string inputCrs; 17 | readonly bool instancing; 18 | 19 | public IonUploadDialog(string zipPath, string assetName, string assetDesc, string inputCrs, bool instancing) 20 | { 21 | InitializeComponent(); 22 | this.zipPath = zipPath; 23 | this.assetName = assetName; 24 | this.assetDesc = assetDesc; 25 | this.inputCrs = inputCrs; 26 | this.instancing = instancing; 27 | 28 | // Disable buttons until the upload is complete or fails 29 | openAssetBtn.Enabled = false; 30 | closeBtn.Enabled = false; 31 | 32 | // Start the upload process 33 | StartUpload(); 34 | } 35 | 36 | private void StartUpload() 37 | { 38 | // Create a progress handler that will report progress to UpdateProgress 39 | var progressHandler = new Progress(percent => 40 | { 41 | UpdateProgress(percent); 42 | }); 43 | 44 | // Start the upload task in a separate thread 45 | Task.Run(async () => 46 | { 47 | var result = await Connection.Upload( 48 | this.zipPath, 49 | this.assetName, 50 | this.assetDesc, 51 | "", 52 | "3DTILES", 53 | "BIM_CAD", 54 | this.inputCrs, 55 | this.instancing, 56 | progressHandler); 57 | 58 | // Invoke UI updates on the main thread 59 | this.Invoke(new Action(() => 60 | { 61 | if (result.Status == ConnectionStatus.Success) 62 | { 63 | assetUrl = result.Message; // Assuming Message contains the asset URL 64 | SetUploadComplete(); 65 | } 66 | else 67 | { 68 | Autodesk.Revit.UI.TaskDialog.Show("Upload failed", result.Message); // Now called on the UI thread 69 | this.Close(); 70 | } 71 | })); 72 | }); 73 | } 74 | 75 | 76 | private void openAsset_Click(object sender, EventArgs e) 77 | { 78 | // Open asset URL in the default browser (if assetUrl is valid) 79 | if (!string.IsNullOrEmpty(assetUrl)) 80 | { 81 | Connection.OpenBrowser(assetUrl); 82 | } 83 | } 84 | 85 | // Method to update the progress bar 86 | public void UpdateProgress(double percent) 87 | { 88 | if (progressBar.InvokeRequired) 89 | { 90 | // Ensure that the UI update happens on the main thread 91 | progressBar.Invoke(new Action(() => progressBar.Value = (int)percent)); 92 | } 93 | else 94 | { 95 | progressBar.Value = (int)percent; 96 | } 97 | 98 | string uploadText = progressLabel.Text = $"Uploading to Cesium ion... {(int)percent}%"; 99 | 100 | if (progressLabel.InvokeRequired) 101 | { 102 | progressLabel.Invoke(new Action(() => progressLabel.Text = uploadText)); 103 | } 104 | else 105 | { 106 | progressLabel.Text = uploadText; 107 | } 108 | } 109 | 110 | public void SetUploadComplete() 111 | { 112 | closeBtn.Enabled = true; 113 | 114 | // Enable the open asset button if the asset URL is valid 115 | openAssetBtn.Enabled = !string.IsNullOrEmpty(assetUrl); 116 | 117 | progressLabel.Text = "Upload complete!"; 118 | } 119 | 120 | private void closeBtn_Click(object sender, EventArgs e) 121 | { 122 | this.Close(); 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /Build/CommonBuild.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | None 6 | 7 | 8 | 9 | 10 | true 11 | full 12 | false 13 | bin\Debug\CesiumIonRevitAddin\ 14 | DEBUG;TRACE 15 | prompt 16 | 4 17 | 18 | 19 | 20 | 21 | none 22 | true 23 | bin\Release\CesiumIonRevitAddin\ 24 | TRACE 25 | prompt 26 | 4 27 | 28 | 29 | 30 | 31 | 32 | $(BaseOutputPath)$(Configuration)\ 33 | 34 | 35 | 36 | 37 | 38 | 39 | $(SolutionDir)CesiumIonRevitAddin\Images\ 40 | $(SolutionDir)CesiumIonRevitAddin\CesiumIonRevit.addin 41 | $(SolutionDir)ThirdParty.json 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 56 | 57 | 59 | 60 | 62 | 63 | 64 | 65 | 66 | 67 | <_FilteredContentWithTargetPath Include="@(ContentWithTargetPath)" Condition="'%(Extension)' == '.pdb'" /> 68 | <_FilteredContentWithTargetPath Include="@(ContentWithTargetPath)" Condition="'%(Extension)' == '.xml'" /> 69 | 70 | 71 | <_FilteredReferenceCopyLocalPaths Include="@(ReferenceCopyLocalPaths)" Condition="'%(Extension)' == '.pdb'" /> 72 | <_FilteredReferenceCopyLocalPaths Include="@(ReferenceCopyLocalPaths)" Condition="'%(Extension)' == '.xml'" /> 73 | 74 | 75 | 76 | 77 | 78 | 79 | false 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | true 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /CesiumIonRevitAddin/Forms/ExportDialog.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text.RegularExpressions; 3 | using System.Windows.Forms; 4 | 5 | namespace CesiumIonRevitAddin.Forms 6 | { 7 | public partial class ExportDialog : Form 8 | { 9 | public ExportDialog(ref Preferences preferences) 10 | { 11 | InitializeComponent(); 12 | this.preferences = preferences; 13 | 14 | // Set the default UI state to match preferences 15 | if (this.preferences.SharedCoordinates) 16 | { 17 | sharedCoordinates.Checked = true; 18 | } 19 | else 20 | { 21 | internalOrigin.Checked = true; 22 | } 23 | 24 | crsInput.Enabled = sharedCoordinates.Checked; 25 | crsInput.Text = this.preferences.EpsgCode; 26 | 27 | #if REVIT2019 || REVIT2020 || REVIT2021 || REVIT2022 28 | instancing.Checked = false; 29 | instancing.Enabled = false; 30 | #else 31 | instancing.Checked = this.preferences.IonInstancing; 32 | #endif 33 | 34 | materials.Checked = this.preferences.Materials; 35 | normals.Checked = this.preferences.Normals; 36 | textures.Checked = this.preferences.Textures; 37 | links.Checked = this.preferences.Links; 38 | metadata.Checked = this.preferences.ExportMetadata; 39 | 40 | maxTextureSize.Text = this.preferences.MaxTextureSize.ToString(); 41 | maxTextureSize.Enabled = textures.Checked && materials.Checked; 42 | 43 | textures.Enabled = materials.Checked; 44 | } 45 | 46 | private readonly Preferences preferences; 47 | 48 | private void ExportButton_Click(object sender, EventArgs e) 49 | { 50 | if (!string.IsNullOrEmpty(this.crsInput.Text) && !ValidateInputCRS(this.crsInput.Text)) 51 | { 52 | Autodesk.Revit.UI.TaskDialog.Show("Invalid EPSG Code", "Please enter a valid 4- or 5-digit EPSG code in the range 1024 to 32767. For combined CRS, use the format 'EPSG+EPSG', where both codes are within the valid range."); 53 | return; 54 | } 55 | 56 | this.preferences.SharedCoordinates = sharedCoordinates.Checked; 57 | this.preferences.TrueNorth = sharedCoordinates.Checked; // For now, true north only be used with shared coordinates 58 | this.preferences.IonInstancing = instancing.Checked; 59 | this.preferences.EpsgCode = crsInput.Text; 60 | this.preferences.Materials = materials.Checked; 61 | this.preferences.Normals = normals.Checked; 62 | this.preferences.Textures = textures.Checked; 63 | this.preferences.Links = links.Checked; 64 | this.preferences.ExportMetadata = metadata.Checked; 65 | this.preferences.MaxTextureSize = int.Parse(maxTextureSize.Text); 66 | 67 | this.DialogResult = DialogResult.OK; 68 | this.Close(); 69 | } 70 | 71 | private void SharedCoordinates_CheckedChanged(object sender, EventArgs e) 72 | { 73 | crsInput.Enabled = sharedCoordinates.Checked; 74 | } 75 | 76 | private void CrsInput_KeyPress(object sender, KeyPressEventArgs e) 77 | { 78 | if (!char.IsControl(e.KeyChar) && !char.IsDigit(e.KeyChar) && e.KeyChar != '+') 79 | { 80 | e.Handled = true; 81 | } 82 | } 83 | 84 | private void Textures_CheckedChanged(object sender, EventArgs e) 85 | { 86 | maxTextureSize.Enabled = textures.Checked; 87 | } 88 | 89 | private void Materials_CheckedChanged(object sender, EventArgs e) 90 | { 91 | textures.Enabled = materials.Checked; 92 | maxTextureSize.Enabled = textures.Checked && materials.Checked; 93 | } 94 | 95 | private static bool ValidateInputCRS(string inputCRS) 96 | { 97 | var regex = new Regex(@"^(\d{4,5})(\+(\d{4,5}))?$"); 98 | var match = regex.Match(inputCRS); 99 | 100 | if (!match.Success) return false; 101 | 102 | int horizontal = int.Parse(match.Groups[1].Value); 103 | int vertical = match.Groups[3].Success ? int.Parse(match.Groups[3].Value) : -1; 104 | 105 | bool isHorizontalValid = horizontal >= 1024 && horizontal <= 32767; 106 | bool isVerticalValid = vertical == -1 || (vertical >= 1024 && vertical <= 32767); 107 | 108 | return isHorizontalValid && isVerticalValid; 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /CesiumIonRevitAddin.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.10.34916.146 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CesiumIonRevitAddin_2024", "CesiumIonRevitAddin_2024\CesiumIonRevitAddin_2024.csproj", "{B0ED72C2-CD9A-4E86-A091-FA328B2F542C}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{B1184186-F266-4080-8E7F-42A0E8787AA2}" 9 | ProjectSection(SolutionItems) = preProject 10 | .editorconfig = .editorconfig 11 | EndProjectSection 12 | EndProject 13 | Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "CesiumIonRevitAddin", "CesiumIonRevitAddin\CesiumIonRevitAddin.shproj", "{1DD3FFA0-AD97-4E51-8FAB-9A065C71AC64}" 14 | EndProject 15 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CesiumIonRevitAddin_2025", "CesiumIonRevitAddin_2025\CesiumIonRevitAddin_2025.csproj", "{F2E68FA5-010E-4C3E-A861-3BBC3AB90E08}" 16 | EndProject 17 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CesiumIonRevitAddin_2023", "CesiumIonRevitAddin_2023\CesiumIonRevitAddin_2023.csproj", "{9AA1E96B-5910-40C0-A871-DDCF118474AB}" 18 | EndProject 19 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CesiumIonRevitAddin_2022", "CesiumIonRevitAddin_2022\CesiumIonRevitAddin_2022.csproj", "{C23BB660-F85F-44DC-872E-47BF1D476DFD}" 20 | EndProject 21 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GenerateHelp", "GenerateHelp\GenerateHelp.csproj", "{4E152EF8-2B09-4F6F-8FB1-184921AE55C0}" 22 | EndProject 23 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CesiumIonRevitAddin_2026", "CesiumIonRevitAddin_2026\CesiumIonRevitAddin_2026.csproj", "{C63C5400-6FCD-DC34-20EA-F8C65B198090}" 24 | EndProject 25 | Global 26 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 27 | Debug|Any CPU = Debug|Any CPU 28 | Release|Any CPU = Release|Any CPU 29 | EndGlobalSection 30 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 31 | {B0ED72C2-CD9A-4E86-A091-FA328B2F542C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 32 | {B0ED72C2-CD9A-4E86-A091-FA328B2F542C}.Debug|Any CPU.Build.0 = Debug|Any CPU 33 | {B0ED72C2-CD9A-4E86-A091-FA328B2F542C}.Release|Any CPU.ActiveCfg = Release|Any CPU 34 | {B0ED72C2-CD9A-4E86-A091-FA328B2F542C}.Release|Any CPU.Build.0 = Release|Any CPU 35 | {F2E68FA5-010E-4C3E-A861-3BBC3AB90E08}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 36 | {F2E68FA5-010E-4C3E-A861-3BBC3AB90E08}.Debug|Any CPU.Build.0 = Debug|Any CPU 37 | {F2E68FA5-010E-4C3E-A861-3BBC3AB90E08}.Release|Any CPU.ActiveCfg = Release|Any CPU 38 | {F2E68FA5-010E-4C3E-A861-3BBC3AB90E08}.Release|Any CPU.Build.0 = Release|Any CPU 39 | {9AA1E96B-5910-40C0-A871-DDCF118474AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 40 | {9AA1E96B-5910-40C0-A871-DDCF118474AB}.Debug|Any CPU.Build.0 = Debug|Any CPU 41 | {9AA1E96B-5910-40C0-A871-DDCF118474AB}.Release|Any CPU.ActiveCfg = Release|Any CPU 42 | {9AA1E96B-5910-40C0-A871-DDCF118474AB}.Release|Any CPU.Build.0 = Release|Any CPU 43 | {C23BB660-F85F-44DC-872E-47BF1D476DFD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 44 | {C23BB660-F85F-44DC-872E-47BF1D476DFD}.Debug|Any CPU.Build.0 = Debug|Any CPU 45 | {C23BB660-F85F-44DC-872E-47BF1D476DFD}.Release|Any CPU.ActiveCfg = Release|Any CPU 46 | {C23BB660-F85F-44DC-872E-47BF1D476DFD}.Release|Any CPU.Build.0 = Release|Any CPU 47 | {4E152EF8-2B09-4F6F-8FB1-184921AE55C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 48 | {4E152EF8-2B09-4F6F-8FB1-184921AE55C0}.Release|Any CPU.ActiveCfg = Release|Any CPU 49 | {C63C5400-6FCD-DC34-20EA-F8C65B198090}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 50 | {C63C5400-6FCD-DC34-20EA-F8C65B198090}.Debug|Any CPU.Build.0 = Debug|Any CPU 51 | {C63C5400-6FCD-DC34-20EA-F8C65B198090}.Release|Any CPU.ActiveCfg = Release|Any CPU 52 | {C63C5400-6FCD-DC34-20EA-F8C65B198090}.Release|Any CPU.Build.0 = Release|Any CPU 53 | EndGlobalSection 54 | GlobalSection(SolutionProperties) = preSolution 55 | HideSolutionNode = FALSE 56 | EndGlobalSection 57 | GlobalSection(ExtensibilityGlobals) = postSolution 58 | SolutionGuid = {9935126A-47E0-4852-97AD-943EDFB38C2E} 59 | EndGlobalSection 60 | GlobalSection(SharedMSBuildProjectFiles) = preSolution 61 | CesiumIonRevitAddin\CesiumIonRevitAddin.projitems*{1dd3ffa0-ad97-4e51-8fab-9a065c71ac64}*SharedItemsImports = 13 62 | CesiumIonRevitAddin\CesiumIonRevitAddin.projitems*{9aa1e96b-5910-40c0-a871-ddcf118474ab}*SharedItemsImports = 4 63 | CesiumIonRevitAddin\CesiumIonRevitAddin.projitems*{b0ed72c2-cd9a-4e86-a091-fa328b2f542c}*SharedItemsImports = 4 64 | CesiumIonRevitAddin\CesiumIonRevitAddin.projitems*{c23bb660-f85f-44dc-872e-47bf1d476dfd}*SharedItemsImports = 4 65 | CesiumIonRevitAddin\CesiumIonRevitAddin.projitems*{c63c5400-6fcd-dc34-20ea-f8c65b198090}*SharedItemsImports = 5 66 | CesiumIonRevitAddin\CesiumIonRevitAddin.projitems*{f2e68fa5-010e-4c3e-a861-3bbc3ab90e08}*SharedItemsImports = 5 67 | EndGlobalSection 68 | EndGlobal 69 | -------------------------------------------------------------------------------- /Documentation/DeveloperGuide/README.md: -------------------------------------------------------------------------------- 1 | # Developer Guide 2 | 3 | Autodesk Revit runs only on Windows, therefore the following documentation is for Windows only. 4 | 5 | ## Project Overview 6 | 7 | The project consists of the following components: 8 | 9 | - Individual .NET projects for each version of Autodesk Revit. 10 | - A shared project containing the majority of add-in code used by the above projects. 11 | 12 | This guide covers how to run and build in Visual Studio. 13 | 14 | ## Prerequisites 15 | 16 | - **Visual Studio 2022** - Install it from https://visualstudio.microsoft.com/downloads. 17 | - **.NET Framework 4.8 and .NET 8** - These can be installed using the Visual Studio installer. We recommend having ".NET desktop development" under the "Workloads" tab installed. 18 | 19 | ## Clone the Repository 20 | 21 | ```sh 22 | git clone git@github.com:CesiumGS/cesium-ion-revit-add-in.git 23 | ``` 24 | 25 | ## Building 26 | 27 | ### Visual Studio 2022 28 | 29 | Open `CesiumIonRevitAddin.sln` in Visual Studio 2022 and build the solution. 30 | 31 | Binaries for each project will be located in the individual project folders in either `bin\Debug` or `bin\Release` depending on the build configuration used. 32 | 33 | 34 | ## Installing 35 | 36 | Open the project folder specific to the version of Revit you want to test with. 37 | 38 | Copy the contents of the `bin\Debug` or `bin\Release` folder into `C:\ProgramData\Autodesk\Revit\Addins` under the specific version of Revit you're using. 39 | 40 | When opening Revit, you may see a Security - Unsigned Add-In dialog appear. This is advising you that Revit cannot verify the publisher and confirming if you want to load the add-in. Click **Always Load** or **Load Once** to use the add-in with Revit. 41 | 42 | ## Developing 43 | 44 | ### Test changes without restarting Revit 45 | 46 | When developing, we use [Revit AddinManager](https://github.com/chuongmep/RevitAddInManager) to test changes to builds without needing to restart Revit. 47 | 48 | Open Autodesk Revit with Revit AddinManager installed. 49 | 50 | Navigate to **Add-Ins > Add-in Manager > Add-in Manager (Manual Mode)** 51 | 52 | With the **Load Command** tab selected, click the **Load* button at the bottom right of the interface. 53 | 54 | Select a **CesiumIonRevitAddin.dll** from your most recent build. 55 | 56 | You will see the different commands in the Add-in available in the interface. Select one and click the **Run** button. This will execute the command, just as you would if you clicked the button on the UI. 57 | 58 | If you make a change and produce a new build, the Revit AddinManager will utilise the latest changes to the .dll. 59 | 60 | **Tips** 61 | 62 | - Use Add-In Manager (Manual Mode, Faceless) to re-run the previous command without needing to navigate the UI 63 | - Click Show/Hide Panel (Debug Trace Events) to display a debug console that prints `Debug.Writeline()` output. 64 | 65 | ### Testing with local files 66 | 67 | Debug builds feature an **Export 3D Tiles to Disk** option in the ribbon. This can export glTF to disk, which can then be used to produce 3D Tiles without uploading to Cesium ion. 68 | 69 | By default, a glTF and tileset.json will be exported through this process. If you have a local tiler, the add-in can be configured to automatically call tilers.exe and convert the glTF into 3D Tiles. The glTF remains on the system after conversion for debugging purposes. 70 | 71 | To configure a tiler, add a `CESIUM_TILER_PATH` environment variable to your system, with a value that represents an absolute path to your `tilers.exe`. 72 | 73 | ### Debugging 74 | 75 | When using Revit AddinManager and a Debug build of the add-in, you can attach the Visual Studio debugger to the revit.exe process for proper debugging workflows. 76 | 77 | ## Creating a Bundle 78 | 79 | The following steps can be used to create a bundle that supports multiple versions of Revit. This is also the content that gets uploaded to the Autodesk App Store for new releases. 80 | 81 | 1. Ensure there are no modified or untracked files in [/AddinBundle/CesiumIonRevitAddin.bundle](/AddinBundle/CesiumIonRevitAddin.bundle). 82 | 2. Switch the Solution **Configuration** to **Release** in Visual Studio. 83 | 3. Right click on the Solution and choose **Rebuild**. 84 | 4. Execute the batch file [BundleDebug.bat](/AddinBundle/BundleDebug.bat) or [BundleRelease.bat](/AddinBundle/BundleRelease.bat) depending on which configuration you want to bundle 85 | 5. All builds from individual projects will be copied into the [/AddinBundle/CesiumIonRevitAddin.bundle](/AddinBundle/CesiumIonRevitAddin.bundle) folder 86 | 6. To test the bundle with Revit, execute the batch file [CopyBundleToRevit.bat](/AddinBundle/CopyBundleToRevit.bat). This will copy the bundle to `C:\ProgramData\Autodesk\ApplicationPlugins\CesiumIonRevitAddin.bundle`, which will make the add-in available to all currently installed and supported versions of Revit. NOTE: This will override any existing bundle in the same location 87 | 7. The bundle can be removed from Revit using [RemoveBundleFromRevit.bat](/AddinBundle/RemoveBundleFromRevit.bat). 88 | 8. The final bundle can be packaged into an installer and uploaded to the Autodesk App Store for new releases (see the [Release Guide](/Documentation/ReleaseGuide/README.md)) 89 | 90 | -------------------------------------------------------------------------------- /CesiumIonRevitAddin_2022/CesiumIonRevitAddin_2022.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Debug 8 | AnyCPU 9 | {C23BB660-F85F-44DC-872E-47BF1D476DFD} 10 | Library 11 | Properties 12 | CesiumIonRevitAddin 13 | CesiumIonRevitAddin 14 | v4.8 15 | 512 16 | true 17 | 18 | 19 | $(DefineConstants);REVIT2022 20 | 21 | 22 | $(DefineConstants);REVIT2022 23 | 24 | 25 | 26 | ..\packages\Revit_All_Main_Versions_API_x64.2022.1.0\lib\net48\AdWindows.dll 27 | False 28 | False 29 | 30 | 31 | ..\packages\AWSSDK.Core.3.7.400.34\lib\net45\AWSSDK.Core.dll 32 | 33 | 34 | ..\packages\AWSSDK.S3.3.7.404.6\lib\net45\AWSSDK.S3.dll 35 | 36 | 37 | ..\packages\AWSSDK.SecurityToken.3.7.400.34\lib\net45\AWSSDK.SecurityToken.dll 38 | 39 | 40 | ..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll 41 | 42 | 43 | 44 | 45 | ..\packages\Revit_All_Main_Versions_API_x64.2022.1.0\lib\net48\RevitAPI.dll 46 | False 47 | False 48 | 49 | 50 | ..\packages\Revit_All_Main_Versions_API_x64.2022.1.0\lib\net48\RevitAPIUI.dll 51 | False 52 | False 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | ..\packages\Revit_All_Main_Versions_API_x64.2022.1.0\lib\net48\UIFramework.dll 70 | False 71 | False 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /CesiumIonRevitAddin_2023/CesiumIonRevitAddin_2023.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Debug 8 | AnyCPU 9 | {9AA1E96B-5910-40C0-A871-DDCF118474AB} 10 | Library 11 | Properties 12 | CesiumIonRevitAddin 13 | CesiumIonRevitAddin 14 | v4.8 15 | 512 16 | true 17 | 18 | 19 | $(DefineConstants);REVIT2023 20 | 21 | 22 | $(DefineConstants);REVIT2023 23 | 24 | 25 | 26 | ..\packages\Revit_All_Main_Versions_API_x64.2023.0.0\lib\net48\AdWindows.dll 27 | False 28 | False 29 | 30 | 31 | ..\packages\AWSSDK.Core.3.7.400.34\lib\net45\AWSSDK.Core.dll 32 | 33 | 34 | ..\packages\AWSSDK.S3.3.7.404.6\lib\net45\AWSSDK.S3.dll 35 | 36 | 37 | ..\packages\AWSSDK.SecurityToken.3.7.400.34\lib\net45\AWSSDK.SecurityToken.dll 38 | 39 | 40 | ..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll 41 | 42 | 43 | 44 | 45 | ..\packages\Revit_All_Main_Versions_API_x64.2023.0.0\lib\net48\RevitAPI.dll 46 | False 47 | False 48 | 49 | 50 | ..\packages\Revit_All_Main_Versions_API_x64.2023.0.0\lib\net48\RevitAPIUI.dll 51 | False 52 | False 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | ..\packages\Revit_All_Main_Versions_API_x64.2023.0.0\lib\net48\UIFramework.dll 70 | False 71 | False 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /CesiumIonRevitAddin_2024/CesiumIonRevitAddin_2024.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Debug 8 | AnyCPU 9 | {B0ED72C2-CD9A-4E86-A091-FA328B2F542C} 10 | Library 11 | Properties 12 | CesiumIonRevitAddin 13 | CesiumIonRevitAddin 14 | v4.8 15 | 512 16 | true 17 | 18 | 19 | $(DefineConstants);REVIT2024 20 | 21 | 22 | $(DefineConstants);REVIT2024 23 | 24 | 25 | 26 | ..\packages\Revit_All_Main_Versions_API_x64.2024.2.0\lib\net48\AdWindows.dll 27 | False 28 | False 29 | 30 | 31 | ..\packages\AWSSDK.Core.3.7.400.34\lib\net45\AWSSDK.Core.dll 32 | 33 | 34 | ..\packages\AWSSDK.S3.3.7.404.6\lib\net45\AWSSDK.S3.dll 35 | 36 | 37 | ..\packages\AWSSDK.SecurityToken.3.7.400.34\lib\net45\AWSSDK.SecurityToken.dll 38 | 39 | 40 | ..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll 41 | 42 | 43 | 44 | 45 | ..\packages\Revit_All_Main_Versions_API_x64.2024.2.0\lib\net48\RevitAPI.dll 46 | False 47 | False 48 | 49 | 50 | ..\packages\Revit_All_Main_Versions_API_x64.2024.2.0\lib\net48\RevitAPIUI.dll 51 | False 52 | False 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | ..\packages\Revit_All_Main_Versions_API_x64.2024.2.0\lib\net48\UIFramework.dll 70 | False 71 | False 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /GenerateHelp/Program.cs: -------------------------------------------------------------------------------- 1 | using Markdig; 2 | using System.Text.RegularExpressions; 3 | 4 | namespace GenerateHelp 5 | { 6 | static class Program 7 | { 8 | static void Main(string[] args) 9 | { 10 | if (args.Length < 2) 11 | { 12 | Console.WriteLine("Usage: GenerateHelp "); 13 | return; 14 | } 15 | 16 | string markdownFilePath = args[0]; 17 | string htmlOutputPath = args[1]; 18 | 19 | string markdownContent = File.ReadAllText(markdownFilePath); 20 | 21 | // Configure the Markdown pipeline with advanced extensions 22 | var pipeline = new MarkdownPipelineBuilder() 23 | .UseAdvancedExtensions() 24 | .Build(); 25 | 26 | // Convert Markdown to HTML 27 | string bodyContent = Markdown.ToHtml(markdownContent, pipeline); 28 | 29 | // Embed images as Base64 in the HTML content 30 | bodyContent = EmbedImagesAsBase64(bodyContent, Path.GetDirectoryName(markdownFilePath) ?? "."); 31 | 32 | // Generate final HTML with GitHub Markdown CSS and Bootstrap for responsive layout 33 | string htmlContent = GenerateResponsiveHtmlWithStyles(bodyContent); 34 | 35 | // Save the generated HTML to the output path 36 | File.WriteAllText(htmlOutputPath, htmlContent); 37 | 38 | Console.WriteLine($"Markdown converted to styled and responsive HTML with embedded images at: {htmlOutputPath}"); 39 | } 40 | 41 | // Embed images as Base64 for tags in the HTML content 42 | static string EmbedImagesAsBase64(string htmlContent, string markdownDir) 43 | { 44 | string imgTagPattern = @"]*src=""([^""]+)"""; 45 | Regex regex = new Regex(imgTagPattern, RegexOptions.IgnoreCase); 46 | 47 | var matches = regex.Matches(htmlContent); 48 | 49 | foreach (var match in matches.Where(m => m.Groups.Count > 1)) 50 | { 51 | string imagePath = match.Groups[1].Value; 52 | 53 | // Handle relative and absolute paths 54 | string fullImagePath = Path.IsPathRooted(imagePath) ? imagePath : Path.Combine(markdownDir, imagePath); 55 | 56 | if (File.Exists(fullImagePath)) 57 | { 58 | string base64Image = ConvertImageToBase64(fullImagePath); 59 | string mimeType = GetImageMimeType(fullImagePath); 60 | 61 | string base64ImageTag = $"data:image/{mimeType};base64,{base64Image}"; 62 | 63 | htmlContent = htmlContent.Replace(imagePath, base64ImageTag); 64 | } 65 | else 66 | { 67 | Console.WriteLine($"Warning: Image not found: {fullImagePath}"); 68 | } 69 | 70 | } 71 | 72 | return htmlContent; 73 | } 74 | 75 | // Generate the final HTML with GitHub-like CSS and Bootstrap 76 | static string GenerateResponsiveHtmlWithStyles(string bodyContent) 77 | { 78 | // GitHub Markdown CSS hosted on CDN 79 | string githubCssLink = ""; 80 | 81 | // Bootstrap CSS for responsive layout 82 | string bootstrapCssLink = ""; 83 | 84 | return $@" 85 | 86 | 87 | 88 | 89 | 90 | Generated Help 91 | {githubCssLink} 92 | {bootstrapCssLink} 93 | 97 | 98 | 99 | {bodyContent} 100 | 101 | "; 102 | } 103 | 104 | // Convert an image file to a Base64 string 105 | static string ConvertImageToBase64(string imagePath) 106 | { 107 | byte[] imageBytes = File.ReadAllBytes(imagePath); 108 | return Convert.ToBase64String(imageBytes); 109 | } 110 | 111 | // Get the MIME type based on the file extension 112 | static string GetImageMimeType(string imagePath) 113 | { 114 | string extension = Path.GetExtension(imagePath).ToLowerInvariant(); 115 | 116 | return extension switch 117 | { 118 | ".jpg" or ".jpeg" => "jpeg", 119 | ".png" => "png", 120 | ".gif" => "gif", 121 | ".bmp" => "bmp", 122 | ".svg" => "svg+xml", 123 | _ => "octet-stream" 124 | }; 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /CesiumIonRevitAddin/Forms/IonUploadDialog.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace CesiumIonRevitAddin.Forms 2 | { 3 | partial class IonUploadDialog 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(IonUploadDialog)); 32 | this.progressBar = new System.Windows.Forms.ProgressBar(); 33 | this.openAssetBtn = new System.Windows.Forms.Button(); 34 | this.closeBtn = new System.Windows.Forms.Button(); 35 | this.progressLabel = new System.Windows.Forms.Label(); 36 | this.SuspendLayout(); 37 | // 38 | // progressBar 39 | // 40 | this.progressBar.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 41 | | System.Windows.Forms.AnchorStyles.Right))); 42 | this.progressBar.Location = new System.Drawing.Point(12, 38); 43 | this.progressBar.Name = "progressBar"; 44 | this.progressBar.Size = new System.Drawing.Size(310, 28); 45 | this.progressBar.TabIndex = 0; 46 | // 47 | // openAssetBtn 48 | // 49 | this.openAssetBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); 50 | this.openAssetBtn.AutoSize = true; 51 | this.openAssetBtn.Location = new System.Drawing.Point(133, 79); 52 | this.openAssetBtn.Name = "openAssetBtn"; 53 | this.openAssetBtn.Size = new System.Drawing.Size(108, 23); 54 | this.openAssetBtn.TabIndex = 1; 55 | this.openAssetBtn.Text = "Open in Cesium ion"; 56 | this.openAssetBtn.UseVisualStyleBackColor = true; 57 | this.openAssetBtn.Click += new System.EventHandler(this.openAsset_Click); 58 | // 59 | // closeBtn 60 | // 61 | this.closeBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); 62 | this.closeBtn.AutoSize = true; 63 | this.closeBtn.Location = new System.Drawing.Point(247, 79); 64 | this.closeBtn.Name = "closeBtn"; 65 | this.closeBtn.Size = new System.Drawing.Size(75, 23); 66 | this.closeBtn.TabIndex = 2; 67 | this.closeBtn.Text = "Close"; 68 | this.closeBtn.UseVisualStyleBackColor = true; 69 | this.closeBtn.Click += new System.EventHandler(this.closeBtn_Click); 70 | // 71 | // progressLabel 72 | // 73 | this.progressLabel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 74 | | System.Windows.Forms.AnchorStyles.Right))); 75 | this.progressLabel.Location = new System.Drawing.Point(12, 9); 76 | this.progressLabel.Name = "progressLabel"; 77 | this.progressLabel.Size = new System.Drawing.Size(310, 26); 78 | this.progressLabel.TabIndex = 3; 79 | this.progressLabel.Text = "Preparing upload..."; 80 | this.progressLabel.TextAlign = System.Drawing.ContentAlignment.TopCenter; 81 | // 82 | // IonUploadDialog 83 | // 84 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 85 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 86 | this.ClientSize = new System.Drawing.Size(334, 114); 87 | this.ControlBox = false; 88 | this.Controls.Add(this.progressLabel); 89 | this.Controls.Add(this.closeBtn); 90 | this.Controls.Add(this.openAssetBtn); 91 | this.Controls.Add(this.progressBar); 92 | this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); 93 | this.Name = "IonUploadDialog"; 94 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; 95 | this.Text = "Uploading to Cesium ion"; 96 | this.ResumeLayout(false); 97 | this.PerformLayout(); 98 | 99 | } 100 | 101 | #endregion 102 | 103 | private System.Windows.Forms.ProgressBar progressBar; 104 | private System.Windows.Forms.Button openAssetBtn; 105 | private System.Windows.Forms.Button closeBtn; 106 | private System.Windows.Forms.Label progressLabel; 107 | } 108 | } -------------------------------------------------------------------------------- /CesiumIonRevitAddin/Utils/TilerExportUtils.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Linq; 3 | using System; 4 | using System.Diagnostics; 5 | using System.IO; 6 | 7 | 8 | namespace CesiumIonRevitAddin.Utils 9 | { 10 | internal static class TilerExportUtils 11 | { 12 | public static void WriteTilerJson(Preferences preferences) 13 | { 14 | 15 | string inputPath = preferences.GltfPath; 16 | string outputPath = preferences.OutputPath; 17 | 18 | // Export to a subfolder if not using a .3dtiles DB 19 | if (!preferences.Export3DTilesDB) 20 | { 21 | outputPath = Path.Combine(preferences.OutputDirectory, Path.GetFileNameWithoutExtension(outputPath), "tileset.json"); 22 | } 23 | else 24 | { 25 | outputPath = preferences.Temp3DTilesPath; 26 | } 27 | 28 | // Generate Relative paths to the JSON 29 | Uri jsonDirectoryUri = new Uri(Path.GetDirectoryName(preferences.JsonPath) + Path.DirectorySeparatorChar); 30 | Uri inputPathUri = new Uri(inputPath); 31 | Uri outputPathUri = new Uri(outputPath); 32 | 33 | inputPathUri = jsonDirectoryUri.MakeRelativeUri(inputPathUri); 34 | outputPathUri = jsonDirectoryUri.MakeRelativeUri(outputPathUri); 35 | 36 | // Convert the URI to a relative path 37 | outputPath = Uri.UnescapeDataString(outputPathUri.ToString()).Replace('/', Path.DirectorySeparatorChar); 38 | 39 | var jsonObject = new JObject 40 | { 41 | ["input"] = new JObject 42 | { 43 | ["path"] = inputPathUri 44 | }, 45 | ["output"] = new JObject 46 | { 47 | ["path"] = outputPath 48 | }, 49 | ["overwrite"] = true, 50 | ["pipeline"] = new JObject 51 | { 52 | ["type"] = "DESIGN_TILER", 53 | ["designTiler"] = new JObject 54 | { 55 | ["enableInstancing"] = preferences.IonInstancing 56 | } 57 | }, 58 | ["gzip"] = true, 59 | ["gltf"] = new JObject 60 | { 61 | ["geometricCompression"] = "MESHOPT", 62 | ["colorTextureCompression"] = "KTX2" 63 | } 64 | }; 65 | 66 | // Only add the CRS information if it exists 67 | if (preferences.EpsgCode != "" && preferences.SharedCoordinates) 68 | { 69 | jsonObject["input"]["coordinateSystem"] = new JObject(); 70 | jsonObject["input"]["coordinateSystem"]["crs"] = "EPSG:" + preferences.EpsgCode; 71 | } 72 | 73 | string jsonString = JsonConvert.SerializeObject(jsonObject, Formatting.Indented); 74 | 75 | File.WriteAllText(preferences.JsonPath, jsonString); 76 | } 77 | 78 | public static void RunTiler(string jsonPath) 79 | { 80 | Logger.Instance.Log("Running tiler"); 81 | 82 | // Find the tiler executable 83 | string exePath = GetTilerLocation(); 84 | if (!File.Exists(exePath)) 85 | { 86 | throw new FileNotFoundException("Tiler executable not found at: " + exePath); 87 | } 88 | 89 | // Define the arguments 90 | string arguments = $"--config \"{jsonPath}\""; 91 | string workingDirectory = Path.GetDirectoryName(jsonPath); 92 | 93 | // Create the process start information 94 | ProcessStartInfo startInfo = new ProcessStartInfo 95 | { 96 | FileName = exePath, 97 | Arguments = arguments, 98 | WorkingDirectory = workingDirectory, // Tiler needs cwd to match the json file if relative paths are used 99 | RedirectStandardOutput = true, 100 | RedirectStandardError = true, 101 | UseShellExecute = false, // Required for redirection 102 | CreateNoWindow = true // Hide the command window 103 | }; 104 | 105 | try 106 | { 107 | // Start the tiler process 108 | using (Process process = Process.Start(startInfo)) 109 | { 110 | string output = process.StandardOutput.ReadToEnd(); 111 | string errors = process.StandardError.ReadToEnd(); 112 | int exitCode = process.ExitCode; 113 | 114 | // Wait for the process to exit 115 | process.WaitForExit(); 116 | 117 | // Capture the output and error 118 | Logger.Instance.Log($"Tiler output: {output}"); 119 | Logger.Instance.Log($"Tiler errors: {errors}"); 120 | Logger.Instance.Log($"Tiler exit code: {exitCode}"); 121 | } 122 | } 123 | catch (Exception ex) 124 | { 125 | Logger.Instance.Log("An error occurred while trying to execute the tiler:"); 126 | Logger.Instance.Log(ex.Message); 127 | } 128 | } 129 | 130 | public static string GetTilerLocation() 131 | { 132 | string envPath = Environment.GetEnvironmentVariable("CESIUM_TILER_PATH"); 133 | if (!string.IsNullOrEmpty(envPath) && File.Exists(envPath)) 134 | { 135 | return envPath; 136 | } 137 | return string.Empty; 138 | } 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /CesiumIonRevitAddin/Utils/Util.cs: -------------------------------------------------------------------------------- 1 | using Autodesk.Revit.DB; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Reflection; 7 | using System.Text; 8 | 9 | namespace CesiumIonRevitAddin.Utils 10 | { 11 | internal static class Util 12 | { 13 | public static string GetGltfName(string input) 14 | { 15 | // Revit enums usually start with "OST_". Remove it. 16 | if (input.StartsWith("OST_")) input = input.Substring(4); 17 | var sb = new StringBuilder(); 18 | for (int i = 0; i < input.Length; ++i) 19 | { 20 | Char c = input[i]; 21 | // Verify the character is ASCII and alphanumeric 22 | if (c <= 127 && Char.IsLetterOrDigit(c)) 23 | { 24 | if (i == 0) c = Char.ToLower(c); 25 | sb.Append(c); 26 | } 27 | } 28 | string gltfName = sb.ToString(); 29 | // glTF properties can't start with a number. 30 | // Some Revit material names like "---" can also become empty-string glTF names. 31 | if (gltfName.Length > 0 && char.IsDigit(gltfName[0])) gltfName = "_" + gltfName; 32 | return gltfName; 33 | } 34 | public static bool CanBeLockOrHidden(Element element, View view) 35 | { 36 | if (element.Category.CanAddSubcategory) 37 | { 38 | return true; 39 | } 40 | if (element.CanBeHidden(view)) 41 | { 42 | return true; 43 | } 44 | 45 | return false; 46 | } 47 | 48 | public static string CreateClassName(string categoryName, string familyName) 49 | { 50 | return categoryName + ": " + familyName; 51 | } 52 | 53 | public static float[] GetVec3MinMax(IEnumerable vec3) 54 | { 55 | var xvalues = vec3.Where((val, idx) => idx % 3 == 0); 56 | var yvalues = vec3.Where((val, idx) => idx % 3 == 1); 57 | var zvalues = vec3.Where((val, idx) => idx % 3 == 2); 58 | 59 | return new float[] { xvalues.Min(), xvalues.Max(), yvalues.Min(), yvalues.Max(), zvalues.Min(), zvalues.Max() }; 60 | } 61 | 62 | public static float[] GetVec2MinMax(IEnumerable vec2) 63 | { 64 | var xvalues = vec2.Where((val, idx) => idx % 2 == 0); 65 | var yvalues = vec2.Where((val, idx) => idx % 2 == 1); 66 | 67 | return new float[] { xvalues.Min(), xvalues.Max(), yvalues.Min(), yvalues.Max() }; 68 | } 69 | 70 | public static int[] GetScalarMinMax(List scalars) 71 | { 72 | if (scalars == null || scalars.Count == 0) 73 | { 74 | return Array.Empty(); 75 | } 76 | 77 | return new int[] { scalars.Min(), scalars.Max() }; 78 | } 79 | 80 | public static string GetAddinUserDataFolder() 81 | { 82 | string localAppDataPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); 83 | return Path.Combine(localAppDataPath, "Cesium", "CesiumIonRevitAddin"); 84 | } 85 | 86 | public static string GetAddinFolder() 87 | { 88 | // Get the location of the executing assembly (the add-in itself) 89 | string assemblyLocation = Assembly.GetExecutingAssembly().Location; 90 | 91 | // Get the directory of the assembly 92 | string addinDirectory = Path.GetDirectoryName(assemblyLocation); 93 | 94 | return addinDirectory; 95 | } 96 | 97 | public static long GetElementIdAsLong(ElementId elementId) 98 | { 99 | #if REVIT2022 || REVIT2023 100 | return (long)elementId.IntegerValue; 101 | #else 102 | return elementId.Value; 103 | #endif 104 | } 105 | 106 | static readonly HashSet metadataFilterValues = new HashSet { "", "-1" }; 107 | public static bool ShouldFilterMetadata(ParameterValue parameterValue) 108 | { 109 | if (parameterValue.IntegerValue.HasValue || parameterValue.DoubleValue.HasValue || parameterValue.LongValue.HasValue) 110 | { 111 | return false; 112 | } 113 | if (parameterValue.StringValue != null) 114 | { 115 | return ShouldFilterMetadata(parameterValue.StringValue); 116 | } 117 | return true; 118 | } 119 | public static bool ShouldFilterMetadata(string value) 120 | { 121 | return metadataFilterValues.Contains(value); 122 | } 123 | 124 | public static ParameterValue GetParameterValue(Autodesk.Revit.DB.Parameter parameter) 125 | { 126 | switch (parameter.StorageType) 127 | { 128 | case StorageType.Integer: 129 | return parameter.AsInteger(); 130 | case StorageType.Double: 131 | return parameter.AsDouble(); 132 | case StorageType.String: 133 | return parameter.AsString(); 134 | case StorageType.ElementId: 135 | return Util.GetElementIdAsLong(parameter.AsElementId()); 136 | default: 137 | return new ParameterValue(); 138 | } 139 | } 140 | 141 | public static Int64 GetElementIdValue(ElementId elementId) 142 | { 143 | #if REVIT2022 || REVIT2023 144 | return (System.Int64) elementId.IntegerValue; 145 | #else 146 | return elementId.Value; 147 | #endif 148 | } 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 | # Cesium ion for Autodesk Revit 6 | 7 | 8 | - [Overview](#overview) 9 | - [Installation](#installation) 10 | - [Usage](#usage) 11 | - [Connect to Cesium ion](#connect-to-cesium-ion) 12 | - [Upload 3D Tiles](#upload-3d-tiles) 13 | - [Export Preferences](#export-preferences) 14 | - [Georeferencing](#georeferencing) 15 | - [Options](#options) 16 | - [Exporting with Georeferencing](#exporting-with-georeferencing) 17 | - [Materials and Textures](#materials-and-textures) 18 | - [Metadata](#metadata) 19 | - [Current Limitations or Known Issues](#current-limitations-or-known-issues) 20 | 21 | 22 | ## Overview 23 | 24 | Cesium ion for Autodesk Revit enables you to effortlessly export your designs to Cesium ion and stream them as 3D Tiles to CesiumJS and other applications. 25 | 26 | By leveraging Cesium ion and the power of 3D Tiles, even multi-gigabyte models can be streamed to any device without having to download the entire tileset up front. By visualizing 3D Tiles with CesiumJS, you can fuse your models with other datasets, add geospatial context to place it at a real world location, or overlay additional details and analysis. 27 | 28 | Autodesk Revit versions 2022 to 2026 are supported. 29 | 30 | For Cesium clients, we recommend using Cesium for Unreal v2.11.0 or later, Cesium for Unity v1.14.0 or later, and CesiumJS v1.124.0 or later 31 | 32 | Learn more at https://cesium.com. 33 | 34 | ![Snowdon](./Documentation/Resources/SnowdonRevitIon.png) 35 |

36 | Snowdon Towers (left) loaded into Autodesk Revit and (right) fused with Cesium World Terrain and imagery in CesiumJS after being tiled with ion. 37 |

38 | 39 | ## Installation 40 | 41 | The add-in can be installed by downloading and running the installer from the Autodesk App Store. 42 | 43 | [Cesium ion for Autodesk Revit on Autodesk App Store](https://apps.autodesk.com/RVT/en/Detail/Index?id=6447681920168081779&appLang=en&os=Win64) 44 | 45 | For development, see [Developer Guide](./Documentation/DeveloperGuide/README.md) 46 | 47 | ## Usage 48 | 49 | ### Connect to Cesium ion 50 | 51 | Open the **Cesium ribbon** and click **Connect** to connect to Cesium ion. 52 | 53 | ![Access Cesium ion exporter](./Documentation/Resources/Tutorial_Connect.png) 54 | 55 | Choose your Cesium ion server and follow the sign in prompts to complete sign in. 56 | 57 | ### Upload 3D Tiles 58 | 59 | Navigate to a 3D View and ensure the content visible is what you want uploaded. 60 | 61 | Open the **Cesium ribbon** and click **Upload** to begin uploading to Cesium ion. 62 | 63 | Review the preferences and click **Save**. For more information, see the **Export Preferences** section below. 64 | 65 | Once upload has completed, click **Open in Cesium ion** to view your design as 3D Tiles. 66 | 67 | ### Export Preferences 68 | 69 | The following preferences control the content exported to 3D Tiles: 70 | 71 | #### Georeferencing 72 | * **Shared Coordinates** - Positions the geometry relative to Revit’s shared coordinates and aligned to True North. 73 | * **Internal Origin** - Positions the geometry relative to Revit’s Internal Origin. 74 | 75 | **NOTE**: For more information on using georeferencing, see the _Exporting with Georeferencing_ section below. 76 | 77 | #### Options 78 | * **Normals** - Exports surface normals for geometry. 79 | * **Materials** - Exports materials using color values from Revit. 80 | * **Textures** - Exports color textures for materials if present. 81 | * **Max Size** - Resizes textures to a maximum resolution, which can help reduce memory usage. 82 | * **Metadata** - Includes Revit properties as metadata. 83 | * **GPU Instancing** - Detects and uses GPU instancing for eligible geometry on export. This can optimize rendering performance and reduce memory usage. Currently, GPU instancing is supported by CesiumJS and Cesium for Unreal, with support for Cesium for Unity and Cesium for Omniverse coming soon. 84 | * **Revit Links** - Exports geometry for Revit files linked to the project. 85 | 86 | ### Exporting with Georeferencing 87 | 88 | For Revit projects located within a geospatial Coordinate Reference System (CRS), the following prerequisites are needed: 89 | 90 | * The EPSG code corresponding to the project’s CRS. 91 | * The project must use shared coordinates that accurately position the project within the CRS. 92 | 93 | For example, the Snowdon sample provided with Autodesk Revit is located in the NAD83 / Pennsylvania South (ftUS) CRS, which is EPSG:2272. Elevations are relative to the EGM96 geoid (EPSG:5773). To automatically place this on the globe during tiling, you would: 94 | 95 | 1. Select _Shared Coordinates_ in the preferences window. 96 | 2. Set the EPSG code to `2272+5773`. 97 | 98 | If your project does not use Shared Coordinates or if you do not know the EPSG code for its CRS, you can export using _Internal Origin_ and manually position the data on the globe when uploading to Cesium ion. 99 | 100 | ### Materials and Textures 101 | 102 | When exporting with materials, the following values will be used: 103 | 104 | * Base Color RGB 105 | * Base Color Texture 106 | * Opacity 107 | 108 | **NOTE**: Revit has various material configurations, and some information may not currently be supported. 109 | 110 | ### Metadata 111 | 112 | All parameters associated with each specific Element in the 3D View are exported as metadata. This includes both instance parameters (specific to an element instance) and type parameters (common to all instances of a given type). 113 | 114 | ![Metadata Example](./Documentation/Resources/cesium-unreal-metadata.png) 115 | 116 | ### Current Limitations or Known Issues 117 | 118 | * Instancing is currently supported in CesiumJS and Cesium for Unreal, with support for Cesium for Unity and Cesium for Omniverse coming soon. 119 | * 3D Tiles containing textures may experience excessive GPU RAM usage in Cesium for Unreal. Exporting with smaller texture sizes may help. Improved support for textures is coming soon. 120 | * Some elements visible in the 3D View are not represented with polygons and subsequently may not export as geometry. Exporting IFC may provide an alternative workflow. 121 | -------------------------------------------------------------------------------- /CesiumIonRevitAddin/CesiumIonRevitAddin.projitems: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(MSBuildAllProjects);$(MSBuildThisFileFullPath) 5 | true 6 | 1dd3ffa0-ad97-4e51-8fab-9a065c71ac64 7 | 8 | 9 | CesiumIonRevitAddin 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | ExportDialog.cs 26 | 27 | 28 | 29 | IonConnectDialog.cs 30 | 31 | 32 | 33 | IonUploadDialog.cs 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | ExportDialog.cs 77 | 78 | 79 | IonConnectDialog.cs 80 | 81 | 82 | IonUploadDialog.cs 83 | 84 | 85 | 86 | 87 | Always 88 | 89 | 90 | Always 91 | 92 | 93 | Always 94 | 95 | 96 | Always 97 | 98 | 99 | Always 100 | 101 | 102 | Always 103 | 104 | 105 | Always 106 | 107 | 108 | Always 109 | 110 | 111 | Always 112 | 113 | 114 | -------------------------------------------------------------------------------- /CesiumIonRevitAddin/Utils/GltfBinaryDataUtils.cs: -------------------------------------------------------------------------------- 1 | using CesiumIonRevitAddin.Gltf; 2 | using CesiumIonRevitAddin.Model; 3 | using System; 4 | using System.Collections.Generic; 5 | 6 | namespace CesiumIonRevitAddin.Utils 7 | { 8 | internal static class GltfBinaryDataUtils 9 | { 10 | private const string VEC2_STR = "VEC2"; 11 | private const string VEC3_STR = "VEC3"; 12 | private const string POSITION_STR = "POSITION"; 13 | private const string NORMAL_STR = "NORMALS"; 14 | private const string TEXCOORD_STR = "TEXCOORD_0"; 15 | private const string SCALAR_STR = "SCALAR"; 16 | private const string FACE_STR = "FACE"; 17 | 18 | public static ulong ExportVertices(int bufferIndex, ulong byteOffset, GeometryDataObject geometryDataObject, 19 | GltfBinaryData bufferData, List bufferViews, List accessors) 20 | { 21 | for (int i = 0; i < geometryDataObject.Vertices.Count; i++) 22 | { 23 | bufferData.VertexBuffer.Add(Convert.ToSingle(geometryDataObject.Vertices[i])); 24 | } 25 | 26 | // Get max and min for vertex data 27 | float[] vertexMinMax = Util.GetVec3MinMax(bufferData.VertexBuffer); 28 | 29 | // Add a vec3 buffer view 30 | const int elementsPerVertex = 3; 31 | const int bytesPerElement = 4; 32 | const int bytesPerVertex = elementsPerVertex * bytesPerElement; 33 | int numVec3s = geometryDataObject.Vertices.Count / elementsPerVertex; 34 | ulong sizeOfVec3View = (ulong) (numVec3s * bytesPerVertex); 35 | 36 | var vec3View = new GltfBufferView(bufferIndex, byteOffset, sizeOfVec3View, Targets.ARRAY_BUFFER, "verts"); 37 | bufferViews.Add(vec3View); 38 | int vec3ViewIndex = bufferViews.Count - 1; 39 | 40 | // add a position accessor 41 | int count = geometryDataObject.Vertices.Count / elementsPerVertex; 42 | var max = new List(3) { vertexMinMax[1], vertexMinMax[3], vertexMinMax[5] }; 43 | var min = new List(3) { vertexMinMax[0], vertexMinMax[2], vertexMinMax[4] }; 44 | 45 | var positionAccessor = new GltfAccessor(vec3ViewIndex, 0, ComponentType.FLOAT, count, VEC3_STR, max, min, POSITION_STR); 46 | accessors.Add(positionAccessor); 47 | bufferData.VertexAccessorIndex = accessors.Count - 1; 48 | return byteOffset + vec3View.ByteLength; 49 | } 50 | 51 | public static ulong ExportFaces(int bufferIndex, ulong byteOffset, GeometryDataObject geometryData, GltfBinaryData binaryData, 52 | List bufferViews, List accessors) 53 | { 54 | foreach (var index in geometryData.Faces) 55 | { 56 | binaryData.IndexBuffer.Add(index); 57 | } 58 | 59 | // Get max and min for index data 60 | int[] faceMinMax = Util.GetScalarMinMax(binaryData.IndexBuffer); 61 | 62 | // Add a faces / indexes buffer view 63 | const int elementsPerIndex = 1; 64 | const int bytesPerIndexElement = 4; 65 | const int bytesPerIndex = elementsPerIndex * bytesPerIndexElement; 66 | var numIndexes = geometryData.Faces.Count; 67 | ulong sizeOfIndexView = (ulong) (numIndexes * bytesPerIndex); 68 | var facesView = new GltfBufferView(bufferIndex, byteOffset, sizeOfIndexView, Targets.ELEMENT_ARRAY_BUFFER, "faces"); 69 | bufferViews.Add(facesView); 70 | var facesViewIndex = bufferViews.Count - 1; 71 | 72 | // add a face accessor 73 | var count = geometryData.Faces.Count / elementsPerIndex; 74 | var max = new List(1) { faceMinMax[1] }; 75 | var min = new List(1) { faceMinMax[0] }; 76 | var faceAccessor = new GltfAccessor(facesViewIndex, 0, ComponentType.UNSIGNED_INT, count, SCALAR_STR, max, min, FACE_STR); 77 | accessors.Add(faceAccessor); 78 | binaryData.IndexAccessorIndex = accessors.Count - 1; 79 | return byteOffset + facesView.ByteLength; 80 | } 81 | 82 | public static ulong ExportNormals(int bufferIndex, ulong byteOffset, GeometryDataObject geometryDataObject, GltfBinaryData binaryData, List bufferViews, List accessors) 83 | { 84 | foreach (double normal in geometryDataObject.Normals) 85 | { 86 | binaryData.NormalBuffer.Add(Convert.ToSingle(normal)); 87 | } 88 | 89 | // Get max and min for normal data 90 | float[] normalMinMax = Util.GetVec3MinMax(binaryData.NormalBuffer); 91 | 92 | // Add a normals (vec3) buffer view 93 | const int elementsPerNormal = 3; 94 | const int bytesPerNormalElement = 4; 95 | const int bytesPerNormal = elementsPerNormal * bytesPerNormalElement; 96 | int normalsCount = geometryDataObject.Normals.Count; 97 | int numVec3Normals = normalsCount / elementsPerNormal; 98 | ulong sizeOfVec3ViewNormals = (ulong)numVec3Normals * (ulong)bytesPerNormal; 99 | GltfBufferView vec3ViewNormals = new GltfBufferView(bufferIndex, byteOffset, sizeOfVec3ViewNormals, Targets.ARRAY_BUFFER, "normals"); 100 | bufferViews.Add(vec3ViewNormals); 101 | int vec3ViewNormalsIndex = bufferViews.Count - 1; 102 | 103 | // add a normals accessor 104 | var count = normalsCount / elementsPerNormal; 105 | var max = new List(3) { normalMinMax[1], normalMinMax[3], normalMinMax[5] }; 106 | var min = new List(3) { normalMinMax[0], normalMinMax[2], normalMinMax[4] }; 107 | 108 | var normalsAccessor = new GltfAccessor(vec3ViewNormalsIndex, 0, ComponentType.FLOAT, count, VEC3_STR, max, min, NORMAL_STR); 109 | accessors.Add(normalsAccessor); 110 | binaryData.NormalsAccessorIndex = accessors.Count - 1; 111 | return byteOffset + vec3ViewNormals.ByteLength; 112 | } 113 | public static ulong ExportTexCoords(int bufferIndex, ulong byteOffset, GeometryDataObject geometryDataObject, GltfBinaryData binaryData, List bufferViews, List accessors) 114 | { 115 | int texCoordsCount = geometryDataObject.TexCoords.Count; 116 | if (texCoordsCount == 0) 117 | { 118 | return byteOffset; 119 | } 120 | 121 | for (int i = 0; i < texCoordsCount; i++) 122 | { 123 | binaryData.TexCoordBuffer.Add(Convert.ToSingle(geometryDataObject.TexCoords[i])); 124 | } 125 | 126 | float[] maxMin = Util.GetVec2MinMax(binaryData.TexCoordBuffer); 127 | 128 | // add a vec2 buffer view 129 | const int elementsPerTexcoord = 2; 130 | const int bytesPerElement = 4; 131 | const int bytesPerTexcoord = elementsPerTexcoord * bytesPerElement; 132 | int numVec2TexCoords = texCoordsCount / elementsPerTexcoord; 133 | ulong sizeOfVec2ViewTexCoords = (ulong) numVec2TexCoords * bytesPerTexcoord; 134 | GltfBufferView vec2ViewTexCoords = new GltfBufferView(bufferIndex, byteOffset, sizeOfVec2ViewTexCoords, Targets.ARRAY_BUFFER, "texcoords_0"); 135 | bufferViews.Add(vec2ViewTexCoords); 136 | int vec2ViewTexCoordsIndex = bufferViews.Count - 1; 137 | 138 | // add the accessor 139 | var count = texCoordsCount / elementsPerTexcoord; 140 | var max = new List(2) { maxMin[1], maxMin[3] }; 141 | var min = new List(2) { maxMin[0], maxMin[2] }; 142 | var accessor = new GltfAccessor(vec2ViewTexCoordsIndex, 0, ComponentType.FLOAT, count, VEC2_STR, max, min, TEXCOORD_STR); 143 | accessors.Add(accessor); 144 | binaryData.TexCoordAccessorIndex = accessors.Count - 1; 145 | return byteOffset + vec2ViewTexCoords.ByteLength; 146 | } 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /CesiumIonRevitAddin/Utils/IonExportUtils.cs: -------------------------------------------------------------------------------- 1 | using Autodesk.Revit.DB; 2 | using Autodesk.Revit.UI; 3 | using CesiumIonRevitAddin.CesiumIonClient; 4 | using CesiumIonRevitAddin.Forms; 5 | using CesiumIonRevitAddin.Gltf; 6 | using System.Collections.Generic; 7 | using System.Diagnostics; 8 | using System.IO; 9 | using System.Linq; 10 | using System.Reflection; 11 | using System.Text; 12 | using System.Windows.Forms; 13 | 14 | 15 | namespace CesiumIonRevitAddin.Utils 16 | { 17 | internal static class IonExportUtils 18 | { 19 | public static Preferences GetUserPreferences(Document doc) 20 | { 21 | // Load preferences for this document, if it exists 22 | Preferences preferences; 23 | string preferencesPath = Preferences.GetPreferencesPathForProject(doc.PathName); 24 | bool existingPreferences = File.Exists(preferencesPath); 25 | if (existingPreferences && doc.PathName != "") 26 | preferences = Preferences.LoadFromFile(preferencesPath); 27 | else 28 | preferences = new Preferences(); 29 | 30 | // Use the existing filename for the first export instead of the default tileset.3dtiles 31 | if (!existingPreferences && doc.PathName != "") 32 | { 33 | string docFileName = Path.GetFileNameWithoutExtension(doc.PathName); 34 | preferences.OutputPath = Path.Combine(preferences.OutputDirectory, docFileName + ".3dtiles"); 35 | } 36 | 37 | // Display the export preferences dialog 38 | using (ExportDialog exportDialog = new ExportDialog(ref preferences)) 39 | { 40 | exportDialog.ShowDialog(); 41 | if (exportDialog.DialogResult != DialogResult.OK) 42 | return null; 43 | } 44 | return preferences; 45 | } 46 | 47 | public static Preferences SaveUserPreferences(Document doc, Preferences preferences) 48 | { 49 | // Write out the updated preferences for this document 50 | if (doc.PathName != "") 51 | preferences.SaveToFile(Preferences.GetPreferencesPathForProject(doc.PathName)); 52 | return preferences; 53 | } 54 | 55 | public static View3D GetExportView(Autodesk.Revit.DB.View view) 56 | { 57 | if (view.Document.IsFamilyDocument) 58 | { 59 | Autodesk.Revit.UI.TaskDialog.Show("Families Not Supported", "Families cannot be uploaded to Cesium ion. Please open a project document and try again."); 60 | return null; 61 | } 62 | 63 | if (view.GetType().Name != "View3D") 64 | { 65 | Autodesk.Revit.UI.TaskDialog.Show("3D View Required", "A 3D view is required to upload to Cesium ion. Please switch to a 3D view and try again."); 66 | return null; 67 | } 68 | return (View3D)view; 69 | } 70 | public static string GetSavePath(Document doc, Preferences preferences) 71 | { 72 | // Display the Save File Dialog 73 | using (SaveFileDialog saveFileDialog = new SaveFileDialog()) 74 | { 75 | saveFileDialog.Filter = "3D Tiles (*.3dtiles)|*.3dtiles"; 76 | saveFileDialog.FilterIndex = 1; 77 | 78 | // Load the initial directory and filename from the preferences 79 | if (preferences.ionExport) 80 | { 81 | saveFileDialog.RestoreDirectory = true; 82 | saveFileDialog.FileName = Path.GetFileNameWithoutExtension(doc.PathName); 83 | } 84 | else 85 | { 86 | saveFileDialog.FileName = preferences.OutputFilename; 87 | saveFileDialog.InitialDirectory = preferences.OutputDirectory; 88 | } 89 | 90 | if (saveFileDialog.ShowDialog() == DialogResult.OK) 91 | { 92 | return saveFileDialog.FileName; 93 | } 94 | else 95 | { 96 | return null; 97 | } 98 | } 99 | } 100 | 101 | public static Result ExportIntermediateFormat(View3D view, Preferences preferences) 102 | { 103 | var exportContext = new GltfExportContext(view.Document, preferences); 104 | var exporter = new CustomExporter(view.Document, exportContext) 105 | { 106 | ShouldStopOnError = false, 107 | IncludeGeometricObjects = false 108 | }; 109 | 110 | exporter.Export(view); 111 | 112 | return Result.Succeeded; 113 | } 114 | 115 | public static void Cleanup(Preferences preferences) 116 | { 117 | // Remove the temp glTF directory 118 | if (!preferences.KeepGltf) 119 | { 120 | Directory.Delete(preferences.TempDirectory, true); 121 | } 122 | } 123 | 124 | public static string GetProjectInformationAsString(Document doc) 125 | { 126 | Element projectInfoElement = doc.ProjectInformation; 127 | 128 | if (projectInfoElement == null) 129 | return ""; 130 | 131 | Dictionary projectInfoDict = new Dictionary(); 132 | 133 | // Loop through the parameters of the project information element 134 | foreach (Parameter param in projectInfoElement.Parameters) 135 | { 136 | string paramName = param.Definition.Name; 137 | string paramValue = GetParameterValue(param); 138 | 139 | // Only add to dictionary if the value is not empty or null 140 | if (!string.IsNullOrWhiteSpace(paramValue)) 141 | { 142 | projectInfoDict[paramName] = paramValue; 143 | } 144 | } 145 | 146 | // Sort the dictionary by key 147 | var sortedProjectInfo = projectInfoDict.OrderBy(kv => kv.Key); 148 | 149 | StringBuilder projectInfoBuilder = new StringBuilder(); 150 | 151 | foreach (var kv in sortedProjectInfo) 152 | { 153 | projectInfoBuilder.AppendLine($"{kv.Key}: {kv.Value}"); 154 | } 155 | 156 | // Return the formatted and sorted string 157 | return projectInfoBuilder.ToString(); 158 | } 159 | 160 | public static string GetParameterValue(Parameter param) 161 | { 162 | if (param == null) 163 | return null; 164 | 165 | switch (param.StorageType) 166 | { 167 | case StorageType.String: 168 | return param.AsString(); 169 | case StorageType.Integer: 170 | return param.AsInteger().ToString(); 171 | case StorageType.Double: 172 | return param.AsDouble().ToString(); 173 | case StorageType.ElementId: 174 | return Util.GetElementIdAsLong(param.AsElementId()).ToString(); 175 | default: 176 | return string.Empty; 177 | } 178 | } 179 | 180 | public static void ConfigureClient(UIApplication app) 181 | { 182 | string location = Assembly.GetExecutingAssembly().Location; 183 | string fileVersionInfo = string.IsNullOrWhiteSpace(location) ? "UnknownVersion" : FileVersionInfo.GetVersionInfo(location).ProductVersion; 184 | 185 | string revitInfo = $"Autodesk Revit {app.Application.SubVersionNumber}"; 186 | string view = app.ActiveUIDocument.ActiveView.Name; 187 | string project = app.ActiveUIDocument.Document.Title; 188 | 189 | string santizedView = string.IsNullOrWhiteSpace(view) ? "UnknownView" : view; 190 | string sanitizedProject = string.IsNullOrWhiteSpace(project) ? "UnknownProject" : project; 191 | string projectInfo = $"{sanitizedProject}:{santizedView}"; 192 | 193 | Connection.ConfigureClient("Cesium ion for Autodesk Revit", fileVersionInfo, revitInfo, projectInfo); 194 | } 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /CesiumIonRevitAddin/Utils/GLTFExportUtils.cs: -------------------------------------------------------------------------------- 1 | using Autodesk.Revit.DB; 2 | using CesiumIonRevitAddin.Gltf; 3 | using CesiumIonRevitAddin.Model; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | 7 | namespace CesiumIonRevitAddin.Utils 8 | { 9 | internal static class GltfExportUtils 10 | { 11 | private const int DEF_COLOR = 250; 12 | private const string DEF_MATERIAL_NAME = "default"; 13 | private const string BIN = ".bin"; 14 | 15 | public static GltfMaterial GetGLTFMaterial(List gltfMaterials, Material material, bool doubleSided) 16 | { 17 | // search for an already existing material 18 | GltfMaterial m = gltfMaterials.FirstOrDefault(x => 19 | x.PbrMetallicRoughness != null && 20 | x.PbrMetallicRoughness.BaseColorFactor[0] == material.Color.Red && 21 | x.PbrMetallicRoughness.BaseColorFactor[1] == material.Color.Green && 22 | x.PbrMetallicRoughness.BaseColorFactor[2] == material.Color.Blue && x.DoubleSided == doubleSided); 23 | 24 | return m ?? GltfExportUtils.CreateGltfMaterial(DEF_MATERIAL_NAME, 0, new Color(DEF_COLOR, DEF_COLOR, DEF_COLOR), doubleSided); 25 | } 26 | public static GltfBinaryData AddGeometryBinaryData( 27 | List buffers, 28 | List accessors, 29 | List bufferViews, 30 | GeometryDataObject geometryDataObject, 31 | string name, 32 | int elementId, 33 | bool exportNormals) 34 | { 35 | ulong byteOffset = 0; 36 | 37 | var buffer = new GltfBuffer 38 | { 39 | Uri = name + BIN 40 | }; 41 | buffers.Add(buffer); 42 | int bufferIndex = buffers.Count - 1; 43 | GltfBinaryData bufferData = new GltfBinaryData 44 | { 45 | Name = buffer.Uri 46 | }; 47 | 48 | byteOffset = GltfBinaryDataUtils.ExportVertices(bufferIndex, byteOffset, geometryDataObject, bufferData, bufferViews, accessors); 49 | 50 | if (exportNormals) 51 | { 52 | byteOffset = GltfBinaryDataUtils.ExportNormals(bufferIndex, byteOffset, geometryDataObject, bufferData, bufferViews, accessors); 53 | } 54 | 55 | byteOffset = GltfBinaryDataUtils.ExportTexCoords(bufferIndex, byteOffset, geometryDataObject, bufferData, bufferViews, accessors); 56 | 57 | GltfBinaryDataUtils.ExportFaces(bufferIndex, byteOffset, geometryDataObject, bufferData, bufferViews, accessors); 58 | 59 | return bufferData; 60 | } 61 | 62 | public static XYZ FixXYZIfZero(XYZ xyz) => xyz.IsZeroLength() ? XYZ.BasisZ : xyz; 63 | 64 | public static void AddNormals(Autodesk.Revit.DB.Transform transform, PolymeshTopology polymeshTopology, List normals) 65 | { 66 | IList polymeshNormals = polymeshTopology.GetNormals(); 67 | 68 | switch (polymeshTopology.DistributionOfNormals) 69 | { 70 | case DistributionOfNormals.AtEachPoint: 71 | { 72 | foreach (PolymeshFacet facet in polymeshTopology.GetFacets()) 73 | { 74 | var normalPoints = new List 75 | { 76 | transform.OfVector(polymeshNormals[facet.V1]), 77 | transform.OfVector(polymeshNormals[facet.V2]), 78 | transform.OfVector(polymeshNormals[facet.V3]) 79 | }; 80 | 81 | foreach (XYZ normalPoint in normalPoints) 82 | { 83 | XYZ newNormalPoint = FixXYZIfZero(normalPoint); 84 | newNormalPoint = newNormalPoint.Normalize(); 85 | 86 | normals.Add(newNormalPoint.X); 87 | normals.Add(newNormalPoint.Y); 88 | normals.Add(newNormalPoint.Z); 89 | } 90 | } 91 | 92 | break; 93 | } 94 | case DistributionOfNormals.OnePerFace: 95 | { 96 | foreach (PolymeshFacet facet in polymeshTopology.GetFacets()) 97 | { 98 | foreach (XYZ normal in polymeshTopology.GetNormals()) 99 | { 100 | XYZ newNormal = FixXYZIfZero(normal); 101 | newNormal = newNormal.Normalize(); 102 | 103 | for (int j = 0; j < 3; j++) 104 | { 105 | normals.Add(newNormal.X); 106 | normals.Add(newNormal.Y); 107 | normals.Add(newNormal.Z); 108 | } 109 | } 110 | } 111 | 112 | break; 113 | } 114 | 115 | case DistributionOfNormals.OnEachFacet: 116 | { 117 | foreach (XYZ normal in polymeshNormals) 118 | { 119 | XYZ newNormal = transform.OfVector(FixXYZIfZero(normal)); 120 | newNormal = newNormal.Normalize(); 121 | 122 | // Add a normal for each vertex of the facet 123 | for (int j = 0; j < 3; j++) 124 | { 125 | normals.Add(newNormal.X); 126 | normals.Add(newNormal.Y); 127 | normals.Add(newNormal.Z); 128 | } 129 | } 130 | 131 | break; 132 | } 133 | } 134 | } 135 | 136 | public static void AddTexCoords(PolymeshTopology polymeshTopology, List uvs) 137 | { 138 | IList polyMeshUvs = polymeshTopology.GetUVs(); 139 | 140 | foreach (PolymeshFacet facet in polymeshTopology.GetFacets()) 141 | { 142 | var facetVertIndex = facet.V1; 143 | uvs.Add(polyMeshUvs[facetVertIndex].U); 144 | uvs.Add(polyMeshUvs[facetVertIndex].V); 145 | 146 | facetVertIndex = facet.V2; 147 | uvs.Add(polyMeshUvs[facetVertIndex].U); 148 | uvs.Add(polyMeshUvs[facetVertIndex].V); 149 | 150 | facetVertIndex = facet.V3; 151 | uvs.Add(polyMeshUvs[facetVertIndex].U); 152 | uvs.Add(polyMeshUvs[facetVertIndex].V); 153 | } 154 | } 155 | 156 | public static void AddOrUpdateCurrentItem(IndexedDictionary nodes, IndexedDictionary geometryDataObjects, 157 | IndexedDictionary vertexLookupIntObjects, IndexedDictionary materials) 158 | { 159 | // Add new "_current" entries if vertex_key is unique 160 | string vertexKey = nodes.CurrentKey + "_" + materials.CurrentKey; 161 | geometryDataObjects.AddOrUpdateCurrent(vertexKey, new GeometryDataObject()); 162 | vertexLookupIntObjects.AddOrUpdateCurrent(vertexKey, new VertexLookupIntObject()); 163 | } 164 | 165 | public static void AddRPCNormals(Preferences preferences, MeshTriangle triangle, GeometryDataObject geomDataObj) 166 | { 167 | XYZ normal = GeometryUtils.GetNormal(triangle); 168 | 169 | for (int j = 0; j < 3; j++) 170 | { 171 | geomDataObj.Normals.Add(normal.X); 172 | geomDataObj.Normals.Add(normal.Y); 173 | geomDataObj.Normals.Add(normal.Z); 174 | } 175 | } 176 | 177 | public static void AddVerticesAndFaces(VertexLookupIntObject vertex, GeometryDataObject geometryDataObject, List pts) 178 | { 179 | var idx = vertex.AddVertex(new PointIntObject(pts[0])); 180 | geometryDataObject.Faces.Add(idx); 181 | 182 | var idx1 = vertex.AddVertex(new PointIntObject(pts[1])); 183 | geometryDataObject.Faces.Add(idx1); 184 | 185 | var idx2 = vertex.AddVertex(new PointIntObject(pts[2])); 186 | geometryDataObject.Faces.Add(idx2); 187 | } 188 | 189 | public static GltfMaterial CreateGltfMaterial(string materialName, int materialOpacity, Color color, bool doubleSided) 190 | { 191 | var gltfMaterial = new GltfMaterial(); 192 | gltfMaterial.DoubleSided = doubleSided; 193 | float opacity = 1 - (float)materialOpacity; 194 | gltfMaterial.Name = materialName; 195 | var pbr = new GltfPbr 196 | { 197 | BaseColorFactor = new List(4) { color.Red / 255f, color.Green / 255f, color.Blue / 255f, opacity }, 198 | MetallicFactor = 0f, 199 | RoughnessFactor = 1f 200 | }; 201 | gltfMaterial.PbrMetallicRoughness = pbr; 202 | 203 | return gltfMaterial; 204 | } 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Remove the line below if you want to inherit .editorconfig settings from higher directories 2 | root = true 3 | 4 | # C# files 5 | [*.cs] 6 | 7 | #### Core EditorConfig Options #### 8 | 9 | # Indentation and spacing 10 | indent_size = 4 11 | indent_style = space 12 | tab_width = 4 13 | 14 | # New line preferences 15 | end_of_line = crlf 16 | insert_final_newline = false 17 | 18 | #### .NET Coding Conventions #### 19 | 20 | # Organize usings 21 | dotnet_separate_import_directive_groups = false 22 | dotnet_sort_system_directives_first = false 23 | file_header_template = unset 24 | 25 | # this. and Me. preferences 26 | dotnet_style_qualification_for_event = false 27 | dotnet_style_qualification_for_field = true 28 | dotnet_style_qualification_for_method = false 29 | dotnet_style_qualification_for_property = true 30 | 31 | # Language keywords vs BCL types preferences 32 | dotnet_style_predefined_type_for_locals_parameters_members = true 33 | dotnet_style_predefined_type_for_member_access = true 34 | 35 | # Parentheses preferences 36 | dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity 37 | dotnet_style_parentheses_in_other_binary_operators = always_for_clarity 38 | dotnet_style_parentheses_in_other_operators = never_if_unnecessary 39 | dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity 40 | 41 | # Modifier preferences 42 | dotnet_style_require_accessibility_modifiers = for_non_interface_members 43 | 44 | # Expression-level preferences 45 | dotnet_style_coalesce_expression = true 46 | dotnet_style_collection_initializer = false 47 | dotnet_style_explicit_tuple_names = true 48 | dotnet_style_namespace_match_folder = true 49 | dotnet_style_null_propagation = true 50 | dotnet_style_object_initializer = true 51 | dotnet_style_operator_placement_when_wrapping = beginning_of_line 52 | dotnet_style_prefer_auto_properties = true 53 | dotnet_style_prefer_collection_expression = false 54 | dotnet_style_prefer_compound_assignment = false 55 | dotnet_style_prefer_conditional_expression_over_assignment = true 56 | dotnet_style_prefer_conditional_expression_over_return = true 57 | dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed 58 | dotnet_style_prefer_inferred_anonymous_type_member_names = true 59 | dotnet_style_prefer_inferred_tuple_names = true 60 | dotnet_style_prefer_is_null_check_over_reference_equality_method = true 61 | dotnet_style_prefer_simplified_boolean_expressions = true 62 | dotnet_style_prefer_simplified_interpolation = true 63 | 64 | 65 | 66 | # Field preferences 67 | dotnet_style_readonly_field = true 68 | 69 | # Parameter preferences 70 | dotnet_code_quality_unused_parameters = all 71 | 72 | # Suppression preferences 73 | dotnet_remove_unnecessary_suppression_exclusions = none 74 | 75 | # New line preferences 76 | dotnet_style_allow_multiple_blank_lines_experimental = false 77 | dotnet_style_allow_statement_immediately_after_block_experimental = true 78 | 79 | #### C# Coding Conventions #### 80 | 81 | # var preferences 82 | csharp_style_var_elsewhere = false 83 | csharp_style_var_for_built_in_types = false 84 | csharp_style_var_when_type_is_apparent = false 85 | 86 | # Expression-bodied members 87 | csharp_style_expression_bodied_accessors = when_on_single_line 88 | csharp_style_expression_bodied_constructors = false 89 | csharp_style_expression_bodied_indexers = true 90 | csharp_style_expression_bodied_lambdas = when_on_single_line 91 | csharp_style_expression_bodied_local_functions = when_on_single_line 92 | csharp_style_expression_bodied_methods = when_on_single_line 93 | csharp_style_expression_bodied_operators = false 94 | csharp_style_expression_bodied_properties = when_on_single_line 95 | 96 | # Pattern matching preferences 97 | csharp_style_pattern_matching_over_as_with_null_check = true 98 | csharp_style_pattern_matching_over_is_with_cast_check = true 99 | csharp_style_prefer_extended_property_pattern = true 100 | csharp_style_prefer_not_pattern = false 101 | csharp_style_prefer_pattern_matching = true 102 | csharp_style_prefer_switch_expression = false 103 | 104 | # Null-checking preferences 105 | csharp_style_conditional_delegate_call = true 106 | 107 | # Modifier preferences 108 | csharp_prefer_static_anonymous_function = true 109 | csharp_prefer_static_local_function = true 110 | csharp_preferred_modifier_order = public,private,protected,internal,file,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async 111 | csharp_style_prefer_readonly_struct = true 112 | csharp_style_prefer_readonly_struct_member = true 113 | 114 | # Code-block preferences 115 | csharp_prefer_braces = true 116 | csharp_prefer_simple_using_statement = false 117 | csharp_style_namespace_declarations = block_scoped 118 | csharp_style_prefer_method_group_conversion = true 119 | csharp_style_prefer_primary_constructors = false 120 | csharp_style_prefer_top_level_statements = true 121 | 122 | # Expression-level preferences 123 | csharp_prefer_simple_default_expression = true 124 | csharp_style_deconstructed_variable_declaration = true 125 | csharp_style_implicit_object_creation_when_type_is_apparent = false 126 | csharp_style_inlined_variable_declaration = true 127 | csharp_style_prefer_index_operator = false 128 | csharp_style_prefer_local_over_anonymous_function = true 129 | csharp_style_prefer_null_check_over_type_check = true 130 | csharp_style_prefer_range_operator = false 131 | csharp_style_prefer_tuple_swap = true 132 | csharp_style_prefer_utf8_string_literals = true 133 | csharp_style_throw_expression = true 134 | csharp_style_unused_value_assignment_preference = discard_variable 135 | csharp_style_unused_value_expression_statement_preference = discard_variable 136 | 137 | # 'using' directive preferences 138 | csharp_using_directive_placement = outside_namespace 139 | 140 | # New line preferences 141 | csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true 142 | csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true 143 | csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true 144 | csharp_style_allow_blank_lines_between_consecutive_braces_experimental = false 145 | csharp_style_allow_embedded_statements_on_same_line_experimental = true 146 | 147 | #### C# Formatting Rules #### 148 | 149 | # New line preferences 150 | csharp_new_line_before_catch = true 151 | csharp_new_line_before_else = true 152 | csharp_new_line_before_finally = true 153 | csharp_new_line_before_members_in_anonymous_types = true 154 | csharp_new_line_before_members_in_object_initializers = true 155 | csharp_new_line_before_open_brace = all 156 | csharp_new_line_between_query_expression_clauses = true 157 | 158 | # Indentation preferences 159 | csharp_indent_block_contents = true 160 | csharp_indent_braces = false 161 | csharp_indent_case_contents = true 162 | csharp_indent_case_contents_when_block = true 163 | csharp_indent_labels = one_less_than_current 164 | csharp_indent_switch_labels = true 165 | 166 | # Space preferences 167 | csharp_space_after_cast = false 168 | csharp_space_after_colon_in_inheritance_clause = true 169 | csharp_space_after_comma = true 170 | csharp_space_after_dot = false 171 | csharp_space_after_keywords_in_control_flow_statements = true 172 | csharp_space_after_semicolon_in_for_statement = true 173 | csharp_space_around_binary_operators = before_and_after 174 | csharp_space_around_declaration_statements = false 175 | csharp_space_before_colon_in_inheritance_clause = true 176 | csharp_space_before_comma = false 177 | csharp_space_before_dot = false 178 | csharp_space_before_open_square_brackets = false 179 | csharp_space_before_semicolon_in_for_statement = false 180 | csharp_space_between_empty_square_brackets = false 181 | csharp_space_between_method_call_empty_parameter_list_parentheses = false 182 | csharp_space_between_method_call_name_and_opening_parenthesis = false 183 | csharp_space_between_method_call_parameter_list_parentheses = false 184 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false 185 | csharp_space_between_method_declaration_name_and_open_parenthesis = false 186 | csharp_space_between_method_declaration_parameter_list_parentheses = false 187 | csharp_space_between_parentheses = false 188 | csharp_space_between_square_brackets = false 189 | 190 | # Wrapping preferences 191 | csharp_preserve_single_line_blocks = true 192 | csharp_preserve_single_line_statements = true 193 | 194 | #### Naming styles #### 195 | 196 | # Naming rules 197 | 198 | dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion 199 | dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface 200 | dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i 201 | 202 | dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion 203 | dotnet_naming_rule.types_should_be_pascal_case.symbols = types 204 | dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case 205 | 206 | dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion 207 | dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members 208 | dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case 209 | 210 | # Symbol specifications 211 | 212 | dotnet_naming_symbols.interface.applicable_kinds = interface 213 | dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected 214 | dotnet_naming_symbols.interface.required_modifiers = 215 | 216 | dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum 217 | dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected 218 | dotnet_naming_symbols.types.required_modifiers = 219 | 220 | dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method 221 | dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected 222 | dotnet_naming_symbols.non_field_members.required_modifiers = 223 | 224 | # Naming styles 225 | 226 | dotnet_naming_style.pascal_case.required_prefix = 227 | dotnet_naming_style.pascal_case.required_suffix = 228 | dotnet_naming_style.pascal_case.word_separator = 229 | dotnet_naming_style.pascal_case.capitalization = pascal_case 230 | 231 | dotnet_naming_style.begins_with_i.required_prefix = I 232 | dotnet_naming_style.begins_with_i.required_suffix = 233 | dotnet_naming_style.begins_with_i.word_separator = 234 | dotnet_naming_style.begins_with_i.capitalization = pascal_case 235 | 236 | # Suppress CA1850 (Prefer HashData over ComputeHash) 237 | dotnet_diagnostic.CA1850.severity = none 238 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | --------------------------------------------------------------------------------