From 76617c2a7ffec2a80e40f6719895939d943b1fe8 Mon Sep 17 00:00:00 2001 From: ganovelli Date: Thu, 6 Oct 2005 10:08:51 +0000 Subject: [PATCH] *** empty log message *** --- apps/nexus/border.h | 97 --- apps/nexus/borderserver.cpp | 176 ----- apps/nexus/borderserver.h | 77 -- apps/nexus/crude.cpp | 147 ---- apps/nexus/crude.h | 129 ---- apps/nexus/crudeview.cpp | 264 ------- apps/nexus/decimate.cpp | 265 ------- apps/nexus/decimate.h | 49 -- apps/nexus/exploder.cpp | 108 --- apps/nexus/extraction.cpp | 485 ------------ apps/nexus/extraction.h | 155 ---- apps/nexus/file.cpp | 130 ---- apps/nexus/file.h | 58 -- apps/nexus/fragment.cpp | 431 ----------- apps/nexus/fragment.h | 106 --- apps/nexus/history.cpp | 400 ---------- apps/nexus/history.h | 130 ---- apps/nexus/index_file.h | 108 --- apps/nexus/metric.h | 109 --- apps/nexus/mfile.cpp | 192 ----- apps/nexus/mfile.h | 91 --- apps/nexus/nexus.cpp | 319 -------- apps/nexus/nexus.h | 146 ---- apps/nexus/nexus.html | 174 ----- apps/nexus/nexus_logo.png | Bin 154955 -> 0 bytes apps/nexus/nexus_logo_large.png | Bin 58680 -> 0 bytes apps/nexus/nexus_logo_midi.png | Bin 27989 -> 0 bytes apps/nexus/nexusmt.cpp | 464 ----------- apps/nexus/nexusmt.h | 162 ---- apps/nexus/nexusview.cpp | 749 ------------------ apps/nexus/normalscone.cpp | 180 ----- apps/nexus/normalscone.h | 53 -- apps/nexus/nxs2ply.cpp | 72 -- apps/nexus/nxsalgo.cpp | 796 ------------------- apps/nexus/nxsalgo.h | 93 --- apps/nexus/nxsbuilder.cpp | 972 ------------------------ apps/nexus/nxsclient.cpp | 71 -- apps/nexus/nxsdispatcher.cpp | 221 ------ apps/nexus/nxsdispatcher.h | 120 --- apps/nexus/nxsedit.cpp | 670 ---------------- apps/nexus/nxsexport.h | 119 --- apps/nexus/nxspatcher.cpp | 48 -- apps/nexus/nxsserver.cpp | 223 ------ apps/nexus/nxstest.cpp | 163 ---- apps/nexus/nxstypes.h | 43 -- apps/nexus/patch.cpp | 386 ---------- apps/nexus/patch.h | 169 ---- apps/nexus/ply2crude.cpp | 167 ---- apps/nexus/preload.cpp | 82 -- apps/nexus/preload.h | 87 --- apps/nexus/remapping.cpp | 520 ------------- apps/nexus/remapping.h | 95 --- apps/nexus/strip.cpp | 67 -- apps/nexus/strip.h | 12 - apps/nexus/tristripper/graph_array.h | 419 ---------- apps/nexus/tristripper/heap_array.h | 275 ------- apps/nexus/tristripper/tri_stripper.cpp | 586 -------------- apps/nexus/tristripper/tri_stripper.h | 372 --------- apps/nexus/vchain.cpp | 134 ---- apps/nexus/vchain.h | 51 -- apps/nexus/vfile.h | 317 -------- apps/nexus/vpartition.cpp | 171 ----- apps/nexus/vpartition.h | 99 --- apps/nexus/watch.cpp | 155 ---- apps/nexus/watch.h | 108 --- apps/nexus/zcurve.h | 46 -- 66 files changed, 13883 deletions(-) delete mode 100644 apps/nexus/border.h delete mode 100644 apps/nexus/borderserver.cpp delete mode 100644 apps/nexus/borderserver.h delete mode 100644 apps/nexus/crude.cpp delete mode 100644 apps/nexus/crude.h delete mode 100644 apps/nexus/crudeview.cpp delete mode 100644 apps/nexus/decimate.cpp delete mode 100644 apps/nexus/decimate.h delete mode 100644 apps/nexus/exploder.cpp delete mode 100644 apps/nexus/extraction.cpp delete mode 100644 apps/nexus/extraction.h delete mode 100644 apps/nexus/file.cpp delete mode 100644 apps/nexus/file.h delete mode 100644 apps/nexus/fragment.cpp delete mode 100644 apps/nexus/fragment.h delete mode 100644 apps/nexus/history.cpp delete mode 100644 apps/nexus/history.h delete mode 100644 apps/nexus/index_file.h delete mode 100644 apps/nexus/metric.h delete mode 100644 apps/nexus/mfile.cpp delete mode 100644 apps/nexus/mfile.h delete mode 100644 apps/nexus/nexus.cpp delete mode 100644 apps/nexus/nexus.h delete mode 100644 apps/nexus/nexus.html delete mode 100644 apps/nexus/nexus_logo.png delete mode 100644 apps/nexus/nexus_logo_large.png delete mode 100644 apps/nexus/nexus_logo_midi.png delete mode 100644 apps/nexus/nexusmt.cpp delete mode 100644 apps/nexus/nexusmt.h delete mode 100644 apps/nexus/nexusview.cpp delete mode 100644 apps/nexus/normalscone.cpp delete mode 100644 apps/nexus/normalscone.h delete mode 100644 apps/nexus/nxs2ply.cpp delete mode 100644 apps/nexus/nxsalgo.cpp delete mode 100644 apps/nexus/nxsalgo.h delete mode 100644 apps/nexus/nxsbuilder.cpp delete mode 100644 apps/nexus/nxsclient.cpp delete mode 100644 apps/nexus/nxsdispatcher.cpp delete mode 100644 apps/nexus/nxsdispatcher.h delete mode 100644 apps/nexus/nxsedit.cpp delete mode 100644 apps/nexus/nxsexport.h delete mode 100644 apps/nexus/nxspatcher.cpp delete mode 100644 apps/nexus/nxsserver.cpp delete mode 100644 apps/nexus/nxstest.cpp delete mode 100644 apps/nexus/nxstypes.h delete mode 100644 apps/nexus/patch.cpp delete mode 100644 apps/nexus/patch.h delete mode 100644 apps/nexus/ply2crude.cpp delete mode 100644 apps/nexus/preload.cpp delete mode 100644 apps/nexus/preload.h delete mode 100644 apps/nexus/remapping.cpp delete mode 100644 apps/nexus/remapping.h delete mode 100644 apps/nexus/strip.cpp delete mode 100644 apps/nexus/strip.h delete mode 100644 apps/nexus/tristripper/graph_array.h delete mode 100644 apps/nexus/tristripper/heap_array.h delete mode 100644 apps/nexus/tristripper/tri_stripper.cpp delete mode 100644 apps/nexus/tristripper/tri_stripper.h delete mode 100644 apps/nexus/vchain.cpp delete mode 100644 apps/nexus/vchain.h delete mode 100644 apps/nexus/vfile.h delete mode 100644 apps/nexus/vpartition.cpp delete mode 100644 apps/nexus/vpartition.h delete mode 100644 apps/nexus/watch.cpp delete mode 100644 apps/nexus/watch.h delete mode 100644 apps/nexus/zcurve.h diff --git a/apps/nexus/border.h b/apps/nexus/border.h deleted file mode 100644 index 25e0fc77..00000000 --- a/apps/nexus/border.h +++ /dev/null @@ -1,97 +0,0 @@ -/**************************************************************************** -* 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 $ -Revision 1.6 2005/01/21 17:09:12 ponchio -Porting and debug. - -Revision 1.5 2004/10/08 14:46:26 ponchio -Working version. - -Revision 1.4 2004/09/30 00:27:08 ponchio -Added used counter. - -Revision 1.3 2004/07/05 17:02:17 ponchio -Couple of const missing. - -Revision 1.2 2004/07/04 14:21:31 ponchio -Added operator< to Link - -Revision 1.1 2004/07/02 13:00:02 ponchio -Created. - - -****************************************************************************/ -#ifndef NXS_BORDER_H -#define NXS_BORDER_H - -namespace nxs { - -struct Link { - Link(): start_vert(0xffff), end_vert(0xffff), end_patch(0xffffffff) {} - Link(unsigned short sv, unsigned short ev, unsigned int ep): - start_vert(sv), end_vert(ev), end_patch(ep) {} - unsigned short start_vert; - unsigned short end_vert; - unsigned int end_patch; - bool IsNull() { return end_patch == 0xffffffff; } - - bool operator==(const Link &l) const { - return end_patch == l.end_patch && - end_vert == l.end_vert && - start_vert == l.start_vert; - } - bool operator<(const Link &l) const { - if(end_patch == l.end_patch) { - if(start_vert == l.start_vert) { - return end_vert < l.end_vert; - } else - return start_vert < l.start_vert; - } else - return end_patch < l.end_patch; - } -}; - -class Border { - public: - Border(Link *l = NULL, unsigned short _used = 0, unsigned short _size = 0): - links(l), used(_used), size(_size), start(0) {} - unsigned int Size() { return used; } - //TODO rename available to capacity. - unsigned int Capacity() { return size; } - Link &operator[](unsigned int i) { return links[i]; } - Link *Start() { return links; } - - //TODO implement an iterator! - Link *links; - unsigned int used; - unsigned int size; - unsigned int start; -}; - -} - - -#endif diff --git a/apps/nexus/borderserver.cpp b/apps/nexus/borderserver.cpp deleted file mode 100644 index f5c726d8..00000000 --- a/apps/nexus/borderserver.cpp +++ /dev/null @@ -1,176 +0,0 @@ -/**************************************************************************** -* 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 $ -Revision 1.10 2005/02/08 12:43:03 ponchio -Added copyright - - -****************************************************************************/ - -#include "borderserver.h" -#include - -using namespace std; -using namespace nxs; - -bool BorderServer::Create(const string &file) { - ram_used = 0; - return IndexFile::Create(file, 2 * sizeof(Link)); -} - -bool BorderServer::Load(const string &file, bool rdonly) { - ram_used = 0; - bool success = IndexFile::Load(file, rdonly); - if(!success) return false; - for(unsigned int i = 0; i < size(); i++) - operator[](i).links = NULL; - return true; -} - -void BorderServer::Close() { - if(!Opened()) return; - Flush(); - IndexFile::Close(); -} - -void BorderServer::Flush() { - std::map::iterator>::iterator i; - for(i = index.begin(); i != index.end(); i++) { - unsigned int patch = (*i).first; - FlushBorder(patch); - } - pqueue.clear(); - index.clear(); -} - -void BorderServer::AddBorder(unsigned short _size, unsigned int used) { - Border entry; - assert((Length() % sizeof(Link)) == 0); - - entry.start = Length()/ sizeof(Link); - entry.size = _size; - entry.used = used; - entry.links = NULL; - push_back(entry); - Redim((int64)entry.start * (int64)sizeof(Link) + (int64)_size * (int64)sizeof(Link)); -} - -Border &BorderServer::GetBorder(unsigned int border, bool flush) { - Border &entry = operator[](border); - //assert(entry.size != 0); - if(index.count(border)) { - //assert(entry.links); - list::iterator i = index[border]; - pqueue.erase(i); - pqueue.push_front(border); - index[border] = pqueue.begin(); - } else { - while(flush && ram_used > ram_max) { - if(!pqueue.size()) { - cerr << "Ram used: " << ram_used << " ram max: " << ram_max << endl; - } - assert(pqueue.size()); - unsigned int to_flush = pqueue.back(); - pqueue.pop_back(); - index.erase(to_flush); - FlushBorder(to_flush); - } - entry.links = GetRegion(entry.start, entry.size); - pqueue.push_front(border); - index[border] = pqueue.begin(); - ram_used += entry.size; - } - return entry; -} -//TODO Change when remving borderentry class. -void BorderServer::ResizeBorder(unsigned int border, unsigned int used) { - assert(border < size()); - Border &entry = GetBorder(border); - if(used <= entry.size) { - entry.used = used; - return; - } - - int capacity = used; - if(capacity < entry.size * 2) - capacity = entry.size * 2; - - unsigned int newstart = Length()/sizeof(Link); - Redim((int64)(newstart + capacity) * (int64)sizeof(Link)); - Link *newlinks = new Link[capacity]; - ram_used += (capacity - entry.size); - - if(entry.used > 0) { - assert(entry.links); - memcpy(newlinks, entry.links, entry.used * sizeof(Link)); - delete []entry.links; - entry.links = NULL; - } - assert(entry.links == NULL); - entry.links = newlinks; - entry.start = newstart; - entry.size = capacity; - entry.used = used; -} - -void BorderServer::FlushBorder(unsigned int border) { - Border &entry = operator[](border); - //assert(entry.links); - if(entry.size && !MFile::IsReadOnly()) { //write back patch - MFile::SetPosition((int64)entry.start * (int64)sizeof(Link)); - MFile::WriteBuffer(entry.links, entry.used * (int64)sizeof(Link)); - } - if(entry.links) - delete [](entry.links); - entry.links = NULL; - ram_used -= entry.size; -} - -Link *BorderServer::GetRegion(unsigned int start, unsigned int size) { - if(size == 0) return NULL; - SetPosition((int64)start * (int64)sizeof(Link)); - Link *buf = new Link[size]; - assert(buf); - ReadBuffer(buf, (int64)size * (int64)sizeof(Link)); - return buf; -} - -bool BorderServer::LoadHeader() { - unsigned int magic; - ReadBuffer(&magic, sizeof(unsigned int)); - if(magic != 0x3042584e) { //NXB0 - cerr << "Invalid magic. Not a nxs file\n"; - return false; - } - ReadBuffer(&offset, sizeof(int64)); - return true; -} - -void BorderServer::SaveHeader() { - unsigned int magic = 0x3042584e; // NXB0 - WriteBuffer(&magic, sizeof(unsigned int)); - WriteBuffer(&offset, sizeof(int64)); -} diff --git a/apps/nexus/borderserver.h b/apps/nexus/borderserver.h deleted file mode 100644 index c47b72b5..00000000 --- a/apps/nexus/borderserver.h +++ /dev/null @@ -1,77 +0,0 @@ -/**************************************************************************** -* 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 $ -Revision 1.5 2005/02/08 12:43:03 ponchio -Added copyright - -****************************************************************************/ - -#ifndef NXS_BORDERSERVER_H -#define NXS_BORDERSERVER_H - -#include -#include - -#include "index_file.h" -#include "border.h" - - -/*nell'header ci sta scritto solo: - spazio riservato: 2 * sizeof(Link); - magic: nxb0 (4 bytes) - offset: (int64) */ - -namespace nxs { - -class BorderServer: public IndexFile { - public: - BorderServer(): ram_max(1000000), ram_used(0) {} - ~BorderServer() { Close(); } - bool Create(const std::string &file); - bool Load(const std::string &file, bool readonly = true); - void Close(); - void Flush(); - - void AddBorder(unsigned short size, unsigned int used = 0); - Border &GetBorder(unsigned int border, bool flush = true); - void ResizeBorder(unsigned int border, unsigned int size); - - unsigned int ram_max; - unsigned int ram_used; - protected: - std::list pqueue; - std::map::iterator> index; - - bool LoadHeader(); - void SaveHeader(); - - void FlushBorder(unsigned int border); - Link *GetRegion(unsigned int start, unsigned int size); //size in links. -}; - -} - -#endif diff --git a/apps/nexus/crude.cpp b/apps/nexus/crude.cpp deleted file mode 100644 index 23814db2..00000000 --- a/apps/nexus/crude.cpp +++ /dev/null @@ -1,147 +0,0 @@ -/**************************************************************************** -* 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 $ -Revision 1.3 2004/11/30 22:50:30 ponchio -Level 0. - -Revision 1.2 2004/07/02 13:08:11 ponchio -Changed extension to .cri, crv, crf - -Revision 1.1 2004/06/24 14:32:45 ponchio -Moved from wrap/nexus - -Revision 1.1 2004/06/22 15:31:54 ponchio -Created - - -****************************************************************************/ - -#include -#include "crude.h" - -using namespace std; -using namespace vcg; -using namespace nxs; - - -Crude::~Crude() { - if(fp) - Close(); -} - -bool Crude::Create(const std::string &file, unsigned int nv, unsigned int nf) { - if(!vert.Create(file + ".crv")) return false; - if(!face.Create(file + ".crf")) return false; - - fp = fopen((file + ".cri").c_str(), "wb+"); - if(!fp) return false; - Resize(nv, nf); - return true; -} - -bool Crude::Load(const std::string &file, bool rdonly) { - if(!vert.Load(file + ".crv", rdonly)) return false; - if(!face.Load(file + ".crf", rdonly)) return false; - - fp = fopen((file + ".cri").c_str(), "rb+"); - if(!fp) return false; - fread(&nvert, sizeof(unsigned int), 1, fp); - fread(&nface, sizeof(unsigned int), 1, fp); - fread(&box, sizeof(Box3f), 1, fp); - return true; -} -void Crude::Close() { - vert.Close(); - face.Close(); - rewind(fp); - fwrite(&nvert, sizeof(unsigned int), 1, fp); - fwrite(&nface, sizeof(unsigned int), 1, fp); - fwrite(&box, sizeof(Box3f), 1, fp); - fclose(fp); - fp = NULL; -} - -void Crude::Resize(unsigned int nv, unsigned int nf) { - nvert = nv; - nface = nf; - vert.Resize(nv); - face.Resize(nf); -} - -unsigned int Crude::Vertices() { - return nvert; -} -unsigned int Crude::Faces() { - return nface; -} - -void Crude::SetVertex(unsigned int i, Point3f &f) { - Point3f &p = vert[i]; - p[0] = f[0]; - p[1] = f[1]; - p[2] = f[2]; -} - -void Crude::SetVertex(unsigned int i, float *f) { - Point3f &p = vert[i]; - p[0] = f[0]; - p[1] = f[1]; - p[2] = f[2]; -} - -Point3f Crude::GetVertex(unsigned int i) { - return vert[i]; -} -Crude::Face Crude::GetFace(unsigned int i) { - return face[i]; -} -void Crude::SetFace(unsigned int i, Face &f) { - Face &ff = face[i]; - ff[0] = f[0]; - ff[1] = f[1]; - ff[2] = f[2]; -} -void Crude::SetFace(unsigned int i, unsigned int *f) { - Face &ff = face[i]; - ff[0] = f[0]; - ff[1] = f[1]; - ff[2] = f[2]; -} - -vcg::Point3f Crude::GetBari(unsigned int i) { - Point3f bari(0, 0, 0); - Face &f = face[i]; - for(int k = 0; k < 3; k++) - bari += vert[f[k]]; - bari /= 3; - return bari; -} - -vcg::Box3f &Crude::GetBox() { - return box; -} - - diff --git a/apps/nexus/crude.h b/apps/nexus/crude.h deleted file mode 100644 index a286a492..00000000 --- a/apps/nexus/crude.h +++ /dev/null @@ -1,129 +0,0 @@ -/**************************************************************************** -* 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 $ -Revision 1.5 2004/11/30 22:50:30 ponchio -Level 0. - -Revision 1.4 2004/07/05 15:49:38 ponchio -Windows (DevCpp, mingw) port. - -Revision 1.3 2004/07/04 15:25:33 ponchio -Backup (work in progress) - -Revision 1.2 2004/06/25 16:47:13 ponchio -Various debug - -Revision 1.1 2004/06/24 14:32:45 ponchio -Moved from wrap/nexus - -Revision 1.1 2004/06/22 15:31:40 ponchio -Created - - -****************************************************************************/ - -#ifndef NXS_CRUDE_H -#define NXS_CRUDE_H - - -#include -#include -#include - -#include "vfile.h" - -namespace nxs { - -class Crude { -public: - - struct Face { - Face() {} - Face(unsigned int a, unsigned int b, unsigned int c) { - v[0] = a; v[1] = b; v[2] = c; - } - unsigned int v[3]; - unsigned int &operator[](int k) { return v[k]; } - unsigned int *ptr() { return v; } - }; - - VFile vert; - VFile face; - - Crude(): fp(NULL), nvert(0), nface(0) {} - ~Crude(); - - bool Create(const std::string &file, unsigned int nvert = 0, - unsigned int nface = 0); - bool Load(const std::string &file, bool rdonly = false); - void Close(); - void Resize(unsigned int nvert, unsigned int nface); - - unsigned int Vertices(); - unsigned int Faces(); - - vcg::Point3f GetVertex(unsigned int i); - void SetVertex(unsigned int i, vcg::Point3f &p); - void SetVertex(unsigned int i, float *p); - - Face GetFace(unsigned int i); - void SetFace(unsigned int i, Face &f); - void SetFace(unsigned int i, unsigned int *f); - - vcg::Point3f GetBari(unsigned int i); - - vcg::Box3f &GetBox(); - - /*template void Export(MESH &mesh) { - MESH::VertexType v; - v.ClearFlags(); - for(unsigned int i = 0; i < on the top - -Revision 1.4 2004/10/19 04:23:57 ponchio -Added trackball. - -Revision 1.3 2004/09/17 15:25:09 ponchio -First working (hopefully) release. - -Revision 1.2 2004/07/05 15:49:39 ponchio -Windows (DevCpp, mingw) port. - -Revision 1.1 2004/07/04 15:30:00 ponchio -Changed directory structure. - -Revision 1.3 2004/07/02 13:03:34 ponchio -*** empty log message *** - -Revision 1.2 2004/07/01 21:33:46 ponchio -Added remap reading. - -Revision 1.1 2004/06/23 00:10:38 ponchio -Created - - -****************************************************************************/ - -#include -using namespace std; - -#include - -#include - -#ifdef WIN32 -#include -#endif - -#include -#include - -#include - -using namespace vcg; -using namespace nxs; - -bool fullscreen = false; -int width =1024; -int height = 768; - -SDL_Surface *screen = NULL; - -bool init() { - - if(SDL_Init(SDL_INIT_VIDEO) != 0) { - return false; - } - - const SDL_VideoInfo *info = SDL_GetVideoInfo(); - int bpp = info->vfmt->BitsPerPixel; - - SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5); - SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); - - int flags = SDL_OPENGL; - if(fullscreen) - flags |= SDL_FULLSCREEN; - - screen = SDL_SetVideoMode(width, height, bpp, flags); - if(!screen) { - return false; - } - - SDL_WM_SetCaption("Crudeview", "Crudeview"); - - - glDisable(GL_DITHER); - glShadeModel(GL_SMOOTH); - glHint( GL_FOG_HINT, GL_NICEST ); - glEnable(GL_DEPTH_TEST); - glDepthFunc( GL_LEQUAL ); - glDisable(GL_LIGHTING); - - glEnableClientState(GL_VERTEX_ARRAY); - return true; -} - - - - -int main(int argc, char *argv[]) { - - if(argc < 2) { - cerr << "Usage: " << argv[0] << " \n"; - return -1; - } - - Crude crude; - if(!crude.Load(argv[1])) { - cerr << "Could not load crude file: " << argv[1] << endl; - return -1; - } - Box3f box = crude.GetBox(); - - bool vremap = false; - bool fremap = false; - - VFile face_remap; - if(face_remap.Load(argv[1] + string(".rfm"), true)) { - cerr << "Found face remap.\n"; - fremap = true; - } else { - cerr << "Face remap not found.\n"; - } - - if(!init()) { - cerr << "Could not init SDL window\n"; - return -1; - } - - Trackball track; - - glClearColor(0, 0, 0, 0); - glEnable(GL_LIGHTING); - glEnable(GL_LIGHT0); - glEnable(GL_NORMALIZE); - glEnable(GL_COLOR_MATERIAL); - - bool redraw = false; - bool show_normals = true; - int quit = 0; - SDL_Event event; - int x, y; - float alpha = 0; - while( !quit ) { - bool first = true; - SDL_WaitEvent(&event); - while( first || SDL_PollEvent( &event ) ){ - first = false; - switch( event.type ) { - case SDL_QUIT: quit = 1; break; - case SDL_KEYDOWN: - switch(event.key.keysym.sym) { - case SDLK_RCTRL: - case SDLK_LCTRL: - track.ButtonDown(Trackball::KEY_CTRL); break; - case SDLK_q: exit(0); break; - } - break; - case SDL_MOUSEBUTTONDOWN: - x = event.button.x; - y = event.button.y; - if(event.button.button == SDL_BUTTON_WHEELUP) - track.MouseWheel(1); - else if(event.button.button == SDL_BUTTON_WHEELDOWN) - track.MouseWheel(-1); - else if(event.button.button == SDL_BUTTON_LEFT) - track.MouseDown(x, y, Trackball::BUTTON_LEFT); - else if(event.button.button == SDL_BUTTON_RIGHT) - track.MouseDown(x, y, Trackball::BUTTON_RIGHT); - break; - case SDL_MOUSEBUTTONUP: - x = event.button.x; - y = event.button.y; - if(event.button.button == SDL_BUTTON_LEFT) - track.MouseUp(x, y, Trackball::BUTTON_LEFT); - else if(event.button.button == SDL_BUTTON_RIGHT) - track.MouseUp(x, y, Trackball::BUTTON_RIGHT); - break; - case SDL_MOUSEMOTION: - while(SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_MOUSEMOTIONMASK)); - x = event.motion.x; - y = event.motion.y; - track.MouseMove(x, y); - break; - default: break; - } - redraw = true; - } - - if(!redraw) continue; - redraw = false; - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - gluPerspective(60, 1, 0.1, 100); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - gluLookAt(0,0,3, 0,0,0, 0,1,0); - - track.GetView(); - track.Apply(); - - float scale = 3/box.Diag(); - glScalef(0.4, 0.4, 0.4); - glScalef(scale, scale, scale); - Point3f center = box.Center(); - glTranslatef(-center[0], -center[1], -center[2]); - - glColor3f(0, 1, 0); - - glBegin(GL_TRIANGLES); - - for(unsigned int i = 0;i < crude.Faces(); i++) { - Crude::Face face = crude.GetFace(i); - if(fremap) { - unsigned int val = face_remap[i]; - glColor3ub((val * 27)%255, (val * 37)%255, (val * 87)%255); - } - Point3f p0 = crude.GetVertex(face[0]); - Point3f p1 = crude.GetVertex(face[1]); - Point3f p2 = crude.GetVertex(face[2]); - - if(show_normals) { - Point3f n = ((p1 - p0) ^ (p2 - p0)); - glNormal3f(n[0], n[1], n[2]); - } - glVertex3f(p0[0], p0[1], p0[2]); - glVertex3f(p1[0], p1[1], p1[2]); - glVertex3f(p2[0], p2[1], p2[2]); - - } - glEnd(); - - SDL_GL_SwapBuffers(); - } - - SDL_Quit(); - return -1; -} - - diff --git a/apps/nexus/decimate.cpp b/apps/nexus/decimate.cpp deleted file mode 100644 index be20552e..00000000 --- a/apps/nexus/decimate.cpp +++ /dev/null @@ -1,265 +0,0 @@ -#include - -// stuff to define the mesh -#include -#include -#include -#include -#include - -#include - -#include -#include - -#include - -#include "vpartition.h" -#include "fragment.h" - -#include "decimate.h" -#include - -using namespace vcg; -using namespace tri; -using namespace nxs; -using namespace std; - -class MyEdge; -class MyFace; -class MyVertex: - public vcg::VertexAFVMVNf { -public: - ScalarType w; - vcg::math::Quadric q; - ScalarType & W() { return w; } -}; - -struct MyEdge: public Edge { - inline MyEdge():Edge(){UberFlags()=0;} - inline MyEdge(MyVertex* a,MyVertex* b):Edge(a,b){ - UberFlags()=0;} -}; -class MyFace : public vcg::FaceAV {}; - -class MyMesh: - public vcg::tri::TriMesh< std::vector, std::vector > {}; - -class MyTriEdgeCollapse: public vcg::tri::TriEdgeCollapseQuadric< MyMesh, MyTriEdgeCollapse > { - public: - typedef vcg::tri::TriEdgeCollapseQuadric< MyMesh, MyTriEdgeCollapse > TECQ; - typedef TECQ::EdgeType EdgeType; - inline MyTriEdgeCollapse( EdgeType p, int i) :TECQ(p,i){} -}; - -float Cluster(MyMesh &mesh, unsigned int target_faces); -float Quadric(MyMesh &mesh, unsigned int target_faces); - -float nxs::Decimate(Decimation mode, - unsigned int target_faces, - vector &newvert, - vector &newface, - vector &newbord) { - - for(unsigned int i = 0; i < newface.size(); i+= 3) { - assert(newface[i] != newface[i+1]); - assert(newface[i] != newface[i+2]); - assert(newface[i+1] != newface[i+2]); - } - - MyMesh mesh; - - //build mesh - for(unsigned int i = 0; i < newvert.size(); i++) { - MyVertex vertex; - vertex.ClearFlags(); - vertex.P() = newvert[i]; - mesh.vert.push_back(vertex); - } - mesh.vn = mesh.vert.size(); - - for(unsigned int i = 0; i < newface.size(); i+=3) { - MyFace face; - face.ClearFlags(); - for(int k = 0; k < 3; k++) { - assert(newface[i+k] < mesh.vert.size()); - face.V(k) = &mesh.vert[newface[i+k]]; - } - mesh.face.push_back(face); - } - mesh.fn = mesh.face.size(); - - //mark borders - for(unsigned int i = 0; i < newbord.size(); i++) - mesh.vert[newbord[i].start_vert].ClearW(); - - float error; - switch(mode) { - case CLUSTER: error = Cluster(mesh, target_faces); break; - case QUADRIC: error = Quadric(mesh, target_faces); break; - default: cerr << "Unknown simplification mode: " << mode << endl; - exit(0); - } - - newvert.clear(); - newface.clear(); - - unsigned int totvert = 0; - vector vert_remap; - vert_remap.resize(mesh.vert.size(), -1); - for(unsigned int i = 0; i < mesh.vert.size(); i++) { - if(mesh.vert[i].IsD()) continue; - newvert.push_back(mesh.vert[i].cP()); - vert_remap[i] = totvert++; - } - - MyMesh::VertexPointer vert_start = &mesh.vert[0]; - for(unsigned int i = 0; i < mesh.face.size(); i++) { - MyFace &face = mesh.face[i]; - if(face.IsD()) continue; - for(int k = 0; k < 3; k++) - newface.push_back(vert_remap[face.V(k) - vert_start]); - } - - for(unsigned int i = 0; i < newbord.size(); i++) { - unsigned int &v = newbord[i].start_vert; - assert(vert_remap[v] != -1); - v = vert_remap[v]; - } - - return error; -} - - -float Quadric(MyMesh &mesh, unsigned int target_faces) { - vcg::tri::UpdateTopology::VertexFace(mesh); - vcg::tri::UpdateBounding::Box(mesh); - - vcg::LocalOptimization DeciSession(mesh); - - MyTriEdgeCollapse::SetDefaultParams(); - - DeciSession.Init(); - - DeciSession.SetTargetSimplices(target_faces); - DeciSession.DoOptimization(); - - float error = 0; - int count = 0; - for(unsigned int i = 0; i < mesh.face.size(); i++) { - MyFace &face = mesh.face[i]; - if(face.IsD()) continue; - for(int k = 0; k < 3; k++) { - error += (face.cV(k)->cP() - face.cV((k+1)%3)->cP()).Norm(); - count++; - } - } - error /= count; - return error; - return 0; -} - -float Cluster(MyMesh &mesh, unsigned int target_faces) { - unsigned int starting = mesh.vn; - - unsigned int nseeds = target_faces/2; -#ifndef NDEBUG - if(nseeds >= mesh.vert.size()) { - cerr << "Strange! nseeds > vert.size(): " << nseeds - << " >= "<< mesh.vert.size() << endl; - } -#endif - - vector remap; - - VPartition part; - for(unsigned int i = 0; i < mesh.vert.size(); i++) { - const Point3f &p = mesh.vert[i].cP(); - if(!mesh.vert[i].IsW()) { - part.push_back(p); - remap.push_back(i); - nseeds--; - } - } - unsigned int nborder = part.size(); - //Todo I should supersample before - while(nseeds > 0 && part.size() < mesh.vn) { - unsigned int i = rand() % mesh.vert.size(); - if(mesh.vert[i].IsW() && !mesh.vert[i].IsV()) { - const Point3f &p = mesh.vert[i].cP(); - part.push_back(p); - mesh.vert[i].SetV(); - remap.push_back(i); - nseeds--; - } - } - if(part.size() == 0) { - cerr << "WARNING: could not simplyfiy... everything was border.\n"; - return 0; //everything is locked... - } - part.Init(); - - - vector centroid; - vector count; - for(unsigned int i = 0; i < 3; i++) { - centroid.clear(); - centroid.resize(mesh.vert.size(), Point3f(0, 0, 0)); - count.clear(); - count.resize(mesh.vert.size(), 0); - for(unsigned int i = 0; i < mesh.vert.size(); i++) { - unsigned int target = part.Locate(mesh.vert[i].cP()); - centroid[target] += mesh.vert[i].cP(); - count[target]++; - } - for(unsigned int i = nborder; i < part.size(); i++) { - if(count[i] > 0) - part[i] = centroid[i]/count[i]; - } - } - - for(unsigned int i = nborder; i < part.size(); i++) { - assert(mesh.vert[remap[i]].IsV()); - mesh.vert[remap[i]].P() = part[i]; - } - - float error = 0; - //rimappiamo le facce..... - for(unsigned int i = 0; i < mesh.face.size(); i++) { - MyFace &face = mesh.face[i]; - for(int k = 0; k < 3; k++) { - unsigned int target = part.Locate(face.V(k)->cP()); - assert(target < remap.size()); - assert(remap[target] < mesh.vert.size()); - MyVertex &vert = mesh.vert[remap[target]]; - - float dist = Distance(vert.cP(), face.V(k)->cP()); - if(dist > error) error = dist; - - face.V(k) = | - } - } - - for(unsigned int i = 0; i < mesh.face.size(); i++) { - MyFace &face = mesh.face[i]; - assert(!face.IsD()); - for(int k = 0; k < 3; k++) { - assert(face.cV(k)->IsV() || !face.cV(k)->IsW()); - } - if(face.cV(0) == face.cV(1) || - face.cV(0) == face.cV(2) || - face.cV(1) == face.cV(2)) { - face.SetD(); - mesh.fn--; - } - } - - for(unsigned int i = 0; i < mesh.vert.size(); i++) - if(!mesh.vert[i].IsV() && mesh.vert[i].IsW()) { - mesh.vert[i].SetD(); - mesh.vn--; - } - return error; - -} - diff --git a/apps/nexus/decimate.h b/apps/nexus/decimate.h deleted file mode 100644 index cd55a011..00000000 --- a/apps/nexus/decimate.h +++ /dev/null @@ -1,49 +0,0 @@ -/**************************************************************************** -* 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_DECIMATE_H -#define NXS_DECIMATE_H - -#include -#include "border.h" -#include "fragment.h" -#include -namespace nxs { - - enum Decimation { QUADRIC, CLUSTER }; - class BigLink; - float Decimate(Decimation mode, - unsigned int target_faces, - std::vector &newvert, - std::vector &newface, - std::vector &newbord); - -} - -#endif diff --git a/apps/nexus/exploder.cpp b/apps/nexus/exploder.cpp deleted file mode 100644 index b5126591..00000000 --- a/apps/nexus/exploder.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include - -#include "nexus.h" - -using namespace nxs; -using namespace vcg; -using namespace std; - -Point3f explode(Point3f &v, Point3f ¢er, Point3f &dir, float mag) { - v = ((v - center) * mag + center) + dir; - return v; -} - -int main(int argc, char *argv[]) { - if(argc < 3) { - cerr << "Usage: " << argv[0] << " [factor]\n"; - return -1; - } - string input = argv[1]; - string output = argv[2]; - float factor = 2; - if(argc == 4) { - factor = atoi(argv[3]); - if(factor == 0) { - cerr << "Invalid factor: " << argv[3] << endl; - return -1; - } - } - Nexus in; - if(!in.Load(input, true)) { - cerr << "Could not load nexus: " << input << endl; - return -1; - } - - Nexus out; - if(!out.Create(output, in.signature, in.chunk_size)) { - cerr << "Could not create nexus: " << output << endl; - return -1; - } - - out.sphere = in.sphere; - out.history = in.history; - for(unsigned int i = 0; i < in.index.size(); i++) { - unsigned int patch = i; - Nexus::PatchInfo &src_entry = in.index[patch]; - Patch &src_patch = in.GetPatch(patch); - Border src_border = in.GetBorder(patch); - - out.AddPatch(src_entry.nvert, src_entry.nface, src_border.Available()); - - Nexus::PatchInfo &dst_entry = out.index[patch]; - - Patch dst_patch = out.GetPatch(patch); - - Point3f dir = src_entry.sphere.Center() - in.sphere.Center(); - dir.Normalize(); - dir *= 10 * src_entry.error; - - memcpy(dst_patch.VertBegin(), src_patch.VertBegin(), - src_patch.nv * sizeof(Point3f)); - - Point3f *ptr = dst_patch.VertBegin(); - for(int i = 0; i < dst_patch.nv; i++) { - ptr[i] = explode(ptr[i], src_entry.sphere.Center(), dir, - 0.5 *src_entry.error); - } - - - if(in.signature & NXS_STRIP) { - memcpy(dst_patch.FaceBegin(), src_patch.FaceBegin(), - src_patch.nf * sizeof(unsigned short)); - } else { - memcpy(dst_patch.FaceBegin(), src_patch.FaceBegin(), - src_patch.nf * sizeof(unsigned short) * 3); - } - - if((in.signature & NXS_COLORS) && (out.signature & NXS_COLORS)) - memcpy(dst_patch.ColorBegin(), src_patch.ColorBegin(), - src_patch.nv * sizeof(unsigned int)); - - if((in.signature & NXS_NORMALS_SHORT) && - (out.signature & NXS_NORMALS_SHORT)) - memcpy(dst_patch.Norm16Begin(), src_patch.Norm16Begin(), - src_patch.nv * sizeof(short)*4); - - //reordering - //WATCH OUT BORDERS! - // Reorder(out.signature, dst_patch); - //copying entry information; - dst_entry.sphere = src_entry.sphere; - dst_entry.error = src_entry.error; - - //adding borders. - for(unsigned int i = 0; i < src_border.Size(); i++) { - Link &link = src_border[i]; - if(link.IsNull()) continue; - assert(link.end_patch < in.index.size()); - } - Border dst_border = out.GetBorder(patch); - out.borders.ResizeBorder(patch, src_border.Size()); - memcpy(dst_border.Start(), src_border.Start(), - src_border.Size() * sizeof(Link)); - } - in.Close(); - out.Close(); - return 0; -} diff --git a/apps/nexus/extraction.cpp b/apps/nexus/extraction.cpp deleted file mode 100644 index c015cf6a..00000000 --- a/apps/nexus/extraction.cpp +++ /dev/null @@ -1,485 +0,0 @@ -/**************************************************************************** -* 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 $ -Revision 1.12 2005/03/02 10:40:17 ponchio -Extraction rewrittten (to fix recusive problems). - -Revision 1.11 2005/02/20 19:49:44 ponchio -cleaning (a bit more). - -Revision 1.10 2005/02/20 18:07:00 ponchio -cleaning. - -Revision 1.9 2005/02/20 00:43:23 ponchio -Less memory x extraction. (removed frags) - -Revision 1.8 2005/02/19 16:22:45 ponchio -Minor changes (visited and Cell) - -Revision 1.7 2005/02/10 09:18:20 ponchio -Statistics. - -Revision 1.6 2005/02/08 12:43:03 ponchio -Added copyright - - -****************************************************************************/ - -#include "extraction.h" -#include "metric.h" -#include "nexus.h" - -#include - -using namespace std; -using namespace nxs; - - /* Updateing strategy: - if i can refine (not at leaves, - have draw and extr buffer, - not past target_error) - i try to refine BUT - i can fail because i finish some buffer - (then i put the operation back on the stack) - if i have finished disk i should just quit - if i cannot refine i consider coarsening: - i need 1) not be at root (eheh) - 2) have finished draw and extr buffer - (unless i am at error < target so i want to coarse - 3) do not make global error worse - (unless it is error < target_error...) - a stability term is added (1.1) so that we do not flip - nodes in and out quickly - i try to coarse BUT - i can fail because i need disk - (then i put the operation back on the stack) - if i cannt coarse i just quit - */ - - - -Extraction::Extraction(): target_error(4.0f), extr_max(10000), - draw_max(10000), disk_max(100) { - metric = new FrustumMetric; -} - -Extraction::~Extraction() { - if(metric) delete metric; -} - -void Extraction::SetMetric(Metric *m) { - if(metric) - delete metric; - metric = m; -} - -void Extraction::Extract(Nexus *_mt) { - mt = _mt; - root = mt->history.Root(); - sink = root + (mt->history.n_nodes()-1); - - //clear statistics - extr_used = draw_used = disk_used = 0; - - //first we clear the visited flags - visited.clear(); - visited.resize(mt->history.n_nodes(), false); - visible.clear(); - visible.resize(mt->size(), true); - node_errors.clear(); - node_errors.resize(mt->history.n_nodes(), -1); - - front.clear(); - - Visit(root); - - while(front.size()) { - pop_heap(front.begin(), front.end()); - HeapNode hnode = front.back(); - front.pop_back(); - - Node *node = hnode.node; - if(Visited(node)) continue; - - if(Expand(hnode)) - Visit(node); - } - Select(); - draw_size = selected.size(); -} - -void Extraction::Init() { - //I want to add all coarsable nodes - //and all refinable node (being careful about recursive dependencies) - for(Node *node = root; node != sink; node++) { - if(!Visited(node)) continue; - if(node != root && CanCoarse(node)) - back.push_back(HeapNode(node, GetNodeError(node))); - - for(Node::iterator n = node->out_begin; n != node->out_end; n++) { - Node *child = n->node; - if(Visited(child)) continue; - if(node_errors[child - root] != -1) continue; //already visited - - float *error = GetNodeError(child); - if(CanRefine(node)) // TODO? && error > target_error - front.push_back(HeapNode(child, error)); - - if(*error > max_error) max_error = *error; - } - } - - //recursively fix error - for(Node *node = root; node != sink; node++) - if(node_errors[node - root] != -1) - SetError(node, node_errors[node-root]); - - - //estimate cost of all the cut arcs (i need the visible info) - Cost cost; - for(Node *node = root; node != sink; node++) { - if(!Visited(node)) continue; - - for(Node::iterator n = node->out_begin; n != node->out_end; n++) { - Link &link = *n; - if(Visited((*n).node)) continue; - for(unsigned int patch = link.begin; patch != link.end; patch++) { - Entry &entry = (*mt)[patch]; - cost.extr += entry.ram_size; - if(Visible(patch)) cost.draw += entry.ram_size; - if(!entry.patch) cost.disk += entry.disk_size; - } - } - } - - make_heap(front.begin(), front.end()); - make_heap(back.begin(), back.end(), greater()); - - extr_used = cost.extr; - draw_used = cost.draw; - disk_used = cost.disk; -} - -void Extraction::Update(Nexus *_mt) { - mt = _mt; - root = mt->history.Root(); - sink = mt->history.Sink(); - - if(!visited.size()) { - visited.resize(mt->history.n_nodes(), false); - SetVisited(root, true); - } - visible.clear(); - visible.resize(mt->size(), true); - node_errors.clear(); - node_errors.resize(mt->history.n_nodes(), -1); - - front.clear(); - back.clear(); - - max_error = -1; - - Init(); - - bool can_refine = true; - - while(1) { - while(can_refine) { //we have available budget - if(!front.size()) break; //we are at max level - if(*front[0].error < target_error) break; //already at target_error - - max_error = *front[0].error; - pop_heap(front.begin(), front.end()); - HeapNode hnode = front.back(); - front.pop_back(); - - if(!Visited(hnode.node) && CanRefine(hnode.node)) { - if(!Refine(hnode)) { - can_refine = false; - front.push_back(hnode); - push_heap(front.begin(), front.end()); - } - } - } - - if(!back.size()) //nothing to coarse (happen only on extr_max < root.extr) - break; - - if(*back.front().error >= target_error && - (!front.size() || - (*back.front().error * 1.4) >= *front.front().error)) - break; - - pop_heap(back.begin(), back.end(), greater()); - HeapNode hnode = back.back(); - back.pop_back(); - - if(Visited(hnode.node) && //not already coarsed - CanCoarse(hnode.node) && //all children !visited - !Coarse(hnode)) { //no more disk - back.push_back(hnode); - push_heap(back.begin(), back.end(), greater()); - break; - } - can_refine = true; - } - - Select(); - draw_size = selected.size(); - - //Preloading now - for(unsigned int i = 0; i < 1000; i++) { - if(!front.size() && !back.size()) break; - if((i%2) && front.size()) { - pop_heap(front.begin(), front.end()); - HeapNode hnode = front.back(); - Node *node = hnode.node; - front.pop_back(); - Node::iterator l; - for(l = node->out_begin; l != node->out_end; l++) { - Link &link = (*l); - for(unsigned int k = link.begin; k != link.end; k++) { - selected.push_back(Item(k, i)); - } - } - } else if(back.size()) { - pop_heap(back.begin(), back.end(), greater()); - HeapNode hnode = back.back(); - Node *node = hnode.node; - back.pop_back(); - - for(Node::iterator l = node->in_begin; l != node->in_end; l++) { - Link &link = (*l); - for(unsigned int k = link.begin; k != link.end; k++) { - selected.push_back(Item(k, i)); - } - } - } - } -} - - -float *Extraction::GetNodeError(Node *node) { - float &maxerror = node_errors[node-root]; - for(Node::iterator i = node->in_begin; i != node->in_end; i++) { - Link &link = *i; - for(unsigned int p = link.begin; p != link.end; p++) { - Entry &entry = (*mt)[p]; - bool visible; - float error = metric->GetError(entry, visible); - // cerr << "Error for patch: " << p << " -> " << error << endl; - if(error > maxerror) maxerror = error; - SetVisible(p, visible); - } - } - return &maxerror; -} - -bool Extraction::Refine(HeapNode hnode) { - - Node *node = hnode.node; - - Cost cost; - Diff(node, cost); - - if(disk_used + cost.disk > disk_max || - extr_used + cost.extr > extr_max || - draw_used + cost.draw > draw_max) - return false; - - extr_used += cost.extr; - draw_used += cost.draw; - disk_used += cost.disk; - - SetVisited(node, true); - - //now add to the front children (unless sink node) - - for(Node::iterator i = node->out_begin; i != node->out_end; i++) { - Link &link = *i; - if(link.node == sink) continue; - - float *error = &node_errors[link.node - root]; - if(*error == -1) - error = GetNodeError(link.node); - if(*hnode.error < *error) *hnode.error = *error; - //TODO if(maxerror > target_error) - if(CanRefine((*i).node)) { - front.push_back(HeapNode((*i).node, error)); - push_heap(front.begin(), front.end()); - } - } - - back.push_back(hnode); - push_heap(back.begin(), back.end(), greater()); - return true; -} - -bool Extraction::Coarse(HeapNode hnode) { - Node *node = hnode.node; - - Cost cost; - Diff(node, cost); - - extr_used -= cost.extr; - draw_used -= cost.draw; - disk_used -= cost.disk; - - if(disk_used > disk_max) return false; - - SetVisited(node, false); - - //now add to the back parents (unless root node) - for(Node::iterator i = node->in_begin; i != node->in_end; i++) { - Link &link = *i; - if(link.node == root) continue; - - float *error = &node_errors[link.node - root]; - if(*error == -1) - error = GetNodeError(link.node); - if(*error < *hnode.error) *error = *hnode.error; - - if(CanCoarse(link.node)) { - back.push_back(HeapNode(link.node, error)); - push_heap(back.begin(), back.end(), greater()); - } - } - - front.push_back(hnode); - push_heap(front.begin(), front.end()); - return true; -} - -void Extraction::Select() { - selected.clear(); - Node *root = mt->history.Root(); - - Node *nodes = mt->history.nodes; - for(unsigned int i = 0; i < visited.size(); i++) { - if(!visited[i]) continue; - Node &node = nodes[i]; - - Node::iterator n; - for(n = node.out_begin; n != node.out_end; n++) { - unsigned int n_out = (*n).node - root; - if(!visited[n_out]) { - Link &link = *n; - for(unsigned int p = link.begin; p != link.end; p++) { - selected.push_back(Item(p, 0)); - } - } - } - } -} - -void Extraction::Visit(Node *node) { - assert(!Visited(node)); - - SetVisited(node, true); - - for(Node::iterator i = node->in_begin; i != node->in_end; i++) { - if(Visited((*i).node)) continue; - Visit((*i).node); - } - - Cost cost; - Diff(node, cost); - extr_used += cost.extr; - draw_used += cost.draw; - disk_used += cost.disk; - - for(Node::iterator i = node->out_begin; i != node->out_end; i++) { - float maxerror = -1; - Link &link = *i; - for(unsigned int p = link.begin; p != link.end; p++) { - Entry &entry = (*mt)[p]; - bool visible; - float error = metric->GetError(entry, visible); - if(error > maxerror) maxerror = error; - SetVisible(p, visible); - } - //TODO this check may be dangerous for non saturating things... - if(maxerror > target_error) { - node_errors[(*i).node - root] = maxerror; - HeapNode hnode((*i).node, &node_errors[(*i).node - root]); - front.push_back(hnode); - push_heap(front.begin(), front.end()); - } - } -} - -bool Extraction::Expand(HeapNode &node) { - if(extr_used >= extr_max) return false; - if(draw_used >= draw_max) return false; - // if(disk_used >= disk_max) return false; - return *node.error > target_error; -} - -void Extraction::Diff(Node *node, Cost &cost) { - Node::iterator i; - for(i = node->in_begin; i != node->in_end; i++) { - Link &link = *i; - for(unsigned int p = link.begin; p != link.end; p++) { - Entry &entry = (*mt)[p]; - cost.extr -= entry.ram_size; - if(Visible(p)) cost.draw -= entry.ram_size; - if(!entry.patch) cost.disk -= entry.disk_size; - } - } - - for(i = node->out_begin; i != node->out_end; i++) { - Link &link = *i; - for(unsigned int p = link.begin; p != link.end; p++) { - Entry &entry = (*mt)[p]; - cost.extr += entry.ram_size; - if(Visible(p)) cost.draw += entry.ram_size; - if(!entry.patch) cost.disk += entry.disk_size; - } - } -} - - void Extraction::SetError(Node *node, float error) { - for(Node::iterator i = node->in_begin; i != node->in_end; i++) - if(node_errors[(*i).node - root] != -1 && - node_errors[(*i).node - root] < error) { - node_errors[(*i).node - root] = error; - SetError((*i).node, error); - } - } - - bool Extraction::CanRefine(Node *node) { - for(Node::iterator i = node->in_begin; i != node->in_end; i++) - if(!Visited((*i).node)) - return false; - return true; - } - - bool Extraction::CanCoarse(Node *node) { - for(Node::iterator i = node->out_begin; i != node->out_end; i++) - if(Visited((*i).node)) - return false; - return true; - } diff --git a/apps/nexus/extraction.h b/apps/nexus/extraction.h deleted file mode 100644 index 4edcba54..00000000 --- a/apps/nexus/extraction.h +++ /dev/null @@ -1,155 +0,0 @@ -/**************************************************************************** -* 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 $ -Revision 1.10 2005/03/02 10:40:17 ponchio -Extraction rewrittten (to fix recusive problems). - -Revision 1.9 2005/02/20 18:07:01 ponchio -cleaning. - -Revision 1.8 2005/02/19 16:22:45 ponchio -Minor changes (visited and Cell) - -Revision 1.7 2005/02/10 09:18:20 ponchio -Statistics. - -Revision 1.6 2005/02/08 12:43:03 ponchio -Added copyright - - -****************************************************************************/ - -#ifndef NXS_EXTRACTION_H -#define NXS_EXTRACTION_H - -#include -#include - -#include - -#include "history.h" - -namespace nxs { - -class Metric; -class Nexus; - -struct Item { - float error; - unsigned int id; - Item(unsigned int i = 0, float e = 0): id(i), error(e) {} - bool operator<(const Item &item) const { - return error < item.error; - } -}; - -class Extraction { - public: - typedef History::Node Node; - typedef History::Link Link; - - struct Cost { - unsigned int extr; - unsigned int draw; - unsigned int disk; - Cost(): extr(0), draw(0), disk(0) {} - }; - - struct HeapNode { - Node *node; - float *error; - - HeapNode(Node *_node, float *_error): node(_node), error(_error) {} - bool operator<(const HeapNode &node) const { - return *error < *node.error; } - bool operator>(const HeapNode &node) const { - return *error > *node.error; } - }; - - Metric *metric; - float target_error; - - float max_error; //actual error at end of extraction - unsigned int extr_used, extr_max; - unsigned int draw_used, draw_max; - unsigned int disk_used, disk_max; - - std::vector visited; - std::vector visible; - std::vector node_errors; - - std::vector selected; - unsigned int draw_size; //first in selected should be drawn - - // std::vector heap; //no realtime extraxtion TODO (use front) - - - std::vector front; //nodes that i can expand to - std::vector back; //nodes that i can contract - - - Extraction(); - ~Extraction(); - - void Extract(Nexus *mt); - void Update(Nexus *mt); - - bool Visible(unsigned int p) { return visible[p]; } - void SetVisible(unsigned int p, bool v) { visible[p] = v; } - - void SetMetric(Metric *m); - - protected: - - void Select(); - void Visit(Node *node); - - bool Expand(HeapNode &node); - void Diff(Node *node, Cost &cost); - - void Init(); - bool Refine(HeapNode node); - bool Coarse(HeapNode node); - - bool Visited(Node *node) { return visited[node - root]; } - void SetVisited(Node *node, bool v) { visited[node - root] = v; } - - private: - Nexus *mt; - Node *root; - Node *sink; - - //return inbound links max error. remember to update patch visibility - float *GetNodeError(Node *node); - //this look for parent nodes with error and fix it should be < - void SetError(Node *node, float error); - bool CanCoarse(Node *node); - bool CanRefine(Node *node); -}; - - -}//namespace -#endif diff --git a/apps/nexus/file.cpp b/apps/nexus/file.cpp deleted file mode 100644 index d1ca0826..00000000 --- a/apps/nexus/file.cpp +++ /dev/null @@ -1,130 +0,0 @@ -#include "file.h" -#include - -using namespace std; -using namespace nxs; - -bool File::Create(const string &filename) { - size = 0; - readonly = false; -#ifdef WIN32 - fp = CreateFile(filename.c_str(), GENERIC_READ | GENERIC_WRITE, 0, - NULL, CREATE_ALWAYS, 0, NULL); - if(fp == INVALID_HANDLE_VALUE) return false; -#else - fp = fopen(filename.c_str(), "wb+"); - if(!fp) return false; -#endif - return true; -} - -bool File::Load(const string &filename, bool ronly) { - readonly = ronly; -#ifdef WIN32 - fp = CreateFile(filename.c_str(), GENERIC_READ | GENERIC_WRITE, - 0, NULL, OPEN_EXISTING, 0, NULL); - if(fp == INVALID_HANDLE_VALUE) return false; -#else - if(readonly) - fp = fopen(filename.c_str(), "rb"); - else - fp = fopen(filename.c_str(), "rb+"); - if(!fp) return false; -#endif - -#ifdef WIN32 - size = GetFileSize(fp, NULL); -#else - //TODO use stat() - fseek(fp, 0, SEEK_END); - size = ftell(fp); -#endif - return true; -} - -void File::Close() { - if(fp) { -#ifdef WIN32 - CloseHandle(fp); -#else - fclose(fp); -#endif - fp = NULL; - } -} - -void File::Redim(unsigned int elem) { - assert(fp); - assert(!readonly); - if(elem > size) { - -#ifdef WIN32 - LONG zero = 0; - if(INVALID_SET_FILE_POINTER == - SetFilePointer(fp, elem - 1, &zero, FILE_BEGIN)) -#else - if(-1 == fseek(fp, elem - 1, SEEK_SET)) -#endif - assert(0 && "Could not resize"); - - unsigned char a; -#ifdef WIN32 - DWORD tmp; - WriteFile(fp, &a, 1, &tmp, NULL); -#else - fwrite(&a, sizeof(unsigned char), 1, fp); -#endif - } else { - //TODO optimize: we do not need flush for buffers over elem. -#ifndef WIN32 - int fd = fileno(fp); - ftruncate(fd, elem); -#else - LONG zero = 0; - SetFilePointer(fp, elem, &zero, FILE_BEGIN); - SetEndOfFile(fp); -#endif - } - size = elem; -} - -void File::SetPosition(unsigned int pos) { -#ifdef WIN32 - LONG zero = 0; - SetFilePointer(fp, pos, &zero, FILE_BEGIN); -#else - fseek(fp, pos, SEEK_SET); -#endif -} - -void File::ReadBuffer(void *data, unsigned int sz) { -#ifdef WIN32 - DWORD tmp; - ReadFile(fp, data, sz, &tmp, NULL); - if(tmp != sz) - assert(0 && "Could not read"); -#else - if(sz != fread(data, 1, sz, fp)) - assert(0 && "Could not read"); -#endif -} - -void File::WriteBuffer(void *data, unsigned int sz) { - assert(!readonly); -#ifdef WIN32 - DWORD tmp; - WriteFile(fp, data, sz, &tmp, NULL); - assert(tmp == sz); -#else - if(sz != fwrite(data, 1, sz, fp)) - assert(0 && "Could not write"); -#endif -} - -void File::Delete(const string &filename) { -#ifdef WIN32 - DeleteFile(filename.c_str()); -#else - unlink(filename.c_str()); -#endif -} diff --git a/apps/nexus/file.h b/apps/nexus/file.h deleted file mode 100644 index e13a3a00..00000000 --- a/apps/nexus/file.h +++ /dev/null @@ -1,58 +0,0 @@ -#ifndef NXS_FILE_H -#define NXS_FILE_H - -//TODO move includes in cpp - -#ifdef WIN32 -#ifndef _WINDOWS_ -#define _WINSOCKAPI_ -#include -#endif -#else -#include -#endif - -#include -#include - -namespace nxs { - -class File { - public: - - File(): fp(NULL) {} - ~File() { Close(); } - - File(const File &file) { - fp = file.fp; - size = file.size; - readonly = file.readonly; - ((File &)file).fp = NULL; - } - bool Create(const std::string &filename); - bool Load(const std::string &filename, bool readonly = false); - void Close(); - - unsigned int Length() { return size; } - void Redim(unsigned int elem); - - void SetPosition(unsigned int chunk); - void ReadBuffer(void *data, unsigned int size); - void WriteBuffer(void *data, unsigned int size); - - bool IsReadOnly() { return readonly; } - - static void Delete(const std::string &filename); - -#ifdef WIN32 - HANDLE fp; -#else - FILE *fp; -#endif - unsigned int size; - bool readonly; -}; - -} - -#endif diff --git a/apps/nexus/fragment.cpp b/apps/nexus/fragment.cpp deleted file mode 100644 index ace2374f..00000000 --- a/apps/nexus/fragment.cpp +++ /dev/null @@ -1,431 +0,0 @@ -/**************************************************************************** -* 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 $ -Revision 1.9 2005/02/08 12:43:03 ponchio -Added copyright - - -****************************************************************************/ - -#include -#include - -#include "fragment.h" -#include "border.h" -#include "nxsalgo.h" -//#include "pvoronoi.h" - - -using namespace std; -using namespace vcg; -using namespace nxs; -using namespace pt; - - - -void NxsPatch::Write(outstm *out) { - out->write(&sphere, sizeof(Sphere3f)); - out->write(&cone, sizeof(ANCone3f)); - int vsize = vert.size(); - int fsize = face.size(); - int bsize = bord.size(); - - out->write(&patch, sizeof(unsigned int)); - out->write(&vsize, sizeof(unsigned int)); - out->write(&fsize, sizeof(unsigned int)); - out->write(&bsize, sizeof(unsigned int)); - - out->write(&*vert.begin(), vert.size() * sizeof(Point3f)); - out->write(&*face.begin(), face.size() * sizeof(unsigned short)); - out->write(&*bord.begin(), bord.size() * sizeof(Link)); -} - -void NxsPatch::Read(instm *in) { - in->read(&sphere, sizeof(Sphere3f)); - in->read(&cone, sizeof(ANCone3f)); - int vsize; - int fsize; - int bsize; - - in->read(&patch, sizeof(unsigned int)); - in->read(&vsize, sizeof(unsigned int)); - in->read(&fsize, sizeof(unsigned int)); - in->read(&bsize, sizeof(unsigned int)); - vert.resize(vsize); - face.resize(fsize); - bord.resize(bsize); - in->read(&*vert.begin(), vert.size() * sizeof(Point3f)); - in->read(&*face.begin(), face.size() * sizeof(unsigned short)); - in->read(&*bord.begin(), bord.size() * sizeof(Link)); -} - -bool Fragment::Write(outstm *out) { - try { - out->write(&id, sizeof(unsigned int)); - out->write(&error, sizeof(float)); - unsigned int ssize = seeds.size(); - out->write(&ssize, sizeof(unsigned int)); - out->write(&*seeds.begin(), ssize * sizeof(Point3f)); - out->write(&*seeds_id.begin(), ssize * sizeof(unsigned int)); - unsigned int psize = pieces.size(); - out->write(&psize, sizeof(unsigned int)); - - for(unsigned int i = 0; i < pieces.size(); i++) - pieces[i].Write(out); - return true; - } catch (estream *e) { - perr.putf("Error: %s\n", pconst(e->get_message())); - delete e; - return false; - } -} - -bool Fragment::Read(instm *in) { - try { - in->read(&id, sizeof(unsigned int)); - in->read(&error, sizeof(float)); - - //TODO move this control to all read! - unsigned int ssize; - if(sizeof(int) != in->read(&ssize, sizeof(unsigned int))) - return false; - seeds.resize(ssize); - seeds_id.resize(ssize); - in->read(&*seeds.begin(), ssize * sizeof(Point3f)); - in->read(&*seeds_id.begin(), ssize * sizeof(unsigned int)); - - unsigned int psize; - in->read(&psize, sizeof(unsigned int)); - pieces.resize(psize); - - for(unsigned int i = 0; i < psize; i++) { - pieces[i].Read(in); - } - return true; - } catch (estream *e) { - perr.putf("Error: %s\n", pconst(e->get_message())); - delete e; - return false; - } -} - -void nxs::Join(Fragment &in, - vector &newvert, - vector &newface, - vector &newbord) { - - map patch_remap; - vector offsets; - - unsigned int totvert = 0; - for(unsigned int i = 0; i < in.pieces.size(); i++) { - offsets.push_back(totvert); - patch_remap[in.pieces[i].patch] = i; - totvert += in.pieces[i].vert.size(); - } - - vector remap; - remap.resize(totvert, 0xffffffff); - - //TODO what if totvert > 1<<22? - //todo we really need a set? - // set newborders; - unsigned int vcount = 0; - unsigned int fcount = 0; - unsigned int bcount = 0; - - for(unsigned int i = 0; i < in.pieces.size(); i++) { - unsigned int offset = offsets[i]; - vector &vert = in.pieces[i].vert; - vector &face = in.pieces[i].face; - vector &bord = in.pieces[i].bord; - - for(unsigned int k = 0; k < face.size(); k+=3) { - assert(face[k] != face[k+1]); - assert(face[k] != face[k+2]); - assert(face[k+1] != face[k+2]); - } - - fcount += face.size()/3; - - for(unsigned int k = 0; k < vert.size(); k++) { - assert(offset + k < remap.size()); - if(remap[offset + k] == 0xffffffff) - remap[offset + k] = vcount++; - } - - for(unsigned int k = 0; k < bord.size(); k++) { - Link link = bord[k]; - if(link.IsNull()) continue; - - if(patch_remap.count(link.end_patch)) {//internal - unsigned int idx = patch_remap[link.end_patch]; - assert(link.end_patch != in.pieces[i].patch); - unsigned int extoffset = offsets[idx]; - - assert(extoffset + link.end_vert < remap.size()); - if(remap[extoffset + link.end_vert] == 0xffffffff) //first time - remap[extoffset + link.end_vert] = remap[offset + link.start_vert]; - } - } - } - assert(vcount < (1<<16)); - - set newborders; - for(unsigned int i = 0; i < in.pieces.size(); i++) { - unsigned int offset = offsets[i]; - vector &bord = in.pieces[i].bord; - for(unsigned int k = 0; k < bord.size(); k++) { - Link llink = bord[k]; - if(llink.IsNull()) continue; - if(!patch_remap.count(llink.end_patch)) {//external - BigLink link; - link.start_vert = remap[offset + llink.start_vert]; - link.end_patch = in.pieces[i].patch; - link.end_vert = llink.start_vert; - newborders.insert(link); - } - } - } - - newvert.resize(vcount); - newface.resize(fcount*3); - newbord.resize(newborders.size()); - - fcount = 0; - for(unsigned int i = 0; i < in.pieces.size(); i++) { - unsigned int offset = offsets[i]; - vector &vert = in.pieces[i].vert; - vector &face = in.pieces[i].face; - vector &bord = in.pieces[i].bord; - - for(unsigned int i = 0; i < vert.size(); i++) { - assert(offset + i < remap.size()); - assert(remap[offset + i] < vcount); - newvert[remap[offset + i]] = vert[i]; - } - - for(unsigned int i = 0; i < face.size(); i++) { - assert(offset + face[i] < remap.size()); - assert(remap[offset + face[i]] < newvert.size()); - assert(fcount < newface.size()); - newface[fcount++] = remap[offset + face[i]]; - } - } - set::iterator b; - for(b = newborders.begin(); b != newborders.end(); b++) { - newbord[bcount++] = *b; - } - - for(unsigned int i = 0; i < newface.size(); i+= 3) { - if(newface[i] == newface[i+1] || - newface[i] == newface[i+2] || - newface[i+1] == newface[i+2]) { - cerr << "i: " << i << endl; - for(unsigned int k = 0; k < newface.size(); k+=3) { - cerr << k << ": " << newface[k] << " " - << newface[k+1] << " " - << newface[k+2] << endl; - } - exit(0); - } - } -} - -void nxs::Split(Fragment &out, - vector &newvert, - vector &newface, - vector &newbord) { - - unsigned int nseeds = out.seeds.size(); - vector &seeds = out.seeds; - vector &seeds_id = out.seeds_id; - //preliminary count - vector count; - count.resize(nseeds, 0); - 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 seed = out.Locate(bari); - assert(seed < nseeds); - count[seed]++; - } - - //pruning small patches - float min_size = (newface.size()/3) / 20.0f; - vector newseeds; - vector newseeds_id; - - for(unsigned int seed = 0; seed < nseeds; seed++) { - if(count[seed] > min_size) { - newseeds.push_back(seeds[seed]); - newseeds_id.push_back(seeds_id[seed]); - } - if(count[seed] > (1<<16)) { - cerr << "Ooops a cell came too big... quitting\n"; - exit(0); - } - } - seeds = newseeds; - seeds_id = newseeds_id; - - nseeds = seeds.size(); - - //if != -1 remap global index to cell index (first arg) - vector< vector > vert_remap; - vector< vector > face_remap; - - vector vert_count; - vector face_count; - - vert_remap.resize(nseeds); - face_remap.resize(nseeds); - vert_count.resize(nseeds, 0); - face_count.resize(nseeds, 0); - - for(unsigned int seed = 0; seed < nseeds; seed++) - vert_remap[seed].resize(newvert.size(), -1); - - 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 seed = out.Locate(bari); - - vector &f_remap = face_remap[seed]; - - f_remap.push_back(newface[f]); - f_remap.push_back(newface[f+1]); - f_remap.push_back(newface[f+2]); - face_count[seed]++; - - - vector &v_remap = vert_remap[seed]; - - for(int i = 0; i < 3; i++) - if(v_remap[newface[f+i]] == -1) - v_remap[newface[f+i]] = vert_count[seed]++; - } - - //TODO assure no big ones. - - out.pieces.resize(nseeds); - - for(unsigned int seed = 0; seed != nseeds; seed++) { - NxsPatch &patch = out.pieces[seed]; - patch.patch = seeds_id[seed]; - - //vertices first - vector &v_remap = vert_remap[seed]; - - assert(vert_count[seed] > 0); - vector &verts = patch.vert; - verts.resize(vert_count[seed]); - for(unsigned int i = 0; i < newvert.size(); i++) { - if(v_remap[i] != -1) - verts[v_remap[i]] = newvert[i]; - } - - //faces now - vector &f_remap = face_remap[seed]; - - vector &faces = patch.face; - faces.resize(face_count[seed]*3); - for(unsigned int i = 0; i < f_remap.size(); i++) { - assert(v_remap[f_remap[i]] != -1); - faces[i] = v_remap[f_remap[i]]; - } - - //borders last - vector &bords = patch.bord; - - //process downward borders - for(unsigned int i = 0; i < newbord.size(); i++) { - BigLink link = newbord[i]; - /* cerr << "Newbord: " << link.start_vert << " " - << link.end_patch << " " - << link.end_vert << endl;*/ - if(v_remap[link.start_vert] == -1) continue; - Link llink; - llink.start_vert = v_remap[link.start_vert]; - llink.end_patch = link.end_patch; - llink.end_vert = link.end_vert; - bords.push_back(llink); - } - - //process internal borders; - //TODO higly inefficient!!! - for(unsigned int rseed = 0; rseed < nseeds; rseed++) { - if(seed == rseed) continue; - - vector &vremapclose = vert_remap[rseed]; - for(unsigned int i = 0; i < newvert.size(); i++) { - if(v_remap[i] != -1 && vremapclose[i] != -1) { - Link link; - link.end_patch = rseed + (1<<31); - link.start_vert = v_remap[i]; - link.end_vert = vremapclose[i]; - bords.push_back(link); - } - } - } - } - //process Cone and sphere - for(unsigned int seed = 0; seed != nseeds; seed++) { - NxsPatch &patch = out.pieces[seed]; - Sphere3f &sphere = patch.sphere; - sphere.CreateTight(patch.vert.size(), &*patch.vert.begin()); - - //NORMALS CONE - vector normals; - for(unsigned int i = 0; i < patch.face.size(); i += 3) { - unsigned short *f = &(patch.face[i]); - Point3f &v0 = patch.vert[f[0]]; - Point3f &v1 = patch.vert[f[1]]; - Point3f &v2 = patch.vert[f[2]]; - - Point3f norm = (v1 - v0) ^ (v2 - v0); - normals.push_back(norm.Normalize()); - } - patch.cone.AddNormals(normals, 0.99f); - } -} - -unsigned int Fragment::Locate(const Point3f &p) { - float max_dist = 1e20f; - unsigned int id = 0xffffffff; - for(unsigned int i = 0; i < seeds.size(); i++) { - float dist = Distance(seeds[i], p); - if(dist < max_dist) { - max_dist = dist; - id = i; - } - } - assert(id != 0xffffffff); - return id; -} diff --git a/apps/nexus/fragment.h b/apps/nexus/fragment.h deleted file mode 100644 index a1dfae84..00000000 --- a/apps/nexus/fragment.h +++ /dev/null @@ -1,106 +0,0 @@ -/**************************************************************************** -* 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 $ -Revision 1.6 2005/02/08 12:43:03 ponchio -Added copyright - - -****************************************************************************/ - -#ifndef NXS_FRAGMENT_H -#define NXS_FRAGMENT_H - -#include - -#include -#include "nexus.h" - -namespace nxs { - -class VoronoiPartition; - -struct BigLink { - unsigned int start_vert; - unsigned int end_patch; - unsigned int end_vert; - bool operator<(const BigLink &l) const { - if(end_patch == l.end_patch) { - if(start_vert == l.start_vert) { - return end_vert < l.end_vert; - } else - return start_vert < l.start_vert; - } else - return end_patch < l.end_patch; - } -}; - -class NxsPatch { - public: - //this fields is the patch number in the infragment - //and the seeds id in the outfragment - unsigned int patch; - vcg::Sphere3f sphere; - ANCone3f cone; - - std::vector vert; - std::vector face; - - std::vector bord; - - void Write(pt::outstm *out); - void Read(pt::instm *in); -}; - -class Fragment { - public: - unsigned int id; - - float error; - - std::vector seeds; - std::vector seeds_id; - - std::vector pieces; - - bool Write(pt::outstm *out); - bool Read(pt::instm *in); - - //returns the index of the seed - unsigned int Locate(const vcg::Point3f &p); -}; - - void Join(Fragment &in, - std::vector &newvert, - std::vector &newface, - std::vector &newbord); - - void Split(Fragment &out, - std::vector &newvert, - std::vector &newface, - std::vector &newbord); -} - -#endif diff --git a/apps/nexus/history.cpp b/apps/nexus/history.cpp deleted file mode 100644 index c05298b4..00000000 --- a/apps/nexus/history.cpp +++ /dev/null @@ -1,400 +0,0 @@ -/**************************************************************************** -* 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 $ -Revision 1.9 2005/02/20 00:43:23 ponchio -Less memory x extraction. (removed frags) - -Revision 1.8 2005/02/19 17:14:02 ponchio -History quick by default. - -Revision 1.7 2005/02/19 16:22:45 ponchio -Minor changes (visited and Cell) - -Revision 1.6 2005/02/17 16:40:35 ponchio -Optimized BuildLevels. - -Revision 1.5 2005/02/08 12:43:03 ponchio -Added copyright - - -****************************************************************************/ - -#include -#include -#include - -#include "history.h" -#include "nexus.h" - - -using namespace std; -using namespace nxs; - -History::~History() { - if(buffer) delete []buffer; - nodes = NULL; - in_links= NULL; - out_links = NULL; -} - -void History::Clear() { - if(buffer) delete []buffer; - buffer = NULL; - updates.clear(); -} - -void History::ClearQuick() { - if(buffer) delete []buffer; - buffer = NULL; -} - -void History::ClearUpdates() { - updates.clear(); -} - -bool History::Load(unsigned int _size, char *mem) { - if(buffer) delete []buffer; - unsigned int is_quick = *(unsigned int *)mem; - bool success; - if(is_quick == 53) { - success = LoadQuick(_size, mem); - } else if(is_quick == 32) { - success = LoadUpdates(_size, mem); - } else { - cerr << "Invalid history: " << is_quick << "\n"; - return false; - } - return success; -} - -bool History::LoadQuick(unsigned int _size, char *mem) { - buffer = mem; - nodes = (Node *)(buffer + 4 * sizeof(int)); - in_links = (Link *)(nodes + n_nodes()); - out_links = in_links + n_in_links(); - - //check size is ok; - assert(n_nodes() * sizeof(Node) + - (n_in_links() + n_out_links()) * sizeof(Link) + - 4 * sizeof(int) == _size); - size = _size; - return LoadPointers(); -} - -bool History::LoadUpdates(unsigned int _size, char *mem) { - unsigned int *tmp = (unsigned int *)mem; - updates.resize(tmp[1]); - - unsigned int pos = 2; - for(unsigned int i = 0; i < updates.size(); i++) { - unsigned int erased = tmp[pos++]; - unsigned int created = tmp[pos++]; - updates[i].erased.resize(erased); - updates[i].created.resize(created); - for(unsigned int e = 0; e < erased; e++) - updates[i].erased[e] = tmp[pos++]; - for(unsigned int e = 0; e < created; e++) - updates[i].created[e] = tmp[pos++]; - } - delete []mem; - buffer = 0; - return true; -} - -bool History::LoadPointers() { - //now convert integer to pointers - for(unsigned int i = 0; i < n_nodes(); i++) { - Node &node = nodes[i]; - assert(((unsigned int)node.in_begin) <= n_in_links()); - assert(((unsigned int)node.out_begin) <= n_out_links()); - node.in_begin = in_links + (unsigned int)(node.in_begin); - node.in_end = in_links + (unsigned int)(node.in_end); - node.out_begin = out_links + (unsigned int)(node.out_begin); - node.out_end = out_links + (unsigned int)(node.out_end); - } - - for(unsigned int i = 0; i < n_in_links(); i++) { - Link &link = in_links[i]; - assert(((unsigned int)link.node) <= n_nodes()); - link.node = nodes + (unsigned int)(link.node); - } - - for(unsigned int i = 0; i < n_out_links(); i++) { - Link &link = out_links[i]; - assert(((unsigned int)link.node) <= n_nodes()); - link.node = nodes + (unsigned int)(link.node); - } - return true; -} - -char *History::Save(unsigned int &_size) { - if(buffer) - return SaveQuick(_size); - else - return SaveUpdates(_size); -} - -char *History::SaveQuick(unsigned int &_size) { - assert(buffer); - for(unsigned int i = 0; i < n_nodes(); i++) { - Node &node = nodes[i]; - node.in_begin = (Link *)(node.in_begin - in_links); - node.in_end = (Link *)(node.in_end - in_links); - node.out_begin = (Link *)(node.out_begin - out_links); - node.out_end = (Link *)(node.out_end - out_links); - } - - for(unsigned int i = 0; i < n_in_links(); i++) { - Link &link = in_links[i]; - link.node = (Node *)(link.node - nodes); - } - - for(unsigned int i = 0; i < n_out_links(); i++) { - Link &link = out_links[i]; - link.node = (Node *)(link.node - nodes); - } - - assert(n_nodes() * sizeof(Node) + - (n_in_links() + n_out_links()) * sizeof(Link) + - 4 * sizeof(int) == size); - - _size = size; - char *tmp = buffer; - buffer = NULL; - return tmp; -} - -char *History::SaveUpdates(unsigned int &_size) { - vector buf; - buf.push_back(32); - buf.push_back(updates.size()); - for(unsigned int i = 0; i < updates.size(); i++) { - Update &update = updates[i]; - buf.push_back(update.erased.size()); - buf.push_back(update.created.size()); - for(unsigned int e = 0; e < update.erased.size(); e++) - buf.push_back(update.erased[e]); - for(unsigned int e = 0; e < update.created.size(); e++) - buf.push_back(update.created[e]); - } - - _size = buf.size() * sizeof(unsigned int); - char *mem = new char[_size]; - memcpy(mem, &*buf.begin(), _size); - return mem; -} - -bool History::UpdatesToQuick(Nexus &nexus) { - //maps cell -> node containing it - map cell_node; - //maps node -> Links - map > node_inlinks; - map > node_outlinks; - - vector tmp_nodes; - tmp_nodes.resize(updates.size()); - - vector tmp_in_links; - vector tmp_out_links; - vector tmp_frags; - - unsigned int current_node = 0; - - vector::iterator u; - for(u = updates.begin(); u != updates.end(); u++) { - Node &node = tmp_nodes[current_node]; - - //created cells belong to this node, - for(unsigned int i = 0; i < (*u).created.size(); i++) { - unsigned int cell = (*u).created[i]; - cell_node[cell] = current_node; - } - - //Every erased cell already belonged to a node //node -> its cells - map > node_erased; - - for(unsigned int i = 0; i < (*u).erased.size(); i++) { - unsigned int cell = (*u).erased[i]; - assert(cell_node.count(cell)); - node_erased[cell_node[cell]].push_back(cell); - } - - //for every node with erased cells we build a fragment and - //put the corresponding cells in it. - map >::iterator e; - for(e = node_erased.begin(); e != node_erased.end(); e++) { - - unsigned int floor_node = (*e).first; - vector &cells = (*e).second; - - Node &parent = tmp_nodes[floor_node]; - - Link inlink; - inlink.node = (Node *)floor_node; - inlink.begin = tmp_frags.size(); - inlink.end = inlink.begin + cells.size(); - - Link outlink; - outlink.node = (Node *)current_node; - outlink.begin = tmp_frags.size(); - outlink.end = outlink.begin + cells.size(); - - //Fill it with erased cells. - vector::iterator k; - for(k = cells.begin(); k != cells.end(); k++) - tmp_frags.push_back(*k); - - //Add the new Frag to the nodes (in and out) - node_outlinks[floor_node].push_back(outlink); - node_inlinks[current_node].push_back(inlink); - } - current_node++; - } - - map >::iterator k; - for(k = node_outlinks.begin(); k != node_outlinks.end(); k++) { - unsigned int inode = (*k).first; - vector &links = (*k).second; - tmp_nodes[inode].out_begin = (Link *)(tmp_out_links.size()); - tmp_nodes[inode].out_end = (Link *)(tmp_out_links.size() + links.size()); - // tmp_nodes[inode].out_link_begin = (Link *)(tmp_out_links.size()); - // tmp_nodes[inode].out_link_size = links.size(); - - for(unsigned int i = 0; i < links.size(); i++) - tmp_out_links.push_back(links[i]); - } - - for(k = node_inlinks.begin(); k != node_inlinks.end(); k++) { - unsigned int inode = (*k).first; - vector &links = (*k).second; - // tmp_nodes[inode].in_link_begin = (Link *)(tmp_in_links.size()); - // tmp_nodes[inode].in_link_size = links.size(); - tmp_nodes[inode].in_begin = (Link *)(tmp_in_links.size()); - tmp_nodes[inode].in_end = (Link *)(tmp_in_links.size() + links.size()); - - for(unsigned int i = 0; i < links.size(); i++) - tmp_in_links.push_back(links[i]); - } - - //Here we reorder entries in nexus... - nexus.Flush(); - - vector entries; - entries.resize(nexus.size()); - for(unsigned int i = 0; i < nexus.size(); i++) { - assert(!nexus[i].patch); - entries[i] = nexus[i]; - } - assert(tmp_frags.size() == nexus.size()); - for(unsigned int i = 0; i < tmp_frags.size(); i++) { - nexus[i] = entries[tmp_frags[i]]; - } - //WARNING CRITICAL TODOHey we should do the same on the borders! - vector backward; - backward.resize(tmp_frags.size()); - for(unsigned int i = 0; i < backward.size(); i++) - backward[tmp_frags[i]] = i; - - for(unsigned int i = 0; i < nexus.borders.size(); i++) { - Border &border = nexus.borders.GetBorder(i); - for(unsigned int k = 0; k < border.Size(); k++) - border[k].end_patch = backward[border[k].end_patch]; - } - nexus.borders.Flush(); - vector borders; - borders.resize(nexus.borders.size()); - for(unsigned int i = 0; i < nexus.borders.size(); i++) { - borders[i] = nexus.borders[i]; - } - assert(tmp_frags.size() == nexus.borders.size()); - for(unsigned int i = 0; i < tmp_frags.size(); i++) { - nexus.borders[i] = borders[tmp_frags[i]]; - } - - - size = tmp_nodes.size() * sizeof(Node) + - tmp_in_links.size() * sizeof(Link) + - tmp_out_links.size() * sizeof(Link) + - 4 * sizeof(int); - - if(buffer) delete []buffer; - buffer = new char[size]; - - quick() = 53; - n_nodes() = tmp_nodes.size(); - n_in_links() = tmp_in_links.size(); - n_out_links() = tmp_out_links.size(); - - nodes = (Node *)(buffer + 4 * sizeof(int)); - in_links = (Link *)(nodes + n_nodes()); - out_links = in_links + n_in_links(); - - memcpy(nodes, &*tmp_nodes.begin(), tmp_nodes.size()*sizeof(Node)); - memcpy(in_links, &*tmp_in_links.begin(), tmp_in_links.size()*sizeof(Link)); - memcpy(out_links, &*tmp_out_links.begin(), - tmp_out_links.size()*sizeof(Link)); - - return LoadPointers(); -} - -void History::BuildLevels(vector &levels) { - levels.clear(); - if(buffer) { - //Saved in quick mode: - for(unsigned int n = 0; n < n_nodes(); n++) { - Node *node = nodes+n; - Node::iterator l; - unsigned int current = 0; - if(node != nodes) { //not root - Link *inlink = node->in_begin; - unsigned int p = inlink->begin; - assert(p < levels.size()); - assert(p >= 0); - current = levels[p]+1; - } - for(l = node->out_begin; l != node->out_end; l++) { - Link &link = *l; - for(unsigned int p = link.begin; p != link.end; p++) { - while(p >= levels.size()) levels.push_back(-1); - levels[p] = current; - } - } - } - } else { - //Saved in updates mode: - for(unsigned int i = 0; i < updates.size(); i++) { - Update &u = updates[i]; - unsigned int current = 0; - if(!u.erased.size()) current = 0; - else current = levels[u.erased[0]] + 1; - for(unsigned int i = 0; i < u.created.size(); i++) { - unsigned int p = u.created[i]; - while(p >= levels.size()) levels.push_back(-1); - levels[p] = current; - } - } - } -} diff --git a/apps/nexus/history.h b/apps/nexus/history.h deleted file mode 100644 index 2807b7b1..00000000 --- a/apps/nexus/history.h +++ /dev/null @@ -1,130 +0,0 @@ -/**************************************************************************** -* 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 $ -Revision 1.7 2005/02/20 19:49:44 ponchio -cleaning (a bit more). - -Revision 1.6 2005/02/20 00:43:23 ponchio -Less memory x extraction. (removed frags) - -Revision 1.5 2005/02/19 16:22:45 ponchio -Minor changes (visited and Cell) - -Revision 1.4 2005/02/17 16:40:35 ponchio -Optimized BuildLevels. - -Revision 1.3 2005/02/08 12:43:03 ponchio -Added copyright - - -****************************************************************************/ - -#ifndef NXS_HISTORY_H -#define NXS_HISTORY_H - -#include -#include - -//TODO fix a bit better the quick <-> updates duality - -namespace nxs { - - class Nexus; - class History { - public: - - enum Mode { QUICK = 1, UPDATES = 2 }; - - struct Update { - std::vector erased; - std::vector created; - }; - - struct Node; - - struct Link { - Node *node; - unsigned int begin; //begin patch of the fragment - unsigned int end; //end patch of the fragment - - typedef unsigned int iterator; - unsigned int size() { return end - begin; } - }; - - struct Node { - typedef Link *iterator; - Link *in_begin, *in_end; - Link *out_begin, *out_end; - }; - - Node *nodes; - Link *in_links; - Link *out_links; - - std::vector updates; - - History(): nodes(NULL), in_links(NULL), out_links(NULL),// frags(NULL), - buffer(NULL) {} - ~History(); - - Node *Root() { return &nodes[0]; } - Node *Sink() { return Root() + n_nodes() -1; } - void Clear(); - void ClearQuick(); - void ClearUpdates(); - //Owns memory afterwards.. do not free mem. - bool Load(unsigned int size, char *mem); - bool LoadQuick(unsigned int size, char *mem); - bool LoadUpdates(unsigned int size, char *mem); - - //after these call history is invalid! and memory returned must be freed... - char *Save(unsigned int &size); //autodetect - char *SaveQuick(unsigned int &size); - char *SaveUpdates(unsigned int &size); - - bool QuickToUpdates(); - bool UpdatesToQuick(Nexus &nexus); - bool IsQuick() { return buffer != NULL; } - - void BuildLevels(std::vector &levels); - - int &quick() { return ((int *)buffer)[0]; } - int &n_nodes() { return ((int *)buffer)[1]; } - int &n_in_links() { return ((int *)buffer)[2]; } - int &n_out_links() { return ((int *)buffer)[3]; } - - // typedef Node *iterator; - // iterator begin() { return nodes; } - // iterator end() { return nodes + n_nodes(); } - protected: - unsigned int size; - char *buffer; - - bool LoadPointers(); - }; - -} -#endif diff --git a/apps/nexus/index_file.h b/apps/nexus/index_file.h deleted file mode 100644 index 8053bca3..00000000 --- a/apps/nexus/index_file.h +++ /dev/null @@ -1,108 +0,0 @@ -/**************************************************************************** -* 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_INDEX_FILE_H -#define NXS_INDEX_FILE_H - -#include - -#include - -#include "mfile.h" - -namespace nxs { - - /* WARINING when subclassing this class you must add a Close() - in the destructor! */ - -template -class IndexFile: public MFile, public std::vector { - - public: - virtual ~IndexFile() {} - - bool Create(const std::string &filename, unsigned int header_size, - unsigned int max_file_size = MFILE_MAX_SIZE) { - clear(); - if(!MFile::Create(filename, max_file_size)) return false; - MFile::Redim(header_size); - offset = header_size; - return true; - } - - bool Load(const std::string &filename, bool readonly = false) { - clear(); - if(!MFile::Load(filename, readonly)) return false; - SetPosition(0); - LoadHeader(); - - SetPosition(offset); - unsigned int tot; - ReadBuffer(&tot, sizeof(unsigned int)); - resize(tot); - ReadBuffer(&*begin(), size() * sizeof(T)); - return true; - } - - void Close() { - if(IsReadOnly()) return; - if(files.size() == 0) return; //not loaded, not created or closed - - MFile::Redim(offset + size() * sizeof(T)); - SetPosition(offset); - unsigned int tot = size(); - WriteBuffer(&tot, sizeof(unsigned int)); - WriteBuffer(&*begin(), size() * sizeof(T)); - SetPosition(0); - SaveHeader(); - MFile::Close(); - } - - int64 Length() { //count the header but not the index... - return offset; - } - - void Redim(int64 size) { - MFile::Redim(size); - offset = size; - assert(MFile::Length() == offset); - } - - protected: - int64 offset; - - //MUST set offset to its correct value - virtual bool LoadHeader() = 0; - //MUST save offset somewhere - virtual void SaveHeader() = 0; -}; - -} //namespace - -#endif diff --git a/apps/nexus/metric.h b/apps/nexus/metric.h deleted file mode 100644 index 387afebf..00000000 --- a/apps/nexus/metric.h +++ /dev/null @@ -1,109 +0,0 @@ -/**************************************************************************** -* 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 $ -Revision 1.9 2005/02/22 10:37:55 ponchio -Debug, cleaning and optimization. - -Revision 1.8 2005/02/21 20:49:30 ponchio -some culling bug. - -Revision 1.7 2005/02/20 19:49:44 ponchio -cleaning (a bit more). - -Revision 1.6 2005/02/20 18:07:01 ponchio -cleaning. - -Revision 1.5 2005/02/19 16:22:45 ponchio -Minor changes (visited and Cell) - -Revision 1.4 2005/02/08 12:43:03 ponchio -Added copyright - - -****************************************************************************/ - -#ifndef NXS_METRIC_H -#define NXS_METRIC_H - -#include -#include -#include "nexus.h" - -namespace nxs { - - enum MetricKind { FRUSTUM, FLAT, DELTA }; - - class Metric { - public: - vcg::Frustumf frustum; - bool culling; - - Metric(): culling(true) {} - virtual void GetView() { frustum.GetView(); } - virtual float GetError(Entry &entry, bool &visible) = 0; - - }; - - class FlatMetric: public Metric { - public: - float GetError(Entry &entry, bool &visible) { - visible = true; - return entry.error; - } - }; - - class FrustumMetric: public Metric { - public: - float GetError(Entry &entry, bool &visible) { - visible = true; - vcg::Sphere3f &sph = entry.sphere; - float dist = (sph.Center() - frustum.ViewPoint()).Norm() - sph.Radius(); - - if(dist < 0) return 1e20f; - - float error = entry.error/frustum.Resolution(dist); - if(culling) { - float remote = frustum.Remoteness(sph.Center(), sph.Radius()); - if(frustum.IsOutside(sph.Center(), sph.Radius())) { - visible = false; - - - // TODO FIXME remoteness is bugged... (not much only bit - //if we are close to the surface, the projection of - //the bounding sphere in screen space comes out too small - //just using resolution and radius. Im too lazy to fix it. - if(remote > 0) - error /= remote; - } else if(entry.cone.Backface(sph, frustum.ViewPoint())) { - visible = false; - } - } - return error; - } - }; -} - -#endif diff --git a/apps/nexus/mfile.cpp b/apps/nexus/mfile.cpp deleted file mode 100644 index a554a289..00000000 --- a/apps/nexus/mfile.cpp +++ /dev/null @@ -1,192 +0,0 @@ -/**************************************************************************** -* 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 "mfile.h" -#include -#include - -using namespace std; -using namespace nxs; - -bool MFile::Create(const string &fname, unsigned int mxs) { - Close(); - filename = fname; - _size = 0; - readonly = false; - assert(mxs <= MFILE_MAX_SIZE); - max_size = mxs; - return AddFile(); -} - -bool MFile::Load(const string &fname, bool ronly) { - Close(); - filename = fname; - readonly = ronly; - max_size = MFILE_MAX_SIZE; - _size = 0; - - while(1) { - string name = Name(files.size()); - File *file = new File; - files.push_back(file); - if(!file->Load(name, ronly)) { - files.pop_back(); - break; - } - _size += file->Length(); - } - if(files.size() == 0) return false; - if(files.size() == 1) { - assert(_size <= max_size); - } else { - //SANITY TEST - for(unsigned int i = 0; i < files.size() -2; i++) { - if(files[i]->Length() != files[i++]->Length()) { - //"Inconsistent file size for some file.\n"; - return false; - } - max_size = files[0]->Length(); - } - } - return true; -} - -void MFile::Close() { - for(unsigned int i = 0; i < files.size(); i++) - delete files[i]; - files.clear(); -} - -void MFile::Delete() { - while(files.size()) - RemoveFile(); -} - -void MFile::Redim(int64 sz) { - assert(!readonly); - if(sz > _size) { - unsigned int totfile = (unsigned int)(sz/max_size); - //TODO test rhis!!!! - while(files.size() <= totfile) { - RedimLast(max_size); - assert(_size == (int64)max_size * (int64)(files.size())); - AddFile(); - } - assert(_size <= sz); - assert(sz - _size < max_size); - assert(files.back()->Length() + (unsigned int)(sz - _size) < max_size); - RedimLast(files.back()->Length() + (unsigned int)(sz - _size)); - } else { - while(_size - files.back()->Length() > sz) - RemoveFile(); - assert(sz <= _size); - RedimLast(files.back()->Length() - (unsigned int)(_size - sz)); - } - assert(sz == _size); -} - -void MFile::SetPosition(int64 pos) { - assert(pos <= _size); - curr_fp = (unsigned int)(pos/(int64)max_size); - curr_pos = (unsigned int)(pos - (int64)max_size * (int64)curr_fp); - assert(curr_pos < max_size); - assert(curr_fp < files.size()); - files[curr_fp]->SetPosition(curr_pos); -} - -void MFile::ReadBuffer(void *data, unsigned int sz) { - while(sz + curr_pos > max_size) { - unsigned int n = max_size - curr_pos; - files[curr_fp]->ReadBuffer(data, n); - data = ((char *)data) + n; - sz -= n; - curr_fp++; - assert(curr_fp < files.size()); - curr_pos = 0; - files[curr_fp]->SetPosition(curr_pos); - } - files[curr_fp]->ReadBuffer(data, sz); -} - -void MFile::WriteBuffer(void *data, unsigned int sz) { - assert(!readonly); - while(sz + curr_pos > max_size) { - unsigned int n = max_size - curr_pos; - files[curr_fp]->WriteBuffer(data, n); - data = ((char *)data) + n; - sz -= n; - curr_fp++; - assert(curr_fp < files.size()); - curr_pos = 0; - files[curr_fp]->SetPosition(curr_pos); - } - files[curr_fp]->WriteBuffer(data, sz); -} - - bool MFile::AddFile() { - string name = Name(files.size()); - File *file = new File; - files.push_back(file); - return file->Create(name); - } - - void MFile::RemoveFile() { - assert(files.size()); - - string name = Name(files.size()-1); - File *file = files.back(); - unsigned int last_size = file->Length(); - delete file; - files.pop_back(); - _size -= last_size; - cerr << "Removing file: " << name << endl; -#ifdef WIN32 - DeleteFile(name.c_str()); -#else - unlink(name.c_str()); -#endif - } - -void MFile::RedimLast(unsigned int sz) { - assert(sz <= max_size); - File &file = *files.back(); - unsigned int last_size = (int64)file.Length(); - file.Redim(sz); - _size += sz - (int64)last_size; -} - -std::string MFile::Name(unsigned int n) { - char buffer[1024]; - if(n == 0) - sprintf(buffer, "%s", filename.c_str()); - else - sprintf(buffer, "%s%d", filename.c_str(), n); - return string(buffer); -} - diff --git a/apps/nexus/mfile.h b/apps/nexus/mfile.h deleted file mode 100644 index a9d94adb..00000000 --- a/apps/nexus/mfile.h +++ /dev/null @@ -1,91 +0,0 @@ -/**************************************************************************** -* 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_MFILE_H -#define NXS_MFILE_H - -#include -#include - -#include "file.h" -#include "nxstypes.h" - -namespace nxs { - - /*#ifdef WIN32 -typedef __int64 int64; -#else -typedef unsigned long long int64; -#endif*/ - -#define MFILE_MAX_SIZE (1<<30) - -class MFile { - public: - - MFile() {} - ~MFile() { Close(); } - - //max is so default is 1 G - bool Create(const std::string &filename, - unsigned int max_file_size = MFILE_MAX_SIZE); - bool Load(const std::string &filename, bool readonly = false); - void Close(); - void Delete(); - - int64 Length() { return _size; } - void Redim(int64 size); - - void SetPosition(int64 pos); - void ReadBuffer(void *data, unsigned int size); - void WriteBuffer(void *data, unsigned int size); - - bool Opened() { return files.size() > 0; } - bool IsReadOnly() { return readonly; } - void SetReadOnly(bool rd) { readonly = rd; } //USE WITH CARE!!!! - protected: - std::string filename; - std::vector files; - unsigned int curr_pos; - unsigned int curr_fp; - int64 _size; - unsigned int max_size; - bool readonly; - private: - //all theese refer to the last in the fp. - bool AddFile(); - void RemoveFile(); - void RedimLast(unsigned int sz); - unsigned int GetSize(); - std::string Name(unsigned int n); -}; - -} - -#endif diff --git a/apps/nexus/nexus.cpp b/apps/nexus/nexus.cpp deleted file mode 100644 index a6db5d90..00000000 --- a/apps/nexus/nexus.cpp +++ /dev/null @@ -1,319 +0,0 @@ -/**************************************************************************** -* 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 $ -Revision 1.29 2005/02/19 10:45:04 ponchio -Patch generalized and small fixes. - -Revision 1.28 2005/02/08 12:43:03 ponchio -Added copyright - - -****************************************************************************/ - -#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; //a bit more than 64 needed - 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 - history_offset = 0; - if(size()) { - //we need to discover where is the last patch - for(unsigned int i = 0; i < size(); i++) { - Entry &e = operator[](i); - if(e.patch_start + e.disk_size > history_offset) - history_offset = e.patch_start + e.disk_size; - } - // 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)); - unsigned int version = 1; - WriteBuffer(&version, sizeof(unsigned int)); - - WriteBuffer(&signature, sizeof(Signature)); - 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; - } - //Current version is 1 - unsigned int version; - ReadBuffer(&version, sizeof(unsigned int)); - if(version != NXS_CURRENT_VERSION) { - cerr << "Old version. Sorry.\n"; - return false; - } - ReadBuffer(&signature, sizeof(Signature)); - 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); -} - -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; -} - -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.compr == 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; - } - } else { - //zero all bytes... so compressio gets better with padding. - memset(ram, 0, entry.ram_size * chunk_size); - } - 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.compr) { - 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->fstart, entry.disk_size * chunk_size); - } - } - - delete [](entry.patch->fstart); - delete entry.patch; - entry.patch = NULL; - ram_used -= entry.ram_size; -} diff --git a/apps/nexus/nexus.h b/apps/nexus/nexus.h deleted file mode 100644 index 0f57e2c8..00000000 --- a/apps/nexus/nexus.h +++ /dev/null @@ -1,146 +0,0 @@ -/**************************************************************************** -* 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 $ -Revision 1.21 2005/02/19 10:45:04 ponchio -Patch generalized and small fixes. - -Revision 1.20 2005/02/08 12:43:03 ponchio -Added copyright - - -****************************************************************************/ - -#ifndef NXS_NEXUS_H -#define NXS_NEXUS_H - -#include -#include -#include -#include - -#include - -#include "normalscone.h" -#include "patch.h" -#include "index_file.h" -#include "history.h" -#include "borderserver.h" - -namespace nxs { - - /* Header fo nexus: - 1Kb riservato per dati globali: - Magic: 'n' 'x' 's' 0x00 - Signature: unsigned int (maschera di bit) - Chunk size: unsigned int - Index offset: unsigned int (offset to the index begin, - must be a multiple of chunk size) - History offset: unsigned int: multiple of chunk_size - - Tot vert: unsigned int - Tot face: unsigned int - Bound sphere: Sphere3f (4 float: Point3f center (x, y, z), (radius)) - - 11 * 4 = 44 bytes -> 4k per alignment purpoouses and reserving space. */ - -struct Entry { - unsigned int patch_start; //granularita' Chunk - unsigned short ram_size; //in chunks - unsigned short disk_size; // in chunks (used when compressed) - - unsigned short nvert; - unsigned short nface; - - vcg::Sphere3f sphere; - float error; - NCone3s cone; - - Patch *patch; - unsigned int vbo_array; - unsigned int vbo_element; -}; - -class Nexus: public IndexFile { - public: - enum Version { NXS_CURRENT_VERSION = 1 }; - - //HEader data: - Signature signature; - unsigned int chunk_size; - //unsigned int .IndexFile::offset; - int64 history_offset; - - unsigned int totvert; - unsigned int totface; - vcg::Sphere3f sphere; - - History history; - - BorderServer borders; - - - Nexus() {} - ~Nexus(); - - bool Create(const std::string &filename, Signature &signature, - unsigned int chunk_size = 1024); - bool Load(const std::string &filename, bool readonly = false); - void Close(); - void Flush(bool all = true); - - unsigned int AddPatch(unsigned int nv, unsigned int nf, unsigned int nb); - Patch &GetPatch(unsigned int patch, bool flush = true); - Border &GetBorder(unsigned int patch, bool flush = true); - - unsigned int &MaxRam() { return ram_max; } - // void AddBorder(unsigned int patch, Link &link); - - //move to nxsalgo! - void Unify(float threshold = 0.0f); - - bool IsCompressed() { return signature.compr != 0; } - bool HasStrips() { return signature.face == Signature::STRIPS; } - bool HasColors() { return signature.vcolor != 0; } - bool HasNormals() { return signature.vnorm != 0; } - bool HasTextures() { return signature.vtext != 0; } - - unsigned int ram_max; - unsigned int ram_used; - protected: - - std::list pqueue; - std::map::iterator> index; - - Patch *LoadPatch(unsigned int id); - virtual void FlushPatch(unsigned int id); - - bool LoadHeader(); - void SaveHeader(); -}; - -} - -#endif diff --git a/apps/nexus/nexus.html b/apps/nexus/nexus.html deleted file mode 100644 index 1ec180bd..00000000 --- a/apps/nexus/nexus.html +++ /dev/null @@ -1,174 +0,0 @@ - - - - -

-

-

-

Index:

-

-

- - -

Big picture

-La truttura nexus divide la mesh in tante piccole parti.
-Ciascuna parte ha le facce indicizzate localmente, e sono codificate -esplicitamente le corrispondenze tra vertici di patches diverse.
- -

Structures and interfaces

- -

VFile

-VFile dovrebbe avere piu' o meno la stessa interfaccia di vector -ma usare un file per storare i dati (un mmapping, ma non limitato a 4Gb).
- -

Crude

-
-Crude e' un formato tipo il ply... solo che usa dei VFile per 
-storare i dati.(3 unsigned int per una faccia e 3 float per un vertice).
-Per cui e' composto di 3 files:
- Header (.crd)
-  Magic:     'c' 'r' 'd' 0x00 (4 bytes)
-  N vertici: unsigned int (4 bytes)
-  N facce: unsigned int (4 bytes)
-  Box: Box3f (6 float min(x, y, z) e max(x, y, z))
-  
- File dei vertici:  Point3f in sequenza (3 float, x, y, z)
- File delle facce:  3 unsigned int
-  
- opzionalmente file degli attributi dei vertici e delle facce.
-
- Come interfaccia e' sufficente che dia accesso casuale a facce e vertici...
-
- -

PChain

-Sta per 'partition chain' cioe' una catena di partizioni dello -spazio.
-Una PChain detto in matematichese e' una funzione da -[0..L] X R^3 -> N (cioe' dato un livello e un punto nello spazio -restituisce un numero naturale. (una patch)
-Oltre a questo va aggiunta una funzione che dato un punto e una patch -ne restituisce la distanza (o un valore 'equivalente').
-Si deve poter salvare su disco e recuperare una pchain
- - - -

Nexus

-
-E' una struttura con 2 files:
-
-File delle patches: (.nxs)
-  1Kb riservato per dati globali:
-     Magic:        'n' 'x' 's' 0x00
-     Signature:    unsigned int (maschera di bit)
-     Chunk size:   unsigned int
-     Index offset: int64 (offset to the index begin, 
-                                 must be a multiple of chunk size)
-     History offset: int64: multiple of chunk_size
-
-     Tot vert:     unsigned int
-     Tot face:     unsigned int  
-     Bound sphere: Sphere3f (4 float: Point3f center (x, y, z), (radius))
-
-  Dati (la sequenza di patches)
- 
-  Index
-  History
-
-  'Index' contiene l'indice delle patches, per ogni patch viene storato:
-  offset, size dei dati geometrici nel file delle patches etc.
-  'History' vedi history.h
-
-  Index e History sono tenuti in memoria se il file e' aperto per creare
-  o per modificare, e sono salvati quando si chiude il file.
-
-File dei bordi:
-   Links.
-   Index (offset, size tot size used)
-
-
-  Ogni link fa riferimento ad un vertice di bordo 
-  tra la patch A (V_a) e la B (V_b) e contiene (V_a, V_b, B) (A e' implicito). 
-   L'informazione e' replicata nei bordi relativi a B con (V_b, V_a, A)
-
-
- -

Bmt

-Bmt (batched multi triangulation) e' il formato per la -multirisoluzione:
-E' un vettore di 'Cell' scritti su disco + un 'Index' e una 'History'. -Cell contiene i dati da visualizzare: vertici e strip (e altro?).
-Index contiene per ogni cell 'offset' per trovare la cell -corrispondente, error e bounding sphere (e per il face o vertex -count?).
-History e' la storia della multirisoluzione.
- -

Algorithms

-

Plys to Crude

-Si copiano i vertici un ply dopo l'altro nel crude.
-Per le facce si copiano sommando un offset ai vertici
-Se si montano insieme piu' ply i vertici rimangono duplicati.
-E' piu' semplice fare il join dentro Nexus.
- -

Crude + PChain to Remap

-Come prima cosa si costruisce il pchain passandogli il Crude.
-Asegnamo per ogni faccia la sua patch al livello 0.
-Per ogni patch sappiamo quante ce ne sono.
-Ridistribuiamo le patch troppo piccole (e rimappiamo i numeri delle -patches per rimuovere i buchi (anche dentro il PChain).
-Per risparmiare nella stessa passata ci segnamo dove vanno i vertici
-Poi facciamo un'altra passate per contare i bordi
-Infine si sortano le facce secondo l'ordine delle patches.
- -

Crude + Remap to Nexus

-Per ogni patch raccogliamo le facce, e i vertici (ma non li -unifichiamo) e costruiamo una patch nel Nexus.
-Ci dobbiamo segnare i bordi mano a mano che lo facciamo: -per ogni patch ci salviamo una lista di triplette: il numero assoluto -del vertice di bordo, il numero relativo nella patch che stiamo -salvando, e il numero della patch (o delle patch) con la quale -confina.
-Queste informazioni le tiriamo fuori dal Remap dei vertici
-Una volta salvate tutte le patches ci smazziamo i bordi: -per ogni patch prendiamo quella lista e la incrociamo con quella -delle patches confinanti per costruire i bordi da passare a nexus.
- -

Nexus + PChain to Bmt

-Per costruire il file multirisoluzione aggiungeremo semplicemente -nuove patches al file nexus.
-Ad ogni passo selezioniamo un gruppo di patches (il PChain ci dice i -gruppi), li joiniamo insieme usando i bordi per riunificare i vertici -e per marcare i bordi esterni.
-Poi semplifichiamo e splittiamo (e sempre il PChain ci dice quali -nuove patches creare), ridistribuiamo le patches troppo piccole.
-E aggiungiamo le nuove patches al Nexus, e i nuovi bordi.
-Ci salviamo anche la storia da qualche parte.
- - diff --git a/apps/nexus/nexus_logo.png b/apps/nexus/nexus_logo.png deleted file mode 100644 index 79eab848254144882d412d6396512e9de062c474..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 154955 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 diff --git a/apps/nexus/nexus_logo_midi.png b/apps/nexus/nexus_logo_midi.png deleted file mode 100644 index 47e791f58be3b56177ad8022d7c999516b9cab44..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27989 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 diff --git a/apps/nexus/nexusmt.cpp b/apps/nexus/nexusmt.cpp deleted file mode 100644 index ba5ed374..00000000 --- a/apps/nexus/nexusmt.cpp +++ /dev/null @@ -1,464 +0,0 @@ -/**************************************************************************** -* 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 $ -Revision 1.37 2005/03/02 10:40:17 ponchio -Extraction rewrittten (to fix recusive problems). - -Revision 1.36 2005/02/22 10:38:06 ponchio -Debug, cleaning and optimization. - -Revision 1.35 2005/02/20 19:49:44 ponchio -cleaning (a bit more). - -Revision 1.34 2005/02/20 18:07:01 ponchio -cleaning. - -Revision 1.33 2005/02/20 00:43:23 ponchio -Less memory x extraction. (removed frags) - -Revision 1.32 2005/02/19 12:06:53 ponchio -Debug... - -Revision 1.31 2005/02/19 10:45:04 ponchio -Patch generalized and small fixes. - -Revision 1.30 2005/02/17 15:39:44 ponchio -Reorderes statistics a bit. - -Revision 1.29 2005/02/14 17:11:07 ponchio -aggiunta delle sphere - -Revision 1.28 2005/02/08 12:43:03 ponchio -Added copyright - - -****************************************************************************/ - -#ifndef WIN32 -#include -#endif - -#include -#include - -#include - -#include - -#include "nexusmt.h" - -using namespace nxs; -using namespace vcg; -using namespace std; - -void Stats::Start() { - tri = extr = disk_tri = 0; - watch.Start(); -} - -void Stats::Disk(float _disk) { - disk.push_front(_disk); - if(disk.size() > log_size) disk.pop_back(); -} - -void Stats::Error(float _error) { - error.push_front(_error); - if(error.size() > log_size) error.pop_back(); -} - -void Stats::Stop() { - time.push_front((float)watch.Time()); - if(time.size() > log_size) time.pop_back(); - fps = (7*fps + 1/time[0])/8.0f; -} - -NexusMt::NexusMt() { - preload.mt = this; - preload.start(); -} - -NexusMt::~NexusMt() { - preload.signal(); - preload.waitfor(); -} - -void NexusMt::SetPreload(bool on) { - if(on == (preload.get_running() && !preload.get_finished())) return; - if(on) preload.start(); - else { - preload.signal(); - // preload.waitfor(); - } -} - -bool NexusMt::Load(const string &filename) { - if(!Nexus::Load(filename, true)) return false; - if(!history.IsQuick() && !history.UpdatesToQuick(*this)) - return false; - -#ifndef WIN32 - //i will read data only once usually. - // for(unsigned int i = 0; i < files.size(); i++) { - // int fd = fileno(files[i]->fp); - // posix_fadvise(fd, 0, 0, POSIX_FADV_NOREUSE); - // } - -#endif - return true; -} - -bool NexusMt::InitGL(bool vbo) { - use_vbo = vbo; - - GLenum ret = glewInit(); - if(ret != GLEW_OK) return false; - if(vbo && !GLEW_ARB_vertex_buffer_object) { - cerr << "No vbo available!" << endl; - use_vbo = false; - } - return true; -} - -void NexusMt::Render(DrawContest contest) { - Extraction extraction; - extraction.metric->GetView(); - extraction.Extract(this); - Render(extraction, contest); -} - - -void NexusMt::Render(Extraction &extraction, DrawContest &contest, - Stats *stats) { - static ::GLUquadricObj * spr = gluNewQuadric(); - - - //Updating heap errors - map errors; - for(unsigned int i = 0; i < extraction.selected.size(); i++) { - Item &item = extraction.selected[i]; - errors[item.id] = item.error; - } - for(unsigned int i = 0; i < heap.size(); i++) { - Item &item = heap[i]; - if(!errors.count(item.id)) { - item.error = 1e20; - } else - item.error = errors[item.id]; - } - make_heap(heap.begin(), heap.end()); - - - - preload.post(extraction.selected); - - glEnableClientState(GL_VERTEX_ARRAY); - if(signature.vcolor && (contest.attrs & DrawContest::COLOR)) - glEnableClientState(GL_COLOR_ARRAY); - if(signature.vnorm && (contest.attrs & DrawContest::NORMAL)) - glEnableClientState(GL_NORMAL_ARRAY); - - vector skipped; - - for(unsigned int i = 0; i < extraction.draw_size; i++) { - unsigned int patch = extraction.selected[i].id; - Entry &entry = operator[](patch); - if(stats) stats->extr += 2*entry.nvert; - - if(!extraction.Visible(patch)) - continue; - - if(stats) stats->tri += 2*entry.nvert; - - if(!entry.patch) { - skipped.push_back(extraction.selected[i]); - continue; - } - Draw(patch, contest); - } - - - preload.trigger.reset(); - preload.lock.enter(); - - // if(skipped.size()) cerr << "Skipped: " << skipped.size() << endl; - - for(vector::iterator i = skipped.begin(); i != skipped.end(); i++) { - GetPatch((*i).id, (*i).error); - Draw((*i).id, contest); - } - Flush(false); //in case there are no skipped... :P - - if(stats) { - stats->Error(extraction.max_error); - stats->Disk(preload.disk); - stats->disk_tri += preload.disk_tri; - preload.disk = 0; - } - - - preload.trigger.post(); - preload.lock.leave(); - - - - glDisableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_COLOR_ARRAY); - glDisableClientState(GL_NORMAL_ARRAY); -} - -void NexusMt::Draw(unsigned int cell, DrawContest &contest) { - static ::GLUquadricObj * spr = gluNewQuadric(); - - Entry &entry = operator[](cell); - Patch &patch = *(entry.patch); - char *fstart; - char *vstart; - char *cstart; - char *nstart; - - if(contest.attrs & DrawContest::SPHERES){ - vcg::Sphere3f &sphere = entry.sphere; - glPushAttrib(GL_POLYGON_BIT); - glPolygonMode(GL_FRONT_AND_BACK,GL_LINE); - glPushMatrix(); - glTranslatef(sphere.Center().X(),sphere.Center().Y(),sphere.Center().Z()); - gluSphere(spr,sphere.Radius(),15,15); - glPopMatrix(); - glPopAttrib(); - } - - - if(use_vbo) { - if(!entry.vbo_element) - LoadVbo(entry); - - glBindBufferARB(GL_ARRAY_BUFFER_ARB, entry.vbo_array); - glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, entry.vbo_element); - - fstart = NULL; - vstart = NULL; - cstart = (char *)(patch.vstartc - patch.vstart); - nstart = (char *)(patch.vstartn - patch.vstart); - } else { - fstart = (char *)patch.FaceBegin(); - vstart = (char *)patch.Vert3fBegin(); - cstart = (char *)patch.VColorBegin(); - nstart = (char *)patch.VNormBegin(); - } - assert(signature.vert == Signature::POINT3F); - glVertexPointer(3, GL_FLOAT, 0, vstart); - - - if(signature.vcolor && contest.attrs & DrawContest::COLOR) { - assert(signature.vcolor == Encodings::BYTE4); - glColorPointer(4, GL_UNSIGNED_BYTE, 0, cstart); - } - - - if(signature.vnorm && contest.attrs & DrawContest::NORMAL) { - // assert(signature.vnorm == Encodings::SHORT4); - if(signature.vnorm == Encodings::SHORT4) - glNormalPointer(GL_SHORT, 8, nstart); - else - glNormalPointer(GL_FLOAT, 0, nstart); - } - - - - switch(contest.mode) { - case DrawContest::POINTS: - glDrawArrays(GL_POINTS, 0, patch.nv); break; - case DrawContest::PATCHES: - glColor3ub((cell * 27)%225 + 30, (cell * 37)%225 + 30, (cell * 87)%225 + 30); - case DrawContest::SMOOTH: - if(signature.face == Signature::TRIANGLES) - glDrawElements(GL_TRIANGLES, patch.nf * 3, - GL_UNSIGNED_SHORT, fstart); - else if(signature.face == Signature::STRIPS) - glDrawElements(GL_TRIANGLE_STRIP, patch.nf, - GL_UNSIGNED_SHORT, fstart); - break; - case DrawContest::FLAT: - if(use_vbo) { - cerr << "Unsupported rendering mode sorry\n"; - } else { - if(signature.face == Signature::TRIANGLES) { - glBegin(GL_TRIANGLES); - unsigned short *f = patch.Face(0); - for(int i = 0; i < patch.nf; i++) { - - Point3f &p0 = patch.Vert3f(f[0]); - Point3f &p1 = patch.Vert3f(f[1]); - Point3f &p2 = patch.Vert3f(f[2]); - Point3f n = ((p1 - p0) ^ (p2 - p0)); - glNormal3f(n[0], n[1], n[2]); - glVertex3f(p0[0], p0[1], p0[2]); - glVertex3f(p1[0], p1[1], p1[2]); - glVertex3f(p2[0], p2[1], p2[2]); - f += 3; - } - glEnd(); - } else if(signature.face = Signature::STRIPS) { - cerr << "Unsupported rendering mode sorry\n"; - } - } - break; - default: - cerr << "Unsupported rendering mode sorry\n"; - exit(0); - break; - } -} - -Patch &NexusMt::GetPatch(unsigned int patch, float error, bool flush) { - Entry &entry = operator[](patch); - if(entry.patch) return *(entry.patch); - - while(flush && ram_used > ram_max) { - if(heap[0].error == 0) break; - unsigned int to_flush = heap[0].id; - pop_heap(heap.begin(), heap.end()); - heap.pop_back(); - FlushPatch(to_flush); - } - entry.patch = LoadPatch(patch); - heap.push_back(Item(patch, error)); - push_heap(heap.begin(), heap.end()); - return *(entry.patch); -} - -void NexusMt::Flush(bool all) { - if(all) { - for(unsigned int i = 0; i < heap.size(); i++) { - unsigned int patch = heap[i].id; - FlushPatch(patch); - } - heap.clear(); - } else { - while(heap.size() && ram_used > ram_max) { - if(heap[0].error == 0) break; - unsigned int to_flush = heap[0].id; - pop_heap(heap.begin(), heap.end()); - heap.pop_back(); - FlushPatch(to_flush); - } - } -} - -bool NexusMt::CanAdd(Item &item) { - if(!heap.size()) return true; - Entry &entry = operator[](item.id); - if(ram_used + entry.ram_size < ram_max) - return true; - return heap[0].error > item.error; -} - -void NexusMt::FlushPatch(unsigned int id) { - Entry &entry = operator[](id); - if(entry.vbo_element) - FlushVbo(entry); - - if(entry.patch->fstart) - delete [](entry.patch->fstart); - delete entry.patch; - entry.patch = NULL; - ram_used -= entry.ram_size; -} - -void NexusMt::LoadVbo(Entry &entry) { - assert(entry.vbo_element == 0); - // if(entry.vbo_element) return; - - Patch &patch = *entry.patch; - unsigned int size = patch.nf * sizeof(unsigned short); - if(signature.face == Signature::TRIANGLES) - size *= 3; - else if(signature.face != Signature::STRIPS) - assert(0); - - glGenBuffersARB(1, &entry.vbo_element); - assert(entry.vbo_element); - glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, entry.vbo_element); - glBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, size, patch.FaceBegin(), - GL_STATIC_DRAW_ARB); - vbo_used += size; - - //TODO fix this when we allow data :p - size = patch.vstartd - patch.vstart; - - glGenBuffersARB(1, &entry.vbo_array); - assert(entry.vbo_array); - glBindBufferARB(GL_ARRAY_BUFFER_ARB, entry.vbo_array); - glBufferDataARB(GL_ARRAY_BUFFER_ARB, size, patch.Vert3fBegin(), - GL_STATIC_DRAW_ARB); - - vbo_used += size; - delete [](entry.patch->fstart); - entry.patch->fstart = NULL; -} - -void NexusMt::FlushVbo(Entry &entry) { - if(!entry.vbo_element) return; - glDeleteBuffersARB(1, &entry.vbo_element); - glDeleteBuffersARB(1, &entry.vbo_array); - entry.vbo_element = 0; - entry.vbo_array = 0; - - Patch &patch = *entry.patch; - vbo_used -= patch.nf * sizeof(unsigned short); - vbo_used -= sizeof(float) * (patch.vstart - patch.vstartd); -} - -//Kept for historical reasons. -/*void NexusMt::ExtractFixed(vector &selected, float error) { - std::vector::iterator n; - for(n = nodes.begin(); n != nodes.end(); n++) - (*n).visited = false; - - std::queue qnodo; - qnodo.push(&nodes[0]); - nodes[0].visited = true; - - for( ; !qnodo.empty(); qnodo.pop()) { - Node &node = *qnodo.front(); - - std::vector::iterator fragment; - std::vector::iterator on; - for(on = node.out.begin(), fragment = node.frags.begin(); - on != node.out.end(); ++on, ++fragment) { - - if((*on)->visited) continue; - - if(error < (*on)->error) { //need to expand this node. - qnodo.push(*on); - (*on)->visited = 1; - } else { - vector::iterator cell; - for(cell=(*fragment).begin(); cell != (*fragment).end(); ++cell) selected.push_back(*cell); - } - } - } -} */ diff --git a/apps/nexus/nexusmt.h b/apps/nexus/nexusmt.h deleted file mode 100644 index d01c6b14..00000000 --- a/apps/nexus/nexusmt.h +++ /dev/null @@ -1,162 +0,0 @@ -/**************************************************************************** -* 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 $ -Revision 1.24 2005/02/17 15:39:44 ponchio -Reorderes statistics a bit. - -Revision 1.23 2005/02/14 17:11:07 ponchio -aggiunta delle sphere - -Revision 1.22 2005/02/10 09:18:20 ponchio -Statistics. - -Revision 1.21 2005/02/08 12:43:03 ponchio -Added copyright - - -****************************************************************************/ - -#ifndef NXS_MT_H -#define NXS_MT_H - -#include -#include -#include - -#include "nexus.h" -#include "history.h" -#include "extraction.h" -#include "metric.h" -#include "preload.h" -#include "watch.h" - -namespace nxs { - - struct DrawContest { - - enum Mode { POINTS, SMOOTH, XRAY, HIDDEN_LINE, FLAT_WIRE, FLAT, PATCHES }; - enum Attr { COLOR = 0x1, NORMAL = 0x2, TEXTURE = 0x4, DATA = 0x8, SPHERES = 0x10 }; - - Mode mode; - unsigned int attrs; - - DrawContest(Mode m = SMOOTH, unsigned int a = 0xf): mode(m), attrs(a) {} - void SetAttr(Attr attr, bool value); - bool HasComponent(Attr attr); - }; - - struct Stats { - //per frame data... - float tri; //k triangles rendered. - float extr; //k triangles extracted - float disk_tri; //k triangles readed from disk - - int log_size; - - deque error; //max error in extraction (push_front pop_back) - deque time; - deque disk; //kdisk readed per frame - - float fps; //averaged over 8 frames - - Watch watch; - - Stats(): log_size(48), fps(0.0f) {} - void Start(); - void Disk(float disk); - void Error(float error); - void Stop(); - }; - - class NexusMt: public Nexus { - public: - bool use_vbo; - bool prefetch; - - unsigned int vbo_used; //TODO remember to zero it! - - Preload preload; - - NexusMt(); - ~NexusMt(); - - bool Load(const std::string &filename); - // void Close(); - - bool InitGL(bool use_vbo = true); - - void Render(DrawContest contest = DrawContest()); - void Render(Extraction &extraction, - DrawContest &contest, - Stats *stats = NULL); - - void SetPreload(bool on); - - void Flush(bool all = true); - Patch &GetPatch(unsigned int patch, float error, bool flush = true); - bool CanAdd(Item &item); - protected: - std::vector heap; - - void FlushPatch(unsigned int id); - void LoadVbo(Entry &entry); - void FlushVbo(Entry &entry); - void Draw(unsigned int cell, DrawContest &contest); - - }; - - /* class NexusViewer { - public: - NexusMt &mt; - - DrawContest contest; - Extraction extraction; - Statistics stats; - - NexusViewer(NexyusMt &_mt): mt(&mt) {} - void Render(); - - - bool SetMode(DrawContest::Mode mode); - bool SetComponent(DrawContest::Component c, bool on); - bool SetComponents(unsigned int mask); - - void SetMetric(MetricKind kind); - void SetError(float error); - - //Uinits expressed in Kb - void SetExtractionSize(unsigned int ksize); - void SetDrawSize(unsigned int ksize); - void SetDiskDelta(unsigned int kdelta); - //void SetPrefetchSize(unsigned int size); - };*/ - - - - -} - -#endif diff --git a/apps/nexus/nexusview.cpp b/apps/nexus/nexusview.cpp deleted file mode 100644 index ddf24cca..00000000 --- a/apps/nexus/nexusview.cpp +++ /dev/null @@ -1,749 +0,0 @@ -/**************************************************************************** - * 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 $ -Revision 1.46 2005/04/04 14:27:53 ponchio -*** empty log message *** - -Revision 1.45 2005/03/02 10:40:17 ponchio -Extraction rewrittten (to fix recusive problems). - -Revision 1.44 2005/03/01 11:20:22 ponchio -nothing really - -Revision 1.43 2005/02/20 18:07:01 ponchio -cleaning. - -Revision 1.42 2005/02/20 00:43:24 ponchio -Less memory x extraction. (removed frags) - -Revision 1.41 2005/02/19 12:06:55 ponchio -Debug... - -Revision 1.40 2005/02/17 15:39:44 ponchio -Reorderes statistics a bit. - -Revision 1.39 2005/02/17 14:02:03 ponchio -Full screen options... - -Revision 1.38 2005/02/16 15:52:09 ponchio -qualche opzione in piu' , tolti i grafici - -Revision 1.37 2005/02/15 15:55:36 ponchio -aggiunta delle sphere - -Revision 1.36 2005/02/14 17:11:08 ponchio -aggiunta delle sphere - -Revision 1.35 2005/02/14 14:49:09 ponchio -*** empty log message *** - -Revision 1.34 2005/02/14 14:21:24 ponchio -Preload disabled at startap (-p) - -Revision 1.33 2005/02/10 09:18:20 ponchio -Statistics. - -Revision 1.32 2005/02/03 12:35:01 ponchio -Patch cache -> heap - -Revision 1.31 2005/02/01 16:42:30 ponchio -Trigger - -Revision 1.30 2005/01/21 17:09:13 ponchio -Porting and debug. - -Revision 1.29 2005/01/17 17:35:47 ponchio -Small changes and adding realtime extr. - -Revision 1.28 2005/01/14 15:25:29 ponchio -Revolution. - -Revision 1.27 2004/12/15 16:37:55 ponchio -Optimizing realtime vis. - -Revision 1.26 2004/12/15 13:50:32 ponchio -Optimizing realtime vis. - -Revision 1.25 2004/12/15 08:46:16 ponchio -Optimizing realtime vis. - -Revision 1.24 2004/12/13 00:44:48 ponchio -Lotsa changes... - -Revision 1.23 2004/12/01 18:46:21 ponchio -Microchanges. - -Revision 1.22 2004/11/28 04:16:19 ponchio -*** empty log message *** - -Revision 1.21 2004/11/28 01:23:26 ponchio -Fixing borders... let's hope. - -Revision 1.20 2004/11/18 18:30:14 ponchio -Using baricenters... lotsa changes. - -Revision 1.19 2004/10/30 20:17:03 ponchio -Fixed big patches problem. - -Revision 1.18 2004/10/21 13:40:16 ponchio -Debugging. - -Revision 1.17 2004/10/21 12:22:21 ponchio -Small changes. - -Revision 1.16 2004/10/19 01:23:02 ponchio -Daily backup (fragment...) - -Revision 1.15 2004/10/15 16:45:27 ponchio -Vbo added. - -Revision 1.14 2004/10/14 13:52:02 ponchio -Small changes. - -Revision 1.13 2004/10/14 13:41:34 ponchio -Added statistics. - -Revision 1.12 2004/10/09 14:46:47 ponchio -Windows porting small changes. - -Revision 1.11 2004/10/04 16:49:54 ponchio -Daily backup. Preparing for compression. - -Revision 1.10 2004/10/01 16:54:57 ponchio -Daily backup. - -Revision 1.9 2004/09/30 23:56:33 ponchio -Backup (added strips and normals) - -Revision 1.8 2004/09/30 00:27:42 ponchio -Lot of changes. Backup. - -Revision 1.7 2004/09/28 10:26:35 ponchio -Backup - -Revision 1.6 2004/09/17 15:25:09 ponchio -First working (hopefully) release. -Revision 1.5 2004/09/16 14:25:16 ponchio -Backup. (lot of changes). - -Revision 1.4 2004/08/27 00:38:34 ponchio -Minor changes. - -Revision 1.3 2004/07/15 14:32:49 ponchio -Debug. - -Revision 1.2 2004/07/05 15:49:39 ponchio -Windows (DevCpp, mingw) port. - -Revision 1.1 2004/07/04 15:30:00 ponchio -Changed directory structure. - -Revision 1.2 2004/07/04 15:16:01 ponchio -*** empty log message *** - -Revision 1.1 2004/07/02 17:41:57 ponchio -Created. - -Revision 1.3 2004/07/02 13:03:34 ponchio -*** empty log message *** - -Revision 1.2 2004/07/01 21:33:46 ponchio -Added remap reading. - -Revision 1.1 2004/06/23 00:10:38 ponchio -Created - - -****************************************************************************/ -#ifdef WIN32 -#include -#else -#include -#endif - -#include -#include -#include - -using namespace std; - -#include - -//this include MUST precede GL includes. -#include - -#ifdef WIN32 -#include -#endif - -#include -#include -#include - -#include -#include "watch.h" - - -using namespace vcg; -using namespace nxs; - -bool fullscreen = false; -int width = 1024; -int height = 768; - -Point3f view(0, 0, 5); -Point3f target(0, 0, 0); - -void gl_print(float x, float y, char *str); - -SDL_Surface *screen = NULL; - -bool init(const string &str) { - - if(SDL_Init(SDL_INIT_VIDEO) != 0) { - return false; - } - - const SDL_VideoInfo *info = SDL_GetVideoInfo(); - int bpp = info->vfmt->BitsPerPixel; - - SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5); - SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); - - int flags = SDL_OPENGL; - if(fullscreen) - flags |= SDL_FULLSCREEN; - - screen = SDL_SetVideoMode(width, height, bpp, flags); - if(!screen) { - return false; - } - - // SDL_WM_SetIcon(SDL_LoadBMP("icon.bmp"), NULL); - SDL_WM_SetCaption(str.c_str(), str.c_str()); - - - glDisable(GL_DITHER); - glShadeModel(GL_SMOOTH); - glHint( GL_FOG_HINT, GL_NICEST ); - glEnable(GL_DEPTH_TEST); - glDepthFunc( GL_LEQUAL ); - glDisable(GL_LIGHTING); - - glEnableClientState(GL_VERTEX_ARRAY); - return true; -} - - - - -int main(int argc, char *argv[]) { - int level = 0; - int apatch = -1; - float error = 4; - string file; - - Trackball track; - - if(argc < 2) { - cerr << "Usage: " << argv[0] << " [options]\n"; - cerr << "-e : set initial target error\n" - << "-m : max ram used\n" - << "-x : max extraction size\n" - << "-r : max draw size\n" - << "-d : max disk read per frame\n" - << "-p : no preload\n" - << "-w : window width\n" - << "-h : window height\n" - << "-f : fullscreen mode\n" - << "-l : log timing on file\n" - << "-o namefile: ouput stats"; - return -1; - } - - NexusMt nexus; - if(!nexus.Load(argv[1])) { - cerr << "Could not load nexus file: " << argv[1] << endl; - return -1; - } - - Sphere3f sphere = nexus.sphere; - Extraction extraction; - DrawContest contest; - Stats stats; - stats.Start(); - - - bool rotate = false; - bool show_borders = false; - bool do_render = true; - bool show_statistics = true; - bool extract = true; - bool realtime = true; - bool preload = true; - Point4f light(1, -1, 1, 0); - - bool output_stats = false; - char output_filename[100]; - - char window_name [100]; - sprintf(window_name,"%s", argv[1]); - - int option; - while((option = getopt(argc, argv, "e:m:x:r:d:l:o:w:h:p:f")) != EOF) { - switch(option) { - case 'e': extraction.target_error = atof(optarg); break; - case 'm': nexus.MaxRam() = atoi(optarg); break; - case 'x': extraction.extr_max = atoi(optarg); break; - case 'r': extraction.draw_max = atoi(optarg); break; - case 'd': extraction.disk_max = atoi(optarg); break; - case 'l': file = optarg; break; - case 'o': output_stats = true; sprintf(output_filename,"%s",optarg); break; - case 'w': width = atoi(optarg); break; - case 'h': height = atoi(optarg); break; - case 'p': preload = false; nexus.SetPreload(preload); break; - case 'f': fullscreen = true; break; - default: - cerr << "Unknow option.\n"; break; - } - } - - FILE *fp = NULL; - if(file != "") { - fp = fopen(file.c_str(), "rwb+"); - if(!fp) { - cerr << "Could not open log file: " << file << endl; - return -1; - } - fprintf(fp, "timeMs, error, extrTri, drawTri, diskTri, diskKb\n"); - } - - if(!init(window_name)) { - cerr << "Could not init SDL window\n"; - return -1; - } - - cerr << "Commands: \n" - " q : quit\n" - " t : toggle statistics\n" - " right arrow: increase memory buffer\n" - " left arrow : decrease memory buffer\n" - " page up : increase disk space\n" - " page down : increase disk space\n" - " 0 : decrease extraction size\n" - " 1 : increase extraction size\n" - " s : toggle preload\n" - " l : change light\n" - - - " d: debug mode (show patches colored)\n" - " f: flat shading mode\n" - " m: smooth mode\n" - " p: draw points\n" - " h: draw bounding spheres\n" - " w: disable glcalls\n" - - " c: show colors\n" - " n: show normals\n" - " r: rotate model\n" - " -: decrease error\n" - " +: increase error (= too)\n"; - - Watch watch; - - if(!nexus.InitGL()) { - cerr << "Could not init glew.\n"; - return -1; - } - - glClearColor(0, 0, 0, 0); - glEnable(GL_LIGHTING); - glEnable(GL_LIGHT0); - glEnable(GL_NORMALIZE); - glEnable(GL_COLOR_MATERIAL); - glEnable(GL_CULL_FACE); - int quit = 0; - SDL_Event event; - int x, y; - float alpha = 0; - float fps = 0; - unsigned int nave = 5; - unsigned int offset = 0; - deque tframe; - deque terror; - unsigned int tlen = 256; - - bool keepdrawing = true; - - watch.Start(); - while( !quit ) { - unsigned int anything = SDL_PollEvent(&event); - if(!anything && !keepdrawing) { - SDL_WaitEvent(&event); - anything = true; - } - while(anything) { - switch( event.type ) { - case SDL_QUIT: quit = 1; break; - case SDL_KEYDOWN: - switch(event.key.keysym.sym) { - case SDLK_RCTRL: - case SDLK_LCTRL: track.ButtonDown(Trackball::KEY_CTRL); break; - case SDLK_q: exit(0); break; - case SDLK_k: keepdrawing = !keepdrawing; break; - case SDLK_e: extract = !extract; break; - case SDLK_c: - if(contest.attrs & DrawContest::COLOR) - contest.attrs &= ~DrawContest::COLOR; - else - contest.attrs |= DrawContest::COLOR; break; - - case SDLK_n: - if(contest.attrs & DrawContest::NORMAL) - contest.attrs &= ~DrawContest::NORMAL; - else - contest.attrs |= DrawContest::NORMAL; break; - - case SDLK_w: do_render = !do_render; break; - - case SDLK_LEFT: nexus.MaxRam() *= 0.8; break; - case SDLK_RIGHT: nexus.MaxRam() *= 1.3; break; - case SDLK_UP: extraction.draw_max *= 1.3; break; - case SDLK_DOWN: extraction.draw_max *= 0.8; break; - case SDLK_PAGEUP: extraction.disk_max *= 1.3; break; - case SDLK_PAGEDOWN: extraction.disk_max *= 0.8; break; - case SDLK_0: extraction.extr_max *= 1.3; break; - case SDLK_9: extraction.extr_max *= 0.8; break; - - case SDLK_p: contest.mode = DrawContest::POINTS; break; - case SDLK_d: contest.mode = DrawContest::PATCHES; break; - case SDLK_f: contest.mode = DrawContest::FLAT; break; - case SDLK_m: contest.mode = DrawContest::SMOOTH; break; - case SDLK_h: if(contest.attrs&DrawContest::SPHERES) - contest.attrs &=~DrawContest::SPHERES; - else - contest.attrs |=DrawContest::SPHERES; - break; - - case SDLK_l: - light[0] = rand(); - light[1] = rand(); - light[2] = rand(); - light.Normalize(); - break; - case SDLK_o: realtime = !realtime; break; - case SDLK_s: preload = !preload; nexus.SetPreload(preload); break; - case SDLK_t: show_statistics = !show_statistics; break; - case SDLK_r: - case SDLK_SPACE: rotate = !rotate; break; - - case SDLK_MINUS: - extraction.target_error *= 0.9f; - cerr << "Error: " << extraction.target_error << endl; - break; - - case SDLK_EQUALS: - case SDLK_PLUS: - extraction.target_error *= 1.1f; - cerr << "Error: " << extraction.target_error << endl; - break; - } - break; - case SDL_KEYUP: - switch(event.key.keysym.sym) { - case SDLK_RCTRL: - case SDLK_LCTRL: - track.ButtonUp(Trackball::KEY_CTRL); break; - } - break; - case SDL_MOUSEBUTTONDOWN: - x = event.button.x; - y = height - event.button.y; -#ifdef SDL_BUTTON_WHEELUP - if(event.button.button == SDL_BUTTON_WHEELUP) - track.MouseWheel(1); - else if(event.button.button == SDL_BUTTON_WHEELDOWN) - track.MouseWheel(-1); - else -#endif - if(event.button.button == SDL_BUTTON_LEFT) - track.MouseDown(x, y, Trackball::BUTTON_LEFT); - else if(event.button.button == SDL_BUTTON_RIGHT) - track.MouseDown(x, y, Trackball::BUTTON_RIGHT); - break; - case SDL_MOUSEBUTTONUP: - x = event.button.x; - y = height - event.button.y; - if(event.button.button == SDL_BUTTON_LEFT) - track.MouseUp(x, y, Trackball::BUTTON_LEFT); - else if(event.button.button == SDL_BUTTON_RIGHT) - track.MouseUp(x, y, Trackball::BUTTON_RIGHT); - break; - case SDL_MOUSEMOTION: - while(SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_MOUSEMOTIONMASK)); - x = event.motion.x; - y = height - event.motion.y; - track.MouseMove(x, y); - break; - case SDL_VIDEOEXPOSE: - default: break; - } - anything = SDL_PeepEvents(&event, 1, SDL_GETEVENT, - SDL_ALLEVENTS); - } - - - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - gluPerspective(40, width/(float)height, 0.1, 100); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - - // gluLookAt(0,0,5, 0,0,0, 0,1,0); - gluLookAt(view[0],view[1], view[2], - target[0],target[1],target[2], 0,1,0); - - - track.GetView(); - track.Apply(); - - - glLightfv(GL_LIGHT0, GL_POSITION, &light[0]); - - - glViewport(0,0,width,height); - glRotatef(alpha, 0, 1, 0); - if(rotate) { - alpha++; - if(alpha > 360) alpha = 0; - if(!keepdrawing) { - SDL_Event redraw; - redraw.type = SDL_VIDEOEXPOSE; - SDL_PushEvent(&redraw); - } - } - - float scale = 2/sphere.Radius(); - - glScalef(scale, scale, scale); - Point3f center = sphere.Center(); - glTranslatef(-center[0], -center[1], -center[2]); - - // Point3f &p = nexus.sphere.Center(); - // float r = nexus.sphere.Radius(); - - - glColor3f(0.8f, 0.8f, 0.8f); - - if(extract) { - // Sould we want a different view from metric and extraction - // extraction.frustum->GetView(); - extraction.metric->GetView(); - if(!realtime) { - extraction.visited.clear(); - extraction.Extract(&nexus); - extraction.Update(&nexus); - } else { - extraction.Update(&nexus); - } - } - stats.Stop(); - stats.Start(); - if(do_render) - nexus.Render(extraction, contest, &stats); - - - - /* if(show_borders) { - for(unsigned int i = 0; i < cells.size(); i++) { - Border &border = nexus.GetBorder(cells[i]); - Patch &patch = nexus.GetPatch(cells[i]); - glPointSize(4); - glColor3f(1.0f, 1.0f, 1.0f); - glBegin(GL_POINTS); - for(unsigned int b = 0; b < border.Size(); b++) { - Link &link = border[b]; - Point3f &p = patch.Vert(link.start_vert); - glVertex3f(p[0], p[1], p[2]); - } - glEnd(); - glPointSize(1); - } - }*/ - - tframe.push_front(watch.Time()); - if(tframe.size() > tlen) tframe.pop_back(); - - terror.push_front(extraction.max_error); - if(terror.size() > tlen) terror.pop_back(); - - if(show_statistics) { - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - gluOrtho2D(0, 1, 0, 1); - - - glDisable(GL_DEPTH_TEST); - glDisable(GL_LIGHTING); - - //show frame and error graphics - if(false){ - glColor4f(0.6f, 0.6f, 0.6f, 0.5f); - - glBegin(GL_LINE_STRIP); - for(unsigned int i = 0; i < tframe.size() -1; i++) { - double diff = (tframe[i] - tframe[i+1]); - //glVertex2f(i/1024.0f,0); - glVertex2f(i/1024.0f,2*diff); - } - glEnd(); - - glColor4f(0.0f, 0.6f, 0.2f, 0.5f); - - glBegin(GL_LINE_STRIP); - for(unsigned int i = 0; i < terror.size() -1; i++) { - // glVertex2f(i/1024.0f,0); - glVertex2f(i/1024.0f,terror[i]/300); - } - glEnd(); - } - - glColor3f(1.0f, 1.0f, 1.0f); - - char buffer[1024]; - sprintf(buffer, "Ram size : %.2f / %.2f Mb", - nexus.ram_used * nexus.chunk_size/(float)(1<<20), - nexus.ram_max * nexus.chunk_size/(float)(1<<20)); - gl_print(0.03, 0.15, buffer); - - sprintf(buffer, "Extr size: %.2f / %.2f Mb", - extraction.extr_used * nexus.chunk_size/(float)(1<<20), - extraction.extr_max * nexus.chunk_size/(float)(1<<20)); - gl_print(0.03, 0.12, buffer); - - sprintf(buffer, "Draw size: %.2f / %.2f Mb", - extraction.draw_used * nexus.chunk_size/(float)(1<<20), - extraction.draw_max * nexus.chunk_size/(float)(1<<20)); - gl_print(0.03, 0.09, buffer); - - sprintf(buffer, "Disk size: %.2f / %.2f Mb", - extraction.disk_max * nexus.chunk_size/(float)(1<<20), - extraction.disk_used * nexus.chunk_size/(float)(1<<20)); - gl_print(0.03, 0.06, buffer); - - sprintf(buffer, "%.2f KTri %.2f FPS %.0f M/s", - stats.tri/(float)(1<<10), - stats.fps, stats.fps * stats.tri/(float)(1<<20)); - gl_print(0.03, 0.03, buffer); - - - glEnable(GL_DEPTH_TEST); - glEnable(GL_LIGHTING); - glPopMatrix(); - glMatrixMode(GL_MODELVIEW); - glPopMatrix(); - - /* statistics: output on file - if(output_stats){ - - static Stats statsAcc; - static float ram_used; - static float extr_used; - static float draw_used; - static float disk_used; - static std::ofstream outf(output_filename); - static bool first=true; - if(first) { - outf<< "ktri\t fps\t ram \t extr \t draw \t disk \n" - << " \t \t" - << nexus.ram_max * nexus.chunk_size/(float)(1<<20) << "\t" - << extraction.extr_max * nexus.chunk_size/(float)(1<<20) << "\t" - << extraction.draw_max * nexus.chunk_size/(float)(1<<20)<<"\t" - << extraction.disk_max * nexus.chunk_size/(float)(1<<20)<< "\n"; - first = false; - } - - statsAcc.count++ ; - if((statsAcc.count%30)==0) { - outf - << (statsAcc.tri/(float)statsAcc.count)/(float)(1<<10) << "\t" - << (statsAcc.fps/(float)statsAcc.count) << "\t" - // << (statsAcc.kdisk/(float)statsAcc.count) << "\t" - << ram_used/(float)statsAcc.count*nexus.chunk_size/(float)(1<<20) << "\t" - << extr_used/(float)statsAcc.count*nexus.chunk_size/(float)(1<<20) << "\t" - << draw_used/(float)statsAcc.count*nexus.chunk_size/(float)(1<<20) << "\t" - << disk_used/(float)statsAcc.count*nexus.chunk_size/(float)(1<<20) << "\t" - << "\n"; - statsAcc.Init(); - statsAcc.count = 0; - statsAcc.fps = 0; - ram_used = extr_used = draw_used = disk_used = 0.0; - } else { - statsAcc.fps += stats.fps; - statsAcc.kdisk += stats.kdisk; - statsAcc.ktri += stats.ktri; - - ram_used += nexus.ram_used; - extr_used += extraction.extr_used; - draw_used += extraction.draw_used; - disk_used += extraction.disk_used; - } - }*/ - } - - SDL_GL_SwapBuffers(); - if(fp && stats.time.size()) { - //fprintf(fp, "timeMs, error, extrTri, drawTri, diskTri, diskKb\n"); - assert(stats.time.size() && stats.error.size() && stats.disk.size()); - fprintf(fp, "%d, %f, %d, %d, %d, %d\n", - (*stats.time.begin())*1000, - stats.error.begin(), - (int)stats.extr, - (int)stats.tri, - (int)stats.disk_tri, - (int)((*stats.disk.begin())/1024)*nexus.chunk_size); - } - } - if(fp) - fclose(fp); - - SDL_Quit(); - return -1; -} - -void gl_print(float x, float y, char *str) { - glRasterPos2f(x, y); - int len = (int)strlen(str); - for(int i = 0; i < len; i++) - glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18, str[i]); -} - - diff --git a/apps/nexus/normalscone.cpp b/apps/nexus/normalscone.cpp deleted file mode 100644 index 20f80a2d..00000000 --- a/apps/nexus/normalscone.cpp +++ /dev/null @@ -1,180 +0,0 @@ -#include "normalscone.h" -#include - -using namespace std; -using namespace vcg; -using namespace nxs; - - -ANCone3f::ANCone3f() { - scaledNormal = Point3f(0,0,0); - frontAnchor = Point3f(0,0,0); - backAnchor = Point3f(0,0,0); -} - -bool ANCone3f::Frontface(const Point3f &viewPoint) { - Point3f d = frontAnchor - viewPoint; //Vector from viewPoint to frontAnchor. - float f = - d * scaledNormal; - if (f < 0.001 || f * f < d * d) - return false; - return true; -} - -bool ANCone3f::Backface(const Point3f &viewPoint) { - Point3f d = backAnchor - viewPoint; //Vector from viewPoint to frontAnchor. - float f = d * scaledNormal; - if (f < 0.001 || f * f < d * d) - return false; - return true; -} - -void ANCone3f::AddNormals(vector &normal, vector &area, float threshold) { - assert(normal.size() == area.size()); - scaledNormal = Point3f(0,0,0); - int count = 0; - vector::iterator i; - for(i = normal.begin(); i != normal.end(); i++) { - scaledNormal += *i; - count++; - } - scaledNormal /= count; - scaledNormal.Normalize(); - - double distr[50]; - for(int k = 0; k < 50; k++) - distr[k] = 0; - double tot_area = 0; - - vector::iterator j; - for(i = normal.begin(), j = area.begin(); i != normal.end(); i++, j++) { - int pos = (int)(49.0 * Angle(scaledNormal, *i)/M_PI); - if(pos < 0) continue; - assert(pos >=0 && pos < 50); - distr[pos] += *j; - tot_area += *j; - } - - float tot = 0; - int best; - for(best = 0; best < 50; best++) { - tot += distr[best]; - if(tot > threshold * tot_area) - break; - } - double alpha = M_PI * (best + 1) / 50; - if(alpha >= M_PI/ 2 - 0.1) - scaledNormal = Point3f(0,0,0); - else - scaledNormal /= cos(M_PI/2 - alpha); -} - -void ANCone3f::AddNormals(vector &normal, float threshold) { - assert(normal.size() > 0); - scaledNormal = Point3f(0,0,0); - int count = 0; - vector::iterator i; - for(i = normal.begin(); i != normal.end(); i++) { - Point3f norm = *i; - if(norm.Norm() < 0.00001) continue; - norm.Normalize(); - scaledNormal += norm; - count++; - } - scaledNormal /= count; - float len = scaledNormal.Norm(); - if(len == 0) return; - scaledNormal /= len; - - int distr[50]; - for(int k = 0; k < 50; k++) - distr[k] =0; - - for(i = normal.begin(); i != normal.end(); i++) { - int pos = (int)(50.0 * Angle(scaledNormal, *i)/M_PI); - distr[pos]++; - } - int tot = 0; - int best; - for(best = 0; best < 50; best++) { - tot += distr[best]; - if(tot >= threshold * normal.size()) - break; - } - double alpha = M_PI * (best +1) / 50; - if(alpha >= M_PI/ 2 - 0.1) { - scaledNormal = Point3f(0,0,0); - } else { - scaledNormal /= cos(M_PI/2 - alpha); - } -} - -void ANCone3f::AddAnchors(vector &anchors) { - assert(anchors.size() > 0); - frontAnchor = anchors[0]; - backAnchor = anchors[0]; - - float fa = frontAnchor * scaledNormal; - float fb = -backAnchor * scaledNormal; - - vector::iterator i; - for(i = anchors.begin(); i != anchors.end(); i++) { - Point3f &anchor = *i; - float na = anchor * scaledNormal; - if(na < fa) { - frontAnchor = anchor; - fa = na; - } - if(-na < fb) { - backAnchor = anchor; - fb = -na; - } - } -} - -void NCone3s::Import(const ANCone3f &c) { - Point3f normal = c.scaledNormal; - float len = normal.Norm(); - if(len != 0) - normal /= len; - - for(int i = 0; i < 3; i++) { - assert(normal[i] < 1.01 && normal[i] > -1.01); - - if(normal[i] > 1.0f) normal[i] = 1; - if(normal[i] < -1.0f) normal[i] = -1; - } - n[0] = (short)(normal[0] * 32766); - n[1] = (short)(normal[1] * 32766); - n[2] = (short)(normal[2] * 32766); - //i want to rapresent number from 1 to 10 - if(len > 10.0f) len = 10.0f; - if(len < -10.0f) len = -10.0f; - n[3] = (short)(len * 3276); -} - - - - -bool NCone3s::Backface(const vcg::Sphere3f &sphere, - const vcg::Point3f &view) const { - vcg::Point3f norm(n[0]/32766.0f, n[1]/32766.0f, n[2]/32766.0f); - vcg::Point3f d = (sphere.Center() - norm * sphere.Radius()) - view; - norm *= n[3]/3276.0f; - - float f = d * norm; - if (f < 0.001 || f * f < d * d) - return false; - return true; -} - -bool NCone3s::Frontface(const vcg::Sphere3f &sphere, - const vcg::Point3f &view) const { - vcg::Point3f norm(n[0]/32766.0f, n[1]/32766.0f, n[2]/32766.0f); - vcg::Point3f d = (sphere.Center() + norm * sphere.Radius()) - view; - norm *= n[3]/3276.0f; - - float f = -d * norm; - if (f < 0.001 || f * f < d * d) - return false; - return true; -} diff --git a/apps/nexus/normalscone.h b/apps/nexus/normalscone.h deleted file mode 100644 index af017531..00000000 --- a/apps/nexus/normalscone.h +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef NXS_NORMALS_CONE_H -#define NXS_NORMALS_CONE_H - -#include - -#include -#include - -namespace nxs { - - - //anchored normal cone. - class ANCone3f { - public: - ANCone3f(); - - bool Frontface(const vcg::Point3f &viewPoint); - bool Backface(const vcg::Point3f &viewPoint); - - //threshold [0, 1]: percentage of normals to left out - void AddNormals(std::vector &normals, - float threshold = 1.0f); - void AddNormals(std::vector &normals, - std::vector &areas, - float threshold = 1.0f); - void AddAnchors(std::vector &anchors); - protected: - vcg::Point3f scaledNormal; - vcg::Point3f frontAnchor; - vcg::Point3f backAnchor; - - friend class NCone3s; - }; - - - - //this is not anchored... need a bounding sphere to report something - class NCone3s { - public: - void Import(const ANCone3f &c); - - bool Backface(const vcg::Sphere3f &sphere, - const vcg::Point3f &view) const; - bool Frontface(const vcg::Sphere3f &sphere, - const vcg::Point3f &view) const; - - //encode normal and sin(alpha) in n[3] - short n[4]; - }; - -} - -#endif diff --git a/apps/nexus/nxs2ply.cpp b/apps/nexus/nxs2ply.cpp deleted file mode 100644 index 86c49edc..00000000 --- a/apps/nexus/nxs2ply.cpp +++ /dev/null @@ -1,72 +0,0 @@ -// mesh definition -#include -#include -#include - -#include - -// input output -#include -#include - -#include "nxsexport.h" -#include "extraction.h" -#include "metric.h" - -using namespace vcg; -using namespace nxs; - -struct MyFace; -struct MyTetra; -struct MyEdge; -struct MyVertex: public VertexAFVNf{}; -struct MyFace: public FaceAF{}; -struct MyMesh: public tri::TriMesh< vector, vector >{}; - -int main(int argc, char *argv[]) { - - string input; - string output; - float target_error = 0; - - int option; - while((option = getopt(argc, argv, "e:")) != EOF) { - switch(option) { - case 'e': target_error = atof(optarg); break; - case 'o': output = optarg; break; - } - } - - if(optind != argc -1) { - cerr << "Usage: " << argv[0] << " [options]\n" - << " -e : extraction target error\n" - << " -o : output name\n\n"; - return -1; - } - input = argv[optind]; - if(!output.size()) output = input + ".ply"; - - Nexus nexus; - if(!nexus.Load(input.c_str())) { - cerr << "Could not open file: " << input.c_str() << ".nxs\n"; - return -1; - } - - Extraction extr; - extr.SetMetric(new FlatMetric); - extr.target_error = target_error; - extr.Extract(&nexus); - - vector selected; - for(unsigned int i = 0; i < extr.selected.size(); i++) - selected.push_back(extr.selected[i].id); - - - MyMesh m; - ExportTriMesh(nexus, selected, m); - - //write to ply: - vcg::tri::io::PlyInfo pi; - vcg::tri::io::ExporterPLY::Save(m, output.c_str(), pi.mask); - return 0; -} diff --git a/apps/nexus/nxsalgo.cpp b/apps/nexus/nxsalgo.cpp deleted file mode 100644 index f14e88a7..00000000 --- a/apps/nexus/nxsalgo.cpp +++ /dev/null @@ -1,796 +0,0 @@ -/**************************************************************************** -* 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 $ -Revision 1.26 2005/03/02 10:40:18 ponchio -Extraction rewrittten (to fix recusive problems). - -Revision 1.25 2005/03/01 11:21:20 ponchio -Added line intersection - -Revision 1.24 2005/02/22 14:20:44 ponchio -debug and mostly vertex unifying across borders -(still not perfect... :P) - -Revision 1.23 2005/02/22 10:38:10 ponchio -Debug, cleaning and optimization. - -Revision 1.22 2005/02/21 17:55:36 ponchio -debug debug debug - -Revision 1.21 2005/02/20 18:07:01 ponchio -cleaning. - -Revision 1.20 2005/02/19 10:45:04 ponchio -Patch generalized and small fixes. - -Revision 1.19 2005/02/18 13:04:12 ponchio -Added patch reordering. - -Revision 1.18 2005/02/17 16:40:35 ponchio -Optimized BuildLevels. - -Revision 1.17 2005/02/08 12:43:03 ponchio -Added copyright - - -****************************************************************************/ - -#include -#include -#include -#include -#include - -//#include - -#include "nxsalgo.h" -#include "extraction.h" -#include "vpartition.h" -#include "vfile.h" -#include "nexus.h" -#include "zcurve.h" -#include "watch.h" - -#include -#include - -using namespace std; -using namespace nxs; -using namespace vcg; - - -void nxs::Connect(Nexus &nexus, std::vector< set > &close, - float threshold) { - - VPartition grid; - float max_radius = 0; - - for(unsigned int patch = 0; patch < nexus.size(); patch++) { - Sphere3f &sphere = nexus[patch].sphere; - grid.push_back(sphere.Center()); - float r = sphere.Radius(); - if(r > max_radius) max_radius = r; - } - grid.Init(); - close.clear(); - close.resize(nexus.size()); - - vector targets; - vector dists; - for(unsigned int patch = 0; patch < nexus.size(); patch++) { - float radius = nexus[patch].sphere.Radius(); - float max_distance = radius + max_radius + threshold; - max_distance *= max_distance; - grid.Closest(grid[patch], targets, dists, max_distance); - - for(unsigned int i = 0; i < targets.size(); i++) { - unsigned int target = targets[i]; - if(target == patch) continue; - float dist = radius + nexus[target].sphere.Radius() + threshold; - dist *= dist; - if(dist >= dists[i]) { - close[patch].insert(target); - } - } - } - - //DOUBLECROSS CHECK - for(unsigned int patch = 0; patch < nexus.size(); patch++) { - set::iterator i; - for(i = close[patch].begin(); i != close[patch].end(); i++) { - if(!close[*i].count(patch)) { - cerr << "Some problem width sphere intersection. Have alook.\n"; - cerr << "Meanwhile i fix it.\n"; - close[*i].insert(patch); - } - } - } -} - -void nxs::ComputeNormals(Nexus &nexus) { - assert(nexus.signature.vnorm); - assert(!nexus.borders.IsReadOnly()); - - //first calculate level 0 normals - //I load all patches in a fragment - //calculate normals - //fix external borders getting from level below - - //first try naive approach just load neighborough get normals and - //fix border with lower level - - vector levels; - nexus.history.BuildLevels(levels); - Report report(nexus.size(), 15); - - //TODO check level 0 is the finer one - - int current_level = 0; - while(1) { - int count = 0; - for(unsigned int p = 0; p < nexus.size(); p++) { - if(levels[p] != current_level) continue; - count++; - report.Step(p); - - Border &border = nexus.GetBorder(p); - - map > normals; - normals[p] = vector(); - - for(unsigned int i = 0; i < border.Size(); i++) { - Link &link = border[i]; - if(levels[link.end_patch] == current_level) - normals[link.end_patch] = vector(); - } - map >::iterator k; - for(k = normals.begin(); k != normals.end(); k++) { - Patch &patch = nexus.GetPatch((*k).first); - - vector &normal = (*k).second; - normal.resize(patch.nv, Point3f(0, 0, 0)); - - if(nexus.signature.face == Signature::TRIANGLES) { - for(unsigned int i = 0; i < patch.nf; i++) { - unsigned short *f = patch.Face(i); - Point3f &v0 = patch.Vert3f(f[0]); - Point3f &v1 = patch.Vert3f(f[1]); - Point3f &v2 = patch.Vert3f(f[2]); - - Point3f a = (v1 - v0); - Point3f b = (v2 - v0); - float n = a.Norm() * b.Norm(); - if(n == 0) continue; - Point3f norm = a^b; - float rn = norm.Norm(); - norm *= asin(rn/n)/n; - - normal[f[0]] += norm; - normal[f[1]] += norm; - normal[f[2]] += norm; - } - } else if(nexus.signature.face == Signature::STRIPS) { - for(int i = 0; i < patch.nf - 2; i++) { - unsigned short *f = patch.FaceBegin() + i; - Point3f &v0 = patch.Vert3f(f[0]); - Point3f &v1 = patch.Vert3f(f[1]); - Point3f &v2 = patch.Vert3f(f[2]); - - Point3f a = (v1 - v0); - Point3f b = (v2 - v0); - float n = a.Norm() * b.Norm(); - if(n == 0) continue; - Point3f norm = a^b; - float rn = norm.Norm(); - norm *= asin(rn/n)/n; - - - if(i%2) norm = -norm; - normal[f[0]] += norm; - normal[f[1]] += norm; - normal[f[2]] += norm; - } - } else - assert(0); - } - - //now fix borders - map > lowers; - for(unsigned int i = 0; i < border.Size(); i++) { - Link &link = border[i]; - if(levels[link.end_patch] == current_level) { - //TODO remove these asserts - assert(normals[p].size() > link.start_vert); - assert(normals.count(link.end_patch)); - assert(normals[link.end_patch].size() > link.end_vert); - normals[p][link.start_vert] += normals[link.end_patch][link.end_vert]; - } else if (levels[link.end_patch] < current_level) { - lowers[link.end_patch].push_back(link); - } - } - - map >::iterator s; - for(s = lowers.begin(); s != lowers.end(); s++) { - Patch &patch = nexus.GetPatch((*s).first); - for(unsigned int i = 0; i < (*s).second.size(); i++) { - Link &link = (*s).second[i]; - if(nexus.signature.vnorm == Encodings::FLOAT3) - normals[p][link.start_vert] = - ((Point3f *)patch.VNormBegin())[link.end_vert]; - else if(nexus.signature.vnorm == Encodings::SHORT4) { - Point3f &nor = normals[p][link.start_vert]; - short *n = ((short *)patch.VNormBegin()) + 4*link.end_vert; - nor[0] = n[0]; - nor[1] = n[1]; - nor[2] = n[2]; - } - } - } - //copy and normalize - Patch &patch = nexus.GetPatch(p); - Entry &entry = nexus[p]; - Point3f *norm = (Point3f *)patch.VNormBegin(); - vector &newnormals = normals[p]; - assert(newnormals.size() == patch.nv); - for(unsigned int i = 0; i < patch.nv; i++) { - newnormals[i].Normalize(); - if(nexus.signature.vnorm == Encodings::SHORT4) { - newnormals[i] *= 32766; - short *np = ((short *)norm) + 4 * i; - np[0] = (short)newnormals[i][0]; - np[1] = (short)newnormals[i][1]; - np[2] = (short)newnormals[i][2]; - np[3] = 0; - } else if(nexus.signature.vnorm == Encodings::FLOAT3) - norm[i] = newnormals[i]; - } - } - if(count == 0) break; - current_level++; - } -} - - - -/*void nxs::ComputeNormals(Nexus &nexus) { - assert(nexus.signature.vnorm); - - //setting borders readonly: - - assert(!nexus.borders.IsReadOnly()); - nexus.borders.SetReadOnly(true); - - //TODO use a temporary file to store border normals - unsigned int tmpb_offset = 0; - vector tmpb_start; - VFile tmpb; - if(!tmpb.Create("tmpb.tmp")) { - cerr << "Could not create temporary border file\n"; - exit(0); - } - - for(unsigned int p = 0; p < nexus.size(); p++) { - Border &border = nexus.borders[p]; - tmpb_start.push_back(tmpb_offset); - tmpb_offset += border.Size(); - } - - Point3f zero(0.0f, 0.0f, 0.0f); - - tmpb.Resize(tmpb_offset); - for(unsigned int i = 0; i < tmpb.Size(); i++) - tmpb[i] = zero; - tmpb.Flush(); - - - vector levels; - nexus.history.BuildLevels(levels); - - //first step normals in the same patch. - cerr << "First Step\n"; - Report report(nexus.size(), 5); - vector normals; - - for(unsigned int p = 0; p < nexus.size(); p++) { - int current_level = levels[p]; - report.Step(p); - Patch &patch = nexus.GetPatch(p); - - normals.clear(); - normals.resize(patch.nv, Point3f(0, 0, 0)); - - if(nexus.signature.face == Signature::TRIANGLES) - for(unsigned int i = 0; i < patch.nf; i++) { - unsigned short *f = patch.Face(i); - Point3f &v0 = patch.Vert3f(f[0]); - Point3f &v1 = patch.Vert3f(f[1]); - Point3f &v2 = patch.Vert3f(f[2]); - - Point3f norm = (v1 - v0) ^ (v2 - v0); - normals[f[0]] += norm; - normals[f[1]] += norm; - normals[f[2]] += norm; - } - if(nexus.signature.face == Signature::STRIPS) - for(int i = 0; i < patch.nf - 2; i++) { - unsigned short *f = patch.FaceBegin() + i; - Point3f &v0 = patch.Vert3f(f[0]); - Point3f &v1 = patch.Vert3f(f[1]); - Point3f &v2 = patch.Vert3f(f[2]); - - Point3f norm = (v1 - v0) ^ (v2 - v0); - if(i%2) norm = -norm; - normals[f[0]] += norm; - normals[f[1]] += norm; - normals[f[2]] += norm; - } - - if(nexus.signature.vnorm == Encodings::SHORT4) { - short *n = (short *)patch.VNormBegin(); - for(unsigned int i = 0; i < patch.nv; i++, n += 4) { - Point3f &norm = normals[i]; - norm.Normalize(); - for(int k = 0; k < 3; k++) - n[k] = (short)(norm[k] * 32766); - n[3] = 0; - } - } else if(nexus.signature.vnorm == Encodings::FLOAT3) { - Point3f *n = (Point3f *)patch.VNormBegin(); - for(unsigned int i = 0; i < patch.nv; i++) { - n[i] = normals[i]; - n[i].Normalize(); - } - } - - - Border &border = nexus.GetBorder(p); - - map > bnorm; - map bcopy; - - unsigned int poff = tmpb_start[p]; - for(unsigned int i = 0; i < border.Size(); i++) { - Link &link = border[i]; - if(link.IsNull()) continue; //this should never happen now. - Point3f pt = normals[link.start_vert]; - if(levels[link.end_patch] == current_level) { - bnorm[link.end_patch][link.end_vert] = pt; - tmpb[poff + i] += pt; - } else if(levels[link.end_patch] > current_level) { - bcopy[i] = link; - } - } - - map >::iterator k; - for(k = bnorm.begin(); k != bnorm.end(); k++) { - unsigned int patch = (*k).first; - Border &rborder = nexus.GetBorder(patch); - unsigned int offset = tmpb_start[patch]; - for(unsigned int i = 0; i < rborder.Size(); i++) { - Link &link = rborder[i]; - //assert(!link.IsNull()); - //TODO not accurate - if(link.end_patch != p) continue; - if((*k).second.count(link.start_vert)) - tmpb[offset + i] += (*k).second[link.start_vert]; - } - } - - //Uncomment this only when links are ok! - map::iterator j; - for(j = bcopy.begin(); j != bcopy.end(); j++) { - unsigned int b = (*j).first; - Link link = (*j).second; - Border &rborder = nexus.GetBorder(link.end_patch, false); - unsigned int offset = tmpb_start[link.end_patch]; - for(unsigned int i = 0; i < rborder.Size(); i++) { - Link &rlink = rborder[i]; - if(rlink.end_patch == p && rlink.start_vert == link.end_vert) { - assert(rlink.end_vert == link.start_vert); - tmpb[poff + b] = tmpb[offset + i]; - } - } - } - } - - //Second step unify normals across borders - cerr << "Second step\n"; - report.Init(nexus.size()); - for(unsigned int p = 0; p < nexus.size(); p++) { - report.Step(p); - Patch &patch = nexus.GetPatch(p); - Border &border = nexus.GetBorder(p); - - Point3f *normf = (Point3f *)patch.VNormBegin(); - short *norms = (short *)patch.VNormBegin(); - - for(unsigned int i = 0; i < border.Size(); i++) { - Link &link = border[i]; - if(link.IsNull()) continue; - unsigned int off = tmpb_start[p]; - // Point3f &n = tmpb[off + i]; - Point3f n = tmpb[off + i]; - if(n == Point3f(0.0f,0.0f,0.0f)) continue; - n.Normalize(); - if(nexus.signature.vnorm == Encodings::SHORT4) { - n *= 32766; - short *np = norms + 4 * link.start_vert; - np[0] = (short)n[0]; - np[1] = (short)n[1]; - np[2] = (short)n[2]; - np[3] = 0; - } else if(nexus.signature.vnorm == Encodings::FLOAT3) { - normf[link.start_vert] = n; - } - } - } - tmpb.Close(); - tmpb.Delete(); - //TODO remove temporary file. - nexus.borders.SetReadOnly(false); - }*/ - - - -/* - //TODO why i created this function? wonder...void nxs::Reorder(Signature &signature, Patch &patch) { - vector remap; - remap.resize(patch.nv, 0xffff); - - int nf = patch.nf; - if(signature.face == Signature::TRIANGLES) - nf *= 3; - else if(signature.face != Signature::STRIPS) { - assert(0); //mah... - } - - //building remap - unsigned short *f = patch.FaceBegin(); - unsigned int count = 0; - for(int i = 0; i < nf; i++) { - assert(f[i] < remap.size()); - if(remap[f[i]] == 0xffff) { - remap[f[i]] = count++; - } - } - //test no unreferenced vertices - for(int i = 0; i < patch.nv; i++) - if(remap[i] == 0xffff) - remap[i] = i; - - //converting faces - for(int i = 0; i < nf; i++) - f[i] = remap[f[i]]; - - vector vert; - vert.resize(patch.nv); - memcpy(&*vert.begin(), patch.Vert3fBegin(), patch.nv * sizeof(Point3f)); - for(int i = 0; i < patch.nv; i++) - patch.Vert3f(remap[i]) = vert[i]; - }*/ - -//TODO actually use threshold - -void nxs::Unify(vector &points, vector &faces, - vector &remap, float threshold) { - vector newfaces = faces; - - VPartition grid; - for(unsigned int i = 0; i < points.size(); i++) - grid.push_back(points[i]); - grid.Init(); - - remap.resize(points.size()); - vector targets; - vector dists; - - points.clear(); - unsigned int count = 0; - for(unsigned int i = 0; i < grid.size(); i++) { - - grid.Closest(grid[i], targets, dists, threshold); - - if(targets.size() > 1) { - unsigned int p; - for(p = 0; p < targets.size(); p++) { - if(targets[p] < i) { - remap[i] = remap[targets[p]]; - break; - } - } - if(p < targets.size()) continue; - } - remap[i] = count++; - points.push_back(grid[i]); - } - - //fixing faces now - faces.clear(); - for(unsigned int i = 0; i < newfaces.size(); i += 3) { - unsigned short f[3]; - f[0] = remap[newfaces[i]]; - f[1] = remap[newfaces[i+1]]; - f[2] = remap[newfaces[i+2]]; - if(f[0] == f[1] || f[0] == f[2] || f[1] == f[2]) - continue; - - for(int k = 0; k < 3; k++) - faces.push_back(f[k]); - } -} - -/*void nxs::Unify(Nexus &nexus, float threshold) { - threshold = 0.00001; - unsigned int duplicated = 0; - unsigned int degenerate = 0; - - for(unsigned int p = 0; p < nexus.size(); p++) { - if(levels[p] != current_level) continue; - count++; - report.Step(p); - - Border &border = nexus.GetBorder(p); - - map > normals; - normals[p] = vector(); - - for(unsigned int i = 0; i < border.Size(); i++) { - Link &link = border[i]; - if(levels[link.end_patch] == current_level) - normals[link.end_patch] = vector(); - } - map >::iterator k; - for(k = normals.begin(); k != normals.end(); k++) { - Patch &patch = nexus.GetPatch((*k).first); - } - } - }*/ - -/* -void nxs::Unify(Nexus &nexus, float threshold) { - threshold = 0.001; - //TODO what if colors or normals or strips? - unsigned int duplicated = 0; - unsigned int degenerate = 0; - - for(unsigned int p = 0; p < nexus.size(); p++) { - Entry &entry = nexus[p]; - Patch &patch = nexus.GetPatch(p); - - - - VPartition part; - for(unsigned int i = 0; i < patch.nv; i++) { - Point3f &point = patch.Vert3f(i); - part.push_back(point); - } - part.Init(); - - unsigned int vcount = 0; - vector remap; - remap.resize(patch.nv); - - int targets[8]; - double dists[8]; - - //TODO CRITICAL FIX this unifying routine. - for(unsigned int i = 0; i < patch.nv; i++) { - Point3f &point = patch.Vert3f(i); - part.Closest(point, 8, targets, dists); - int k = 0; - for(k = 0; k < 8; k++) { - if(dists[k] > threshold) { - remap[i] = vcount++; - break; - } - if(targets[k] < i) { - remap[i] = remap[targets[k]]; - duplicated++; - break; - } - } - if(k == 8) - remap[i] = vcount++; - - } - - if(vcount == patch.nv) //no need to unify - continue; - - vector newvert; - newvert.resize(vcount); - for(unsigned int i = 0; i < patch.nv; i++) - newvert[remap[i]] = patch.Vert3f(i); - - 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(nexus.signature, entry.nvert, entry.nface); - - memcpy(patch.Vert3fBegin(), &(newvert[0]), entry.nvert*sizeof(Point3f)); - memcpy(patch.FaceBegin(), &(newface[0]), entry.nface*3*sizeof(short)); - - //testiamo il tutto... TODO remove this of course -#ifdef NDEBUG - 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); - } - } -#endif - //TODO CRITICAL FIX unify vertices across borders..... HOW?????? - - //fix patch borders now - set close; //bordering pathes - Border &border = nexus.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 = nexus.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 < nexus.size(); p++) { - Border &border = nexus.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; - - nexus.borders[p].used = links.size(); - } - - nexus.totvert -= duplicated; - if(duplicated) - cerr << "Found " << duplicated << " duplicated vertices" << endl; - if(degenerate) - cerr << "Found " << degenerate << " degenerate face while unmifying\n"; -} -*/ -void nxs::ZSort(Nexus &nexus, vector &forward, - vector &backward) { - //lets get a bounding box from the sphere: - ZCurve zcurve; - float r = nexus.sphere.Radius(); - Point3f radius(r, r, r); - zcurve.Set(nexus.sphere.Center()); - zcurve.Add(nexus.sphere.Center() - radius); - zcurve.Add(nexus.sphere.Center() + radius); - - vector levels; - nexus.history.BuildLevels(levels); - - forward.clear(); - - vector< vector > entries; - - for(unsigned int i = 0; i < nexus.size(); i++) { - int level = levels[i]; - while(level >= entries.size()) entries.push_back(vector()); - - ZEntry e; - e.id = i; - e.pos = zcurve.Pos(nexus[i].sphere.Center()); - entries[level].push_back(e); - } - - for(unsigned int i = 0; i < entries.size(); i++) { - vector &lev = entries[i]; - std::sort(lev.begin(), lev.end()); - for(unsigned int k = 0; k < lev.size(); k++) - forward.push_back(lev[k].id); - } - - backward.resize(forward.size()); - for(unsigned int i = 0; i < backward.size(); i++) - backward[forward[i]] = i; -} - -bool nxs::LineIntersect(Nexus &nexus, Extraction &extraction, - Line3f line, Point3f &hit) { - //seguiamo la history; - Point3f tmp; - if(!Intersection(nexus.sphere, line, hit, tmp)) - return false; - - bool found = false; - float min_dist = -1; - float bar1, bar2, dist; - for(unsigned int i = 0; i < extraction.draw_size; i++) { - unsigned int p = extraction.selected[i].id; - if(!Intersection(nexus[p].sphere, line, hit, tmp)) - continue; - Patch &patch = nexus.GetPatch(p); - if(nexus.signature.face == Signature::TRIANGLES) { - for(unsigned int i = 0; i < patch.nf; i++) { - unsigned short *f = patch.Face(i); - Point3f &v0 = patch.Vert3f(f[0]); - Point3f &v1 = patch.Vert3f(f[1]); - Point3f &v2 = patch.Vert3f(f[2]); - if(Intersection(line, v0, v1, v2, bar1, bar2, dist) && - dist > 0 && - (min_dist == -1 || min_dist > dist)) { - hit = v0*(1-bar1-bar2) + v1*bar1 + v2*bar2; - min_dist = dist; - found = true; - } - } - } else if(nexus.signature.face == Signature::STRIPS) { - for(int i = 0; i < patch.nf - 2; i++) { - unsigned short *f = patch.FaceBegin() + i; - Point3f &v0 = patch.Vert3f(f[0]); - Point3f &v1 = patch.Vert3f(f[1]); - Point3f &v2 = patch.Vert3f(f[2]); - if(Intersection(line, v0, v1, v2, bar1, bar2, dist) && - dist > 0 && - (min_dist == -1 || min_dist > dist)) { - hit = v0*(1-bar1-bar2) + v1*bar1 + v2*bar2; - min_dist = dist; - found = true; - } - } - } else - assert(0); - } - return found; -} diff --git a/apps/nexus/nxsalgo.h b/apps/nexus/nxsalgo.h deleted file mode 100644 index b2baacf5..00000000 --- a/apps/nexus/nxsalgo.h +++ /dev/null @@ -1,93 +0,0 @@ -/**************************************************************************** -* 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 $ -Revision 1.11 2005/03/01 11:21:20 ponchio -Added line intersection - -Revision 1.10 2005/02/22 14:20:44 ponchio -debug and mostly vertex unifying across borders -(still not perfect... :P) - -Revision 1.9 2005/02/22 10:38:11 ponchio -Debug, cleaning and optimization. - -Revision 1.8 2005/02/21 17:55:36 ponchio -debug debug debug - -Revision 1.7 2005/02/20 18:07:01 ponchio -cleaning. - -Revision 1.6 2005/02/19 10:45:04 ponchio -Patch generalized and small fixes. - -Revision 1.5 2005/02/18 13:04:13 ponchio -Added patch reordering. - -Revision 1.4 2005/02/08 12:43:03 ponchio -Added copyright - - -****************************************************************************/ - -#ifndef NXS_ALGO_H -#define NXS_ALGO_H - -#include -#include -#include "patch.h" -#include -#include - -namespace nxs { - - - class Nexus; - class Patch; - class Extraction; - - - struct ZEntry { - unsigned int id; - unsigned int pos; - bool operator<(const ZEntry &e) const { return pos < e.pos; } - }; - - //for every patch return close by (sphere intersecting) //threshold is added to the distance to make sure we do not miss anything - void Connect(Nexus &nexus, std::vector< std::set > &close, - float threshold); - - void ComputeNormals(Nexus &nexus); - - void Unify(std::vector &points, - std::vector &faces, - std::vector &vremap, float threshold); - void ZSort(Nexus &nexus, std::vector &forward, - std::vector &backward); - bool LineIntersect(Nexus &nexus, Extraction &extraction, - vcg::Line3f line, vcg::Point3f &hit); -} - -#endif diff --git a/apps/nexus/nxsbuilder.cpp b/apps/nexus/nxsbuilder.cpp deleted file mode 100644 index 89dc1694..00000000 --- a/apps/nexus/nxsbuilder.cpp +++ /dev/null @@ -1,972 +0,0 @@ -/**************************************************************************** -* 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 $ -Revision 1.22 2005/02/22 14:20:44 ponchio -debug and mostly vertex unifying across borders -(still not perfect... :P) - -Revision 1.21 2005/02/22 10:38:11 ponchio -Debug, cleaning and optimization. - -Revision 1.20 2005/02/21 17:55:47 ponchio -debug debug debug - -Revision 1.19 2005/02/20 18:07:01 ponchio -cleaning. - -Revision 1.18 2005/02/20 00:43:24 ponchio -Less memory x extraction. (removed frags) - -Revision 1.17 2005/02/19 17:14:02 ponchio -History quick by default. - -Revision 1.16 2005/02/19 10:45:04 ponchio -Patch generalized and small fixes. - -Revision 1.15 2005/02/14 15:17:36 ponchio -Cleaning up. - -Revision 1.14 2005/01/21 17:09:13 ponchio -Porting and debug. - -Revision 1.13 2005/01/17 17:35:48 ponchio -Small changes and adding realtime extr. - -Revision 1.12 2005/01/14 15:25:29 ponchio -Revolution. - -Revision 1.11 2004/12/13 00:44:48 ponchio -Lotsa changes... - -Revision 1.10 2004/12/09 22:33:28 ponchio -Different splitting optimization. - -Revision 1.9 2004/12/04 13:22:55 ponchio -*** empty log message *** - -Revision 1.8 2004/12/03 21:19:00 ponchio -Fixed a couple of memory leak... - -Revision 1.7 2004/12/03 01:20:56 ponchio -Debug - -Revision 1.6 2004/12/02 20:22:42 ponchio -Level 5; - -Revision 1.5 2004/12/02 19:10:18 ponchio -Bounding sphere fix. - -Revision 1.4 2004/12/01 16:00:35 ponchio -Level 3 - -Revision 1.3 2004/12/01 03:32:46 ponchio -Level 2 (debug). - -Revision 1.2 2004/12/01 03:24:32 ponchio -Level 2. - -Revision 1.1 2004/12/01 01:15:03 ponchio -Level 0. - -Revision 1.26 2004/11/30 22:49:39 ponchio -Level 0. - - -****************************************************************************/ - -#ifdef WIN32 -#include -#else -#include -#endif - -#ifdef WIN32 -#include -#endif - -#include - - -#include "nxstypes.h" -#include "crude.h" -#include "remapping.h" -#include "decimate.h" -#include "fragment.h" -#include "nxsalgo.h" -#include "nxsdispatcher.h" -#include "watch.h" -#include "nexus.h" - - -using namespace std; -using namespace vcg; -using namespace nxs; - -void BuildFragment(Nexus &nexus, VPartition &part, - set &patches, - Fragment &fragment); - -void SaveFragment(Nexus &nexus, VChain &chain, - Fragment &fragin, - Fragment &fragout); - -void ReverseHistory(vector &history); - - -unsigned int current_level; -vector patch_levels; - -void usage() { - cerr << "Usage: voronoinxs [options]\n" - " Options:\n" - " -f N: use N faces per patch (default 1000, max 32000)\n" - " -t N: mini faces per patch (default 200)\n" - " -l N: number of levels\n" - " -s F: scaling factor (0 < F < 1, default 0.5)\n" - " -o N: number of optimization steps\n" - " -d : decimation method: quadric, cluster. (default quadric)\n" - " -b N: ram buffer size (in bytes)\n" - " -p N: which fase perform:\n" - " 0) Remap faces\n" - " 1) Sort faces\n" - " 2) Build patches\n" - " 3) Build borders\n" - " 4) Build levels\n\n" - " If you use the step option, all other parameters MUST stay the same\n\n"; - -} - - -void FirstStep(const string &crudefile, const string &output, - unsigned int patch_size, unsigned int patch_threshold, - float scaling, unsigned int optimization_steps) { - Crude crude; - - if(!crude.Load(crudefile, true)) { - cerr << "Could not open crude input: " << crudefile << endl; - exit(0); - } - - if(patch_size > crude.vert.Size()/2) { - cerr << "Patch size too big: " << patch_size << " * 2 > " - << crude.vert.Size() << endl; - exit(0); - } - - if(patch_threshold == 0xffffffff) - patch_threshold = patch_size/4; - - - VChain vchain; - - VFile face_remap; - if(!face_remap.Create(output + ".rfm")) { - cerr << "Could not create remap files: " << output << ".rmf\n"; - exit(0); - } - face_remap.Resize(crude.Faces()); - - VFile baricenters; - if(!baricenters.Create(output + ".bvr")) { - cerr << "Could not create temporary baricenters file\n"; - exit(0); - } - baricenters.Resize(crude.Faces()); - for(unsigned int i = 0; i < crude.Faces(); i++) { - baricenters[i] = crude.GetBari(i); - } - - BlockIndex face_index; - - Remap(vchain, baricenters, face_remap, face_index, - patch_size, patch_threshold, 65000, scaling, - optimization_steps); - - if(!vchain.Save(output + ".vchain")) { - cerr << "Could not save file: " << output << ".vchain\n"; - exit(0); - } - if(!face_index.Save(output + ".rfi")) { - cerr << "Could not save file: " << output << ".rmi\n"; - exit(0); - } - - baricenters.Delete(); -} - - -void SecondStep(const string &crudefile, const string &output) { - Crude crude; - - if(!crude.Load(crudefile, true)) { - cerr << "Could not open crude input: " << crudefile << endl; - exit(0); - } - VFile face_remap; - if(!face_remap.Load(output + ".rfm", true)) { - cerr << "Could not load: " << output << ".rmf\n;"; - exit(0); - } - assert(face_remap.Size() == crude.Faces()); - - VFile sorted; - if(!sorted.Create(output + ".faces")) { - cerr << "Could not create sorted faces\n"; - exit(0); - } - sorted.Resize(face_remap.Size()); - - BlockIndex face_index; - if(!face_index.Load(output + ".rfi")) { - cerr << "Could not load index\n"; - exit(0); - } - - //Sorting now. - vector done; - done.resize(face_index.size(), 0); - - for(unsigned int i = 0; i < face_remap.Size(); i++) { - unsigned int patch = face_remap[i]; - assert(patch < face_index.size()); - int64 offset = face_index[patch].offset + done[patch]++; - sorted[offset] = crude.GetFace(i); - } - - //once sorted - crude.Close(); - sorted.Close(); - - /* TODO fix this (after debug!) - WARNING what if multiple files? - - if(0 != unlink((crudefile + ".crf").c_str())) { - cerr << "Could not remove " << crudefile << ".crf\n"; - exit(0); - } - if(0 != rename((output + ".faces").c_str(), (crudefile + ".crf").c_str())) { - cerr << "Could not rename to: " << crudefile + ".crf\n"; - exit(0); - } - face_remap.Close(); */ - //TODO remove the file... (after finishing debug!) - // face_remap.Delete(); -} - -void ThirdStep(const string &crudefile, const string &output, - unsigned int chunk_size) { - - cerr << "Third step!\n"; - Crude crude; - - if(!crude.Load(crudefile, true)) { - cerr << "Could not open crude input: " << crudefile << endl; - exit(0); - } - - VFile sorted; - if(!sorted.Load(crudefile + ".faces", true)) { - cerr << "Could not load sorted faces\n"; - exit(0); - } - BlockIndex face_index; - if(!face_index.Load(output + ".rfi")) { - cerr << "Could not load index\n"; - exit(0); - } - - VFile vert_remap; - if(!vert_remap.Create(output + ".rvm")) { - cerr << "Could not create: " << output << ".rvm\n"; - exit(0); - } - - BlockIndex vert_index; - - Nexus nexus; - //TODO here i really need no ram_buffer..... - nexus.MaxRam() = 0; - Signature signature; //default triangles and vertices3f - if(!nexus.Create(output, signature, chunk_size)) { - cerr << "Could not create nexus output: " << output << endl; - getchar(); - exit(0); - } - - Report report(face_index.size()); - for(unsigned int patch = 0; patch < face_index.size(); patch++) { - report.Step(patch); - - unsigned int vcount = 0; - unsigned int fcount = 0; - map vremap; - - vector vertices; - vector faces; - - int64 &offset = face_index[patch].offset; - unsigned int size = face_index[patch].size; - for(unsigned int i = offset; i < offset + size; i++) { - //TODO fix this after debug - Crude::Face face = sorted[i]; - if(face[0] == face[1] || face[1] == face[2] || face[0] == face[2]) - continue; //degenerate - for(int j = 0; j < 3; j++) { - assert(face[j] < crude.Vertices()); - if(!vremap.count(face[j])) { - Point3f &v = crude.vert[face[j]]; - vertices.push_back(v); - vremap[face[j]] = vcount++; - } - faces.push_back(vremap[face[j]]); - fcount++; - } - } - vector remap; - nxs::Unify(vertices, faces, remap, 0.0001); - //fixing vremap - map::iterator q; - for(q = vremap.begin(); q != vremap.end(); q++) - (*q).second = remap[(*q).second]; - - vcount = vertices.size(); - fcount = faces.size(); - - //TODO deal with this case adding another patch at the end... - //its not that difficult! - - //This can happen on degenerate cases when we have a lot of detached faces. - if(vcount > 65000 && fcount > 65000) { - cerr << "Too many vertices or faces in patch: " << patch - << " v: " << vcount << " f: " << fcount << endl; - exit(0); - } - - unsigned int patch_idx = nexus.AddPatch(vertices.size(), - faces.size()/3, - 0); //no borders! - Patch &patch = nexus.GetPatch(patch_idx); - memcpy(patch.FaceBegin(), &*faces.begin(), fcount * sizeof(short)); - memcpy(patch.Vert3fBegin(), &*vertices.begin(), vcount * sizeof(Point3f)); - - - //Fixing sphere - Sphere3f &sphere = nexus[patch_idx].sphere; - sphere.CreateTight(vertices.size(), &*vertices.begin()); - - - //Fixing normalscone - vector normals; - normals.reserve(patch.nf); - for(unsigned int i = 0; i < patch.nf; i++) { - unsigned short *f = patch.Face(i); - Point3f &v0 = patch.Vert3f(f[0]); - Point3f &v1 = patch.Vert3f(f[1]); - Point3f &v2 = patch.Vert3f(f[2]); - - Point3f norm = (v1 - v0) ^ (v2 - v0); - float len = norm.Norm(); - if(len == 0) { - cerr << "Degenerate face... should not be here.\n"; - continue; - } - norm /= len; -#ifndef WIN32 - if(isnan(norm[0]) || isnan(norm[1]) || isnan(norm[2])) { - cerr << "Invalid normal computation. Strange.\n"; - continue; - } -#endif - normals.push_back(norm); - } - ANCone3f cone; - cone.AddNormals(normals, 0.99f); - nexus[patch_idx].cone.Import(cone); - - - //saving vert_remap - int64 vroffset = vert_remap.Size(); - vert_index.push_back(BlockEntry(vroffset, vcount)); - vert_remap.Resize(vroffset + vcount); - - map::iterator r; - for(r = vremap.begin(); r != vremap.end(); r++) { - assert((*r).second < vcount); - assert(vroffset + (*r).second < vert_remap.Size()); - vert_remap[vroffset + (*r).second] = (*r).first; - } - if(vcount < 100) { - cerr << "Small patch: " << vcount << "\n"; - } - } - - //we can now update bounding sphere. - for(unsigned int i = 0; i < nexus.size(); i++) - nexus.sphere.Add(nexus[i].sphere); - - History::Update update; - for(unsigned int i = 1; i < nexus.size(); i++) - update.created.push_back(i); - nexus.history.updates.push_back(update); - - update.created.clear(); - update.created.push_back(0); - for(unsigned int i = 1; i < nexus.size(); i++) - update.erased.push_back(i); - nexus.history.updates.push_back(update); - - if(!vert_index.Save(output + ".rvi")) { - cerr << "Could not save: " << output << ".rvi\n"; - exit(0); - } - nexus.Close(); -} - -void FourthStep(const string &crudefile, const string &output, - unsigned int ram_buffer) { - float threshold = 0.0001; - cerr << "Creating borders\n"; - Nexus nexus; - if(!nexus.Load(output)) { - cerr << "Could not load nexus " << output << endl; - exit(0); - } - nexus.MaxRam() = ram_buffer / nexus.chunk_size; - - VFile vert_remap; - if(!vert_remap.Load(output + ".rvm", true)) { - cerr << "Could not load: " << crudefile << ".rvm\n"; - exit(0); - } - - BlockIndex vert_index; - if(!vert_index.Load(output + ".rvi")) { - cerr << "Could not load index\n"; - exit(0); - } - - vector > close; - Connect(nexus, close, 0.0001); - - Report report(nexus.size()); - - //WARNING this is not robust at all!!!! - //TO make all this work we should neeed to quantize - //vertex position with 2 * threshold step (at least) - for(int start = 0; start < nexus.size(); start++) { - report.Step(start); - - vector links; - set::iterator t; - for(t = close[start].begin(); t != close[start].end(); t++) { - unsigned int end = (*t); - Patch &pend = nexus.GetPatch(end); - VPartition grid; - for(int i = 0; i < pend.nv; i++) { - grid.push_back(pend.Vert3f(i)); - } - grid.Init(); - - vector targets; - vector dists; - Patch &patch = nexus.GetPatch(start); - for(int i = 0; i < patch.nv; i++) { - grid.Closest(patch.Vert3f(i), targets, dists, threshold); - for(unsigned int k = 0; k < targets.size(); k++) { - Link link; - link.start_vert = i; - link.end_vert = targets[k]; - link.end_patch = end; - links.push_back(link); - patch.Vert3f(i) = grid[targets[k]]; - } - } - - } - - Border &border = nexus.GetBorder(start); - nexus.borders.ResizeBorder(start, 3 * links.size()); - border.used = links.size(); - memcpy(&(border[0]), &*links.begin(), links.size() * sizeof(Link)); - } - - /*old way.. - for(int start = 0; start < nexus.size(); start++) { - report.Step(start); - - vector links; - map vremap; - for(unsigned int i = 0; i < vert_index[start].size; i++) { - unsigned int global = vert_remap[vert_index[start].offset + i]; - vremap[global] = i; - } - - set::iterator t; - for(t = close[start].begin(); t != close[start].end(); t++) { - unsigned int end = (*t); - - for(unsigned int i = 0; i < vert_index[end].size; i++) { - unsigned int global = vert_remap[vert_index[end].offset + i]; - if(vremap.count(global)) { - Link link; - link.start_vert = vremap[global]; - link.end_vert = i; - link.end_patch = end; - links.push_back(link); - } - } - } - - - Border &border = nexus.GetBorder(start); - nexus.borders.ResizeBorder(start, 3 * links.size()); - border.used = links.size(); - memcpy(&(border[0]), &*links.begin(), links.size() * sizeof(Link)); - }*/ -} - -void FifthStep(const string &crudefile, const string &output, - unsigned int ram_buffer, - unsigned int optimization_steps, - unsigned int patch_size, - unsigned int patch_threshold, - Decimation decimation, - float scaling, - unsigned int max_level) { - - Nexus nexus; - if(!nexus.Load(output)) { - cerr << "Could not load nexus " << output << endl; - exit(0); - } - nexus.MaxRam() = ram_buffer / nexus.chunk_size; - - VChain vchain; - if(!vchain.Load(output + ".vchain")) { - cerr << "Could not load : " << output << ".vchain\n"; - exit(0); - } - nexus.history.Clear(); - History::Update update; - for(unsigned int i = 0; i < nexus.size(); i++) { - update.created.push_back(i); - patch_levels.push_back(0); - } - nexus.history.updates.push_back(update); - // Unify(nexus, 0.0f); - // nexus.Unify(); - nexus.Flush(); - - - Dispatcher dispatcher(&nexus, &vchain); - dispatcher.mode = decimation; - dispatcher.scaling = scaling; - if(!dispatcher.Init("servers.txt")) { - cerr << "Could not parse server file: " << "servers.txt" - << " proceding locally\n"; - } - - unsigned int oldoffset = 0; - for(unsigned int level = 1; level < max_level; level++) { - current_level = level; - cerr << "Level: " << level << endl; - - unsigned int newoffset = nexus.size(); - BuildLevel(vchain, nexus, oldoffset, scaling, - patch_size, patch_threshold, 65000, - optimization_steps); - - Report report(vchain.oldfragments.size()); - - unsigned int fcount = 0; - map >::iterator fragment; - for(fragment = vchain.oldfragments.begin(); - fragment != vchain.oldfragments.end(); fragment++) { - report.Step(fcount++); - - Fragment *fragin = new Fragment; - BuildFragment(nexus, *vchain[level+1], - (*fragment).second, *fragin); - - dispatcher.SendFragment(fragin); - - - /* - //this can be executed on a remote host - - //TODO move this part to remote.... - vector newvert; - vector newface; - vector newbord; - Join(fragin, newvert, newface, newbord); - - float error = Decimate(decimation, - (unsigned int)((newface.size()/3) * scaling), - newvert, newface, newbord); - Fragment fragout; - fragout.error = error; - fragout.id = fragin.id; - fragout.seeds = fragin.seeds; - fragout.seeds_id = fragin.seeds_id; - Split(fragout, newvert, newface, newbord);//, vchain.levels[level+1]); - - - SaveFragment(nexus, vchain, fragin, fragout); - */ - dispatcher.processmsgs(); - } - //TODO porcata!!!! - while(dispatcher.frags.size()) { - // cerr << "frags: " << dispatcher.frags.size() << endl; - dispatcher.processmsgs(); - } - - report.Finish(); - - if(vchain.oldfragments.size() == 1) break; - - vchain.oldfragments = vchain.newfragments; - oldoffset = newoffset; - } - - //last level clean history: - update.created.clear(); - update.erased.clear(); - map >::iterator fragment; - for(fragment = vchain.newfragments.begin(); - fragment != vchain.newfragments.end(); fragment++) { - set &fcells = (*fragment).second; - set::iterator s; - for(s = fcells.begin(); s != fcells.end(); s++) - update.erased.push_back(*s); - } - nexus.history.updates.push_back(update); - ReverseHistory(nexus.history.updates); - - nexus.borders.Flush(); - if(!nexus.history.IsQuick()) - nexus.history.UpdatesToQuick(nexus); - // TestBorders(nexus); - nexus.Close(); - -} - -int main(int argc, char *argv[]) { - - /* Parameters: */ - unsigned int patch_size = 1000; //step 0 - unsigned int patch_threshold = 0xffffffff; //Step 0 - float scaling = 0.5; //step 0, 4 - unsigned int optimization_steps = 5; //step 0, 4 - - Decimation decimation = CLUSTER; //step 4 - unsigned int max_level = 0xffffffff; //step 4 - - unsigned int ram_buffer = 128000000; //step 2, 3, 4 - unsigned int chunk_size = 1024; //step 2, 3, 4 - int step = -1; //means all of them. - - int option; - while((option = getopt(argc, argv, "f:t:l:s:d:o:b:c:p:")) != 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; - case 's': scaling = (float)atof(optarg); - if(scaling <= 0 || scaling >= 1) { - cerr << "Invalid scaling: " << optarg << endl; - cerr << "Must be 0 < scaling < 1" << endl; - } - break; - case 'd': - if(!strcmp("quadric", optarg)) - decimation = QUADRIC; - else if(!strcmp("cluster", optarg)) - decimation = CLUSTER; - else { - cerr << "Unknown decimation method: " << optarg << endl; - return -1; - } - break; - case 'o': optimization_steps = atoi(optarg); break; - case 'p': step = atoi(optarg); break; - case 'b': ram_buffer = atoi(optarg); - if(ram_buffer == 0) { - cerr << "Invalid ram buffer: " << optarg << endl; - return -1; - } - break; - case 'c': chunk_size = atoi(optarg); - if(chunk_size == 0) { - cerr << "Invalid chunk size: " << optarg << endl; - return -1; - } - break; - default: cerr << "Unknown option: " << (char)option << endl; - return -1; - } - } - - if(optind != argc -2) { - usage(); - return -1; - } - string crudefile = argv[optind]; - string output = argv[optind+1]; - - if(step < 0 || step == 0) - FirstStep(crudefile, output, patch_size, patch_threshold, - scaling, optimization_steps); - if(step < 0 || step == 1) - SecondStep(crudefile, output); - - if(step < 0 || step == 2) - ThirdStep(crudefile, output, chunk_size); - - if(step < 0 || step == 3) - FourthStep(crudefile, output, ram_buffer); - - if(step < 0 || step == 4) - FifthStep(crudefile, output, - ram_buffer, - optimization_steps, - patch_size, patch_threshold, - decimation, - scaling, max_level); - return 0; -} - -void BuildFragment(Nexus &nexus, VPartition &part, - set &patches, - Fragment &fragment) { - - set::iterator f; - for(f = patches.begin(); f != patches.end(); f++) { - fragment.pieces.push_back(NxsPatch()); - NxsPatch &nxs = fragment.pieces.back(); - nxs.patch = *f; - - Patch &patch = nexus.GetPatch(*f); - Border &border = nexus.GetBorder(*f); - - for(unsigned int k = 0; k < patch.nf; k++) { - assert(patch.FaceBegin()[3*k + 0] != patch.FaceBegin()[3*k + 1]); - assert(patch.FaceBegin()[3*k + 0] != patch.FaceBegin()[3*k + 2]); - assert(patch.FaceBegin()[3*k + 1] != patch.FaceBegin()[3*k + 2]); - } - - - nxs.vert.resize(patch.nv); - nxs.face.resize(patch.nf * 3); - memcpy(&*nxs.vert.begin(), patch.Vert3fBegin(), patch.nv * sizeof(Point3f)); - memcpy(&*nxs.face.begin(), patch.FaceBegin(), patch.nf * 3*sizeof(short)); - for(unsigned int i = 0; i < border.Size(); i++) { - Link &link = border[i]; - if(!link.IsNull() && - patch_levels[link.end_patch] == current_level-1) { - assert(link.end_patch != *f); - nxs.bord.push_back(link); - } - } - } - - //TODO Use the closest with threshold here instead of a cracnut number?? - set seeds; - vector nears; - vector dists; - int nnears = 10; - if(part.size() < 10) nnears = part.size(); - for(f = patches.begin(); f != patches.end(); f++) { - Point3f ¢er = nexus[*f].sphere.Center(); - part.Closest(center, nnears, nears, dists); - for(int i = 0; i < nnears; i++) - seeds.insert(nears[i]); - } - for(f = seeds.begin(); f != seeds.end(); f++) { - Point3f &p = part[*f]; - fragment.seeds.push_back(p); - fragment.seeds_id.push_back(*f); - } -} - -void SaveFragment(Nexus &nexus, VChain &chain, - Fragment &fragin, - Fragment &fragout) { - - set orig_patches; - - History::Update update; - for(unsigned int i = 0; i < fragin.pieces.size(); i++) { - NxsPatch &patch = fragin.pieces[i]; - update.erased.push_back(patch.patch); - orig_patches.insert(patch.patch); - } - - vector patch_remap; - patch_remap.resize(fragout.pieces.size()); - - for(unsigned int i = 0; i < fragout.pieces.size(); i++) { - NxsPatch &patch = fragout.pieces[i]; - //TODO detect best parameter below for bordersize... - unsigned int bordsize = 6 * patch.bord.size(); - if(bordsize > 65500) bordsize = 65499; - unsigned int patch_idx = nexus.AddPatch(patch.vert.size(), - patch.face.size()/3, - bordsize); - patch_levels.push_back(current_level); - Entry &entry = nexus[patch_idx]; - entry.error = fragout.error; - entry.sphere = patch.sphere; - entry.cone.Import(patch.cone); - - patch_remap[i] = patch_idx; - chain.newfragments[patch.patch].insert(patch_idx); - update.created.push_back(patch_idx); - } - - //here i put for every patch all its new links. - map > newlinks; - - for(unsigned int i = 0; i < fragout.pieces.size(); i++) { - NxsPatch &outpatch = fragout.pieces[i]; - unsigned int patch_idx = patch_remap[i]; - - for(unsigned int k = 0; k < outpatch.face.size(); k += 3) { - assert(k+2 < outpatch.face.size()); - assert(outpatch.face[k] != outpatch.face[k+1]); - assert(outpatch.face[k] != outpatch.face[k+2]); - assert(outpatch.face[k+1] != outpatch.face[k+2]); - } - - Patch &patch = nexus.GetPatch(patch_idx); - memcpy(patch.FaceBegin(), &outpatch.face[0], - outpatch.face.size() * sizeof(unsigned short)); - memcpy(patch.Vert3fBegin(), &outpatch.vert[0], - outpatch.vert.size() * sizeof(Point3f)); - - Entry &entry = nexus[patch_idx]; - - //remap internal borders - for(unsigned int k = 0; k < outpatch.bord.size(); k++) { - Link &link = outpatch.bord[k]; - if(link.end_patch >= (1<<31)) { //internal - link.end_patch = patch_remap[link.end_patch - (1<<31)]; - assert(link.end_patch != patch_remap[i]); - newlinks[patch_idx].insert(link); - } - } - //TODO not efficient! - //processing external borders - for(unsigned int l = 0; l < outpatch.bord.size(); l++) { - Link &link = outpatch.bord[l]; - if(link.end_patch >= (1<<31)) continue; - - unsigned short &start_vert = link.start_vert; - unsigned int &start_patch = patch_idx; - - //Adding vertical connection - newlinks[patch_idx].insert(link); - - Link up(link.end_vert, link.start_vert, patch_idx); - newlinks[link.end_patch].insert(up); - - assert(link.end_patch != patch_idx); - Border &cborder = nexus.GetBorder(link.end_patch); - for(unsigned int k = 0; k < cborder.Size(); k++) { - //cerr << "Cborder.size: " << cborder.Size() << endl; - //cerr << "K: " << k << endl; - Link &clink = cborder[k]; - assert(!clink.IsNull()); - - if(clink.start_vert != link.end_vert) continue; - if(patch_levels[clink.end_patch] < current_level-1) continue; - //boy i want only the external links! - if(orig_patches.count(clink.end_patch)) continue; - - unsigned short &end_vert = clink.end_vert; - unsigned int &end_patch = clink.end_patch; - - //TODO FIX THIS!!!! - assert(clink.end_patch != start_patch); - assert(clink.end_patch != link.end_patch); - - Link newlink; - - newlink.start_vert = start_vert; - newlink.end_vert = end_vert; - newlink.end_patch = end_patch; - - newlinks[patch_idx].insert(newlink); - - newlink.start_vert = end_vert; - newlink.end_vert = start_vert; - newlink.end_patch = start_patch; - - newlinks[end_patch].insert(newlink); - } - } - } - map >::iterator n; - for(n = newlinks.begin(); n != newlinks.end(); n++) { - set::iterator l; - unsigned int patch = (*n).first; - set &links = (*n).second; - Border &border = nexus.GetBorder(patch); - unsigned int bstart = border.Size(); - nexus.borders.ResizeBorder(patch, border.Size() + links.size()); - for(l = links.begin(); l != links.end(); l++) { - Link link = *l; - border[bstart++] = link; - } - } - nexus.history.updates.push_back(update); -} - -void ReverseHistory(vector &history) { - vector revert = history; - history.clear(); - for(int i = revert.size()-1; i >= 0; i--) - history.push_back(revert[i]); - //std::reverse(history.begin(), history.end()); - vector::iterator i; - for(i = history.begin(); i != history.end(); i++) - swap((*i).erased, (*i).created); -} diff --git a/apps/nexus/nxsclient.cpp b/apps/nexus/nxsclient.cpp deleted file mode 100644 index 0428c8bc..00000000 --- a/apps/nexus/nxsclient.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/**************************************************************************** -* 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 - -class NxsClientPool { - vector clients; - void Push(NxsRequest *request); -}; - -class NxsClient: public pt::ipstream { -public: - queue requests; - - void Push(NxsRequest *request); -}; - - -ipaddress addr = phostbyname("nb-ponchio.isti.cnr.it"); - ipstream client; - client.set_ip(addr); - client.set_port(testport); - try - { - client.open(); - - pout.put("Sending a request to the server...\n"); - client.write("Hello", 6); - client.flush(); - - // receive the response - string rsp = client.line(maxtoken); - pout.putf("Received: %s\n", pconst(rsp)); - - // need to close the socket explicitly to gracefully shutdown - // the peer host too. otherwise, ~ipstream() will call cancel() - // and leave the peer in a waiting state (not forever though). - client.close(); - } - catch(estream* e) - { - perr.putf("Error: %s\n", pconst(e->get_message())); - delete e; - } - diff --git a/apps/nexus/nxsdispatcher.cpp b/apps/nexus/nxsdispatcher.cpp deleted file mode 100644 index 76f8d451..00000000 --- a/apps/nexus/nxsdispatcher.cpp +++ /dev/null @@ -1,221 +0,0 @@ -/**************************************************************************** -* 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 "nxsdispatcher.h" -#include "fragment.h" -#include "decimate.h" -#include -#include - -using namespace std; -using namespace vcg; -using namespace nxs; -using namespace pt; - -void SaveFragment(Nexus &nexus, VChain &chain, - Fragment &fragin, - Fragment &fragout); - - -void Opener::execute() { - // cerr << "Trying to connect to: " << server->get_host() << endl; - server->reading.lock(); - server->writing.lock(); - while(1) { - if(get_signaled()) - return; - // cerr << "Trying to connect to: " << server->get_host() << endl; - try { - server->open(); - server->connected = true; - server->queue = 0; - // cerr << "Connected to: " << server->get_host() << endl; - break; - } catch(...) { - } - psleep(4000); - } - server->reading.unlock(); - server->writing.unlock(); -} - -void FragIO::execute() { - pincrement(&(server->queue)); - server->writing.lock(); - // cerr << "Writing frag...: " << fragin->id << "\n"; - - outmemory outm; - outm.open(); - fragin->Write(&outm); - pt::string a = outm.get_strdata(); - try { - server->write((const char *)a, length(a)); - server->flush(); - } catch (estream *e) { - perr.putf("Error reading: %s\n", pconst(e->get_message())); - delete e; - server->close(); - server->connected = false; - server->writing.unlock(); - message *msg = new message(MSG_FAIL, (int)fragin); - dispatcher->post(msg); - server->opener.start(); - return; - } - - server->reading.lock(); - server->writing.unlock(); - - Fragment *out = new Fragment; - if(!server->waitfor(10000) || (!out->Read(server))) { - perr.putf("Error reading!!\n"); - server->close(); - server->connected = false; - server->reading.unlock(); - message *msg = new message(MSG_FAIL, (int)fragin); - dispatcher->post(msg); - server->opener.start(); - return; - } - server->reading.unlock(); - pdecrement(&(server->queue)); - // cerr << "Received frag: " << out->id << endl; - - message *msg = new message(MSG_RECEIVE, (int)fragin); - msg->result = (int)out; - dispatcher->post(msg); -} - -bool Dispatcher::Init(const std::string &file) { - FILE *fp = fopen(file.c_str(), "rb"); - if(!fp) return false; - char host[256]; - int port; - while(fscanf(fp, "%s %d\n", host, &port) == 2) { - cerr << "Host: " << host << " port: " << port << endl; - Server *server = new Server(host, port); - server->opener.start(); - servers.push_back(server); - } - fclose(fp); - if(servers.size() == 0) { - cerr << "Empty server file!\n"; - return false; - } - return true; -} - -Dispatcher::~Dispatcher() { - for(unsigned int i = 0; i < servers.size(); i++) { - Server *server = servers[i]; - server->opener.signal(); - server->close(); - delete server; - } -} - -void Dispatcher::SendFragment(Fragment *frag) { - //WARNING this handles no more than 1<<31 fragments! - frag->id = count++; - message *msg = new message(MSG_SEND, (int)frag); - post(msg); -} - -Server *Dispatcher::BestServer() { - Server *best = NULL; - for(unsigned int i = 0; i < servers.size(); i++){ - if(servers[i]->connected) { - if((servers[i]->queue <= maxqueue) && - (!best || servers[i]->queue < best->queue)) { - - best = servers[i]; - // cerr << "best: " << i << " queue: " << best->queue << endl; - } - } - } - return best; -} - -void Dispatcher::ReceiveFragment(Fragment *in, Fragment *out) { - //lock nexus if run in thread. - // cerr << "Saving: " << in->id << endl; - SaveFragment(*nexus, *chain, *in, *out); - - if(frags.count(in->id)) { - FragIO *frag = frags[in->id]; - delete frag; - frags.erase(frags.find(in->id)); - } - delete in; - delete out; -} - -void Dispatcher::msghandler(message &msg) { - switch(msg.id) { - case MSG_FAIL: - case MSG_SEND: { - //get server! - Server *best = BestServer(); - Fragment *fragin = (Fragment *)(msg.param); - - if(!best || mode == CLUSTER) { //no server process locally.... - // cerr << "Local: " << fragin->id << endl; - vector newvert; - vector newface; - vector newbord; - Join(*fragin, newvert, newface, newbord); - - float error = Decimate(mode, - (unsigned int)((newface.size()/3) * scaling), - newvert, newface, newbord); - - Fragment *fragout = new Fragment; - - fragout->error = error; - fragout->id = fragin->id; - fragout->seeds = fragin->seeds; - fragout->seeds_id = fragin->seeds_id; - Split(*fragout, newvert, newface, newbord); - ReceiveFragment(fragin, fragout); - } else { - // cerr << "Server: " << fragin->id << endl; - FragIO *frag = new FragIO(best, this, fragin); - if(msg.id == MSG_SEND) - assert(!frags.count(fragin->id)); - frags[fragin->id] = frag; - frag->start(); - } - } break; - case MSG_RECEIVE: - ReceiveFragment((Fragment *)(msg.param), (Fragment *)(msg.result)); - break; - default: - defhandler(msg); - } -} diff --git a/apps/nexus/nxsdispatcher.h b/apps/nexus/nxsdispatcher.h deleted file mode 100644 index c44446aa..00000000 --- a/apps/nexus/nxsdispatcher.h +++ /dev/null @@ -1,120 +0,0 @@ -/**************************************************************************** -* 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_DISPATCHER_H -#define NXS_DISPATCHER_H - -#include -#include -#include -#include -#include - -#include "decimate.h" - -namespace nxs { - -#define MSG_SEND MSG_USER + 1 -#define MSG_RECEIVE MSG_USER + 2 -#define MSG_FAIL MSG_USER + 3 - - class Fragment; - class Nexus; - class VChain; - - class Server; - class FragIO; - class Dispatcher; - - - class Opener: public pt::thread { - public: - Opener(Server *s): thread(false), server(s) {} - ~Opener() { waitfor(); } - void execute(); - void cleanup() {} - - Server *server; - }; - - - class Server: public pt::ipstream { - public: - Server(pt::string host, int port): ipstream(host, port), queue(0), - connected(false), opener(this) {} - - int queue; - pt::mutex reading; - pt::mutex writing; - bool connected; - Opener opener; - }; - - - class Dispatcher: public pt::msgqueue { - public: - Dispatcher(Nexus *nx, VChain *ch): - count(0), maxqueue(3), nexus(nx), chain(ch) {} - ~Dispatcher(); - - bool Init(const std::string &file); - void SendFragment(Fragment *frag); - void ReceiveFragment(Fragment *in, Fragment *out); - Server *BestServer(); - - void msghandler(pt::message &msg); - - int count; - int maxqueue; - Nexus *nexus; - VChain *chain; - Decimation mode; - float scaling; - std::vector servers; - std::map frags; - }; - - class FragIO: public pt::thread { - public: - FragIO(Server *se, Dispatcher *di, Fragment *frag): - thread(false), server(se), dispatcher(di), fragin(frag) {} - ~FragIO() { waitfor(); } - void execute(); - void cleanup() {} - - Server *server; - Dispatcher *dispatcher; - Fragment *fragin; - }; - - - -} - -#endif diff --git a/apps/nexus/nxsedit.cpp b/apps/nexus/nxsedit.cpp deleted file mode 100644 index 48a4cc0b..00000000 --- a/apps/nexus/nxsedit.cpp +++ /dev/null @@ -1,670 +0,0 @@ -/**************************************************************************** -* 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 $ -Revision 1.26 2005/02/22 10:38:14 ponchio -Debug, cleaning and optimization. - -Revision 1.25 2005/02/21 17:55:47 ponchio -debug debug debug - -Revision 1.24 2005/02/20 19:49:44 ponchio -cleaning (a bit more). - -Revision 1.23 2005/02/20 18:07:01 ponchio -cleaning. - -Revision 1.22 2005/02/20 00:43:24 ponchio -Less memory x extraction. (removed frags) - -Revision 1.21 2005/02/19 17:14:02 ponchio -History quick by default. - -Revision 1.20 2005/02/19 10:45:04 ponchio -Patch generalized and small fixes. - -Revision 1.19 2005/02/18 13:04:13 ponchio -Added patch reordering. - -Revision 1.18 2005/02/08 12:43:03 ponchio -Added copyright - - -****************************************************************************/ - -#ifdef WIN32 -#include -#else -#include -#endif - -#include - - -#include - - -#include -#include -#include -//WARNING WARNING this must be included AFTER mesh includes.... -#include -#include - -#include "nxsalgo.h" -#include "strip.h" -#include "nexus.h" -#include "watch.h" - -using namespace nxs; -using namespace vcg; -using namespace std; -using namespace tri; - -class CFace; - -class CVertex: public VertexVCf {}; - -class CFace: public Face{}; - -class CMesh: public tri::TriMesh, vector > {}; - -string getSuffix(Signature &signature) { - string suff; - if(signature.compr) suff += "Z"; - if(signature.face == Signature::STRIPS) suff += "S"; - if(signature.vcolor) suff += "C"; - if(signature.vnorm) suff += "N"; - if(signature.vtext) suff += "T"; - if(signature.vdata) suff += "D"; - return suff; -} - -void printInfo(Nexus &nexus, bool verbose, bool dump_history); - -int main(int argc, char *argv[]) { - - string input; - string output; - string plysource; - - bool info = false; - bool verbose = false; - bool dump_history = false; - - unsigned int ram_size = 128000000; - unsigned int chunk_size = 0; - - bool add = false; - bool add_strips = false; - bool add_colors = false; - unsigned char add_normals = 0; - bool add_textures = false; - bool add_data = false; - - bool remove = false; - bool remove_strips = false; - bool remove_colors = false; - bool remove_normals = false; - bool remove_textures = false; - bool remove_data = false; - - bool compress = false; - bool uncompress = false; - bool zsort = false; - - float qvertex = 0; - float qnormal = 0; - float qcolor = 0; - float qtexture = 0; - float cone_threshold = 0; - - int option; - while((option = getopt(argc, argv, "ilho:a:r:zxsv:n:k:t:b:c:V:")) != EOF) { - switch(option) { - case 'i': info = true; break; - case 'l': verbose = true; break; - case 'h': dump_history = true; break; - case 'o': output = optarg; break; - case 'p': plysource = optarg; break; - - case 'a': { - if(strstr(optarg, "strips")) { add_strips = true; add = true; } - if(strstr(optarg, "colors")) { add_colors = true; add = true; } - if(strstr(optarg, "normals")) { - add_normals = Encodings::SHORT4; add = true; } - if(strstr(optarg, "normalf")) { - add_normals = Encodings::FLOAT3; add = true; } - if(strstr(optarg, "textures")) { add_textures = true; add = true; } - if(strstr(optarg, "data")) { add_data = true; add = true; } - if(add == false) { - cerr << "Invalid -a argument: " << optarg << "\n" - << "Valid options are: strips, colors, normals, textures, data\n"; - return -1; - } - break; - } - case 'r': { - if(strstr(optarg, "strips")) { - cerr << "Strips removing not supported!\n"; - return -1; - } - if(strstr(optarg, "colors")) { remove_colors = true; remove = true; } - if(strstr(optarg, "normals")) { remove_normals = true; remove = true; } - if(strstr(optarg, "textures")) { remove_textures = true; remove = true; } - if(strstr(optarg, "data")) { remove_data = true; remove = true; } - if(remove == false) { - cerr << "Invalid -a argument: " << optarg << "\n" - << "Valid options are: strip, colors, normals, normalf, " - << "textures, data\n"; - return -1; - } - break; - } - - - - case 'z': compress = true; break; - case 'x': uncompress = true; break; - case 's': zsort = true; break; - - case 'V': cone_threshold = atof(optarg); break; - case 'v': qvertex = (float)atof(optarg); - if(qvertex == 0) { - cerr << "Invalid value for quantization: " << optarg << endl; - return -1; - } - break; - case 'n': qnormal = (float)atof(optarg); - if(qnormal == 0) { - cerr << "Invalid value for quantization: " << optarg << endl; - return -1; - } - break; - case 'k': qcolor = (float)atof(optarg); - if(qcolor == 0) { - cerr << "Invalid value for quantization: " << optarg << endl; - return -1; - } - break; - case 't': qtexture = (float)atof(optarg); - if(qtexture == 0) { - cerr << "Invalid value for quantization: " << optarg << endl; - return -1; - } - break; - - case 'b': ram_size = atoi(optarg); - if(ram_size == 0) { - cerr << "Invalid ram_size: " << optarg << endl; - return -1; - } - break; - case 'c': chunk_size = atoi(optarg); - if(chunk_size == 0) { - cerr << "Invalid chunk_size: " << optarg << endl; - return -1; - } - break; - default: cerr << "Unknown option: " << (char)option << endl; - return -1; - } - } - - if(optind != argc - 1) { - cerr << "Usage: " << argv[0] << " [options]\n" - << " -i : display some info about nexus file\n" - << " -l : list nodes\n" - << " -h : list history\n" - << " -o : output filename (default is adding 00 to nexus)\n" - << " -a : Add [colors|normals|normalf|strips|textures|data|borders]\n" - << " -r : As add...\n" - << " -p : Ply source for colors or textures or data\n" - << " -z : compress\n" - << " -x : uncompress\n" - << " -s : sort using zcurve\n" - << " -v: Vertex quantization (float is the 0 level amount)\n" - << " -n: Normal quantization\n" - << " -c: Color quantization\n" - << " -t: Texture quantization\n" - << " -V: Normal cone threshold [0, 1] (0.95 default)\n\n" - << " This option will not create a new nexus file\n"; - return -1; - } - input = argv[optind]; - - //Sanity test of options... - - if(compress && uncompress) { - cerr << "x and z are obviously exclusive :P\n"; - return -1; - } - - if(add_normals && compress) { - cerr << "Its not possible to add normals and compress in the same step\n" - << "Because normals requires 2 passes to be calculated\n\n"; - return -1; - } - - - - bool compute_cones = false; - if(!add && !remove && !compress && !uncompress && !zsort && - !qvertex && !qcolor && !qnormal && !qtexture && cone_threshold != 0) - compute_cones = true; - - - Nexus nexus; - - if(!nexus.Load(input, true)) { - cerr << "Could not open nexus file: " << input << "\n"; - return -1; - } - nexus.MaxRam() = ram_size / nexus.chunk_size; - - - //Sanity tests - if(remove_strips && !(nexus.signature.face != Signature::STRIPS)) { - cerr << "Nexus file does not have strips\n"; - return -1; - } - if(remove_colors && !nexus.signature.vcolor) { - cerr << "Nexus file does not have colors\n"; - return -1; - } - if(remove_normals && !nexus.signature.vnorm) { - cerr << "Nexus file does not have normals\n"; - return -1; - } - if(remove_textures && !nexus.signature.vtext) { - cerr << "Nexus file does not have textures\n"; - return -1; - } - if(remove_data && !nexus.signature.vdata) { - cerr << "Nexus file does not have data\n"; - return -1; - } - - if(add_strips && (nexus.signature.face == Signature::STRIPS)) { - cerr << "Nexus file already has strips\n"; - return -1; - } - if(add_colors && nexus.signature.vcolor) { - cerr << "Nexus file already has colors\n"; - return -1; - } - if(add_normals && nexus.signature.vnorm) { - cerr << "Nexus file already has normals\n"; - return -1; - } - if(add_textures && nexus.signature.vtext) { - cerr << "Nexus file already has textures\n"; - return -1; - } - if(add_data && nexus.signature.vdata) { - cerr << "Nexus file already has data\n"; - return -1; - } - - if(nexus.IsCompressed() && compress) { - cerr << "File already compressed.\n"; - return -1; - } - - if(!nexus.IsCompressed() && uncompress) { - cerr << "File not compressed.\n"; - return -1; - } - - - if(info) { - cout << "Nexus file: " << input << "\n"; - printInfo(nexus, verbose, dump_history); - } - - - //determine if we must proceed: - if(!add && !remove && !compress && !uncompress && !zsort && - qvertex == 0 && qnormal == 0 && qcolor == 0 && qtexture == 0 && - cone_threshold == 0) { - nexus.Close(); - return 0; - } - - if(compute_cones) {//just recalculate normal cones - cerr << "Unimplemented at the moment...\n"; - - /*vector cones; - // ComputeCones(Nexus &nexus, float cone_threshold); - nexus.Close(); - nexus.Load(intput, false); - for(unsigned int i = 0; i < nexus.size(); i++) { - nexus[i].cone = cones[i]; - }*/ - nexus.Close(); - return 0; - } - - CMesh mesh; - GridStaticPtr grid; - if(add_colors) { - if(!plysource.size()) { - cerr << "No plysource specified when adding color (-p option)\n"; - } else { - if(!tri::io::ImporterPLY::Open(mesh, plysource.c_str())) { - cerr << "Could not load ply: " << plysource << endl; - return -1; - } - //calcoliamo il box: - Box3f box; - for(unsigned int i = 0; i < mesh.vert.size(); i++) - box.Add(mesh.vert[i].P()); - grid.SetBBox(box); - grid.Set(mesh.face); - } - } - - Signature signature = nexus.signature; - if(add_strips) signature.face = Signature::STRIPS; - if(add_normals) signature.vnorm = add_normals; - if(add_colors) signature.vcolor = Encodings::BYTE4; - - if(remove_normals) signature.vnorm = 0; - if(remove_colors) signature.vcolor = 0; - - if(compress) signature.compr = Signature::LZO; - if(uncompress) signature.compr = 0; - - if(!output.size()) output = input + getSuffix(signature); - if(output == input) { - cerr << "Output and input files are the same.\n" - << "Use option -o \n" - << "You do not want to overwrite your data. Trust me.\n"; - return -1; - } - - cout << "Writing to nexus: " << output << endl; - - Nexus out; - - if(!chunk_size) - chunk_size = nexus.chunk_size; - - if(!out.Create(output, signature, chunk_size)) { - cerr << "Could not open output: " << output << endl; - return -1; - } - - //TODO fix this broken interface (you should not care abou chunk_size - out.MaxRam() = ram_size / out.chunk_size; - //TODO set rambuffer low (or even direct access!) - - vector forward; - vector backward; - if(zsort) - ZSort(nexus, forward, backward); - - //Fixing history - assert(nexus.history.IsQuick()); - - unsigned int hsize; - char *buffer = nexus.history.Save(hsize); - out.history.Load(hsize, buffer); - - - - if(zsort) { - assert(0); - //TODO FIX THIS... - /* if(out.history.IsQuick()) { - for(unsigned int i = 0; i < out.history.n_frags(); i++) - out.history.frags[i] = backward[out.history.frags[i]]; - } else { - for(unsigned int i = 0; i < out.history.updates.size(); i++) { - History::Update &update = out.history.updates[i]; - for(unsigned int k = 0; k < update.created.size(); k++) - update.created[k] = backward[update.created[k]]; - - for(unsigned int k = 0; k < update.erased.size(); k++) - update.erased[k] = backward[update.erased[k]]; - } - }*/ - } - - Report report(nexus.size()); - cout << "Copying and allocating...\n"; - for(unsigned int p = 0; p < nexus.size(); p++) { - unsigned int patch = p; - report.Step(patch); - - if(zsort) patch = forward[patch]; - - Entry &src_entry = nexus[patch]; - Patch &src_patch = nexus.GetPatch(patch); - Border &src_border = nexus.GetBorder(patch); - - - vector strip; - if(add_strips) { - ComputeTriStrip(src_patch.nf, src_patch.FaceBegin(), strip); - assert(strip.size() < 65000); - out.AddPatch(src_entry.nvert, strip.size(), src_border.Capacity()); - if(verbose) { - cerr << "tri: " << src_patch.nf << " strip: " << strip.size() - << " ratio: " << (float)strip.size()/(float)src_patch.nf - << endl; - } - } else - out.AddPatch(src_entry.nvert, src_entry.nface, src_border.Capacity()); - - - Entry &dst_entry = out[p]; - Patch &dst_patch = out.GetPatch(p); - - //copy vertices: - assert(out.signature.vert == Signature::POINT3F); - assert(out.signature.vert = nexus.signature.vert); - memcpy(dst_patch.Vert3fBegin(), src_patch.Vert3fBegin(), - src_patch.nv * sizeof(Point3f)); - - if(qvertex && !add_normals) { - float *ptr = (float *)dst_patch.Vert3fBegin(); - for(int i = 0; i < dst_patch.nv*3; i++) - ptr[i] = qvertex * (int)(ptr[i]/qvertex); - } - - - //now faces. - if(add_strips) { - assert(out.signature.face == Signature::STRIPS); - memcpy(dst_patch.FaceBegin(), &*strip.begin(), - strip.size() * sizeof(short)); - } else { - assert(nexus.signature.face == out.signature.face); - if(nexus.signature.face == Signature::STRIPS) { - memcpy(dst_patch.FaceBegin(), src_patch.FaceBegin(), - src_patch.nf * sizeof(unsigned short)); - } else if(nexus.signature.face == Signature::TRIANGLES) { - memcpy(dst_patch.FaceBegin(), src_patch.FaceBegin(), - src_patch.nf * sizeof(unsigned short) * 3); - } else { - assert(0); - } - } - - if(nexus.signature.vcolor) { - if(nexus.signature.vcolor == out.signature.vcolor) { - memcpy(dst_patch.VColorBegin(), src_patch.VColorBegin(), - Patch::encodings[out.signature.vcolor].size(dst_patch.nv)); - } else { - assert(0); - } - } - - if(nexus.signature.vnorm) { - if(nexus.signature.vnorm == out.signature.vnorm) { - memcpy(dst_patch.VNormBegin(), src_patch.VNormBegin(), - Patch::encodings[out.signature.vnorm].size(dst_patch.nv)); - } else { - assert(0); - } - } - - //copying entry information; - dst_entry.sphere = src_entry.sphere; - dst_entry.error = src_entry.error; - dst_entry.cone = src_entry.cone; - - out.borders.ResizeBorder(p, src_border.Size()); - Border &dst_border = out.GetBorder(p); - memcpy(dst_border.Start(), src_border.Start(), - src_border.Size() * sizeof(Link)); - //TODO test this - if(zsort) - for(unsigned i = 0; i < dst_border.Size(); i++) - dst_border[i].end_patch = backward[dst_border[i].end_patch]; - - } - report.Finish(); - - //TODO this is ok only if we have faces still! - if(add_normals) { - cout << "Computing normals" << endl; - ComputeNormals(out); - } - - if(add_colors) { - // if(!plysource.size()) - //source of color: - // cerr << "Unsupported color\n"; - // return -1; - } - - if(qvertex && add_normals) { - report.Init(nexus.size()); - cout << "Quantizing vertices\n"; - for(unsigned int patch = 0; patch < nexus.size(); patch++) { - report.Step(patch); - Patch src_patch = nexus.GetPatch(patch); - - float *ptr = (float *)src_patch.Vert3fBegin(); - for(int i = 0; i < src_patch.nv*3; i++) - ptr[i] = qvertex * (int)(ptr[i]/qvertex); - } - report.Finish(); - } - - out.sphere = nexus.sphere; - - out.Close(); - nexus.Close(); - return 0; -} - - -void printInfo(Nexus &nexus, bool verbose, bool dump_history) { - //perform locality statistics - double meandist = 0; - vcg::Sphere3f last = nexus[0].sphere; - for(unsigned int i = 1; i < nexus.size(); i++) { - vcg::Sphere3f &sphere = nexus[i].sphere; - double dist = vcg::Distance(last.Center(), sphere.Center()); - meandist += dist; - last = sphere; - } - meandist /= nexus.size() -1; - cout << "\n\tCompressed: " << nexus.IsCompressed() - << "\n\tStripped : " - << (int)(nexus.signature.face == Signature::STRIPS) - << "\n\tColor : " << (int)(nexus.signature.vcolor) - << "\n\tNormal : " << (int)(nexus.signature.vnorm) - << "\n\tTexture : " << (int)(nexus.signature.vtext) - << "\n\tData : " << (int)(nexus.signature.vdata) - << "\n\n\tVertices: " << nexus.totvert - << "\tFaces : " << nexus.totface - << "\tPatches : " << nexus.size() - << "\n\tSphere : " - << nexus.sphere.Center()[0] << " " - << nexus.sphere.Center()[1] << " " - << nexus.sphere.Center()[2] << " R: " - << nexus.sphere.Radius() - << "\n\tAverage distance: " << meandist - << "\n\tChunk size " << nexus.chunk_size << endl; - - if(dump_history) { - if(nexus.history.IsQuick()) { - cout << "Quick format\n"; - for(unsigned int i = 0; i < nexus.history.n_nodes(); i++) { - cout << "Node: " << i << " out: "; - History::Node node = nexus.history.nodes[i]; - for(History::Link *l = node.out_begin; l != node.out_end; l++) { - cout << "."; - for(unsigned int p = l->begin; p != l->end; p++) { - cout << p << " "; - } - } - cout << " in: "; - for(History::Link *j = node.in_begin; j != node.in_end; j++) { - cout << "."; - for(unsigned int p = j->begin; p != j->end; p++) { - cout << p << " "; - } - } - cout << endl; - } - - } else { - cout << "Update format\n"; - for(unsigned int i = 0; i < nexus.history.updates.size(); i++) { - History::Update &update = nexus.history.updates[i]; - cout << "Created: "; - for(unsigned int k = 0; k < update.created.size(); k++) { - cout << update.created[k] << " "; - } - cout << "\nErased: "; - for(unsigned int k = 0; k < update.erased.size(); k++) { - cout << update.erased[k] << " "; - } - cout << "\n\n"; - } - } - } - - if(verbose) { - for(unsigned int i = 0; i < nexus.size(); i++) { - Entry &entry = nexus[i]; - cout << i << " -> nv: " << entry.nvert << " nf: " << entry.nface - << " error: " << entry.error - << " disk_size: " << entry.disk_size - << " start: " << entry.patch_start << endl; - cout << " Cone: " << entry.cone.n[0] << " " - << entry.cone.n[1] << " " - << entry.cone.n[2] << " " - << entry.cone.n[3] << "\n"; - - } - cout << endl; - } -} diff --git a/apps/nexus/nxsexport.h b/apps/nexus/nxsexport.h deleted file mode 100644 index a38bacc8..00000000 --- a/apps/nexus/nxsexport.h +++ /dev/null @@ -1,119 +0,0 @@ -#ifndef NXS_EXPORT_H -#define NXS_EXPORT_H - -#include -#include -#include "nexus.h" -#include "extraction.h" - -namespace nxs { - -template void ExportTriMesh(Nexus &nexus, - vector &patches, - MESH &mesh) { - - - typedef typename MESH::VertexType VertexType; - typedef typename MESH::FaceType FaceType; - typedef typename MESH::ScalarType ScalarType; - //for every patch record the global position of the vertices - std::map > remap; - - //Resize all remapping vectors - for(unsigned int p = 0; p < patches.size(); p++) { - unsigned int npatch = patches[p]; - remap[npatch].resize(nexus[npatch].nvert, 0xffffffff); - } - - //Filling remapping vectors - unsigned int vcount = 0; - for(unsigned int p = 0; p < patches.size(); p++) { - unsigned int npatch = patches[p]; - Patch &patch = nexus.GetPatch(npatch); - - std::vector &rmp = remap[npatch]; - - for(unsigned int v = 0; v < patch.nv; v++) - if(rmp[v] == 0xffffffff) - rmp[v] = vcount++; - - Border &border = nexus.GetBorder(npatch); - for(unsigned int k = 0; k < border.Size(); k++) { - Link link = border[k]; - if(link.IsNull()) continue; - - if(remap.count(link.end_patch)) //internal - if(remap[link.end_patch][link.end_vert] == 0xffffffff) - remap[link.end_patch][link.end_vert] = rmp[link.start_vert]; - } - } - - mesh.vert.resize(vcount); - mesh.VertexNumber() = vcount; - //copying vectors and faces - for(unsigned int p = 0; p < patches.size(); p++) { - unsigned int npatch = patches[p]; - Patch &patch = nexus.GetPatch(npatch); - - std::vector &rmp = remap[npatch]; - - //coping vertices - VertexType vertex; - vertex.ClearFlags(); - for(unsigned int v = 0; v < patch.nv; v++) { - vertex.P()[0] = (ScalarType)patch.Vert3f(v)[0]; - vertex.P()[1] = (ScalarType)patch.Vert3f(v)[1]; - vertex.P()[2] = (ScalarType)patch.Vert3f(v)[2]; - if(mesh.HasPerVertexNormal()) { - if(nexus.signature.vnorm == Encodings::SHORT4) { - vertex.N()[0] = (ScalarType)((short *)patch.VNormBegin())[v*4]; - vertex.N()[1] = (ScalarType)((short *)patch.VNormBegin())[v*4 +1]; - vertex.N()[2] = (ScalarType)((short *)patch.VNormBegin())[v*4 +2]; - } else if(nexus.signature.vnorm == Encodings::FLOAT3) { - vertex.N()[0] = (ScalarType)((float *)patch.VNormBegin())[v*3]; - vertex.N()[0] = (ScalarType)((float *)patch.VNormBegin())[v*3 +1]; - vertex.N()[0] = (ScalarType)((float *)patch.VNormBegin())[v*3 +2]; - } else if(nexus.signature.vnorm) { - //i should write other exporters - assert(0); - } - } - if(mesh.HasPerVertexColor() && nexus.signature.vcolor) { - if(nexus.signature.vcolor == Encodings::BYTE4) { - vertex.C()[0] = ((unsigned char *)patch.VColorBegin())[v*4]; - vertex.C()[1] = ((unsigned char *)patch.VColorBegin())[v*4+1]; - vertex.C()[2] = ((unsigned char *)patch.VColorBegin())[v*4+2]; - vertex.C()[3] = ((unsigned char *)patch.VColorBegin())[v*4+3]; - } else if(nexus.signature.vcolor) { - //i should write other exporters - assert(0); - } - } - if(mesh.HasPerVertexTexture() && nexus.signature.vtext) { - //i should write other exporters - assert(0); - } - assert(rmp[v] < mesh.vert.size()); - mesh.vert[rmp[v]] = vertex; - } - //remap faces now - FaceType face; - if(nexus.signature.face == Signature::TRIANGLES) { - for(unsigned int f = 0; f < patch.nf; f++) { - face.V(0) = &mesh.vert[rmp[patch.Face(f)[0]]]; - face.V(1) = &mesh.vert[rmp[patch.Face(f)[1]]]; - face.V(2) = &mesh.vert[rmp[patch.Face(f)[2]]]; - mesh.face.push_back(face); - mesh.SimplexNumber()++; - /*static bool HasPerFaceColor() {return FaceType::HasFaceColor() ;} - static bool HasPerFaceNormal() {return FaceType::HasFaceNormal();} - static bool HasPerFaceMark() {return FaceType::HasFaceMark() ;} - static bool HasPerFaceQuality(){return FaceType::HasFaceQuality();}*/ - } - } - } -} - -}//namespace - -#endif diff --git a/apps/nexus/nxspatcher.cpp b/apps/nexus/nxspatcher.cpp deleted file mode 100644 index 43f543c3..00000000 --- a/apps/nexus/nxspatcher.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include -#include "crude.h" -using namespace nxs; -using namespace vcg; -using namespace std; - -int main(int argc, char *argv[]) { - if(argc != 2) { - cerr << "Uso: " << argv[0] << " \n"; - return -1; - } - - unsigned int side = atoi(argv[1]); - if(side > 1000 || side == 0) { - cerr << "Invalid side: " << argv[1] << endl; - return -1; - } - - Crude crude; - if(!crude.Create("square")) { - cerr << "Could not create square" << endl; - return -1; - } - - int half = side/2; - crude.Resize(side * side, (side-1) * (side-1) * 2); - for(unsigned int x = 0; x < side; x++) - for(unsigned int y = 0; y < side; y++) { - // Point3f p(x*x*x/((float)side), - // y*y*y/((float)side), x*y/((float)side)); - Point3f p(x, y, sqrt((float)x*x + y*y)); - crude.SetVertex(x + side * y, p); - crude.GetBox().Add(p); - } - - for(unsigned int x = 0; x < side-1; x++) - for(unsigned int y = 0; y < side-1; y++) { - unsigned int pos = x + side*y; - Crude::Face face(pos, pos + 1, pos + side); - crude.SetFace(0 + 2*x + (side-1)*y*2, face); - - face = Crude::Face(pos + 1, pos + 1 + side, pos +side); - crude.SetFace(1 + 2*x + (side-1)*y*2, face); - } - - crude.Close(); - return 0; -} diff --git a/apps/nexus/nxsserver.cpp b/apps/nexus/nxsserver.cpp deleted file mode 100644 index a474f9d4..00000000 --- a/apps/nexus/nxsserver.cpp +++ /dev/null @@ -1,223 +0,0 @@ -/**************************************************************************** -* 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 -#include - - -#include "fragment.h" -#include "decimate.h" - -using namespace pt; -using namespace nxs; -using namespace vcg; -using namespace std; - - -class FragOutQueue: public msgqueue { -public: - FragOutQueue(ipstream &cli): client(cli) {} - void msghandler(message &msg) { - if(msg.id != MSG_USER) { - defhandler(msg); - return; - } - Fragment &fragment = *(Fragment *)(msg.param); - - // pout.putf("Sending: %d\n", fragment.id); - outmemory outm; - outm.open(); - fragment.Write(&outm); - pt::string a = outm.get_strdata(); - try { - client.write((const char *)a, length(a)); - client.flush(); - pout.putf("Sent fragment id: %d\n", fragment.id); - } catch (estream *e) { - perr.putf("Error: %s\n", pconst(e->get_message())); - delete e; - posturgent(MSG_QUIT); - } - delete (Fragment *)(msg.param); - } - ipstream &client; -}; - -class FragInQueue: public msgqueue { -public: - FragInQueue(FragOutQueue &o): out(o) {} - void msghandler(message &msg) { - if(msg.id != MSG_USER) { - if(msg.id == MSG_QUIT) - out.posturgent(MSG_QUIT); - defhandler(msg); - return; - } - Fragment &fragin = *(Fragment *)(msg.param); - // pout.putf("Processing: %d\n", fragin.id); - vector newvert; - vector newface; - vector newbord; - Join(fragin, newvert, newface, newbord); - - float error = Decimate(QUADRIC, - (unsigned int)((newface.size()/3) * 0.5), - newvert, newface, newbord); - - message *outmsg = new message(MSG_USER); - outmsg->param = (int)(new Fragment); - Fragment &fragout = *(Fragment *)(outmsg->param); - - fragout.error = error; - fragout.id = fragin.id; - fragout.seeds = fragin.seeds; - fragout.seeds_id = fragin.seeds_id; - Split(fragout, newvert, newface, newbord); - out.post(outmsg); - delete (Fragment *)(msg.param); - } - - FragOutQueue &out; -}; - - -class Reader: public thread { -public: - Reader(ipstream &cli, FragInQueue &que): - thread(false), client(cli), queue(que) {} - - ~Reader() {waitfor(); } - - void execute() { - while(1) { - if(get_signaled()) return; - message *msg = new message(MSG_USER); - msg->param = (int)(new Fragment); - Fragment &fragment = *(Fragment *)(msg->param); - if(!fragment.Read(&client)) { - pout.putf("Could not read!\n"); - queue.posturgent(MSG_QUIT); - return; - } - queue.post(msg); - // pout.putf("Incoming: %d\n", fragment.id); - } - } - void cleanup() {} - - ipstream &client; - FragInQueue &queue; -}; - -class Worker: public thread { -public: - Worker(FragInQueue &que): thread(false), queue(que) {} - ~Worker() { waitfor(); } - - void execute() { - queue.run(); - } - void cleanup() {} - FragInQueue &queue; -}; - -class Writer: public thread { -public: - Writer(FragOutQueue &que): thread(false), queue(que) {} - ~Writer() {waitfor(); } - void execute() { - queue.run(); - } - void cleanup() {} - FragOutQueue &queue; -}; - - -void servermain(ipstmserver& svr) { - ipstream client; - - while(true) { - // serve() will wait for a connection request and will prepare - // the supplied ipstream object for talking to the peer. - svr.serve(client); - perr.putf("Serving clients!\n"); - if (client.get_active()) { - try { - pout.putf("Incoming connection\n"); - FragOutQueue out(client); - FragInQueue in(out); - Reader reader(client, in); - Worker worker(in); - Writer writer(out); - - reader.start(); - worker.start(); - writer.start(); - - reader.waitfor(); - worker.waitfor(); - writer.waitfor(); - - client.flush(); - client.close(); - } catch(estream* e) { - perr.putf("Error: %s\n", pconst(e->get_message())); - delete e; - } - } - perr.putf("Restarting\n"); - } -} - -int main(int argc, char *argv[]) { - ipstmserver svr; - - int port = 10102; - if(argc == 2) { - port = atoi(argv[1]); - if(port < 1024) { - perr.putf("Error: invalid port: %s\n", argv[1]); - return -1; - } - } - - try { - // bind to all local addresses on port 8085 - svr.bindall(port); - - pout.putf("Ready to answer queries on port %d\n", port); - - // enter an infinite loop of serving requests - servermain(svr); - } catch(estream* e) { - perr.putf("FATAL: %s\n", pconst(e->get_message())); - delete e; - } - return 0; -} diff --git a/apps/nexus/nxstest.cpp b/apps/nexus/nxstest.cpp deleted file mode 100644 index 0461e185..00000000 --- a/apps/nexus/nxstest.cpp +++ /dev/null @@ -1,163 +0,0 @@ -/**************************************************************************** -* 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 $ -Revision 1.5 2005/02/08 12:43:03 ponchio -Added copyright - - -****************************************************************************/ - -#include - -#include "nexus.h" -#include "watch.h" - -using namespace vcg; -using namespace std; -using namespace nxs; - -int main(int argc, char *argv[]) { - if(argc != 2) { - cerr << "Usage: " << argv[0] << " \n"; - return -1; - } - Nexus nexus; - if(!nexus.Load(argv[1], true)) { - cerr << "Could not open file: " << argv[1] << endl; - return -1; - } - Report report(nexus.size()); - for(unsigned int patchid = 0; patchid < nexus.size(); patchid++) { - report.Step(patchid); - Entry &info = nexus[patchid]; - Patch &patch = nexus.GetPatch(patchid); - for(int f = 0; f < patch.nf; f++) { - unsigned short *face = patch.Face(f); - for(int k = 0; k < 3; k++) { - if(face[k] > patch.nv) { - cerr << "Invalid face number: " << face[k] << " > " - << patch.nv << endl; - cerr << "At patch: " << patchid << endl; - cerr << "start: " << info.patch_start << endl; - cerr << "nf: " << info.nface << " nv: " << info.nvert << endl; - //exit(0); - } - } - } - Sphere3f &sphere = info.sphere; - for(int v = 0; v < patch.nv; v++) { - Point3f &p = patch.Vert3f(v); - float dist = Distance(sphere, p); - if(dist > 0.001) { - //if(!info.sphere.IsIn(p)) { - cerr << "Vertex outside bound: (" << p[0] << " " << p[1] << " " << p[2] << ")\n"; - Point3f &c = sphere.Center(); - cerr << "Sphere: (" << c[0] << " " << c[1] << " " << c[2] << ") R: " << sphere.Radius() << endl;; - cerr << "Distance: " << dist << endl; - cerr << "At patch: " << patchid << endl; - - } - } - } - report.Finish(); - - cerr << "Testing borders\n"; - - for(unsigned int patchid = 0; patchid < nexus.size(); patchid++) { - Entry &info = nexus[patchid]; - Border &border = nexus.GetBorder(patchid); - for(unsigned int i = 0; i < border.Size(); i++) { - Link &link = border[i]; - if(link.start_vert == 0 && link.end_vert == 0 && link.end_patch == 0) { - cerr << "patch: " << patchid << " corrupted memory?" << endl; - } - if(link.IsNull()) { - cerr << "Null link: " << i << " at patch: " << patchid << endl; - exit(0); - } - if(link.end_patch < 0 || link.end_patch >= nexus.size()) { - cerr << "Invalid link end patch: " << link.end_patch << " at patch: " << patchid << endl; - exit(0); - } - if(link.start_vert > info.nvert) { - cerr << "Invalid link start_vert: " << link.start_vert << " at patch: " << patchid << endl; - exit(0); - } - if(link.end_vert > nexus[link.end_patch].nvert) { - cerr << "Invalid link end vert: " << link.end_vert << " at patch: " << patchid << endl; - exit(0); - } - } - } - - cerr << "Reciprocity borders test\n"; - for(unsigned int patchid = 0; patchid < nexus.size(); patchid++) { - Entry &info = nexus[patchid]; - Border &border = nexus.GetBorder(patchid); - vector links; - links.resize(border.Size()); - memcpy(&*links.begin(),&(border[0]),links.size() * sizeof(Link)); - for(unsigned int i = 0; i < links.size(); i++) { - Link &link = links[i]; - Border &rborder = nexus.GetBorder(link.end_patch, false); - - bool found = false; - for(unsigned int k = 0; k < rborder.Size(); k++) { - Link rlink = rborder[k]; - if(rlink.end_patch == patchid) { - - if(rlink.end_vert == link.start_vert) { - if(rlink.start_vert != link.end_vert) { - cerr << "Something wrong with links!\n"; - exit(0); - } - found = true; - break; - } - if(rlink.start_vert == link.end_vert) { - if(rlink.end_vert != link.start_vert) { - cerr << "Something wrong with links!\n"; - exit(0); - } - found = true; - break; - } - } - } - if(!found) { - cerr << "A link is one way from patch: " << patchid << " vert: " << link.start_vert - << " to patch: " << link.end_patch << " vert: " << link.end_vert << endl; - for(unsigned int t = 0; t < rborder.Size(); t++) { - Link &rlink = rborder[t]; - cerr << rlink.start_vert << " -> p: " << rlink.end_patch << " v: " << rlink.end_vert << endl; - } - exit(0); - } - } - } - - return 0; -} diff --git a/apps/nexus/nxstypes.h b/apps/nexus/nxstypes.h deleted file mode 100644 index 66032183..00000000 --- a/apps/nexus/nxstypes.h +++ /dev/null @@ -1,43 +0,0 @@ -/**************************************************************************** -* 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_TYPES_H -#define NXS_TYPES_H - -#ifdef WIN32 -typedef __int64 int64; -#else -typedef unsigned long long int64; -//typedef unsigned long long uint64; -#endif - -typedef int int32; -typedef unsigned int uint32; - -#endif diff --git a/apps/nexus/patch.cpp b/apps/nexus/patch.cpp deleted file mode 100644 index 5320b688..00000000 --- a/apps/nexus/patch.cpp +++ /dev/null @@ -1,386 +0,0 @@ -/**************************************************************************** -* 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 $ -Revision 1.13 2005/02/22 10:38:15 ponchio -Debug, cleaning and optimization. - -Revision 1.12 2005/02/21 19:05:58 ponchio -i already fixed this bug. I hate you cvs. - -Revision 1.11 2005/02/19 12:06:55 ponchio -Debug... - -Revision 1.10 2005/02/19 10:45:05 ponchio -Patch generalized and small fixes. - -Revision 1.9 2005/02/08 12:43:03 ponchio -Added copyright - - -****************************************************************************/ - -#include "patch.h" -#ifdef WIN32 -#include "minilzo.108/minilzo.h" -#else -#include -#endif -#include -using namespace std; -using namespace nxs; - -#ifdef WIN32 -static double wrkmem[LZO1X_1_MEM_COMPRESS/sizeof(double) +1]; -#else -static double wrkmem[LZO1X_999_MEM_COMPRESS/sizeof(double) +1]; -#endif - - -Encodings Patch::encodings; - -Encodings::Encodings() { - for(unsigned int i = 0; i < 17; i++) { - e[i].bytes = 0; - e[i].comps = 0; - e[i].pack = NULL; - e[i].unpack = NULL; - } - e[1].bytes = 1; - e[2].bytes = 2; - e[3].bytes = 4; - e[4].bytes = 8; - e[1].comps = e[2].comps = e[3].comps = e[4].comps = 1; - e[5].bytes = 1; - e[6].bytes = 2; - e[7].bytes = 4; - e[8].bytes = 8; - e[5].comps = e[6].comps = e[7].comps = e[8].comps = 2; - e[9].bytes = 1; - e[10].bytes = 2; - e[11].bytes = 4; - e[12].bytes = 8; - e[9].comps = e[10].comps = e[11].comps = e[12].comps = 3; - e[13].bytes = 1; - e[14].bytes = 2; - e[15].bytes = 4; - e[16].bytes = 8; - e[13].comps = e[14].comps = e[15].comps = e[16].comps = 4; -} - - -void pad8(unsigned int &s) { - if((s & 0x00000007) != 0) { - s>>=3; s++; s<<=3; - } -} -void pad(unsigned int &size) { - while(size&0x3) size++; -} - -void shuffle(float *buffer, unsigned int size, unsigned int stride) { - float *tmp = new float[size]; - unsigned int count = 0; - - unsigned int nelem = size/stride; - for(unsigned int s = 0; s < stride; s++) { - float *ptr = buffer + s; - for(unsigned int i = 0; i < nelem; i++) { - tmp[count++] = *ptr; - ptr += stride; - } - } - memcpy(buffer, tmp, size * sizeof(float)); - delete []tmp; -} - -void unshuffle(float *buffer, unsigned int size, unsigned int stride) { - float *tmp = new float[size]; - - unsigned int count = 0; - unsigned int nelem = size/stride; - for(unsigned int s = 0; s < stride; s++) { - float *ptr = tmp + s; - for(unsigned int i = 0; i < nelem; i++) { - *ptr = buffer[count++]; - ptr += stride; - } - } - memcpy(buffer, tmp, size * sizeof(float)); - delete []tmp; -} - -void subtract(float *buffer, unsigned int size) { - float p = buffer[0]; - float q; - for(unsigned int i = 1; i < size; i++) { - q = buffer[i]; - buffer[i] -= p; - p = q; - } -} - -void unsubtract(float *buffer, unsigned int size) { - for(unsigned int i = 1; i < size; i++) - buffer[i] += buffer[i-1]; -} - - -Patch::Patch(Signature &signature, char *s, - unsigned short nvert, unsigned short nface): - fstart(s) { - Init(signature, nvert, nface); -} - -void Patch::Init(Signature &signature, - unsigned short nvert, unsigned short nface) { - nv = nvert; - nf = nface; - - unsigned int offset = 0; - - if(signature.face == Signature::TRIANGLES) - offset += nf * 3 * sizeof(unsigned short); - else if (signature.face == Signature::STRIPS) - offset += nf * sizeof(unsigned short); - else if (signature.face == Signature::TETRAS) - offset += nf * 4 * sizeof(unsigned short); - else if (signature.face == Signature::SLICE) { - assert(0); - //non lo so... - } - pad8(offset); - - fstartc = fstart + offset; - offset += encodings[signature.fcolor].size(nf); - fstartn = fstart + offset; - offset += encodings[signature.fnorm].size(nf); - fstartt = fstart + offset; - offset += encodings[signature.ftext].size(nf); - fstartd = fstart + offset; - offset += encodings[signature.fdata].size(nf); - - vstart = fstart + offset; - if(signature.vert == Signature::POINT3F) - offset += nv * sizeof(float) * 3; - else if(signature.vert == Signature::POINT4F) - offset += nv * sizeof(float) * 4; - else - assert(0); - pad8(offset); - - vstartc = fstart + offset; - offset += encodings[signature.vcolor].size(nv); - vstartn = fstart + offset; - offset += encodings[signature.vnorm].size(nv); - vstartt = fstart + offset; - offset += encodings[signature.vtext].size(nv); - vstartd = fstart + offset; - offset += encodings[signature.vdata].size(nv); - - - /* if(signature & NXS_FACES) - vstart = (float *)(((char *)start) + nf * sizeof(unsigned short) * 3); - else if(signature & NXS_STRIP) - vstart = (float *)(((char *)start) + nf * sizeof(unsigned short)); - else - vstart = (float *)start; - - //align memory - if(((int)vstart) & 0x2) vstart = (float *)(((char *)vstart) + 2); - - cstart = nv * 3; - if(signature & NXS_COLORS) - nstart = cstart + nv; - else - nstart = cstart; - - if(signature & NXS_NORMALS_SHORT) - tstart = nstart + nv * 2; - else if(signature & NXS_NORMALS_FLOAT) - tstart = nstart + nv * 3; - else - tstart = nstart; - - if(signature & NXS_TEXTURES_SHORT) - dstart = tstart + nv; - else if(signature & NXS_TEXTURES_FLOAT) - dstart = tstart + nv; - else - dstart = tstart;*/ -} - -unsigned int Patch::ChunkSize(Signature &signature, - unsigned short nvert, - unsigned short nface, - unsigned int chunk_size) { - unsigned int size = ByteSize(signature, nvert, nface); - size = (size/chunk_size + 1); - return size; -} - -unsigned int Patch::ByteSize(Signature &signature, - unsigned short nvert, - unsigned short nface) { - - unsigned int size = 0; - if(signature.face == Signature::TRIANGLES) - size += nface * 3 * sizeof(unsigned short); - else if (signature.face == Signature::STRIPS) - size += nface * sizeof(unsigned short); - else if (signature.face == Signature::TETRAS) - size += nface * 4 * sizeof(unsigned short); - else if (signature.face == Signature::SLICE) { - assert(0); - //non lo so... - } - pad8(size); - - size += encodings[signature.fcolor].size(nface); - size += encodings[signature.fnorm].size(nface); - size += encodings[signature.ftext].size(nface); - size += encodings[signature.fdata].size(nface); - - if(signature.vert == Signature::POINT3F) - size += nvert * sizeof(float) * 3; - else if(signature.vert == Signature::POINT4F) - size += nvert * sizeof(float) * 4; - else - assert(0); - pad8(size); - - size += encodings[signature.vcolor].size(nvert); - size += encodings[signature.vnorm].size(nvert); - size += encodings[signature.vtext].size(nvert); - size += encodings[signature.vdata].size(nvert); - - //this condition should really rarely happen but helps save space - //during construction - if(size < nface * 3 * sizeof(unsigned int)) - size = nface * 3 * sizeof(unsigned int); - - return size; - - - /* unsigned int size = 0; - if(signature & NXS_STRIP) - size += nface * sizeof(unsigned short); - else if(signature & NXS_FACES) - size += nface * 3 * sizeof(unsigned short); - - //memory alignment - pad(size); - - size += nvert * sizeof(vcg::Point3f); - - if(signature & NXS_COLORS) - size += nvert * sizeof(unsigned int); - - if(signature & NXS_NORMALS_SHORT) - size += nvert * 4 * sizeof(short); - - if(signature & NXS_NORMALS_FLOAT) - size += nvert * 3 * sizeof(float); - - if(signature & NXS_TEXTURES_SHORT) - size += nvert * 2 * sizeof(short); - - if(signature & NXS_TEXTURES_FLOAT) - size += nvert * 2 * sizeof(float); - - if(signature & NXS_DATA8) - size += nvert * sizeof(char); - pad(size); - if(signature & NXS_DATA16) - size += nvert * 2 * sizeof(char); - pad(size); - if(signature & NXS_DATA32) - size += nvert * 4 * sizeof(char); - if(signature & NXS_DATA64) - size += nvert * 8 * sizeof(char); - - - //this condition should really rarely happen but helps save space - //during construction - if(size < nface * 3 * sizeof(unsigned int)) - size = nface * 3 * sizeof(unsigned int); - - return size;*/ -} - - -char *Patch::Compress(unsigned int ram_size, unsigned int &size) { - - //lets use differences - // shuffle((float *)VertBegin(), nv * 3, 3); - // subtract((float *)VertBegin(), nv * 3); - - //TODO use OVERLAP and test speed - //TODO fill chunk padding with zeroes? - size = ram_size + ram_size/64 + 23; - char *buffer = new char[size]; -#ifdef WIN32 - lzo1x_1_compress(((unsigned char *)fstart), ram_size, - (unsigned char *)buffer + sizeof(int), &size, - (char *)wrkmem); -#else - lzo1x_999_compress(((unsigned char *)fstart), ram_size, - (unsigned char *)buffer + sizeof(int), &size, - (char *)wrkmem); - - lzo1x_optimize((unsigned char *)buffer + sizeof(int), size, - ((unsigned char *)fstart), &ram_size, - NULL); -#endif - - *(int *)buffer = size; - size += sizeof(int); - - // memcpy(buffer, start, ram_size); - // size = ram_size; - - return buffer; - -} - -void Patch::Decompress(unsigned int ram_size, void *src, unsigned int src_sz) { - - unsigned int size = *(int *)src; - assert(size < src_sz + sizeof(int)); - unsigned int dst_size = ram_size; - - int ret = lzo1x_decompress_safe(((unsigned char *)src) + sizeof(int), size, - (unsigned char *)fstart, &dst_size, 0); - if(ret != 0) { - cerr << "Ret from decompress: " << ret << endl; - exit(-1); - } - assert(dst_size == ram_size); - //TODO add 3 to start... so we can use asm_fast decompressor - - // unsubtract((float *)VertBegin(), nv * 3); - // unshuffle((float *)VertBegin(), nv * 3, 3); -} - diff --git a/apps/nexus/patch.h b/apps/nexus/patch.h deleted file mode 100644 index da77b2b9..00000000 --- a/apps/nexus/patch.h +++ /dev/null @@ -1,169 +0,0 @@ -/**************************************************************************** -* 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 $ -Revision 1.13 2005/02/19 12:06:55 ponchio -Debug... - -Revision 1.12 2005/02/19 10:45:05 ponchio -Patch generalized and small fixes. - -Revision 1.11 2005/02/08 12:43:03 ponchio -Added copyright - - -****************************************************************************/ - -#ifndef NXS_PATCH_H -#define NXS_PATCH_H - -#include -#include - -namespace nxs { - - struct Signature { - - enum Face { TRIANGLES = 1, STRIPS = 2, TETRAS = 3, SLICE = 4 }; - - enum Vert { POINT2F = 1, POINT2D = 2, - POINT3F = 2, POINT3D = 3, - POINT4F = 4, POINT4D = 5 }; - - enum Compr { LZO = 1 }; - - unsigned char face; - unsigned char vert; - unsigned char compr; - unsigned char future; //who knows... - - unsigned char fcolor; - unsigned char fnorm; - unsigned char ftext; - unsigned char fdata; - - unsigned char vcolor; - unsigned char vnorm; - unsigned char vtext; - unsigned char vdata; - - Signature(): face(1), vert(2), compr(0), future(0), - fcolor(0), fnorm(0), ftext(0), fdata(0), - vcolor(0), vnorm(0), vtext(0), vdata(0) {} - }; - - - struct Encoding { - - unsigned char bytes; //size per element - unsigned char comps; //number of components - void (*pack)(char *start, unsigned int nelem); - void (*unpack)(char *start, unsigned int nelem); - - unsigned int size(unsigned short n) { - unsigned int s = (int)n * (int)bytes * (int)comps; - //padding a 8 bytes - if((s & 0x0000007) != 0) { - s>>=3; s++; s<<=3; - } - return s; - } - }; - - struct Encodings { - enum Name { EMPTY = 0, - BYTE1 = 1, SHORT1 = 2, FLOAT1 = 3, DOUBLE1 = 4, - BYTE2 = 5, SHORT2 = 6, FLOAT2 = 7, DOUBLE2 = 8, - BYTE3 = 9, SHORT3 = 10, FLOAT3 = 11, DOUBLE3 = 12, - BYTE4 = 13, SHORT4 = 14, FLOAT4 = 15, DOUBLE4 = 16 }; - Encodings(); - Encoding &operator[](int n) { return e[n]; } - protected: - Encoding e[17]; - - - }; - -class Patch { - public: - - static Encodings encodings; - - Patch(Signature &signature, char *s, - unsigned short nv, unsigned short nf); - - void Init(Signature &signature, unsigned short nv, unsigned short nf); - - vcg::Point3f *Vert3fBegin() { return (vcg::Point3f *)vstart; } - vcg::Point3f &Vert3f(int n) { return Vert3fBegin()[n]; } - unsigned short *FaceBegin() { return (unsigned short *)fstart; } - unsigned short *Face(int n) { return FaceBegin() + 3 * n; } - - //vcg::Point3f &Vert(unsigned short v) { return VertBegin()[v]; } - // unsigned short *Face(unsigned short f) { return FaceBegin() + f * 3; } - - char *VColorBegin() { return vstartc; } - char *VNormBegin() { return vstartn; } - char *VTextBegin() { return vstartt; } - char *VDataBegin() { return vstartd; } - - char *FColorBegin() { return fstartc; } - char *FNormBegin() { return fstartn; } - char *FTextBegin() { return fstartt; } - char *FDataBegin() { return fstartd; } - - static unsigned int ChunkSize(Signature &signature, - unsigned short nvert, - unsigned short nface, - unsigned int chunk_size); - - static unsigned int ByteSize(Signature &signature, - unsigned short nvert, - unsigned short nface); - - char *Compress(unsigned int ram_size, unsigned int &size); - void Decompress(unsigned int ram_size, void *src, unsigned int src_sz); - - - char *fstart; - char *vstart; - - unsigned short nf; - unsigned short nv; - - char *fstartc; - char *fstartn; - char *fstartt; - char *fstartd; - - char *vstartc; - char *vstartn; - char *vstartt; - char *vstartd; -}; - -} //namespace - -#endif diff --git a/apps/nexus/ply2crude.cpp b/apps/nexus/ply2crude.cpp deleted file mode 100644 index ef6e71a2..00000000 --- a/apps/nexus/ply2crude.cpp +++ /dev/null @@ -1,167 +0,0 @@ -/**************************************************************************** -* 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 $ -Revision 1.3 2004/07/15 14:32:49 ponchio -Debug. - -Revision 1.2 2004/07/05 15:49:39 ponchio -Windows (DevCpp, mingw) port. - -Revision 1.1 2004/07/04 15:30:00 ponchio -Changed directory structure. - -Revision 1.2 2004/07/02 13:09:57 ponchio -*** empty log message *** - -Revision 1.1 2004/06/23 00:08:05 ponchio -Created - - -****************************************************************************/ - -///TODO: allow other kinds of ply to be readed. - -#include -#include -#include -#include "crude.h" - -using namespace std; -using namespace vcg; -using namespace vcg::ply; -using namespace nxs; - -struct PlyVertex { - float v[3]; -}; - -struct PlyFace { - unsigned int f[3]; - unsigned char flags; -}; - -PropDescriptor plyprop1[3]= { - {"vertex","x",T_FLOAT,T_FLOAT,offsetof(PlyVertex,v[0]),0,0,0,0,0}, - {"vertex","y",T_FLOAT,T_FLOAT,offsetof(PlyVertex,v[1]),0,0,0,0,0}, - {"vertex","z",T_FLOAT,T_FLOAT,offsetof(PlyVertex,v[2]),0,0,0,0,0} -}; - -PropDescriptor plyprop2[1]= { - {"face", "vertex_indices",T_INT,T_UINT,offsetof(PlyFace,f[0]), - 1,0,T_UCHAR,T_UCHAR,offsetof(PlyFace,flags) } -}; - -int main(int argc, char *argv[]) { - if(argc <= 2) { - cerr << "Usage: " << argv[0] << " <...> \n"; - return 0; - } - string output = argv[argc-1]; - //test last one is not a ply - if(output.size() > 4 && - output.substr(output.size()-4, output.size()) == ".ply") { - cerr << "Last argument is output (so not a .ply)\n"; - return -1; - } - Crude crude; - if(!crude.Create(output, 0, 0)) { - cerr << "Could not create crude output\n"; - return -1; - } - Box3f box; - box.SetNull(); - for(int k = 1; k < argc-1; k++) { - PlyFile pf; - //Opening ply file - int val = pf.Open(argv[k], PlyFile::MODE_READ); - if(val == -1) { - cerr << "Could not open file '" << argv[k] << "'\n"; - return false; - } - - //testing for required vertex fields. - if( pf.AddToRead(plyprop1[0])==-1 || - pf.AddToRead(plyprop1[1])==-1 || - pf.AddToRead(plyprop1[2])==-1) { - cerr << "Error Ply file has not one of the required elements :" - << "xyz coords\n"; - return false; - } - - //testing for required face fields. - if( pf.AddToRead(plyprop2[0])==-1 ) { - cerr << "Error Ply file has not one of the required elements:" - << "faces\n"; - return false; - } - - unsigned int vertex_offset = crude.Vertices(); - for(unsigned int i = 0; i < pf.elements.size(); i++) { - if(!strcmp( pf.ElemName(i),"vertex")) { - unsigned int n_vertices = pf.ElemNumber(i); - unsigned int offset = crude.Vertices(); - crude.Resize(offset + n_vertices, crude.Faces()); - - cerr << "Adding " << n_vertices << " n_vertices" << endl; - pf.SetCurElement(i); - PlyVertex vertex; - Point3f p; - for(unsigned v = offset; v < offset + n_vertices; v++) { - pf.Read((void *) &vertex); - p[0] = vertex.v[0]; - p[1] = vertex.v[1]; - p[2] = vertex.v[2]; - box.Add(p); - crude.SetVertex(v, vertex.v); - } - - } else if( !strcmp( pf.ElemName(i),"face") ) { - unsigned int n_faces = pf.ElemNumber(i); - unsigned int offset = crude.Faces(); - crude.Resize(crude.Vertices(), offset + n_faces); - - cerr << "Adding " << n_faces << " n_faces" << endl; - pf.SetCurElement(i); - PlyFace face; - for(unsigned v = offset; v < offset + n_faces; v++) { - pf.Read((void *) &face); - face.f[0] += vertex_offset; - face.f[1] += vertex_offset; - face.f[2] += vertex_offset; - assert(face.f[0] < crude.Vertices() && - face.f[1] < crude.Vertices() && - face.f[2] < crude.Vertices()); - - crude.SetFace(v, face.f); - } - } - } - pf.Destroy(); - } - crude.GetBox() = box; - - return 0; -} diff --git a/apps/nexus/preload.cpp b/apps/nexus/preload.cpp deleted file mode 100644 index 1dbea185..00000000 --- a/apps/nexus/preload.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/**************************************************************************** -* 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 $ -Revision 1.7 2005/02/17 15:39:44 ponchio -Reorderes statistics a bit. - -Revision 1.6 2005/02/08 12:43:03 ponchio -Added copyright - - -****************************************************************************/ - -#include "preload.h" -#include "nexusmt.h" -#include - -using namespace std; -using namespace nxs; - - void Preload::execute() { - - total_disk = 0; - disk = 0; - - assert(mt); - while(!get_signaled()) { - trigger.wait(); - lock.enter(); - while(!queue.size()) { - trigger.reset(); - lock.leave(); - trigger.wait(); - lock.enter(); - } - //TODO check we are not loading too much memory! - assert(queue.size()); - Item &item = queue.back(); - if(item.error == 0 || mt->CanAdd(item)) { - //we cannot flush since we are not in the openGL thread - //and flushing includes VBO buffer flushing also. - Entry &entry = (*mt)[item.id]; - if(!entry.patch) { - disk += entry.disk_size; - disk_tri += entry.nvert * 2; - } - - Patch &patch = mt->GetPatch(item.id, item.error, false); - - //test... make sure memory is in ram (if not on vbo that is. - if(!entry.vbo_array) - total_disk += patch.Face(0)[0]; - - queue.pop_back(); - } else - queue.clear(); - lock.leave(); - } - } - diff --git a/apps/nexus/preload.h b/apps/nexus/preload.h deleted file mode 100644 index 34c7a406..00000000 --- a/apps/nexus/preload.h +++ /dev/null @@ -1,87 +0,0 @@ -/**************************************************************************** -* 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 $ -Revision 1.5 2005/02/17 15:39:44 ponchio -Reorderes statistics a bit. - -Revision 1.4 2005/02/08 12:43:03 ponchio -Added copyright - - -****************************************************************************/ - -#ifndef NXS_PRELOAD_H -#define NXS_PRELOAD_H - -#include - -#include - -#include - -#include "extraction.h" - -namespace nxs { - -class NexusMt; - -class Preload: public pt::thread{ - public: - - NexusMt *mt; - - pt::mutex lock; - pt::trigger trigger; - - std::vector queue; - - unsigned int disk; //kbytes readed from disk - unsigned int disk_tri; //number of triangles readed from disk - unsigned int total_disk; - Preload(): thread(false), trigger(false, false) {} - ~Preload() { - waitfor(); - } - - void execute(); - - void post(std::vector &patches) { - trigger.reset(); - lock.enter(); - - queue.reserve(patches.size()); - for(int i = patches.size() -1; i >= 0; i--) - queue.push_back(patches[i]); - - trigger.post(); - lock.leave(); - } - - void cleanup() {} -}; - -} -#endif diff --git a/apps/nexus/remapping.cpp b/apps/nexus/remapping.cpp deleted file mode 100644 index 2f118a13..00000000 --- a/apps/nexus/remapping.cpp +++ /dev/null @@ -1,520 +0,0 @@ -/**************************************************************************** -* 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 $ -Revision 1.11 2005/02/19 14:00:43 ponchio -Small opt. - -Revision 1.10 2005/02/19 10:45:05 ponchio -Patch generalized and small fixes. - -Revision 1.9 2005/02/08 12:43:03 ponchio -Added copyright - - -****************************************************************************/ - -#include - -#include "remapping.h" -#include "watch.h" - - -using namespace std; -using namespace vcg; -using namespace nxs; - -bool BlockIndex::Save(const string &file) { - FILE *fp = fopen(file.c_str(), "wb+"); - if(!fp) { - cerr << "Could not save: " << file << endl; - return false; - } - unsigned int nsize = size(); - fwrite(&nsize, sizeof(unsigned int), 1, fp); - fwrite(&*begin(), sizeof(BlockEntry), nsize, fp); - fclose(fp); - return true; -} - -bool BlockIndex::Load(const string &file) { - FILE *fp = fopen(file.c_str(), "rb"); - if(!fp) { - cerr << "Could not load: " << file << endl; - return false; - } - unsigned int nsize; - fread(&nsize, sizeof(unsigned int), 1, fp); - resize(nsize); - fread(&*begin(), sizeof(BlockEntry), nsize, fp); - fclose(fp); - return true; -} - -void nxs::Remap(VChain &chain, - VFile &points, - VFile &remap, - BlockIndex &index, - unsigned int target_size, - unsigned int min_size, - unsigned int max_size, - float scaling, - int steps) { - - VPartition *finepart = new VPartition; - chain.push_back(finepart); - BuildPartition(*finepart, points, target_size, min_size, max_size, steps); - - VPartition *coarsepart = new VPartition; - chain.push_back(coarsepart); - BuildPartition(*coarsepart, points, - (int)(target_size/scaling), min_size, max_size, steps); - - - cerr << "Fine size: " << finepart->size() << endl; - cerr << "Coarse size: " << coarsepart->size() << endl; - - -// typedef map, unsigned int> FragIndex; - typedef map > FragIndex; - FragIndex patches; - - unsigned int totpatches = 0; - vector count; - - Point3f bari; - for(unsigned int i = 0; i < points.Size(); i++) { - bari = points[i]; - - unsigned int fine = finepart->Locate(bari); - unsigned int coarse = coarsepart->Locate(bari); - - unsigned int patch; - if(!patches.count(coarse) || !patches[coarse].count(fine)) { - patch = totpatches; - patches[coarse][fine] = totpatches++; - } else - patch = patches[coarse][fine]; - - remap[i] = patch; - - while(count.size() <= patch) - count.push_back(0); - count[patch]++; - } - - for(unsigned int i = 0; i < totpatches; i++) { - if(count[i] > 32000) { - //TODO do something to reduce patch size... :P - cerr << "Found a patch too big... sorry\n"; - exit(0); - } - } - - unsigned int mean = 0; - for(unsigned int i = 0; i < count.size(); i++) - mean += count[i]; - mean /= count.size(); - - min_size /= 4; - cerr << "Pruning small patches... < " << min_size << " mean: " << mean << endl; - - - //prune small patches - - vector patch_map; - patch_map.resize(totpatches); - for(unsigned int i = 0; i < totpatches; i++) - patch_map[i] = i; - - for(FragIndex::iterator s = patches.begin(); s != patches.end(); s++) { - map &fines = (*s).second; - - while(1) { - if(fines.size() <= 1) break; - unsigned int inf_fine = 0xffffffff; - unsigned int inf_count, min_count; - unsigned int min_fine = 0xffffffff; - unsigned int min_patch, inf_patch; - map::iterator t; - for(t = fines.begin(); t != fines.end(); t++) { - unsigned int c = count[(*t).second]; - if(inf_fine == 0xffffffff || c < inf_count) { - if(inf_fine != 0xffffffff) { - min_fine = inf_fine; - min_count = inf_count; - min_patch = inf_patch; - } - inf_fine = (*t).first; - inf_count = c; - inf_patch = (*t).second; - } else if(min_fine == 0xffffffff || c < min_count) { - min_fine = (*t).first; - min_count = c; - min_patch = (*t).second; - } - } - if(inf_count >= min_size || - inf_count + min_count > max_size) break; - - count[min_patch] += count[inf_patch]; - patch_map[inf_patch] = min_patch; - fines.erase(inf_fine); - } - } - for(unsigned int i = 0; i < totpatches; i++) - while(patch_map[patch_map[i]] != patch_map[i]) - patch_map[i] = patch_map[patch_map[i]]; - - //now we remap remaining patches into 0 - n. - unsigned int new_totpatches = 0; - vector patch_remap; - patch_remap.resize(totpatches, -1); - for(unsigned int i = 0; i < totpatches; i++) { - unsigned int p = patch_map[i]; - if(patch_remap[p] == -1) - patch_remap[p] = new_totpatches++; - patch_remap[i] = patch_remap[p]; - } - - cerr << "Building fragments\n"; - - //building fragments - for(FragIndex::iterator s = patches.begin(); s != patches.end(); s++) { - unsigned int coarse = (*s).first; - map &fines = (*s).second; - map::iterator t; - for(t = fines.begin(); t != fines.end(); t++) { - unsigned int fine = (*t).first; - unsigned int oldpatch = (*t).second; - assert(oldpatch < patch_remap.size()); - unsigned int patch = patch_remap[oldpatch]; - if(patch != -1) //not deleted... - chain.oldfragments[coarse].insert(patch); - } - } - - cerr << "remapping faces again\n"; - //remapping faces - index.resize(new_totpatches); - for(unsigned int i = 0; i < remap.Size(); i++) { - unsigned int patch = remap[i]; -#ifdef CONTROLS - if(patch == 0xffffffff) { - cerr << "RESIGH\n"; - exit(0); - } - if(patch_remap[patch] == -1) {//must relocate this thing.... - //TODO - cerr << "Could not do this\n"; - exit(0); - } -#endif - - - unsigned int newpatch = patch_remap[patch]; - assert(newpatch < index.size()); - remap[i] = newpatch; - BlockEntry &entry = index[newpatch]; - entry.size++; - } - - cerr << "fixing offsets in index\n"; - //Fixing offset - int64 offset = 0; - for(unsigned int i = 0; i < index.size(); i++) { - assert(index[i].size < 65000); - index[i].offset = offset; - offset += index[i].size; - } - -} - - -void nxs::BuildPartition(VPartition &part, - VFile &points, - unsigned int target_size, - unsigned int min_size, - unsigned int max_size, - int steps) { - - //TODO: improve quality of patches and implement threshold. - unsigned int ncells = points.Size()/target_size; - cerr << "Target partition size: " << ncells - << " mean: " << points.Size()/ncells << endl; - srand(0); - - for(unsigned int i = 0; i < points.Size(); i++) { - int f = (int)(target_size * (float)rand()/(RAND_MAX + 1.0)); - if(f == 2) { - Point3f &point = points[i]; - part.push_back(point); - } - } - - //TODO! Check for duplicates (use the closest :P) - part.Init(); - - - vector centroids; - vector counts; - - for(int step = 0; step < steps; step++) { - cerr << "Optimization step: " << step+1 << "/" << steps << endl; - - centroids.clear(); - counts.clear(); - centroids.resize(part.size(), Point3f(0, 0, 0)); - counts.resize(part.size(), 0); - - Report report(points.Size()); - - for(unsigned int v = 0; v < points.Size(); v++) { - if(v & 0xffff == 0xffff) - report.Step(v); - - unsigned int target = part.Locate(points[v]); - centroids[target] += points[v]; - counts[target]++; - } - - for(unsigned int v = 0; v < centroids.size(); v++) - if(counts[v] != 0) - centroids[v]/= counts[v]; - - double quality = 0; - for(int i = 0; i < part.size(); i++) - quality += (counts[i] - target_size) * (counts[i] - target_size); - - cerr << "Quality: " << quality << endl; - - if(step == steps-1) { - if(!Optimize(part, ncells, target_size, min_size, max_size, - centroids, counts, false)) - step--; - } else - Optimize(part, ncells, target_size, min_size, max_size, - centroids, counts, true); - } - cerr << "Partition size: " << part.size() - << " mean: " << (float)(points.Size()/part.size()) << endl << endl; -} - -void nxs::BuildLevel(VChain &chain, - Nexus &nexus, - unsigned int offset, - float scaling, - unsigned int target_size, - unsigned int min_size, - unsigned int max_size, - int steps) { - - unsigned int totface = 0; - unsigned int totvert = 0; - for(unsigned int idx = offset; idx < nexus.size(); idx++) { - totface += nexus[idx].nface; - totvert += nexus[idx].nvert; - } - - VPartition *fine = chain[chain.size()-1]; - fine->Init(); - - VPartition *coarse = new VPartition; - chain.push_back(coarse); - - //unsigned int ncells = (unsigned int)(fine.size() * scaling); - unsigned int ncells = (unsigned int)(scaling * totface/target_size); - - //TODO this method for selecting the seeds is ugly! - float ratio = ncells/(float)(nexus.size() - offset); - float cratio = 0; - for(unsigned int idx = offset; idx < nexus.size(); idx++) { - cratio += ratio; - if(cratio > 1) { - Patch patch = nexus.GetPatch(idx); - Point3f &v = patch.Vert3f(0); - coarse->push_back(v); - cratio -= 1; - } - } - - if(coarse->size() == 0) { - Patch patch = nexus.GetPatch(0); - coarse->push_back(patch.Vert3f(0)); - } - - float coarse_vmean = totface/(float)coarse->size(); - - coarse->Init(); - cerr << "Ncells: " << ncells << endl; - cerr << "Coarse size: " << coarse->size() << endl; - cerr << "Coarse mean: " << coarse_vmean << " mean_size: " << target_size << endl; - - //here goes some optimization pass. - //Coarse optimization. - vector centroids; - vector counts; - - for(int step = 0; step < steps; step++) { - cerr << "Optimization step: " << step+1 << "/" << steps << endl; - centroids.clear(); - counts.clear(); - centroids.resize(coarse->size(), Point3f(0, 0, 0)); - counts.resize(coarse->size(), 0); - - Report report(nexus.size()); - for(unsigned int idx = offset; idx < nexus.size(); idx++) { - report.Step(idx); - Patch patch = nexus.GetPatch(idx); - for(unsigned int i = 0; i < patch.nf; i++) { - unsigned short *face = patch.Face(i); - Point3f bari = (patch.Vert3f(face[0]) + - patch.Vert3f(face[1]) + - patch.Vert3f(face[2]))/3; - assert(coarse->size() > 0); - unsigned int target = coarse->Locate(bari); - assert(target < coarse->size()); - centroids[target] += bari; - counts[target]++; - } - } - - for(unsigned int v = 0; v < centroids.size(); v++) - if(counts[v] != 0) - centroids[v]/= counts[v]; - - if(step == steps-1) { - if(!Optimize(*coarse, ncells, (int)coarse_vmean, min_size, max_size, - centroids, counts, false)) - step--; - } else - Optimize(*coarse, ncells, (int)coarse_vmean, min_size, max_size, - centroids, counts, true); - } - chain.newfragments.clear(); -} - -int nxs::GetBest(VPartition &part, unsigned int seed, - vector &mark, - vector &counts) { - - vector nears; - vector dists; - int nnear = 7; - if(part.size() < 7) nnear = part.size()/2; - if(!nnear) return -1; - - part.Closest(part[seed], nnear, nears, dists); - int best = -1; - int bestcount = -1; - int bestdist = -1; - - for(int k = 0; k < nnear; k++) { - int c = nears[k]; - if(c == seed) continue; - assert(c >= 0); - assert(c < part.size()); - if(mark[c]) continue; - - if(bestcount < 0 || - (counts[c] < bestcount)) { - best = c; - bestcount = counts[c]; - } - } - return best; -} - -bool nxs::Optimize(VPartition &part, - unsigned int target_cells, - unsigned int target_size, - unsigned int min_size, - unsigned int max_size, - vector ¢roids, - vector &counts, - bool join) { - - if(max_size > target_size *3) - max_size = target_size * 3; - min_size = (unsigned int)(target_size * 0.3f); - - unsigned int toobig = 0; - unsigned int toosmall = 0; - for(unsigned int i = 0; i < part.size(); i++) { - if(counts[i] > max_size) toobig++; - if(counts[i] < min_size) toosmall--; - } - - unsigned int close = part.size()/2; - if(close < 1) close = 1; - if(close > 10) close = 10; - - unsigned int failed = 0; - vector seeds; - vector mark; - mark.resize(part.size(), false); - - vector nears; - vector dists; - //removing small ones. - for(unsigned int i = 0; i < part.size(); i++) { - if(counts[i] > max_size) { - float radius; - if(part.size() == 1) - radius = 0.00001; - else - radius = part.Radius(i)/4; - seeds.push_back(centroids[i] + Point3f(1, -1, 1) * radius); - seeds.push_back(centroids[i] + Point3f(-1, 1, 1) * radius); - seeds.push_back(centroids[i] + Point3f(-1, -1, -1) * radius); - seeds.push_back(centroids[i] + Point3f(1, 1, -1) * radius); - continue; - } - if(counts[i] < min_size) - continue; - - part.Closest(part[i], close, nears, dists); - Point3f dir(0,0,0); - - for(unsigned int k = 0; k < close; k++) { - unsigned int n = nears[k]; - float c = (target_size - (float)counts[n])/ - ((float)target_size * close); - - dir += (centroids[i] - part[n]) * c; - } - seeds.push_back(centroids[i] + dir); - } - part.clear(); - for(unsigned int i = 0; i < seeds.size(); i++) - part.push_back(seeds[i]); - - if(part.size() == 0) { - cerr << "OOOPS i accidentally deleted all seeds... backup :P\n"; - part.push_back(Point3f(0,0,0)); - } - part.Init(); - return true; -} diff --git a/apps/nexus/remapping.h b/apps/nexus/remapping.h deleted file mode 100644 index 51d6d3f4..00000000 --- a/apps/nexus/remapping.h +++ /dev/null @@ -1,95 +0,0 @@ -/**************************************************************************** -* 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_REMAPPING_H -#define NXS_REMAPPING_H - -#include -#include -#include "nxstypes.h" -#include "vchain.h" -#include "nexus.h" -#include "vfile.h" - -namespace nxs { - - struct BlockEntry { - BlockEntry(int64 o = 0, unsigned int s = 0): offset(o), size(s) {} - int64 offset; - unsigned int size; - }; - - class BlockIndex: public std::vector { - public: - bool Save(const std::string &file); - bool Load(const std::string &file); - }; - - void Remap(VChain &chain, - VFile &points, - VFile &remap, - BlockIndex &index, - unsigned int target_size, - unsigned int min_size, - unsigned int max_size, - float scaling, - int step); - - void BuildPartition(VPartition &part, - VFile &points, - unsigned int target_size, - unsigned int min_size, - unsigned int max_size, - int steps); - - void BuildLevel(VChain &chain, - Nexus &nexus, - unsigned int offset, - float scaling, - unsigned int target_size, - unsigned int min_size, - unsigned int max_size, - int steps); - - //removes small or really big patches. - bool Optimize(VPartition &part, - unsigned int target_cells, - unsigned int target_size, - unsigned int min_size, - unsigned int max_size, - std::vector ¢roids, - std::vector &counts, - bool join); - - int GetBest(VPartition &part, unsigned int seed, - std::vector &mark, - std::vector &counts); -} - -#endif diff --git a/apps/nexus/strip.cpp b/apps/nexus/strip.cpp deleted file mode 100644 index 8fe4ad85..00000000 --- a/apps/nexus/strip.cpp +++ /dev/null @@ -1,67 +0,0 @@ -#include - -//These header are neede byt tristipper... -#include -#include -#include - -#include "tristripper/tri_stripper.h" -using namespace triangle_stripper; - -#include "strip.h" - -using namespace std; -using namespace nxs; - -void nxs::ComputeTriStrip(unsigned short nfaces, unsigned short *faces, - vector &strip) { - - vector index; - index.resize(nfaces*3); - - for(int i = 0; i < nfaces*3; i++) - index[i] = faces[i]; - - int cache_size = 16; - tri_stripper stripper(index); - stripper.SetCacheSize(cache_size); - // = 0 will disable the cache optimizer - stripper.SetMinStripSize(0); - tri_stripper::primitives_vector primitives; - stripper.Strip(&primitives); - - if(primitives.back().m_Indices.size() < 3) - primitives.pop_back(); - - //TODO do this when mounting strips together. - if(primitives.back().m_Type == tri_stripper::PT_Triangles) { - tri_stripper::primitives p; - p = primitives.back(); - primitives.pop_back(); - for(unsigned int i = 0; i < p.m_Indices.size(); i += 3) { - tri_stripper::primitives s; - s.m_Type = tri_stripper::PT_Triangle_Strip; - s.m_Indices.push_back(p.m_Indices[i]); - s.m_Indices.push_back(p.m_Indices[i+1]); - s.m_Indices.push_back(p.m_Indices[i+2]); - primitives.push_back(s); - } - } - - for(unsigned int i = 0; i < primitives.size(); i++) { - tri_stripper::primitives &primitive = primitives[i]; - assert(primitive.m_Indices.size() != 0); - int len = primitive.m_Indices.size(); - for(int l = 0; l < len; l++) - strip.push_back(primitive.m_Indices[l]); - - - if(i < primitives.size()-1) { //not the last primitive. - strip.push_back(primitive.m_Indices[len-1]); - //TODO optimize this! - if((len%2) == 1) //do not change orientation.... - strip.push_back(primitive.m_Indices[len-1]); - strip.push_back(primitives[i+1].m_Indices[0]); - } - } -} diff --git a/apps/nexus/strip.h b/apps/nexus/strip.h deleted file mode 100644 index dcf315e6..00000000 --- a/apps/nexus/strip.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef NXS_TRISTRIP_H -#define NXS_TRISTRIP_H - -#include - -namespace nxs { - - void ComputeTriStrip(unsigned short nfaces, unsigned short *faces, - std::vector &strip); -} - -#endif diff --git a/apps/nexus/tristripper/graph_array.h b/apps/nexus/tristripper/graph_array.h deleted file mode 100644 index bea8f0f2..00000000 --- a/apps/nexus/tristripper/graph_array.h +++ /dev/null @@ -1,419 +0,0 @@ -// graph_array.h: interface for the graph_array class. -// -////////////////////////////////////////////////////////////////////// -// -// Copyright (C) 2002 Tanguy Fautré. -// -// This software is provided 'as-is', without any express or implied -// warranty. In no event will the authors be held liable for any damages -// arising from the use of this software. -// -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it -// freely, subject to the following restrictions: -// -// 1. The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. If you use this software -// in a product, an acknowledgment in the product documentation would be -// appreciated but is not required. -// 2. Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// 3. This notice may not be removed or altered from any source distribution. -// -// Tanguy Fautré -// softdev@pandora.be -// -////////////////////////////////////////////////////////////////////// -// -// Semi-dynamic directed graph -// *************************** -// -// Current version: 3.00 BETA 3 (04/12/2002) -// -// Comment: graph_array is equivalent to an array of nodes linked by -// arcs. -// This means you can't change the size (the number of nodes) -// of the graph once you created it (setsize() will delete -// any previous nodes and arcs). -// But you can add or remove arcs. -// -// History: - 3.00 BETA 3 (04/12/2002) - Added empty() -// - Changed some parameters from copy to reference -// - Fixed a bug with erase_arc -// - Un-inlined external functions -// - Added "insert_arc" which is equivalent to "insert" -// - 3.00 BETA 2 (16/11/2002) - Improved portability -// - 3.00 BETA 1 (27/08/2002) - First public release -// -////////////////////////////////////////////////////////////////////// - -#pragma once - -// namespace common_structures -namespace common_structures { - - - - -// graph_array main class -template -class graph_array -{ -public: - - class arc; - class node; - - // New types - typedef size_t nodeid; - typedef std::vector::iterator node_iterator; - typedef std::vector::const_iterator const_node_iterator; - typedef std::vector::reverse_iterator node_reverse_iterator; - typedef std::vector::const_reverse_iterator const_node_reverse_iterator; - - typedef graph_array _mytype; - - - // graph_array::arc class - class arc - { - public: - arc & mark() { m_Marker = true; return (* this); } - arc & unmark() { m_Marker = false; return (* this); } - bool marked() const { return m_Marker; } - - node_iterator initial() const { return m_Initial; } - node_iterator terminal() const { return m_Terminal; } - - arctype & operator * () { return m_Elem; } - arctype * operator -> () { return &m_Elem; } - const arctype & operator * () const { return m_Elem; } - const arctype * operator -> () const { return &m_Elem; } - - protected: - friend class graph_array; - - arc(const node_iterator & Initial, const node_iterator & Terminal) - : m_Initial(Initial), m_Terminal(Terminal), m_Marker(false) { } - - arc(const node_iterator & Initial, const node_iterator & Terminal, const arctype & Elem) - : m_Initial(Initial), m_Terminal(Terminal), m_Elem(Elem), m_Marker(false) { } - - node_iterator m_Initial; - node_iterator m_Terminal; - arctype m_Elem; - bool m_Marker; - }; - - - // New types - typedef std::list::iterator out_arc_iterator; - typedef std::list::const_iterator const_out_arc_iterator; - - - // graph_array::node class - class node - { - public: - node & mark() { m_Marker = true; return (* this); } - node & unmark() { m_Marker = false; return (* this); } - bool marked() const { return m_Marker; } - - bool out_empty() const { return m_OutArcs.empty(); } - size_t number_of_out_arcs() const { return m_OutArcs.size(); } - - out_arc_iterator out_begin() { return m_OutArcs.begin(); } - out_arc_iterator out_end() { return m_OutArcs.end(); } - const_out_arc_iterator out_begin() const { return m_OutArcs.begin(); } - const_out_arc_iterator out_end() const { return m_OutArcs.end(); } - - nodetype & operator * () { return m_Elem; } - nodetype * operator -> () { return &m_Elem; } - const nodetype & operator * () const { return m_Elem; } - const nodetype * operator -> () const { return &m_Elem; } - - nodetype & operator = (const nodetype & Elem) { return (m_Elem = Elem); } - - protected: - friend class graph_array; - friend class std::vector; - - node() : m_Marker(false) { } - - std::list m_OutArcs; - nodetype m_Elem; - bool m_Marker; - }; - - - // Construction/Destruction - graph_array(); - explicit graph_array(const size_t NbNodes); - - // Node related member functions - void clear(); - bool empty() const; - void setsize(const size_t NbNodes); - size_t size() const; - - node & operator [] (const nodeid & i); - const node & operator [] (const nodeid & i) const; - - node_iterator begin(); - node_iterator end(); - const_node_iterator begin() const; - const_node_iterator end() const; - - node_reverse_iterator rbegin(); - node_reverse_iterator rend(); - const_node_reverse_iterator rbegin() const; - const_node_reverse_iterator rend() const; - - // Arc related member functions - size_t number_of_arcs() const; - - void erase_arcs(); - void erase_arcs(const node_iterator & Initial); - out_arc_iterator erase_arc(const out_arc_iterator & Pos); - - out_arc_iterator insert_arc(const nodeid & Initial, const nodeid & Terminal); - out_arc_iterator insert_arc(const nodeid & Initial, const nodeid & Terminal, const arctype & Elem); - out_arc_iterator insert_arc(const node_iterator & Initial, const node_iterator & Terminal); - out_arc_iterator insert_arc(const node_iterator & Initial, const node_iterator & Terminal, const arctype & Elem); - - // Another interface for insert_arc - out_arc_iterator insert(const nodeid & Initial, const nodeid & Terminal) { return insert_arc(Initial, Terminal); } - out_arc_iterator insert(const nodeid & Initial, const nodeid & Terminal, const arctype & Elem) { return insert_arc(Initial, Terminal, Elem); } - out_arc_iterator insert(const node_iterator & Initial, const node_iterator & Terminal) { return insert_arc(Initial, Terminal); } - out_arc_iterator insert(const node_iterator & Initial, const node_iterator & Terminal, const arctype & Elem) { return insert_arc(Initial, Terminal, Elem); } - - // Optimized (overloaded) functions - void swap(_mytype & Right); - friend void swap(_mytype & Left, _mytype & Right) { Left.swap(Right); } - -protected: - size_t m_NbArcs; - std::vector m_Nodes; -}; - - - -// Additional "low level", graph related, functions -template -void unmark_nodes(graph_array & G); - -template -void unmark_arcs_from_node(graph_array::node & N); - -template -void unmark_arcs(graph_array & G); - - - - -////////////////////////////////////////////////////////////////////////// -// graph_array Inline functions -////////////////////////////////////////////////////////////////////////// - -template -inline graph_array::graph_array() : m_NbArcs(0) { } - - -template -inline graph_array::graph_array(const size_t NbNodes) : m_NbArcs(0), m_Nodes(NbNodes) { } - - -template -inline void graph_array::clear() { - m_NbArcs = 0; - m_Nodes.clear(); -} - - - -template -inline bool graph_array::empty() const { - return m_Nodes.empty(); -} - - -template -inline size_t graph_array::size() const { - return m_Nodes.size(); -} - - -template -inline void graph_array::setsize(const size_t NbNodes) { - clear(); - m_Nodes.resize(NbNodes); -} - - -template -inline graph_array::node & graph_array::operator [] (const nodeid & i) { - // Debug check - assert(i < size()); - - return m_Nodes[i]; -} - - -template -inline const graph_array::node & graph_array::operator [] (const nodeid & i) const { - // Debug check - assert(i < size()); - - return m_Nodes[i]; -} - - -template -inline graph_array::node_iterator graph_array::begin() { - return m_Nodes.begin(); -} - - -template -inline graph_array::node_iterator graph_array::end() { - return m_Nodes.end(); -} - - -template -inline graph_array::const_node_iterator graph_array::begin() const { - return m_Nodes.begin(); -} - - -template -inline graph_array::const_node_iterator graph_array::end() const { - return m_Nodes.end(); -} - - -template -inline graph_array::node_reverse_iterator graph_array::rbegin() { - return m_Nodes.rbegin(); -} - - -template -inline graph_array::node_reverse_iterator graph_array::rend() { - return m_Nodes.rend(); -} - - -template -inline graph_array::const_node_reverse_iterator graph_array::rbegin() const { - return m_Nodes.rbegin(); -} - - -template -inline graph_array::const_node_reverse_iterator graph_array::rend() const { - return m_Nodes.rend(); -} - - -template -inline size_t graph_array::number_of_arcs() const { - return m_NbArcs; -} - - -template -inline graph_array::out_arc_iterator graph_array::insert_arc(const nodeid & Initial, const nodeid & Terminal) { - return (insert(begin() + Initial, begin() + Terminal)); -} - - -template -inline graph_array::out_arc_iterator graph_array::insert_arc(const nodeid & Initial, const nodeid & Terminal, const arctype & Elem) { - return (insert(begin() + Initial, begin() + Terminal, Elem)); -} - - -template -inline graph_array::out_arc_iterator graph_array::insert_arc(const node_iterator & Initial, const node_iterator & Terminal) { - ++m_NbArcs; - Initial->m_OutArcs.push_back(arc(Initial, Terminal)); - return (--(Initial->m_OutArcs.end())); -} - - -template -inline graph_array::out_arc_iterator graph_array::insert_arc(const node_iterator & Initial, const node_iterator & Terminal, const arctype & Elem) { - ++m_NbArcs; - Initial->m_OutArcs.push_back(arc(Initial, Terminal, Elem)); - return (--(Initial->m_OutArcs.end())); -} - - -template -inline graph_array::out_arc_iterator graph_array::erase_arc(const out_arc_iterator & Pos) { - --m_NbArcs; - return (Pos->initial()->m_OutArcs.erase(Pos)); -} - - -template -inline void graph_array::erase_arcs(const node_iterator & Initial) { - m_NbArcs -= (Initial->m_OutArcs.size()); - Initial->m_OutArcs.clear(); -} - - -template -inline void graph_array::erase_arcs() { - m_NbArcs = 0; - for (nodeid i = 0; i < Size(); ++i) - m_Nodes[i].m_OutArcs.clear(); -} - - -template -inline void graph_array::swap(_mytype & Right) { - std::swap(m_NbArcs, Right.m_NbArcs); - std::swap(m_Nodes, Right.m_Nodes); -} - - - -////////////////////////////////////////////////////////////////////////// -// additional functions -////////////////////////////////////////////////////////////////////////// - -template -void unmark_nodes(graph_array & G) -{ - typedef graph_array::node_iterator node_it; - - for (node_it NodeIt = G.begin(); NodeIt != G.end(); ++NodeIt) - NodeIt->unmark(); -} - - -template -void unmark_arcs_from_node(graph_array::node & N) -{ - typedef graph_array::out_arc_iterator arc_it; - - for (arc_it ArcIt = N.out_begin(); ArcIt != N.out_end(); ++ArcIt) - ArcIt->unmark(); -} - - -template -void unmark_arcs(graph_array & G) -{ - typedef graph_array::node_iterator node_it; - - for (node_it NodeIt = G.begin(); NodeIt != G.end(); ++NodeIt) - unmark_arcs_from_node(* NodeIt); -} - - - - -}; // namespace common_structures diff --git a/apps/nexus/tristripper/heap_array.h b/apps/nexus/tristripper/heap_array.h deleted file mode 100644 index b164e91f..00000000 --- a/apps/nexus/tristripper/heap_array.h +++ /dev/null @@ -1,275 +0,0 @@ -// heap_array.h: interface for the heap_array class. -// -////////////////////////////////////////////////////////////////////// -// -// Copyright (C) 2002 Tanguy Fautré. -// -// This software is provided 'as-is', without any express or implied -// warranty. In no event will the authors be held liable for any damages -// arising from the use of this software. -// -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it -// freely, subject to the following restrictions: -// -// 1. The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. If you use this software -// in a product, an acknowledgment in the product documentation would be -// appreciated but is not required. -// 2. Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// 3. This notice may not be removed or altered from any source distribution. -// -// Tanguy Fautré -// softdev@pandora.be -// -////////////////////////////////////////////////////////////////////// -// -// Semi-dynamic indexed heap -// ************************* -// -// Current version: 1.00 BETA 1 (24/10/2002) -// -// Comment: heap_array acts like a normal heap, you can push elements -// and then get the greatest one. -// However you cannot push any more element once an element -// has been removed (pop, erase, etc...). -// Elements can be modified after they've been pushed into -// the heap via their indice. -// -// History: - -// -////////////////////////////////////////////////////////////////////// - -#pragma once - - - -// namespace common_structures -namespace common_structures { - - - - -template > -class heap_array -{ -public: - - struct heap_is_locked { }; - - - // heap_array main interface. Pre = PreCondition, Post = PostCondition - - heap_array() : m_Locked(false) { } // Post: ((size() == 0) && ! locked()) - - void clear(); // Post: ((size() == 0) && ! locked()) - - void reserve(size_t Size); - size_t size() const; - - bool empty() const; - bool locked() const; - bool removed(size_t i) const; // Pre: (valid(i)) - bool valid(size_t i) const; - - const T & top() const; // Pre: (! empty()) - const T & peek(size_t i) const; // Pre: (valid(i) && ! removed(i)) - const T & operator [] (size_t i) const; // Pre: (valid(i) && ! removed(i)) - - size_t push(const T & Elem); // Pre: (! locked()) else throw (heap_is_locked) - - void pop(); // Pre: (! empty()) Post: (locked()) - void erase(size_t i); // Pre: (valid(i) && ! removed(i)) Post: (locked()) - void update(size_t i, const T & Elem); // Pre: (valid(i) && ! removed(i)) Post: (locked()) - -protected: - - struct linker { - linker(const T & Elem, size_t i) : m_Elem(Elem), m_Indice(i) { } - - T m_Elem; - size_t m_Indice; - }; - - typedef std::vector linked_heap; - typedef std::vector finder; - - void Adjust(size_t i); - void Swap(size_t a, size_t b); - bool Less(const linker & a, const linker & b) const; - - linked_heap m_Heap; - finder m_Finder; - CmpT m_Compare; - bool m_Locked; -}; - - - - -////////////////////////////////////////////////////////////////////////// -// heap_indexed Inline functions -////////////////////////////////////////////////////////////////////////// - -template -inline void heap_array::clear() { - m_Heap.clear(); - m_Finder.clear(); - m_Locked = false; -} - - -template -inline bool heap_array::empty() const { - return m_Heap.empty(); -} - - -template -inline bool heap_array::locked() const { - return m_Locked; -} - - -template -inline void heap_array::reserve(size_t Size) { - m_Heap.reserve(Size); - m_Finder.reserve(Size); -} - - -template -inline size_t heap_array::size() const { - return m_Heap.size(); -} - - -template -inline const T & heap_array::top() const { - // Debug check to ensure heap is not empty - assert(! empty()); - - return m_Heap.front().m_Elem; -} - - -template -inline const T & heap_array::peek(size_t i) const { - // Debug check to ensure element is still present - assert(! removed(i)); - - return (m_Heap[m_Finder[i]].m_Elem); -} - - -template -inline const T & heap_array::operator [] (size_t i) const { - return peek(i); -} - - -template -inline void heap_array::pop() { - m_Locked = true; - - // Debug check to ensure heap is not empty - assert(! empty()); - - Swap(0, size() - 1); - m_Heap.pop_back(); - Adjust(0); -} - - -template -inline size_t heap_array::push(const T & Elem) { - if (m_Locked) - throw heap_is_locked(); - - size_t Id = size(); - m_Finder.push_back(Id); - m_Heap.push_back(linker(Elem, Id)); - Adjust(Id); - - return Id; -} - - -template -inline void heap_array::erase(size_t i) { - m_Locked = true; - - // Debug check to ensure element is still present - assert(! removed(i)); - - size_t j = m_Finder[i]; - Swap(j, size() - 1); - m_Heap.pop_back(); - Adjust(j); -} - - -template -inline bool heap_array::removed(size_t i) const { - return (m_Finder[i] >= m_Heap.size()); -} - - -template -inline bool heap_array::valid(size_t i) const { - return (i < m_Finder.size()); -} - - -template -inline void heap_array::update(size_t i, const T & Elem) { - // Debug check to ensure element is still present - assert(! removed(i)); - - size_t j = m_Finder[i]; - m_Heap[j].m_Elem = Elem; - Adjust(j); -} - - -template -inline void heap_array::Adjust(size_t i) { - size_t j; - - // Check the upper part of the heap - for (j = i; (j > 0) && (Less(m_Heap[(j - 1) / 2], m_Heap[j])); j = ((j - 1) / 2)) - Swap(j, (j - 1) / 2); - - // Check the lower part of the heap - for (i = j; (j = 2 * i + 1) < size(); i = j) { - if ((j + 1 < size()) && (Less(m_Heap[j], m_Heap[j + 1]))) - ++j; - - if (Less(m_Heap[j], m_Heap[i])) - return; - - Swap(i, j); - } -} - - -template -inline void heap_array::Swap(size_t a, size_t b) { - std::swap(m_Heap[a], m_Heap[b]); - - // use (size_t &) to get rid of a bogus compile warning - (size_t &) (m_Finder[(m_Heap[a].m_Indice)]) = a; - (size_t &) (m_Finder[(m_Heap[b].m_Indice)]) = b; -} - - -template -inline bool heap_array::Less(const linker & a, const linker & b) const { - return m_Compare(a.m_Elem, b.m_Elem); -} - - - - -}; // namespace common_structures \ No newline at end of file diff --git a/apps/nexus/tristripper/tri_stripper.cpp b/apps/nexus/tristripper/tri_stripper.cpp deleted file mode 100644 index 6e7dbb14..00000000 --- a/apps/nexus/tristripper/tri_stripper.cpp +++ /dev/null @@ -1,586 +0,0 @@ -// tri_stripper.cpp: implementation of the Tri Stripper class. -// -// Copyright (C) 2002 Tanguy Fautré. -// For conditions of distribution and use, -// see copyright notice in tri_stripper.h -// -////////////////////////////////////////////////////////////////////// - -#include -#include - -#include -#include -//#include -//#include -#include -#include -#include -#include -using namespace std; -#include "tri_stripper.h" - - - -// namespace triangle_stripper -namespace triangle_stripper { - - - - -////////////////////////////////////////////////////////////////////// -// Construction/Destruction -////////////////////////////////////////////////////////////////////// - - - -////////////////////////////////////////////////////////////////////// -// Members Functions -////////////////////////////////////////////////////////////////////// - -void tri_stripper::Strip(primitives_vector * out_pPrimitivesVector) -{ - // verify that the number of indices is correct - if (m_TriIndices.size() % 3 != 0) - throw triangles_indices_error(); - - // clear possible garbage - m_PrimitivesVector.clear(); - out_pPrimitivesVector->clear(); - - // Initialize the triangle graph - InitTriGraph(); - - // Initialize the triangle priority queue - InitTriHeap(); - - // Initialize the cache simulator - InitCache(); - - // Launch the triangle strip generator - Stripify(); - - // Add the triangles that couldn't be stripped - AddLeftTriangles(); - - // Free ressources - m_Triangles.clear(); - - // Put the results into the user's vector - std::swap(m_PrimitivesVector, (* out_pPrimitivesVector)); -} - - - -void tri_stripper::InitTriGraph() -{ - // Set up the graph size and complete the triangles data - // note: setsize() completely resets the graph as well as the node markers - m_Triangles.setsize(m_TriIndices.size() / 3); - size_t i; - for (i = 0; i < m_Triangles.size(); ++i) - m_Triangles[i] = triangle(m_TriIndices[i * 3 + 0], m_TriIndices[i * 3 + 1], m_TriIndices[i * 3 + 2]); - - // Build the edges lookup table - triangle_edges TriInterface; - TriInterface.reserve(m_Triangles.size() * 3); - - for (i = 0; i < m_Triangles.size(); ++i) { - TriInterface.push_back(triangle_edge(m_Triangles[i]->A(), m_Triangles[i]->B(), i)); - TriInterface.push_back(triangle_edge(m_Triangles[i]->B(), m_Triangles[i]->C(), i)); - TriInterface.push_back(triangle_edge(m_Triangles[i]->C(), m_Triangles[i]->A(), i)); - } - - // Sort the lookup table for faster searches - std::sort(TriInterface.begin(), TriInterface.end(), _cmp_tri_interface_lt()); - - // Link neighbour triangles together using the edges lookup table - for (i = 0; i < m_Triangles.size(); ++i) { - - const triangle_edge EdgeBA(m_Triangles[i]->B(), m_Triangles[i]->A(), i); - const triangle_edge EdgeCB(m_Triangles[i]->C(), m_Triangles[i]->B(), i); - const triangle_edge EdgeAC(m_Triangles[i]->A(), m_Triangles[i]->C(), i); - - LinkNeighboursTri(TriInterface, EdgeBA); - LinkNeighboursTri(TriInterface, EdgeCB); - LinkNeighboursTri(TriInterface, EdgeAC); - } -} - - - -void tri_stripper::LinkNeighboursTri(const triangle_edges & TriInterface, const triangle_edge Edge) -{ - typedef triangle_edges::const_iterator edge_const_it; - - // Find the first edge equal to Edge - edge_const_it It = std::lower_bound(TriInterface.begin(), TriInterface.end(), Edge, _cmp_tri_interface_lt()); - - // See if there are any other edges that are equal - // (if so, it means that more than 2 triangles are sharing the same edge, - // which is unlikely but not impossible) - for (; (It != TriInterface.end()) && ((It->A() == Edge.A()) && (It->B() == Edge.B())); ++It) - m_Triangles.insert(Edge.TriPos(), It->TriPos()); - - // Note: degenerated triangles will also point themselves as neighbour triangles -} - - - -void tri_stripper::InitTriHeap() -{ - m_TriHeap.clear(); - m_TriHeap.reserve(m_Triangles.size()); - - // Set up the triangles priority queue - // The lower the number of available neighbour triangles, the higher the priority. - for (size_t i = 0; i < m_Triangles.size(); ++i) - m_TriHeap.push(triangle_degree(i, m_Triangles[i].number_of_out_arcs())); - - // Remove useless triangles - // (Note: we had to put all of them into the heap before to ensure coherency of the heap_array object) - while ((! m_TriHeap.empty()) && (m_TriHeap.top().Degree() == 0)) - m_TriHeap.pop(); -} - - - -void tri_stripper::InitCache() -{ - m_IndicesCache.clear(); - - if (m_CacheSize > 0) - m_IndicesCache.resize(m_CacheSize, static_cast(-1)); -} - - - -void tri_stripper::Stripify() -{ - // Reset the triangle strip id selector - m_StripID = 0; - - // Reset the candidate list - m_NextCandidates.clear(); - - // Loop untill there is no available candidate triangle left - while (! m_TriHeap.empty()) { - - // There is no triangle in the candidates list, refill it with the loneliest triangle - const size_t HeapTop = m_TriHeap.top().TriPos(); - m_NextCandidates.push_back(HeapTop); - - // Loop while BuildStrip can find good candidates for us - while (! m_NextCandidates.empty()) { - - // Choose the best strip containing that triangle - // Note: FindBestStrip empties m_NextCandidates - const triangle_strip TriStrip = FindBestStrip(); - - // Build it if it's long enough, otherwise discard it - // Note: BuildStrip refills m_NextCandidates - if (TriStrip.Size() >= m_MinStripSize) - BuildStrip(TriStrip); - } - - // We must discard the triangle we inserted in the candidate list from the heap - // if it led to nothing. (We simply removed it if it hasn't been removed by BuildStrip() yet) - if (! m_TriHeap.removed(HeapTop)) - m_TriHeap.erase(HeapTop); - - - // Eliminate all the triangles that have now become useless - while ((! m_TriHeap.empty()) && (m_TriHeap.top().Degree() == 0)) - m_TriHeap.pop(); - } -} - - - -inline tri_stripper::triangle_strip tri_stripper::FindBestStrip() -{ - triangle_strip BestStrip; - size_t BestStripDegree = 0; - size_t BestStripCacheHits = 0; - - // Backup the cache, because it'll be erased during the simulations - indices_cache CacheBackup = m_IndicesCache; - - while (! m_NextCandidates.empty()) { - - // Discard useless triangles from the candidates list - if ((m_Triangles[m_NextCandidates.back()].marked()) || (m_TriHeap[m_NextCandidates.back()].Degree() == 0)) { - m_NextCandidates.pop_back(); - - // "continue" is evil! But it really makes things easier here. - // The useless triangle is discarded, and the "while" just rebegins again - continue; - } - - // Invariant: (CandidateTri's Degree() >= 1) && (CandidateTri is not marked). - // So it can directly be used. - const size_t CandidateTri = m_NextCandidates.back(); - m_NextCandidates.pop_back(); - - // Try to extend the triangle in the 3 possible directions - for (size_t i = 0; i < 3; ++i) { - - // Reset the cache hit count - m_CacheHits = 0; - - // Try a new strip with that triangle in a particular direction - const triangle_strip TempStrip = ExtendTriToStrip(CandidateTri, triangle_strip::start_order(i)); - - // Restore the cache (modified by ExtendTriToStrip) - m_IndicesCache = CacheBackup; - - // We want to keep the best strip - // Discard strips that don't match the minimum required size - if (TempStrip.Size() >= m_MinStripSize) { - - // Cache simulator disabled? - if (m_CacheSize == 0) { - - // Cache is disabled, take the longest strip - if (TempStrip.Size() > BestStrip.Size()) - BestStrip = TempStrip; - - // Cache simulator enabled - // Use other criteria to find the "best" strip - } else { - - // Priority 1: Keep the strip with the best cache hit count - if (m_CacheHits > BestStripCacheHits) { - BestStrip = TempStrip; - BestStripDegree = m_TriHeap[TempStrip.StartTriPos()].Degree(); - BestStripCacheHits = m_CacheHits; - - } else if (m_CacheHits == BestStripCacheHits) { - - // Priority 2: Keep the strip with the loneliest start triangle - if ((BestStrip.Size() != 0) && (m_TriHeap[TempStrip.StartTriPos()].Degree() < BestStripDegree)) { - BestStrip = TempStrip; - BestStripDegree = m_TriHeap[TempStrip.StartTriPos()].Degree(); - - // Priority 3: Keep the longest strip - } else if (TempStrip.Size() > BestStrip.Size()) { - BestStrip = TempStrip; - BestStripDegree = m_TriHeap[TempStrip.StartTriPos()].Degree(); - } - } - } - } - - } - - } - - return BestStrip; -} - - - -tri_stripper::triangle_strip tri_stripper::ExtendTriToStrip(const size_t StartTriPos, const triangle_strip::start_order StartOrder) -{ - typedef triangles_graph::const_out_arc_iterator const_tri_link_iter; - typedef triangles_graph::node_iterator tri_node_iter; - - size_t Size = 1; - bool ClockWise = false; - triangle_strip::start_order Order = StartOrder; - - // Begin a new strip - ++m_StripID; - - // Mark the first triangle as used for this strip - m_Triangles[StartTriPos]->SetStripID(m_StripID); - - // Update the indice cache - AddTriToCache((* m_Triangles[StartTriPos]), Order); - - - // Loop while we can further extend the strip - for (tri_node_iter TriNodeIt = (m_Triangles.begin() + StartTriPos); - (TriNodeIt != m_Triangles.end()) && ((m_CacheSize <= 0) || ((Size + 2) < m_CacheSize)); - ++Size) { - - // Get the triangle edge that would lead to the next triangle - const triangle_edge Edge = GetLatestEdge(** TriNodeIt, Order); - - // Link to a neighbour triangle - const_tri_link_iter LinkIt; - for (LinkIt = TriNodeIt->out_begin(); LinkIt != TriNodeIt->out_end(); ++LinkIt) { - - // Get the reference to the possible next triangle - const triangle & Tri = (** ((*LinkIt).terminal())); - - // Check whether it's already been used - if ((Tri.StripID() != m_StripID) && (! ((*LinkIt).terminal()->marked()))) { - - // Does the current candidate triangle match the required for the strip? - - if ((Edge.B() == Tri.A()) && (Edge.A() == Tri.B())) { - Order = (ClockWise) ? triangle_strip::ABC : triangle_strip::BCA; - AddIndiceToCache(Tri.C(), true); - break; - } - - else if ((Edge.B() == Tri.B()) && (Edge.A() == Tri.C())) { - Order = (ClockWise) ? triangle_strip::BCA : triangle_strip::CAB; - AddIndiceToCache(Tri.A(), true); - break; - } - - else if ((Edge.B() == Tri.C()) && (Edge.A() == Tri.A())) { - Order = (ClockWise) ? triangle_strip::CAB : triangle_strip::ABC; - AddIndiceToCache(Tri.B(), true); - break; - } - } - } - - // Is it the end of the strip? - if (LinkIt == TriNodeIt->out_end()) { - TriNodeIt = m_Triangles.end(); - --Size; - } else { - TriNodeIt = (*LinkIt).terminal(); - - // Setup for the next triangle - (* TriNodeIt)->SetStripID(m_StripID); - ClockWise = ! ClockWise; - } - } - - - return triangle_strip(StartTriPos, StartOrder, Size); -} - - - -inline tri_stripper::triangle_edge tri_stripper::GetLatestEdge(const triangle & Triangle, const triangle_strip::start_order Order) const -{ - switch (Order) { - case triangle_strip::ABC: - return triangle_edge(Triangle.B(), Triangle.C(), 0); - case triangle_strip::BCA: - return triangle_edge(Triangle.C(), Triangle.A(), 0); - case triangle_strip::CAB: - return triangle_edge(Triangle.A(), Triangle.B(), 0); - default: - return triangle_edge(0, 0, 0); - } -} - - - -void tri_stripper::BuildStrip(const triangle_strip TriStrip) -{ - typedef triangles_graph::const_out_arc_iterator const_tri_link_iter; - typedef triangles_graph::node_iterator tri_node_iter; - - const size_t StartTriPos = TriStrip.StartTriPos(); - - bool ClockWise = false; - triangle_strip::start_order Order = TriStrip.StartOrder(); - - // Create a new strip - m_PrimitivesVector.push_back(primitives()); - m_PrimitivesVector.back().m_Type = PT_Triangle_Strip; - - // Put the first triangle into the strip - AddTriToIndices((* m_Triangles[StartTriPos]), Order); - - // Mark the first triangle as used - MarkTriAsTaken(StartTriPos); - - - // Loop while we can further extend the strip - tri_node_iter TriNodeIt = (m_Triangles.begin() + StartTriPos); - - for (size_t Size = 1; Size < TriStrip.Size(); ++Size) { - - // Get the triangle edge that would lead to the next triangle - const triangle_edge Edge = GetLatestEdge(** TriNodeIt, Order); - - // Link to a neighbour triangle - const_tri_link_iter LinkIt; - for (LinkIt = TriNodeIt->out_begin(); LinkIt != TriNodeIt->out_end(); ++LinkIt) { - - // Get the reference to the possible next triangle - const triangle & Tri = (** ((*LinkIt).terminal())); - - // Check whether it's already been used - if (! ((*LinkIt).terminal()->marked())) { - - // Does the current candidate triangle match the required for the strip? - // If it does, then add it to the Indices - if ((Edge.B() == Tri.A()) && (Edge.A() == Tri.B())) { - Order = (ClockWise) ? triangle_strip::ABC : triangle_strip::BCA; - AddIndice(Tri.C()); - break; - } - - else if ((Edge.B() == Tri.B()) && (Edge.A() == Tri.C())) { - Order = (ClockWise) ? triangle_strip::BCA : triangle_strip::CAB; - AddIndice(Tri.A()); - break; - } - - else if ((Edge.B() == Tri.C()) && (Edge.A() == Tri.A())) { - Order = (ClockWise) ? triangle_strip::CAB : triangle_strip::ABC; - AddIndice(Tri.B()); - break; - } - } - } - - // Debug check: we must have found the next triangle - assert(LinkIt != TriNodeIt->out_end()); - - // Go to the next triangle - TriNodeIt = (*LinkIt).terminal(); - MarkTriAsTaken(TriNodeIt - m_Triangles.begin()); - - // Setup for the next triangle - ClockWise = ! ClockWise; - } -} - - - -void tri_stripper::MarkTriAsTaken(const size_t i) -{ - typedef triangles_graph::node_iterator tri_node_iter; - typedef triangles_graph::out_arc_iterator tri_link_iter; - - // Mark the triangle node - m_Triangles[i].mark(); - - // Remove triangle from priority queue if it isn't yet - if (! m_TriHeap.removed(i)) - m_TriHeap.erase(i); - - // Adjust the degree of available neighbour triangles - for (tri_link_iter LinkIt = m_Triangles[i].out_begin(); LinkIt != m_Triangles[i].out_end(); ++LinkIt) { - - const size_t j = (*LinkIt).terminal() - m_Triangles.begin(); - - if ((! m_Triangles[j].marked()) && (! m_TriHeap.removed(j))) { - triangle_degree NewDegree = m_TriHeap.peek(j); - NewDegree.SetDegree(NewDegree.Degree() - 1); - m_TriHeap.update(j, NewDegree); - - // Update the candidate list if cache is enabled - if ((m_CacheSize > 0) && (NewDegree.Degree() > 0)) - m_NextCandidates.push_back(j); - } - } -} - - - -inline void tri_stripper::AddIndiceToCache(const indice i, bool CacheHitCount) -{ - // Cache simulator enabled? - if (m_CacheSize > 0) { - - // Should we simulate the cache hits and count them? - if (CacheHitCount) { - if (std::find(m_IndicesCache.begin(), m_IndicesCache.end(), i) != m_IndicesCache.end()) - ++m_CacheHits; - } - - // Manage the indices cache as a FIFO structure - m_IndicesCache.pop_back(); - m_IndicesCache.push_front(i); - } -} - - - -inline void tri_stripper::AddIndice(const indice i) -{ - // Add the indice to the current indices array - m_PrimitivesVector.back().m_Indices.push_back(i); - - // Run cache simulator - AddIndiceToCache(i); -} - - - -inline void tri_stripper::AddTriToCache(const triangle & Tri, const triangle_strip::start_order Order) -{ - // Add Tri indices in the right order into the indices cache simulator. - // And enable the cache hit count - switch (Order) { - case triangle_strip::ABC: - AddIndiceToCache(Tri.A(), true); - AddIndiceToCache(Tri.B(), true); - AddIndiceToCache(Tri.C(), true); - return; - case triangle_strip::BCA: - AddIndiceToCache(Tri.B(), true); - AddIndiceToCache(Tri.C(), true); - AddIndiceToCache(Tri.A(), true); - return; - case triangle_strip::CAB: - AddIndiceToCache(Tri.C(), true); - AddIndiceToCache(Tri.A(), true); - AddIndiceToCache(Tri.B(), true); - return; - } -} - - - -inline void tri_stripper::AddTriToIndices(const triangle & Tri, const triangle_strip::start_order Order) -{ - // Add Tri indices in the right order into the latest Indices vector. - switch (Order) { - case triangle_strip::ABC: - AddIndice(Tri.A()); - AddIndice(Tri.B()); - AddIndice(Tri.C()); - return; - case triangle_strip::BCA: - AddIndice(Tri.B()); - AddIndice(Tri.C()); - AddIndice(Tri.A()); - return; - case triangle_strip::CAB: - AddIndice(Tri.C()); - AddIndice(Tri.A()); - AddIndice(Tri.B()); - return; - } -} - - - -void tri_stripper::AddLeftTriangles() -{ - // Create the latest indices array - // and fill it with all the triangles that couldn't be stripped - primitives Primitives; - Primitives.m_Type = PT_Triangles; - m_PrimitivesVector.push_back(Primitives); - indices & Indices = m_PrimitivesVector.back().m_Indices; - - for (size_t i = 0; i < m_Triangles.size(); ++i) - if (! m_Triangles[i].marked()) { - Indices.push_back(m_Triangles[i]->A()); - Indices.push_back(m_Triangles[i]->B()); - Indices.push_back(m_Triangles[i]->C()); - } - - // Undo if useless - if (Indices.size() == 0) - m_PrimitivesVector.pop_back(); -} - - - - -}; // namespace triangle_stripper diff --git a/apps/nexus/tristripper/tri_stripper.h b/apps/nexus/tristripper/tri_stripper.h deleted file mode 100644 index 80c1701b..00000000 --- a/apps/nexus/tristripper/tri_stripper.h +++ /dev/null @@ -1,372 +0,0 @@ -// tri_stripper.h: interface for the tri_stripper class. -// -////////////////////////////////////////////////////////////////////// -// -// Copyright (C) 2002 Tanguy Fautré. -// -// This software is provided 'as-is', without any express or implied -// warranty. In no event will the authors be held liable for any damages -// arising from the use of this software. -// -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it -// freely, subject to the following restrictions: -// -// 1. The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. If you use this software -// in a product, an acknowledgment in the product documentation would be -// appreciated but is not required. -// 2. Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// 3. This notice may not be removed or altered from any source distribution. -// -// Tanguy Fautré -// softdev@pandora.be -// -////////////////////////////////////////////////////////////////////// -// -// Tri Stripper -// ************ -// -// Current version: 1.00 BETA 5 (10/12/2002) -// -// Comment: Triangle stripper in O(n.log(n)). -// -// Currently there are no protection against crazy values -// given via SetMinStripSize() and SetCacheSize(). -// So be careful. (Min. strip size should be equal or greater -// than 2, cache size should be about 10 for GeForce 256/2 -// and about 16-18 for GeForce 3/4.) -// -// History: - 1.00 BETA 5 (10/12/2002) - Fixed a bug in Stripify() that could sometimes -// cause it to go into an infinite loop. -// (thanks to Remy for the bug report) -// - 1.00 BETA 4 (18/11/2002) - Removed the dependency on OpenGL: -// modified gl_primitives to primitives, -// and gl_primitives_vector to primitives_vector; -// and added primitive_type. -// (thanks to Patrik for noticing this useless dependency) -// - 1.00 BETA 3 (18/11/2002) - Fixed a bug in LinkNeightboursTri() that could cause a crash -// (thanks to Nicolas for finding it) -// - 1.00 BETA 2 (16/11/2002) - Improved portability -// - 1.00 BETA 1 (27/10/2002) - First public release -// -////////////////////////////////////////////////////////////////////// - -#pragma once - -#include - -// namespace triangle_stripper -namespace triangle_stripper { - - - -//#include "../Common Structures/graph_array.h" -//#include "../Common Structures/heap_array.h" -#include "graph_array.h" -#include "heap_array.h" - - - -class tri_stripper -{ -public: - - // New Public types - typedef unsigned int indice; - typedef std::vector indices; - - enum primitive_type { - PT_Triangles = 0x0004, // = GL_TRIANGLES - PT_Triangle_Strip = 0x0005 // = GL_TRIANGLE_STRIP - }; - - struct primitives - { - indices m_Indices; - primitive_type m_Type; - }; - - typedef std::vector primitives_vector; - - struct triangles_indices_error { }; - - - // constructor/initializer - tri_stripper(const indices & TriIndices); - - // Settings functions - void SetCacheSize(const size_t CacheSize = 16); // = 0 will disable the cache optimizer - void SetMinStripSize(const size_t MinStripSize = 2); - - // Stripper - void Strip(primitives_vector * out_pPrimitivesVector); // throw triangles_indices_error(); - -private: - - friend struct _cmp_tri_interface_lt; - - - class triangle - { - public: - triangle(); - triangle(const indice A, const indice B, const indice C); - - void SetStripID(const size_t StripID); - - indice A() const; - indice B() const; - indice C() const; - size_t StripID() const; - - private: - indice m_A; - indice m_B; - indice m_C; - size_t m_StripID; - }; - - - class triangle_edge - { - public: - triangle_edge(const indice A, const indice B, const size_t TriPos); - - indice A() const; - indice B() const; - size_t TriPos() const; - - private: - indice m_A; - indice m_B; - size_t m_TriPos; - }; - - - class triangle_degree - { - public: - triangle_degree(); - triangle_degree(const size_t TriPos, const size_t Degree); - - size_t Degree() const; - size_t TriPos() const; - - void SetDegree(const size_t Degree); - - private: - size_t m_TriPos; - size_t m_Degree; - }; - - - class triangle_strip - { - public: - enum start_order { ABC = 0, BCA = 1, CAB = 2 }; - - triangle_strip(); - triangle_strip(size_t StartTriPos, start_order StartOrder, size_t Size); - - size_t StartTriPos() const; - start_order StartOrder() const; - size_t Size() const; - - private: - size_t m_StartTriPos; - start_order m_StartOrder; - size_t m_Size; - }; - - - struct _cmp_tri_interface_lt - { - bool operator() (const triangle_edge & a, const triangle_edge & b) const; - }; - - - struct _cmp_tri_degree_gt - { - bool operator () (const triangle_degree & a, const triangle_degree & b) const; - }; - - - typedef common_structures::graph_array triangles_graph; - typedef common_structures::heap_array triangles_heap; - typedef std::vector triangle_edges; - typedef std::vector triangle_indices; - typedef std::deque indices_cache; - - - void InitCache(); - void InitTriGraph(); - void InitTriHeap(); - void Stripify(); - void AddLeftTriangles(); - - void LinkNeighboursTri(const triangle_edges & TriInterface, const triangle_edge Edge); - void MarkTriAsTaken(const size_t i); - - triangle_edge GetLatestEdge(const triangle & Triangle, const triangle_strip::start_order Order) const; - - triangle_strip FindBestStrip(); - triangle_strip ExtendTriToStrip(const size_t StartTriPos, const triangle_strip::start_order StartOrder); - void BuildStrip(const triangle_strip TriStrip); - void AddIndice(const indice i); - void AddIndiceToCache(const indice i, bool CacheHitCount = false); - void AddTriToCache(const triangle & Tri, const triangle_strip::start_order Order); - void AddTriToIndices(const triangle & Tri, const triangle_strip::start_order Order); - - const indices & m_TriIndices; - - size_t m_MinStripSize; - size_t m_CacheSize; - - primitives_vector m_PrimitivesVector; - triangles_graph m_Triangles; - triangles_heap m_TriHeap; - triangle_indices m_NextCandidates; - indices_cache m_IndicesCache; - size_t m_StripID; - size_t m_CacheHits; -}; - - - - -////////////////////////////////////////////////////////////////////////// -// tri_stripper Inline functions -////////////////////////////////////////////////////////////////////////// - -inline tri_stripper::tri_stripper(const indices & TriIndices) : m_TriIndices(TriIndices) { - SetCacheSize(); - SetMinStripSize(); -} - - -inline void tri_stripper::SetCacheSize(const size_t CacheSize) { - m_CacheSize = CacheSize; -} - - -inline void tri_stripper::SetMinStripSize(const size_t MinStripSize) { - m_MinStripSize = MinStripSize; -} - - -inline tri_stripper::triangle::triangle() { } - - -inline tri_stripper::triangle::triangle(const indice A, const indice B, const indice C) : m_A(A), m_B(B), m_C(C), m_StripID(0) { } - - -inline void tri_stripper::triangle::SetStripID(const size_t StripID) { - m_StripID = StripID; -} - - -inline tri_stripper::indice tri_stripper::triangle::A() const { - return m_A; -} - - -inline tri_stripper::indice tri_stripper::triangle::B() const { - return m_B; -} - - -inline tri_stripper::indice tri_stripper::triangle::C() const { - return m_C; -} - - -inline size_t tri_stripper::triangle::StripID() const { - return m_StripID; -} - - -inline tri_stripper::triangle_edge::triangle_edge(const indice A, const indice B, const size_t TriPos) : m_A(A), m_B(B), m_TriPos(TriPos) { } - - -inline tri_stripper::indice tri_stripper::triangle_edge::A() const { - return m_A; -} - - -inline tri_stripper::indice tri_stripper::triangle_edge::B() const { - return m_B; -} - - -inline size_t tri_stripper::triangle_edge::TriPos() const { - return m_TriPos; -} - - -inline tri_stripper::triangle_degree::triangle_degree() { } - - -inline tri_stripper::triangle_degree::triangle_degree(const size_t TriPos, const size_t Degree) : m_TriPos(TriPos), m_Degree(Degree) { } - - -inline size_t tri_stripper::triangle_degree::Degree() const { - return m_Degree; -} - - -inline size_t tri_stripper::triangle_degree::TriPos() const { - return m_TriPos; -} - - -inline void tri_stripper::triangle_degree::SetDegree(const size_t Degree) { - m_Degree = Degree; -} - - -inline tri_stripper::triangle_strip::triangle_strip() : m_StartTriPos(0), m_StartOrder(ABC), m_Size(0) { } - - -inline tri_stripper::triangle_strip::triangle_strip(const size_t StartTriPos, const start_order StartOrder, const size_t Size) - : m_StartTriPos(StartTriPos), m_StartOrder(StartOrder), m_Size(Size) { } - - -inline size_t tri_stripper::triangle_strip::StartTriPos() const { - return m_StartTriPos; -} - - -inline tri_stripper::triangle_strip::start_order tri_stripper::triangle_strip::StartOrder() const { - return m_StartOrder; -} - - -inline size_t tri_stripper::triangle_strip::Size() const { - return m_Size; -} - - -inline bool tri_stripper::_cmp_tri_interface_lt::operator() (const triangle_edge & a, const triangle_edge & b) const { - const tri_stripper::indice A1 = a.A(); - const tri_stripper::indice B1 = a.B(); - const tri_stripper::indice A2 = b.A(); - const tri_stripper::indice B2 = b.B(); - - if ((A1 < A2) || ((A1 == A2) && (B1 < B2))) - return true; - else - return false; -} - - -inline bool tri_stripper::_cmp_tri_degree_gt::operator () (const triangle_degree & a, const triangle_degree & b) const { - // the triangle with a smaller degree has more priority - return a.Degree() > b.Degree(); -} - - - - -}; // namespace triangle_stripper diff --git a/apps/nexus/vchain.cpp b/apps/nexus/vchain.cpp deleted file mode 100644 index faf9dadc..00000000 --- a/apps/nexus/vchain.cpp +++ /dev/null @@ -1,134 +0,0 @@ -/**************************************************************************** -* 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 "vchain.h" -#include -#include - -using namespace std; -using namespace vcg; -using namespace nxs; - -VChain::~VChain() { - for(iterator i = begin(); i != end(); i++) - delete *i; -} - -bool VChain::Save(const string &file) { - FILE *fp = fopen(file.c_str(), "wb+"); - if(!fp) { - cerr << "Could not save vchain data.\n"; - return false; - } - - unsigned int nlevels = size(); - fwrite(&nlevels, sizeof(unsigned int), 1, fp); - for(unsigned int i = 0; i < nlevels; i++) { - VPartition &level = *operator[](i); - unsigned int npoints = level.size(); - fwrite(&npoints, sizeof(unsigned int), 1, fp); - fwrite(&(level[0]), sizeof(Point3f), npoints, fp); - } - //writing fragments - - unsigned int nfrag = newfragments.size(); - fwrite(&nfrag, sizeof(unsigned int), 1, fp); - - std::map >::iterator j; - for(j = newfragments.begin(); j != newfragments.end(); j++) { - unsigned int n = (*j).second.size(); - fwrite(&((*j).first), sizeof(unsigned int), 1, fp); - fwrite(&n, sizeof(unsigned int), 1, fp); - set::iterator k; - for(k = (*j).second.begin(); k != (*j).second.end(); k++) - fwrite(&*k, sizeof(unsigned int), 1, fp); - } - nfrag = oldfragments.size(); - fwrite(&nfrag, sizeof(unsigned int), 1, fp); - - for(j = oldfragments.begin(); j != oldfragments.end(); j++) { - unsigned int n = (*j).second.size(); - fwrite(&((*j).first), sizeof(unsigned int), 1, fp); - fwrite(&n, sizeof(unsigned int), 1, fp); - set::iterator k; - for(k = (*j).second.begin(); k != (*j).second.end(); k++) - fwrite(&*k, sizeof(unsigned int), 1, fp); - } - fclose(fp); - return true; -} - -bool VChain::Load(const string &file) { - FILE *fp = fopen(file.c_str(), "rb"); - if(!fp) { - cerr << "Could not load vchain data\n"; - return false; - } - unsigned int nlevels; - fread(&nlevels, sizeof(unsigned int), 1, fp); - for(unsigned int i = 0; i < nlevels; i++) { - push_back(new VPartition()); - VPartition &level = *back(); - - unsigned int npoints; - fread(&npoints, sizeof(unsigned int), 1, fp); - level.resize(npoints); - fread(&(level[0]), sizeof(Point3f), npoints, fp); - level.Init(); - } - //reading fragments - unsigned int nfrag; - fread(&nfrag, sizeof(unsigned int), 1, fp); - for(unsigned int i = 0; i < nfrag; i++) { - unsigned int p, n; - fread(&p, sizeof(unsigned int), 1, fp); - set &s = newfragments[p]; - fread(&n, sizeof(unsigned int), 1, fp); - for(unsigned int k = 0; k < n; k++) { - unsigned int j; - fread(&j, sizeof(unsigned int), 1, fp); - s.insert(j); - } - } - - fread(&nfrag, sizeof(unsigned int), 1, fp); - for(unsigned int i = 0; i < nfrag; i++) { - unsigned int p, n; - fread(&p, sizeof(unsigned int), 1, fp); - set &s = oldfragments[p]; - fread(&n, sizeof(unsigned int), 1, fp); - for(unsigned int k = 0; k < n; k++) { - unsigned int j; - fread(&j, sizeof(unsigned int), 1, fp); - s.insert(j); - } - } - fclose(fp); - return true; -} diff --git a/apps/nexus/vchain.h b/apps/nexus/vchain.h deleted file mode 100644 index 5d5a58e1..00000000 --- a/apps/nexus/vchain.h +++ /dev/null @@ -1,51 +0,0 @@ -/**************************************************************************** -* 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_VCHAIN_H -#define NXS_VCHAIN_H - -#include -#include -#include -#include "vpartition.h" - -namespace nxs { - -class VChain: public std::vector { - public: - ~VChain(); - bool Save(const std::string &file); - bool Load(const std::string &file); - - std::map > newfragments; - std::map > oldfragments; -}; - -} -#endif diff --git a/apps/nexus/vfile.h b/apps/nexus/vfile.h deleted file mode 100644 index eb194c71..00000000 --- a/apps/nexus/vfile.h +++ /dev/null @@ -1,317 +0,0 @@ -/**************************************************************************** -* 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 $ -Revision 1.19 2004/12/03 01:20:56 ponchio -Debug - -Revision 1.18 2004/12/01 03:24:32 ponchio -Level 2. - -Revision 1.17 2004/11/30 22:49:39 ponchio -Level 0. - -Revision 1.16 2004/11/28 04:12:04 ponchio -winsockapi include problem - -Revision 1.15 2004/11/18 18:30:14 ponchio -Using baricenters... lotsa changes. - -Revision 1.14 2004/10/19 16:50:27 ponchio -Added row file access .... - -Revision 1.13 2004/10/08 15:12:04 ponchio -Working version (maybe) - -Revision 1.12 2004/10/04 16:49:54 ponchio -Daily backup. Preparing for compression. - -Revision 1.11 2004/10/01 16:00:12 ponchio -Added include - -Revision 1.10 2004/09/30 23:56:33 ponchio -Backup (added strips and normals) - -Revision 1.9 2004/07/20 14:04:32 ponchio -Improved efficience in operator[] - -Revision 1.8 2004/07/15 14:32:49 ponchio -Debug. - -Revision 1.7 2004/07/05 15:49:39 ponchio -Windows (DevCpp, mingw) port. - -Revision 1.6 2004/07/04 15:23:48 ponchio -Debug - -Revision 1.5 2004/07/02 17:41:37 ponchio -Debug. - -Revision 1.4 2004/07/02 13:02:39 ponchio -Added GetRegion, Read and Write - -Revision 1.3 2004/07/01 21:36:54 ponchio -*** empty log message *** - -Revision 1.2 2004/06/25 16:47:13 ponchio -Various debug - -Revision 1.1 2004/06/24 14:32:45 ponchio -Moved from wrap/nexus - -Revision 1.3 2004/06/22 15:32:09 ponchio -Tested - -Revision 1.2 2004/06/22 10:27:16 ponchio -*** empty log message *** - -Revision 1.1 2004/06/22 00:39:56 ponchio -Created - - -****************************************************************************/ - -#ifndef VFILE_H -#define VFILE_H - -#include "mfile.h" - -#include -#include -#include -#include - -/**Vector structure on file with simulated mmapping. - * a priority queue of buffers is used - * TODO: port to over 4Gb usable space - * add interface for readonly reads even when file is readwrite... - * instead of queue size use ramsize!!!! - * some mechanism to report errors? - * use an Iterator? - */ - -namespace nxs { - -template class VFile: public MFile { - public: - - struct Buffer { - unsigned int key; - unsigned int size; //in number of elements - T *data; - }; - - protected: - unsigned int n_elements; - std::list buffers; - typedef typename std::list::iterator list_iterator; - - std::map index; //TODO move to hash_map - Buffer *last_buffer; - - unsigned int chunk_size; //default buffer size (expressed in number of T) - unsigned int queue_size; - - public: - class iterator { - public: - iterator(unsigned int p = 0, VFile *b = 0): n(p), buffer(b) {} - T &operator*() { return (*buffer)[n]; } - void operator++() { n++; } - bool operator!=(const iterator &i) { return i.n != n; } - private: - unsigned int n; - VFile *buffer; - }; - - VFile(): last_buffer(NULL) {} - ~VFile() { Close(); } - bool Create(const std::string &filename, - unsigned int _chunk_size = 4096/sizeof(T), - unsigned int _queue_size = 1000) { - - assert(_chunk_size > 0); - n_elements = 0; - last_buffer = NULL; - chunk_size = _chunk_size; - queue_size = _queue_size; - - return MFile::Create(filename); - } - - bool Load(const std:: string &filename, bool rdonly = false, - unsigned int _chunk_size = 4096/sizeof(T), - unsigned int _queue_size = 1000) { - - assert(_chunk_size > 0); - last_buffer = NULL; - chunk_size = _chunk_size; - queue_size = _queue_size; - - if(!MFile::Load(filename, rdonly)) return false; - n_elements = _size/sizeof(T); - return true; - } - - void Close() { - Flush(); - } - - void Delete() { - Flush(); - MFile::Delete(); - } - - void Flush() { - list_iterator i; - for(i = buffers.begin(); i != buffers.end(); i++) - FlushBuffer(*i); - buffers.clear(); - index.clear(); - last_buffer = NULL; - } - - void FlushBuffer(Buffer buffer) { - if(!readonly) { - SetPosition((int64)buffer.key * (int64)chunk_size * (int64)sizeof(T)); - WriteBuffer((char *)(buffer.data), buffer.size * sizeof(T)); - } - delete []buffer.data; - } - - void Resize(unsigned int elem) { - //TODO do i really need to flush? - Flush(); - MFile::Redim((int64)elem * (int64)sizeof(T)); - n_elements = elem; - } - - /** Remember that T is a valid pointer only until next call of - * getElement or setElement - */ - T &operator[](unsigned int n) { - - assert(n < n_elements); - - unsigned int chunk = n/chunk_size; - unsigned int offset = n - chunk*chunk_size; - assert(offset < chunk_size * sizeof(T)); - - if(last_buffer && last_buffer->key == chunk) - return *(last_buffer->data + offset); - - if(index.count(chunk)) { - last_buffer = &*index[chunk]; - return *((*(index[chunk])).data + offset); - } - - if(buffers.size() > queue_size) { - Buffer &buffer= buffers.back(); - assert(buffer.key != chunk); - FlushBuffer(buffer); - index.erase(buffer.key); - buffers.pop_back(); - } - - Buffer buffer; - buffer.key = chunk; - buffer.size = chunk_size; - - if(buffer.size + chunk * chunk_size > n_elements) - buffer.size = n_elements - chunk * chunk_size; - - buffer.data = new T[buffer.size]; - - buffers.push_front(buffer); - index[buffer.key] = buffers.begin(); - last_buffer = &*buffers.begin(); - - SetPosition((int64)chunk * (int64)chunk_size * (int64)sizeof(T)); - ReadBuffer((char *)(buffer.data), buffer.size * sizeof(T)); - - return *(buffer.data + offset); - } - - /** you can get a region instead of an element but: - 1)region must be Chunk aligned. - 2)you get impredictable results if regions overlap or mix with operator[] - */ - T *GetRegion(unsigned int start, unsigned int size, bool flush = true) { - assert(start + size <= n_elements); - assert((size % chunk_size) == 0); - assert((start % chunk_size) == 0); - if(size == 0) return NULL; - - unsigned int chunk = start/chunk_size; - - if(index.count(chunk)) - return ((*(index[chunk])).data); - - while(flush && buffers.size() > queue_size) { - Buffer &buffer= buffers.back(); - FlushBuffer(buffer); - index.erase(buffer.key); - buffers.pop_back(); - } - - Buffer buffer; - buffer.key = chunk; - buffer.size = size; - buffer.data = new T[buffer.size]; - - buffers.push_front(buffer); - index[chunk] = buffers.begin(); - - SetPosition((int64)chunk * (int64)chunk_size * (int64)sizeof(T)); - ReadBuffer((char *)(buffer.data), buffer.size * sizeof(T)); - return buffer.data; - } - //non buffered read only acces. - T read(unsigned int element) { - SetPosition((int64)element * (int64)sizeof(T)); - T t; - ReadBuffer(&t, sizeof(T)); - return t; - } - void write(unsigned int element, T &t) { - SetPosition((int64)element * (int64)sizeof(T)); - WriteBuffer(&t, sizeof(T)); - } - - void PushBack(const T &t) { - Resize(n_elements+1); - operator[](n_elements-1) = t; - } - - unsigned int Size() { return n_elements; } - unsigned int ChunkSize() { return chunk_size; } - unsigned int QueueSize() { return queue_size; } - iterator Begin() { return iterator(0, this); } - iterator End() { return iterator(Size(), this); } -}; - -}//namespace - -#endif diff --git a/apps/nexus/vpartition.cpp b/apps/nexus/vpartition.cpp deleted file mode 100644 index a4b8e027..00000000 --- a/apps/nexus/vpartition.cpp +++ /dev/null @@ -1,171 +0,0 @@ -/**************************************************************************** -* 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 $ -Revision 1.5 2005/02/22 10:38:17 ponchio -Debug, cleaning and optimization. - -Revision 1.4 2005/02/21 17:55:48 ponchio -debug debug debug - -Revision 1.3 2005/01/21 17:09:13 ponchio -Porting and debug. - -Revision 1.2 2004/12/04 13:24:27 ponchio -Fixed a couple of memory leak... - -Revision 1.1 2004/11/30 22:50:30 ponchio -Level 0. - - -****************************************************************************/ - -#include - -#include "vpartition.h" -#include - -using namespace std; -using namespace vcg; -using namespace nxs; - -VPartition::~VPartition() { - if(bd) delete bd; -} - -void VPartition::Init() { - if(bd) delete bd; - buffer.resize(size() * 3); - for(unsigned int i = 0; i < size(); i++) { - for(int k = 0; k < 3; k++) - buffer[i*3+k] = operator[](i)[k]; - } - points.resize(size()); - for(unsigned int i = 0; i < size(); i++) { - points[i] = &buffer[i*3]; - } - bd = new ANNkd_tree(&*points.begin(), size(), 3); -} - -void VPartition::Closest(const vcg::Point3f &p, unsigned int nsize, - vector &nears, - vector &dist) { - double point[3]; - point[0] = p[0]; - point[1] = p[1]; - point[2] = p[2]; - if(nsize > size()) nsize = size(); - - nears.resize(nsize); - dist.resize(nsize); - vector dists; - dists.resize(nsize); - bd->annkSearch(&point[0], nsize, &*nears.begin(), &*dists.begin()); - for(unsigned int i = 0; i < nsize; i++) - dist[i] = (float)dists[i]; -} - -void VPartition::Closest(const vcg::Point3f &p, - int &target, float &dist) { - double point[3]; - point[0] = p[0]; - point[1] = p[1]; - point[2] = p[2]; - double dists; - bd->annkSearch(&point[0], 1, &target, &dists); - assert(target >= 0); - assert(target < size()); - - dist = (float)dists; -} - -void VPartition::Closest(const vcg::Point3f &p, - vector &targets, - vector &dists, - float max_distance) { - - double point[3]; point[0] = p[0]; point[1] = p[1]; point[2] = p[2]; - - int seeds = 6; - while(1) { - if(seeds > size()) seeds = size(); - targets.resize(seeds); - dists.resize(seeds); - bd->annkSearch(&point[0], seeds, &(targets[0]), &(dists[0])); - for(int i = 0; i < seeds; i++) { - if(dists[i] > max_distance) { - targets.resize(i); - dists.resize(i); - break; - } - } - if(targets.size() < seeds) break; - if(seeds == size()) break; - seeds *= 2; - } -} - -void VPartition::Closest(const vcg::Point3f &p, unsigned int nsize, - int *targets, - double *dists) { - double point[3]; - point[0] = p[0]; - point[1] = p[1]; - point[2] = p[2]; - bd->annkSearch(&point[0], nsize, targets, dists); -} - -int VPartition::Locate(const vcg::Point3f &p) { - - double point[3]; - point[0] = p[0]; - point[1] = p[1]; - point[2] = p[2]; - - int target = -1; - double dists; - bd->annkSearch(&point[0], 1, &target, &dists); - - return target; -} - -float VPartition::Radius(unsigned int seed) { - assert(size() > 1); - int nears[2]; - double dists[2]; - - double point[3]; - Point3f &p = operator[](seed); - point[0] = p[0]; - point[1] = p[1]; - point[2] = p[2]; - bd->annkSearch(&point[0], 2, nears, dists); - - if(dists[1] == 0) return 0.0f; - assert(nears[0] == seed); - assert(dists[0] == 0); - - return (float)sqrt(dists[1]); -} diff --git a/apps/nexus/vpartition.h b/apps/nexus/vpartition.h deleted file mode 100644 index 0453a825..00000000 --- a/apps/nexus/vpartition.h +++ /dev/null @@ -1,99 +0,0 @@ -/**************************************************************************** -* 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 $ -Revision 1.4 2005/02/21 17:55:49 ponchio -debug debug debug - -Revision 1.3 2005/01/18 22:46:58 ponchio -Small changes. - -Revision 1.2 2004/12/04 13:24:28 ponchio -Fixed a couple of memory leak... - -Revision 1.1 2004/11/30 22:50:30 ponchio -Level 0. - - -****************************************************************************/ - -#ifndef NXS_VPARTITION_H -#define NXS_VPARTITION_H - -#include -#include - -#include - - -//TODO provide a Sort function, to sort spatially the seeds. - -class ANNkd_tree; -class ANNbd_tree; -class ANNbruteForce; - -namespace nxs { - - //WARNING: all distances returned are SQUARED!!!! -class VPartition: public std::vector { - public: - VPartition(): bd(NULL) {} - ~VPartition(); - private: - VPartition &operator=(const VPartition &part) { - for(unsigned int i = 0; i < part.size(); i++) - push_back(part[i]); - Init(); - return *this; - } - public: - void Init(); - int Locate(const vcg::Point3f &p); - - //looks for the min distance point from seed. - float Radius(unsigned int seed); - void Closest(const vcg::Point3f &p, unsigned int nsize, - std::vector &nears, - std::vector &dist); - void Closest(const vcg::Point3f &p, - int &target, float &dist); - - //return all targets widthin that distance (SQUARED!!!) - void Closest(const vcg::Point3f &p, std::vector &targets, - std::vector &dists, - float max_distance); - //most efficient! - void Closest(const vcg::Point3f &p, unsigned int nsize, - int *targets, - double *dists); - - - ANNkd_tree *bd; - std::vector buffer; - std::vector points; -}; - -} //namespace nxs -#endif diff --git a/apps/nexus/watch.cpp b/apps/nexus/watch.cpp deleted file mode 100644 index f35491c6..00000000 --- a/apps/nexus/watch.cpp +++ /dev/null @@ -1,155 +0,0 @@ -/**************************************************************************** -* 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 $ -Revision 1.3 2004/10/21 13:40:16 ponchio -Debugging. - -Revision 1.2 2004/10/21 12:14:02 ponchio -Support for mfile (>4Gb) - -Revision 1.1 2004/10/19 17:20:24 ponchio -renamed - -Revision 1.5 2004/10/19 17:16:52 ponchio -Changed interface - -Revision 1.4 2004/07/30 12:44:14 ponchio -#ifdef corrected - -Revision 1.3 2004/07/20 14:03:47 ponchio -Changed interface. - -Revision 1.2 2004/07/05 15:49:39 ponchio -Windows (DevCpp, mingw) port. - -Revision 1.1 2004/07/01 21:38:30 ponchio -First draft created. - - -****************************************************************************/ -#include "watch.h" -#include - -#ifdef WIN32 -Watch::Watch(): elapsed(0) { - QueryPerformanceFrequency(&freq); -} - - -void Watch::Start(void) { - QueryPerformanceCounter(&tstart); - elapsed = 0; -} - -double Watch::Pause() { - QueryPerformanceCounter(&tend); - elapsed += Diff(); - return (double)elapsed; -} - -void Watch::Continue() { - QueryPerformanceCounter(&tstart); -} - -double Watch::Time() { - QueryPerformanceCounter(&tend); - return (double)(elapsed + Diff()); -} - -double Watch::Diff() { - return ((double)tend.QuadPart - - (double)tstart.QuadPart)/ - ((double)freq.QuadPart); -} - -#else - -Watch::Watch(): elapsed() {} - -void Watch::Start() { - gettimeofday(&tstart, &tz); - elapsed = 0; -} - -double Watch::Pause() { - gettimeofday(&tend, &tz); - elapsed += Diff(); - return (double)elapsed; -} - -void Watch::Continue() { - gettimeofday(&tstart, &tz); -} - -double Watch::Time() { - gettimeofday(&tend, &tz); - return (double)(elapsed + Diff()); -} - -double Watch::Diff() { - double t1 = (double)tstart.tv_sec + (double)tstart.tv_usec/(1000*1000); - double t2 = (double)tend.tv_sec + (double)tend.tv_usec/(1000*1000); - return t2 - t1; -} -#endif - -void Watch::Reset() { - elapsed = 0; -} - -int Watch::Usec() { -#ifdef WIN32 - return 0; -#else - struct timeval ttime; - gettimeofday(&ttime, &tz); - return ttime.tv_usec; -#endif -} - -void Report::Init(unsigned int t, double inter) { - watch.Start(); - tot = t; - last = 0; - interval = inter; -} - -void Report::Step(unsigned int count) { - if(count == 0) return; - double now = watch.Time(); - if(now - last < interval) return; - //estimate final time - double tot_time = now * tot/(double)count; - printf("%d/%d\telapsed: %.1f\tremaining: %.1f\ttotal: %.1f\n", - count, tot, now, tot_time - now, tot_time); - last = now; -} - -void Report::Finish() { - double now = watch.Time(); - printf("Tot: %.1f\tN: %d\tN/sec: %.1f\n", - now, tot, tot/now); -} diff --git a/apps/nexus/watch.h b/apps/nexus/watch.h deleted file mode 100644 index 160af708..00000000 --- a/apps/nexus/watch.h +++ /dev/null @@ -1,108 +0,0 @@ -/**************************************************************************** -* 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 $ -Revision 1.6 2004/12/15 13:50:32 ponchio -Optimizing realtime vis. - -Revision 1.5 2004/12/01 16:00:35 ponchio -Level 3 - -Revision 1.4 2004/11/28 04:10:59 ponchio -winsockapi include problem - -Revision 1.3 2004/10/21 13:40:16 ponchio -Debugging. - -Revision 1.2 2004/10/21 12:14:02 ponchio -Support for mfile (>4Gb) - -Revision 1.1 2004/10/19 17:20:24 ponchio -renamed - -Revision 1.4 2004/10/19 17:16:53 ponchio -Changed interface - -Revision 1.3 2004/07/20 14:03:47 ponchio -Changed interface. - -Revision 1.2 2004/07/05 15:49:39 ponchio -Windows (DevCpp, mingw) port. - -Revision 1.1 2004/07/01 21:38:30 ponchio -First draft created. - - -****************************************************************************/ - -#ifndef VCG_WATCH_H -#define VCG_WATCH_H - -#ifdef WIN32 -#ifndef _WINDOWS_ -#define _WINSOCKAPI_ -#include -#endif -#else -#include -#include -#endif - -class Watch { -public: - Watch(); - void Start(); - double Pause(); - void Continue(); - void Reset(); - double Time(); - int Usec(); -private: - double Diff(); - -#ifdef WIN32 - LARGE_INTEGER tstart, tend; - LARGE_INTEGER freq; -#else - struct timeval tstart, tend; - struct timezone tz; -#endif - double elapsed; -}; - -class Report { - public: - Report(unsigned int tot = 1, double inter = 30.0f) { Init(tot, inter); } - void Init(unsigned int tot, double inter = 30.0f); - void Step(unsigned int count); - void Finish(); - private: - Watch watch; - int tot; - double last; - double interval; -}; - -#endif diff --git a/apps/nexus/zcurve.h b/apps/nexus/zcurve.h deleted file mode 100644 index 8aa5a9ff..00000000 --- a/apps/nexus/zcurve.h +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef VCG_ZCURVE_H - -#include - -namespace vcg { - -class ZCurve: public Box3f { -public: - - unsigned int side; - - ZCurve(): side(1024) {} - - void SetSide(int s) { assert(s <= (1<<10)); side = s; } - - unsigned int Pos(const Point3f &p) const { - assert(!IsNull()); - unsigned int position = 0; - - float x = (p[0] - min[0])/(max[0] - min[0]); - float y = (p[1] - min[1])/(max[1] - min[1]); - float z = (p[2] - min[2])/(max[2] - min[2]); - - unsigned int s = side; - while(s > 1) { - position *= 8; - x *= 2; - y *= 2; - z *= 2; - int dx = (int)floor(x); - int dy = (int)floor(y); - int dz = (int)floor(z); - position += dx + 2 * dy + 4 * dz; - x -= dx; - y -= dy; - z -= dz; - s /= 2; - } - return position; - } -}; - -} - - -#endif