Skip to content

Commit b7d883f

Browse files
committed
v2.2 update: added velocity initialization in revoxelization to enable simulating moving/rotating geometries
1 parent 245303e commit b7d883f

File tree

7 files changed

+265
-31
lines changed

7 files changed

+265
-31
lines changed

README.md

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,40 @@
22

33
The fastest and most memory efficient lattice Boltzmann CFD software, running on all GPUs via [OpenCL](https://github.com/ProjectPhysX/OpenCL-Wrapper "OpenCL-Wrapper").
44

5-
<a href="https://youtu.be/o3TPN142HxM"><img src="https://img.youtube.com/vi/o3TPN142HxM/maxresdefault.jpg" alt="FluidX3D - A New Era of Computational Fluid Dynamics Software" width="50%"></img></a><a href="https://youtu.be/oC6U1M0Fsug"><img src="https://img.youtube.com/vi/oC6U1M0Fsug/maxresdefault.jpg" alt="8 billion voxel raindrop simulation" width="50%"></img></a><br>
6-
<a href="https://youtu.be/NQPgumd3Ei8"><img src="https://img.youtube.com/vi/NQPgumd3Ei8/maxresdefault.jpg" alt="Hydraulic jump simulation" width="50%"></img></a><a href="https://youtu.be/3JNVBQyetMA"><img src="https://img.youtube.com/vi/3JNVBQyetMA/maxresdefault.jpg" alt="Star Wars X-wing simulation" width="50%"></img></a>
5+
<a href="https://youtu.be/o3TPN142HxM"><img src="https://img.youtube.com/vi/o3TPN142HxM/maxresdefault.jpg" width="50%"></img></a><a href="https://youtu.be/oC6U1M0Fsug"><img src="https://img.youtube.com/vi/oC6U1M0Fsug/maxresdefault.jpg" width="50%"></img></a><br>
6+
<a href="https://youtu.be/NQPgumd3Ei8"><img src="https://img.youtube.com/vi/NQPgumd3Ei8/maxresdefault.jpg" width="50%"></img></a><a href="https://youtu.be/aqG8qZ_Gc4U"><img src="https://img.youtube.com/vi/aqG8qZ_Gc4U/maxresdefault.jpg" width="50%"></img></a>
7+
8+
9+
<details><summary>Update History</summary>
10+
11+
- v1.0
12+
- initial release
13+
- v1.1
14+
- added solid voxelization on GPU (slow algorithm)
15+
- added tool to print current camera position (key_H)
16+
- minor bug fix (workaround for Intel iGPU driver bug with triangle rendering)
17+
- v1.2
18+
- added functions to compute force/torque on objects
19+
- added function to translate Mesh
20+
- added Stokes drag validation setup
21+
- v1.3
22+
- added unit conversion functions for torque
23+
- `FORCE_FIELD` and `VOLUME_FORCE` can now be used independently
24+
- minor bug fix (workaround for AMD legacy driver bug with binary number literals)
25+
- v1.4
26+
- added interactive graphics mode on Linux with X11
27+
- fixed streamline visualization bug in 2D
28+
- v2.0
29+
- added (cross-vendor) multi-GPU support on a single node (PC/laptop/server)
30+
- v2.1
31+
- made solid voxelization on GPU lightning fast (new algorithm, from minutes to milliseconds)
32+
- v2.2
33+
- added option to voxelize moving/rotating geometry on GPU, with automatic velocity initialization for each grid point based on center of rotation, linear velocity and rotational velocity
34+
- cells that are converted from solid->fluid during re-voxelization now have their DDFs properly initialized
35+
- added option to not auto-scale mesh during `read_stl(...)`, with negative `size` parameter
36+
- added kernel for solid boundary rendering with marching-cubes
737

38+
</details>
839

940

1041
## Compute Features

src/info.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ void Info::print_logo() const {
7070
print("| "); print("\\ \\ / /", c); print(" |\n");
7171
print("| "); print("\\ ' /", c); print(" |\n");
7272
print("| "); print("\\ /", c); print(" |\n");
73-
print("| "); print("\\ /", c); print(" FluidX3D Version 2.0 |\n");
73+
print("| "); print("\\ /", c); print(" FluidX3D Version 2.2 |\n");
7474
print("| "); print("'", c); print(" Copyright (c) Moritz Lehmann |\n");
7575
}
7676
void Info::print_initialize() {

src/kernel.cpp

Lines changed: 98 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2065,9 +2065,13 @@ string opencl_c_container() { return R( // ########################## begin of O
20652065

20662066

20672067

2068-
)+R(kernel void voxelize_mesh(const uint direction, global uchar* flags, const uchar flag, const global float* p0, const global float* p1, const global float* p2, const uint triangle_number, float x0, float y0, float z0, float x1, float y1, float z1) { // voxelize triangle mesh
2068+
)+R(kernel void voxelize_mesh(const uint direction, global fpxx* fi, global float* u, global uchar* flags, const ulong t, const uchar flag, const global float* p0, const global float* p1, const global float* p2, const global float* bbu) { // voxelize triangle mesh
20692069
const uint a=get_global_id(0), A=get_area(direction); // a = domain area index for each side, A = area of the domain boundary
20702070
if(a>=A) return; // area might not be a multiple of def_workgroup_size, so return here to avoid writing in unallocated memory space
2071+
const uint triangle_number = as_uint(bbu[0]);
2072+
const float x0=bbu[ 1], y0=bbu[ 2], z0=bbu[ 3], x1=bbu[ 4], y1=bbu[ 5], z1=bbu[ 6];
2073+
const float cx=bbu[ 7], cy=bbu[ 8], cz=bbu[ 9], ux=bbu[10], uy=bbu[11], uz=bbu[12], rx=bbu[13], ry=bbu[14], rz=bbu[15];
2074+
20712075
const uint3 xyz = direction==0u ? (uint3)((uint)max(0, (int)x0-def_Ox), a%def_Ny, a/def_Ny) : direction==1u ? (uint3)(a/def_Nz, (uint)max(0, (int)y0-def_Oy), a%def_Nz) : (uint3)(a%def_Nx, a/def_Nx, (uint)max(0, (int)z0-def_Oz));
20722076
const float3 p = position(xyz);
20732077
const float3 offset = (float3)(0.5f*(float)((def_Nx-2u*(def_Dx>1u))*def_Dx)-0.5f, 0.5f*(float)((def_Ny-2u*(def_Dy>1u))*def_Dy)-0.5f, 0.5f*(float)((def_Nz-2u*(def_Dz>1u))*def_Dz)-0.5f)+(float3)(def_domain_offset_x, def_domain_offset_y, def_domain_offset_z);
@@ -2127,10 +2131,7 @@ string opencl_c_container() { return R( // ########################## begin of O
21272131
}
21282132
}/**/
21292133

2130-
if(intersections==0u) return; // no intersection for the entire column, so return immediately
2131-
bool inside = (intersections%2u)&&(intersections_check%2u);
2132-
2133-
for(int i=1; i<(int)intersections; i++) { // insertion sort of distances
2134+
for(int i=1; i<(int)intersections; i++) { // insertion-sort distances
21342135
ushort t = distances[i];
21352136
int j = i-1;
21362137
while(distances[j]>t&&j>=0) {
@@ -2140,15 +2141,47 @@ string opencl_c_container() { return R( // ########################## begin of O
21402141
distances[j+1] = t;
21412142
}
21422143

2143-
uint intersection = 0u; // iterate through column
2144+
bool inside = (intersections%2u)&&(intersections_check%2u);
2145+
const bool set_u = sq(ux)+sq(uy)+sq(uz)+sq(rx)+sq(ry)+sq(rz)>0.0f;
2146+
uint intersection = intersections%2u!=intersections_check%2u; // iterate through column, start with 0 regularly, start with 1 if forward and backward intersection count evenness differs (error correction)
21442147
const uint h0 = direction==0u ? xyz.x : direction==1u ? xyz.y : xyz.z;
2145-
for(uint h=h0; h<min(h0+(uint)distances[intersections-1u], (uint)def_N/A); h++) {
2148+
const uint hmax = direction==0u ? (uint)clamp((int)x1-def_Ox, 0, (int)def_Nx-1) : direction==1u ? (uint)clamp((int)y1-def_Oy, 0, (int)def_Ny-1) : (uint)clamp((int)z1-def_Oz, 0, (int)def_Nz-1);
2149+
const uint hmesh = h0+(uint)distances[intersections-1u];
2150+
for(uint h=h0; h<hmax; h++) {
21462151
while(intersection<intersections&&h>h0+(uint)distances[intersection]) {
21472152
inside = !inside; // passed mesh intersection, so switch inside/outside state
21482153
intersection++;
21492154
}
2155+
inside &= (intersection<intersections&&h<hmesh); // point must be outside if there are no more ray-mesh intersections ahead (error correction)
21502156
const ulong n = index((uint3)(direction==0u?h:xyz.x, direction==1u?h:xyz.y, direction==2u?h:xyz.z));
2151-
if(inside) flags[n] |= flag;
2157+
uchar flagsn = flags[n];
2158+
if(inside) {
2159+
flagsn = (flagsn&~TYPE_BO)|flag;
2160+
if(set_u) {
2161+
const float3 p = position(coordinates(n))+offset;
2162+
const float3 un = (float3)(ux, uy, uz)+cross((float3)(cx, cy, cz)-p, (float3)(rx, ry, rz));
2163+
u[ n] = un.x;
2164+
u[ def_N+(ulong)n] = un.y;
2165+
u[2ul*def_N+(ulong)n] = un.z;
2166+
}
2167+
} else {
2168+
if(set_u) {
2169+
const float3 un = (float3)(u[n], u[def_N+(ulong)n], u[2ul*def_N+(ulong)n]); // for velocity voxelization, only clear moving boundaries
2170+
if((flagsn&TYPE_BO)==TYPE_S) { // reconstruct DDFs when boundary point is converted to fluid
2171+
uint j[def_velocity_set]; // neighbor indices
2172+
neighbors(n, j); // calculate neighbor indices
2173+
float feq[def_velocity_set]; // f_equilibrium
2174+
calculate_f_eq(1.0f, un.x, un.y, un.z, feq);
2175+
store_f(n, feq, fi, j, t); // write to fi
2176+
}
2177+
if(sq(un.x)+sq(un.y)+sq(un.z)>0.0f) {
2178+
flagsn = (flagsn&TYPE_BO)==TYPE_MS ? flagsn&~TYPE_MS : flagsn&~flag;
2179+
}
2180+
} else {
2181+
flagsn = (flagsn&TYPE_BO)==TYPE_MS ? flagsn&~TYPE_MS : flagsn&~flag;
2182+
}
2183+
}
2184+
flags[n] = flagsn;
21522185
}
21532186
} // voxelize_mesh()
21542187

@@ -2164,7 +2197,7 @@ string opencl_c_container() { return R( // ########################## begin of O
21642197

21652198
)+"#ifdef GRAPHICS"+R(
21662199

2167-
)+"#ifndef FORCE_FIELD"+R(
2200+
)+"#ifndef FORCE_FIELD"+R( // render flags as grid
21682201
)+R(kernel void graphics_flags(const global uchar* flags, const global float* camera, global int* bitmap, global int* zbuffer) {
21692202
)+"#else"+R( // FORCE_FIELD
21702203
)+R(kernel void graphics_flags(const global uchar* flags, const global float* camera, global int* bitmap, global int* zbuffer, const global float* F) {
@@ -2231,7 +2264,62 @@ string opencl_c_container() { return R( // ########################## begin of O
22312264
}
22322265
}
22332266
)+"#endif"+R( // FORCE_FIELD
2234-
}
2267+
}/**/
2268+
2269+
/*)+"#ifndef FORCE_FIELD"+R( // render solid boundaries with marching-cubes
2270+
)+R(kernel void graphics_flags(const global uchar* flags, const global float* camera, global int* bitmap, global int* zbuffer) {
2271+
)+"#else"+R( // FORCE_FIELD
2272+
)+R(kernel void graphics_flags(const global uchar* flags, const global float* camera, global int* bitmap, global int* zbuffer, const global float* F) {
2273+
)+"#endif"+R( // FORCE_FIELD
2274+
const uint n = get_global_id(0);
2275+
if(n>=(uint)def_N||is_halo(n)) return; // don't execute graphics_flags() on halo
2276+
const uint3 xyz = coordinates(n);
2277+
if(xyz.x>=def_Nx-1u||xyz.y>=def_Ny-1u||xyz.z>=def_Nz-1u) return;
2278+
//if(xyz.x==0u||xyz.y==0u||xyz.z==0u||xyz.x>=def_Nx-2u||xyz.y>=def_Ny-2u||xyz.z>=def_Nz-2u) return;
2279+
uint j[8];
2280+
const uint x0 = xyz.x; // cube stencil
2281+
const uint xp = xyz.x+1u;
2282+
const uint y0 = xyz.y *def_Nx;
2283+
const uint yp = (xyz.y+1u)*def_Nx;
2284+
const uint z0 = xyz.z *def_Ny*def_Nx;
2285+
const uint zp = (xyz.z+1u)*def_Ny*def_Nx;
2286+
j[0] = n ; // 000
2287+
j[1] = xp+y0+z0; // +00
2288+
j[2] = xp+y0+zp; // +0+
2289+
j[3] = x0+y0+zp; // 00+
2290+
j[4] = x0+yp+z0; // 0+0
2291+
j[5] = xp+yp+z0; // ++0
2292+
j[6] = xp+yp+zp; // +++
2293+
j[7] = x0+yp+zp; // 0++
2294+
float v[8];
2295+
for(uint i=0u; i<8u; i++) v[i] = (float)((flags[j[i]]&TYPE_BO)==TYPE_S);
2296+
float3 triangles[15]; // maximum of 5 triangles with 3 vertices each
2297+
const uint tn = marching_cubes(v, 0.5f, triangles); // run marching cubes algorithm
2298+
if(tn==0u) return;
2299+
float camera_cache[15]; // cache camera parameters in case the kernel draws more than one shape
2300+
for(uint i=0u; i<15u; i++) camera_cache[i] = camera[i];
2301+
const float3 offset = (float3)((float)xyz.x+0.5f-0.5f*(float)def_Nx, (float)xyz.y+0.5f-0.5f*(float)def_Ny, (float)xyz.z+0.5f-0.5f*(float)def_Nz);
2302+
for(uint i=0u; i<tn; i++) {
2303+
const float3 p0 = triangles[3u*i ]+offset;
2304+
const float3 p1 = triangles[3u*i+1u]+offset;
2305+
const float3 p2 = triangles[3u*i+2u]+offset;
2306+
const float3 p=(p0+p1+p2)/3.0f, normal=cross(p1-p0, p2-p0);
2307+
const int c = lighting(191<<16|191<<8|191, p, normal, camera_cache);
2308+
draw_triangle(p0, p1, p2, c, camera_cache, bitmap, zbuffer);
2309+
}
2310+
)+"#ifdef FORCE_FIELD"+R(
2311+
const uchar flagsn_bo = flags[n]&TYPE_BO;
2312+
const float3 p = position(xyz);
2313+
if(flagsn_bo==TYPE_S) {
2314+
const float3 Fn = def_scale_F*(float3)(F[n], F[def_N+(ulong)n], F[2ul*def_N+(ulong)n]);
2315+
const float Fnl = length(Fn);
2316+
if(Fnl>0.0f) {
2317+
const int c = iron_color(255.0f*Fnl); // color boundaries depending on the force on them
2318+
draw_line(p, p+5.0f*Fn, c, camera_cache, bitmap, zbuffer); // draw colored force vectors
2319+
}
2320+
}
2321+
)+"#endif"+R( // FORCE_FIELD
2322+
}/**/
22352323

22362324
)+R(kernel void graphics_field(const global uchar* flags, const global float* u, const global float* camera, global int* bitmap, global int* zbuffer) {
22372325
const uint n = get_global_id(0);

src/lbm.cpp

Lines changed: 48 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -164,25 +164,54 @@ uint LBM_Domain::get_velocity_set() const {
164164
return velocity_set;
165165
}
166166

167-
void LBM_Domain::voxelize_mesh_on_device(const Mesh* mesh, const uchar flag) { // voxelize triangle mesh
167+
void LBM_Domain::voxelize_mesh_on_device(const Mesh* mesh, const uchar flag, const float3& rotation_center, const float3& linear_velocity, const float3& rotational_velocity) { // voxelize triangle mesh
168168
Memory<float3> p0(device, mesh->triangle_number, 1u, mesh->p0);
169169
Memory<float3> p1(device, mesh->triangle_number, 1u, mesh->p1);
170170
Memory<float3> p2(device, mesh->triangle_number, 1u, mesh->p2);
171+
Memory<float> bounding_box_and_velocity(device, 16u);
171172
const float x0=mesh->pmin.x, y0=mesh->pmin.y, z0=mesh->pmin.z, x1=mesh->pmax.x, y1=mesh->pmax.y, z1=mesh->pmax.z; // use bounding box of mesh to speed up voxelization
172-
const float M[3] = { (y1-y0)*(z1-z0), (z1-z0)*(x1-x0), (x1-x0)*(y1-y0) };
173-
float Mmin = M[0];
173+
bounding_box_and_velocity[ 0] = as_float(mesh->triangle_number);
174+
bounding_box_and_velocity[ 1] = x0;
175+
bounding_box_and_velocity[ 2] = y0;
176+
bounding_box_and_velocity[ 3] = z0;
177+
bounding_box_and_velocity[ 4] = x1;
178+
bounding_box_and_velocity[ 5] = y1;
179+
bounding_box_and_velocity[ 6] = z1;
180+
bounding_box_and_velocity[ 7] = rotation_center.x;
181+
bounding_box_and_velocity[ 8] = rotation_center.y;
182+
bounding_box_and_velocity[ 9] = rotation_center.z;
183+
bounding_box_and_velocity[10] = linear_velocity.x;
184+
bounding_box_and_velocity[11] = linear_velocity.y;
185+
bounding_box_and_velocity[12] = linear_velocity.z;
186+
bounding_box_and_velocity[13] = rotational_velocity.x;
187+
bounding_box_and_velocity[14] = rotational_velocity.y;
188+
bounding_box_and_velocity[15] = rotational_velocity.z;
174189
uint direction = 0u;
175-
for(uint i=1u; i<3u; i++) {
176-
if(M[i]<Mmin) {
177-
Mmin = M[i];
178-
direction = i; // find direction of minimum bounding-box cross-section area
190+
if(length(rotational_velocity)==0.0f) { // choose direction of minimum bounding-box cross-section area
191+
float v[3] = { (y1-y0)*(z1-z0), (z1-z0)*(x1-x0), (x1-x0)*(y1-y0) };
192+
float vmin = v[0];
193+
for(uint i=1u; i<3u; i++) {
194+
if(v[i]<vmin) {
195+
vmin = v[i];
196+
direction = i;
197+
}
198+
}
199+
} else { // choose direction closest to rotation axis
200+
float v[3] = { rotational_velocity.x, rotational_velocity.y, rotational_velocity.z };
201+
float vmax = v[0];
202+
for(uint i=1u; i<3u; i++) {
203+
if(v[i]>vmax) {
204+
vmax = v[i];
205+
direction = i; // find direction of minimum bounding-box cross-section area
206+
}
179207
}
180208
}
181209
const ulong A[3] = { (ulong)Ny*(ulong)Nz, (ulong)Nz*(ulong)Nx, (ulong)Nx*(ulong)Ny };
182-
Kernel kernel_voxelize_mesh(device, A[direction], "voxelize_mesh", direction, flags, flag, p0, p1, p2, mesh->triangle_number, x0, y0, z0, x1, y1, z1);
210+
Kernel kernel_voxelize_mesh(device, A[direction], "voxelize_mesh", direction, fi, u, flags, t+1ull, flag, p0, p1, p2, bounding_box_and_velocity);
183211
p0.write_to_device();
184212
p1.write_to_device();
185213
p2.write_to_device();
214+
bounding_box_and_velocity.write_to_device();
186215
kernel_voxelize_mesh.run();
187216
}
188217
void LBM_Domain::enqueue_unvoxelize_mesh_on_device(const Mesh* mesh, const uchar flag) { // remove voxelized triangle mesh from LBM grid
@@ -750,16 +779,23 @@ void LBM::write_status(const string& path) { // write LBM status report to a .tx
750779
write_file(filename, status);
751780
}
752781

753-
void LBM::voxelize_mesh_on_device(const Mesh* mesh, const uchar flag) { // voxelize triangle mesh
782+
void LBM::voxelize_mesh_on_device(const Mesh* mesh, const uchar flag, const float3& rotation_center, const float3& linear_velocity, const float3& rotational_velocity) { // voxelize triangle mesh
754783
if(get_D()==1u) {
755-
lbm[0]->voxelize_mesh_on_device(mesh, flag); // if this crashes on Windows, create a TdrDelay 32-bit DWORD with decimal value 300 in Computer\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\GraphicsDrivers
784+
lbm[0]->voxelize_mesh_on_device(mesh, flag, rotation_center, linear_velocity, rotational_velocity); // if this crashes on Windows, create a TdrDelay 32-bit DWORD with decimal value 300 in Computer\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\GraphicsDrivers
756785
} else {
757786
thread* threads=new thread[get_D()]; for(uint d=0u; d<get_D(); d++) threads[d]=thread([=]() {
758-
lbm[d]->voxelize_mesh_on_device(mesh, flag);
787+
lbm[d]->voxelize_mesh_on_device(mesh, flag, rotation_center, linear_velocity, rotational_velocity);
759788
}); for(uint d=0u; d<get_D(); d++) threads[d].join(); delete[] threads;
760789
}
790+
#ifdef MOVING_BOUNDARIES
791+
if(flag==TYPE_S&&(length(linear_velocity)>0.0f||length(rotational_velocity)>0.0f)) update_moving_boundaries();
792+
#endif // MOVING_BOUNDARIES
793+
if(!initialized) {
794+
flags.read_from_device();
795+
u.read_from_device();
796+
}
761797
}
762-
void LBM::unvoxelize_mesh_on_device(const Mesh* mesh, const uchar flag) { // remove voxelized triangle mesh from LBM grid
798+
void LBM::unvoxelize_mesh_on_device(const Mesh* mesh, const uchar flag) { // remove voxelized triangle mesh from LBM grid by removing all flags in mesh bounding box (only required when bounding box size changes during re-voxelization)
763799
for(uint d=0u; d<get_D(); d++) lbm[d]->enqueue_unvoxelize_mesh_on_device(mesh, flag);
764800
for(uint d=0u; d<get_D(); d++) lbm[d]->finish_queue();
765801
}

src/lbm.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ class LBM_Domain {
121121
void set_fz(const float fz) { this->fz = fz; } // set global froce per volume
122122
void set_f(const float fx, const float fy, const float fz) { set_fx(fx); set_fy(fy); set_fz(fz); } // set global froce per volume
123123

124-
void voxelize_mesh_on_device(const Mesh* mesh, const uchar flag=TYPE_S); // voxelize mesh
124+
void voxelize_mesh_on_device(const Mesh* mesh, const uchar flag=TYPE_S, const float3& rotation_center=float3(0.0f), const float3& linear_velocity=float3(0.0f), const float3& rotational_velocity=float3(0.0f)); // voxelize mesh
125125
void enqueue_unvoxelize_mesh_on_device(const Mesh* mesh, const uchar flag=TYPE_S); // remove voxelized triangle mesh from LBM grid
126126

127127
#ifdef GRAPHICS
@@ -473,7 +473,7 @@ class LBM {
473473
}
474474
void write_status(const string& path=""); // write LBM status report to a .txt file
475475

476-
void voxelize_mesh_on_device(const Mesh* mesh, const uchar flag=TYPE_S); // voxelize mesh
476+
void voxelize_mesh_on_device(const Mesh* mesh, const uchar flag=TYPE_S, const float3& rotation_center=float3(0.0f), const float3& linear_velocity=float3(0.0f), const float3& rotational_velocity=float3(0.0f)); // voxelize mesh
477477
void unvoxelize_mesh_on_device(const Mesh* mesh, const uchar flag=TYPE_S); // remove voxelized triangle mesh from LBM grid
478478
void voxelize_stl(const string& path, const float3& center, const float3x3& rotation, const float size=-1.0f, const uchar flag=TYPE_S); // read and voxelize binary .stl file
479479
void voxelize_stl(const string& path, const float3x3& rotation, const float size=-1.0f, const uchar flag=TYPE_S); // read and voxelize binary .stl file (place in box center)

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy