Small changes and adding realtime extr.
This commit is contained in:
parent
90e4d33361
commit
7f31d74240
|
@ -24,6 +24,9 @@
|
|||
History
|
||||
|
||||
$Log: not supported by cvs2svn $
|
||||
Revision 1.7 2005/01/14 15:49:42 ponchio
|
||||
VertRemap removed.
|
||||
|
||||
Revision 1.6 2004/12/01 03:24:30 ponchio
|
||||
Level 2.
|
||||
|
||||
|
@ -77,8 +80,6 @@ bool fullscreen = false;
|
|||
int width =1024;
|
||||
int height = 768;
|
||||
|
||||
//TrackHand hand;
|
||||
|
||||
SDL_Surface *screen = NULL;
|
||||
|
||||
bool init() {
|
||||
|
@ -102,8 +103,7 @@ bool init() {
|
|||
return false;
|
||||
}
|
||||
|
||||
SDL_WM_SetIcon(SDL_LoadBMP("inspector.bmp"), NULL);
|
||||
SDL_WM_SetCaption(" Inspector", "Inspector");
|
||||
SDL_WM_SetCaption("Crudeview", "Crudeview");
|
||||
|
||||
|
||||
glDisable(GL_DITHER);
|
||||
|
@ -225,13 +225,10 @@ int main(int argc, char *argv[]) {
|
|||
|
||||
float scale = 3/box.Diag();
|
||||
glScalef(0.4, 0.4, 0.4);
|
||||
// glRotatef(alpha, 0, 1, 0);
|
||||
// alpha++;
|
||||
// if(alpha > 360) alpha = 0;
|
||||
glScalef(scale, scale, scale);
|
||||
Point3f center = box.Center();
|
||||
glTranslatef(-center[0], -center[1], -center[2]);
|
||||
// render.render();
|
||||
|
||||
glColor3f(0, 1, 0);
|
||||
|
||||
glBegin(GL_TRIANGLES);
|
||||
|
@ -260,8 +257,6 @@ int main(int argc, char *argv[]) {
|
|||
SDL_GL_SwapBuffers();
|
||||
}
|
||||
|
||||
// Clean up
|
||||
|
||||
SDL_Quit();
|
||||
return -1;
|
||||
}
|
||||
|
|
|
@ -93,8 +93,6 @@ float nxs::Decimate(Decimation mode,
|
|||
for(unsigned int i = 0; i < newbord.size(); i++)
|
||||
mesh.vert[newbord[i].start_vert].ClearW();
|
||||
|
||||
// vcg::tri::io::ExporterPLY<MyMesh>::Save(mesh, "ribum.ply");
|
||||
|
||||
float error;
|
||||
switch(mode) {
|
||||
case CLUSTER: error = Cluster(mesh, target_faces); break;
|
||||
|
@ -129,13 +127,6 @@ float nxs::Decimate(Decimation mode,
|
|||
v = vert_remap[v];
|
||||
}
|
||||
|
||||
//Temporary test again:
|
||||
/* 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]);
|
||||
}*/
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
|
@ -191,7 +182,7 @@ float Cluster(MyMesh &mesh, unsigned int target_faces) {
|
|||
}
|
||||
}
|
||||
unsigned int nborder = part.size();
|
||||
//Dovrei supersamplare prima....
|
||||
//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()) {
|
||||
|
|
|
@ -18,6 +18,8 @@ Extraction::~Extraction() {
|
|||
|
||||
void Extraction::Extract(NexusMt *_mt) {
|
||||
mt = _mt;
|
||||
root = mt->history.Root();
|
||||
sink = root + (mt->history.n_nodes()-1);
|
||||
|
||||
//clear statistics
|
||||
extr_used = draw_used = disk_used = 0;
|
||||
|
@ -28,13 +30,6 @@ void Extraction::Extract(NexusMt *_mt) {
|
|||
|
||||
heap.clear();
|
||||
|
||||
History::Node *root = mt->history.Root();
|
||||
HeapNode hroot(root, 0);
|
||||
Diff(hroot);
|
||||
extr_used += hroot.extr;
|
||||
draw_used += hroot.draw;
|
||||
disk_used += hroot.disk;
|
||||
|
||||
Visit(root);
|
||||
|
||||
while(heap.size()) {
|
||||
|
@ -42,36 +37,250 @@ void Extraction::Extract(NexusMt *_mt) {
|
|||
HeapNode hnode = heap.back();
|
||||
heap.pop_back();
|
||||
|
||||
History::Node *node = hnode.node;
|
||||
unsigned int id = node - root;
|
||||
if(visited[id]) continue;
|
||||
Node *node = hnode.node;
|
||||
if(visited[node - root]) continue;
|
||||
|
||||
if(Expand(hnode)) {
|
||||
extr_used += hnode.extr;
|
||||
draw_used += hnode.draw;
|
||||
disk_used += hnode.disk;
|
||||
if(Expand(hnode))
|
||||
Visit(node);
|
||||
}
|
||||
Select();
|
||||
draw_size = selected.size();
|
||||
}
|
||||
|
||||
void Extraction::Init() {
|
||||
front.clear();
|
||||
back.clear();
|
||||
|
||||
Cost cost;
|
||||
|
||||
Node *nodes = mt->history.nodes;
|
||||
for(unsigned int i = 0; i < visited.size(); i++) {
|
||||
if(!visited[i]) continue;
|
||||
Node &node = nodes[i];
|
||||
|
||||
bool cancoarse = true;
|
||||
Node::iterator n;
|
||||
for(n = node.out_begin(); n != node.out_end(); n++) {
|
||||
|
||||
if(!visited[(*n).node - root]) {
|
||||
float maxerror = 0;
|
||||
|
||||
Link &link = *n;
|
||||
for(Link::iterator k = link.begin(); k != link.end(); k++) {
|
||||
unsigned int patch = (*k).patch;
|
||||
Entry &entry = (*mt)[patch];
|
||||
float error = metric->GetError(entry);
|
||||
if(error > maxerror) maxerror = error;
|
||||
|
||||
cost.extr += entry.ram_size;
|
||||
vcg::Sphere3f &sphere = entry.sphere;
|
||||
if(!frustum.IsOutside(sphere.Center(), sphere.Radius()))
|
||||
cost.draw += entry.ram_size;
|
||||
if(!entry.patch)
|
||||
cost.disk += entry.disk_size;
|
||||
}
|
||||
if((*n).node != sink && maxerror > target_error)
|
||||
front.push_back(HeapNode((*n).node, maxerror));
|
||||
} else
|
||||
cancoarse = false;
|
||||
}
|
||||
if(cancoarse && &node != root) {
|
||||
float error = GetRefineError(&node);
|
||||
back.push_back(HeapNode(&node, error));
|
||||
}
|
||||
}
|
||||
make_heap(front.begin(), front.end());
|
||||
make_heap(back.begin(), back.end(), greater<HeapNode>());
|
||||
|
||||
extr_used = cost.extr;
|
||||
draw_used = cost.draw;
|
||||
disk_used = cost.disk;
|
||||
}
|
||||
|
||||
void Extraction::Update(NexusMt *_mt) {
|
||||
mt = _mt;
|
||||
root = mt->history.Root();
|
||||
sink = root + (mt->history.n_nodes()-1);
|
||||
//clear statistics
|
||||
|
||||
if(!visited.size()) {
|
||||
visited.resize(mt->history.n_nodes(), false);
|
||||
visited[0] = true;
|
||||
}
|
||||
|
||||
Init();
|
||||
|
||||
//first we coarse
|
||||
while(back.size()) {
|
||||
if(draw_used <= draw_max &&
|
||||
extr_used <= extr_max &&
|
||||
(front.size() && back.front().error > front.front().error))
|
||||
break;
|
||||
pop_heap(back.begin(), back.end(), greater<HeapNode>());
|
||||
HeapNode hnode = back.back();
|
||||
|
||||
if(Visited(hnode.node)) {
|
||||
if(!Coarse(hnode)) { //push back on heap the heapnode
|
||||
push_heap(back.begin(), back.end(), greater<HeapNode>());
|
||||
break;
|
||||
}
|
||||
}
|
||||
back.pop_back();
|
||||
}
|
||||
|
||||
while(front.size() && (*front.begin()).error > target_error) {
|
||||
pop_heap(front.begin(), front.end());
|
||||
HeapNode hnode = front.back();
|
||||
|
||||
if(!Visited(hnode.node)) {
|
||||
if(!Refine(hnode.node)) {
|
||||
push_heap(front.begin(), front.end());
|
||||
break;
|
||||
}
|
||||
}
|
||||
front.pop_back();
|
||||
}
|
||||
|
||||
Select();
|
||||
draw_size = selected.size();
|
||||
|
||||
|
||||
//Preloading now
|
||||
for(unsigned int i = 0; i < 100; 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 i;
|
||||
for(i = node->out_begin(); i != node->out_end(); i++) {
|
||||
Link &link = (*i);
|
||||
for(Link::iterator k = link.begin(); k != link.end(); k++) {
|
||||
selected.push_back((*k).patch);
|
||||
}
|
||||
}
|
||||
} else if(back.size()) {
|
||||
pop_heap(back.begin(), back.end(), greater<HeapNode>());
|
||||
HeapNode hnode = back.back();
|
||||
Node *node = hnode.node;
|
||||
back.pop_back();
|
||||
Node::iterator i;
|
||||
for(i = node->in_begin(); i != node->in_end(); i++) {
|
||||
Link &link = (*i);
|
||||
for(Link::iterator k = link.begin(); k != link.end(); k++) {
|
||||
selected.push_back((*k).patch);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float Extraction::GetRefineError(Node *node) {
|
||||
float maxerror = 0;
|
||||
Node::iterator i;
|
||||
for(i = node->in_begin(); i != node->in_end(); i++) {
|
||||
Link &link = *i;
|
||||
for(Link::iterator k = link.begin(); k != link.end(); k++) {
|
||||
Entry &entry = (*mt)[(*k).patch];
|
||||
float error = metric->GetError(entry);
|
||||
if(error > maxerror) maxerror = error;
|
||||
}
|
||||
}
|
||||
return maxerror;
|
||||
}
|
||||
|
||||
bool Extraction::Refine(Node *node) {
|
||||
//recursively refine parent if applicable.
|
||||
Node::iterator i;
|
||||
for(i = node->in_begin(); i != node->in_end(); i++) {
|
||||
Node *parent = (*i).node;
|
||||
if(!Visited(parent))
|
||||
if(!Refine(parent))
|
||||
return false;
|
||||
}
|
||||
|
||||
Cost cost;
|
||||
Diff(node, cost);
|
||||
|
||||
if(disk_used + cost.disk > disk_max) return false;
|
||||
if(extr_used + cost.extr > extr_max) return false;
|
||||
if(draw_used + cost.draw > draw_max) return false;
|
||||
|
||||
extr_used += cost.extr;
|
||||
draw_used += cost.draw;
|
||||
disk_used += cost.disk;
|
||||
|
||||
visited[node - root] = true;
|
||||
|
||||
|
||||
//now add to the front children (unless sink node)
|
||||
|
||||
for(i = node->out_begin(); i != node->out_end(); i++) {
|
||||
Link &link = *i;
|
||||
if(link.node == sink) continue;
|
||||
float maxerror = GetRefineError(link.node);
|
||||
|
||||
if(maxerror > target_error)
|
||||
front.push_back(HeapNode((*i).node, maxerror));
|
||||
}
|
||||
push_heap(front.begin(), front.end());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Extraction::Coarse(HeapNode &hnode) {
|
||||
Node *node = hnode.node;
|
||||
//recursively coarse children if applicable.
|
||||
Node::iterator i;
|
||||
for(i = node->out_begin(); i != node->out_end(); i++) {
|
||||
Node *child = (*i).node;
|
||||
float error = GetRefineError(child);
|
||||
HeapNode hchild(child, error);
|
||||
if(Visited(child))
|
||||
if(!Coarse(hchild)) return false;
|
||||
}
|
||||
|
||||
|
||||
Cost cost;
|
||||
Diff(node, cost);
|
||||
extr_used -= cost.extr;
|
||||
draw_used -= cost.draw;
|
||||
disk_used -= cost.disk;
|
||||
|
||||
if(disk_used > disk_max) return false;
|
||||
|
||||
visited[node - root] = false;
|
||||
|
||||
//now add to the back parents (unless root node)
|
||||
for(i = node->in_begin(); i != node->in_end(); i++) {
|
||||
Link &link = *i;
|
||||
if(link.node == root) continue;
|
||||
float maxerror = GetRefineError(link.node);
|
||||
|
||||
back.push_back(HeapNode(link.node, maxerror));
|
||||
push_heap(back.begin(), back.end(), greater<HeapNode>());
|
||||
}
|
||||
|
||||
front.push_back(hnode);
|
||||
push_heap(front.begin(), front.end());
|
||||
return true;
|
||||
}
|
||||
|
||||
void Extraction::Select() {
|
||||
selected.clear();
|
||||
History::Node *root = mt->history.Root();
|
||||
Node *root = mt->history.Root();
|
||||
|
||||
History::Node *nodes = mt->history.nodes;
|
||||
Node *nodes = mt->history.nodes;
|
||||
for(unsigned int i = 0; i < visited.size(); i++) {
|
||||
if(!visited[i]) continue;
|
||||
History::Node &node = nodes[i];
|
||||
Node &node = nodes[i];
|
||||
|
||||
History::Node::iterator n;
|
||||
Node::iterator n;
|
||||
for(n = node.out_begin(); n != node.out_end(); n++) {
|
||||
unsigned int n_out = (*n).node - root;
|
||||
if(!visited[n_out]) {
|
||||
History::Link &link = *n;
|
||||
for(History::Link::iterator k = link.begin(); k != link.end(); k++) {
|
||||
Link &link = *n;
|
||||
for(Link::iterator k = link.begin(); k != link.end(); k++) {
|
||||
unsigned int patch = (*k).patch;
|
||||
selected.push_back(patch);
|
||||
}
|
||||
|
@ -80,29 +289,28 @@ void Extraction::Select() {
|
|||
}
|
||||
}
|
||||
|
||||
void Extraction::Visit(History::Node *node) {
|
||||
History::Node *root = mt->history.Root();
|
||||
unsigned int n_node = node - root;
|
||||
if(visited[n_node]) return;
|
||||
void Extraction::Visit(Node *node) {
|
||||
if(visited[node - root]) return;
|
||||
|
||||
visited[n_node] = true;
|
||||
visited[node - root] = true;
|
||||
|
||||
History::Node::iterator i;
|
||||
Node::iterator i;
|
||||
for(i = node->in_begin(); i != node->in_end(); i++) {
|
||||
unsigned int n_in = (*i).node - root;
|
||||
if(visited[n_in]) continue;
|
||||
HeapNode hin((*i).node, 0);
|
||||
Diff(hin);
|
||||
extr_used += hin.extr;
|
||||
draw_used += hin.draw;
|
||||
disk_used += hin.disk;
|
||||
Visit((*i).node);
|
||||
}
|
||||
|
||||
Cost cost;
|
||||
Diff(node, cost);
|
||||
extr_used += cost.extr;
|
||||
draw_used += cost.draw;
|
||||
disk_used += cost.disk;
|
||||
|
||||
for(i = node->out_begin(); i != node->out_end(); i++) {
|
||||
float maxerror = 0;
|
||||
History::Link &link = *i;
|
||||
for(History::Link::iterator k = link.begin(); k != link.end(); k++) {
|
||||
Link &link = *i;
|
||||
for(Link::iterator k = link.begin(); k != link.end(); k++) {
|
||||
Entry &entry = (*mt)[(*k).patch];
|
||||
float error = metric->GetError(entry);
|
||||
if(error > maxerror) maxerror = error;
|
||||
|
@ -110,7 +318,6 @@ void Extraction::Visit(History::Node *node) {
|
|||
//TODO this check may be dangerous for non saturating things...
|
||||
if(maxerror > target_error) {
|
||||
HeapNode hnode((*i).node, maxerror);
|
||||
Diff(hnode);
|
||||
heap.push_back(hnode);
|
||||
push_heap(heap.begin(), heap.end());
|
||||
}
|
||||
|
@ -124,34 +331,34 @@ bool Extraction::Expand(HeapNode &node) {
|
|||
return node.error > target_error;
|
||||
}
|
||||
|
||||
void Extraction::Diff(HeapNode &hnode) {
|
||||
History::Node &node = *(hnode.node);
|
||||
History::Node::iterator i;
|
||||
for(i = node.in_begin(); i != node.in_end(); i++) {
|
||||
History::Link &link = *i;
|
||||
for(History::Link::iterator k = link.begin(); k != link.end(); k++) {
|
||||
void Extraction::Diff(Node *node, Cost &cost) {
|
||||
Node::iterator i;
|
||||
for(i = node->in_begin(); i != node->in_end(); i++) {
|
||||
Link &link = *i;
|
||||
for(Link::iterator k = link.begin(); k != link.end(); k++) {
|
||||
unsigned int patch = (*k).patch;
|
||||
Entry &entry = (*mt)[patch];
|
||||
hnode.extr -= entry.ram_size;
|
||||
cost.extr -= entry.ram_size;
|
||||
vcg::Sphere3f &sphere = entry.sphere;
|
||||
if(!frustum.IsOutside(sphere.Center(), sphere.Radius()))
|
||||
hnode.draw -= entry.ram_size;
|
||||
cost.draw -= entry.ram_size;
|
||||
if(!entry.patch)
|
||||
hnode.disk -= entry.disk_size;
|
||||
cost.disk -= entry.disk_size;
|
||||
}
|
||||
}
|
||||
|
||||
for(i = node.out_begin(); i != node.out_end(); i++) {
|
||||
History::Link &link = *i;
|
||||
for(History::Link::iterator k = link.begin(); k != link.end(); k++) {
|
||||
for(i = node->out_begin(); i != node->out_end(); i++) {
|
||||
Link &link = *i;
|
||||
for(Link::iterator k = link.begin(); k != link.end(); k++) {
|
||||
unsigned int patch = (*k).patch;
|
||||
Entry &entry = (*mt)[patch];
|
||||
hnode.extr += entry.ram_size;
|
||||
cost.extr += entry.ram_size;
|
||||
vcg::Sphere3f &sphere = entry.sphere;
|
||||
if(!frustum.IsOutside(sphere.Center(), sphere.Radius()))
|
||||
hnode.draw += entry.ram_size;
|
||||
cost.draw += entry.ram_size;
|
||||
if(!entry.patch)
|
||||
hnode.disk += entry.disk_size;
|
||||
cost.disk += entry.disk_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
#ifndef NXS_EXTRACTION_H
|
||||
#define NXS_EXTRACTION_H
|
||||
|
||||
#include "history.h"
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
#include <wrap/gui/frustum.h>
|
||||
|
||||
#include "history.h"
|
||||
|
||||
namespace nxs {
|
||||
|
||||
class Metric;
|
||||
|
@ -12,16 +15,25 @@ class NexusMt;
|
|||
|
||||
class Extraction {
|
||||
public:
|
||||
struct HeapNode {
|
||||
History::Node *node;
|
||||
float error;
|
||||
typedef History::Node Node;
|
||||
typedef History::Link Link;
|
||||
|
||||
struct Cost {
|
||||
unsigned int extr;
|
||||
unsigned int draw;
|
||||
unsigned int disk;
|
||||
HeapNode(History::Node *_node, float _error): node(_node), error(_error),
|
||||
extr(0), draw(0), disk(0) {}
|
||||
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;
|
||||
|
@ -36,23 +48,44 @@ class Extraction {
|
|||
std::vector<bool> visited;
|
||||
std::vector<HeapNode> heap;
|
||||
std::vector<unsigned int> selected;
|
||||
unsigned int draw_size; //first in selected should be drawn
|
||||
|
||||
//nodes that i can expand to
|
||||
std::vector<HeapNode> front;
|
||||
//nodes that i can contract
|
||||
std::vector<HeapNode> back;
|
||||
|
||||
unsigned int tot_budget;
|
||||
|
||||
Extraction();
|
||||
~Extraction();
|
||||
|
||||
void Extract(NexusMt *mt);
|
||||
// void Update(std::vector<unsigned int> &selected);
|
||||
void Update(NexusMt *mt);
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
void Select();
|
||||
void Visit(History::Node *node);
|
||||
void Visit(Node *node);
|
||||
|
||||
bool Expand(HeapNode &node);
|
||||
void Diff(HeapNode &node);
|
||||
void Diff(Node *node, Cost &cost);
|
||||
|
||||
bool Refine(Node *node);
|
||||
bool Coarse(HeapNode &node);
|
||||
|
||||
void Init();
|
||||
private:
|
||||
NexusMt *mt;
|
||||
Node *root;
|
||||
Node *sink;
|
||||
|
||||
bool Visited(Node *node) {
|
||||
return visited[node - root];
|
||||
}
|
||||
|
||||
float GetRefineError(Node *node);
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
#include <set>
|
||||
#include <iostream>
|
||||
|
||||
#include "fragment.h"
|
||||
#include "border.h"
|
||||
#include "pvoronoi.h"
|
||||
#include <iostream>
|
||||
//#include "pvoronoi.h"
|
||||
|
||||
|
||||
using namespace std;
|
||||
using namespace vcg;
|
||||
|
@ -211,76 +214,6 @@ void nxs::Join(Fragment &in,
|
|||
exit(0);
|
||||
}
|
||||
}
|
||||
/* old code (more general.. but not parallelizable)
|
||||
|
||||
|
||||
//L(a, b): Exist link between a, b
|
||||
//An external link L(e, v) where v belongs to the patches (and e not)
|
||||
//is valid only if: for every x in patches L(v, x) => L(e, x)
|
||||
//this means the number of internal links for the same shared
|
||||
//vertex is E = (n * (n-1)) where n is the number of duplicated vertices
|
||||
//and n must be the number of externa links.
|
||||
vector<unsigned int> internal_links;
|
||||
internal_links.resize(vcount, 0);
|
||||
|
||||
map<BigLink, unsigned int> newborders;
|
||||
for(unsigned int i = 0; i < in.pieces.size(); i++) {
|
||||
unsigned int offset = offsets[i];
|
||||
vector<Link> &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...may be erased
|
||||
BigLink link;
|
||||
link.orig_vert = llink.start_vert;
|
||||
link.orig_patch = in.pieces[i].patch;
|
||||
link.start_vert = remap[offset + llink.start_vert];
|
||||
link.end_patch = llink.end_patch;
|
||||
link.end_vert = llink.end_vert;
|
||||
if(!newborders.count(link))
|
||||
newborders[link] = 1;
|
||||
else
|
||||
newborders[link]++;
|
||||
} else { //internal
|
||||
internal_links[remap[offset + llink.start_vert]]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
newvert.resize(vcount);
|
||||
newface.resize(fcount*3);
|
||||
newbord.resize(0);
|
||||
|
||||
fcount = 0;
|
||||
for(unsigned int i = 0; i < in.pieces.size(); i++) {
|
||||
unsigned int offset = offsets[i];
|
||||
vector<Point3f> &vert = in.pieces[i].vert;
|
||||
vector<unsigned short> &face = in.pieces[i].face;
|
||||
vector<Link> &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]];
|
||||
}
|
||||
}
|
||||
|
||||
map<BigLink, unsigned int>::iterator b;
|
||||
for(b = newborders.begin(); b != newborders.end(); b++) {
|
||||
//test that number of links on this vertex is equal to
|
||||
//number of internal links of the internal vertex
|
||||
const BigLink &link = (*b).first;
|
||||
unsigned int n = (*b).second;
|
||||
if(n * (n-1) == internal_links[link.start_vert])
|
||||
newbord.push_back(link);
|
||||
}*/
|
||||
}
|
||||
|
||||
void nxs::Split(Fragment &out,
|
||||
|
@ -422,12 +355,6 @@ void nxs::Split(Fragment &out,
|
|||
}
|
||||
}
|
||||
}
|
||||
/* cerr << "patch seed: " << patch.patch << endl;
|
||||
for(unsigned int i = 0; i < bords.size(); i++) {
|
||||
Link &link = bords[i];
|
||||
cerr << "link: " << link.start_vert << " "
|
||||
<< link.end_patch << " " << link.end_vert << endl;
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,15 +5,12 @@
|
|||
|
||||
#include <ptypes/pstreams.h>
|
||||
#include "nexus.h"
|
||||
#include "pvoronoi.h"
|
||||
|
||||
namespace nxs {
|
||||
|
||||
class VoronoiPartition;
|
||||
|
||||
struct BigLink {
|
||||
// unsigned int orig_vert;
|
||||
// unsigned int orig_patch;
|
||||
unsigned int start_vert;
|
||||
unsigned int end_patch;
|
||||
unsigned int end_vert;
|
||||
|
|
|
@ -205,7 +205,7 @@ unsigned int Nexus::AddPatch(unsigned int nvert, unsigned int nface,
|
|||
return size() - 1;
|
||||
}
|
||||
|
||||
void Nexus::Unify(float threshold) {
|
||||
/*void Nexus::Unify(float threshold) {
|
||||
//TODO what if colors or normals or strips?
|
||||
unsigned int duplicated = 0;
|
||||
unsigned int degenerate = 0;
|
||||
|
@ -318,7 +318,7 @@ void Nexus::Unify(float threshold) {
|
|||
cerr << "Found " << duplicated << " duplicated vertices" << endl;
|
||||
if(degenerate)
|
||||
cerr << "Found " << degenerate << " degenerate face while unmifying\n";
|
||||
}
|
||||
}*/
|
||||
|
||||
Patch *Nexus::LoadPatch(unsigned int idx) {
|
||||
assert(idx < size());
|
||||
|
|
|
@ -22,110 +22,9 @@ void Stats::Init() {
|
|||
count++;
|
||||
}
|
||||
|
||||
/*float ExtractContest::GetError(PatchInfo &entry) {
|
||||
Sphere3f &sphere = entry.sphere;
|
||||
float dist = Distance(sphere, frustum.ViewPoint());
|
||||
if(dist < 0)
|
||||
return 1e20f;
|
||||
if(frustum.IsOutside(sphere.Center(), sphere.Radius()))
|
||||
return -1;
|
||||
return entry.error/frustum.Resolution(dist);
|
||||
}*/
|
||||
|
||||
/*
|
||||
float Metric::GetError(Node *node) {
|
||||
float max_error = 0;
|
||||
vector<Frag>::iterator frag;
|
||||
for(frag = node->frags.begin(); frag != node->frags.end(); frag++) {
|
||||
float error = GetError(*frag);
|
||||
if(max_error < error) max_error = error;
|
||||
}
|
||||
return max_error;
|
||||
}
|
||||
|
||||
float Metric::GetError(Frag &frag) {
|
||||
float max_error = 0;
|
||||
vector<unsigned int>::iterator cell;
|
||||
for(cell = frag.begin(); cell != frag.end(); cell++) {
|
||||
float error = GetError(*cell);
|
||||
if(max_error < error) max_error = error;
|
||||
}
|
||||
return max_error;
|
||||
}
|
||||
|
||||
float FrustumMetric::GetError(unsigned int cell) {
|
||||
float max_error = 0;
|
||||
PatchInfo &entry = (*index)[cell];
|
||||
Sphere3f &sphere = entry.sphere;
|
||||
float dist = Distance(sphere, frustum.ViewPoint());
|
||||
if(dist < 0) return 1e20f;
|
||||
float error = entry.error/frustum.Resolution(dist);
|
||||
if(frustum.IsOutside(sphere.Center(), sphere.Radius()))
|
||||
error /= 32;
|
||||
return error;
|
||||
}
|
||||
|
||||
bool NexusMt::Expand(TNode &tnode) {
|
||||
//expand if node error > target error
|
||||
if(extraction_used >= extraction_max) return false;
|
||||
if(draw_used >= draw_max) return false;
|
||||
if(disk_used >= disk_max) {
|
||||
float ratio = disk_max / (float)disk_used;
|
||||
float error = tnode.error * ratio;
|
||||
return error > target_error;
|
||||
}
|
||||
return tnode.error > target_error;
|
||||
}*/
|
||||
|
||||
/*void NexusMt::NodeVisited(Node *node) {
|
||||
//TODO write this a bit more elegant.
|
||||
//first we process arcs removed:
|
||||
|
||||
for(unsigned int i = 0; i < node->out.size(); i++) {
|
||||
//assert(!(node->out[i]->visited));
|
||||
Frag &frag = node->frags[i];
|
||||
for(unsigned int k = 0; k < frag.size(); k++) {
|
||||
unsigned int patch = frag[k];
|
||||
PServer::Entry &entry = patches.entries[patch];
|
||||
unsigned int ram_size = entry.ram_size;
|
||||
extraction_used += ram_size;
|
||||
PatchInfo &info = index[patch];
|
||||
if(!frustum.IsOutside(info.sphere.Center(), info.sphere.Radius()))
|
||||
draw_used += ram_size;
|
||||
if(!patches.entries[patch].patch)
|
||||
disk_used += ram_size;
|
||||
}
|
||||
}
|
||||
|
||||
vector<Node *>::iterator from;
|
||||
for(from = node->in.begin(); from != node->in.end(); from++) {
|
||||
assert((*from)->visited);
|
||||
vector<Frag> &frags = (*from)->frags;
|
||||
for(unsigned int i = 0; i < frags.size(); i++) {
|
||||
if((*from)->out[i] == node) {
|
||||
vector<unsigned int> &frag = frags[i];
|
||||
for(unsigned int k = 0; k < frag.size(); k++) {
|
||||
unsigned int patch = frag[k];
|
||||
PServer::Entry &entry = patches.entries[patch];
|
||||
extraction_used -= entry.ram_size;
|
||||
PatchInfo &info = index[patch];
|
||||
if(!frustum.IsOutside(info.sphere.Center(), info.sphere.Radius()))
|
||||
draw_used -= entry.ram_size;
|
||||
if(!patches.entries[patch].patch)
|
||||
disk_used -= entry.ram_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
NexusMt::NexusMt() {
|
||||
preload.mt = this;
|
||||
preload.start();
|
||||
|
||||
cerr << "Ramsize: " << ram_max << endl;
|
||||
// SetPrefetchSize(patches.ram_max/2);
|
||||
// prefetch.start();
|
||||
}
|
||||
|
||||
NexusMt::~NexusMt() {
|
||||
|
@ -180,9 +79,9 @@ void NexusMt::Render(Extraction &extraction, DrawContest &contest,
|
|||
|
||||
vector<unsigned int> skipped;
|
||||
|
||||
vector<unsigned int>::iterator i;
|
||||
for(i = extraction.selected.begin(); i != extraction.selected.end(); i++) {
|
||||
Entry &entry = operator[](*i);
|
||||
for(unsigned int i = 0; i < extraction.draw_size; i++) {
|
||||
unsigned int patch = extraction.selected[i];
|
||||
Entry &entry = operator[](patch);
|
||||
vcg::Sphere3f &sphere = entry.sphere;
|
||||
if(extraction.frustum.IsOutside(sphere.Center(), sphere.Radius()))
|
||||
continue;
|
||||
|
@ -190,91 +89,27 @@ void NexusMt::Render(Extraction &extraction, DrawContest &contest,
|
|||
if(stats) stats->ktri += entry.nface;
|
||||
|
||||
if(!entry.patch) {
|
||||
skipped.push_back(*i);
|
||||
skipped.push_back(patch);
|
||||
continue;
|
||||
}
|
||||
|
||||
Draw(*i, contest);
|
||||
Draw(patch, contest);
|
||||
}
|
||||
|
||||
preload.lock.enter();
|
||||
for(i = skipped.begin(); i != skipped.end(); i++) {
|
||||
for(vector<unsigned int>::iterator i = skipped.begin();
|
||||
i != skipped.end(); i++) {
|
||||
GetPatch(*i);
|
||||
Draw(*i, contest);
|
||||
}
|
||||
// Flush(false); //not useful now
|
||||
Flush(false); //in case there are no skipped... :P
|
||||
preload.lock.leave();
|
||||
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
glDisableClientState(GL_COLOR_ARRAY);
|
||||
glDisableClientState(GL_NORMAL_ARRAY);
|
||||
|
||||
//flushing mem
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*void NexusMt::Draw(vector<unsigned int> &cells) {
|
||||
stats.start();
|
||||
|
||||
todraw.clear();
|
||||
vector<QueuePServer::Data> flush;
|
||||
for(unsigned int i = 0; i < cells.size(); i++) {
|
||||
PatchInfo &entry = index[cells[i]];
|
||||
QueuePServer::Data &data = patches.Lookup(cells[i], entry.nvert,
|
||||
entry.nface, flush);
|
||||
todraw.push_back(&data);
|
||||
}
|
||||
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
if(draw_cnt.use_colors)
|
||||
glEnableClientState(GL_COLOR_ARRAY);
|
||||
if(draw_cnt.use_normals)
|
||||
glEnableClientState(GL_NORMAL_ARRAY);
|
||||
//TODO textures and data.
|
||||
|
||||
|
||||
// unsigned int count = draw.size();
|
||||
unsigned int count = todraw.size();
|
||||
while(count > 0 || prefetch.draw.get_count()) {
|
||||
if(todraw.size()) {
|
||||
QueuePServer::Data *data = todraw.back();
|
||||
todraw.pop_back();
|
||||
Draw((unsigned int)(data->patch), *data);
|
||||
} else {
|
||||
// cerr << "Getting message: " << count << endl;
|
||||
pt::message *msg = prefetch.draw.getmessage();
|
||||
QueuePServer::Data *data = (QueuePServer::Data *)(msg->param);
|
||||
if(msg->id == Prefetch::FLUSH) {
|
||||
patches.FlushVbo(*data);
|
||||
// if(data->vbo_element) {
|
||||
//glDeleteBuffersARB(1, &(data->vbo_element));
|
||||
//glDeleteBuffersARB(1, &(data->vbo_array));
|
||||
delete data;
|
||||
delete msg;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(msg->id != Prefetch::DRAW) {
|
||||
cerr << "Unknown message!\n";
|
||||
continue;
|
||||
}
|
||||
|
||||
unsigned int cell = msg->result;
|
||||
Draw(cell, *data);
|
||||
|
||||
delete msg;
|
||||
}
|
||||
count--;
|
||||
}
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
glDisableClientState(GL_COLOR_ARRAY);
|
||||
glDisableClientState(GL_NORMAL_ARRAY);
|
||||
}*/
|
||||
|
||||
void NexusMt::Draw(unsigned int cell, DrawContest &contest) {
|
||||
Entry &entry = operator[](cell);
|
||||
Patch &patch = *(entry.patch);
|
||||
|
@ -400,70 +235,7 @@ void NexusMt::FlushVbo(Entry &entry) {
|
|||
vbo_used -= sizeof(float) * patch.dstart;
|
||||
}
|
||||
|
||||
/*void NexusMt::SetExtractionSize(unsigned int r_size) {
|
||||
extract_cnt.extraction_max = r_size/patches.chunk_size;
|
||||
}
|
||||
|
||||
//void NexusMt::SetMetric(NexusMt::MetricKind kind) {
|
||||
//do nothing at the moment.
|
||||
|
||||
//}
|
||||
|
||||
void NexusMt::SetError(float error) {
|
||||
extract_cnt.target_error = error;
|
||||
}
|
||||
|
||||
//void NexusMt::SetVboSize(unsigned int _vbo_max) {
|
||||
//WORKING patches.vbo_max = _vbo_max;
|
||||
//}
|
||||
|
||||
void NexusMt::SetPrefetchSize(unsigned int size) {
|
||||
//TODO do something reasonable with this.
|
||||
}
|
||||
|
||||
bool NexusMt::SetMode(DrawContest::Mode _mode) {
|
||||
draw_cnt.mode = _mode;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NexusMt::SetComponent(DrawContest::Component c, bool on) {
|
||||
if(c == DrawContest::COLOR && (signature & NXS_COLORS))
|
||||
draw_cnt.use_colors = on;
|
||||
if(c == DrawContest::NORMAL && (signature & NXS_NORMALS_SHORT))
|
||||
draw_cnt.use_normals = on;
|
||||
if(c == DrawContest::TEXTURE && (signature & NXS_TEXTURES_SHORT))
|
||||
draw_cnt.use_textures = on;
|
||||
if(c == DrawContest::DATA && (signature & NXS_DATA32))
|
||||
draw_cnt.use_data = on;
|
||||
|
||||
// unsigned int components = DrawContest::COLOR * use_colors +
|
||||
// DrawContest::NORMAL * use_normals +
|
||||
// DrawContest::TEXTURE * use_textures +
|
||||
// DrawContest::DATA * use_data;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NexusMt::SetComponents(unsigned int mask) {
|
||||
SetComponent(DrawContest::COLOR, (mask & DrawContest::COLOR) != 0);
|
||||
SetComponent(DrawContest::NORMAL, (mask & DrawContest::NORMAL) != 0);
|
||||
SetComponent(DrawContest::TEXTURE, (mask & DrawContest::TEXTURE) != 0);
|
||||
SetComponent(DrawContest::DATA, (mask & DrawContest::DATA) != 0);
|
||||
|
||||
// unsigned int components = mask;
|
||||
|
||||
if( ((mask & DrawContest::COLOR) && !(signature & NXS_COLORS)) ||
|
||||
((mask & DrawContest::NORMAL) && !(signature & NXS_NORMALS_SHORT)) ||
|
||||
((mask & DrawContest::TEXTURE) && !(signature & NXS_TEXTURES_SHORT)) ||
|
||||
((mask & DrawContest::DATA) && !(signature & NXS_DATA32)) )
|
||||
return false;
|
||||
return true;
|
||||
}*/
|
||||
|
||||
//TODO: nodes and fragment sholuld be kept in another way,
|
||||
// so we can save that structure instead of the history!
|
||||
|
||||
|
||||
|
||||
//Kept for historical reasons.
|
||||
/*void NexusMt::ExtractFixed(vector<unsigned int> &selected, float error) {
|
||||
std::vector<Node>::iterator n;
|
||||
for(n = nodes.begin(); n != nodes.end(); n++)
|
||||
|
@ -493,525 +265,3 @@ bool NexusMt::SetComponents(unsigned int mask) {
|
|||
}
|
||||
}
|
||||
} */
|
||||
|
||||
/*void NexusMt::Expand(Node &node, vector<HeapNode> &expand) {
|
||||
if(node.visited) return;
|
||||
cerr << "Expanding\n";
|
||||
for(unsigned int i = 0; i < node.in_size; i++) {
|
||||
Link &link = innodes[node.in + i];
|
||||
Node &parent = nodes[link.node];
|
||||
if(!parent.visited)
|
||||
Expand(parent, expand);
|
||||
}
|
||||
|
||||
for(unsigned int o = 0; o < node.out_size; o++) {
|
||||
Link &link = outnodes[node.out + o];
|
||||
Node &outnode = nodes[link.node];
|
||||
if(outnode.visited) continue;
|
||||
assert(link.error != -1);
|
||||
if(link.error > extract_cnt.target_error) {
|
||||
HeapNode hnode(&outnode, node.error - extract_cnt.target_error);
|
||||
expand.push_back(hnode);
|
||||
push_heap(expand.begin(), expand.end());
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int d_extr = 0;
|
||||
unsigned int d_draw = 0;
|
||||
unsigned int d_disk = 0;
|
||||
Diff(node, d_extr, d_draw, d_disk);
|
||||
extract_cnt.extraction_used += d_extr;
|
||||
extract_cnt.draw_used += d_draw;
|
||||
extract_cnt.disk_used += d_disk;
|
||||
node.visited = true;
|
||||
}
|
||||
|
||||
void NexusMt::Contract(Node &node) {
|
||||
if(!node.visited) return;
|
||||
cerr << "Contracting...\n";
|
||||
for(unsigned int i = 0; i < node.out_size; i++) {
|
||||
Link &link = outnodes[node.out + i];
|
||||
Node &child = nodes[link.node];
|
||||
if(child.visited)
|
||||
Contract(child);
|
||||
}
|
||||
|
||||
unsigned int d_extr = 0;
|
||||
unsigned int d_draw = 0;
|
||||
unsigned int d_disk = 0;
|
||||
Diff(node, d_extr, d_draw, d_disk);
|
||||
extract_cnt.extraction_used -= d_extr;
|
||||
extract_cnt.draw_used -= d_draw;
|
||||
extract_cnt.disk_used -= d_disk;
|
||||
node.visited = false;
|
||||
}
|
||||
|
||||
void NexusMt::GetErrorLink(Node &node, Link &link) {
|
||||
if(link.error != -1) return;
|
||||
for(unsigned int c = link.frag; frags[c].patch != 0xffffffff; c++) {
|
||||
Cell &cell = frags[c];
|
||||
if(cell.error != -1) continue;
|
||||
PatchInfo &info = index[cell.patch];
|
||||
cell.error = extract_cnt.GetError(info);
|
||||
cerr << "Cell.error: " << cell.error << endl;
|
||||
if(link.error < cell.error) link.error = cell.error;
|
||||
}
|
||||
}
|
||||
|
||||
void NexusMt::GetErrorNode(Node &node) {
|
||||
if(node.error != -1) return;
|
||||
for(unsigned int o = 0; o < node.out_size; o++) {
|
||||
Link &link = outnodes[node.out + o];
|
||||
GetErrorLink(node, link);
|
||||
if(node.error < link.error) node.error = link.error;
|
||||
}
|
||||
}
|
||||
|
||||
void NexusMt::NodeExplore(Node &node,
|
||||
vector<HeapNode> &expand,
|
||||
vector<HeapNode> &contract) {
|
||||
assert(node.visited);
|
||||
GetErrorNode(node);
|
||||
|
||||
cerr << "node.error: " << node.error <<
|
||||
" target: " << extract_cnt.target_error << endl;
|
||||
if(node.error > extract_cnt.target_error) {
|
||||
cerr << "Expandible node...\n";
|
||||
for(unsigned int o = 0; o < node.out_size; o++) {
|
||||
Link &link = outnodes[node.out + o];
|
||||
assert(link.error != -1);
|
||||
Node &outnode = nodes[link.node];
|
||||
if(outnode.visited) continue;
|
||||
|
||||
if(link.error > extract_cnt.target_error) {
|
||||
HeapNode hnode(&outnode, node.error - extract_cnt.target_error);
|
||||
expand.push_back(hnode);
|
||||
push_heap(expand.begin(), expand.end());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float max_err = -1;
|
||||
|
||||
bool can_contract = true;
|
||||
for(unsigned int o = 0; o < node.out_size; o++) {
|
||||
Link &link = outnodes[node.out + o];
|
||||
Node &outnode = nodes[link.node];
|
||||
if(node.visited) {
|
||||
can_contract = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(can_contract) {
|
||||
for(unsigned int o = 0; o < node.in_size; o++) {
|
||||
Link &link = innodes[node.in + o];
|
||||
GetErrorLink(node, link);
|
||||
if(max_err < link.error) max_err = link.error;
|
||||
}
|
||||
HeapNode hnode(&node, extract_cnt.target_error - max_err);
|
||||
contract.push_back(hnode);
|
||||
push_heap(contract.begin(), contract.end());
|
||||
}
|
||||
}
|
||||
|
||||
void NexusMt::FrontExplore(vector<HeapNode> &expand,
|
||||
vector<HeapNode> &contract) {
|
||||
|
||||
|
||||
extract_cnt.extraction_used = 0;
|
||||
extract_cnt.draw_used = 0;
|
||||
extract_cnt.disk_used = 0;
|
||||
|
||||
std::vector<Node>::iterator i;
|
||||
for(i = nodes.begin(); i != nodes.end(); i++) {
|
||||
Node &node = *i;
|
||||
if(!node.visited)
|
||||
continue;
|
||||
|
||||
for(int k = 0; k < node.out_size; k++) {
|
||||
Link &link = outnodes[node.out + k];
|
||||
assert(link.node < nodes.size());
|
||||
Node &children = nodes[link.node];
|
||||
if(!children.visited) {
|
||||
cerr << "Exploting child...\n";
|
||||
NodeExplore(node, expand, contract);
|
||||
|
||||
for(unsigned int c = link.frag; frags[c].patch != 0xffffffff; c++) {
|
||||
PServer::Entry &entry = patches.entries[c];
|
||||
if(!entry.patch) extract_cnt.disk_used += entry.disk_size;
|
||||
extract_cnt.extraction_used += entry.ram_size;
|
||||
vcg::Sphere3f &sphere = index[c].sphere;
|
||||
if(!extract_cnt.frustum.IsOutside(sphere.Center(), sphere.Radius()))
|
||||
extract_cnt.draw_used += entry.ram_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NexusMt::Diff(Node &node,
|
||||
unsigned int &d_extr,
|
||||
unsigned int &d_draw,
|
||||
unsigned int &d_disk) {
|
||||
d_extr = 0;
|
||||
d_draw = 0;
|
||||
d_disk = 0;
|
||||
for(unsigned int o = 0; o < node.out_size; o++) {
|
||||
Link &link = outnodes[node.out + o];
|
||||
for(unsigned int c = link.frag; frags[c].patch != 0xffffffff; c++) {
|
||||
PServer::Entry &entry = patches.entries[c];
|
||||
if(!entry.patch) d_disk += entry.disk_size;
|
||||
d_extr += entry.ram_size;
|
||||
Sphere3f &sphere = index[c].sphere;
|
||||
if(!extract_cnt.frustum.IsOutside(sphere.Center(), sphere.Radius()))
|
||||
d_draw += entry.ram_size;
|
||||
}
|
||||
}
|
||||
for(unsigned int o = 0; o < node.in_size; o++) {
|
||||
Link &link = innodes[node.in + o];
|
||||
for(unsigned int c = link.frag; frags[c].patch != 0xffffffff; c++) {
|
||||
PServer::Entry &entry = patches.entries[c];
|
||||
if(!entry.patch) d_disk -= entry.disk_size;
|
||||
d_extr -= entry.ram_size;
|
||||
if(!extract_cnt.frustum.IsOutside(sphere.Center(), sphere.Radius()))
|
||||
d_draw -= entry.ram_size;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
/*void NexusMt::Extract(std::vector<unsigned int> &selected) {
|
||||
extract_cnt.frustum.GetView();
|
||||
|
||||
|
||||
//clear error (no more valid...)
|
||||
for(unsigned int i = 0; i < nodes.size(); i++)
|
||||
nodes[i].error = -1;
|
||||
for(unsigned int i = 0; i < outnodes.size(); i++)
|
||||
outnodes[i].error = -1;
|
||||
for(unsigned int i = 0; i < innodes.size(); i++)
|
||||
innodes[i].error = -1;
|
||||
for(unsigned int i = 0; i < frags.size(); i++)
|
||||
frags[i].error = -1;
|
||||
|
||||
|
||||
std::vector<HeapNode> expand;
|
||||
std::vector<HeapNode> contract;
|
||||
|
||||
if(!nodes[0].visited)
|
||||
nodes[0].visited = true;
|
||||
//explore front
|
||||
FrontExplore(expand, contract);
|
||||
|
||||
bool can_expand = true;
|
||||
bool can_contract = true;
|
||||
float current_error = 1e30;
|
||||
while(can_expand || can_contract) {
|
||||
if(can_expand) {
|
||||
if(!expand.size()) {
|
||||
can_expand = false;
|
||||
continue;
|
||||
}
|
||||
cerr << "trying to expand..\n";
|
||||
pop_heap(expand.begin(), expand.end());
|
||||
HeapNode hnode = expand.back();
|
||||
|
||||
Node *node = hnode.node;
|
||||
// if(node->visited) {
|
||||
//cerr << "Failed cause already visited\n";
|
||||
// expand.pop_back();
|
||||
//continue; //already expanded...
|
||||
// }
|
||||
unsigned int d_extr = 0;
|
||||
unsigned int d_draw = 0;
|
||||
unsigned int d_disk = 0;
|
||||
Diff(*node, d_extr, d_draw, d_disk);
|
||||
|
||||
if(extract_cnt.disk_used + d_disk > extract_cnt.disk_max) {
|
||||
cerr << "Failed cause disk...\n";
|
||||
cerr << "d_disl: " << d_disk << " extract_cnt.disk_max "
|
||||
<< extract_cnt.disk_max << endl;
|
||||
expand.pop_back();
|
||||
continue;
|
||||
}
|
||||
if(extract_cnt.extraction_used + d_extr > extract_cnt.extraction_max ||
|
||||
extract_cnt.draw_used + d_draw > extract_cnt.draw_max) {
|
||||
cerr << "Failed...\n";
|
||||
can_expand = false;
|
||||
push_heap(expand.begin(), expand.end());
|
||||
continue;
|
||||
} else {
|
||||
cerr << "Expanding...\n";
|
||||
current_error = hnode.error;
|
||||
expand.pop_back();
|
||||
Expand(*node, expand);
|
||||
}
|
||||
} else if(can_contract) {
|
||||
if(!contract.size()) {
|
||||
can_contract = false;
|
||||
continue;
|
||||
}
|
||||
pop_heap(contract.begin(), contract.end());
|
||||
HeapNode hnode = contract.back();
|
||||
|
||||
if(hnode.error < extract_cnt.target_error - current_error) {
|
||||
can_contract = false;
|
||||
continue;
|
||||
}
|
||||
Node *node = hnode.node;
|
||||
if(!node->visited) {
|
||||
contract.pop_back();
|
||||
continue;//already contracted
|
||||
}
|
||||
|
||||
unsigned int d_extr = 0;
|
||||
unsigned int d_draw = 0;
|
||||
unsigned int d_disk = 0;
|
||||
Diff(*node, d_extr, d_draw, d_disk);
|
||||
|
||||
if(extract_cnt.disk_used - d_disk > extract_cnt.disk_max) {
|
||||
expand.pop_back();
|
||||
continue;
|
||||
}
|
||||
if(extract_cnt.extraction_used - d_extr < extract_cnt.extraction_max &&
|
||||
extract_cnt.draw_used - d_draw < extract_cnt.draw_max) {
|
||||
can_expand = true;
|
||||
}
|
||||
current_error = hnode.error;
|
||||
contract.pop_back();
|
||||
Contract(*node);
|
||||
}
|
||||
}
|
||||
Select(selected);
|
||||
}*/
|
||||
|
||||
/*void NexusMt::Extract(std::vector<unsigned int> &selected, Policy *policy) {
|
||||
std::vector<Node>::iterator n;
|
||||
for(n = nodes.begin(); n != nodes.end(); n++)
|
||||
(*n).visited = false;
|
||||
|
||||
std::queue<Node *> qnodo;
|
||||
qnodo.push(&nodes[0]);
|
||||
nodes[0].visited = true;
|
||||
|
||||
for( ; !qnodo.empty(); qnodo.pop()) {
|
||||
Node &node = *qnodo.front();
|
||||
|
||||
std::vector<Frag>::iterator i;
|
||||
std::vector<Node *>::iterator on;
|
||||
for(i = node.frags.begin(), on = node.out.begin();
|
||||
i != node.frags.end(); i++, on++) {
|
||||
if((*on)->visited) continue;
|
||||
Frag &frag = (*i);
|
||||
std::vector<unsigned int>::iterator cell;
|
||||
for(cell = frag.begin(); cell != frag.end(); cell++) {
|
||||
if(policy->Expand(*cell, index[*cell]))
|
||||
policy->Visit(*on, qnodo);
|
||||
}
|
||||
}
|
||||
}
|
||||
Select(selected);
|
||||
} */
|
||||
|
||||
/*void NexusMt::Select(vector<unsigned int> &selected) {
|
||||
selected.clear();
|
||||
std::vector<Node>::iterator i;
|
||||
for(i = nodes.begin(); i != nodes.end(); i++) {
|
||||
Node &node = *i;
|
||||
if(!node.visited)
|
||||
continue;
|
||||
|
||||
for(unsigned int o = 0; o < node.out_size; o++) {
|
||||
Link &link = outnodes[node.out + o];
|
||||
for(unsigned int c = link.frag; frags[c].patch != 0xffffffff; c++) {
|
||||
Cell &cell = frags[c];
|
||||
selected.push_back(cell.patch);
|
||||
}
|
||||
}
|
||||
}*/
|
||||
// for(unsigned int i = 0; i < selected.size(); i++) {
|
||||
// cerr << "Sel: " << selected[i] << endl;
|
||||
// }
|
||||
|
||||
/* std::vector<Node *>::iterator n;
|
||||
std::vector<>::iterator f;
|
||||
for(n = node.out.begin(), f = node.frags.begin();
|
||||
n != node.out.end(); n++, f++) {
|
||||
if(!(*n)->visited || (*n)->error == 0) {
|
||||
vector<unsigned int>::iterator c;
|
||||
Frag &frag = (*f);
|
||||
for(c = frag.begin(); c != frag.end(); c++)
|
||||
selected.push_back(*c);
|
||||
}
|
||||
}
|
||||
}*/
|
||||
//}
|
||||
/*bool NexusMt::TestCurrent(Node *node) {
|
||||
if(!visited) return;
|
||||
for(unsigned int i = 0; i < node->out.size(); i++)
|
||||
if(!node->out[k].visited) {
|
||||
node->current = true;
|
||||
return true;
|
||||
}
|
||||
node->current = false;
|
||||
return false;
|
||||
} */
|
||||
|
||||
/*void NexusMt::VisitNode(Node *node, vector<TNode> &heap) {
|
||||
//TestCurrent(*i);
|
||||
|
||||
if(node->visited) return;
|
||||
node->visited = true;
|
||||
|
||||
|
||||
vector<Node *>::iterator i;
|
||||
for(i = node->in.begin(); i != node->in.end(); i++) {
|
||||
VisitNode(*i, heap);
|
||||
}
|
||||
|
||||
for(unsigned int k = 0; k < node->out.size(); k++) {
|
||||
Node *outnode = node->out[k];
|
||||
float max_error = metric->GetError(outnode);
|
||||
for(unsigned int j = 0; j < node->frags[k].size(); j++) {
|
||||
unsigned int patch = node->frags[k][j];
|
||||
float error = metric->GetError(patch);
|
||||
if(max_error < error) max_error = error;
|
||||
visited.push_back(PServer::Item(patch, fabs(error - target_error)));
|
||||
// push_heap(visited.begin(), visited.end());
|
||||
}
|
||||
|
||||
heap.push_back(TNode(outnode, max_error));
|
||||
push_heap(heap.begin(), heap.end());
|
||||
}
|
||||
|
||||
sequence.push_back(node);
|
||||
NodeVisited(node);
|
||||
}*/
|
||||
|
||||
/*void NexusMt::UnvisitNode(Node *node, vector<TNode> &heap) {
|
||||
node->current = false;
|
||||
vector<Node *>::iterator i;
|
||||
for(i = node->in.begin(); i != node->in.end(); i++)
|
||||
if(TestCurrent(*i)) {
|
||||
float error = metric->GetError(*i);
|
||||
heap.push_back(TNode(*i, error));
|
||||
push_heap(heap.begin(), heap.end());
|
||||
}
|
||||
} */
|
||||
|
||||
|
||||
/*void NexusMt::LoadHistory() {
|
||||
//The last update erases everything.
|
||||
assert(history[0].erased.size() == 0);
|
||||
|
||||
//maps cell -> node containing it
|
||||
map<unsigned int, unsigned int> cell_node;
|
||||
//maps node -> Links
|
||||
map<unsigned int, vector<Link> > node_inlinks;
|
||||
map<unsigned int, vector<Link> > node_outlinks;
|
||||
nodes.resize(history.size());
|
||||
|
||||
//building fragments and nodes.
|
||||
unsigned int current_node = 0;
|
||||
vector<Update>::iterator u;
|
||||
for(u = history.begin(); u != history.end(); u++) {
|
||||
|
||||
Node &node = nodes[current_node];
|
||||
node.error = 0;
|
||||
|
||||
//created cells belong to this node, we look also for max error.
|
||||
for(unsigned int i = 0; i < (*u).created.size(); i++) {
|
||||
unsigned int cell = (*u).created[i];
|
||||
if(index[cell].error > node.error)
|
||||
node.error = index[cell].error;
|
||||
cell_node[cell] = current_node;
|
||||
}
|
||||
|
||||
//Every erased cell already belonged to a node.
|
||||
//we record for each node its cells.
|
||||
map<unsigned int, vector<unsigned int> > 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 frag and
|
||||
//put the corresponding cells in it.
|
||||
map<unsigned int, vector<unsigned int> >::iterator e;
|
||||
for(e = node_erased.begin(); e != node_erased.end(); e++) {
|
||||
|
||||
//node.in.push_back(innodes.size());
|
||||
|
||||
unsigned int floor_node = (*e).first;
|
||||
Node &oldnode = nodes[floor_node];
|
||||
|
||||
Link inlink;
|
||||
inlink.node = floor_node;
|
||||
inlink.frag = frags.size();
|
||||
|
||||
Link outlink;
|
||||
outlink.node = current_node;
|
||||
outlink.frag = frags.size();
|
||||
|
||||
float max_err = -1;
|
||||
|
||||
//Fill it with erased cells.
|
||||
vector<unsigned int> &cells = (*e).second;
|
||||
vector<unsigned int>::iterator k;
|
||||
for(k = cells.begin(); k != cells.end(); k++) {
|
||||
Cell cell;
|
||||
cell.patch = (*k);
|
||||
assert(cell.patch < index.size());
|
||||
frags.push_back(cell);
|
||||
if(index[cell.patch].error > max_err)
|
||||
max_err = index[cell.patch].error;
|
||||
}
|
||||
Cell cell;
|
||||
cell.patch = 0xffffffff;
|
||||
cell.error = -1;
|
||||
frags.push_back(cell);
|
||||
|
||||
inlink.error = max_err;
|
||||
outlink.error = max_err;
|
||||
|
||||
//Add the new Frag to the node.
|
||||
|
||||
node_outlinks[floor_node].push_back(outlink);
|
||||
node_outlinks[current_node].push_back(inlink);
|
||||
if(node.error < max_err)
|
||||
node.error = max_err;
|
||||
|
||||
//Update in and out of the nodes.
|
||||
}
|
||||
current_node++;
|
||||
}
|
||||
|
||||
map<unsigned int, vector<Link> >::iterator k;
|
||||
for(k = node_outlinks.begin(); k != node_outlinks.end(); k++) {
|
||||
unsigned int inode = (*k).first;
|
||||
vector<Link> &links = (*k).second;
|
||||
nodes[inode].out = outnodes.size();
|
||||
nodes[inode].out_size = links.size();
|
||||
|
||||
for(unsigned int i = 0; i < links.size(); i++)
|
||||
outnodes.push_back(links[i]);
|
||||
}
|
||||
|
||||
for(k = node_inlinks.begin(); k != node_inlinks.end(); k++) {
|
||||
unsigned int inode = (*k).first;
|
||||
vector<Link> &links = (*k).second;
|
||||
nodes[inode].in = innodes.size();
|
||||
nodes[inode].in_size = links.size();
|
||||
|
||||
for(unsigned int i = 0; i < links.size(); i++)
|
||||
innodes.push_back(links[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void NexusMt::ClearHistory() {
|
||||
nodes.clear();
|
||||
innodes.clear();
|
||||
outnodes.clear();
|
||||
frags.clear();
|
||||
}*/
|
||||
|
|
|
@ -24,6 +24,9 @@
|
|||
History
|
||||
|
||||
$Log: not supported by cvs2svn $
|
||||
Revision 1.28 2005/01/14 15:25:29 ponchio
|
||||
Revolution.
|
||||
|
||||
Revision 1.27 2004/12/15 16:37:55 ponchio
|
||||
Optimizing realtime vis.
|
||||
|
||||
|
@ -250,12 +253,10 @@ int main(int argc, char *argv[]) {
|
|||
bool show_normals = true;
|
||||
bool show_statistics = true;
|
||||
bool extract = true;
|
||||
bool realtime = true;
|
||||
bool stepping = true;
|
||||
bool step = true;
|
||||
|
||||
// NexusMt::MetricKind metric;
|
||||
//DrawContest::Mode mode = DrawContest::SMOOTH;
|
||||
|
||||
//nexus.SetError(error);
|
||||
// nexus.SetMetric(NexusMt::FRUSTUM);
|
||||
if(!nexus.InitGL()) {
|
||||
cerr << "Could not init glew.\n";
|
||||
}
|
||||
|
@ -314,6 +315,9 @@ int main(int argc, char *argv[]) {
|
|||
case SDLK_f: contest.mode = DrawContest::FLAT; break;
|
||||
case SDLK_m: contest.mode = DrawContest::SMOOTH; break;
|
||||
|
||||
case SDLK_o: realtime = !realtime; break;
|
||||
case SDLK_s: step = true; break;
|
||||
|
||||
case SDLK_r:
|
||||
case SDLK_SPACE: rotate = !rotate; break;
|
||||
|
||||
|
@ -411,7 +415,11 @@ int main(int argc, char *argv[]) {
|
|||
if(extract) {
|
||||
extraction.frustum.GetView();
|
||||
extraction.metric->GetView();
|
||||
if(!realtime) {
|
||||
extraction.Extract(&nexus);
|
||||
} else {
|
||||
extraction.Update(&nexus);
|
||||
}
|
||||
}
|
||||
nexus.Render(extraction, contest, &stats);
|
||||
|
||||
|
@ -494,8 +502,6 @@ int main(int argc, char *argv[]) {
|
|||
|
||||
}
|
||||
|
||||
// Clean up
|
||||
|
||||
SDL_Quit();
|
||||
return -1;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include <vector>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <iostream>
|
||||
|
||||
//#include <wrap/strip/tristrip.h>
|
||||
|
@ -198,36 +199,6 @@ void nxs::ComputeNormals(Nexus &nexus) {
|
|||
nexus.borders.SetReadOnly(false);
|
||||
}
|
||||
|
||||
/*void nxs::ComputeTriStrip(unsigned short nfaces, unsigned short *faces,
|
||||
vector<unsigned short> &strip) {
|
||||
|
||||
|
||||
vector<unsigned int> indices;
|
||||
indices.resize(nfaces*3);
|
||||
for(unsigned int i = 0; i < nfaces*3; i++) {
|
||||
indices[i] = faces[i];
|
||||
}
|
||||
vector<unsigned int> restrip;
|
||||
ComputeStrip(indices, restrip);
|
||||
|
||||
unsigned int len = 0;
|
||||
for(unsigned int i = 0; i < restrip.size(); i++) {
|
||||
if(restrip[i] != 0xffffffff) {
|
||||
strip.push_back(restrip[i]);
|
||||
len++;
|
||||
} else {
|
||||
if(i < restrip.size()-1) { //not the last primitive.
|
||||
strip.push_back(restrip[i-1]);
|
||||
//TODO optimize this!
|
||||
if((len%2) == 1) //do not change orientation....
|
||||
strip.push_back(restrip[i-1]);
|
||||
strip.push_back(restrip[i+1]);
|
||||
}
|
||||
len = 0;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
void nxs::ComputeTriStrip(unsigned short nfaces, unsigned short *faces,
|
||||
vector<unsigned short> &strip) {
|
||||
|
||||
|
@ -313,3 +284,119 @@ void nxs::Reorder(unsigned int signature, Patch &patch) {
|
|||
for(int i = 0; i < patch.nv; i++)
|
||||
patch.Vert(remap[i]) = vert[i];
|
||||
}
|
||||
|
||||
//TODO actually use threshold
|
||||
void nxs::Unify(Nexus &nexus, float threshold) {
|
||||
//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);
|
||||
|
||||
unsigned int vcount = 0;
|
||||
map<Point3f, unsigned short> vertices;
|
||||
|
||||
vector<unsigned short> remap;
|
||||
remap.resize(patch.nv);
|
||||
|
||||
for(unsigned int i = 0; i < patch.nv; i++) {
|
||||
Point3f &point = patch.Vert(i);
|
||||
|
||||
if(!vertices.count(point))
|
||||
vertices[point] = vcount++;
|
||||
else
|
||||
duplicated++;
|
||||
|
||||
remap[i] = vertices[point];
|
||||
}
|
||||
assert(vertices.size() <= patch.nv);
|
||||
if(vertices.size() == patch.nv) //no need to unify
|
||||
continue;
|
||||
|
||||
vector<Point3f> newvert;
|
||||
newvert.resize(vertices.size());
|
||||
map<Point3f, unsigned short>::iterator k;
|
||||
for(k = vertices.begin(); k != vertices.end(); k++) {
|
||||
newvert[(*k).second] = (*k).first;
|
||||
}
|
||||
|
||||
vector<unsigned short> 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.VertBegin(), &(newvert[0]), entry.nvert*sizeof(Point3f));
|
||||
memcpy(patch.FaceBegin(), &(newface[0]), entry.nface*3*sizeof(short));
|
||||
|
||||
//testiamo il tutto... TODO remove this of course
|
||||
for(unsigned int i =0; i < patch.nf; i++) {
|
||||
for(int k =0 ; k < 3; k++)
|
||||
if(patch.Face(i)[k] >= patch.nv) {
|
||||
cerr <<" Unify has problems\n";
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
//fix patch borders now
|
||||
set<unsigned int> 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<unsigned int>::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<Link> 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<Link>::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";
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ namespace nxs {
|
|||
void ComputeTriStrip(unsigned short nfaces, unsigned short *faces,
|
||||
std::vector<unsigned short> &strip);
|
||||
void Reorder(unsigned int signature, nxs::Patch &patch);
|
||||
void Unify(Nexus &nexus, float threshold);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -24,6 +24,9 @@
|
|||
History
|
||||
|
||||
$Log: not supported by cvs2svn $
|
||||
Revision 1.12 2005/01/14 15:25:29 ponchio
|
||||
Revolution.
|
||||
|
||||
Revision 1.11 2004/12/13 00:44:48 ponchio
|
||||
Lotsa changes...
|
||||
|
||||
|
@ -75,11 +78,13 @@ Level 0.
|
|||
|
||||
#include <iostream>
|
||||
|
||||
|
||||
#include "nxstypes.h"
|
||||
#include "crude.h"
|
||||
#include "remapping.h"
|
||||
#include "decimate.h"
|
||||
#include "fragment.h"
|
||||
#include "nxsalgo.h"
|
||||
#include "nxsdispatcher.h"
|
||||
#include "watch.h"
|
||||
|
||||
|
@ -488,7 +493,8 @@ void FifthStep(const string &crudefile, const string &output,
|
|||
patch_levels.push_back(0);
|
||||
}
|
||||
nexus.history.updates.push_back(update);
|
||||
nexus.Unify();
|
||||
Unify(nexus, 0.0f);
|
||||
// nexus.Unify();
|
||||
nexus.Flush();
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue