├── .gitignore ├── Images ├── 1.JPG └── 2.JPG ├── LICENSE ├── README.md ├── Revit2Gltf.Plugin ├── AddinApplication.cs ├── Commands │ └── ExportToGltfCommand.cs ├── PackageContents.xml ├── PluginController.cs ├── Properties │ └── AssemblyInfo.cs ├── Resources │ └── icons8-upload-to-cloud-32.png ├── Revit2Gltf.Plugin.csproj ├── Revit2Gltf.addin └── packages.config ├── Revit2Gltf.sln ├── Revit2Gltf ├── AssetPropertyDescriptor.cs ├── AssimpUtilities.cs ├── CentroidVolume.cs ├── Configuration.cs ├── Enums.cs ├── Extensions.cs ├── GeometryExtensions.cs ├── MaterialUtilities.cs ├── Properties │ └── AssemblyInfo.cs ├── RenderAppearanceDescriptor.cs ├── Resources │ └── icons8-upload-to-cloud-32.png ├── Revit2Gltf.csproj ├── SafenedFilename.cs ├── TextureBundle.cs └── packages.config └── ThirdParty ├── API ├── 2015 │ ├── RevitAPI.dll │ ├── RevitAPIIFC.dll │ └── RevitAPIUI.dll ├── 2016 │ ├── RevitAPI.dll │ ├── RevitAPIIFC.dll │ └── RevitAPIUI.dll ├── 2017 │ ├── RevitAPI.dll │ ├── RevitAPIIFC.dll │ └── RevitAPIUI.dll ├── 2018 │ ├── RevitAPI.dll │ ├── RevitAPIIFC.dll │ └── RevitAPIUI.dll ├── 2019 │ ├── RevitAPI.dll │ ├── RevitAPIIFC.dll │ └── RevitAPIUI.dll └── 2020 │ ├── RevitAPI.dll │ ├── RevitAPIIFC.dll │ └── RevitAPIUI.dll └── Snappy ├── Crc32C.NET.dll └── Snappy.NET.dll /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Aa][Rr][Mm]/ 27 | [Aa][Rr][Mm]64/ 28 | bld/ 29 | [Bb]in/ 30 | [Oo]bj/ 31 | [Ll]og/ 32 | [Ll]ogs/ 33 | 34 | # Visual Studio 2015/2017 cache/options directory 35 | .vs/ 36 | # Uncomment if you have tasks that create the project's static files in wwwroot 37 | #wwwroot/ 38 | 39 | # Visual Studio 2017 auto generated files 40 | Generated\ Files/ 41 | 42 | # MSTest test Results 43 | [Tt]est[Rr]esult*/ 44 | [Bb]uild[Ll]og.* 45 | 46 | # NUnit 47 | *.VisualState.xml 48 | TestResult.xml 49 | nunit-*.xml 50 | 51 | # Build Results of an ATL Project 52 | [Dd]ebugPS/ 53 | [Rr]eleasePS/ 54 | dlldata.c 55 | 56 | # Benchmark Results 57 | BenchmarkDotNet.Artifacts/ 58 | 59 | # .NET Core 60 | project.lock.json 61 | project.fragment.lock.json 62 | artifacts/ 63 | 64 | # StyleCop 65 | StyleCopReport.xml 66 | 67 | # Files built by Visual Studio 68 | *_i.c 69 | *_p.c 70 | *_h.h 71 | *.ilk 72 | *.meta 73 | *.obj 74 | *.iobj 75 | *.pch 76 | *.pdb 77 | *.ipdb 78 | *.pgc 79 | *.pgd 80 | *.rsp 81 | *.sbr 82 | *.tlb 83 | *.tli 84 | *.tlh 85 | *.tmp 86 | *.tmp_proj 87 | *_wpftmp.csproj 88 | *.log 89 | *.vspscc 90 | *.vssscc 91 | .builds 92 | *.pidb 93 | *.svclog 94 | *.scc 95 | 96 | # Chutzpah Test files 97 | _Chutzpah* 98 | 99 | # Visual C++ cache files 100 | ipch/ 101 | *.aps 102 | *.ncb 103 | *.opendb 104 | *.opensdf 105 | *.sdf 106 | *.cachefile 107 | *.VC.db 108 | *.VC.VC.opendb 109 | 110 | # Visual Studio profiler 111 | *.psess 112 | *.vsp 113 | *.vspx 114 | *.sap 115 | 116 | # Visual Studio Trace Files 117 | *.e2e 118 | 119 | # TFS 2012 Local Workspace 120 | $tf/ 121 | 122 | # Guidance Automation Toolkit 123 | *.gpState 124 | 125 | # ReSharper is a .NET coding add-in 126 | _ReSharper*/ 127 | *.[Rr]e[Ss]harper 128 | *.DotSettings.user 129 | 130 | # TeamCity is a build add-in 131 | _TeamCity* 132 | 133 | # DotCover is a Code Coverage Tool 134 | *.dotCover 135 | 136 | # AxoCover is a Code Coverage Tool 137 | .axoCover/* 138 | !.axoCover/settings.json 139 | 140 | # Visual Studio code coverage results 141 | *.coverage 142 | *.coveragexml 143 | 144 | # NCrunch 145 | _NCrunch_* 146 | .*crunch*.local.xml 147 | nCrunchTemp_* 148 | 149 | # MightyMoose 150 | *.mm.* 151 | AutoTest.Net/ 152 | 153 | # Web workbench (sass) 154 | .sass-cache/ 155 | 156 | # Installshield output folder 157 | [Ee]xpress/ 158 | 159 | # DocProject is a documentation generator add-in 160 | DocProject/buildhelp/ 161 | DocProject/Help/*.HxT 162 | DocProject/Help/*.HxC 163 | DocProject/Help/*.hhc 164 | DocProject/Help/*.hhk 165 | DocProject/Help/*.hhp 166 | DocProject/Help/Html2 167 | DocProject/Help/html 168 | 169 | # Click-Once directory 170 | publish/ 171 | 172 | # Publish Web Output 173 | *.[Pp]ublish.xml 174 | *.azurePubxml 175 | # Note: Comment the next line if you want to checkin your web deploy settings, 176 | # but database connection strings (with potential passwords) will be unencrypted 177 | *.pubxml 178 | *.publishproj 179 | 180 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 181 | # checkin your Azure Web App publish settings, but sensitive information contained 182 | # in these scripts will be unencrypted 183 | PublishScripts/ 184 | 185 | # NuGet Packages 186 | *.nupkg 187 | # NuGet Symbol Packages 188 | *.snupkg 189 | # The packages folder can be ignored because of Package Restore 190 | **/[Pp]ackages/* 191 | # except build/, which is used as an MSBuild target. 192 | !**/[Pp]ackages/build/ 193 | # Uncomment if necessary however generally it will be regenerated when needed 194 | #!**/[Pp]ackages/repositories.config 195 | # NuGet v3's project.json files produces more ignorable files 196 | *.nuget.props 197 | *.nuget.targets 198 | 199 | # Microsoft Azure Build Output 200 | csx/ 201 | *.build.csdef 202 | 203 | # Microsoft Azure Emulator 204 | ecf/ 205 | rcf/ 206 | 207 | # Windows Store app package directories and files 208 | AppPackages/ 209 | BundleArtifacts/ 210 | Package.StoreAssociation.xml 211 | _pkginfo.txt 212 | *.appx 213 | *.appxbundle 214 | *.appxupload 215 | 216 | # Visual Studio cache files 217 | # files ending in .cache can be ignored 218 | *.[Cc]ache 219 | # but keep track of directories ending in .cache 220 | !?*.[Cc]ache/ 221 | 222 | # Others 223 | ClientBin/ 224 | ~$* 225 | *~ 226 | *.dbmdl 227 | *.dbproj.schemaview 228 | *.jfm 229 | *.pfx 230 | *.publishsettings 231 | orleans.codegen.cs 232 | 233 | # Including strong name files can present a security risk 234 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 235 | #*.snk 236 | 237 | # Since there are multiple workflows, uncomment next line to ignore bower_components 238 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 239 | #bower_components/ 240 | 241 | # RIA/Silverlight projects 242 | Generated_Code/ 243 | 244 | # Backup & report files from converting an old project file 245 | # to a newer Visual Studio version. Backup files are not needed, 246 | # because we have git ;-) 247 | _UpgradeReport_Files/ 248 | Backup*/ 249 | UpgradeLog*.XML 250 | UpgradeLog*.htm 251 | ServiceFabricBackup/ 252 | *.rptproj.bak 253 | 254 | # SQL Server files 255 | *.mdf 256 | *.ldf 257 | *.ndf 258 | 259 | # Business Intelligence projects 260 | *.rdl.data 261 | *.bim.layout 262 | *.bim_*.settings 263 | *.rptproj.rsuser 264 | *- [Bb]ackup.rdl 265 | *- [Bb]ackup ([0-9]).rdl 266 | *- [Bb]ackup ([0-9][0-9]).rdl 267 | 268 | # Microsoft Fakes 269 | FakesAssemblies/ 270 | 271 | # GhostDoc plugin setting file 272 | *.GhostDoc.xml 273 | 274 | # Node.js Tools for Visual Studio 275 | .ntvs_analysis.dat 276 | node_modules/ 277 | 278 | # Visual Studio 6 build log 279 | *.plg 280 | 281 | # Visual Studio 6 workspace options file 282 | *.opt 283 | 284 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 285 | *.vbw 286 | 287 | # Visual Studio LightSwitch build output 288 | **/*.HTMLClient/GeneratedArtifacts 289 | **/*.DesktopClient/GeneratedArtifacts 290 | **/*.DesktopClient/ModelManifest.xml 291 | **/*.Server/GeneratedArtifacts 292 | **/*.Server/ModelManifest.xml 293 | _Pvt_Extensions 294 | 295 | # Paket dependency manager 296 | .paket/paket.exe 297 | paket-files/ 298 | 299 | # FAKE - F# Make 300 | .fake/ 301 | 302 | # CodeRush personal settings 303 | .cr/personal 304 | 305 | # Python Tools for Visual Studio (PTVS) 306 | __pycache__/ 307 | *.pyc 308 | 309 | # Cake - Uncomment if you are using it 310 | # tools/** 311 | # !tools/packages.config 312 | 313 | # Tabs Studio 314 | *.tss 315 | 316 | # Telerik's JustMock configuration file 317 | *.jmconfig 318 | 319 | # BizTalk build output 320 | *.btp.cs 321 | *.btm.cs 322 | *.odx.cs 323 | *.xsd.cs 324 | 325 | # OpenCover UI analysis results 326 | OpenCover/ 327 | 328 | # Azure Stream Analytics local run output 329 | ASALocalRun/ 330 | 331 | # MSBuild Binary and Structured Log 332 | *.binlog 333 | 334 | # NVidia Nsight GPU debugger configuration file 335 | *.nvuser 336 | 337 | # MFractors (Xamarin productivity tool) working folder 338 | .mfractor/ 339 | 340 | # Local History for Visual Studio 341 | .localhistory/ 342 | 343 | # BeatPulse healthcheck temp database 344 | healthchecksdb 345 | 346 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 347 | MigrationBackup/ 348 | 349 | # Ionide (cross platform F# VS Code tools) working folder 350 | .ionide/ 351 | -------------------------------------------------------------------------------- /Images/1.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twinup/Revit2Gltf/b7c475aea4eead0c05816afe60a7eb184fca9c15/Images/1.JPG -------------------------------------------------------------------------------- /Images/2.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twinup/Revit2Gltf/b7c475aea4eead0c05816afe60a7eb184fca9c15/Images/2.JPG -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. 122 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Revit2Gltf 2 | Open source converter and plugin for Revit files to gLTF. Created by [Cityzenith](http://www.cityzenith.com) for seeding digital twins. 3 | 4 | ![Simple Export](Images/2.JPG) 5 | 6 | ## Why gLTF? 7 | gLTF is a portable 3d object schema produced by Khronos Group which combines high performance, portability and human-readability. 8 | Its accepted by many softwares and can be used in JS 3d viewers such as Three and Babylon. 9 | 10 | gLTFs consist of two key parts: 11 | 12 | - myfile.gltf <- Contains the model's heirarchy and metadata in JSON. 13 | 14 | - myfile.bin <- A binary of the model's geometry ("buffer"), intended to be efficiently compressed for portability and performance. This is explicitly referred to in the .gltf file, so don't change the name of this file without changing the reference in the .gltf file! 15 | 16 | and... 17 | 18 | - everythingelse <- Your textures and linked files specified within the .gltf 19 | 20 | You can add further compression with processes such as [DRACO](https://google.github.io/draco/) 21 | 22 | ## How to use 23 | 24 | ### Exporting a gLTF 25 | - Clone the repository 26 | - Restore NuGet packages 27 | - Build the solution 28 | - Hit the Export To Gltf button in the Cityzenith tab and you'll have your GLTF. 29 | - Enjoy it! 30 | 31 | ### Adding gLTF export to your plugin 32 | - Include the Revit2Gltf library in your solution (NuGet package coming soon!) 33 | - Use the ExportToGltf method to convert your document to a gltf model. 34 | - Use Assimp.Net's library to access and modify the contents of the gltf model while loaded into memory. 35 | 36 | ## Caveats 37 | - Tested only in Revit 2016 (Licenses are expensive!) but should work 99% perfectly on other platforms. Compiler conditionals are set up for breaking API changes such as in Revit 2020 38 | - Not all colors and textures are exported correctly. If you take a close look inside the sausage you'll see why - a lot of digging around with magic strings to get at these resources. Although we will gradually improve this, guidance welcomed here 39 | 40 | #### Maintained by Leland Jobson (gh: lelandjobson) 41 | -------------------------------------------------------------------------------- /Revit2Gltf.Plugin/AddinApplication.cs: -------------------------------------------------------------------------------- 1 | using Autodesk.Revit.UI; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Revit2Gltf.Plugin 9 | { 10 | class AddinApplication : IExternalApplication 11 | { 12 | public static PluginController Controller; 13 | 14 | public Result OnStartup(UIControlledApplication application) 15 | { 16 | Controller = new PluginController(application); 17 | return Result.Succeeded; 18 | } 19 | 20 | public Result OnShutdown(UIControlledApplication application) 21 | { 22 | return Result.Succeeded; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Revit2Gltf.Plugin/Commands/ExportToGltfCommand.cs: -------------------------------------------------------------------------------- 1 | using Autodesk.Revit.Attributes; 2 | using Autodesk.Revit.DB; 3 | using Autodesk.Revit.UI; 4 | using Newtonsoft.Json; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Diagnostics; 8 | using System.IO; 9 | using System.Linq; 10 | using System.Text; 11 | using System.Threading.Tasks; 12 | 13 | namespace Revit2Gltf.Plugin.Commands 14 | { 15 | /// 16 | /// 17 | /// 18 | [Transaction(TransactionMode.Manual)] 19 | class ExportToGltfCommand : IExternalCommand 20 | { 21 | /// 22 | /// 23 | /// 24 | /// 25 | /// 26 | /// 27 | /// 28 | Result IExternalCommand.Execute(ExternalCommandData commandData, ref string message, ElementSet elements) 29 | { 30 | if (!TryGetActiveDocument(commandData, out Autodesk.Revit.DB.Document docToExport)) { return Result.Failed; } 31 | if (!TryGetDefaultView(docToExport, out View viewToExport)) { return Result.Failed; } 32 | 33 | 34 | // Grab doc name 35 | string docName = Path.GetFileNameWithoutExtension(docToExport.Title); 36 | 37 | // Prep FBX export 38 | var vs = new ViewSet(); 39 | vs.Insert(viewToExport); 40 | 41 | // Grab configuration 42 | var configuration = ConfigurationManager.ActiveConfiguration; 43 | 44 | // Create a new folder for the export 45 | configuration.ExportFilePathRoot = $"C:/Cityzenith/{docName}"; 46 | if (!Directory.Exists(configuration.ExportFilePathRoot)) 47 | { 48 | Directory.CreateDirectory(configuration.ExportFilePathRoot); 49 | } 50 | 51 | // Export FBX file 52 | docToExport.Export( 53 | configuration.ExportFilePathRoot, 54 | docName, 55 | vs, 56 | new FBXExportOptions() 57 | { 58 | LevelsOfDetailValue = 15, 59 | UseLevelsOfDetail = true, 60 | WithoutBoundaryEdges = true 61 | } 62 | ); 63 | 64 | 65 | 66 | // Check that file was created successfully 67 | var fbxFileName = configuration.ExportFilePathRoot + "/" + docName + ".fbx"; 68 | if (!System.IO.File.Exists(fbxFileName)) { return Result.Failed; } 69 | var fbxModel = AssimpUtilities.LoadFbx(fbxFileName); 70 | 71 | // Get conversions between local ids 72 | var localToUniqueIdMap = docToExport.ExportableElements() 73 | .ToDictionary(e => e.Id.ToString(), e => e.UniqueId.ToString()); 74 | 75 | // Replace auto-generated element names in Fbx with unqiue ids from revit doc 76 | AssimpUtilities.ReplaceNamesWithUniqueIds(fbxModel, localToUniqueIdMap); 77 | 78 | // Create textures subfolder 79 | string textureDirPath = configuration.ExportFilePathRoot + '/' + "Textures"; 80 | if (!Directory.Exists(textureDirPath)) { Directory.CreateDirectory(textureDirPath); } 81 | 82 | var bundles = MaterialUtilities.GetTextureBundles(docToExport, out var paths); 83 | foreach (var b in bundles) 84 | { 85 | // Create material 86 | var assimpMaterial = AssimpUtilities.ConvertToAssimpMaterial(b, docToExport); 87 | 88 | // Add material to model and assign 89 | AssimpUtilities.AddAndAssignMaterial( 90 | fbxModel, 91 | assimpMaterial, 92 | docToExport.ExportableElements().Where(e => 93 | { 94 | var id = e.GetMaterialIds(false).FirstOrDefault(); 95 | if (id != null && id == b.Material.Id) { return true; } 96 | return false; 97 | }) 98 | .Select(e => e.UniqueId.ToString()) 99 | .ToHashSet() 100 | , out bool utilized); 101 | 102 | if (!utilized) { continue; } 103 | 104 | // Copy textures into textures folder 105 | foreach (var path in b.TexturePaths.Values) 106 | { 107 | string destination = $"{textureDirPath}/{path.SafeFileName}"; 108 | try 109 | { 110 | File.Copy(path.FileLocation, destination, true); 111 | } 112 | catch (Exception e) 113 | { 114 | // This is likely due to duplicate materials copied in. 115 | // This could also be an access issue, but less commonly. 116 | //Logger.LogException("Error in copying textures: ", e); 117 | } 118 | } 119 | } 120 | 121 | // Grab all element data 122 | var paramData = docToExport.SiphonElementParamValues(out var legend); 123 | var combined = paramData.Values.Combine(); 124 | JsonConvert.SerializeObject(combined).WriteToFile($"{configuration.ExportFilePathRoot}/Params.json"); 125 | JsonConvert.SerializeObject(legend).WriteToFile($"{configuration.ExportFilePathRoot}/Legend.json"); 126 | 127 | // Write out gltf 128 | AssimpUtilities.SaveToGltf(fbxModel, $"{configuration.ExportFilePathRoot}", docName); 129 | 130 | // Delete .FBX 131 | File.Delete(fbxFileName); 132 | 133 | // Let em know! 134 | TaskDialog dlg = new TaskDialog("Export Successful"); 135 | dlg.MainInstruction = $"Gltf file exported successfully: \n\n {configuration.ExportFilePathRoot}"; 136 | dlg.Show(); 137 | 138 | Process.Start(configuration.ExportFilePathRoot); 139 | 140 | return Result.Succeeded; 141 | } 142 | 143 | private bool TryGetDefaultView(Document docToExport, out View viewToExport) 144 | { 145 | // view to export 146 | viewToExport = null; 147 | //#if !REVIT2020 148 | viewToExport = docToExport.GetOrCreateDefault3DView(); 149 | //#else 150 | // viewToExport = AddinApplication.UIController.GetCurrentView(docToExport); 151 | //#endif 152 | if (viewToExport == null) 153 | { 154 | return false; 155 | } 156 | 157 | // Set view settings 158 | using (var tr = new Transaction(docToExport)) 159 | { 160 | tr.Start("Changing view settings to realistic"); 161 | viewToExport.DetailLevel = ViewDetailLevel.Fine; 162 | viewToExport.DisplayStyle = DisplayStyle.Realistic; 163 | tr.Commit(); 164 | } 165 | 166 | return true; 167 | } 168 | 169 | private bool TryGetActiveDocument(ExternalCommandData commandData, out Document docToExport) 170 | { 171 | // document to export 172 | docToExport = commandData.Application.ActiveUIDocument.Document; 173 | if (docToExport == null) 174 | { 175 | return false; 176 | } 177 | return true; 178 | } 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /Revit2Gltf.Plugin/PackageContents.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Revit2Gltf.Plugin/PluginController.cs: -------------------------------------------------------------------------------- 1 | using Autodesk.Revit.DB; 2 | using Autodesk.Revit.UI; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Reflection; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using System.Windows.Media.Imaging; 10 | 11 | namespace Revit2Gltf.Plugin 12 | { 13 | internal class PluginController 14 | { 15 | private const string RibbonTabName = "Cityzenith"; 16 | private const string SWPRibbonPanelName = "Revit To Gltf"; 17 | private const string ExportButtonName = " Export To Gltf "; 18 | 19 | private static string _thisAssemblyPath = Assembly.GetExecutingAssembly().Location; 20 | 21 | /// 22 | /// The export button 23 | /// 24 | PushButton Btn_Export { get; set; } 25 | 26 | 27 | public PluginController(UIControlledApplication application) 28 | { 29 | CreateUI(application); 30 | } 31 | 32 | private void CreateUI(UIControlledApplication application) 33 | { 34 | application.ViewActivated += Application_ViewActivated; 35 | 36 | try 37 | { 38 | application.CreateRibbonTab(RibbonTabName); 39 | } 40 | // This exception is usually thrown because the 41 | // ribbontab of the same name already exists. 42 | catch { } 43 | 44 | RibbonPanel panel = application.CreateRibbonPanel(RibbonTabName, SWPRibbonPanelName); 45 | 46 | AddExportButton(); 47 | 48 | #region Button Fns 49 | void AddExportButton() 50 | { 51 | PushButtonData pbd = new PushButtonData( 52 | "ExportToGltf", 53 | ExportButtonName, 54 | _thisAssemblyPath, 55 | "Revit2Gltf.Plugin.Commands.ExportToGltfCommand"); 56 | Btn_Export = panel.AddItem(pbd) as PushButton; 57 | Btn_Export.ToolTip = "Exports the 3D view to GLTF"; 58 | Btn_Export.LargeImage = new BitmapImage(new Uri("pack://application:,,,/Revit2Gltf.Plugin;component/Resources/icons8-upload-to-cloud-32.png")); 59 | Btn_Export.Enabled = true; 60 | } 61 | #endregion 62 | } 63 | 64 | private void Application_ViewActivated(object sender, Autodesk.Revit.UI.Events.ViewActivatedEventArgs e) 65 | { 66 | Btn_Export.Enabled = e.CurrentActiveView is View3D; 67 | } 68 | 69 | 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Revit2Gltf.Plugin/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 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("Revit2Gltf.Plugin")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Revit2Gltf.Plugin")] 13 | [assembly: AssemblyCopyright("Copyright © 2021")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("e10817ac-cb7f-4c86-921f-19d047426a3b")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /Revit2Gltf.Plugin/Resources/icons8-upload-to-cloud-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twinup/Revit2Gltf/b7c475aea4eead0c05816afe60a7eb184fca9c15/Revit2Gltf.Plugin/Resources/icons8-upload-to-cloud-32.png -------------------------------------------------------------------------------- /Revit2Gltf.Plugin/Revit2Gltf.Plugin.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {E10817AC-CB7F-4C86-921F-19D047426A3B} 8 | Library 9 | Properties 10 | Revit2Gltf.Plugin 11 | Revit2Gltf.Plugin 12 | v4.7.2 13 | 512 14 | true 15 | 16 | 17 | 18 | 19 | true 20 | full 21 | false 22 | bin\Debug\ 23 | TRACE;DEBUG;REVIT2016 24 | prompt 25 | 4 26 | 27 | 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE;REVIT2016 32 | prompt 33 | 4 34 | 35 | 36 | true 37 | bin\Debug2016\ 38 | TRACE;DEBUG;REVIT2016 39 | full 40 | AnyCPU 41 | 7.3 42 | prompt 43 | MinimumRecommendedRules.ruleset 44 | 45 | 46 | bin\Release2016\ 47 | TRACE;REVIT2016 48 | true 49 | pdbonly 50 | AnyCPU 51 | 7.3 52 | prompt 53 | MinimumRecommendedRules.ruleset 54 | 55 | 56 | true 57 | bin\Debug2019\ 58 | TRACE;DEBUG;REVIT2019 59 | full 60 | AnyCPU 61 | 7.3 62 | prompt 63 | MinimumRecommendedRules.ruleset 64 | 65 | 66 | 67 | ..\packages\AssimpNet.4.1.0\lib\net40\AssimpNet.dll 68 | 69 | 70 | ..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll 71 | 72 | 73 | 74 | ..\ThirdParty\API\2016\RevitAPI.dll 75 | False 76 | 77 | 78 | ..\ThirdParty\API\2015\RevitAPIIFC.dll 79 | False 80 | 81 | 82 | ..\ThirdParty\API\2016\RevitAPIUI.dll 83 | False 84 | 85 | 86 | ..\ThirdParty\API\2016\RevitAPI.dll 87 | False 88 | 89 | 90 | ..\ThirdParty\API\2016\RevitAPIIFC.dll 91 | False 92 | 93 | 94 | ..\ThirdParty\API\2016\RevitAPIUI.dll 95 | False 96 | 97 | 98 | ..\ThirdParty\API\2017\RevitAPI.dll 99 | False 100 | 101 | 102 | ..\ThirdParty\API\2017\RevitAPIIFC.dll 103 | False 104 | 105 | 106 | ..\ThirdParty\API\2017\RevitAPIUI.dll 107 | False 108 | 109 | 110 | ..\ThirdParty\API\2018\RevitAPI.dll 111 | False 112 | 113 | 114 | ..\ThirdParty\API\2018\RevitAPIIFC.dll 115 | False 116 | 117 | 118 | ..\ThirdParty\API\2018\RevitAPIUI.dll 119 | False 120 | 121 | 122 | ..\ThirdParty\API\2019\RevitAPI.dll 123 | False 124 | 125 | 126 | ..\ThirdParty\API\2019\RevitAPIIFC.dll 127 | False 128 | 129 | 130 | ..\ThirdParty\API\2019\RevitAPIUI.dll 131 | False 132 | 133 | 134 | ..\ThirdParty\API\2020\RevitAPI.dll 135 | False 136 | 137 | 138 | ..\ThirdParty\API\2020\RevitAPIIFC.dll 139 | False 140 | 141 | 142 | ..\ThirdParty\API\2020\RevitAPIUI.dll 143 | False 144 | 145 | 146 | ..\ThirdParty\Snappy\Snappy.NET.dll 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | {ad5ae38a-3bde-4fb0-88f6-1f4133566ab5} 174 | Revit2Gltf 175 | 176 | 177 | 178 | 179 | 180 | echo Configuration: $(Configuration) 181 | 182 | if $(Configuration) == Debug2016 goto Debug2016 183 | if $(Configuration) == Release2016 goto Release2016 184 | if $(Configuration) == Debug2017 goto Debug2017 185 | if $(Configuration) == Release2017 goto Release2017 186 | if $(Configuration) == Debug2019 goto Debug2019 187 | if $(Configuration) == Release2019 goto Release2019 188 | 189 | 190 | :Debug2016 191 | echo Copying results to 2016 192 | 193 | xcopy "$(TargetDir)*" "C:\ProgramData\Autodesk\ApplicationPlugins\Revit2Gltf.bundle\Contents\x64\R2016\" /y /c /i 194 | xcopy "$(SolutionDir)Revit2Gltf.Plugin\PackageContents.xml" "C:\ProgramData\Autodesk\ApplicationPlugins\Revit2Gltf.bundle\" /i /y /c 195 | xcopy "$(SolutionDir)Revit2Gltf.Plugin\Revit2Gltf.addin" "C:\ProgramData\Autodesk\ApplicationPlugins\Revit2Gltf.bundle\Contents\x64\R2016\" /i /y /c 196 | goto exit 197 | 198 | :Release2016 199 | echo Copying results to 2016 200 | 201 | xcopy "$(TargetDir)*" "C:\ProgramData\Autodesk\ApplicationPlugins\Revit2Gltf.bundle\Contents\x64\R2016\" /y /c /i 202 | xcopy "$(SolutionDir)Revit2Gltf.Plugin\PackageContents.xml" "C:\ProgramData\Autodesk\ApplicationPlugins\Revit2Gltf.bundle\" /i /y /c 203 | xcopy "$(SolutionDir)Revit2Gltf.Plugin\Revit2Gltf.addin" "C:\ProgramData\Autodesk\ApplicationPlugins\Revit2Gltf.bundle\Contents\x64\R2016\" /i /y /c 204 | goto exit 205 | 206 | :Debug2017 207 | echo Copying results to 2017 208 | 209 | xcopy "$(TargetDir)*" "C:\ProgramData\Autodesk\ApplicationPlugins\Revit2Gltf.bundle\Contents\x64\R2017\" /y /c /i 210 | xcopy "$(SolutionDir)Revit2Gltf.Plugin\PackageContents.xml" "C:\ProgramData\Autodesk\ApplicationPlugins\Revit2Gltf.bundle\" /i /y /c 211 | xcopy "$(SolutionDir)Revit2Gltf.Plugin\Revit2Gltf.addin" "C:\ProgramData\Autodesk\ApplicationPlugins\Revit2Gltf.bundle\Contents\x64\R2017\" /i /y /c 212 | goto exit 213 | 214 | :Release2017 215 | echo Copying results to 2017 216 | 217 | xcopy "$(TargetDir)*" "C:\ProgramData\Autodesk\ApplicationPlugins\Revit2Gltf.bundle\Contents\x64\R2017\" /y /c /i 218 | xcopy "$(SolutionDir)Revit2Gltf.Plugin\PackageContents.xml" "C:\ProgramData\Autodesk\ApplicationPlugins\Revit2Gltf.bundle\" /i /y /c 219 | xcopy "$(SolutionDir)Revit2Gltf.Plugin\Revit2Gltf.addin" "C:\ProgramData\Autodesk\ApplicationPlugins\Revit2Gltf.bundle\Contents\x64\R2017\" /i /y /c 220 | goto exit 221 | 222 | :Debug2019 223 | echo Copying results to 2019 224 | 225 | xcopy "$(TargetDir)*" "C:\ProgramData\Autodesk\ApplicationPlugins\Revit2Gltf.bundle\Contents\x64\R2019\" /y /c /i 226 | xcopy "$(SolutionDir)Revit2Gltf.Plugin\PackageContents.xml" "C:\ProgramData\Autodesk\ApplicationPlugins\Revit2Gltf.bundle\" /i /y /c 227 | xcopy "$(SolutionDir)Revit2Gltf.Plugin\Revit2Gltf.addin" "C:\ProgramData\Autodesk\ApplicationPlugins\Revit2Gltf.bundle\Contents\x64\R2019\" /i /y /c 228 | goto exit 229 | 230 | :Release2019 231 | echo Copying results to 2019 232 | 233 | xcopy "$(TargetDir)*" "C:\ProgramData\Autodesk\ApplicationPlugins\Revit2Gltf.bundle\Contents\x64\R2019\" /y /c /i 234 | xcopy "$(SolutionDir)Revit2Gltf.Plugin\PackageContents.xml" "C:\ProgramData\Autodesk\ApplicationPlugins\Revit2Gltf.bundle\" /i /y /c 235 | xcopy "$(SolutionDir)Revit2Gltf.Plugin\Revit2Gltf.addin" "C:\ProgramData\Autodesk\ApplicationPlugins\Revit2Gltf.bundle\Contents\x64\R2019\" /i /y /c 236 | goto exit 237 | 238 | :exit 239 | 240 | 241 | 242 | 243 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 244 | 245 | 246 | 247 | -------------------------------------------------------------------------------- /Revit2Gltf.Plugin/Revit2Gltf.addin: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Revit2Gltf 5 | .\Revit2Gltf.Plugin.dll 6 | 22d6f9f5-f3db-4adc-bf44-13a0db48c9d2 7 | Revit2Gltf.Plugin.AddinApplication 8 | 1e26ac03-d9a8-4136-82d9-2ebd8edda8be 9 | Cityzenith, Leland Jobson 10 | www.cityzenith.com 11 | 12 | -------------------------------------------------------------------------------- /Revit2Gltf.Plugin/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Revit2Gltf.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30204.135 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Revit2Gltf", "Revit2Gltf\Revit2Gltf.csproj", "{AD5AE38A-3BDE-4FB0-88F6-1F4133566AB5}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Revit2Gltf.Plugin", "Revit2Gltf.Plugin\Revit2Gltf.Plugin.csproj", "{E10817AC-CB7F-4C86-921F-19D047426A3B}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Debug2016|Any CPU = Debug2016|Any CPU 14 | Debug2019|Any CPU = Debug2019|Any CPU 15 | Release|Any CPU = Release|Any CPU 16 | Release2016|Any CPU = Release2016|Any CPU 17 | EndGlobalSection 18 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 19 | {AD5AE38A-3BDE-4FB0-88F6-1F4133566AB5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 20 | {AD5AE38A-3BDE-4FB0-88F6-1F4133566AB5}.Debug|Any CPU.Build.0 = Debug|Any CPU 21 | {AD5AE38A-3BDE-4FB0-88F6-1F4133566AB5}.Debug2016|Any CPU.ActiveCfg = Debug2016|Any CPU 22 | {AD5AE38A-3BDE-4FB0-88F6-1F4133566AB5}.Debug2016|Any CPU.Build.0 = Debug2016|Any CPU 23 | {AD5AE38A-3BDE-4FB0-88F6-1F4133566AB5}.Debug2019|Any CPU.ActiveCfg = Debug2019|Any CPU 24 | {AD5AE38A-3BDE-4FB0-88F6-1F4133566AB5}.Debug2019|Any CPU.Build.0 = Debug2019|Any CPU 25 | {AD5AE38A-3BDE-4FB0-88F6-1F4133566AB5}.Release|Any CPU.ActiveCfg = Release|Any CPU 26 | {AD5AE38A-3BDE-4FB0-88F6-1F4133566AB5}.Release|Any CPU.Build.0 = Release|Any CPU 27 | {AD5AE38A-3BDE-4FB0-88F6-1F4133566AB5}.Release2016|Any CPU.ActiveCfg = Release2016|Any CPU 28 | {AD5AE38A-3BDE-4FB0-88F6-1F4133566AB5}.Release2016|Any CPU.Build.0 = Release2016|Any CPU 29 | {E10817AC-CB7F-4C86-921F-19D047426A3B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 30 | {E10817AC-CB7F-4C86-921F-19D047426A3B}.Debug|Any CPU.Build.0 = Debug|Any CPU 31 | {E10817AC-CB7F-4C86-921F-19D047426A3B}.Debug2016|Any CPU.ActiveCfg = Debug2016|Any CPU 32 | {E10817AC-CB7F-4C86-921F-19D047426A3B}.Debug2016|Any CPU.Build.0 = Debug2016|Any CPU 33 | {E10817AC-CB7F-4C86-921F-19D047426A3B}.Debug2019|Any CPU.ActiveCfg = Debug2019|Any CPU 34 | {E10817AC-CB7F-4C86-921F-19D047426A3B}.Debug2019|Any CPU.Build.0 = Debug2019|Any CPU 35 | {E10817AC-CB7F-4C86-921F-19D047426A3B}.Release|Any CPU.ActiveCfg = Release|Any CPU 36 | {E10817AC-CB7F-4C86-921F-19D047426A3B}.Release|Any CPU.Build.0 = Release|Any CPU 37 | {E10817AC-CB7F-4C86-921F-19D047426A3B}.Release2016|Any CPU.ActiveCfg = Release2016|Any CPU 38 | {E10817AC-CB7F-4C86-921F-19D047426A3B}.Release2016|Any CPU.Build.0 = Release2016|Any CPU 39 | EndGlobalSection 40 | GlobalSection(SolutionProperties) = preSolution 41 | HideSolutionNode = FALSE 42 | EndGlobalSection 43 | GlobalSection(ExtensibilityGlobals) = postSolution 44 | SolutionGuid = {AA549948-D401-4848-872E-9F9C7CACBF4C} 45 | EndGlobalSection 46 | EndGlobal 47 | -------------------------------------------------------------------------------- /Revit2Gltf/AssetPropertyDescriptor.cs: -------------------------------------------------------------------------------- 1 | using Autodesk.Revit.DB; 2 | #if REVIT2019 || REVIT2020 3 | using Autodesk.Revit.DB.Visual; 4 | #else 5 | using Autodesk.Revit.Utility; 6 | #endif 7 | using System; 8 | using System.Collections.Generic; 9 | using System.ComponentModel; 10 | using System.Linq; 11 | using System.Text; 12 | using System.Threading.Tasks; 13 | 14 | 15 | 16 | namespace Revit2Gltf 17 | { 18 | /// 19 | /// A description of a property consists of a name, its attributes and value 20 | /// here AssetPropertyPropertyDescriptor is used to wrap AssetProperty 21 | /// to display its name and value in PropertyGrid 22 | /// 23 | internal class AssetPropertyPropertyDescriptor : PropertyDescriptor 24 | { 25 | #region Fields 26 | /// 27 | /// A reference to an AssetProperty 28 | /// 29 | private AssetProperty m_assetProperty; 30 | 31 | /// 32 | /// The type of AssetProperty's property "Value" 33 | /// m 34 | private Type m_valueType; 35 | 36 | /// 37 | /// The value of AssetProperty's property "Value" 38 | /// 39 | private Object m_value; 40 | #endregion 41 | 42 | #region Properties 43 | /// 44 | /// Property to get internal AssetProperty 45 | /// 46 | public AssetProperty AssetProperty 47 | { 48 | get { return m_assetProperty; } 49 | } 50 | #endregion 51 | 52 | #region override Properties 53 | /// 54 | /// Gets a value indicating whether this property is read-only 55 | /// 56 | public override bool IsReadOnly 57 | { 58 | get 59 | { 60 | return true; 61 | } 62 | } 63 | 64 | /// 65 | /// Gets the type of the component this property is bound to. 66 | /// 67 | public override Type ComponentType 68 | { 69 | get 70 | { 71 | return m_assetProperty.GetType(); 72 | } 73 | } 74 | 75 | /// 76 | /// Gets the type of the property. 77 | /// 78 | public override Type PropertyType 79 | { 80 | get 81 | { 82 | return m_valueType; 83 | } 84 | } 85 | #endregion 86 | 87 | /// 88 | /// Public class constructor 89 | /// 90 | /// the AssetProperty which a AssetPropertyPropertyDescriptor instance describes 91 | public AssetPropertyPropertyDescriptor(AssetProperty assetProperty) 92 | : base(assetProperty.Name, new Attribute[0]) 93 | { 94 | m_assetProperty = assetProperty; 95 | } 96 | 97 | #region override methods 98 | /// 99 | /// Compares this to another object to see if they are equivalent 100 | /// 101 | /// The object to compare to this AssetPropertyPropertyDescriptor. 102 | /// 103 | public override bool Equals(object obj) 104 | { 105 | AssetPropertyPropertyDescriptor other = obj as AssetPropertyPropertyDescriptor; 106 | return other != null && other.AssetProperty.Equals(m_assetProperty); 107 | } 108 | 109 | /// 110 | /// Returns the hash code for this object. 111 | /// Here override the method "Equals", so it is necessary to override GetHashCode too. 112 | /// 113 | /// 114 | public override int GetHashCode() 115 | { 116 | return m_assetProperty.GetHashCode(); 117 | } 118 | 119 | /// 120 | /// Resets the value for this property of the component to the default value. 121 | /// 122 | /// The component with the property value that is to be reset to the default value. 123 | public override void ResetValue(object component) 124 | { 125 | 126 | } 127 | 128 | /// 129 | /// Returns whether resetting an object changes its value. 130 | /// 131 | /// The component to test for reset capability. 132 | /// true if resetting the component changes its value; otherwise, false. 133 | public override bool CanResetValue(object component) 134 | { 135 | return false; 136 | } 137 | 138 | /// G 139 | /// Determines a value indicating whether the value of this property needs to be persisted. 140 | /// 141 | /// The component with the property to be examined for persistence. 142 | /// true if the property should be persisted; otherwise, false. 143 | public override bool ShouldSerializeValue(object component) 144 | { 145 | return false; 146 | } 147 | 148 | ///// 149 | ///// Gets the current value of the property on a component. 150 | ///// 151 | ///// The component with the property for which to retrieve the value. 152 | ///// The value of a property for a given component. 153 | //public override object GetValue(object component) 154 | //{ 155 | // Tuple typeAndValue = GetTypeAndValue(m_assetProperty, 0); 156 | // m_value = typeAndValue.Item2; 157 | // m_valueType = typeAndValue.Item1; 158 | 159 | // return m_value; 160 | //} 161 | 162 | //private static Tuple GetTypeAndValue(AssetProperty assetProperty, int level) 163 | //{ 164 | // Object theValue; 165 | // Type valueType; 166 | // //For each AssetProperty, it has different type and value 167 | // //must deal with it separately 168 | // try 169 | // { 170 | // if (assetProperty is AssetPropertyBoolean) 171 | // { 172 | // AssetPropertyBoolean property = assetProperty as AssetPropertyBoolean; 173 | // valueType = typeof(AssetPropertyBoolean); 174 | // theValue = property.Value; 175 | // } 176 | // else if (assetProperty is AssetPropertyDistance) 177 | // { 178 | // AssetPropertyDistance property = assetProperty as AssetPropertyDistance; 179 | // valueType = typeof(AssetPropertyDistance); 180 | // theValue = property.Value; 181 | // } 182 | // else if (assetProperty is AssetPropertyDouble) 183 | // { 184 | // AssetPropertyDouble property = assetProperty as AssetPropertyDouble; 185 | // valueType = typeof(AssetPropertyDouble); 186 | // theValue = property.Value; 187 | // } 188 | // else if (assetProperty is AssetPropertyDoubleArray2d) 189 | // { 190 | // //Default, it is supported by PropertyGrid to display Double [] 191 | // //Try to convert DoubleArray to Double [] 192 | // AssetPropertyDoubleArray2d property = assetProperty as AssetPropertyDoubleArray2d; 193 | // valueType = typeof(AssetPropertyDoubleArray2d); 194 | // theValue = GetSystemArrayAsString(property.Value); 195 | // } 196 | // else if (assetProperty is AssetPropertyDoubleArray3d) 197 | // { 198 | // AssetPropertyDoubleArray3d property = assetProperty as AssetPropertyDoubleArray3d; 199 | // valueType = typeof(AssetPropertyDoubleArray3d); 200 | // theValue = GetSystemArrayAsString(property.GetValueAsDoubles()); 201 | // } 202 | // else if (assetProperty is AssetPropertyDoubleArray4d) 203 | // { 204 | // AssetPropertyDoubleArray4d property = assetProperty as AssetPropertyDoubleArray4d; 205 | // valueType = typeof(AssetPropertyDoubleArray4d); 206 | // theValue = GetSystemArrayAsString(property.Value); 207 | // } 208 | // else if (assetProperty is AssetPropertyDoubleMatrix44) 209 | // { 210 | // AssetPropertyDoubleMatrix44 property = assetProperty as AssetPropertyDoubleMatrix44; 211 | // valueType = typeof(AssetPropertyDoubleMatrix44); 212 | // theValue = GetSystemArrayAsString(property.Value); 213 | // } 214 | // else if (assetProperty is AssetPropertyEnum) 215 | // { 216 | // AssetPropertyEnum property = assetProperty as AssetPropertyEnum; 217 | // valueType = typeof(AssetPropertyEnum); 218 | // theValue = property.Value; 219 | // } 220 | // else if (assetProperty is AssetPropertyFloat) 221 | // { 222 | // AssetPropertyFloat property = assetProperty as AssetPropertyFloat; 223 | // valueType = typeof(AssetPropertyFloat); 224 | // theValue = property.Value; 225 | // } 226 | // else if (assetProperty is AssetPropertyInteger) 227 | // { 228 | // AssetPropertyInteger property = assetProperty as AssetPropertyInteger; 229 | // valueType = typeof(AssetPropertyInteger); 230 | // theValue = property.Value; 231 | // } 232 | // else if (assetProperty is AssetPropertyReference) 233 | // { 234 | // AssetPropertyReference property = assetProperty as AssetPropertyReference; 235 | // valueType = typeof(AssetPropertyReference); 236 | // theValue = "REFERENCE"; //property.Type; 237 | // } 238 | // else if (assetProperty is AssetPropertyString) 239 | // { 240 | // AssetPropertyString property = assetProperty as AssetPropertyString; 241 | // valueType = typeof(AssetPropertyString); 242 | // theValue = property.Value; 243 | // } 244 | // else if (assetProperty is AssetPropertyTime) 245 | // { 246 | // AssetPropertyTime property = assetProperty as AssetPropertyTime; 247 | // valueType = typeof(AssetPropertyTime); 248 | // theValue = property.Value; 249 | // } 250 | // else 251 | // { 252 | // valueType = typeof(String); 253 | // theValue = "Unprocessed asset type: " + assetProperty.GetType().Name; 254 | // } 255 | 256 | // if (assetProperty.NumberOfConnectedProperties > 0) 257 | // { 258 | 259 | // String result = ""; 260 | // result = theValue.ToString(); 261 | 262 | // IList properties = assetProperty.GetAllConnectedProperties(); 263 | 264 | // foreach (AssetProperty property in properties) 265 | // { 266 | // if (property is Asset) 267 | // { 268 | // // Nested? 269 | // Asset asset = property as Asset; 270 | // int size = asset.Size; 271 | // for (int i = 0; i < size; i++) 272 | // { 273 | // AssetProperty subproperty = asset[i]; 274 | // Tuple valueAndType = GetTypeAndValue(subproperty, level + 1); 275 | // String indent = ""; 276 | // if (level > 0) 277 | // { 278 | // for (int iLevel = 1; iLevel <= level; iLevel++) 279 | // indent += " "; 280 | // } 281 | // result += "\n " + indent + "- connected: name: " + subproperty.Name + " | type: " + valueAndType.Item1.Name + 282 | // " | value: " + valueAndType.Item2.ToString(); 283 | // } 284 | // } 285 | // } 286 | 287 | // theValue = result; 288 | // } 289 | // } 290 | // catch 291 | // { 292 | // return null; 293 | // } 294 | // return new Tuple(valueType, theValue); 295 | //} 296 | 297 | /// 298 | /// Sets the value of the component to a different value. 299 | /// For AssetProperty, it is not allowed to set its value, so here just return. 300 | /// 301 | /// The component with the property value that is to be set. 302 | /// The new value. 303 | public override void SetValue(object component, object value) 304 | { 305 | return; 306 | } 307 | #endregion 308 | 309 | /// 310 | /// Convert Autodesk.Revit.DB.DoubleArray to Double []. 311 | /// For Double [] is supported by PropertyGrid. 312 | /// 313 | /// the original Autodesk.Revit.DB.DoubleArray 314 | /// The converted Double [] 315 | private static Double[] GetSystemArray(DoubleArray doubleArray) 316 | { 317 | double[] values = new double[doubleArray.Size]; 318 | int index = 0; 319 | foreach (Double value in doubleArray) 320 | { 321 | values[index++] = value; 322 | } 323 | return values; 324 | } 325 | 326 | private static String GetSystemArrayAsString(DoubleArray doubleArray) 327 | { 328 | double[] values = GetSystemArray(doubleArray); 329 | 330 | String result = ""; 331 | foreach (double d in values) 332 | { 333 | result += d; 334 | result += ","; 335 | } 336 | 337 | return result; 338 | } 339 | 340 | public override string ToString() 341 | { 342 | return base.Name; 343 | } 344 | 345 | public override object GetValue(object component) 346 | { 347 | //throw new NotImplementedException(); 348 | return null; 349 | } 350 | } 351 | } 352 | -------------------------------------------------------------------------------- /Revit2Gltf/AssimpUtilities.cs: -------------------------------------------------------------------------------- 1 | using Assimp; 2 | using Assimp.Configs; 3 | using Autodesk.Revit.DB; 4 | #if REVIT2017 || REVIT2016 || REVIT2015 5 | using Autodesk.Revit.Utility; 6 | #endif 7 | using System; 8 | using System.Collections.Generic; 9 | using System.ComponentModel; 10 | using System.IO; 11 | using System.Linq; 12 | using System.Text; 13 | using System.Threading.Tasks; 14 | using Material = Assimp.Material; 15 | using RevitMaterial = Autodesk.Revit.DB.Material; 16 | 17 | #if REVIT2018 || REVIT2019 || REVIT2020 18 | using Autodesk.Revit.DB.Visual; 19 | using Revit2Gltf; 20 | #endif 21 | 22 | namespace Revit2Gltf 23 | { 24 | public static class AssimpUtilities 25 | { 26 | public const string UnknownObjectName = "Unknown Revit Object"; 27 | 28 | 29 | public static float GetOpacity(this RevitMaterial mat) 30 | { 31 | return (Math.Abs(mat.Transparency - 100) * 1f / 100f); 32 | } 33 | 34 | public static Color4D ToColor4D(this RevitMaterial mat) 35 | { 36 | var color = mat.Color.IsValid ? mat.Color : new Color(0, 0, 0); 37 | return new Color4D(color.Red * 1f / 255f, color.Green * 1f / 255f, color.Blue * 1f / 255f, mat.GetOpacity()); 38 | } 39 | 40 | public static Color4D ColorFromAssetDoubleArray4d(AssetPropertyDoubleArray4d prop) 41 | { 42 | #if REVIT2019 || REVIT2020 43 | var colorValues = prop.GetValueAsDoubles()?.ToList() ?? new List(); 44 | if (colorValues != null && colorValues.Count > 3) 45 | { 46 | return new Color4D( 47 | (float)colorValues[0], 48 | (float)colorValues[1], 49 | (float)colorValues[2], 50 | (float)colorValues[3]); 51 | } 52 | else 53 | { 54 | // Default return if invalid 55 | return new Color4D(200); 56 | } 57 | #else 58 | return new Color4D( 59 | (float)prop.Value.get_Item(0), 60 | (float)prop.Value.get_Item(1), 61 | (float)prop.Value.get_Item(2), 62 | (float)prop.Value.get_Item(3)); 63 | #endif 64 | } 65 | 66 | public static T GetAssetProperty(this Asset a, string key) where T : AssetProperty 67 | { 68 | #if REVIT2019 || REVIT2020 69 | return a.FindByName(key) as T; 70 | #else 71 | return a[key] as T; 72 | #endif 73 | } 74 | 75 | public static Assimp.Material ConvertToAssimpMaterial(TextureBundle bundle, Document doc) 76 | { 77 | // Create new material with base props 78 | // from the Revit material 79 | var newmat = new Assimp.Material() 80 | { 81 | Opacity = bundle.Material.GetOpacity(), 82 | Reflectivity = 0f, 83 | Name = bundle.Material.Name, 84 | ColorDiffuse = bundle.Material.ToColor4D() 85 | }; 86 | 87 | // Extract base properties from revit material 88 | ElementId appearanceAssetId = bundle.Material.AppearanceAssetId; 89 | AppearanceAssetElement appearanceAsset = doc.GetElement(appearanceAssetId) as AppearanceAssetElement; 90 | Asset renderingAsset = appearanceAsset.GetRenderingAsset(); 91 | RenderAppearanceDescriptor rad 92 | = new RenderAppearanceDescriptor(renderingAsset); 93 | PropertyDescriptorCollection collection = rad.GetProperties(); 94 | List orderableCollection = new List(collection.Count); 95 | 96 | List allPropNames = orderableCollection.Select(f => f.Name).ToList(); 97 | 98 | foreach (PropertyDescriptor descr in collection) 99 | { 100 | orderableCollection.Add(descr); 101 | switch (descr.Name) 102 | { 103 | #region Notes 104 | 105 | // The commented out properties aren't in use yet, 106 | // but do work with revit materials as expected. 107 | 108 | //case "texture_UScale": 109 | // var uScale = renderingAsset["texture_UScale"] as AssetPropertyDouble; 110 | // break; 111 | //case "texture_VScale": 112 | // break; 113 | //case "texture_UOffset": 114 | // break; 115 | //case "texture_VOffset": 116 | // break; 117 | //case "texture_RealWorldScaleX": 118 | // var xScale = renderingAsset["texture_RealWorldScaleX"] as AssetPropertyDistance; 119 | // break; 120 | //case "texture_RealWorldScaleY": 121 | // break; 122 | 123 | #endregion 124 | 125 | case "generic_diffuse": 126 | var prop = renderingAsset.GetAssetProperty("generic_diffuse"); 127 | newmat.ColorDiffuse = ColorFromAssetDoubleArray4d(prop); 128 | break; 129 | case "glazing_reflectance": 130 | // This is glass, so we should reduce the transparency. 131 | var refl = renderingAsset.GetAssetProperty("glazing_reflectance"); 132 | if (refl == null) 133 | { 134 | var reflFloat = renderingAsset.GetAssetProperty("glazing_reflectance"); 135 | newmat.Reflectivity = reflFloat?.Value ?? 0f; 136 | } 137 | else 138 | { 139 | newmat.Reflectivity = (float)refl.Value; 140 | } 141 | newmat.Opacity = Math.Abs(0f - newmat.Reflectivity); 142 | break; 143 | case "common_Tint_color": 144 | // Tint shouldn't be used if generic diffuse is set 145 | if ( 146 | renderingAsset.GetAssetProperty("generic_diffuse") != null 147 | ) { continue; } 148 | var tintProp = renderingAsset.GetAssetProperty("common_Tint_color"); 149 | newmat.ColorDiffuse = ColorFromAssetDoubleArray4d(tintProp); 150 | break; 151 | default: 152 | break; 153 | } 154 | } 155 | 156 | // Set textures 157 | foreach (var tx in bundle.TexturePaths) 158 | { 159 | // Get the filename 160 | var txFileName = tx.Value.SafeFileName; 161 | if (tx.Key == RevitTextureType.Color) 162 | { 163 | newmat.TextureDiffuse = new TextureSlot( 164 | $"Textures/{txFileName}", 165 | TextureType.Diffuse, 166 | 0, // Texture index in the material 167 | TextureMapping.Box, 168 | 0, // 169 | 0.5f, // Blend mode 170 | TextureOperation.Add, 171 | TextureWrapMode.Clamp, 172 | TextureWrapMode.Clamp, 173 | 0 // Flags, 174 | ); 175 | } 176 | else if (tx.Key == RevitTextureType.Bump) 177 | { 178 | newmat.TextureHeight = new TextureSlot( 179 | $"Textures/{txFileName}", 180 | TextureType.Diffuse, 181 | 0, // Texture index in the material 182 | TextureMapping.Box, 183 | 0, // 184 | 0.5f, // Blend mode 185 | TextureOperation.Add, 186 | TextureWrapMode.Clamp, 187 | TextureWrapMode.Clamp, 188 | 0 // Flags, 189 | ); 190 | } 191 | } 192 | return newmat; 193 | } 194 | 195 | public static void AddAndAssignMaterial( 196 | Scene model, 197 | Assimp.Material mat, 198 | HashSet uniqueIdsOfElementsWithMat, 199 | out bool utilized 200 | ) 201 | { 202 | utilized = false; 203 | 204 | // If no elements in the model use this material, 205 | // don't bother. 206 | if (uniqueIdsOfElementsWithMat.Count == 0) { return; } 207 | 208 | int matIndex = model.MaterialCount; 209 | foreach (var mesh in model.Meshes) 210 | { 211 | if (uniqueIdsOfElementsWithMat.Contains(mesh.Name)) 212 | { 213 | mesh.MaterialIndex = matIndex; 214 | utilized = true; 215 | } 216 | } 217 | if (utilized) 218 | { 219 | //Logger.LogInfo($"Adding material {mat.Name} to gltf."); 220 | model.Materials.Add(mat); 221 | } 222 | else 223 | { 224 | //Logger.LogInfo($"Won't add material {mat.Name} to gltf " + 225 | // $"because no objects utilize it."); 226 | } 227 | } 228 | 229 | public static void ReplaceNamesWithUniqueIds(Scene model, Dictionary localToUniqueIdMap) 230 | { 231 | foreach (var node in model.RootNode.GetNodes()) 232 | { 233 | node.Name = GetUniqueIdFromNodeName(node.Name); 234 | } 235 | foreach (var mesh in model.Meshes) 236 | { 237 | mesh.Name = GetUniqueIdFromNodeName(mesh.Name); 238 | } 239 | 240 | string GetUniqueIdFromNodeName(string nodeName) 241 | { 242 | // Fragment the node name to get the local id of the element 243 | // in the brackets 244 | string[] fragments = nodeName.Split(new char[] { '[', ']' }); 245 | 246 | string result = fragments.Length > 1 ? 247 | localToUniqueIdMap.ContainsKey(fragments[1]) ? 248 | localToUniqueIdMap[fragments[1]] : 249 | fragments[1] 250 | : 251 | fragments.FirstOrDefault() ?? UnknownObjectName; 252 | 253 | return result; 254 | } 255 | } 256 | 257 | 258 | public static IEnumerable GetNodes(this Node root) 259 | { 260 | if (!root.HasChildren) { yield break; } 261 | foreach (var childNode in root.Children) 262 | { 263 | yield return childNode; 264 | if (childNode.HasChildren) 265 | { 266 | foreach (var childChildNode in GetNodes(childNode)) 267 | { 268 | yield return childChildNode; 269 | } 270 | } 271 | } 272 | yield break; 273 | } 274 | 275 | public static Scene LoadFbx(string fileName) 276 | { 277 | //Create a new importer 278 | using (var importer = new AssimpContext()) 279 | { 280 | //This is how we add a configuration (each config is its own class) 281 | NormalSmoothingAngleConfig config = new NormalSmoothingAngleConfig(66.0f); 282 | importer.SetConfig(config); 283 | 284 | //This is how we add a logging callback 285 | LogStream logstream = new LogStream(delegate (String msg, String userData) { 286 | Console.WriteLine(msg); 287 | }); 288 | logstream.Attach(); 289 | 290 | //Import the model. All configs are set. The model 291 | //is imported, loaded into managed memory. Then the unmanaged memory is released, and everything is reset. 292 | Scene model = importer.ImportFile(fileName, PostProcessPreset.TargetRealTimeMaximumQuality); 293 | 294 | return model; 295 | } 296 | } 297 | 298 | public static bool TryRemoveFile(string fileName, TimeSpan pause, int tries = 5) 299 | { 300 | if (pause == default(TimeSpan)) 301 | { 302 | // Default to 1 second 303 | pause = TimeSpan.FromSeconds(1); 304 | } 305 | while (tries > 0) 306 | { 307 | try 308 | { 309 | File.Delete(fileName); 310 | return true; 311 | } 312 | catch (Exception e) 313 | { 314 | //Logger.LogException("TryRemoveFile failed: ", e); 315 | tries--; 316 | } 317 | } 318 | return false; 319 | } 320 | 321 | public static bool SaveToGltf(this Scene model, string path, string fileName) 322 | { 323 | try 324 | { 325 | using (var importer = new AssimpContext()) 326 | { 327 | string outputFilePath = $"{path}/{fileName}.gltf"; 328 | if (importer.ExportFile(model, outputFilePath, "gltf2")) 329 | { 330 | // Replace the buffer path to a relative one. 331 | string gltf = File.ReadAllText(outputFilePath); 332 | gltf = gltf.Replace(path + '/', ""); 333 | File.WriteAllText(outputFilePath, gltf); 334 | return true; 335 | } 336 | else 337 | { 338 | return false; 339 | } 340 | } 341 | } 342 | catch (Exception e) 343 | { 344 | //Logger.LogException("Error in saving gltf: ", e); 345 | return false; 346 | } 347 | 348 | } 349 | } 350 | } 351 | -------------------------------------------------------------------------------- /Revit2Gltf/CentroidVolume.cs: -------------------------------------------------------------------------------- 1 | using Autodesk.Revit.DB; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Revit2Gltf 9 | { 10 | public class CentroidVolume 11 | { 12 | public const int CoordinateRoundingDecimals = 3; 13 | public const int VolumeRoundingDecimals = 3; 14 | 15 | public string Centroid_Str => 16 | $"{Math.Round(Centroid.X, CoordinateRoundingDecimals)}," + 17 | $"{Math.Round(Centroid.Y, CoordinateRoundingDecimals)}," + 18 | $"{Math.Round(Centroid.Z, CoordinateRoundingDecimals)}"; 19 | public string Volume_Str => Math.Round(Volume, VolumeRoundingDecimals).ToString(); 20 | 21 | public XYZ Centroid { get; set; } = XYZ.Zero; 22 | public double Volume { get; set; } = 0; 23 | 24 | public CentroidVolume() { } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Revit2Gltf/Configuration.cs: -------------------------------------------------------------------------------- 1 | using Autodesk.Revit.DB; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Revit2Gltf 9 | { 10 | /// 11 | /// Configures the export. 12 | /// 13 | public class Configuration 14 | { 15 | /// 16 | /// Categories of Revit elements which will 17 | /// be exported. 18 | /// 19 | public List ExportedCategories; 20 | 21 | /// 22 | /// Name applied to elements whose name cannot 23 | /// be ascertained. 24 | /// 25 | public string UnknownObjectName; 26 | 27 | /// 28 | /// Where the gltf files will inevitably be exported to. 29 | /// 30 | public string ExportFilePathRoot = "C:/Cityzenith"; 31 | 32 | 33 | public Configuration() 34 | { 35 | 36 | } 37 | 38 | public static Configuration Default = new Configuration() 39 | { 40 | UnknownObjectName = "Unknown Revit Object", 41 | ExportedCategories = new List() 42 | { 43 | BuiltInCategory.OST_Walls, 44 | BuiltInCategory.OST_Floors, 45 | BuiltInCategory.OST_Doors, 46 | BuiltInCategory.OST_Windows, 47 | BuiltInCategory.OST_CurtainWallMullions, 48 | BuiltInCategory.OST_CurtainWallPanels, 49 | BuiltInCategory.OST_EdgeSlab, 50 | BuiltInCategory.OST_Rooms, 51 | BuiltInCategory.OST_Ceilings, 52 | BuiltInCategory.OST_Furniture, 53 | BuiltInCategory.OST_FurnitureSystems, 54 | BuiltInCategory.OST_Assemblies, 55 | BuiltInCategory.OST_Columns, 56 | BuiltInCategory.OST_Casework, 57 | BuiltInCategory.OST_Site, 58 | BuiltInCategory.OST_Stairs, 59 | BuiltInCategory.OST_StructuralColumns, 60 | BuiltInCategory.OST_StructuralFraming, 61 | BuiltInCategory.OST_Views, 62 | BuiltInCategory.OST_Truss 63 | } 64 | }; 65 | } 66 | 67 | /// 68 | /// Light singleton posessing the active 69 | /// configuration of the exporter. 70 | /// 71 | public static class ConfigurationManager 72 | { 73 | public static Configuration ActiveConfiguration = Configuration.Default; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Revit2Gltf/Enums.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Revit2Gltf 8 | { 9 | public enum RevitTextureType 10 | { 11 | Unknown = -1, 12 | Color, 13 | Bump 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Revit2Gltf/Extensions.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.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace Revit2Gltf 10 | { 11 | public static class Extensions 12 | { 13 | public static Dictionary ToFilenamePathDictionarySafe(this IEnumerable fullPaths) 14 | { 15 | Dictionary output = new Dictionary(); 16 | foreach(var p in fullPaths) 17 | { 18 | var fn = Path.GetFileNameWithoutExtension(p); 19 | if (output.ContainsKey(fn)) { continue; } 20 | output.Add(fn, p); 21 | } 22 | return output; 23 | } 24 | 25 | public static void WriteToFile(this string file, string path) 26 | { 27 | System.IO.File.WriteAllText(path, file); 28 | } 29 | 30 | public static Dictionary> Combine(this IEnumerable>> set) 31 | { 32 | var output = new Dictionary>(); 33 | 34 | foreach(var d in set) 35 | { 36 | foreach(var kv in d) 37 | { 38 | if (!output.ContainsKey(kv.Key)) 39 | { 40 | output.Add(kv.Key, kv.Value); 41 | } 42 | } 43 | 44 | } 45 | 46 | return output; 47 | } 48 | 49 | public static FilteredElementCollector ExportableElements(this Document doc) 50 | { 51 | var catsToExport = 52 | ConfigurationManager.ActiveConfiguration.ExportedCategories; 53 | 54 | List ids 55 | = new List(catsToExport) 56 | .ConvertAll(c 57 | => new ElementId((int)c)); 58 | 59 | FilterCategoryRule r 60 | = new FilterCategoryRule(ids); 61 | 62 | ElementParameterFilter f 63 | = new ElementParameterFilter(r, true); 64 | 65 | // Use a logical OR of category filters 66 | 67 | IList a 68 | = new List(catsToExport.Count); 69 | 70 | foreach (BuiltInCategory bic in catsToExport) 71 | { 72 | a.Add(new ElementCategoryFilter(bic)); 73 | } 74 | 75 | LogicalOrFilter categoryFilter 76 | = new LogicalOrFilter(a); 77 | 78 | // Run the collector 79 | 80 | FilteredElementCollector els 81 | = new FilteredElementCollector(doc) 82 | .WhereElementIsNotElementType() 83 | .WhereElementIsViewIndependent() 84 | .WherePasses(categoryFilter); 85 | 86 | return els; 87 | } 88 | 89 | static Options DefaultExportGeometryOptions = new Options() 90 | { 91 | DetailLevel = ViewDetailLevel.Medium, 92 | IncludeNonVisibleObjects = true 93 | }; 94 | 95 | /// 96 | /// Return parameter data for all 97 | /// elements of all the given categories 98 | /// 99 | public static Dictionary>> 100 | SiphonElementParamValues( 101 | this Document doc, 102 | out Dictionary legend, 103 | bool calculateCentroids = true, 104 | List cats = null) 105 | { 106 | if(cats == null) 107 | { 108 | cats = ConfigurationManager.ActiveConfiguration.ExportedCategories; 109 | } 110 | 111 | // Set up the return value dictionary 112 | var map_cat_to_uid_to_param_values 113 | = new Dictionary>>(); 116 | 117 | // One top level dictionary per category 118 | foreach (BuiltInCategory cat in cats) 119 | { 120 | map_cat_to_uid_to_param_values.Add( 121 | cat.Description(), 122 | new Dictionary>()); 124 | } 125 | 126 | // Collect all required elements 127 | var els = doc.ExportableElements(); 128 | 129 | // Retrieve parameter data for each element 130 | var legendReversed = new Dictionary(); 131 | legendReversed.Add("Category", 0); 132 | if (calculateCentroids) 133 | { 134 | legendReversed.Add("Centroid", 1); 135 | } 136 | 137 | foreach (Element e in els) 138 | { 139 | Category cat = e.Category; 140 | if (null == cat) 141 | { 142 | continue; 143 | } 144 | Dictionary param_values = GetParamValues(e, ref legendReversed); 145 | 146 | if (calculateCentroids && 147 | e.TryGetCentroid(DefaultExportGeometryOptions, out var c)) 148 | { 149 | param_values.Add("1", c.Centroid_Str); 150 | } 151 | 152 | BuiltInCategory bic = (BuiltInCategory) 153 | (e.Category.Id.IntegerValue); 154 | 155 | string catkey = bic.Description(); 156 | string uniqueId = e.UniqueId.ToString(); 157 | 158 | map_cat_to_uid_to_param_values[catkey].Add( 159 | uniqueId, param_values); 160 | } 161 | 162 | legend = legendReversed.ToDictionary(kv => kv.Value, kv => kv.Key); 163 | 164 | 165 | return map_cat_to_uid_to_param_values; 166 | } 167 | 168 | public static string Description( 169 | this BuiltInCategory bic) 170 | { 171 | //string s = bic.ToString().ToLower(); 172 | //s = s.Substring(4); 173 | //s = s.Substring(0, s.Length - 1); 174 | //return s; 175 | return bic.ToString().ToLower(); 176 | } 177 | 178 | /// 179 | /// Return all the parameter values 180 | /// deemed relevant for the given element 181 | /// in string form. 182 | /// 183 | static Dictionary GetParamValues(Element e, ref Dictionary paramLegend) 184 | { 185 | // Two choices: 186 | // Element.Parameters property -- Retrieves 187 | // a set containing all the parameters. 188 | // GetOrderedParameters method -- Gets the 189 | // visible parameters in order. 190 | 191 | IList ps = e.GetOrderedParameters(); 192 | 193 | List param_values = new List( 194 | ps.Count); 195 | 196 | 197 | var paramList = 198 | ps 199 | .Where(p => p.HasValue && !String.IsNullOrEmpty(p.AsValueString())); 200 | Dictionary output = new Dictionary(); 201 | output.Add("0", e.Category.Name); 202 | 203 | foreach (var p in paramList) 204 | { 205 | var key = GetLegendValue(p,ref paramLegend).ToString(); 206 | if (!output.ContainsKey(key)) 207 | { 208 | output.Add(key, p.AsValueString()); 209 | } 210 | } 211 | return output; 212 | 213 | int GetLegendValue(Parameter p, ref Dictionary lgd) 214 | { 215 | string n = p.Definition.Name; 216 | if (!lgd.ContainsKey(n)) 217 | { 218 | int c = lgd.Count; 219 | lgd.Add(n, c); 220 | return c; 221 | } 222 | return lgd[n]; 223 | } 224 | 225 | } 226 | 227 | public static View3D GetOrCreateDefault3DView(this Document doc) 228 | { 229 | var view3d = new FilteredElementCollector(doc) 230 | .OfClass(typeof(View3D)) 231 | .Cast() 232 | .Where(v => v.Name.ToUpper() == "{3D}") 233 | .FirstOrDefault(); 234 | 235 | if (view3d == null) 236 | { 237 | // Create a new 3d view (rare) 238 | // Grab the view family type. 239 | var vft = new FilteredElementCollector(doc) 240 | .OfClass(typeof(ViewFamilyType)) 241 | .Cast() 242 | .Where(v => v.ViewFamily == ViewFamily.ThreeDimensional) 243 | .FirstOrDefault(); 244 | 245 | using (var tr = new Transaction(doc)) 246 | { 247 | tr.Start("Create 3D view for export to gltf"); 248 | try 249 | { 250 | view3d = View3D.CreateIsometric(doc, vft.Id); 251 | tr.Commit(); 252 | } 253 | catch (Exception e) 254 | { 255 | tr.RollBack(); 256 | } 257 | } 258 | } 259 | 260 | return view3d; 261 | } 262 | } 263 | } 264 | -------------------------------------------------------------------------------- /Revit2Gltf/GeometryExtensions.cs: -------------------------------------------------------------------------------- 1 | using Autodesk.Revit.DB; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Revit2Gltf 9 | { 10 | public static class GeometryExtensions 11 | { 12 | public static bool TryGetCentroidVolume(this Solid s, out CentroidVolume cv) 13 | { 14 | cv = null; 15 | try 16 | { 17 | if (null != s 18 | && 0 < s.Faces.Size 19 | && SolidUtils.IsValidForTessellation(s) 20 | && (null != (cv = new CentroidVolume() 21 | { 22 | Centroid = s.ComputeCentroid(), 23 | Volume = s.Volume 24 | } 25 | ))) 26 | { 27 | return true; 28 | } 29 | } 30 | catch (Exception e) 31 | { 32 | } 33 | return false; 34 | } 35 | 36 | /// 37 | /// Calculate centroid for all non-empty solids 38 | /// found for the given element. Family instances 39 | /// may have their own non-empty solids, in which 40 | /// case those are used, otherwise the symbol geometry. 41 | /// The symbol geometry could keep track of the 42 | /// instance transform to map it to the actual 43 | /// project location. Instead, we ask for 44 | /// transformed geometry to be returned, so the 45 | /// resulting solids are already in place. 46 | /// 47 | public static bool TryGetCentroid( 48 | this Element e, 49 | Options opt, 50 | out CentroidVolume cvol) 51 | { 52 | cvol = null; 53 | GeometryElement geo = e.get_Geometry(opt); 54 | CentroidVolume combined = new CentroidVolume(); 55 | 56 | if (null == geo) { return false; } 57 | 58 | // List of pairs of centroid, volume for each solid 59 | 60 | List a 61 | = new List(); 62 | 63 | if (e is FamilyInstance) 64 | { 65 | geo = geo.GetTransformed( 66 | Transform.Identity); 67 | } 68 | 69 | GeometryInstance inst = null; 70 | 71 | foreach (GeometryObject obj in geo) 72 | { 73 | if (!TryGetCentroidVolume(obj as Solid, out var cv)) { continue; } 74 | a.Add(cv); 75 | inst = obj as GeometryInstance; 76 | } 77 | 78 | if (0 == a.Count && null != inst) 79 | { 80 | geo = inst.GetSymbolGeometry(); 81 | 82 | foreach (GeometryObject obj in geo) 83 | { 84 | if (!TryGetCentroidVolume(obj as Solid, out var cv)) { continue; } 85 | a.Add(cv); 86 | } 87 | } 88 | 89 | // Get the total centroid from the partial 90 | // contributions. Each contribution is weighted 91 | // with its associated volume, which needs to 92 | // be factored out again at the end. 93 | 94 | try 95 | { 96 | if (0 < a.Count) 97 | { 98 | combined = new CentroidVolume(); 99 | bool unweighted = false; 100 | 101 | // Revit may give us volumes of 0. 102 | // In which case we will not do a 103 | // weighted calculation, which will 104 | // throw a divide by zero exception. 105 | foreach (CentroidVolume cv2 in a) 106 | { 107 | if (cv2.Volume == 0) 108 | { 109 | unweighted = true; 110 | break; 111 | } 112 | } 113 | if (unweighted) 114 | { 115 | foreach (var cv2 in a) 116 | { 117 | combined.Centroid += cv2.Centroid; 118 | } 119 | combined.Centroid /= a.Count; 120 | } 121 | else 122 | { 123 | foreach (var cv2 in a) 124 | { 125 | combined.Centroid += cv2.Volume * cv2.Centroid; 126 | combined.Volume += cv2.Volume; 127 | } 128 | combined.Centroid /= (a.Count == 0 ? 1 : a.Count) * (combined.Volume == 0 ? 1 : combined.Volume); 129 | } 130 | } 131 | } catch(Exception ex) 132 | { 133 | combined = null; 134 | } 135 | cvol = combined; 136 | return combined != null && combined != default(CentroidVolume); 137 | } 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /Revit2Gltf/MaterialUtilities.cs: -------------------------------------------------------------------------------- 1 | using Autodesk.Revit.DB; 2 | using Autodesk.Revit.UI; 3 | #if REVIT2019 || REVIT2020 4 | using Autodesk.Revit.DB.Visual; 5 | #else 6 | using Autodesk.Revit.Utility; 7 | #endif 8 | using System; 9 | using System.Collections.Generic; 10 | using System.IO; 11 | using System.Linq; 12 | using System.Text; 13 | using System.Threading.Tasks; 14 | 15 | namespace Revit2Gltf 16 | { 17 | public static class MaterialUtilities 18 | { 19 | /// 20 | /// Keywords inside of asset attribute 21 | /// names which indicate what kind of 22 | /// texture they are. 23 | /// Thank you Revit 24 | /// for being such a pain in the ass. 25 | /// 26 | public static Dictionary> TextureTypeKeywords = 27 | new Dictionary>() 28 | { 29 | { 30 | RevitTextureType.Color, new List() 31 | { 32 | "color", 33 | "diffuse", 34 | "unifiedbitmapschema" 35 | } 36 | }, 37 | { 38 | RevitTextureType.Bump, new List() 39 | { 40 | "bm_map", 41 | "bump", 42 | "pattern_map" 43 | } 44 | } 45 | }; 46 | 47 | public static string TexturesPathRoot = 48 | $"{Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86)}" + 49 | @"\Common Files\Autodesk Shared\Materials\Textures".Replace("\\\\", "\\"); 50 | 51 | public static Dictionary RevitTextures = 52 | Directory.GetFiles(TexturesPathRoot, "*", SearchOption.AllDirectories) 53 | .ToFilenamePathDictionarySafe(); 54 | 55 | private static Dictionary> _bundleCache = 56 | new Dictionary>(); 57 | private static Dictionary> _texturePathCache = 58 | new Dictionary>(); 59 | 60 | public static bool TryGetTextureTypeFromAssetName(string assetName, out RevitTextureType t) 61 | { 62 | t = RevitTextureType.Unknown; 63 | string lowerCaseName = assetName.ToLower(); 64 | foreach (var kv in TextureTypeKeywords) 65 | { 66 | foreach (var val in kv.Value) 67 | { 68 | if (lowerCaseName.Contains(val)) 69 | { 70 | t = kv.Key; 71 | return true; 72 | } 73 | } 74 | } 75 | return false; 76 | } 77 | 78 | public static List GetTextureBundles(Document doc, out List paths) 79 | { 80 | 81 | if (_bundleCache.ContainsKey(doc.PathName)) 82 | { 83 | paths = _texturePathCache[doc.PathName]; 84 | return _bundleCache[doc.PathName]; 85 | } 86 | _texturePathCache.Add(doc.PathName, new List()); 87 | 88 | // Find materials 89 | FilteredElementCollector fec = new FilteredElementCollector(doc).OfClass(typeof(Material)); 90 | 91 | // Convert materials to bundles 92 | List bundles = new List(); 93 | foreach (var m in fec.Cast()) 94 | { 95 | try 96 | { 97 | var bundle = new TextureBundle(m); 98 | 99 | ElementId appearanceAssetId = m.AppearanceAssetId; 100 | AppearanceAssetElement appearanceAssetElem 101 | = doc.GetElement(appearanceAssetId) 102 | as AppearanceAssetElement; 103 | 104 | if (appearanceAssetElem == null) { continue; } 105 | 106 | Asset asset = appearanceAssetElem 107 | .GetRenderingAsset(); 108 | 109 | if (asset == null) { continue; } 110 | 111 | for (int assetIdx = 0; assetIdx < asset.Size; assetIdx++) 112 | { 113 | AssetProperty aProperty = asset[assetIdx]; 114 | if (aProperty.NumberOfConnectedProperties < 1) { continue; } 115 | 116 | Asset connectedAsset = aProperty 117 | .GetConnectedProperty(0) as Asset; 118 | 119 | // See if there is a path associated. 120 | #if REVIT2018 || REVIT2019 || REVIT2020 121 | // This line is 2018.1 & up because of the 122 | // property reference to UnifiedBitmap 123 | // .UnifiedbitmapBitmap. In earlier versions, 124 | // you can still reference the string name 125 | // instead: "unifiedbitmap_Bitmap" 126 | AssetPropertyString path = connectedAsset.FindByName( 127 | UnifiedBitmap.UnifiedbitmapBitmap) 128 | as AssetPropertyString; 129 | #else 130 | AssetPropertyString path = 131 | connectedAsset["unifiedbitmap_Bitmap"] as AssetPropertyString; 132 | #endif 133 | // If there is no asset path, nothing to pursue (Empty field) 134 | if (path == null || String.IsNullOrEmpty(path.Value)) { continue; } 135 | 136 | // See what kind of texture it is. 137 | if (TryGetTextureTypeFromAssetName(connectedAsset.Name, out var t)) 138 | { 139 | // This will be a relative path to the 140 | // built -in materials folder, addiitonal 141 | // render appearance folder, or an 142 | // absolute path. 143 | string assetName = Path.GetFileNameWithoutExtension(path.Value); 144 | 145 | // Ensure that we have a valid texture path. 146 | if (RevitTextures.ContainsKey(assetName)) 147 | { 148 | bundle.TexturePaths.Add(t, new SafenedFilename(RevitTextures[assetName])); 149 | } 150 | else 151 | { 152 | //Logger.LogError( 153 | // $"Found asset outisde of Revit material lib: {path.Value}. Could not add to export" 154 | // ); 155 | } 156 | } 157 | } 158 | 159 | // Return the bundle we created. 160 | bundles.Add(bundle); 161 | } 162 | catch (Exception e) 163 | { 164 | //Logger.LogException("Error in bundle creation: ", e); 165 | } 166 | } 167 | 168 | var bundleList = bundles.Where(b => b != null).ToList(); 169 | _bundleCache.Add(doc.PathName, bundleList); 170 | paths = _texturePathCache[doc.PathName]; 171 | 172 | return bundleList; 173 | } 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /Revit2Gltf/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 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("Revit2Gltf")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Revit2Gltf")] 13 | [assembly: AssemblyCopyright("Copyright © 2021")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("ad5ae38a-3bde-4fb0-88f6-1f4133566ab5")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /Revit2Gltf/RenderAppearanceDescriptor.cs: -------------------------------------------------------------------------------- 1 | #if REVIT2019 || REVIT2020 2 | using Autodesk.Revit.DB.Visual; 3 | #else 4 | using Autodesk.Revit.Utility; 5 | #endif 6 | using System; 7 | using System.Collections.Generic; 8 | using System.ComponentModel; 9 | using System.Linq; 10 | using System.Text; 11 | using System.Threading.Tasks; 12 | 13 | namespace Revit2Gltf 14 | { 15 | /// 16 | /// supplies dynamic custom type information for an Asset while it is displayed in PropertyGrid. 17 | /// 18 | internal class RenderAppearanceDescriptor : ICustomTypeDescriptor 19 | { 20 | #region Fields 21 | /// 22 | /// Reference to Asset 23 | /// 24 | Asset m_asset; 25 | 26 | /// 27 | /// Asset's property descriptors 28 | /// 29 | PropertyDescriptorCollection m_propertyDescriptors; 30 | #endregion 31 | 32 | #region Constructors 33 | /// 34 | /// Initializes Asset object 35 | /// 36 | /// an Asset object 37 | public RenderAppearanceDescriptor(Asset asset) 38 | { 39 | m_asset = asset; 40 | GetAssetProperties(); 41 | } 42 | 43 | #endregion 44 | 45 | #region Methods 46 | #region ICustomTypeDescriptor Members 47 | 48 | /// 49 | /// Returns a collection of custom attributes for this instance of Asset. 50 | /// 51 | /// Asset's attributes 52 | public AttributeCollection GetAttributes() 53 | { 54 | return TypeDescriptor.GetAttributes(m_asset, false); 55 | } 56 | 57 | /// 58 | /// Returns the class name of this instance of Asset. 59 | /// 60 | /// Asset's class name 61 | public string GetClassName() 62 | { 63 | return TypeDescriptor.GetClassName(m_asset, false); 64 | } 65 | 66 | /// 67 | /// Returns the name of this instance of Asset. 68 | /// 69 | /// The name of Asset 70 | public string GetComponentName() 71 | { 72 | return TypeDescriptor.GetComponentName(m_asset, false); 73 | } 74 | 75 | /// 76 | /// Returns a type converter for this instance of Asset. 77 | /// 78 | /// The converter of the Asset 79 | public TypeConverter GetConverter() 80 | { 81 | return TypeDescriptor.GetConverter(m_asset, false); 82 | } 83 | 84 | /// 85 | /// Returns the default event for this instance of Asset. 86 | /// 87 | /// An EventDescriptor that represents the default event for this object, 88 | /// or null if this object does not have events. 89 | public EventDescriptor GetDefaultEvent() 90 | { 91 | return TypeDescriptor.GetDefaultEvent(m_asset, false); 92 | } 93 | 94 | /// 95 | /// Returns the default property for this instance of Asset. 96 | /// 97 | /// A PropertyDescriptor that represents the default property for this object, 98 | /// or null if this object does not have properties. 99 | public PropertyDescriptor GetDefaultProperty() 100 | { 101 | return TypeDescriptor.GetDefaultProperty(m_asset, false); 102 | } 103 | 104 | /// 105 | /// Returns an editor of the specified type for this instance of Asset. 106 | /// 107 | /// A Type that represents the editor for this object. 108 | /// An Object of the specified type that is the editor for this object, 109 | /// or null if the editor cannot be found. 110 | public object GetEditor(Type editorBaseType) 111 | { 112 | return TypeDescriptor.GetEditor(m_asset, editorBaseType, false); 113 | } 114 | 115 | /// 116 | /// Returns the events for this instance of Asset using the specified attribute array as a filter. 117 | /// 118 | /// An array of type Attribute that is used as a filter. 119 | /// An EventDescriptorCollection that represents the filtered events for this Asset instance. 120 | public EventDescriptorCollection GetEvents(Attribute[] attributes) 121 | { 122 | return TypeDescriptor.GetEvents(m_asset, attributes, false); 123 | } 124 | 125 | /// 126 | /// Returns the events for this instance of Asset. 127 | /// 128 | /// An EventDescriptorCollection that represents the events for this Asset instance. 129 | public EventDescriptorCollection GetEvents() 130 | { 131 | return TypeDescriptor.GetEvents(m_asset, false); 132 | } 133 | 134 | /// 135 | /// Returns the properties for this instance of Asset using the attribute array as a filter. 136 | /// 137 | /// An array of type Attribute that is used as a filter. 138 | /// A PropertyDescriptorCollection that 139 | /// represents the filtered properties for this Asset instance. 140 | public PropertyDescriptorCollection GetProperties(Attribute[] attributes) 141 | { 142 | return m_propertyDescriptors; 143 | } 144 | 145 | /// 146 | /// Returns the properties for this instance of Asset. 147 | /// 148 | /// A PropertyDescriptorCollection that represents the properties 149 | /// for this Asset instance. 150 | public PropertyDescriptorCollection GetProperties() 151 | { 152 | return m_propertyDescriptors; 153 | } 154 | 155 | /// 156 | /// Returns an object that contains the property described by the specified property descriptor. 157 | /// 158 | /// A PropertyDescriptor that represents the property whose owner is to be found. 159 | /// Asset object 160 | public object GetPropertyOwner(PropertyDescriptor pd) 161 | { 162 | return m_asset; 163 | } 164 | #endregion 165 | 166 | /// 167 | /// Get Asset's property descriptors 168 | /// 169 | private void GetAssetProperties() 170 | { 171 | if (null == m_propertyDescriptors) 172 | { 173 | m_propertyDescriptors = new PropertyDescriptorCollection(new AssetPropertyPropertyDescriptor[0]); 174 | } 175 | else 176 | { 177 | return; 178 | } 179 | 180 | //For each AssetProperty in Asset, create an AssetPropertyPropertyDescriptor. 181 | //It means that each AssetProperty will be a property of Asset 182 | for (int index = 0; index < m_asset.Size; index++) 183 | { 184 | AssetProperty assetProperty = m_asset[index]; 185 | if (null != assetProperty) 186 | { 187 | AssetPropertyPropertyDescriptor assetPropertyPropertyDescriptor = new AssetPropertyPropertyDescriptor(assetProperty); 188 | m_propertyDescriptors.Add(assetPropertyPropertyDescriptor); 189 | } 190 | } 191 | } 192 | #endregion 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /Revit2Gltf/Resources/icons8-upload-to-cloud-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twinup/Revit2Gltf/b7c475aea4eead0c05816afe60a7eb184fca9c15/Revit2Gltf/Resources/icons8-upload-to-cloud-32.png -------------------------------------------------------------------------------- /Revit2Gltf/Revit2Gltf.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {AD5AE38A-3BDE-4FB0-88F6-1F4133566AB5} 8 | Library 9 | Properties 10 | Revit2Gltf 11 | Revit2Gltf 12 | v4.7.2 13 | 512 14 | true 15 | 16 | 17 | 18 | 19 | true 20 | full 21 | false 22 | bin\Debug\ 23 | TRACE;DEBUG;REVIT2016 24 | prompt 25 | 4 26 | 27 | 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE;REVIT2016 32 | prompt 33 | 4 34 | 35 | 36 | true 37 | bin\Debug2016\ 38 | TRACE;DEBUG;REVIT2016 39 | full 40 | AnyCPU 41 | 7.3 42 | prompt 43 | MinimumRecommendedRules.ruleset 44 | 45 | 46 | bin\Release2016\ 47 | TRACE;REVIT2016 48 | true 49 | pdbonly 50 | AnyCPU 51 | 7.3 52 | prompt 53 | MinimumRecommendedRules.ruleset 54 | 55 | 56 | true 57 | bin\Debug2019\ 58 | TRACE;DEBUG;REVIT2019 59 | full 60 | AnyCPU 61 | 7.3 62 | prompt 63 | MinimumRecommendedRules.ruleset 64 | 65 | 66 | 67 | ..\packages\AssimpNet.4.1.0\lib\net40\AssimpNet.dll 68 | 69 | 70 | ..\ThirdParty\API\2015\RevitAPI.dll 71 | False 72 | 73 | 74 | ..\ThirdParty\API\2015\RevitAPIIFC.dll 75 | False 76 | 77 | 78 | ..\ThirdParty\API\2015\RevitAPIUI.dll 79 | False 80 | 81 | 82 | ..\ThirdParty\API\2016\RevitAPI.dll 83 | False 84 | 85 | 86 | ..\ThirdParty\API\2016\RevitAPIIFC.dll 87 | False 88 | 89 | 90 | ..\ThirdParty\API\2016\RevitAPIUI.dll 91 | False 92 | 93 | 94 | ..\ThirdParty\API\2017\RevitAPI.dll 95 | False 96 | 97 | 98 | ..\ThirdParty\API\2017\RevitAPIIFC.dll 99 | False 100 | 101 | 102 | ..\ThirdParty\API\2017\RevitAPIUI.dll 103 | False 104 | 105 | 106 | ..\ThirdParty\API\2018\RevitAPI.dll 107 | False 108 | 109 | 110 | ..\ThirdParty\API\2018\RevitAPIIFC.dll 111 | False 112 | 113 | 114 | ..\ThirdParty\API\2018\RevitAPIUI.dll 115 | False 116 | 117 | 118 | ..\ThirdParty\API\2019\RevitAPI.dll 119 | False 120 | 121 | 122 | ..\ThirdParty\API\2019\RevitAPIIFC.dll 123 | False 124 | 125 | 126 | ..\ThirdParty\API\2019\RevitAPIUI.dll 127 | False 128 | 129 | 130 | ..\ThirdParty\API\2020\RevitAPI.dll 131 | False 132 | 133 | 134 | ..\ThirdParty\API\2020\RevitAPIIFC.dll 135 | False 136 | 137 | 138 | ..\ThirdParty\API\2020\RevitAPIUI.dll 139 | False 140 | 141 | 142 | ..\ThirdParty\Snappy\Snappy.NET.dll 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 175 | 176 | 177 | 178 | -------------------------------------------------------------------------------- /Revit2Gltf/SafenedFilename.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Text.RegularExpressions; 7 | using System.Threading.Tasks; 8 | 9 | namespace Revit2Gltf 10 | { 11 | /// 12 | /// Removes whitespace and special characters 13 | /// from a filename to make it safe for upload and 14 | /// access in storage 15 | /// 16 | public class SafenedFilename 17 | { 18 | 19 | private static readonly Regex sWhitespace = new Regex(@"\s+", RegexOptions.Compiled); 20 | private static readonly Regex sSpecialChars = new Regex("[^a-zA-Z0-9_.]+", RegexOptions.Compiled); 21 | 22 | public readonly string FileLocation; 23 | public readonly string SafeFileName; 24 | 25 | public SafenedFilename(string path) 26 | { 27 | FileLocation = path; 28 | SafeFileName = RemoveSpecialCharacters(Path.GetFileNameWithoutExtension(path), Path.GetExtension(path)); 29 | } 30 | 31 | static string RemoveSpecialCharacters(string fileName, string ext) 32 | { 33 | string output; 34 | output = sSpecialChars.Replace(fileName, ""); 35 | output = sWhitespace.Replace(output, ""); 36 | output = Path.GetFileNameWithoutExtension(output); 37 | output = output.Replace(".", "_") + ext; 38 | return output; 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Revit2Gltf/TextureBundle.cs: -------------------------------------------------------------------------------- 1 | using Autodesk.Revit.DB; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Revit2Gltf 9 | { 10 | /// 11 | /// Combines 12 | /// 13 | public class TextureBundle 14 | { 15 | /// 16 | /// Full paths to the textures associated with this material. 17 | /// 18 | public Dictionary TexturePaths 19 | = new Dictionary(); 20 | 21 | /// 22 | /// The material. 23 | /// 24 | public readonly Material Material; 25 | 26 | public TextureBundle(Material m) 27 | { 28 | Material = m; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Revit2Gltf/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ThirdParty/API/2015/RevitAPI.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twinup/Revit2Gltf/b7c475aea4eead0c05816afe60a7eb184fca9c15/ThirdParty/API/2015/RevitAPI.dll -------------------------------------------------------------------------------- /ThirdParty/API/2015/RevitAPIIFC.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twinup/Revit2Gltf/b7c475aea4eead0c05816afe60a7eb184fca9c15/ThirdParty/API/2015/RevitAPIIFC.dll -------------------------------------------------------------------------------- /ThirdParty/API/2015/RevitAPIUI.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twinup/Revit2Gltf/b7c475aea4eead0c05816afe60a7eb184fca9c15/ThirdParty/API/2015/RevitAPIUI.dll -------------------------------------------------------------------------------- /ThirdParty/API/2016/RevitAPI.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twinup/Revit2Gltf/b7c475aea4eead0c05816afe60a7eb184fca9c15/ThirdParty/API/2016/RevitAPI.dll -------------------------------------------------------------------------------- /ThirdParty/API/2016/RevitAPIIFC.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twinup/Revit2Gltf/b7c475aea4eead0c05816afe60a7eb184fca9c15/ThirdParty/API/2016/RevitAPIIFC.dll -------------------------------------------------------------------------------- /ThirdParty/API/2016/RevitAPIUI.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twinup/Revit2Gltf/b7c475aea4eead0c05816afe60a7eb184fca9c15/ThirdParty/API/2016/RevitAPIUI.dll -------------------------------------------------------------------------------- /ThirdParty/API/2017/RevitAPI.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twinup/Revit2Gltf/b7c475aea4eead0c05816afe60a7eb184fca9c15/ThirdParty/API/2017/RevitAPI.dll -------------------------------------------------------------------------------- /ThirdParty/API/2017/RevitAPIIFC.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twinup/Revit2Gltf/b7c475aea4eead0c05816afe60a7eb184fca9c15/ThirdParty/API/2017/RevitAPIIFC.dll -------------------------------------------------------------------------------- /ThirdParty/API/2017/RevitAPIUI.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twinup/Revit2Gltf/b7c475aea4eead0c05816afe60a7eb184fca9c15/ThirdParty/API/2017/RevitAPIUI.dll -------------------------------------------------------------------------------- /ThirdParty/API/2018/RevitAPI.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twinup/Revit2Gltf/b7c475aea4eead0c05816afe60a7eb184fca9c15/ThirdParty/API/2018/RevitAPI.dll -------------------------------------------------------------------------------- /ThirdParty/API/2018/RevitAPIIFC.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twinup/Revit2Gltf/b7c475aea4eead0c05816afe60a7eb184fca9c15/ThirdParty/API/2018/RevitAPIIFC.dll -------------------------------------------------------------------------------- /ThirdParty/API/2018/RevitAPIUI.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twinup/Revit2Gltf/b7c475aea4eead0c05816afe60a7eb184fca9c15/ThirdParty/API/2018/RevitAPIUI.dll -------------------------------------------------------------------------------- /ThirdParty/API/2019/RevitAPI.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twinup/Revit2Gltf/b7c475aea4eead0c05816afe60a7eb184fca9c15/ThirdParty/API/2019/RevitAPI.dll -------------------------------------------------------------------------------- /ThirdParty/API/2019/RevitAPIIFC.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twinup/Revit2Gltf/b7c475aea4eead0c05816afe60a7eb184fca9c15/ThirdParty/API/2019/RevitAPIIFC.dll -------------------------------------------------------------------------------- /ThirdParty/API/2019/RevitAPIUI.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twinup/Revit2Gltf/b7c475aea4eead0c05816afe60a7eb184fca9c15/ThirdParty/API/2019/RevitAPIUI.dll -------------------------------------------------------------------------------- /ThirdParty/API/2020/RevitAPI.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twinup/Revit2Gltf/b7c475aea4eead0c05816afe60a7eb184fca9c15/ThirdParty/API/2020/RevitAPI.dll -------------------------------------------------------------------------------- /ThirdParty/API/2020/RevitAPIIFC.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twinup/Revit2Gltf/b7c475aea4eead0c05816afe60a7eb184fca9c15/ThirdParty/API/2020/RevitAPIIFC.dll -------------------------------------------------------------------------------- /ThirdParty/API/2020/RevitAPIUI.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twinup/Revit2Gltf/b7c475aea4eead0c05816afe60a7eb184fca9c15/ThirdParty/API/2020/RevitAPIUI.dll -------------------------------------------------------------------------------- /ThirdParty/Snappy/Crc32C.NET.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twinup/Revit2Gltf/b7c475aea4eead0c05816afe60a7eb184fca9c15/ThirdParty/Snappy/Crc32C.NET.dll -------------------------------------------------------------------------------- /ThirdParty/Snappy/Snappy.NET.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twinup/Revit2Gltf/b7c475aea4eead0c05816afe60a7eb184fca9c15/ThirdParty/Snappy/Snappy.NET.dll --------------------------------------------------------------------------------