应用程序:DuplicateGraphics

Revit平台:所有版本

Revit版本:2018.0

首次发布:2018.0

编程语言:C#

技能级别:高级

类别:基础知识,几何学

类型:ExternalCommand 和 ExternalApplication

主题:使用DirectContext3D显示图形

摘要:该示例展示了使用DirectContext3D的基本用法。外部程序创建DirectContext3D服务器,从所选的Revit元素中提取几何信息,将其编码为顶点和索引缓冲区的一对,并使用DirectContext3D提交它以进行渲染。通过使用一个ExternalCommand触发这个过程的效果是,在一个偏移位置上显示所选Revit元素的几何内容,以便图形看起来被复制。

相关类:

Autodesk.Revit.DB.ExternalService.IExternalApplication

Autodesk.Revit.UI.IExternalCommand

Autodesk.Revit.DB.DirectContext3D.IDirectContext3DServer

Autodesk.Revit.DB.DirectContext3D.DrawContext

项目文件

Application.cs

Application类是从IExternalApplication派生出来的,它创建并注册DirectContext3D服务器,向Revit提供外部几何元素。

Command.cs

两个派生自IExternalCommand的命令类提供了与应用程序的接口。命令类CommandDuplicateGraphics指示应用程序提示用户选择要处理的Revit元素。命令类CommandClearExternalGraphics指示应用程序删除已创建的DirectContext3D图形。

RevitElementDrawServer.cs

该文件包含了类RevitElementDrawServer,实现了IDirectContext3DServer接口。服务器类从Revit元素中提取几何信息,使用DirectContext3D.DrawContext提交渲染。

描述:

该示例展示了如何使用DirectContext3D来绘制几何图形,这些图形是从现有的Revit元素中获取的。几何图形被编码为一对顶点缓冲区和索引缓冲区。为了使所选的Revit元素在相同的显示样式下显示为其余Revit视图的图形内容的重复,不同类型的几何图形被准备用于不同的显示样式。

操作步骤:

打开Revit应用程序并执行“使用DirectContext3D显示图形”的命令。

1.选择需要使用DirectContext3D重新绘制图形的元素。

2.完成选择。

预期结果:所选图形将呈现为具有偏移量的重复形式。

3.更改视图的显示样式。

预期结果:DirectContext3D图形会改变以匹配显示样式。

完整的源代码请加入QQ群649037449,在群文件中下载RevitSDK.exe,解压后在文件夹中搜索本文中应用程序名称即可获得完整源码

Application.cs

//
// (C) Copyright 2003-2019 by Autodesk, Inc. All rights reserved.
//
// Permission to use, copy, modify, and distribute this software in
// object code form for any purpose and without fee is hereby granted
// provided that the above copyright notice appears in all copies and
// that both that copyright notice and the limited warranty and
// restricted rights notice below appear in all supporting
// documentation.

//
// AUTODESK PROVIDES THIS PROGRAM 'AS IS' AND WITH ALL ITS FAULTS.
// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
// UNINTERRUPTED OR ERROR FREE.
//
// Use, duplication, or disclosure by the U.S. Government is subject to
// restrictions set forth in FAR 52.227-19 (Commercial Computer
// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
// (Rights in Technical Data and Computer Software), as applicable. 

/*
 * This sample demonstrates how to use DirectContext3D to draw graphics. The graphics primitives that
 * are needed for DirectContext3D are taken from the contents of existing Revit elements. The result of
 * the macro is to duplicate the graphics of existing Revit elements by using DirectContext3D. No new
 * elements are created to contain the graphics produced with DirectContext3D.
 * 
 * The following are the main steps for using DirectContext3D:
 *  1) create a DirectContext3D server (derived from Autodesk.Revit.DB.DirectContext3D.IDirectContext3DServer)
 *     a. register the server with the DirectContext3D service
 *  2) use the server to submit geometry for drawing
 *     a. represent geometry primitives using pairs of vertex and index buffers
 *     b. determine when to submit opaque/transparent geometry
 *     c. flush the buffers
 *     d. update the buffers when necessary
 */


using Autodesk.Revit.UI;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.ExternalService;
using Autodesk.Revit.DB.Events;
using Autodesk.Revit.UI.Selection;
using System;
using System.Collections.Generic;

