diff --git a/apps/nexus/voronoichain.cpp b/apps/nexus/voronoichain.cpp new file mode 100644 index 00000000..5edaf9ed --- /dev/null +++ b/apps/nexus/voronoichain.cpp @@ -0,0 +1,184 @@ +/**************************************************************************** +* VCGLib o o * +* Visual and Computer Graphics Library o o * +* _ O _ * +* Copyright(C) 2004 \/)\/ * +* Visual Computing Lab /\/| * +* ISTI - Italian National Research Council | * +* \ * +* All rights reserved. * +* * +* This program is free software; you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation; either version 2 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) * +* for more details. * +* * +****************************************************************************/ +/**************************************************************************** + History + +$Log: not supported by cvs2svn $ + +****************************************************************************/ + +#include + +#include "voronoichain.h" + +using namespace std; +using namespace vcg; +using namespace nxs; + +void VoronoiChain::Initialize(unsigned int psize, unsigned int pthreshold) { + patch_size = psize; + patch_threshold = pthreshold; +} + +void VoronoiChain::Init(Crude &crude) { + box = crude.GetBox(); + radius = VoronoiPartition::OptimalRadius(crude, patch_size); + float radius0 = radius; + float radius1 = radius * 1.4; + // float radius1 = VoronoiPartition::OptimalRadius(crude, patch_size*2); + + levels.push_back(VoronoiPartition()); + levels.push_back(VoronoiPartition()); + VoronoiPartition &part0 = levels[0]; + VoronoiPartition &part1 = levels[1]; + part0.Init(crude.GetBox()); + part1.Init(crude.GetBox()); + + VFile::iterator iter; + for(iter = crude.vert.Begin(); iter != crude.vert.End(); ++iter) { + Point3f &v = *iter; + unsigned int target_patch; + + float dist = part0.Closest(v, target_patch); + if(dist >= radius0 || dist == -1) + part0.Add(v, radius0); + + dist = part1.Closest(v, target_patch); + if(dist >= radius1 || dist == -1) + part1.Add(v, radius1); + } + + //here goes some optimization pass. +} + +unsigned int VoronoiChain::Locate(unsigned int level, + const vcg::Point3f &p) { + return levels[level].Locate(p); + /* assert(levels.size() > level+1); + unsigned int fine = levels[level].Locate(p); + unsigned int coarse = levels[level+1].Locate(p); + return fine + coarse * levels[level].size();*/ +} + +void VoronoiChain::RemapFaces(Crude &crude, + VFile &face_remap, + vector &patch_faces) { + + Init(crude); + + //TODO: improve quality of patches and implement threshold. + typedef map, unsigned int> FragIndex; + + // map, unsigned int> patches; + FragIndex patches; + + unsigned int totpatches = 0; + + Point3f bari; + for(unsigned int i = 0; i < crude.Faces(); i++) { + bari = crude.GetBari(i); + // unsigned int patch = Locate(0, bari); + unsigned int fine = Locate(0, bari); + unsigned int coarse = Locate(1, bari); + + unsigned int patch; + + if(!patches.count(make_pair(coarse, fine))) { + patch = totpatches; + patches[make_pair(coarse, fine)] = totpatches++; + } else + patch = patches[make_pair(coarse, fine)]; + + face_remap[i] = patch; + if(patch_faces.size() <= patch) + patch_faces.resize(patch+1, 0); + patch_faces[patch]++; + } + + + + //prune faces (now only 0 faces); + unsigned int tot_patches = 0; + vector patch_remap; + for(unsigned int i = 0; i < patch_faces.size(); i++) { + //if below threshold (and can join faces) + if(patch_faces[i] == 0) + patch_remap.push_back(-1); + else + patch_remap.push_back(tot_patches++); + } + + + //building fragments + FragIndex::iterator f; + for(f = patches.begin(); f != patches.end(); f++) { + unsigned int coarse = (*f).first.first; + unsigned int fine = (*f).first.second; + unsigned int patch = (*f).second; + oldfragments[coarse].insert(patch_remap[patch]); + } + + //remapping faces + for(unsigned int i = 0; i < face_remap.Size(); i++) { + unsigned int patch = face_remap[i]; + assert(patch != 0xffffffff); + assert(patch_remap[patch] != -1); + face_remap[i] = patch_remap[patch]; + } + + //remapping patch_faces + for(unsigned int i = 0; i < patch_faces.size(); i++) { + assert(patch_remap[i] <= (int)i); + if(patch_remap[i] != -1) { + assert(patch_faces[i] > 0); + patch_faces[patch_remap[i]] = patch_faces[i]; + } + } + + patch_faces.resize(tot_patches); +} + +void VoronoiChain::BuildLevel(Nexus &nexus, unsigned int offset) { + unsigned int target_faces = (int)(patch_size * + pow(scaling, (float)levels.size())); + + float rad = radius * pow(1.4f, (float)levels.size()); + + levels.push_back(VoronoiPartition()); + VoronoiPartition &part = levels[levels.size()-1]; + part.Init(box); + + for(unsigned int idx = offset; idx < nexus.index.size(); idx++) { + Patch patch = nexus.GetPatch(idx); + for(unsigned int i = 0; i < patch.VertSize(); i++) { + Point3f &v = patch.Vert(i); + unsigned int target_patch; + + float dist = part.Closest(v, target_patch); + if(dist >= rad || dist == -1) + part.Add(v, rad); + } + } + cerr << "radius: " << rad << endl; + //TODO add some optimization +} diff --git a/apps/nexus/voronoichain.h b/apps/nexus/voronoichain.h new file mode 100644 index 00000000..c3e326f4 --- /dev/null +++ b/apps/nexus/voronoichain.h @@ -0,0 +1,74 @@ +/**************************************************************************** +* VCGLib o o * +* Visual and Computer Graphics Library o o * +* _ O _ * +* Copyright(C) 2004 \/)\/ * +* Visual Computing Lab /\/| * +* ISTI - Italian National Research Council | * +* \ * +* All rights reserved. * +* * +* This program is free software; you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation; either version 2 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) * +* for more details. * +* * +****************************************************************************/ +/**************************************************************************** + History + +$Log: not supported by cvs2svn $ + +****************************************************************************/ + +#ifndef NXS_VORONOI_CHAIN_H +#define NXS_VORONOI_CHAIN_H + +#include +#include + +#include + +#include "pchain.h" +#include "pvoronoi.h" +#include "crude.h" +#include "nexus.h" +#include "vfile.h" + +namespace nxs { + +class VoronoiChain: public PChain { + public: + VoronoiChain():scaling(0.5), patch_size(1000), patch_threshold(300) {} + virtual ~VoronoiChain() {} + void Initialize(unsigned int psize, unsigned int pthreshold); + void Init(Crude &crude); + + virtual unsigned int Locate(unsigned int level, const vcg::Point3f &p); + void RemapFaces(Crude &crude, VFile &face_remap, + std::vector &patch_faces); + + void BuildLevel(Nexus &nexus, unsigned int offset); + + std::vector levels; + + // coarse partition -> associated patches + std::map > newfragments; + std::map > oldfragments; + private: + float scaling; + unsigned int patch_size; + unsigned int patch_threshold; + + float radius; + vcg::Box3f box; +}; +} + +#endif diff --git a/apps/nexus/voronoinxs.cpp b/apps/nexus/voronoinxs.cpp new file mode 100644 index 00000000..3dfae1ec --- /dev/null +++ b/apps/nexus/voronoinxs.cpp @@ -0,0 +1,556 @@ +/**************************************************************************** +* VCGLib o o * +* Visual and Computer Graphics Library o o * +* _ O _ * +* Copyright(C) 2004 \/)\/ * +* Visual Computing Lab /\/| * +* ISTI - Italian National Research Council | * +* \ * +* All rights reserved. * +* * +* This program is free software; you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation; either version 2 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) * +* for more details. * +* * +****************************************************************************/ +/**************************************************************************** + History + +$Log: not supported by cvs2svn $ + +****************************************************************************/ + +#ifdef WIN32 +#include "getopt.h" +#endif + +#include +using namespace std; + +#include "crude.h" +#include "nexus.h" + +#include "voronoichain.h" +#include "pintersect.h" +#include "vert_remap.h" +using namespace vcg; +using namespace nxs; + +struct RemapLink { + unsigned int rel_vert; + unsigned int patch; + unsigned int abs_vert; +}; + +/*void RemapFaces(Crude &crude, + VoronoiChain &vchain, + VFile &face_remap, + vector &patch_faces);*/ + +void RemapVertices(Crude &crude, + VertRemap &vert_remap, + VFile &face_remap, + vector &patch_verts); + +void NexusAllocate(Crude &crude, + Nexus &nexus, + VFile &face_remap, + vector &patch_faces, + vector &patch_verts); + +void NexusFill(Crude &crude, + Nexus &nexus, + VertRemap &vert_remap, + VFile &border_remap); + +void NexusFixBorder(Nexus &nexus, + VFile &border_remap); + +void NexusSplit(Nexus &nexus, VoronoiChain &vchain, + unsigned int level, + vector &newvert, + vector &newface, + vector &newbord); + +int main(int argc, char *argv[]) { + + unsigned int patch_size = 1000; + unsigned int patch_threshold = 200; + unsigned int max_level = 0xffffffff; + + int option; + while((option = getopt(argc, argv, "f:t:l:")) != EOF) { + switch(option) { + case 'f': patch_size = atoi(optarg); + if(patch_size == 0 || patch_size > 32000) { + cerr << "Invalid number of faces per patch: " << optarg << endl; + return -1; + } + break; + case 't': patch_threshold = atoi(optarg); + if(patch_threshold == 0 || patch_threshold > patch_size) { + cerr << "Invalid patch threshold: " << optarg << endl; + return -1; + } + break; + case 'l': max_level = atoi(optarg); + if(max_level == 0) { + cerr << "Invalid number of levels: " << optarg << endl; + return -1; + } + break; + default: cerr << "Unknown option: " << (char)option << endl; + return -1; + } + } + + //Test there are still 2 arguments + if(optind != argc - 2) { + cerr << "Usage: " << argv[0] << " [options]\n"; + cerr << " Options:\n"; + cerr << " -f N: use N faces per patch (default 1000, max 32000)\n"; + cerr << " -t N: mini faces per patch (default 200)\n"; + cerr << " -l N: number of levels\n\n"; + return -1; + } + + Crude crude; + if(!crude.Load(argv[optind])) { + cerr << "Could not open crude input: " << argv[optind] << endl; + return -1; + } + + if(patch_size > crude.vert.Size()/2) { + cerr << "Patch size too big: " << patch_size << " ~ " << crude.vert.Size() + << endl; + return -1; + } + + string output = argv[optind+1]; + Nexus nexus; + if(!nexus.Create(output)) { + cerr << "Could not create nexus output: " << output << endl; + return -1; + } + + VoronoiChain vchain; + vchain.Initialize(patch_size, patch_threshold); + + //Now building level 0 of the Nexus + + VFile face_remap; + if(!face_remap.Create(output + ".rmf")) { + cerr << "Could not create remap files: " << output << ".frm\n"; + return -1; + } + face_remap.Resize(crude.Faces()); + + + VertRemap vert_remap; + if(!vert_remap.Create(output)) { + cerr << "Could not create remap files: " << output << ".rmv and .rmb\n"; + return -1; + } + vert_remap.Resize(crude.Vertices()); + + VFile border_remap; + if(!border_remap.Create(output + string(".tmp"))) { + cerr << "Could not create temporary border remap file\n"; + return -1; + } + + /* BUILDING FIRST LEVEL */ + + //Remapping faces and vertices using level 0 and 1 of the chain + + vector patch_faces; + vchain.RemapFaces(crude, face_remap, patch_faces); + + vector patch_verts; + patch_verts.resize(patch_faces.size(), 0); + RemapVertices(crude, vert_remap, face_remap, patch_verts); + + + //allocate chunks for patches and copy faces (absoklute indexing) into them. + NexusAllocate(crude, nexus, face_remap, patch_faces, patch_verts); + + //insert vertices and remap faces, prepare borders + NexusFill(crude, nexus, vert_remap, border_remap); + + NexusFixBorder(nexus, border_remap); + + + /* BUILDING OTHER LEVELS */ + unsigned int oldoffset = 0; + + for(unsigned int level = 1; level < max_level; level++) { + cerr << "Level: " << level << endl; + + unsigned int newoffset = nexus.index.size(); + vchain.BuildLevel(nexus, oldoffset); + + + map >::iterator fragment; + for(fragment = vchain.oldfragments.begin(); + fragment != vchain.oldfragments.end(); fragment++) { + + cerr << "Joining: "; + set &fcells = (*fragment).second; + set::iterator s; + for(s = fcells.begin(); s != fcells.end(); s++) + cerr << " " << (*s) << endl; + cerr << endl; + + vector newvert; + vector newface; + vector newbord; + + nexus.Join((*fragment).second, newvert, newface, newbord); + + //simplyfy mesh + + NexusSplit(nexus, vchain, level, newvert, newface, newbord); + + } + + + vchain.oldfragments = vchain.newfragments; + oldoffset = newoffset; + } + + //Clean up: + nexus.Close(); + + //TODO remove vert_remap, face_remap, border_remap + return 0; +} + +void RemapVertices(Crude &crude, + VertRemap &vert_remap, + VFile &face_remap, + vector &patch_verts) { + + for(unsigned int i = 0; i < crude.Faces(); i++) { + Crude::Face &face = crude.GetFace(i); + unsigned int patch = face_remap[i]; + for(int k = 0; k < 3; k++) { + set pp; + vert_remap.GetValues(face[k], pp); + if(!pp.count(patch)) { + vert_remap.Insert(face[k], patch); + patch_verts[patch]++; + } + } + } +} + +void NexusAllocate(Crude &crude, + Nexus &nexus, + VFile &face_remap, + vector &patch_faces, + vector &patch_verts) { + + + nexus.index.resize(patch_faces.size()); + + unsigned int totchunks = 0; + //now that we know various sizes, lets allocate space + for(unsigned int i = 0; i < nexus.index.size(); i++) { + Nexus::Entry &entry = nexus.index[i]; + + if(patch_faces[i] == 0 || patch_verts[i] == 0) + cerr << "Warning! Empty patch.\n"; + + entry.patch_start = totchunks; + entry.patch_size = Patch::ChunkSize(patch_verts[i], patch_faces[i]); + + totchunks += entry.patch_size; + entry.border_start = 0xffffffff; + entry.nvert = patch_verts[i]; + entry.nface = 0; + } + + nexus.patches.Resize(totchunks); + + + //now we sort the faces into the patches (but still using absolute indexing + //instead of relative indexing + for(unsigned int i = 0; i < crude.face.Size(); i++) { + Crude::Face &face = crude.face[i]; + unsigned int npatch = face_remap[i]; + + Nexus::Entry &entry = nexus.index[npatch]; + Patch patch = nexus.GetPatch(npatch); + + Crude::Face *faces = (Crude::Face *)patch.VertBegin(); + faces[entry.nface] = face; + entry.nface++; + } +} + + +void NexusFill(Crude &crude, + Nexus &nexus, + VertRemap &vert_remap, + VFile &border_remap) { + + + //finally for every patch we collect the vertices + //and fill the patch. + //we need to remember start and size in border_remap; + // vector border_start; + // vector border_size; + + for(unsigned int i = 0; i < nexus.index.size(); i++) { + Patch patch = nexus.GetPatch(i); + Nexus::Entry &entry = nexus.index[i]; + + //make a copy of faces (we need to write there :P) + Crude::Face *faces = new Crude::Face[patch.FaceSize()]; + memcpy(faces, (Crude::Face *)patch.VertBegin(), + patch.FaceSize() * sizeof(Crude::Face)); + + //collect all vertices we need. + //TODO an hash_map would be faster? + unsigned int count = 0; + map remap; + for(unsigned int k = 0; k < patch.FaceSize(); k++) { + Crude::Face &face = faces[k]; + + for(int j = 0; j < 3; j++) { + if(!remap.count(face[j])) { + assert(count < patch.VertSize()); + Point3f &v = crude.vert[face[j]]; + patch.VertBegin()[remap.size()] = v; + entry.sphere.Add(v); + remap[face[j]] = count++; + } + patch.FaceBegin()[k*3 + j] = remap[face[j]]; + } + } + assert(count == remap.size()); + assert(entry.nvert == remap.size()); + + //record start of border: + entry.border_start = border_remap.Size(); + + //TODO hash_set? + set border_patches; + map::iterator m; + for(m = remap.begin(); m != remap.end(); m++) { + RemapLink link; + link.abs_vert = (*m).first; + link.rel_vert = (*m).second; + + vert_remap.GetValues(link.abs_vert, border_patches); + assert(border_patches.size() >= 1); + if(border_patches.size() == 1) continue; //its not a border + + set::iterator s; + for(s = border_patches.begin(); s != border_patches.end(); s++) { + if((*s) == i) continue; + link.patch = *s; + border_remap.PushBack(link); + } + } + //and number of borders: + entry.border_size = border_remap.Size() - entry.border_start; + delete []faces; + } + + //we can now update bounding sphere. + for(unsigned int i = 0; i < nexus.index.size(); i++) + nexus.sphere.Add(nexus.index[i].sphere); +} + +void NexusFixBorder(Nexus &nexus, + VFile &border_remap) { + + //and last convert RemapLinks into Links + nexus.borders.Resize(border_remap.Size()); + + for(unsigned int i = 0; i < nexus.index.size(); i++) { + Nexus::Entry &local = nexus.index[i]; + + // K is the main iterator (where we write to in nexus.borders) + for(unsigned int k = local.border_start; + k < local.border_start + local.border_size; k++) { + + RemapLink start_link = border_remap[k]; + assert(start_link.rel_vert < local.nvert); + + Nexus::Entry &remote = nexus.index[start_link.patch]; + + bool found = false; + for(unsigned int j = remote.border_start; + j < remote.border_start + remote.border_size; j++) { + + RemapLink end_link = border_remap[j]; + assert(end_link.rel_vert < remote.nvert); + + if(start_link.abs_vert == end_link.abs_vert && + end_link.patch == i) { //found the match + assert(!found); + nexus.borders[k] = Link(start_link.rel_vert, + end_link.rel_vert, start_link.patch); + found = true; + } + } + assert(nexus.borders[k].start_vert < local.nvert); + assert(found); + } + } + nexus.borders.Flush(); + + //Checking border consistency: + /* for(unsigned int i = 0; i < nexus.index.size(); i++) { + Border border = nexus.GetBorder(i); + Nexus::Entry &entry = nexus.index[i]; + for(unsigned int k = 0; k < border.Size(); k++) { + Link &link = border[k]; + if(link.start_vert >= entry.nvert) { + cerr << "K: " << k << endl; + cerr << "patch: " << i << " nvert: " << entry.nvert << " startv: " + << link.start_vert << endl; + cerr << "bstart: " << entry.border_start + << "bsize: " << entry.border_size << endl; + } + assert(link.end_patch < nexus.index.size()); + assert(link.start_vert < entry.nvert); + Nexus::Entry &remote = nexus.index[link.end_patch]; + assert(link.end_vert < remote.nvert); + } + + }*/ +} + + +void NexusSplit(Nexus &nexus, VoronoiChain &vchain, + unsigned int level, + vector &newvert, + vector &newface, + vector &newbord) { + + //if != -1 remap global index to cell index + map > vert_remap; + map vert_count; + + //simply collects faces + map > face_remap; + map face_count; + + for(unsigned int f = 0; f < newface.size(); f += 3) { + Point3f bari = (newvert[newface[f]] + + newvert[newface[f+1]] + + newvert[newface[f+2]])/3; + + unsigned int cell = vchain.Locate(level+1, bari); + + vector &f_remap = face_remap[cell]; + f_remap.push_back(newface[f]); + f_remap.push_back(newface[f+1]); + f_remap.push_back(newface[f+2]); + face_count[cell]++; + + if(!vert_remap.count(cell)) { + vert_remap[cell].resize(newvert.size(), -1); + vert_count[cell] = 0; + } + + vector &v_remap = vert_remap[cell]; + + for(int i = 0; i < 3; i++) + if(v_remap[newface[f+i]] == -1) + v_remap[newface[f+i]] = vert_count[cell]++; + } + + + //TODO prune small count cells + + //for every new cell + map::iterator c; + for(c = vert_count.begin(); c != vert_count.end(); c++) { + unsigned int cell = (*c).first; + cerr << "Processing cell: " << cell << endl; + + vector faces; + vector verts; + vector bords; + + vector &v_remap = vert_remap[cell]; + vector &f_remap = face_remap[cell]; + + verts.resize(vert_count[cell]); + for(unsigned int i = 0; i < newvert.size(); i++) { + if(v_remap[i] != -1) + verts[v_remap[i]] = newvert[i]; + // verts.push_back(newvert[v_remap[i]]); + } + + assert(verts.size() == vert_count[cell]); + assert(f_remap.size()/3 == face_count[cell]); + + faces.resize(face_count[cell]*3); + + for(unsigned int i = 0; i < f_remap.size(); i++) { + if(v_remap[f_remap[i]] == -1) { + cerr << "i: " << i << " f_remap[i]: " << f_remap[i] << endl; + } + assert(v_remap[f_remap[i]] != -1); + faces[i] = v_remap[f_remap[i]]; + } + //process external borders + for(unsigned int i = 0; i < newbord.size(); i++) { + Link link = newbord[i]; + if(v_remap[link.start_vert] == -1) continue; + link.start_vert = v_remap[link.start_vert]; + bords.push_back(link); + } + + //process internal borders; + //TODO higly inefficient!!! + map::iterator t; + for(t = vert_count.begin(); t != vert_count.end(); t++) { + if(cell == (*t).first) continue; + vector &vremapclose = vert_remap[(*t).first]; + for(unsigned int i = 0; i < newvert.size(); i++) { + if(v_remap[i] != -1 && vremapclose[i] != -1) { + Link link; + link.end_patch = (*t).first; + link.start_vert = v_remap[i]; + link.end_vert = vremapclose[i]; + bords.push_back(link); + } + } + } + //create new nexus patch + unsigned int patch_idx = nexus.AddPatch(verts.size(), + faces.size()/3, + bords.size()); + + Nexus::Entry &entry = nexus.index[patch_idx]; + + Patch patch = nexus.GetPatch(patch_idx); + memcpy(patch.FaceBegin(), &faces[0], + faces.size() * sizeof(unsigned short)); + memcpy(patch.VertBegin(), &verts[0], verts.size() * sizeof(Point3f)); + + + for(int v = 0; v < verts.size(); v++) { + entry.sphere.Add(verts[v]); + nexus.sphere.Add(verts[v]); + } + + Border border = nexus.GetBorder(patch_idx); + memcpy(&border[0], &bords[0], bords.size()); + + } +} +