diff --git a/cpp/SampleCommands/SampleCommands.vcxproj b/cpp/SampleCommands/SampleCommands.vcxproj
index c8537d21..f79b1295 100644
--- a/cpp/SampleCommands/SampleCommands.vcxproj
+++ b/cpp/SampleCommands/SampleCommands.vcxproj
@@ -108,6 +108,7 @@
+
diff --git a/cpp/SampleCommands/SampleCommands.vcxproj.filters b/cpp/SampleCommands/SampleCommands.vcxproj.filters
index 140064c8..52b076ed 100644
--- a/cpp/SampleCommands/SampleCommands.vcxproj.filters
+++ b/cpp/SampleCommands/SampleCommands.vcxproj.filters
@@ -693,6 +693,9 @@
Source Files
+
+ Source Files
+
diff --git a/cpp/SampleCommands/cmdSampleIntersectMeshRay.cpp b/cpp/SampleCommands/cmdSampleIntersectMeshRay.cpp
new file mode 100644
index 00000000..db905f01
--- /dev/null
+++ b/cpp/SampleCommands/cmdSampleIntersectMeshRay.cpp
@@ -0,0 +1,83 @@
+#include "stdafx.h"
+
+/*
+Description:
+ Finds the first intersection of a ray with a mesh.
+Parameters:
+ mesh - [in] - A mesh to intersect.
+ ray - [in] - A ray to be casted.
+ face_indices - [out] - Faces on mesh that ray intersects (can be nullptr).
+Returns:
+ >= 0.0 parameter along ray if successful. < 0.0 if no intersection found.
+Remarks:
+ The ray may intersect more than one face in cases where the ray hits the edge
+ between two faces or the vertex corner shared by multiple faces.
+*/
+double SampleIntersectMeshRay(const ON_Mesh* mesh, ON_3dRay* ray, ON_SimpleArray* face_indices)
+{
+ double rc = -1.0;
+ if (mesh && ray)
+ {
+ ON_3dVector dir = ray->m_V;
+ const ON_MeshTree* mesh_tree = mesh->MeshTree(true);
+ if (mesh_tree && dir.Unitize())
+ {
+ // increase the range by a factor of 2 so we are confident the
+ // line passes entirely through the mesh
+ double range = mesh_tree->m_bbox.MaximumDistanceTo(ray->m_P) * 2.0;
+ ON_Line line(ray->m_P, ray->m_P + range * dir);
+
+ ON_SimpleArray hits;
+ mesh_tree->IntersectLine(line, hits);
+ int hit_count = hits.Count();
+ if (hit_count > 0)
+ {
+ ON_SimpleArray tvals;
+ ON_SimpleArray indices;
+ // tmin should be between 0 and 1 for the line
+ double tmin = 100.0;
+ for (int i = 0; i < hit_count; i++)
+ {
+ const ON_CMX_EVENT& e = hits[i];
+ if (e.m_C[0].m_t <= tmin)
+ {
+ tmin = e.m_C[0].m_t;
+ if (face_indices)
+ {
+ tvals.Append(tmin);
+ indices.Append(e.m_M[0].m_face_index);
+ }
+ }
+ if (e.m_type == ON_CMX_EVENT::cmx_overlap && e.m_C[1].m_t <= tmin)
+ {
+ tmin = e.m_C[1].m_t;
+ if (face_indices)
+ {
+ tvals.Append(tmin);
+ indices.Append(e.m_M[1].m_face_index);
+ }
+ }
+ }
+ if (tmin >= 0 && tmin <= 1.0)
+ {
+ if (face_indices)
+ {
+ for (int i = 0; i < tvals.Count(); i++)
+ {
+ if (tvals[i] == tmin)
+ face_indices->Append(indices[i]);
+ }
+ }
+
+ double line_length = line.Length();
+ double ray_length = ray->m_V.Length();
+ if (ray_length > ON_SQRT_EPSILON)
+ {
+ rc = tmin * line_length / ray_length;
+ }
+ }
+ }
+ }
+ }
+ return rc;
+}
\ No newline at end of file