namespace Revit.SDK.Samples.DuplicateGraphics.CS
{
    /// <summary>
    /// Implements the Revit add-in interface IExternalApplication
    /// </summary>
    [Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)]
    [Autodesk.Revit.Attributes.Regeneration(Autodesk.Revit.Attributes.RegenerationOption.Manual)]
    public class Application : IExternalApplication
   {
      #region IExternalApplication Members

      /// <summary>
      /// Implements the OnStartup event
      /// </summary>
      /// <param name="application"></param>
      /// <returns>Result that indicates whether the external application has completed its work successfully.</returns>
      public Result OnStartup(UIControlledApplication application)
      {
         try
         {
            // Register events. 
            application.ControlledApplication.DocumentClosing += new EventHandler
                <Autodesk.Revit.DB.Events.DocumentClosingEventArgs>(OnDocumentClosing);
            m_servers = new List<RevitElementDrawingServer>();
            m_documents = new HashSet<Document>();

            s_applicationInstance = this;

         }
         catch (Exception)
         {
            return Result.Failed;
         }

         return Result.Succeeded;
      }

      /// <summary>
      /// Implements the OnShutdown event
      /// </summary>
      /// <param name="application"></param>
      /// <returns>Result that indicates whether the external application has completed its work successfully.</returns>
      public Result OnShutdown(UIControlledApplication application)
      {
         // remove the event.
         application.ControlledApplication.DocumentClosing -= OnDocumentClosing;
         return Result.Succeeded;
      }

      /// <summary>
      /// Implements the OnDocumentClosing event
      /// </summary>
      /// <param name="sender"></param>
      /// <param name="args"></param>
      /// <returns></returns>
      public void OnDocumentClosing(object sender, DocumentClosingEventArgs args)
      {
         unregisterServers(args.Document, false);
      }

      #endregion

      /// <summary>
      /// Responds to the external command CommandDuplicateGraphics.
      /// </summary>
      /// <param name="document"></param>
      public static void ProcessCommandDuplicateGraphics(Document document)
      {
         if (s_applicationInstance != null)
         {
            s_applicationInstance.AddMultipleRevitElementServers(new UIDocument(document));
         }
      }

      /// <summary>
      /// Responds to the external command CommandClearExternalGraphics.
      /// </summary>
      /// <param name="document"></param>
      public static void ProcessCommandClearExternalGraphics(Document document)
      {
         if (s_applicationInstance != null)
         {
            s_applicationInstance.unregisterServers(null, true);
         }
      }

      /// <summary>
      /// Picks a Revit element and creates a corresponding DirectContext3D server that will draw the element's graphics at a fixed offset from the original location.
      /// </summary>
      /// <param name="uidoc"></param>
      public void AddRevitElementServer(UIDocument uidoc)
      {
         Reference reference = uidoc.Selection.PickObject(ObjectType.Element, "Select an element to duplicate with DirectContext3D");
         Element elem = uidoc.Document.GetElement(reference);

         // Create the server and register it with the DirectContext3D service.
         ExternalService directContext3DService = ExternalServiceRegistry.GetService(ExternalServices.BuiltInExternalServices.DirectContext3DService);
         RevitElementDrawingServer revitServer = new RevitElementDrawingServer(uidoc, elem, m_offset);
         directContext3DService.AddServer(revitServer);
         m_servers.Add(revitServer);

         MultiServerService msDirectContext3DService = directContext3DService as MultiServerService;

         IList<Guid> serverIds = msDirectContext3DService.GetActiveServerIds();

         serverIds.Add(revitServer.GetServerId());

         // Add the new server to the list of active servers.
         msDirectContext3DService.SetActiveServers(serverIds);

         m_documents.Add(uidoc.Document);
         uidoc.UpdateAllOpenViews();
      }

      /// <summary>
      /// Same as AddRevitElementServer(), but for multiple elements.
      /// </summary>
      /// <param name="uidoc"></param>
      public void AddMultipleRevitElementServers(UIDocument uidoc)
      {
         IList<Reference> references = uidoc.Selection.PickObjects(ObjectType.Element, "Select elements to duplicate with DirectContext3D");

         ExternalService directContext3DService = ExternalServiceRegistry.GetService(ExternalServices.BuiltInExternalServices.DirectContext3DService);
         MultiServerService msDirectContext3DService = directContext3DService as MultiServerService;
         IList<Guid> serverIds = msDirectContext3DService.GetActiveServerIds();

         // Create one server per element.
         foreach (Reference reference in references)
         {
            Element elem = uidoc.Document.GetElement(reference);

            RevitElementDrawingServer revitServer = new RevitElementDrawingServer(uidoc, elem, m_offset);
            directContext3DService.AddServer(revitServer);
            m_servers.Add(revitServer);

            serverIds.Add(revitServer.GetServerId());
         }

         msDirectContext3DService.SetActiveServers(serverIds);

         m_documents.Add(uidoc.Document);
         uidoc.UpdateAllOpenViews();
      }

      /// <summary>
      /// Cleans up by unregistering the servers corresponding to the specified document, or all servers if the document is not provided.
      /// </summary>
      /// <param name="document">The document whose servers should be removed, or null.</param>
      /// <param name="updateViews">Update views of the affected document(s).</param>
      public void unregisterServers(Document document, bool updateViews)
      {
         ExternalServiceId externalDrawerServiceId = ExternalServices.BuiltInExternalServices.DirectContext3DService;
         var externalDrawerService = ExternalServiceRegistry.GetService(externalDrawerServiceId) as MultiServerService;
         if (externalDrawerService == null)
            return;

         foreach (var registeredServerId in externalDrawerService.GetRegisteredServerIds())
         {
            var externalDrawServer = externalDrawerService.GetServer(registeredServerId) as RevitElementDrawingServer;
            if (externalDrawServer == null)
               continue;
            if (document != null && !document.Equals(externalDrawServer.Document))
               continue;
            externalDrawerService.RemoveServer(registeredServerId);
         }

         if (document != null)
         {
            m_servers.RemoveAll(server => document.Equals(server.Document));

            if (updateViews)
            {
               UIDocument uidoc = new UIDocument(document);
               uidoc.UpdateAllOpenViews();
            }

            m_documents.Remove(document);
         }
         else
         {
            m_servers.Clear();

            if (updateViews)
               foreach (var doc in m_documents)
               {
                  UIDocument uidoc = new UIDocument(doc);
                  uidoc.UpdateAllOpenViews();
               }

            m_documents.Clear();
         }
      }

      List<RevitElementDrawingServer> m_servers;
      XYZ m_offset = new XYZ(0, 0, 45);
      HashSet<Document> m_documents;
      static Application s_applicationInstance;
   }
}

