Hardware-accelerated object picker (finding triangle or point under the cursor).
Viewport camera modes
Perspective and Orthographic,
Renderable objects
Cloud points,
Meshes,
Lines,
Voxels
DistanceMaps
Color and textures
Vertices color map,
Triangles color map,
Static textures.
Order independed transparency
Perfect and fast visualization of many transparent objects with complex geometry using OpenGL 4:
Convenient for mesh algorithms debugging, showing mesh edges, boundaries
Computed-tomography reconstruction
An ability to reconstruct voxel object from a set of projection images (radiographs) and known cone-beam CT scanner geometry parameters. Nvidia GPU is utilized via CUDA technology for the best reconstruction performance.
I have this simple stl file of 4 triangles, very simple (the attached file is an stl file renamed as txt).
Running this python code, the memory increase till the crash of the VM:
Hi,
Great work! But I feel the documentation is lagging behind the efforts. I wanted to know if it is possible to use the "Inspect > Distance Compare" functionality of Mesh Inspector, but completely in python? For example, I could compute the Signed Distance between the two meshes using:
import meshlib.mrmeshpy as mr
mesh1 = mr.loadMesh("Cube.stl")
mesh2 = mr.loadMesh("Torus.stl"))
z = mr.findSignedDistance(mesh1, mesh2)
print(z.signedDist)
Which gives the same output as "Inspect > Signed Distance" in Mesh Inspector.
How to have the Distance Compare map in python?
I found that there is a global search for feature edges in the software.But if I want to find a local segment of a line.What should I do better?
For example, the cyan edge has two tangent lines at both ends.
How should I find out the local characteristic edge out
I see that the software currently only supports one contour for filling.
I can't choose two contours
If I want to fill between two contours, how should I do it best?I need to fill between two contours
I'm trying to gain a better understanding of how to navigate the mesh topology. Could you please point me in the right direction when it comes to the following operations?
Face/Edge/Vertex Selection
Given a ray, how can I select the intersecting mesh element? The function below returns a MeshTriPoint, is there a way to retrieve the corresponding Face, Edge or Vertex?
Internal Face Selection
Given a closed edge loop (cyan), select all internal faces (yellow):
Hole & Boundary Selection
Given a single edge (cyan), select the entire edge loop forming the hole boundary (yellow). Same applies to external boundaries:
Expanding/Shrinking Selection
Given an arbitrary amount of selected faces expand or shrink the selection to include the neighboring faces:
The meshlib demo web application(https://app.meshinspector.com/RMISingle/) cannot run on iphone8 (ios 14.5). I tested it on real machine and the simulator, both failed. Besides, it runs normally with higher ios version.
I compiled the web application(single-thread) from source code, but result in same problem.
It will be much helpful if the single-thread (and maybe no simd?) meshlib can work on much more mobile deivces. Do you have any ideas? Thanks.
I'm interested in meshlib, which has a lot of features I need, and I want to use it in the browser to improve the efficiency of 3D operations. Does meshlib support converting to wasm to run in a browser? If there is a feasible plan can you provide some ideas?
However, instead of returning the intersection of the two meshes, I end up getting the not(intersection) of the two meshes. By not(intersection) i mean parts of mesh_a that don't lie inside mesh_b.
For example, given mesh_a:
with mesh_b on top:
i get the intersection as:
If I input two coordinate points of the surface model, I want to find the half of the geodesic distance between the two points?
How should I split the mesh and figure out the proper half edge?
code**************************
auto cylinder = MR::MeshLoad::fromAnyStl("D:/code/meshlib221/chamfer/model/cylinder.stl");
MR::vector3f pointA;
MR::vector3f pointB;
code****************************
Do I need to split the mesh one by one?
std::vectorMR::EdgeId MeshContours;
I tried to compile on mac's ubuntu virtual machine but failed,I could't find cpuid.h in my /usr/include so I copy one from arm g++ compiler dir in order to pass the compiling, but it dosen't work
When I use library files for debugging,Can I have a link to the source code?
Can I debug directly into the cpp file?
I want to see other data during the debugging process
Would be good to add ability to transfer progress callback function to MR::compressZip. This will make it possiblle to show smooth ProgressBar in the UI.
There is an attribute error when i want to subdivide a region of mesh and set the 'region' attribute. Below is my test code, its the test code from MeshLib/test_python/test_subdivider.py but with 'region' attribute modifed. And I have not figure out how to set the 'region' attribute value rightly, so just set as None to show the error.
'meshlib.mrmeshpy.SubdivideSettings' object has no attribute 'region'
Besides, I found the 'region' attribute is indeed binded and there also exists 'region' attribute in debug window, but i still cannot set this attribute and the error occured.
I'm testing stitching_two_holes function using python code which is similar to here, but got bad result in some cases. For example, (1) very narrow triangle directly linking the two hole border even after subdivision. (2) or the result mesh is exploded. I modified some filing hole configurations but cannot solve those problems. Besides, because we cannot modify the mesh's existing region when filing holes, I need to set SubdivideSettings.subdivideBorder=False(but this cannot avoid problems).
I tested the meshInspector's stitching_two_holes function, and it works correctly on my test meshes. I wonder if you can provide some sample code which can get same result as MeshInspector. With that, we can compare hole filling result with meshinspector directly when we encountered bad result. And it will be much convenient to report problems to you.
Hello,Is it not possible to use the output results directly?
When I use the geodesic algorithm directly, I find that the points in the middle cannot be printed code**********
auto model = MR::MeshLoad::fromAnyStl("D:/code/meshlib223/ChamferAllEdges/model/Subtraction.stl");
auto firstTriPointTest = MR::MeshTriPoint::MeshTriPoint(model->topology, model->topology.org(MR::EdgeId(211)));
auto secondTriPointTest = MR::MeshTriPoint::MeshTriPoint(model->topology, model->topology.org(MR::EdgeId(111)));
MR::GeodesicPathApprox atype = MR::GeodesicPathApprox::DijkstraBiDir;
auto geoDesicPath = MR::computeGeodesicPathApprox(model,firstTriPointTest,secondTriPointTest,atype);
for (auto geoDesic : geoDesicPath)
{
std::cout << model->edgePoint(geoDesic).x << " " << model->edgePoint(geoDesic).y << " " << model->edgePoint(geoDesic).z << std::endl;;
} code************
In the upper left corner you see the timings and MeshLib is a clear winner here, especially with higher amount of remeshing iterations.
There are, however, a few issues I can see with the results:
The algorithm doesn't seem to change the mesh when target edge length is set to exceed the longest edge (in this case >4.5)
GC and cinolib refine the mesh with each remeshing iteration. MeshLib seems to only work once.
MeshLib is quite inconsistent - notice how some portions of the mesh are mor refined than the others:
Hello,If I use a plane to cut off a model, can I cut off some mesh features?For example, I want to cut off the A feature of the mesh model, but I don't want to cut off the B feature of the mesh model.
Is there any better way to solve this problem?
In python, after manipulation of meshlib meshes, I want to export the mesh back to two numpy arrays, one containing the vertices and another containing the faces. Any help?
First of all - thanks for your great work on this library! It's really coming together nicely!
Since there is no dedicated Discussion section, I'd like to ask here whether it's possible to cut meshes with polylines. Example below:
Please note how new vertices and edges are introduced to the mesh and it is pulled towards the user provided polyline.
Any suggestions how this could be achieved in MeshLib?
Do you have plans to write mesh denoising algorithms that can keep sharp features of flat regions and smooth regions.
see the flat regions and the hole in the above image
This might sound a bit silly, as I'm following the tutorial on https://emscripten.org/ for the conversion, but I'm encountering various errors. If possible, could you please provide additional steps on how to use Emscripten for wasm conversion? Thank you.
I replaced the triplet with x64-windows-release and it proceeded further but fails at installing boost:
error: building boost-system:x64-windows-release failed with: BUILD_FAILED
error: Please ensure you're using the latest port files with `git pull` and `vcpkg update`.
Then check for known issues at:
https://github.com/microsoft/vcpkg/issues?q=is%3Aissue+is%3Aopen+in%3Atitle+boost-system
You can submit a new issue at:
https://github.com/microsoft/vcpkg/issues/new?template=report-package-build-failure.md&title=[boost-system]+Build+error
Include '[boost-system] Build error' in your bug report title, the following version information in your bug description, and attach any relevant failure logs from above.
vcpkg-tool version: 2022-11-10-5fdee72bc1fceca198fb1ab7589837206a8b81ba
vcpkg-scripts version: 6f7ffeb18 2022-11-11 (7 months ago)
Thank you for providing such an excellent library. However, I have encountered the following issue while using it. As indicated in the comments in the main function, I provided verts and faces to generate the mesh and saved the mesh locally. However, upon reloading the mesh, I noticed that the lengths of the read verts and faces no longer match the original ones. What could be the reason for this?
#include<iostream>#include<vector>#include"MRMesh/MRMesh.h"#include"MRMesh/MRMeshLoad.h"#include"MRMesh/MRMeshSave.h"#include"MRMesh/MRPointCloud.h"#include"MRMesh/MRMeshBuilder.h"template<typenameT>std::vector<std::array<T, 3>> array1dTo2d(constT*data, constintdataLength) {
std::vector<std::array<T, 3>> ret;
for (inti=0; i<dataLength; i+=3) {
std::array<T, 3>vs{data[i], data[i+1], data[i+2]};
ret.push_back(vs);
}
returnret;
}
voidprintVerts(constMR::Mesh&mesh)
{
intnumVerts=mesh.topology.lastValidVert() +1;
std::vector<float>datas;
datas.reserve(numVerts*3);
for (inti=0; i<numVerts; i++)
for (intj=0; j<3; j++)
datas.push_back(mesh.points.vec_[i][j]);
for (constfloat&value : datas) {
std::cout << value << " ";
}
std::cout << std::endl;
}
voidprintFaces(constMR::Mesh&mesh)
{
const auto&validFaces=mesh.topology.getValidFaces();
intnumFaces=mesh.topology.lastValidFace() +1;
std::vector<int>datas;
datas.reserve(numFaces*3);
for ( inti=0; i<numFaces; ++i )
{
MR::FaceIdf=MR::FaceId( i );
if ( validFaces.test( f ) )
{
MR::VertIdv[3];
mesh.topology.getTriVerts( f, v );
for ( intvi=0; vi<3; ++vi )
datas.push_back(v[vi]);
}
else
{
for ( intvi=0; vi<3; ++vi )
datas.push_back(0);
}
}
for (constint&value : datas) {
std::cout << value << " ";
}
std::cout << std::endl;
}
MR::MeshtransToMesh(constfloat*verts1d, constintverts1dLength, constint*faces1d, constintfaces1dLength)
{
std::vector<std::array<float, 3>> verts2d=array1dTo2d(verts1d, verts1dLength);
std::vector<std::array<int, 3>> faces2d=array1dTo2d(faces1d, faces1dLength);
MR::Triangulationt;
MR::Meshresult;
t.reserve(faces2d.size());
for (conststd::array<int, 3>&data : faces2d) {
t.push_back( {
MR::VertId( int( data[0] ) ),
MR::VertId( int( data[1] ) ),
MR::VertId( int( data[2] ) )
} );
}
result.topology=MR::MeshBuilder::fromTriangles( t );
result.points.resize(verts2d.size());
for (inti=0; i<faces2d.size(); i++){
result.points[MR::VertId( i )] =MR::Vector3f(
float( faces2d[i][0] ),
float( faces2d[i][1] ),
float( faces2d[i][2] ) );
}
returnresult;
}
intmain() {
floatverts[] = {0.0, 1.0, 2.0, 2.0, 3.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
intfaces[] = {0, 1, 2, 2, 3, 0};
MR::Meshmesh=transToMesh(verts, 12, faces, 6);
// In the generated mesh using the provided verts and faces, // the read verts and faces match the given ones.printVerts(mesh); // The output print is as follows: 0 1 2 2 3 0 0 0 0 0 0 0printFaces(mesh); // The output print is as follows: 0 1 2 2 3 0MR::MeshSave::toAnySupportedFormat( mesh, "output.stl" );
MR::MeshoutputMesh=MR::MeshLoad::fromAnySupportedFormat("output.stl").value();
// When I save the previous mesh to the local storage and then reload it, // the read verts and faces do not match the provided ones.printVerts(outputMesh); // The output print is as follows: 0 1 2 2 3 0 0 0 0printFaces(outputMesh); // The output print is as follows: 0 1 2return0;
}
Is there any demo code to simplify/decimate mesh to target face number? The test_decimate code with DecimateSettings parameters seems cannot work as expected.
Hi, thanks to such a powerful library for 3d process.
Could anyone please help me with an easy case here: How to split mesh with a new point?
For example:
I have read all documents from meshlib, but examples useful for my case cannot be found.
Because I am familiar with python but not with C++, I have difficults to understand all the function details from documents.
If I have a contour surface,I want to fill one part first and then fill another part. I can split contours into two parts.
How should I avoid the influence of patch modification after the first filling?
I'm dealing with relatively large point-clouds (5-10M points). For my workflow, I need to sample these with a regular grid of approx. 10 000 sample points and find the id of the closest points from the point cloud to each sample point.
In Rhino 5M points and 10 000 samples takes approx. 20s to compute. Do you think MeshLib could do this faster?
I need this for a wind simulation study. Here is an example for a much smaller point cloud which runs in real-time:
I am trying to segment a mesh using MeshLib, I am not able to find the related source library or example. Do you an example for it?
Assuming this input mesh:
import meshlib.mrmeshpy as mrmeshpy
try:
mesh = mrmeshpy.loadMesh(mrmeshpy.Path("mesh.stl"))
except ValueError as e:
print(e)
you use openvdb to do mesh offset. It is good. but the origin openvdb code can not preserve sharp features.
Do you have plans to optimize the code to keep sharp features?
Hello,Is it possible to add index information of triangular faces to this software?When I use this software, I want to quickly find the index value or coordinates of a certain vertex, or the index value of a certain patch through the visual interface.
If the mesh have overlapping triangles ,it is easy to resolve this problems I guess, just split triangles.But if the mesh has many nearly overlapping triangles,(this triangles In theory, are not coplanar due to floating-point error),
I used a lot of free softwares, like meshlab meshmixer etc,none of them have this function, I think models with such flaws are common
,Do you have plan to solve this problem?
This one is a long shot and not directly related to MeshLib so please bear with me while I explain. In my application, I rely on a GLSL shader to draw contour lines interactively. The majority of the logic takes place in the Geometry Shader, which essentially implements this algorithm.
It works very fast even for complex meshes and I'm happily sharing the code below if you wanted to incorporate it into the MeshInspector.
2023-07-18.16-17-50.mp4
Things slow down, however, when I start displaying labels. The GLSL shader, is only aware of one primitive (triangle) at a time and generates unconnected line segments. I couldn't figure out a way of joining these segments on the GPU, hence I copy them over to the CPU and sort them there. It gets me the results I want, but leads to noticeable slow downs with more complex meshes.
Do you have any suggestions on how to approach it in a more efficient way?
The GLSL shader code:
//vertexShader #version 330
layout(location=0) invec3_meshVertex;
layout(location=1) invec3_meshNormal;
outvec3_normal;
voidmain() {
_normal=vec3(_meshNormal*0.01); // offset contours by a small amount to prevent Z-fightinggl_Position=vec4(_meshVertex+_normal, 1.0);
}
// geometryShader#version 330 core
layout (triangles) in;
layout (line_strip, max_vertices=64) out;
uniformmat4_worldToClip;
uniformvec4minorColor;
uniformvec4majorColor;
uniformfloatmajorInterval;
uniformfloatinterval;
uniformintmultiplier=100; // used for float precision while searching for major contoursinvec3_normal[];
outvec4color;
outvec3_positionT;
outvec3_normalT;
intcountAB;
intcountBC;
intcountCA;
floatinterpolate(floatstart, floatend, floatt){
returnstart+t* (end-start);
}
vec3[100] computeIntersections(vec4start, vec4end, inoutintcounter){
vec3[100] points;
counter=0;
floatminValue=min(start.z, end.z);
floatmaxValue=max(start.z, end.z);
floatstartValue=minValue-mod(minValue, interval);
floatdelta=maxValue-minValue;
floatnLevels=floor((maxValue-startValue) / interval);
for(inti=0; i <= nLevels; i++){
floatvalue=startValue+ (i*interval);
if (value<minValue||value>maxValue)
continue;
floatratio= (value-minValue) / delta;
if(start.z>end.z) ratio=1-ratio;
vec3point=vec3(interpolate(start.x, end.x, ratio), interpolate(start.y, end.y, ratio), value);
points[counter] =point;
counter++;
}
returnpoints;
}
voiddrawContours(vec3normalVector, vec3[100] start, vec3[100] end, vec3[100] mid, intstartCount, intendCount, intmidCount) {
_normalT=normalVector;
for (inti=0; i<35; i++) {
if (i >= startCount) break;
color=mod(round(start[i].z*multiplier), round(majorInterval*multiplier)) !=0 ? minorColor : majorColor;
_positionT=vec3(start[i].x, start[i].y, start[i].z);
gl_Position=_worldToClip*vec4(start[i], 1.0);
EmitVertex();
_positionT=vec3(end[i].x, end[i].y, end[i].z);
gl_Position=_worldToClip*vec4(end[i], 1.0);
EmitVertex();
EndPrimitive();
}
intcount=endCount-startCount;
for (inti=0; i<15; i++) {
if (i >= count) break;
color=mod(round(end[endCount-1-i].z*multiplier), round(majorInterval*multiplier)) !=0 ? minorColor : majorColor;
_positionT=vec3(end[endCount-1-i].x, end[endCount-1-i].y, end[endCount-1-i].z);
gl_Position=_worldToClip*vec4(end[endCount-1-i], 1.0);
EmitVertex();
_positionT=vec3(mid[midCount-1-i].x, mid[midCount-1-i].y, mid[midCount-1-i].z);
gl_Position=_worldToClip*vec4(mid[midCount-1-i], 1.0);
EmitVertex();
EndPrimitive();
}
}
voidmain() {
intcounter1;
intcounter2;
vec3[] AB=computeIntersections(gl_in[0].gl_Position, gl_in[1].gl_Position, countAB);
vec3[] BC=computeIntersections(gl_in[1].gl_Position, gl_in[2].gl_Position, countBC);
vec3[] CA=computeIntersections(gl_in[2].gl_Position, gl_in[0].gl_Position, countCA);
// A is the lowest pointif (gl_in[0].gl_Position.z<gl_in[1].gl_Position.z&&gl_in[0].gl_Position.z<gl_in[2].gl_Position.z)
{
if(countAB<countCA)
drawContours(_normal[0], AB, CA, BC, countAB, countCA, countBC);
elsedrawContours(_normal[0], CA, AB, BC, countCA, countAB, countBC);
}
// A is the highest pointelseif (gl_in[0].gl_Position.z>gl_in[1].gl_Position.z&&gl_in[0].gl_Position.z>gl_in[2].gl_Position.z)
{
if(countAB<countCA)
drawContours(_normal[0], BC, CA, AB, countBC, countCA, countAB);
elsedrawContours(_normal[0], BC, AB, CA, countBC, countAB, countCA);
}
// B lowest & C highestelseif (gl_in[2].gl_Position.z>gl_in[0].gl_Position.z&&gl_in[0].gl_Position.z>gl_in[1].gl_Position.z)
{
drawContours(_normal[0], AB, BC, CA, countAB, countBC, countCA);
}
// C lowest & B highestelse
{
drawContours(_normal[0], CA, BC, AB, countCA, countBC, countAB);
}
}
//fragmentShader#version 330 core
outvec4FragColor;
invec4color;
voidmain()
{
FragColor=color;
}
The c# code taking an array of float containing vertex coordinates and normal vectors of respective start & end points of each line segment. Here is the layout, where V1 is a vertex, and N1 its corresponding normal vector.
publicvoidUpdateContourLabels(){varlabels=newDictionary<double,(List<Point3d>,List<Vector3d>)>();if(WorkingMesh==null)return;intcorrectForQuadFaces= WorkingMesh.Faces.QuadCount >10?2:1;intcount=0;if(generatedPointsCount!=null)count=(int)generatedPointsCount[0]*6*correctForQuadFaces;for(inti=0;i<count;i+=6)// Stride is 6 due to two vec3 coming from the geometry shader{Point3dp=new Point3d
{X= contourPoints[i],Y= contourPoints[i+1],Z= Math.Round(contourPoints[i+2],2)};if(!labels.ContainsKey(p.Z)){varv=new Vector3d
{X= contourPoints[i+3],Y= contourPoints[i+4],Z= contourPoints[i+5]};
labels.Add(p.Z,(newList<Point3d>{ p },newList<Vector3d>{ v }));}else{boolisClose=false;foreach(var point in labels[p.Z].Item1){if(point.DistanceToSquared(p)< TerrainData.ContourLabelSpacing.CurrentValue){isClose=true;break;}}if(!isClose){varv=new Vector3d
{X= contourPoints[i+3],Y= contourPoints[i+4],Z= contourPoints[i+5]};
labels[p.Z].Item1.Add(p);
labels[p.Z].Item2.Add(v);}}}ContourLabels=labels;}
Hello,Does it currently support finding coordinates by point index value?I found that the point coordinates are found through the half-edge structure. code*******
MR::Vector3f orgpoint = cylinder->orgPnt(edge);
If I have a vertex id, can I directly retrieve its coordinates?
For example:MR::VertId Id;Can I find it directly through this id?
Thanks a lot
If I input ordered points(The points are not in the same plane), first I want to project the points onto the surface of the model, secondly cut the grid model through the connection between points, and finally I want to fill the cut surface with a new surface.
1.I need to intercept this contour line on the model
2.I need to cutmesh by this contour(The contour points are not in the same plane)
It would be nice to support >1 textures per object.
The max number of textures could be an initialization param of TPViewer (even better if it could be changed at runtime). Making the parameter a #define would be slighty unconvenient.
Each vertex would have N different texture coords, one per texture. Textures would be mixed according to their alpha channel.
I'm trying to compare two meshes and compute the volume difference between them. Here is a simple illustration. The cyan mesh (A) is sometimes higher, sometimes lower and sometimes at the same elevation as the magenta mesh (B). Sometimes there is no overlap at all:
Cross-sectional view to better illustrate the relationship between these two objects:
I'd like to calculate the difference between them and split it into cut (A is below B), and fill (A is above B). The results would be returned both as numerical values and as a heatmap on a resulting mesh. Here is an example from Wikipedia
My approach would be to:
Retrieve a bounding box of both meshes
Populate it with a regular grid of points
Shoot rays from each of the points
Calculate the distance between hit points and save the results
Construct a mesh from the grid of points and visualize the results using vertex colors for each of the points
It's relatively simple to implement but the results would be heavily dependent on the resolution of the point grid and it would be hard to trace the exact isolines where meshes transition from cut to fill. Furthermore, I'd like the result to be a series of closed solid meshes as opposed to an open surface mesh.