diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1e1cd132c..22541dfa7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -49,6 +49,7 @@
- `Profile.ThickenedEdgePolygons`
- `Elements.MEP`
- `GeometricElement.RepresentationInstances`
+- `ContentRepresentation`
### Fixed
diff --git a/Elements/src/CoreModels/ElementRepresentation.cs b/Elements/src/CoreModels/ElementRepresentation.cs
index bd18b5cd4..535badb4e 100644
--- a/Elements/src/CoreModels/ElementRepresentation.cs
+++ b/Elements/src/CoreModels/ElementRepresentation.cs
@@ -2,7 +2,7 @@
using Elements;
using Elements.Geometry;
using glTFLoader.Schema;
-using System;
+using Elements.Serialization.glTF;
///
/// The element's representation
@@ -13,13 +13,18 @@ public abstract class ElementRepresentation : SharedObject
/// Get graphics buffers and other metadata required to modify a GLB.
///
/// The element with this representation.
- /// The list of graphc buffers.
+ /// The list of graphic buffers.
/// The buffer id. It will be used as a primitive name.
/// The gltf primitive mode
///
- /// True if there is graphicsbuffers data applicable to add, false otherwise.
+ /// True if there is graphics buffers data applicable to add, false otherwise.
/// Out variables should be ignored if the return value is false.
///
public abstract bool TryToGraphicsBuffers(GeometricElement element, out List graphicsBuffers,
out string id, out MeshPrimitive.ModeEnum? mode);
+
+ internal virtual List GetNodeExtensions(GeometricElement element)
+ {
+ return new List();
+ }
}
\ No newline at end of file
diff --git a/Elements/src/Representations/ContentRepresentation.cs b/Elements/src/Representations/ContentRepresentation.cs
new file mode 100644
index 000000000..2ec792384
--- /dev/null
+++ b/Elements/src/Representations/ContentRepresentation.cs
@@ -0,0 +1,87 @@
+using System.Collections.Generic;
+using Elements.Geometry;
+using Elements.Geometry.Solids;
+using Elements.Serialization.glTF;
+using Elements.Utilities;
+using glTFLoader.Schema;
+
+namespace Elements
+{
+ ///
+ /// Represents an element as a reference to a GLB file location within the content catalog.
+ ///
+ public class ContentRepresentation : ElementRepresentation
+ {
+ /// The URI of the glb for this element.
+ public string GlbLocation { get; set; }
+
+ /// The bounding box of the content.
+ public BBox3 BoundingBox { get; set; }
+
+ ///
+ /// Initializes a new instance of ContentRepresentation class
+ ///
+ /// The URI of the glb for this element.
+ /// The bounding box of the content.
+ public ContentRepresentation(string glbLocation, BBox3 boundingBox)
+ {
+ GlbLocation = glbLocation;
+ BoundingBox = boundingBox;
+ }
+
+ ///
+ /// Initializes a new instance of ContentRepresentation class
+ ///
+ /// The URI of the glb for this element.
+ public ContentRepresentation(string glbLocation) : this(glbLocation, default) { }
+
+ ///
+ public override bool TryToGraphicsBuffers(GeometricElement element, out List graphicsBuffers, out string id, out MeshPrimitive.ModeEnum? mode)
+ {
+ id = element.Id + "_mesh";
+
+ graphicsBuffers = new List();
+ mode = MeshPrimitive.ModeEnum.TRIANGLES;
+
+ if (!BoundingBox.IsValid() || BoundingBox.IsDegenerate())
+ {
+ return true;
+ }
+
+ var bottomProfile = new Geometry.Polygon(new List{
+ new Vector3(BoundingBox.Min.X, BoundingBox.Min.Y, BoundingBox.Min.Z),
+ new Vector3(BoundingBox.Min.X, BoundingBox.Max.Y, BoundingBox.Min.Z),
+ new Vector3(BoundingBox.Max.X, BoundingBox.Max.Y, BoundingBox.Min.Z),
+ new Vector3(BoundingBox.Max.X, BoundingBox.Min.Y, BoundingBox.Min.Z),
+ });
+
+ var height = BoundingBox.Max.Z - BoundingBox.Min.Z;
+ var boxSolid = new Extrude(bottomProfile, height, Vector3.ZAxis, false);
+
+ var csg = SolidOperationUtils.GetFinalCsgFromSolids(new List() { boxSolid }, element, false);
+
+ if (csg == null)
+ {
+ return false;
+ }
+
+ GraphicsBuffers buffers = null;
+ buffers = csg.Tessellate();
+
+ if (buffers.Vertices.Count == 0)
+ {
+ return false;
+ }
+
+ graphicsBuffers.Add(buffers);
+ return true;
+ }
+
+ internal override List GetNodeExtensions(GeometricElement element)
+ {
+ var extensions = base.GetNodeExtensions(element);
+ extensions.Add(new NodeExtension("HYPAR_referenced_content", "contentUrl", GlbLocation));
+ return extensions;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Elements/src/Serialization/glTF/GltfExtensions.cs b/Elements/src/Serialization/glTF/GltfExtensions.cs
index e56d99ede..0ad8f3324 100644
--- a/Elements/src/Serialization/glTF/GltfExtensions.cs
+++ b/Elements/src/Serialization/glTF/GltfExtensions.cs
@@ -1080,7 +1080,7 @@ internal static Gltf InitializeGlTF(Model model,
errors.Add(new ElementError(e.Id, ex));
}
}
- if (allBuffers.Sum(b => b.Count()) + buffer.Count == 0 && lights.Count == 0)
+ if (allBuffers.Sum(b => b.Count()) + buffer.Count == 0 && lights.Count == 0 && nodes.Count < 1)
{
return null;
}
@@ -1292,7 +1292,7 @@ private static void GetRenderDataForElement(Element e,
var elementNodeId = NodeUtilities.AddInstanceNode(nodes, element.Transform, element.Id);
foreach (var representation in element.RepresentationInstances)
{
- // get the unique id that contains representation Id and opening Ids
+ // get the unique id that contains representation Id and opening Ids
int combinedId = representation.GetHashCode(element);
if (representationsMap.TryGetValue(combinedId, out var mesh))
@@ -1302,31 +1302,52 @@ private static void GetRenderDataForElement(Element e,
{
NodeUtilities.SetRepresentationInfo(nodes[index], representation);
NodeUtilities.SetElementInfo(nodes[index], element.Id);
+ foreach (var nodeExtension in representation.Representation.GetNodeExtensions(element))
+ {
+ AddExtension(gltf, nodes[index], nodeExtension.Name, nodeExtension.Attributes);
+ }
}
}
else if (representation.Representation.TryToGraphicsBuffers(geometricElement, out var graphicsBuffers,
out var bufferId, out var mode))
{
- meshId = AddMesh(bufferId,
- buffer,
- bufferViews,
- accessors,
- materialIndexMap[representation.Material.Id.ToString()],
- graphicsBuffers,
- (MeshPrimitive.ModeEnum)mode,
- meshes);
+ var addedNodes = new List();
+ if (graphicsBuffers.Any())
+ {
+ meshId = AddMesh(bufferId,
+ buffer,
+ bufferViews,
+ accessors,
+ materialIndexMap[representation.Material.Id.ToString()],
+ graphicsBuffers,
+ (MeshPrimitive.ModeEnum)mode,
+ meshes);
+
+ if (meshId != -1)
+ {
+ var meshIdList = new List { meshId };
+ representationsMap.Add(combinedId, meshIdList);
+ addedNodes.AddRange(NodeUtilities.AddNodes(nodes, meshIdList, elementNodeId));
+ }
+ }
+ else
+ {
+ meshId = NodeUtilities.AddEmptyNode(nodes, elementNodeId);
+ addedNodes.Add(meshId);
+ }
// If the id == -1, the mesh is malformed.
// It may have no geometry.
if (meshId != -1)
{
- var meshIdList = new List { meshId };
- representationsMap.Add(combinedId, meshIdList);
- var addedNodes = NodeUtilities.AddNodes(nodes, meshIdList, elementNodeId);
foreach (var index in addedNodes)
{
NodeUtilities.SetRepresentationInfo(nodes[index], representation);
NodeUtilities.SetElementInfo(nodes[index], element.Id);
+ foreach (var nodeExtension in representation.Representation.GetNodeExtensions(element))
+ {
+ AddExtension(gltf, nodes[index], nodeExtension.Name, nodeExtension.Attributes);
+ }
}
}
}
diff --git a/Elements/src/Serialization/glTF/NodeExtension.cs b/Elements/src/Serialization/glTF/NodeExtension.cs
new file mode 100644
index 000000000..3b964128f
--- /dev/null
+++ b/Elements/src/Serialization/glTF/NodeExtension.cs
@@ -0,0 +1,28 @@
+using System.Collections.Generic;
+
+namespace Elements.Serialization.glTF
+{
+ internal class NodeExtension
+ {
+ public NodeExtension(string name, Dictionary attributes)
+ {
+ Name = name;
+ Attributes = attributes;
+ }
+
+ public NodeExtension(string name)
+ {
+ Name = name;
+ }
+
+ public NodeExtension(string name, string attributeName, object attributeValue)
+ {
+ Name = name;
+ Attributes.Add(attributeName, attributeValue);
+ }
+
+ public string Name { get; set; }
+
+ public Dictionary Attributes { get; } = new Dictionary();
+ }
+}
\ No newline at end of file
diff --git a/Elements/src/Serialization/glTF/NodeUtilities.cs b/Elements/src/Serialization/glTF/NodeUtilities.cs
index 7059a7348..91ee6214e 100644
--- a/Elements/src/Serialization/glTF/NodeUtilities.cs
+++ b/Elements/src/Serialization/glTF/NodeUtilities.cs
@@ -156,6 +156,11 @@ internal static int AddInstanceNode(List nodes, Transfor
return AddNode(nodes, newNode, 0);
}
+ internal static int AddEmptyNode(List nodes, int parentId)
+ {
+ return AddNode(nodes, new Node(), parentId);
+ }
+
internal static int[] AddInstanceNode(
List nodes,
List meshIds,