RevitElementDrawServer.cs

//
// (C) Copyright 2003-2019 by Autodesk, Inc.
//
// Permission to use, copy, modify, and distribute this software in
// object code form for any purpose and without fee is hereby granted,
// provided that the above copyright notice appears in all copies and
// that both that copyright notice and the limited warranty and
// restricted rights notice below appear in all supporting
// documentation.
//
// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
// UNINTERRUPTED OR ERROR FREE.
//
// Use, duplication, or disclosure by the U.S. Government is subject to
// restrictions set forth in FAR 52.227-19 (Commercial Computer
// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
// (Rights in Technical Data and Computer Software), as applicable.
//

/*
 * This sample demonstrates how to use DirectContext3D to draw graphics. 
 * This file defines a DirectContext3D server.
 */

using Autodesk.Revit.UI;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.ExternalService;
using Autodesk.Revit.DB.DirectContext3D;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;

namespace Revit.SDK.Samples.DuplicateGraphics.CS
{
   class RevitElementDrawingServer : IDirectContext3DServer
   {
      public RevitElementDrawingServer(UIDocument uiDoc, Element elem, XYZ offset)
      {
         m_guid = Guid.NewGuid();

         m_uiDocument = uiDoc;
         m_element = elem;
         m_offset = offset;
      }

      public System.Guid GetServerId() { return m_guid; }
      public System.String GetVendorId() { return "ADSK"; }
      public ExternalServiceId GetServiceId() { return ExternalServices.BuiltInExternalServices.DirectContext3DService; }
      public System.String GetName() { return "Revit Element Drawing Server"; }
      public System.String GetDescription() { return "Duplicates graphics from a Revit element."; }

      // Corresponds to functionality that is not used in this sample.
      public System.String GetApplicationId() { return ""; }

      // Corresponds to functionality that is not used in this sample.
      public System.String GetSourceId() { return ""; }

      // Corresponds to functionality that is not used in this sample.
      public bool UsesHandles() { return false; }

