2

I've been trying to implement the marching cubes algorithm with C++ and Qt. Anyway, so far all the steps have been written, but I'm getting a really bad result. I'm looking for orientation or advices about what can be going wrong. I suspect one of the problems may be with the voxel conception, specifically about which vertex goes in which corner (0, 1, ..., 7). Also, I'm not a 100% sure about how to interpret the input for the algorithm (I'm using datasets). Should I read it in the ZYX order and move the marching cube in the same way or it doesn't matter at all? (Leaving aside the fact that no every dimension has to have the same size).

Here is what I'm getting against what it should look like...

http://i57.tinypic.com/2nb7g46.jpg

BlastDV
  • 143
  • 5
  • It's pretty curious what I've discovered... If I limit the reconstruction to half the size on X and Y, I get pretty good results, completely different from the other mess: http://i59.tinypic.com/20jmws4.jpg I'll post any updates! – BlastDV Jan 20 '15 at 05:00
  • So here's my update! I changed the normal calculation to vertex ones instead of having normals per each triangle. And for some reason it's doing it almost perfectly! I don't really know whats the difference: http://i58.tinypic.com/23izeo5.jpg – BlastDV Jan 26 '15 at 06:14
  • It looks like your original cube lattice is x y z was placing the cubes not next to each other in the first example but plotting neighbour cubes as being at other ends of the scene. print the cube positions of the first example for some rows and analyse their position in space. – bandybabboon Apr 20 '16 at 07:20

2 Answers2

3

http://en.wikipedia.org/wiki/Marching_cubes

http://en.wikipedia.org/wiki/Marching_cubes#External_links

Paul Bourke. "Overview and source code".

http://paulbourke.net/geometry/polygonise/

Qt_MARCHING_CUBES.zip: Qt/OpenGL example courtesy Dr. Klaus Miltenberger.

http://paulbourke.net/geometry/polygonise/Qt_MARCHING_CUBES.zip

The example requires boost, but looks like it probably should work.

In his example, it has in marchingcubes.cpp, a few different methods for calculating the marching cubes: vMarchCube1 and vMarchCube2.

In the comments it says vMarchCube2 performs the Marching Tetrahedrons algorithm on a single cube by making six calls to vMarchTetrahedron.

Below is the source for the first one vMarchCube1:

//vMarchCube1 performs the Marching Cubes algorithm on a single cube
GLvoid GL_Widget::vMarchCube1(const GLfloat &fX, const GLfloat &fY, const GLfloat &fZ, const GLfloat &fScale, const GLfloat &fTv)
{
        GLint iCorner, iVertex, iVertexTest, iEdge, iTriangle, iFlagIndex, iEdgeFlags;
        GLfloat fOffset;
        GLvector sColor;
        GLfloat afCubeValue[8];
        GLvector asEdgeVertex[12];
        GLvector asEdgeNorm[12];

        //Make a local copy of the values at the cube's corners
        for(iVertex = 0; iVertex < 8; iVertex++)
        {
            afCubeValue[iVertex] = (this->*fSample)(fX + a2fVertexOffset[iVertex][0]*fScale,fY + a2fVertexOffset[iVertex][1]*fScale,fZ + a2fVertexOffset[iVertex][2]*fScale);
        }

        //Find which vertices are inside of the surface and which are outside
        iFlagIndex = 0;
        for(iVertexTest = 0; iVertexTest < 8; iVertexTest++)
        {
                if(afCubeValue[iVertexTest] <= fTv)     iFlagIndex |= 1<<iVertexTest;
        }

        //Find which edges are intersected by the surface
        iEdgeFlags = aiCubeEdgeFlags[iFlagIndex];

        //If the cube is entirely inside or outside of the surface, then there will be no intersections
        if(iEdgeFlags == 0)
        {
                return;
        }

        //Find the point of intersection of the surface with each edge
        //Then find the normal to the surface at those points
        for(iEdge = 0; iEdge < 12; iEdge++)
        {
            //if there is an intersection on this edge
            if(iEdgeFlags & (1<<iEdge))
            {
                fOffset = fGetOffset(afCubeValue[ a2iEdgeConnection[iEdge][0] ],afCubeValue[ a2iEdgeConnection[iEdge][1] ], fTv);

                asEdgeVertex[iEdge].fX = fX + (a2fVertexOffset[ a2iEdgeConnection[iEdge][0] ][0]  +  fOffset * a2fEdgeDirection[iEdge][0]) * fScale;
                asEdgeVertex[iEdge].fY = fY + (a2fVertexOffset[ a2iEdgeConnection[iEdge][0] ][1]  +  fOffset * a2fEdgeDirection[iEdge][1]) * fScale;
                asEdgeVertex[iEdge].fZ = fZ + (a2fVertexOffset[ a2iEdgeConnection[iEdge][0] ][2]  +  fOffset * a2fEdgeDirection[iEdge][2]) * fScale;

                vGetNormal(asEdgeNorm[iEdge], asEdgeVertex[iEdge].fX, asEdgeVertex[iEdge].fY, asEdgeVertex[iEdge].fZ);
            }
}


        //Draw the triangles that were found.  There can be up to five per cube
        for(iTriangle = 0; iTriangle < 5; iTriangle++)
        {
            if(a2iTriangleConnectionTable[iFlagIndex][3*iTriangle] < 0) break;

            for(iCorner = 0; iCorner < 3; iCorner++)
            {
                iVertex = a2iTriangleConnectionTable[iFlagIndex][3*iTriangle+iCorner];

                vGetColor(sColor, asEdgeVertex[iVertex], asEdgeNorm[iVertex]);
                glColor4f(sColor.fX, sColor.fY, sColor.fZ, 0.6);
                glNormal3f(asEdgeNorm[iVertex].fX,   asEdgeNorm[iVertex].fY,   asEdgeNorm[iVertex].fZ);
                glVertex3f(asEdgeVertex[iVertex].fX, asEdgeVertex[iVertex].fY, asEdgeVertex[iVertex].fZ);
            }
        }
}

UPDATE: Github working example, tested

https://github.com/peteristhegreat/qt-marching-cubes

enter image description here

Hope that helps.

phyatt
  • 18,472
  • 5
  • 61
  • 80
  • I've seen those sites before. The paulborke's one helps to get in the topic but it provides wrong data. There are several mistakes on the triangles list which I had to rebuild myself using the applet on http://users.polytech.unice.fr/~lingrand/MarchingCubes/algo.html. – BlastDV Jan 19 '15 at 20:09
  • I wonder why paulborke would post his example if it was so broken? I have some familiarity with OpenGL and QGLWidget and lots of experience with qt, but this is my first time looking at the Marching Cubes algorithm. Addressing your question directly, with my limited understanding of all the aspects of this particular algorithm, but as long as you are consistent, it shouldn't matter. The math should work itself out as long as the coordinate system is consistent. – phyatt Jan 19 '15 at 20:16
  • Also, the Qt example requires Glu too. I downloaded boost and try to use it with my project, but it doesn't build and Qt gives me a "Debug/File Error 2" empty message. It's a pain to have to install 100MB of libraries in order to run just one example. I'll follow your advice and try to check if there are any mistakes on the code. Thanks! – BlastDV Jan 19 '15 at 20:34
  • Good luck. I'll see about porting his example to be glu free sometime, and maybe even boost free, too. The `QVector3D` class can also be used for keeping track of verticies. http://doc.qt.io/qt-5/qvector3d.html. I sometimes `typedef QVertex` off of `QVector3D` and use it to track my x,y, z points. – phyatt Jan 19 '15 at 20:39
  • Hi phyatt! I wasn't able to run what you posted until now! And yeah, it works. Thanks for setting it to run on modern Qt and without Boost! I'm checking it right now and I'll post any results I get! – BlastDV Jan 25 '15 at 22:52
  • I just posted an update! You may want to chek it, this is really weird :/ – BlastDV Jan 26 '15 at 06:15
  • So which lines of code did you change to get it to show that picture? What data set are you using? Are you using the github stuff I posted? – phyatt Jan 26 '15 at 19:51
  • In my code, every time I had 3 vertices ready to pack a triangle, I calculated its normal and packed it 3 times in a vector of normals. Now, when I build my VBO, I use repeated vertices to get to know the ones that are being used by more than one triangle. The only thing I do is making a sumatory out of their normals and then divide them by it's length. It's the same idea from the "Vertex Normal" section on this tutorial: http://www.opengl-tutorial.org/beginners-tutorials/tutorial-8-basic-shading/ – BlastDV Jan 26 '15 at 20:07
  • On the other side, I checked what you uploaded there and It works great! But since it's using functions instead of datasets, somethings are different between the approaches. I replaced my triangles and edges table by theirs. If you want, I can upload my marching cubes code. – BlastDV Jan 26 '15 at 20:08
  • And the dataset I'm using is this one: http://www.cs.princeton.edu/~cdecoro/volview/brain.zip For some reason, its the only one that somehow works with my code. Everything else ends in a even worst result. – BlastDV Jan 26 '15 at 20:16
  • If you have a fork of the github code I posted (or a pull request), that may be easiest to try out. So the purpose of rewriting the triangles is so that it can do better shading? – phyatt Jan 26 '15 at 20:20
  • Yeah! Just shading, it shouldn't mess with the geometry but it does.... I think there might be something wrong with the input loading. I can upload my own code to github if you want, there you'll see why it would be difficult to start again from yours. – BlastDV Jan 26 '15 at 20:28
  • Hi, I finally found the problem. Thanks for your help! I left an answer for anyone having the same particular issue. – BlastDV Feb 02 '15 at 17:12
0

Finally, I found what was wrong.

I use a VBO indexer class to reduce the ammount of duplicated vertices and make the render faster. This class is implemented with a std::map to find and discard already existing vertices, using a tuple of < vec3, unsigned short >. As you may imagine, a marching cubes algorithm generates structures with thousands if not millions of vertices. The highest number a common unsigned short can hold is 65536, or 2^16. So, when the output geometry had more than that, the map index started to overflow and the result was a mess, since it started to overwrite vertices with the new ones. I just changed my implementation to draw with common VBO and not indexed while I fix my class to support millions of vertices.

The result, with some minor vertex normal issues, speaks for itself: http://i61.tinypic.com/fep2t3.jpg

BlastDV
  • 143
  • 5