I know that my problem is similar to the one described in this post, but there appears to be a lack of material on the topic custom Point Cloud Engines.
Rgiht now I am trying to implement a point cloud engine for a custom file format. However, only parts of the point cloud are being displayed, depending on the zoom level nothing will be shown. The same happens when using dummy point cloud engine which I implemented to better understand how Revit handles custom point cloud engines. For reference, the code for the dummy point cloud engine and its components are listed below:
DummyCloudEngine:
using Autodesk.Revit.DB.PointClouds; namespace RevitTest { class DummyCloudEngine : IPointCloudEngine { public DummyCloudEngine() { } public IPointCloudAccess CreatePointCloudAccess(string identifier) { return new DummyCloudAccess(identifier); } public void Free() { } } }
DummyCloudAccess:
using System; using Autodesk.Revit.DB; using Autodesk.Revit.DB.PointClouds; namespace RevitTest { class DummyCloudAccess : IPointCloudAccess { private Outline outline; float resolution; float voxelsize; public DummyCloudAccess(string identifier) { XYZ min = new XYZ(-5.0, -5.0, 0.0); XYZ max = new XYZ(5.0, 5.0, 0.0); outline = new Outline(min, max); resolution = 0.025f; voxelsize = 0.1f; } private int ConvertColor(float r, float g, float b) { int col = 0; byte A = (byte)(0); byte R = (byte)(r); byte G = (byte)(g); byte B = (byte)(b); col |= A << 0; col |= R << 8; col |= G << 16; col |= B << 32; return col; } public IPointSetIterator CreatePointSetIterator(PointCloudFilter rFilter, ElementId viewId) { return new DummyCloudIterator(rFilter, outline, resolution, voxelsize); } public IPointSetIterator CreatePointSetIterator(PointCloudFilter rFilter, double density, ElementId viewId) { return CreatePointSetIterator(rFilter, viewId); } public void Free() { } public PointCloudColorEncoding GetColorEncoding() { return PointCloudColorEncoding.ARGB; } public Outline GetExtent() { return outline; } public string GetName() { return "dummy"; } public XYZ GetOffset() { return new XYZ(0.0, 0.0, 0.0); } public double GetUnitsToFeetConversionFactor() { return 1.0; } public int ReadPoints(PointCloudFilter rFilter, ElementId viewId, IntPtr buffer, int nBufferSize) { int idx = 0; XYZ min = outline.MinimumPoint; XYZ max = outline.MaximumPoint; int ppd = (int)((max.X - min.X) / resolution); rFilter.PrepareForCell(outline.MinimumPoint, outline.MaximumPoint, ppd * ppd); unsafe { CloudPoint* dst = (CloudPoint*)buffer.ToPointer(); for (int x = 0; x < ppd; ++x) { for (int y = 0; y < ppd; ++y) { CloudPoint point = new CloudPoint((float)(min.X + x * resolution), (float)(min.Y + y * resolution), 0.0f, ConvertColor(x / ppd, y / ppd, 0.0f)); if (rFilter.TestPoint(point)) { dst[idx++] = point; if (idx >= nBufferSize) { return idx; } } } } } return idx; } } }
DummyCloudIterator:
using System; using Autodesk.Revit.DB; using Autodesk.Revit.DB.PointClouds; namespace RevitTest { class DummyCloudIterator : IPointSetIterator { Outline outline; float resolution; float voxelsize; PointCloudFilter rFilter; public DummyCloudIterator(PointCloudFilter rFilter, Outline outline, float resolution, float voxelsize) { this.rFilter = rFilter; this.outline = outline; this.resolution = resolution; this.voxelsize = voxelsize; } public int ReadPoints(IntPtr buffer, int bufferSize) { int idx = 0; XYZ min = outline.MinimumPoint; XYZ max = outline.MaximumPoint; int ppd = (int)((max.X - min.X) / resolution); unsafe { CloudPoint* dst = (CloudPoint*)buffer.ToPointer(); for (int x = 0; x < ppd; ++x) { for (int y = 0; y < ppd; ++y) { CloudPoint point = new CloudPoint((float)(min.X + x * resolution), (float)(min.Y + y * resolution), 0.0f, ConvertColor(255.0f * ((float)x / (float)ppd), 255.0f * (float)((float)y / (float)ppd), 255.0f)); if (rFilter.TestPoint(point)) { dst[idx++] = point; if (idx >= bufferSize) { return idx; } } } } } return idx; } public void Free() { } private int ConvertColor(float r, float g, float b) { int col = 0; byte A = (byte)(255); byte R = (byte)(r); byte G = (byte)(g); byte B = (byte)(b); col |= A << 0; col |= R << 8; col |= G << 16; col |= B << 32; return col; } } }
Hooking up a logger to my custom point cloud engines revealed that Revit will call the PointSetIterators a couple of times, but then stops despite the iterators still being able to provide more points. The result is, that portions of the point cloud end up missing.
Whenever Revit requests a point chunk from the iterator, it will tell the iterator how many points it expects to receive. However, the total number of points an iterator is allowed to return during its lifecycle is unknown. In consequence, planning ahead is impossible. If this total number was known, it would be possible to either to decide which points are relevant for display or to provide a subsampled the point data.
Are there any projects/ documentations/ blog entries dealing with the implementation of custom PointCloudEngines which might help me or others understand how Revit handles them? Even with the fact that bouding coordinates of PointFilter objects (which are excessively used in the PointCloudAccess objects) can not be retrieved by users suggests that PointCloudEngines have this far not been the focus of the Revit API. Looking at the API's guts with tools like dotPeek has proven fruitless too, as the important functionality related to this topic is tucked away in native code. So any help is greatly appreciated!
Can't find what you're looking for? Ask the community or share your knowledge.