      // Tests whether this server should be invoked for the given view.
      // The server only wants to be invoked for 3D views that are part of the document that contains the element in m_element.
      public bool CanExecute(Autodesk.Revit.DB.View view)
      {
         if (!m_element.IsValidObject)
            return false;
         if (view.ViewType != ViewType.ThreeD)
            return false;

         Document doc = view.Document;
         Document otherDoc = m_element.Document;
         return doc.Equals(otherDoc);
      }

      // Reports a bounding box of the geometry that this server submits for drawing.
      public Outline GetBoundingBox(Autodesk.Revit.DB.View view)
      {
         try
         {
            BoundingBoxXYZ boundingBox = m_element.get_BoundingBox(null);

            Outline outline = new Outline(boundingBox.Min + m_offset, boundingBox.Max + m_offset);

            return outline;
         }
         catch (Exception e)
         {
            MessageBox.Show(e.ToString());
            throw;
         }
      }

      // Indicates that this server will submit geometry during the rendering pass for transparent geometry.
      public bool UseInTransparentPass(Autodesk.Revit.DB.View view) { return true; }

      // Submits the geometry for rendering.
      public void RenderScene(Autodesk.Revit.DB.View view, DisplayStyle displayStyle)
      {
         try
         {
            // Populate geometry buffers if they are not initialized or need updating.
            if (m_nonTransparentFaceBufferStorage == null || m_nonTransparentFaceBufferStorage.needsUpdate(displayStyle) ||
                m_transparentFaceBufferStorage == null || m_transparentFaceBufferStorage.needsUpdate(displayStyle) ||
                m_edgeBufferStorage == null || m_edgeBufferStorage.needsUpdate(displayStyle))
            {
               Options options = new Options();
               GeometryElement geomElem = m_element.get_Geometry(options);

               CreateBufferStorageForElement(geomElem, displayStyle);
            }

            // Submit a subset of the geometry for drawing. Determine what geometry should be submitted based on
            // the type of the rendering pass (opaque or transparent) and DisplayStyle (wireframe or shaded).

            // If the server is requested to submit transparent geometry, DrawContext().IsTransparentPass()
            // will indicate that the current rendering pass is for transparent objects.
            RenderingPassBufferStorage faceBufferStorage = DrawContext.IsTransparentPass() ? m_transparentFaceBufferStorage : m_nonTransparentFaceBufferStorage;

            // Conditionally submit triangle primitives (for non-wireframe views).
            if (displayStyle != DisplayStyle.Wireframe &&
                faceBufferStorage.PrimitiveCount > 0)
               DrawContext.FlushBuffer(faceBufferStorage.VertexBuffer,
                                       faceBufferStorage.VertexBufferCount,
                                       faceBufferStorage.IndexBuffer,
                                       faceBufferStorage.IndexBufferCount,
                                       faceBufferStorage.VertexFormat,
                                       faceBufferStorage.EffectInstance, PrimitiveType.TriangleList, 0,
                                       faceBufferStorage.PrimitiveCount);

            // Conditionally submit line segment primitives.
            if (displayStyle != DisplayStyle.Shading &&
                m_edgeBufferStorage.PrimitiveCount > 0)
               DrawContext.FlushBuffer(m_edgeBufferStorage.VertexBuffer,
                                       m_edgeBufferStorage.VertexBufferCount,
                                       m_edgeBufferStorage.IndexBuffer,
                                       m_edgeBufferStorage.IndexBufferCount,
                                       m_edgeBufferStorage.VertexFormat,
                                       m_edgeBufferStorage.EffectInstance, PrimitiveType.LineList, 0,
                                       m_edgeBufferStorage.PrimitiveCount);
         }
         catch (Exception e)
         {
            MessageBox.Show(e.ToString());
         }
      }

