Announcement

Collapse
No announcement yet.

Create 3D surface out of bunch of coordinates

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Create 3D surface out of bunch of coordinates

    I am fairly new to using open inventor and was wondering how I can take a bunch of 3D coordinates and turn them into a 3D surface. I only have the basic Open Inventor tool kit at my disposal (no extensions such as MeshViz). I cannot guarantee that the points will be in any sort of meaningful order. Is there anything in the basic toolkit that can take these points and create a 3D surface out of them?

  • #2
    Hi,
    If you had the MeshViz extension you could try the PoTriangleMesh2D setGeometry() method, which implements a "2D" Delaunay triangulation. This algorithm can be used on 3D vertices as long as the coordinates represent an "elevation map", e.g. a seismic horizon, where the Z values can be ignored.

    Since you don't have MeshViz, you might want to look at geometry toolkits. For example, the Triangle toolkit: http://www.cs.cmu.edu/~quake/triangle.html is focused on meshing from points. Triangle is free for non-commercial use. Or a more advanced geometry library such as CGAL (https://doc.cgal.org/latest/Triangulation_3/index.html). CGAL has many different algorithms to build meshes, but it is not free.
    Regards,
    Mike

    Comment


    • #3
      So will PoTriangleMesh2D be able to produce something like this?
      i
      Untitled.png

      Comment


      • #4
        One of the examples that come with open inventor seemed to just about do what we wanted (MeshFilled)

        1..png

        However, looking at the code it implied that one of the inputs is the configuration of the triangles to make up the mesh.

        PoTriangleMesh2D *mesh = new PoTriangleMesh2D;
        mesh->setGeometry(num_nodes,xm,ym,zm, num_triangles, triangle_node);

        When I tried an alternative setGeometry

        It gave a flat surface.

        2.png

        Do I need to do something additional to PoTriangleMesh2D to get this to work with *just* x,y,z points, or do I need to look at alternative data structures/approaches to do this?

        Can MeshViz provide something out of the box that takes x,y,z points and creates a surface out of these points?

        Comment


        • #5
          One of the examples that come with open inventor seemed to just about do what we wanted (MeshFilled).
          However, looking at the code it implied that one of the inputs is the configuration of the triangles to make up the mesh.
          PoTriangleMesh2D *mesh = new PoTriangleMesh2D;
          mesh->setGeometry(num_nodes,xm,ym,zm, num_triangles, triangle_node);
          Yes, that's correct. The MeshFilled example sets the mesh data using a list of coordinates and a list of triangle indices. The topology (triangle indices) was precomputed and stored in a data file.

          Do I need to do something additional to PoTriangleMesh2D to get this to work with *just* x,y,z points, or do I need to look at alternative data structures/approaches to do this?
          Can MeshViz provide something out of the box that takes x,y,z points and creates a surface out of these points?
          In some cases, yes. But remember that PoTriangleMesh2D only implements a 2D Delaunay triangulation. You cannot provide Z values for this computation. And it won't work for an arbitrary cloud of points (e.g. a scan of a solid 3D object). But...

          If your 3D points represent an XY surface equivalent to a "height field", then you can probably make it work. The "trick" is:
          1. Set the mesh data with only X and Y values, triggering the 2D Delaunay algorithm to compute a 2D triangle mesh.
          2. Query the computed triangle mesh, i.e. the triangle indices.
          3. Set the mesh data again, this time using X, Y, and Z values plus the triangle indices (similar to the MeshFilled example).
          The code would look something like this:
          Code:
          int numCoords;
          float* xvals, yvals, zvals;
          // Somehow numCoords and the XYZ arrays are initialized...
          
          // Create mesh data node and set 2D (!) points, triggering the Delaunay algorithm.
          auto meshDataNode = new PoTriangleMesh2D();
          meshDataNode->setGeometry( numCoords, xvals, yvals );
          
          // Get the computed triangle mesh 
          auto mesh = meshDataNode->mesh.getValue();
          int numVals, numTris;
          const float* xVals = nullptr; // Not used
          const float* yVals = nullptr; // Not used
          const float* zVals = nullptr; // Not used
          const int32_t* indices = nullptr;
          mesh.getGeometry( numVals, xVals, yVals, zVals, numTris, indices );
          std::cout << numTris << " triangles created\n";
          
          // Set the mesh data again, but this time with Z coords and triangle indices
          int32_t* triIndices = new int32_t[numTris * 3];
          memcpy(triIndices, indices, sizeof(int32_t) * numTris * 3);
          meshDataNode = new PoTriangleMesh2D();
          meshDataNode->setGeometry( numVals, xvals, yvals, zvals, numTris, triIndices );
          
          m_scene->addChild(meshDataNode);
          Simple example attached.

          Side note: You haven't said what kind of data this is. But the image you posted in the previous message looks like a seismic horizon and typically horizons are generated on a regular grid of points. If that's the case then you can treat the surface as a height field and you don't need a Delaunay algorithm. Options for a regular grid include:
          1. Use the SoHeightField nodes in the VolumeViz extension.
            This is the best option because you don't have to generate triangles, we do that for you (on the GPU). Plus you save memory because you don't need to supply X and Y values. You just supply the XY start point, the X and Y increments and the Z values. Finally, you will get better performance for large horizons because we automatically adjust the number of triangles.
          2. Compute the triangle indices and use SoIndexedTriangleSet.
          3. Use MiSurfaceMeshRegular in the MeshVizXLM extension.
          Regards,
          Mike
          TestDelaunay.zip

          Comment


          • #6
            Hi Mike,

            thanks for all of your help. The requirement we were given was this

            Display a surface in the 3D viewer based on given files from the user. These files will define the x, y, z points that are on the surface. The surface should be coloured.

            But I'm presuming it is seismic data that they want rendered and from the example files we've been sent (in different formats) the data is fairly uniform.

            At the moment our application only is licensed for Open Inventor Core and we are trying to determine whether to extend this to an extension. Like most things resources, time and money are a big factor

            Lisa

            Comment


            • #7
              Originally posted by lisamcgregor View Post
              Display a surface in the 3D viewer based on given files from the user. These files will define the x, y, z points that are on the surface. The surface should be coloured.
              Hi Lisa,
              An easy experiment would be: Read the XYZ points into an array of SbVec3f and render them using an SoPointSet (set the points in the 'vertex' field of SoVertexProperty). That will show you quickly if the points are defined on a grid. It doesn't have to be a regular grid (equal spacing between points), just a grid (same number of points in every row/column).

              If it's a grid, then you can render a surface using an SoIndexedTriangleSet. You just need to generate the triangle indices, which is a simple algorithm.

              Coloring the surface is easy using the MeshViz or MeshVizXLM extension, but you can do it using core Open Inventor. (That's what the extensions do internally.) There are multiple ways to color a surface. Here is one way:
              • First, you need a data set that will be mapped to color.
                Since you (apparently) only have XYZ values, the coloring could be based on the Z value.
              • Define a color map (array of RGB colors)
              • Iterate over the points and compute the data set min/max (min/max Z in this case)
              • Allocate a color array (unsigned int of size 'numPoints')
              • For each point:
                • Compute the normalized data value in 0..1 using the data set min/max
                • Compute the color by lookup in the color map (interpolating between the specified colors)
                • Add the color to the color array as "packed RGBA", e.g. 0xFF0000FF is opaque red.
                  SbColor and SbColorRGBA have helper functions for this.
              • Set the color array in the 'orderedRGBA' field of the SoVertexProperty
              • Set 'materialBinding' field of the SoVertexProperty to PER_VERTEX_INDEXED
              Regards,
              Mike

              Comment

              Working...
              X