bool FilterCreateIso::applyFilter(QAction *filter, MeshDocument &md, RichParameterSet & par, vcg::CallBackPos * cb) { md.addNewMesh("",this->filterName(ID(filter))); MeshModel &m=*(md.mm()); if(filter->text() == filterName(FP_CREATEISO) ) { SimpleVolume<SimpleVoxel<Scalarm> > volume; typedef vcg::tri::TrivialWalker<CMeshO, SimpleVolume<SimpleVoxel<Scalarm> > > MyWalker; typedef vcg::tri::MarchingCubes<CMeshO, MyWalker> MyMarchingCubes; MyWalker walker; const int gridSize=par.getInt("Resolution"); // Simple initialization of the volume with some cool perlin noise volume.Init(Point3i(gridSize,gridSize,gridSize), Box3m(Point3m(0,0,0),Point3m(1,1,1))); for(int i=0;i<gridSize;i++) for(int j=0;j<gridSize;j++) for(int k=0;k<gridSize;k++) volume.Val(i,j,k)=(j-gridSize/2)*(j-gridSize/2)+(k-gridSize/2)*(k-gridSize/2) + i*gridSize/5*(float)math::Perlin::Noise(i*.2,j*.2,k*.2); printf("[MARCHING CUBES] Building mesh..."); MyMarchingCubes mc(m.cm, walker); walker.BuildMesh<MyMarchingCubes>(m.cm, volume, mc, (gridSize*gridSize)/10,cb); m.UpdateBoxAndNormals(); } return true; }
SimpleModel* loadModelSTL_ascii(const char* filename, FMatrix3x3& matrix) { SimpleModel* m = new SimpleModel(); m->volumes.push_back(SimpleVolume()); SimpleVolume* vol = &m->volumes[0]; FILE* f = fopen(filename, "rt"); char buffer[1024]; FPoint3 vertex; int n = 0; Point3 v0(0,0,0), v1(0,0,0), v2(0,0,0); while(fgets_(buffer, sizeof(buffer), f)) { if (sscanf(buffer, " vertex %lf %lf %lf", &vertex.x, &vertex.y, &vertex.z) == 3) { n++; switch(n) { case 1: v0 = matrix.apply(vertex); break; case 2: v1 = matrix.apply(vertex); break; case 3: v2 = matrix.apply(vertex); vol->addFace(v0, v1, v2); n = 0; break; } } } fclose(f); return m; }
SimpleModel* loadModelSTL_binary(const char* filename, FMatrix3x3& matrix) { FILE* f = fopen(filename, "rb"); char buffer[80]; uint32_t faceCount; //Skip the header if (fread(buffer, 80, 1, f) != 1) { fclose(f); return NULL; } //Read the face count if (fread(&faceCount, sizeof(uint32_t), 1, f) != 1) { fclose(f); return NULL; } //For each face read: //float(x,y,z) = normal, float(X,Y,Z)*3 = vertexes, uint16_t = flags SimpleModel* m = new SimpleModel(); m->volumes.push_back(SimpleVolume()); SimpleVolume* vol = &m->volumes[0]; if(vol == NULL) { fclose(f); return NULL; } for(unsigned int i=0;i<faceCount;i++) { if (fread(buffer, sizeof(float) * 3, 1, f) != 1) { fclose(f); return NULL; } float v[9]; if (fread(v, sizeof(float) * 9, 1, f) != 1) { fclose(f); return NULL; } Point3 v0 = matrix.apply(FPoint3(v[0], v[1], v[2])); Point3 v1 = matrix.apply(FPoint3(v[3], v[4], v[5])); Point3 v2 = matrix.apply(FPoint3(v[6], v[7], v[8])); vol->addFace(v0, v1, v2); if (fread(buffer, sizeof(uint16_t), 1, f) != 1) { fclose(f); return NULL; } } fclose(f); return m; }
void CreateNoisyIsosurface(uintptr_t _m, int gridSize) { MyMesh &m = *((MyMesh*) _m); SimpleVolume<SimpleVoxel<float> > volume; typedef vcg::tri::TrivialWalker<MyMesh, SimpleVolume<SimpleVoxel<float> > > MyWalker; typedef vcg::tri::MarchingCubes<MyMesh, MyWalker> MyMarchingCubes; MyWalker walker; // Simple initialization of the volume with some cool perlin noise volume.Init(Point3i(gridSize,gridSize,gridSize), Box3f(Point3f(0,0,0),Point3f(1,1,1))); for(int i=0;i<gridSize;i++) for(int j=0;j<gridSize;j++) for(int k=0;k<gridSize;k++) volume.Val(i,j,k)=(j-gridSize/2)*(j-gridSize/2)+(k-gridSize/2)*(k-gridSize/2) + i*gridSize/5*(float)math::Perlin::Noise(i*.2,j*.2,k*.2); printf("[MARCHING CUBES] Building mesh...\n"); MyMarchingCubes mc(m, walker); walker.BuildMesh<MyMarchingCubes>(m, volume, mc, (gridSize*gridSize)/10); }
SimpleModel* loadModel(const char* filename, FMatrix3x3& matrix) { const char* ext = strrchr(filename, '.'); if (ext && strcasecmp(ext, ".stl") == 0) { return loadModelSTL(filename, matrix); } if (filename[0] == '#' && binaryMeshBlob != NULL) { SimpleModel* m = new SimpleModel(); while(*filename == '#') { filename++; m->volumes.push_back(SimpleVolume()); SimpleVolume* vol = &m->volumes[m->volumes.size()-1]; int32_t n, pNr = 0; if (fread(&n, 1, sizeof(int32_t), binaryMeshBlob) < 1) return NULL; log("Reading mesh from binary blob with %i vertexes\n", n); Point3 v[3]; while(n) { float f[3]; if (fread(f, 3, sizeof(float), binaryMeshBlob) < 1) return NULL; FPoint3 fp(f[0], f[1], f[2]); v[pNr++] = matrix.apply(fp); if (pNr == 3) { vol->addFace(v[0], v[1], v[2]); pNr = 0; } n--; } } return m; } return NULL; }
void createSphereInVolume(SimpleVolume<Density8>& volData, float fRadius) { //This vector hold the position of the center of the volume Vector3DFloat v3dVolCenter(volData.getWidth() / 2, volData.getHeight() / 2, volData.getDepth() / 2); //This three-level for loop iterates over every voxel in the volume for (int z = 0; z < volData.getWidth(); z++) { for (int y = 0; y < volData.getHeight(); y++) { for (int x = 0; x < volData.getDepth(); x++) { //Store our current position as a vector... Vector3DFloat v3dCurrentPos(x,y,z); //And compute how far the current position is from the center of the volume float fDistToCenter = (v3dCurrentPos - v3dVolCenter).length(); if(fDistToCenter <= fRadius) { //Our new density value uint8_t uDensity = Density8::getMaxDensity(); //Get the old voxel Density8 voxel = volData.getVoxelAt(x,y,z); //Modify the density voxel.setDensity(uDensity); //Wrte the voxel value into the volume volData.setVoxelAt(x, y, z, voxel); } //144 in the middle, (144 - 32) at the edges. Threshold of 128 is between these //volData.setVoxelAt(x, y, z, 144 - fDistToCenter); } } } }
void render_gdpt(const char *filename, const int width, const int height, const int num_sample_per_subpixel, const int num_thread, unsigned long long seed, const SimpleVolume& ovolume, const rt::PerspectiveCamera& camera) { std::vector<Color> coarse_image(width * height); std::vector<Color> debug_image(width * height); std::vector<Color> diff_image[4]; for (int i = 0; i < 4; ++i) diff_image[i].resize(width * height); Timer timer; timer.begin(); for (int iy = 0; iy < height; ++iy) { std::cout << "y: " << iy << std::endl; #pragma omp parallel for schedule(dynamic, 1) num_threads(8) for (int ix = 0; ix < width; ++ix) { Random random((unsigned long long)(iy * width + ix)); const int image_index = iy * width + ix; for (int ss = 0; ss < num_sample_per_subpixel; ++ss) { float sx = random.next01(); float sy = random.next01(); std::vector<Vertex> baseVertices; baseVertices.reserve(16); float basePathPDF = 1; Color baseContritbuion; Color finalL; float baseX = ix + sx; float baseY = iy + sy; const unsigned long long random2_seed = (unsigned long long)(iy * width + ix) * num_sample_per_subpixel + ss; rt::Ray base_ray; { const float u = baseX / width; const float v = baseY / height; base_ray = camera.get_ray(u, v); baseContritbuion = radiance_gdpt(random, random, ovolume, base_ray, &baseVertices, &basePathPDF, &finalL); } if (basePathPDF > 0) coarse_image[image_index] += baseContritbuion / basePathPDF; if (basePathPDF <= 0) continue; // 4 sub-path const Float3 offset[4] = { Float3( 1, 0, 0), Float3(-1, 0, 0), Float3( 0, 1, 0), Float3( 0,-1, 0) }; std::vector<Vertex> offsetVertices[4]; for (int offsetDir = 0; offsetDir < 4; ++offsetDir) { const float u = (baseX + offset[offsetDir].x) / width; const float v = (baseY + offset[offsetDir].y) / height; const rt::Ray offset_ray = camera.get_ray(u, v); offsetVertices[offsetDir].reserve(16); enum Result { Naive, Invertible }; // Shift rt::Ray current_ray = offset_ray; Result result = Naive; // baseが少なくとも2回は散乱している、有効なパスのときのみ考える Color offsetContribution; float J = 0; if (basePathPDF > 0 && baseVertices.size() >= 3) { const float initial_length = (baseVertices[1].position - base_ray.org).length(); const Float3 offset_dir0 = current_ray.dir; const Float3 offset_1 = current_ray.org + initial_length * offset_dir0; if (!ovolume.inside(offset_1)) { // offsetが外に出てしまったので、Naiveにやる } else { result = Invertible; float hitt0 = -1, hitt1 = -1; ovolume.check_intersect(current_ray, &hitt0, &hitt1); const Float3 new_org0 = current_ray.org + (hitt0 + 1e-3f) * offset_dir0; const Float3 new_org1 = current_ray.org + (hitt1 + 1e-3f) * offset_dir0; if (!ovolume.inside(current_ray.org)) { // Volumeの外だったら中からスタート current_ray.org = new_org0; } const Float3 offset_0 = current_ray.org; // Baseの3個目の頂点につなげる const Float3 offset_2 = baseVertices[2].position; // 0 - 1と 1- 2の間のtransmittanceを求める const rt::Ray ray01(offset_0, normalize(offset_1 - offset_0)); const rt::Ray ray12(offset_1, normalize(offset_2 - offset_1)); const float length01 = (offset_0 - offset_1).length(); const float length12 = (offset_1 - offset_2).length(); float transmittance01 = 0; float transmittance12 = 0; const int kNumSample = 16; /* { float sum = 0; for (int i = 0; i < kNumSample; ++i) { const float current = length01 / kNumSample * (i + random.next01()); sum += ovolume.sample_transmittance(ray01.org + current * ray01.dir) / kNumSample; } transmittance01 = exp(-sum); } { float sum = 0; for (int i = 0; i < kNumSample; ++i) { const float current = length12 / kNumSample * (i + random.next01()); sum += ovolume.sample_transmittance(ray12.org + current * ray12.dir) / kNumSample; } transmittance12 = exp(-sum); } */ for (int i = 0; i < kNumSample; ++i) { const float X = ovolume.next_event(ray01, random); if (!(0 <= X && X < length01)) { transmittance01 += 1.0f / kNumSample; } } /* if (baseVertices[1].transmittance != transmittance01) printf("%f %f\n", baseVertices[1].transmittance, transmittance01); */ for (int i = 0; i < kNumSample; ++i) { const float X = ovolume.next_event(ray12, random); if (!(0 <= X && X < length12)) { transmittance12 += 1.0f / kNumSample; } } // ヤコビアン計算 J = (baseVertices[2].position - baseVertices[1].position).lengthSquared() / (offset_2 - offset_1).lengthSquared(); // 残りのTransmittanceはBaseと同じ! float total_transmittance = transmittance01 * transmittance12; for (int i = 3; i < baseVertices.size(); ++i) total_transmittance *= baseVertices[i].transmittance; // その他の要素 const float phase = 1.0f / (4.0f * kPI); const int numScattering = baseVertices.size() - 1; const float throughput = total_transmittance * pow(phase * kAlbedo, numScattering); offsetContribution = throughput * finalL; } } Color accum; if (result == Naive) { /* if (iy > 100) printf("%f %d\n", basePathPDF, baseVertices.size()); */ float pdf = 1; const Float3 contribution = radiance(random, ovolume, offset_ray, &pdf); if (pdf > 0) { accum = baseContritbuion / basePathPDF - contribution / pdf; } else { accum = baseContritbuion / basePathPDF; } /* if (ix == 320 && iy == 240) { printf("%f %f %f %f\n", baseContritbuion.x, basePathPDF, contribution.x, pdf); std::cout << accum << std::endl; } */ } else { // printf("%f %f %f\n", baseContritbuion.x, offsetContribution.x, J); accum = 0.5f * (baseContritbuion - offsetContribution * J) / basePathPDF; } // 前方差分 or 後方差分 if (offsetDir == 1 || offsetDir == 2) diff_image[offsetDir][image_index] += accum; else diff_image[offsetDir][image_index] += -accum; } } coarse_image[image_index] = coarse_image[image_index] / num_sample_per_subpixel; for (int i = 0; i < 4; ++i) diff_image[i][image_index] = diff_image[i][image_index] / num_sample_per_subpixel; } } std::cout << "Time: " << (timer.end() / 1000.0f) << " sec" << std::endl; char buf[256]; char name[4][256] = { "__1_0", "_-1_0", "__0_1", "__0-1" }; sprintf(buf, "%s_J.hdr", filename); save_hdr_file(buf, &debug_image[0], width, height); sprintf(buf, "%s_coarse.hdr", filename); save_hdr_file(buf, &coarse_image[0], width, height); sprintf(buf, "%s_coarse.bin", filename); save_binary_file(buf, &coarse_image[0], width, height); for (int i = 0; i < 4; ++i) { sprintf(buf, "%s_%s.hdr", filename, name[i]); save_hdr_file(buf, &diff_image[i][0], width, height); sprintf(buf, "%s_%s.bin", filename, name[i]); save_binary_file(buf, &diff_image[i][0], width, height); } }
Float3 radiance_gdpt(Random& random, Random& random2, const SimpleVolume& volume, const rt::Ray& initial_ray, std::vector<Vertex>* vertices, float *path_pdf, Color *finalL) { const int kMaxScattering = 256; rt::Ray current_ray = initial_ray; Color througput(1, 1, 1); Color L(0, 0, 0); for (int scattering = 0; scattering < kMaxScattering; ++scattering) { // VolumeのBBoxにヒットするか否か float hitt0 = -1, hitt1 = -1; bool is_volume_hit = false; if (volume.check_intersect(current_ray, &hitt0, &hitt1)) { is_volume_hit = true; } rt::Hitpoint current_hp; if (!volume.inside(current_ray.org)) { if (is_volume_hit) { current_hp.distance = hitt0; } } if (!is_volume_hit) { L += outside(current_ray); break; } const Float3 new_org0 = current_ray.org + (hitt0 + 1e-3f) * current_ray.dir; const Float3 new_org1 = current_ray.org + (hitt1 + 1e-3f) * current_ray.dir; if (!volume.inside(current_ray.org)) { // Volumeの外だったら中からスタート current_ray.org = new_org0; } if (scattering == 0) { // 一個目の頂点を入れる vertices->push_back(Vertex(current_ray.org, current_ray.dir, 1)); } const float tu = volume.next_event(current_ray, random); if (tu < 0) { // ボリューム外 current_ray.org = new_org1; continue; } // 散乱した! // pdf_transmittance float transmittance = 0; const int kNumSample = 16; /* { float sum = 0; for (int i = 0; i < kNumSample; ++i) { const float length = tu; const float current = length / kNumSample * (i + random.next01()); sum += volume.sample_transmittance(current_ray.org + current * current_ray.dir) / kNumSample; } transmittance = exp(-sum); }*/ for (int i = 0; i < kNumSample; ++i) { const float X = volume.next_event(current_ray, random2); if (!(0 <= X && X < tu)) { transmittance += 1.0f / kNumSample; } } const Float3 next_pos = current_ray.org + tu * current_ray.dir; const bool cond2 = random.next01() < kAlbedo; if (!cond2) { *path_pdf = 0; througput = Float3(0, 0, 0); // absorp break; } // 位相関数でインポータンスサンプリング // 今回はとりあえず等方散乱としておく const Float3 next_dir = Sampling::uniformSphereSurface(random); const float phase = 1.0f / (4.0f * kPI); const float pdf_phase = 1.0f / (4.0f * kPI); /* througput *= 0.99f; *path_pdf *= 0.99f; */ througput *= transmittance * phase * kAlbedo; *path_pdf *= transmittance * pdf_phase * kAlbedo; // 頂点追加 vertices->push_back(Vertex(next_pos, next_dir, transmittance)); // throughtput = (phase / pdf) * throughtput; current_ray = rt::Ray(next_pos, next_dir); } *finalL = L; return times(L, througput); }
Float3 radiance(Random& random, const SimpleVolume& volume, const rt::Ray& initial_ray, float *path_pdf) { const int kMaxScattering = 256; rt::Ray current_ray = initial_ray; Color througput(1, 1, 1); Color L(0, 0, 0); for (int scattering = 0; scattering < kMaxScattering; ++scattering) { // VolumeのBBoxにヒットするか否か float hitt0 = -1, hitt1 = -1; bool is_volume_hit = false; if (volume.check_intersect(current_ray, &hitt0, &hitt1)) { is_volume_hit = true; } rt::Hitpoint current_hp; if (!volume.inside(current_ray.org)) { if (is_volume_hit) { current_hp.distance = hitt0; } } if (!is_volume_hit) { L += outside(current_ray); break; } const Float3 new_org0 = current_ray.org + (hitt0 + 1e-3f) * current_ray.dir; const Float3 new_org1 = current_ray.org + (hitt1 + 1e-3f) * current_ray.dir; if (!volume.inside(current_ray.org)) { // Volumeの外だったら中からスタート current_ray.org = new_org0; } const float tu = volume.next_event(current_ray, random); if (tu < 0) { // ボリューム外 current_ray.org = new_org1; continue; } // pdf_transmittance float transmittance = 0; const int kNumSample = 16; for (int i = 0; i < kNumSample; ++i) { const float X = volume.next_event(current_ray, random); if (!(0 <= X && X < tu)) { transmittance += 1.0f / kNumSample; } } const Float3 next_pos = current_ray.org + tu * current_ray.dir; const bool cond2 = random.next01() < kAlbedo; if (!cond2) { througput = Float3(0, 0, 0); // absorp break; } // 位相関数でインポータンスサンプリング // 今回はとりあえず等方散乱としておく const Float3 next_dir = Sampling::uniformSphereSurface(random); const float phase = 1.0f / (4.0f * kPI); const float pdf_phase = 1.0f / (4.0f * kPI); /* througput *= 0.99f; *path_pdf *= 0.99f; */ througput *= transmittance * phase * kAlbedo; *path_pdf *= transmittance * pdf_phase * kAlbedo; // throughtput = (phase / pdf) * throughtput; current_ray = rt::Ray(next_pos, next_dir); } return times(L, througput); }
// The Real Core Function doing the actual mesh processing. bool FilterFunctionPlugin::applyFilter(QAction *filter, MeshDocument &md, RichParameterSet & par, vcg::CallBackPos *cb) { if(this->getClass(filter) == MeshFilterInterface::MeshCreation) md.addNewMesh("",this->filterName(ID(filter))); MeshModel &m=*(md.mm()); Q_UNUSED(cb); switch(ID(filter)) { case FF_VERT_SELECTION : { std::string expr = par.getString("condSelect").toStdString(); // muparser initialization and explicitely define parser variables Parser p; setPerVertexVariables(p,m.cm); // set expression inserted by user as string (required by muparser) p.SetExpr(expr); int numvert = 0; time_t start = clock(); // every parser variables is related to vertex coord and attributes. CMeshO::VertexIterator vi; for(vi = m.cm.vert.begin(); vi != m.cm.vert.end(); ++vi)if(!(*vi).IsD()) { setAttributes(vi,m.cm); bool selected = false; // use parser to evaluate boolean function specified above // in case of fail, error dialog contains details of parser's error try { selected = p.Eval(); } catch(Parser::exception_type &e) { errorMessage = e.GetMsg().c_str(); return false; } // set vertex as selected or clear selection if(selected) { (*vi).SetS(); numvert++; } else (*vi).ClearS(); } // strict face selection if(par.getBool("strictSelect")) tri::UpdateSelection<CMeshO>::FaceFromVertexStrict(m.cm); else tri::UpdateSelection<CMeshO>::FaceFromVertexLoose(m.cm); // if succeded log stream contains number of vertices and time elapsed Log( "selected %d vertices in %.2f sec.", numvert, (clock() - start) / (float) CLOCKS_PER_SEC); return true; } break; case FF_FACE_SELECTION : { QString select = par.getString("condSelect"); // muparser initialization and explicitely define parser variables Parser p; setPerFaceVariables(p,m.cm); // set expression inserted by user as string (required by muparser) p.SetExpr(select.toStdString()); int numface = 0; time_t start = clock(); // every parser variables is related to face attributes. CMeshO::FaceIterator fi; for(fi = m.cm.face.begin(); fi != m.cm.face.end(); ++fi)if(!(*fi).IsD()) { setAttributes(fi,m.cm); bool selected = false; // use parser to evaluate boolean function specified above // in case of fail, error dialog contains details of parser's error try { selected = p.Eval(); } catch(Parser::exception_type &e) { errorMessage = e.GetMsg().c_str(); return false; } // set face as selected or clear selection if(selected) { (*fi).SetS(); numface++; } else (*fi).ClearS(); } // if succeded log stream contains number of vertices and time elapsed Log( "selected %d faces in %.2f sec.", numface, (clock() - start) / (float) CLOCKS_PER_SEC); return true; } break; case FF_GEOM_FUNC : case FF_VERT_COLOR: case FF_VERT_NORMAL: { std::string func_x,func_y,func_z,func_a; // FF_VERT_COLOR : x = r, y = g, z = b // FF_VERT_NORMAL : x = r, y = g, z = b func_x = par.getString("x").toStdString(); func_y = par.getString("y").toStdString(); func_z = par.getString("z").toStdString(); if(ID(filter) == FF_VERT_COLOR) func_a = par.getString("a").toStdString(); // muparser initialization and explicitely define parser variables // function for x,y and z must use different parser and variables Parser p1,p2,p3,p4; setPerVertexVariables(p1,m.cm); setPerVertexVariables(p2,m.cm); setPerVertexVariables(p3,m.cm); setPerVertexVariables(p4,m.cm); p1.SetExpr(func_x); p2.SetExpr(func_y); p3.SetExpr(func_z); p4.SetExpr(func_a); double newx=0,newy=0,newz=0,newa=255; errorMessage = ""; time_t start = clock(); // every parser variables is related to vertex coord and attributes. CMeshO::VertexIterator vi; for(vi = m.cm.vert.begin(); vi != m.cm.vert.end(); ++vi)if(!(*vi).IsD()) { setAttributes(vi,m.cm); // every function is evaluated by different parser. // errorMessage dialog contains errors for func x, func y and func z try { newx = p1.Eval(); } catch(Parser::exception_type &e) { showParserError("1st func : ",e); } try { newy = p2.Eval(); } catch(Parser::exception_type &e) { showParserError("2nd func : ",e); } try { newz = p3.Eval(); } catch(Parser::exception_type &e) { showParserError("3rd func : ",e); } if(ID(filter) == FF_VERT_COLOR) { try { newa = p4.Eval(); } catch(Parser::exception_type &e) { showParserError("4th func : ",e); } } if(errorMessage != "") return false; if(ID(filter) == FF_GEOM_FUNC) // set new vertex coord for this iteration (*vi).P() = Point3f(newx,newy,newz); if(ID(filter) == FF_VERT_COLOR) // set new color for this iteration (*vi).C() = Color4b(newx,newy,newz,newa); if(ID(filter) == FF_VERT_NORMAL) // set new color for this iteration (*vi).N() = Point3f(newx,newy,newz); } if(ID(filter) == FF_GEOM_FUNC) { // update bounding box, normalize normals tri::UpdateNormals<CMeshO>::PerVertexNormalizedPerFace(m.cm); tri::UpdateNormals<CMeshO>::NormalizeFace(m.cm); tri::UpdateBounding<CMeshO>::Box(m.cm); } // if succeded log stream contains number of vertices processed and time elapsed Log( "%d vertices processed in %.2f sec.", m.cm.vn, (clock() - start) / (float) CLOCKS_PER_SEC); return true; } break; case FF_VERT_QUALITY: { std::string func_q = par.getString("q").toStdString(); m.updateDataMask(MeshModel::MM_VERTQUALITY); // muparser initialization and define custom variables Parser p; setPerVertexVariables(p,m.cm); // set expression to calc with parser p.SetExpr(func_q); // every parser variables is related to vertex coord and attributes. time_t start = clock(); CMeshO::VertexIterator vi; for(vi = m.cm.vert.begin(); vi != m.cm.vert.end(); ++vi) if(!(*vi).IsD()) { setAttributes(vi,m.cm); // use parser to evaluate function specified above // in case of fail, errorMessage dialog contains details of parser's error try { (*vi).Q() = p.Eval(); } catch(Parser::exception_type &e) { errorMessage = e.GetMsg().c_str(); return false; } } // normalize quality with values in [0..1] if(par.getBool("normalize")) tri::UpdateQuality<CMeshO>::VertexNormalize(m.cm); // map quality into per-vertex color if(par.getBool("map")) tri::UpdateColor<CMeshO>::VertexQualityRamp(m.cm); // if succeded log stream contains number of vertices and time elapsed Log( "%d vertices processed in %.2f sec.", m.cm.vn, (clock() - start) / (float) CLOCKS_PER_SEC); return true; } break; case FF_FACE_COLOR: { std::string func_r = par.getString("r").toStdString(); std::string func_g = par.getString("g").toStdString(); std::string func_b = par.getString("b").toStdString(); std::string func_a = par.getString("a").toStdString(); // muparser initialization and explicitely define parser variables // every function must uses own parser and variables Parser p1,p2,p3,p4; setPerFaceVariables(p1,m.cm); setPerFaceVariables(p2,m.cm); setPerFaceVariables(p3,m.cm); setPerFaceVariables(p4,m.cm); p1.SetExpr(func_r); p2.SetExpr(func_g); p3.SetExpr(func_b); p4.SetExpr(func_a); // RGB is related to every face CMeshO::FaceIterator fi; double newr=0,newg=0,newb=0,newa=255; errorMessage = ""; time_t start = clock(); // every parser variables is related to face attributes. for(fi = m.cm.face.begin(); fi != m.cm.face.end(); ++fi)if(!(*fi).IsD()) { setAttributes(fi,m.cm); // evaluate functions to generate new color // in case of fail, error dialog contains details of parser's error try { newr = p1.Eval(); } catch(Parser::exception_type &e) { showParserError("func r: ",e); } try { newg = p2.Eval(); } catch(Parser::exception_type &e) { showParserError("func g: ",e); } try { newb = p3.Eval(); } catch(Parser::exception_type &e) { showParserError("func b: ",e); } try { newa = p4.Eval(); } catch(Parser::exception_type &e) { showParserError("func a: ",e); } if(errorMessage != "") return false; // set new color for this iteration (*fi).C() = Color4b(newr,newg,newb,newa); } // if succeded log stream contains number of vertices processed and time elapsed Log( "%d faces processed in %.2f sec.", m.cm.fn, (clock() - start) / (float) CLOCKS_PER_SEC); return true; } break; case FF_FACE_QUALITY: { std::string func_q = par.getString("q").toStdString(); m.updateDataMask(MeshModel::MM_FACEQUALITY); // muparser initialization and define custom variables Parser pf; setPerFaceVariables(pf,m.cm); // set expression to calc with parser pf.SetExpr(func_q); time_t start = clock(); errorMessage = ""; // every parser variables is related to face attributes. CMeshO::FaceIterator fi; for(fi = m.cm.face.begin(); fi != m.cm.face.end(); ++fi)if(!(*fi).IsD()) { setAttributes(fi,m.cm); // evaluate functions to generate new quality // in case of fail, error dialog contains details of parser's error try { (*fi).Q() = pf.Eval(); } catch(Parser::exception_type &e) { showParserError("func q: ",e); } if(errorMessage != "") return false; } // normalize quality with values in [0..1] if(par.getBool("normalize")) tri::UpdateQuality<CMeshO>::FaceNormalize(m.cm); // map quality into per-vertex color if(par.getBool("map")) tri::UpdateColor<CMeshO>::FaceQualityRamp(m.cm); // if succeded log stream contains number of faces processed and time elapsed Log( "%d faces processed in %.2f sec.", m.cm.fn, (clock() - start) / (float) CLOCKS_PER_SEC); return true; } break; case FF_DEF_VERT_ATTRIB : { std::string name = par.getString("name").toStdString(); std::string expr = par.getString("expr").toStdString(); // add per-vertex attribute with type float and name specified by user CMeshO::PerVertexAttributeHandle<float> h; if(tri::HasPerVertexAttribute(m.cm,name)) { h = tri::Allocator<CMeshO>::GetPerVertexAttribute<float>(m.cm, name); if(!tri::Allocator<CMeshO>::IsValidHandle<float>(m.cm,h)) { errorMessage = "attribute already exists with a different type"; return false; } } else h = tri::Allocator<CMeshO>::AddPerVertexAttribute<float> (m.cm,name); std::vector<std::string> AllVertexAttribName; tri::Allocator<CMeshO>::GetAllPerVertexAttribute< float >(m.cm,AllVertexAttribName); qDebug("Now mesh has %i vertex float attribute",AllVertexAttribName.size()); Parser p; setPerVertexVariables(p,m.cm); p.SetExpr(expr); time_t start = clock(); // perform calculation of attribute's value with function specified by user CMeshO::VertexIterator vi; for(vi = m.cm.vert.begin(); vi != m.cm.vert.end(); ++vi)if(!(*vi).IsD()) { setAttributes(vi,m.cm); // add new user-defined attribute try { h[vi] = p.Eval(); } catch(Parser::exception_type &e) { errorMessage = e.GetMsg().c_str(); return false; } } // add string, double and handler to vector. // vectors keep tracks of new attributes and let muparser use explicit variables // it's possibile to use custom attributes in other filters v_attrNames.push_back(name); v_attrValue.push_back(0); v_handlers.push_back(h); // if succeded log stream contains number of vertices processed and time elapsed Log( "%d vertices processed in %.2f sec.", m.cm.vn, (clock() - start) / (float) CLOCKS_PER_SEC); return true; } break; case FF_DEF_FACE_ATTRIB : { std::string name = par.getString("name").toStdString(); std::string expr = par.getString("expr").toStdString(); // add per-face attribute with type float and name specified by user // add per-vertex attribute with type float and name specified by user CMeshO::PerFaceAttributeHandle<float> h; if(tri::HasPerFaceAttribute(m.cm,name)) { h = tri::Allocator<CMeshO>::GetPerFaceAttribute<float>(m.cm, name); if(!tri::Allocator<CMeshO>::IsValidHandle<float>(m.cm,h)) { errorMessage = "attribute already exists with a different type"; return false; } } else h = tri::Allocator<CMeshO>::AddPerFaceAttribute<float> (m.cm,name); Parser p; setPerFaceVariables(p,m.cm); p.SetExpr(expr); time_t start = clock(); // every parser variables is related to face attributes. CMeshO::FaceIterator fi; for(fi = m.cm.face.begin(); fi != m.cm.face.end(); ++fi)if(!(*fi).IsD()) { setAttributes(fi,m.cm); // add new user-defined attribute try { h[fi] = p.Eval(); } catch(Parser::exception_type &e) { errorMessage = e.GetMsg().c_str(); return false; } } // // add string, double and handler to vector. // // vectors keep tracks of new attributes and let muparser use explicit variables // // it's possibile to use custom attributes in other filters // f_attrNames.push_back(name); // f_attrValue.push_back(0); // fhandlers.push_back(h); // if succeded log stream contains number of vertices processed and time elapsed Log( "%d faces processed in %.2f sec.", m.cm.fn, (clock() - start) / (float) CLOCKS_PER_SEC); return true; } break; case FF_GRID : { // obtain parameters to generate 2D Grid int w = par.getInt("numVertX"); int h = par.getInt("numVertY"); float wl = par.getFloat("absScaleX"); float hl = par.getFloat("absScaleY"); if(w <= 0 || h <= 0) { errorMessage = "number of vertices must be positive"; return false; } // use Grid function to generate Grid std::vector<float> data(w*h,0); tri::Grid<CMeshO>(m.cm, w, h, wl, hl, &data[0]); // if "centered on origin" is checked than move generated Grid in (0,0,0) if(par.getBool("center")) { // move x and y double halfw = double(w-1)/2; double halfh = double(h-1)/2; double wld = wl/double(w); double hld = hl/float(h); CMeshO::VertexIterator vi; for(vi = m.cm.vert.begin(); vi != m.cm.vert.end(); ++vi) { (*vi).P()[0] = (*vi).P()[0] - (wld * halfw); (*vi).P()[1] = (*vi).P()[1] - (hld * halfh); } } // update bounding box, normals Matrix44f rot; rot.SetRotateDeg(180,Point3f(0,1,0)); tri::UpdatePosition<CMeshO>::Matrix(m.cm,rot,false); tri::UpdateNormals<CMeshO>::PerVertexNormalizedPerFace(m.cm); tri::UpdateNormals<CMeshO>::NormalizeFace(m.cm); tri::UpdateBounding<CMeshO>::Box(m.cm); return true; } break; case FF_ISOSURFACE : { SimpleVolume<SimpleVoxel> volume; typedef vcg::tri::TrivialWalker<CMeshO, SimpleVolume<SimpleVoxel> > MyWalker; typedef vcg::tri::MarchingCubes<CMeshO, MyWalker> MyMarchingCubes; MyWalker walker; Box3d rbb; rbb.min[0]=par.getFloat("minX"); rbb.min[1]=par.getFloat("minY"); rbb.min[2]=par.getFloat("minZ"); rbb.max[0]=par.getFloat("maxX"); rbb.max[1]=par.getFloat("maxY"); rbb.max[2]=par.getFloat("maxZ"); double step=par.getFloat("voxelSize"); Point3i siz= Point3i::Construct((rbb.max-rbb.min)*(1.0/step)); Parser p; double x,y,z; p.DefineVar("x", &x); p.DefineVar("y", &y); p.DefineVar("z", &z); std::string expr = par.getString("expr").toStdString(); p.SetExpr(expr); Log("Filling a Volume of %i %i %i",siz[0],siz[1],siz[2]); volume.Init(siz); for(double i=0;i<siz[0];i++) for(double j=0;j<siz[1];j++) for(double k=0;k<siz[2];k++) { x = rbb.min[0]+step*i; y = rbb.min[1]+step*j; z = rbb.min[2]+step*k; try { volume.Val(i,j,k)=p.Eval(); } catch(Parser::exception_type &e) { errorMessage = e.GetMsg().c_str(); return false; } } // MARCHING CUBES Log("[MARCHING CUBES] Building mesh..."); MyMarchingCubes mc(m.cm, walker); walker.BuildMesh<MyMarchingCubes>(m.cm, volume, mc, 0); Matrix44f tr; tr.SetIdentity(); tr.SetTranslate(rbb.min[0],rbb.min[1],rbb.min[2]); Matrix44f sc; sc.SetIdentity(); sc.SetScale(step,step,step); tr=tr*sc; tri::UpdatePosition<CMeshO>::Matrix(m.cm,tr); tri::UpdateNormals<CMeshO>::PerVertexNormalizedPerFace(m.cm); tri::UpdateBounding<CMeshO>::Box(m.cm); // updates bounding box return true; } break; case FF_REFINE : { std::string condSelect = par.getString("condSelect").toStdString(); std::string expr1 = par.getString("x").toStdString(); std::string expr2 = par.getString("y").toStdString(); std::string expr3 = par.getString("z").toStdString(); bool errorMidPoint = false; bool errorEdgePred = false; std::string msg = ""; // check parsing errors while creating two func obj // display error message MidPointCustom<CMeshO> mid = MidPointCustom<CMeshO>(m.cm,expr1,expr2,expr3,errorMidPoint,msg); CustomEdge<CMeshO> edge = CustomEdge<CMeshO>(condSelect,errorEdgePred,msg); if(errorMidPoint || errorEdgePred) { errorMessage = msg.c_str(); return false; } // Refine current mesh. // Only edge specified with CustomEdge pred are selected // and the new vertex is choosen with MidPointCustom created above RefineE<CMeshO, MidPointCustom<CMeshO>, CustomEdge<CMeshO> > (m.cm, mid, edge, false, cb); m.clearDataMask( MeshModel::MM_VERTMARK); vcg::tri::UpdateNormals<CMeshO>::PerVertexNormalizedPerFace(m.cm); return true; } break; default : assert (0); } return false; }