#include #include #include #include "nexus.h" using namespace std; using namespace vcg; using namespace nxs; Nexus::~Nexus() { Close(); } bool Nexus::Create(const string &file, Signature sig, unsigned int c_size) { signature = sig; totvert = 0; totface = 0; sphere = Sphere3f(); chunk_size = c_size; unsigned int header_size = 256; if(chunk_size > header_size) header_size = chunk_size; history.Clear(); ram_used = 0; ram_max = 50 * (1<<20) / chunk_size; if(!IndexFile::Create(file + ".nxp", header_size)) { cerr << "Could not create file: " << file << ".nxp" << endl; return false; } //Important: chunk_size must be 1 so that i can use Region in VFile. if(!borders.Create(file + ".nxb")) { cerr << "Could not create file: " << file << ".nxb" << endl; return false; } return true; } bool Nexus::Load(const string &file, bool rdonly) { if(!IndexFile::Load(file + ".nxp", rdonly)) return false; ram_used = 0; ram_max = 50 * (1<<20) / chunk_size; history.Clear(); SetPosition(history_offset); unsigned int history_size; ReadBuffer(&history_size, sizeof(unsigned int)); char *buffer = new char[history_size]; ReadBuffer(buffer, history_size); if(!history.Load(history_size, buffer)) { cerr << "Error loading history\n"; return false; } borders.Load(file + ".nxb", rdonly); //TODO on nxsbuilder assure borders are loaded return true; } void Nexus::Close() { if(!Opened()) return; Flush(); if(!IsReadOnly()) { //set history_offset if(!size()) history_offset = 0; else history_offset = (back().patch_start + back().disk_size); history_offset *= chunk_size; unsigned int history_size; char *mem = history.Save(history_size); Redim(history_offset + history_size + sizeof(unsigned int)); SetPosition(history_offset); WriteBuffer(&history_size, sizeof(unsigned int)); WriteBuffer(mem, history_size); delete []mem; } borders.Close(); IndexFile::Close(); } void Nexus::SaveHeader() { unsigned int magic = 0x3053584e; // nxs0 WriteBuffer(&magic, sizeof(unsigned int)); WriteBuffer(&signature, sizeof(unsigned int)); WriteBuffer(&chunk_size, sizeof(unsigned int)); WriteBuffer(&offset, sizeof(int64)); WriteBuffer(&history_offset, sizeof(int64)); WriteBuffer(&totvert, sizeof(unsigned int)); WriteBuffer(&totface, sizeof(unsigned int)); WriteBuffer(&sphere, sizeof(Sphere3f)); } bool Nexus::LoadHeader() { unsigned int magic; ReadBuffer(&magic, sizeof(unsigned int)); if(magic != 0x3053584e) { cerr << "Invalid magic. Not a nxs file\n"; return false; } ReadBuffer(&signature, sizeof(unsigned int)); ReadBuffer(&chunk_size, sizeof(unsigned int)); ReadBuffer(&offset, sizeof(int64)); ReadBuffer(&history_offset, sizeof(int64)); ReadBuffer(&totvert, sizeof(unsigned int)); ReadBuffer(&totface, sizeof(unsigned int)); ReadBuffer(&sphere, sizeof(Sphere3f)); return true; } void Nexus::Flush(bool all) { if(all) { std::map::iterator>::iterator i; for(i = index.begin(); i != index.end(); i++) { unsigned int patch = (*i).first; FlushPatch(patch); } pqueue.clear(); index.clear(); } else { while(ram_used > ram_max) { unsigned int to_flush = pqueue.back(); pqueue.pop_back(); index.erase(to_flush); FlushPatch(to_flush); } } } Patch &Nexus::GetPatch(unsigned int patch, bool flush) { Entry &entry = operator[](patch); if(index.count(patch)) { assert(entry.patch); list::iterator i = index[patch]; pqueue.erase(i); pqueue.push_front(patch); index[patch] = pqueue.begin(); } else { while(flush && ram_used > ram_max) { unsigned int to_flush = pqueue.back(); pqueue.pop_back(); index.erase(to_flush); FlushPatch(to_flush); } assert(!entry.patch); entry.patch = LoadPatch(patch); pqueue.push_front(patch); list::iterator i = pqueue.begin(); index[patch] = i; } return *(entry.patch); } Border Nexus::GetBorder(unsigned int patch, bool flush) { return borders.GetBorder(patch); } /*void Nexus::AddBorder(unsigned int patch, Link &link) { Border border = GetBorder(patch); unsigned int pos = border.Size(); if(pos > 65500) { cerr << "Exceding border size!!!\n"; exit(0); } if(borders.ResizeBorder(patch, pos+1)) { border = GetBorder(patch); } assert(border.Available() > pos); border[pos] = link; }*/ unsigned int Nexus::AddPatch(unsigned int nvert, unsigned int nface, unsigned int nbord) { Entry entry; entry.patch_start = 0xffffffff; entry.ram_size = Patch::ChunkSize(signature, nvert, nface, chunk_size); entry.disk_size = 0xffff; entry.nvert = nvert; entry.nface = nface; entry.error = 0; //sphere undefined. entry.patch = NULL; entry.vbo_array = 0; entry.vbo_element = 0; push_back(entry); borders.AddBorder(nbord); totvert += nvert; totface += nface; return size() - 1; } /*void Nexus::Unify(float threshold) { //TODO what if colors or normals or strips? unsigned int duplicated = 0; unsigned int degenerate = 0; for(unsigned int p = 0; p < size(); p++) { Entry &entry = operator[](p); Patch &patch = GetPatch(p); unsigned int vcount = 0; map vertices; vector remap; remap.resize(patch.nv); for(unsigned int i = 0; i < patch.nv; i++) { Point3f &point = patch.Vert(i); if(!vertices.count(point)) vertices[point] = vcount++; else duplicated++; remap[i] = vertices[point]; } assert(vertices.size() <= patch.nv); if(vertices.size() == patch.nv) //no need to unify continue; vector newvert; newvert.resize(vertices.size()); map::iterator k; for(k = vertices.begin(); k != vertices.end(); k++) { newvert[(*k).second] = (*k).first; } vector newface; //check no degenerate faces get created. for(unsigned int f = 0; f < entry.nface; f++) { unsigned short *face = patch.Face(f); if(face[0] != face[1] && face[1] != face[2] && face[0] != face[2] && newvert[remap[face[0]]] != newvert[remap[face[1]]] && newvert[remap[face[0]]] != newvert[remap[face[2]]] && newvert[remap[face[1]]] != newvert[remap[face[2]]]) { newface.push_back(remap[face[0]]); newface.push_back(remap[face[1]]); newface.push_back(remap[face[2]]); } else { degenerate++; } } //rewrite patch now. entry.nvert = newvert.size(); entry.nface = newface.size()/3; patch.Init(signature, entry.nvert, entry.nface); memcpy(patch.VertBegin(), &(newvert[0]), entry.nvert*sizeof(Point3f)); memcpy(patch.FaceBegin(), &(newface[0]), entry.nface*3*sizeof(short)); //testiamo il tutto... TODO remove this of course for(unsigned int i =0; i < patch.nf; i++) { for(int k =0 ; k < 3; k++) if(patch.Face(i)[k] >= patch.nv) { cerr <<" Unify has problems\n"; exit(0); } } //fix patch borders now set close; //bordering pathes Border border = GetBorder(p); for(unsigned int b = 0; b < border.Size(); b++) { if(border[b].IsNull()) continue; close.insert(border[b].end_patch); border[b].start_vert = remap[border[b].start_vert]; } set::iterator c; for(c = close.begin(); c != close.end(); c++) { Border bord = GetBorder(*c); for(unsigned int b = 0; b < bord.Size(); b++) { if(bord[b].IsNull()) continue; if(bord[b].end_patch == p) { bord[b].end_vert = remap[bord[b].end_vert]; } } } } //better to compact directly borders than setting them null. //finally: there may be duplicated borders for(unsigned int p = 0; p < size(); p++) { Border border = GetBorder(p); set links; for(unsigned int b = 0; b < border.Size(); b++) { Link &link = border[b]; assert(!link.IsNull()); //if(border[b].IsNull()) continue; links.insert(link); } int count = 0; for(set::iterator k = links.begin(); k != links.end(); k++) border[count++] = *k; borders[p].used = links.size(); } totvert -= duplicated; if(duplicated) cerr << "Found " << duplicated << " duplicated vertices" << endl; if(degenerate) cerr << "Found " << degenerate << " degenerate face while unmifying\n"; }*/ Patch *Nexus::LoadPatch(unsigned int idx) { assert(idx < size()); Entry &entry = operator[](idx); if(entry.patch) return entry.patch; char *ram = new char[entry.ram_size * chunk_size]; #ifndef NDEBUG if(!ram) { cerr << "COuld not allocate ram!\n"; exit(0); } #endif Patch *patch = new Patch(signature, ram, entry.nvert, entry.nface); if(entry.patch_start != 0xffffffff) { //was allocated. assert(entry.disk_size != 0xffff); MFile::SetPosition((int64)entry.patch_start * (int64)chunk_size); if((signature & NXS_COMPRESSED) == 0) { //not compressed MFile::ReadBuffer(ram, entry.disk_size * chunk_size); } else { unsigned char *disk = new unsigned char[entry.disk_size * chunk_size]; MFile::ReadBuffer(disk, entry.disk_size * chunk_size); patch->Decompress(entry.ram_size * chunk_size, disk, entry.disk_size * chunk_size); delete []disk; } } ram_used += entry.ram_size; entry.patch = patch; return patch; } void Nexus::FlushPatch(unsigned int id) { Entry &entry = operator[](id); assert(entry.patch); if(!MFile::IsReadOnly()) { //write back patch if((signature & NXS_COMPRESSED)) { unsigned int compressed_size; char *compressed = entry.patch->Compress(entry.ram_size * chunk_size, compressed_size); if(entry.disk_size == 0xffff) {//allocate space assert(entry.patch_start == 0xffffffff); entry.disk_size = (unsigned int)((compressed_size-1)/chunk_size) + 1; entry.patch_start = (unsigned int)(Length()/chunk_size); Redim(Length() + entry.disk_size * chunk_size); } else { //cerr << "OOOOPSPPPS not supported!" << endl; exit(-1); } MFile::SetPosition((int64)entry.patch_start * (int64)chunk_size); MFile::WriteBuffer(compressed, entry.disk_size * chunk_size); delete []compressed; } else { if(entry.disk_size == 0xffff) { entry.disk_size = entry.ram_size; entry.patch_start = (unsigned int)(Length()/chunk_size); Redim(Length() + entry.disk_size * chunk_size); } MFile::SetPosition((int64)entry.patch_start * (int64)chunk_size); MFile::WriteBuffer(entry.patch->start, entry.disk_size * chunk_size); } } delete [](entry.patch->start); delete entry.patch; entry.patch = NULL; ram_used -= entry.ram_size; }