Voxelization of a mesh with preset voxel sizes

Good evening everyone!

I am looking for a way to voxelize the mesh attached here (a reconstructed Jain temple column), but with custom sized boxes. I was thinking whether it would be possible to evaluate it based it on mesh vertices density, or sort based on mesh sizes and use closest point based on an equally spaced point grid… But i am not sure how to wrap my head around it. Any help, both components and script based, would be appreciated! I have attached the rhino and gh files in this link. I have already looked at a previous forum regarding subdivided voxelization, but I need specific box sizes :frowning:

https://www.food4rhino.com/en/app/voxelizer

https://www.food4rhino.com/en/resource/voxelize?lang=en

https://www.food4rhino.com/en/app/voxeltools

There are quite a few external plugins that might be of help

You can also use this plugin: https://www.food4rhino.com/en/app/crystallon (the “CrystallonV2 Voxelize (Distance)” node). I recommend it, as it not only allows you to voxelize geometry but also lets you create lattice structures later without needing to install an additional plugin.

Tried it with Crystallon, here’s the result.

-Robert

Hi Robert,

Thanks a lot for the suggestion! This is a great result, but I have already achieved a similar look using a c# script, attached below; but what I was hoping to achieve was different sized boxes based on the mesh geometry. Finer smaller mesh details would be filled with small boxes of a preset size, and larger faces/geometries would be filled with bigger boxes. The reason behind this is to adhere to specific ratios from the vastu shastra. I have seen another forum on recursive voxelization, but that is just dividing by 2 repeatedly (1/2, 1/4, 1/8, 1/16, etc).

-Aylin

using System;
using System.Collections.Generic;
using Rhino;
using Rhino.Geometry;
using Grasshopper;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Data;
using Grasshopper.Kernel.Types;

public class Script_Instance : GH_ScriptInstance
{
private void Print(string text) { }
private void Print(string format, params object args) { }
private void Reflect(object obj) { }
private void Reflect(object obj, string method_name) { }

private readonly RhinoDoc RhinoDocument;
private readonly GH_Document GrasshopperDocument;
private readonly IGH_Component Component;
private readonly int Iteration;

private void RunScript(Mesh M, double MaxSize, double MinSize, double DetailFactor, int MaxDepth,
ref object Centers, ref object Sizes, ref object Boxes)
{
if (M == null || !M.IsValid)
{
Centers = null;
Sizes = null;
Boxes = null;
return;
}

Mesh mesh = M.DuplicateMesh();
mesh.FaceNormals.ComputeFaceNormals();
mesh.Normals.ComputeNormals();
mesh.Compact();

BoundingBox bb = mesh.GetBoundingBox(true);
Point3d c = bb.Center;

double sx = bb.Max.X - bb.Min.X;
double sy = bb.Max.Y - bb.Min.Y;
double sz = bb.Max.Z - bb.Min.Z;
double rootSize = Math.Max(sx, Math.Max(sy, sz));

if (MinSize <= 0.0) MinSize = rootSize / 64.0;
if (MaxSize <= 0.0) MaxSize = rootSize / 8.0;
if (DetailFactor <= 0.0) DetailFactor = 1.0;
if (MaxDepth < 1) MaxDepth = 1;

double[] faceFeatureSize = ComputeFaceFeatureSizes(mesh, MinSize, MaxSize);

List<Point3d> centers = new List<Point3d>();
List<double> sizes = new List<double>();
List<Box> boxes = new List<Box>();

SubdivideCell(mesh, faceFeatureSize, c, rootSize, 0, MaxDepth, MinSize, MaxSize, DetailFactor, centers, sizes, boxes);

Centers = centers;
Sizes = sizes;
Boxes = boxes;

}

private void SubdivideCell(
Mesh mesh,
double faceFeatureSize,
Point3d center,
double size,
int depth,
int maxDepth,
double minSize,
double maxSize,
double detailFactor,
List centers,
List sizes,
List boxes)
{
double half = size * 0.5;
double halfDiag = Math.Sqrt(3.0) * half;

MeshPoint mp = mesh.ClosestMeshPoint(center, 0.0);
if (mp == null) return;

Point3d cp = mp.Point;
double dist = center.DistanceTo(cp);

bool relevant = dist <= halfDiag;
if (!relevant) return;

int fi = mp.FaceIndex;
if (fi < 0 || fi >= faceFeatureSize.Length) return;

double localTarget = faceFeatureSize[fi];
localTarget = Math.Max(minSize, Math.Min(maxSize, localTarget));

bool shouldSubdivide = false;

if (depth < maxDepth && size > minSize)
{
  if (size > localTarget * detailFactor)
    shouldSubdivide = true;
}

if (shouldSubdivide)
{
  double child = size * 0.5;
  double off = child * 0.5;

  for (int ix = -1; ix <= 1; ix += 2)
  for (int iy = -1; iy <= 1; iy += 2)
  for (int iz = -1; iz <= 1; iz += 2)
  {
    Point3d childCenter = new Point3d(
      center.X + ix * off,
      center.Y + iy * off,
      center.Z + iz * off
      );

    SubdivideCell(mesh, faceFeatureSize, childCenter, child, depth + 1, maxDepth, minSize, maxSize, detailFactor, centers, sizes, boxes);
  }
}
else
{
  centers.Add(center);
  sizes.Add(size);

  Interval ix = new Interval(center.X - half, center.X + half);
  Interval iy = new Interval(center.Y - half, center.Y + half);
  Interval iz = new Interval(center.Z - half, center.Z + half);
  boxes.Add(new Box(Plane.WorldXY, ix, iy, iz));
}

}

private double ComputeFaceFeatureSizes(Mesh mesh, double minSize, double maxSize)
{
int fCount = mesh.Faces.Count;
double values = new double[fCount];

for (int i = 0; i < fCount; i++)
{
  MeshFace f = mesh.Faces[i];

  Point3d a = mesh.Vertices[f.A];
  Point3d b = mesh.Vertices[f.B];
  Point3d c = mesh.Vertices[f.C];

  double e0 = a.DistanceTo(b);
  double e1 = b.DistanceTo(c);
  double e2 = c.DistanceTo(a);

  double avgEdge;

  if (f.IsQuad)
  {
    Point3d d = mesh.Vertices[f.D];
    double e3 = c.DistanceTo(d);
    double e4 = d.DistanceTo(a);
    avgEdge = (e0 + e1 + e3 + e4) / 4.0;
  }
  else
  {
    avgEdge = (e0 + e1 + e2) / 3.0;
  }

  values[i] = Math.Max(minSize, Math.Min(maxSize, avgEdge));
}

return values;

}
}

Hello
“classical” voxelization with dividing just by 2 is a way to ensure that each box contains a percentage of the object. Like Here where it seems that each box (on the right) contains 100 % of the points Fast parallel surface and solid voxelization on GPUs
https://michael-schwarz.com/research/publ/2010/vox/teaser.png
Division by 2 (could be 3 4 …) enable to keep boxes to just touching.
Subdivided Voxelization: - #12 by maje90

If you don’t want that, can you draw what you are after ?