////////////////////////////////////////////////////////////////////////////// // // Author : Josh Grant // Date : February 12th, 2002 // File : main.cpp.in // Description : A small Open Inventor program to demonstrate how to use the // MarchingCubes engine. // ////////////////////////////////////////////////////////////////////////////// #include #include #include /////////////////////////////////////////////////////////////////// // // Q: Why are there '@' characters around the word Gui, and why doesn't it // say 'Xt'? // A: This file is not your typical C++ file. It has special macros embedded // in it which will be interpreted by the script 'config.status'. All // characters with '@' characters wrapped around them are considered // macros. When the 'config.status' script parses through the file it // replaces all occurrences of these strings with those determined by the // 'configure' script. This allows the code to be portable across // multiple platforms with differing windowing toolkits (SoXt, SoQt, // SoGtk, etc.) // // The compilable main.cpp is generated by running the script // > config.status --file=main.cpp // // This should always be the file you update. // // ***** NEVER CHANGE THE main.cpp FILE!!!! THE config.status // ***** SCRIPT WILL ONLY WRITE OVER IT!!!!! // //////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////// // To be replaced by the config.status script #include #include //////////////////////////////////////////////////////////// #include #include #include #include #include #include #include #include #include "MarchingCubes.h" void setData (SFScalarField &sfield, int xDim, int yDim, int zDim) { // set dimensions of mesh int index, dims[3] = {xDim, yDim, zDim}; // allocate memory for data float *data = new float[dims[0]*dims[1]*dims[2]]; // set the minimum and maximum bounds of the exponential function SbVec3f min(-1.0, -1.0, -1.0); SbVec3f max( 1.0, 1.0, 1.0); SbVec3f size = max - min; // compute the size of each voxel SbVec3f voxel, p; voxel[0] = (dims[0] > 1) ? size[0] / (float)(dims[0] - 1) : 0.0; voxel[1] = (dims[1] > 1) ? size[1] / (float)(dims[1] - 1) : 0.0; voxel[2] = (dims[2] > 1) ? size[2] / (float)(dims[2] - 1) : 0.0; // iterate through each grid point index = 0; for (int k = 0; k < dims[2]; k++) { p[2] = min[2] + voxel[2]*k; for (int j = 0; j < dims[1]; j++) { p[1] = min[1] + voxel[1]*j; for (int i = 0; i < dims[0]; i++) { p[0] = min[0] + voxel[0]*i; data[index] = p[0]*p[0] + p[1]*p[1] + p[2]*p[2]; index++; } } } // set data, but don't copy the data since we already have a valid copy // allocated. This is just an option provided by the SFScalarField class. // Setting it to true will have the same effect if just means the // SFScalarField class will allocate memory for the data and copy all the // values to its local space. sfield.setValue(dims, data, false); } ////////////////////////////////////////////////////////////////////////////// // // Description: // creates a simple scene with a surface created by the MarchingCubes // engine. // ////////////////////////////////////////////////////////////////////////////// SoSeparator *makeScene () { SoSeparator *root = new SoSeparator(); // create MarchingCubes engine and connect fields MarchingCubes *mcubes = new MarchingCubes(); mcubes->ref(); setData(mcubes->data, 20, 20, 20); // set isoValue, in this case this means find the surface of the sphere // where the radius is 1.0 mcubes->isoValue = 1.0; // MarchingCubes gives the triangles with the vertices ordered clockwise SoShapeHints *hints = new SoShapeHints(); hints->vertexOrdering.setValue(SoShapeHints::CLOCKWISE); root->addChild(hints); // Create a VertexProperty because they are more efficient the // SoCoordinate3 nodes. SoVertexProperty *vprop = new SoVertexProperty(); vprop->ref(); vprop->vertex.connectFrom(&mcubes->points); vprop->normal.connectFrom(&mcubes->normals); // connect the indexes from MarchingCubes SoIndexedFaceSet *faceSet = new SoIndexedFaceSet(); faceSet->vertexProperty = vprop; faceSet->coordIndex.connectFrom(&mcubes->indexes); root->addChild(faceSet); // Translate the dragger by two in the y direction SoTransform *trans = new SoTransform(); trans->translation.setValue(0, 2, 0); root->addChild(trans); // Create the dragger and connect an SoCalculator to it. // Why? This allows for a slick way to make the program interactive. // Whenever the dragger is moved the 'isoValue' field of the MarchingCubes // engine will be updated, as well as the isosurface it outputs. SoTranslate1Dragger *dragger = new SoTranslate1Dragger(); SoCalculator *calc = new SoCalculator(); // translate the dragger to <1, 0, 0>. dragger->translation.setValue(1, 0, 0); // connect the dragger translation field to an input to the calculator calc->A.connectFrom(&dragger->translation); // Now set the expression for the calculator to evaluate. This means // everytime the input vector field 'A' changes assign the x component of // the vector to the output variable 'a' of the calculator. In terms of // this program, that means...everytime the dragger moves the current x // position of the dragger will be the new isoValue for the MarchingCubes // engine to compute. calc->expression.setValue("oa = A[0]"); // connect the output of the engine to the isoValue field of MarchingCubes mcubes->isoValue.connectFrom(&calc->oa); root->addChild(dragger); return root; } int main (int argc, char **argv) { // To be replaced by the config.status script // Initialize Inventor and @Gui@ @WIDGET@ myWindow = So@Gui@::init(argv[0]); // Initialize new classes SFScalarField::initClass(); MarchingCubes::initClass(); // To be replaced by the config.status script // create viewer and editors So@Gui@ExaminerViewer *myViewer = new So@Gui@ExaminerViewer(myWindow); myViewer->setTitle("Marching Cubes Example"); // attach the nodes to the viewer and editors myViewer->setSceneGraph(makeScene()); // show the viewer myViewer->show(); // To be replaced by the config.status script So@Gui@::show(myWindow); So@Gui@::mainLoop(); }