├── .gitignore ├── CustomExporterCollada.sln ├── CustomExporterCollada ├── Command.cs ├── CustomExporterCollada.addin ├── CustomExporterCollada.csproj ├── MyExportContext.cs └── Properties │ └── AssemblyInfo.cs ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | artifacts/ 46 | 47 | *_i.c 48 | *_p.c 49 | *_i.h 50 | *.ilk 51 | *.meta 52 | *.obj 53 | *.pch 54 | *.pdb 55 | *.pgc 56 | *.pgd 57 | *.rsp 58 | *.sbr 59 | *.tlb 60 | *.tli 61 | *.tlh 62 | *.tmp 63 | *.tmp_proj 64 | *.log 65 | *.vspscc 66 | *.vssscc 67 | .builds 68 | *.pidb 69 | *.svclog 70 | *.scc 71 | 72 | # Chutzpah Test files 73 | _Chutzpah* 74 | 75 | # Visual C++ cache files 76 | ipch/ 77 | *.aps 78 | *.ncb 79 | *.opendb 80 | *.opensdf 81 | *.sdf 82 | *.cachefile 83 | *.VC.db 84 | *.VC.VC.opendb 85 | 86 | # Visual Studio profiler 87 | *.psess 88 | *.vsp 89 | *.vspx 90 | *.sap 91 | 92 | # TFS 2012 Local Workspace 93 | $tf/ 94 | 95 | # Guidance Automation Toolkit 96 | *.gpState 97 | 98 | # ReSharper is a .NET coding add-in 99 | _ReSharper*/ 100 | *.[Rr]e[Ss]harper 101 | *.DotSettings.user 102 | 103 | # JustCode is a .NET coding add-in 104 | .JustCode 105 | 106 | # TeamCity is a build add-in 107 | _TeamCity* 108 | 109 | # DotCover is a Code Coverage Tool 110 | *.dotCover 111 | 112 | # NCrunch 113 | _NCrunch_* 114 | .*crunch*.local.xml 115 | nCrunchTemp_* 116 | 117 | # MightyMoose 118 | *.mm.* 119 | AutoTest.Net/ 120 | 121 | # Web workbench (sass) 122 | .sass-cache/ 123 | 124 | # Installshield output folder 125 | [Ee]xpress/ 126 | 127 | # DocProject is a documentation generator add-in 128 | DocProject/buildhelp/ 129 | DocProject/Help/*.HxT 130 | DocProject/Help/*.HxC 131 | DocProject/Help/*.hhc 132 | DocProject/Help/*.hhk 133 | DocProject/Help/*.hhp 134 | DocProject/Help/Html2 135 | DocProject/Help/html 136 | 137 | # Click-Once directory 138 | publish/ 139 | 140 | # Publish Web Output 141 | *.[Pp]ublish.xml 142 | *.azurePubxml 143 | # TODO: Comment the next line if you want to checkin your web deploy settings 144 | # but database connection strings (with potential passwords) will be unencrypted 145 | *.pubxml 146 | *.publishproj 147 | 148 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 149 | # checkin your Azure Web App publish settings, but sensitive information contained 150 | # in these scripts will be unencrypted 151 | PublishScripts/ 152 | 153 | # NuGet Packages 154 | *.nupkg 155 | # The packages folder can be ignored because of Package Restore 156 | **/packages/* 157 | # except build/, which is used as an MSBuild target. 158 | !**/packages/build/ 159 | # Uncomment if necessary however generally it will be regenerated when needed 160 | #!**/packages/repositories.config 161 | # NuGet v3's project.json files produces more ignoreable files 162 | *.nuget.props 163 | *.nuget.targets 164 | 165 | # Microsoft Azure Build Output 166 | csx/ 167 | *.build.csdef 168 | 169 | # Microsoft Azure Emulator 170 | ecf/ 171 | rcf/ 172 | 173 | # Windows Store app package directories and files 174 | AppPackages/ 175 | BundleArtifacts/ 176 | Package.StoreAssociation.xml 177 | _pkginfo.txt 178 | 179 | # Visual Studio cache files 180 | # files ending in .cache can be ignored 181 | *.[Cc]ache 182 | # but keep track of directories ending in .cache 183 | !*.[Cc]ache/ 184 | 185 | # Others 186 | ClientBin/ 187 | ~$* 188 | *~ 189 | *.dbmdl 190 | *.dbproj.schemaview 191 | *.pfx 192 | *.publishsettings 193 | node_modules/ 194 | orleans.codegen.cs 195 | 196 | # Since there are multiple workflows, uncomment next line to ignore bower_components 197 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 198 | #bower_components/ 199 | 200 | # RIA/Silverlight projects 201 | Generated_Code/ 202 | 203 | # Backup & report files from converting an old project file 204 | # to a newer Visual Studio version. Backup files are not needed, 205 | # because we have git ;-) 206 | _UpgradeReport_Files/ 207 | Backup*/ 208 | UpgradeLog*.XML 209 | UpgradeLog*.htm 210 | 211 | # SQL Server files 212 | *.mdf 213 | *.ldf 214 | 215 | # Business Intelligence projects 216 | *.rdl.data 217 | *.bim.layout 218 | *.bim_*.settings 219 | 220 | # Microsoft Fakes 221 | FakesAssemblies/ 222 | 223 | # GhostDoc plugin setting file 224 | *.GhostDoc.xml 225 | 226 | # Node.js Tools for Visual Studio 227 | .ntvs_analysis.dat 228 | 229 | # Visual Studio 6 build log 230 | *.plg 231 | 232 | # Visual Studio 6 workspace options file 233 | *.opt 234 | 235 | # Visual Studio LightSwitch build output 236 | **/*.HTMLClient/GeneratedArtifacts 237 | **/*.DesktopClient/GeneratedArtifacts 238 | **/*.DesktopClient/ModelManifest.xml 239 | **/*.Server/GeneratedArtifacts 240 | **/*.Server/ModelManifest.xml 241 | _Pvt_Extensions 242 | 243 | # Paket dependency manager 244 | .paket/paket.exe 245 | paket-files/ 246 | 247 | # FAKE - F# Make 248 | .fake/ 249 | 250 | # JetBrains Rider 251 | .idea/ 252 | *.sln.iml 253 | -------------------------------------------------------------------------------- /CustomExporterCollada.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25123.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CustomExporterCollada", "CustomExporterCollada\CustomExporterCollada.csproj", "{63E6E57D-3D0D-4CF6-AE76-1322FE8C7DE2}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {63E6E57D-3D0D-4CF6-AE76-1322FE8C7DE2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {63E6E57D-3D0D-4CF6-AE76-1322FE8C7DE2}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {63E6E57D-3D0D-4CF6-AE76-1322FE8C7DE2}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {63E6E57D-3D0D-4CF6-AE76-1322FE8C7DE2}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /CustomExporterCollada/Command.cs: -------------------------------------------------------------------------------- 1 | #region Namespaces 2 | using System.Diagnostics; 3 | using Autodesk.Revit.ApplicationServices; 4 | using Autodesk.Revit.Attributes; 5 | using Autodesk.Revit.DB; 6 | using Autodesk.Revit.Exceptions; 7 | using Autodesk.Revit.UI; 8 | #endregion 9 | 10 | namespace CustomExporterCollada 11 | { 12 | [Transaction( TransactionMode.Manual )] 13 | public class Command : IExternalCommand 14 | { 15 | public Result Execute( 16 | ExternalCommandData commandData, 17 | ref string message, 18 | ElementSet elements ) 19 | { 20 | UIApplication uiapp = commandData.Application; 21 | UIDocument uidoc = uiapp.ActiveUIDocument; 22 | Application app = uiapp.Application; 23 | Document doc = uidoc.Document; 24 | View view = doc.ActiveView; 25 | 26 | if( view is View3D ) 27 | ExportView3D( doc, view as View ); 28 | else 29 | TaskDialog.Show( "Collada Export", 30 | "You must be in 3D view to export." ); 31 | 32 | return Result.Succeeded; 33 | } 34 | 35 | static void ExportView3D( 36 | Document document, 37 | View view3D ) 38 | { 39 | MyExportContext context = new MyExportContext( 40 | document, "C:/tmp/test.dae" ); 41 | 42 | // Create an instance of a custom exporter by 43 | // giving it a document and the context. 44 | 45 | CustomExporter exporter = new CustomExporter( 46 | document, context ); 47 | 48 | // Note: Excluding faces just excludes the calls, 49 | // not the actual processing of face tessellation. 50 | // Meshes of the faces will still be received by 51 | // the context. 52 | //exporter.IncludeFaces = false; 53 | 54 | exporter.ShouldStopOnError = false; 55 | 56 | try 57 | { 58 | exporter.Export( view3D ); 59 | } 60 | catch( ExternalApplicationException ex ) 61 | { 62 | Debug.Print( "ExternalApplicationException " 63 | + ex.Message ); 64 | } 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /CustomExporterCollada/CustomExporterCollada.addin: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Command CustomExporterCollada 5 | Some description for CustomExporterCollada 6 | CustomExporterCollada.dll 7 | CustomExporterCollada.Command 8 | 15bfc57e-fc19-48fc-8918-f92310b58a5b 9 | com.typepad.thebuildingcoder 10 | The Building Coder, http://thebuildingcoder.typepad.com 11 | 12 | 13 | -------------------------------------------------------------------------------- /CustomExporterCollada/CustomExporterCollada.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | None 7 | 8 | 9 | 10 | 11 | Debug 12 | AnyCPU 13 | {63E6E57D-3D0D-4CF6-AE76-1322FE8C7DE2} 14 | Library 15 | Properties 16 | CustomExporterCollada 17 | CustomExporterCollada 18 | v4.7 19 | 512 20 | 21 | 22 | true 23 | full 24 | false 25 | bin\Debug\ 26 | DEBUG;TRACE 27 | prompt 28 | 4 29 | Program 30 | $(ProgramW6432)\Autodesk\Revit 2017\Revit.exe 31 | 32 | 33 | pdbonly 34 | true 35 | bin\Release\ 36 | TRACE 37 | prompt 38 | 4 39 | Program 40 | $(ProgramW6432)\Autodesk\Revit 2017\Revit.exe 41 | 42 | 43 | 44 | C:\Program Files\Autodesk\Revit 2020\RevitAPI.dll 45 | False 46 | 47 | 48 | C:\Program Files\Autodesk\Revit 2020\RevitAPIUI.dll 49 | False 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | copy "$(ProjectDir)*.addin" "$(AppData)\Autodesk\REVIT\Addins\2020" 64 | copy "$(ProjectDir)bin\debug\*.dll" "$(AppData)\Autodesk\REVIT\Addins\2020" 65 | 66 | 73 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /CustomExporterCollada/MyExportContext.cs: -------------------------------------------------------------------------------- 1 | #region Namespaces 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.IO; 6 | using Autodesk.Revit.DB; 7 | #endregion 8 | 9 | namespace CustomExporterCollada 10 | { 11 | class MyExportContext : IExportContext 12 | { 13 | private Document exportedDocument = null; 14 | 15 | public uint CurrentPolymeshIndex { get; set; } 16 | 17 | ElementId CurrentElementId 18 | { 19 | get 20 | { 21 | return ( elementStack.Count > 0 ) 22 | ? elementStack.Peek() 23 | : ElementId.InvalidElementId; 24 | } 25 | } 26 | 27 | Element CurrentElement 28 | { 29 | get 30 | { 31 | return exportedDocument.GetElement( 32 | CurrentElementId ); 33 | } 34 | } 35 | 36 | Transform CurrentTransform 37 | { 38 | get 39 | { 40 | return transformationStack.Peek(); 41 | } 42 | } 43 | 44 | private bool isCancelled = false; 45 | 46 | Stack elementStack = new Stack(); 47 | 48 | private Stack transformationStack = new Stack(); 49 | 50 | ElementId currentMaterialId = ElementId.InvalidElementId; 51 | 52 | StreamWriter streamWriter = null; 53 | 54 | Dictionary polymeshToMaterialId = new Dictionary(); 55 | 56 | public MyExportContext( 57 | Document document, 58 | string filepath ) 59 | { 60 | this.exportedDocument = document; 61 | transformationStack.Push( Transform.Identity ); 62 | 63 | streamWriter = new StreamWriter( filepath ); 64 | } 65 | 66 | public bool Start() 67 | { 68 | CurrentPolymeshIndex = 0; 69 | polymeshToMaterialId.Clear(); 70 | 71 | WriteXmlColladaBegin(); 72 | WriteXmlAsset(); 73 | 74 | WriteXmlLibraryGeometriesBegin(); 75 | 76 | return true; 77 | } 78 | 79 | public void Finish() 80 | { 81 | WriteXmlLibraryGeometriesEnd(); 82 | 83 | WriteXmlLibraryMaterials(); 84 | WriteXmlLibraryEffects(); 85 | WriteXmlLibraryVisualScenes(); 86 | WriteXmlColladaEnd(); 87 | 88 | streamWriter.Close(); 89 | } 90 | 91 | private void WriteXmlColladaBegin() 92 | { 93 | streamWriter.Write( "\n" ); 94 | streamWriter.Write( "\n" ); 95 | } 96 | 97 | private void WriteXmlColladaEnd() 98 | { 99 | streamWriter.Write( "\n" ); 100 | } 101 | 102 | private void WriteXmlAsset() 103 | { 104 | streamWriter.Write( "\n" ); 105 | streamWriter.Write( "\n" ); 106 | streamWriter.Write( " Lumion - Revit COLLADA exporter\n" ); 107 | streamWriter.Write( "\n" ); 108 | streamWriter.Write( "" + DateTime.Now.ToString() + "\n" ); 109 | 110 | //Units 111 | streamWriter.Write( "\n" ); 112 | streamWriter.Write( "Z_UP\n" ); 113 | streamWriter.Write( "\n" ); 114 | } 115 | 116 | private void WriteXmlLibraryGeometriesBegin() 117 | { 118 | streamWriter.Write( "\n" ); 119 | } 120 | 121 | private void WriteXmlLibraryGeometriesEnd() 122 | { 123 | streamWriter.Write( "\n" ); 124 | } 125 | 126 | public void OnPolymesh( PolymeshTopology polymesh ) 127 | { 128 | CurrentPolymeshIndex++; 129 | 130 | WriteXmlGeometryBegin(); 131 | WriteXmlGeometrySourcePositions( polymesh ); 132 | WriteXmlGeometrySourceNormals( polymesh ); 133 | if( polymesh.NumberOfUVs > 0 ) 134 | WriteXmlGeometrySourceMap( polymesh ); 135 | 136 | WriteXmlGeometryVertices(); 137 | 138 | if( polymesh.NumberOfUVs > 0 ) 139 | WriteXmlGeometryTrianglesWithMap( polymesh ); 140 | else 141 | WriteXmlGeometryTrianglesWithoutMap( polymesh ); 142 | 143 | WriteXmlGeometryEnd(); 144 | 145 | polymeshToMaterialId.Add( CurrentPolymeshIndex, currentMaterialId ); 146 | } 147 | 148 | private void WriteXmlGeometryBegin() 149 | { 150 | streamWriter.Write( "\n" ); 151 | streamWriter.Write( "\n" ); 152 | } 153 | 154 | private string GetCurrentElementName() 155 | { 156 | Element element = CurrentElement; 157 | if( element != null ) 158 | return element.Name; 159 | 160 | return ""; //default name 161 | } 162 | 163 | private void WriteXmlGeometryEnd() 164 | { 165 | streamWriter.Write( "\n" ); 166 | streamWriter.Write( "\n" ); 167 | } 168 | 169 | private void WriteXmlGeometrySourcePositions( PolymeshTopology polymesh ) 170 | { 171 | streamWriter.Write( "\n" ); 172 | streamWriter.Write( "\n" ); 173 | 174 | XYZ point; 175 | Transform currentTransform = transformationStack.Peek(); 176 | 177 | for( int iPoint = 0; iPoint < polymesh.NumberOfPoints; ++iPoint ) 178 | { 179 | point = polymesh.GetPoint( iPoint ); 180 | point = currentTransform.OfPoint( point ); 181 | streamWriter.Write( "{0:0.0000} {1:0.0000} {2:0.0000}\n", point.X, point.Y, point.Z ); 182 | } 183 | 184 | streamWriter.Write( "\n" ); 185 | streamWriter.Write( "\n" ); 186 | streamWriter.Write( "\n" ); 187 | streamWriter.Write( "\n" ); 188 | streamWriter.Write( "\n" ); 189 | streamWriter.Write( "\n" ); 190 | streamWriter.Write( "\n" ); 191 | streamWriter.Write( "\n" ); 192 | streamWriter.Write( "\n" ); 193 | } 194 | 195 | private void WriteXmlGeometrySourceNormals( PolymeshTopology polymesh ) 196 | { 197 | int nNormals = 0; 198 | 199 | switch( polymesh.DistributionOfNormals ) 200 | { 201 | case DistributionOfNormals.AtEachPoint: 202 | nNormals = polymesh.NumberOfPoints; 203 | break; 204 | case DistributionOfNormals.OnePerFace: 205 | nNormals = 1; 206 | break; 207 | case DistributionOfNormals.OnEachFacet: 208 | //TODO : DistributionOfNormals.OnEachFacet 209 | nNormals = 1; 210 | break; 211 | } 212 | 213 | streamWriter.Write( "\n" ); 214 | streamWriter.Write( "\n" ); 215 | 216 | XYZ point; 217 | Transform currentTransform = transformationStack.Peek(); 218 | 219 | for( int iNormal = 0; iNormal < nNormals; ++iNormal ) 220 | { 221 | point = polymesh.GetNormal( iNormal ); 222 | point = currentTransform.OfVector( point ); 223 | streamWriter.Write( "{0:0.0000} {1:0.0000} {2:0.0000}\n", point.X, point.Y, point.Z ); 224 | } 225 | 226 | streamWriter.Write( "\n" ); 227 | streamWriter.Write( "\n" ); 228 | streamWriter.Write( "\n" ); 229 | streamWriter.Write( "\n" ); 230 | streamWriter.Write( "\n" ); 231 | streamWriter.Write( "\n" ); 232 | streamWriter.Write( "\n" ); 233 | streamWriter.Write( "\n" ); 234 | streamWriter.Write( "\n" ); 235 | } 236 | 237 | private void WriteXmlGeometrySourceMap( PolymeshTopology polymesh ) 238 | { 239 | streamWriter.Write( "\n" ); 240 | streamWriter.Write( "\n" ); 241 | 242 | UV uv; 243 | 244 | for( int iUv = 0; iUv < polymesh.NumberOfUVs; ++iUv ) 245 | { 246 | uv = polymesh.GetUV( iUv ); 247 | streamWriter.Write( "{0:0.0000} {1:0.0000}\n", uv.U, uv.V ); 248 | } 249 | 250 | streamWriter.Write( "\n" ); 251 | streamWriter.Write( "\n" ); 252 | streamWriter.Write( "\n" ); 253 | streamWriter.Write( "\n" ); 254 | streamWriter.Write( "\n" ); 255 | streamWriter.Write( "\n" ); 256 | streamWriter.Write( "\n" ); 257 | streamWriter.Write( "\n" ); 258 | } 259 | 260 | private void WriteXmlGeometryVertices() 261 | { 262 | streamWriter.Write( "\n" ); 263 | streamWriter.Write( "\n" ); 264 | streamWriter.Write( "\n" ); 265 | } 266 | 267 | private void WriteXmlGeometryTrianglesWithoutMap( PolymeshTopology polymesh ) 268 | { 269 | streamWriter.Write( "\n" ); 273 | streamWriter.Write( "\n" ); 274 | streamWriter.Write( "\n" ); 275 | streamWriter.Write( "