      // Initialize and populate buffers that hold graphics primitives, set up related parameters that are needed for drawing.
      private void CreateBufferStorageForElement(GeometryElement geomElem, DisplayStyle displayStyle)
      {
         List<Solid> allSolids = new List<Solid>();

         foreach (GeometryObject geomObj in geomElem)
         {
            if (geomObj is Solid)
            {
               Solid solid = (Solid)geomObj;
               if (solid.Volume > 1e-06)
                  allSolids.Add(solid);
            }
         }

         m_nonTransparentFaceBufferStorage = new RenderingPassBufferStorage(displayStyle);
         m_transparentFaceBufferStorage = new RenderingPassBufferStorage(displayStyle);
         m_edgeBufferStorage = new RenderingPassBufferStorage(displayStyle);

         // Collect primitives (and associated rendering parameters, such as colors) from faces and edges.
         foreach (Solid solid in allSolids)
         {
            foreach (Face face in solid.Faces)
            {
               if (face.Area > 1e-06)
               {
                  Mesh mesh = face.Triangulate();

                  ElementId materialId = face.MaterialElementId;
                  bool isTransparent = false;
                  ColorWithTransparency cwt = new ColorWithTransparency(127, 127, 127, 0);
                  if (materialId != ElementId.InvalidElementId)
                  {
                     Material material = m_element.Document.GetElement(materialId) as Material;

                     Color color = material.Color;
                     int transparency0To100 = material.Transparency;
                     uint transparency0To255 = (uint)((float)transparency0To100 / 100f * 255f);

                     cwt = new ColorWithTransparency(color.Red, color.Green, color.Blue, transparency0To255);
                     if (transparency0To255 > 0)
                     {
                        isTransparent = true;
                     }
                  }

                  BoundingBoxUV env = face.GetBoundingBox();
                  UV center = 0.5 * (env.Min + env.Max);
                  XYZ normal = face.ComputeNormal(center);

                  MeshInfo meshInfo = new MeshInfo(mesh, normal, cwt);

                  if (isTransparent)
                  {
                     m_transparentFaceBufferStorage.Meshes.Add(meshInfo);
                     m_transparentFaceBufferStorage.VertexBufferCount += mesh.Vertices.Count;
                     m_transparentFaceBufferStorage.PrimitiveCount += mesh.NumTriangles;
                  }
                  else
                  {
                     m_nonTransparentFaceBufferStorage.Meshes.Add(meshInfo);
                     m_nonTransparentFaceBufferStorage.VertexBufferCount += mesh.Vertices.Count;
                     m_nonTransparentFaceBufferStorage.PrimitiveCount += mesh.NumTriangles;
                  }
               }
            }

            foreach (Edge edge in solid.Edges)
            {
               // if (edge.Length > 1e-06)
               {
                  IList<XYZ> xyzs = edge.Tessellate();

                  m_edgeBufferStorage.VertexBufferCount += xyzs.Count;
                  m_edgeBufferStorage.PrimitiveCount += xyzs.Count - 1;
                  m_edgeBufferStorage.EdgeXYZs.Add(xyzs);
               }
            }
         }

         // Fill out buffers with primitives based on the intermediate information about faces and edges.
         ProcessFaces(m_nonTransparentFaceBufferStorage);
         ProcessFaces(m_transparentFaceBufferStorage);
         ProcessEdges(m_edgeBufferStorage);
      }

