Skip to content

Commit

Permalink
Added SurfaceNormal function to Voxels
Browse files Browse the repository at this point in the history
- Added function to get the surface normal of a surface point on a voxel field
- Added an example how to use RayCasting and extracting of surface normal with Voxels
- Requires Runtime v1.3
  • Loading branch information
LinKayser committed Feb 7, 2024
1 parent 74abf45 commit f570273
Show file tree
Hide file tree
Showing 3 changed files with 148 additions and 1 deletion.
126 changes: 126 additions & 0 deletions Examples/Ex_RayCasting.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
//
// SPDX-License-Identifier: CC0-1.0
//
// This example code file is released to the public under Creative Commons CC0.
// See https://creativecommons.org/publicdomain/zero/1.0/legalcode
//
// To the extent possible under law, LEAP 71 has waived all copyright and
// related or neighboring rights to this PicoGK example code file.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

using PicoGK;
using System.Diagnostics;
using System.Numerics;
using System.Runtime.ConstrainedExecution;

namespace PicoGKExamples
{
///////////////////////////////////////////////////////////////////////////
// Below is a static class that implements a single static function
// that can be called from Library::Go()

class RayCasting
{
public static void Task()
{
try
{
// Set up viewer materials (transparent green for mesh outline)
Library.oViewer().SetGroupMaterial(0, "FF0000", 0f, 1f);
Library.oViewer().SetGroupMaterial(1, "00FF0033", 0f, 1f);

// Create a mesh from an existing teapot file
Mesh msh = Mesh.mshFromStlFile(
Path.Combine( Utils.strPicoGKSourceCodeFolder(),
"Examples/Testfiles/Teapot.stl"));

Library.oViewer().Add(msh, 1);

// Create Voxels from teapot mesh
Voxels vox = new(msh);

// Add it to the viewer
Library.oViewer().Add(vox);

// Create a box 10mm larger than the teapot's bounds
BBox3 oBounds = msh.oBoundingBox();
oBounds.Grow(10);

// Generate 1000 rays and try to randomly
// intersect with teapot

Random rnd = new Random();

// Add a log entry, so we can benchmark
Library.Log("Starting Raycasting");

for (int n=0; n<1000; n++)
{
// random point from 0 .. 1 in x/y/z
Vector3 vecPos = new( rnd.NextSingle(),
rnd.NextSingle(),
rnd.NextSingle());


// Multiple by size and offset by bounding box origin
// to create a random point inside the bounding box
vecPos *= oBounds.vecSize();
vecPos += oBounds.vecMin;

// Create a random direction vector for the ray
Vector3 vecDir = Vector3.Normalize(
new( rnd.NextSingle() - 0.5f,
rnd.NextSingle() - 0.5f,
rnd.NextSingle() - 0.5f));


// Create a point in the direction of our ray.
// We will replace it with the point on the surface
// if the ray hits
Vector3 vecSurfacePt = vecPos + vecDir;

// The third point will show us the surface normal if the
// ray hits
Vector3 vecNormalPt = vecSurfacePt;

// Default the color to transparent gray (= ray did not hit)
ColorFloat clr = new("00AA");

if (vox.bRayCastToSurface(vecPos, vecDir, out Vector3 vecHit))
{
// We found a surface point
vecSurfacePt = vecHit;

// indicate we have hit by coloring the line in red
clr = new("FF0000");
vecNormalPt = vecSurfacePt + vox.vecSurfaceNormal(vecSurfacePt) * 5f;
}

// Show the result
PolyLine oPoly = new(clr);
oPoly.nAddVertex(vecPos);
oPoly.nAddVertex(vecSurfacePt);
oPoly.nAddVertex(vecNormalPt);
Library.oViewer().Add(oPoly);
}

// Finish log entry (allows us to benchmark)
Library.Log("Done Raycasting");
}

catch (Exception e)
{
Library.Log($"Failed to run example: \n{e.Message}"); ;
}
}
}
}

18 changes: 17 additions & 1 deletion PicoGK_Voxels.cs
Original file line number Diff line number Diff line change
Expand Up @@ -255,13 +255,29 @@ public bool bIsEqual(in Voxels voxOther)
/// </summary>
/// <param name="fVolumeCubicMM">Cubic MMs of volume filled with voxels</param>
/// <param name="oBBox">The real world bounding box of the voxels</param>
public void CalculateProperties(out float fVolumeCubicMM,
public void CalculateProperties( out float fVolumeCubicMM,
out BBox3 oBBox)
{
oBBox = new();
_CalculateProperties(m_hThis, out fVolumeCubicMM, ref oBBox);
}

/// <summary>
/// Returns the normal of the surface found at the specified point.
/// Use after functions like bClosestPointOnSurface or bRayCastToSurface
/// </summary>
/// <param name="vecSurfacePoint">
/// The point (on the surface of a voxel field, for which to return
/// the normal
/// </param>
/// <returns>The normal vector of the surface at the point</returns>
public Vector3 vecSurfaceNormal( in Vector3 vecSurfacePoint)
{
Vector3 vecNormal = Vector3.Zero;
_GetSurfaceNormal(m_hThis, vecSurfacePoint, ref vecNormal);
return vecNormal;
}

/// <summary>
/// Returns the closest point from the search point on the surface
/// of the voxel field
Expand Down
5 changes: 5 additions & 0 deletions PicoGK__Interop.cs
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,11 @@ private extern static void _CalculateProperties( IntPtr hThis,
out float pfVolume,
ref BBox3 oBBox);

[DllImport(Config.strPicoGKLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "Voxels_GetSurfaceNormal")]
private extern static void _GetSurfaceNormal(IntPtr hThis,
in Vector3 vecSurfacePoint,
ref Vector3 vecSurfaceNormal);

[DllImport(Config.strPicoGKLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "Voxels_bClosestPointOnSurface")]
private extern static bool _bClosestPointOnSurface( IntPtr hThis,
in Vector3 vecSearch,
Expand Down

0 comments on commit f570273

Please sign in to comment.