\n" ); 276 | PolymeshFacet facet; 277 | 278 | switch( polymesh.DistributionOfNormals ) 279 | { 280 | case DistributionOfNormals.AtEachPoint: 281 | for( int i = 0; i < polymesh.NumberOfFacets; ++i ) 282 | { 283 | facet = polymesh.GetFacet( i ); 284 | streamWriter.Write( facet.V1 + " " + facet.V1 + " " + 285 | facet.V2 + " " + facet.V2 + " " + 286 | facet.V3 + " " + facet.V3 + " " + 287 | "\n" ); 288 | } 289 | break; 290 | 291 | case DistributionOfNormals.OnEachFacet: 292 | //TODO : DistributionOfNormals.OnEachFacet 293 | case DistributionOfNormals.OnePerFace: 294 | for( int i = 0; i < polymesh.NumberOfFacets; ++i ) 295 | { 296 | facet = polymesh.GetFacet( i ); 297 | streamWriter.Write( facet.V1 + " 0 " + 298 | facet.V2 + " 0 " + 299 | facet.V3 + " 0 " + 300 | "\n" ); 301 | } 302 | break; 303 | 304 | } 305 | 306 | streamWriter.Write( "

\n" ); 307 | streamWriter.Write( "
\n" ); 308 | } 309 | 310 | private void WriteXmlGeometryTrianglesWithMap( PolymeshTopology polymesh ) 311 | { 312 | streamWriter.Write( "\n" ); 316 | streamWriter.Write( "\n" ); 317 | streamWriter.Write( "\n" ); 318 | streamWriter.Write( "\n" ); 319 | streamWriter.Write( "