      // Create and populate a pair of vertex and index buffers. Also update parameters associated with the format of the vertices.
      private void ProcessFaces(RenderingPassBufferStorage bufferStorage)
      {
         List<MeshInfo> meshes = bufferStorage.Meshes;
         List<int> numVerticesInMeshesBefore = new List<int>();
         if (meshes.Count == 0) return;

         bool useNormals = bufferStorage.DisplayStyle == DisplayStyle.Shading ||
            bufferStorage.DisplayStyle == DisplayStyle.ShadingWithEdges;

         // Vertex attributes are stored sequentially in vertex buffers. The attributes can include position, normal vector, and color.
         // All vertices within a vertex buffer must have the same format. Possible formats are enumerated by VertexFormatBits.
         // Vertex format also determines the type of rendering effect that can be used with the vertex buffer. In this sample,
         // the color is always encoded in the vertex attributes.

         bufferStorage.FormatBits = useNormals ? VertexFormatBits.PositionNormalColored : VertexFormatBits.PositionColored;

         // The format of the vertices determines the size of the vertex buffer.
         int vertexBufferSizeInFloats = (useNormals ? VertexPositionNormalColored.GetSizeInFloats() : VertexPositionColored.GetSizeInFloats()) *
            bufferStorage.VertexBufferCount;
         numVerticesInMeshesBefore.Add(0);

         bufferStorage.VertexBuffer = new VertexBuffer(vertexBufferSizeInFloats);
         bufferStorage.VertexBuffer.Map(vertexBufferSizeInFloats);

         if (useNormals)
         {
            // A VertexStream is used to write data into a VertexBuffer.
            VertexStreamPositionNormalColored vertexStream = bufferStorage.VertexBuffer.GetVertexStreamPositionNormalColored();
            foreach (MeshInfo meshInfo in meshes)
            {
               Mesh mesh = meshInfo.Mesh;
               foreach (XYZ vertex in mesh.Vertices)
               {
                  vertexStream.AddVertex(new VertexPositionNormalColored(vertex + m_offset, meshInfo.Normal, meshInfo.ColorWithTransparency));
               }

               numVerticesInMeshesBefore.Add(numVerticesInMeshesBefore.Last() + mesh.Vertices.Count);
            }
         }
         else
         {
            // A VertexStream is used to write data into a VertexBuffer.
            VertexStreamPositionColored vertexStream = bufferStorage.VertexBuffer.GetVertexStreamPositionColored();
            foreach (MeshInfo meshInfo in meshes)
            {
               Mesh mesh = meshInfo.Mesh;
               // make the color of all faces white in HLR
               ColorWithTransparency color = (bufferStorage.DisplayStyle == DisplayStyle.HLR) ?
                  new ColorWithTransparency(255, 255, 255, meshInfo.ColorWithTransparency.GetTransparency()) :
                  meshInfo.ColorWithTransparency;
               foreach (XYZ vertex in mesh.Vertices)
               {
                  vertexStream.AddVertex(new VertexPositionColored(vertex + m_offset, color));
               }

               numVerticesInMeshesBefore.Add(numVerticesInMeshesBefore.Last() + mesh.Vertices.Count);
            }
         }

         bufferStorage.VertexBuffer.Unmap();

         // Primitives are specified using a pair of vertex and index buffers. An index buffer contains a sequence of indices into
         // the associated vertex buffer, each index referencing a particular vertex.

         int meshNumber = 0;
         bufferStorage.IndexBufferCount = bufferStorage.PrimitiveCount * IndexTriangle.GetSizeInShortInts();
         int indexBufferSizeInShortInts = 1 * bufferStorage.IndexBufferCount;
         bufferStorage.IndexBuffer = new IndexBuffer(indexBufferSizeInShortInts);
         bufferStorage.IndexBuffer.Map(indexBufferSizeInShortInts);
         {
            // An IndexStream is used to write data into an IndexBuffer.
            IndexStreamTriangle indexStream = bufferStorage.IndexBuffer.GetIndexStreamTriangle();
            foreach (MeshInfo meshInfo in meshes)
            {
               Mesh mesh = meshInfo.Mesh;
               int startIndex = numVerticesInMeshesBefore[meshNumber];
               for (int i = 0; i < mesh.NumTriangles; i++)
               {
                  MeshTriangle mt = mesh.get_Triangle(i);
                  // Add three indices that define a triangle.
                  indexStream.AddTriangle(new IndexTriangle((int)(startIndex + mt.get_Index(0)),
                                                            (int)(startIndex + mt.get_Index(1)),
                                                            (int)(startIndex + mt.get_Index(2))));
               }
               meshNumber++;
            }
         }
         bufferStorage.IndexBuffer.Unmap();


         // VertexFormat is a specification of the data that is associated with a vertex (e.g., position).
         bufferStorage.VertexFormat = new VertexFormat(bufferStorage.FormatBits);
         // Effect instance is a specification of the appearance of geometry. For example, it may be used to specify color, if there is no color information provided with the vertices.
         bufferStorage.EffectInstance = new EffectInstance(bufferStorage.FormatBits);
      }

