diff --git a/src/Spectacles.RevitExporter/Command.cs b/src/Spectacles.RevitExporter/Command.cs
index 0b945c7..ff49e1a 100644
--- a/src/Spectacles.RevitExporter/Command.cs
+++ b/src/Spectacles.RevitExporter/Command.cs
@@ -415,12 +415,12 @@ public Result Execute(
{
try
{
- if (camera.IsPerspective)
+ if ((camera.IsTemplate == false) && (camera.IsPerspective))
{
ViewOrientation3D vo = camera.GetOrientation();
cameraNames.Add(camera.Name);
cameraPositions.Add((-vo.EyePosition.X * 304.8).ToString() + "," + (vo.EyePosition.Z * 304.8).ToString() + "," + (vo.EyePosition.Y* 304.8).ToString());
- cameraTargets.Add((-vo.ForwardDirection.X * 304.8).ToString() + "," + (vo.ForwardDirection.Z * 304.8).ToString() + "," + (vo.ForwardDirection.Y* 304.8).ToString());
+ cameraTargets.Add((-vo.ForwardDirection.X).ToString() + "," + (vo.ForwardDirection.Z).ToString() + "," + (vo.ForwardDirection.Y).ToString());
}
}
catch { }
diff --git a/src/Spectacles.RevitExporter/Properties/AssemblyInfo.cs b/src/Spectacles.RevitExporter/Properties/AssemblyInfo.cs
index 25ef92d..38424e2 100644
--- a/src/Spectacles.RevitExporter/Properties/AssemblyInfo.cs
+++ b/src/Spectacles.RevitExporter/Properties/AssemblyInfo.cs
@@ -40,7 +40,7 @@
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Thornton Tomasetti")]
[assembly: AssemblyProduct("Spectacles.RevitExporter")]
-[assembly: AssemblyCopyright("Copyright 2015 © Thornton Tomasetti")]
+[assembly: AssemblyCopyright("Copyright 2015-2017 © Thornton Tomasetti, et al.")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
@@ -75,5 +75,5 @@
// 2014-11-25 2015.0.0.28 skip elements with null category in OnElementEnd as well
// 2015-02-15 2015.0.0.29 incremented copyright year
//
-[assembly: AssemblyVersion("0.1.0.3")]
-[assembly: AssemblyFileVersion("0.1.0.3")]
\ No newline at end of file
+[assembly: AssemblyVersion("0.1.0.4")]
+[assembly: AssemblyFileVersion("0.1.0.4")]
\ No newline at end of file
diff --git a/src/Spectacles.RevitExporter/Spectacles.RevitExporter.csproj b/src/Spectacles.RevitExporter/Spectacles.RevitExporter.csproj
index 9d0b0dc..eda0a88 100644
--- a/src/Spectacles.RevitExporter/Spectacles.RevitExporter.csproj
+++ b/src/Spectacles.RevitExporter/Spectacles.RevitExporter.csproj
@@ -1,149 +1,150 @@
-
-
-
+
+
+
None
-
-
-
-
- Debug
- AnyCPU
-
-
-
-
- {3F865D22-8849-4DFA-9E7C-3533A8A159EA}
- Library
- Properties
- Spectacles.RevitExporter
- v4.5
- 512
-
-
- true
- full
- false
- bin\
- DEBUG;TRACE
- prompt
- 4
- Program
- $(ProgramW6432)\Autodesk\Revit 2015\Revit.exe
- false
-
-
- pdbonly
- true
- bin\
- TRACE
- prompt
- 4
- Program
- $(ProgramW6432)\Autodesk\Revit 2015\Revit.exe
- false
-
-
- Spectacles.RevitExporter
-
-
-
-
- packages\Newtonsoft.Json.dll
-
-
-
-
- ..\..\..\..\..\..\..\Program Files\Autodesk\Revit 2016\RevitAPI.dll
- False
-
-
- ..\..\..\..\..\..\..\Program Files\Autodesk\Revit 2016\RevitAPIUI.dll
- False
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Form
-
-
- ExportOptions.cs
-
-
- Form
-
-
- ParameterFilter.cs
-
-
-
- True
- True
- Resources.resx
-
-
-
-
-
- Form
-
-
- Success.cs
-
-
-
-
-
-
-
-
- ExportOptions.cs
-
-
- ParameterFilter.cs
-
-
- ResXFileCodeGenerator
- Resources.Designer.cs
- Designer
-
-
- Success.cs
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+ Debug
+ AnyCPU
+
+
+
+
+ {3F865D22-8849-4DFA-9E7C-3533A8A159EA}
+ Library
+ Properties
+ Spectacles.RevitExporter
+ v4.5
+ 512
+
+
+ true
+ full
+ false
+ bin\
+ DEBUG;TRACE
+ prompt
+ 4
+ Program
+ $(ProgramW6432)\Autodesk\Revit 2015\Revit.exe
+ false
+
+
+ pdbonly
+ true
+ bin\
+ TRACE
+ prompt
+ 4
+ Program
+ $(ProgramW6432)\Autodesk\Revit 2015\Revit.exe
+ false
+
+
+ Spectacles.RevitExporter
+
+
+
+
+ ..\packages\Newtonsoft.Json.8.0.3\lib\net45\Newtonsoft.Json.dll
+ True
+
+
+
+
+ ..\..\..\..\..\..\..\Program Files\Autodesk\Revit 2016\RevitAPI.dll
+ False
+
+
+ ..\..\..\..\..\..\..\Program Files\Autodesk\Revit 2016\RevitAPIUI.dll
+ False
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Form
+
+
+ ExportOptions.cs
+
+
+ Form
+
+
+ ParameterFilter.cs
+
+
+
+ True
+ True
+ Resources.resx
+
+
+
+
+
+ Form
+
+
+ Success.cs
+
+
+
+
+
+ ExportOptions.cs
+
+
+ ParameterFilter.cs
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+ Designer
+
+
+ Success.cs
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Spectacles.RevitExporter/SpectaclesExportContext.cs b/src/Spectacles.RevitExporter/SpectaclesExportContext.cs
index fd8c9a4..34c08b0 100644
--- a/src/Spectacles.RevitExporter/SpectaclesExportContext.cs
+++ b/src/Spectacles.RevitExporter/SpectaclesExportContext.cs
@@ -1,903 +1,919 @@
-//The MIT License (MIT)
-
-//Those portions created by va3c authors are provided with the following copyright:
-
-//Copyright (c) 2014 va3c
-
-//Those portions created by Thornton Tomasetti employees are provided with the following copyright:
-
-//Copyright (c) 2015 Thornton Tomasetti
-
-//Permission is hereby granted, free of charge, to any person obtaining a copy
-//of this software and associated documentation files (the "Software"), to deal
-//in the Software without restriction, including without limitation the rights
-//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-//copies of the Software, and to permit persons to whom the Software is
-//furnished to do so, subject to the following conditions:
-
-//The above copyright notice and this permission notice shall be included in all
-//copies or substantial portions of the Software.
-
-//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-//SOFTWARE.
-
-#region Namespaces
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Diagnostics;
-using System.IO;
-using Autodesk.Revit.DB;
-using Autodesk.Revit.Utility;
-using Newtonsoft.Json;
-#endregion // Namespaces
-
-namespace Spectacles.RevitExporter
-{
- // Done:
- // Check instance transformation
- // Support transparency
- // Add scaling for Theo [(0,0),(20000,20000)]
- // Implement the external application button
- // Implement element properties
- // Eliminate multiple materials
- // Prompt user for output file name and location
- // Eliminate null element properties, i.e. useless
- // JSON userData entries
- // Todo:
- // Check for file size
- // Instance/type reuse
-
- public class SpectaclesExportContext : IExportContext
- {
- ///
- /// Scale entire top level BIM object nove in JSON
- /// output. A scale of 1.0 will output the model in
- /// millimetres. Currently we scale it to decimetres
- /// so that a typical model has a chance of fitting
- /// into a cube with side length 100, i.e. 10 metres.
- ///
- double _scale_bim = 1.0;
-
- ///
- /// Scale applied to each vertex in each individual
- /// BIM element. This can be used to scale the model
- /// down from millimetres to metres, e.g.
- /// Currently we stick with millimetres after all
- /// at this level.
- ///
- double _scale_vertex = 1.0;
-
- ///
- /// If true, switch Y and Z coordinate
- /// and flip X to negative to convert from
- /// Revit coordinate system to standard 3d
- /// computer graphics coordinate system with
- /// Z pointing out of screen, X towards right,
- /// Y up.
- ///
- bool _switch_coordinates = true;
-
- #region VertexLookupXyz
- ///
- /// A vertex lookup class to eliminate
- /// duplicate vertex definitions.
- ///
- class VertexLookupXyz : Dictionary
- {
- #region XyzEqualityComparer
- ///
- /// Define equality for Revit XYZ points.
- /// Very rough tolerance, as used by Revit itself.
- ///
- class XyzEqualityComparer : IEqualityComparer
- {
- const double _sixteenthInchInFeet
- = 1.0 / (16.0 * 12.0);
-
- public bool Equals(XYZ p, XYZ q)
- {
- return p.IsAlmostEqualTo(q,
- _sixteenthInchInFeet);
- }
-
- public int GetHashCode(XYZ p)
- {
- return Util.PointString(p).GetHashCode();
- }
- }
- #endregion // XyzEqualityComparer
-
- public VertexLookupXyz()
- : base(new XyzEqualityComparer())
- {
- }
-
- ///
- /// Return the index of the given vertex,
- /// adding a new entry if required.
- ///
- public int AddVertex(XYZ p)
- {
- return ContainsKey(p)
- ? this[p]
- : this[p] = Count;
- }
- }
- #endregion // VertexLookupXyz
-
- #region VertexLookupInt
- ///
- /// An integer-based 3D point class.
- ///
- class PointInt : IComparable
- {
- public long X { get; set; }
- public long Y { get; set; }
- public long Z { get; set; }
-
- //public PointInt( int x, int y, int z )
- //{
- // X = x;
- // Y = y;
- // Z = z;
- //}
-
- ///
- /// Consider a Revit length zero
- /// if is smaller than this.
- ///
- const double _eps = 1.0e-9;
-
- ///
- /// Conversion factor from feet to millimetres.
- ///
- const double _feet_to_mm = 25.4 * 12;
-
- ///
- /// Conversion a given length value
- /// from feet to millimetre.
- ///
- static long ConvertFeetToMillimetres(double d)
- {
- if (0 < d)
- {
- return _eps > d
- ? 0
- : (long)(_feet_to_mm * d + 0.5);
-
- }
- else
- {
- return _eps > -d
- ? 0
- : (long)(_feet_to_mm * d - 0.5);
-
- }
- }
-
- public PointInt(XYZ p, bool switch_coordinates)
- {
- X = ConvertFeetToMillimetres(p.X);
- Y = ConvertFeetToMillimetres(p.Y);
- Z = ConvertFeetToMillimetres(p.Z);
-
- if (switch_coordinates)
- {
- X = -X;
- long tmp = Y;
- Y = Z;
- Z = tmp;
- }
- }
-
- public int CompareTo(PointInt a)
- {
- long d = X - a.X;
-
- if (0 == d)
- {
- d = Y - a.Y;
-
- if (0 == d)
- {
- d = Z - a.Z;
- }
- }
- return (0 == d) ? 0 : ((0 < d) ? 1 : -1);
- }
- }
-
- ///
- /// A vertex lookup class to eliminate
- /// duplicate vertex definitions.
- ///
- class VertexLookupInt : Dictionary
- {
- #region PointIntEqualityComparer
- ///
- /// Define equality for integer-based PointInt.
- ///
- class PointIntEqualityComparer : IEqualityComparer
- {
- public bool Equals(PointInt p, PointInt q)
- {
- return 0 == p.CompareTo(q);
- }
-
- public int GetHashCode(PointInt p)
- {
- return (p.X.ToString()
- + "," + p.Y.ToString()
- + "," + p.Z.ToString())
- .GetHashCode();
- }
- }
- #endregion // PointIntEqualityComparer
-
- public VertexLookupInt()
- : base(new PointIntEqualityComparer())
- {
- }
-
- ///
- /// Return the index of the given vertex,
- /// adding a new entry if required.
- ///
- public int AddVertex(PointInt p)
- {
- return ContainsKey(p)
- ? this[p]
- : this[p] = Count;
- }
- }
- #endregion // VertexLookupInt
-
- Document _doc;
- string _filename;
- SpectaclesContainer _container;
- Dictionary _materials;
- Dictionary _objects;
- Dictionary _geometries;
- Dictionary _viewsAndLayersDict;
- List layerList;
-
- SpectaclesContainer.SpectaclesObject _currentElement;
-
- // Keyed on material uid to handle several materials per element:
-
- Dictionary _currentObject;
- Dictionary _currentGeometry;
- Dictionary _vertices;
-
- Stack _elementStack = new Stack();
- Stack _transformationStack = new Stack();
-
- string _currentMaterialUid;
-
- public string myjs = null;
-
- SpectaclesContainer.SpectaclesObject CurrentObjectPerMaterial
- {
- get
- {
- return _currentObject[_currentMaterialUid];
- }
- }
-
- SpectaclesContainer.SpectaclesGeometry CurrentGeometryPerMaterial
- {
- get
- {
- return _currentGeometry[_currentMaterialUid];
- }
- }
-
- VertexLookupInt CurrentVerticesPerMaterial
- {
- get
- {
- return _vertices[_currentMaterialUid];
- }
- }
-
- Transform CurrentTransform
- {
- get
- {
- return _transformationStack.Peek();
- }
- }
-
- public override string ToString()
- {
- return myjs;
- }
- ///
- /// Set the current material
- ///
- void SetCurrentMaterial(string uidMaterial)
- {
- if (!_materials.ContainsKey(uidMaterial))
- {
- Material material = _doc.GetElement(
- uidMaterial) as Material;
-
- SpectaclesContainer.SpectaclesMaterial m
- = new SpectaclesContainer.SpectaclesMaterial();
-
- //m.metadata = new SpectaclesContainer.SpectaclesMaterialMetadata();
- //m.metadata.type = "material";
- //m.metadata.version = 4.2;
- //m.metadata.generator = "Spectacles.RevitExporter 2015.0.0.0";
-
- m.uuid = uidMaterial;
- m.name = material.Name;
- m.type = "MeshLambertMaterial";
- m.color = Util.ColorToInt(material.Color);
- m.ambient = m.color;
- m.emissive = 0;
- m.opacity = 0.01 * (double)(100 - material.Transparency); // Revit has material.Transparency in [0,100], three.js expects opacity in [0.0,1.0]
- m.transparent = 0 < material.Transparency;
- m.shading = 1;
- m.wireframe = false;
-
- _materials.Add(uidMaterial, m);
- }
- _currentMaterialUid = uidMaterial;
-
- string uid_per_material = _currentElement.uuid + "-" + uidMaterial;
-
- if (!_currentObject.ContainsKey(uidMaterial))
- {
- Debug.Assert(!_currentGeometry.ContainsKey(uidMaterial), "expected same keys in both");
-
- _currentObject.Add(uidMaterial, new SpectaclesContainer.SpectaclesObject());
- CurrentObjectPerMaterial.name = _currentElement.name;
- CurrentObjectPerMaterial.geometry = uid_per_material;
- CurrentObjectPerMaterial.material = _currentMaterialUid;
- CurrentObjectPerMaterial.matrix = new double[] { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 };
- CurrentObjectPerMaterial.type = "Mesh";
- CurrentObjectPerMaterial.uuid = uid_per_material;
- }
-
- if (!_currentGeometry.ContainsKey(uidMaterial))
- {
- _currentGeometry.Add(uidMaterial, new SpectaclesContainer.SpectaclesGeometry());
- CurrentGeometryPerMaterial.uuid = uid_per_material;
- CurrentGeometryPerMaterial.type = "Geometry";
- CurrentGeometryPerMaterial.data = new SpectaclesContainer.SpectaclesGeometryData();
- CurrentGeometryPerMaterial.data.faces = new List();
- CurrentGeometryPerMaterial.data.vertices = new List();
- CurrentGeometryPerMaterial.data.normals = new List();
- CurrentGeometryPerMaterial.data.uvs = new List();
- CurrentGeometryPerMaterial.data.visible = true;
- CurrentGeometryPerMaterial.data.castShadow = true;
- CurrentGeometryPerMaterial.data.receiveShadow = false;
- CurrentGeometryPerMaterial.data.doubleSided = true;
- CurrentGeometryPerMaterial.data.scale = 1.0;
- }
-
- if (!_vertices.ContainsKey(uidMaterial))
- {
- _vertices.Add(uidMaterial, new VertexLookupInt());
- }
- }
-
- public SpectaclesExportContext(Document document, string filename)
- {
- _doc = document;
- _filename = filename;
- }
-
- public bool Start()
- {
- _materials = new Dictionary();
- _geometries = new Dictionary();
- _objects = new Dictionary();
-
- _viewsAndLayersDict = new Dictionary();
- layerList = new List();
-
- _transformationStack.Push(Transform.Identity);
-
- _container = new SpectaclesContainer();
-
- _container.metadata = new SpectaclesContainer.Metadata();
- _container.metadata.type = "Object";
- _container.metadata.version = 4.3;
- _container.metadata.generator = "Spectacles.RevitExporter Revit Spectacles exporter";
- _container.geometries = new List();
-
- _container.obj = new SpectaclesContainer.SpectaclesObject();
- _container.obj.uuid = _doc.ActiveView.UniqueId;
- _container.obj.name = "BIM " + _doc.Title;
- _container.obj.type = "Scene";
-
- // Scale entire BIM from millimetres to metres.
-
- _container.obj.matrix = new double[] {
- _scale_bim, 0, 0, 0,
- 0, _scale_bim, 0, 0,
- 0, 0, _scale_bim, 0,
- 0, 0, 0, _scale_bim };
-
- return true;
- }
-
- public void Finish()
- {
- // Finish populating scene
-
- _container.materials = _materials.Values.ToList();
-
- _container.geometries = _geometries.Values.ToList();
-
- _container.obj.children = _objects.Values.ToList();
-
- if (ExportOptions.includeViews && Command.cameraNames.Count>0)
- {
- //create an empty string to append the list of views
- string viewList = Command.cameraNames[0] + "," + Command.cameraPositions[0] + "," + Command.cameraTargets[0];
- for (int i = 1; i < Command.cameraPositions.Count; i++)
- {
- viewList += "," + Command.cameraNames[i] + "," + Command.cameraPositions[i] + "," + Command.cameraTargets[i];
- }
- _viewsAndLayersDict.Add("views", viewList);
- }
-
- _container.obj.userData = _viewsAndLayersDict;
-
- // Serialise scene
-
- //using( FileStream stream
- // = File.OpenWrite( filename ) )
- //{
- // DataContractJsonSerializer serialiser
- // = new DataContractJsonSerializer(
- // typeof( SpectaclesContainer ) );
- // serialiser.WriteObject( stream, _container );
- //}
-
- JsonSerializerSettings settings
- = new JsonSerializerSettings();
-
- settings.NullValueHandling
- = NullValueHandling.Ignore;
-
- Formatting formatting = Formatting.Indented;
-
- myjs = JsonConvert.SerializeObject(
- _container, formatting, settings);
-
-
- File.WriteAllText(_filename, myjs);
-
-#if USE_DYNAMIC_JSON
- // This saves the whole hassle of explicitly
- // defining a whole hierarchy of C# classes
- // to serialise to JSON - do it all on the
- // fly instead.
-
- // https://github.com/Spectacles/GHSpectacles/blob/master/GHSpectacles/GHSpectacles/Spectacles_geometry.cs
-
- dynamic jason = new ExpandoObject();
-
- //populate object properties
-
- jason.geometry = new ExpandoObject();
- jason.groups = new object[0];
- jason.material = matName;
- jason.position = new object[3];
- jason.position[0] = 0; jason.position[1] = 0; jason.position[2] = 0;
- jason.rotation = new object[3];
- jason.rotation[0] = 0; jason.rotation[1] = 0; jason.rotation[2] = 0;
- jason.quaternion = new object[4];
- jason.quaternion[0] = 0; jason.quaternion[1] = 0; jason.quaternion[2] = 0; jason.quaternion[3] = 0;
- jason.scale = new object[3];
- jason.scale[0] = 1; jason.scale[1] = 1; jason.scale[2] = 1;
- jason.visible = true;
- jason.castShadow = true;
- jason.receiveShadow = false;
- jason.doubleSided = true;
-
-
- //populate geometry object
- jason.geometry.metadata = new ExpandoObject();
- jason.geometry.metadata.version = 3.1;
- jason.geometry.metadata.generatedBy = "Spectacles.RevitExporter Revit Spectacles exporter";
- jason.geometry.metadata.vertices = mesh.Vertices.Count;
- jason.geometry.metadata.faces = mesh.Faces.Count;
- jason.geometry.metadata.normals = 0;
- jason.geometry.metadata.colors = 0;
- jason.geometry.metadata.uvs = 0;
- jason.geometry.metadata.materials = 0;
- jason.geometry.metadata.morphTargets = 0;
- jason.geometry.metadata.bones = 0;
-
- jason.geometry.scale = 1.000;
- jason.geometry.materials = new object[0];
- jason.geometry.vertices = new object[mesh.Vertices.Count * 3];
- jason.geometry.morphTargets = new object[0];
- jason.geometry.normals = new object[0];
- jason.geometry.colors = new object[0];
- jason.geometry.uvs = new object[0];
- jason.geometry.faces = new object[mesh.Faces.Count * 3];
- jason.geometry.bones = new object[0];
- jason.geometry.skinIndices = new object[0];
- jason.geometry.skinWeights = new object[0];
- jason.geometry.animation = new ExpandoObject();
-
- //populate vertices
- int counter = 0;
- int i = 0;
- foreach( var v in mesh.Vertices )
- {
- jason.geometry.vertices[counter++] = mesh.Vertices[i].X;
- jason.geometry.vertices[counter++] = mesh.Vertices[i].Y;
- jason.geometry.vertices[counter++] = mesh.Vertices[i].Z;
- i++;
- }
-
- //populate faces
- counter = 0;
- i = 0;
- foreach( var f in mesh.Faces )
- {
- jason.geometry.faces[counter++] = mesh.Faces[i].A;
- jason.geometry.faces[counter++] = mesh.Faces[i].B;
- jason.geometry.faces[counter++] = mesh.Faces[i].C;
- i++;
- }
-
- return JsonConvert.SerializeObject( jason );
-#endif // USE_DYNAMIC_JSON
- }
-
- public void OnPolymesh(PolymeshTopology polymesh)
- {
- //Debug.WriteLine( string.Format(
- // " OnPolymesh: {0} points, {1} facets, {2} normals {3}",
- // polymesh.NumberOfPoints,
- // polymesh.NumberOfFacets,
- // polymesh.NumberOfNormals,
- // polymesh.DistributionOfNormals ) );
-
- IList pts = polymesh.GetPoints();
-
- Transform t = CurrentTransform;
-
- pts = pts.Select(p => t.OfPoint(p)).ToList();
-
- int v1, v2, v3;
-
- foreach (PolymeshFacet facet
- in polymesh.GetFacets())
- {
- //Debug.WriteLine( string.Format(
- // " {0}: {1} {2} {3}", i++,
- // facet.V1, facet.V2, facet.V3 ) );
-
- v1 = CurrentVerticesPerMaterial.AddVertex(new PointInt(
- pts[facet.V1], _switch_coordinates));
-
- v2 = CurrentVerticesPerMaterial.AddVertex(new PointInt(
- pts[facet.V2], _switch_coordinates));
-
- v3 = CurrentVerticesPerMaterial.AddVertex(new PointInt(
- pts[facet.V3], _switch_coordinates));
-
- CurrentGeometryPerMaterial.data.faces.Add(0);
- CurrentGeometryPerMaterial.data.faces.Add(v1);
- CurrentGeometryPerMaterial.data.faces.Add(v2);
- CurrentGeometryPerMaterial.data.faces.Add(v3);
- }
- }
-
- public void OnMaterial(MaterialNode node)
- {
- //Debug.WriteLine( " --> On Material: "
- // + node.MaterialId + ": " + node.NodeName );
-
- // OnMaterial method can be invoked for every
- // single out-coming mesh even when the material
- // has not actually changed. Thus it is usually
- // beneficial to store the current material and
- // only get its attributes when the material
- // actually changes.
-
- ElementId id = node.MaterialId;
-
- if (ElementId.InvalidElementId != id)
- {
- Element m = _doc.GetElement(node.MaterialId);
- SetCurrentMaterial(m.UniqueId);
- }
- else
- {
- //string uid = Guid.NewGuid().ToString();
-
- // Generate a GUID based on colour,
- // transparency, etc. to avoid duplicating
- // non-element material definitions.
-
- int iColor = Util.ColorToInt(node.Color);
-
- string uid = string.Format("MaterialNode_{0}_{1}",
- iColor, Util.RealString(node.Transparency * 100));
-
- if (!_materials.ContainsKey(uid))
- {
- SpectaclesContainer.SpectaclesMaterial m
- = new SpectaclesContainer.SpectaclesMaterial();
-
- m.uuid = uid;
- m.type = "MeshLambertMaterial";
- m.color = iColor;
- m.ambient = m.color;
- m.emissive = 0;
- m.shading = 1;
- m.opacity = 1; // 128 - material.Transparency;
- m.opacity = 1.0 - node.Transparency; // Revit MaterialNode has double Transparency in ?range?, three.js expects opacity in [0.0,1.0]
- m.transparent = 0.0 < node.Transparency;
- m.wireframe = false;
-
- _materials.Add(uid, m);
- }
- SetCurrentMaterial(uid);
- }
- }
-
- public bool IsCanceled()
- {
- // This method is invoked many
- // times during the export process.
-
- return false;
- }
-
- public void OnDaylightPortal(DaylightPortalNode node)
- {
- Debug.WriteLine("OnDaylightPortal: " + node.NodeName);
- Asset asset = node.GetAsset();
- Debug.WriteLine("OnDaylightPortal: Asset:"
- + ((asset != null) ? asset.Name : "Null"));
- }
-
- public void OnRPC(RPCNode node)
- {
- Debug.WriteLine("OnRPC: " + node.NodeName);
- Asset asset = node.GetAsset();
- Debug.WriteLine("OnRPC: Asset:"
- + ((asset != null) ? asset.Name : "Null"));
- }
-
- public RenderNodeAction OnViewBegin(ViewNode node)
- {
- Debug.WriteLine("OnViewBegin: "
- + node.NodeName + "(" + node.ViewId.IntegerValue
- + "): LOD: " + node.LevelOfDetail);
-
- return RenderNodeAction.Proceed;
- }
-
- public void OnViewEnd(ElementId elementId)
- {
- Debug.WriteLine("OnViewEnd: Id: " + elementId.IntegerValue);
- // Note: This method is invoked even for a view that was skipped.
- }
-
- public RenderNodeAction OnElementBegin(
- ElementId elementId)
- {
- Element e = _doc.GetElement(elementId);
- string uid = e.UniqueId;
-
- Debug.WriteLine(string.Format(
- "OnElementBegin: id {0} category {1} name {2}",
- elementId.IntegerValue, e.Category.Name, e.Name));
-
- if (_objects.ContainsKey(uid))
- {
- Debug.WriteLine("\r\n*** Duplicate element!\r\n");
- return RenderNodeAction.Skip;
- }
-
- if (null == e.Category)
- {
- Debug.WriteLine("\r\n*** Non-category element!\r\n");
- return RenderNodeAction.Skip;
- }
-
- _elementStack.Push(elementId);
-
- ICollection idsMaterialGeometry = e.GetMaterialIds(false);
- ICollection idsMaterialPaint = e.GetMaterialIds(true);
-
- int n = idsMaterialGeometry.Count;
-
- if (1 < n)
- {
- Debug.Print("{0} has {1} materials: {2}",
- Util.ElementDescription(e), n,
- string.Join(", ", idsMaterialGeometry.Select(
- id => _doc.GetElement(id).Name)));
- }
-
- // We handle a current element, which may either
- // be identical to the current object and have
- // one single current geometry or have
- // multiple current child objects each with a
- // separate current geometry.
-
- _currentElement = new SpectaclesContainer.SpectaclesObject();
-
- _currentElement.name = Util.ElementDescription(e);
- _currentElement.material = _currentMaterialUid;
- _currentElement.matrix = new double[] { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 };
- _currentElement.type = "RevitElement";
- _currentElement.uuid = uid;
-
- _currentObject = new Dictionary();
- _currentGeometry = new Dictionary();
- _vertices = new Dictionary();
-
- if (null != e.Category
- && null != e.Category.Material)
- {
- SetCurrentMaterial(e.Category.Material.UniqueId);
- }
-
- return RenderNodeAction.Proceed;
- }
-
- public void OnElementEnd(
- ElementId id)
- {
- // Note: this method is invoked even for
- // elements that were skipped.
-
- Element e = _doc.GetElement(id);
- string uid = e.UniqueId;
-
- Debug.WriteLine(string.Format(
- "OnElementEnd: id {0} category {1} name {2}",
- id.IntegerValue, e.Category.Name, e.Name));
-
- if (_objects.ContainsKey(uid))
- {
- Debug.WriteLine("\r\n*** Duplicate element!\r\n");
- return;
- }
-
- if (null == e.Category)
- {
- Debug.WriteLine("\r\n*** Non-category element!\r\n");
- return;
- }
-
- List materials = _vertices.Keys.ToList();
-
- int n = materials.Count;
-
- _currentElement.children = new List(n);
-
- foreach (string material in materials)
- {
- SpectaclesContainer.SpectaclesObject obj = _currentObject[material];
- SpectaclesContainer.SpectaclesGeometry geo = _currentGeometry[material];
-
- foreach (KeyValuePair p in _vertices[material])
- {
- geo.data.vertices.Add(_scale_vertex * p.Key.X);
- geo.data.vertices.Add(_scale_vertex * p.Key.Y);
- geo.data.vertices.Add(_scale_vertex * p.Key.Z);
- }
- obj.geometry = geo.uuid;
- _geometries.Add(geo.uuid, geo);
- _currentElement.children.Add(obj);
- }
-
- //Dictionary d = Util.GetElementProperties(e, true);
- Dictionary d = new Dictionary();
- if (Command._filterParameters)
- {
- d = Util.GetElementFilteredProperties(e, true);
- }
- else
- {
- d = Util.GetElementProperties(e, true);
- }
-
- string layerName = e.Category.Name;
-
- //add a property for layer
- d.Add("layer", layerName);
-
-
-
- if (!_viewsAndLayersDict.ContainsKey("layers")) _viewsAndLayersDict.Add("layers", layerName);
- else
- {
- if (!layerList.Contains(layerName))
- {
- _viewsAndLayersDict["layers"] += "," + layerName;
- }
- }
-
- if (!layerList.Contains(layerName)) layerList.Add(layerName);
-
- _currentElement.userData = d;
-
- //also add guid to user data dict
- _currentElement.userData.Add("revit_id", uid);
-
- _objects.Add(_currentElement.uuid, _currentElement);
-
- _elementStack.Pop();
- }
-
- public RenderNodeAction OnFaceBegin(FaceNode node)
- {
- // This method is invoked only if the
- // custom exporter was set to include faces.
-
- Debug.Assert(false, "we set exporter.IncludeFaces false");
- Debug.WriteLine(" OnFaceBegin: " + node.NodeName);
- return RenderNodeAction.Proceed;
- }
-
- public void OnFaceEnd(FaceNode node)
- {
- // This method is invoked only if the
- // custom exporter was set to include faces.
-
- Debug.Assert(false, "we set exporter.IncludeFaces false");
- Debug.WriteLine(" OnFaceEnd: " + node.NodeName);
- // Note: This method is invoked even for faces that were skipped.
- }
-
- public RenderNodeAction OnInstanceBegin(InstanceNode node)
- {
- Debug.WriteLine(" OnInstanceBegin: " + node.NodeName + " symbol: " + node.GetSymbolId().IntegerValue);
- // This method marks the start of processing a family instance
- _transformationStack.Push(CurrentTransform.Multiply(node.GetTransform()));
-
- // We can either skip this instance or proceed with rendering it.
- return RenderNodeAction.Proceed;
- }
-
- public void OnInstanceEnd(InstanceNode node)
- {
- Debug.WriteLine(" OnInstanceEnd: " + node.NodeName);
- // Note: This method is invoked even for instances that were skipped.
- _transformationStack.Pop();
- }
-
- public RenderNodeAction OnLinkBegin(LinkNode node)
- {
- Debug.WriteLine(" OnLinkBegin: " + node.NodeName + " Document: " + node.GetDocument().Title + ": Id: " + node.GetSymbolId().IntegerValue);
- _transformationStack.Push(CurrentTransform.Multiply(node.GetTransform()));
- return RenderNodeAction.Proceed;
- }
-
- public void OnLinkEnd(LinkNode node)
- {
- Debug.WriteLine(" OnLinkEnd: " + node.NodeName);
- // Note: This method is invoked even for instances that were skipped.
- _transformationStack.Pop();
- }
-
- public void OnLight(LightNode node)
- {
- Debug.WriteLine("OnLight: " + node.NodeName);
- Asset asset = node.GetAsset();
- Debug.WriteLine("OnLight: Asset:" + ((asset != null) ? asset.Name : "Null"));
- }
- }
-}
+//The MIT License (MIT)
+
+//Those portions created by va3c authors are provided with the following copyright:
+
+//Copyright (c) 2014 va3c
+
+//Those portions created by Thornton Tomasetti employees are provided with the following copyright:
+
+//Copyright (c) 2015 Thornton Tomasetti
+
+//Permission is hereby granted, free of charge, to any person obtaining a copy
+//of this software and associated documentation files (the "Software"), to deal
+//in the Software without restriction, including without limitation the rights
+//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//copies of the Software, and to permit persons to whom the Software is
+//furnished to do so, subject to the following conditions:
+
+//The above copyright notice and this permission notice shall be included in all
+//copies or substantial portions of the Software.
+
+//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+//SOFTWARE.
+
+#region Namespaces
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Diagnostics;
+using System.IO;
+using Autodesk.Revit.DB;
+using Autodesk.Revit.Utility;
+using Newtonsoft.Json;
+#endregion // Namespaces
+
+namespace Spectacles.RevitExporter
+{
+ // Done:
+ // Check instance transformation
+ // Support transparency
+ // Add scaling for Theo [(0,0),(20000,20000)]
+ // Implement the external application button
+ // Implement element properties
+ // Eliminate multiple materials
+ // Prompt user for output file name and location
+ // Eliminate null element properties, i.e. useless
+ // JSON userData entries
+ // Todo:
+ // Check for file size
+ // Instance/type reuse
+
+ public class SpectaclesExportContext : IExportContext
+ {
+ ///
+ /// Scale entire top level BIM object nove in JSON
+ /// output. A scale of 1.0 will output the model in
+ /// millimetres. Currently we scale it to decimetres
+ /// so that a typical model has a chance of fitting
+ /// into a cube with side length 100, i.e. 10 metres.
+ ///
+ double _scale_bim = 1.0;
+
+ ///
+ /// Scale applied to each vertex in each individual
+ /// BIM element. This can be used to scale the model
+ /// down from millimetres to metres, e.g.
+ /// Currently we stick with millimetres after all
+ /// at this level.
+ ///
+ double _scale_vertex = 1.0;
+
+ ///
+ /// If true, switch Y and Z coordinate
+ /// and flip X to negative to convert from
+ /// Revit coordinate system to standard 3d
+ /// computer graphics coordinate system with
+ /// Z pointing out of screen, X towards right,
+ /// Y up.
+ ///
+ bool _switch_coordinates = true;
+
+ #region VertexLookupXyz
+ ///
+ /// A vertex lookup class to eliminate
+ /// duplicate vertex definitions.
+ ///
+ class VertexLookupXyz : Dictionary
+ {
+ #region XyzEqualityComparer
+ ///
+ /// Define equality for Revit XYZ points.
+ /// Very rough tolerance, as used by Revit itself.
+ ///
+ class XyzEqualityComparer : IEqualityComparer
+ {
+ const double _sixteenthInchInFeet
+ = 1.0 / (16.0 * 12.0);
+
+ public bool Equals(XYZ p, XYZ q)
+ {
+ return p.IsAlmostEqualTo(q,
+ _sixteenthInchInFeet);
+ }
+
+ public int GetHashCode(XYZ p)
+ {
+ return Util.PointString(p).GetHashCode();
+ }
+ }
+ #endregion // XyzEqualityComparer
+
+ public VertexLookupXyz()
+ : base(new XyzEqualityComparer())
+ {
+ }
+
+ ///
+ /// Return the index of the given vertex,
+ /// adding a new entry if required.
+ ///
+ public int AddVertex(XYZ p)
+ {
+ return ContainsKey(p)
+ ? this[p]
+ : this[p] = Count;
+ }
+ }
+ #endregion // VertexLookupXyz
+
+ #region VertexLookupInt
+ ///
+ /// An integer-based 3D point class.
+ ///
+ class PointInt : IComparable
+ {
+ public long X { get; set; }
+ public long Y { get; set; }
+ public long Z { get; set; }
+
+ //public PointInt( int x, int y, int z )
+ //{
+ // X = x;
+ // Y = y;
+ // Z = z;
+ //}
+
+ ///
+ /// Consider a Revit length zero
+ /// if is smaller than this.
+ ///
+ const double _eps = 1.0e-9;
+
+ ///
+ /// Conversion factor from feet to millimetres.
+ ///
+ const double _feet_to_mm = 25.4 * 12;
+
+ ///
+ /// Conversion a given length value
+ /// from feet to millimetre.
+ ///
+ static long ConvertFeetToMillimetres(double d)
+ {
+ if (0 < d)
+ {
+ return _eps > d
+ ? 0
+ : (long)(_feet_to_mm * d + 0.5);
+
+ }
+ else
+ {
+ return _eps > -d
+ ? 0
+ : (long)(_feet_to_mm * d - 0.5);
+
+ }
+ }
+
+ public PointInt(XYZ p, bool switch_coordinates)
+ {
+ X = ConvertFeetToMillimetres(p.X);
+ Y = ConvertFeetToMillimetres(p.Y);
+ Z = ConvertFeetToMillimetres(p.Z);
+
+ if (switch_coordinates)
+ {
+ X = -X;
+ long tmp = Y;
+ Y = Z;
+ Z = tmp;
+ }
+ }
+
+ public int CompareTo(PointInt a)
+ {
+ long d = X - a.X;
+
+ if (0 == d)
+ {
+ d = Y - a.Y;
+
+ if (0 == d)
+ {
+ d = Z - a.Z;
+ }
+ }
+ return (0 == d) ? 0 : ((0 < d) ? 1 : -1);
+ }
+ }
+
+ ///
+ /// A vertex lookup class to eliminate
+ /// duplicate vertex definitions.
+ ///
+ class VertexLookupInt : Dictionary
+ {
+ #region PointIntEqualityComparer
+ ///
+ /// Define equality for integer-based PointInt.
+ ///
+ class PointIntEqualityComparer : IEqualityComparer
+ {
+ public bool Equals(PointInt p, PointInt q)
+ {
+ return 0 == p.CompareTo(q);
+ }
+
+ public int GetHashCode(PointInt p)
+ {
+ return (p.X.ToString()
+ + "," + p.Y.ToString()
+ + "," + p.Z.ToString())
+ .GetHashCode();
+ }
+ }
+ #endregion // PointIntEqualityComparer
+
+ public VertexLookupInt()
+ : base(new PointIntEqualityComparer())
+ {
+ }
+
+ ///
+ /// Return the index of the given vertex,
+ /// adding a new entry if required.
+ ///
+ public int AddVertex(PointInt p)
+ {
+ return ContainsKey(p)
+ ? this[p]
+ : this[p] = Count;
+ }
+ }
+ #endregion // VertexLookupInt
+
+ Document _doc;
+ Document _currentDoc;
+ string _filename;
+ SpectaclesContainer _container;
+ Dictionary _materials;
+ Dictionary _objects;
+ Dictionary _geometries;
+ Dictionary _viewsAndLayersDict;
+ List layerList;
+
+ SpectaclesContainer.SpectaclesObject _currentElement;
+
+ // Keyed on material uid to handle several materials per element:
+
+ Dictionary _currentObject;
+ Dictionary _currentGeometry;
+ Dictionary _vertices;
+
+ Stack _elementStack = new Stack();
+ Stack _transformationStack = new Stack();
+
+ string _currentMaterialUid;
+
+ public string myjs = null;
+
+ SpectaclesContainer.SpectaclesObject CurrentObjectPerMaterial
+ {
+ get
+ {
+ return _currentObject[_currentMaterialUid];
+ }
+ }
+
+ SpectaclesContainer.SpectaclesGeometry CurrentGeometryPerMaterial
+ {
+ get
+ {
+ return _currentGeometry[_currentMaterialUid];
+ }
+ }
+
+ VertexLookupInt CurrentVerticesPerMaterial
+ {
+ get
+ {
+ return _vertices[_currentMaterialUid];
+ }
+ }
+
+ Transform CurrentTransform
+ {
+ get
+ {
+ return _transformationStack.Peek();
+ }
+ }
+
+ public override string ToString()
+ {
+ return myjs;
+ }
+ ///
+ /// Set the current material
+ ///
+ void SetCurrentMaterial(string uidMaterial)
+ {
+ if (!_materials.ContainsKey(uidMaterial))
+ {
+ Material material = _currentDoc.GetElement(
+ uidMaterial) as Material;
+
+ SpectaclesContainer.SpectaclesMaterial m
+ = new SpectaclesContainer.SpectaclesMaterial();
+
+ //m.metadata = new SpectaclesContainer.SpectaclesMaterialMetadata();
+ //m.metadata.type = "material";
+ //m.metadata.version = 4.2;
+ //m.metadata.generator = "Spectacles.RevitExporter 2015.0.0.0";
+
+ m.uuid = uidMaterial;
+ m.name = material.Name;
+ m.type = "MeshLambertMaterial";
+ m.color = Util.ColorToInt(material.Color);
+ m.ambient = m.color;
+ m.emissive = 0;
+ m.opacity = 0.01 * (double)(100 - material.Transparency); // Revit has material.Transparency in [0,100], three.js expects opacity in [0.0,1.0]
+ m.transparent = 0 < material.Transparency;
+ m.shading = 1;
+ m.wireframe = false;
+
+ _materials.Add(uidMaterial, m);
+ }
+ _currentMaterialUid = uidMaterial;
+
+ string uid_per_material = _currentElement.uuid + "-" + uidMaterial;
+
+ if (!_currentObject.ContainsKey(uidMaterial))
+ {
+ Debug.Assert(!_currentGeometry.ContainsKey(uidMaterial), "expected same keys in both");
+
+ _currentObject.Add(uidMaterial, new SpectaclesContainer.SpectaclesObject());
+ CurrentObjectPerMaterial.name = _currentElement.name;
+ CurrentObjectPerMaterial.geometry = uid_per_material;
+ CurrentObjectPerMaterial.material = _currentMaterialUid;
+ CurrentObjectPerMaterial.matrix = new double[] { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 };
+ CurrentObjectPerMaterial.type = "Mesh";
+ CurrentObjectPerMaterial.uuid = uid_per_material;
+ }
+
+ if (!_currentGeometry.ContainsKey(uidMaterial))
+ {
+ _currentGeometry.Add(uidMaterial, new SpectaclesContainer.SpectaclesGeometry());
+ CurrentGeometryPerMaterial.uuid = uid_per_material;
+ CurrentGeometryPerMaterial.type = "Geometry";
+ CurrentGeometryPerMaterial.data = new SpectaclesContainer.SpectaclesGeometryData();
+ CurrentGeometryPerMaterial.data.faces = new List();
+ CurrentGeometryPerMaterial.data.vertices = new List();
+ CurrentGeometryPerMaterial.data.normals = new List();
+ CurrentGeometryPerMaterial.data.uvs = new List();
+ CurrentGeometryPerMaterial.data.visible = true;
+ CurrentGeometryPerMaterial.data.castShadow = true;
+ CurrentGeometryPerMaterial.data.receiveShadow = false;
+ CurrentGeometryPerMaterial.data.doubleSided = true;
+ CurrentGeometryPerMaterial.data.scale = 1.0;
+ }
+
+ if (!_vertices.ContainsKey(uidMaterial))
+ {
+ _vertices.Add(uidMaterial, new VertexLookupInt());
+ }
+ }
+
+ public SpectaclesExportContext(Document document, string filename)
+ {
+ _doc = document;
+ _currentDoc = document;
+ _filename = filename;
+ }
+
+ public bool Start()
+ {
+ _materials = new Dictionary();
+ _geometries = new Dictionary();
+ _objects = new Dictionary();
+
+ _viewsAndLayersDict = new Dictionary();
+ layerList = new List();
+
+ _transformationStack.Push(Transform.Identity);
+
+ _container = new SpectaclesContainer();
+
+ _container.metadata = new SpectaclesContainer.Metadata();
+ _container.metadata.type = "Object";
+ _container.metadata.version = 4.3;
+ _container.metadata.generator = "Spectacles.RevitExporter Revit Spectacles exporter";
+ _container.geometries = new List();
+
+ _container.obj = new SpectaclesContainer.SpectaclesObject();
+ _container.obj.uuid = _currentDoc.ActiveView.UniqueId;
+ _container.obj.name = "BIM " + _currentDoc.Title;
+ _container.obj.type = "Scene";
+
+ // Scale entire BIM from millimetres to metres.
+
+ _container.obj.matrix = new double[] {
+ _scale_bim, 0, 0, 0,
+ 0, _scale_bim, 0, 0,
+ 0, 0, _scale_bim, 0,
+ 0, 0, 0, _scale_bim };
+
+ return true;
+ }
+
+ public void Finish()
+ {
+ // Finish populating scene
+
+ _container.materials = _materials.Values.ToList();
+
+ _container.geometries = _geometries.Values.ToList();
+
+ _container.obj.children = _objects.Values.ToList();
+
+ if (ExportOptions.includeViews && Command.cameraNames.Count>0)
+ {
+ //create an empty string to append the list of views
+ string viewList = Command.cameraNames[0] + "," + Command.cameraPositions[0] + "," + Command.cameraTargets[0];
+ for (int i = 1; i < Command.cameraPositions.Count; i++)
+ {
+ viewList += "," + Command.cameraNames[i] + "," + Command.cameraPositions[i] + "," + Command.cameraTargets[i];
+ }
+ _viewsAndLayersDict.Add("views", viewList);
+ }
+
+ _container.obj.userData = _viewsAndLayersDict;
+
+ // Serialise scene
+
+ //using( FileStream stream
+ // = File.OpenWrite( filename ) )
+ //{
+ // DataContractJsonSerializer serialiser
+ // = new DataContractJsonSerializer(
+ // typeof( SpectaclesContainer ) );
+ // serialiser.WriteObject( stream, _container );
+ //}
+
+ JsonSerializerSettings settings
+ = new JsonSerializerSettings();
+
+ settings.NullValueHandling
+ = NullValueHandling.Ignore;
+
+ Formatting formatting = Formatting.None;
+
+ myjs = JsonConvert.SerializeObject(
+ _container, formatting, settings);
+
+
+ File.WriteAllText(_filename, myjs);
+
+#if USE_DYNAMIC_JSON
+ // This saves the whole hassle of explicitly
+ // defining a whole hierarchy of C# classes
+ // to serialise to JSON - do it all on the
+ // fly instead.
+
+ // https://github.com/Spectacles/GHSpectacles/blob/master/GHSpectacles/GHSpectacles/Spectacles_geometry.cs
+
+ dynamic jason = new ExpandoObject();
+
+ //populate object properties
+
+ jason.geometry = new ExpandoObject();
+ jason.groups = new object[0];
+ jason.material = matName;
+ jason.position = new object[3];
+ jason.position[0] = 0; jason.position[1] = 0; jason.position[2] = 0;
+ jason.rotation = new object[3];
+ jason.rotation[0] = 0; jason.rotation[1] = 0; jason.rotation[2] = 0;
+ jason.quaternion = new object[4];
+ jason.quaternion[0] = 0; jason.quaternion[1] = 0; jason.quaternion[2] = 0; jason.quaternion[3] = 0;
+ jason.scale = new object[3];
+ jason.scale[0] = 1; jason.scale[1] = 1; jason.scale[2] = 1;
+ jason.visible = true;
+ jason.castShadow = true;
+ jason.receiveShadow = false;
+ jason.doubleSided = true;
+
+
+ //populate geometry object
+ jason.geometry.metadata = new ExpandoObject();
+ jason.geometry.metadata.version = 3.1;
+ jason.geometry.metadata.generatedBy = "Spectacles.RevitExporter Revit Spectacles exporter";
+ jason.geometry.metadata.vertices = mesh.Vertices.Count;
+ jason.geometry.metadata.faces = mesh.Faces.Count;
+ jason.geometry.metadata.normals = 0;
+ jason.geometry.metadata.colors = 0;
+ jason.geometry.metadata.uvs = 0;
+ jason.geometry.metadata.materials = 0;
+ jason.geometry.metadata.morphTargets = 0;
+ jason.geometry.metadata.bones = 0;
+
+ jason.geometry.scale = 1.000;
+ jason.geometry.materials = new object[0];
+ jason.geometry.vertices = new object[mesh.Vertices.Count * 3];
+ jason.geometry.morphTargets = new object[0];
+ jason.geometry.normals = new object[0];
+ jason.geometry.colors = new object[0];
+ jason.geometry.uvs = new object[0];
+ jason.geometry.faces = new object[mesh.Faces.Count * 3];
+ jason.geometry.bones = new object[0];
+ jason.geometry.skinIndices = new object[0];
+ jason.geometry.skinWeights = new object[0];
+ jason.geometry.animation = new ExpandoObject();
+
+ //populate vertices
+ int counter = 0;
+ int i = 0;
+ foreach( var v in mesh.Vertices )
+ {
+ jason.geometry.vertices[counter++] = mesh.Vertices[i].X;
+ jason.geometry.vertices[counter++] = mesh.Vertices[i].Y;
+ jason.geometry.vertices[counter++] = mesh.Vertices[i].Z;
+ i++;
+ }
+
+ //populate faces
+ counter = 0;
+ i = 0;
+ foreach( var f in mesh.Faces )
+ {
+ jason.geometry.faces[counter++] = mesh.Faces[i].A;
+ jason.geometry.faces[counter++] = mesh.Faces[i].B;
+ jason.geometry.faces[counter++] = mesh.Faces[i].C;
+ i++;
+ }
+
+ return JsonConvert.SerializeObject( jason );
+#endif // USE_DYNAMIC_JSON
+ }
+
+ public void OnPolymesh(PolymeshTopology polymesh)
+ {
+ //Debug.WriteLine( string.Format(
+ // " OnPolymesh: {0} points, {1} facets, {2} normals {3}",
+ // polymesh.NumberOfPoints,
+ // polymesh.NumberOfFacets,
+ // polymesh.NumberOfNormals,
+ // polymesh.DistributionOfNormals ) );
+
+ IList pts = polymesh.GetPoints();
+
+ Transform t = CurrentTransform;
+
+ pts = pts.Select(p => t.OfPoint(p)).ToList();
+
+ int v1, v2, v3;
+
+ foreach (PolymeshFacet facet
+ in polymesh.GetFacets())
+ {
+ //Debug.WriteLine( string.Format(
+ // " {0}: {1} {2} {3}", i++,
+ // facet.V1, facet.V2, facet.V3 ) );
+
+ v1 = CurrentVerticesPerMaterial.AddVertex(new PointInt(
+ pts[facet.V1], _switch_coordinates));
+
+ v2 = CurrentVerticesPerMaterial.AddVertex(new PointInt(
+ pts[facet.V2], _switch_coordinates));
+
+ v3 = CurrentVerticesPerMaterial.AddVertex(new PointInt(
+ pts[facet.V3], _switch_coordinates));
+
+ CurrentGeometryPerMaterial.data.faces.Add(0);
+ CurrentGeometryPerMaterial.data.faces.Add(v1);
+ CurrentGeometryPerMaterial.data.faces.Add(v2);
+ CurrentGeometryPerMaterial.data.faces.Add(v3);
+ }
+ }
+
+ public void OnMaterial(MaterialNode node)
+ {
+ //Debug.WriteLine( " --> On Material: "
+ // + node.MaterialId + ": " + node.NodeName );
+
+ // OnMaterial method can be invoked for every
+ // single out-coming mesh even when the material
+ // has not actually changed. Thus it is usually
+ // beneficial to store the current material and
+ // only get its attributes when the material
+ // actually changes.
+
+ ElementId id = node.MaterialId;
+
+ if (ElementId.InvalidElementId != id)
+ {
+ Element m = _currentDoc.GetElement(node.MaterialId);
+ SetCurrentMaterial(m.UniqueId);
+ }
+ else
+ {
+ //string uid = Guid.NewGuid().ToString();
+
+ // Generate a GUID based on colour,
+ // transparency, etc. to avoid duplicating
+ // non-element material definitions.
+
+ int iColor = Util.ColorToInt(node.Color);
+
+ string uid = string.Format("MaterialNode_{0}_{1}",
+ iColor, Util.RealString(node.Transparency * 100));
+
+ if (!_materials.ContainsKey(uid))
+ {
+ SpectaclesContainer.SpectaclesMaterial m
+ = new SpectaclesContainer.SpectaclesMaterial();
+
+ m.uuid = uid;
+ m.type = "MeshLambertMaterial";
+ m.color = iColor;
+ m.ambient = m.color;
+ m.emissive = 0;
+ m.shading = 1;
+ m.opacity = 1; // 128 - material.Transparency;
+ m.opacity = 1.0 - node.Transparency; // Revit MaterialNode has double Transparency in ?range?, three.js expects opacity in [0.0,1.0]
+ m.transparent = 0.0 < node.Transparency;
+ m.wireframe = false;
+
+ _materials.Add(uid, m);
+ }
+ SetCurrentMaterial(uid);
+ }
+ }
+
+ public bool IsCanceled()
+ {
+ // This method is invoked many
+ // times during the export process.
+
+ return false;
+ }
+
+ public void OnDaylightPortal(DaylightPortalNode node)
+ {
+ Debug.WriteLine("OnDaylightPortal: " + node.NodeName);
+ Asset asset = node.GetAsset();
+ Debug.WriteLine("OnDaylightPortal: Asset:"
+ + ((asset != null) ? asset.Name : "Null"));
+ }
+
+ public void OnRPC(RPCNode node)
+ {
+ Debug.WriteLine("OnRPC: " + node.NodeName);
+ Asset asset = node.GetAsset();
+ Debug.WriteLine("OnRPC: Asset:"
+ + ((asset != null) ? asset.Name : "Null"));
+ }
+
+ public RenderNodeAction OnViewBegin(ViewNode node)
+ {
+ Debug.WriteLine("OnViewBegin: "
+ + node.NodeName + "(" + node.ViewId.IntegerValue
+ + "): LOD: " + node.LevelOfDetail);
+
+ return RenderNodeAction.Proceed;
+ }
+
+ public void OnViewEnd(ElementId elementId)
+ {
+ Debug.WriteLine("OnViewEnd: Id: " + elementId.IntegerValue);
+ // Note: This method is invoked even for a view that was skipped.
+ }
+
+ public RenderNodeAction OnElementBegin(
+ ElementId elementId)
+ {
+ Element e = _currentDoc.GetElement(elementId);
+
+ // note: because of links and that the linked models might have had the same template, we need to
+ // make this further unique...
+ string uid = e.UniqueId + "_" + _currentDoc.Title;
+
+ Debug.WriteLine(string.Format(
+ "OnElementBegin: id {0} category {1} name {2}",
+ elementId.IntegerValue, e.Category.Name, e.Name));
+
+
+
+ if (_objects.ContainsKey(uid))
+ {
+ Debug.WriteLine("\r\n*** Duplicate element!\r\n");
+ return RenderNodeAction.Skip;
+ }
+
+ if (null == e.Category)
+ {
+ Debug.WriteLine("\r\n*** Non-category element!\r\n");
+ return RenderNodeAction.Skip;
+ }
+
+ _elementStack.Push(elementId);
+
+ ICollection idsMaterialGeometry = e.GetMaterialIds(false);
+ ICollection idsMaterialPaint = e.GetMaterialIds(true);
+
+ int n = idsMaterialGeometry.Count;
+
+ if (1 < n)
+ {
+ Debug.Print("{0} has {1} materials: {2}",
+ Util.ElementDescription(e), n,
+ string.Join(", ", idsMaterialGeometry.Select(
+ id => _currentDoc.GetElement(id).Name)));
+ }
+
+ // We handle a current element, which may either
+ // be identical to the current object and have
+ // one single current geometry or have
+ // multiple current child objects each with a
+ // separate current geometry.
+
+ _currentElement = new SpectaclesContainer.SpectaclesObject();
+
+ _currentElement.name = Util.ElementDescription(e);
+ _currentElement.material = _currentMaterialUid;
+ _currentElement.matrix = new double[] { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 };
+ _currentElement.type = "RevitElement";
+ _currentElement.uuid = uid;
+
+ _currentObject = new Dictionary();
+ _currentGeometry = new Dictionary();
+ _vertices = new Dictionary();
+
+ if (null != e.Category
+ && null != e.Category.Material)
+ {
+ SetCurrentMaterial(e.Category.Material.UniqueId);
+ }
+
+ return RenderNodeAction.Proceed;
+ }
+
+ public void OnElementEnd(
+ ElementId id)
+ {
+ // Note: this method is invoked even for
+ // elements that were skipped.
+
+ Element e = _currentDoc.GetElement(id);
+ string uid = e.UniqueId;
+
+ Debug.WriteLine(string.Format(
+ "OnElementEnd: id {0} category {1} name {2}",
+ id.IntegerValue, e.Category.Name, e.Name));
+
+ if (_elementStack.Contains(id) == false) return; // it was skipped?
+
+ if (_objects.ContainsKey(uid))
+ {
+ Debug.WriteLine("\r\n*** Duplicate element!\r\n");
+ return;
+ }
+
+ if (null == e.Category)
+ {
+ Debug.WriteLine("\r\n*** Non-category element!\r\n");
+ return;
+ }
+
+ List materials = _vertices.Keys.ToList();
+
+ int n = materials.Count;
+
+ _currentElement.children = new List(n);
+
+ foreach (string material in materials)
+ {
+ SpectaclesContainer.SpectaclesObject obj = _currentObject[material];
+ SpectaclesContainer.SpectaclesGeometry geo = _currentGeometry[material];
+
+ foreach (KeyValuePair p in _vertices[material])
+ {
+ geo.data.vertices.Add(_scale_vertex * p.Key.X);
+ geo.data.vertices.Add(_scale_vertex * p.Key.Y);
+ geo.data.vertices.Add(_scale_vertex * p.Key.Z);
+ }
+ obj.geometry = geo.uuid;
+
+ //QUESTION: Should we attempt to further ensure uniqueness? or should we just update the geometry that is there?
+ //old: _geometries.Add(geo.uuid, geo);
+ _geometries[geo.uuid] = geo;
+ _currentElement.children.Add(obj);
+ }
+
+ //Dictionary d = Util.GetElementProperties(e, true);
+ Dictionary d = new Dictionary();
+ if (Command._filterParameters)
+ {
+ d = Util.GetElementFilteredProperties(e, true);
+ }
+ else
+ {
+ d = Util.GetElementProperties(e, true);
+ }
+
+ string layerName = e.Category.Name;
+
+ //add a property for layer
+ d.Add("layer", layerName);
+
+
+
+ if (!_viewsAndLayersDict.ContainsKey("layers")) _viewsAndLayersDict.Add("layers", layerName);
+ else
+ {
+ if (!layerList.Contains(layerName))
+ {
+ _viewsAndLayersDict["layers"] += "," + layerName;
+ }
+ }
+
+ if (!layerList.Contains(layerName)) layerList.Add(layerName);
+
+ _currentElement.userData = d;
+
+ //also add guid to user data dict
+ _currentElement.userData.Add("revit_id", uid);
+
+ _objects[_currentElement.uuid] = _currentElement;
+
+ _elementStack.Pop();
+ }
+
+ public RenderNodeAction OnFaceBegin(FaceNode node)
+ {
+ // This method is invoked only if the
+ // custom exporter was set to include faces.
+
+ Debug.Assert(false, "we set exporter.IncludeFaces false");
+ Debug.WriteLine(" OnFaceBegin: " + node.NodeName);
+ return RenderNodeAction.Proceed;
+ }
+
+ public void OnFaceEnd(FaceNode node)
+ {
+ // This method is invoked only if the
+ // custom exporter was set to include faces.
+
+ Debug.Assert(false, "we set exporter.IncludeFaces false");
+ Debug.WriteLine(" OnFaceEnd: " + node.NodeName);
+ // Note: This method is invoked even for faces that were skipped.
+ }
+
+ public RenderNodeAction OnInstanceBegin(InstanceNode node)
+ {
+ Debug.WriteLine(" OnInstanceBegin: " + node.NodeName + " symbol: " + node.GetSymbolId().IntegerValue);
+ // This method marks the start of processing a family instance
+ _transformationStack.Push(CurrentTransform.Multiply(node.GetTransform()));
+
+ // We can either skip this instance or proceed with rendering it.
+ return RenderNodeAction.Proceed;
+ }
+
+ public void OnInstanceEnd(InstanceNode node)
+ {
+ Debug.WriteLine(" OnInstanceEnd: " + node.NodeName);
+ // Note: This method is invoked even for instances that were skipped.
+ _transformationStack.Pop();
+ }
+
+ public RenderNodeAction OnLinkBegin(LinkNode node)
+ {
+ Debug.WriteLine(" OnLinkBegin: " + node.NodeName + " Document: " + node.GetDocument().Title + ": Id: " + node.GetSymbolId().IntegerValue);
+ _currentDoc = node.GetDocument();
+ _transformationStack.Push(CurrentTransform.Multiply(node.GetTransform()));
+ return RenderNodeAction.Proceed;
+ }
+
+ public void OnLinkEnd(LinkNode node)
+ {
+ Debug.WriteLine(" OnLinkEnd: " + node.NodeName);
+ // reset for the original document
+ _currentDoc = _doc;
+
+ // Note: This method is invoked even for instances that were skipped.
+ _transformationStack.Pop();
+ }
+
+ public void OnLight(LightNode node)
+ {
+ Debug.WriteLine("OnLight: " + node.NodeName);
+ Asset asset = node.GetAsset();
+ Debug.WriteLine("OnLight: Asset:" + ((asset != null) ? asset.Name : "Null"));
+ }
+ }
+}
diff --git a/src/Spectacles.RevitExporter/packages.config b/src/Spectacles.RevitExporter/packages.config
index 945d431..55658b8 100644
--- a/src/Spectacles.RevitExporter/packages.config
+++ b/src/Spectacles.RevitExporter/packages.config
@@ -1,4 +1,4 @@
-
-
-
+
+
+
\ No newline at end of file