\n" ); 320 | PolymeshFacet facet; 321 | 322 | switch( polymesh.DistributionOfNormals ) 323 | { 324 | case DistributionOfNormals.AtEachPoint: 325 | for( int i = 0; i < polymesh.NumberOfFacets; ++i ) 326 | { 327 | facet = polymesh.GetFacet( i ); 328 | streamWriter.Write( facet.V1 + " " + facet.V1 + " " + facet.V1 + " " + 329 | facet.V2 + " " + facet.V2 + " " + facet.V2 + " " + 330 | facet.V3 + " " + facet.V3 + " " + facet.V3 + " " + 331 | "\n" ); 332 | } 333 | break; 334 | 335 | case DistributionOfNormals.OnEachFacet: 336 | //TODO : DistributionOfNormals.OnEachFacet 337 | case DistributionOfNormals.OnePerFace: 338 | for( int i = 0; i < polymesh.NumberOfFacets; ++i ) 339 | { 340 | facet = polymesh.GetFacet( i ); 341 | streamWriter.Write( facet.V1 + " 0 " + facet.V1 + " " + 342 | facet.V2 + " 0 " + facet.V2 + " " + 343 | facet.V3 + " 0 " + facet.V3 + " " + 344 | "\n" ); 345 | } 346 | break; 347 | } 348 | 349 | streamWriter.Write( "