      // A helper function, analogous to ProcessFaces.
      private void ProcessEdges(RenderingPassBufferStorage bufferStorage)
      {
         List<IList<XYZ>> edges = bufferStorage.EdgeXYZs;
         if (edges.Count == 0)
            return;

         // Edges are encoded as line segment primitives whose vertices contain only position information.
         bufferStorage.FormatBits = VertexFormatBits.Position;

         int edgeVertexBufferSizeInFloats = VertexPosition.GetSizeInFloats() * bufferStorage.VertexBufferCount;
         List<int> numVerticesInEdgesBefore = new List<int>();
         numVerticesInEdgesBefore.Add(0);

         bufferStorage.VertexBuffer = new VertexBuffer(edgeVertexBufferSizeInFloats);
         bufferStorage.VertexBuffer.Map(edgeVertexBufferSizeInFloats);
         {
            VertexStreamPosition vertexStream = bufferStorage.VertexBuffer.GetVertexStreamPosition();
            foreach (IList<XYZ> xyzs in edges)
            {
               foreach (XYZ vertex in xyzs)
               {
                  vertexStream.AddVertex(new VertexPosition(vertex + m_offset));
               }

               numVerticesInEdgesBefore.Add(numVerticesInEdgesBefore.Last() + xyzs.Count);
            }
         }
         bufferStorage.VertexBuffer.Unmap();

         int edgeNumber = 0;
         bufferStorage.IndexBufferCount = bufferStorage.PrimitiveCount * IndexLine.GetSizeInShortInts();
         int indexBufferSizeInShortInts = 1 * bufferStorage.IndexBufferCount;
         bufferStorage.IndexBuffer = new IndexBuffer(indexBufferSizeInShortInts);
         bufferStorage.IndexBuffer.Map(indexBufferSizeInShortInts);
         {
            IndexStreamLine indexStream = bufferStorage.IndexBuffer.GetIndexStreamLine();
            foreach (IList<XYZ> xyzs in edges)
            {
               int startIndex = numVerticesInEdgesBefore[edgeNumber];
               for (int i = 1; i < xyzs.Count; i++)
               {
                  // Add two indices that define a line segment.
                  indexStream.AddLine(new IndexLine((int)(startIndex + i - 1),
                                                    (int)(startIndex + i)));
               }
               edgeNumber++;
            }
         }
         bufferStorage.IndexBuffer.Unmap();


         bufferStorage.VertexFormat = new VertexFormat(bufferStorage.FormatBits);
         bufferStorage.EffectInstance = new EffectInstance(bufferStorage.FormatBits);
      }

      public Document Document
      {
         get { return (m_uiDocument != null) ? m_uiDocument.Document : null; }
      }

      private Guid m_guid;

      private Element m_element;
      private XYZ m_offset;
      private UIDocument m_uiDocument;

      private RenderingPassBufferStorage m_nonTransparentFaceBufferStorage;
      private RenderingPassBufferStorage m_transparentFaceBufferStorage;
      private RenderingPassBufferStorage m_edgeBufferStorage;

      #region Helper classes

      // A container to hold information associated with a triangulated face.
      class MeshInfo
      {
         public MeshInfo(Mesh mesh, XYZ normal, ColorWithTransparency color)
         {
            Mesh = mesh;
            Normal = normal;
            ColorWithTransparency = color;
         }

         public Mesh Mesh;
         public XYZ Normal;
         public ColorWithTransparency ColorWithTransparency;
      }

      // A class that brings together all the data and rendering parameters that are needed to draw one sequence of primitives (e.g., triangles)
      // with the same format and appearance.
      class RenderingPassBufferStorage
      {
         public RenderingPassBufferStorage(DisplayStyle displayStyle)
         {
            DisplayStyle = displayStyle;
            Meshes = new List<MeshInfo>();
            EdgeXYZs = new List<IList<XYZ>>();
         }

         public bool needsUpdate(DisplayStyle newDisplayStyle)
         {
            if (newDisplayStyle != DisplayStyle)
               return true;

            if (PrimitiveCount > 0)
               if (VertexBuffer == null || !VertexBuffer.IsValid() ||
                   IndexBuffer == null || !IndexBuffer.IsValid() ||
                   VertexFormat == null || !VertexFormat.IsValid() ||
                   EffectInstance == null || !EffectInstance.IsValid())
                  return true;

            return false;
         }

         public DisplayStyle DisplayStyle { get; set; }

         public VertexFormatBits FormatBits { get; set; }

         public List<MeshInfo> Meshes { get; set; }
         public List<IList<XYZ>> EdgeXYZs { get; set; }

         public int PrimitiveCount { get; set; }
         public int VertexBufferCount { get; set; }
         public int IndexBufferCount { get; set; }
         public VertexBuffer VertexBuffer { get; set; }
         public IndexBuffer IndexBuffer { get; set; }
         public VertexFormat VertexFormat { get; set; }
         public EffectInstance EffectInstance { get; set; }
      }

      #endregion
   }
}