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,