\n" ); 350 | streamWriter.Write( "
\n" ); 351 | } 352 | 353 | public void OnMaterial( MaterialNode node ) 354 | { 355 | // OnMaterial method can be invoked for every single out-coming mesh 356 | // even when the material has not actually changed. Thus it is usually 357 | // beneficial to store the current material and only get its attributes 358 | // when the material actually changes. 359 | 360 | currentMaterialId = node.MaterialId; 361 | } 362 | 363 | private void WriteXmlLibraryMaterials() 364 | { 365 | streamWriter.Write( "\n" ); 366 | 367 | foreach( var materialId in polymeshToMaterialId.Values.Distinct() ) 368 | { 369 | if( IsMaterialValid( materialId ) == false ) 370 | continue; 371 | 372 | streamWriter.Write( "\n" ); 373 | streamWriter.Write( "\n" ); 374 | streamWriter.Write( "\n" ); 375 | } 376 | 377 | streamWriter.Write( "\n" ); 378 | } 379 | 380 | private string GetMaterialName( ElementId materialId ) 381 | { 382 | Material material = exportedDocument.GetElement( materialId ) as Material; 383 | if( material != null ) 384 | return material.Name; 385 | 386 | return ""; //default material name 387 | } 388 | 389 | private bool IsMaterialValid( ElementId materialId ) 390 | { 391 | Material material = exportedDocument.GetElement( materialId ) as Material; 392 | if( material != null ) 393 | return true; 394 | 395 | return false; 396 | } 397 | 398 | private void WriteXmlLibraryEffects() 399 | { 400 | streamWriter.Write( "\n" ); 401 | 402 | foreach( var materialId in polymeshToMaterialId.Values.Distinct() ) 403 | { 404 | if( IsMaterialValid( materialId ) == false ) 405 | continue; 406 | 407 | Material material = exportedDocument.GetElement( materialId ) as Material; 408 | 409 | streamWriter.Write( "\n" ); 410 | streamWriter.Write( "\n" ); 411 | 412 | streamWriter.Write( "\n" ); 413 | streamWriter.Write( "\n" ); 414 | streamWriter.Write( "\n" ); 415 | streamWriter.Write( "0.1 0.1 0.1 1.000000\n" ); 416 | streamWriter.Write( "\n" ); 417 | 418 | 419 | //diffuse 420 | streamWriter.Write( "\n" ); 421 | streamWriter.Write( "" + material.Color.Red + " " + material.Color.Green + " " + material.Color.Blue + " 1.0\n" ); 422 | streamWriter.Write( "\n" ); 423 | 424 | 425 | streamWriter.Write( "\n" ); 426 | streamWriter.Write( "1.000000 1.000000 1.000000 1.000000\n" ); 427 | streamWriter.Write( "\n" ); 428 | 429 | streamWriter.Write( "\n" ); 430 | streamWriter.Write( "" + material.Shininess + "\n" ); 431 | streamWriter.Write( "\n" ); 432 | 433 | streamWriter.Write( "\n" ); 434 | streamWriter.Write( "0 0 0 1.000000\n" ); 435 | streamWriter.Write( "\n" ); 436 | streamWriter.Write( "\n" ); 437 | streamWriter.Write( "1.000000\n" ); 438 | streamWriter.Write( "\n" ); 439 | 440 | streamWriter.Write( "\n" ); 441 | streamWriter.Write( "1.000000 1.000000 1.000000 1.000000\n" ); 442 | streamWriter.Write( "\n" ); 443 | 444 | streamWriter.Write( "\n" ); 445 | streamWriter.Write( "" + material.Transparency + "\n" ); 446 | streamWriter.Write( "\n" ); 447 | 448 | streamWriter.Write( "\n" ); 449 | streamWriter.Write( "\n" ); 450 | 451 | 452 | streamWriter.Write( "\n" ); 453 | streamWriter.Write( "\n" ); 454 | } 455 | 456 | streamWriter.Write( "\n" ); 457 | } 458 | 459 | public void WriteXmlLibraryVisualScenes() 460 | { 461 | streamWriter.Write( "\n" ); 462 | streamWriter.Write( "\n" ); 463 | 464 | foreach( var pair in polymeshToMaterialId ) 465 | { 466 | streamWriter.Write( "\n" ); 467 | streamWriter.Write( "\n" ); 468 | if( IsMaterialValid( pair.Value ) ) 469 | { 470 | streamWriter.Write( "\n" ); 471 | streamWriter.Write( "\n" ); 472 | streamWriter.Write( "\n" ); 473 | streamWriter.Write( "\n" ); 474 | streamWriter.Write( "\n" ); 475 | streamWriter.Write( "\n" ); 476 | } 477 | streamWriter.Write( "\n" ); 478 | streamWriter.Write( "\n" ); 479 | } 480 | 481 | streamWriter.Write( "\n" ); 482 | streamWriter.Write( "\n" ); 483 | 484 | streamWriter.Write( "\n" ); 485 | streamWriter.Write( "\n" ); 486 | streamWriter.Write( "\n" ); 487 | } 488 | 489 | public bool IsCanceled() 490 | { 491 | // This method is invoked many times during the export process. 492 | return isCancelled; 493 | } 494 | 495 | //public void OnDaylightPortal( DaylightPortalNode node ) 496 | //{ 497 | //} 498 | 499 | public void OnRPC( RPCNode node ) 500 | { 501 | } 502 | 503 | public RenderNodeAction OnViewBegin( ViewNode node ) 504 | { 505 | return RenderNodeAction.Proceed; 506 | } 507 | 508 | public void OnViewEnd( ElementId elementId ) 509 | { 510 | // Note: This method is invoked even for a view that was skipped. 511 | } 512 | 513 | public RenderNodeAction OnElementBegin( ElementId elementId ) 514 | { 515 | elementStack.Push( elementId ); 516 | 517 | return RenderNodeAction.Proceed; 518 | } 519 | 520 | public void OnElementEnd( ElementId elementId ) 521 | { 522 | // Note: this method is invoked even for elements that were skipped. 523 | elementStack.Pop(); 524 | } 525 | 526 | public RenderNodeAction OnFaceBegin( FaceNode node ) 527 | { 528 | // This method is invoked only if the custom exporter was set to include faces. 529 | return RenderNodeAction.Proceed; 530 | } 531 | 532 | public void OnFaceEnd( FaceNode node ) 533 | { 534 | // This method is invoked only if the custom exporter was set to include faces. 535 | // Note: This method is invoked even for faces that were skipped. 536 | } 537 | 538 | public RenderNodeAction OnInstanceBegin( InstanceNode node ) 539 | { 540 | // This method marks the start of processing a family instance 541 | transformationStack.Push( transformationStack.Peek().Multiply( node.GetTransform() ) ); 542 | 543 | // We can either skip this instance or proceed with rendering it. 544 | return RenderNodeAction.Proceed; 545 | } 546 | 547 | public void OnInstanceEnd( InstanceNode node ) 548 | { 549 | // Note: This method is invoked even for instances that were skipped. 550 | transformationStack.Pop(); 551 | } 552 | 553 | public RenderNodeAction OnLinkBegin( LinkNode node ) 554 | { 555 | transformationStack.Push( transformationStack.Peek().Multiply( node.GetTransform() ) ); 556 | return RenderNodeAction.Proceed; 557 | } 558 | 559 | public void OnLinkEnd( LinkNode node ) 560 | { 561 | // Note: This method is invoked even for instances that were skipped. 562 | transformationStack.Pop(); 563 | } 564 | 565 | public void OnLight( LightNode node ) 566 | { 567 | } 568 | } 569 | } 570 | -------------------------------------------------------------------------------- /CustomExporterCollada/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( "CustomExporterCollada" )] 9 | [assembly: AssemblyDescription( "Revit Add-In Description for CustomExporterCollada" )] 10 | [assembly: AssemblyConfiguration( "" )] 11 | [assembly: AssemblyCompany( "Autodesk Inc." )] 12 | [assembly: AssemblyProduct( "CustomExporterCollada Revit C# .NET Add-In" )] 13 | [assembly: AssemblyCopyright( "Copyright 2017 (C) Artur Brzegowy, Act-3D B.V. and Jeremy Tammik, Autodesk Inc." )] 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( "321044f7-b0b2-4b1c-af18-e71a19252be0" )] 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 | // 36 | // 2017-02-28 2017.0.0.0 initial release based on http://thebuildingcoder.typepad.com/files/customexportercollada.zip and http://thebuildingcoder.typepad.com/blog/2013/07/graphics-pipeline-custom-exporter.html#5 37 | // 2017-02-28 2017.0.0.1 workaround -- added exception handler around call to exporter.Export 38 | // 2017-10-26 2018.0.0.0 flat migration to Revit 2018 39 | // 2019-09-02 2020.0.0.0 flat migration to Revit 2020 40 | // 2019-09-02 2020.0.0.1 eliminated deprecated function call 41 | // 2019-09-02 2020.0.0.1 removed use of Windows.Forms 42 | // 2019-09-03 2020.0.0.2 added filepath argument and open strea writer in constructor 43 | // 44 | [assembly: AssemblyVersion( "2020.0.0.2" )] 45 | [assembly: AssemblyFileVersion( "2020.0.0.2" )] 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Jeremy Tammik 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CustomExporterCollada 2 | 3 | C# .NET Revit add-in implementing a custom exporter to Collada. 4 | 5 | For further information, please refer to The Building Coder article on 6 | the [graphics pipeline custom exporter](http://thebuildingcoder.typepad.com/blog/2013/07/graphics-pipeline-custom-exporter.html), 7 | specifically 8 | the [custom exporter to Collada](http://thebuildingcoder.typepad.com/blog/2013/07/graphics-pipeline-custom-exporter.html#5), 9 | and the entire topic group on [custom exporters](http://thebuildingcoder.typepad.com/blog/about-the-author.html#5.1). 10 | 11 | Jeremy posted the code here and migrated it to Revit 2017 in order to resolve the issue raised by Artur in 12 | the [Revit API discussion forum](http://forums.autodesk.com/t5/revit-api/bd-p/160) thread 13 | on [how to avoid canceling export when user use scroll wheel](https://forums.autodesk.com/t5/revit-api-forum/how-to-avoid-canceling-export-when-user-use-scroll-wheel/m-p/6882457). 14 | 15 | 16 | ## Authors 17 | 18 | - Artur Brzegowy of [Act-3D B.V.](http://www.lumion3d.com) 19 | - Jeremy Tammik, 20 | [The Building Coder](http://thebuildingcoder.typepad.com) and 21 | [The 3D Web Coder](http://the3dwebcoder.typepad.com), 22 | [Forge](http://forge.autodesk.com) [Platform](https://developer.autodesk.com) Development, 23 | [ADN](http://www.autodesk.com/adn) 24 | [Open](http://www.autodesk.com/adnopen), 25 | [Autodesk Inc.](http://www.autodesk.com) 26 | 27 | 28 | ## License 29 | 30 | This sample is licensed under the terms of the [MIT License](http://opensource.org/licenses/MIT). 31 | Please see the [LICENSE](LICENSE) file for full details. 32 | --------------------------------------------------------------------------------