From 38dc81da45b41c903b2a729dfddc06ddb879bd59 Mon Sep 17 00:00:00 2001 From: ponchio Date: Tue, 21 Jan 2025 20:01:15 +0100 Subject: [PATCH 01/33] added ts file format support and mesh scaling --- src/corto | 2 +- src/nxsbuild/main.cpp | 30 +++++- src/nxsbuild/meshloader.h | 1 + src/nxsbuild/meshstream.cpp | 5 + src/nxsbuild/meshstream.h | 1 + src/nxsbuild/nxsbuild.pro | 6 +- src/nxsbuild/objloader.cpp | 6 ++ src/nxsbuild/plyloader.cpp | 32 +++--- src/nxsbuild/stlloader.cpp | 5 +- src/nxsbuild/tsloader.cpp | 197 ++++++++++++++++++++++++++++++++++++ src/nxsbuild/tsloader.h | 27 +++++ 11 files changed, 293 insertions(+), 19 deletions(-) create mode 100644 src/nxsbuild/tsloader.cpp create mode 100644 src/nxsbuild/tsloader.h diff --git a/src/corto b/src/corto index d880519c..8bff7726 160000 --- a/src/corto +++ b/src/corto @@ -1 +1 @@ -Subproject commit d880519c490c88a39d12c31a914b6a687a7019c3 +Subproject commit 8bff7726b4ef0c3edc709db68fb2998cbc988200 diff --git a/src/nxsbuild/main.cpp b/src/nxsbuild/main.cpp index 215b2e1c..1bad472a 100644 --- a/src/nxsbuild/main.cpp +++ b/src/nxsbuild/main.cpp @@ -53,6 +53,7 @@ int main(int argc, char *argv[]) { QString output(""); //output file QString mtl; QString translate; + QString scalate; bool center = false; @@ -118,6 +119,7 @@ int main(int argc, char *argv[]) { opt.addOption('r', "ram", "max ram used (in MegaBytes), default 2000 (WARNING: just an approximation)", &ram_buffer); opt.addOption('w', "workers", "number of workers: default = 4", &n_threads); opt.addOption('T', "origin", "new origin for the model in the format X:Y:Z", &translate); + opt.addOption('W', "scale", "scale vector (after origin subtraction) X:Y:Z", &scalate); opt.addSwitch('G', "center", "set origin in the bounding box center of the input meshes", ¢er); opt.parse(); @@ -144,7 +146,11 @@ int main(int argc, char *argv[]) { } } - if(output == "") output = inputs[0].left(inputs[0].length()-4); + + if(output == "") { + int last = inputs[0].lastIndexOf("."); + output = inputs[0].left(last); + } if(!output.endsWith(".nxs")) output += ".nxs"; @@ -175,6 +181,26 @@ int main(int argc, char *argv[]) { } } + + vcg::Point3d scale(0, 0, 0); + if(!scalate.isEmpty()) { + QStringList p = scalate.split(':'); + if(p.size() != 3) { + cerr << "Malformed scale parameter, expecting X:Y:Z" << endl; + return -1; + } + + bool ok = false; + for(int i = 0; i < 3; i++) { + scale[i] = p[i].toDouble(&ok); + if(!ok) { + cerr << "Malformed scale parameter, expecting X:Y:Z" << endl; + return -1; + } + } + } + + Stream *stream = 0; KDTree *tree = 0; int returncode = 0; @@ -207,6 +233,8 @@ int main(int argc, char *argv[]) { stream->origin = box.Center(); } else stream->origin = origin; + if(!scalate.isEmpty()) + stream->scale = scale; vcg::Point3d &o = stream->origin; if(o[0] != 0.0 || o[1] != 0.0 || o[2] != 0.0) { diff --git a/src/nxsbuild/meshloader.h b/src/nxsbuild/meshloader.h index 97755cc9..ad8752ad 100644 --- a/src/nxsbuild/meshloader.h +++ b/src/nxsbuild/meshloader.h @@ -46,6 +46,7 @@ class MeshLoader { virtual bool hasTextures() { return has_textures; } vcg::Point3d origin = vcg::Point3d(0, 0, 0); + vcg::Point3d scale = vcg::Point3d(1, 1, 1); vcg::Box3d box; std::vector texture_filenames; diff --git a/src/nxsbuild/meshstream.cpp b/src/nxsbuild/meshstream.cpp index 0eb689ad..0076097e 100644 --- a/src/nxsbuild/meshstream.cpp +++ b/src/nxsbuild/meshstream.cpp @@ -26,6 +26,7 @@ for more details. #include "stlloader.h" #include "vcgloadermesh.h" #include "vcgloader.h" +#include "tsloader.h" #include @@ -59,6 +60,9 @@ MeshLoader *loader = nullptr; else if(file.endsWith(".stl")) loader = new STLLoader(file); + else if(file.endsWith(".ts")) + loader = new TsLoader(file); + else loader = new VcgLoader(file); /* else if(file.endsWith(".off")) @@ -95,6 +99,7 @@ vcg::Box3d Stream::getBox(QStringList paths) { void Stream::load(MeshLoader *loader) { loader->setVertexQuantization(vertex_quantization); loader->origin = origin; + loader->scale = scale; loadMesh(loader); has_colors &= loader->hasColors(); has_normals &= loader->hasNormals(); diff --git a/src/nxsbuild/meshstream.h b/src/nxsbuild/meshstream.h index 817e0189..c436d0d8 100644 --- a/src/nxsbuild/meshstream.h +++ b/src/nxsbuild/meshstream.h @@ -39,6 +39,7 @@ class Stream { bool has_textures; std::vector textures; vcg::Point3d origin = vcg::Point3d(0, 0, 0); + vcg::Point3d scale = vcg::Point3d(1, 1, 1); Stream(); virtual ~Stream() {} diff --git a/src/nxsbuild/nxsbuild.pro b/src/nxsbuild/nxsbuild.pro index 491ef5ef..3a862286 100644 --- a/src/nxsbuild/nxsbuild.pro +++ b/src/nxsbuild/nxsbuild.pro @@ -34,7 +34,8 @@ SOURCES += \ objloader.cpp \ tmesh.cpp \ texpyramid.cpp \ - stlloader.cpp + stlloader.cpp \ + tsloader.cpp HEADERS += \ ../../../vcglib/wrap/system/qgetopt.h \ @@ -57,7 +58,8 @@ HEADERS += \ texpyramid.h \ stlloader.h \ vcgloader.h \ - vcgloadermesh.h + vcgloadermesh.h \ + tsloader.h DESTDIR = "../../bin" diff --git a/src/nxsbuild/objloader.cpp b/src/nxsbuild/objloader.cpp index f9893d9a..4d3d8a16 100644 --- a/src/nxsbuild/objloader.cpp +++ b/src/nxsbuild/objloader.cpp @@ -247,6 +247,9 @@ void ObjLoader::cacheVertices() { int n = sscanf(buffer, "v %lf %lf %lf", &p[0], &p[1], &p[2]); if(n != 3) throw QString("error parsing vertex line %1 while caching").arg(buffer); p -= origin; + p[0] *= scale[0]; + p[1] *= scale[1]; + p[2] *= scale[2]; box.Add(p); vertex.v[0] = (float)p[0]; @@ -543,6 +546,9 @@ quint32 ObjLoader::getVertices(quint32 size, Splat *vertices) { if(n != 3) throw QString("error parsing vertex line %1").arg(buffer); p -= origin; + p[0] *= scale[0]; + p[1] *= scale[1]; + p[2] *= scale[2]; box.Add(p); vertex.v[0] = (float)p[0]; diff --git a/src/nxsbuild/plyloader.cpp b/src/nxsbuild/plyloader.cpp index 04971671..7d5e49e8 100644 --- a/src/nxsbuild/plyloader.cpp +++ b/src/nxsbuild/plyloader.cpp @@ -249,13 +249,13 @@ void PlyLoader::cacheVertices() { Vertex &v = vertices[i]; pf.Read((void *)&vertex); if(double_coords) { - v.v[0] = vertex.dv[0] - origin[0]; - v.v[1] = vertex.dv[1] - origin[1]; - v.v[2] = vertex.dv[2] - origin[2]; + v.v[0] = (float)(vertex.dv[0] - origin[0])*scale[0]; + v.v[1] = (float)(vertex.dv[1] - origin[1])*scale[1]; + v.v[2] = (float)(vertex.dv[2] - origin[2])*scale[2]; } else { - v.v[0] = vertex.v[0] - origin[0]; - v.v[1] = vertex.v[1] - origin[1]; - v.v[2] = vertex.v[2] - origin[2]; + v.v[0] = (float)(vertex.v[0] - origin[0])*scale[0]; + v.v[1] = (float)(vertex.v[1] - origin[1])*scale[1]; + v.v[2] = (float)(vertex.v[2] - origin[2])*scale[2]; } if(has_colors) { v.c[0] = vertex.c[0]; @@ -352,17 +352,21 @@ quint32 PlyLoader::getVertices(quint32 size, Splat *splats) { Splat &v = splats[count++]; current_vertex++; + vcg::Point3d p; if(double_coords) { - box.Add(vcg::Point3d(vertex.dv) - origin); - v.v[0] = (float)(vertex.dv[0] - origin[0]); - v.v[1] = (float)(vertex.dv[1] - origin[1]); - v.v[2] = (float)(vertex.dv[2] - origin[2]); + p = vcg::Point3d(vertex.dv); } else { - box.Add(vcg::Point3d(vertex.v[0], vertex.v[1], vertex.v[2]) - origin); - v.v[0] = vertex.v[0] - (float)origin[0]; - v.v[1] = vertex.v[1] - (float)origin[1]; - v.v[2] = vertex.v[2] - (float)origin[2]; + p = vcg::Point3d(vertex.v[0], vertex.v[1], vertex.v[2]); } + p -= origin; + p[0] *= scale[0]; + p[1] *= scale[1]; + p[2] *= scale[2]; + box.Add(p); + v.v[0] = (float)p[0]; + v.v[1] = (float)p[1]; + v.v[2] = (float)p[2]; + if(has_colors) { v.c[0] = vertex.c[0]; v.c[1] = vertex.c[1]; diff --git a/src/nxsbuild/stlloader.cpp b/src/nxsbuild/stlloader.cpp index cb95099f..01a9e3d7 100644 --- a/src/nxsbuild/stlloader.cpp +++ b/src/nxsbuild/stlloader.cpp @@ -62,6 +62,9 @@ quint32 STLLoader::getTrianglesAscii(quint32 size, Triangle *buffer) { if(n != 4) throw QString("Invalid STL file"); d -= origin; + d[0] *= scale[0]; + d[1] *= scale[1]; + d[0] *= scale[2]; box.Add(d); v[0] = (float)(d[0]); @@ -97,7 +100,7 @@ quint32 STLLoader::getTrianglesBinary(quint32 size, Triangle *buffer) { Triangle &tri = buffer[i]; for(int t = 0; t < 3; t++) for(int k = 0; k < 3; k++) - tri.vertices[t].v[k] = pos[t*3 + k] - origin[k]; + tri.vertices[t].v[k] = (pos[t*3 + k] - origin[k])*scale[k]; tri.node = 0; current_triangle++; start += 50; diff --git a/src/nxsbuild/tsloader.cpp b/src/nxsbuild/tsloader.cpp new file mode 100644 index 00000000..bf586ac0 --- /dev/null +++ b/src/nxsbuild/tsloader.cpp @@ -0,0 +1,197 @@ +/* +Nexus + +Copyright(C) 2012 - Federico Ponchio +ISTI - Italian National Research Council - Visual Computing Lab + +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. +*/ +#include "tsloader.h" +#include +#include +#include +#include + + +TsLoader::TsLoader(QString filename): + vertices("cache_tsvertex"), + n_vertices(0), + n_triangles(0), + current_vertex(0) { + + file.setFileName(filename); + if(!file.open(QFile::ReadOnly)) + throw QString("could not open file %1. Error: %2").arg(filename).arg(file.errorString()); +} + +TsLoader::~TsLoader() { + file.close(); +} + + + +void TsLoader::cacheVertices() { + vertices.setElementsPerBlock(1<<20); + file.seek(0); + char buffer[1024]; + char tmp[32]; + int cnt = 0; + + while(1) { + + int s = file.readLine(buffer, 1024); + if (s == -1) { //end of file + std::cout << "Vertices read: " << cnt << std::endl; + break; + + } + if(s == 0) continue; //skip empty lines + buffer[s] = '\0'; //terminating line, readLine wont do this. + + if(strncmp(buffer, "VRTX", 4) == 0 || strncmp(buffer, "PVRTX", 5) == 0) { + + vertices.resize(n_vertices+1); + Vertex &vertex = vertices[n_vertices]; + n_vertices++; + + vcg::Point3d p; + int id; + int n = sscanf(buffer, "%*s %*d %lf %lf %lf", &p[0], &p[1], &p[2]); + if(n != 3) throw QString("error parsing vertex line %1 while caching").arg(buffer); + p -= origin; + p[0] *= scale[0]; + p[1] *= scale[1]; + p[2] *= scale[2]; + box.Add(p); + + vertex.v[0] = (float)p[0]; + vertex.v[1] = (float)p[1]; + vertex.v[2] = (float)p[2]; + + cnt++; + if(quantization) { + quantize(vertex.v[0]); + quantize(vertex.v[1]); + quantize(vertex.v[2]); + } + } + if(strncmp(buffer, "ATOM", 4) == 0) { + int id; + int n = sscanf(buffer, "%*s %*d %d", &id); + if(n != 1) throw QString("error parsing atom line %1 while caching").arg(buffer); + vertices.resize(n_vertices+1); + vertices[n_vertices] = vertices[id -1]; + } + } +} + +void TsLoader::setMaxMemory(quint64 max_memory) { + vertices.setMaxMemory(max_memory); +} + +quint32 TsLoader::getTriangles(quint32 size, Triangle *faces) { + + if (n_triangles == 0) { + cacheVertices(); + } + + char buffer[1024]; + file.seek(current_tri_pos); + + qint32 R = (0x7f << 24); + qint32 G = (0x7f << 16); + qint32 B = (0x7f << 8); + + quint32 count = 0; + qint64 cpos = current_tri_pos; + + while (count < size) { + cpos = file.pos(); + int s = file.readLine(buffer, 1024); + if (s == -1) { //end of file + cpos = file.pos(); + break; + } + + if (strncmp(buffer, "TRGL", 4) != 0) + continue; + + Triangle ¤t = faces[count]; + int f[3]; + int n = sscanf(buffer, "%*s %d %d %d", f, f+1, f+2); + if(n != 3) throw QString("error parsing triangle line %1 while reading").arg(buffer); + + current.vertices[0] = vertices[f[0]-1]; + current.vertices[1] = vertices[f[1]-1]; + current.vertices[2] = vertices[f[2]-1]; + + current.node = 0; + count++; + n_triangles++; + cpos = file.pos(); + } + + current_tri_pos = cpos; + + if (count == 0) + std::cout << "faces read: " << n_triangles << std::endl; + + return count; +} + + +/* + * Seeks all verteces in whole file + */ +quint32 TsLoader::getVertices(quint32 size, Splat *vertices) { + char buffer[1024]; + + quint32 count = 0; + while(count < size) { + int s = file.readLine(buffer, 1024); + if(s == -1) //end of file + return count; + + if(strncmp(buffer, "VRTX", 4) != 0 || strncmp(buffer, "PVRTX", 5) != 0) + continue; + + + buffer[s] = '\0'; //terminating line, readLine wont do this. + + + Splat &vertex = vertices[count]; + + vcg::Point3d p; + int n = sscanf(buffer, "%*s %*d %lf %lf %lf", &p[0], &p[1], &p[2]); + if(n != 3) throw QString("error parsing vertex line %1").arg(buffer); + + p -= origin; + p[0] *= scale[0]; + p[1] *= scale[1]; + p[2] *= scale[2]; + box.Add(p); + + vertex.v[0] = (float)p[0]; + vertex.v[1] = (float)p[1]; + vertex.v[2] = (float)p[2]; + + if(quantization) { + quantize(vertex.v[0]); + quantize(vertex.v[1]); + quantize(vertex.v[2]); + } + n_vertices++; + current_vertex++; + count++; + } + return count; +} diff --git a/src/nxsbuild/tsloader.h b/src/nxsbuild/tsloader.h new file mode 100644 index 00000000..fcba5878 --- /dev/null +++ b/src/nxsbuild/tsloader.h @@ -0,0 +1,27 @@ +#ifndef TSLOADER_H +#define TSLOADER_H + +#include "meshloader.h" + +class TsLoader: public MeshLoader { +public: + TsLoader(QString file); + ~TsLoader(); + + void setMaxMemory(quint64 max_memory); + quint32 getTriangles(quint32 size, Triangle *buffer); + quint32 getVertices(quint32 size, Splat *vertex); + +private: + + void cacheVertices(); + + QFile file; + VirtualArray vertices; + quint64 n_vertices; + quint64 n_triangles; + quint64 current_triangle; + quint64 current_vertex; + qint64 current_tri_pos = 0; +}; +#endif // TSLOADER_H From 9248d1f5538772971055dcd29d0c58908ff56df4 Mon Sep 17 00:00:00 2001 From: ponchio Date: Tue, 21 Jan 2025 23:34:17 +0100 Subject: [PATCH 02/33] fixed CMakeLists.txt --- src/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f3a7d22c..cfb4eaf5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -21,6 +21,7 @@ set(HEADERS nxsbuild/texpyramid.h nxsbuild/tmesh.h nxsbuild/tsploader.h + nxsbuild/tsloader.h nxsbuild/vcgloader.h nxsedit/extractor.h nxszip/bitstream.h @@ -47,6 +48,7 @@ set(SOURCES nxsbuild/texpyramid.cpp nxsbuild/tmesh.cpp nxsbuild/tsploader.cpp + nxsbuild/tploader.cpp nxsedit/extractor.cpp nxszip/abitstream.cpp nxszip/atunstall.cpp From 7f88480d92262a3f78d101cb7ecf3a2fe3b14584 Mon Sep 17 00:00:00 2001 From: ponchio Date: Tue, 21 Jan 2025 23:36:44 +0100 Subject: [PATCH 03/33] typo. --- src/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index cfb4eaf5..24846be7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -48,7 +48,7 @@ set(SOURCES nxsbuild/texpyramid.cpp nxsbuild/tmesh.cpp nxsbuild/tsploader.cpp - nxsbuild/tploader.cpp + nxsbuild/tsloader.cpp nxsedit/extractor.cpp nxszip/abitstream.cpp nxszip/atunstall.cpp From 390eca2370abf5256b62d7e5771dc81c35478a95 Mon Sep 17 00:00:00 2001 From: Federico Ponchio Date: Mon, 3 Feb 2025 17:25:30 +0100 Subject: [PATCH 04/33] Update Windows.yml (release) --- .github/workflows/Windows.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/Windows.yml b/.github/workflows/Windows.yml index c3858d26..3d438e1a 100644 --- a/.github/workflows/Windows.yml +++ b/.github/workflows/Windows.yml @@ -30,7 +30,7 @@ jobs: mkdir build mkdir install cd build - cmake -GNinja -DCMAKE_INSTALL_PREFIX=../install .. + cmake -DCMAKE_BUILD_TYPE=Release -GNinja -DCMAKE_INSTALL_PREFIX=../install .. ninja ninja install - name: Deploy From 7965ac37381ad66943fb4788b273ca03d5956039 Mon Sep 17 00:00:00 2001 From: Federico Ponchio Date: Mon, 3 Feb 2025 17:35:44 +0100 Subject: [PATCH 05/33] Update Windows.yml to v4 --- .github/workflows/Windows.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/Windows.yml b/.github/workflows/Windows.yml index 3d438e1a..47478f41 100644 --- a/.github/workflows/Windows.yml +++ b/.github/workflows/Windows.yml @@ -9,13 +9,13 @@ jobs: runs-on: windows-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: recursive - name: Setup MSVC uses: ilammy/msvc-dev-cmd@v1 - name: Install Qt - uses: jurplel/install-qt-action@v3 + uses: jurplel/install-qt-action@v4 with: cache: true version: '5.15.2' @@ -41,7 +41,7 @@ jobs: cd .. mv bin nexus_portable - name: Uploading Nexus Portable - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: nexus_windows_portable path: install/nexus_portable From 848f7a6a49460e94d0af627cb47ca99c7362b3d8 Mon Sep 17 00:00:00 2001 From: ponchio Date: Mon, 3 Feb 2025 17:42:48 +0100 Subject: [PATCH 06/33] fixed log2 redefinition for ms --- src/common/renderer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/renderer.cpp b/src/common/renderer.cpp index 840cf321..9defcb78 100644 --- a/src/common/renderer.cpp +++ b/src/common/renderer.cpp @@ -28,11 +28,11 @@ for more details. #ifdef WIN32 //microsoft compiler does not provide it. -double log2( double n ) +/*double log2( double n ) { // log(n)/log(2) is log2. return log( n )* 3.32192809; -} +}*/jurplel/install-qt-action@v3 #endif extern int current_texture; From 989e907d9c520de8064051e797689435a7a57df1 Mon Sep 17 00:00:00 2001 From: ponchio Date: Mon, 3 Feb 2025 17:50:26 +0100 Subject: [PATCH 07/33] removed spurius text --- src/common/renderer.cpp | 2 +- src/corto | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/renderer.cpp b/src/common/renderer.cpp index 9defcb78..4625dc67 100644 --- a/src/common/renderer.cpp +++ b/src/common/renderer.cpp @@ -32,7 +32,7 @@ for more details. { // log(n)/log(2) is log2. return log( n )* 3.32192809; -}*/jurplel/install-qt-action@v3 +}*/ #endif extern int current_texture; diff --git a/src/corto b/src/corto index 8bff7726..d880519c 160000 --- a/src/corto +++ b/src/corto @@ -1 +1 @@ -Subproject commit 8bff7726b4ef0c3edc709db68fb2998cbc988200 +Subproject commit d880519c490c88a39d12c31a914b6a687a7019c3 From 2e022cbc315fc52072b515f4886d8ae9c2dc0a32 Mon Sep 17 00:00:00 2001 From: ponchio Date: Thu, 6 Feb 2025 14:57:14 +0100 Subject: [PATCH 08/33] looking for mtllib in .obj --- src/nxsbuild/objloader.cpp | 24 ++++++++++++++++++------ src/nxsbuild/objloader.h | 2 +- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/nxsbuild/objloader.cpp b/src/nxsbuild/objloader.cpp index 4d3d8a16..fe43c2cd 100644 --- a/src/nxsbuild/objloader.cpp +++ b/src/nxsbuild/objloader.cpp @@ -40,7 +40,7 @@ ObjLoader::ObjLoader(QString filename, QString _mtl): if(!file.open(QFile::ReadOnly)) throw QString("could not open file %1. Error: %2").arg(filename).arg(file.errorString()); - readMTL(); + readMTL(file); } ObjLoader::~ObjLoader() { @@ -83,20 +83,32 @@ void ObjLoader::cacheTextureUV() { } } -void ObjLoader::readMTL() { +void ObjLoader::readMTL(QFile &file) { - quint64 pos = file.pos(); char buffer[1024]; if (!mtl.isNull()) { if(!QFileInfo::exists(mtl)) throw QString("Could not find .mtl file: %1").arg(mtl); - } else { + } + + if(mtl.isNull()) { //look for mtllib + while (1) { + if(file.readLine(buffer, 1000) == -1) + break; + if(strncmp(buffer, "mtllib", 6) != 0) + continue; + + QString m = QString(buffer).mid(7).trimmed(); + if (QFileInfo::exists(m)) + mtl = m; + break; + } + } + if(mtl.isNull()) { //assume the name is the same QString fname = file.fileName(); QFileInfo info = QFileInfo(fname); - - //assuming mtl base file name the same as obj mtl = info.path() + "/" + info.completeBaseName() + ".mtl"; } diff --git a/src/nxsbuild/objloader.h b/src/nxsbuild/objloader.h index 6c70b33c..5e778b92 100644 --- a/src/nxsbuild/objloader.h +++ b/src/nxsbuild/objloader.h @@ -37,7 +37,7 @@ class ObjLoader: public MeshLoader { private: - void readMTL(); + void readMTL(QFile &file); //obj file passed to search for mtllib void cacheTextureUV(); void cacheVertices(); From baa57518fe00a766859fb84ee96aadfbceffe4e8 Mon Sep 17 00:00:00 2001 From: ponchio Date: Thu, 13 Feb 2025 16:44:35 +0100 Subject: [PATCH 09/33] added colormap for .tsp files properties. --- src/nxsbuild/colormap.cpp | 77 ++++++++++++++++++++++++++++++++++ src/nxsbuild/colormap.h | 34 +++++++++++++++ src/nxsbuild/main.cpp | 12 ++++++ src/nxsbuild/meshstream.cpp | 8 +++- src/nxsbuild/meshstream.h | 1 + src/nxsbuild/nxsbuild.pro | 2 + src/nxsbuild/tsloader.cpp | 83 ++++++++++++++++++++++++++++++++++++- src/nxsbuild/tsloader.h | 5 +++ 8 files changed, 218 insertions(+), 4 deletions(-) create mode 100644 src/nxsbuild/colormap.cpp create mode 100644 src/nxsbuild/colormap.h diff --git a/src/nxsbuild/colormap.cpp b/src/nxsbuild/colormap.cpp new file mode 100644 index 00000000..3b10914b --- /dev/null +++ b/src/nxsbuild/colormap.cpp @@ -0,0 +1,77 @@ +#include "colormap.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#define clamp(v, m, M) std::min(M, std::max(m, v)); + +using namespace std; + +bool Colormap::loadFromCSV(const string& filename) { + ifstream file(filename); + if (!file.is_open()) return false; + + colormap.clear(); + string line; + while (getline(file, line)) { + stringstream ss(line); + Color color; + for (float& c : color) { + string value; + if (!getline(ss, value, ',')) return false; + c = stof(value); + } + colormap.push_back(color); + } + return !colormap.empty(); +} + +bool Colormap::setColormap(const string& name) { + static const std::map> predefinedColormaps = { + {"viridis", {{0.267f, 0.004f, 0.329f, 1.0f}, {0.283f, 0.141f, 0.458f, 1.0f}, {0.254f, 0.265f, 0.530f, 1.0f}, {0.207f, 0.372f, 0.553f, 1.0f}, {0.164f, 0.471f, 0.558f, 1.0f}, {0.128f, 0.567f, 0.551f, 1.0f}, {0.135f, 0.659f, 0.518f, 1.0f}, {0.267f, 0.850f, 0.441f, 1.0f}, {0.477f, 1.000f, 0.318f, 1.0f}}}, + {"spectral", {{0.619f, 0.003f, 0.258f, 1.0f}, {0.835f, 0.243f, 0.310f, 1.0f}, {0.957f, 0.427f, 0.263f, 1.0f}, {0.992f, 0.682f, 0.380f, 1.0f}, {1.000f, 0.878f, 0.545f, 1.0f}, {0.878f, 1.000f, 0.392f, 1.0f}, {0.671f, 0.866f, 0.204f, 1.0f}, {0.471f, 0.671f, 0.059f, 1.0f}, {0.267f, 0.459f, 0.000f, 1.0f}}}, + {"plasma", {{0.050f, 0.029f, 0.529f, 1.0f}, {0.294f, 0.001f, 0.635f, 1.0f}, {0.492f, 0.012f, 0.603f, 1.0f}, {0.676f, 0.209f, 0.486f, 1.0f}, {0.830f, 0.369f, 0.326f, 1.0f}, {0.959f, 0.566f, 0.152f, 1.0f}, {0.987f, 0.788f, 0.071f, 1.0f}, {0.941f, 0.975f, 0.320f, 1.0f}}} + }; + + auto it = predefinedColormaps.find(name); + if (it == predefinedColormaps.end()) { + return false; + } + colormap = it->second; + cout << "Returning true" << endl; + return true; +} + +Colormap::Colorb Colormap::map(float value) const { + value = clamp((value - minValue) / (maxValue - minValue), 0.0f, 1.0f); + float scaled = value * (colormap.size() - 1); + size_t idx = static_cast(scaled); + float t = scaled - idx; + + Color c; + if (idx >= colormap.size() - 1) + c = colormap.back(); + else + c = interpolate(colormap[idx], colormap[idx + 1], t); + + Colorb cb; + for(int i = 0; i < 4; i++) { + cb[i] = static_cast(floor(c[i]*255)); + } + return cb; +} + + +Colormap::Color Colormap::interpolate(const Color& c1, const Color& c2, float t) { + Color result; + for (size_t i = 0; i < 4; ++i) { + result[i] = c1[i] * (1.0f - t) + c2[i] * t; + } + return result; +} diff --git a/src/nxsbuild/colormap.h b/src/nxsbuild/colormap.h new file mode 100644 index 00000000..3d5584d2 --- /dev/null +++ b/src/nxsbuild/colormap.h @@ -0,0 +1,34 @@ +#ifndef COLORMAP_H +#define COLORMAP_H + + +#include +#include +#include +#include +#include +#include +#include + + +class Colormap { +public: + using Color = std::array; // RGBA + using Colorb = std::array; + + float minValue, maxValue; + std::vector colormap; + + Colormap(float min = 0.0f, float max = 1.0f) : minValue(min), maxValue(max) {} + + void setColormap(const std::vector& colors) { colormap = colors; } + bool setColormap(const std::string& name); + bool loadFromCSV(const std::string& filename); + + Colorb map(float value) const; + + static Color interpolate(const Color& c1, const Color& c2, float t); +}; + + +#endif // COLORMAP_H diff --git a/src/nxsbuild/main.cpp b/src/nxsbuild/main.cpp index 1bad472a..d8069027 100644 --- a/src/nxsbuild/main.cpp +++ b/src/nxsbuild/main.cpp @@ -54,6 +54,7 @@ int main(int argc, char *argv[]) { QString mtl; QString translate; QString scalate; + QString colormap; bool center = false; @@ -121,6 +122,9 @@ int main(int argc, char *argv[]) { opt.addOption('T', "origin", "new origin for the model in the format X:Y:Z", &translate); opt.addOption('W', "scale", "scale vector (after origin subtraction) X:Y:Z", &scalate); opt.addSwitch('G', "center", "set origin in the bounding box center of the input meshes", ¢er); + + //ts specific options + opt.addOption('K', "colormap", "for .ts files: property:colormap such as temperature:viridis (or plasma, spectral)", &colormap); opt.parse(); //Check parameters are correct @@ -223,6 +227,14 @@ int main(int argc, char *argv[]) { else stream = new StreamSoup("cache_stream"); + if(!colormap.isNull()) { + stream->colormap = colormap.split(":"); + if(stream->colormap.size() != 2) { + throw QString("Format for color map is 'property:colormap'"); + } + colors = true; + } + stream->setVertexQuantization(vertex_quantization); stream->setMaxMemory(max_memory); if(center) { diff --git a/src/nxsbuild/meshstream.cpp b/src/nxsbuild/meshstream.cpp index 0076097e..16e0af77 100644 --- a/src/nxsbuild/meshstream.cpp +++ b/src/nxsbuild/meshstream.cpp @@ -60,8 +60,12 @@ MeshLoader *loader = nullptr; else if(file.endsWith(".stl")) loader = new STLLoader(file); - else if(file.endsWith(".ts")) - loader = new TsLoader(file); + else if(file.endsWith(".ts")) { + TsLoader *ts = new TsLoader(file); + loader = ts; + if(!colormap.isEmpty()) + ts->useColormapFor(colormap[0], colormap[1]); + } else loader = new VcgLoader(file); diff --git a/src/nxsbuild/meshstream.h b/src/nxsbuild/meshstream.h index c436d0d8..3188bb80 100644 --- a/src/nxsbuild/meshstream.h +++ b/src/nxsbuild/meshstream.h @@ -40,6 +40,7 @@ class Stream { std::vector textures; vcg::Point3d origin = vcg::Point3d(0, 0, 0); vcg::Point3d scale = vcg::Point3d(1, 1, 1); + QStringList colormap; //used to convert a value into a color, .ts only Stream(); virtual ~Stream() {} diff --git a/src/nxsbuild/nxsbuild.pro b/src/nxsbuild/nxsbuild.pro index 3a862286..a8d8ad52 100644 --- a/src/nxsbuild/nxsbuild.pro +++ b/src/nxsbuild/nxsbuild.pro @@ -23,6 +23,7 @@ SOURCES += \ ../../../vcglib/wrap/ply/plylib.cpp \ ../common/virtualarray.cpp \ ../common/cone.cpp \ + colormap.cpp \ main.cpp \ meshstream.cpp \ meshloader.cpp \ @@ -43,6 +44,7 @@ HEADERS += \ ../common/signature.h \ ../common/cone.h \ ../common/virtualarray.h \ + colormap.h \ meshstream.h \ meshloader.h \ plyloader.h \ diff --git a/src/nxsbuild/tsloader.cpp b/src/nxsbuild/tsloader.cpp index bf586ac0..276afa07 100644 --- a/src/nxsbuild/tsloader.cpp +++ b/src/nxsbuild/tsloader.cpp @@ -21,6 +21,7 @@ for more details. #include #include +using namespace std; TsLoader::TsLoader(QString filename): vertices("cache_tsvertex"), @@ -31,6 +32,68 @@ TsLoader::TsLoader(QString filename): file.setFileName(filename); if(!file.open(QFile::ReadOnly)) throw QString("could not open file %1. Error: %2").arg(filename).arg(file.errorString()); + + //search for properties, here an example: + /* + PROPERTY_CLASS_HEADER temperature { + kind: Real Number + unit: unitless + pclip: 99 + low_clip: 9 + high_clip: 143.54482 + } + */ +} + +bool TsLoader::useColormapFor(const QString &_property, const QString &palette) { + property = _property; + + bool inside_property = false; + while(1) { + QString line = file.readLine(1024).trimmed(); + if(line.isEmpty()) + break; + if(line.startsWith(QString("PROPERTY_CLASSES"))) { + QStringList props = line.split(" "); + int pos = props.indexOf(property); + if(pos == -1) { + throw QString("Could not find property: %1").arg(property); + } + property_position = pos-1; + } + + if(line.startsWith(QString("PROPERTY_CLASS_HEADER %1").arg(property))) { + inside_property = true; + } + + if(inside_property) { + if(line.startsWith("low_clip")) { + bool ok; + colormap.minValue = line.split(" ").back().toFloat(&ok); + if(!ok) throw QString("Proble parsing line: %1").arg(line); + } + if(line.startsWith("high_clip")) { + bool ok; + colormap.maxValue = line.split(" ").back().toFloat(&ok); + if(!ok) throw QString("Proble parsing line: %1").arg(line); + } + if(line[0] == "}") { + break; + } + } + if(line.startsWith("TFACE")) { + break; + } + } + file.seek(0); + + bool found = colormap.setColormap(palette.toStdString()); + + if(!found) { + throw QString("Could not find colormap: %1 (values accepted are plasma, spectral and viridis)"); + return false; + } + return true; } TsLoader::~TsLoader() { @@ -65,8 +128,17 @@ void TsLoader::cacheVertices() { vcg::Point3d p; int id; - int n = sscanf(buffer, "%*s %*d %lf %lf %lf", &p[0], &p[1], &p[2]); - if(n != 3) throw QString("error parsing vertex line %1 while caching").arg(buffer); + QString line(buffer); + QStringList parts = line.trimmed().split(" "); + if(parts.size() < 5 || parts.size() < property_position) + throw QString("error parsing vertex line %1 while caching").arg(buffer); + p[0] = parts[2].toFloat(); + p[1] = parts[3].toFloat(); + p[2] = parts[4].toFloat(); + + //std::string pattern = "%*s %*d %lf %lf %lf"; + //int n = sscanf(buffer, pattern.c_str(), &p[0], &p[1], &p[2]); + p -= origin; p[0] *= scale[0]; p[1] *= scale[1]; @@ -77,6 +149,13 @@ void TsLoader::cacheVertices() { vertex.v[1] = (float)p[1]; vertex.v[2] = (float)p[2]; + if(property_position >= 0) { + float value = parts[5 + property_position].toFloat(); + std::array c = colormap.map(value); + for(int k = 0; k < 4; k++) + vertex.c[k] = c[k]; + } + cnt++; if(quantization) { quantize(vertex.v[0]); diff --git a/src/nxsbuild/tsloader.h b/src/nxsbuild/tsloader.h index fcba5878..f4fa7c25 100644 --- a/src/nxsbuild/tsloader.h +++ b/src/nxsbuild/tsloader.h @@ -2,12 +2,14 @@ #define TSLOADER_H #include "meshloader.h" +#include "colormap.h" class TsLoader: public MeshLoader { public: TsLoader(QString file); ~TsLoader(); + bool useColormapFor(const QString &_property, const QString &palette); void setMaxMemory(quint64 max_memory); quint32 getTriangles(quint32 size, Triangle *buffer); quint32 getVertices(quint32 size, Splat *vertex); @@ -23,5 +25,8 @@ class TsLoader: public MeshLoader { quint64 current_triangle; quint64 current_vertex; qint64 current_tri_pos = 0; + QString property; + int property_position = -1; + Colormap colormap; }; #endif // TSLOADER_H From b807a688df9dd30bfaa133fe93d3721cc5d0e961 Mon Sep 17 00:00:00 2001 From: ponchio Date: Thu, 13 Feb 2025 16:51:27 +0100 Subject: [PATCH 10/33] missing colormap added to cmake --- src/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 24846be7..47c3b8dc 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -20,6 +20,7 @@ set(HEADERS nxsbuild/stlloader.h nxsbuild/texpyramid.h nxsbuild/tmesh.h + nxsbuild/colormap.h nxsbuild/tsploader.h nxsbuild/tsloader.h nxsbuild/vcgloader.h @@ -47,6 +48,7 @@ set(SOURCES nxsbuild/stlloader.cpp nxsbuild/texpyramid.cpp nxsbuild/tmesh.cpp + nxsbuild/colormap.cpp nxsbuild/tsploader.cpp nxsbuild/tsloader.cpp nxsedit/extractor.cpp From 1fa3022e12c37bfa31e5539edd598db647244e40 Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Fri, 28 Mar 2025 09:16:36 +0100 Subject: [PATCH 11/33] new build workflow --- .github/workflows/BuildNexus.yml | 89 ++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 .github/workflows/BuildNexus.yml diff --git a/.github/workflows/BuildNexus.yml b/.github/workflows/BuildNexus.yml new file mode 100644 index 00000000..c0928442 --- /dev/null +++ b/.github/workflows/BuildNexus.yml @@ -0,0 +1,89 @@ +name: BuildNexus + +on: + [push, pull_request] + +jobs: + build_nexus: + name: Build Nexus + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: ['ubuntu-22.04', 'ubuntu-22.04-arm', 'macos-13', 'macos-latest', 'windows-latest'] + + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + - name: Setup MSVC + uses: ilammy/msvc-dev-cmd@v1 + - name: Install Qt + uses: jurplel/install-qt-action@v4 + if: ${{ matrix.os != 'macos-latest' && matrix.os != 'ubuntu-22.04-arm' }} + with: + cache: true + version: '5.15.2' + - name: Setup Env variables + shell: bash + id: envs + run: | + echo "name=VCINSTALLDIR::C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC" >> $GITHUB_ENV + ARCH=$(uname -m) + echo "arch=$ARCH" >> $GITHUB_OUTPUT + - name: Clone VCG + shell: bash + run: | + cd .. + git clone --single-branch --branch devel https://github.com/cnr-isti-vclab/vcglib + - name: Install dependencies Linux + if: ${{ runner.os == 'Linux' }} + run: | + sudo apt-get install -y mesa-common-dev libglu1-mesa-dev libglew-dev ninja-build + - name: Install dependencies Linux arm + if: ${{ matrix.os == 'ubuntu-22.04-arm' }} + run: | + sudo apt-get install -y qtbase5-dev qt5-qmake + - name: Install dependencies macOS arm + if: ${{ matrix.os == 'macos-latest' }} + run: | + brew install qt@5 + echo Qt5_DIR=$(brew --prefix qt@5) >> $GITHUB_ENV + - name: Configure and Build + shell: bash + run: | + mkdir build + mkdir install + cd build + cmake -DCMAKE_BUILD_TYPE=Release -GNinja -DCMAKE_INSTALL_PREFIX=../install .. + ninja + ninja install + - name: Deploy + shell: bash + if: ${{ runner.os == 'macOS' }} + run: | + cd install + macdeployqt nxsview.app -executable=nxsview.app/Contents/MacOS/nxsbuild -executable=nxsview.app/Contents/MacOS/nxscompress -executable=nxsview.app/Contents/MacOS/nxsedit + rm -r bin + rm -r lib + tar -cvzf nxsview-macos-${{ steps.envs.outputs.arch }}.tar.gz * + - name: Deploy + shell: bash + if: ${{ runner.os == 'Windows' }} + run: | + cd install/bin + windeployqt nxsview.exe + cd .. + mv bin nexus_portable-${{ steps.envs.outputs.arch }} + - name: Upload nxsview Portable + uses: actions/upload-artifact@v4 + if: ${{ runner.os == 'macOS' }} + with: + name: nxsview_macos_portable_${{ steps.envs.outputs.arch }} + path: install/nxsview-macos-${{ steps.envs.outputs.arch }}.tar.gz + - name: Uploading Nexus Portable + uses: actions/upload-artifact@v4 + if: ${{ runner.os == 'Windows' }} + with: + name: nexus_windows_portable_${{ steps.envs.outputs.arch }} + path: install/nexus_portable-${{ steps.envs.outputs.arch }} \ No newline at end of file From bd60e6fa3fd0eec75a4d36e98681eb2743986120 Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Fri, 28 Mar 2025 09:25:45 +0100 Subject: [PATCH 12/33] fix cmake qt --- CMakeLists.txt | 2 +- src/nxsview/CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3a484b15..c41d0c7d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,7 +13,7 @@ set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) -find_package(Qt5Widgets) +find_package(Qt5 COMPONENTS Widgets REQUIRED) if (BUILD_NXS_VIEW) include(${CMAKE_CURRENT_SOURCE_DIR}/src/cmake/tools.cmake) diff --git a/src/nxsview/CMakeLists.txt b/src/nxsview/CMakeLists.txt index d011b16c..46fb11bb 100644 --- a/src/nxsview/CMakeLists.txt +++ b/src/nxsview/CMakeLists.txt @@ -46,7 +46,7 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOUIC ON) -find_package(Qt5OpenGL) +find_package(Qt5 COMPONENTS OpenGL REQUIRED) include(${CMAKE_CURRENT_SOURCE_DIR}/glew.cmake) From 6866f48c07ca803c549f06c79139d1d1a207f264 Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Fri, 28 Mar 2025 09:38:27 +0100 Subject: [PATCH 13/33] remove old workflows --- .github/workflows/BuildNexus.yml | 1 + .github/workflows/Linux.yml | 31 --------------------- .github/workflows/MacOS.yml | 45 ------------------------------ .github/workflows/Windows.yml | 47 -------------------------------- 4 files changed, 1 insertion(+), 123 deletions(-) delete mode 100644 .github/workflows/Linux.yml delete mode 100644 .github/workflows/MacOS.yml delete mode 100644 .github/workflows/Windows.yml diff --git a/.github/workflows/BuildNexus.yml b/.github/workflows/BuildNexus.yml index c0928442..d3a320f2 100644 --- a/.github/workflows/BuildNexus.yml +++ b/.github/workflows/BuildNexus.yml @@ -49,6 +49,7 @@ jobs: run: | brew install qt@5 echo Qt5_DIR=$(brew --prefix qt@5) >> $GITHUB_ENV + echo $(brew --prefix qt@5)/bin >> $GITHUB_PATH - name: Configure and Build shell: bash run: | diff --git a/.github/workflows/Linux.yml b/.github/workflows/Linux.yml deleted file mode 100644 index 7283f610..00000000 --- a/.github/workflows/Linux.yml +++ /dev/null @@ -1,31 +0,0 @@ -name: Linux - -on: - [push, pull_request] - -jobs: - linux_build: - name: Build Nexus (Linux) - runs-on: ubuntu-20.04 #in order to deploy, need to use oldest supported version - - steps: - - uses: actions/checkout@v3 - with: - submodules: recursive - - name: Install Qt - uses: jurplel/install-qt-action@v3 - with: - cache: true - version: '5.15.2' - - name: Install dependencies - run: | - sudo apt-get install -y mesa-common-dev libglu1-mesa-dev libglew-dev - cd .. - git clone --single-branch --branch devel https://github.com/cnr-isti-vclab/vcglib - - name: Configure and Build - shell: bash - run: | - mkdir build - cd build - cmake .. - make -j4 diff --git a/.github/workflows/MacOS.yml b/.github/workflows/MacOS.yml deleted file mode 100644 index 1785b4f7..00000000 --- a/.github/workflows/MacOS.yml +++ /dev/null @@ -1,45 +0,0 @@ -name: MacOS - -on: - [push, pull_request] - -jobs: - macos_build: - name: Build Nexus (MacOS) - runs-on: macos-latest - - steps: - - uses: actions/checkout@v3 - with: - submodules: recursive - - name: Install Qt - uses: jurplel/install-qt-action@v3 - with: - cache: true - version: '5.15.2' - - name: Install dependencies - run: | - cd .. - git clone --single-branch --branch devel https://github.com/cnr-isti-vclab/vcglib - - name: Configure and Build - shell: bash - run: | - mkdir build - mkdir install - cd build - cmake -DCMAKE_INSTALL_PREFIX=../install .. - make -j4 - make install - - name: Deploy - shell: bash - run: | - cd install - macdeployqt nxsview.app -executable=nxsview.app/Contents/MacOS/nxsbuild -executable=nxsview.app/Contents/MacOS/nxscompress -executable=nxsview.app/Contents/MacOS/nxsedit - rm -r bin - rm -r lib - tar -cvzf nxsview-macos.tar.gz * - - name: Upload nxsview Portable - uses: actions/upload-artifact@v3 - with: - name: nxsview_macos_portable - path: install/nxsview-macos.tar.gz diff --git a/.github/workflows/Windows.yml b/.github/workflows/Windows.yml deleted file mode 100644 index 47478f41..00000000 --- a/.github/workflows/Windows.yml +++ /dev/null @@ -1,47 +0,0 @@ -name: Windows - -on: - [push, pull_request] - -jobs: - linux_build: - name: Build Nexus (Windows) - runs-on: windows-latest - - steps: - - uses: actions/checkout@v4 - with: - submodules: recursive - - name: Setup MSVC - uses: ilammy/msvc-dev-cmd@v1 - - name: Install Qt - uses: jurplel/install-qt-action@v4 - with: - cache: true - version: '5.15.2' - - name: Install dependencies - run: | - cd .. - git clone --single-branch --branch devel https://github.com/cnr-isti-vclab/vcglib - echo "VCINSTALLDIR=C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC" >> $GITHUB_ENV - - name: Configure and Build - shell: bash - run: | - mkdir build - mkdir install - cd build - cmake -DCMAKE_BUILD_TYPE=Release -GNinja -DCMAKE_INSTALL_PREFIX=../install .. - ninja - ninja install - - name: Deploy - shell: bash - run: | - cd install/bin - windeployqt nxsview.exe - cd .. - mv bin nexus_portable - - name: Uploading Nexus Portable - uses: actions/upload-artifact@v4 - with: - name: nexus_windows_portable - path: install/nexus_portable From dd3ed6115c019ac241531c7ce40a2783307352d7 Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Fri, 28 Mar 2025 10:31:00 +0100 Subject: [PATCH 14/33] deploy linux --- .github/workflows/BuildNexus.yml | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/.github/workflows/BuildNexus.yml b/.github/workflows/BuildNexus.yml index d3a320f2..eac32e8d 100644 --- a/.github/workflows/BuildNexus.yml +++ b/.github/workflows/BuildNexus.yml @@ -59,7 +59,24 @@ jobs: cmake -DCMAKE_BUILD_TYPE=Release -GNinja -DCMAKE_INSTALL_PREFIX=../install .. ninja ninja install - - name: Deploy + - name: Deploy Linux + shell: bash + if: ${{ runner.os == 'Linux' }} + run: | + cd install + # download linuxdeployqt + wget https://github.com/linuxdeploy/linuxdeploy/releases/download/1-alpha-20250213-2/linuxdeploy-${{ steps.envs.outputs.arch }}.AppImage + chmod +x linuxdeploy-${{ steps.envs.outputs.arch }}.AppImage + wget https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/1-alpha-20250213-1/linuxdeploy-plugin-qt-${{ steps.envs.outputs.arch }}.AppImage + chmod +x linuxdeploy-plugin-qt-${{ steps.envs.outputs.arch }}.AppImage + + # deploy + ./linuxdeploy-${{ steps.envs.outputs.arch }}.AppImage --executable=$PWD/bin/nxsbuild --executable=$PWD/bin/nxscompress --executable=$PWD/bin/nxsedit --executable=$PWD/bin/nxsview --appdir=. --plugin qt + + rm -r bin + rm linuxdeploy-${{ steps.envs.outputs.arch }}.AppImage linuxdeploy-plugin-qt-${{ steps.envs.outputs.arch }}.AppImage + tar -cvzf nexus-linux-${{ steps.envs.outputs.arch }}.tar.gz * + - name: Deploy MacOS shell: bash if: ${{ runner.os == 'macOS' }} run: | @@ -68,7 +85,7 @@ jobs: rm -r bin rm -r lib tar -cvzf nxsview-macos-${{ steps.envs.outputs.arch }}.tar.gz * - - name: Deploy + - name: Deploy Windows shell: bash if: ${{ runner.os == 'Windows' }} run: | @@ -76,6 +93,12 @@ jobs: windeployqt nxsview.exe cd .. mv bin nexus_portable-${{ steps.envs.outputs.arch }} + - name: Upload nxsview Portable + uses: actions/upload-artifact@v4 + if: ${{ runner.os == 'Linux' }} + with: + name: nexus-linux_portable_${{ steps.envs.outputs.arch }} + path: install/nexus-linux-${{ steps.envs.outputs.arch }}.tar.gz - name: Upload nxsview Portable uses: actions/upload-artifact@v4 if: ${{ runner.os == 'macOS' }} From b0b8f1c2a4d1b34360a1df226513a8cfc8621e4f Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Fri, 28 Mar 2025 11:06:14 +0100 Subject: [PATCH 15/33] composite action --- .github/actions/build_and_deploy/action.yml | 86 ++++++++++++++++++ .github/actions/upload/action.yml | 30 +++++++ .github/workflows/BuildNexus.yml | 99 +-------------------- 3 files changed, 120 insertions(+), 95 deletions(-) create mode 100644 .github/actions/build_and_deploy/action.yml create mode 100644 .github/actions/upload/action.yml diff --git a/.github/actions/build_and_deploy/action.yml b/.github/actions/build_and_deploy/action.yml new file mode 100644 index 00000000..139801fb --- /dev/null +++ b/.github/actions/build_and_deploy/action.yml @@ -0,0 +1,86 @@ +name: 'Build and Deploy' +description: 'Build and Deploy' + +runs: + using: "composite" + steps: + - name: Setup MSVC + uses: ilammy/msvc-dev-cmd@v1 + - name: Install Qt + uses: jurplel/install-qt-action@v4 + if: ${{ matrix.os != 'macos-latest' && matrix.os != 'ubuntu-22.04-arm' }} + with: + cache: true + version: '5.15.2' + - name: Setup Env variables + shell: bash + id: envs + run: | + echo "name=VCINSTALLDIR::C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC" >> $GITHUB_ENV + ARCH=$(uname -m) + echo "arch=$ARCH" >> $GITHUB_OUTPUT + - name: Clone VCG + shell: bash + run: | + cd .. + git clone --single-branch --branch devel https://github.com/cnr-isti-vclab/vcglib + - name: Install dependencies Linux + shell: bash + if: ${{ runner.os == 'Linux' }} + run: | + sudo apt-get install -y mesa-common-dev libglu1-mesa-dev libglew-dev ninja-build + - name: Install dependencies Linux arm + shell: bash + if: ${{ matrix.os == 'ubuntu-22.04-arm' }} + run: | + sudo apt-get install -y qtbase5-dev qt5-qmake + - name: Install dependencies macOS arm + shell: bash + if: ${{ matrix.os == 'macos-latest' }} + run: | + brew install qt@5 + echo Qt5_DIR=$(brew --prefix qt@5) >> $GITHUB_ENV + echo $(brew --prefix qt@5)/bin >> $GITHUB_PATH + - name: Configure and Build + shell: bash + run: | + mkdir build + mkdir install + cd build + cmake -DCMAKE_BUILD_TYPE=Release -GNinja -DCMAKE_INSTALL_PREFIX=../install .. + ninja + ninja install + - name: Deploy Linux + shell: bash + if: ${{ runner.os == 'Linux' }} + run: | + cd install + # download linuxdeployqt + wget https://github.com/linuxdeploy/linuxdeploy/releases/download/1-alpha-20250213-2/linuxdeploy-${{ steps.envs.outputs.arch }}.AppImage + chmod +x linuxdeploy-${{ steps.envs.outputs.arch }}.AppImage + wget https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/1-alpha-20250213-1/linuxdeploy-plugin-qt-${{ steps.envs.outputs.arch }}.AppImage + chmod +x linuxdeploy-plugin-qt-${{ steps.envs.outputs.arch }}.AppImage + + # deploy + ./linuxdeploy-${{ steps.envs.outputs.arch }}.AppImage --executable=$PWD/bin/nxsbuild --executable=$PWD/bin/nxscompress --executable=$PWD/bin/nxsedit --executable=$PWD/bin/nxsview --appdir=. --plugin qt + + rm -r bin + rm linuxdeploy-${{ steps.envs.outputs.arch }}.AppImage linuxdeploy-plugin-qt-${{ steps.envs.outputs.arch }}.AppImage + tar -cvzf nexus-linux-${{ steps.envs.outputs.arch }}.tar.gz * + - name: Deploy MacOS + shell: bash + if: ${{ runner.os == 'macOS' }} + run: | + cd install + macdeployqt nxsview.app -executable=nxsview.app/Contents/MacOS/nxsbuild -executable=nxsview.app/Contents/MacOS/nxscompress -executable=nxsview.app/Contents/MacOS/nxsedit + rm -r bin + rm -r lib + tar -cvzf nxsview-macos-${{ steps.envs.outputs.arch }}.tar.gz * + - name: Deploy Windows + shell: bash + if: ${{ runner.os == 'Windows' }} + run: | + cd install/bin + windeployqt nxsview.exe + cd .. + mv bin nexus_portable-${{ steps.envs.outputs.arch }} \ No newline at end of file diff --git a/.github/actions/upload/action.yml b/.github/actions/upload/action.yml new file mode 100644 index 00000000..2e4ea304 --- /dev/null +++ b/.github/actions/upload/action.yml @@ -0,0 +1,30 @@ +name: 'Upload' +description: 'Upload' + +runs: + using: "composite" + steps: + - name: Setup Env variables + shell: bash + id: envs + run: | + ARCH=$(uname -m) + echo "arch=$ARCH" >> $GITHUB_OUTPUT + - name: Upload nxsview Portable + uses: actions/upload-artifact@v4 + if: ${{ runner.os == 'Linux' }} + with: + name: nexus-linux_portable_${{ steps.envs.outputs.arch }} + path: install/nexus-linux-${{ steps.envs.outputs.arch }}.tar.gz + - name: Upload nxsview Portable + uses: actions/upload-artifact@v4 + if: ${{ runner.os == 'macOS' }} + with: + name: nxsview_macos_portable_${{ steps.envs.outputs.arch }} + path: install/nxsview-macos-${{ steps.envs.outputs.arch }}.tar.gz + - name: Uploading Nexus Portable + uses: actions/upload-artifact@v4 + if: ${{ runner.os == 'Windows' }} + with: + name: nexus_windows_portable_${{ steps.envs.outputs.arch }} + path: install/nexus_portable-${{ steps.envs.outputs.arch }} \ No newline at end of file diff --git a/.github/workflows/BuildNexus.yml b/.github/workflows/BuildNexus.yml index eac32e8d..1e16d970 100644 --- a/.github/workflows/BuildNexus.yml +++ b/.github/workflows/BuildNexus.yml @@ -16,98 +16,7 @@ jobs: - uses: actions/checkout@v4 with: submodules: recursive - - name: Setup MSVC - uses: ilammy/msvc-dev-cmd@v1 - - name: Install Qt - uses: jurplel/install-qt-action@v4 - if: ${{ matrix.os != 'macos-latest' && matrix.os != 'ubuntu-22.04-arm' }} - with: - cache: true - version: '5.15.2' - - name: Setup Env variables - shell: bash - id: envs - run: | - echo "name=VCINSTALLDIR::C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC" >> $GITHUB_ENV - ARCH=$(uname -m) - echo "arch=$ARCH" >> $GITHUB_OUTPUT - - name: Clone VCG - shell: bash - run: | - cd .. - git clone --single-branch --branch devel https://github.com/cnr-isti-vclab/vcglib - - name: Install dependencies Linux - if: ${{ runner.os == 'Linux' }} - run: | - sudo apt-get install -y mesa-common-dev libglu1-mesa-dev libglew-dev ninja-build - - name: Install dependencies Linux arm - if: ${{ matrix.os == 'ubuntu-22.04-arm' }} - run: | - sudo apt-get install -y qtbase5-dev qt5-qmake - - name: Install dependencies macOS arm - if: ${{ matrix.os == 'macos-latest' }} - run: | - brew install qt@5 - echo Qt5_DIR=$(brew --prefix qt@5) >> $GITHUB_ENV - echo $(brew --prefix qt@5)/bin >> $GITHUB_PATH - - name: Configure and Build - shell: bash - run: | - mkdir build - mkdir install - cd build - cmake -DCMAKE_BUILD_TYPE=Release -GNinja -DCMAKE_INSTALL_PREFIX=../install .. - ninja - ninja install - - name: Deploy Linux - shell: bash - if: ${{ runner.os == 'Linux' }} - run: | - cd install - # download linuxdeployqt - wget https://github.com/linuxdeploy/linuxdeploy/releases/download/1-alpha-20250213-2/linuxdeploy-${{ steps.envs.outputs.arch }}.AppImage - chmod +x linuxdeploy-${{ steps.envs.outputs.arch }}.AppImage - wget https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/1-alpha-20250213-1/linuxdeploy-plugin-qt-${{ steps.envs.outputs.arch }}.AppImage - chmod +x linuxdeploy-plugin-qt-${{ steps.envs.outputs.arch }}.AppImage - - # deploy - ./linuxdeploy-${{ steps.envs.outputs.arch }}.AppImage --executable=$PWD/bin/nxsbuild --executable=$PWD/bin/nxscompress --executable=$PWD/bin/nxsedit --executable=$PWD/bin/nxsview --appdir=. --plugin qt - - rm -r bin - rm linuxdeploy-${{ steps.envs.outputs.arch }}.AppImage linuxdeploy-plugin-qt-${{ steps.envs.outputs.arch }}.AppImage - tar -cvzf nexus-linux-${{ steps.envs.outputs.arch }}.tar.gz * - - name: Deploy MacOS - shell: bash - if: ${{ runner.os == 'macOS' }} - run: | - cd install - macdeployqt nxsview.app -executable=nxsview.app/Contents/MacOS/nxsbuild -executable=nxsview.app/Contents/MacOS/nxscompress -executable=nxsview.app/Contents/MacOS/nxsedit - rm -r bin - rm -r lib - tar -cvzf nxsview-macos-${{ steps.envs.outputs.arch }}.tar.gz * - - name: Deploy Windows - shell: bash - if: ${{ runner.os == 'Windows' }} - run: | - cd install/bin - windeployqt nxsview.exe - cd .. - mv bin nexus_portable-${{ steps.envs.outputs.arch }} - - name: Upload nxsview Portable - uses: actions/upload-artifact@v4 - if: ${{ runner.os == 'Linux' }} - with: - name: nexus-linux_portable_${{ steps.envs.outputs.arch }} - path: install/nexus-linux-${{ steps.envs.outputs.arch }}.tar.gz - - name: Upload nxsview Portable - uses: actions/upload-artifact@v4 - if: ${{ runner.os == 'macOS' }} - with: - name: nxsview_macos_portable_${{ steps.envs.outputs.arch }} - path: install/nxsview-macos-${{ steps.envs.outputs.arch }}.tar.gz - - name: Uploading Nexus Portable - uses: actions/upload-artifact@v4 - if: ${{ runner.os == 'Windows' }} - with: - name: nexus_windows_portable_${{ steps.envs.outputs.arch }} - path: install/nexus_portable-${{ steps.envs.outputs.arch }} \ No newline at end of file + - name: Build and Deploy + uses: ./.github/actions/build_and_deploy + - name: Upload + uses: ./.github/actions/upload \ No newline at end of file From 1f7c6aacddcb1fc2357d17d67a7081e9f6067154 Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Fri, 28 Mar 2025 11:24:26 +0100 Subject: [PATCH 16/33] ccache --- .github/actions/build_and_deploy/action.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/actions/build_and_deploy/action.yml b/.github/actions/build_and_deploy/action.yml index 139801fb..635a50cb 100644 --- a/.github/actions/build_and_deploy/action.yml +++ b/.github/actions/build_and_deploy/action.yml @@ -41,13 +41,17 @@ runs: brew install qt@5 echo Qt5_DIR=$(brew --prefix qt@5) >> $GITHUB_ENV echo $(brew --prefix qt@5)/bin >> $GITHUB_PATH + - name: Ccache + uses: hendrikmuhs/ccache-action@v1.2 + with: + key: ${{ matrix.os }}-${{ github.ref }} - name: Configure and Build shell: bash run: | mkdir build mkdir install cd build - cmake -DCMAKE_BUILD_TYPE=Release -GNinja -DCMAKE_INSTALL_PREFIX=../install .. + cmake -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_BUILD_TYPE=Release -GNinja -DCMAKE_INSTALL_PREFIX=../install .. ninja ninja install - name: Deploy Linux From 0b2b830951c030bcdd4921f156e6b46421f36325 Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Fri, 28 Mar 2025 11:45:37 +0100 Subject: [PATCH 17/33] windows zip --- .github/actions/build_and_deploy/action.yml | 3 ++- .github/actions/upload/action.yml | 14 +++++++------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/.github/actions/build_and_deploy/action.yml b/.github/actions/build_and_deploy/action.yml index 635a50cb..705f7dca 100644 --- a/.github/actions/build_and_deploy/action.yml +++ b/.github/actions/build_and_deploy/action.yml @@ -87,4 +87,5 @@ runs: cd install/bin windeployqt nxsview.exe cd .. - mv bin nexus_portable-${{ steps.envs.outputs.arch }} \ No newline at end of file + mv bin nexus_portable-${{ steps.envs.outputs.arch }} + powershell Compress-Archive -Path nexus_portable-${{ steps.envs.outputs.arch }} -DestinationPath nexus-windows-${{ steps.envs.outputs.arch }}.zip \ No newline at end of file diff --git a/.github/actions/upload/action.yml b/.github/actions/upload/action.yml index 2e4ea304..47d5e088 100644 --- a/.github/actions/upload/action.yml +++ b/.github/actions/upload/action.yml @@ -10,21 +10,21 @@ runs: run: | ARCH=$(uname -m) echo "arch=$ARCH" >> $GITHUB_OUTPUT - - name: Upload nxsview Portable + - name: Upload nexus Linux uses: actions/upload-artifact@v4 if: ${{ runner.os == 'Linux' }} with: - name: nexus-linux_portable_${{ steps.envs.outputs.arch }} + name: nexus-linux-${{ steps.envs.outputs.arch }} path: install/nexus-linux-${{ steps.envs.outputs.arch }}.tar.gz - - name: Upload nxsview Portable + - name: Upload nxsview MacOS uses: actions/upload-artifact@v4 if: ${{ runner.os == 'macOS' }} with: - name: nxsview_macos_portable_${{ steps.envs.outputs.arch }} + name: nxsview-macos-${{ steps.envs.outputs.arch }} path: install/nxsview-macos-${{ steps.envs.outputs.arch }}.tar.gz - - name: Uploading Nexus Portable + - name: Uploading Nexus Windows uses: actions/upload-artifact@v4 if: ${{ runner.os == 'Windows' }} with: - name: nexus_windows_portable_${{ steps.envs.outputs.arch }} - path: install/nexus_portable-${{ steps.envs.outputs.arch }} \ No newline at end of file + name: nexus-windows-${{ steps.envs.outputs.arch }} + path: install/nexus-windows-${{ steps.envs.outputs.arch }}.zip \ No newline at end of file From 9fb8dfaa277fcc425f3660f05c2870dd6b232909 Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Fri, 28 Mar 2025 12:04:50 +0100 Subject: [PATCH 18/33] create release workflow --- .github/workflows/CreateRelease.yml | 61 +++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 .github/workflows/CreateRelease.yml diff --git a/.github/workflows/CreateRelease.yml b/.github/workflows/CreateRelease.yml new file mode 100644 index 00000000..4c44c1d6 --- /dev/null +++ b/.github/workflows/CreateRelease.yml @@ -0,0 +1,61 @@ +name: CreateRelease + +on: + workflow_dispatch: + inputs: + version: + description: 'New Nexus Version' + required: true + default: 'X.Y' + +jobs: + update_version: + name: Update Version + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Update NEXUS_VERSION + run: | + echo ${{ github.event.inputs.version }} | tr -d '\n'> NEXUS_VERSION + - name: Commit change + uses: stefanzweifel/git-auto-commit-action@v5 + with: + commit_message: Set Nexus version to ${{ github.event.inputs.version }} + + build_nexus: + name: Build Nexus + needs: update_version + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: ['ubuntu-22.04', 'ubuntu-22.04-arm', 'macos-13', 'macos-latest', 'windows-latest'] + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + - name: Build and Deploy + uses: ./.github/actions/build_and_deploy + # TODO: sign + - name: Upload + uses: ./.github/actions/upload + + create_release: + name: Create Release + needs: build_nexus + runs-on: ubuntu-latest + steps: + #download all the artifacts + - name: Download Nexus Artifacts + uses: actions/download-artifact@v4 + with: + path: artifacts/ + + #Create release + - name: Create Release + uses: "ncipollo/release-action@v1" + with: + token: "${{ secrets.GITHUB_TOKEN }}" + tag: v${{ github.event.inputs.version }} + name: 'Nexus v${{ github.event.inputs.version }}' + artifacts: | + artifacts/n*/* \ No newline at end of file From 9ade4a10fd51c628aff9c16ce1b7cf70dc64f5d6 Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Fri, 28 Mar 2025 12:30:10 +0100 Subject: [PATCH 19/33] move archive to upload action, and add sign --- .github/actions/build_and_deploy/action.yml | 7 +------ .github/actions/upload/action.yml | 18 ++++++++++++++++++ .github/workflows/CreateRelease.yml | 11 ++++++++++- 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/.github/actions/build_and_deploy/action.yml b/.github/actions/build_and_deploy/action.yml index 705f7dca..3e5da20b 100644 --- a/.github/actions/build_and_deploy/action.yml +++ b/.github/actions/build_and_deploy/action.yml @@ -64,13 +64,10 @@ runs: chmod +x linuxdeploy-${{ steps.envs.outputs.arch }}.AppImage wget https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/1-alpha-20250213-1/linuxdeploy-plugin-qt-${{ steps.envs.outputs.arch }}.AppImage chmod +x linuxdeploy-plugin-qt-${{ steps.envs.outputs.arch }}.AppImage - # deploy ./linuxdeploy-${{ steps.envs.outputs.arch }}.AppImage --executable=$PWD/bin/nxsbuild --executable=$PWD/bin/nxscompress --executable=$PWD/bin/nxsedit --executable=$PWD/bin/nxsview --appdir=. --plugin qt - rm -r bin rm linuxdeploy-${{ steps.envs.outputs.arch }}.AppImage linuxdeploy-plugin-qt-${{ steps.envs.outputs.arch }}.AppImage - tar -cvzf nexus-linux-${{ steps.envs.outputs.arch }}.tar.gz * - name: Deploy MacOS shell: bash if: ${{ runner.os == 'macOS' }} @@ -79,7 +76,6 @@ runs: macdeployqt nxsview.app -executable=nxsview.app/Contents/MacOS/nxsbuild -executable=nxsview.app/Contents/MacOS/nxscompress -executable=nxsview.app/Contents/MacOS/nxsedit rm -r bin rm -r lib - tar -cvzf nxsview-macos-${{ steps.envs.outputs.arch }}.tar.gz * - name: Deploy Windows shell: bash if: ${{ runner.os == 'Windows' }} @@ -87,5 +83,4 @@ runs: cd install/bin windeployqt nxsview.exe cd .. - mv bin nexus_portable-${{ steps.envs.outputs.arch }} - powershell Compress-Archive -Path nexus_portable-${{ steps.envs.outputs.arch }} -DestinationPath nexus-windows-${{ steps.envs.outputs.arch }}.zip \ No newline at end of file + mv bin nexus_portable-${{ steps.envs.outputs.arch }} \ No newline at end of file diff --git a/.github/actions/upload/action.yml b/.github/actions/upload/action.yml index 47d5e088..30f89464 100644 --- a/.github/actions/upload/action.yml +++ b/.github/actions/upload/action.yml @@ -10,18 +10,36 @@ runs: run: | ARCH=$(uname -m) echo "arch=$ARCH" >> $GITHUB_OUTPUT + - name: Archive Linux + shell: bash + if: ${{ runner.os == 'Linux' }} + run: | + cd install + tar -cvzf nexus-linux-${{ steps.envs.outputs.arch }}.tar.gz * - name: Upload nexus Linux uses: actions/upload-artifact@v4 if: ${{ runner.os == 'Linux' }} with: name: nexus-linux-${{ steps.envs.outputs.arch }} path: install/nexus-linux-${{ steps.envs.outputs.arch }}.tar.gz + - name: Archive MacOS + shell: bash + if: ${{ runner.os == 'macOS' }} + run: | + cd install + tar -cvzf nxsview-macos-${{ steps.envs.outputs.arch }}.tar.gz * - name: Upload nxsview MacOS uses: actions/upload-artifact@v4 if: ${{ runner.os == 'macOS' }} with: name: nxsview-macos-${{ steps.envs.outputs.arch }} path: install/nxsview-macos-${{ steps.envs.outputs.arch }}.tar.gz + - name: Archive Windows + shell: bash + if: ${{ runner.os == 'Windows' }} + run: | + cd install + powershell Compress-Archive -Path nexus_portable-${{ steps.envs.outputs.arch }} -DestinationPath nexus-windows-${{ steps.envs.outputs.arch }}.zip - name: Uploading Nexus Windows uses: actions/upload-artifact@v4 if: ${{ runner.os == 'Windows' }} diff --git a/.github/workflows/CreateRelease.yml b/.github/workflows/CreateRelease.yml index 4c44c1d6..b7aa18a2 100644 --- a/.github/workflows/CreateRelease.yml +++ b/.github/workflows/CreateRelease.yml @@ -35,7 +35,16 @@ jobs: submodules: recursive - name: Build and Deploy uses: ./.github/actions/build_and_deploy - # TODO: sign + - name: Sign and Notarize + uses: cnr-isti-vclab/sign-and-notarize@v1 + with: + input-path: 'install/*.app' + macos-certificate: '${{ secrets.MACOS_CERTIFICATE }}' + macos-certificate-id: '${{ secrets.MACOS_CERT_ID }}' + macos-certificate-password: '${{ secrets.MACOS_CERTIFICATE_PSSW }}' + macos-notarization-team: '${{ secrets.MACOS_NOTARIZATION_TEAM_ID }}' + macos-notarization-user: '${{ secrets.MACOS_NOTARIZATION_USER }}' + macos-notarization-password: '${{ secrets.MACOS_NOTARIZATION_PSSW }}' - name: Upload uses: ./.github/actions/upload From eb8c1ad90415953dba04574d272d6e2bb60c3503 Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Wed, 2 Apr 2025 11:51:24 +0200 Subject: [PATCH 20/33] cmake minimum versions 3.10 --- CMakeLists.txt | 2 +- src/CMakeLists.txt | 2 +- src/corto | 2 +- src/nxsbuild/CMakeLists.txt | 6 +----- src/nxsedit/CMakeLists.txt | 10 +--------- src/nxsview/CMakeLists.txt | 2 +- 6 files changed, 6 insertions(+), 18 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c41d0c7d..c14f621a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ #export CMAKE_PREFIX_PATH=path to Qt/5.5/gcc_64/ (where you would find bin/qmake) -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.10) project(nexus) option(BUILD_NXS_BUILD "" ON) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 47c3b8dc..65285c17 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.10) project(nexus) set(HEADERS diff --git a/src/corto b/src/corto index d880519c..6a49a72f 160000 --- a/src/corto +++ b/src/corto @@ -1 +1 @@ -Subproject commit d880519c490c88a39d12c31a914b6a687a7019c3 +Subproject commit 6a49a72f6f1935f3a197727ebfdb0dc9dd02b46a diff --git a/src/nxsbuild/CMakeLists.txt b/src/nxsbuild/CMakeLists.txt index 13a7fc02..2abf05a8 100644 --- a/src/nxsbuild/CMakeLists.txt +++ b/src/nxsbuild/CMakeLists.txt @@ -1,6 +1,6 @@ #export CMAKE_PREFIX_PATH=path to Qt/5.5/gcc_64/ (save for bin/qmake) -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.10) project(nxsbuild) SET(HEADERS @@ -15,10 +15,6 @@ SET(SOURCES main.cpp ) -cmake_minimum_required(VERSION 3.1) - -project(nxsbuild) - set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_AUTOMOC ON) diff --git a/src/nxsedit/CMakeLists.txt b/src/nxsedit/CMakeLists.txt index 2a4f0c28..378db4a7 100644 --- a/src/nxsedit/CMakeLists.txt +++ b/src/nxsedit/CMakeLists.txt @@ -1,6 +1,6 @@ #export CMAKE_PREFIX_PATH=path to Qt/5.5/gcc_64/ (save for bin/qmake) -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.10) project(nxsedit) SET(HEADERS @@ -23,10 +23,6 @@ SET(SOURCES extractor.cpp ) -cmake_minimum_required(VERSION 3.1) - -project(nxsedit) - set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_AUTOMOC ON) @@ -56,10 +52,6 @@ SET(SOURCES main_compress.cpp ) -cmake_minimum_required(VERSION 3.1) - -project(nxscompress) - set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_AUTOMOC ON) diff --git a/src/nxsview/CMakeLists.txt b/src/nxsview/CMakeLists.txt index 46fb11bb..4500e2f7 100644 --- a/src/nxsview/CMakeLists.txt +++ b/src/nxsview/CMakeLists.txt @@ -1,6 +1,6 @@ #export CMAKE_PREFIX_PATH=path to Qt/5.5/gcc_64/ (save for bin/qmake) -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.10) project(nxsview) SET(HEADERS From ca03cc42f4ea30379d2fb83fd423d3bf24953ada Mon Sep 17 00:00:00 2001 From: alemuntoni <6656168+alemuntoni@users.noreply.github.com> Date: Fri, 9 May 2025 14:47:03 +0000 Subject: [PATCH 21/33] Set Nexus version to 2025.05 --- NEXUS_VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEXUS_VERSION b/NEXUS_VERSION index 89f71c74..15689348 100644 --- a/NEXUS_VERSION +++ b/NEXUS_VERSION @@ -1 +1 @@ -4.3 \ No newline at end of file +2025.05 \ No newline at end of file From 51d827520fa8e0672f492c7e4d583b82b0088b6b Mon Sep 17 00:00:00 2001 From: Federico Ponchio Date: Thu, 5 Jun 2025 18:05:50 +0200 Subject: [PATCH 22/33] ply with texture coords but no textures specified needs to disable textures --- src/nxsbuild/plyloader.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/nxsbuild/plyloader.cpp b/src/nxsbuild/plyloader.cpp index 7d5e49e8..f1075a7e 100644 --- a/src/nxsbuild/plyloader.cpp +++ b/src/nxsbuild/plyloader.cpp @@ -145,8 +145,8 @@ PlyLoader::PlyLoader(QString filename): texture_filenames.push_back(tex); } } -// if(has_textures && texture_filenames.size() == 0) -// has_textures = false; + if(has_textures && texture_filenames.size() == 0) + has_textures = false; for(auto &tex: texture_filenames) { sanitizeTextureFilepath(tex.filename); From 141ba17059f3680a74ce1178ed4245412f76061f Mon Sep 17 00:00:00 2001 From: Guilhem Saurel Date: Wed, 17 Sep 2025 18:28:34 +0200 Subject: [PATCH 23/33] CMake: install lib & exports --- CMakeLists.txt | 35 +++++++++++++++++++++++++++++++++-- Config.cmake.in | 7 +++++++ src/CMakeLists.txt | 13 ++++++++++++- 3 files changed, 52 insertions(+), 3 deletions(-) create mode 100644 Config.cmake.in diff --git a/CMakeLists.txt b/CMakeLists.txt index c14f621a..4a417e90 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,7 +27,12 @@ else() endif() # VCGLib -- required -if (VCGDIR) # VCGDIR exists - using custom user vcglib path +find_package(vcglib CONFIG) +if(TARGET vcglib::vcglib) + message(STATUS "nexus - VCGLib - using system provided library") + add_library(vcglib ALIAS vcglib::vcglib) + get_target_property(VCGDIR vcglib::vcglib INTERFACE_INCLUDE_DIRECTORIES) +elseif (VCGDIR) # VCGDIR exists - using custom user vcglib path if(EXISTS ${VCGDIR}) add_subdirectory(${VCGDIR} ${CMAKE_CURRENT_BINARY_DIR}/vcglib) message(STATUS "nexus - VCGLib - using custom VCGDIR path library") @@ -53,7 +58,13 @@ endif() set(BUILD_CORTO_CODEC_UNITY OFF) set(BUILD_CORTO_EXE OFF) -add_subdirectory(src/corto EXCLUDE_FROM_ALL) +find_package(corto CONFIG) +if(TARGET corto::corto) + message(STATUS "nexus - corto - using system provided library") + add_library(corto ALIAS corto::corto) +else() + add_subdirectory(src/corto EXCLUDE_FROM_ALL) +endif() add_subdirectory(src) @@ -68,3 +79,23 @@ endif() if(BUILD_NXS_VIEW) add_subdirectory(src/nxsview) endif() + +install(EXPORT nexusTargets + DESTINATION lib/cmake/nexus + NAMESPACE nexus:: + FILE nexusTargets.cmake +) + +include(CMakePackageConfigHelpers) +# generate the config file that includes the exports +configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in + "${CMAKE_CURRENT_BINARY_DIR}/nexusConfig.cmake" + INSTALL_DESTINATION lib/cmake/nexus + NO_SET_AND_CHECK_MACRO + NO_CHECK_REQUIRED_COMPONENTS_MACRO +) + +install(FILES + "${CMAKE_CURRENT_BINARY_DIR}/nexusConfig.cmake" + DESTINATION lib/cmake/nexus +) diff --git a/Config.cmake.in b/Config.cmake.in new file mode 100644 index 00000000..0457a3ae --- /dev/null +++ b/Config.cmake.in @@ -0,0 +1,7 @@ +@PACKAGE_INIT@ + +include(CMakeFindDependencyMacro) +find_dependency(vcglib) +find_dependency(corto) + +include ( "${CMAKE_CURRENT_LIST_DIR}/nexusTargets.cmake" ) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 65285c17..9b035938 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -62,10 +62,21 @@ add_library(nexus STATIC ${SOURCES} ${HEADERS}) target_include_directories(nexus PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}) + $ + $ +) target_link_libraries(nexus PUBLIC vcglib corto Qt5::Widgets) if (WIN32) target_compile_definitions(nexus PUBLIC NOMINMAX) endif() + +foreach(DIR common nxsbuild nxsedit nxsview nxszip) + install(DIRECTORY ${DIR} + DESTINATION include + FILES_MATCHING PATTERN "*.h") +endforeach() +install(TARGETS nexus + EXPORT nexusTargets + LIBRARY DESTINATION lib) From af309c0d080e948d59608d9c215ddb7cce9498ca Mon Sep 17 00:00:00 2001 From: ponchio Date: Mon, 22 Sep 2025 17:33:20 +0200 Subject: [PATCH 24/33] Update corto submodule to latest version (v2025.07-2-gc0c80fe) --- src/corto | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/corto b/src/corto index 6a49a72f..c0c80fe9 160000 --- a/src/corto +++ b/src/corto @@ -1 +1 @@ -Subproject commit 6a49a72f6f1935f3a197727ebfdb0dc9dd02b46a +Subproject commit c0c80fe9357cd4f5c3428a2fe73361a6e9ea40c3 From ad0933daae6fd4b17fbef4572ccc5ff989c56290 Mon Sep 17 00:00:00 2001 From: Guilhem Saurel Date: Mon, 22 Sep 2025 18:13:49 +0200 Subject: [PATCH 25/33] CMake: only export if system dependencies --- CMakeLists.txt | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4a417e90..de83ca86 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -80,22 +80,24 @@ if(BUILD_NXS_VIEW) add_subdirectory(src/nxsview) endif() -install(EXPORT nexusTargets - DESTINATION lib/cmake/nexus - NAMESPACE nexus:: - FILE nexusTargets.cmake -) - -include(CMakePackageConfigHelpers) -# generate the config file that includes the exports -configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in - "${CMAKE_CURRENT_BINARY_DIR}/nexusConfig.cmake" - INSTALL_DESTINATION lib/cmake/nexus - NO_SET_AND_CHECK_MACRO - NO_CHECK_REQUIRED_COMPONENTS_MACRO -) - -install(FILES - "${CMAKE_CURRENT_BINARY_DIR}/nexusConfig.cmake" - DESTINATION lib/cmake/nexus -) +if(TARGET vcglib::vcglib AND TARGET corto::corto) + install(EXPORT nexusTargets + DESTINATION lib/cmake/nexus + NAMESPACE nexus:: + FILE nexusTargets.cmake + ) + + include(CMakePackageConfigHelpers) + # generate the config file that includes the exports + configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in + "${CMAKE_CURRENT_BINARY_DIR}/nexusConfig.cmake" + INSTALL_DESTINATION lib/cmake/nexus + NO_SET_AND_CHECK_MACRO + NO_CHECK_REQUIRED_COMPONENTS_MACRO + ) + + install(FILES + "${CMAKE_CURRENT_BINARY_DIR}/nexusConfig.cmake" + DESTINATION lib/cmake/nexus + ) +endif() From 84dc005152c805a236f15279b69fc0eddabd3dad Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Fri, 7 Nov 2025 11:24:47 +0100 Subject: [PATCH 26/33] Update matrix OS to use macos-15-intel instead of macos-13 --- .github/actions/build_and_deploy/action.yml | 2 +- .github/actions/upload/action.yml | 2 +- .github/workflows/BuildNexus.yml | 4 ++-- .github/workflows/CreateRelease.yml | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/actions/build_and_deploy/action.yml b/.github/actions/build_and_deploy/action.yml index 3e5da20b..29acb255 100644 --- a/.github/actions/build_and_deploy/action.yml +++ b/.github/actions/build_and_deploy/action.yml @@ -83,4 +83,4 @@ runs: cd install/bin windeployqt nxsview.exe cd .. - mv bin nexus_portable-${{ steps.envs.outputs.arch }} \ No newline at end of file + mv bin nexus_portable-${{ steps.envs.outputs.arch }} diff --git a/.github/actions/upload/action.yml b/.github/actions/upload/action.yml index 30f89464..be7d571e 100644 --- a/.github/actions/upload/action.yml +++ b/.github/actions/upload/action.yml @@ -45,4 +45,4 @@ runs: if: ${{ runner.os == 'Windows' }} with: name: nexus-windows-${{ steps.envs.outputs.arch }} - path: install/nexus-windows-${{ steps.envs.outputs.arch }}.zip \ No newline at end of file + path: install/nexus-windows-${{ steps.envs.outputs.arch }}.zip diff --git a/.github/workflows/BuildNexus.yml b/.github/workflows/BuildNexus.yml index 1e16d970..6d2ecdc0 100644 --- a/.github/workflows/BuildNexus.yml +++ b/.github/workflows/BuildNexus.yml @@ -10,7 +10,7 @@ jobs: strategy: fail-fast: false matrix: - os: ['ubuntu-22.04', 'ubuntu-22.04-arm', 'macos-13', 'macos-latest', 'windows-latest'] + os: ['ubuntu-22.04', 'ubuntu-22.04-arm', 'macos-15-intel', 'macos-latest', 'windows-latest'] steps: - uses: actions/checkout@v4 @@ -19,4 +19,4 @@ jobs: - name: Build and Deploy uses: ./.github/actions/build_and_deploy - name: Upload - uses: ./.github/actions/upload \ No newline at end of file + uses: ./.github/actions/upload diff --git a/.github/workflows/CreateRelease.yml b/.github/workflows/CreateRelease.yml index b7aa18a2..b5fc62bd 100644 --- a/.github/workflows/CreateRelease.yml +++ b/.github/workflows/CreateRelease.yml @@ -28,7 +28,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: ['ubuntu-22.04', 'ubuntu-22.04-arm', 'macos-13', 'macos-latest', 'windows-latest'] + os: ['ubuntu-22.04', 'ubuntu-22.04-arm', 'macos-15-intel', 'macos-latest', 'windows-latest'] steps: - uses: actions/checkout@v4 with: @@ -67,4 +67,4 @@ jobs: tag: v${{ github.event.inputs.version }} name: 'Nexus v${{ github.event.inputs.version }}' artifacts: | - artifacts/n*/* \ No newline at end of file + artifacts/n*/* From 2476cd39d3fa4ce35f62e20b1ad80d144f2dc88d Mon Sep 17 00:00:00 2001 From: ponchio Date: Wed, 10 Dec 2025 12:13:16 +0100 Subject: [PATCH 27/33] Added threejs nexus classes and demo. --- nexus3d/README.md | 65 ++ nexus3d/build/nexus3D.js | 1570 +++++++++++++++++++++++++++++++++ nexus3d/build/nexus3D.min.js | 1 + nexus3d/dist/corto.em.js | 216 +++++ nexus3d/dist/index.html | 29 + nexus3d/dist/models/gargo.nxz | Bin 0 -> 216832 bytes nexus3d/package.json | 41 + nexus3d/rollup.config.js | 32 + nexus3d/src/Binary.js | 104 +++ nexus3d/src/Cache.js | 426 +++++++++ nexus3d/src/Mesh.js | 261 ++++++ nexus3d/src/Monitor.js | 321 +++++++ nexus3d/src/NXS.js | 295 +++++++ nexus3d/src/NXSRaw.js | 402 +++++++++ nexus3d/src/Nexus3D.js | 465 ++++++++++ nexus3d/src/PriorityQueue.js | 76 ++ nexus3d/src/Traversal.js | 259 ++++++ nexus3d/src/index.js | 207 +++++ nexus3d/webpack.config.js | 32 + nexus3d/webpacklib.config.js | 19 + 20 files changed, 4821 insertions(+) create mode 100644 nexus3d/README.md create mode 100644 nexus3d/build/nexus3D.js create mode 100644 nexus3d/build/nexus3D.min.js create mode 100644 nexus3d/dist/corto.em.js create mode 100644 nexus3d/dist/index.html create mode 100644 nexus3d/dist/models/gargo.nxz create mode 100644 nexus3d/package.json create mode 100644 nexus3d/rollup.config.js create mode 100644 nexus3d/src/Binary.js create mode 100644 nexus3d/src/Cache.js create mode 100644 nexus3d/src/Mesh.js create mode 100644 nexus3d/src/Monitor.js create mode 100644 nexus3d/src/NXS.js create mode 100644 nexus3d/src/NXSRaw.js create mode 100644 nexus3d/src/Nexus3D.js create mode 100644 nexus3d/src/PriorityQueue.js create mode 100644 nexus3d/src/Traversal.js create mode 100644 nexus3d/src/index.js create mode 100644 nexus3d/webpack.config.js create mode 100644 nexus3d/webpacklib.config.js diff --git a/nexus3d/README.md b/nexus3d/README.md new file mode 100644 index 00000000..9cc11e0d --- /dev/null +++ b/nexus3d/README.md @@ -0,0 +1,65 @@ +## Install + +Install threejs: +```bash +npm install -save three +``` + +Install webpack, (optional): +```bash +npm install -save webpack webpack-cli +``` +Build the example in webpack: +```bash +npx webpack --mode development +``` + +If you don't want to usa another http server: +```bash +npm install webpack-dev-server --save-dev +``` +Finally add the following lines to package.json +```json +"scripts": { + "start:dev": "webpack-dev-server" +} +``` + +and run the server: +```bash +npm run start:dev +``` + +## Docs + +# Material + +NXS material is actually a template material that is used for each patch of the nexus, you cannot set the map properties, +which will be overwritten by the patches own textures. +You can change the material of the NXS either by replacing the material or by changing a value +(the color for exmple). If you want to disable texture rendering set the parameter 'map' to false, +and set material.needsUpdate = true. + +# FPS: +The Cache class can throttle the amount of geometry rendered trying to mantain the fps above a certain target (minFps), +reducing the targetError. You can set this property through the NXS.cache.minFps property. (default 30). + +# Instances: +You can render multiple instances of the same mesh, just using the reference to an existing NXS +instead of the url in the constructor. Cache will be shared, balancing the quality among all the instances. + + +## Roadmap + +Cache: at the moment NXS creates a cache per model, we shuold have a global one per context (as it was), but the options to have . +Nexus global object with cache management. + +TODO: check minFps actually works. + +GPU resource transfer metering: better control of the amount of geometry and texture sent to the GPU per frame. +Prefetch: control prefetch amouont in MB (unlimited in case) and add onProgress event. +use blocked for prefetch based on size, number of nodes. +onProgress when some node is added or removed. + +Library: make the code an npm library +https://webpack.js.org/guides/author-libraries/ diff --git a/nexus3d/build/nexus3D.js b/nexus3d/build/nexus3D.js new file mode 100644 index 00000000..5472c953 --- /dev/null +++ b/nexus3d/build/nexus3D.js @@ -0,0 +1,1570 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('three')) : + typeof define === 'function' && define.amd ? define(['exports', 'three'], factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.Nexus3D = {}, global.THREE)); +})(this, (function (exports, THREE) { 'use strict'; + + function _interopNamespace(e) { + if (e && e.__esModule) return e; + var n = Object.create(null); + if (e) { + Object.keys(e).forEach(function (k) { + if (k !== 'default') { + var d = Object.getOwnPropertyDescriptor(e, k); + Object.defineProperty(n, k, d.get ? d : { + enumerable: true, + get: function () { return e[k]; } + }); + } + }); + } + n["default"] = e; + return Object.freeze(n); + } + + var THREE__namespace = /*#__PURE__*/_interopNamespace(THREE); + + /* UTILITIES */ + + function getUint64(view) { + var lo = view.getUint32(view.offset, true); + var hi = view.getUint32(view.offset + 4, true); + view.offset += 8; + return ((hi * (1 << 32)) + lo); + } + + function getUint32(view) { + var s = view.getUint32(view.offset, true); + view.offset += 4; + return s; + } + + function getUint16(view) { + var s = view.getUint16(view.offset, true); + view.offset += 2; + return s; + } + + function getFloat32(view) { + var s = view.getFloat32(view.offset, true); + view.offset += 4; + return s; + } + + + function matMul(a, b, r) { + r[ 0] = a[0]*b[0] + a[4]*b[1] + a[8]*b[2] + a[12]*b[3]; + r[ 1] = a[1]*b[0] + a[5]*b[1] + a[9]*b[2] + a[13]*b[3]; + r[ 2] = a[2]*b[0] + a[6]*b[1] + a[10]*b[2] + a[14]*b[3]; + r[ 3] = a[3]*b[0] + a[7]*b[1] + a[11]*b[2] + a[15]*b[3]; + + r[ 4] = a[0]*b[4] + a[4]*b[5] + a[8]*b[6] + a[12]*b[7]; + r[ 5] = a[1]*b[4] + a[5]*b[5] + a[9]*b[6] + a[13]*b[7]; + r[ 6] = a[2]*b[4] + a[6]*b[5] + a[10]*b[6] + a[14]*b[7]; + r[ 7] = a[3]*b[4] + a[7]*b[5] + a[11]*b[6] + a[15]*b[7]; + + r[ 8] = a[0]*b[8] + a[4]*b[9] + a[8]*b[10] + a[12]*b[11]; + r[ 9] = a[1]*b[8] + a[5]*b[9] + a[9]*b[10] + a[13]*b[11]; + r[10] = a[2]*b[8] + a[6]*b[9] + a[10]*b[10] + a[14]*b[11]; + r[11] = a[3]*b[8] + a[7]*b[9] + a[11]*b[10] + a[15]*b[11]; + + r[12] = a[0]*b[12] + a[4]*b[13] + a[8]*b[14] + a[12]*b[15]; + r[13] = a[1]*b[12] + a[5]*b[13] + a[9]*b[14] + a[13]*b[15]; + r[14] = a[2]*b[12] + a[6]*b[13] + a[10]*b[14] + a[14]*b[15]; + r[15] = a[3]*b[12] + a[7]*b[13] + a[11]*b[14] + a[15]*b[15]; + } + + function matInv(m, t) { + var s = 1.0/( + m[12]* m[9]*m[6]*m[3]-m[8]*m[13]*m[6]*m[3]-m[12]*m[5]*m[10]*m[3]+m[4]*m[13]*m[10]*m[3]+ + m[8]*m[5]*m[14]*m[3]-m[4]*m[9]*m[14]*m[3]-m[12]*m[9]*m[2]*m[7]+m[8]*m[13]*m[2]*m[7]+ + m[12]*m[1]*m[10]*m[7]-m[0]*m[13]*m[10]*m[7]-m[8]*m[1]*m[14]*m[7]+m[0]*m[9]*m[14]*m[7]+ + m[12]*m[5]*m[2]*m[11]-m[4]*m[13]*m[2]*m[11]-m[12]*m[1]*m[6]*m[11]+m[0]*m[13]*m[6]*m[11]+ + m[4]*m[1]*m[14]*m[11]-m[0]*m[5]*m[14]*m[11]-m[8]*m[5]*m[2]*m[15]+m[4]*m[9]*m[2]*m[15]+ + m[8]*m[1]*m[6]*m[15]-m[0]*m[9]*m[6]*m[15]-m[4]*m[1]*m[10]*m[15]+m[0]*m[5]*m[10]*m[15] + ); + + t[ 0] = (m[9]*m[14]*m[7]-m[13]*m[10]*m[7]+m[13]*m[6]*m[11]-m[5]*m[14]*m[11]-m[9]*m[6]*m[15]+m[5]*m[10]*m[15])*s; + t[ 1] = (m[13]*m[10]*m[3]-m[9]*m[14]*m[3]-m[13]*m[2]*m[11]+m[1]*m[14]*m[11]+m[9]*m[2]*m[15]-m[1]*m[10]*m[15])*s; + t[ 2] = (m[5]*m[14]*m[3]-m[13]*m[6]*m[3]+m[13]*m[2]*m[7]-m[1]*m[14]*m[7]-m[5]*m[2]*m[15]+m[1]*m[6]*m[15])*s; + t[ 3] = (m[9]*m[6]*m[3]-m[5]*m[10]*m[3]-m[9]*m[2]*m[7]+m[1]*m[10]*m[7]+m[5]*m[2]*m[11]-m[1]*m[6]*m[11])*s; + + t[ 4] = (m[12]*m[10]*m[7]-m[8]*m[14]*m[7]-m[12]*m[6]*m[11]+m[4]*m[14]*m[11]+m[8]*m[6]*m[15]-m[4]*m[10]*m[15])*s; + t[ 5] = (m[8]*m[14]*m[3]-m[12]*m[10]*m[3]+m[12]*m[2]*m[11]-m[0]*m[14]*m[11]-m[8]*m[2]*m[15]+m[0]*m[10]*m[15])*s; + t[ 6] = (m[12]*m[6]*m[3]-m[4]*m[14]*m[3]-m[12]*m[2]*m[7]+m[0]*m[14]*m[7]+m[4]*m[2]*m[15]-m[0]*m[6]*m[15])*s; + t[ 7] = (m[4]*m[10]*m[3]-m[8]*m[6]*m[3]+m[8]*m[2]*m[7]-m[0]*m[10]*m[7]-m[4]*m[2]*m[11]+m[0]*m[6]*m[11])*s; + + t[ 8] = (m[8]*m[13]*m[7]-m[12]*m[9]*m[7]+m[12]*m[5]*m[11]-m[4]*m[13]*m[11]-m[8]*m[5]*m[15]+m[4]*m[9]*m[15])*s; + t[ 9] = (m[12]*m[9]*m[3]-m[8]*m[13]*m[3]-m[12]*m[1]*m[11]+m[0]*m[13]*m[11]+m[8]*m[1]*m[15]-m[0]*m[9]*m[15])*s; + t[10] = (m[4]*m[13]*m[3]-m[12]*m[5]*m[3]+m[12]*m[1]*m[7]-m[0]*m[13]*m[7]-m[4]*m[1]*m[15]+m[0]*m[5]*m[15])*s; + t[11] = (m[8]*m[5]*m[3]-m[4]*m[9]*m[3]-m[8]*m[1]*m[7]+m[0]*m[9]*m[7]+m[4]*m[1]*m[11]-m[0]*m[5]*m[11])*s; + + t[12] = (m[12]*m[9]*m[6]-m[8]*m[13]*m[6]-m[12]*m[5]*m[10]+m[4]*m[13]*m[10]+m[8]*m[5]*m[14]-m[4]*m[9]*m[14])*s; + t[13] = (m[8]*m[13]*m[2]-m[12]*m[9]*m[2]+m[12]*m[1]*m[10]-m[0]*m[13]*m[10]-m[8]*m[1]*m[14]+m[0]*m[9]*m[14])*s; + t[14] = (m[12]*m[5]*m[2]-m[4]*m[13]*m[2]-m[12]*m[1]*m[6]+m[0]*m[13]*m[6]+m[4]*m[1]*m[14]-m[0]*m[5]*m[14])*s; + t[15] = (m[4]*m[9]*m[2]-m[8]*m[5]*m[2]+m[8]*m[1]*m[6]-m[0]*m[9]*m[6]-m[4]*m[1]*m[10]+m[0]*m[5]*m[10])*s; + } + + function PriorityQueue(max_length) { + this.error = new Float32Array(max_length); + this.data = new Int32Array(max_length); + this.size = 0; + } + + PriorityQueue.prototype = { + push: function(data, error) { + this.data[this.size] = data; + this.error[this.size] = error; + this.bubbleUp(this.size); + this.size++; + }, + + pop: function() { + var result = this.data[0]; + this.size--; + if(this.size > 0) { + this.data[0] = this.data[this.size]; + this.error[0] = this.error[this.size]; + this.sinkDown(0); + } + return result; + }, + + bubbleUp: function(n) { + var data = this.data[n]; + var error = this.error[n]; + while (n > 0) { + var pN = ((n+1)>>1) -1; + var pError = this.error[pN]; + if(pError > error) + break; + //swap + this.data[n] = this.data[pN]; + this.error[n] = pError; + this.data[pN] = data; + this.error[pN] = error; + n = pN; + } + }, + + sinkDown: function(n) { + var data = this.data[n]; + var error = this.error[n]; + + while(true) { + var child2N = (n + 1) * 2; + var child1N = child2N - 1; + var swap = -1; + if (child1N < this.size) { + var child1Error = this.error[child1N]; + if(child1Error > error) + swap = child1N; + } + if (child2N < this.size) { + var child2Error = this.error[child2N]; + if (child2Error > (swap == -1 ? error : child1Error)) + swap = child2N; + } + + if (swap == -1) break; + + this.data[n] = this.data[swap]; + this.error[n] = this.error[swap]; + this.data[swap] = data; + this.error[swap] = error; + n = swap; + } + } + }; + + function Traversal() { + let t = this; + t.maxBlocked = 30; + + t.modelMatrix = new Float32Array(16); + t.viewMatrix = new Float32Array(16); + t.projectionMatrix = new Float32Array(16); + t.modelView = new Float32Array(16); + t.modelViewInv = new Float32Array(16); + t.modelViewProj = new Float32Array(16); + t.modelViewProjInv = new Float32Array(16); + t.planes = new Float32Array(24); + t.viewport = new Float32Array(4); + t.viewpoint = new Float32Array(4); + } + + Traversal.prototype = { + updateView: function(viewport, projection, modelView) { + let t = this; + + for(let i = 0; i < 16; i++) { + t.projectionMatrix[i] = projection[i]; + t.modelView[i] = modelView[i]; + } + for(let i = 0; i < 4; i++) + t.viewport[i] = viewport[i]; + + matMul(t.projectionMatrix, t.modelView, t.modelViewProj); + matInv(t.modelViewProj, t.modelViewProjInv); + + matInv(t.modelView, t.modelViewInv); + t.viewpoint[0] = t.modelViewInv[12]; + t.viewpoint[1] = t.modelViewInv[13]; + t.viewpoint[2] = t.modelViewInv[14]; + t.viewpoint[3] = 1.0; + + + const m = t.modelViewProj; + const mi = t.modelViewProjInv; + let p = t.planes; + + //frustum planes Ax + By + Cz + D = 0; + p[0] = m[0] + m[3]; p[1] = m[4] + m[7]; p[2] = m[8] + m[11]; p[3] = m[12] + m[15]; //left + p[4] = -m[0] + m[3]; p[5] = -m[4] + m[7]; p[6] = -m[8] + m[11]; p[7] = -m[12] + m[15]; //right + p[8] = m[1] + m[3]; p[9] = m[5] + m[7]; p[10] = m[9] + m[11]; p[11] = m[13] + m[15]; //bottom + p[12] = -m[1] + m[3]; p[13] = -m[5] + m[7]; p[14] = -m[9] + m[11]; p[15] = -m[13] + m[15]; //top + p[16] = -m[2] + m[3]; p[17] = -m[6] + m[7]; p[18] = -m[10] + m[11]; p[19] = -m[14] + m[15]; //near + p[20] = -m[2] + m[3]; p[21] = -m[6] + m[7]; p[22] = -m[10] + m[11]; p[23] = -m[14] + m[15]; //far + + //normalize planes to get also correct distances + for(let i = 0; i < 24; i+= 4) { + let l = Math.sqrt(p[i]*p[i] + p[i+1]*p[i+1] + p[i+2]*p[i+2]); + p[i] /= l; p[i+1] /= l; p[i+2] /= l; p[i+3] /= l; + } + + //side is M'(1,0,0,1) - M'(-1,0,0,1) and they lie on the planes + const r3 = mi[3] + mi[15]; + const r0 = (mi[0] + mi[12 ])/r3; + const r1 = (mi[1] + mi[13 ])/r3; + const r2 = (mi[2] + mi[14 ])/r3; + + const l3 = -mi[3] + mi[15]; + const l0 = (-mi[0] + mi[12 ])/l3 - r0; + const l1 = (-mi[1] + mi[13 ])/l3 - r1; + const l2 = (-mi[2] + mi[14 ])/l3 - r2; + + const side = Math.sqrt(l0*l0 + l1*l1 + l2*l2); + + //center of the scene is M'*(0, 0, 0, 1) + const c0 = mi[12]/mi[15] - t.viewpoint[0]; + const c1 = mi[13]/mi[15] - t.viewpoint[1]; + const c2 = mi[14]/mi[15] - t.viewpoint[2]; + const dist = Math.sqrt(c0*c0 + c1*c1 + c2*c2); + + const resolution = (2*side/dist)/ t.viewport[2]; + t.currentResolution == resolution ? t.sameResolution = true : t.sameResolution = false; + t.currentResolution = resolution; + }, + + traverse : function (mesh, cache) { + let t = this; + t.mesh = mesh; + // if(Debug.extract == true) + // return; + + if(!mesh.isReady) return; + + const n = mesh.nodesCount; + t.visited = new Uint8Array(n); + t.blocked = new Uint8Array(n); + t.selected = new Uint8Array(n); + + t.frame = cache.frame; + + t.instance_errors = new Float32Array(n); + + if(t.frame > mesh.frame) { //clean the errors. + mesh.errors = new Float32Array(n); + mesh.frame = t.frame; + } + + t.visitQueue = new PriorityQueue(n); + for(var i = 0; i < mesh.nroots; i++) + t.insertNode(i); + + t.currentError = cache.currentError; + t.drawSize = 0; + t.nblocked = 0; + + var requested = 0; + while(t.visitQueue.size && t.nblocked < t.maxBlocked) { + var error = t.visitQueue.error[0]; + var id = t.visitQueue.pop(); + + //if not loaded and the queue is not full add to the candidates. + if (mesh.status[id] == 0 && requested < cache.maxPending) { + cache.candidates.push({id: id, mesh:mesh, frame:t.frame, error:error}); + requested++; + } + /* we don't want to stop as soon as a node is not availabe, because the nodes are sorted by error, + this could cause a large drop in quality elsewere. + we still need to mark all children as blocked, to prevent including them in the cut of the dag. */ + var blocked = t.blocked[id] || !t.expandNode(id, error); + if (blocked) + t.nblocked++; + else { + t.selected[id] = 1; + //cache.realError = Math.min(error, cache.realError); + } + t.insertChildren(id, blocked); + } + + //update remaining errors in the cache + if(cache.nodes.has(mesh)) { + for(let id of cache.nodes.get(mesh)) { + let error = t.nodeError(id); + if(t.instance_errors[id] == 0) { + t.instance_errors[i] = error; + mesh.errors[id] = Math.max(mesh.errors[id], error); + } + } + } + + t.mesh = null; + return t.instance_errors; + }, + + insertNode: function (node) { + let t = this; + t.visited[node] = 1; + + const error = t.nodeError(node); + + t.instance_errors[node] = error; + t.mesh.errors[node] = Math.max(error, t.mesh.errors[node]); + t.mesh.frames[node] = t.frame; + + // if(node > 0 && error < t.currentError) return; //2% speed TODO check if needed + + t.visitQueue.push(node, error); + }, + + insertChildren : function (node, block) { + let t = this; + for(let i = t.mesh.nfirstpatch[node]; i < t.mesh.nfirstpatch[node+1]; ++i) { + const child = t.mesh.patches[i*3]; + if (child == t.mesh.sink) return; + if (block) t.blocked[child] = 1; + if (!t.visited[child]) + t.insertNode(child); + } + }, + + expandNode : function (node, error) { + let t = this; + if(node > 0 && error < t.currentError) { + // console.log("Reached error", error, t.currentError); + return false; + } + + if(t.drawSize > t.drawBudget) { + // console.log("Reached drawsize", t.drawSize, t.drawBudget); + return false; + } + + if(t.mesh.status[node] != 1) { //not ready + // console.log("Node " + node + " still not loaded (cache?)"); + return false; + } + + const sp = t.mesh.nspheres; + const off = node*5; + if(t.isVisible(sp[off], sp[off+1], sp[off+2], sp[off+3])) //expanded radius + t.drawSize += t.mesh.nvertices[node]*0.8; + //we are adding half of the new faces. (but we are using the vertices so *2) + + return true; + }, + + nodeError : function (n, tight) { + let t = this; + const spheres = t.mesh.nspheres; + const b = t.viewpoint; + const off = n*5; + const cx = spheres[off+0]; + const cy = spheres[off+1]; + const cz = spheres[off+2]; + let r = spheres[off+3]; + if(tight) + r = spheres[off+4]; + const d0 = b[0] - cx; + const d1 = b[1] - cy; + const d2 = b[2] - cz; + let dist = Math.sqrt(d0*d0 + d1*d1 + d2*d2) - r; + if (dist < 0.1) + dist = 0.1; + + //resolution is how long is a pixel at distance 1. + let error = t.mesh.nerrors[n]/(t.currentResolution*dist); //in pixels + + //causes flickering due to things popping in and out of visibility, causes a huge resorting. + /*if (!t.isVisible(cx, cy, cz, spheres[off+4])) + error /= 100.0; */ + + //more stable, at least it's continuous. + let d = t.distance(cx, cy, cz, spheres[off+4]); + if(d < -r) { + error /= 101.0; + } else if(d < 0) { + error /= 1 -( d/r)*100.0; + } + return error; + }, + + distance: function(x, y, z, r) { + const p = this.planes; + let min_distance = 1e20; + for (let i = 0; i < 24; i +=4) { + let d = p[i]*x + p[i+1]*y + p[i+2]*z + p[i+3] + r; + if(d < min_distance) + min_distance = d; + } + return min_distance; + }, + + isVisible : function (x, y, z, r) { + const p = this.planes; + for (let i = 0; i < 24; i +=4) { + if(p[i]*x + p[i+1]*y + p[i+2]*z + p[i+3] + r < 0) //plane is ax+by+cz+d = 0; + return false; + } + return true; + } + }; + + let glP = WebGLRenderingContext.prototype; + let attrGlMap = [glP.NONE, glP.BYTE, glP.UNSIGNED_BYTE, glP.SHORT, glP.UNSIGNED_SHORT, glP.INT, glP.UNSIGNED_INT, glP.FLOAT, glP.DOUBLE]; + let attrSizeMap = [0, 1, 1, 2, 2, 4, 4, 4, 8]; + + //All addresses in the file are n*256 so, 256 * 2^32 is the max size of a Nxs file + var padding = 256; + + let Mesh = function(url) { + var t = this; + t.isReady = false; + t.onLoad = []; + t.onUpdate = []; + t.reqAttempt = 0; + t.georeq = {}; //keeps track of existing httprequests + t.texreq = {}; + t.frame = 0; //last time this mesh was traversed in rendering. + t.availableNodes = 0; + if(url) + t.open(url); + }; + + Mesh.prototype = { + open: function(url) { + let mesh = this; + mesh.url = url; + mesh.httpRequest(url, + 0, + 88, + function() { + console.log("Loading header for " + mesh.url); + let view = new DataView(this.response); + view.offset = 0; + mesh.reqAttempt++; + const header = mesh.importHeader(view); + if(!header) { + console.log("Empty header!"); + if(mesh.reqAttempt < maxReqAttempt) mesh.open(mesh.url + '?' + Math.random()); // BLINK ENGINE CACHE BUG PATCH + return; + } + mesh.reqAttempt = 0; + for(let i in header) + mesh[i] = header[i]; + mesh.vertex = mesh.signature.vertex; + mesh.face = mesh.signature.face; + mesh.renderMode = mesh.face.index?["FILL", "POINT"]:["POINT"]; + mesh.compressed = (mesh.signature.flags & (2 | 4)); //meco or corto + mesh.meco = (mesh.signature.flags & 2); + mesh.corto = (mesh.signature.flags & 4); + + mesh.deepzoom = (mesh.signature.flags & 8); + if(mesh.deepzoom) + mesh.baseurl = url.substr(0, url.length -4) + '_files/'; + + + mesh.requestIndex(); + }, + function() { console.log("Open request error!");}, + function() { console.log("Open request abort!");} + ); + }, + + httpRequest: function(url, start, end, load, error, abort, type) { + if(!type) type = 'arraybuffer'; + var r = new XMLHttpRequest(); + r.open('GET', url, true); + r.responseType = type; + if(end) + r.setRequestHeader("Range", "bytes=" + start + "-" + (end -1)); + r.onload = function(){ + switch (this.status){ + case 0: + // console.log("0 response: server unreachable.");//returned in chrome for local files + error(); + break; + case 206: + // console.log("206 response: partial content loaded."); + load.bind(this)(); + break; + case 200: + // console.log("200 response: server does not support byte range requests."); + if(end == 0) + load.bind(this)(); + else + error(); + break; + } + }; + r.onerror = error; + r.onabort = abort; + r.send(); + return r; + }, + + requestIndex: function() { + var mesh = this; + var end = 88 + mesh.nodesCount*44 + mesh.patchesCount*12 + mesh.texturesCount*68; + mesh.httpRequest(this.url, + 88, + end, + function() { console.log("Loading index for " + mesh.url); mesh.handleIndex(this.response); }, + function() { console.log("Index request error!");}, + function() { console.log("Index request abort!");} + ); + }, + + handleIndex: function(buffer) { + let t = this; + let view = new DataView(buffer); + view.offset = 0; + + const n = t.nodesCount; + + t.noffsets = new Uint32Array(n); + t.nvertices = new Uint32Array(n); + t.nfaces = new Uint32Array(n); + t.nerrors = new Float32Array(n); + t.nspheres = new Float32Array(n*5); + t.nsize = new Float32Array(n); + t.nfirstpatch = new Uint32Array(n); + + for(let i = 0; i < n; i++) { + t.noffsets[i] = padding*getUint32(view); //offset + t.nvertices[i] = getUint16(view); //verticesCount + t.nfaces[i] = getUint16(view); //facesCount + t.nerrors[i] = getFloat32(view); + view.offset += 8; //skip cone + for(let k = 0; k < 5; k++) + t.nspheres[i*5+k] = getFloat32(view); //sphere + tight + t.nfirstpatch[i] = getUint32(view); //first patch + } + t.sink = n -1; + + t.patches = new Uint32Array(view.buffer, view.offset, t.patchesCount*3); //noded, lastTriangle, texture + t.nroots = t.nodesCount; + for(let j = 0; j < t.nroots; j++) { + for(let i = t.nfirstpatch[j]; i < t.nfirstpatch[j+1]; i++) { + if(t.patches[i*3] < t.nroots) + t.nroots = t.patches[i*3]; + } + } + + view.offset += t.patchesCount*12; + + t.textures = new Uint32Array(t.texturesCount); + t.texref = new Uint32Array(t.texturesCount); + for(let i = 0; i < t.texturesCount; i++) { + t.textures[i] = padding*getUint32(view); + view.offset += 16*4; //skip proj matrix + } + + t.vsize = 12 + (t.vertex.normal?6:0) + (t.vertex.color?4:0) + (t.vertex.texCoord?8:0); + t.fsize = 6; + + //problem: I have no idea how much space a texture is needed in GPU. 10x factor assumed. + let tmptexsize = new Uint32Array(n-1); + let tmptexcount = new Uint32Array(n-1); + for(let i = 0; i < n-1; i++) { + for(let p = t.nfirstpatch[i]; p != t.nfirstpatch[i+1]; p++) { + let tex = t.patches[p*3+2]; + tmptexsize[i] += t.textures[tex+1] - t.textures[tex]; + tmptexcount[i]++; + } + t.nsize[i] = t.vsize*t.nvertices[i] + t.fsize*t.nfaces[i]; + } + for(let i = 0; i < n-1; i++) { + t.nsize[i] += 10*tmptexsize[i]/tmptexcount[i]; + } + + t.status = new Uint8Array(n); //0 for none, 1 for ready, 2+ for waiting data + t.frames = new Uint32Array(n); + t.errors = new Float32Array(n); //biggest error of instances + t.reqAttempt = new Uint8Array(n); + + t.isReady = true; + for(let callback of t.onLoad) + callback(this); + }, + + importAttribute: function(view) { + let a = {}; + a.type = view.getUint8(view.offset++, true); + a.size = view.getUint8(view.offset++, true); + a.glType = attrGlMap[a.type]; + a.normalized = a.type < 7; + a.stride = attrSizeMap[a.type]*a.size; + if(a.size == 0) return null; + return a; + }, + + importElement: function(view) { + let e = []; + for(let i = 0; i < 8; i++) + e[i] = this.importAttribute(view); + return e; + }, + + importVertex: function(view) { //enum POSITION, NORMAL, COLOR, TEXCOORD, DATA0 + const e = this.importElement(view); + let color = e[2]; + if(color) { + color.type = 2; //unsigned byte + color.glType = attrGlMap[2]; + } + return { position: e[0], normal: e[1], color: e[2], texCoord: e[3], data: e[4] }; + }, + + //enum INDEX, NORMAL, COLOR, TEXCOORD, DATA0 + importFace: function(view) { + const e = this.importElement(view); + let color = e[2]; + if(color) { + color.type = 2; //unsigned byte + color.glType = attrGlMap[2]; + } + return { index: e[0], normal: e[1], color: e[2], texCoord: e[3], data: e[4] }; + }, + + importSignature: function(view) { + let s = {}; + s.vertex = this.importVertex(view); + s.face = this.importFace(view); + s.flags = getUint32(view); + return s; + }, + + importHeader: function(view) { + const magic = getUint32(view); + if(magic != 0x4E787320) return null; + let h = {}; + h.version = getUint32(view); + h.verticesCount = getUint64(view); + h.facesCount = getUint64(view); + h.signature = this.importSignature(view); + h.nodesCount = getUint32(view); + h.patchesCount = getUint32(view); + h.texturesCount = getUint32(view); + h.sphere = { + center: [getFloat32(view), getFloat32(view), getFloat32(view)], + radius: getFloat32(view) + }; + return h; + }, + //OVERRIDE THESE METHOS + + //assemble node and geometry + createNode: function(id) {}, + + createNodeGeometry: function(id, data) {}, + deleteNodeGeometry: function(id) {}, + + createTexture: function(id, image) {}, + deleteTexture: function(id) {}, + }; + + var targetError = 2.0; //error won't go lower than this if we reach it + var maxError = 15; //error won't go over this even if fps is low + var minFps = 15; + var maxPending = 3; + var maxReqAttempt$1 = 2; + var maxCacheSize = 1024 *(1<<20); + + function _Cache() { + let t = this; + t.cortopath = '.'; + t.frame = 0; //keep track of the time + + t.maxCacheSize = maxCacheSize; + t.minFps = minFps; + t.currentFps = 0; + t.targetError = targetError; + t.currentError = targetError; + t.maxError = maxError; + t.realError = 0; + + t.pending = 0; + t.maxPending = 3; + t.cacheSize = 0; + t.candidates = []; //list of nodes to be loaded + t.nodes = new Map(); //for each mesh a list of node ids. + + t.last_frametime = 0; + t.frametime = 0; + t.end_frametime = 0; + + t.debug = { + verbose : false, //debug messages + nodes : false, //color each node + draw : false, //final rendering call disabled + extract : false, //extraction disabled} + }; + + t.totswapped = 0; //in the last frame. + t.swaprate = 0; + t.lastupdate = performance.now(); + } + + _Cache.prototype = { + getTargetError: function() { return this.targetError; }, + getMinFps: function() { return this.minFps; }, + setMinFps: function(fps) { this.minFps = fps; }, + getMaxCacheSize: function() { return this.maxCacheSize; }, + setMaxCacheSize: function(size) { this.maxCacheSize = size; }, + setTargetError: function(error) { this.targetError = error; }, + + loadCorto: function() { + let corto = new Worker(this.cortopath + '/corto.em.js'); + corto.requests = {}; + corto.count = 0; + corto.postRequest = function(node) { + corto.postMessage({ buffer: node.buffer, request:this.count, rgba_colors: true, short_index: true, short_normals: true}); + node.buffer = null; + this.requests[this.count++] = node; + }; + corto.onmessage = function(e) { + var request = e.data.request; + var node = this.requests[request]; + delete this.requests[request]; + node.model = e.data.model; + node.cache.readyGeometryNode(node.mesh, node.id, node.model); + }; + this.corto = corto; + }, + + + beginFrame: function(fps) { //each context has a separate frame count. + let c = this; + c.frametime = performance.now(); + let elapsed = c.frametime - c.last_frametime; + c.last_frametime = c.frametime; + if(elapsed < 500) + c.currentFps = 0.9*c.currentFps + 0.1*(1000/elapsed); + + fps = c.currentFps; + + c.frame++; + c.candidates = []; + if(fps && c.minFps) { + c.currentFps = fps; + const r = c.minFps/fps; + if(r > 1.1) + c.currentError *= 1.05; + if(r < 0.9) + c.currentError *= 0.95; + + c.currentError = Math.max(c.targetError, Math.min(c.maxError, c.currentError)); + + } else + c.currentError = c.targetError; + + c.rendered = 0; + c.realError = 1e20; + c.totswapped = 0; + }, + + endFrame: function() { + this.update(); + }, + + + + requestNode: function(mesh, id) { + mesh.status[id] = 2; + + + this.cacheSize += mesh.nsize[id]; + mesh.reqAttempt[id] = 0; + + if(!this.nodes.has(mesh)) + this.nodes.set(mesh, new Set()); + this.nodes.get(mesh).add(id); + + this.requestNodeGeometry(mesh, id); + this.requestNodeTexture(mesh, id); + }, + + requestNodeGeometry: function(mesh, id) { + this.pending++; + mesh.status[id]++; + let url = mesh.deepzoom ? mesh.baseurl + id + ".nxn" : mesh.url; + let start = mesh.deepzoom ? 0 : mesh.noffsets[id]; + let end = mesh.deepzoom ? 0 : mesh.noffsets[id+1]; + let request = mesh.georeq[id] = mesh.httpRequest(url, + start, + end, + ()=>{ + delete mesh.georeq[id]; + this.loadNodeGeometry(request, mesh, id); + this.pending--; + }, + ()=>{ + delete mesh.georeq[id]; + if(this.debug.verbose) console.log("Geometry request error!"); + this.recoverNode(mesh, id, 0); + this.pending--; + }, + ()=>{ + delete mesh.georeq[id]; + if(this.debug.verbose) console.log("Geometry request abort!"); + this.removeNode(mesh, id); + this.pending--; + }, + 'arraybuffer' + ); + }, + + requestNodeTexture: function(mesh, id) { + + if(!mesh.vertex.texCoord) return; + + let tex = mesh.patches[mesh.nfirstpatch[id]*3+2]; + mesh.texref[tex]++; + mesh.status[id]++; + + let url = mesh.deepzoom ? mesh.baseurl + tex + ".jpg" : mesh.url; + let start = mesh.deepzoom ? 0 : mesh.textures[tex]; + let end = mesh.deepzoom ? 0 : mesh.textures[tex+1]; + let request = mesh.texreq[tex] = mesh.httpRequest(url, + start, + end, + ()=>{ + delete mesh.texreq[tex]; + this.loadNodeTexture(request, mesh, id, tex); + + }, + ()=>{ + delete mesh.texreq[tex]; + if(this.debug.verbose) console.log("Texture request error!"); + this.recoverNode(mesh, id, 1); + }, + ()=>{ + delete mesh.texreq[tex]; + if(this.debug.verbose) console.log("Texture request abort!"); + this.removeNode(mesh, id); + }, + 'blob' + ); + }, + + recoverNode: function(mesh, id, mode) { + if(mesh.status[id] == 0) return; + + mesh.status[id]--; + + let t = this; + + if(mesh.reqAttempt[id] > maxReqAttempt$1) { + if(this.debug.verbose) console.log("Max request limit for " + m.url + " node: " + n); + t.removeNode(mesh, id); + return; + } + + mesh.reqAttempt[id]++; + + switch (mode){ + case 0: + t.requestNodeGeometry(mesh, id); + if(this.debug.verbose) console.log("Recovering geometry for " + m.url + " node: " + n); + break; + case 1: + t.requestNodeTexture(mesh, id); + if(this.debug.verbose) console.log("Recovering texture for " + m.url + " node: " + n); + break; + } + }, + + loadNodeGeometry: function(request, mesh, id) { + if(mesh.status[id] == 0) return; + + if(!mesh.compressed) + this.readyGeometryNode(mesh, id, request.response); + else { + if(!this.corto) this.loadCorto(); + this.corto.postRequest( { mesh:mesh, id:id, buffer: request.response, cache: this }); + } + }, + + + loadNodeTexture: function(request, mesh, id, texid) { + if(mesh.status[id] == 0) { + throw "Should not load texture twice"; + } + + let blob = request.response; + let callback = (img) => { + if(mesh.status[id] == 0) //call was aborted. + return; + mesh.createTexture(texid, img); + + mesh.status[id]--; + + if(mesh.status[id] == 2) + this.readyNode(mesh, id); + }; + + if(typeof createImageBitmap != 'undefined') { + try { + createImageBitmap(blob, { imageOrientation: 'flipY' }).then(callback); + } catch(error) { //old version of firefox do not support options (but flip anyway) + createImageBitmap(blob).then(callback); + } + + } else { //fallback for IOS + var urlCreator = window.URL || window.webkitURL; + var img = document.createElement('img'); + img.onerror = function(e) { console.log("Texture loading error!"); }; + img.src = urlCreator.createObjectURL(blob); + + img.onload = function() { + urlCreator.revokeObjectURL(img.src); + callback(img); + }; + } + }, + + removeNode: function(mesh, id) { + this.nodes.get(mesh).delete(id); + + if(mesh.status[id] == 0) { + if(this.debug.verbose) + console.log("Double remove due to abort."); + return; //TODO + } + + mesh.status[id] = 0; + if (id in mesh.georeq && mesh.georeq[id].readyState != 4) { + mesh.georeq[id].abort(); + delete mesh.georeq[id]; + //this.pending--; + } + mesh.availableNodes--; + this.cacheSize -= mesh.nsize[id]; + mesh.deleteNodeGeometry(id); + + if(!mesh.vertex.texCoord) return; + + const tex = mesh.patches[mesh.nfirstpatch[id]*3+2]; //TODO assuming one texture per node + + if (tex in mesh.texreq && mesh.texreq[tex].readyState != 4) { + mesh.texreq[tex].abort(); + delete mesh.texreq[tex]; + } + + mesh.texref[tex]--; + if(mesh.texref[tex] == 0) { + mesh.deleteTexture(tex); + } + }, + + readyGeometryNode: function(mesh, id, buffer) { + if(mesh.status[id] == 0) //call was aborted + return; + const nv = mesh.nvertices[id]; + const nf = mesh.nfaces[id]; + let geometry = {}; + + + if(!mesh.corto) { + geometry.index = new Uint16Array(buffer, nv*mesh.vsize, nf*3); + geometry.position = new Float32Array(buffer, 0, nv*3); + + var off = nv*12; + if(mesh.vertex.texCoord) { + geometry.uv = new Float32Array(buffer, off, nv*2); + off += nv*8; + } + if(mesh.vertex.normal) { + geometry.normal = new Int16Array(buffer, off, nv*3); + off += nv*6; + } + + if(mesh.vertex.color) { + geometry.color = new Uint8Array(buffer, off, nv*4); + off += nv*4; + } + + + } else { + geometry = buffer; + } + + //if(nf == 0) + // scramble(nv, v, no, co); + + mesh.createNodeGeometry(id, geometry); + mesh.status[id]--; + + if(mesh.status[id] == 2) { + this.readyNode(mesh, id); + } + }, + + //the node is finished, add to cache, and update counters + readyNode: function(mesh, id) { + + mesh.status[id]--; + if(mesh.status[id] != 1) throw "A ready node should have status ==1" + + + mesh.reqAttempt[id] = 0; + //this.pending--; + mesh.createNode(id); + mesh.availableNodes++; + + for(let callback of mesh.onUpdate) + callback(); + this.update(); + }, + + flush: function(mesh) { + if(!this.nodes.has(mesh)) return; + + for(let id of this.nodes.get(mesh)) + this.removeNode(mesh, id); + this.nodes.delete(mesh); + }, + + update: function() { + if(this.pending >= maxPending) + return; + + //the best candidate has the highest error + let best = null; + for(let c of this.candidates) { + if(c.mesh.status[c.id] == 0 && (!best || c.error > best.error)) + best = c; + } + if(!best) return; + + // record amount of data transfer per second. + /* + let now = performance.now(); + if(Math.floor(now/1000) > Math.floor(this.lastupdate/1000)) { //new second + this.swaprate = (this.totswapped/1000)/(now - this.lastupdate); //transfer in mb/s + if(this.debug.verbose) + console.log("Memory loaded in GPU: ", this.swaprate); + this.totswapped = 0; + this.lastupdate = now; + } + */ + + + //make room for new nodes! + + while(this.cacheSize > this.maxCacheSize) { + let worst = null; + + //find node with smallest error in cache and remove it if worse than the best candidate. + for(let [mesh, ids] of this.nodes) { + for(let id of ids) { + //we need to recompute the errors for the cache, as if not traversed doesn't get updated. + let error = mesh.errors[id]; + let frame = mesh.frames[id]; + if( !worst || error < worst.error) { + worst = { id: id, frame: frame, error: error, mesh: mesh }; + } + } + } + if(!worst || worst.error >= best.error*0.9) { + //(worst.frame + 30 >= best.frame && )) //dont' remove if the best candidate is not good enogh + return; + } + this.removeNode(worst.mesh, worst.id); + } + this.totswapped += best.mesh.nsize[best.id]; + this.candidates = this.candidates.filter(e => e.mesh == best.mesh && e.id == best.id); + this.requestNode(best.mesh, best.id); + this.update(); //try again. + } + }; + + let Cache = new _Cache; + + class Nexus3D extends THREE__namespace.Mesh { + + constructor(url, renderer, options) { + + super(); + if(typeof renderer == 'function') + throw "Nexus3D constructor has changed: Nexus3D(url, renderer, options) where options include: onLoad, onUpdate, onProgress and material" + + this.patchWebGLRenderer(renderer); + + Object.assign(this, { + isNXS: true, + type:'NXS', + url: url, + gl: renderer.getContext(), + material: null, + + autoUpdate: true, + mesh: new Mesh(), + vbo: [], + ibo: [], + vao: [], + textures: [], + attributes: {}, //here we store the uniform attributes of the shader. + + basemesh: null, //highest level of the nexus, for picking + }); + + if('material' in options) + this.material = options.material; + if(!this.material) + this.material = new THREE__namespace.MeshStandardMaterial(); + + for(let call of ['onLoad', 'onUpdate', 'onProgress']) { + this['_' + call] = []; + if(call in options) + this['_' + call].push(options[call]); + } + + if(this.url) { + if(typeof url == 'object') { + this.nxs = this.url; + this.nxs.onLoad.push((m) => { + this.mesh = this.nxs.mesh; + this.traversal = this.nxs.traversal; + this.cache = this.nxs.cache; + this.vbo = this.nxs.vbo; + this.ibo = this.nxs.ibo; + this.vao = this.nxs.vao; + this.textures = this.nxs.textures; + this.onLoadCallback(this); + }); + } else + this.open(this.url); + } + } + + copy(source) { + Object3D.prototype.copy.call( this, source, false ); + throw "Can't really copy." + } + + + open(url) { + let t = this; + this.mesh.open(url); + this.mesh.createNode = (id) => { }; + this.mesh.createNodeGeometry = (id, geometry) => { t.createNodeGeometry(id, geometry); }; + this.mesh.createTexture = (id, image) => { t.createTexture(id, image); }; + this.mesh.deleteNodeGeometry = (id) => { t.deleteNodeGeometry(id); }; + this.mesh.deleteTexture = (id) => { t.deleteTexture(id); }; + this.mesh.onLoad.push(() => { t.onLoadCallback(); }); + this.mesh.onUpdate.push(() => { + for(let callback of t._onUpdate) callback(this); + for(let callback of t._onProgress) callback(this, this.mesh.availableNodes, this.mesh.nodesCount); + }); + + this.traversal = new Traversal(); + this.cache = Cache; //new Cache(); + this.textures = {}; + } + + set onLoad(callback) { + this._onLoad.push(callback); + } + + set onUpdate(callback) { + this._onUpdate.push(callback); + } + + set onProgress(callback) { + this._onProgress.push(callback); + } + + //TODO this is not really needed, we might just conform to THREEJS standard of updating. + /*set material(material) { + this.material = material; + this.material.needsUpdate = true; + }*/ + + updateMaterials() { + if(this.material.map !== false && this.mesh.vertex.texCoord) + this.material.map = this.material_texture; + + if(this.mesh.vertex.color) +<<<<<<< HEAD + this.material.vertexColors = true; +======= + this.material.vertexColors = THREE__namespace.VertexColors; +>>>>>>> c5d1f5c9885a0ab0f0ea816a162bce0d1d14c02b + this.material.needsUpdate = true; + } + + onLoadCallback() { + const c = this.mesh.sphere.center; + const center = new THREE__namespace.Vector3(c[0], c[1], c[2]); + const radius = this.mesh.sphere.radius; + this.boundingSphere = new THREE__namespace.Sphere(center, radius); + + var geometry = new THREE__namespace.BufferGeometry(); + + geometry.setAttribute( 'position', new THREE__namespace.BufferAttribute(new Float32Array(3), 3)); + + if(this.mesh.vertex.normal) + geometry.setAttribute( 'normal', new THREE__namespace.BufferAttribute(new Float32Array(3), 3)); + if(this.mesh.vertex.color) + geometry.setAttribute( 'color', new THREE__namespace.BufferAttribute(new Float32Array(4), 4)); + if(this.mesh.vertex.texCoord) + geometry.setAttribute( 'uv', new THREE__namespace.BufferAttribute(new Float32Array(2), 2)); + + if(this.mesh.vertex.texCoord) { +<<<<<<< HEAD + this.material_texture = new THREE.DataTexture( new Uint8Array([1, 1, 1, 1]), 1, 1, THREE.RGBAFormat ); +======= + this.material_texture = new THREE__namespace.DataTexture( new Uint8Array([1, 1, 1]), 1, 1, THREE__namespace.RGBFormat ); +>>>>>>> c5d1f5c9885a0ab0f0ea816a162bce0d1d14c02b + this.material_texture.needsUpdate = true; + } + + this.updateMaterials(); + this.geometry = geometry; + + this.frustumCulled = false; + + for(let callback of this._onLoad) + callback(this); + } + + renderBufferDirect(renderer, scene, camera, geometry, material, group) { + let s = new THREE__namespace.Vector2(); + renderer.getSize(s); + + //object modelview is multiplied by camera during rendering, we need to do it here for visibility computations + this.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, this.matrixWorld ); + this.traversal.updateView([0, 0, s.width, s.height], camera.projectionMatrix.elements, this.modelViewMatrix.elements); + this.instance_errors = this.traversal.traverse(this.mesh, this.cache); + + //threejs increments version when setting neeedsUpdate + /*if(this.material.version > 0) { + this.updateMaterials(); + this.material.version = 0; + for(let callback of this.onUpdate) + callback(this); + }*/ + let gl = this.gl; + let program = gl.getParameter(gl.CURRENT_PROGRAM); + + //TODO these calls could be cached saving attrs per each material. + let attr = this.attributes; + attr.position = gl.getAttribLocation(program, "position"); + attr.normal = gl.getAttribLocation(program, "normal"); + attr.color = gl.getAttribLocation(program, "color"); + attr.uv = gl.getAttribLocation(program, "uv"); + attr.size = gl.getUniformLocation(program, "size"); + attr.scale = gl.getUniformLocation(program, "scale"); + + let map_location = gl.getUniformLocation(program, "map"); + attr.map = map_location ? gl.getUniform(program, map_location) : null; + + + //hack to detect if threejs using point or triangle shaders + //instance.mode = attr.size ? "POINT" : "FILL"; + //if(attr.size != -1) + // instance.pointsize = material.size; + + //can't find docs or code on how material.scale is computed in threejs. + //if(attr.scale != -1) + // instance.pointscale = 2.0; + + this.setVisibility(); + } + + setVisibility() { + //set visibile what is visible! + let t = this.traversal; + let m = this.mesh; + + if(!m.isReady) + return; + let rendered = 0; + + let attr = this.attributes; + let mesh = this.mesh; + let gl = this.gl; + let gl2 = gl instanceof WebGL2RenderingContext; + let state = attr.uv + 10*attr.color + 100*attr.normal; + + for(let id = 0; id < m.nodesCount; id++) { + // let err = m.nerrors[id]; + + if(!t.selected[id]) continue; + + //check for children: if all are selected, bail out. + { + let visible = false; + m.nfirstpatch[id+1]-1; + for (var p = m.nfirstpatch[id]; p < m.nfirstpatch[id+1]; ++p) { + var child = m.patches[p*3]; + + if(!t.selected[child]) { + visible = true; + break; + } + } + if(!visible) continue; + } + + var sp = m.nspheres; + var off = id*5; + if(!t.isVisible(sp[off], sp[off+1], sp[off+2], sp[off+4])) //tight radius + continue; + + let doBind = true; + + if(gl2) { + if(state in this.vao[id]) + doBind = false; + else + this.vao[id][state] = gl.createVertexArray(); + + gl.bindVertexArray(this.vao[id][state]); + } + + if(doBind) { + gl.bindBuffer(gl.ARRAY_BUFFER, this.vbo[id]); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.ibo[id]); + + gl.vertexAttribPointer(attr.position, 3, gl.FLOAT, false, 12, 0); + gl.enableVertexAttribArray(attr.position); + + let nv = this.mesh.nvertices[id]; + let offset = nv*12; + + if(mesh.vertex.texCoord) { + if(attr.uv >= 0) { + gl.vertexAttribPointer(attr.uv, 2, gl.FLOAT, false, 8, offset); + gl.enableVertexAttribArray(attr.uv); + } + offset += nv*8; + } + if(mesh.vertex.color) { + if(attr.color >= 0) { + gl.vertexAttribPointer(attr.color, 4, gl.UNSIGNED_BYTE, true, 4, offset); + gl.enableVertexAttribArray(attr.color); + } + offset += nv*4; + } + if(mesh.vertex.normal) { + if(attr.normal >= 0) { + gl.vertexAttribPointer(attr.normal, 3, gl.SHORT, true, 6, offset); + gl.enableVertexAttribArray(attr.normal); + } + } + + if(this.cache.debug.nodes) { + gl.disableVertexAttribArray(attr.color); + + var error = this.instance_errors[id]; //this.mesh.errors[id]; + var palette = [ + [1, 1, 1, 1], //white + [1, 1, 1, 1], //white + [0, 1, 0, 1], //green + [0, 1, 1, 1], //cyan + [1, 1, 0, 1], //yellow + [1, 0, 1, 1], //magenta + [1, 0, 0, 1] //red + ]; + let w = Math.min(5.99, Math.max(0, Math.log2(error)/2)); + let low = Math.floor(w); + w -= low; + let color = []; + for( let k = 0; k < 4; k++) + color[k] = palette[low][k]*(1-w) + palette[low+1][k]*w; + + gl.vertexAttrib4fv(attr.color, color); + } + } + this.cache.realError = Math.min(this.mesh.errors[id], this.cache.realError); + + let offset = 0; + let end = 0; + let last = m.nfirstpatch[id+1]-1; + for (let p = m.nfirstpatch[id]; p < m.nfirstpatch[id+1]; ++p) { + let child = m.patches[p*3]; + + if(!t.selected[child]) { + end = m.patches[p*3+1]; + if(p < last) //we join patches if possible. + continue; + } + + if(end > offset) { + if(m.vertex.texCoord && attr.uv >= 0) { + var tex = m.patches[p*3+2]; + if(tex != -1) { //bind texture + var texid = this.textures[tex]; + gl.activeTexture(gl.TEXTURE0 + attr.map); + gl.bindTexture(gl.TEXTURE_2D, texid); + } + } + let mode = this.material.wireframe ? gl.LINE_STRIP : gl.TRIANGLES; + + gl.drawElements(mode, (end - offset) * 3, gl.UNSIGNED_SHORT, offset * 6); + rendered += end - offset; + } + offset = m.patches[p*3+1]; + } + } + this.cache.rendered += rendered; + } + + createNodeGeometry(id, data) { + let m = this.mesh; + var nv = m.nvertices[id]; + m.nfaces[id]; + let indices = data.index; + let vertices = new ArrayBuffer(nv*m.vsize); + var position = new Float32Array(vertices, 0, nv*3); + position.set(data.position); + var off = nv*12; + if(m.vertex.texCoord) { + var uv = new Float32Array(vertices, off, nv*2); + uv.set(data.uv); + off += nv*8; + } + if(m.vertex.color) { + var color = new Uint8Array(vertices, off, nv*4); + color.set(data.color); + off += nv*4; + } + if(m.vertex.normal) { + var normal = new Int16Array(vertices, off, nv*3); + normal.set(data.normal); + off += nv*6; + } + + //needed for approximate picking. + if(id < this.mesh.nroots) { + let basegeometry = new THREE__namespace.BufferGeometry(); + basegeometry.setAttribute( 'position', new THREE__namespace.BufferAttribute(data.position, 3 ) ); + basegeometry.setAttribute( 'normal', new THREE__namespace.BufferAttribute(data.normal, 3 ) ); + basegeometry.setIndex(new THREE__namespace.BufferAttribute( data.index, 1 ) ); + + this.basemesh = new THREE__namespace.Mesh(basegeometry, this.material); + this.basemesh.visible = false; + this.add(this.basemesh); + } + + var gl = this.gl; + this.vao[id] = {}; //one for each attrib combination in use (casting shadows for example). + gl.bindVertexArray(null); + + var vbo = this.vbo[id] = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, vbo); + gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW); + var ibo = this.ibo[id] = gl.createBuffer(); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW); + } + + createTexture(id, image) { + let gl = this.gl; + var flip = gl.getParameter(gl.UNPACK_FLIP_Y_WEBGL); + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); + let tex = this.textures[id] = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex); + + //TODO some textures might be alpha only! save space + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + + function powerOf2(n) { return n && (n & (n - 1)) === 0; } + if(!(gl instanceof WebGLRenderingContext) || (powerOf2(image.width) && powerOf2(image.height))) { + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST_MIPMAP_LINEAR); + gl.generateMipmap(gl.TEXTURE_2D); + } else { + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + } + + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flip); + } + //schedule for removal of this node( might not want to delete it in the middle of something. + //TODO check if this is really needed! + deleteNodeGeometry(id) { + this.gl.deleteBuffer(this.vbo[id]); + this.gl.deleteBuffer(this.ibo[id]); + + this.vbo[id] = this.ibo[id] = null; + if(this.vao[id]) //node might have been loaded and unloaded before a rendering + for(const [state, vao] of Object.entries(this.vao[id])) + this.gl.deleteVertexArray(vao); + this.vao[id] = null; + } + + deleteTexture(tex) { + if(!this.textures[tex]) + return; + // throw "Deleting missing texture!" + + this.gl.deleteTexture(this.textures[tex]); + this.textures[tex] = 0; + } + + flush() { + this.cache.flush(this.mesh); + } + + dispose() { + this.flush(); + for(let child of this.children) + child.geometry.dispose(); + } + + toJSON(meta) { + throw "Can't"; + } + + patchWebGLRenderer(renderer) { + if(renderer.nexusPatched) return; + let f = renderer.renderBufferDirect; + renderer.renderBufferDirect = ( camera, scene, geometry, material, object, group) => { + f( camera, scene, geometry, material, object, group ); + if ( object.renderBufferDirect) + object.renderBufferDirect(renderer, scene, camera, geometry, material, group); + }; + renderer.originalRender = renderer.render; + renderer.render = (scene, camera) => { + Cache.beginFrame(30); + renderer.originalRender(scene, camera); + Cache.endFrame(); + }; + renderer.nexusPatched = true; + } + + } + + exports.Cache = Cache; + exports.Nexus3D = Nexus3D; + + Object.defineProperty(exports, '__esModule', { value: true }); + +})); diff --git a/nexus3d/build/nexus3D.min.js b/nexus3d/build/nexus3D.min.js new file mode 100644 index 00000000..da14e87a --- /dev/null +++ b/nexus3d/build/nexus3D.min.js @@ -0,0 +1 @@ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("three")):"function"==typeof define&&define.amd?define(["exports","three"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).Nexus3D={},e.THREE)}(this,(function(e,t){"use strict";function r(e){if(e&&e.__esModule)return e;var t=Object.create(null);return e&&Object.keys(e).forEach((function(r){if("default"!==r){var o=Object.getOwnPropertyDescriptor(e,r);Object.defineProperty(t,r,o.get?o:{enumerable:!0,get:function(){return e[r]}})}})),t.default=e,Object.freeze(t)}var o=r(t);function s(e){var t=e.getUint32(e.offset,!0),r=e.getUint32(e.offset+4,!0);return e.offset+=8,1*r+t}function i(e){var t=e.getUint32(e.offset,!0);return e.offset+=4,t}function a(e){var t=e.getUint16(e.offset,!0);return e.offset+=2,t}function h(e){var t=e.getFloat32(e.offset,!0);return e.offset+=4,t}function l(e,t){var r=1/(e[12]*e[9]*e[6]*e[3]-e[8]*e[13]*e[6]*e[3]-e[12]*e[5]*e[10]*e[3]+e[4]*e[13]*e[10]*e[3]+e[8]*e[5]*e[14]*e[3]-e[4]*e[9]*e[14]*e[3]-e[12]*e[9]*e[2]*e[7]+e[8]*e[13]*e[2]*e[7]+e[12]*e[1]*e[10]*e[7]-e[0]*e[13]*e[10]*e[7]-e[8]*e[1]*e[14]*e[7]+e[0]*e[9]*e[14]*e[7]+e[12]*e[5]*e[2]*e[11]-e[4]*e[13]*e[2]*e[11]-e[12]*e[1]*e[6]*e[11]+e[0]*e[13]*e[6]*e[11]+e[4]*e[1]*e[14]*e[11]-e[0]*e[5]*e[14]*e[11]-e[8]*e[5]*e[2]*e[15]+e[4]*e[9]*e[2]*e[15]+e[8]*e[1]*e[6]*e[15]-e[0]*e[9]*e[6]*e[15]-e[4]*e[1]*e[10]*e[15]+e[0]*e[5]*e[10]*e[15]);t[0]=(e[9]*e[14]*e[7]-e[13]*e[10]*e[7]+e[13]*e[6]*e[11]-e[5]*e[14]*e[11]-e[9]*e[6]*e[15]+e[5]*e[10]*e[15])*r,t[1]=(e[13]*e[10]*e[3]-e[9]*e[14]*e[3]-e[13]*e[2]*e[11]+e[1]*e[14]*e[11]+e[9]*e[2]*e[15]-e[1]*e[10]*e[15])*r,t[2]=(e[5]*e[14]*e[3]-e[13]*e[6]*e[3]+e[13]*e[2]*e[7]-e[1]*e[14]*e[7]-e[5]*e[2]*e[15]+e[1]*e[6]*e[15])*r,t[3]=(e[9]*e[6]*e[3]-e[5]*e[10]*e[3]-e[9]*e[2]*e[7]+e[1]*e[10]*e[7]+e[5]*e[2]*e[11]-e[1]*e[6]*e[11])*r,t[4]=(e[12]*e[10]*e[7]-e[8]*e[14]*e[7]-e[12]*e[6]*e[11]+e[4]*e[14]*e[11]+e[8]*e[6]*e[15]-e[4]*e[10]*e[15])*r,t[5]=(e[8]*e[14]*e[3]-e[12]*e[10]*e[3]+e[12]*e[2]*e[11]-e[0]*e[14]*e[11]-e[8]*e[2]*e[15]+e[0]*e[10]*e[15])*r,t[6]=(e[12]*e[6]*e[3]-e[4]*e[14]*e[3]-e[12]*e[2]*e[7]+e[0]*e[14]*e[7]+e[4]*e[2]*e[15]-e[0]*e[6]*e[15])*r,t[7]=(e[4]*e[10]*e[3]-e[8]*e[6]*e[3]+e[8]*e[2]*e[7]-e[0]*e[10]*e[7]-e[4]*e[2]*e[11]+e[0]*e[6]*e[11])*r,t[8]=(e[8]*e[13]*e[7]-e[12]*e[9]*e[7]+e[12]*e[5]*e[11]-e[4]*e[13]*e[11]-e[8]*e[5]*e[15]+e[4]*e[9]*e[15])*r,t[9]=(e[12]*e[9]*e[3]-e[8]*e[13]*e[3]-e[12]*e[1]*e[11]+e[0]*e[13]*e[11]+e[8]*e[1]*e[15]-e[0]*e[9]*e[15])*r,t[10]=(e[4]*e[13]*e[3]-e[12]*e[5]*e[3]+e[12]*e[1]*e[7]-e[0]*e[13]*e[7]-e[4]*e[1]*e[15]+e[0]*e[5]*e[15])*r,t[11]=(e[8]*e[5]*e[3]-e[4]*e[9]*e[3]-e[8]*e[1]*e[7]+e[0]*e[9]*e[7]+e[4]*e[1]*e[11]-e[0]*e[5]*e[11])*r,t[12]=(e[12]*e[9]*e[6]-e[8]*e[13]*e[6]-e[12]*e[5]*e[10]+e[4]*e[13]*e[10]+e[8]*e[5]*e[14]-e[4]*e[9]*e[14])*r,t[13]=(e[8]*e[13]*e[2]-e[12]*e[9]*e[2]+e[12]*e[1]*e[10]-e[0]*e[13]*e[10]-e[8]*e[1]*e[14]+e[0]*e[9]*e[14])*r,t[14]=(e[12]*e[5]*e[2]-e[4]*e[13]*e[2]-e[12]*e[1]*e[6]+e[0]*e[13]*e[6]+e[4]*e[1]*e[14]-e[0]*e[5]*e[14])*r,t[15]=(e[4]*e[9]*e[2]-e[8]*e[5]*e[2]+e[8]*e[1]*e[6]-e[0]*e[9]*e[6]-e[4]*e[1]*e[10]+e[0]*e[5]*e[10])*r}function u(e){this.error=new Float32Array(e),this.data=new Int32Array(e),this.size=0}function d(){let e=this;e.maxBlocked=30,e.modelMatrix=new Float32Array(16),e.viewMatrix=new Float32Array(16),e.projectionMatrix=new Float32Array(16),e.modelView=new Float32Array(16),e.modelViewInv=new Float32Array(16),e.modelViewProj=new Float32Array(16),e.modelViewProjInv=new Float32Array(16),e.planes=new Float32Array(24),e.viewport=new Float32Array(4),e.viewpoint=new Float32Array(4)}u.prototype={push:function(e,t){this.data[this.size]=e,this.error[this.size]=t,this.bubbleUp(this.size),this.size++},pop:function(){var e=this.data[0];return this.size--,this.size>0&&(this.data[0]=this.data[this.size],this.error[0]=this.error[this.size],this.sinkDown(0)),e},bubbleUp:function(e){for(var t=this.data[e],r=this.error[e];e>0;){var o=(e+1>>1)-1,s=this.error[o];if(s>r)break;this.data[e]=this.data[o],this.error[e]=s,this.data[o]=t,this.error[o]=r,e=o}},sinkDown:function(e){for(var t=this.data[e],r=this.error[e];;){var o=2*(e+1),s=o-1,i=-1;if(sr&&(i=s)}if(o(-1==i?r:n)&&(i=o);if(-1==i)break;this.data[e]=this.data[i],this.error[e]=this.error[i],this.data[i]=t,this.error[i]=r,e=i}}},d.prototype={updateView:function(e,t,r){let o=this;for(let e=0;e<16;e++)o.projectionMatrix[e]=t[e],o.modelView[e]=r[e];for(let t=0;t<4;t++)o.viewport[t]=e[t];var s,i,n;s=o.projectionMatrix,i=o.modelView,(n=o.modelViewProj)[0]=s[0]*i[0]+s[4]*i[1]+s[8]*i[2]+s[12]*i[3],n[1]=s[1]*i[0]+s[5]*i[1]+s[9]*i[2]+s[13]*i[3],n[2]=s[2]*i[0]+s[6]*i[1]+s[10]*i[2]+s[14]*i[3],n[3]=s[3]*i[0]+s[7]*i[1]+s[11]*i[2]+s[15]*i[3],n[4]=s[0]*i[4]+s[4]*i[5]+s[8]*i[6]+s[12]*i[7],n[5]=s[1]*i[4]+s[5]*i[5]+s[9]*i[6]+s[13]*i[7],n[6]=s[2]*i[4]+s[6]*i[5]+s[10]*i[6]+s[14]*i[7],n[7]=s[3]*i[4]+s[7]*i[5]+s[11]*i[6]+s[15]*i[7],n[8]=s[0]*i[8]+s[4]*i[9]+s[8]*i[10]+s[12]*i[11],n[9]=s[1]*i[8]+s[5]*i[9]+s[9]*i[10]+s[13]*i[11],n[10]=s[2]*i[8]+s[6]*i[9]+s[10]*i[10]+s[14]*i[11],n[11]=s[3]*i[8]+s[7]*i[9]+s[11]*i[10]+s[15]*i[11],n[12]=s[0]*i[12]+s[4]*i[13]+s[8]*i[14]+s[12]*i[15],n[13]=s[1]*i[12]+s[5]*i[13]+s[9]*i[14]+s[13]*i[15],n[14]=s[2]*i[12]+s[6]*i[13]+s[10]*i[14]+s[14]*i[15],n[15]=s[3]*i[12]+s[7]*i[13]+s[11]*i[14]+s[15]*i[15],l(o.modelViewProj,o.modelViewProjInv),l(o.modelView,o.modelViewInv),o.viewpoint[0]=o.modelViewInv[12],o.viewpoint[1]=o.modelViewInv[13],o.viewpoint[2]=o.modelViewInv[14],o.viewpoint[3]=1;const a=o.modelViewProj,h=o.modelViewProjInv;let u=o.planes;u[0]=a[0]+a[3],u[1]=a[4]+a[7],u[2]=a[8]+a[11],u[3]=a[12]+a[15],u[4]=-a[0]+a[3],u[5]=-a[4]+a[7],u[6]=-a[8]+a[11],u[7]=-a[12]+a[15],u[8]=a[1]+a[3],u[9]=a[5]+a[7],u[10]=a[9]+a[11],u[11]=a[13]+a[15],u[12]=-a[1]+a[3],u[13]=-a[5]+a[7],u[14]=-a[9]+a[11],u[15]=-a[13]+a[15],u[16]=-a[2]+a[3],u[17]=-a[6]+a[7],u[18]=-a[10]+a[11],u[19]=-a[14]+a[15],u[20]=-a[2]+a[3],u[21]=-a[6]+a[7],u[22]=-a[10]+a[11],u[23]=-a[14]+a[15];for(let e=0;e<24;e+=4){let t=Math.sqrt(u[e]*u[e]+u[e+1]*u[e+1]+u[e+2]*u[e+2]);u[e]/=t,u[e+1]/=t,u[e+2]/=t,u[e+3]/=t}const d=h[3]+h[15],c=(h[0]+h[12])/d,f=(h[1]+h[13])/d,m=(h[2]+h[14])/d,p=-h[3]+h[15],x=(-h[0]+h[12])/p-c,v=(-h[1]+h[13])/p-f,b=(-h[2]+h[14])/p-m,g=Math.sqrt(x*x+v*v+b*b),w=h[12]/h[15]-o.viewpoint[0],y=h[13]/h[15]-o.viewpoint[1],A=h[14]/h[15]-o.viewpoint[2],E=2*g/Math.sqrt(w*w+y*y+A*A)/o.viewport[2];o.currentResolution==E?o.sameResolution=!0:o.sameResolution=!1,o.currentResolution=E},traverse:function(e,t){let r=this;if(r.mesh=e,!e.isReady)return;const o=e.nodesCount;r.visited=new Uint8Array(o),r.blocked=new Uint8Array(o),r.selected=new Uint8Array(o),r.frame=t.frame,r.instance_errors=new Float32Array(o),r.frame>e.frame&&(e.errors=new Float32Array(o),e.frame=r.frame),r.visitQueue=new u(o);for(var s=0;s0&&tr.drawBudget)return!1;if(1!=r.mesh.status[e])return!1;const o=r.mesh.nspheres,s=5*e;return r.isVisible(o[s],o[s+1],o[s+2],o[s+3])&&(r.drawSize+=.8*r.mesh.nvertices[e]),!0},nodeError:function(e,t){let r=this;const o=r.mesh.nspheres,s=r.viewpoint,i=5*e,n=o[i+0],a=o[i+1],h=o[i+2];let l=o[i+3];t&&(l=o[i+4]);const u=s[0]-n,d=s[1]-a,c=s[2]-h;let f=Math.sqrt(u*u+d*d+c*c)-l;f<.1&&(f=.1);let m=r.mesh.nerrors[e]/(r.currentResolution*f),p=r.distance(n,a,h,o[i+4]);return p<-l?m/=101:p<0&&(m/=1-p/l*100),m},distance:function(e,t,r,o){const s=this.planes;let i=1e20;for(let n=0;n<24;n+=4){let a=s[n]*e+s[n+1]*t+s[n+2]*r+s[n+3]+o;a1.1&&(t.currentError*=1.05),r<.9&&(t.currentError*=.95),t.currentError=Math.max(t.targetError,Math.min(t.maxError,t.currentError))}else t.currentError=t.targetError;t.rendered=0,t.realError=1e20,t.totswapped=0},endFrame:function(){this.update()},requestNode:function(e,t){e.status[t]=2,this.cacheSize+=e.nsize[t],e.reqAttempt[t]=0,this.nodes.has(e)||this.nodes.set(e,new Set),this.nodes.get(e).add(t),this.requestNodeGeometry(e,t),this.requestNodeTexture(e,t)},requestNodeGeometry:function(e,t){this.pending++,e.status[t]++;let r=e.deepzoom?e.baseurl+t+".nxn":e.url,o=e.deepzoom?0:e.noffsets[t],s=e.deepzoom?0:e.noffsets[t+1],i=e.georeq[t]=e.httpRequest(r,o,s,(()=>{delete e.georeq[t],this.loadNodeGeometry(i,e,t),this.pending--}),(()=>{delete e.georeq[t],this.debug.verbose&&console.log("Geometry request error!"),this.recoverNode(e,t,0),this.pending--}),(()=>{delete e.georeq[t],this.debug.verbose&&console.log("Geometry request abort!"),this.removeNode(e,t),this.pending--}),"arraybuffer")},requestNodeTexture:function(e,t){if(!e.vertex.texCoord)return;let r=e.patches[3*e.nfirstpatch[t]+2];e.texref[r]++,e.status[t]++;let o=e.deepzoom?e.baseurl+r+".jpg":e.url,s=e.deepzoom?0:e.textures[r],i=e.deepzoom?0:e.textures[r+1],n=e.texreq[r]=e.httpRequest(o,s,i,(()=>{delete e.texreq[r],this.loadNodeTexture(n,e,t,r)}),(()=>{delete e.texreq[r],this.debug.verbose&&console.log("Texture request error!"),this.recoverNode(e,t,1)}),(()=>{delete e.texreq[r],this.debug.verbose&&console.log("Texture request abort!"),this.removeNode(e,t)}),"blob")},recoverNode:function(e,t,r){if(0==e.status[t])return;e.status[t]--;let o=this;if(e.reqAttempt[t]>2)return this.debug.verbose&&console.log("Max request limit for "+m.url+" node: "+n),void o.removeNode(e,t);switch(e.reqAttempt[t]++,r){case 0:o.requestNodeGeometry(e,t),this.debug.verbose&&console.log("Recovering geometry for "+m.url+" node: "+n);break;case 1:o.requestNodeTexture(e,t),this.debug.verbose&&console.log("Recovering texture for "+m.url+" node: "+n)}},loadNodeGeometry:function(e,t,r){0!=t.status[r]&&(t.compressed?(this.corto||this.loadCorto(),this.corto.postRequest({mesh:t,id:r,buffer:e.response,cache:this})):this.readyGeometryNode(t,r,e.response))},loadNodeTexture:function(e,t,r,o){if(0==t.status[r])throw"Should not load texture twice";let s=e.response,i=e=>{0!=t.status[r]&&(t.createTexture(o,e),t.status[r]--,2==t.status[r]&&this.readyNode(t,r))};if("undefined"!=typeof createImageBitmap)try{createImageBitmap(s,{imageOrientation:"flipY"}).then(i)}catch(e){createImageBitmap(s).then(i)}else{var n=window.URL||window.webkitURL,a=document.createElement("img");a.onerror=function(e){console.log("Texture loading error!")},a.src=n.createObjectURL(s),a.onload=function(){n.revokeObjectURL(a.src),i(a)}}},removeNode:function(e,t){if(this.nodes.get(e).delete(t),0==e.status[t])return void(this.debug.verbose&&console.log("Double remove due to abort."));if(e.status[t]=0,t in e.georeq&&4!=e.georeq[t].readyState&&(e.georeq[t].abort(),delete e.georeq[t]),e.availableNodes--,this.cacheSize-=e.nsize[t],e.deleteNodeGeometry(t),!e.vertex.texCoord)return;const r=e.patches[3*e.nfirstpatch[t]+2];r in e.texreq&&4!=e.texreq[r].readyState&&(e.texreq[r].abort(),delete e.texreq[r]),e.texref[r]--,0==e.texref[r]&&e.deleteTexture(r)},readyGeometryNode:function(e,t,r){if(0==e.status[t])return;const o=e.nvertices[t],s=e.nfaces[t];let i={};if(e.corto)i=r;else{i.index=new Uint16Array(r,o*e.vsize,3*s),i.position=new Float32Array(r,0,3*o);var n=12*o;e.vertex.texCoord&&(i.uv=new Float32Array(r,n,2*o),n+=8*o),e.vertex.normal&&(i.normal=new Int16Array(r,n,3*o),n+=6*o),e.vertex.color&&(i.color=new Uint8Array(r,n,4*o),n+=4*o)}e.createNodeGeometry(t,i),e.status[t]--,2==e.status[t]&&this.readyNode(e,t)},readyNode:function(e,t){if(e.status[t]--,1!=e.status[t])throw"A ready node should have status ==1";e.reqAttempt[t]=0,e.createNode(t),e.availableNodes++;for(let t of e.onUpdate)t();this.update()},flush:function(e){if(this.nodes.has(e)){for(let t of this.nodes.get(e))this.removeNode(e,t);this.nodes.delete(e)}},update:function(){if(this.pending>=3)return;let e=null;for(let t of this.candidates)0==t.mesh.status[t.id]&&(!e||t.error>e.error)&&(e=t);if(e){for(;this.cacheSize>this.maxCacheSize;){let t=null;for(let[e,r]of this.nodes)for(let o of r){let r=e.errors[o],s=e.frames[o];(!t||r=.9*e.error)return;this.removeNode(t.mesh,t.id)}this.totswapped+=e.mesh.nsize[e.id],this.candidates=this.candidates.filter((t=>t.mesh==e.mesh&&t.id==e.id)),this.requestNode(e.mesh,e.id),this.update()}}};let b=new v;class g extends o.Mesh{constructor(e,t,r){if(super(),"function"==typeof t)throw"Nexus3D constructor has changed: Nexus3D(url, renderer, options) where options include: onLoad, onUpdate, onProgress and material";this.patchWebGLRenderer(t),Object.assign(this,{isNXS:!0,type:"NXS",url:e,gl:t.getContext(),material:null,autoUpdate:!0,mesh:new x,vbo:[],ibo:[],vao:[],textures:[],attributes:{},basemesh:null}),"material"in r&&(this.material=r.material),this.material||(this.material=new o.MeshStandardMaterial);for(let e of["onLoad","onUpdate","onProgress"])this["_"+e]=[],e in r&&this["_"+e].push(r[e]);this.url&&("object"==typeof e?(this.nxs=this.url,this.nxs.onLoad.push((e=>{this.mesh=this.nxs.mesh,this.traversal=this.nxs.traversal,this.cache=this.nxs.cache,this.vbo=this.nxs.vbo,this.ibo=this.nxs.ibo,this.vao=this.nxs.vao,this.textures=this.nxs.textures,this.onLoadCallback(this)}))):this.open(this.url))}copy(e){throw Object3D.prototype.copy.call(this,e,!1),"Can't really copy."}open(e){let t=this;this.mesh.open(e),this.mesh.createNode=e=>{},this.mesh.createNodeGeometry=(e,r)=>{t.createNodeGeometry(e,r)},this.mesh.createTexture=(e,r)=>{t.createTexture(e,r)},this.mesh.deleteNodeGeometry=e=>{t.deleteNodeGeometry(e)},this.mesh.deleteTexture=e=>{t.deleteTexture(e)},this.mesh.onLoad.push((()=>{t.onLoadCallback()})),this.mesh.onUpdate.push((()=>{for(let e of t._onUpdate)e(this);for(let e of t._onProgress)e(this,this.mesh.availableNodes,this.mesh.nodesCount)})),this.traversal=new d,this.cache=b,this.textures={}}set onLoad(e){this._onLoad.push(e)}set onUpdate(e){this._onUpdate.push(e)}set onProgress(e){this._onProgress.push(e)}updateMaterials(){!1!==this.material.map&&this.mesh.vertex.texCoord&&(this.material.map=this.material_texture),this.mesh.vertex.color&&(this.material.vertexColors=o.VertexColors),this.material.needsUpdate=!0}onLoadCallback(){const e=this.mesh.sphere.center,t=new o.Vector3(e[0],e[1],e[2]),r=this.mesh.sphere.radius;this.boundingSphere=new o.Sphere(t,r);var s=new o.BufferGeometry;s.setAttribute("position",new o.BufferAttribute(new Float32Array(3),3)),this.mesh.vertex.normal&&s.setAttribute("normal",new o.BufferAttribute(new Float32Array(3),3)),this.mesh.vertex.color&&s.setAttribute("color",new o.BufferAttribute(new Float32Array(4),4)),this.mesh.vertex.texCoord&&s.setAttribute("uv",new o.BufferAttribute(new Float32Array(2),2)),this.mesh.vertex.texCoord&&(this.material_texture=new o.DataTexture(new Uint8Array([1,1,1]),1,1,o.RGBFormat),this.material_texture.needsUpdate=!0),this.updateMaterials(),this.geometry=s,this.frustumCulled=!1;for(let e of this._onLoad)e(this)}renderBufferDirect(e,t,r,s,i,n){let a=new o.Vector2;e.getSize(a),this.modelViewMatrix.multiplyMatrices(r.matrixWorldInverse,this.matrixWorld),this.traversal.updateView([0,0,a.width,a.height],r.projectionMatrix.elements,this.modelViewMatrix.elements),this.instance_errors=this.traversal.traverse(this.mesh,this.cache);let h=this.gl,l=h.getParameter(h.CURRENT_PROGRAM),u=this.attributes;u.position=h.getAttribLocation(l,"position"),u.normal=h.getAttribLocation(l,"normal"),u.color=h.getAttribLocation(l,"color"),u.uv=h.getAttribLocation(l,"uv"),u.size=h.getUniformLocation(l,"size"),u.scale=h.getUniformLocation(l,"scale");let d=h.getUniformLocation(l,"map");u.map=d?h.getUniform(l,d):null,this.setVisibility()}setVisibility(){let e=this.traversal,t=this.mesh;if(!t.isReady)return;let r=0,o=this.attributes,s=this.mesh,i=this.gl,n=i instanceof WebGL2RenderingContext,a=o.uv+10*o.color+100*o.normal;for(let x=0;x=0&&(i.vertexAttribPointer(o.uv,2,i.FLOAT,!1,8,t),i.enableVertexAttribArray(o.uv)),t+=8*e),s.vertex.color&&(o.color>=0&&(i.vertexAttribPointer(o.color,4,i.UNSIGNED_BYTE,!0,4,t),i.enableVertexAttribArray(o.color)),t+=4*e),s.vertex.normal&&o.normal>=0&&(i.vertexAttribPointer(o.normal,3,i.SHORT,!0,6,t),i.enableVertexAttribArray(o.normal)),this.cache.debug.nodes){i.disableVertexAttribArray(o.color);var c=this.instance_errors[x],f=[[1,1,1,1],[1,1,1,1],[0,1,0,1],[0,1,1,1],[1,1,0,1],[1,0,1,1],[1,0,0,1]];let e=Math.min(5.99,Math.max(0,Math.log2(c)/2)),t=Math.floor(e);e-=t;let r=[];for(let o=0;o<4;o++)r[o]=f[t][o]*(1-e)+f[t+1][o]*e;i.vertexAttrib4fv(o.color,r)}}this.cache.realError=Math.min(this.mesh.errors[x],this.cache.realError);let b=0,g=0,w=t.nfirstpatch[x+1]-1;for(let s=t.nfirstpatch[x];sb){if(t.vertex.texCoord&&o.uv>=0){var m=t.patches[3*s+2];if(-1!=m){var p=this.textures[m];i.activeTexture(i.TEXTURE0+o.map),i.bindTexture(i.TEXTURE_2D,p)}}let e=this.material.wireframe?i.LINE_STRIP:i.TRIANGLES;i.drawElements(e,3*(g-b),i.UNSIGNED_SHORT,6*b),r+=g-b}b=t.patches[3*s+1]}}}this.cache.rendered+=r}createNodeGeometry(e,t){let r=this.mesh;var s=r.nvertices[e];r.nfaces[e];let i=t.index,n=new ArrayBuffer(s*r.vsize);new Float32Array(n,0,3*s).set(t.position);var a=12*s;r.vertex.texCoord&&(new Float32Array(n,a,2*s).set(t.uv),a+=8*s);r.vertex.color&&(new Uint8Array(n,a,4*s).set(t.color),a+=4*s);r.vertex.normal&&(new Int16Array(n,a,3*s).set(t.normal),a+=6*s);if(e{t(r,o,s,i,n,a),n.renderBufferDirect&&n.renderBufferDirect(e,o,r,s,i,a)},e.originalRender=e.render,e.render=(t,r)=>{b.beginFrame(30),e.originalRender(t,r),b.endFrame()},e.nexusPatched=!0}}e.Cache=b,e.Nexus3D=g,Object.defineProperty(e,"__esModule",{value:!0})})); diff --git a/nexus3d/dist/corto.em.js b/nexus3d/dist/corto.em.js new file mode 100644 index 00000000..ce276157 --- /dev/null +++ b/nexus3d/dist/corto.em.js @@ -0,0 +1,216 @@ +/* +Corto +Copyright (c) 2017-2020, Visual Computing Lab, ISTI - CNR +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +onmessage = async function(job) { + if(typeof(job.data) == "string") return; + + var buffer = job.data.buffer; + if(!buffer) return; + if(!CortoDecoder.instance) + await CortoDecoder.ready; + + var model = CortoDecoder.decode(buffer, job.data.short_index, job.data.short_normals, job.data.rgba_colors ? 4 : 3); + postMessage({ model: model, buffer: buffer, request: job.data.request}); +}; + +var CortoDecoder = (function() { + "use strict"; + var wasm_base = "0061736D0100000001530D60037F7F7F0060027F7F0060017F017F60017F0060047F7F7F7F0060027F7F017F60057F7F7F7F7F0060037F7F7F017F60000060067F7F7F7F7F7F0060047F7F7F7F017F60077F7F7F7F7F7F7F006000017F02410203656E761F656D736372697074656E5F6E6F746966795F6D656D6F72795F67726F77746800030D776173695F756E737461626C650970726F635F65786974000303A401A2010302070107080C08070703010307050102080200010901010A00050003010405040501050101020008010402000603070402000605030802040500010203020401030300020204050100000404000200000004000102020100020302010005000606000000010600060A020003010003010101000203030102010000090901060605070504040007070503030302010B00000101010000010004000100030201020504050170013E3E05030100100608017F014190DD010B07FF0117066D656D6F727902000A6E65774465636F64657200A301076E67726F75707300760667726F7570730072056E76657274006C056E66616365003307686173417474720060096861734E6F726D616C005008686173436F6C6F72004705686173557600A2010C736574506F736974696F6E730099010C7365744E6F726D616C7333320094010C7365744E6F726D616C73313600900109736574436F6C6F7273008801067365745576730080010A736574496E6465783136007B0A736574496E64657833320079066465636F646500780D64656C6574654465636F6465720077065F7374617274002A066D616C6C6F63003904667265650002047362726B00120955010041010B3D285D5C5B696867666564635E5830575A2F565553522F59302D514F4C4AA0019F012D9E019D019C019A01970196012A401E8F018D01338C01401E3F3F8A011E89017E810187011E7F820186011E84010A89A303A2018C0D01077F02402000450D00200041786A22032000417C6A280200220141787122006A2105024020014101710D002001410371450D012003200328020022026B220341A8192802002204490D01200020026A2100200341AC19280200470440200241FF014D0440200328020822042002410376220241037441C0196A471A2004200328020C2201460440419819419819280200417E200277713602000C030B2004200136020C200120043602080C020B2003280218210602402003200328020C22014704402004200328020822024D0440200228020C1A0B2002200136020C200120023602080C010B0240200341146A220228020022040D00200341106A220228020022040D00410021010C010B0340200221072004220141146A220228020022040D00200141106A2102200128021022040D000B200741003602000B2006450D0102402003200328021C220241027441C81B6A22042802004604402004200136020020010D01419C19419C19280200417E200277713602000C030B20064110411420062802102003461B6A20013602002001450D020B2001200636021820032802102202044020012002360210200220013602180B20032802142202450D0120012002360214200220013602180C010B200528020422014103714103470D0041A019200036020020052001417E7136020420032000410172360204200020036A20003602000F0B200520034D0D0020052802042201410171450D0002402001410271450440200541B01928020046044041B019200336020041A41941A41928020020006A220036020020032000410172360204200341AC19280200470D0341A019410036020041AC1941003602000F0B200541AC1928020046044041AC19200336020041A01941A01928020020006A220036020020032000410172360204200020036A20003602000F0B200141787120006A21000240200141FF014D0440200528020C2102200528020822042001410376220141037441C0196A220747044041A8192802001A0B20022004460440419819419819280200417E200177713602000C020B2002200747044041A8192802001A0B2004200236020C200220043602080C010B2005280218210602402005200528020C220147044041A819280200200528020822024D0440200228020C1A0B2002200136020C200120023602080C010B0240200541146A220228020022040D00200541106A220228020022040D00410021010C010B0340200221072004220141146A220228020022040D00200141106A2102200128021022040D000B200741003602000B2006450D0002402005200528021C220241027441C81B6A22042802004604402004200136020020010D01419C19419C19280200417E200277713602000C020B20064110411420062802102005461B6A20013602002001450D010B2001200636021820052802102202044020012002360210200220013602180B20052802142202450D0020012002360214200220013602180B20032000410172360204200020036A2000360200200341AC19280200470D0141A01920003602000F0B20052001417E7136020420032000410172360204200020036A20003602000B200041FF014D04402000410376220141037441C0196A2100027F41981928020022024101200174220171450440419819200120027236020020000C010B20002802080B2102200020033602082002200336020C2003200036020C200320023602080F0B200342003702102003027F410020004108762201450D001A411F200041FFFFFF074B0D001A200120014180FE3F6A411076410871220174220220024180E01F6A411076410471220274220420044180800F6A411076410271220474410F7620012002722004726B22014101742000200141156A7641017172411C6A0B220236021C200241027441C81B6A21010240419C1928020022044101200274220771450440419C192004200772360200200120033602002003200336020C20032001360218200320033602080C010B20004100411920024101766B2002411F461B7421022001280200210102400340200122042802044178712000460D012002411D76210120024101742102200420014104716A220741106A28020022010D000B200720033602102003200336020C20032004360218200320033602080C010B20042802082200200336020C20042003360208200341003602182003200436020C200320003602080B41B81941B819280200417F6A220036020020000D0041E01C210303402003280200220041086A210320000D000B41B819417F3602000B0B2F01027F2000410120001B2100034002402000103922010D004194192802002202450D0020021108000C010B0B20010B820401037F20024180C0004F0440200020012002107C20000F0B200020026A210302402000200173410371450440024020024101480440200021020C010B2000410371450440200021020C010B200021020340200220012D00003A0000200141016A2101200241016A220220034F0D0120024103710D000B0B02402003417C71220441C000490D002002200441406A22054B0D0003402002200128020036020020022001280204360204200220012802083602082002200128020C36020C2002200128021036021020022001280214360214200220012802183602182002200128021C36021C2002200128022036022020022001280224360224200220012802283602282002200128022C36022C2002200128023036023020022001280234360234200220012802383602382002200128023C36023C200141406B2101200241406B220220054D0D000B0B200220044F0D01034020022001280200360200200141046A2101200241046A22022004490D000B0C010B20034104490440200021020C010B2003417C6A22042000490440200021020C010B200021020340200220012D00003A0000200220012D00013A0001200220012D00023A0002200220012D00033A0003200141046A2101200241046A220220044D0D000B0B200220034904400340200220012D00003A0000200141016A2101200241016A22022003470D000B0B20000BE40101067F0240024020002802082203200028020422026B20014F04400340200241003A00002000200028020441016A22023602042001417F6A22010D000C02000B000B2002200028020022046B220620016A2202417F4C0D01027F2002200320046B2203410174220720072002491B41FFFFFFFF07200341FFFFFFFF03491B220304402003100321050B200520066A22020B41002001100A1A0340200241016A21022001417F6A22010D000B200641014E044020052004200610041A0B200020053602002000200320056A360208200020023602042004450D00200410020B0F0B1007000B1C00200245044020002001460F0B20002802042001280204108B01450B080041F912100C000B05001038000B05001038000BF30202027F017E02402002450D00200020026A2203417F6A20013A0000200020013A000020024103490D002003417E6A20013A0000200020013A00012003417D6A20013A0000200020013A000220024107490D002003417C6A20013A0000200020013A000320024109490D002000410020006B41037122046A2203200141FF017141818284086C22013602002003200220046B417C7122046A2202417C6A200136020020044109490D002003200136020820032001360204200241786A2001360200200241746A200136020020044119490D002003200136021820032001360214200320013602102003200136020C200241706A20013602002002416C6A2001360200200241686A2001360200200241646A20013602002004200341047141187222046B22024120490D002001AD22054220862005842105200320046A2101034020012005370318200120053703102001200537030820012005370300200141206A2101200241606A2202411F4B0D000B0B20000BA50201037F230041406A220324002000280200220441786A28020021052004417C6A280200210420034100360214200320013602102003200036020C2003200236020841002101200341186A41004127100A1A200020056A2100024020042002410010060440200341013602382004200341086A200020004101410020042802002802141109002000410020032802204101461B21010C010B2004200341086A2000410141002004280200280218110600200328022C220041014B0D00200041016B0440200328021C410020032802284101461B410020032802244101461B410020032802304101461B21010C010B2003280220410147044020032802300D0120032802244101470D0120032802284101470D010B200328021821010B200341406B240020010B2D01027F100822022201419813360200200141C413360200200141046A2000109501200241F4133602001009000BF20101077F20002802082202200028020422036B41027520014F044020002003410020014102742200100A20006A3602040F0B02402003200028020022036B2206410275220720016A2205418080808004490440027F41002005200220036B2202410175220420042005491B41FFFFFFFF03200241027541FFFFFFFF01491B2202450D001A20024180808080044F0D022002410274100322080B220420074102746A41002001410274100A1A200641014E044020082003200610041A0B200020043602002000200420024102746A3602082000200420054102746A36020420030440200310020B0F0B1007000B41F009100C000B1B0002402000280208450D0020002802042200450D00200010020B0B4301037F02402002450D00034020002D0000220420012D00002205460440200141016A2101200041016A21002002417F6A22020D010C020B0B200420056B21030B20030B810101037F027F20002802142202200148044020004120200120026B22026B22013602142000200028020C220341046A36020C200041106A2104200028021020027420032802002202200176720C010B2000200220016B2201360214200041106A2104200028021022022001760B210320042002417F200174417F737136020020030B5501017F230041106B220224002000420037020020004100360208024020012C000B41004E044020002001280208360208200020012902003702000C010B2000200128020020012802041093010B200241106A24000B4A01017F41901D280200220120006A2200417F4C04404190194130360200417F0F0B024020003F004110744D0D002000107A0D004190194130360200417F0F0B41901D200036020020010B080041A812100C000B8F0101037F20002101024002402000410371450D0020002D00004504400C020B0340200141016A2201410371450D0120012D00000D000B0C010B03402001220241046A210120022802002203417F73200341FFFDFB776A7141808182847871450D000B200341FF0171450440200221010C010B034020022D00012103200241016A2201210220030D000B0B200120006B0BC50201067F027F20002802082204044020002802140C010B418002210420004180023602084180081003210320004280808080800437021020004100360200200020033602042000200336020C41200B220320024C0440200020002802102003742001200220036B767222083602100240200028020022032004480440200028020421060C010B417F20044103742004410174220541FEFFFFFF03712005471B1003220620002802042207200441027410042104200704402007100220002802084101742105200028020021030B20002005360208200020043602040B2000200341016A360200200620034102746A200836020020004100360210200028021421044120210320004120360214200220046B220241027441F0086A28020020017121010B200241014E04402000200320026B360214200020002802102002742001723602100B0B9D0101047F02402000280200220241014D0440200241016B450D012000280208220328000021022000200341046A2203360208024020022001280204200128020022046B22054B04402001200220056B100520012802002104200028020821030C010B200220054F0D002001200220046A3602040B2000200220036A36020820042003200210041A0F0B100822004199083602001009000B2000200110740B900201077F02402000280204200028020022076B220841186D220B41016A220641ABD5AAD5004904402006200028020820076B41186D2209410174220C200C2006491B41AAD5AAD500200941D5AAD52A491B22060440200641ABD5AAD5004F0D02200641186C1003210A0B2001280200210920022802002102200328020021032004280200210420052802002105200A200B41186C6A220141003A0014200120053602102001200436020C2001200336020820012002360204200120093602002001200841686D41186C6A2102200841014E044020022007200810041A0B200020023602002000200A200641186C6A3602082000200141186A36020420070440200710020B0F0B1007000B41AD11100C000B41002001044020002001280200101820002001280204101820012C0027417F4C0440200128021C10020B20012C001B417F4C0440200128021010020B200110020B0BFA0101067F20011071200041046A21062001280200210502402000280208200028020422026B2203417B4D044020064104100520002802082104200028020421020C010B2000200220036A41046A22043602080B200220036A20053602000240200420026B220320034104200341037122056B410020051B22076A220549044020062007100520002802082104200028020421020C010B200320054D0D002000200220056A22043602080B200128020421050240200420026B22042004200128020041027422016A2203490440200620011005200628020021020C010B200420034D0D002000200220036A3602080B200220046A2005200110041A0BFD0201047F20022D00012104024020012D0001220720002D00014D0440200441FF017120074D0D0120012F00002104200120022F00003B0000200220043B00004101210520012D000120002D00014D0440200441087621040C020B20002F00002104200020012F00003B0000200120043B000020022D00012104410221050C010B20002F00002106200441FF017120074B0440200020022F00003B0000200220063B000020064180FE03714108762104410121050C010B200020012F00003B0000200120063B00004101210520022D00012204200641087622074D0D00200120022F00003B0000200220063B000041022105200721040B20032D0001200441FF01714B047F20022F00002104200220032F00003B0000200320043B000020022D000120012D00014D0440200541016A0F0B20012F00002103200120022F00003B0000200220033B000020012D000120002D00014D0440200541026A0F0B20002F00002102200020012F00003B0000200120023B0000200541036A0520050B0B8E0201067F20002802082204200028020422036B41027520014F0440034020032002280200360200200341046A21032001417F6A22010D000B200020033602040F0B02402003200028020022066B2207410275220820016A2203418080808004490440027F41002003200420066B2204410175220520052003491B41FFFFFFFF03200441027541FFFFFFFF01491B2204450D001A20044180808080044F0D02200441027410030B220520084102746A2103034020032002280200360200200341046A21032001417F6A22010D000B200741014E044020052006200710041A0B200020053602002000200520044102746A3602082000200336020420060440200610020B0F0B1007000B41F009100C000B440020002001360208417F2001410274200141FFFFFFFF03712001471B1003210120004280808080800437021020004100360200200020013602042000200136020C20000BBF0101047F02402000280200220341014D0440200341016B450D01200041046A210602402000280208200028020422036B2204417B4D044020064104100520002802082105200028020421030C010B2000200320046A41046A22053602080B200320046A20013602000240200520036B2204200120046A2205490440200620011005200628020021030C010B200420054D0D002000200320056A3602080B200320046A2002200110041A0F0B10084199083602001009000B20002002200110750B0600200010020BC50101067F02402000280204200028020022046B2206410275220541016A2202418080808004490440027F41002002200028020820046B2203410175220720072002491B41FFFFFFFF03200341027541FFFFFFFF01491B2202450D001A20024180808080044F0D02200241027410030B220320054102746A22052001280200360200200641014E044020032004200610041A0B200020033602002000200320024102746A3602082000200541046A36020420040440200410020B0F0B1007000B41AD11100C000B850301047F230041306B22042400200441003602102004420037030802402001101422064170490440024002402006410B4F0440200641106A4170712207100321052004200741808080807872360210200420053602082004200636020C0C010B200420063A0013200441086A21052006450D010B20052001200610041A0B200520066A41003A0000200041146A2206200441086A1023210520042C0013417F4C0440200428020810020B2005200041186A470440200441003602102004420037030820011014220041704F0D02024002402000410B4F0440200041106A4170712207100321052004200741808080807872360210200420053602082004200036020C0C010B200420003A0013200441086A21052000450D010B20052001200010041A0B200020056A41003A00002004200441086A360220200441286A2006200441086A200441206A1022200428022828021C210020042C0013417F4C0440200428020810020B20002002360204200020033602140B200441306A24000F0B1013000B1013000B8D03010A7F230041106B2202240020024100360208200242003703002001101422034170490440024002402003410B4F0440200341106A417071220610032104200220064180808080787236020820022004360200200220033602040C010B200220033A000B200221042003450D010B20042001200310041A0B200320046A41003A000020022D000B2201411874411875210A027F410020002802182205450D001A20022802042001200A41004822001B21082002280200200220001B210B0340200541106A2107024002400240024002400240200528021420052D001B2200200041187441187541004822031B22062008200620084922011B22090440200B2007280200200720031B22042009100F22000D010B20082006490D052009450D022007280200200720031B21040C010B2000417F4C0D040B2004200B2009100F22000D010B20010D0141010C040B2000417F4C0D0041010C030B200541046A21050B200528020022050D000B41000B2100200A417F4C0440200228020010020B200241106A240020000F0B1013000BAA03010A7F0240200141046A220528020022040440200228020420022D000B2205200541187441187541004822051B21072002280200200220051B2108200141046A21050340200441106A21020240024002400240024002400240200428021420042D001B2206200641187441187541004822091B220A2007200A200749220C1B2206044020082002280200200220091B220B2006100F220D0D010B2007200A490D012006450D032002280200200220091B210B0C020B200D417F4A0D010B200428020022020D04200421050C070B200B20082006100F22020D010B200C0D010C050B2002417F4A0D040B200441046A210520042802042202450D03200521040B20042105200221040C00000B000B200521040B200020052802002202047F4100054120100322022003280200220329020037021020022003280208360218200342003702002003410036020820022004360208200242003702002002410036021C200520023602002002210420012802002802002203044020012003360200200528020021040B2001280204200410262001200128020841016A36020841010B3A0004200020023602000B9B0201087F02400240200041046A22052802002200450D00200128020420012D000B2202200241187441187541004822021B21032001280200200120021B210720052102034002402003200028021420002D001B2201200141187441187541004822091B2208200320084922041B22060440200041106A2201280200200120091B20072006100F22010D010B417F200420082003491B21010B2002200020014100481B210220002001411D764104716A28020022000D000B20022005460D000240200228021420022D001B2200200041187441187541004822061B2204200320042003491B220104402007200241106A2200280200200020061B2001100F22000D010B20032004490D010C020B2000417F4A0D010B200521020B20020B2F002001044020002001280200102420002001280204102420012C001B417F4C0440200128021010020B200110020B0B5701027F2000420037020020004100360208024020010440200141D6AAD5AA014F0D0120002001410C6C2201100322023602002000200120026A2203360208200241002001100A1A200020033602040B20000F0B1007000B9C0401037F2001200020014622033A000C024020030D000340200128020822032D000C0D0102402003200328020822022802002204460440024020022802042204450D0020042D000C0D000C020B024020012003280200460440200321010C010B200320032802042201280200220036020420012000047F2000200336020820032802080520020B36020820032802082200200041046A20002802002003461B20013602002001200336020020032001360208200128020821020B200141013A000C200241003A000C200220022802002200280204220136020020010440200120023602080B2000200228020836020820022802082201200141046A20012802002002461B200036020020002002360204200220003602080F0B02402004450D0020042D000C0D000C010B024020012003280200470440200321010C010B20032001280204220036020020012000047F2000200336020820032802080520020B36020820032802082200200041046A20002802002003461B20013602002001200336020420032001360208200128020821020B200141013A000C200241003A000C200220022802042200280200220136020420010440200120023602080B2000200228020836020820022802082201200141046A20012802002002461B200036020020002002360200200220003602080C020B2004410C6A2101200341013A000C200220002002463A000C200141013A0000200222012000470D000B0B0BC60E01097F024003402001417F6A210A2001417E6A210803402000210203400240024002400240200120026B2203410175220041054D0440024002400240200041026B0E0400010402070B2001417F6A2D000020022D00014D0D0620022F0000210020022001417E6A22012F00003B0000200120003B00000F0B2001417E6A21002001417F6A22072D0000210320022D0003220420022D00014D0440200320044D0D0620022F00022101200220002F00003B0002200020013B000020022D000320022D00014D0D0620022F000221000C0A0B20022F00002101200320044B0440200220002F00003B0000200020013B00000F0B20022F00022103200220013B0002200220033B000020072D000020014180FE03714108764D0D05200220002F00003B0002200020013B00000F0B2002200241026A200241046A200241066A101A1A2001417F6A2D0000200241076A2D00004D0D0420022F0006210020022001417E6A22012F00003B0006200120003B000020022D000720022D00054D0D0420022F00062100200220022F00043B0006200220003B000420022D0003200041087622014F0D04200220022F00023B0004200220003B000220022D000120014F0D040C080B2003413D4C044020022D00052100024020022D0003220420022D000122034D0440200020044D0D0120022F00042100200220022F00023B0004200220003B0002200320004108764F0D01200220022F00003B0002200220003B00000C010B20022F00002103200020044B044020022F00042100200220033B0004200220003B00000C010B20022F00022104200220033B0002200220043B0000200020034180FE03714108764D0D0020022F00042100200220033B0004200220003B00020B200241066A22002001460D04200241046A21040340200022072D000120042D00014B044020072F00002208410876210520072103034002402003200422002F00003B000020002002460440200221000C010B2000417E6A2104200021032000417F6A2D00002005490D010B0B200020083B00000B2007220441026A22002001470D000B0C040B2002200041026D41017422096A21050240200341CF0F4E044020022002200041046D41017422036A22002005200320056A2203101A2106200A2D000020032D00014D0D0120032F00002104200320082F00003B0000200820043B000020032D0001200220096A220441016A2D00004D0440200641016A21060C020B20052F00002107200520032F00003B0000200320073B000020042D000120002D00014D0440200641026A21060C020B20002F00002103200020052F00003B0000200520033B000020002D000120022D00014D0440200641036A21060C020B20022F00002103200220002F00003B0000200020033B0000200641046A21060C010B200A2D000021030240200220096A220041016A2D0000220420022D00014D044041002106200320044D0D0220052F00002103200520082F00003B0000200820033B00004101210620002D000120022D00014D0D0220022F00002100200220052F00003B0000200520003B00000C010B20022F00002100200320044B0440200220082F00003B0000200820003B0000410121060C020B200220052F00003B0000200520003B000041012106200A2D000020004180FE03714108764D0D01200520082F00003B0000200820003B00000B410221060B2008210402402002220741016A2D00002203200220096A2D000122024B0440200821000C010B03402004417E6A22002007460440200741026A21042003200A2D00004B0D0520042008460D060340200320042D00014B044020042F00002100200420082F00003B0000200820003B0000200441026A21040C070B200441026A22042008470D000B0C060B2004417F6A21092000210420092D000020024D0D000B20072F00002103200720002F00003B0000200020033B0000200641016A21060B200741026A220320004F0D01034020052D0001210403402003220241026A210320022D000120044B0D000B03402000417F6A21092000417E6A210020092D000020044D0D000B200220004B0440200221030C030520022F00002104200220002F00003B0000200020043B00002000200520022005461B2105200641016A21060C010B00000B000B2002200241026A200241046A2001417E6A101A1A0C020B024020032005460D0020052D000120032D00014D0D0020032F00002100200320052F00003B0000200520003B0000200641016A21060B20064504402007200310362102200341026A220020011036044020032101200721002002450D060C030B20020D040B200320076B200120036B480440200720031027200341026A21000C040B200341026A2001102720032101200721000C040B200420082200460D00034020072D0001210303402004220241026A2104200320022D00014D0D000B03402000417F6A21052000417E6A2100200320052D00004B0D000B200220004F0D0220022F00002103200220002F00003B0000200020033B00000C00000B000B0B0B0B0F0B200220022F00003B0002200220003B00000B1400200041C413360200200041046A108E0120000B10002002044020002001200210041A0B0B0300010BE803010A7F230041306B220324002001280208220428000021022001200441046A360208200041206A21050240200220002802242204200028022022076B41047522064B04402005200220066B10A101200028022421040C010B200220064F0D002004200720024104746A22024704400340200441746A200441786A2802001018200441706A22042002470D000B0B20002002360224200221040B20042005280200220547044003402005200128020822022800003602002001200241056A2200360208024020022D00042208450D00200541046A210941002106034002402001200041026A220720002F00006A22003602082001200041026A220A20002F00006A360208200341003602102003420037030820071014220241704F0D00024002402002410B4F0440200241106A417071220B100321002003200B41808080807872360210200320003602082003200236020C0C010B200320023A0013200341086A21002002450D010B20002007200210041A0B200020026A41003A00002003200341086A360220200341286A2009200341086A200341206A102C2003280228411C6A200A103D20032C0013417F4C0440200328020810020B200641016A22062008460D02200128020821000C010B0B1013000B200541106A22052004470D000B0B200341306A24000BB103010A7F0240200141046A220528020022040440200228020420022D000B2205200541187441187541004822051B21072002280200200220051B2108200141046A21050340200441106A21020240024002400240024002400240200428021420042D001B2206200641187441187541004822091B220A2007200A200749220C1B2206044020082002280200200220091B220B2006100F220D0D010B2007200A490D012006450D032002280200200220091B210B0C020B200D417F4A0D010B200428020022020D04200421050C070B200B20082006100F22020D010B200C0D010C050B2002417F4A0D040B200441046A210520042802042202450D03200521040B20042105200221040C00000B000B200521040B200020052802002202047F410005412810032202200328020022032902003702102002200328020836021820034200370200200341003602082002200436020820024200370200200241003602242002420037021C200520023602002002210420012802002802002203044020012003360200200528020021040B2001280204200410262001200128020841016A36020841010B3A0004200020023602000B040041010B9D0301077F230041306B220324002003420037032820034200370320200342003703182000280208220628000021042000200641046A2205360208200520002802046B410371220604402000200520066B41046A22053602080B2003420037022820034100360220200320043602182003200536021C200320053602242000200028020820044102746A3602082003410036021020034200370308024020024101480D000240200145044003402000200341086A1016200741016A22072002470D000B20032802082104200328020C1A0C010B03402000200341086A101641002105200328020C2208210420032802082206200847044003400240200520066A22092D000022044504402001200220056C20076A6A41003A00000C010B2001200220056C20076A6A4100200341186A200410102204410120092D0000417F6A7422066A6B200420042006481B3A000020032802082106200328020C21080B200541016A22052008200622046B490D000B0B200741016A22072002470D000B0B2004450D002003200436020C200410020B200341186A100E200341306A24000B0300010B3A01017F2000418410360200200028022C2201044020002001360230200110020B20002802202201044020002001360224200110020B200010020B970301077F230041306B220324002003420037032820034200370320200342003703182000280208220428000021052000200441046A2204360208200420002802046B410371220604402000200420066B41046A22043602080B2003420037022820034100360220200320053602182003200436021C200320043602242000200028020820054102746A36020820034100360210200342003703082000200341086A1016200328020C2206200328020822006B210402402001450D002004450D0020024101480440410021010340200141016A22012004490D000B0C010B200241027421084100210503402001200220056C4102746A21070240200020056A22092D00002204044041012004744101752106410021000340200720004102746A200341186A200441FF0171101020066B3602002002200041016A220047044020092D000021040C010B0B20032802082100200328020C21060C010B200741002008100A1A0B200541016A2205200620006B2204490D000B0B200004402003200036020C200010020B200341186A100E200341306A240020040B960501097F230041306B22062400200641186A2001101C210A200641003602102006420037030802402001450D0002402001417F4A044020062001100322053602082006200536020C2006200120056A360210200121040340200541003A00002006200628020C41016A220536020C2004417F6A22040D000B2001450D0220034102480D01034002402002200320096C4102746A220C280200220741016A220441014D0440200441016B450D01410121070C010B2007411F7520077321044102210503402005220741016A2105200441017522040D000B0B4101210B03400240200C200B4102746A280200220841016A220441014D0440200441016B450D01410121080C010B2008411F7520087321054102210403402004220841016A2104200541017522050D000B0B2008200720072008491B2107200B41016A220B2003470D000B200628020820096A20073A000002402007450D0020034101480D0041012007417F6A742104410021050340200A200C20054102746A28020020046A20071015200541016A22052003470D000B0B200941016A22092001470D000B0C020B1007000B03400240024002402002200320096C4102746A2207280200220441016A220841014D044041012104200841016B0D010C020B2004411F7520047321054102210803402008220441016A2108200541017522050D000B0B200628020820096A20043A000020034101480D0141012004417F6A742108410021050340200A200720054102746A28020020086A20041015200541016A22052003470D000B0C010B200628020820096A41003A00000B200941016A22092001470D000B0B2000200A10192000200628020C200628020822006B2000101D2006280208220004402006200036020C200010020B200A100E200641306A24000B070020002802040BC50201057F02400240024020002802082205200028020422036B410C6D20014F04400340200320022902003702002003200228020836020820002000280204410C6A22033602042001417F6A22010D000C02000B000B2003200028020022046B410C6D220620016A220341D6AAD5AA014F0D01027F41002003200520046B410C6D2205410174220420042003491B41D5AAD5AA01200541AAD5AAD500491B2205450D001A200541D6AAD5AA014F0D032005410C6C10030B22072006410C6C6A22042103034020032002290200370200200320022802083602082003410C6A21032001417F6A22010D000B20042000280204200028020022016B220241746D410C6C6A2104200241014E044020042001200210041A0B20002004360200200020072005410C6C6A360208200020033602042001450D00200110020B0F0B1007000B41800E100C000B880302097F067D230041106B220524002004200428020036020420054100360208200542003703002000044020042000200510340B200241036C220041014E0440200320004102746A210A20042802002106034020012003280208410C6C220B6A2207280200210C20012003280204410C6C220D6A2208280200210420012003280200410C6C22026A22092802002100200220066A220220022A02002008280204B22009280204B222109322122007280208B22009280208B2220E93220F942008280208B2200E93220E2007280204B2201093221394932211923802002002200E200CB22000B2220E932210942004B2200E93220E200F9493220F20022A0204923802042002200E201394201220109493220E20022A0208923802082006200D6A2200201120002A0200923802002000200F20002A0204923802042000200E20002A0208923802082006200B6A2200201120002A0200923802002000200F20002A0204923802042000200E20002A0208923802082003410C6A2203200A490D000B0B200541106A24000BAF0601077F41012102024002400240200120006B410175220341054D04400240024002400240200341026B0E0400010203050B2001417F6A2D000020002D00014D0D0420002F0000210220002001417E6A22002F00003B0000200020023B00000C060B2001417E6A21032001417F6A22062D0000210420002D0003220520002D00014D0440200420054D0D0420002F00022101200020032F00003B0002200320013B000020002D000320002D00014D0D0420002F000221010C050B20002F00002101200420054B0440200020032F00003B0000200320013B00000C060B20002F00022104200020013B0002200020043B000020062D000020014180FE03714108764D0D03200020032F00003B0002200320013B00000C050B2000200041026A200041046A2001417E6A101A1A0C040B2000200041026A200041046A200041066A101A1A2001417F6A2D000020002D00074D0D0120002F0006210320002001417E6A22012F00003B0006200120033B000020002D000720002D00054D0D0120002F00062101200020002F00043B0006200020013B000420002D0003200141087622034F0D01200020002F00023B0004200020013B000220002D000120034F0D010C020B20002D00052102024020002D0003220420002D000122034D0440200220044D0D0120002F00042102200020002F00023B0004200020023B0002200320024108764F0D01200020002F00003B0002200020023B00000C010B20002F00002103200220044B044020002F00042102200020033B0004200020023B00000C010B20002F00022104200020033B0002200020043B0000200220034180FE03714108764D0D0020002F00042102200020033B0004200020023B00020B2001200041066A2202460D02200041046A210402400340200222032D000120042D00014B044020032F00002207410876210820032105034002402005200422022F00003B000020002002460440200021020C010B2002417E6A2104200221052002417F6A2D00002008490D010B0B200220073B0000200641016A22064108460D020B2003220441026A22022001470D000B0C030B200341026A20014621020B20020F0B200020002F00003B0002200020013B00000B41010B940F01177F230041206B2205240002402000280210200028020C6B221441017522064102490D002000280204210220054100360218200542003703100240027F0240027F02400240024002404101200274220B4101742204044020044180808080044F0D012005200B4103742201100322023602102005200220044102746A3602182005200241002001100A20016A3602140B200041186A210F02402004200028021C200028021822026B41027522014B0440200F200420016B100D0C010B200420014F0D002000200220044102746A36021C0B200041246A2110024020042000280228200028022422026B41027522014B04402010200420016B100D0C010B200420014F0D002000200220044102746A3602280B200041306A210E02402000280234200028023022026B220141FF3F4D0440200E4180C00020016B10050C010B20014180C000460D00200020024180406B3602340B200541003602082005420037030002402014044020064180808080044F0D01200520144101742202100322073602002005200720064102746A3602082005200741002002100A20026A3602040B200B417F6A22172006417F6A22166E2104200028020C22022D000341087421014102210320022D000122084108742212210A03400240200A20126C411076210A2003220920044F0D00200941016A2103200A20014B0D010B0B2009410F4D044020140D03410021014100210241000C080B200E28020020022D00003A00002009417F6A210C41012101200641014D0D034101210203402001200C6A2107200028020C210303402001220420002802306A20032D00003A0000200028020C2103200141016A22012007470D000B200E28020020076A200320024101746A2D00003A0000200441026A2101200241016A22022006470D000B200528020022022006200C6C221336020041012103200641014D0D040340200220034102746A2003360200200341016A22032006470D000B20052802102215200641014D0D051A2000280224210D20002802182111200028020C210E41002102034041012103200241016A2104200220066C210C024020020440034020152003200C6A41027422076A200E20034101746A2D0001200A6C410874411076360200200720116A200320096C20026B3602002007200D6A2004360200200341016A22032006470D000C02000B000B034020152003200C6A41027422076A200E20034101746A2D0001410874360200200720116A200320096C3602002007200D6A2004360200200341016A22032006470D000B0B200A20126C411076201220021B210A200422022009470D000B200528021021080C060B1007000B1007000B4100210103402007200141027422046A2001360200200528021020046A200841FF0171410874360200200F28020020046A2001360200201028020020046A4101360200200E28020020016A200220014101746A2D00003A0000200141016A220120064F04402001210220060C0605200028020C220220014101746A2D00012108200528020021070C010B00000B000B20052802002006200C6C22133602000B20052802100B2108410021030340200A20126C411076201220031B210A200341016A22032009470D000B2010280200210D200F28020021110B2008201341027422026A200A360200200220116A41003602002002200D6A2009360200200620096C2102200920166C41016A0B220D200B4F0D00201404400340410021032005280210210A2005280200210941002108410021070340200A200920034102746A2802004102746A28020022042008200420084B22041B21082003200720041B2107200341016A22032006490D000B201028020022112009200741027422156A28020041027422046A280200221341016A210E2004200A6A280200410874210C200F280200220820046A28020021074100210303400240200A200241027422046A200C20034101742209200028020C6A2D00016C411076360200200420086A2001360200200420116A200E3602002000280230220420016A200420076A201310041A200120136A220120002802306A200028020C20096A2D00003A0000200241016A2102200141016A21012003200D6A2017460D00200341016A220320064F0D0020102802002111200F28020021082005280210210A0C010B0B20032006460440200528020020156A2204200428020020066A3602000B200D20166A220D200B490D000C02000B000B0340200D20166A220D200B490D000B0B200204402005280200210C4100210141002103410021080340200C2001410020012006491B22074102746A28020020034D0440200F2802002209200841027422016A2009200341027422046A2802003602002001201028020022016A200120046A280200360200200841016A21080B200741016A2101200341016A22032002470D000B0B0240200B200028021C200028021822026B41027522014B0440200F200B20016B100D0C010B200B20014F0D0020002002200B4102746A36021C0B0240200B2000280228200028022422026B41027522014B04402010200B20016B100D0C010B200B20014F0D0020002002200B4102746A3602280B20052802002200044020052000360204200010020B20052802102200450D0020052000360214200010020B200541206A24000B070041011001000BC82D010B7F230041106B220B240002400240024002400240024002400240024002400240200041F4014D0440419819280200220641102000410B6A4178712000410B491B2205410376220076220141037104402001417F7341017120006A2202410374220441C8196A280200220141086A2100024020012802082203200441C0196A22044604404198192006417E200277713602000C010B41A8192802001A2003200436020C200420033602080B200120024103742202410372360204200120026A220120012802044101723602040C0C0B200541A01928020022084D0D0120010440024041022000742202410020026B722001200074712200410020006B71417F6A22002000410C764110712200762201410576410871220220007220012002762200410276410471220172200020017622004101764102712201722000200176220041017641017122017220002001766A2202410374220341C8196A28020022012802082200200341C0196A22034604404198192006417E2002777122063602000C010B41A8192802001A2000200336020C200320003602080B200141086A210020012005410372360204200120056A22072002410374220220056B2203410172360204200120026A2003360200200804402008410376220441037441C0196A210141AC192802002102027F20064101200474220471450440419819200420067236020020010C010B20012802080B2104200120023602082004200236020C2002200136020C200220043602080B41AC19200736020041A01920033602000C0C0B419C19280200220A450D01200A4100200A6B71417F6A22002000410C764110712200762201410576410871220220007220012002762200410276410471220172200020017622004101764102712201722000200176220041017641017122017220002001766A41027441C81B6A280200220128020441787120056B210320012102034002402002280210220045044020022802142200450D010B200028020441787120056B22022003200220034922021B21032000200120021B2101200021020C010B0B200128021821092001200128020C220447044041A819280200200128020822004D0440200028020C1A0B2000200436020C200420003602080C0B0B200141146A2202280200220045044020012802102200450D03200141106A21020B0340200221072000220441146A220228020022000D00200441106A2102200428021022000D000B200741003602000C0A0B417F2105200041BF7F4B0D002000410B6A22004178712105419C192802002207450D00410020056B2102024002400240027F410020004108762200450D001A411F200541FFFFFF074B0D001A200020004180FE3F6A411076410871220074220120014180E01F6A411076410471220174220320034180800F6A411076410271220374410F7620002001722003726B22004101742005200041156A7641017172411C6A0B220841027441C81B6A2802002203450440410021000C010B20054100411920084101766B2008411F461B7421014100210003400240200328020441787120056B220620024F0D0020032104200622020D0041002102200321000C030B200020032802142206200620032001411D764104716A2802102203461B200020061B21002001200341004774210120030D000B0B200020047245044041022008742200410020006B722007712200450D032000410020006B71417F6A22002000410C764110712200762201410576410871220320007220012003762200410276410471220172200020017622004101764102712201722000200176220041017641017122017220002001766A41027441C81B6A28020021000B2000450D010B0340200028020441787120056B220320024921012003200220011B21022000200420011B210420002802102201047F20010520002802140B22000D000B0B2004450D00200241A01928020020056B4F0D00200428021821082004200428020C220147044041A819280200200428020822004D0440200028020C1A0B2000200136020C200120003602080C090B200441146A2203280200220045044020042802102200450D03200441106A21030B0340200321062000220141146A220328020022000D00200141106A2103200128021022000D000B200641003602000C080B41A019280200220120054F044041AC1928020021000240200120056B220241104F044041A019200236020041AC19200020056A220336020020032002410172360204200020016A2002360200200020054103723602040C010B41AC19410036020041A019410036020020002001410372360204200020016A220120012802044101723602040B200041086A21000C0A0B41A419280200220120054B044041A419200120056B220136020041B01941B019280200220020056A22023602002002200141017236020420002005410372360204200041086A21000C0A0B410021002005412F6A2204027F41F01C280200044041F81C2802000C010B41FC1C427F37020041F41C4280A0808080800437020041F01C200B410C6A41707141D8AAD5AA057336020041841D410036020041D41C41003602004180200B22026A2206410020026B220771220220054D0D0941D01C2802002203044041C81C280200220820026A220920084D0D0A200920034B0D0A0B41D41C2D00004104710D040240024041B0192802002203044041D81C210003402000280200220820034D0440200820002802046A20034B0D030B200028020822000D000B0B410010122201417F460D052002210641F41C2802002200417F6A22032001710440200220016B200120036A410020006B716A21060B200620054D0D05200641FEFFFFFF074B0D0541D01C2802002200044041C81C280200220320066A220720034D0D06200720004B0D060B2006101222002001470D010C070B200620016B200771220641FEFFFFFF074B0D04200610122201200028020020002802046A460D03200121000B200021010240200541306A20064D0D00200641FEFFFFFF074B0D002001417F460D0041F81C2802002200200420066B6A410020006B71220041FEFFFFFF074B0D0620001012417F470440200020066A21060C070B410020066B10121A0C040B2001417F470D050C030B410021040C070B410021010C050B2001417F470D020B41D41C41D41C2802004104723602000B200241FEFFFFFF074B0D012002101222014100101222004F0D012001417F460D012000417F460D01200020016B2206200541286A4D0D010B41C81C41C81C28020020066A2200360200200041CC1C2802004B044041CC1C20003602000B02400240024041B0192802002203044041D81C21000340200120002802002202200028020422046A460D02200028020822000D000B0C020B41A81928020022004100200120004F1B45044041A81920013602000B4100210041DC1C200636020041D81C200136020041B819417F36020041BC1941F01C28020036020041E41C410036020003402000410374220241C8196A200241C0196A2203360200200241CC196A2003360200200041016A22004120470D000B41A419200641586A2200417820016B4107714100200141086A4107711B22026B220336020041B019200120026A220236020020022003410172360204200020016A412836020441B41941801D2802003602000C020B20002D000C4108710D00200120034D0D00200220034B0D002000200420066A36020441B0192003417820036B4107714100200341086A4107711B22006A220136020041A41941A41928020020066A220220006B220036020020012000410172360204200220036A412836020441B41941801D2802003602000C010B200141A819280200220449044041A8192001360200200121040B200120066A210241D81C2100024002400240024002400240034020022000280200470440200028020822000D010C020B0B20002D000C410871450D010B41D81C210003402000280200220220034D0440200220002802046A220420034B0D030B200028020821000C00000B000B200020013602002000200028020420066A3602042001417820016B4107714100200141086A4107711B6A220920054103723602042002417820026B4107714100200241086A4107711B6A220120096B20056B2100200520096A21072001200346044041B019200736020041A41941A41928020020006A2200360200200720004101723602040C030B200141AC1928020046044041AC19200736020041A01941A01928020020006A220036020020072000410172360204200020076A20003602000C030B2001280204220241037141014604402002417871210A0240200241FF014D0440200128020822032002410376220441037441C0196A471A2003200128020C2202460440419819419819280200417E200477713602000C020B2003200236020C200220033602080C010B2001280218210802402001200128020C22064704402004200128020822024D0440200228020C1A0B2002200636020C200620023602080C010B0240200141146A220328020022050D00200141106A220328020022050D00410021060C010B0340200321022005220641146A220328020022050D00200641106A2103200628021022050D000B200241003602000B2008450D0002402001200128021C220241027441C81B6A22032802004604402003200636020020060D01419C19419C19280200417E200277713602000C020B20084110411420082802102001461B6A20063602002006450D010B2006200836021820012802102202044020062002360210200220063602180B20012802142202450D0020062002360214200220063602180B2001200A6A21012000200A6A21000B20012001280204417E7136020420072000410172360204200020076A2000360200200041FF014D04402000410376220141037441C0196A2100027F41981928020022024101200174220171450440419819200120027236020020000C010B20002802080B2101200020073602082001200736020C2007200036020C200720013602080C030B2007027F410020004108762201450D001A411F200041FFFFFF074B0D001A200120014180FE3F6A411076410871220174220220024180E01F6A411076410471220274220320034180800F6A411076410271220374410F7620012002722003726B22014101742000200141156A7641017172411C6A0B220136021C20074200370210200141027441C81B6A21020240419C1928020022034101200174220471450440419C192003200472360200200220073602000C010B20004100411920014101766B2001411F461B742103200228020021010340200122022802044178712000460D032003411D76210120034101742103200220014104716A220428021022010D000B200420073602100B200720023602182007200736020C200720073602080C020B41A419200641586A2200417820016B4107714100200141086A4107711B22026B220736020041B019200120026A220236020020022007410172360204200020016A412836020441B41941801D28020036020020032004412720046B4107714100200441596A4107711B6A41516A22002000200341106A491B2202411B360204200241E01C290200370210200241D81C29020037020841E01C200241086A36020041DC1C200636020041D81C200136020041E41C4100360200200241186A2100034020004107360204200041086A2101200041046A210020012004490D000B20022003460D0320022002280204417E713602042003200220036B220441017236020420022004360200200441FF014D04402004410376220141037441C0196A2100027F41981928020022024101200174220171450440419819200120027236020020000C010B20002802080B2101200020033602082001200336020C2003200036020C200320013602080C040B200342003702102003027F410020044108762200450D001A411F200441FFFFFF074B0D001A200020004180FE3F6A411076410871220074220120014180E01F6A411076410471220174220220024180800F6A411076410271220274410F7620002001722002726B22004101742004200041156A7641017172411C6A0B220036021C200041027441C81B6A21010240419C1928020022024101200074220671450440419C19200220067236020020012003360200200320013602180C010B20044100411920004101766B2000411F461B742100200128020021010340200122022802044178712004460D042000411D76210120004101742100200220014104716A220628021022010D000B20062003360210200320023602180B2003200336020C200320033602080C030B20022802082200200736020C20022007360208200741003602182007200236020C200720003602080B200941086A21000C050B20022802082200200336020C20022003360208200341003602182003200236020C200320003602080B41A419280200220020054D0D0041A419200020056B220136020041B01941B019280200220020056A22023602002002200141017236020420002005410372360204200041086A21000C030B4190194130360200410021000C020B02402008450D000240200428021C220041027441C81B6A220328020020044604402003200136020020010D01419C192007417E2000777122073602000C020B20084110411420082802102004461B6A20013602002001450D010B2001200836021820042802102200044020012000360210200020013602180B20042802142200450D0020012000360214200020013602180B02402002410F4D04402004200220056A2200410372360204200020046A220020002802044101723602040C010B20042005410372360204200420056A22032002410172360204200220036A2002360200200241FF014D04402002410376220141037441C0196A2100027F41981928020022024101200174220171450440419819200120027236020020000C010B20002802080B2101200020033602082001200336020C2003200036020C200320013602080C010B2003027F410020024108762200450D001A411F200241FFFFFF074B0D001A200020004180FE3F6A411076410871220074220120014180E01F6A411076410471220174220520054180800F6A411076410271220574410F7620002001722005726B22004101742002200041156A7641017172411C6A0B220036021C20034200370210200041027441C81B6A21010240024020074101200074220571450440419C192005200772360200200120033602000C010B20024100411920004101766B2000411F461B742100200128020021050340200522012802044178712002460D022000411D76210520004101742100200120054104716A220628021022050D000B200620033602100B200320013602182003200336020C200320033602080C010B20012802082200200336020C20012003360208200341003602182003200136020C200320003602080B200441086A21000C010B02402009450D000240200128021C220041027441C81B6A220228020020014604402002200436020020040D01419C19200A417E200077713602000C020B20094110411420092802102001461B6A20043602002004450D010B2004200936021820012802102200044020042000360210200020043602180B20012802142200450D0020042000360214200020043602180B02402003410F4D04402001200320056A2200410372360204200020016A220020002802044101723602040C010B20012005410372360204200120056A22042003410172360204200320046A2003360200200804402008410376220541037441C0196A210041AC192802002102027F41012005742205200671450440419819200520067236020020000C010B20002802080B2105200020023602082005200236020C2002200036020C200220053602080B41AC19200436020041A01920033602000B200141086A21000B200B41106A240020000BA30100200041013A0035024020002802042002470D00200041013A00342000280210220245044020004101360224200020033602182000200136021020034101470D0120002802304101470D01200041013A00360F0B2001200246044020002802182202410246044020002003360218200321020B20002802304101470D0120024101470D01200041013A00360F0B200041013A00362000200028022441016A3602240B0B4E01017F02402001450D00200141DC1441DC16100B2201450D0020012802082000280208417F73710D00200028020C200128020C41001006450D00200028021020012802104100100621020B20020B5D01017F200028021022034504402000410136022420002002360218200020013602100F0B02402001200346044020002802184102470D01200020023602180F0B200041013A0036200041023602182000200028022441016A3602240B0B0D0020002001200110141092010B1400417F200049044041B512100C000B200010030B0300010B040020000B981702137F017E23004190016B22042400200441003602880120044200370380010240200028027022050440200541ABD5AAD5004F0D012004200541186C2206100322053602840120042005360280012004200520066A360288010B20044100360278200442003703700240200220016B2205410176220604402005417F4C0D0120042006410274220610032205360274200420053602702004200520066A3602780B200441003602682004420037036041C000200035020042018879A76B21052004417F36025C0240200220014D0D00200541016A210D200041D8006A210C2004412C6A2114200441186A2112200441D0006A210F200441CC006A2113200441C8006A210E200441C4006A21100340027F024002400240200428025C2205417F460440024020112004280274200428027022056B4102754F044020042802642004280260470D01200028028401210520032003280200220741016A3602002005417F6A2105410021062004027F02402007200028024C6A2D00004106470D00200C410310102206410171450D00200C200D10100C010B2000280234200028028401410C6C6A2207200536020820072005360204200720053602002000200028028401220541016A3602840120050B22073602400240200028022422090440200920014101746A20073B01000C010B200028022020014102746A20073602000B2006410271450D05200C200D10100C060B2004200520114102746A2802002205360258201141016A21110C030B2004280264220520042802604704402005417C6A22062802002105200420063602640C020B10084180113602001009000B2004417F36025C0B200420053602580B200F2004280280012207200541186C6A2206290210370300200E200629020837030020042006290200370340024020042D00540D0020032003280200220641016A3602002006200028024C6A2D0000220A4104460D0020042004280240221536023C20042004280244221636023820042007200428024C41186C6A2209290208370328200420092902103703302004200929020037032020042007200428025041186C6A220829020837031020122008290210370300200420082902003703082004200428028401220620076B41186D220B36025C2004417F3602040240200A41064B0D000240024002400240024002400240200A41016B0E06030406070500010B200C200D101021050C010B2000280234200028028401410C6C6A2205200428024836020820052015360204200520163602002000200028028401220541016A360284010B200420053602042004280280012206200428024C41186C6A200428025C3602102006200428025041186C6A200428025C41016A36020C2004200428025C41016A220736020002402004280284012205200428028801490440200428023C2109200428020421082004280238210A200428024C210B200541003A0014200520073602102005200B36020C2005200A36020820052008360204200520093602002004200541186A2205360284010C010B20044180016A2004413C6A200441046A200441386A201320041017200428028401210520042802800121060B2004200520066B41186D22073602000240200428027422062004280278490440200620073602002004200641046A3602740C010B200441F0006A2004101F20042802840121050B20052004280288014904402004280204210620042802382107200428023C2109200428025C21082004280250210A200541003A00142005200A3602102005200836020C2005200936020820052007360204200520063602002004200541186A360284010C050B20044180016A200441046A200441386A2004413C6A200441DC006A200F10170C040B200941013A00142007200428022C41186C6A200B3602102007200428025041186C6A200428025C36020C20042004280220220536020420042802880120064B044020042802382107200428023C2109200428022C21082004280250210A200641003A00142006200A3602102006200836020C2006200936020820062007360204200620053602002004200641186A360284010C040B20044180016A200441046A200441386A2004413C6A2014200F10170C030B200841013A00142007200428021841186C6A200B36020C2007200428024C41186C6A200428025C3602102004200428020C220536020420042802880120064B0440200428023C210720042802382109200428024C21082004280218210A200641003A00142006200A3602102006200836020C2006200936020820062005360204200620073602002004200641186A360284010C030B20044180016A2004413C6A200441046A200441386A2013201210170C020B200428026422062004280268470440200620053602002004200641046A3602642004417F36025C0C030B200441E0006A200441D8006A101F2004417F36025C0C020B200941013A0014200841013A00142007200428022C220541186C6A20042802183602102007200428021841186C6A200536020C2004417F36025C200420042802203602040B200428023821050240200028022422060440200620014101746A220620053B01002006200428023C3B0102200620042802043B01040C010B200028022020014102746A220620053602002006200428023C360204200620042802043602080B200141036A21010B20012002490D020C030B2000280234200028028401410C6C6A2207200536020820072005360204200720053602002000200028028401220541016A3602840120050B2107200141016A2109200420073602440240200028022422080440200820094101746A20073B01000C010B200028022020094102746A20073602000B024020064104710440200C200D101021050C010B2000280234200028028401410C6C6A2206200536020820062005360204200620053602002000200028028401220541016A360284010B200141026A2106200420053602480240200028022422070440200720064101746A20053B01000C010B200028022020064102746A20053602000B200420042802840122052004280280016B41186D22063602200240200428027422072004280278490440200720063602002004200741046A3602740C010B200441F0006A200441206A101F20042802840121050B2004200641026A22073602202004200641016A2209360208024020052004280288014904402004290244211720042802402108200541003A0014200520093602102005200736020C20052008360208200520173702002004200541186A2205360284010C010B20044180016A2010200E200441406B200441206A200441086A101720042802840121050B200420052004280280016B41186D220A36022002402004280274220820042802784904402008200A3602002004200841046A3602740C010B200441F0006A200441206A101F20042802840121050B200420063602202004200736020802402005200428028801490440200428024821082004280240210A2004280244210B200541003A0014200520073602102005200636020C2005200B3602082005200A360204200520083602002004200541186A2205360284010C010B20044180016A200E200441406B2010200441206A200441086A101720042802840121050B200420052004280280016B41186D22083602200240200428027422072004280278490440200720083602002004200741046A3602740C010B200441F0006A200441206A101F20042802840121050B200141036A21012004200936022020042006360208024020052004280288014904402004290240211720042802482107200541003A0014200520063602102005200936020C20052007360208200520173702002004200541186A360284010C010B20044180016A200441406B2010200E200441206A200441086A10170B20012002490D000B0B20042802602200044020042000360264200010020B20042802702200044020042000360274200010020B200428028001220004402004200036028401200010020B20044190016A24000F0B41AD11100C000B41AD11100C000BEB0101067F20002802082203200028020422026B410C6D20014F0440200020022001410C6C6A3602040F0B02402002200028020022026B2205410C6D220620016A220441D6AAD5AA01490440027F41002004200320026B410C6D2203410174220720072004491B41D5AAD5AA01200341AAD5AAD500491B2204450D001A200441D6AAD5AA014F0D022004410C6C10030B22072006410C6C6A2206200541746D410C6C6A2103200541014E044020032002200510041A0B20002003360200200020072004410C6C6A360208200020062001410C6C6A36020420020440200210020B0F0B1007000B41AD11100C000BC10401067F230041206B220324002003410036021820034200370310200041206A200041F8006A2206102B20002802142201200041186A2205470440034020032001220241106A10112003200228021C220136020C200120002802002006200128020028021C11000020032C000B417F4C0440200328020010020B024020022802042204450440200228020822012802002002460D01200241086A210203402002280200220441086A2102200420042802082201280200470D000B0C010B03402004220128020022040D000B0B20012005470D000B200028021421010B024020012005460D00034020032001220241106A10112003200228021C220136020C20012000280200200341106A200128020028022011000020032C000B417F4C0440200328020010020B024020022802042204450440200228020822012802002002460D01200241086A210203402002280200220441086A2102200420042802082201280200470D000B0C010B03402004220128020022040D000B0B20012005470D000B200028021422042005460D00034020032004220241106A10112003200228021C220136020C20012000280200200128020028022811010020032C000B417F4C0440200328020010020B024020022802042201450440200228020822042802002002460D01200241086A210203402002280200220141086A2102200120012802082204280200470D000B0C010B03402001220428020022010D000B0B20042005470D000B0B20032802102200044020032000360214200010020B200341206A24000B800801087F230041206B22042400200041206A2208200041F8006A2205102B200020002802800122012800003602702000200141046A360280012005200041CC006A1016200028028001220328000021012000200341046A2203360280012003200028027C6B410371220204402000200320026B41046A2203360280010B2000420037026820004100360260200020013602582000200336025C20002003360264200020002802800120014102746A3602800120002802142201200041186A22064704400340200441106A2001220241106A10112004200228021C220136021C200120002802002005200128020028021C11000020042C001B417F4C0440200428021010020B024020022802042203450440200228020822012802002002460D01200241086A210303402003280200220241086A2103200220022802082201280200470D000B0C010B03402003220128020022030D000B0B20012006470D000B0B0240200028020022012000280238200041346A220728020022026B410C6D22034B04402007200120036B10420C010B200120034F0D00200020022001410C6C6A3602380B410021012004410036020C200041406B28020022032000280244220247044003402000200141036C200328020041036C2004410C6A104120032802002101200341106A2205210320022005470D000B0B0240200041146A220528020022012006460D000340200441106A2001220241106A10112004200228021C220136021C200120002802002007200128020028022011000020042C001B417F4C0440200428021010020B024020022802042203450440200228020822012802002002460D01200241086A210303402003280200220241086A2103200220022802082201280200470D000B0C010B03402003220128020022030D000B0B20012006470D000B200528020022012006460D000340200441106A2001220241106A10112004200228021C220136021C20012000280200200028020420052008200128020028022411060020042C001B417F4C0440200428021010020B024020022802042203450440200228020822012802002002460D01200241086A210303402003280200220241086A2103200220022802082201280200470D000B0C010B03402003220128020022030D000B0B20012006470D000B200528020022012006460D000340200441106A2001220241106A10112004200228021C220136021C20012000280200200128020028022811010020042C001B417F4C0440200428021010020B024020022802042203450440200228020822012802002002460D01200241086A210303402003280200220241086A2103200220022802082201280200470D000B0C010B03402003220128020022030D000B0B20012006470D000B0B200441206A24000BFE0101037F230041306B220324002003418080802836021020034200370308200341003A000D200341FA10280000360208200341FE102D00003A000C200041146A2204200341086A1023210520032C0013417F4C0440200328020810020B2005200041186A470440200341808080283602102003420037030841002100200341FA10280000360208200341FE102D00003A000C200341003A000D2003200341086A360220200341286A2004200341086A200341206A1022200328022828021C22040440200441B40B41F00F100B21000B20032C0013417F4C0440200328020810020B2000200236024820002001360204200041043602140B200341306A24000BED0201057F230041106B2204240020002802142201200041186A2205470440034020042001220341106A10112004200128021C220136020C20010440200120012802002802041103000B20042C000B417F4C0440200428020010020B024020032802042202450440200328020822012802002003460D01200341086A210203402002280200220341086A2102200320032802082201280200470D000B0C010B03402002220128020022020D000B0B20012005470D000B0B200041D8006A100E200028024C2201044020002001360250200110020B200041406B28020022010440027F2001200120002802442202460D001A0340200241746A200241786A2802001018200241706A22022001470D000B20002802400B210220002001360244200210020B20002802342201044020002001360238200110020B2000280228220104402000200136022C200110020B200041146A20002802181024200041086A200028020C1018200441106A240020000B0900200041870810210B8F03010A7F0240200141046A220528020022040440200228020420022D000B2205200541187441187541004822051B21072002280200200220051B2108200141046A21050340200441106A21020240024002400240024002400240200428021420042D001B2206200641187441187541004822091B220A2007200A200749220C1B2206044020082002280200200220091B220B2006100F220D0D010B2007200A490D012006450D032002280200200220091B210B0C020B200D417F4A0D010B200428020022020D04200421050C070B200B20082006100F22020D010B200C0D010C050B2002417F4A0D040B200441046A210520042802042202450D03200521040B20042105200221040C00000B000B200521040B200020052802002202047F41000541201003220241106A2003280200101120022004360208200242003702002002410036021C200520023602002002210420012802002802002203044020012003360200200528020021040B2001280204200410262001200128020841016A36020841010B3A0004200020023602000B830901097F230041306B220224002000410C6A22034200370200200041186A220442003702002000200336020820002004360214200041206A410041D400100A1A200042003702800120004201370278024002400240200141037145044020002001360280012000200136027C200128000021032000200141046A3602800120034180C6E9C307470D012000200141096A36028001200020012D00083602782001280009210420002001410D6A2201360280012004450D03200041086A210803402000200141026A220620012F00006A2201360280012000200141026A220520012F00006A36028001200241003602102002420037030820061014220141704F0D03024002402001410B4F0440200141106A4170712209100321032002200941808080807872360210200220033602082002200136020C0C010B200220013A0013200241086A21032001450D010B20032006200110041A0B200120036A41003A00002002200241086A360220200241286A2008200241086A200241206A102C2002280228411C6A2005103D20022C0013417F4C0440200228020810020B2000280280012101200741016A22072004470D000B0C030B100841C4103602001009000B100841EA103602001009000B1013000B200128000021062000200141046A2201360280010240200641014E0440200041146A21094100210803402000200141026A220520012F00006A220136028001200241003602102002420037030820051014220341704F0D02024002402003410B4F0440200341106A4170712207100321042002200741808080807872360210200220043602082002200336020C0C010B200220033A0013200241086A21042003450D010B20042005200310041A0B200320046A41003A0000200128000021042000200141046A36028001200128000421052000200141096A3602800120012D0008210320002001410A6A3602800120012D0009210720002001410B6A3602800120012D000A210A02402004417E6A220141014D0440200141016B044041C8001003220141B40D36020020014201370214200142003702242001420037022C200142003702342001420037023C200141003602442001428080808030370204200141003602202001430000004438020C200141023602100C020B41CC0010032201420037020420014104360248200141900F36020020014284808080800137024020014284808080C0003702382001420037020C20014200370220200142013702142001420037022820014200370230200120033602080C010B4138100322014200370204200141FC113602002001420037020C20014201370214200142003702202001420037022820014200370230200120033602080B2001200536020C2001200A360210200120073602142002200241086A360220200241286A2009200241086A200241206A10482002280228200136021C20022C0013417F4C0440200228020810020B2000280280012101200841016A22082006470D000B0B200020012800003602002000200141046A36028001200020012800043602042000200141086A36028001200241306A240020000F0B1013000BB10602047F027D024020002802042204450D002000280214220341074B0D00200028020820016C210202400240024002400240024002400240200341016B0E0703060200040105070B2002450D072004027F20002A020C20042C0000B29422068B430000004F5D04402006A80C010B4180808080780B3A00004101210320024101460D072000220121040340027F20012A020C200428020420036A22052C0000B29422068B430000004F5D04402006A80C010B4180808080780B2100200520003A0000200341016A22032002470D000B0C070B2002450D06410021010340200420014102746A20002A020C200120046A2D0000B394380200200141016A22012002470D000B0C060B2002450D0520002A020C2107410021010340027F2007200420014101746A22032F0100B3942206430000804F5D20064300000000607104402006A90C010B41000B2100200320003B0100200141016A22012002470D000B0C050B2002450D0420002A020C2107410021010340027F2007200420014102746A2203280200B3942206430000804F5D20064300000000607104402006A90C010B41000B210020032000360200200141016A22012002470D000B0C040B2002450D032004027F20002A020C20042C0000B29422068B430000004F5D04402006A80C010B4180808080780B3A00004101210320024101460D032000220121040340027F20012A020C200428020420036A22052C0000B29422068B430000004F5D04402006A80C010B4180808080780B2100200520003A0000200341016A22032002470D000B0C030B2002450D0220002A020C2106410021010340200420014103746A2006200120046A2D0000B394BB390300200141016A22012002470D000B0C020B2002450D0120002A020C2107410021010340027F2007200420014101746A22032F0100B3942206430000804F5D20064300000000607104402006A90C010B41000B2100200320003B0100200141016A22012002470D000B0C010B2002450D0020002A020C2107410021010340027F2007200420014102746A2203280200B3942206430000804F5D20064300000000607104402006A90C010B41000B210020032000360200200141016A22012002470D000B0B0B840301067F230041306B220324002003420037032820034200370320200342003703182000280208220428000021052000200441046A2204360208200420002802046B410371220604402000200420066B41046A22043602080B2003420037022820034100360220200320053602182003200436021C200320043602242000200028020820054102746A36020820034100360210200342003703082000200341086A1016200328020C2206200328020822006B210402402001450D002004450D0041002105200241014E044003402001200220056C6A21070240200020056A22082D00002204044041012004744101762106410021000340200020076A200341186A200441FF0171101020066B3A00002002200041016A220047044020082D000021040C010B0B20032802082100200328020C21060C010B200741002002100A1A0B200541016A2205200620006B490D000C02000B000B0340200541016A22052004490D000B0B200004402003200036020C200010020B200341186A100E200341306A24000B2E01017F200028020821012000280204210320002D00104102710440200220032001104B0F0B200220032001102E0BC00301087F230041306B22082400200841186A2001101C2109200841086A200310252105024020034101480D00200104400340027F02402005280200200A410C6C6A220622072802042006280200220B6B220420014F0440200420014D0D0120072001200B6A36020441000C020B2006200120046B10050B41000B2104034002402002200320046C200A6A6A2D000022070440200628020020046A41C1002007AD42018879A76B220B3A000020092007200B10150C010B200628020020046A41003A00000B200441016A22042001470D000B200A41016A220A2003470D000B0C010B20052802002101034020012004410C6C6A2202220628020420022802002202470440200620023602040B200441016A22042003470D000B0B2000200910194100210420052802002101200341004A04400340200020012004410C6C6A2201280204200128020022016B2001101D20052802002101200441016A22042003470D000B0B20010440027F2001200120052802042200460D001A0340200041746A220228020022030440200041786A2003360200200310020B200222002001470D000B20052802000B210020052001360204200010020B2009100E200841306A24000BF90301077F230041306B22062400200641186A2001101C21092006410036021020064200370308024002402001450D002001417F4C0D0120062001100322043602082006200436020C2006200120046A360210200121050340200441003A00002006200628020C41016A220436020C2005417F6A22050D000B2001450D00200341024E0440034041222002200320076C6A220A2D00002205410176676B410020051B210441012105034041222005200A6A2D00002208410176676B410020081B2208200420042008481B2104200541016A22052003470D000B200628020820076A20043A000002402004450D0020034101480D0041012004417F6A742108410021050340200920082005200A6A2D00006A20041015200541016A22052003470D000B0B200741016A22072001470D000C02000B000B034002402002200320076C6A220A2D00002205450440200628020820076A41003A00000C010B200628020820076A412220054101766722046B22053A00002005450D0020034101480D004101412120046B742108410021040340200920082004200A6A2D00006A20051015200441016A22042003470D000B0B200741016A22072001470D000B0B2000200910192000200628020C200628020822006B2000101D2006280208220004402006200036020C200010020B2009100E200641306A24000F0B1007000B6901027F2002200228020820022802046B36021020002802082103200028022C2104024020002D001041027104402002200120042003104E0C010B2002200120042003104D0B200228021021012002200228020820022802046B22023602102000200220016B3602180B0900200041800810210B9A0603077F017D017C200041206A21080240200028020820016C22032000280224200028022022046B22014B04402008200320016B10050C010B200320014F0D002000200320046A3602240B024020032000280230200028022C22046B22014B04402000412C6A200320016B10050C010B200320014F0D002000200320046A3602300B0240024002400240024002402000280214417F6A220141064B0D000240200141016B0E06010301040500020B2003450D05410021010340200028022020016A027F200220014103746A2B030020002A020CBBA3220B44000000000000F04163200B44000000000000000066710440200BAB0C010B41000B3A0000200141016A22012003470D000B0C050B100841B0103602001009000B2003450D03410021010340200028022020016A027F200220014102746A280200B220002A020C95220A430000804F5D200A430000000060710440200AA90C010B41000B3A0000200141016A22012003470D000B0C030B2003450D02410021010340200028022020016A027F200220014101746A2E0100B220002A020C95220A430000804F5D200A430000000060710440200AA90C010B41000B3A0000200141016A22012003470D000B0C020B2003450D01410021010340200028022020016A027F200120026A2C0000B220002A020C95220A430000804F5D200A430000000060710440200AA90C010B41000B3A0000200141016A22012003470D000B0C010B2003450D00410021010340200028022020016A027F200220014102746A2A020020002A020C95220A430000804F5D200A430000000060710440200AA90C010B41000B3A0000200141016A22012003470D000B0B2000410036021C2000280208220441014E044003402008280200220920056A2D000021020240200520034F0440200221010C010B20022101200420056A220620034F0D000340200620096A2D00002207200120012007491B210120072002200220074A1B2102200420066A22062003490D000B0B2000200028021C220441C00020012002417F736AAC42018879A76B220141016A200420014A1B36021C200541016A220520002802082204480D000B0B0BC80301067F024020002802042205450D00200228020421042002280200210320002D00104101710440200420036B410C6D4102490D012000220641086A2802002100410121010340200041014E044020032001410C6C6A220421074100210303402005200020016C20036A6A220820082D00002005200428020420006C20036A6A2D00002005200428020020006C20036A6A2D00006A2005200728020820006C20036A6A2D00006B6A3A0000200341016A220320062802082200480D000B20022802042104200228020021030B200141016A2201200420036B410C6D490D000B0C010B20032004470440200420036B410C6D4102490D012000220641086A2802002100410121010340200041014E044020032001410C6C6A21044100210303402005200020016C20036A6A220720072D00002005200428020020006C20036A6A2D00006A3A0000200341016A220320062802082200480D000B20022802042104200228020021030B200141016A2201200420036B410C6D490D000B0C010B2000220241086A2802002200200020016C4F0D00200021030340200320056A220620062D00002005200320006B6A2D00006A3A0000200341016A22032002280208220020016C490D000B0B0B5201037F2000280208220441014E04402002280208210103402002200141016A2205360208200020034102746A20012D000036023820052101200341016A22032004480D000B0B200220002802042004102E0BE30301087F230041306B22092400200941186A2001101C210A200941086A200310252106024020034101480D00200104400340027F02402006280200200B410C6C6A22082205280204200828020022076B220420014F0440200420014D0D012005200120076A36020441000C020B2008200120046B10050B41000B2104034002402002200320046C200B6A6A2C000022050440200828020020046A41C10020052005411F7522076A200773AD42018879A76B22073A0000200A4100200541012007744101756A6B200520054100481B200710150C010B200828020020046A41003A00000B200441016A22042001470D000B200B41016A220B2003470D000B0C010B2006280200210241002101034020022001410C6C6A2204220528020420042802002204470440200520043602040B200141016A22012003470D000B0B2000200A10194100210120062802002104200341004A04400340200020042001410C6C6A2202280204200228020022026B2002101D20062802002104200141016A22012003470D000B0B20040440027F2004200420062802042200460D001A0340200041746A220128020022020440200041786A2002360200200210020B200122002004470D000B20062802000B210020062004360204200010020B200A100E200941306A24000BC10101057F200220022802082204200228020422036B3602100240200028020822054101480D00200241046A21070340200020064102746A28023821050240200420036B2204417F470440200741011005200728020021030C010B200220033602080B200320046A20053A0000200641016A2206200028020822054E0D0120022802082104200228020421030C00000B000B20022001200028022C20051054200228021021012002200228020820022802046B22023602102000200220016B3602180BD10301087F2000280208220341014E04400340200028022C20026A2000280220200128020028020020036C20026A6A2D00003A0000200241016A220220002802082203480D000B0B2001280204200128020022026B410475220441024F0440410121060340200220064104746A22052108024002402005280204220420052802082209460D0020002D0010410171450D0020034101480D01410021020340200028022C200320066C20026A6A20002802202207200528020020036C20026A6A2D00002007200320046C20026A6A2D00006B2007200320096C20026A6A2D00006B2007200528020C20036C20026A6A2D00006A3A0000200241016A2202200028020822034E0D0220052802082109200828020421040C00000B000B20034101480D00410021020340200028022C200320066C20026A6A20002802202207200528020020036C20026A6A2D00002007200320046C20026A6A2D00006B3A0000200241016A2202200028020822034E0D01200828020421040C00000B000B200641016A22062001280204200128020022026B4104752204490D000B0B200320046C22012000280230200028022C22036B22024B04402000412C6A200120026B10050F0B200120024904402000200120036A3602300B0B040041030B3801017F2000418410360200200028022C2201044020002001360230200110020B20002802202201044020002001360224200110020B20000BDC0401077F230041106B220324000240024020002802042206450D00024002402000280214417C6A220241024B0D000240200241016B0E020102000B2000280208210420002802482107200341FF013A0003200120046C22024101480D02200220066A21082006200120076C6A21050340200820046B210841002102200441004A04400340200220036A200220086A2D00003A0000200241016A22022004470D000B0B200520076B2105200320032D00002201410874200120032D00026A41FF01717220032D000120016A41FF01714110747220032D00034118747236020041002102200741004A04400340200220056A200220036A2D0000200020024102746A2D00386C3A0000200241016A220220002802482207480D000B200028020421060B200820064D0D03200028020821040C00000B000B100841E50E3602001009000B2003410036020820034200370300200145044041002006200141027410041A0C010B20014180808080044F0D01200320014102742205100322023602002003200220056A22043602082003200436020420022006200510041A410021050340200220054102746A220220022D00002204410874200420022D00026A41FF01717220022D000120046A41FF01714110747220022D000341187472360000410021022000280248220441004A044003402006200241027422076A220820082A0200200020076A280238B2944300007F4395380200200241016A22022004480D000B0B200620044102746A210620032802002102200541016A22052001470D000B2002450D0020032002360204200210020B200341106A24000F0B1007000BA80502077F017D230041106B220424000240200028020820016C22032000280224200041206A220528020022076B22064B04402005200320066B10050C010B200320064F0D002000200320076A3602240B0240200320002802302000412C6A220728020022086B22064B04402007200320066B10050C010B200320064F0D002000200320086A3602300B0240024002402000280214417C6A220341024B0D000240200341016B0E020102000B2001450D02200528020021062000280208210541002107034041002103200541004A044003402004410C6A20036A200220036A2D0000200020034102746A2802386D3A0000200341016A22032005480D000B0B200420042D000E20042D000D22036B41FF017141087420037220042D000C20036B41FF01714110747220042D000F4118747236020C41002103200541004A04400340200320066A2004410C6A20036A2D00003A0000200341016A220320002802082205480D000B0B200520066A2106200220056A2102200741016A22072001470D000B0C020B1008220041C50E3602001009000B200441FF013A000B2001450D00200028020821052000280220210741002108034041002103200541004A044003402000200341027422066A2802382109200441086A20036A027F200220066A2A02004300007F4394220A8B430000004F5D0440200AA80C010B4180808080780B20096D3A0000200341016A22032005480D000B0B200420042D000A20042D000922036B41FF017141087420037220042D000820036B41FF01714110747220042D000B4118747236020841002103200541004A04400340200320076A200441086A20036A2D00003A0000200341016A220320002802082205480D000B0B200520076A2107200220054102746A2102200841016A22082001470D000B0B2000410036021C200441106A24000B040041020B5201017F200041B40D360200200028023C22010440200041406B2001360200200110020B20002802302201044020002001360234200110020B20002802242201044020002001360228200110020B200010020B5001017F200041B40D360200200028023C22010440200041406B2001360200200110020B20002802302201044020002001360234200110020B20002802242201044020002001360228200110020B20000B950502077F047D02402000280204450D0020002802200D00024002402000280214417D6A220241034B0D000240200241016B0E03010102000B2001450D020340200028023C2202200541037422064104726A280200411074220341107522042003411F7522036A2003732103200220066A280200411074220241107522062002411F7522026A2002732107027F20002A020C22098B430000004F5D04402009A80C010B4180808080780B220220076B220720036B2208B2210A027D2008417F4A04402006B2210B2004B20C010B200220036B2202410020026B200641004A1BB2210B2007410020076B200441004A1BB20B21092000280204200541066C6A2204027F200A200A200A942009200994200B200B94929291220A954300FEFF4694220C8B430000004F5D0440200CA80C010B4180808080780B3B01042004027F2009200A954300FEFF469422098B430000004F5D04402009A80C010B4180808080780B3B01022004027F200B200A954300FEFF469422098B430000004F5D04402009A80C010B4180808080780B3B0100200541016A22052001470D000B0C020B1008220041E30C3602001009000B2001450D000340200028023C2202200541037422034104726A28020022042004411F7522066A2006732106200220036A28020022022002411F7522036A2003732107027F20002A020C22098B430000004F5D04402009A80C010B4180808080780B220320076B220720066B2208B221092008417F4C0440410020076B200720044101481B21044100200320066B22036B200320024101481B21020B20002802042005410C6C6A2203200920092009942004B222092009942002B2220B200B94929291220A9538020820032009200A953802042003200B200A95380200200541016A22052001470D000B0B0BBB04020B7F047D2002280204200228020022036B2204410C6D210B02402004450D00200028023C210C034020012008410C6C22056A2104200320056A21030240024020002802204101470440200028022420084102746A280200450D010B20032A020022108B20032A020422118B9220032A0208220E8B92210F200E43000000005D4101732103027F20002A020C220E8B430000004F5D0440200EA80C010B4180808080780B21052011200F95210E2010200F95210F024020030440200F21100C010B430000803F200E8B93220E8C200E201043000000005D1B2110430000803F200F8B93210E201143000000005D4101730D00200E8C210E0B027F200E2005B2220F94220E8B430000004F5D0440200EA80C010B4180808080780B200C20094103746A22062802046A22032003411F7522076A200773210A2005027F2010200F94220F8B430000004F5D0440200FA80C010B4180808080780B20062802006A22072007411F7522066A2006736B2206200A6B220DB2210F200D417F4C044041002005200A6B22056B200520074101481B2107410020066B200620034101481B21030B200941016A210920042007B22210200F200F942003B2220E200E9420102010949292912210953802002004200F2010953802082004200E2010953802040C010B200420032A0200220F200F200F9420032A0204220F200F949220032A020822102010949291220E9538020020042010200E953802082004200F200E953802040B200841016A2208200B4F0D01200228020021030C00000B000B0B08002000200110210BBD06020A7F047D2002280204200228020022046B2203410C6D210A02402003450D00200028023C210B034020042006410C6C6A21030240024020002802204101470440200028022420064102746A280200450D010B20032A0200220F8B20032A020422108B9220032A0208220D8B92210E200D43000000005D4101732103027F20002A020C220D8B430000004F5D0440200DA80C010B4180808080780B21042010200E95210D200F200E95210E024020030440200E210F0C010B430000803F200D8B93220D8C200D200F43000000005D1B210F430000803F200E8B93210D201043000000005D4101730D00200D8C210D0B027F200D2004B2220E94220D8B430000004F5D0440200DA80C010B4180808080780B200B20084103746A22052802046A411074220741107522032007411F7522076A20077321072004027F200F200E94220F8B430000004F5D0440200FA80C010B4180808080780B20052802006A411074220541107522092005411F7522056A2005736B220520076B220CB2210D027D200C417F4A04402003B2210E2009B20C010B2005410020056B200341004A1BB2210E200420076B2204410020046B200941004A1BB20B210F2001200641066C6A2204027F200D200D200D94200E200E94200F200F94929291220D954300FEFF469422108B430000004F5D04402010A80C010B4180808080780B3B0104200841016A21082004027F200E200D954300FEFF4694220E8B430000004F5D0440200EA80C010B4180808080780B3B0102200F200D954300FEFF4694220F8B430000004F5D04402004200FA83B01000C020B20044180808080783B01000C010B20032A0200220F200F9420032A0204220E200E949220032A0208220D200D949291221043ACC527375D4101734504402003410036020020034100360204200341808080FC033602080C010B2001200641066C6A2204027F4300FEFF462010952210200D94220D8B430000004F5D0440200DA80C010B4180808080780B3B01042004027F2010200E94220E8B430000004F5D0440200EA80C010B4180808080780B3B01022004027F2010200F94220F8B430000004F5D0440200FA80C010B4180808080780B3B01000B200641016A2206200A4F0D01200228020021040C00000B000B0B880302097F067D230041106B220524002004200428020036020420054100360208200542003703002000044020042000200510340B200241036C220041014E0440200320004101746A210A200428020021060340200120032F0104410C6C220B6A2207280200210C200120032F0102410C6C220D6A22082802002104200120032F0100410C6C22026A22092802002100200220066A220220022A02002008280204B22009280204B222109322122007280208B22009280208B2220E93220F942008280208B2200E93220E2007280204B2201093221394932211923802002002200E200CB22000B2220E932210942004B2200E93220E200F9493220F20022A0204923802042002200E201394201220109493220E20022A0208923802082006200D6A2200201120002A0200923802002000200F20002A0204923802042000200E20002A0208923802082006200B6A2200201120002A0200923802002000200F20002A0204923802042000200E20002A020892380208200341066A2203200A490D000B0B200541106A24000BCD0701047F230041306B22052400024002400240024002402000280204450D002000280220450D0020054100360210200541003A0010200542F0DECDCBC6AEDAB7EE00370308200541083A00132003200541086A1023210620052C0013417F4C0440200528020810020B2006200341046A460D0120054100360210200541003A0010200542F0DECDCBC6AEDAB7EE00370308200541083A00132005200541086A360220200541286A2003200541086A200541206A1022200528022828021C22030440200341B40B41D40B100B21070B20052C0013417F4C0440200528020810020B2007450D02200541003602102005420037030820010440200141D6AAD5AA014F0D0420052001410C6C2206100322033602082005200336020C2005200320066A360210200341002006100A1A2001210603402003410C6A21032006417F6A22060D000B2005200336020C0B2007280204210302402004280200220604402001200320022006200541086A10350C010B2001200320022004280204200541086A10620B024020002802204102470D002004280200220304402000200028022436022820054100360228200041246A21042001044020042001200541286A101B0B200241036C22014101480D01200320014102746A2102200428020021010340200120032802004102746A22042004280200200328020473360200200120032802004102746A22042004280200200328020873360200200120032802044102746A22042004280200200328020873360200200120032802044102746A22042004280200200328020073360200200120032802084102746A22042004280200200328020073360200200120032802084102746A220420042802002003280204733602002003410C6A22032002490D000B0C010B200428020421032000200028022436022820054100360228200041246A21042001044020042001200541286A101B0B200241036C22014101480D00200320014101746A2107200428020021010340200120032F010022024102746A220820032F0104220420032F0102220620082802007373360200200120064102746A22082008280200200220047373360200200120044102746A22042004280200200220067373360200200341066A22032007490D000B0B0240024002402000280214417D6A220141034B0D000240200141016B0E03010102000B20002000280204200541086A10610C020B100841A50C3602000C060B20002000280204200541086A105F0B20052802082200450D002005200036020C200010020B200541306A24000F0B100841DD0A3602000C020B100841E00B3602000C010B1007000B1009000BE20101037F02402000280204450D0020002802200D002002280204200228020022056B2202410C6D21032002044020034102490D01200028023C21004101210203402000200241037422016A22042004280200200020052002410C6C6A22042802004103746A2802006A360200200020014104726A22012001280200200020042802004103744104726A2802006A360200200241016A22022003470D000B0C010B200141017422024103490D00200028023C2103410221000340200320004102746A22012001280200200141786A2802006A360200200041016A22002002470D000B0B0BC80101037F20022002280208220341016A360208200020032D00003602202000413C6A2103024020014101742204200041406B280200200028023C22016B41027522054B04402003200420056B100D200328020021010C010B200420054F0D00200041406B200120044102746A3602000B20022001410210312101024020002802204102470D0020014101742201200041406B280200200028023C22046B41027522024B04402003200120026B100D0F0B200120024F0D00200041406B200420014102746A3602000B0B970101027F2000280220210302402002280208200228020422016B2204417F470440200241046A41011005200228020421010C010B200220013602080B200120046A20033A00002002200228020820022802046B3602102002200041406B280200200028023C22016B410275410176200141021032200228021021012002200228020820022802046B22023602102000200220016B3602180BFB0301087F0240024002402000280220220304402001280204220420012802002205460D01200420056B4104752106410021010340200520014104746A22072802002104024020034102460440200028022420044102746A280200450D010B200028023C2203200241037422086A2000280230220920044103746A280200360200200320084104726A200920072802004103744104726A280200360200200241016A21020B200141016A220120064F0D02200028022021030C00000B000B200028023C220320002802302205200128020022042802004103746A2802003602002003200520042802004103744104726A28020036020441012102200128020420046B2206410475220741014B044003402003200241037422086A2005200420024104746A22012802004103746A280200200520012802044103746A2802006B360200200320084104726A200520012802004103744104726A280200200520012802044103744104726A2802006B360200200241016A22022007490D000B0B20064103752201200041406B28020020036B41027522024B04400C030B200120024F0D01200041406B200320014102746A3602000F0B20024101742201200041406B280200200028023C22036B41027522024B04400C020B200120024F0D00200041406B200320014102746A3602000B0F0B2000413C6A200120026B100D0BE00602027F067D230041306B2205240002402000280220450D0020054100360210200541003A0010200542F0DECDCBC6AEDAB7EE00370308200541083A00132003200541086A1023210620052C0013417F4C0440200528020810020B0240024002402006200341046A4704404100210620054100360210200541003A0010200542F0DECDCBC6AEDAB7EE00370308200541083A00132005200541086A360220200541286A2003200541086A200541206A1022200528022828021C22030440200341B40B41D40B100B21060B20052C0013417F4C0440200528020810020B2006450D012004280208210320054100360210200542003703082001200628022020022003200541086A1035024020002802204102470D002000200028022436022820054100360228200041246A21042001044020042001200541286A101B0B200241036C22024101480D00200320024102746A2106200428020021020340200220032802004102746A22042004280200200328020473360200200220032802004102746A22042004280200200328020873360200200220032802044102746A22042004280200200328020873360200200220032802044102746A22042004280200200328020073360200200220032802084102746A22042004280200200328020073360200200220032802084102746A220420042802002003280204733602002003410C6A22032006490D000B0B200528020821022001450D0220002802302104027F20002A020C22078B430000004F5D04402007A80C010B4180808080780BB2210A41002103034020022003410C6C6A22002A0204220B20002A020022098B200B8B9220002A0208220C8B922207952108200920079521070240200C43000000005D4101730440200721090C010B430000803F20088B9322088C2008200943000000005D1B2109430000803F20078B932108200B43000000005D4101730D0020088C21080B200420034103746A22002000280200027F2009200A9422078B430000004F5D04402007A80C010B4180808080780B6B36020020002000280204027F2008200A9422078B430000004F5D04402007A80C010B4180808080780B6B360204200341016A22032001470D000B0C030B100841DD0A3602001009000B100841E00B3602001009000B2002450D010B2005200236020C200210020B200541306A24000BCA0C04097F017E047D027C200041306A210B0240200141017422032000280234200028023022056B410275220A4B0440200B2003200A6B100D0C010B2003200A4F0D002000200520034102746A3602340B02402003200041406B280200200028023C22056B410275220A4B04402000413C6A2003200A6B100D0C010B2003200A4F0D00200041406B200520034102746A3602000B0240024002400240024002402000280214417F6A220341054B0D002000280230210A024002400240200341016B0E050301030204000B2001450D05034020022007410C6C6A220328020422052005411F7522066A200673200328020022062006411F7522046A2004736A200328020822092009411F7522036A2003736A2104027F20002A020C220D8B430000004F5D0440200DA80C010B4180808080780B2108200A20074103746A027F20044504404100210341000C010B200520086C20046D2103200620086C20046D22042009417F4A0D001A200541004821094100027F2008B722112004B799A122129944000000000000E0416304402012AA0C010B4180808080780B22046B210820064100482106027F20112003B799A122119944000000000000E0416304402011AA0C010B4180808080780B21052008200420091B2103410020056B200520061B0BAD2003AD42208684370200200741016A22072001470D000B0C040B2001450D0403402002200741066C6A22032E010222052005411F7522066A20067320032E010022062006411F7522046A2004736A20032E010422092009411F7522036A2003736A2104027F20002A020C220D8B430000004F5D0440200DA80C010B4180808080780B2108200A20074103746A027F20044504404100210341000C010B200520086C20046D2103200620086C20046D22042009417F4A0D001A200541004821094100027F2008B722112004B799A122129944000000000000E0416304402012AA0C010B4180808080780B22046B210820064100482106027F20112003B799A122119944000000000000E0416304402011AA0C010B4180808080780B21052008200420091B2103410020056B200520061B0BAD2003AD42208684370200200741016A22072001470D000B0C030B2001450D0303402002200741036C6A22032C000122052005411F7522066A20067320032C000022062006411F7522046A2004736A20032C000222092009411F7522036A2003736A2104027F20002A020C220D8B430000004F5D0440200DA80C010B4180808080780B2108200A20074103746A027F20044504404100210341000C010B200520086C20046D2103200620086C20046D22042009417F4A0D001A200541004821094100027F2008B722112004B799A122129944000000000000E0416304402012AA0C010B4180808080780B22046B210820064100482106027F20112003B799A122119944000000000000E0416304402011AA0C010B4180808080780B21052008200420091B2103410020056B200520061B0BAD2003AD42208684370200200741016A22072001470D000B0C020B100841B40A3602001009000B2001450D01034020022007410C6C6A22032A0200220F8B20032A020422108B9220032A0208220E8B92210D200E43000000005D4101732103027F20002A020C220E8B430000004F5D0440200EA80C010B4180808080780B21052010200D95210E200F200D95210D024020030440200D210F0C010B430000803F200E8B93220E8C200E200F43000000005D1B210F430000803F200D8B93210E201043000000005D4101730D00200E8C210E0B027F200E2005B2220D94220E8B430000004F5D0440200EA80C010B4180808080780BAD422086210C200A20074103746A200C027F200F200D94220D8B430000004F5D0440200DA80C010B4180808080780BAD84370200200741016A22072001470D000B0B41012105200B2802002202280204210720022802002103200141014B0D012007210B200321020C020B200B2802002201280204220B21072001280200220221030C010B200321022007210B0340200A20054103746A220428020422062007200620074A1B2107200428020022042003200420034A1B21032006200B2006200B481B210B2004200220042002481B2102200541016A22052001470D000B0B200041C0002007200B417F736AAC42018879A76B220041C00020032002417F736AAC42018879A76B220120012000481B41016A36021C0B9E0101027F2000280210200028020C22056B4102470440200320046A2104200120026A417F6A220220014B0440034020012D0000410274220520002802246A280200220620032000280230200028021820056A2802006A200610046A2103200141016A22012002470D000B200221010B20032000280230200028021820012D00004102746A2802006A200420036B10041A0F0B200320052D00002004100A1A0BF902010D7F2000280210200028020C6B220641024604402003410036020041000F0B2002410174417F2002417F4A1B1003210920034100360200024020024101480D002006410175210C200041406B280200210F410021060340410021080240200028023C22072002200A6B220D200D20074A1B220B4101480440410021040C010B2001200A6A211041002104034020072008460D01200028024C200820106A2D00006A2D00002004200C6C6A2104200841016A2208200B470D000B0B2007200D4A044003402004200C6C2104200B41016A220B2007480D000B0B027F200F200420056B4102746A280200220541004E04402003200641016A2204360200200620096A20053A0000200028022420054102746A280200200E6B2107200421064100210541000C010B2007200E6A0B210E2007200A6A220A2002480D000B2005417F4A0D00200041406B28020021000340200020054102746B28020022054100480D000B2003200641016A360200200620096A20053A00000B20090B070020002802000B850201047F0240024020002802082204200028020422036B20014F04400340200320022D00003A00002000200028020441016A22033602042001417F6A22010D000C02000B000B2003200028020022056B220620016A2203417F4C0D01027F41002003200420056B2204410174220520052003491B41FFFFFFFF07200441FFFFFFFF03491B2203450D001A200310030B220420036A2105200420066A220421030340200320022D00003A0000200341016A21032001417F6A22010D000B20042000280204200028020022016B22026B2104200241014E044020042001200210041A0B2000200436020020002005360208200020033602042001450D00200110020B0F0B1007000BC805010D7F230041106B2206240002402000280210200028020C22026B22034104480D004101210720034101752104200028023C220541004A04400340200420076C2107200141016A22012005480D000B0B200641003A000F02402000280250200028024C22056B220141FF014D0440200041CC006A41800220016B2006410F6A106D200028020C21020C010B2001418002460D00200020054180026A3602500B200341014E0440410021010340200028024C200220014101746A2D00006A20013A0000200028020C2102200141016A22012004480D000B0B200028020020022D0001480D0020002000280240360244200641FFFFFF07360208200041406B21052007044020052007200641086A101B0B2000280218220B200028021C460D000340410021094100210C03402000280210200028020C6B410175210D200028023C210A4100210402402008410274220320002802246A28020020096B22024101480440410021010C010B20002802302003200B6A28020020096A6A21034100210103402004200A4E0D01200028024C200320046A2D00006A2D00002001200D6C6A2101200441016A22042002470D000B0B200141016A210420022103200A20024A044003402004200D6C21042001200D6C2101200341016A2203200A470D000B0B2002200A4C04402001200448044020052802002102034020022001200C6A4102746A2008360200200141016A22012004470D000B0B200841016A2208200028021C200B6B410275490D02052006200528020022022001200C6A41027422046A22032802002201360204024020014100480D0020034100200028024420026B41027522036B3602002003200320076A220149044020052007200641046A101B2000280218210B200028024021020C010B200320014D0D002000200220014102746A3602440B200028023C20096A21094100200220046A2802006B210C0C010B0B0B0B200641106A24000BBA0101057F02402000280204200028020022046B2205417D4A0440027F41002005410175220641016A2203200028020820046B220220022003491B41FFFFFFFF07200241017541FFFFFFFF03491B2203450D001A2003417F4C0D02200341017410030B2102200220064101746A220620012F00003B0000200541014E044020022004200510041A0B200020023602002000200220034101746A3602082000200641026A36020420040440200410020B0F0B1007000B41F009100C000BEB0101047F230041106B220524002000200028020C36021041800810034100418008100A2106200241004A044003402006200120036A2D00004102746A2204200428020041016A360200200341016A22032002470D000B0B2000410C6A21014100210303400240200620034102746A28020022044101480D00200520033A000E2005200441FF016C20026D3A000F200028021022042000280214490440200420052F010E3B00002000200028021041026A3602100C010B20012005410E6A106F0B200341FF01492104200341016A210320040D000B200028020C2000280210102720061002200541106A24000BB10101067F2000280214220141204704402000280210200174210602402000280200220120002802082202480440200028020421040C010B417F20024103742002410174220341FEFFFFFF03712003471B1003220420002802042205200241027410042102200504402005100220002802084101742103200028020021010B20002003360208200020023602040B2000200141016A360200200420014102746A20063602002000428080808080043702100B0B4801027F2000280244200041406B28020022006B220341004A0440200341047521030340200120024102746A200020024104746A280200360200200241016A22022003480D000B0B0BD10101057F20002802082202200028020422036B41017520014F04402000200320014101746A3602040F0B02402003200028020022036B2205410175220620016A2204417F4A0440027F41002004200220036B220220022004491B41FFFFFFFF07200241017541FFFFFFFF03491B2202450D001A2002417F4C0D02200241017410030B2104200541014E044020042003200510041A0B200020043602002000200420024101746A3602082000200420064101746A20014101746A36020420030440200310020B0F0B1007000B41A908100C000BAD0301057F230041E0006B220224002002420037021C200242003702242002420037022C200242003702342002420037023C200242003703502002420037035820024200370214200242FF8180808001370308200242003703482002410236024420002000280208220341016A22043602082000200420032D0000220341017422056A3602082003047F200241146A2003107320022802140541000B2004200510041A200241086A10372000280208220328000021042000200341046A2203360208024020042001280204200128020022066B22054B04402001200420056B1005200028020821030C010B200420054F0D002001200420066A3602040B2000200341046A2200200328000022036A36020820040440200241086A2000200320012802002004106A0B20022802542200044020022000360258200010020B2002280248220004402002200036024C200010020B2002280238220004402002200036023C200010020B200228022C2200044020022000360230200010020B20022802202200044020022000360224200010020B20022802142200044020022000360218200010020B200241E0006A24000BA40501077F230041E0006B220324002003420037021C200342003702242003420037022C200342003702342003420037023C200342003703502003420037035820034200370214200342FF81808080013703082003420037034820034102360244200341086A200120021070200341086A1037200341086A106E200341086A20012002200341046A106B2108200041046A2106200328021820032802146B410176210402402000280208200028020422016B2205417F470440200641011005200628020021010C010B200020013602080B200120056A20043A000002402000280208200028020422016B220420042003280218200328021422096B22056A2207490440200620051005200628020021010C010B200420074D0D002000200120076A3602080B200120046A2009200510041A02402000280208200028020422016B2204417B4D044020064104100520002802082105200028020421010C010B2000200120046A41046A22053602080B200120046A2002360200200328020421070240200520016B2202417B4D044020064104100520002802082104200028020421010C010B2000200120026A41046A22043602080B200120026A20073602000240200420016B22022002200328020422046A2205490440200620041005200628020021010C010B200220054D0D002000200120056A3602080B200120026A2008200410041A20080440200810020B20032802141A20032802181A20032802041A20032802542200044020032000360258200010020B2003280248220004402003200036024C200010020B2003280238220004402003200036023C200010020B200328022C2200044020032000360230200010020B20032802202200044020032000360224200010020B20032802142200044020032000360218200010020B200341E0006A24000B13002000280244200041406B2802006B4104750B0D00200004402000104610020B0B1700024020002802040440200010440C010B200010430B0B0900200020013602200B230020003F004110746B41FFFF036A4110764000417F46044041000F0B4100100041010B0900200020013602240B3B01017F2002044003402000200120024180202002418020491B22031004210020014180206A210120004180206A2100200220036B22020D000B0B0BE40201027F024020002001460D000240200120026A20004B0440200020026A220420014B0D010B20002001200210041A0F0B20002001734103712103024002402000200149044020030D022000410371450D0103402002450D04200020012D00003A0000200141016A21012002417F6A2102200041016A22004103710D000B0C010B024020030D002004410371044003402002450D0520002002417F6A22026A2203200120026A2D00003A000020034103710D000B0B200241034D0D00034020002002417C6A22026A200120026A280200360200200241034B0D000B0B2002450D02034020002002417F6A22026A200120026A2D00003A000020020D000B0C020B200241034D0D0020022103034020002001280200360200200141046A2101200041046A21002003417C6A220341034B0D000B200241037121020B2002450D000340200020012D00003A0000200041016A2100200141016A21012002417F6A22020D000B0B0B1A00200020012802082005100604402001200220032004103A0B0B3700200020012802082005100604402001200220032004103A0F0B200028020822002001200220032004200520002802002802141109000B0D002000418D082001410610200BA7010020002001280208200410060440024020012802042002470D00200128021C4101460D002001200336021C0B0F0B02402000200128020020041006450D0002402002200128021047044020012802142002470D010B20034101470D01200141013602200F0B20012002360214200120033602202001200128022841016A360228024020012802244101470D0020012802184102470D00200141013A00360B2001410436022C0B0B88020020002001280208200410060440024020012802042002470D00200128021C4101460D002001200336021C0B0F0B02402000200128020020041006044002402002200128021047044020012802142002470D010B20034101470D02200141013602200F0B200120033602200240200128022C4104460D00200141003B01342000280208220020012002200241012004200028020028021411090020012D003504402001410336022C20012D0034450D010C030B2001410436022C0B200120023602142001200128022841016A36022820012802244101470D0120012802184102470D01200141013A00360F0B20002802082200200120022003200420002802002802181106000B0BA10101027F02400340200145044041000F0B200141DC1441EC15100B2202450D012002280208200041086A280200417F73710D0120002201410C6A280200200228020C41001006044041010F0B20002D0008410171450D01200128020C2200450D01200041DC1441EC15100B22000440200228020C21010C010B0B200128020C2200450D00200041DC1441DC16100B2200450D002000200228020C103B21030B20030BE80301047F230041406A22052400024002400240200141C817410010060440200241003602000C010B2000200110850104404101210320022802002200450D03200220002802003602000C030B2001450D01200141DC1441EC15100B2201450D02200228020022040440200220042802003602000B2001280208220420002802082206417F73714107710D022004417F7320067141E000710D0241012103200028020C200128020C410010060D02200028020C41BC17410010060440200128020C2200450D03200041DC1441A016100B4521030C030B200028020C2204450D0141002103200441DC1441EC15100B2204044020002D0008410171450D032004200128020C10830121030C030B200028020C2204450D02200441DC1441DC16100B2204044020002D0008410171450D032004200128020C103B21030C030B200028020C2200450D02200041DC14418C15100B2204450D02200128020C2200450D02200041DC14418C15100B2200450D022005417F360214200520043602102005410036020C20052000360208200541186A41004127100A1A200541013602382000200541086A20022802004101200028020028021C11040020052802204101470D022002280200450D00200220052802183602000B410121030C010B410021030B200541406B240020030B400002402000200120002D0008411871047F410105410021002001450D01200141DC1441BC15100B2201450D0120012D00084118714100470B100621000B20000B310020002001280208410010060440200120022003103C0F0B20002802082200200120022003200028020028021C1104000B180020002001280208410010060440200120022003103C0B0B0A0020002001200210450BA30101017F230041406A22032400027F410120002001410010060D001A41002001450D001A4100200141DC14418C15100B2201450D001A2003417F360214200320003602102003410036020C20032001360208200341186A41004127100A1A200341013602382001200341086A20022802004101200128020028021C110400410020032802204101470D001A2002200328021836020041010B2100200341406B240020000B0A0020002001410010060B4D01027F20012D00002102024020002D00002203450D0020022003470D00034020012D0001210220002D00012203450D01200141016A2101200041016A210020022003460D000B0B200320026B0B0B00200010281A200010020B08002000102810020B2B01017F0240200028020041746A220022012001280208417F6A22013602082001417F4A0D00200010020B0B05004180130B0D0020004180082001410310200BAD0201057F230041106B220724002001417F73416F6A20024F0440027F20002C000B410048044020002802000C010B20000B2109027F41E7FFFFFF0720014B0440200720014101743602082007200120026A36020C027F230041106B220224002007410C6A2208280200200741086A220A28020049210B200241106A2400200A2008200B1B2802002202410B4F0B047F200241106A41707122022002417F6A22022002410B461B05410A0B0C010B416E0B41016A2208103E21022005044020022006200510290B200320046B220322060440200220056A200420096A200610290B2001410A470440200910020B2000200236020020002008418080808078723602082000200320056A2200360204200741003A0007200020026A20072D00073A0000200741106A24000F0B1013000BC60101037F230041106B22042400024020002C000B410048047F200028020841FFFFFFFF0771417F6A05410A0B220320024F0440027F20002C000B410048044020002802000C010B20000B2203210520020440200520012002107D0B200441003A000F200220036A20042D000F3A0000024020002C000B4100480440200020023602040C010B200020023A000B0B0C010B20002003200220036B027F20002C000B410048044020002802040C010B20002D000B0B22002000200220011091010B200441106A24000B990101037F230041106B22042400416F20024F044002402002410A4D0440200020023A000B200021030C010B20002002410B4F047F200241106A41707122032003417F6A22032003410B461B05410A0B41016A2205103E22033602002000200541808080807872360208200020023602040B2003200120021029200441003A000F200220036A20042D000F3A0000200441106A24000F0B1013000B0D0020004180082001410610200B3701027F200110142202410D6A100322034100360208200320023602042003200236020020002003410C6A2001200241016A10043602000BB30602047F027D024020002802042204450D002000280214220241074B0D00200028020820016C210302400240024002400240024002400240200241016B0E0703060200040105070B2003450D072004027F20002A020C20042C0000B29422068B430000004F5D04402006A80C010B4180808080780B3A00004101210220034101460D072000220121040340027F20012A020C200428020420026A22052C0000B29422068B430000004F5D04402006A80C010B4180808080780B2100200520003A0000200241016A22022003470D000B0C070B2003450D06410021010340200420014102746A220220002A020C2002280200B294380200200141016A22012003470D000B0C060B2003450D0520002A020C2107410021010340027F2007200420014101746A22022F0100B3942206430000804F5D20064300000000607104402006A90C010B41000B2100200220003B0100200141016A22012003470D000B0C050B2003450D0420002A020C2107410021010340027F2007200420014102746A2202280200B3942206430000804F5D20064300000000607104402006A90C010B41000B210020022000360200200141016A22012003470D000B0C040B2003450D032004027F20002A020C20042C0000B29422068B430000004F5D04402006A80C010B4180808080780B3A00004101210220034101460D032000220121040340027F20012A020C200428020420026A22052C0000B29422068B430000004F5D04402006A80C010B4180808080780B2100200520003A0000200241016A22022003470D000B0C030B2003450D0220002A020C2106410021010340200420014103746A2006200420014102746A280200B294BB390300200141016A22012003470D000B0C020B2003450D0120002A020C2107410021010340027F2007200420014101746A22022F0100B3942206430000804F5D20064300000000607104402006A90C010B41000B2100200220003B0100200141016A22012003470D000B0C010B2003450D0020002A020C2107410021010340027F2007200420014102746A2202280200B3942206430000804F5D20064300000000607104402006A90C010B41000B210020022000360200200141016A22012003470D000B0B0BAF0301077F024020002802042203450D002002280204200228020022086B2202410C6D210420002D0010410171044020044102490D012000220541086A2802002102410121010340200241014E044020082001410C6C6A220621074100210003402003200120026C20006A4102746A220920092802002003200628020420026C20006A4102746A2802002003200628020020026C20006A4102746A2802006A2003200728020820026C20006A4102746A2802006B6A360200200041016A220020052802082202480D000B0B200141016A22012004490D000B0C010B2002044020044102490D012000220541086A2802002102410121010340200241014E044020082001410C6C6A21064100210003402003200120026C20006A4102746A220720072802002003200628020020026C20006A4102746A2802006A360200200041016A220020052802082202480D000B0B200141016A22012004470D000B0C010B2000220541086A2802002202200120026C4F0D00200221000340200320004102746A220420042802002003200020026B4102746A2802006A360200200041016A22002005280208220220016C490D000B0B0BA30301077F230041306B220324002003420037032820034200370320200342003703182000280208220628000021042000200641046A2205360208200520002802046B410371220604402000200520066B41046A22053602080B2003420037022820034100360220200320043602182003200536021C200320053602242000200028020820044102746A3602082003410036021020034200370308024020024101480D000240200145044003402000200341086A1016200741016A22072002470D000B20032802082104200328020C1A0C010B03402000200341086A101641002105200328020C2208210420032802082206200847044003400240200520066A22092D000022044504402001200220056C20076A4102746A41003602000C010B2001200220056C20076A4102746A4100200341186A200410102204410120092D0000417F6A7422066A6B200420042006481B36020020032802082106200328020C21080B200541016A22052008200622046B490D000B0B200741016A22072002470D000B0B2004450D002003200436020C200410020B200341186A100E200341306A24000B0D0020004190082001410610200B3001017F200028020821012000280204210320002D0010410271044020022003200110311A0F0B2002200320011098010BE60301087F230041306B22092400200941186A2001101C210A200941086A200310252106024020034101480D00200104400340027F02402006280200200B410C6C6A22082205280204200828020022076B220420014F0440200420014D0D012005200120076A36020441000C020B2008200120046B10050B41000B2104034002402002200320046C200B6A4102746A28020022050440200828020020046A41C10020052005411F7522076A200773AD42018879A76B22073A0000200A4100200541012007744101756A6B200520054100481B200710150C010B200828020020046A41003A00000B200441016A22042001470D000B200B41016A220B2003470D000B0C010B2006280200210241002101034020022001410C6C6A2204220528020420042802002204470440200520043602040B200141016A22012003470D000B0B2000200A10194100210120062802002104200341004A04400340200020042001410C6C6A2202280204200228020022026B2002101D20062802002104200141016A22012003470D000B0B20040440027F2004200420062802042200460D001A0340200041746A220128020022020440200041786A2002360200200210020B200122002004470D000B20062802000B210020062004360204200010020B200A100E200941306A24000B6A01027F2002200228020820022802046B36021020002802082103200028022C2104024020002D00104102710440200220012004200310320C010B2002200120042003109B010B200228021021012002200228020820022802046B22023602102000200220016B3602180B8D04010A7F02402000280208220341004C0440200128020021070C010B200028022C210420002802202106200128020021070340200420024102746A2006200728020020036C20026A4102746A280200360200200241016A220220002802082203480D000B0B200128020420076B2201410475220941024F044020014104752109410121010340200720014104746A2204210A024002402004280204220620042802082208460D0020002D0010410171450D0020034101480D01200028022C210B20002802202105410021020340200B200120036C20026A4102746A2005200428020C20036C20026A4102746A2802002005200428020020036C20026A4102746A2802002005200320066C20026A4102746A2802006B2005200320086C20026A4102746A2802006B6A360200200241016A2202200028020822034E0D0220042802082108200A28020421060C00000B000B20034101480D00200028022C2108200028022021054100210203402008200120036C20026A4102746A2005200428020020036C20026A4102746A2802002005200320066C20026A4102746A2802006B360200200241016A2202200028020822034E0D01200A28020421060C00000B000B200141016A22012009490D000B0B200320096C22012000280230200028022C22036B41027522024B04402000412C6A200120026B100D0F0B200120024904402000200320014102746A3602300B0BCD0603087F027D027C200041206A21090240200028020820016C22032000280224200028022022046B41027522014B04402009200320016B100D0C010B200320014F0D002000200420034102746A3602240B024020032000280230200028022C22046B41027522014B04402000412C6A200320016B100D0C010B200320014F0D002000200420034102746A3602300B0240024002400240024002402000280214417F6A220141064B0D000240200141016B0E06010301040500020B2003450D0520002A020CBB210D20002802202104410021010340200420014102746A027F200220014103746A2B0300200DA3220E9944000000000000E041630440200EAA0C010B4180808080780B360200200141016A22012003470D000B0C050B10084199113602001009000B2003450D0320002A020C210B200028022021044100210103402004200141027422056A027F200220056A280200B2200B95220C8B430000004F5D0440200CA80C010B4180808080780B360200200141016A22012003470D000B0C030B2003450D0220002A020C210B20002802202104410021010340200420014102746A027F200220014101746A2E0100B2200B95220C8B430000004F5D0440200CA80C010B4180808080780B360200200141016A22012003470D000B0C020B2003450D0120002A020C210B20002802202104410021010340200420014102746A027F200120026A2C0000B2200B95220C8B430000004F5D0440200CA80C010B4180808080780B360200200141016A22012003470D000B0C010B2003450D0020002A020C210B200028022021044100210103402004200141027422056A027F200220056A2A0200200B95220C8B430000004F5D0440200CA80C010B4180808080780B360200200141016A22012003470D000B0B20002202410036021C20002204280208220541014E044003402009280200220A20064102746A28020021010240200620034F0440200121000C010B20012100200520066A220720034F0D000340200A20074102746A2802002208200020002008481B210020082001200120084A1B2101200520076A22072003490D000B0B2002200228021C220541C00020002001417F736AAC42018879A76B220041016A200520004A1B36021C200641016A220620042802082205480D000B0B0B3A01017F200041FC11360200200028022C2201044020002001360230200110020B20002802202201044020002001360224200110020B200010020B3801017F200041FC11360200200028022C2201044020002001360230200110020B20002802202201044020002001360224200110020B20000BE30301087F024020002802082204200028020422026B41047520014F04400340200241086A22034200370200200220033602042000200028020441106A22023602042001417F6A22010D000C02000B000B027F024002402002200028020022026B410475220520016A2203418080808001490440027F41002003200420026B2202410375220420042003491B41FFFFFFFF00200241047541FFFFFF3F491B2203450D001A20034180808080014F0D02200341047410030B2102200220034104746A2107200220054104746A220521020340200241086A2203420037020020022003360204200241106A21022001417F6A22010D000B2000280204220320002802002204460D020340200541706A2205200341706A220128020036020020052001280204360204200541086A2206200128020822083602002005200128020C220936020C02402009450440200520063602040C010B200820063602082001200341786A2203360204200341003602002001410036020C0B200122032004470D000B2000280204210420002802000C030B1007000B41AD11100C000B20040B2101200020053602002000200736020820002002360204200120044704400340200441746A200441786A2802001018200441706A22042001470D000B0B2001450D00200110020B0B09002000418D0810210B0B004188011003200110490B0B9A1104004180080B6C6E6F726D616C00636F6C6F7200757600706F736974696F6E00556E6B6E6F776E20656E74726F707900616C6C6F6361746F723C543E3A3A616C6C6F636174652873697A655F74206E2920276E272065786365656473206D6178696D756D20737570706F727465642073697A650041F4080BFC080100000003000000070000000F0000001F0000003F0000007F000000FF000000FF010000FF030000FF070000FF0F0000FF1F0000FF3F0000FF7F0000FFFF0000FFFF0100FFFF0300FFFF0700FFFF0F00FFFF1F00FFFF3F00FFFF7F00FFFFFF00FFFFFF01FFFFFF03FFFFFF07FFFFFF0FFFFFFF1FFFFFFF3FFFFFFF7F616C6C6F6361746F723C543E3A3A616C6C6F636174652873697A655F74206E2920276E272065786365656473206D6178696D756D20737570706F727465642073697A6500556E7369676E6564207479706573206E6F7420737570706F7274656420666F72206E6F726D616C73004E6F20706F736974696F6E2061747472696275746520666F756E642E205573652044494646206E6F726D616C20737472617465677920696E73746561642E004E3363727431355665727465784174747269627574654500F80B00009C0500004E33637274313147656E6572696341747472496945450000200C0000BC050000B4050000506F736974696F6E206174747220686173206265656E206F7665726C6F616465642C205573652044494646206E6F726D616C20737472617465677920696E73746561642E00466F726D6174206E6F7420737570706F7274656420666F72206E6F726D616C206174747269627574652028666C6F61742C20696E743136206F6E6C792900466F726D6174206E6F7420737570706F7274656420666F72206E6F726D616C206174747269627574652028666C6F61742C20696E743332206F7220696E743136206F6E6C792900000000000000F406000002000000030000000400000005000000060000000700000008000000090000000A0000000B0000000C0000004E3363727431304E6F726D616C41747472450000200C0000E0060000B4050000616C6C6F6361746F723C543E3A3A616C6C6F636174652873697A655F74206E2920276E272065786365656473206D6178696D756D20737570706F727465642073697A650000556E737570706F7274656420636F6C6F7220696E70757420666F726D61742E00556E737570706F7274656420636F6C6F72206F757470757420666F726D61742E00000000000000F00700000D0000000E0000000F00000010000000110000001200000013000000140000001500000016000000170000004E3363727439436F6C6F724174747245004E33637274313147656E65726963417474724968454500200C0000CD070000B4050000200C0000BC070000E407000000000000E40700000D00000018000000190000001A00000011000000120000001B0000001C00000015000000160000001D000000556E737570706F7274656420666F726D61742E004D656D6F7279206D75737420626520616C69676E65676E6564206F6E20342062797465732E004E6F742061206372742066696C652E00636F6C6F72004465636F64696E6720746F706F6C6F6779206661696C656400556E737570706F7274656420666F726D61742E00616C6C6F6361746F723C543E3A3A616C6C6F636174652873697A655F74206E2920276E272065786365656473206D6178696D756D20737570706F727465642073697A650041F8110B9507D40500001E0000001F00000020000000210000001100000022000000230000002400000025000000160000002600000062617369635F737472696E6700616C6C6F6361746F723C543E3A3A616C6C6F636174652873697A655F74206E2920276E272065786365656473206D6178696D756D20737570706F727465642073697A6500766563746F72007374643A3A657863657074696F6E000000000000B409000028000000290000002A000000537439657863657074696F6E00000000F80B0000A409000000000000E0090000010000002B0000002C000000537431316C6F6769635F6572726F7200200C0000D0090000B409000000000000140A0000010000002D0000002C000000537431326C656E6774685F6572726F7200000000200C0000000A0000E0090000537439747970655F696E666F00000000F80B0000200A00004E31305F5F637878616269763131365F5F7368696D5F747970655F696E666F4500000000200C0000380A0000300A00004E31305F5F637878616269763131375F5F636C6173735F747970655F696E666F45000000200C0000680A00005C0A00004E31305F5F637878616269763131375F5F70626173655F747970655F696E666F45000000200C0000980A00005C0A00004E31305F5F637878616269763131395F5F706F696E7465725F747970655F696E666F4500200C0000C80A0000BC0A00004E31305F5F637878616269763132305F5F66756E6374696F6E5F747970655F696E666F4500000000200C0000F80A00005C0A00004E31305F5F637878616269763132395F5F706F696E7465725F746F5F6D656D6265725F747970655F696E666F45000000200C00002C0B0000BC0A000000000000AC0B00002E0000002F0000003000000031000000320000004E31305F5F637878616269763132335F5F66756E64616D656E74616C5F747970655F696E666F4500200C0000840B00005C0A000076000000700B0000B80B0000446E0000700B0000C40B000063000000700B0000D00B0000504B63007C0C0000DC0B000001000000D40B0000000000008C0A00002E0000003300000030000000310000003400000035000000360000003700000000000000680C00002E00000038000000300000003100000034000000390000003A0000003B0000004E31305F5F637878616269763132305F5F73695F636C6173735F747970655F696E666F4500000000200C0000400C00008C0A000000000000EC0A00002E0000003C00000030000000310000003D0041901D0B02306F"; +/* var wasm_simd = ""; + var detector = new Uint8Array([0,97,115,109,1,0,0,0,1,4,1,96,0,0,3,3,2,0,0,5,3,1,0,1,12,1,0,10,22,2,12,0,65,0,65,0,65,0,252,10,0,0,11,7,0,65,0,253,4,26,11]); + + var wasm = wasm_base; + + if (WebAssembly.validate(detector)) { + wasm = wasm_simd; + console.log("Warning: corto is using experimental SIMD support"); + }*/ + var instance, heap; + + var env = { + emscripten_notify_memory_growth: function(index) { + heap = new Uint8Array(instance.exports.memory.buffer); + }, + proc_exit: function(rval) { return 52; }, //WASI_ENOSYS + }; + + function unhex(data) { + var bytes = new Uint8Array(data.length / 2); + for (var i = 0; i < data.length; i += 2) { + bytes[i / 2] = parseInt(data.substr(i, 2), 16); + } + return bytes.buffer; + } + + var promise = + WebAssembly.instantiate(unhex(wasm_base), { env:env, wasi_unstable:env }) + .then(function(result) { + instance = result.instance; + instance.exports._start(); + env.emscripten_notify_memory_growth(0); + }); + + function pad() { + return; + var s = instance.exports.sbrk(0); + var t = s & 0x3; + if(t) + instance.exports.sbrk(4 -t); + } + + function decode(source, shortIndex = false, shortNormal = false, colorComponents = 4) { + if(!source.length) + source = new Uint8Array(source); + var len = source.length; + var exports = instance.exports; + var sbrk = exports.malloc; //yes I am a criminal. + var free = exports.free; + + +//copy source to heap. we could use directly source, but that is good only for the first em call. + + + //We could use the heap instead of malloc, don't know cost (not much probably), and can't debug properly. + //set initial heap position, to be restored at the end of the deconding. + var pos = sbrk(0); + + var sptr = sbrk(len); + heap.set(source, sptr); + + var decoder = exports.newDecoder(len, sptr); + var nvert = exports.nvert(decoder); + var nface = exports.nface(decoder); + + var geometry = { + nvert: nvert, + nface: nface, + } + + var ngroups = exports.ngroups(decoder); + if(ngroups > 0) { + pad(); + var gp = sbrk(4*ngroups); + exports.groups(decoder, gp); + geometry.groups = new Uint32Array( ngroups*4); + geometry.grous.set(gp); + free(gp); + } + + var hasNormal = exports.hasNormal(decoder); + var hasColor = exports.hasColor(decoder); + var hasUv = exports.hasUv(decoder); + + var iptr = 0, pptr = 0, nptr = 0, cptr = 0, uptr = 0; + if(nface) { + pad(); //memory align needed for int, short, floats arrays if using sbrk + if(shortIndex) { + iptr = sbrk(nface * 6); + exports.setIndex16(decoder, iptr); + } else { + iptr = sbrk(nface * 12); + exports.setIndex32(decoder, iptr); + } + } + + pptr = sbrk(nvert * 12); + exports.setPositions(decoder, pptr); + + if(hasUv) { + pad(); + uptr = sbrk(nvert * 8); + exports.setUvs(decoder, uptr); + } + + if(hasNormal) { + pad(); + if(shortNormal) { + nptr = sbrk(nvert * 6); + exports.setNormals16(decoder, nptr); + } else { + pptr = sbrk(nvert * 12); + exports.setNormals32(decoder, nptr); + } + } + + if(hasColor) { + pad(); + cptr = sbrk(nvert * colorComponents); + exports.setColors(decoder, cptr, colorComponents); + } + pad(); + exports.decode(decoder); + + + //typed arrays needs to be created in javascript space, not as views of heap (next call will overwrite them!) + //hence the double typed array. + if(nface) { + if(shortIndex) + geometry.index = new Uint16Array(new Uint16Array(heap.buffer, iptr, nface*3)); + else + geometry.index = new Uint32Array(new Uint32Array(heap.buffer, iptr, nface*3)); + } + + geometry.position = new Float32Array(new Float32Array(heap.buffer, pptr, nvert*3)); + + if(hasNormal) { + if(shortNormal) + geometry.normal = new Int16Array(new Int16Array(heap.buffer, nptr, nvert*3)); + else + geometry.normal = new Float32Array(new Float32Array(heap.buffer, nptr, nvert*3)); + } + + if(hasColor) + geometry.color = new Uint8Array(new Uint8Array(heap.buffer, cptr, nvert*colorComponents)); + + if(hasUv) + geometry.uv = new Float32Array(new Float32Array(heap.buffer, uptr, nvert*2)); + + exports.deleteDecoder(decoder); + + //restore heap position if using sbrk + //sbrk(pos - sbrk(0)); + free(sptr); + if(iptr) free(iptr); + if(pptr) free(pptr); + if(cptr) free(cptr); + if(nptr) free(nptr); + if(uptr) free(uptr); + + return geometry; + }; + + return { + ready: promise, + decode: decode + }; +})(); + +if (typeof exports === 'object' && typeof module === 'object') + module.exports = CortoDecoder; +else if (typeof define === 'function' && define['amd']) + define([], function() { + return CortoDecoder; + }); +else if (typeof exports === 'object') + exports["CortoDecoder"] = CortoDecoder; diff --git a/nexus3d/dist/index.html b/nexus3d/dist/index.html new file mode 100644 index 00000000..58d148e1 --- /dev/null +++ b/nexus3d/dist/index.html @@ -0,0 +1,29 @@ + + + + + Nexus example + + + + + + + + + + +
+ + + + diff --git a/nexus3d/dist/models/gargo.nxz b/nexus3d/dist/models/gargo.nxz new file mode 100644 index 0000000000000000000000000000000000000000..4172627876f342432e5d7fbd063925d322c53d9b GIT binary patch literal 216832 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 z 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%0A&t?rM&*4jNeJv}w~ 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 literal 0 HcmV?d00001 diff --git a/nexus3d/package.json b/nexus3d/package.json new file mode 100644 index 00000000..2c3476b0 --- /dev/null +++ b/nexus3d/package.json @@ -0,0 +1,41 @@ +{ + "name": "nexus3d", + "version": "1.0.10", + "description": "Streaming 3D on the web (threejs supported)", + "author": "", + "license": "ISC", + "private": false, + "browser": "build/nexus3D.min.js", + "main": "build/nexus3D.min.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "start": "webpack-dev-server --hot", + "build": "webpack --mode development", + "rollup": "rollup -c rollup.config.js", + "buildlib": "webpack -c webpacklib.config.js --mode development" + }, + "repository": { + "type": "git", + "url": "https://github.com/cnr-isti-vclab/nexus" + }, + "keywords": [], + "devDependencies": { + "html-webpack-plugin": "^5.5.4", + "rollup-plugin-terser": "^7.0.2", + "webpack": "^5.89.0", + "webpack-cli": "^5.1.4", + "webpack-dev-server": "^4.15.1" + }, + "dependencies": { + "three": "^0.159.0" + }, + "files": [ + "build/nexus3D.min.js", + "build/nexus3D.js", + "dist/corto.em.js", + "LICENSE", + "package.json", + "README.md", + "src" + ] +} diff --git a/nexus3d/rollup.config.js b/nexus3d/rollup.config.js new file mode 100644 index 00000000..c7c5b557 --- /dev/null +++ b/nexus3d/rollup.config.js @@ -0,0 +1,32 @@ +import { terser } from "rollup-plugin-terser"; + +function header() { + return { + + renderChunk( code ) { + return "// vcg.isti.cnr.it/nexus/license\n" + code; + } + }; + +} + +export default [ + { + input: './src/Nexus3D.js', + output: [{ + format: 'umd', + name: 'Nexus3D', + file: 'build/nexus3D.min.js', + plugins: [terser()], + globals: { three: 'THREE' } + }, + { + format: 'umd', + name: 'Nexus3D', + file: 'build/nexus3D.js', + globals: { three: 'THREE' } + }], + external: [ 'three' ] + } + +]; diff --git a/nexus3d/src/Binary.js b/nexus3d/src/Binary.js new file mode 100644 index 00000000..024b4802 --- /dev/null +++ b/nexus3d/src/Binary.js @@ -0,0 +1,104 @@ +/* UTILITIES */ + +function getUint64(view) { + var s = 0; + var lo = view.getUint32(view.offset, true); + var hi = view.getUint32(view.offset + 4, true); + view.offset += 8; + return ((hi * (1 << 32)) + lo); +} + +function getUint32(view) { + var s = view.getUint32(view.offset, true); + view.offset += 4; + return s; +} + +function getUint16(view) { + var s = view.getUint16(view.offset, true); + view.offset += 2; + return s; +} + +function getFloat32(view) { + var s = view.getFloat32(view.offset, true); + view.offset += 4; + return s; +} + +/* MATRIX STUFF */ + +function vecMul(m, v, r) { + var w = m[3]*v[0] + m[7]*v[1] + m[11]*v[2] + m[15]; + + r[0] = (m[0]*v[0] + m[4]*v[1] + m[8 ]*v[2] + m[12 ])/w; + r[1] = (m[1]*v[0] + m[5]*v[1] + m[9 ]*v[2] + m[13 ])/w; + r[2] = (m[2]*v[0] + m[6]*v[1] + m[10]*v[2] + m[14])/w; +} + + +function matMul(a, b, r) { + r[ 0] = a[0]*b[0] + a[4]*b[1] + a[8]*b[2] + a[12]*b[3]; + r[ 1] = a[1]*b[0] + a[5]*b[1] + a[9]*b[2] + a[13]*b[3]; + r[ 2] = a[2]*b[0] + a[6]*b[1] + a[10]*b[2] + a[14]*b[3]; + r[ 3] = a[3]*b[0] + a[7]*b[1] + a[11]*b[2] + a[15]*b[3]; + + r[ 4] = a[0]*b[4] + a[4]*b[5] + a[8]*b[6] + a[12]*b[7]; + r[ 5] = a[1]*b[4] + a[5]*b[5] + a[9]*b[6] + a[13]*b[7]; + r[ 6] = a[2]*b[4] + a[6]*b[5] + a[10]*b[6] + a[14]*b[7]; + r[ 7] = a[3]*b[4] + a[7]*b[5] + a[11]*b[6] + a[15]*b[7]; + + r[ 8] = a[0]*b[8] + a[4]*b[9] + a[8]*b[10] + a[12]*b[11]; + r[ 9] = a[1]*b[8] + a[5]*b[9] + a[9]*b[10] + a[13]*b[11]; + r[10] = a[2]*b[8] + a[6]*b[9] + a[10]*b[10] + a[14]*b[11]; + r[11] = a[3]*b[8] + a[7]*b[9] + a[11]*b[10] + a[15]*b[11]; + + r[12] = a[0]*b[12] + a[4]*b[13] + a[8]*b[14] + a[12]*b[15]; + r[13] = a[1]*b[12] + a[5]*b[13] + a[9]*b[14] + a[13]*b[15]; + r[14] = a[2]*b[12] + a[6]*b[13] + a[10]*b[14] + a[14]*b[15]; + r[15] = a[3]*b[12] + a[7]*b[13] + a[11]*b[14] + a[15]*b[15]; +} + +function matInv(m, t) { + var s = 1.0/( + m[12]* m[9]*m[6]*m[3]-m[8]*m[13]*m[6]*m[3]-m[12]*m[5]*m[10]*m[3]+m[4]*m[13]*m[10]*m[3]+ + m[8]*m[5]*m[14]*m[3]-m[4]*m[9]*m[14]*m[3]-m[12]*m[9]*m[2]*m[7]+m[8]*m[13]*m[2]*m[7]+ + m[12]*m[1]*m[10]*m[7]-m[0]*m[13]*m[10]*m[7]-m[8]*m[1]*m[14]*m[7]+m[0]*m[9]*m[14]*m[7]+ + m[12]*m[5]*m[2]*m[11]-m[4]*m[13]*m[2]*m[11]-m[12]*m[1]*m[6]*m[11]+m[0]*m[13]*m[6]*m[11]+ + m[4]*m[1]*m[14]*m[11]-m[0]*m[5]*m[14]*m[11]-m[8]*m[5]*m[2]*m[15]+m[4]*m[9]*m[2]*m[15]+ + m[8]*m[1]*m[6]*m[15]-m[0]*m[9]*m[6]*m[15]-m[4]*m[1]*m[10]*m[15]+m[0]*m[5]*m[10]*m[15] + ); + + t[ 0] = (m[9]*m[14]*m[7]-m[13]*m[10]*m[7]+m[13]*m[6]*m[11]-m[5]*m[14]*m[11]-m[9]*m[6]*m[15]+m[5]*m[10]*m[15])*s; + t[ 1] = (m[13]*m[10]*m[3]-m[9]*m[14]*m[3]-m[13]*m[2]*m[11]+m[1]*m[14]*m[11]+m[9]*m[2]*m[15]-m[1]*m[10]*m[15])*s; + t[ 2] = (m[5]*m[14]*m[3]-m[13]*m[6]*m[3]+m[13]*m[2]*m[7]-m[1]*m[14]*m[7]-m[5]*m[2]*m[15]+m[1]*m[6]*m[15])*s; + t[ 3] = (m[9]*m[6]*m[3]-m[5]*m[10]*m[3]-m[9]*m[2]*m[7]+m[1]*m[10]*m[7]+m[5]*m[2]*m[11]-m[1]*m[6]*m[11])*s; + + t[ 4] = (m[12]*m[10]*m[7]-m[8]*m[14]*m[7]-m[12]*m[6]*m[11]+m[4]*m[14]*m[11]+m[8]*m[6]*m[15]-m[4]*m[10]*m[15])*s; + t[ 5] = (m[8]*m[14]*m[3]-m[12]*m[10]*m[3]+m[12]*m[2]*m[11]-m[0]*m[14]*m[11]-m[8]*m[2]*m[15]+m[0]*m[10]*m[15])*s; + t[ 6] = (m[12]*m[6]*m[3]-m[4]*m[14]*m[3]-m[12]*m[2]*m[7]+m[0]*m[14]*m[7]+m[4]*m[2]*m[15]-m[0]*m[6]*m[15])*s; + t[ 7] = (m[4]*m[10]*m[3]-m[8]*m[6]*m[3]+m[8]*m[2]*m[7]-m[0]*m[10]*m[7]-m[4]*m[2]*m[11]+m[0]*m[6]*m[11])*s; + + t[ 8] = (m[8]*m[13]*m[7]-m[12]*m[9]*m[7]+m[12]*m[5]*m[11]-m[4]*m[13]*m[11]-m[8]*m[5]*m[15]+m[4]*m[9]*m[15])*s; + t[ 9] = (m[12]*m[9]*m[3]-m[8]*m[13]*m[3]-m[12]*m[1]*m[11]+m[0]*m[13]*m[11]+m[8]*m[1]*m[15]-m[0]*m[9]*m[15])*s; + t[10] = (m[4]*m[13]*m[3]-m[12]*m[5]*m[3]+m[12]*m[1]*m[7]-m[0]*m[13]*m[7]-m[4]*m[1]*m[15]+m[0]*m[5]*m[15])*s; + t[11] = (m[8]*m[5]*m[3]-m[4]*m[9]*m[3]-m[8]*m[1]*m[7]+m[0]*m[9]*m[7]+m[4]*m[1]*m[11]-m[0]*m[5]*m[11])*s; + + t[12] = (m[12]*m[9]*m[6]-m[8]*m[13]*m[6]-m[12]*m[5]*m[10]+m[4]*m[13]*m[10]+m[8]*m[5]*m[14]-m[4]*m[9]*m[14])*s; + t[13] = (m[8]*m[13]*m[2]-m[12]*m[9]*m[2]+m[12]*m[1]*m[10]-m[0]*m[13]*m[10]-m[8]*m[1]*m[14]+m[0]*m[9]*m[14])*s; + t[14] = (m[12]*m[5]*m[2]-m[4]*m[13]*m[2]-m[12]*m[1]*m[6]+m[0]*m[13]*m[6]+m[4]*m[1]*m[14]-m[0]*m[5]*m[14])*s; + t[15] = (m[4]*m[9]*m[2]-m[8]*m[5]*m[2]+m[8]*m[1]*m[6]-m[0]*m[9]*m[6]-m[4]*m[1]*m[10]+m[0]*m[5]*m[10])*s; +} + +let binary = { + getUint64: getUint64, + getUint32: getUint32, + getUint16: getUint16, + getFloat32: getFloat32, + vecMul: vecMul, + matMul: matMul, + matInv: matInv +} + +export { getUint64, getUint32, getUint16, getFloat32, vecMul, matMul, matInv } + diff --git a/nexus3d/src/Cache.js b/nexus3d/src/Cache.js new file mode 100644 index 00000000..3b1504f1 --- /dev/null +++ b/nexus3d/src/Cache.js @@ -0,0 +1,426 @@ + +function powerOf2(n) { + return n && (n & (n - 1)) === 0; +} + +var targetError = 2.0; //error won't go lower than this if we reach it +var maxError = 15; //error won't go over this even if fps is low +var minFps = 15; +var maxPending = 3; +var maxBlocked = 3; +var maxReqAttempt = 2; +var maxCacheSize = 1024 *(1<<20); +var drawBudget = 5*(1<<20); + +function _Cache() { + let t = this; + t.cortopath = '.'; + t.frame = 0; //keep track of the time + + t.maxCacheSize = maxCacheSize; + t.minFps = minFps; + t.currentFps = 0; + t.targetError = targetError; + t.currentError = targetError; + t.maxError = maxError; + t.realError = 0; + + t.pending = 0; + t.maxPending = 3; + t.cacheSize = 0; + t.candidates = []; //list of nodes to be loaded + t.nodes = new Map(); //for each mesh a list of node ids. + + t.last_frametime = 0; + t.frametime = 0; + t.end_frametime = 0; + + t.debug = { + verbose : false, //debug messages + nodes : false, //color each node + draw : false, //final rendering call disabled + extract : false, //extraction disabled} + } + + t.totswapped = 0; //in the last frame. + t.swaprate = 0; + t.lastupdate = performance.now(); +} + +_Cache.prototype = { + getTargetError: function() { return this.targetError; }, + getMinFps: function() { return this.minFps; }, + setMinFps: function(fps) { this.minFps = fps; }, + getMaxCacheSize: function() { return this.maxCacheSize; }, + setMaxCacheSize: function(size) { this.maxCacheSize = size; }, + setTargetError: function(error) { this.targetError = error; }, + + loadCorto: function() { + let corto = new Worker(this.cortopath + '/corto.em.js'); + corto.requests = {}; + corto.count = 0; + corto.postRequest = function(node) { + corto.postMessage({ buffer: node.buffer, request:this.count, rgba_colors: true, short_index: true, short_normals: true}); + node.buffer = null; + this.requests[this.count++] = node; + } + corto.onmessage = function(e) { + var request = e.data.request; + var node = this.requests[request]; + delete this.requests[request]; + node.model = e.data.model; + node.cache.readyGeometryNode(node.mesh, node.id, node.model); + }; + this.corto = corto; + }, + + + beginFrame: function(fps) { //each context has a separate frame count. + let c = this; + c.frametime = performance.now(); + let elapsed = c.frametime - c.last_frametime; + c.last_frametime = c.frametime; + if(elapsed < 500) + c.currentFps = 0.9*c.currentFps + 0.1*(1000/elapsed); + + fps = c.currentFps; + + c.frame++; + c.candidates = []; + if(fps && c.minFps) { + c.currentFps = fps; + const r = c.minFps/fps; + if(r > 1.1) + c.currentError *= 1.05; + if(r < 0.9) + c.currentError *= 0.95; + + c.currentError = Math.max(c.targetError, Math.min(c.maxError, c.currentError)); + + } else + c.currentError = c.targetError; + + c.rendered = 0; + c.realError = 1e20; + c.totswapped = 0; + }, + + endFrame: function() { + this.update(); + }, + + + + requestNode: function(mesh, id) { + mesh.status[id] = 2; + + + this.cacheSize += mesh.nsize[id]; + mesh.reqAttempt[id] = 0; + + if(!this.nodes.has(mesh)) + this.nodes.set(mesh, new Set()); + this.nodes.get(mesh).add(id); + + this.requestNodeGeometry(mesh, id); + this.requestNodeTexture(mesh, id); + }, + + requestNodeGeometry: function(mesh, id) { + this.pending++; + mesh.status[id]++; + let url = mesh.deepzoom ? mesh.baseurl + id + ".nxn" : mesh.url; + let start = mesh.deepzoom ? 0 : mesh.noffsets[id]; + let end = mesh.deepzoom ? 0 : mesh.noffsets[id+1]; + let request = mesh.georeq[id] = mesh.httpRequest(url, + start, + end, + ()=>{ + delete mesh.georeq[id]; + this.loadNodeGeometry(request, mesh, id); + this.pending--; + }, + ()=>{ + delete mesh.georeq[id]; + if(this.debug.verbose) console.log("Geometry request error!"); + this.recoverNode(mesh, id, 0); + this.pending--; + }, + ()=>{ + delete mesh.georeq[id]; + if(this.debug.verbose) console.log("Geometry request abort!"); + this.removeNode(mesh, id); + this.pending--; + }, + 'arraybuffer' + ); + }, + + requestNodeTexture: function(mesh, id) { + + if(!mesh.vertex.texCoord) return; + + let tex = mesh.patches[mesh.nfirstpatch[id]*3+2]; + mesh.texref[tex]++; + mesh.status[id]++; + + let url = mesh.deepzoom ? mesh.baseurl + tex + ".jpg" : mesh.url; + let start = mesh.deepzoom ? 0 : mesh.textures[tex]; + let end = mesh.deepzoom ? 0 : mesh.textures[tex+1]; + let request = mesh.texreq[tex] = mesh.httpRequest(url, + start, + end, + ()=>{ + delete mesh.texreq[tex]; + this.loadNodeTexture(request, mesh, id, tex); + + }, + ()=>{ + delete mesh.texreq[tex]; + if(this.debug.verbose) console.log("Texture request error!"); + this.recoverNode(mesh, id, 1); + }, + ()=>{ + delete mesh.texreq[tex]; + if(this.debug.verbose) console.log("Texture request abort!"); + this.removeNode(mesh, id); + }, + 'blob' + ); + }, + + recoverNode: function(mesh, id, mode) { + if(mesh.status[id] == 0) return; + + mesh.status[id]--; + + let t = this; + + if(mesh.reqAttempt[id] > maxReqAttempt) { + if(this.debug.verbose) console.log("Max request limit for " + m.url + " node: " + n); + t.removeNode(mesh, id); + return; + } + + mesh.reqAttempt[id]++; + + switch (mode){ + case 0: + t.requestNodeGeometry(mesh, id); + if(this.debug.verbose) console.log("Recovering geometry for " + m.url + " node: " + n); + break; + case 1: + t.requestNodeTexture(mesh, id); + if(this.debug.verbose) console.log("Recovering texture for " + m.url + " node: " + n); + break; + } + }, + + loadNodeGeometry: function(request, mesh, id) { + if(mesh.status[id] == 0) return; + + if(!mesh.compressed) + this.readyGeometryNode(mesh, id, request.response); + else { + if(!this.corto) this.loadCorto(); + this.corto.postRequest( { mesh:mesh, id:id, buffer: request.response, cache: this }); + } + }, + + + loadNodeTexture: function(request, mesh, id, texid) { + if(mesh.status[id] == 0) { + throw "Should not load texture twice"; + } + + let blob = request.response; + let callback = (img) => { + if(mesh.status[id] == 0) //call was aborted. + return; + mesh.createTexture(texid, img); + + mesh.status[id]--; + + if(mesh.status[id] == 2) + this.readyNode(mesh, id); + } + + if(typeof createImageBitmap != 'undefined') { + try { + createImageBitmap(blob, { imageOrientation: 'flipY' }).then(callback); + } catch(error) { //old version of firefox do not support options (but flip anyway) + createImageBitmap(blob).then(callback); + } + + } else { //fallback for IOS + var urlCreator = window.URL || window.webkitURL; + var img = document.createElement('img'); + img.onerror = function(e) { console.log("Texture loading error!"); }; + img.src = urlCreator.createObjectURL(blob); + + img.onload = function() { + urlCreator.revokeObjectURL(img.src); + callback(img); + } + } + }, + + removeNode: function(mesh, id) { + this.nodes.get(mesh).delete(id); + + if(mesh.status[id] == 0) { + if(this.debug.verbose) + console.log("Double remove due to abort."); + return; //TODO + } + + mesh.status[id] = 0; + if (id in mesh.georeq && mesh.georeq[id].readyState != 4) { + mesh.georeq[id].abort(); + delete mesh.georeq[id]; + //this.pending--; + } + mesh.availableNodes--; + this.cacheSize -= mesh.nsize[id]; + mesh.deleteNodeGeometry(id); + + if(!mesh.vertex.texCoord) return; + + const tex = mesh.patches[mesh.nfirstpatch[id]*3+2]; //TODO assuming one texture per node + + if (tex in mesh.texreq && mesh.texreq[tex].readyState != 4) { + mesh.texreq[tex].abort(); + delete mesh.texreq[tex]; + } + + mesh.texref[tex]--; + if(mesh.texref[tex] == 0) { + mesh.deleteTexture(tex); + } + }, + + readyGeometryNode: function(mesh, id, buffer) { + if(mesh.status[id] == 0) //call was aborted + return; + const nv = mesh.nvertices[id]; + const nf = mesh.nfaces[id]; + let geometry = {}; + + + if(!mesh.corto) { + geometry.index = new Uint16Array(buffer, nv*mesh.vsize, nf*3); + geometry.position = new Float32Array(buffer, 0, nv*3); + + var off = nv*12; + if(mesh.vertex.texCoord) { + geometry.uv = new Float32Array(buffer, off, nv*2); + off += nv*8; + } + if(mesh.vertex.normal) { + geometry.normal = new Int16Array(buffer, off, nv*3); + off += nv*6; + } + + if(mesh.vertex.color) { + geometry.color = new Uint8Array(buffer, off, nv*4); + off += nv*4; + } + + + } else { + geometry = buffer; + } + + //if(nf == 0) + // scramble(nv, v, no, co); + + mesh.createNodeGeometry(id, geometry); + mesh.status[id]--; + + if(mesh.status[id] == 2) { + this.readyNode(mesh, id); + } + }, + + //the node is finished, add to cache, and update counters + readyNode: function(mesh, id) { + + mesh.status[id]--; + if(mesh.status[id] != 1) throw "A ready node should have status ==1" + + + mesh.reqAttempt[id] = 0; + //this.pending--; + mesh.createNode(id); + mesh.availableNodes++; + + for(let callback of mesh.onUpdate) + callback(); + this.update(); + }, + + flush: function(mesh) { + if(!this.nodes.has(mesh)) return; + + for(let id of this.nodes.get(mesh)) + this.removeNode(mesh, id); + this.nodes.delete(mesh); + }, + + update: function() { + if(this.pending >= maxPending) + return; + + //the best candidate has the highest error + let best = null; + for(let c of this.candidates) { + if(c.mesh.status[c.id] == 0 && (!best || c.error > best.error)) + best = c; + } + if(!best) return; + + // record amount of data transfer per second. + /* + let now = performance.now(); + if(Math.floor(now/1000) > Math.floor(this.lastupdate/1000)) { //new second + this.swaprate = (this.totswapped/1000)/(now - this.lastupdate); //transfer in mb/s + if(this.debug.verbose) + console.log("Memory loaded in GPU: ", this.swaprate); + this.totswapped = 0; + this.lastupdate = now; + } + */ + + + //make room for new nodes! + + while(this.cacheSize > this.maxCacheSize) { + let worst = null; + + //find node with smallest error in cache and remove it if worse than the best candidate. + for(let [mesh, ids] of this.nodes) { + for(let id of ids) { + //we need to recompute the errors for the cache, as if not traversed doesn't get updated. + let error = mesh.errors[id]; + let frame = mesh.frames[id]; + if( !worst || error < worst.error) { + worst = { id: id, frame: frame, error: error, mesh: mesh } + } + } + } + if(!worst || worst.error >= best.error*0.9) { + //(worst.frame + 30 >= best.frame && )) //dont' remove if the best candidate is not good enogh + return; + } + this.removeNode(worst.mesh, worst.id); + } + this.totswapped += best.mesh.nsize[best.id]; + this.candidates = this.candidates.filter(e => e.mesh == best.mesh && e.id == best.id); + this.requestNode(best.mesh, best.id); + this.update(); //try again. + } +}; + +let Cache = new _Cache; +export { Cache } + diff --git a/nexus3d/src/Mesh.js b/nexus3d/src/Mesh.js new file mode 100644 index 00000000..24fd750d --- /dev/null +++ b/nexus3d/src/Mesh.js @@ -0,0 +1,261 @@ +import { getUint64, getUint32, getUint16, getFloat32 } from './Binary.js' + +/* MESH DEFINITION */ +let Debug = { verbose: true }; + +let glP = WebGLRenderingContext.prototype; +let attrGlMap = [glP.NONE, glP.BYTE, glP.UNSIGNED_BYTE, glP.SHORT, glP.UNSIGNED_SHORT, glP.INT, glP.UNSIGNED_INT, glP.FLOAT, glP.DOUBLE]; +let attrSizeMap = [0, 1, 1, 2, 2, 4, 4, 4, 8]; + +//All addresses in the file are n*256 so, 256 * 2^32 is the max size of a Nxs file +var padding = 256; + +let Mesh = function(url) { + var t = this; + t.isReady = false; + t.onLoad = []; + t.onUpdate = []; + t.reqAttempt = 0; + t.georeq = {}; //keeps track of existing httprequests + t.texreq = {}; + t.frame = 0; //last time this mesh was traversed in rendering. + t.availableNodes = 0 + if(url) + t.open(url); +} + +Mesh.prototype = { + open: function(url) { + let mesh = this; + mesh.url = url; + mesh.httpRequest(url, + 0, + 88, + function() { + if(Debug.verbose) console.log("Loading header for " + mesh.url); + let view = new DataView(this.response); + view.offset = 0; + mesh.reqAttempt++; + const header = mesh.importHeader(view); + if(!header) { + if(Debug.verbose) console.log("Empty header!"); + if(mesh.reqAttempt < maxReqAttempt) mesh.open(mesh.url + '?' + Math.random()); // BLINK ENGINE CACHE BUG PATCH + return; + } + mesh.reqAttempt = 0; + for(let i in header) + mesh[i] = header[i]; + mesh.vertex = mesh.signature.vertex; + mesh.face = mesh.signature.face; + mesh.renderMode = mesh.face.index?["FILL", "POINT"]:["POINT"]; + mesh.compressed = (mesh.signature.flags & (2 | 4)); //meco or corto + mesh.meco = (mesh.signature.flags & 2); + mesh.corto = (mesh.signature.flags & 4); + + mesh.deepzoom = (mesh.signature.flags & 8); + if(mesh.deepzoom) + mesh.baseurl = url.substr(0, url.length -4) + '_files/'; + + + mesh.requestIndex(); + }, + function() { console.log("Open request error!");}, + function() { console.log("Open request abort!");} + ); + }, + + httpRequest: function(url, start, end, load, error, abort, type) { + if(!type) type = 'arraybuffer'; + var r = new XMLHttpRequest(); + r.open('GET', url, true); + r.responseType = type; + if(end) + r.setRequestHeader("Range", "bytes=" + start + "-" + (end -1)); + r.onload = function(){ + switch (this.status){ + case 0: +// console.log("0 response: server unreachable.");//returned in chrome for local files + error(); + break; + case 206: +// console.log("206 response: partial content loaded."); + load.bind(this)(); + break; + case 200: +// console.log("200 response: server does not support byte range requests."); + if(end == 0) + load.bind(this)(); + else + error(); + break; + } + }; + r.onerror = error; + r.onabort = abort; + r.send(); + return r; + }, + + requestIndex: function() { + var mesh = this; + var end = 88 + mesh.nodesCount*44 + mesh.patchesCount*12 + mesh.texturesCount*68; + mesh.httpRequest(this.url, + 88, + end, + function() { if(Debug.verbose) console.log("Loading index for " + mesh.url); mesh.handleIndex(this.response); }, + function() { console.log("Index request error!");}, + function() { console.log("Index request abort!");} + ); + }, + + handleIndex: function(buffer) { + let t = this; + let view = new DataView(buffer); + view.offset = 0; + + const n = t.nodesCount; + + t.noffsets = new Uint32Array(n); + t.nvertices = new Uint32Array(n); + t.nfaces = new Uint32Array(n); + t.nerrors = new Float32Array(n); + t.nspheres = new Float32Array(n*5); + t.nsize = new Float32Array(n); + t.nfirstpatch = new Uint32Array(n); + + for(let i = 0; i < n; i++) { + t.noffsets[i] = padding*getUint32(view); //offset + t.nvertices[i] = getUint16(view); //verticesCount + t.nfaces[i] = getUint16(view); //facesCount + t.nerrors[i] = getFloat32(view); + view.offset += 8; //skip cone + for(let k = 0; k < 5; k++) + t.nspheres[i*5+k] = getFloat32(view); //sphere + tight + t.nfirstpatch[i] = getUint32(view); //first patch + } + t.sink = n -1; + + t.patches = new Uint32Array(view.buffer, view.offset, t.patchesCount*3); //noded, lastTriangle, texture + t.nroots = t.nodesCount; + for(let j = 0; j < t.nroots; j++) { + for(let i = t.nfirstpatch[j]; i < t.nfirstpatch[j+1]; i++) { + if(t.patches[i*3] < t.nroots) + t.nroots = t.patches[i*3]; + } + } + + view.offset += t.patchesCount*12; + + t.textures = new Uint32Array(t.texturesCount); + t.texref = new Uint32Array(t.texturesCount); + for(let i = 0; i < t.texturesCount; i++) { + t.textures[i] = padding*getUint32(view); + view.offset += 16*4; //skip proj matrix + } + + t.vsize = 12 + (t.vertex.normal?6:0) + (t.vertex.color?4:0) + (t.vertex.texCoord?8:0); + t.fsize = 6; + + //problem: I have no idea how much space a texture is needed in GPU. 10x factor assumed. + let tmptexsize = new Uint32Array(n-1); + let tmptexcount = new Uint32Array(n-1); + for(let i = 0; i < n-1; i++) { + for(let p = t.nfirstpatch[i]; p != t.nfirstpatch[i+1]; p++) { + let tex = t.patches[p*3+2]; + tmptexsize[i] += t.textures[tex+1] - t.textures[tex]; + tmptexcount[i]++; + } + t.nsize[i] = t.vsize*t.nvertices[i] + t.fsize*t.nfaces[i]; + } + for(let i = 0; i < n-1; i++) { + t.nsize[i] += 10*tmptexsize[i]/tmptexcount[i]; + } + + t.status = new Uint8Array(n); //0 for none, 1 for ready, 2+ for waiting data + t.frames = new Uint32Array(n); + t.errors = new Float32Array(n); //biggest error of instances + t.reqAttempt = new Uint8Array(n); + + t.isReady = true; + for(let callback of t.onLoad) + callback(this); + }, + + importAttribute: function(view) { + let a = {}; + a.type = view.getUint8(view.offset++, true); + a.size = view.getUint8(view.offset++, true); + a.glType = attrGlMap[a.type]; + a.normalized = a.type < 7; + a.stride = attrSizeMap[a.type]*a.size; + if(a.size == 0) return null; + return a; + }, + + importElement: function(view) { + let e = []; + for(let i = 0; i < 8; i++) + e[i] = this.importAttribute(view); + return e; + }, + + importVertex: function(view) { //enum POSITION, NORMAL, COLOR, TEXCOORD, DATA0 + const e = this.importElement(view); + let color = e[2]; + if(color) { + color.type = 2; //unsigned byte + color.glType = attrGlMap[2]; + } + return { position: e[0], normal: e[1], color: e[2], texCoord: e[3], data: e[4] }; + }, + + //enum INDEX, NORMAL, COLOR, TEXCOORD, DATA0 + importFace: function(view) { + const e = this.importElement(view); + let color = e[2]; + if(color) { + color.type = 2; //unsigned byte + color.glType = attrGlMap[2]; + } + return { index: e[0], normal: e[1], color: e[2], texCoord: e[3], data: e[4] }; + }, + + importSignature: function(view) { + let s = {}; + s.vertex = this.importVertex(view); + s.face = this.importFace(view); + s.flags = getUint32(view); + return s; + }, + + importHeader: function(view) { + const magic = getUint32(view); + if(magic != 0x4E787320) return null; + let h = {}; + h.version = getUint32(view); + h.verticesCount = getUint64(view); + h.facesCount = getUint64(view); + h.signature = this.importSignature(view); + h.nodesCount = getUint32(view); + h.patchesCount = getUint32(view); + h.texturesCount = getUint32(view); + h.sphere = { + center: [getFloat32(view), getFloat32(view), getFloat32(view)], + radius: getFloat32(view) + }; + return h; + }, + //OVERRIDE THESE METHOS + + //assemble node and geometry + createNode: function(id) {}, + + createNodeGeometry: function(id, data) {}, + deleteNodeGeometry: function(id) {}, + + createTexture: function(id, image) {}, + deleteTexture: function(id) {}, +}; + + +export { Mesh } diff --git a/nexus3d/src/Monitor.js b/nexus3d/src/Monitor.js new file mode 100644 index 00000000..fa25bdfe --- /dev/null +++ b/nexus3d/src/Monitor.js @@ -0,0 +1,321 @@ +/* +Nexus +Copyright (c) 2012-2020, Visual Computing Lab, ISTI - CNR +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +let css = ` +#nexusMonitor { + position:absolute; + top:10px; left:10px; + opacity:0.7; + background-color:black; + color:white; + padding:10px 20px; + width:300px; + font-family:Arial; + font-size: 12px; +} +#nexusMonitor table, #monitor tr { color:white; } +#nexusMonitor p { margin:0px 10px; padding:5px 0px; } + +.progress { + color:black; + height: 1.5em; + width: 240px; + background-color: #c9c9c9; + position: relative; +} +.progress:before { + content: attr(data-label); + font-size: 0.8em; + position: absolute; + text-align: center; + top: 5px; + left: 0; + right: 0; +} +.progress .value { + background-color: #7cc4ff; + display: inline-block; + height: 100%; +} +#nexusMonitor canvas { + width:300px; + height:600px; + background: transparent; +} +` + + +let html = ` + + + + + + + + + + + + + + + + + + +
Cache
+ +
Error
+ +
Fps
+ +
Progress
+ +
Rendered
Show patches
Target Error px
+ +`; + +//INJECT STYLE IN THE HEADER + + +function greenToRed(percent) { + const r = 255 * percent/100; + const g = 255 - (255 * percent/100); + return 'rgb('+r+','+g+',0)'; +} + +function toHuman(bytes, unit = '', decimals = 1) { + if (bytes === 0) return '0 Bytes'; + + const k = 1024; + const dm = decimals < 0 ? 0 : decimals; + const sizes = ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y']; + + const i = Math.floor(Math.log(bytes) / Math.log(k)); + return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i] + unit; +} + +class Monitor { + + constructor(cache, model) { + this.cache = cache; + this.div = null; + this.interval = null; + this.model = model; + this.mesh = model.mesh; + + document.addEventListener('DOMContentLoaded', () => { + + let style = document.createElement('STYLE'); + style.type = 'text/css'; + style.appendChild(document.createTextNode(css)); + document.head.appendChild(style); + + + this.div = document.createElement('div'); + this.div.setAttribute('id', 'nexusMonitor'); + + this.div.innerHTML = html; + let e = this.elements = { + cacheLabel: this.div.querySelector('#nexusMonitorCache'), + cacheStyle: this.div.querySelector('#nexusMonitorCache > span'), + errorLabel: this.div.querySelector('#nexusMonitorError'), + errorStyle: this.div.querySelector('#nexusMonitorError > span'), + fpsLabel : this.div.querySelector('#nexusMonitorFps'), + fpsStyle : this.div.querySelector('#nexusMonitorFps > span'), + progressLabel : this.div.querySelector('#nexusMonitorProgress'), + progressStyle : this.div.querySelector('#nexusMonitorProgress > span'), + + + trianglesCount: this.div.querySelector('#nexusMonitorTriangles'), + nodesCheckbox : this.div.querySelector('#nexusMonitorNodes'), + editError : this.div.querySelector('#nexusMonitorEditError') + } + + let cache = this.cache; + e.nodesCheckbox.addEventListener('click', function() { + cache.debug.nodes = this.checked; + for( let [mesh, ids] of cache.nodes) { + for(let callback of mesh.onUpdate) + callback(mesh); + } + }); + + e.editError.addEventListener('change', function() { + cache.targetError = this.value; + for( let [mesh, ids] of cache.nodes) { + for(let callback of mesh.onUpdate) + callback(mesh); + } + }); + + document.body.appendChild(this.div); + this.canvas = this.div.querySelector('canvas'); + this.ctx = this.canvas.getContext('2d'); + + + this.start(); + }, false); + } + + update() { + let context = this.cache; + + let cacheSize = context.cacheSize; + let maxCacheSize = context.maxCacheSize; + let cacheFraction = parseInt(100*cacheSize/maxCacheSize); + + let targetError = context.targetError; + let realError = Math.min(1000, context.realError); + let currentError = context.currentError; + let errorFraction = 100*Math.min(5, Math.log2(currentError/targetError))/5; + + let minFps = context.minFps; + let currentFps = context.currentFps; + let fpsFraction = 100*(Math.min(3, Math.max(0, 60/currentFps -1)))/3; + + + let mesh = context.nodes.keys().next(); + if(mesh) mesh = mesh.value; + + let progressFraction = mesh? mesh.availableNodes / mesh.nodesCount : 0; + let progressNodes = mesh ? mesh.availableNodes : 0; + + let rendered = context.rendered; + + let e = this.elements; + + e.cacheLabel.setAttribute('data-label', `${toHuman(cacheSize, 'B')}/${toHuman(maxCacheSize, 'B')}`); + e.cacheStyle.style.background_color = greenToRed(cacheFraction); + e.cacheStyle.style.width = cacheFraction; + + e.errorLabel.setAttribute('data-label', `Real: ${realError.toFixed(1)} px Target: ${currentError.toFixed(1)} px`); + e.errorStyle.style.background_color = greenToRed(errorFraction); + e.errorStyle.style.width = errorFraction; + + e.fpsLabel.setAttribute('data-label', `${parseInt(Math.round(currentFps))} fps`); + e.fpsStyle.style.background_color = greenToRed(fpsFraction); + e.fpsStyle.style.width = fpsFraction; + + e.progressLabel.setAttribute('data-label', `${parseInt(Math.round(progressNodes))} nodes`); + e.progressStyle.style.background_color = greenToRed(progressFraction); + e.progressStyle.style.width = progressFraction; + + e.trianglesCount.innerHTML = `${toHuman(rendered)} triangles`; + + if(!e.editError.value) + e.editError.value = targetError; + + if(!this.levels) { + if(this.mesh.isReady) + this.buildLevels(); + } else + this.updateNodeMonitor(); + } + + start() { + this.stop(); + this.div.style.display = 'block'; + this.interval = setInterval(() => { this.update(); }, 100); + } + + stop() { + this.div.style.display = 'none'; + if(this.interval) + clearInterval(this.interval); + } + + buildLevels() { + let m = this.mesh; + this.levelsCount = 0; + this.levels = new Array(m.nodesCount); + this.levels.fill(-1); + this.nodes = []; + for(let n = 0; n < m.nodesCount-1; n++) { + let level = this.levels[n]; + if(level == -1) + level = this.levels[n] = 0; + if(this.nodes.length <= level) + this.nodes[level] = []; + + this.nodes[level].push(n); + + for(let p = m.nfirstpatch[n]; p < m.nfirstpatch[n+1]; p++) { + let child = m.patches[p*3]; + if(child == m.nodesCount-1) + continue; + + this.levels[child] = level+1; + } + this.levelsCount = Math.max(level, this.levelsCount); + } + } + + updateNodeMonitor() { + + let ctx = this.ctx; + ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); + + let h = 10; + let w = 300; + + let y = 0; + for(let level = 0; level < this.nodes.length; level++) { + let nodes = this.nodes[level]; + let x = 0; + let dx = 10; + for(let i = 0; i < nodes.length; i++) { + let n = nodes[i]; + let selected = this.model.traversal.selected[n]; + let status = this.mesh.status[n]; + let color = 'red'; + if(selected) + color = 'green'; + else if(status == 1) + color = 'yellow'; + else if(status > 1) + color = 'orange'; + else + color = 'red'; + //let color = status == 0 ? 'red' : (status == 1? 'green' : 'orange'); + + + ctx.beginPath(); + ctx.rect(x, y, dx, h); + ctx.fillStyle = color; + ctx.fill(); + x += dx + 1; + if(x >= w - 10) { + x = 0; + y += 11; + } + } + y += h + 3; + } + } +} + + +export { Monitor } diff --git a/nexus3d/src/NXS.js b/nexus3d/src/NXS.js new file mode 100644 index 00000000..de533457 --- /dev/null +++ b/nexus3d/src/NXS.js @@ -0,0 +1,295 @@ +import * as THREE from 'three' +import { Traversal } from './Traversal.js' +import { Mesh } from './Mesh.js' +import { Cache } from './Cache.js' + +function NXS(url, onLoad, onUpdate, material) { + + THREE.Object3D.call( this ); + + this.type = 'NXS'; + + this.autoUpdate = true; + this.mesh = null; + this.url = url; + this.onLoadCallback = onLoad; + this.onUpdate = onUpdate || function() {}; + this.debug = false; + + if(this.url) + this.open(this.url) + +} + +NXS.prototype = Object.assign( Object.create( THREE.Object3D.prototype ), { + + constructor: NXS, + + isNXS: true, + + copy: function(source) { + + Object3D.prototype.copy.call( this, source, false ); + throw "Can't really copy." + return this; + + }, + + + open: function(url) { + let t = this; + this.mesh = new Mesh(url); + this.mesh.createMaterials = (materials) => { t.createMaterials(materials) }; + this.mesh.createNode = (id) => { t.updateMaterial(id) }; + this.mesh.createNodeGeometry = (id, geometry) => { t.createNodeGeometry(id, geometry); }; + this.mesh.createTexture = (id, image) => { t.createTexture(id, image); }; + this.mesh.deleteNodeGeometry = (id) => { t.deleteNodeGeometry(id); }; + this.mesh.deleteTexture = (id) => { t.deleteTexture(id); }; + this.mesh.onLoad = () => { t.onLoad(); } + this.mesh.onUpdate = () => { t.onUpdate(t.mesh); } + this.traversal = new Traversal(); + this.cache = new Cache(); + this.materials = []; + this.textures = {}; + this.nodes = {} //can't use getObjectById, it's slow //TODO use Map! + + this.toDelete = []; + + //this is needed for a onbeforerender callback! + const geometry = new THREE.BoxBufferGeometry(0.0, 0.0, 0.0); + const material = new THREE.MeshBasicMaterial(); + let cube = new THREE.Mesh(geometry, material); + cube.frustumCulled = false; + cube.onBeforeRender = (renderer, scene, camera, geometry, material, group) => { + t.onBeforeRender(renderer, scene, camera, geometry, material, group) } + cube.onAfterRender = (renderer, scene, camera, geometry, material, group) => { + t.onAfterRender(renderer, scene, camera, geometry, material, group) } + this.add(cube); + + }, + + set material(material) { + this.material = material; + this.material.needsUpdate = true; + }, + + set debug(debug) { + if(this.debug != debug) { + this.debug = debug; + this.material.needsUpdate = true; + } + }, + + onLoad: function() { + const c = this.mesh.sphere.center; + const center = new THREE.Vector3(c[0], c[1], c[2]); + const radius = this.mesh.sphere.radius; + + this.boundingSphere = new THREE.Sphere(center, radius); + if(this.material == null || this.material == false) + this.createMaterial(); + this.onLoadCallback(this); + }, + + onBeforeRender: function(renderer, scene, camera, geometry, material, group) { + let s = new THREE.Vector2(); + renderer.getSize(s); + + for(let id of this.toDelete) + this.actuallyDeleteNodeGeometry(id); + this.toDelete = []; + //object modelview is multiplied by camera during rendering, we need to do it here for visibility computations + this.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, this.matrixWorld ); + this.traversal.updateView([0, 0, s.width, s.height], camera.projectionMatrix.elements, this.modelViewMatrix.elements); + this.traversal.traverse(this.mesh, this.cache); + this.setVisibility(); + }, + onAfterRender: function(renderer, scene, camera, geometry, material, group) { + this.cache.update(); + }, + + setVisibility: function() { + if(this.material) { + if(this.debug != this.cache.debug.nodes) { + this.debug = this.cache.debug.nodes; + this.material.needsUpdate = true; + } + //threejs increments version when setting neeedsUpdate + if(this.material.version > 0) { + this.updateMaterials(); + this.material.version = 0; + this.onUpdate(); + } + } + + //set visibile what is visible! + let t = this.traversal; + let m = this.mesh; + + if(!m.isReady) + return; + let rendered = 0; + + for(let id = 0; id < m.nodesCount; id++) { + if(!(id in this.nodes)) + continue; + + let node = this.nodes[id]; + //let err = m.errors[n]; //careful this is wrong if multiple instances + let err = m.nerrors[id]; + + node.visible = false; + + + if(!t.selected[id]) continue; + + var sp = m.nspheres; + var off = id*5; + if(!t.isVisible(sp[off], sp[off+1], sp[off+2], sp[off+4])) //tight radius + continue; + + //There is no way to control group visibility, so we remove and recreate them. + node.geometry.clearGroups(); + + var offset = 0; + var end = 0; + var last = m.nfirstpatch[id+1]-1; + for (var p = m.nfirstpatch[id]; p < m.nfirstpatch[id+1]; ++p) { + var child = m.patches[p*3]; + + if(!t.selected[child]) { + end = m.patches[p*3+1]; + if(p < last) //we join patches if possible. + continue; + } + + if(end > offset) { + node.visible = true; + if(this.cache.debug.nodes) { + node.material[0].color = new THREE.Color(0, 1, 0); + if(child == m.sink) { + } else { + //node.material[0].color.setHSL(err/50, 0.5, 0.7); + let h = id*89.0/244.0; + h -= Math.floor(h); + node.material[0].color.setHSL(h, 0.6, 0.7); + } + } + + node.geometry.addGroup(offset*3, (end-offset)*3) + rendered += end - offset; + } + offset = m.patches[p*3+1]; + } + } + this.cache.rendered += rendered; + }, + + //we need to hijack the material when nxs has textures: when we change the material it might have a map or not + //and we need to update all the materials! + //we also need to create an array of materials for groups to work. + + //Nexus has a list of materials and, usually, each node attach his texture. + createMaterial: function() { + let options = {} + if(this.mesh.vertex.texCoord) + options.map = null; + if(this.mesh.vertex.color) + options.vertexColors = true; + //options.wireframe = true; + this.material = new THREE.MeshStandardMaterial(options); + //this.material = [new THREE.MeshPhongMaterial(options)]; + //this.material[0] = new THREE.MeshPhongMaterial( { color: 0xff0000 } ); + }, + + updateMaterials: function() { + for(let id in this.nodes) + this.updateMaterial(id); + }, + + updateMaterial: function(id) { + let node = this.nodes[id]; + const m = this.mesh; + //debug has each node with a different color, so we need to clone materials + if(!m.vertex.texCoord && !this.cache.debug.nodes) { + this.nodes[id].material = [this.material]; + return; + } + + let material = this.material.clone(); + if(m.vertex.texCoord) { + var texid = m.patches[m.nfirstpatch[id]*3+2]; + if(texid != -1) + material.map = this.textures[texid]; + } + + this.nodes[id].material = [material]; + }, + + createNodeGeometry: function(id, data) { + let geometry = new THREE.BufferGeometry() + geometry.setAttribute( 'position', new THREE.BufferAttribute(data.position, 3 ) ); + if(data.normal) { + geometry.setAttribute( 'normal', new THREE.BufferAttribute(data.normal, 3, true ) ); + } + if(data.color) + geometry.setAttribute( 'color', new THREE.BufferAttribute(data.color, 4, true ) ); + if(data.uv) + geometry.setAttribute( 'uv', new THREE.BufferAttribute(data.uv, 2 ) ); + geometry.setIndex(new THREE.BufferAttribute( data.index, 1 ) ); + + //if material include maps we need a different material for each + let node = new THREE.Mesh(geometry); + node.frustumCulled = false; + geometry.addGroup(0, data.index.length); + node.visible = false; + this.nodes[id] = node; + this.add(node); + }, + + createTexture: function(id, image) { + this.textures[id] = new THREE.CanvasTexture(image); + }, + //schedule for removal of this node( might not want to delete it in the middle of something. + //TODO check if this is really needed! + deleteNodeGeometry: function(id) { + this.toDelete.push(id); + }, + //check for leaking memory. + actuallyDeleteNodeGeometry(id) { + this.remove(this.nodes[id]); + delete this.nodes[id]; + }, + + deleteTexture: function(tex) { + if(!this.textures[tex]) + throw "Deleting missing texture!" + this.textures[tex].dispose(); + delete this.textures[tex]; + }, + + + toJSON: function ( meta ) { + throw "Can't" + }, + + raycast: function ( raycaster, intersects ) { + + const levels = this.levels; + + if ( levels.length > 0 ) { + + _v1.setFromMatrixPosition( this.matrixWorld ); + + const distance = raycaster.ray.origin.distanceTo( _v1 ); + + this.getObjectForDistance( distance ).raycast( raycaster, intersects ); + + } + + }, + +} ); + + +export { NXS }; diff --git a/nexus3d/src/NXSRaw.js b/nexus3d/src/NXSRaw.js new file mode 100644 index 00000000..d4179aba --- /dev/null +++ b/nexus3d/src/NXSRaw.js @@ -0,0 +1,402 @@ +import * as THREE from 'three' +import { Traversal } from './traversal.js' +import { Mesh } from './mesh.js' +import { Cache } from './cache.js' + +function NXSRaw(url, onLoad, onUpdate, renderer, material) { + + THREE.Object3D.call( this ); + + this.type = 'NXS'; + + this.gl = renderer.getContext(); + this.material = material; + + this.url = url; + this.onLoad = [onLoad]; + this.onUpdate = []; + if(onUpdate) this.onUpdate.push(onUpdate); + + this.autoUpdate = true; + this.mesh = new Mesh(); + + this.vbo = []; + this.ibo = []; + this.textures = []; + this.attributes = {}; //here we store the uniform attributes of the shader. + + //this is needed for a onbeforerender callback! + if(!this.material) + this.material = new THREE.MeshStandardMaterial(); + + if(this.url) { + if(typeof url == 'object') { + this.nxs = this.url; + this.nxs.onLoad.push((m) => { + this.mesh = this.nxs.mesh; + this.traversal = this.nxs.traversal; + this.cache = this.nxs.cache; + this.vbo = this.nxs.vbo; + this.ibo = this.nxs.ibo; + this.textures = this.nxs.textures; + this.onLoadCallback(this); + }); + } else + this.open(this.url); + } + +} + +NXSRaw.prototype = Object.assign( Object.create( THREE.Object3D.prototype ), { + + constructor: NXSRaw, + + isNXS: true, + + copy: function(source) { + + Object3D.prototype.copy.call( this, source, false ); + throw "Can't really copy." + return this; + + }, + + + open: function(url) { + let t = this; + this.mesh.open(url); + this.mesh.createNode = (id) => { }; + this.mesh.createNodeGeometry = (id, geometry) => { t.createNodeGeometry(id, geometry); }; + this.mesh.createTexture = (id, image) => { t.createTexture(id, image); }; + this.mesh.deleteNodeGeometry = (id) => { t.deleteNodeGeometry(id); }; + this.mesh.deleteTexture = (id) => { t.deleteTexture(id); }; + this.mesh.onLoad.push(() => { t.onLoadCallback(); }); + this.mesh.onUpdate.push(() => { for(let callback of t.onUpdate) callback(this); }); + + this.traversal = new Traversal(); + this.cache = new Cache(); + this.textures = {}; + }, + + set material(material) { + this.cube.material = this.material = material; + this.material.needsUpdate = true; + }, + + //we need to hijack the material when nxs has textures: when we change the material it might have a map or not + //and we need to update all the materials! + //we also need to create an array of materials for groups to work. + + //Nexus has a list of materials and, usually, each node attach his texture. + + updateMaterials: function() { + if(this.material.map !== false && this.mesh.vertex.texCoord) + this.material.map = this.cube_texture; + + if(this.mesh.vertex.color) + this.material.vertexColors = true; + }, + + onLoadCallback: function() { + const c = this.mesh.sphere.center; + const center = new THREE.Vector3(c[0], c[1], c[2]); + const radius = this.mesh.sphere.radius; + this.boundingSphere = new THREE.Sphere(center, radius); + + var geometry = new THREE.BufferGeometry(); + + if(this.mesh.vertex.normal) + geometry.setAttribute( 'normal', new THREE.BufferAttribute(new Float32Array(3), 3)); + if(this.mesh.vertex.color) + geometry.setAttribute( 'color', new THREE.BufferAttribute(new Float32Array(4), 4)); + if(this.mesh.vertex.texCoord) + geometry.setAttribute( 'uv', new THREE.BufferAttribute(new Float32Array(2), 2)); + + this.cube_texture = new THREE.DataTexture( new Uint8Array([1, 1, 1]), 1, 1, THREE.RGBFormat ); + this.cube_texture.needsUpdate = true; + + this.updateMaterials(); + + let cube = new THREE.Mesh(geometry, this.material); + cube.frustumCulled = false; + cube.onBeforeRender = (renderer, scene, camera, geometry, material, group) => { + this.onBeforeRender(renderer, scene, camera, geometry, material, group) } + cube.onAfterRender = (renderer, scene, camera, geometry, material, group) => { + this.onAfterRender(renderer, scene, camera, geometry, material, group) } + + this.add(cube); + + for(let callback of this.onLoad) + callback(this); + }, + + onBeforeRender: function(renderer, scene, camera, geometry, material, group) { + }, + onAfterRender: function(renderer, scene, camera, geometry, material, group) { + let s = new THREE.Vector2(); + renderer.getSize(s); + + //object modelview is multiplied by camera during rendering, we need to do it here for visibility computations + this.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, this.matrixWorld ); + this.traversal.updateView([0, 0, s.width, s.height], camera.projectionMatrix.elements, this.modelViewMatrix.elements); + this.instance_errors = this.traversal.traverse(this.mesh, this.cache); + + let gl = this.gl; + var program = gl.getParameter(gl.CURRENT_PROGRAM); + + + var attr = this.attributes; + attr.position = gl.getAttribLocation(program, "position"); + attr.normal = gl.getAttribLocation(program, "normal"); + attr.color = gl.getAttribLocation(program, "color"); + attr.uv = gl.getAttribLocation(program, "uv"); + attr.size = gl.getUniformLocation(program, "size"); + attr.scale = gl.getUniformLocation(program, "scale"); + let map_location = gl.getUniformLocation(program, "map"); + attr.map = map_location ? gl.getUniform(program, map_location) : null; + + + //hack to detect if threejs using point or triangle shaders + //instance.mode = attr.size ? "POINT" : "FILL"; + //if(attr.size != -1) + // instance.pointsize = material.size; + + //can't find docs or code on how material.scale is computed in threejs. + //if(attr.scale != -1) + // instance.pointscale = 2.0; + + this.setVisibility(); + }, + + setVisibility: function() { + //threejs increments version when setting neeedsUpdate + if(this.material.version > 0) { + this.updateMaterials(); + this.material.version = 0; + for(let callback of t.onUpdate) callback(this); + } + + //set visibile what is visible! + let t = this.traversal; + let m = this.mesh; + + if(!m.isReady) + return; + let rendered = 0; + + for(let id = 0; id < m.nodesCount; id++) { +// let err = m.nerrors[id]; + + if(!t.selected[id]) continue; + + //check for children: if all are selected, bail out. + { + let visible = false; + let offset = 0; + let end = 0; + let last = m.nfirstpatch[id+1]-1; + for (var p = m.nfirstpatch[id]; p < m.nfirstpatch[id+1]; ++p) { + var child = m.patches[p*3]; + + if(!t.selected[child]) { + visible = true; + break; + } + } + if(!visible) continue; + } + + var sp = m.nspheres; + var off = id*5; + if(!t.isVisible(sp[off], sp[off+1], sp[off+2], sp[off+4])) //tight radius + continue; + + let attr = this.attributes; + let mesh = this.mesh; + let gl = this.gl; + gl.bindVertexArray(null); + gl.bindBuffer(gl.ARRAY_BUFFER, this.vbo[id]); + //if(t.mode != "POINT") + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.ibo[id]); + + gl.vertexAttribPointer(attr.position, 3, gl.FLOAT, false, 12, 0); + gl.enableVertexAttribArray(attr.position); + + let nv = this.mesh.nvertices[id]; + let offset = nv*12; + + if(mesh.vertex.texCoord) { + if(attr.uv >= 0) { + gl.vertexAttribPointer(attr.uv, 2, gl.FLOAT, false, 8, offset); + gl.enableVertexAttribArray(attr.uv); + } + offset += nv*8; + } + if(mesh.vertex.color) { + if(attr.color >= 0) { + gl.vertexAttribPointer(attr.color, 4, gl.UNSIGNED_BYTE, true, 4, offset); + gl.enableVertexAttribArray(attr.color); + } + offset += nv*4; + } + if(mesh.vertex.normal) { + if(attr.normal >= 0) { + gl.vertexAttribPointer(attr.normal, 3, gl.SHORT, true, 6, offset); + gl.enableVertexAttribArray(attr.normal); + } + } + + if(this.cache.debug.nodes) { + gl.disableVertexAttribArray(attr.color); + + var error = this.instance_errors[id]; //this.mesh.errors[id]; + var palette = [ + [1, 1, 1, 1], //white + [1, 1, 1, 1], //white + [0, 1, 0, 1], //green + [0, 1, 1, 1], //cyan + [1, 1, 0, 1], //yellow + [1, 0, 1, 1], //magenta + [1, 0, 0, 1] //red + ]; + let w = Math.min(5.99, Math.max(0, Math.log2(error)/2)); + let low = Math.floor(w); + w -= low; + let color = []; + for( let k = 0; k < 4; k++) + color[k] = palette[low][k]*(1-w) + palette[low+1][k]*w; + + gl.vertexAttrib4fv(attr.color, color); + } + this.cache.realError = Math.min(this.mesh.errors[id], this.cache.realError); + + offset = 0; + let end = 0; + let last = m.nfirstpatch[id+1]-1; + for (let p = m.nfirstpatch[id]; p < m.nfirstpatch[id+1]; ++p) { + let child = m.patches[p*3]; + + if(!t.selected[child]) { + end = m.patches[p*3+1]; + if(p < last) //we join patches if possible. + continue; + } + + if(end > offset) { + if(m.vertex.texCoord && attr.uv >= 0) { + var tex = m.patches[p*3+2]; + if(tex != -1) { //bind texture + var texid = this.textures[tex]; + gl.activeTexture(gl.TEXTURE0 + attr.map); + gl.bindTexture(gl.TEXTURE_2D, texid); + } + } + gl.drawElements(gl.TRIANGLES, (end - offset) * 3, gl.UNSIGNED_SHORT, offset * 6); + rendered += end - offset; + } + offset = m.patches[p*3+1]; + } + } + this.cache.rendered += rendered; + }, + + + + createNodeGeometry: function(id, data) { + let m = this.mesh; + var nv = m.nvertices[id]; + var nf = m.nfaces[id]; + let indices = data.index; + let vertices = new ArrayBuffer(nv*m.vsize); + var position = new Float32Array(vertices, 0, nv*3); + position.set(data.position); + var off = nv*12; + if(m.vertex.texCoord) { + var uv = new Float32Array(vertices, off, nv*2); + uv.set(data.uv); + off += nv*8; + } + if(m.vertex.color) { + var color = new Uint8Array(vertices, off, nv*4); + color.set(data.color); + off += nv*4; + } + if(m.vertex.normal) { + var normal = new Int16Array(vertices, off, nv*3); + normal.set(data.normal); + off += nv*6; + } + + var gl = this.gl + var vbo = this.vbo[id] = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, vbo); + gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW); + var ibo = this.ibo[id] = gl.createBuffer(); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW); + }, + + createTexture: function(id, image) { + let gl = this.gl; + var flip = gl.getParameter(gl.UNPACK_FLIP_Y_WEBGL); + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); + let tex = this.textures[id] = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex); + + //TODO some textures might be alpha only! save space + var s = gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + + function powerOf2(n) { return n && (n & (n - 1)) === 0; } + if(!(gl instanceof WebGLRenderingContext) || (powerOf2(image.width) && powerOf2(image.height))) { + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST_MIPMAP_LINEAR); + gl.generateMipmap(gl.TEXTURE_2D); + } else { + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + } + + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flip); + }, + //schedule for removal of this node( might not want to delete it in the middle of something. + //TODO check if this is really needed! + deleteNodeGeometry: function(id) { + this.gl.deleteBuffer(this.vbo[id]); + this.gl.deleteBuffer(this.ibo[id]); + this.vbo[id] = this.ibo[id] = null; + }, + + deleteTexture: function(tex) { + if(!this.textures[tex]) + throw "Deleting missing texture!" + + this.gl.deleteTexture(this.textures[tex]); + this.textures[tex] = 0; + }, + + + toJSON: function ( meta ) { + throw "Can't" + }, + + raycast: function ( raycaster, intersects ) { + + const levels = this.levels; + + if ( levels.length > 0 ) { + + _v1.setFromMatrixPosition( this.matrixWorld ); + + const distance = raycaster.ray.origin.distanceTo( _v1 ); + + this.getObjectForDistance( distance ).raycast( raycaster, intersects ); + + } + + }, + +} ); + + +export { NXSRaw }; diff --git a/nexus3d/src/Nexus3D.js b/nexus3d/src/Nexus3D.js new file mode 100644 index 00000000..f255adcf --- /dev/null +++ b/nexus3d/src/Nexus3D.js @@ -0,0 +1,465 @@ +import * as THREE from 'three' +import { Traversal } from './Traversal.js' +import { Mesh } from './Mesh.js' +import { Cache } from './Cache.js' + +class Nexus3D extends THREE.Mesh { + + constructor(url, renderer, options) { + + super(); + if(typeof renderer == 'function') + throw "Nexus3D constructor has changed: Nexus3D(url, renderer, options) where options include: onLoad, onUpdate, onProgress and material" + + this.patchWebGLRenderer(renderer); + + Object.assign(this, { + isNXS: true, + type:'NXS', + url: url, + gl: renderer.getContext(), + XRMode: false, //set to true to avoid flickering due to double eye rendering. + material: null, + + autoUpdate: true, + mesh: new Mesh(), + vbo: [], + ibo: [], + vao: [], + textures: [], + attributes: {}, //here we store the uniform attributes of the shader. + + basemesh: null, //highest level of the nexus, for picking + }); + + if('material' in options) + this.material = options.material; + if(!this.material) + this.material = new THREE.MeshStandardMaterial(); + + for(let call of ['onLoad', 'onUpdate', 'onProgress']) { + this['_' + call] = []; + if(call in options) + this['_' + call].push(options[call]) + } + + if(this.url) { + if(typeof url == 'object') { + this.nxs = this.url; + this.nxs.onLoad.push((m) => { + this.mesh = this.nxs.mesh; + this.traversal = this.nxs.traversal; + this.cache = this.nxs.cache; + this.vbo = this.nxs.vbo; + this.ibo = this.nxs.ibo; + this.vao = this.nxs.vao; + this.textures = this.nxs.textures; + this.onLoadCallback(this); + }); + } else + this.open(this.url); + } + } + + copy(source) { + Object3D.prototype.copy.call( this, source, false ); + throw "Can't really copy." + return this; + } + + + open(url) { + let t = this; + this.mesh.open(url); + this.mesh.createNode = (id) => { }; + this.mesh.createNodeGeometry = (id, geometry) => { t.createNodeGeometry(id, geometry); }; + this.mesh.createTexture = (id, image) => { t.createTexture(id, image); }; + this.mesh.deleteNodeGeometry = (id) => { t.deleteNodeGeometry(id); }; + this.mesh.deleteTexture = (id) => { t.deleteTexture(id); }; + this.mesh.onLoad.push(() => { t.onLoadCallback(); }); + this.mesh.onUpdate.push(() => { + for(let callback of t._onUpdate) callback(this); + for(let callback of t._onProgress) callback(this, this.mesh.availableNodes, this.mesh.nodesCount); + }); + + this.traversal = new Traversal(); + this.cache = Cache; //new Cache(); + this.textures = {}; + } + + set onLoad(callback) { + this._onLoad.push(callback); + } + + set onUpdate(callback) { + this._onUpdate.push(callback); + } + + set onProgress(callback) { + this._onProgress.push(callback); + } + + //TODO this is not really needed, we might just conform to THREEJS standard of updating. + /*set material(material) { + this.material = material; + this.material.needsUpdate = true; + }*/ + + updateMaterials() { + if(this.material.map !== false && this.mesh.vertex.texCoord) + this.material.map = this.material_texture; + + if(this.mesh.vertex.color) + this.material.vertexColors = true; + this.material.needsUpdate = true; + } + + onLoadCallback() { + const c = this.mesh.sphere.center; + const center = new THREE.Vector3(c[0], c[1], c[2]); + const radius = this.mesh.sphere.radius; + this.boundingSphere = new THREE.Sphere(center, radius); + + var geometry = new THREE.BufferGeometry(); + + geometry.setAttribute( 'position', new THREE.BufferAttribute(new Float32Array(3), 3)); + + if(this.mesh.vertex.normal) + geometry.setAttribute( 'normal', new THREE.BufferAttribute(new Float32Array(3), 3)); + if(this.mesh.vertex.color) + geometry.setAttribute( 'color', new THREE.BufferAttribute(new Float32Array(4), 4)); + if(this.mesh.vertex.texCoord) + geometry.setAttribute( 'uv', new THREE.BufferAttribute(new Float32Array(2), 2)); + + if(this.mesh.vertex.texCoord) { + this.material_texture = new THREE.DataTexture( new Uint8Array([1, 1, 1, 1]), 1, 1, THREE.RGBAFormat ); + this.material_texture.needsUpdate = true; + } + + this.updateMaterials(); + this.geometry = geometry; + + this.frustumCulled = false; + + for(let callback of this._onLoad) + callback(this); + } + + renderBufferDirect(renderer, scene, camera, geometry, material, group) { + let s = new THREE.Vector4(); + renderer.getViewport(s); + + //object modelview is multiplied by camera during rendering, we need to do it here for visibility computations + this.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, this.matrixWorld ); + if(s.x() == 0 && this.XRMode) { //hack to only traverse on the left eye + this.traversal.updateView(s, camera.projectionMatrix.elements, this.modelViewMatrix.elements); + this.instance_errors = this.traversal.traverse(this.mesh, this.cache); + } + //threejs increments version when setting neeedsUpdate + /*if(this.material.version > 0) { + this.updateMaterials(); + this.material.version = 0; + for(let callback of this.onUpdate) + callback(this); + }*/ + let gl = this.gl; + let program = gl.getParameter(gl.CURRENT_PROGRAM); + + //TODO these calls could be cached saving attrs per each material. + let attr = this.attributes; + attr.position = gl.getAttribLocation(program, "position"); + attr.normal = gl.getAttribLocation(program, "normal"); + attr.color = gl.getAttribLocation(program, "color"); + attr.uv = gl.getAttribLocation(program, "uv"); + attr.size = gl.getUniformLocation(program, "size"); + attr.scale = gl.getUniformLocation(program, "scale"); + + let map_location = gl.getUniformLocation(program, "map"); + attr.map = map_location ? gl.getUniform(program, map_location) : null; + + + //hack to detect if threejs using point or triangle shaders + //instance.mode = attr.size ? "POINT" : "FILL"; + //if(attr.size != -1) + // instance.pointsize = material.size; + + //can't find docs or code on how material.scale is computed in threejs. + //if(attr.scale != -1) + // instance.pointscale = 2.0; + + this.setVisibility(); + } + + setVisibility() { + //set visibile what is visible! + let t = this.traversal; + let m = this.mesh; + + if(!m.isReady) + return; + let rendered = 0; + + let attr = this.attributes; + let mesh = this.mesh; + let gl = this.gl; + let gl2 = gl instanceof WebGL2RenderingContext; + let state = attr.uv + 10*attr.color + 100*attr.normal; + + for(let id = 0; id < m.nodesCount; id++) { +// let err = m.nerrors[id]; + + if(!t.selected[id]) continue; + + //check for children: if all are selected, bail out. + { + let visible = false; + let offset = 0; + let end = 0; + let last = m.nfirstpatch[id+1]-1; + for (var p = m.nfirstpatch[id]; p < m.nfirstpatch[id+1]; ++p) { + var child = m.patches[p*3]; + + if(!t.selected[child]) { + visible = true; + break; + } + } + if(!visible) continue; + } + + var sp = m.nspheres; + var off = id*5; + if(!t.isVisible(sp[off], sp[off+1], sp[off+2], sp[off+4])) //tight radius + continue; + + let doBind = true; + + if(gl2) { + if(state in this.vao[id]) + doBind = false; + else + this.vao[id][state] = gl.createVertexArray(); + + gl.bindVertexArray(this.vao[id][state]); + } + + if(doBind) { + gl.bindBuffer(gl.ARRAY_BUFFER, this.vbo[id]); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.ibo[id]); + + gl.vertexAttribPointer(attr.position, 3, gl.FLOAT, false, 12, 0); + gl.enableVertexAttribArray(attr.position); + + let nv = this.mesh.nvertices[id]; + let offset = nv*12; + + if(mesh.vertex.texCoord) { + if(attr.uv >= 0) { + gl.vertexAttribPointer(attr.uv, 2, gl.FLOAT, false, 8, offset); + gl.enableVertexAttribArray(attr.uv); + } + offset += nv*8; + } + if(mesh.vertex.color) { + if(attr.color >= 0) { + gl.vertexAttribPointer(attr.color, 4, gl.UNSIGNED_BYTE, true, 4, offset); + gl.enableVertexAttribArray(attr.color); + } + offset += nv*4; + } + if(mesh.vertex.normal) { + if(attr.normal >= 0) { + gl.vertexAttribPointer(attr.normal, 3, gl.SHORT, true, 6, offset); + gl.enableVertexAttribArray(attr.normal); + } + } + + if(this.cache.debug.nodes) { + gl.disableVertexAttribArray(attr.color); + + var error = this.instance_errors[id]; //this.mesh.errors[id]; + var palette = [ + [1, 1, 1, 1], //white + [1, 1, 1, 1], //white + [0, 1, 0, 1], //green + [0, 1, 1, 1], //cyan + [1, 1, 0, 1], //yellow + [1, 0, 1, 1], //magenta + [1, 0, 0, 1] //red + ]; + let w = Math.min(5.99, Math.max(0, Math.log2(error)/2)); + let low = Math.floor(w); + w -= low; + let color = []; + for( let k = 0; k < 4; k++) + color[k] = palette[low][k]*(1-w) + palette[low+1][k]*w; + + gl.vertexAttrib4fv(attr.color, color); + } + } + this.cache.realError = Math.min(this.mesh.errors[id], this.cache.realError); + + let offset = 0; + let end = 0; + let last = m.nfirstpatch[id+1]-1; + for (let p = m.nfirstpatch[id]; p < m.nfirstpatch[id+1]; ++p) { + let child = m.patches[p*3]; + + if(!t.selected[child]) { + end = m.patches[p*3+1]; + if(p < last) //we join patches if possible. + continue; + } + + if(end > offset) { + if(m.vertex.texCoord && attr.uv >= 0) { + var tex = m.patches[p*3+2]; + if(tex != -1) { //bind texture + var texid = this.textures[tex]; + gl.activeTexture(gl.TEXTURE0 + attr.map); + gl.bindTexture(gl.TEXTURE_2D, texid); + } + } + let mode = this.material.wireframe ? gl.LINE_STRIP : gl.TRIANGLES; + + gl.drawElements(mode, (end - offset) * 3, gl.UNSIGNED_SHORT, offset * 6); + rendered += end - offset; + } + offset = m.patches[p*3+1]; + } + } + this.cache.rendered += rendered; + } + + createNodeGeometry(id, data) { + let m = this.mesh; + var nv = m.nvertices[id]; + var nf = m.nfaces[id]; + let indices = data.index; + let vertices = new ArrayBuffer(nv*m.vsize); + var position = new Float32Array(vertices, 0, nv*3); + position.set(data.position); + var off = nv*12; + if(m.vertex.texCoord) { + var uv = new Float32Array(vertices, off, nv*2); + uv.set(data.uv); + off += nv*8; + } + if(m.vertex.color) { + var color = new Uint8Array(vertices, off, nv*4); + color.set(data.color); + off += nv*4; + } + if(m.vertex.normal) { + var normal = new Int16Array(vertices, off, nv*3); + normal.set(data.normal); + off += nv*6; + } + + //needed for approximate picking. + if(id < this.mesh.nroots) { + let basegeometry = new THREE.BufferGeometry(); + basegeometry.setAttribute( 'position', new THREE.BufferAttribute(data.position, 3 ) ); + basegeometry.setAttribute( 'normal', new THREE.BufferAttribute(data.normal, 3 ) ); + basegeometry.setIndex(new THREE.BufferAttribute( data.index, 1 ) ); + + this.basemesh = new THREE.Mesh(basegeometry, this.material); + this.basemesh.visible = false; + this.add(this.basemesh); + } + + var gl = this.gl + this.vao[id] = {}; //one for each attrib combination in use (casting shadows for example). + gl.bindVertexArray(null); + + var vbo = this.vbo[id] = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, vbo); + gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW); + var ibo = this.ibo[id] = gl.createBuffer(); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW); + } + + createTexture(id, image) { + let gl = this.gl; + var flip = gl.getParameter(gl.UNPACK_FLIP_Y_WEBGL); + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); + let tex = this.textures[id] = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex); + + //TODO some textures might be alpha only! save space + let internalFormat = this.material.map.colorSpace == THREE.LinearSRGBColorSpace ? gl.RGBA : gl.SRGB8_ALPHA8; + let s = gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, gl.RGBA, gl.UNSIGNED_BYTE, image); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + + function powerOf2(n) { return n && (n & (n - 1)) === 0; } + if(!(gl instanceof WebGLRenderingContext) || (powerOf2(image.width) && powerOf2(image.height))) { + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST_MIPMAP_LINEAR); + gl.generateMipmap(gl.TEXTURE_2D); + } else { + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + } + + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flip); + } + //schedule for removal of this node( might not want to delete it in the middle of something. + //TODO check if this is really needed! + deleteNodeGeometry(id) { + this.gl.deleteBuffer(this.vbo[id]); + this.gl.deleteBuffer(this.ibo[id]); + + this.vbo[id] = this.ibo[id] = null; + if(this.vao[id]) //node might have been loaded and unloaded before a rendering + for(const [state, vao] of Object.entries(this.vao[id])) + this.gl.deleteVertexArray(vao); + this.vao[id] = null; + } + + deleteTexture(tex) { + if(!this.textures[tex]) + return; + // throw "Deleting missing texture!" + + this.gl.deleteTexture(this.textures[tex]); + this.textures[tex] = 0; + } + + flush() { + this.cache.flush(this.mesh); + } + + dispose() { + this.flush(); + for(let child of this.children) + child.geometry.dispose(); + } + + toJSON(meta) { + throw "Can't"; + } + + patchWebGLRenderer(renderer) { + if(renderer.nexusPatched) return; + let f = renderer.renderBufferDirect; + renderer.renderBufferDirect = ( camera, scene, geometry, material, object, group) => { + f( camera, scene, geometry, material, object, group ); + if ( object.renderBufferDirect) + object.renderBufferDirect(renderer, scene, camera, geometry, material, group); + }; + renderer.originalRender = renderer.render; + renderer.render = (scene, camera) => { + Cache.beginFrame(30); + renderer.originalRender(scene, camera); + Cache.endFrame(); + } + renderer.nexusPatched = true; + } + +} + + + + +export { Nexus3D, Cache }; diff --git a/nexus3d/src/PriorityQueue.js b/nexus3d/src/PriorityQueue.js new file mode 100644 index 00000000..2b9c2622 --- /dev/null +++ b/nexus3d/src/PriorityQueue.js @@ -0,0 +1,76 @@ + + function PriorityQueue(max_length) { + this.error = new Float32Array(max_length); + this.data = new Int32Array(max_length); + this.size = 0; +} + +PriorityQueue.prototype = { + push: function(data, error) { + this.data[this.size] = data; + this.error[this.size] = error; + this.bubbleUp(this.size); + this.size++; + }, + + pop: function() { + var result = this.data[0]; + this.size--; + if(this.size > 0) { + this.data[0] = this.data[this.size]; + this.error[0] = this.error[this.size]; + this.sinkDown(0); + } + return result; + }, + + bubbleUp: function(n) { + var data = this.data[n]; + var error = this.error[n]; + while (n > 0) { + var pN = ((n+1)>>1) -1; + var pError = this.error[pN]; + if(pError > error) + break; + //swap + this.data[n] = this.data[pN]; + this.error[n] = pError; + this.data[pN] = data; + this.error[pN] = error; + n = pN; + } + }, + + sinkDown: function(n) { + var data = this.data[n]; + var error = this.error[n]; + + while(true) { + var child2N = (n + 1) * 2; + var child1N = child2N - 1; + var swap = -1; + if (child1N < this.size) { + var child1Error = this.error[child1N]; + if(child1Error > error) + swap = child1N; + } + if (child2N < this.size) { + var child2Error = this.error[child2N]; + if (child2Error > (swap == -1 ? error : child1Error)) + swap = child2N; + } + + if (swap == -1) break; + + this.data[n] = this.data[swap]; + this.error[n] = this.error[swap]; + this.data[swap] = data; + this.error[swap] = error; + n = swap; + } + } +}; + + +export { PriorityQueue } + diff --git a/nexus3d/src/Traversal.js b/nexus3d/src/Traversal.js new file mode 100644 index 00000000..4cbb6065 --- /dev/null +++ b/nexus3d/src/Traversal.js @@ -0,0 +1,259 @@ +import { vecMul, matMul, matInv } from './Binary.js' +import { PriorityQueue } from "./PriorityQueue.js" + +function Traversal() { + let t = this; + t.maxBlocked = 30; + + t.modelMatrix = new Float32Array(16); + t.viewMatrix = new Float32Array(16); + t.projectionMatrix = new Float32Array(16); + t.modelView = new Float32Array(16); + t.modelViewInv = new Float32Array(16); + t.modelViewProj = new Float32Array(16); + t.modelViewProjInv = new Float32Array(16); + t.planes = new Float32Array(24); + t.viewport = new Float32Array(4); + t.viewpoint = new Float32Array(4); +} + +Traversal.prototype = { + updateView: function(viewport, projection, modelView) { + let t = this; + + for(let i = 0; i < 16; i++) { + t.projectionMatrix[i] = projection[i]; + t.modelView[i] = modelView[i]; + } + for(let i = 0; i < 4; i++) + t.viewport[i] = viewport[i]; + + matMul(t.projectionMatrix, t.modelView, t.modelViewProj); + matInv(t.modelViewProj, t.modelViewProjInv); + + matInv(t.modelView, t.modelViewInv); + t.viewpoint[0] = t.modelViewInv[12]; + t.viewpoint[1] = t.modelViewInv[13]; + t.viewpoint[2] = t.modelViewInv[14]; + t.viewpoint[3] = 1.0; + + + const m = t.modelViewProj; + const mi = t.modelViewProjInv; + let p = t.planes; + + //frustum planes Ax + By + Cz + D = 0; + p[0] = m[0] + m[3]; p[1] = m[4] + m[7]; p[2] = m[8] + m[11]; p[3] = m[12] + m[15]; //left + p[4] = -m[0] + m[3]; p[5] = -m[4] + m[7]; p[6] = -m[8] + m[11]; p[7] = -m[12] + m[15]; //right + p[8] = m[1] + m[3]; p[9] = m[5] + m[7]; p[10] = m[9] + m[11]; p[11] = m[13] + m[15]; //bottom + p[12] = -m[1] + m[3]; p[13] = -m[5] + m[7]; p[14] = -m[9] + m[11]; p[15] = -m[13] + m[15]; //top + p[16] = -m[2] + m[3]; p[17] = -m[6] + m[7]; p[18] = -m[10] + m[11]; p[19] = -m[14] + m[15]; //near + p[20] = -m[2] + m[3]; p[21] = -m[6] + m[7]; p[22] = -m[10] + m[11]; p[23] = -m[14] + m[15]; //far + + //normalize planes to get also correct distances + for(let i = 0; i < 24; i+= 4) { + let l = Math.sqrt(p[i]*p[i] + p[i+1]*p[i+1] + p[i+2]*p[i+2]); + p[i] /= l; p[i+1] /= l; p[i+2] /= l; p[i+3] /= l; + } + + //side is M'(1,0,0,1) - M'(-1,0,0,1) and they lie on the planes + const r3 = mi[3] + mi[15]; + const r0 = (mi[0] + mi[12 ])/r3; + const r1 = (mi[1] + mi[13 ])/r3; + const r2 = (mi[2] + mi[14 ])/r3; + + const l3 = -mi[3] + mi[15]; + const l0 = (-mi[0] + mi[12 ])/l3 - r0; + const l1 = (-mi[1] + mi[13 ])/l3 - r1; + const l2 = (-mi[2] + mi[14 ])/l3 - r2; + + const side = Math.sqrt(l0*l0 + l1*l1 + l2*l2); + + //center of the scene is M'*(0, 0, 0, 1) + const c0 = mi[12]/mi[15] - t.viewpoint[0]; + const c1 = mi[13]/mi[15] - t.viewpoint[1]; + const c2 = mi[14]/mi[15] - t.viewpoint[2]; + const dist = Math.sqrt(c0*c0 + c1*c1 + c2*c2); + + const resolution = (2*side/dist)/ t.viewport[2]; + t.currentResolution == resolution ? t.sameResolution = true : t.sameResolution = false; + t.currentResolution = resolution; + }, + + traverse : function (mesh, cache) { + let t = this; + t.mesh = mesh; +// if(Debug.extract == true) + // return; + + if(!mesh.isReady) return; + + const n = mesh.nodesCount; + t.visited = new Uint8Array(n); + t.blocked = new Uint8Array(n); + t.selected = new Uint8Array(n); + + t.frame = cache.frame; + + t.instance_errors = new Float32Array(n); + + if(t.frame > mesh.frame) { //clean the errors. + mesh.errors = new Float32Array(n); + mesh.frame = t.frame; + } + + t.visitQueue = new PriorityQueue(n); + for(var i = 0; i < mesh.nroots; i++) + t.insertNode(i); + + t.currentError = cache.currentError; + t.drawSize = 0; + t.nblocked = 0; + + var requested = 0; + while(t.visitQueue.size && t.nblocked < t.maxBlocked) { + var error = t.visitQueue.error[0]; + var id = t.visitQueue.pop(); + + //if not loaded and the queue is not full add to the candidates. + if (mesh.status[id] == 0 && requested < cache.maxPending) { + cache.candidates.push({id: id, mesh:mesh, frame:t.frame, error:error}); + requested++; + } + /* we don't want to stop as soon as a node is not availabe, because the nodes are sorted by error, + this could cause a large drop in quality elsewere. + we still need to mark all children as blocked, to prevent including them in the cut of the dag. */ + var blocked = t.blocked[id] || !t.expandNode(id, error); + if (blocked) + t.nblocked++; + else { + t.selected[id] = 1; + //cache.realError = Math.min(error, cache.realError); + } + t.insertChildren(id, blocked); + } + + //update remaining errors in the cache + if(cache.nodes.has(mesh)) { + for(let id of cache.nodes.get(mesh)) { + let error = t.nodeError(id); + if(t.instance_errors[id] == 0) { + t.instance_errors[i] = error; + mesh.errors[id] = Math.max(mesh.errors[id], error); + } + } + } + + t.mesh = null; + return t.instance_errors; + }, + + insertNode: function (node) { + let t = this; + t.visited[node] = 1; + + const error = t.nodeError(node); + + t.instance_errors[node] = error; + t.mesh.errors[node] = Math.max(error, t.mesh.errors[node]); + t.mesh.frames[node] = t.frame; + +// if(node > 0 && error < t.currentError) return; //2% speed TODO check if needed + + t.visitQueue.push(node, error); + }, + + insertChildren : function (node, block) { + let t = this; + for(let i = t.mesh.nfirstpatch[node]; i < t.mesh.nfirstpatch[node+1]; ++i) { + const child = t.mesh.patches[i*3]; + if (child == t.mesh.sink) return; + if (block) t.blocked[child] = 1; + if (!t.visited[child]) + t.insertNode(child); + } + }, + + expandNode : function (node, error) { + let t = this; + if(node > 0 && error < t.currentError) { +// console.log("Reached error", error, t.currentError); + return false; + } + + if(t.drawSize > t.drawBudget) { +// console.log("Reached drawsize", t.drawSize, t.drawBudget); + return false; + } + + if(t.mesh.status[node] != 1) { //not ready +// console.log("Node " + node + " still not loaded (cache?)"); + return false; + } + + const sp = t.mesh.nspheres; + const off = node*5; + if(t.isVisible(sp[off], sp[off+1], sp[off+2], sp[off+3])) //expanded radius + t.drawSize += t.mesh.nvertices[node]*0.8; + //we are adding half of the new faces. (but we are using the vertices so *2) + + return true; + }, + + nodeError : function (n, tight) { + let t = this; + const spheres = t.mesh.nspheres; + const b = t.viewpoint; + const off = n*5; + const cx = spheres[off+0]; + const cy = spheres[off+1]; + const cz = spheres[off+2]; + let r = spheres[off+3]; + if(tight) + r = spheres[off+4]; + const d0 = b[0] - cx; + const d1 = b[1] - cy; + const d2 = b[2] - cz; + let dist = Math.sqrt(d0*d0 + d1*d1 + d2*d2) - r; + if (dist < 0.1) + dist = 0.1; + + //resolution is how long is a pixel at distance 1. + let error = t.mesh.nerrors[n]/(t.currentResolution*dist); //in pixels + + //causes flickering due to things popping in and out of visibility, causes a huge resorting. + /*if (!t.isVisible(cx, cy, cz, spheres[off+4])) + error /= 100.0; */ + + //more stable, at least it's continuous. + let d = t.distance(cx, cy, cz, spheres[off+4]); + if(d < -r) { + error /= 101.0; + } else if(d < 0) { + error /= 1 -( d/r)*100.0 + } + return error; + }, + + distance: function(x, y, z, r) { + const p = this.planes; + let min_distance = 1e20; + for (let i = 0; i < 24; i +=4) { + let d = p[i]*x + p[i+1]*y + p[i+2]*z + p[i+3] + r; + if(d < min_distance) + min_distance = d; + } + return min_distance; + }, + + isVisible : function (x, y, z, r) { + const p = this.planes; + for (let i = 0; i < 24; i +=4) { + if(p[i]*x + p[i+1]*y + p[i+2]*z + p[i+3] + r < 0) //plane is ax+by+cz+d = 0; + return false; + } + return true; + } +}; + +export { Traversal } diff --git a/nexus3d/src/index.js b/nexus3d/src/index.js new file mode 100644 index 00000000..980ecbcf --- /dev/null +++ b/nexus3d/src/index.js @@ -0,0 +1,207 @@ +//npx webpack + +import { + Vector2, + Euler, + Color, + Fog, + AmbientLight, + DirectionalLight, + PerspectiveCamera, + Raycaster, + WebGLRenderer, + Scene, + Clock, + + + Mesh, + BoxGeometry, + MeshPhongMaterial, + BasicShadowMap + //BoxBufferGeometry, + + //MeshBasicMaterial, +} from 'three' + +import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'; +import * as Nexus3D from './Nexus3D.js' +import { Monitor } from './Monitor.js' + + +let container = document.getElementById( 'scene'); + +let camera = new PerspectiveCamera( 30, container.innerWidth / container.innerHeight, 0.1, 100 ); +camera.position.set(0, 0, 10); + + +let controls_options = { + rotateSpeed: 0.5, + zoomSpeed: 4, + panSpeed: 0.8, + noZoom: false, + noPan: false, + enableDamping: true, + staticMoving: true, + dynamicDampingFactor: 0.3, +} +let controls = new OrbitControls(camera, container ); +for( const [key, value] of Object.entries(controls_options)) + controls[key] = value; + +controls.addEventListener( 'change', function() { redraw = true; } ); + + + +let scene = new Scene(); +scene.background = new Color( 0xaaaaff ); +scene.fog = new Fog( 0x050505, 2000, 3500 ); +scene.add( new AmbientLight( 0x444444 ) ); + +var light1 = new DirectionalLight( 0xffffff, 1.0 ); +light1.position.set( 2, 1, -0.5 ); + +light1.castShadow = true; +light1.shadow.camera.near = 1; +light1.shadow.camera.far = 10; +light1.shadow.camera.right = 2; +light1.shadow.camera.left = - 2; +light1.shadow.camera.top = 2; +light1.shadow.camera.bottom = - 2; +light1.shadow.mapSize.width = 1024; +light1.shadow.mapSize.height = 1024; + + +scene.add( light1 ); + +var light2 = new DirectionalLight( 0xffffff, 1.0 ); +light2.position.set( -1, -1, 1 ); +//scene.add( light2 ); + +var renderer = new WebGLRenderer( { antialias: false } ); +renderer.setClearColor( scene.fog.color ); +renderer.setPixelRatio( window.devicePixelRatio ); +renderer.shadowMap.enabled = true; +renderer.shadowMap.type = BasicShadowMap; + +container.append(renderer.domElement); + + +function onNexusLoad(nexus) { + const p = nexus.boundingSphere.center.clone().negate(); + const s = 1/nexus.boundingSphere.radius; + //nexus.rotateX(-3.1415/2); + + if(nexus == nexus1) { + nexus.position.set(p.x*s + 1, p.y*s, p.z*s); + } else + nexus.position.set(p.x*s - 1, p.y*s, p.z*s); + + nexus.scale.set(s, s, s); + redraw = true; +} + +var url = "models/gargo.nxz"; + +//onUpdate parameter here is used to trigger a redraw +let nexus1 = new Nexus3D.Nexus3D(url, renderer, { onLoad: onNexusLoad, onUpdate: () => { redraw = true; }} ); +//nexus1.castShadow = true; +nexus1.receiveShadow = true; + +//create a second instance and position it. +let nexus2 = new Nexus3D.Nexus3D(url, renderer, { onLoad: onNexusLoad, onUpdate: () => { redraw = true; }} ); +nexus2.castShadow = true; +nexus2.receiveShadow = true; + + + +//material can be changed replacing the material or modifying it. +//nexus1.material = new MeshBasicMaterial( { color: 0xffff00, wireframe: true } ); +//nexus.material.color = new Color(1, 0, 0)); +//nexus.material.wireframe = true; + +let monitor = new Monitor(Nexus3D.Cache, nexus2); +scene.add(nexus1); +scene.add(nexus2); + +nexus1.castShadow = true; + + +let geometry = new BoxGeometry( 10, 0.15, 10 ); +let material = new MeshPhongMaterial( { + color: 0xa0adaf, + shininess: 150, + specular: 0x111111 +} ); + +const ground = new Mesh( geometry, material ); +ground.scale.multiplyScalar( 3 ); +ground.castShadow = false; +ground.receiveShadow = true; +ground.position.set(0, -1, 0); +scene.add( ground ); + + + +window.addNexus = () => { + nexus2 = new Nexus3D.Nexus3D(url, renderer, { onLoad: onNexusLoad, onUpdate: () => { redraw = true; }} ); + scene.add(nexus2); + redraw = true; +}; +window.removeNexus = () => { + scene.remove(nexus2); + nexus2.dispose(); + nexus2 = null; + redraw = true; +}; + +var mouse = new Vector2(); + +function onMouseMove( event ) { + event.preventDefault(); + mouse.x = ( event.clientX / container.offsetWidth ) * 2 - 1; + mouse.y = - ( event.clientY / container.offsetHeight ) * 2 + 1; +} + +document.addEventListener( 'mousemove', onMouseMove, false ); + + + +new ResizeObserver(onWindowResize).observe(container); + +function onWindowResize() { + camera.aspect = container.clientWidth / container.clientHeight; + camera.updateProjectionMatrix(); + + renderer.setSize( container.clientWidth, container.clientHeight ); + + redraw = true; +} + +const clock = new Clock(); +var redraw = true; + +renderer.setAnimationLoop(()=> { + const delta = clock.getDelta() + controls.update(delta); + + if(redraw) { + //during rendering it might be apparent we need another render pass, set it to false BEFORE render + redraw = true; + + + var raycaster = new Raycaster(); + raycaster.setFromCamera( mouse, camera ); + + var intersections = raycaster.intersectObjects( [nexus1], true ); + if(intersections.length) { + nexus1.material.color = new Color(1, 0, 0); +// nexus1.material.wireframe = true; + nexus1.material.needsUpdate = true; + } else { + nexus1.material.color = new Color(1, 1, 1); + } + renderer.render( scene, camera ); + } +}) + +onWindowResize(); diff --git a/nexus3d/webpack.config.js b/nexus3d/webpack.config.js new file mode 100644 index 00000000..d9f7e36f --- /dev/null +++ b/nexus3d/webpack.config.js @@ -0,0 +1,32 @@ +var webpack = require('webpack') +const HtmlWebpackPlugin = require('html-webpack-plugin') +const path = require('path') + +module.exports = { + + mode: 'production', + entry: { + main: './src/index.js', + }, + + output: { + path: path.resolve(__dirname + '/dist'), + filename: 'main.js', + }, + + devServer: { + static: './dist' + }, + + optimization: { + minimize: false + }, + plugins: [ + new webpack.HotModuleReplacementPlugin(), + new HtmlWebpackPlugin({ + title: 'Nexus3D', + template: __dirname + '/dist/index.html', + inject: false + }), + ], +} diff --git a/nexus3d/webpacklib.config.js b/nexus3d/webpacklib.config.js new file mode 100644 index 00000000..ed9cda2f --- /dev/null +++ b/nexus3d/webpacklib.config.js @@ -0,0 +1,19 @@ +var webpack = require('webpack') + +module.exports = { + + entry: { + Nexus3D: './Nexus3D.js', + }, + + output: { + path: __dirname + '/dist', + filename: '[name].js', + libraryTarget: 'var', + library: '[name]' + }, + + optimization: { + minimize: false + } +} From 270aa98ef43d8741359ee44212528fa99ec8abfa Mon Sep 17 00:00:00 2001 From: ponchio Date: Wed, 10 Dec 2025 19:08:27 +0100 Subject: [PATCH 28/33] Updated to last threejs version, threejs is now an external dependency. --- nexus3d/README.md | 111 ++- nexus3d/build/nexus3D.js | 1740 ++++------------------------------ nexus3d/build/nexus3D.min.js | 2 +- nexus3d/dist/index.html | 3 +- nexus3d/package.json | 13 +- nexus3d/src/Monitor.js | 2 +- nexus3d/src/Nexus3D.js | 71 +- nexus3d/src/index.js | 18 +- nexus3d/webpack.config.js | 33 +- nexus3d/webpacklib.config.js | 20 +- src/corto | 2 +- 11 files changed, 353 insertions(+), 1662 deletions(-) diff --git a/nexus3d/README.md b/nexus3d/README.md index 9cc11e0d..6a6a0095 100644 --- a/nexus3d/README.md +++ b/nexus3d/README.md @@ -1,65 +1,102 @@ +# Nexus3D + +Streaming 3D on the web (three.js supported). + ## Install -Install threejs: +Install three.js (npm will save to `dependencies` automatically): + ```bash -npm install -save three +npm install three ``` -Install webpack, (optional): +If you use webpack for local development: + ```bash -npm install -save webpack webpack-cli +npm install --save-dev webpack webpack-cli webpack-dev-server ``` -Build the example in webpack: + +Build the example with webpack: + ```bash npx webpack --mode development ``` -If you don't want to usa another http server: +Run the dev server: + ```bash -npm install webpack-dev-server --save-dev +npm run start ``` -Finally add the following lines to package.json -```json -"scripts": { - "start:dev": "webpack-dev-server" + +## Getting started (minimal) + +**Important:** Nexus3D expects three.js to be available. You need to include three.js in your project either via npm or a CDN. + +```js +import * as THREE from 'three'; +import { Nexus3D } from './build/nexus3D.js'; + +const renderer = new THREE.WebGLRenderer({ antialias: true }); +renderer.setSize(window.innerWidth, window.innerHeight); +document.body.appendChild(renderer.domElement); + +const scene = new THREE.Scene(); +const camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000); + +const url = 'https://example.com/models/my.nxs'; +const n = new Nexus3D(url, renderer, { + material: new THREE.MeshStandardMaterial(), + onLoad: (mesh) => { scene.add(mesh); }, + onProgress: (mesh, available, total) => { console.log('progress', available, total); } +}); + +function animate() { + requestAnimationFrame(animate); + renderer.render(scene, camera); } +animate(); ``` -and run the server: -```bash -npm run start:dev -``` +## API (brief) + +- Constructor: `new Nexus3D(urlOrNxsObject, renderer, options = {})` + - `urlOrNxsObject`: string URL to the NXS file or an already-loaded NXS object + - `renderer`: a `THREE.WebGLRenderer` instance (required) + - `options`: + - `material`: optional `THREE.Material` instance to use as the template + - `onLoad(mesh)`: optional function called when root metadata is ready + - `onUpdate(mesh)`: optional function called when node availability changes + - `onProgress(mesh, availableNodes, nodesCount)`: progress callback + +- Cache: a shared `Cache` instance is used by default (see `src/Cache.js`). You can tune behavior via `mesh.cache.minFps` and similar properties. ## Docs -# Material +### Material + +NXS material is actually a template material that is used for each patch of the nexus, you cannot set the map properties, which will be overwritten by the patches own textures. + +You can change the material of the NXS either by replacing the material or by changing a value (the color for example). If you want to disable texture rendering set the parameter 'map' to false, and set `material.needsUpdate = true`. + +### FPS -NXS material is actually a template material that is used for each patch of the nexus, you cannot set the map properties, -which will be overwritten by the patches own textures. -You can change the material of the NXS either by replacing the material or by changing a value -(the color for exmple). If you want to disable texture rendering set the parameter 'map' to false, -and set material.needsUpdate = true. +The Cache class can throttle the amount of geometry rendered trying to maintain the fps above a certain target (minFps), reducing the targetError. You can set this property through the `NXS.cache.minFps` property (default 30). -# FPS: -The Cache class can throttle the amount of geometry rendered trying to mantain the fps above a certain target (minFps), -reducing the targetError. You can set this property through the NXS.cache.minFps property. (default 30). +### Instances -# Instances: -You can render multiple instances of the same mesh, just using the reference to an existing NXS -instead of the url in the constructor. Cache will be shared, balancing the quality among all the instances. +You can render multiple instances of the same mesh, just using the reference to an existing NXS instead of the url in the constructor. Cache will be shared, balancing the quality among all the instances. +## Notes -## Roadmap +- This library is compatible with three.js v0.181.x (see `package.json`). -Cache: at the moment NXS creates a cache per model, we shuold have a global one per context (as it was), but the options to have . -Nexus global object with cache management. +## Roadmap / TODO -TODO: check minFps actually works. +- Make cache global per rendering context instead of per-model. +- GPU transfer metering and prefetch control. +- Improve examples and add automated tests. +- Add CI to build and test on push. -GPU resource transfer metering: better control of the amount of geometry and texture sent to the GPU per frame. -Prefetch: control prefetch amouont in MB (unlimited in case) and add onProgress event. -use blocked for prefetch based on size, number of nodes. -onProgress when some node is added or removed. +## Contributing -Library: make the code an npm library -https://webpack.js.org/guides/author-libraries/ +Please open issues or pull requests on the repository. If you change three.js versions, please note compatibility in the README. diff --git a/nexus3d/build/nexus3D.js b/nexus3d/build/nexus3D.js index 5472c953..24915944 100644 --- a/nexus3d/build/nexus3D.js +++ b/nexus3d/build/nexus3D.js @@ -1,1570 +1,170 @@ -(function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('three')) : - typeof define === 'function' && define.amd ? define(['exports', 'three'], factory) : - (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.Nexus3D = {}, global.THREE)); -})(this, (function (exports, THREE) { 'use strict'; - - function _interopNamespace(e) { - if (e && e.__esModule) return e; - var n = Object.create(null); - if (e) { - Object.keys(e).forEach(function (k) { - if (k !== 'default') { - var d = Object.getOwnPropertyDescriptor(e, k); - Object.defineProperty(n, k, d.get ? d : { - enumerable: true, - get: function () { return e[k]; } - }); - } - }); - } - n["default"] = e; - return Object.freeze(n); - } - - var THREE__namespace = /*#__PURE__*/_interopNamespace(THREE); - - /* UTILITIES */ - - function getUint64(view) { - var lo = view.getUint32(view.offset, true); - var hi = view.getUint32(view.offset + 4, true); - view.offset += 8; - return ((hi * (1 << 32)) + lo); - } - - function getUint32(view) { - var s = view.getUint32(view.offset, true); - view.offset += 4; - return s; - } - - function getUint16(view) { - var s = view.getUint16(view.offset, true); - view.offset += 2; - return s; - } - - function getFloat32(view) { - var s = view.getFloat32(view.offset, true); - view.offset += 4; - return s; - } - - - function matMul(a, b, r) { - r[ 0] = a[0]*b[0] + a[4]*b[1] + a[8]*b[2] + a[12]*b[3]; - r[ 1] = a[1]*b[0] + a[5]*b[1] + a[9]*b[2] + a[13]*b[3]; - r[ 2] = a[2]*b[0] + a[6]*b[1] + a[10]*b[2] + a[14]*b[3]; - r[ 3] = a[3]*b[0] + a[7]*b[1] + a[11]*b[2] + a[15]*b[3]; - - r[ 4] = a[0]*b[4] + a[4]*b[5] + a[8]*b[6] + a[12]*b[7]; - r[ 5] = a[1]*b[4] + a[5]*b[5] + a[9]*b[6] + a[13]*b[7]; - r[ 6] = a[2]*b[4] + a[6]*b[5] + a[10]*b[6] + a[14]*b[7]; - r[ 7] = a[3]*b[4] + a[7]*b[5] + a[11]*b[6] + a[15]*b[7]; - - r[ 8] = a[0]*b[8] + a[4]*b[9] + a[8]*b[10] + a[12]*b[11]; - r[ 9] = a[1]*b[8] + a[5]*b[9] + a[9]*b[10] + a[13]*b[11]; - r[10] = a[2]*b[8] + a[6]*b[9] + a[10]*b[10] + a[14]*b[11]; - r[11] = a[3]*b[8] + a[7]*b[9] + a[11]*b[10] + a[15]*b[11]; - - r[12] = a[0]*b[12] + a[4]*b[13] + a[8]*b[14] + a[12]*b[15]; - r[13] = a[1]*b[12] + a[5]*b[13] + a[9]*b[14] + a[13]*b[15]; - r[14] = a[2]*b[12] + a[6]*b[13] + a[10]*b[14] + a[14]*b[15]; - r[15] = a[3]*b[12] + a[7]*b[13] + a[11]*b[14] + a[15]*b[15]; - } - - function matInv(m, t) { - var s = 1.0/( - m[12]* m[9]*m[6]*m[3]-m[8]*m[13]*m[6]*m[3]-m[12]*m[5]*m[10]*m[3]+m[4]*m[13]*m[10]*m[3]+ - m[8]*m[5]*m[14]*m[3]-m[4]*m[9]*m[14]*m[3]-m[12]*m[9]*m[2]*m[7]+m[8]*m[13]*m[2]*m[7]+ - m[12]*m[1]*m[10]*m[7]-m[0]*m[13]*m[10]*m[7]-m[8]*m[1]*m[14]*m[7]+m[0]*m[9]*m[14]*m[7]+ - m[12]*m[5]*m[2]*m[11]-m[4]*m[13]*m[2]*m[11]-m[12]*m[1]*m[6]*m[11]+m[0]*m[13]*m[6]*m[11]+ - m[4]*m[1]*m[14]*m[11]-m[0]*m[5]*m[14]*m[11]-m[8]*m[5]*m[2]*m[15]+m[4]*m[9]*m[2]*m[15]+ - m[8]*m[1]*m[6]*m[15]-m[0]*m[9]*m[6]*m[15]-m[4]*m[1]*m[10]*m[15]+m[0]*m[5]*m[10]*m[15] - ); - - t[ 0] = (m[9]*m[14]*m[7]-m[13]*m[10]*m[7]+m[13]*m[6]*m[11]-m[5]*m[14]*m[11]-m[9]*m[6]*m[15]+m[5]*m[10]*m[15])*s; - t[ 1] = (m[13]*m[10]*m[3]-m[9]*m[14]*m[3]-m[13]*m[2]*m[11]+m[1]*m[14]*m[11]+m[9]*m[2]*m[15]-m[1]*m[10]*m[15])*s; - t[ 2] = (m[5]*m[14]*m[3]-m[13]*m[6]*m[3]+m[13]*m[2]*m[7]-m[1]*m[14]*m[7]-m[5]*m[2]*m[15]+m[1]*m[6]*m[15])*s; - t[ 3] = (m[9]*m[6]*m[3]-m[5]*m[10]*m[3]-m[9]*m[2]*m[7]+m[1]*m[10]*m[7]+m[5]*m[2]*m[11]-m[1]*m[6]*m[11])*s; - - t[ 4] = (m[12]*m[10]*m[7]-m[8]*m[14]*m[7]-m[12]*m[6]*m[11]+m[4]*m[14]*m[11]+m[8]*m[6]*m[15]-m[4]*m[10]*m[15])*s; - t[ 5] = (m[8]*m[14]*m[3]-m[12]*m[10]*m[3]+m[12]*m[2]*m[11]-m[0]*m[14]*m[11]-m[8]*m[2]*m[15]+m[0]*m[10]*m[15])*s; - t[ 6] = (m[12]*m[6]*m[3]-m[4]*m[14]*m[3]-m[12]*m[2]*m[7]+m[0]*m[14]*m[7]+m[4]*m[2]*m[15]-m[0]*m[6]*m[15])*s; - t[ 7] = (m[4]*m[10]*m[3]-m[8]*m[6]*m[3]+m[8]*m[2]*m[7]-m[0]*m[10]*m[7]-m[4]*m[2]*m[11]+m[0]*m[6]*m[11])*s; - - t[ 8] = (m[8]*m[13]*m[7]-m[12]*m[9]*m[7]+m[12]*m[5]*m[11]-m[4]*m[13]*m[11]-m[8]*m[5]*m[15]+m[4]*m[9]*m[15])*s; - t[ 9] = (m[12]*m[9]*m[3]-m[8]*m[13]*m[3]-m[12]*m[1]*m[11]+m[0]*m[13]*m[11]+m[8]*m[1]*m[15]-m[0]*m[9]*m[15])*s; - t[10] = (m[4]*m[13]*m[3]-m[12]*m[5]*m[3]+m[12]*m[1]*m[7]-m[0]*m[13]*m[7]-m[4]*m[1]*m[15]+m[0]*m[5]*m[15])*s; - t[11] = (m[8]*m[5]*m[3]-m[4]*m[9]*m[3]-m[8]*m[1]*m[7]+m[0]*m[9]*m[7]+m[4]*m[1]*m[11]-m[0]*m[5]*m[11])*s; - - t[12] = (m[12]*m[9]*m[6]-m[8]*m[13]*m[6]-m[12]*m[5]*m[10]+m[4]*m[13]*m[10]+m[8]*m[5]*m[14]-m[4]*m[9]*m[14])*s; - t[13] = (m[8]*m[13]*m[2]-m[12]*m[9]*m[2]+m[12]*m[1]*m[10]-m[0]*m[13]*m[10]-m[8]*m[1]*m[14]+m[0]*m[9]*m[14])*s; - t[14] = (m[12]*m[5]*m[2]-m[4]*m[13]*m[2]-m[12]*m[1]*m[6]+m[0]*m[13]*m[6]+m[4]*m[1]*m[14]-m[0]*m[5]*m[14])*s; - t[15] = (m[4]*m[9]*m[2]-m[8]*m[5]*m[2]+m[8]*m[1]*m[6]-m[0]*m[9]*m[6]-m[4]*m[1]*m[10]+m[0]*m[5]*m[10])*s; - } - - function PriorityQueue(max_length) { - this.error = new Float32Array(max_length); - this.data = new Int32Array(max_length); - this.size = 0; - } - - PriorityQueue.prototype = { - push: function(data, error) { - this.data[this.size] = data; - this.error[this.size] = error; - this.bubbleUp(this.size); - this.size++; - }, - - pop: function() { - var result = this.data[0]; - this.size--; - if(this.size > 0) { - this.data[0] = this.data[this.size]; - this.error[0] = this.error[this.size]; - this.sinkDown(0); - } - return result; - }, - - bubbleUp: function(n) { - var data = this.data[n]; - var error = this.error[n]; - while (n > 0) { - var pN = ((n+1)>>1) -1; - var pError = this.error[pN]; - if(pError > error) - break; - //swap - this.data[n] = this.data[pN]; - this.error[n] = pError; - this.data[pN] = data; - this.error[pN] = error; - n = pN; - } - }, - - sinkDown: function(n) { - var data = this.data[n]; - var error = this.error[n]; - - while(true) { - var child2N = (n + 1) * 2; - var child1N = child2N - 1; - var swap = -1; - if (child1N < this.size) { - var child1Error = this.error[child1N]; - if(child1Error > error) - swap = child1N; - } - if (child2N < this.size) { - var child2Error = this.error[child2N]; - if (child2Error > (swap == -1 ? error : child1Error)) - swap = child2N; - } - - if (swap == -1) break; - - this.data[n] = this.data[swap]; - this.error[n] = this.error[swap]; - this.data[swap] = data; - this.error[swap] = error; - n = swap; - } - } - }; - - function Traversal() { - let t = this; - t.maxBlocked = 30; - - t.modelMatrix = new Float32Array(16); - t.viewMatrix = new Float32Array(16); - t.projectionMatrix = new Float32Array(16); - t.modelView = new Float32Array(16); - t.modelViewInv = new Float32Array(16); - t.modelViewProj = new Float32Array(16); - t.modelViewProjInv = new Float32Array(16); - t.planes = new Float32Array(24); - t.viewport = new Float32Array(4); - t.viewpoint = new Float32Array(4); - } - - Traversal.prototype = { - updateView: function(viewport, projection, modelView) { - let t = this; - - for(let i = 0; i < 16; i++) { - t.projectionMatrix[i] = projection[i]; - t.modelView[i] = modelView[i]; - } - for(let i = 0; i < 4; i++) - t.viewport[i] = viewport[i]; - - matMul(t.projectionMatrix, t.modelView, t.modelViewProj); - matInv(t.modelViewProj, t.modelViewProjInv); - - matInv(t.modelView, t.modelViewInv); - t.viewpoint[0] = t.modelViewInv[12]; - t.viewpoint[1] = t.modelViewInv[13]; - t.viewpoint[2] = t.modelViewInv[14]; - t.viewpoint[3] = 1.0; - - - const m = t.modelViewProj; - const mi = t.modelViewProjInv; - let p = t.planes; - - //frustum planes Ax + By + Cz + D = 0; - p[0] = m[0] + m[3]; p[1] = m[4] + m[7]; p[2] = m[8] + m[11]; p[3] = m[12] + m[15]; //left - p[4] = -m[0] + m[3]; p[5] = -m[4] + m[7]; p[6] = -m[8] + m[11]; p[7] = -m[12] + m[15]; //right - p[8] = m[1] + m[3]; p[9] = m[5] + m[7]; p[10] = m[9] + m[11]; p[11] = m[13] + m[15]; //bottom - p[12] = -m[1] + m[3]; p[13] = -m[5] + m[7]; p[14] = -m[9] + m[11]; p[15] = -m[13] + m[15]; //top - p[16] = -m[2] + m[3]; p[17] = -m[6] + m[7]; p[18] = -m[10] + m[11]; p[19] = -m[14] + m[15]; //near - p[20] = -m[2] + m[3]; p[21] = -m[6] + m[7]; p[22] = -m[10] + m[11]; p[23] = -m[14] + m[15]; //far - - //normalize planes to get also correct distances - for(let i = 0; i < 24; i+= 4) { - let l = Math.sqrt(p[i]*p[i] + p[i+1]*p[i+1] + p[i+2]*p[i+2]); - p[i] /= l; p[i+1] /= l; p[i+2] /= l; p[i+3] /= l; - } - - //side is M'(1,0,0,1) - M'(-1,0,0,1) and they lie on the planes - const r3 = mi[3] + mi[15]; - const r0 = (mi[0] + mi[12 ])/r3; - const r1 = (mi[1] + mi[13 ])/r3; - const r2 = (mi[2] + mi[14 ])/r3; - - const l3 = -mi[3] + mi[15]; - const l0 = (-mi[0] + mi[12 ])/l3 - r0; - const l1 = (-mi[1] + mi[13 ])/l3 - r1; - const l2 = (-mi[2] + mi[14 ])/l3 - r2; - - const side = Math.sqrt(l0*l0 + l1*l1 + l2*l2); - - //center of the scene is M'*(0, 0, 0, 1) - const c0 = mi[12]/mi[15] - t.viewpoint[0]; - const c1 = mi[13]/mi[15] - t.viewpoint[1]; - const c2 = mi[14]/mi[15] - t.viewpoint[2]; - const dist = Math.sqrt(c0*c0 + c1*c1 + c2*c2); - - const resolution = (2*side/dist)/ t.viewport[2]; - t.currentResolution == resolution ? t.sameResolution = true : t.sameResolution = false; - t.currentResolution = resolution; - }, - - traverse : function (mesh, cache) { - let t = this; - t.mesh = mesh; - // if(Debug.extract == true) - // return; - - if(!mesh.isReady) return; - - const n = mesh.nodesCount; - t.visited = new Uint8Array(n); - t.blocked = new Uint8Array(n); - t.selected = new Uint8Array(n); - - t.frame = cache.frame; - - t.instance_errors = new Float32Array(n); - - if(t.frame > mesh.frame) { //clean the errors. - mesh.errors = new Float32Array(n); - mesh.frame = t.frame; - } - - t.visitQueue = new PriorityQueue(n); - for(var i = 0; i < mesh.nroots; i++) - t.insertNode(i); - - t.currentError = cache.currentError; - t.drawSize = 0; - t.nblocked = 0; - - var requested = 0; - while(t.visitQueue.size && t.nblocked < t.maxBlocked) { - var error = t.visitQueue.error[0]; - var id = t.visitQueue.pop(); - - //if not loaded and the queue is not full add to the candidates. - if (mesh.status[id] == 0 && requested < cache.maxPending) { - cache.candidates.push({id: id, mesh:mesh, frame:t.frame, error:error}); - requested++; - } - /* we don't want to stop as soon as a node is not availabe, because the nodes are sorted by error, - this could cause a large drop in quality elsewere. - we still need to mark all children as blocked, to prevent including them in the cut of the dag. */ - var blocked = t.blocked[id] || !t.expandNode(id, error); - if (blocked) - t.nblocked++; - else { - t.selected[id] = 1; - //cache.realError = Math.min(error, cache.realError); - } - t.insertChildren(id, blocked); - } - - //update remaining errors in the cache - if(cache.nodes.has(mesh)) { - for(let id of cache.nodes.get(mesh)) { - let error = t.nodeError(id); - if(t.instance_errors[id] == 0) { - t.instance_errors[i] = error; - mesh.errors[id] = Math.max(mesh.errors[id], error); - } - } - } - - t.mesh = null; - return t.instance_errors; - }, - - insertNode: function (node) { - let t = this; - t.visited[node] = 1; - - const error = t.nodeError(node); - - t.instance_errors[node] = error; - t.mesh.errors[node] = Math.max(error, t.mesh.errors[node]); - t.mesh.frames[node] = t.frame; - - // if(node > 0 && error < t.currentError) return; //2% speed TODO check if needed - - t.visitQueue.push(node, error); - }, - - insertChildren : function (node, block) { - let t = this; - for(let i = t.mesh.nfirstpatch[node]; i < t.mesh.nfirstpatch[node+1]; ++i) { - const child = t.mesh.patches[i*3]; - if (child == t.mesh.sink) return; - if (block) t.blocked[child] = 1; - if (!t.visited[child]) - t.insertNode(child); - } - }, - - expandNode : function (node, error) { - let t = this; - if(node > 0 && error < t.currentError) { - // console.log("Reached error", error, t.currentError); - return false; - } - - if(t.drawSize > t.drawBudget) { - // console.log("Reached drawsize", t.drawSize, t.drawBudget); - return false; - } - - if(t.mesh.status[node] != 1) { //not ready - // console.log("Node " + node + " still not loaded (cache?)"); - return false; - } - - const sp = t.mesh.nspheres; - const off = node*5; - if(t.isVisible(sp[off], sp[off+1], sp[off+2], sp[off+3])) //expanded radius - t.drawSize += t.mesh.nvertices[node]*0.8; - //we are adding half of the new faces. (but we are using the vertices so *2) - - return true; - }, - - nodeError : function (n, tight) { - let t = this; - const spheres = t.mesh.nspheres; - const b = t.viewpoint; - const off = n*5; - const cx = spheres[off+0]; - const cy = spheres[off+1]; - const cz = spheres[off+2]; - let r = spheres[off+3]; - if(tight) - r = spheres[off+4]; - const d0 = b[0] - cx; - const d1 = b[1] - cy; - const d2 = b[2] - cz; - let dist = Math.sqrt(d0*d0 + d1*d1 + d2*d2) - r; - if (dist < 0.1) - dist = 0.1; - - //resolution is how long is a pixel at distance 1. - let error = t.mesh.nerrors[n]/(t.currentResolution*dist); //in pixels - - //causes flickering due to things popping in and out of visibility, causes a huge resorting. - /*if (!t.isVisible(cx, cy, cz, spheres[off+4])) - error /= 100.0; */ - - //more stable, at least it's continuous. - let d = t.distance(cx, cy, cz, spheres[off+4]); - if(d < -r) { - error /= 101.0; - } else if(d < 0) { - error /= 1 -( d/r)*100.0; - } - return error; - }, - - distance: function(x, y, z, r) { - const p = this.planes; - let min_distance = 1e20; - for (let i = 0; i < 24; i +=4) { - let d = p[i]*x + p[i+1]*y + p[i+2]*z + p[i+3] + r; - if(d < min_distance) - min_distance = d; - } - return min_distance; - }, - - isVisible : function (x, y, z, r) { - const p = this.planes; - for (let i = 0; i < 24; i +=4) { - if(p[i]*x + p[i+1]*y + p[i+2]*z + p[i+3] + r < 0) //plane is ax+by+cz+d = 0; - return false; - } - return true; - } - }; - - let glP = WebGLRenderingContext.prototype; - let attrGlMap = [glP.NONE, glP.BYTE, glP.UNSIGNED_BYTE, glP.SHORT, glP.UNSIGNED_SHORT, glP.INT, glP.UNSIGNED_INT, glP.FLOAT, glP.DOUBLE]; - let attrSizeMap = [0, 1, 1, 2, 2, 4, 4, 4, 8]; - - //All addresses in the file are n*256 so, 256 * 2^32 is the max size of a Nxs file - var padding = 256; - - let Mesh = function(url) { - var t = this; - t.isReady = false; - t.onLoad = []; - t.onUpdate = []; - t.reqAttempt = 0; - t.georeq = {}; //keeps track of existing httprequests - t.texreq = {}; - t.frame = 0; //last time this mesh was traversed in rendering. - t.availableNodes = 0; - if(url) - t.open(url); - }; - - Mesh.prototype = { - open: function(url) { - let mesh = this; - mesh.url = url; - mesh.httpRequest(url, - 0, - 88, - function() { - console.log("Loading header for " + mesh.url); - let view = new DataView(this.response); - view.offset = 0; - mesh.reqAttempt++; - const header = mesh.importHeader(view); - if(!header) { - console.log("Empty header!"); - if(mesh.reqAttempt < maxReqAttempt) mesh.open(mesh.url + '?' + Math.random()); // BLINK ENGINE CACHE BUG PATCH - return; - } - mesh.reqAttempt = 0; - for(let i in header) - mesh[i] = header[i]; - mesh.vertex = mesh.signature.vertex; - mesh.face = mesh.signature.face; - mesh.renderMode = mesh.face.index?["FILL", "POINT"]:["POINT"]; - mesh.compressed = (mesh.signature.flags & (2 | 4)); //meco or corto - mesh.meco = (mesh.signature.flags & 2); - mesh.corto = (mesh.signature.flags & 4); - - mesh.deepzoom = (mesh.signature.flags & 8); - if(mesh.deepzoom) - mesh.baseurl = url.substr(0, url.length -4) + '_files/'; - - - mesh.requestIndex(); - }, - function() { console.log("Open request error!");}, - function() { console.log("Open request abort!");} - ); - }, - - httpRequest: function(url, start, end, load, error, abort, type) { - if(!type) type = 'arraybuffer'; - var r = new XMLHttpRequest(); - r.open('GET', url, true); - r.responseType = type; - if(end) - r.setRequestHeader("Range", "bytes=" + start + "-" + (end -1)); - r.onload = function(){ - switch (this.status){ - case 0: - // console.log("0 response: server unreachable.");//returned in chrome for local files - error(); - break; - case 206: - // console.log("206 response: partial content loaded."); - load.bind(this)(); - break; - case 200: - // console.log("200 response: server does not support byte range requests."); - if(end == 0) - load.bind(this)(); - else - error(); - break; - } - }; - r.onerror = error; - r.onabort = abort; - r.send(); - return r; - }, - - requestIndex: function() { - var mesh = this; - var end = 88 + mesh.nodesCount*44 + mesh.patchesCount*12 + mesh.texturesCount*68; - mesh.httpRequest(this.url, - 88, - end, - function() { console.log("Loading index for " + mesh.url); mesh.handleIndex(this.response); }, - function() { console.log("Index request error!");}, - function() { console.log("Index request abort!");} - ); - }, - - handleIndex: function(buffer) { - let t = this; - let view = new DataView(buffer); - view.offset = 0; - - const n = t.nodesCount; - - t.noffsets = new Uint32Array(n); - t.nvertices = new Uint32Array(n); - t.nfaces = new Uint32Array(n); - t.nerrors = new Float32Array(n); - t.nspheres = new Float32Array(n*5); - t.nsize = new Float32Array(n); - t.nfirstpatch = new Uint32Array(n); - - for(let i = 0; i < n; i++) { - t.noffsets[i] = padding*getUint32(view); //offset - t.nvertices[i] = getUint16(view); //verticesCount - t.nfaces[i] = getUint16(view); //facesCount - t.nerrors[i] = getFloat32(view); - view.offset += 8; //skip cone - for(let k = 0; k < 5; k++) - t.nspheres[i*5+k] = getFloat32(view); //sphere + tight - t.nfirstpatch[i] = getUint32(view); //first patch - } - t.sink = n -1; - - t.patches = new Uint32Array(view.buffer, view.offset, t.patchesCount*3); //noded, lastTriangle, texture - t.nroots = t.nodesCount; - for(let j = 0; j < t.nroots; j++) { - for(let i = t.nfirstpatch[j]; i < t.nfirstpatch[j+1]; i++) { - if(t.patches[i*3] < t.nroots) - t.nroots = t.patches[i*3]; - } - } - - view.offset += t.patchesCount*12; - - t.textures = new Uint32Array(t.texturesCount); - t.texref = new Uint32Array(t.texturesCount); - for(let i = 0; i < t.texturesCount; i++) { - t.textures[i] = padding*getUint32(view); - view.offset += 16*4; //skip proj matrix - } - - t.vsize = 12 + (t.vertex.normal?6:0) + (t.vertex.color?4:0) + (t.vertex.texCoord?8:0); - t.fsize = 6; - - //problem: I have no idea how much space a texture is needed in GPU. 10x factor assumed. - let tmptexsize = new Uint32Array(n-1); - let tmptexcount = new Uint32Array(n-1); - for(let i = 0; i < n-1; i++) { - for(let p = t.nfirstpatch[i]; p != t.nfirstpatch[i+1]; p++) { - let tex = t.patches[p*3+2]; - tmptexsize[i] += t.textures[tex+1] - t.textures[tex]; - tmptexcount[i]++; - } - t.nsize[i] = t.vsize*t.nvertices[i] + t.fsize*t.nfaces[i]; - } - for(let i = 0; i < n-1; i++) { - t.nsize[i] += 10*tmptexsize[i]/tmptexcount[i]; - } - - t.status = new Uint8Array(n); //0 for none, 1 for ready, 2+ for waiting data - t.frames = new Uint32Array(n); - t.errors = new Float32Array(n); //biggest error of instances - t.reqAttempt = new Uint8Array(n); - - t.isReady = true; - for(let callback of t.onLoad) - callback(this); - }, - - importAttribute: function(view) { - let a = {}; - a.type = view.getUint8(view.offset++, true); - a.size = view.getUint8(view.offset++, true); - a.glType = attrGlMap[a.type]; - a.normalized = a.type < 7; - a.stride = attrSizeMap[a.type]*a.size; - if(a.size == 0) return null; - return a; - }, - - importElement: function(view) { - let e = []; - for(let i = 0; i < 8; i++) - e[i] = this.importAttribute(view); - return e; - }, - - importVertex: function(view) { //enum POSITION, NORMAL, COLOR, TEXCOORD, DATA0 - const e = this.importElement(view); - let color = e[2]; - if(color) { - color.type = 2; //unsigned byte - color.glType = attrGlMap[2]; - } - return { position: e[0], normal: e[1], color: e[2], texCoord: e[3], data: e[4] }; - }, - - //enum INDEX, NORMAL, COLOR, TEXCOORD, DATA0 - importFace: function(view) { - const e = this.importElement(view); - let color = e[2]; - if(color) { - color.type = 2; //unsigned byte - color.glType = attrGlMap[2]; - } - return { index: e[0], normal: e[1], color: e[2], texCoord: e[3], data: e[4] }; - }, - - importSignature: function(view) { - let s = {}; - s.vertex = this.importVertex(view); - s.face = this.importFace(view); - s.flags = getUint32(view); - return s; - }, - - importHeader: function(view) { - const magic = getUint32(view); - if(magic != 0x4E787320) return null; - let h = {}; - h.version = getUint32(view); - h.verticesCount = getUint64(view); - h.facesCount = getUint64(view); - h.signature = this.importSignature(view); - h.nodesCount = getUint32(view); - h.patchesCount = getUint32(view); - h.texturesCount = getUint32(view); - h.sphere = { - center: [getFloat32(view), getFloat32(view), getFloat32(view)], - radius: getFloat32(view) - }; - return h; - }, - //OVERRIDE THESE METHOS - - //assemble node and geometry - createNode: function(id) {}, - - createNodeGeometry: function(id, data) {}, - deleteNodeGeometry: function(id) {}, - - createTexture: function(id, image) {}, - deleteTexture: function(id) {}, - }; - - var targetError = 2.0; //error won't go lower than this if we reach it - var maxError = 15; //error won't go over this even if fps is low - var minFps = 15; - var maxPending = 3; - var maxReqAttempt$1 = 2; - var maxCacheSize = 1024 *(1<<20); - - function _Cache() { - let t = this; - t.cortopath = '.'; - t.frame = 0; //keep track of the time - - t.maxCacheSize = maxCacheSize; - t.minFps = minFps; - t.currentFps = 0; - t.targetError = targetError; - t.currentError = targetError; - t.maxError = maxError; - t.realError = 0; - - t.pending = 0; - t.maxPending = 3; - t.cacheSize = 0; - t.candidates = []; //list of nodes to be loaded - t.nodes = new Map(); //for each mesh a list of node ids. - - t.last_frametime = 0; - t.frametime = 0; - t.end_frametime = 0; - - t.debug = { - verbose : false, //debug messages - nodes : false, //color each node - draw : false, //final rendering call disabled - extract : false, //extraction disabled} - }; - - t.totswapped = 0; //in the last frame. - t.swaprate = 0; - t.lastupdate = performance.now(); - } - - _Cache.prototype = { - getTargetError: function() { return this.targetError; }, - getMinFps: function() { return this.minFps; }, - setMinFps: function(fps) { this.minFps = fps; }, - getMaxCacheSize: function() { return this.maxCacheSize; }, - setMaxCacheSize: function(size) { this.maxCacheSize = size; }, - setTargetError: function(error) { this.targetError = error; }, - - loadCorto: function() { - let corto = new Worker(this.cortopath + '/corto.em.js'); - corto.requests = {}; - corto.count = 0; - corto.postRequest = function(node) { - corto.postMessage({ buffer: node.buffer, request:this.count, rgba_colors: true, short_index: true, short_normals: true}); - node.buffer = null; - this.requests[this.count++] = node; - }; - corto.onmessage = function(e) { - var request = e.data.request; - var node = this.requests[request]; - delete this.requests[request]; - node.model = e.data.model; - node.cache.readyGeometryNode(node.mesh, node.id, node.model); - }; - this.corto = corto; - }, - - - beginFrame: function(fps) { //each context has a separate frame count. - let c = this; - c.frametime = performance.now(); - let elapsed = c.frametime - c.last_frametime; - c.last_frametime = c.frametime; - if(elapsed < 500) - c.currentFps = 0.9*c.currentFps + 0.1*(1000/elapsed); - - fps = c.currentFps; - - c.frame++; - c.candidates = []; - if(fps && c.minFps) { - c.currentFps = fps; - const r = c.minFps/fps; - if(r > 1.1) - c.currentError *= 1.05; - if(r < 0.9) - c.currentError *= 0.95; - - c.currentError = Math.max(c.targetError, Math.min(c.maxError, c.currentError)); - - } else - c.currentError = c.targetError; - - c.rendered = 0; - c.realError = 1e20; - c.totswapped = 0; - }, - - endFrame: function() { - this.update(); - }, - - - - requestNode: function(mesh, id) { - mesh.status[id] = 2; - - - this.cacheSize += mesh.nsize[id]; - mesh.reqAttempt[id] = 0; - - if(!this.nodes.has(mesh)) - this.nodes.set(mesh, new Set()); - this.nodes.get(mesh).add(id); - - this.requestNodeGeometry(mesh, id); - this.requestNodeTexture(mesh, id); - }, - - requestNodeGeometry: function(mesh, id) { - this.pending++; - mesh.status[id]++; - let url = mesh.deepzoom ? mesh.baseurl + id + ".nxn" : mesh.url; - let start = mesh.deepzoom ? 0 : mesh.noffsets[id]; - let end = mesh.deepzoom ? 0 : mesh.noffsets[id+1]; - let request = mesh.georeq[id] = mesh.httpRequest(url, - start, - end, - ()=>{ - delete mesh.georeq[id]; - this.loadNodeGeometry(request, mesh, id); - this.pending--; - }, - ()=>{ - delete mesh.georeq[id]; - if(this.debug.verbose) console.log("Geometry request error!"); - this.recoverNode(mesh, id, 0); - this.pending--; - }, - ()=>{ - delete mesh.georeq[id]; - if(this.debug.verbose) console.log("Geometry request abort!"); - this.removeNode(mesh, id); - this.pending--; - }, - 'arraybuffer' - ); - }, - - requestNodeTexture: function(mesh, id) { - - if(!mesh.vertex.texCoord) return; - - let tex = mesh.patches[mesh.nfirstpatch[id]*3+2]; - mesh.texref[tex]++; - mesh.status[id]++; - - let url = mesh.deepzoom ? mesh.baseurl + tex + ".jpg" : mesh.url; - let start = mesh.deepzoom ? 0 : mesh.textures[tex]; - let end = mesh.deepzoom ? 0 : mesh.textures[tex+1]; - let request = mesh.texreq[tex] = mesh.httpRequest(url, - start, - end, - ()=>{ - delete mesh.texreq[tex]; - this.loadNodeTexture(request, mesh, id, tex); - - }, - ()=>{ - delete mesh.texreq[tex]; - if(this.debug.verbose) console.log("Texture request error!"); - this.recoverNode(mesh, id, 1); - }, - ()=>{ - delete mesh.texreq[tex]; - if(this.debug.verbose) console.log("Texture request abort!"); - this.removeNode(mesh, id); - }, - 'blob' - ); - }, - - recoverNode: function(mesh, id, mode) { - if(mesh.status[id] == 0) return; - - mesh.status[id]--; - - let t = this; - - if(mesh.reqAttempt[id] > maxReqAttempt$1) { - if(this.debug.verbose) console.log("Max request limit for " + m.url + " node: " + n); - t.removeNode(mesh, id); - return; - } - - mesh.reqAttempt[id]++; - - switch (mode){ - case 0: - t.requestNodeGeometry(mesh, id); - if(this.debug.verbose) console.log("Recovering geometry for " + m.url + " node: " + n); - break; - case 1: - t.requestNodeTexture(mesh, id); - if(this.debug.verbose) console.log("Recovering texture for " + m.url + " node: " + n); - break; - } - }, - - loadNodeGeometry: function(request, mesh, id) { - if(mesh.status[id] == 0) return; - - if(!mesh.compressed) - this.readyGeometryNode(mesh, id, request.response); - else { - if(!this.corto) this.loadCorto(); - this.corto.postRequest( { mesh:mesh, id:id, buffer: request.response, cache: this }); - } - }, - - - loadNodeTexture: function(request, mesh, id, texid) { - if(mesh.status[id] == 0) { - throw "Should not load texture twice"; - } - - let blob = request.response; - let callback = (img) => { - if(mesh.status[id] == 0) //call was aborted. - return; - mesh.createTexture(texid, img); - - mesh.status[id]--; - - if(mesh.status[id] == 2) - this.readyNode(mesh, id); - }; - - if(typeof createImageBitmap != 'undefined') { - try { - createImageBitmap(blob, { imageOrientation: 'flipY' }).then(callback); - } catch(error) { //old version of firefox do not support options (but flip anyway) - createImageBitmap(blob).then(callback); - } - - } else { //fallback for IOS - var urlCreator = window.URL || window.webkitURL; - var img = document.createElement('img'); - img.onerror = function(e) { console.log("Texture loading error!"); }; - img.src = urlCreator.createObjectURL(blob); - - img.onload = function() { - urlCreator.revokeObjectURL(img.src); - callback(img); - }; - } - }, - - removeNode: function(mesh, id) { - this.nodes.get(mesh).delete(id); - - if(mesh.status[id] == 0) { - if(this.debug.verbose) - console.log("Double remove due to abort."); - return; //TODO - } - - mesh.status[id] = 0; - if (id in mesh.georeq && mesh.georeq[id].readyState != 4) { - mesh.georeq[id].abort(); - delete mesh.georeq[id]; - //this.pending--; - } - mesh.availableNodes--; - this.cacheSize -= mesh.nsize[id]; - mesh.deleteNodeGeometry(id); - - if(!mesh.vertex.texCoord) return; - - const tex = mesh.patches[mesh.nfirstpatch[id]*3+2]; //TODO assuming one texture per node - - if (tex in mesh.texreq && mesh.texreq[tex].readyState != 4) { - mesh.texreq[tex].abort(); - delete mesh.texreq[tex]; - } - - mesh.texref[tex]--; - if(mesh.texref[tex] == 0) { - mesh.deleteTexture(tex); - } - }, - - readyGeometryNode: function(mesh, id, buffer) { - if(mesh.status[id] == 0) //call was aborted - return; - const nv = mesh.nvertices[id]; - const nf = mesh.nfaces[id]; - let geometry = {}; - - - if(!mesh.corto) { - geometry.index = new Uint16Array(buffer, nv*mesh.vsize, nf*3); - geometry.position = new Float32Array(buffer, 0, nv*3); - - var off = nv*12; - if(mesh.vertex.texCoord) { - geometry.uv = new Float32Array(buffer, off, nv*2); - off += nv*8; - } - if(mesh.vertex.normal) { - geometry.normal = new Int16Array(buffer, off, nv*3); - off += nv*6; - } - - if(mesh.vertex.color) { - geometry.color = new Uint8Array(buffer, off, nv*4); - off += nv*4; - } - - - } else { - geometry = buffer; - } - - //if(nf == 0) - // scramble(nv, v, no, co); - - mesh.createNodeGeometry(id, geometry); - mesh.status[id]--; - - if(mesh.status[id] == 2) { - this.readyNode(mesh, id); - } - }, - - //the node is finished, add to cache, and update counters - readyNode: function(mesh, id) { - - mesh.status[id]--; - if(mesh.status[id] != 1) throw "A ready node should have status ==1" - - - mesh.reqAttempt[id] = 0; - //this.pending--; - mesh.createNode(id); - mesh.availableNodes++; - - for(let callback of mesh.onUpdate) - callback(); - this.update(); - }, - - flush: function(mesh) { - if(!this.nodes.has(mesh)) return; - - for(let id of this.nodes.get(mesh)) - this.removeNode(mesh, id); - this.nodes.delete(mesh); - }, - - update: function() { - if(this.pending >= maxPending) - return; - - //the best candidate has the highest error - let best = null; - for(let c of this.candidates) { - if(c.mesh.status[c.id] == 0 && (!best || c.error > best.error)) - best = c; - } - if(!best) return; - - // record amount of data transfer per second. - /* - let now = performance.now(); - if(Math.floor(now/1000) > Math.floor(this.lastupdate/1000)) { //new second - this.swaprate = (this.totswapped/1000)/(now - this.lastupdate); //transfer in mb/s - if(this.debug.verbose) - console.log("Memory loaded in GPU: ", this.swaprate); - this.totswapped = 0; - this.lastupdate = now; - } - */ - - - //make room for new nodes! - - while(this.cacheSize > this.maxCacheSize) { - let worst = null; - - //find node with smallest error in cache and remove it if worse than the best candidate. - for(let [mesh, ids] of this.nodes) { - for(let id of ids) { - //we need to recompute the errors for the cache, as if not traversed doesn't get updated. - let error = mesh.errors[id]; - let frame = mesh.frames[id]; - if( !worst || error < worst.error) { - worst = { id: id, frame: frame, error: error, mesh: mesh }; - } - } - } - if(!worst || worst.error >= best.error*0.9) { - //(worst.frame + 30 >= best.frame && )) //dont' remove if the best candidate is not good enogh - return; - } - this.removeNode(worst.mesh, worst.id); - } - this.totswapped += best.mesh.nsize[best.id]; - this.candidates = this.candidates.filter(e => e.mesh == best.mesh && e.id == best.id); - this.requestNode(best.mesh, best.id); - this.update(); //try again. - } - }; - - let Cache = new _Cache; - - class Nexus3D extends THREE__namespace.Mesh { - - constructor(url, renderer, options) { - - super(); - if(typeof renderer == 'function') - throw "Nexus3D constructor has changed: Nexus3D(url, renderer, options) where options include: onLoad, onUpdate, onProgress and material" - - this.patchWebGLRenderer(renderer); - - Object.assign(this, { - isNXS: true, - type:'NXS', - url: url, - gl: renderer.getContext(), - material: null, - - autoUpdate: true, - mesh: new Mesh(), - vbo: [], - ibo: [], - vao: [], - textures: [], - attributes: {}, //here we store the uniform attributes of the shader. - - basemesh: null, //highest level of the nexus, for picking - }); - - if('material' in options) - this.material = options.material; - if(!this.material) - this.material = new THREE__namespace.MeshStandardMaterial(); - - for(let call of ['onLoad', 'onUpdate', 'onProgress']) { - this['_' + call] = []; - if(call in options) - this['_' + call].push(options[call]); - } - - if(this.url) { - if(typeof url == 'object') { - this.nxs = this.url; - this.nxs.onLoad.push((m) => { - this.mesh = this.nxs.mesh; - this.traversal = this.nxs.traversal; - this.cache = this.nxs.cache; - this.vbo = this.nxs.vbo; - this.ibo = this.nxs.ibo; - this.vao = this.nxs.vao; - this.textures = this.nxs.textures; - this.onLoadCallback(this); - }); - } else - this.open(this.url); - } - } - - copy(source) { - Object3D.prototype.copy.call( this, source, false ); - throw "Can't really copy." - } - - - open(url) { - let t = this; - this.mesh.open(url); - this.mesh.createNode = (id) => { }; - this.mesh.createNodeGeometry = (id, geometry) => { t.createNodeGeometry(id, geometry); }; - this.mesh.createTexture = (id, image) => { t.createTexture(id, image); }; - this.mesh.deleteNodeGeometry = (id) => { t.deleteNodeGeometry(id); }; - this.mesh.deleteTexture = (id) => { t.deleteTexture(id); }; - this.mesh.onLoad.push(() => { t.onLoadCallback(); }); - this.mesh.onUpdate.push(() => { - for(let callback of t._onUpdate) callback(this); - for(let callback of t._onProgress) callback(this, this.mesh.availableNodes, this.mesh.nodesCount); - }); - - this.traversal = new Traversal(); - this.cache = Cache; //new Cache(); - this.textures = {}; - } - - set onLoad(callback) { - this._onLoad.push(callback); - } - - set onUpdate(callback) { - this._onUpdate.push(callback); - } - - set onProgress(callback) { - this._onProgress.push(callback); - } - - //TODO this is not really needed, we might just conform to THREEJS standard of updating. - /*set material(material) { - this.material = material; - this.material.needsUpdate = true; - }*/ - - updateMaterials() { - if(this.material.map !== false && this.mesh.vertex.texCoord) - this.material.map = this.material_texture; - - if(this.mesh.vertex.color) -<<<<<<< HEAD - this.material.vertexColors = true; -======= - this.material.vertexColors = THREE__namespace.VertexColors; ->>>>>>> c5d1f5c9885a0ab0f0ea816a162bce0d1d14c02b - this.material.needsUpdate = true; - } - - onLoadCallback() { - const c = this.mesh.sphere.center; - const center = new THREE__namespace.Vector3(c[0], c[1], c[2]); - const radius = this.mesh.sphere.radius; - this.boundingSphere = new THREE__namespace.Sphere(center, radius); - - var geometry = new THREE__namespace.BufferGeometry(); - - geometry.setAttribute( 'position', new THREE__namespace.BufferAttribute(new Float32Array(3), 3)); - - if(this.mesh.vertex.normal) - geometry.setAttribute( 'normal', new THREE__namespace.BufferAttribute(new Float32Array(3), 3)); - if(this.mesh.vertex.color) - geometry.setAttribute( 'color', new THREE__namespace.BufferAttribute(new Float32Array(4), 4)); - if(this.mesh.vertex.texCoord) - geometry.setAttribute( 'uv', new THREE__namespace.BufferAttribute(new Float32Array(2), 2)); - - if(this.mesh.vertex.texCoord) { -<<<<<<< HEAD - this.material_texture = new THREE.DataTexture( new Uint8Array([1, 1, 1, 1]), 1, 1, THREE.RGBAFormat ); -======= - this.material_texture = new THREE__namespace.DataTexture( new Uint8Array([1, 1, 1]), 1, 1, THREE__namespace.RGBFormat ); ->>>>>>> c5d1f5c9885a0ab0f0ea816a162bce0d1d14c02b - this.material_texture.needsUpdate = true; - } - - this.updateMaterials(); - this.geometry = geometry; - - this.frustumCulled = false; - - for(let callback of this._onLoad) - callback(this); - } - - renderBufferDirect(renderer, scene, camera, geometry, material, group) { - let s = new THREE__namespace.Vector2(); - renderer.getSize(s); - - //object modelview is multiplied by camera during rendering, we need to do it here for visibility computations - this.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, this.matrixWorld ); - this.traversal.updateView([0, 0, s.width, s.height], camera.projectionMatrix.elements, this.modelViewMatrix.elements); - this.instance_errors = this.traversal.traverse(this.mesh, this.cache); - - //threejs increments version when setting neeedsUpdate - /*if(this.material.version > 0) { - this.updateMaterials(); - this.material.version = 0; - for(let callback of this.onUpdate) - callback(this); - }*/ - let gl = this.gl; - let program = gl.getParameter(gl.CURRENT_PROGRAM); - - //TODO these calls could be cached saving attrs per each material. - let attr = this.attributes; - attr.position = gl.getAttribLocation(program, "position"); - attr.normal = gl.getAttribLocation(program, "normal"); - attr.color = gl.getAttribLocation(program, "color"); - attr.uv = gl.getAttribLocation(program, "uv"); - attr.size = gl.getUniformLocation(program, "size"); - attr.scale = gl.getUniformLocation(program, "scale"); - - let map_location = gl.getUniformLocation(program, "map"); - attr.map = map_location ? gl.getUniform(program, map_location) : null; - - - //hack to detect if threejs using point or triangle shaders - //instance.mode = attr.size ? "POINT" : "FILL"; - //if(attr.size != -1) - // instance.pointsize = material.size; - - //can't find docs or code on how material.scale is computed in threejs. - //if(attr.scale != -1) - // instance.pointscale = 2.0; - - this.setVisibility(); - } - - setVisibility() { - //set visibile what is visible! - let t = this.traversal; - let m = this.mesh; - - if(!m.isReady) - return; - let rendered = 0; - - let attr = this.attributes; - let mesh = this.mesh; - let gl = this.gl; - let gl2 = gl instanceof WebGL2RenderingContext; - let state = attr.uv + 10*attr.color + 100*attr.normal; - - for(let id = 0; id < m.nodesCount; id++) { - // let err = m.nerrors[id]; - - if(!t.selected[id]) continue; - - //check for children: if all are selected, bail out. - { - let visible = false; - m.nfirstpatch[id+1]-1; - for (var p = m.nfirstpatch[id]; p < m.nfirstpatch[id+1]; ++p) { - var child = m.patches[p*3]; - - if(!t.selected[child]) { - visible = true; - break; - } - } - if(!visible) continue; - } - - var sp = m.nspheres; - var off = id*5; - if(!t.isVisible(sp[off], sp[off+1], sp[off+2], sp[off+4])) //tight radius - continue; - - let doBind = true; - - if(gl2) { - if(state in this.vao[id]) - doBind = false; - else - this.vao[id][state] = gl.createVertexArray(); - - gl.bindVertexArray(this.vao[id][state]); - } - - if(doBind) { - gl.bindBuffer(gl.ARRAY_BUFFER, this.vbo[id]); - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.ibo[id]); - - gl.vertexAttribPointer(attr.position, 3, gl.FLOAT, false, 12, 0); - gl.enableVertexAttribArray(attr.position); - - let nv = this.mesh.nvertices[id]; - let offset = nv*12; - - if(mesh.vertex.texCoord) { - if(attr.uv >= 0) { - gl.vertexAttribPointer(attr.uv, 2, gl.FLOAT, false, 8, offset); - gl.enableVertexAttribArray(attr.uv); - } - offset += nv*8; - } - if(mesh.vertex.color) { - if(attr.color >= 0) { - gl.vertexAttribPointer(attr.color, 4, gl.UNSIGNED_BYTE, true, 4, offset); - gl.enableVertexAttribArray(attr.color); - } - offset += nv*4; - } - if(mesh.vertex.normal) { - if(attr.normal >= 0) { - gl.vertexAttribPointer(attr.normal, 3, gl.SHORT, true, 6, offset); - gl.enableVertexAttribArray(attr.normal); - } - } - - if(this.cache.debug.nodes) { - gl.disableVertexAttribArray(attr.color); - - var error = this.instance_errors[id]; //this.mesh.errors[id]; - var palette = [ - [1, 1, 1, 1], //white - [1, 1, 1, 1], //white - [0, 1, 0, 1], //green - [0, 1, 1, 1], //cyan - [1, 1, 0, 1], //yellow - [1, 0, 1, 1], //magenta - [1, 0, 0, 1] //red - ]; - let w = Math.min(5.99, Math.max(0, Math.log2(error)/2)); - let low = Math.floor(w); - w -= low; - let color = []; - for( let k = 0; k < 4; k++) - color[k] = palette[low][k]*(1-w) + palette[low+1][k]*w; - - gl.vertexAttrib4fv(attr.color, color); - } - } - this.cache.realError = Math.min(this.mesh.errors[id], this.cache.realError); - - let offset = 0; - let end = 0; - let last = m.nfirstpatch[id+1]-1; - for (let p = m.nfirstpatch[id]; p < m.nfirstpatch[id+1]; ++p) { - let child = m.patches[p*3]; - - if(!t.selected[child]) { - end = m.patches[p*3+1]; - if(p < last) //we join patches if possible. - continue; - } - - if(end > offset) { - if(m.vertex.texCoord && attr.uv >= 0) { - var tex = m.patches[p*3+2]; - if(tex != -1) { //bind texture - var texid = this.textures[tex]; - gl.activeTexture(gl.TEXTURE0 + attr.map); - gl.bindTexture(gl.TEXTURE_2D, texid); - } - } - let mode = this.material.wireframe ? gl.LINE_STRIP : gl.TRIANGLES; - - gl.drawElements(mode, (end - offset) * 3, gl.UNSIGNED_SHORT, offset * 6); - rendered += end - offset; - } - offset = m.patches[p*3+1]; - } - } - this.cache.rendered += rendered; - } - - createNodeGeometry(id, data) { - let m = this.mesh; - var nv = m.nvertices[id]; - m.nfaces[id]; - let indices = data.index; - let vertices = new ArrayBuffer(nv*m.vsize); - var position = new Float32Array(vertices, 0, nv*3); - position.set(data.position); - var off = nv*12; - if(m.vertex.texCoord) { - var uv = new Float32Array(vertices, off, nv*2); - uv.set(data.uv); - off += nv*8; - } - if(m.vertex.color) { - var color = new Uint8Array(vertices, off, nv*4); - color.set(data.color); - off += nv*4; - } - if(m.vertex.normal) { - var normal = new Int16Array(vertices, off, nv*3); - normal.set(data.normal); - off += nv*6; - } - - //needed for approximate picking. - if(id < this.mesh.nroots) { - let basegeometry = new THREE__namespace.BufferGeometry(); - basegeometry.setAttribute( 'position', new THREE__namespace.BufferAttribute(data.position, 3 ) ); - basegeometry.setAttribute( 'normal', new THREE__namespace.BufferAttribute(data.normal, 3 ) ); - basegeometry.setIndex(new THREE__namespace.BufferAttribute( data.index, 1 ) ); - - this.basemesh = new THREE__namespace.Mesh(basegeometry, this.material); - this.basemesh.visible = false; - this.add(this.basemesh); - } - - var gl = this.gl; - this.vao[id] = {}; //one for each attrib combination in use (casting shadows for example). - gl.bindVertexArray(null); - - var vbo = this.vbo[id] = gl.createBuffer(); - gl.bindBuffer(gl.ARRAY_BUFFER, vbo); - gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW); - var ibo = this.ibo[id] = gl.createBuffer(); - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo); - gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW); - } - - createTexture(id, image) { - let gl = this.gl; - var flip = gl.getParameter(gl.UNPACK_FLIP_Y_WEBGL); - gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); - let tex = this.textures[id] = gl.createTexture(); - gl.bindTexture(gl.TEXTURE_2D, tex); - - //TODO some textures might be alpha only! save space - gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); - - function powerOf2(n) { return n && (n & (n - 1)) === 0; } - if(!(gl instanceof WebGLRenderingContext) || (powerOf2(image.width) && powerOf2(image.height))) { - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST_MIPMAP_LINEAR); - gl.generateMipmap(gl.TEXTURE_2D); - } else { - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); - } - - gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flip); - } - //schedule for removal of this node( might not want to delete it in the middle of something. - //TODO check if this is really needed! - deleteNodeGeometry(id) { - this.gl.deleteBuffer(this.vbo[id]); - this.gl.deleteBuffer(this.ibo[id]); - - this.vbo[id] = this.ibo[id] = null; - if(this.vao[id]) //node might have been loaded and unloaded before a rendering - for(const [state, vao] of Object.entries(this.vao[id])) - this.gl.deleteVertexArray(vao); - this.vao[id] = null; - } - - deleteTexture(tex) { - if(!this.textures[tex]) - return; - // throw "Deleting missing texture!" - - this.gl.deleteTexture(this.textures[tex]); - this.textures[tex] = 0; - } - - flush() { - this.cache.flush(this.mesh); - } - - dispose() { - this.flush(); - for(let child of this.children) - child.geometry.dispose(); - } - - toJSON(meta) { - throw "Can't"; - } - - patchWebGLRenderer(renderer) { - if(renderer.nexusPatched) return; - let f = renderer.renderBufferDirect; - renderer.renderBufferDirect = ( camera, scene, geometry, material, object, group) => { - f( camera, scene, geometry, material, object, group ); - if ( object.renderBufferDirect) - object.renderBufferDirect(renderer, scene, camera, geometry, material, group); - }; - renderer.originalRender = renderer.render; - renderer.render = (scene, camera) => { - Cache.beginFrame(30); - renderer.originalRender(scene, camera); - Cache.endFrame(); - }; - renderer.nexusPatched = true; - } - - } - - exports.Cache = Cache; - exports.Nexus3D = Nexus3D; - - Object.defineProperty(exports, '__esModule', { value: true }); - -})); +/* + * ATTENTION: The "eval" devtool has been used (maybe by default in mode: "development"). + * This devtool is neither made for production nor for readable output files. + * It uses "eval()" calls to create a separate source file in the browser devtools. + * If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/) + * or disable the default devtool with "devtool: false". + * If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/). + */ +(function webpackUniversalModuleDefinition(root, factory) { + if(typeof exports === 'object' && typeof module === 'object') + module.exports = factory(require("three")); + else if(typeof define === 'function' && define.amd) + define(["three"], factory); + else if(typeof exports === 'object') + exports["Nexus3D"] = factory(require("three")); + else + root["Nexus3D"] = factory(root["THREE"]); +})(this, (__WEBPACK_EXTERNAL_MODULE_three__) => { +return /******/ (() => { // webpackBootstrap +/******/ "use strict"; +/******/ var __webpack_modules__ = ({ + +/***/ "./src/Binary.js": +/*!***********************!*\ + !*** ./src/Binary.js ***! + \***********************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ getFloat32: () => (/* binding */ getFloat32),\n/* harmony export */ getUint16: () => (/* binding */ getUint16),\n/* harmony export */ getUint32: () => (/* binding */ getUint32),\n/* harmony export */ getUint64: () => (/* binding */ getUint64),\n/* harmony export */ matInv: () => (/* binding */ matInv),\n/* harmony export */ matMul: () => (/* binding */ matMul),\n/* harmony export */ vecMul: () => (/* binding */ vecMul)\n/* harmony export */ });\n/* UTILITIES */\n \nfunction getUint64(view) {\n var s = 0;\n var lo = view.getUint32(view.offset, true);\n var hi = view.getUint32(view.offset + 4, true);\n view.offset += 8;\n return ((hi * (1 << 32)) + lo);\n}\n\nfunction getUint32(view) {\n var s = view.getUint32(view.offset, true);\n view.offset += 4;\n return s;\n}\n\nfunction getUint16(view) {\n var s = view.getUint16(view.offset, true);\n view.offset += 2;\n return s;\n}\n\nfunction getFloat32(view) {\n var s = view.getFloat32(view.offset, true);\n view.offset += 4;\n return s;\n}\n\n/* MATRIX STUFF */\n\nfunction vecMul(m, v, r) {\n var w = m[3]*v[0] + m[7]*v[1] + m[11]*v[2] + m[15];\n\n r[0] = (m[0]*v[0] + m[4]*v[1] + m[8 ]*v[2] + m[12 ])/w;\n r[1] = (m[1]*v[0] + m[5]*v[1] + m[9 ]*v[2] + m[13 ])/w;\n r[2] = (m[2]*v[0] + m[6]*v[1] + m[10]*v[2] + m[14])/w;\n}\n\n\nfunction matMul(a, b, r) {\n r[ 0] = a[0]*b[0] + a[4]*b[1] + a[8]*b[2] + a[12]*b[3];\n r[ 1] = a[1]*b[0] + a[5]*b[1] + a[9]*b[2] + a[13]*b[3];\n r[ 2] = a[2]*b[0] + a[6]*b[1] + a[10]*b[2] + a[14]*b[3];\n r[ 3] = a[3]*b[0] + a[7]*b[1] + a[11]*b[2] + a[15]*b[3];\n\n r[ 4] = a[0]*b[4] + a[4]*b[5] + a[8]*b[6] + a[12]*b[7];\n r[ 5] = a[1]*b[4] + a[5]*b[5] + a[9]*b[6] + a[13]*b[7];\n r[ 6] = a[2]*b[4] + a[6]*b[5] + a[10]*b[6] + a[14]*b[7];\n r[ 7] = a[3]*b[4] + a[7]*b[5] + a[11]*b[6] + a[15]*b[7];\n\n r[ 8] = a[0]*b[8] + a[4]*b[9] + a[8]*b[10] + a[12]*b[11];\n r[ 9] = a[1]*b[8] + a[5]*b[9] + a[9]*b[10] + a[13]*b[11];\n r[10] = a[2]*b[8] + a[6]*b[9] + a[10]*b[10] + a[14]*b[11];\n r[11] = a[3]*b[8] + a[7]*b[9] + a[11]*b[10] + a[15]*b[11];\n\n r[12] = a[0]*b[12] + a[4]*b[13] + a[8]*b[14] + a[12]*b[15];\n r[13] = a[1]*b[12] + a[5]*b[13] + a[9]*b[14] + a[13]*b[15];\n r[14] = a[2]*b[12] + a[6]*b[13] + a[10]*b[14] + a[14]*b[15];\n r[15] = a[3]*b[12] + a[7]*b[13] + a[11]*b[14] + a[15]*b[15];\n}\n\nfunction matInv(m, t) {\n var s = 1.0/(\n m[12]* m[9]*m[6]*m[3]-m[8]*m[13]*m[6]*m[3]-m[12]*m[5]*m[10]*m[3]+m[4]*m[13]*m[10]*m[3]+\n m[8]*m[5]*m[14]*m[3]-m[4]*m[9]*m[14]*m[3]-m[12]*m[9]*m[2]*m[7]+m[8]*m[13]*m[2]*m[7]+\n m[12]*m[1]*m[10]*m[7]-m[0]*m[13]*m[10]*m[7]-m[8]*m[1]*m[14]*m[7]+m[0]*m[9]*m[14]*m[7]+\n m[12]*m[5]*m[2]*m[11]-m[4]*m[13]*m[2]*m[11]-m[12]*m[1]*m[6]*m[11]+m[0]*m[13]*m[6]*m[11]+\n m[4]*m[1]*m[14]*m[11]-m[0]*m[5]*m[14]*m[11]-m[8]*m[5]*m[2]*m[15]+m[4]*m[9]*m[2]*m[15]+\n m[8]*m[1]*m[6]*m[15]-m[0]*m[9]*m[6]*m[15]-m[4]*m[1]*m[10]*m[15]+m[0]*m[5]*m[10]*m[15]\n );\n\n t[ 0] = (m[9]*m[14]*m[7]-m[13]*m[10]*m[7]+m[13]*m[6]*m[11]-m[5]*m[14]*m[11]-m[9]*m[6]*m[15]+m[5]*m[10]*m[15])*s;\n t[ 1] = (m[13]*m[10]*m[3]-m[9]*m[14]*m[3]-m[13]*m[2]*m[11]+m[1]*m[14]*m[11]+m[9]*m[2]*m[15]-m[1]*m[10]*m[15])*s;\n t[ 2] = (m[5]*m[14]*m[3]-m[13]*m[6]*m[3]+m[13]*m[2]*m[7]-m[1]*m[14]*m[7]-m[5]*m[2]*m[15]+m[1]*m[6]*m[15])*s;\n t[ 3] = (m[9]*m[6]*m[3]-m[5]*m[10]*m[3]-m[9]*m[2]*m[7]+m[1]*m[10]*m[7]+m[5]*m[2]*m[11]-m[1]*m[6]*m[11])*s;\n\n t[ 4] = (m[12]*m[10]*m[7]-m[8]*m[14]*m[7]-m[12]*m[6]*m[11]+m[4]*m[14]*m[11]+m[8]*m[6]*m[15]-m[4]*m[10]*m[15])*s;\n t[ 5] = (m[8]*m[14]*m[3]-m[12]*m[10]*m[3]+m[12]*m[2]*m[11]-m[0]*m[14]*m[11]-m[8]*m[2]*m[15]+m[0]*m[10]*m[15])*s;\n t[ 6] = (m[12]*m[6]*m[3]-m[4]*m[14]*m[3]-m[12]*m[2]*m[7]+m[0]*m[14]*m[7]+m[4]*m[2]*m[15]-m[0]*m[6]*m[15])*s;\n t[ 7] = (m[4]*m[10]*m[3]-m[8]*m[6]*m[3]+m[8]*m[2]*m[7]-m[0]*m[10]*m[7]-m[4]*m[2]*m[11]+m[0]*m[6]*m[11])*s;\n\n t[ 8] = (m[8]*m[13]*m[7]-m[12]*m[9]*m[7]+m[12]*m[5]*m[11]-m[4]*m[13]*m[11]-m[8]*m[5]*m[15]+m[4]*m[9]*m[15])*s;\n t[ 9] = (m[12]*m[9]*m[3]-m[8]*m[13]*m[3]-m[12]*m[1]*m[11]+m[0]*m[13]*m[11]+m[8]*m[1]*m[15]-m[0]*m[9]*m[15])*s;\n t[10] = (m[4]*m[13]*m[3]-m[12]*m[5]*m[3]+m[12]*m[1]*m[7]-m[0]*m[13]*m[7]-m[4]*m[1]*m[15]+m[0]*m[5]*m[15])*s;\n t[11] = (m[8]*m[5]*m[3]-m[4]*m[9]*m[3]-m[8]*m[1]*m[7]+m[0]*m[9]*m[7]+m[4]*m[1]*m[11]-m[0]*m[5]*m[11])*s;\n\n t[12] = (m[12]*m[9]*m[6]-m[8]*m[13]*m[6]-m[12]*m[5]*m[10]+m[4]*m[13]*m[10]+m[8]*m[5]*m[14]-m[4]*m[9]*m[14])*s;\n t[13] = (m[8]*m[13]*m[2]-m[12]*m[9]*m[2]+m[12]*m[1]*m[10]-m[0]*m[13]*m[10]-m[8]*m[1]*m[14]+m[0]*m[9]*m[14])*s;\n t[14] = (m[12]*m[5]*m[2]-m[4]*m[13]*m[2]-m[12]*m[1]*m[6]+m[0]*m[13]*m[6]+m[4]*m[1]*m[14]-m[0]*m[5]*m[14])*s;\n t[15] = (m[4]*m[9]*m[2]-m[8]*m[5]*m[2]+m[8]*m[1]*m[6]-m[0]*m[9]*m[6]-m[4]*m[1]*m[10]+m[0]*m[5]*m[10])*s;\n}\n\nlet binary = { \n getUint64: getUint64,\n getUint32: getUint32,\n getUint16: getUint16,\n getFloat32: getFloat32,\n vecMul: vecMul,\n matMul: matMul,\n matInv: matInv\n}\n\n\n\n\n\n//# sourceURL=webpack://Nexus3D/./src/Binary.js?\n}"); + +/***/ }), + +/***/ "./src/Cache.js": +/*!**********************!*\ + !*** ./src/Cache.js ***! + \**********************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ Cache: () => (/* binding */ Cache)\n/* harmony export */ });\n\nfunction powerOf2(n) {\n\treturn n && (n & (n - 1)) === 0;\n}\n\nvar targetError = 2.0; //error won't go lower than this if we reach it\nvar maxError = 15; //error won't go over this even if fps is low\nvar minFps = 15;\nvar maxPending = 3;\nvar maxBlocked = 3;\nvar maxReqAttempt = 2;\nvar maxCacheSize = 1024 *(1<<20); \nvar drawBudget = 5*(1<<20);\n\nfunction _Cache() {\n let t = this;\n t.cortopath = '.';\n\tt.frame = 0; //keep track of the time\n\n t.maxCacheSize = maxCacheSize;\n t.minFps = minFps;\n t.currentFps = 0;\n t.targetError = targetError;\n t.currentError = targetError;\n t.maxError = maxError;\n t.realError = 0;\n\n t.pending = 0;\n t.maxPending = 3;\n t.cacheSize = 0;\n t.candidates = []; //list of nodes to be loaded\n t.nodes = new Map(); //for each mesh a list of node ids.\n \n t.last_frametime = 0;\n t.frametime = 0;\n t.end_frametime = 0;\n\n t.debug = { \n verbose : false, //debug messages\n nodes : false, //color each node\n draw : false, //final rendering call disabled\n extract : false, //extraction disabled}\n }\n\n t.totswapped = 0; //in the last frame.\n t.swaprate = 0;\n t.lastupdate = performance.now();\n}\n\n_Cache.prototype = {\n getTargetError: function() { return this.targetError; },\n getMinFps: function() { return this.minFps; },\n setMinFps: function(fps) { this.minFps = fps; },\n getMaxCacheSize: function() { return this.maxCacheSize; },\n setMaxCacheSize: function(size) { this.maxCacheSize = size; },\n setTargetError: function(error) { this.targetError = error; },\n \n loadCorto: function() {\n let corto = new Worker(this.cortopath + '/corto.em.js');\n corto.requests = {};\n corto.count = 0;\n corto.postRequest = function(node) {\n corto.postMessage({ buffer: node.buffer, request:this.count, rgba_colors: true, short_index: true, short_normals: true});\n node.buffer = null;\n this.requests[this.count++] = node;\n }\n corto.onmessage = function(e) {\n var request = e.data.request;\n var node = this.requests[request];\n delete this.requests[request];\n node.model = e.data.model;\n node.cache.readyGeometryNode(node.mesh, node.id, node.model);\n };\n this.corto = corto;\n },\n\n \n beginFrame: function(fps) { //each context has a separate frame count.\n let c = this;\n c.frametime = performance.now();\n let elapsed = c.frametime - c.last_frametime;\n c.last_frametime = c.frametime;\n if(elapsed < 500)\n c.currentFps = 0.9*c.currentFps + 0.1*(1000/elapsed);\n\n fps = c.currentFps;\n\n\t c.frame++;\n\t c.candidates = [];\n\t if(fps && c.minFps) {\n\t\t c.currentFps = fps;\n\t\t const r = c.minFps/fps;\n\t\t if(r > 1.1)\n\t\t\t c.currentError *= 1.05;\n\t\t if(r < 0.9)\n\t\t\t c.currentError *= 0.95;\n\n\t\t c.currentError = Math.max(c.targetError, Math.min(c.maxError, c.currentError));\n\n\t } else\n\t\t c.currentError = c.targetError;\n\n\t c.rendered = 0;\n\t c.realError = 1e20;\n c.totswapped = 0;\n },\n\n endFrame: function() {\n\t this.update();\n },\n\n\n\n requestNode: function(mesh, id) {\n\t mesh.status[id] = 2; \n\n\t \n\t this.cacheSize += mesh.nsize[id];\n\t mesh.reqAttempt[id] = 0;\n\n\t\tif(!this.nodes.has(mesh))\n\t\t\tthis.nodes.set(mesh, new Set());\n\t\tthis.nodes.get(mesh).add(id);\n\n\t this.requestNodeGeometry(mesh, id);\n\t this.requestNodeTexture(mesh, id);\n },\n\n requestNodeGeometry: function(mesh, id) {\n\t\tthis.pending++;\n\t mesh.status[id]++;\n\t\tlet url = mesh.deepzoom ? mesh.baseurl + id + \".nxn\" : mesh.url;\n\t\tlet start = mesh.deepzoom ? 0 : mesh.noffsets[id];\n\t\tlet end = mesh.deepzoom ? 0 : mesh.noffsets[id+1];\n\t let request = mesh.georeq[id] = mesh.httpRequest(url,\n\t\t start,\n\t\t end, \n\t\t ()=>{\n delete mesh.georeq[id];\n this.loadNodeGeometry(request, mesh, id);\n\t\t\t\tthis.pending--;\n },\n\t\t ()=>{\n delete mesh.georeq[id];\n\t\t\t if(this.debug.verbose) console.log(\"Geometry request error!\");\n\t\t\t this.recoverNode(mesh, id, 0);\n\t\t\t\tthis.pending--;\n\t\t },\n\t\t ()=>{\n delete mesh.georeq[id];\n\t\t\t if(this.debug.verbose) console.log(\"Geometry request abort!\");\n\t\t\t this.removeNode(mesh, id);\n\t\t\t\tthis.pending--;\n\t\t },\n\t\t 'arraybuffer'\n );\n },\n\n requestNodeTexture: function(mesh, id) {\n\n\t if(!mesh.vertex.texCoord) return;\n\n\t let tex = mesh.patches[mesh.nfirstpatch[id]*3+2];\n\t mesh.texref[tex]++;\n\t mesh.status[id]++;\n\n\t\tlet url = mesh.deepzoom ? mesh.baseurl + tex + \".jpg\" : mesh.url;\n\t\tlet start = mesh.deepzoom ? 0 : mesh.textures[tex];\n\t\tlet end = mesh.deepzoom ? 0 : mesh.textures[tex+1];\n\t let request = mesh.texreq[tex] = mesh.httpRequest(url,\n\t\t start,\n\t\t end,\n\t\t ()=>{ \n delete mesh.texreq[tex];\n this.loadNodeTexture(request, mesh, id, tex); \n\n },\n\t\t ()=>{\n delete mesh.texreq[tex];\n\t\t \tif(this.debug.verbose) console.log(\"Texture request error!\");\n\t\t\t this.recoverNode(mesh, id, 1);\n\t\t },\n\t\t ()=>{\n delete mesh.texreq[tex];\n\t\t \tif(this.debug.verbose) console.log(\"Texture request abort!\");\n\t\t \tthis.removeNode(mesh, id);\n\t\t },\n\t\t 'blob'\n\t );\n },\n\n recoverNode: function(mesh, id, mode) {\n\t if(mesh.status[id] == 0) return;\n\n\t mesh.status[id]--;\n\n let t = this;\n\n\t if(mesh.reqAttempt[id] > maxReqAttempt) {\n\t\t if(this.debug.verbose) console.log(\"Max request limit for \" + m.url + \" node: \" + n);\n\t\t t.removeNode(mesh, id);\n\t\t return;\n\t }\n\n\t mesh.reqAttempt[id]++;\n\n\t switch (mode){\n\t\tcase 0:\n\t\t t.requestNodeGeometry(mesh, id);\n\t\t if(this.debug.verbose) console.log(\"Recovering geometry for \" + m.url + \" node: \" + n);\n\t\t break;\n\t\tcase 1:\n\t\t\tt.requestNodeTexture(mesh, id);\n\t\t\tif(this.debug.verbose) console.log(\"Recovering texture for \" + m.url + \" node: \" + n);\n\t\t\tbreak;\n\t }\n },\n\n loadNodeGeometry: function(request, mesh, id) {\n\t if(mesh.status[id] == 0) return;\n \n\t if(!mesh.compressed)\n\t\t this.readyGeometryNode(mesh, id, request.response);\n\t else {\n if(!this.corto) this.loadCorto();\n\t\t this.corto.postRequest( { mesh:mesh, id:id, buffer: request.response, cache: this });\n }\n },\n\n\n loadNodeTexture: function(request, mesh, id, texid) {\n if(mesh.status[id] == 0) {\n throw \"Should not load texture twice\";\n }\n\n\t let blob = request.response;\n let callback = (img) => {\n if(mesh.status[id] == 0) //call was aborted.\n return;\n mesh.createTexture(texid, img);\n\n\t\t mesh.status[id]--;\n\n if(mesh.status[id] == 2)\n this.readyNode(mesh, id);\n\t\t }\n\n if(typeof createImageBitmap != 'undefined') {\n\t\t\ttry {\n\t\t\t\tcreateImageBitmap(blob, { imageOrientation: 'flipY' }).then(callback);\n\t\t\t} catch(error) { //old version of firefox do not support options (but flip anyway)\n\t\t\t\tcreateImageBitmap(blob).then(callback);\n\t\t\t}\n\n } else { //fallback for IOS\n var urlCreator = window.URL || window.webkitURL;\n var img = document.createElement('img');\n img.onerror = function(e) { console.log(\"Texture loading error!\"); };\n img.src = urlCreator.createObjectURL(blob);\n \n img.onload = function() {\n urlCreator.revokeObjectURL(img.src);\n callback(img);\n }\n }\n },\n\n removeNode: function(mesh, id) {\n this.nodes.get(mesh).delete(id);\n\n if(mesh.status[id] == 0) {\n\t\t\tif(this.debug.verbose)\n\t\t\t\tconsole.log(\"Double remove due to abort.\");\n return; //TODO\n\t\t}\n\n\t mesh.status[id] = 0;\n\t if (id in mesh.georeq && mesh.georeq[id].readyState != 4) {\n mesh.georeq[id].abort();\n delete mesh.georeq[id];\n\t\t //this.pending--;\n\t }\n mesh.availableNodes--;\n this.cacheSize -= mesh.nsize[id];\n mesh.deleteNodeGeometry(id);\n\n\t if(!mesh.vertex.texCoord) return;\n\n\t const tex = mesh.patches[mesh.nfirstpatch[id]*3+2]; //TODO assuming one texture per node\n\n\t if (tex in mesh.texreq && mesh.texreq[tex].readyState != 4) {\n mesh.texreq[tex].abort();\n delete mesh.texreq[tex];\n }\n\n\t mesh.texref[tex]--;\n \tif(mesh.texref[tex] == 0) {\n mesh.deleteTexture(tex);\n }\n },\n\n readyGeometryNode: function(mesh, id, buffer) {\n if(mesh.status[id] == 0) //call was aborted\n return;\n const nv = mesh.nvertices[id];\n const nf = mesh.nfaces[id];\n\t let geometry = {};\n\n\t \n\t if(!mesh.corto) {\n\t\t geometry.index = new Uint16Array(buffer, nv*mesh.vsize, nf*3);\n geometry.position = new Float32Array(buffer, 0, nv*3);\n\n\t\t var off = nv*12;\n\t\t if(mesh.vertex.texCoord) {\n geometry.uv = new Float32Array(buffer, off, nv*2);\n off += nv*8;\n }\n if(mesh.vertex.normal) {\n\t\t\t\tgeometry.normal = new Int16Array(buffer, off, nv*3);\n off += nv*6;\n\t\t\t}\n\n if(mesh.vertex.color) {\n geometry.color = new Uint8Array(buffer, off, nv*4);\n off += nv*4;\n\t\t\t}\n\n\t\t\n\t } else {\n geometry = buffer;\n\t }\n\n\t //if(nf == 0)\n \t//\tscramble(nv, v, no, co);\n\n mesh.createNodeGeometry(id, geometry);\n\t mesh.status[id]--;\n\n\t if(mesh.status[id] == 2) {\n this.readyNode(mesh, id);\n\t }\n },\n\n //the node is finished, add to cache, and update counters\n readyNode: function(mesh, id) {\n\n\t mesh.status[id]--;\n if(mesh.status[id] != 1) throw \"A ready node should have status ==1\"\n\n\t\t\n\t\tmesh.reqAttempt[id] = 0;\n //this.pending--;\n mesh.createNode(id);\n\t\tmesh.availableNodes++;\n\n for(let callback of mesh.onUpdate)\n callback();\n this.update();\n },\n\n flush: function(mesh) {\n if(!this.nodes.has(mesh)) return;\n\n for(let id of this.nodes.get(mesh))\n this.removeNode(mesh, id);\n this.nodes.delete(mesh);\n }, \n\n update: function() {\n if(this.pending >= maxPending)\n return;\n\n //the best candidate has the highest error\n let best = null;\n for(let c of this.candidates) {\n if(c.mesh.status[c.id] == 0 && (!best || c.error > best.error)) \n best = c;\n\t }\n if(!best) return;\n\n\t// record amount of data transfer per second.\n /*\n let now = performance.now();\n if(Math.floor(now/1000) > Math.floor(this.lastupdate/1000)) { //new second\n this.swaprate = (this.totswapped/1000)/(now - this.lastupdate); //transfer in mb/s\n if(this.debug.verbose)\n console.log(\"Memory loaded in GPU: \", this.swaprate);\n this.totswapped = 0;\n this.lastupdate = now;\n }\n */\n \n \n //make room for new nodes!\n \n\t while(this.cacheSize > this.maxCacheSize) {\n let worst = null;\n \n //find node with smallest error in cache and remove it if worse than the best candidate.\n for(let [mesh, ids] of this.nodes) {\n for(let id of ids) {\n //we need to recompute the errors for the cache, as if not traversed doesn't get updated.\n let error = mesh.errors[id];\n let frame = mesh.frames[id];\n if( !worst || error < worst.error) {\n worst = { id: id, frame: frame, error: error, mesh: mesh }\n }\n }\n }\n if(!worst || worst.error >= best.error*0.9) {\n //(worst.frame + 30 >= best.frame && )) //dont' remove if the best candidate is not good enogh\n return;\n }\n\t\t this.removeNode(worst.mesh, worst.id);\n\t }\n this.totswapped += best.mesh.nsize[best.id];\n this.candidates = this.candidates.filter(e => e.mesh == best.mesh && e.id == best.id);\n \tthis.requestNode(best.mesh, best.id);\n\t this.update(); //try again.\n }\n};\n\nlet Cache = new _Cache;\n\n\n\n\n//# sourceURL=webpack://Nexus3D/./src/Cache.js?\n}"); + +/***/ }), + +/***/ "./src/Mesh.js": +/*!*********************!*\ + !*** ./src/Mesh.js ***! + \*********************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ Mesh: () => (/* binding */ Mesh)\n/* harmony export */ });\n/* harmony import */ var _Binary_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./Binary.js */ \"./src/Binary.js\");\n\n\n/* MESH DEFINITION */\nlet Debug = { verbose: true };\n\nlet glP = WebGLRenderingContext.prototype;\nlet attrGlMap = [glP.NONE, glP.BYTE, glP.UNSIGNED_BYTE, glP.SHORT, glP.UNSIGNED_SHORT, glP.INT, glP.UNSIGNED_INT, glP.FLOAT, glP.DOUBLE];\nlet attrSizeMap = [0, 1, 1, 2, 2, 4, 4, 4, 8];\n\n//All addresses in the file are n*256 so, 256 * 2^32 is the max size of a Nxs file \nvar padding = 256;\n\nlet Mesh = function(url) {\n var t = this;\n t.isReady = false;\n t.onLoad = [];\n t.onUpdate = [];\n t.reqAttempt = 0;\n t.georeq = {}; //keeps track of existing httprequests\n t.texreq = {};\n t.frame = 0; //last time this mesh was traversed in rendering.\n t.availableNodes = 0\n if(url)\n t.open(url);\n}\n\nMesh.prototype = {\n open: function(url) {\n let mesh = this;\n mesh.url = url;\n mesh.httpRequest(url,\n 0,\n 88,\n function() {\n if(Debug.verbose) console.log(\"Loading header for \" + mesh.url);\n let view = new DataView(this.response);\n view.offset = 0;\n mesh.reqAttempt++;\n const header = mesh.importHeader(view);\n if(!header) {\n if(Debug.verbose) console.log(\"Empty header!\");\n if(mesh.reqAttempt < maxReqAttempt) mesh.open(mesh.url + '?' + Math.random()); // BLINK ENGINE CACHE BUG PATCH\n return;\n }\n mesh.reqAttempt = 0;\n for(let i in header)\n mesh[i] = header[i];\n mesh.vertex = mesh.signature.vertex;\n mesh.face = mesh.signature.face;\n mesh.renderMode = mesh.face.index?[\"FILL\", \"POINT\"]:[\"POINT\"];\n mesh.compressed = (mesh.signature.flags & (2 | 4)); //meco or corto\n mesh.meco = (mesh.signature.flags & 2);\n mesh.corto = (mesh.signature.flags & 4);\n\n\t\t\t\tmesh.deepzoom = (mesh.signature.flags & 8);\n\t\t\t\tif(mesh.deepzoom)\n\t\t\t\t\tmesh.baseurl = url.substr(0, url.length -4) + '_files/';\n\n\n mesh.requestIndex();\n },\n function() { console.log(\"Open request error!\");},\n function() { console.log(\"Open request abort!\");}\n );\n },\n\n httpRequest: function(url, start, end, load, error, abort, type) {\n if(!type) type = 'arraybuffer';\n var r = new XMLHttpRequest();\n r.open('GET', url, true);\n r.responseType = type;\n\t\tif(end)\n\t\t\tr.setRequestHeader(\"Range\", \"bytes=\" + start + \"-\" + (end -1));\n r.onload = function(){\n switch (this.status){\n case 0:\n//\t\t\t\t\tconsole.log(\"0 response: server unreachable.\");//returned in chrome for local files\n\t\t\t\t\terror();\n\t\t\t\t\tbreak;\n case 206:\n//\t\t\t\t\tconsole.log(\"206 response: partial content loaded.\");\n load.bind(this)();\n break;\n case 200:\n//\t\t\t\t\tconsole.log(\"200 response: server does not support byte range requests.\");\n\t\t\t\t\tif(end == 0)\n\t\t\t\t\t\tload.bind(this)();\n\t\t\t\t\telse\n\t\t\t\t\t\terror();\n\t\t\t\t\tbreak;\n }\n };\n r.onerror = error;\n r.onabort = abort;\n r.send();\n return r;\n },\n\n requestIndex: function() {\n var mesh = this;\n var end = 88 + mesh.nodesCount*44 + mesh.patchesCount*12 + mesh.texturesCount*68;\n mesh.httpRequest(this.url,\n 88,\n end,\n function() { if(Debug.verbose) console.log(\"Loading index for \" + mesh.url); mesh.handleIndex(this.response); },\n function() { console.log(\"Index request error!\");},\n function() { console.log(\"Index request abort!\");}\n );\n },\n\n handleIndex: function(buffer) {\n let t = this;\n let view = new DataView(buffer);\n view.offset = 0;\n\n const n = t.nodesCount;\n\n t.noffsets = new Uint32Array(n);\n t.nvertices = new Uint32Array(n);\n t.nfaces = new Uint32Array(n);\n t.nerrors = new Float32Array(n);\n t.nspheres = new Float32Array(n*5);\n t.nsize = new Float32Array(n);\n t.nfirstpatch = new Uint32Array(n);\n\n for(let i = 0; i < n; i++) {\n t.noffsets[i] = padding*(0,_Binary_js__WEBPACK_IMPORTED_MODULE_0__.getUint32)(view); //offset\n t.nvertices[i] = (0,_Binary_js__WEBPACK_IMPORTED_MODULE_0__.getUint16)(view); //verticesCount\n t.nfaces[i] = (0,_Binary_js__WEBPACK_IMPORTED_MODULE_0__.getUint16)(view); //facesCount\n t.nerrors[i] = (0,_Binary_js__WEBPACK_IMPORTED_MODULE_0__.getFloat32)(view);\n view.offset += 8; //skip cone\n for(let k = 0; k < 5; k++)\n t.nspheres[i*5+k] = (0,_Binary_js__WEBPACK_IMPORTED_MODULE_0__.getFloat32)(view); //sphere + tight\n t.nfirstpatch[i] = (0,_Binary_js__WEBPACK_IMPORTED_MODULE_0__.getUint32)(view); //first patch\n }\n t.sink = n -1;\n\n t.patches = new Uint32Array(view.buffer, view.offset, t.patchesCount*3); //noded, lastTriangle, texture\n t.nroots = t.nodesCount;\n for(let j = 0; j < t.nroots; j++) {\n for(let i = t.nfirstpatch[j]; i < t.nfirstpatch[j+1]; i++) {\n if(t.patches[i*3] < t.nroots)\n t.nroots = t.patches[i*3];\n }\n }\n\n view.offset += t.patchesCount*12;\n\n t.textures = new Uint32Array(t.texturesCount);\n t.texref = new Uint32Array(t.texturesCount);\n for(let i = 0; i < t.texturesCount; i++) {\n t.textures[i] = padding*(0,_Binary_js__WEBPACK_IMPORTED_MODULE_0__.getUint32)(view);\n view.offset += 16*4; //skip proj matrix\n }\n\n t.vsize = 12 + (t.vertex.normal?6:0) + (t.vertex.color?4:0) + (t.vertex.texCoord?8:0);\n t.fsize = 6;\n\n //problem: I have no idea how much space a texture is needed in GPU. 10x factor assumed.\n let tmptexsize = new Uint32Array(n-1);\n let tmptexcount = new Uint32Array(n-1);\n for(let i = 0; i < n-1; i++) {\n for(let p = t.nfirstpatch[i]; p != t.nfirstpatch[i+1]; p++) {\n let tex = t.patches[p*3+2];\n tmptexsize[i] += t.textures[tex+1] - t.textures[tex];\n tmptexcount[i]++;\n }\n t.nsize[i] = t.vsize*t.nvertices[i] + t.fsize*t.nfaces[i];\n }\n for(let i = 0; i < n-1; i++) {\n t.nsize[i] += 10*tmptexsize[i]/tmptexcount[i];\n }\n\n t.status = new Uint8Array(n); //0 for none, 1 for ready, 2+ for waiting data\n t.frames = new Uint32Array(n);\n t.errors = new Float32Array(n); //biggest error of instances\n t.reqAttempt = new Uint8Array(n);\n \n t.isReady = true;\n for(let callback of t.onLoad)\n callback(this);\n },\n\n importAttribute: function(view) {\n let a = {};\n a.type = view.getUint8(view.offset++, true);\n a.size = view.getUint8(view.offset++, true);\n a.glType = attrGlMap[a.type];\n a.normalized = a.type < 7;\n a.stride = attrSizeMap[a.type]*a.size;\n if(a.size == 0) return null;\n return a;\n },\n\n importElement: function(view) {\n let e = [];\n for(let i = 0; i < 8; i++)\n e[i] = this.importAttribute(view);\n return e;\n },\n\n importVertex: function(view) {\t//enum POSITION, NORMAL, COLOR, TEXCOORD, DATA0\n const e = this.importElement(view);\n let color = e[2];\n if(color) {\n color.type = 2; //unsigned byte\n color.glType = attrGlMap[2];\n }\n return { position: e[0], normal: e[1], color: e[2], texCoord: e[3], data: e[4] };\n },\n\n //enum INDEX, NORMAL, COLOR, TEXCOORD, DATA0\n importFace: function(view) {\n const e = this.importElement(view);\n let color = e[2];\n if(color) {\n color.type = 2; //unsigned byte\n color.glType = attrGlMap[2];\n }\n return { index: e[0], normal: e[1], color: e[2], texCoord: e[3], data: e[4] };\n },\n\n importSignature: function(view) {\n let s = {};\n s.vertex = this.importVertex(view);\n s.face = this.importFace(view);\n s.flags = (0,_Binary_js__WEBPACK_IMPORTED_MODULE_0__.getUint32)(view);\n return s;\n },\n\n importHeader: function(view) {\n const magic = (0,_Binary_js__WEBPACK_IMPORTED_MODULE_0__.getUint32)(view);\n if(magic != 0x4E787320) return null;\n let h = {};\n h.version = (0,_Binary_js__WEBPACK_IMPORTED_MODULE_0__.getUint32)(view);\n h.verticesCount = (0,_Binary_js__WEBPACK_IMPORTED_MODULE_0__.getUint64)(view);\n h.facesCount = (0,_Binary_js__WEBPACK_IMPORTED_MODULE_0__.getUint64)(view);\n h.signature = this.importSignature(view);\n h.nodesCount = (0,_Binary_js__WEBPACK_IMPORTED_MODULE_0__.getUint32)(view);\n h.patchesCount = (0,_Binary_js__WEBPACK_IMPORTED_MODULE_0__.getUint32)(view);\n h.texturesCount = (0,_Binary_js__WEBPACK_IMPORTED_MODULE_0__.getUint32)(view);\n h.sphere = {\n center: [(0,_Binary_js__WEBPACK_IMPORTED_MODULE_0__.getFloat32)(view), (0,_Binary_js__WEBPACK_IMPORTED_MODULE_0__.getFloat32)(view), (0,_Binary_js__WEBPACK_IMPORTED_MODULE_0__.getFloat32)(view)],\n radius: (0,_Binary_js__WEBPACK_IMPORTED_MODULE_0__.getFloat32)(view)\n };\n return h;\n },\n //OVERRIDE THESE METHOS\n\n //assemble node and geometry\n createNode: function(id) {},\n\n createNodeGeometry: function(id, data) {},\n deleteNodeGeometry: function(id) {},\n\n createTexture: function(id, image) {},\n deleteTexture: function(id) {},\n};\n\n\n\n\n\n//# sourceURL=webpack://Nexus3D/./src/Mesh.js?\n}"); + +/***/ }), + +/***/ "./src/Nexus3D.js": +/*!************************!*\ + !*** ./src/Nexus3D.js ***! + \************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ Cache: () => (/* reexport safe */ _Cache_js__WEBPACK_IMPORTED_MODULE_3__.Cache),\n/* harmony export */ Nexus3D: () => (/* binding */ Nexus3D)\n/* harmony export */ });\n/* harmony import */ var three__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! three */ \"three\");\n/* harmony import */ var three__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(three__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _Traversal_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./Traversal.js */ \"./src/Traversal.js\");\n/* harmony import */ var _Mesh_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./Mesh.js */ \"./src/Mesh.js\");\n/* harmony import */ var _Cache_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./Cache.js */ \"./src/Cache.js\");\n\n\n\n\n\nclass Nexus3D extends three__WEBPACK_IMPORTED_MODULE_0__.Mesh {\n\n\tconstructor(url, renderer, options = {}) {\n\n\t\tsuper();\n\n\t\tthis.patchWebGLRenderer(renderer);\n\n\t\tObject.assign(this, {\n\t\t\tisNXS: true,\n\t\t\ttype:'NXS',\n\t\t\turl: url,\n\t\t\tgl: renderer.getContext(),\n\t\t\tXRMode: false, //set to true to avoid flickering due to double eye rendering.\n\t\t\tmaterial: null,\n\n\t\t\tautoUpdate: true,\n\t\t\tmesh: new _Mesh_js__WEBPACK_IMPORTED_MODULE_2__.Mesh(),\n\t\t\tvbo: [],\n\t\t\tibo: [],\n\t\t\tvao: [],\n\t\t\ttextures: [],\n\t\t\tattributes: {}, //here we store the uniform attributes of the shader.\n\n\t\t\tbasemesh: null, //highest level of the nexus, for picking\n\t\t});\n\n\t\tif('material' in options)\n\t\t\tthis.material = options.material;\n\t\tif(!this.material) \n\t\t\tthis.material = new three__WEBPACK_IMPORTED_MODULE_0__.MeshStandardMaterial();\n\t\t\t\n\t\tfor(let call of ['onLoad', 'onUpdate', 'onProgress']) {\n\t\t\tthis['_' + call] = [];\n\t\t\tif(call in options)\n\t\t\t\tthis['_' + call].push(options[call])\n\t\t}\n\n\t\tif(this.url) {\n\t\t\tif(typeof url == 'object') {\n\t\t\t\tthis.nxs = this.url;\n\t\t\t\tthis.nxs.onLoad.push((m) => { \n\t\t\t\t\tthis.mesh = this.nxs.mesh;\n\t\t\t\t\tthis.traversal = this.nxs.traversal;\n\t\t\t\t\tthis.cache = this.nxs.cache;\n\t\t\t\t\tthis.vbo = this.nxs.vbo;\n\t\t\t\t\tthis.ibo = this.nxs.ibo;\n\t\t\t\t\tthis.vao = this.nxs.vao;\n\t\t\t\t\tthis.textures = this.nxs.textures;\n\t\t\t\t\tthis.onLoadCallback(this); \n\t\t\t\t});\n\t\t\t} else\n\t\t\t\tthis.open(this.url);\n\t\t}\n\t}\n\n\tcopy(source) {\n\t\tthree__WEBPACK_IMPORTED_MODULE_0__.Object3D.prototype.copy.call(this, source, false);\n\t\tthrow new Error(\"Can't really copy a Nexus3D object.\");\n\t\t// removed by dead control flow\n\n\t}\n\n\t\n\topen(url) {\n\t\tlet t = this;\n\t\tthis.mesh.open(url);\n\t\tthis.mesh.createNode = (id) => { };\n\t\tthis.mesh.createNodeGeometry = (id, geometry) => { t.createNodeGeometry(id, geometry); };\n\t\tthis.mesh.createTexture = (id, image) => { t.createTexture(id, image); };\n\t\tthis.mesh.deleteNodeGeometry = (id) => { t.deleteNodeGeometry(id); };\n\t\tthis.mesh.deleteTexture = (id) => { t.deleteTexture(id); };\n\t\tthis.mesh.onLoad.push(() => { t.onLoadCallback(); });\n\t\tthis.mesh.onUpdate.push(() => { \n\t\t\tfor(let callback of t._onUpdate) callback(this); \n\t\t\tfor(let callback of t._onProgress) callback(this, this.mesh.availableNodes, this.mesh.nodesCount); \n\t\t});\n\n\t\tthis.traversal = new _Traversal_js__WEBPACK_IMPORTED_MODULE_1__.Traversal();\n\t\tthis.cache = _Cache_js__WEBPACK_IMPORTED_MODULE_3__.Cache; //new Cache();\n\t\tthis.textures = {}; \n\t}\n\n\tset onLoad(callback) {\n\t\tthis._onLoad.push(callback);\n\t}\n\n\tset onUpdate(callback) {\n\t\tthis._onUpdate.push(callback);\n\t}\n\n\tset onProgress(callback) {\n\t\tthis._onProgress.push(callback);\n\t}\n\n\t//TODO this is not really needed, we might just conform to THREEJS standard of updating.\n\t/*set material(material) {\n\t\tthis.material = material;\n\t\tthis.material.needsUpdate = true;\n\t}*/\n\t\n\tupdateMaterials() {\n\t\tif(this.material.map !== false && this.mesh.vertex.texCoord)\n\t\t\tthis.material.map = this.material_texture;\n\n\t\tif(this.mesh.vertex.color)\n\t\t\tthis.material.vertexColors = true; \n\t\tthis.material.needsUpdate = true; \n\t}\n\n\tonLoadCallback() {\n\t\tconst c = this.mesh.sphere.center;\n\t\tconst center = new three__WEBPACK_IMPORTED_MODULE_0__.Vector3(c[0], c[1], c[2]);\n\t\tconst radius = this.mesh.sphere.radius;\n\t\tthis.boundingSphere = new three__WEBPACK_IMPORTED_MODULE_0__.Sphere(center, radius);\n\n\t\tvar geometry = new three__WEBPACK_IMPORTED_MODULE_0__.BufferGeometry();\n\n\t\tgeometry.setAttribute( 'position', new three__WEBPACK_IMPORTED_MODULE_0__.BufferAttribute(new Float32Array(3), 3));\n\t\t\n\t\tif(this.mesh.vertex.normal)\n\t\t\tgeometry.setAttribute( 'normal', new three__WEBPACK_IMPORTED_MODULE_0__.BufferAttribute(new Float32Array(3), 3));\n\t\tif(this.mesh.vertex.color)\n\t\t\tgeometry.setAttribute( 'color', new three__WEBPACK_IMPORTED_MODULE_0__.BufferAttribute(new Float32Array(4), 4));\n\t\tif(this.mesh.vertex.texCoord)\n\t\t\tgeometry.setAttribute( 'uv', new three__WEBPACK_IMPORTED_MODULE_0__.BufferAttribute(new Float32Array(2), 2));\n\n\t\tif(this.mesh.vertex.texCoord) {\n\t\t\tthis.material_texture = new three__WEBPACK_IMPORTED_MODULE_0__.DataTexture( new Uint8Array([1, 1, 1, 1]), 1, 1, three__WEBPACK_IMPORTED_MODULE_0__.RGBAFormat );\n\t\t\tthis.material_texture.needsUpdate = true;\n\t\t}\n\n\t\tthis.updateMaterials();\n\t\tthis.geometry = geometry;\n\t\t\n\t\tthis.frustumCulled = false;\n\t\t\t\n\t\tfor(let callback of this._onLoad)\n\t\t\tcallback(this);\n\t}\n\n\trenderBufferDirect(renderer, scene, camera, geometry, material, group) {\n\t\tlet s = new three__WEBPACK_IMPORTED_MODULE_0__.Vector4();\n\t\trenderer.getViewport(s);\n\n\t \t//object modelview is multiplied by camera during rendering, we need to do it here for visibility computations\n\t\tthis.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, this.matrixWorld );\n\t\tif(s.x == 0 && this.XRMode) { //hack to only traverse on the left eye\n\t\t\tthis.traversal.updateView(s, camera.projectionMatrix.elements, this.modelViewMatrix.elements);\n\t\tthis.instance_errors = this.traversal.traverse(this.mesh, this.cache);\n\t\t}\n\t\t//threejs increments version when setting neeedsUpdate\n\t\t/*if(this.material.version > 0) {\n\t\t\tthis.updateMaterials();\n\t\t\tthis.material.version = 0;\n\t\t\tfor(let callback of this.onUpdate) \n\t\t\t\tcallback(this); \n\t\t}*/\n\t\tlet gl = this.gl;\n\t\tlet program = gl.getParameter(gl.CURRENT_PROGRAM);\n\n\t\t//TODO these calls could be cached saving attrs per each material.\n\t\tlet attr = this.attributes;\n\t\tattr.position = gl.getAttribLocation(program, \"position\");\n\t\tattr.normal = gl.getAttribLocation(program, \"normal\");\n\t\tattr.color = gl.getAttribLocation(program, \"color\");\n\t\tattr.uv = gl.getAttribLocation(program, \"uv\");\n\t\tattr.size = gl.getUniformLocation(program, \"size\");\n\t\tattr.scale = gl.getUniformLocation(program, \"scale\");\n\n\t\tlet map_location = gl.getUniformLocation(program, \"map\"); \n\t\tattr.map = map_location ? gl.getUniform(program, map_location) : null;\n\t\t\n\t\n\t\t//hack to detect if threejs using point or triangle shaders\n\t\t//instance.mode = attr.size ? \"POINT\" : \"FILL\";\n\t\t//if(attr.size != -1) \n\t\t// instance.pointsize = material.size;\n\t\n\t\t//can't find docs or code on how material.scale is computed in threejs.\n\t\t//if(attr.scale != -1)\n\t\t// instance.pointscale = 2.0;\n\n\t\tthis.setVisibility();\n\t}\n\n\tsetVisibility() {\n\t\t//set visibile what is visible!\n\t\tlet t = this.traversal;\n\t\tlet m = this.mesh;\n\t\n\t\tif(!m.isReady)\n\t\t\treturn;\n\t\tlet rendered = 0;\n\t\t\n\t\tlet attr = this.attributes;\n\t\tlet mesh = this.mesh;\n\t\tlet gl = this.gl;\n\t\tlet gl2 = gl instanceof WebGL2RenderingContext;\n\t\tlet state = attr.uv + 10*attr.color + 100*attr.normal;\n\t\t\t\n\t\tfor(let id = 0; id < m.nodesCount; id++) {\n// let err = m.nerrors[id];\n\n\t\t\tif(!t.selected[id]) continue;\n\n\t\t\t//check for children: if all are selected, bail out.\n\t\t\t{\n\t\t\t\tlet visible = false;\n\t\t\t\tlet offset = 0;\n\t\t\t\tlet end = 0;\n\t\t\t\tlet last = m.nfirstpatch[id+1]-1;\n\t\t\t\tfor (var p = m.nfirstpatch[id]; p < m.nfirstpatch[id+1]; ++p) {\n\t\t\t\t\tvar child = m.patches[p*3];\n\t\n\t\t\t\t\tif(!t.selected[child]) {\n\t\t\t\t\t\tvisible = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif(!visible) continue;\n\t\t\t}\n\n\t\t\tvar sp = m.nspheres;\n\t\t\tvar off = id*5;\n\t\t\tif(!t.isVisible(sp[off], sp[off+1], sp[off+2], sp[off+4])) //tight radius\n\t\t\t\tcontinue;\n\t\n\t\t\tlet doBind = true;\n\n\t\t\tif(gl2) {\n\t\t\t\tif(state in this.vao[id])\n\t\t\t\t\tdoBind = false;\n\t\t\t\telse\n\t\t\t\t\tthis.vao[id][state] = gl.createVertexArray();\n\t\t\t\t\t\n\t\t\t \tgl.bindVertexArray(this.vao[id][state]);\n\t\t\t}\n\t\t\t\n\t\t\tif(doBind) { \n\t\t\t\tgl.bindBuffer(gl.ARRAY_BUFFER, this.vbo[id]);\n\t\t\t \tgl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.ibo[id]);\n\t\n\t\t\t\tgl.vertexAttribPointer(attr.position, 3, gl.FLOAT, false, 12, 0);\n\t\t\t\tgl.enableVertexAttribArray(attr.position);\n\t\n\t\t\t\tlet nv = this.mesh.nvertices[id];\n\t\t\t\tlet offset = nv*12;\n\t\n\t\t\t\tif(mesh.vertex.texCoord) {\n\t\t\t\t\tif(attr.uv >= 0) {\n\t\t\t\t\t\tgl.vertexAttribPointer(attr.uv, 2, gl.FLOAT, false, 8, offset);\n\t\t\t\t\t\tgl.enableVertexAttribArray(attr.uv);\n\t\t\t\t\t}\n\t\t\t\t\toffset += nv*8;\n\t\t\t\t}\n\t\t\t\tif(mesh.vertex.color) {\n\t\t\t\t\tif(attr.color >= 0) {\n\t\t\t\t\t\tgl.vertexAttribPointer(attr.color, 4, gl.UNSIGNED_BYTE, true, 4, offset);\n\t\t\t\t\t\tgl.enableVertexAttribArray(attr.color);\n\t\t\t\t\t}\n\t\t\t\t\toffset += nv*4;\n\t\t\t\t}\n\t\t\t\tif(mesh.vertex.normal) {\n\t\t\t\t\tif(attr.normal >= 0) {\n\t\t\t\t\t\tgl.vertexAttribPointer(attr.normal, 3, gl.SHORT, true, 6, offset);\n\t\t\t\t\t\tgl.enableVertexAttribArray(attr.normal);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif(this.cache.debug.nodes) {\n\t\t\t\t\tgl.disableVertexAttribArray(attr.color);\n\n\t\t\t\t\tvar error = this.instance_errors[id]; //this.mesh.errors[id];\n\t\t\t\t\tvar palette = [\n\t\t\t\t\t\t[1, 1, 1, 1], //white\n\t\t\t\t\t\t[1, 1, 1, 1], //white\n\t\t\t\t\t\t[0, 1, 0, 1], //green\n\t\t\t\t\t\t[0, 1, 1, 1], //cyan\n\t\t\t\t\t\t[1, 1, 0, 1], //yellow\n\t\t\t\t\t\t[1, 0, 1, 1], //magenta\n\t\t\t\t\t\t[1, 0, 0, 1] //red\n\t\t\t\t\t];\n\t\t\t\t\tlet w = Math.min(5.99, Math.max(0, Math.log2(error)/2));\n\t\t\t\t\tlet low = Math.floor(w);\n\t\t\t\t\tw -= low;\n\t\t\t\t\tlet color = [];\n\t\t\t\t\tfor( let k = 0; k < 4; k++)\n\t\t\t\t\t\tcolor[k] = palette[low][k]*(1-w) + palette[low+1][k]*w;\n\t\t\t\t\t\n\t\t\t\t\tgl.vertexAttrib4fv(attr.color, color);\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.cache.realError = Math.min(this.mesh.errors[id], this.cache.realError);\n\t\t\t\n\t\t\tlet offset = 0;\n\t\t\tlet end = 0;\n\t\t\tlet last = m.nfirstpatch[id+1]-1;\n\t\t\tfor (let p = m.nfirstpatch[id]; p < m.nfirstpatch[id+1]; ++p) {\n\t\t\t\tlet child = m.patches[p*3];\n\t\n\t\t\t\tif(!t.selected[child]) {\n\t\t\t\t\tend = m.patches[p*3+1];\n\t\t\t\t\tif(p < last) //we join patches if possible.\n\t\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif(end > offset) {\n\t\t\t\t\tif(m.vertex.texCoord && attr.uv >= 0) {\n\t\t\t\t\t\tvar tex = m.patches[p*3+2];\n\t\t\t\t\t\tif(tex != -1) { //bind texture\n\t\t\t\t\t\t\tvar texid = this.textures[tex];\n\t\t\t\t\t\t\tgl.activeTexture(gl.TEXTURE0 + attr.map);\n\t\t\t\t\t\t\tgl.bindTexture(gl.TEXTURE_2D, texid);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tlet mode = this.material.wireframe ? gl.LINE_STRIP : gl.TRIANGLES;\n\n\t\t\t\t\tgl.drawElements(mode, (end - offset) * 3, gl.UNSIGNED_SHORT, offset * 6);\n\t\t\t\t\trendered += end - offset;\n\t\t\t\t}\n\t\t\t\toffset = m.patches[p*3+1];\n\t\t\t}\n\t\t}\n\t\tthis.cache.rendered += rendered;\n\t}\n\n\tcreateNodeGeometry(id, data) {\n\t\tlet m = this.mesh;\n\t\tvar nv = m.nvertices[id];\n\t\tvar nf = m.nfaces[id];\n\t\tlet indices = data.index;\n\t\tlet vertices = new ArrayBuffer(nv*m.vsize);\n\t\tvar position = new Float32Array(vertices, 0, nv*3);\n\t\tposition.set(data.position);\n\t\tvar off = nv*12;\n\t\tif(m.vertex.texCoord) {\n\t\t\tvar uv = new Float32Array(vertices, off, nv*2);\n\t\t\tuv.set(data.uv);\n\t\t\toff += nv*8;\n\t\t}\n\t\tif(m.vertex.color) {\n\t\t\tvar color = new Uint8Array(vertices, off, nv*4);\n\t\t\tcolor.set(data.color);\n\t\t\toff += nv*4;\n\t\t}\n\t\tif(m.vertex.normal) {\n\t\t\tvar normal = new Int16Array(vertices, off, nv*3);\n\t\t\tnormal.set(data.normal);\n\t\t\toff += nv*6;\n\t\t}\n\t\t\n\t\t//needed for approximate picking.\n\t\tif(id < this.mesh.nroots) {\n\t\t\tlet basegeometry = new three__WEBPACK_IMPORTED_MODULE_0__.BufferGeometry();\n\t\t\tbasegeometry.setAttribute( 'position', new three__WEBPACK_IMPORTED_MODULE_0__.BufferAttribute(data.position, 3 ) );\n\t\t\tbasegeometry.setAttribute( 'normal', new three__WEBPACK_IMPORTED_MODULE_0__.BufferAttribute(data.normal, 3 ) );\n\t\t\tbasegeometry.setIndex(new three__WEBPACK_IMPORTED_MODULE_0__.BufferAttribute( data.index, 1 ) );\n\n\t\t\tthis.basemesh = new three__WEBPACK_IMPORTED_MODULE_0__.Mesh(basegeometry, this.material);\n\t\t\tthis.basemesh.visible = false;\n\t\t\tthis.add(this.basemesh);\n\t\t}\n\t\t\n\t\tvar gl = this.gl\n\t\tthis.vao[id] = {}; //one for each attrib combination in use (casting shadows for example).\n\t\tgl.bindVertexArray(null);\n\t\t\n\t\tvar vbo = this.vbo[id] = gl.createBuffer();\n\t\tgl.bindBuffer(gl.ARRAY_BUFFER, vbo);\n\t\tgl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);\n\t\tvar ibo = this.ibo[id] = gl.createBuffer();\n\t\tgl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo);\n\t\tgl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);\n\t}\n\n\tcreateTexture(id, image) {\n\t\tlet gl = this.gl;\n\t\tvar flip = gl.getParameter(gl.UNPACK_FLIP_Y_WEBGL);\n\t\tgl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);\n\t\tlet tex = this.textures[id] = gl.createTexture();\n\t\tgl.bindTexture(gl.TEXTURE_2D, tex);\n\n\t\t//TODO some textures might be alpha only! save space\n\t\tlet internalFormat;\n\t\tif (this.material && this.material.map && this.material.map.colorSpace === three__WEBPACK_IMPORTED_MODULE_0__.LinearSRGBColorSpace) {\n\t\t\tinternalFormat = gl.RGBA;\n\t\t} else if (typeof gl.SRGB8_ALPHA8 !== 'undefined') {\n\t\t\tinternalFormat = gl.SRGB8_ALPHA8;\n\t\t} else {\n\t\t\tinternalFormat = gl.RGBA;\n\t\t}\n\t\tgl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, gl.RGBA, gl.UNSIGNED_BYTE, image);\n\t\tgl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n\t\tgl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n\t\tgl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);\n\n\t\tfunction powerOf2(n) { return n && (n & (n - 1)) === 0; }\n\t\tif(!(gl instanceof WebGLRenderingContext) || (powerOf2(image.width) && powerOf2(image.height))) {\n\t\t\tgl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST_MIPMAP_LINEAR);\n\t\t\tgl.generateMipmap(gl.TEXTURE_2D);\n\t\t} else {\n\t\t\tgl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);\n\t\t}\n\n\t\tgl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flip); \n\t}\n\t//schedule for removal of this node( might not want to delete it in the middle of something.\n\t//TODO check if this is really needed!\n\tdeleteNodeGeometry(id) {\n\t\tthis.gl.deleteBuffer(this.vbo[id]);\n\t\tthis.gl.deleteBuffer(this.ibo[id]);\n\n\t\tthis.vbo[id] = this.ibo[id] = null;\n\t\tif(this.vao[id]) //node might have been loaded and unloaded before a rendering\n\t\t\tfor(const [state, vao] of Object.entries(this.vao[id]))\n\t\t\t\tthis.gl.deleteVertexArray(vao);\n\t\tthis.vao[id] = null;\n\t}\n\n\tdeleteTexture(tex) {\n\t\tif(!this.textures[tex])\n\t\t\treturn;\n\t\t//\tthrow \"Deleting missing texture!\"\n\n\t\tthis.gl.deleteTexture(this.textures[tex]);\n\t\tthis.textures[tex] = 0;\n\t}\n\n\tflush() {\n\t\tthis.cache.flush(this.mesh);\n\t}\n\n\tdispose() {\n\t\tthis.flush();\n\t\tfor(let child of this.children)\n\t\t\tchild.geometry.dispose();\n\t}\n\n\ttoJSON(meta) {\n\t\tthrow new Error(\"Can't convert to json.\");\n\t}\n\n\tpatchWebGLRenderer(renderer) {\n\t\tif(renderer.nexusPatched) return;\n\t\tlet f = renderer.renderBufferDirect;\n\t\trenderer.renderBufferDirect = ( camera, scene, geometry, material, object, group) => { \n\t\t\tf( camera, scene, geometry, material, object, group );\n\t\t\tif ( object.renderBufferDirect)\n\t\t\t\tobject.renderBufferDirect(renderer, scene, camera, geometry, material, group);\n\t\t};\n\t\trenderer.originalRender = renderer.render;\n\t\trenderer.render = (scene, camera) => {\n\t\t\t_Cache_js__WEBPACK_IMPORTED_MODULE_3__.Cache.beginFrame(30);\n\t\t\trenderer.originalRender(scene, camera);\n\t\t\t_Cache_js__WEBPACK_IMPORTED_MODULE_3__.Cache.endFrame();\n\t\t}\n\t\trenderer.nexusPatched = true;\n\t} \n\n}\n\n\n\n\n\n\n\n//# sourceURL=webpack://Nexus3D/./src/Nexus3D.js?\n}"); + +/***/ }), + +/***/ "./src/PriorityQueue.js": +/*!******************************!*\ + !*** ./src/PriorityQueue.js ***! + \******************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ PriorityQueue: () => (/* binding */ PriorityQueue)\n/* harmony export */ });\n \n function PriorityQueue(max_length) {\n this.error = new Float32Array(max_length);\n this.data = new Int32Array(max_length);\n this.size = 0;\n}\n\nPriorityQueue.prototype = {\n push: function(data, error) {\n this.data[this.size] = data;\n this.error[this.size] = error;\n this.bubbleUp(this.size);\n this.size++;\n },\n\n pop: function() {\n var result = this.data[0];\n this.size--;\n if(this.size > 0) {\n this.data[0] = this.data[this.size];\n this.error[0] = this.error[this.size];\n this.sinkDown(0);\n }\n return result;\n },\n\n bubbleUp: function(n) {\n var data = this.data[n];\n var error = this.error[n];\n while (n > 0) {\n var pN = ((n+1)>>1) -1;\n var pError = this.error[pN];\n if(pError > error)\n break;\n //swap\n this.data[n] = this.data[pN];\n this.error[n] = pError;\n this.data[pN] = data;\n this.error[pN] = error;\n n = pN;\n }\n },\n\n sinkDown: function(n) {\n var data = this.data[n];\n var error = this.error[n];\n\n while(true) {\n var child2N = (n + 1) * 2;\n var child1N = child2N - 1;\n var swap = -1;\n if (child1N < this.size) {\n var child1Error = this.error[child1N];\n if(child1Error > error)\n swap = child1N;\n }\n if (child2N < this.size) {\n var child2Error = this.error[child2N];\n if (child2Error > (swap == -1 ? error : child1Error))\n swap = child2N;\n }\n\n if (swap == -1) break;\n\n this.data[n] = this.data[swap];\n this.error[n] = this.error[swap];\n this.data[swap] = data;\n this.error[swap] = error;\n n = swap;\n }\n }\n};\n\n\n\n\n\n\n//# sourceURL=webpack://Nexus3D/./src/PriorityQueue.js?\n}"); + +/***/ }), + +/***/ "./src/Traversal.js": +/*!**************************!*\ + !*** ./src/Traversal.js ***! + \**************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ Traversal: () => (/* binding */ Traversal)\n/* harmony export */ });\n/* harmony import */ var _Binary_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./Binary.js */ \"./src/Binary.js\");\n/* harmony import */ var _PriorityQueue_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./PriorityQueue.js */ \"./src/PriorityQueue.js\");\n\n\n\nfunction Traversal() {\n let t = this;\n t.maxBlocked = 30;\n\n t.modelMatrix = new Float32Array(16);\n t.viewMatrix = new Float32Array(16);\n t.projectionMatrix = new Float32Array(16);\n t.modelView = new Float32Array(16);\n t.modelViewInv = new Float32Array(16);\n t.modelViewProj = new Float32Array(16);\n t.modelViewProjInv = new Float32Array(16);\n t.planes = new Float32Array(24);\n t.viewport = new Float32Array(4);\n t.viewpoint = new Float32Array(4);\n}\n\nTraversal.prototype = {\n updateView: function(viewport, projection, modelView) {\n let t = this;\n\n for(let i = 0; i < 16; i++) {\n t.projectionMatrix[i] = projection[i];\n t.modelView[i] = modelView[i];\n }\n for(let i = 0; i < 4; i++)\n t.viewport[i] = viewport[i];\n\n (0,_Binary_js__WEBPACK_IMPORTED_MODULE_0__.matMul)(t.projectionMatrix, t.modelView, t.modelViewProj);\n (0,_Binary_js__WEBPACK_IMPORTED_MODULE_0__.matInv)(t.modelViewProj, t.modelViewProjInv);\n\n (0,_Binary_js__WEBPACK_IMPORTED_MODULE_0__.matInv)(t.modelView, t.modelViewInv);\n t.viewpoint[0] = t.modelViewInv[12];\n t.viewpoint[1] = t.modelViewInv[13];\n t.viewpoint[2] = t.modelViewInv[14];\n t.viewpoint[3] = 1.0;\n\n\n const m = t.modelViewProj;\n const mi = t.modelViewProjInv;\n let p = t.planes;\n\n //frustum planes Ax + By + Cz + D = 0;\n p[0] = m[0] + m[3]; p[1] = m[4] + m[7]; p[2] = m[8] + m[11]; p[3] = m[12] + m[15]; //left\n p[4] = -m[0] + m[3]; p[5] = -m[4] + m[7]; p[6] = -m[8] + m[11]; p[7] = -m[12] + m[15]; //right\n p[8] = m[1] + m[3]; p[9] = m[5] + m[7]; p[10] = m[9] + m[11]; p[11] = m[13] + m[15]; //bottom\n p[12] = -m[1] + m[3]; p[13] = -m[5] + m[7]; p[14] = -m[9] + m[11]; p[15] = -m[13] + m[15]; //top\n p[16] = -m[2] + m[3]; p[17] = -m[6] + m[7]; p[18] = -m[10] + m[11]; p[19] = -m[14] + m[15]; //near\n p[20] = -m[2] + m[3]; p[21] = -m[6] + m[7]; p[22] = -m[10] + m[11]; p[23] = -m[14] + m[15]; //far\n\n //normalize planes to get also correct distances\n for(let i = 0; i < 24; i+= 4) {\n let l = Math.sqrt(p[i]*p[i] + p[i+1]*p[i+1] + p[i+2]*p[i+2]);\n p[i] /= l; p[i+1] /= l; p[i+2] /= l; p[i+3] /= l;\n }\n \n //side is M'(1,0,0,1) - M'(-1,0,0,1) and they lie on the planes\n const r3 = mi[3] + mi[15];\n const r0 = (mi[0] + mi[12 ])/r3;\n const r1 = (mi[1] + mi[13 ])/r3;\n const r2 = (mi[2] + mi[14 ])/r3;\n\n const l3 = -mi[3] + mi[15];\n const l0 = (-mi[0] + mi[12 ])/l3 - r0;\n const l1 = (-mi[1] + mi[13 ])/l3 - r1;\n const l2 = (-mi[2] + mi[14 ])/l3 - r2;\n\n const side = Math.sqrt(l0*l0 + l1*l1 + l2*l2);\n\n //center of the scene is M'*(0, 0, 0, 1)\n const c0 = mi[12]/mi[15] - t.viewpoint[0];\n const c1 = mi[13]/mi[15] - t.viewpoint[1];\n const c2 = mi[14]/mi[15] - t.viewpoint[2];\n const dist = Math.sqrt(c0*c0 + c1*c1 + c2*c2);\n\n const resolution = (2*side/dist)/ t.viewport[2];\n t.currentResolution == resolution ? t.sameResolution = true : t.sameResolution = false;\n t.currentResolution = resolution;\n },\n\n traverse : function (mesh, cache) {\n let t = this;\n t.mesh = mesh;\n// if(Debug.extract == true)\n // return;\n\n if(!mesh.isReady) return;\n \n const n = mesh.nodesCount;\n t.visited = new Uint8Array(n);\n t.blocked = new Uint8Array(n);\n t.selected = new Uint8Array(n);\n\n t.frame = cache.frame;\n\n t.instance_errors = new Float32Array(n);\n\n if(t.frame > mesh.frame) { //clean the errors.\n mesh.errors = new Float32Array(n); \n mesh.frame = t.frame;\n }\n\n t.visitQueue = new _PriorityQueue_js__WEBPACK_IMPORTED_MODULE_1__.PriorityQueue(n);\n for(var i = 0; i < mesh.nroots; i++)\n t.insertNode(i);\n \n t.currentError = cache.currentError;\n t.drawSize = 0;\n t.nblocked = 0;\n\n var requested = 0;\n while(t.visitQueue.size && t.nblocked < t.maxBlocked) {\n var error = t.visitQueue.error[0];\n var id = t.visitQueue.pop();\n\n //if not loaded and the queue is not full add to the candidates.\n if (mesh.status[id] == 0 && requested < cache.maxPending) {\n cache.candidates.push({id: id, mesh:mesh, frame:t.frame, error:error});\n requested++;\n }\n /* we don't want to stop as soon as a node is not availabe, because the nodes are sorted by error, \n this could cause a large drop in quality elsewere.\n we still need to mark all children as blocked, to prevent including them in the cut of the dag. */\n var blocked = t.blocked[id] || !t.expandNode(id, error);\n if (blocked)\n t.nblocked++;\n else {\n t.selected[id] = 1;\n //cache.realError = Math.min(error, cache.realError);\n }\n t.insertChildren(id, blocked);\n }\n\n //update remaining errors in the cache\n if(cache.nodes.has(mesh)) {\n for(let id of cache.nodes.get(mesh)) {\n let error = t.nodeError(id);\n if(t.instance_errors[id] == 0) {\n t.instance_errors[i] = error;\n mesh.errors[id] = Math.max(mesh.errors[id], error);\n }\n }\n }\n\n t.mesh = null;\n return t.instance_errors;\n },\n\n insertNode: function (node) {\n let t = this;\n t.visited[node] = 1;\n\n const error = t.nodeError(node);\n\n t.instance_errors[node] = error;\n t.mesh.errors[node] = Math.max(error, t.mesh.errors[node]);\n t.mesh.frames[node] = t.frame;\n\n// if(node > 0 && error < t.currentError) return; //2% speed TODO check if needed\n\n t.visitQueue.push(node, error);\n },\n\n insertChildren : function (node, block) {\n let t = this;\n for(let i = t.mesh.nfirstpatch[node]; i < t.mesh.nfirstpatch[node+1]; ++i) {\n const child = t.mesh.patches[i*3];\n if (child == t.mesh.sink) return;\n if (block) t.blocked[child] = 1;\n if (!t.visited[child])\n t.insertNode(child);\n }\n },\n\n expandNode : function (node, error) {\n let t = this;\n if(node > 0 && error < t.currentError) {\n//\t\t\tconsole.log(\"Reached error\", error, t.currentError);\n return false;\n }\n\n if(t.drawSize > t.drawBudget) {\n//\t\t\tconsole.log(\"Reached drawsize\", t.drawSize, t.drawBudget);\n return false;\n }\n\n if(t.mesh.status[node] != 1) { //not ready\n//\t\t\tconsole.log(\"Node \" + node + \" still not loaded (cache?)\");\n return false;\n }\n\n const sp = t.mesh.nspheres;\n const off = node*5;\n if(t.isVisible(sp[off], sp[off+1], sp[off+2], sp[off+3])) //expanded radius\n t.drawSize += t.mesh.nvertices[node]*0.8;\n //we are adding half of the new faces. (but we are using the vertices so *2)\n\n return true;\n },\n\n nodeError : function (n, tight) {\n let t = this;\n const spheres = t.mesh.nspheres;\n const b = t.viewpoint;\n const off = n*5;\n const cx = spheres[off+0];\n const cy = spheres[off+1];\n const cz = spheres[off+2];\n let r = spheres[off+3];\n if(tight)\n r = spheres[off+4];\n const d0 = b[0] - cx;\n const d1 = b[1] - cy;\n const d2 = b[2] - cz;\n let dist = Math.sqrt(d0*d0 + d1*d1 + d2*d2) - r;\n if (dist < 0.1)\n dist = 0.1;\n\n //resolution is how long is a pixel at distance 1.\n let error = t.mesh.nerrors[n]/(t.currentResolution*dist); //in pixels\n\n //causes flickering due to things popping in and out of visibility, causes a huge resorting.\n /*if (!t.isVisible(cx, cy, cz, spheres[off+4]))\n error /= 100.0; */\n\n //more stable, at least it's continuous.\n let d = t.distance(cx, cy, cz, spheres[off+4]);\n if(d < -r) {\n error /= 101.0;\n } else if(d < 0) {\n error /= 1 -( d/r)*100.0\n } \n return error;\n },\n\n distance: function(x, y, z, r) {\n const p = this.planes;\n let min_distance = 1e20;\n for (let i = 0; i < 24; i +=4) {\n let d = p[i]*x + p[i+1]*y + p[i+2]*z + p[i+3] + r;\n if(d < min_distance)\n min_distance = d;\n }\n return min_distance;\n },\n\n isVisible : function (x, y, z, r) {\n const p = this.planes;\n for (let i = 0; i < 24; i +=4) {\n if(p[i]*x + p[i+1]*y + p[i+2]*z + p[i+3] + r < 0) //plane is ax+by+cz+d = 0; \n return false;\n }\n return true;\n }\n}; \n\n\n\n\n//# sourceURL=webpack://Nexus3D/./src/Traversal.js?\n}"); + +/***/ }), + +/***/ "three": +/*!**************************************************************************************!*\ + !*** external {"commonjs":"three","commonjs2":"three","amd":"three","root":"THREE"} ***! + \**************************************************************************************/ +/***/ ((module) => { + +module.exports = __WEBPACK_EXTERNAL_MODULE_three__; + +/***/ }) + +/******/ }); +/************************************************************************/ +/******/ // The module cache +/******/ var __webpack_module_cache__ = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ // Check if module is in cache +/******/ var cachedModule = __webpack_module_cache__[moduleId]; +/******/ if (cachedModule !== undefined) { +/******/ return cachedModule.exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = __webpack_module_cache__[moduleId] = { +/******/ // no module.id needed +/******/ // no module.loaded needed +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/************************************************************************/ +/******/ /* webpack/runtime/compat get default export */ +/******/ (() => { +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = (module) => { +/******/ var getter = module && module.__esModule ? +/******/ () => (module['default']) : +/******/ () => (module); +/******/ __webpack_require__.d(getter, { a: getter }); +/******/ return getter; +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/define property getters */ +/******/ (() => { +/******/ // define getter functions for harmony exports +/******/ __webpack_require__.d = (exports, definition) => { +/******/ for(var key in definition) { +/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { +/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); +/******/ } +/******/ } +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/hasOwnProperty shorthand */ +/******/ (() => { +/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) +/******/ })(); +/******/ +/******/ /* webpack/runtime/make namespace object */ +/******/ (() => { +/******/ // define __esModule on exports +/******/ __webpack_require__.r = (exports) => { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ })(); +/******/ +/************************************************************************/ +/******/ +/******/ // startup +/******/ // Load entry module and return exports +/******/ // This entry module can't be inlined because the eval devtool is used. +/******/ var __webpack_exports__ = __webpack_require__("./src/Nexus3D.js"); +/******/ +/******/ return __webpack_exports__; +/******/ })() +; +}); \ No newline at end of file diff --git a/nexus3d/build/nexus3D.min.js b/nexus3d/build/nexus3D.min.js index da14e87a..9402fc9a 100644 --- a/nexus3d/build/nexus3D.min.js +++ b/nexus3d/build/nexus3D.min.js @@ -1 +1 @@ -!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("three")):"function"==typeof define&&define.amd?define(["exports","three"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).Nexus3D={},e.THREE)}(this,(function(e,t){"use strict";function r(e){if(e&&e.__esModule)return e;var t=Object.create(null);return e&&Object.keys(e).forEach((function(r){if("default"!==r){var o=Object.getOwnPropertyDescriptor(e,r);Object.defineProperty(t,r,o.get?o:{enumerable:!0,get:function(){return e[r]}})}})),t.default=e,Object.freeze(t)}var o=r(t);function s(e){var t=e.getUint32(e.offset,!0),r=e.getUint32(e.offset+4,!0);return e.offset+=8,1*r+t}function i(e){var t=e.getUint32(e.offset,!0);return e.offset+=4,t}function a(e){var t=e.getUint16(e.offset,!0);return e.offset+=2,t}function h(e){var t=e.getFloat32(e.offset,!0);return e.offset+=4,t}function l(e,t){var r=1/(e[12]*e[9]*e[6]*e[3]-e[8]*e[13]*e[6]*e[3]-e[12]*e[5]*e[10]*e[3]+e[4]*e[13]*e[10]*e[3]+e[8]*e[5]*e[14]*e[3]-e[4]*e[9]*e[14]*e[3]-e[12]*e[9]*e[2]*e[7]+e[8]*e[13]*e[2]*e[7]+e[12]*e[1]*e[10]*e[7]-e[0]*e[13]*e[10]*e[7]-e[8]*e[1]*e[14]*e[7]+e[0]*e[9]*e[14]*e[7]+e[12]*e[5]*e[2]*e[11]-e[4]*e[13]*e[2]*e[11]-e[12]*e[1]*e[6]*e[11]+e[0]*e[13]*e[6]*e[11]+e[4]*e[1]*e[14]*e[11]-e[0]*e[5]*e[14]*e[11]-e[8]*e[5]*e[2]*e[15]+e[4]*e[9]*e[2]*e[15]+e[8]*e[1]*e[6]*e[15]-e[0]*e[9]*e[6]*e[15]-e[4]*e[1]*e[10]*e[15]+e[0]*e[5]*e[10]*e[15]);t[0]=(e[9]*e[14]*e[7]-e[13]*e[10]*e[7]+e[13]*e[6]*e[11]-e[5]*e[14]*e[11]-e[9]*e[6]*e[15]+e[5]*e[10]*e[15])*r,t[1]=(e[13]*e[10]*e[3]-e[9]*e[14]*e[3]-e[13]*e[2]*e[11]+e[1]*e[14]*e[11]+e[9]*e[2]*e[15]-e[1]*e[10]*e[15])*r,t[2]=(e[5]*e[14]*e[3]-e[13]*e[6]*e[3]+e[13]*e[2]*e[7]-e[1]*e[14]*e[7]-e[5]*e[2]*e[15]+e[1]*e[6]*e[15])*r,t[3]=(e[9]*e[6]*e[3]-e[5]*e[10]*e[3]-e[9]*e[2]*e[7]+e[1]*e[10]*e[7]+e[5]*e[2]*e[11]-e[1]*e[6]*e[11])*r,t[4]=(e[12]*e[10]*e[7]-e[8]*e[14]*e[7]-e[12]*e[6]*e[11]+e[4]*e[14]*e[11]+e[8]*e[6]*e[15]-e[4]*e[10]*e[15])*r,t[5]=(e[8]*e[14]*e[3]-e[12]*e[10]*e[3]+e[12]*e[2]*e[11]-e[0]*e[14]*e[11]-e[8]*e[2]*e[15]+e[0]*e[10]*e[15])*r,t[6]=(e[12]*e[6]*e[3]-e[4]*e[14]*e[3]-e[12]*e[2]*e[7]+e[0]*e[14]*e[7]+e[4]*e[2]*e[15]-e[0]*e[6]*e[15])*r,t[7]=(e[4]*e[10]*e[3]-e[8]*e[6]*e[3]+e[8]*e[2]*e[7]-e[0]*e[10]*e[7]-e[4]*e[2]*e[11]+e[0]*e[6]*e[11])*r,t[8]=(e[8]*e[13]*e[7]-e[12]*e[9]*e[7]+e[12]*e[5]*e[11]-e[4]*e[13]*e[11]-e[8]*e[5]*e[15]+e[4]*e[9]*e[15])*r,t[9]=(e[12]*e[9]*e[3]-e[8]*e[13]*e[3]-e[12]*e[1]*e[11]+e[0]*e[13]*e[11]+e[8]*e[1]*e[15]-e[0]*e[9]*e[15])*r,t[10]=(e[4]*e[13]*e[3]-e[12]*e[5]*e[3]+e[12]*e[1]*e[7]-e[0]*e[13]*e[7]-e[4]*e[1]*e[15]+e[0]*e[5]*e[15])*r,t[11]=(e[8]*e[5]*e[3]-e[4]*e[9]*e[3]-e[8]*e[1]*e[7]+e[0]*e[9]*e[7]+e[4]*e[1]*e[11]-e[0]*e[5]*e[11])*r,t[12]=(e[12]*e[9]*e[6]-e[8]*e[13]*e[6]-e[12]*e[5]*e[10]+e[4]*e[13]*e[10]+e[8]*e[5]*e[14]-e[4]*e[9]*e[14])*r,t[13]=(e[8]*e[13]*e[2]-e[12]*e[9]*e[2]+e[12]*e[1]*e[10]-e[0]*e[13]*e[10]-e[8]*e[1]*e[14]+e[0]*e[9]*e[14])*r,t[14]=(e[12]*e[5]*e[2]-e[4]*e[13]*e[2]-e[12]*e[1]*e[6]+e[0]*e[13]*e[6]+e[4]*e[1]*e[14]-e[0]*e[5]*e[14])*r,t[15]=(e[4]*e[9]*e[2]-e[8]*e[5]*e[2]+e[8]*e[1]*e[6]-e[0]*e[9]*e[6]-e[4]*e[1]*e[10]+e[0]*e[5]*e[10])*r}function u(e){this.error=new Float32Array(e),this.data=new Int32Array(e),this.size=0}function d(){let e=this;e.maxBlocked=30,e.modelMatrix=new Float32Array(16),e.viewMatrix=new Float32Array(16),e.projectionMatrix=new Float32Array(16),e.modelView=new Float32Array(16),e.modelViewInv=new Float32Array(16),e.modelViewProj=new Float32Array(16),e.modelViewProjInv=new Float32Array(16),e.planes=new Float32Array(24),e.viewport=new Float32Array(4),e.viewpoint=new Float32Array(4)}u.prototype={push:function(e,t){this.data[this.size]=e,this.error[this.size]=t,this.bubbleUp(this.size),this.size++},pop:function(){var e=this.data[0];return this.size--,this.size>0&&(this.data[0]=this.data[this.size],this.error[0]=this.error[this.size],this.sinkDown(0)),e},bubbleUp:function(e){for(var t=this.data[e],r=this.error[e];e>0;){var o=(e+1>>1)-1,s=this.error[o];if(s>r)break;this.data[e]=this.data[o],this.error[e]=s,this.data[o]=t,this.error[o]=r,e=o}},sinkDown:function(e){for(var t=this.data[e],r=this.error[e];;){var o=2*(e+1),s=o-1,i=-1;if(sr&&(i=s)}if(o(-1==i?r:n)&&(i=o);if(-1==i)break;this.data[e]=this.data[i],this.error[e]=this.error[i],this.data[i]=t,this.error[i]=r,e=i}}},d.prototype={updateView:function(e,t,r){let o=this;for(let e=0;e<16;e++)o.projectionMatrix[e]=t[e],o.modelView[e]=r[e];for(let t=0;t<4;t++)o.viewport[t]=e[t];var s,i,n;s=o.projectionMatrix,i=o.modelView,(n=o.modelViewProj)[0]=s[0]*i[0]+s[4]*i[1]+s[8]*i[2]+s[12]*i[3],n[1]=s[1]*i[0]+s[5]*i[1]+s[9]*i[2]+s[13]*i[3],n[2]=s[2]*i[0]+s[6]*i[1]+s[10]*i[2]+s[14]*i[3],n[3]=s[3]*i[0]+s[7]*i[1]+s[11]*i[2]+s[15]*i[3],n[4]=s[0]*i[4]+s[4]*i[5]+s[8]*i[6]+s[12]*i[7],n[5]=s[1]*i[4]+s[5]*i[5]+s[9]*i[6]+s[13]*i[7],n[6]=s[2]*i[4]+s[6]*i[5]+s[10]*i[6]+s[14]*i[7],n[7]=s[3]*i[4]+s[7]*i[5]+s[11]*i[6]+s[15]*i[7],n[8]=s[0]*i[8]+s[4]*i[9]+s[8]*i[10]+s[12]*i[11],n[9]=s[1]*i[8]+s[5]*i[9]+s[9]*i[10]+s[13]*i[11],n[10]=s[2]*i[8]+s[6]*i[9]+s[10]*i[10]+s[14]*i[11],n[11]=s[3]*i[8]+s[7]*i[9]+s[11]*i[10]+s[15]*i[11],n[12]=s[0]*i[12]+s[4]*i[13]+s[8]*i[14]+s[12]*i[15],n[13]=s[1]*i[12]+s[5]*i[13]+s[9]*i[14]+s[13]*i[15],n[14]=s[2]*i[12]+s[6]*i[13]+s[10]*i[14]+s[14]*i[15],n[15]=s[3]*i[12]+s[7]*i[13]+s[11]*i[14]+s[15]*i[15],l(o.modelViewProj,o.modelViewProjInv),l(o.modelView,o.modelViewInv),o.viewpoint[0]=o.modelViewInv[12],o.viewpoint[1]=o.modelViewInv[13],o.viewpoint[2]=o.modelViewInv[14],o.viewpoint[3]=1;const a=o.modelViewProj,h=o.modelViewProjInv;let u=o.planes;u[0]=a[0]+a[3],u[1]=a[4]+a[7],u[2]=a[8]+a[11],u[3]=a[12]+a[15],u[4]=-a[0]+a[3],u[5]=-a[4]+a[7],u[6]=-a[8]+a[11],u[7]=-a[12]+a[15],u[8]=a[1]+a[3],u[9]=a[5]+a[7],u[10]=a[9]+a[11],u[11]=a[13]+a[15],u[12]=-a[1]+a[3],u[13]=-a[5]+a[7],u[14]=-a[9]+a[11],u[15]=-a[13]+a[15],u[16]=-a[2]+a[3],u[17]=-a[6]+a[7],u[18]=-a[10]+a[11],u[19]=-a[14]+a[15],u[20]=-a[2]+a[3],u[21]=-a[6]+a[7],u[22]=-a[10]+a[11],u[23]=-a[14]+a[15];for(let e=0;e<24;e+=4){let t=Math.sqrt(u[e]*u[e]+u[e+1]*u[e+1]+u[e+2]*u[e+2]);u[e]/=t,u[e+1]/=t,u[e+2]/=t,u[e+3]/=t}const d=h[3]+h[15],c=(h[0]+h[12])/d,f=(h[1]+h[13])/d,m=(h[2]+h[14])/d,p=-h[3]+h[15],x=(-h[0]+h[12])/p-c,v=(-h[1]+h[13])/p-f,b=(-h[2]+h[14])/p-m,g=Math.sqrt(x*x+v*v+b*b),w=h[12]/h[15]-o.viewpoint[0],y=h[13]/h[15]-o.viewpoint[1],A=h[14]/h[15]-o.viewpoint[2],E=2*g/Math.sqrt(w*w+y*y+A*A)/o.viewport[2];o.currentResolution==E?o.sameResolution=!0:o.sameResolution=!1,o.currentResolution=E},traverse:function(e,t){let r=this;if(r.mesh=e,!e.isReady)return;const o=e.nodesCount;r.visited=new Uint8Array(o),r.blocked=new Uint8Array(o),r.selected=new Uint8Array(o),r.frame=t.frame,r.instance_errors=new Float32Array(o),r.frame>e.frame&&(e.errors=new Float32Array(o),e.frame=r.frame),r.visitQueue=new u(o);for(var s=0;s0&&tr.drawBudget)return!1;if(1!=r.mesh.status[e])return!1;const o=r.mesh.nspheres,s=5*e;return r.isVisible(o[s],o[s+1],o[s+2],o[s+3])&&(r.drawSize+=.8*r.mesh.nvertices[e]),!0},nodeError:function(e,t){let r=this;const o=r.mesh.nspheres,s=r.viewpoint,i=5*e,n=o[i+0],a=o[i+1],h=o[i+2];let l=o[i+3];t&&(l=o[i+4]);const u=s[0]-n,d=s[1]-a,c=s[2]-h;let f=Math.sqrt(u*u+d*d+c*c)-l;f<.1&&(f=.1);let m=r.mesh.nerrors[e]/(r.currentResolution*f),p=r.distance(n,a,h,o[i+4]);return p<-l?m/=101:p<0&&(m/=1-p/l*100),m},distance:function(e,t,r,o){const s=this.planes;let i=1e20;for(let n=0;n<24;n+=4){let a=s[n]*e+s[n+1]*t+s[n+2]*r+s[n+3]+o;a1.1&&(t.currentError*=1.05),r<.9&&(t.currentError*=.95),t.currentError=Math.max(t.targetError,Math.min(t.maxError,t.currentError))}else t.currentError=t.targetError;t.rendered=0,t.realError=1e20,t.totswapped=0},endFrame:function(){this.update()},requestNode:function(e,t){e.status[t]=2,this.cacheSize+=e.nsize[t],e.reqAttempt[t]=0,this.nodes.has(e)||this.nodes.set(e,new Set),this.nodes.get(e).add(t),this.requestNodeGeometry(e,t),this.requestNodeTexture(e,t)},requestNodeGeometry:function(e,t){this.pending++,e.status[t]++;let r=e.deepzoom?e.baseurl+t+".nxn":e.url,o=e.deepzoom?0:e.noffsets[t],s=e.deepzoom?0:e.noffsets[t+1],i=e.georeq[t]=e.httpRequest(r,o,s,(()=>{delete e.georeq[t],this.loadNodeGeometry(i,e,t),this.pending--}),(()=>{delete e.georeq[t],this.debug.verbose&&console.log("Geometry request error!"),this.recoverNode(e,t,0),this.pending--}),(()=>{delete e.georeq[t],this.debug.verbose&&console.log("Geometry request abort!"),this.removeNode(e,t),this.pending--}),"arraybuffer")},requestNodeTexture:function(e,t){if(!e.vertex.texCoord)return;let r=e.patches[3*e.nfirstpatch[t]+2];e.texref[r]++,e.status[t]++;let o=e.deepzoom?e.baseurl+r+".jpg":e.url,s=e.deepzoom?0:e.textures[r],i=e.deepzoom?0:e.textures[r+1],n=e.texreq[r]=e.httpRequest(o,s,i,(()=>{delete e.texreq[r],this.loadNodeTexture(n,e,t,r)}),(()=>{delete e.texreq[r],this.debug.verbose&&console.log("Texture request error!"),this.recoverNode(e,t,1)}),(()=>{delete e.texreq[r],this.debug.verbose&&console.log("Texture request abort!"),this.removeNode(e,t)}),"blob")},recoverNode:function(e,t,r){if(0==e.status[t])return;e.status[t]--;let o=this;if(e.reqAttempt[t]>2)return this.debug.verbose&&console.log("Max request limit for "+m.url+" node: "+n),void o.removeNode(e,t);switch(e.reqAttempt[t]++,r){case 0:o.requestNodeGeometry(e,t),this.debug.verbose&&console.log("Recovering geometry for "+m.url+" node: "+n);break;case 1:o.requestNodeTexture(e,t),this.debug.verbose&&console.log("Recovering texture for "+m.url+" node: "+n)}},loadNodeGeometry:function(e,t,r){0!=t.status[r]&&(t.compressed?(this.corto||this.loadCorto(),this.corto.postRequest({mesh:t,id:r,buffer:e.response,cache:this})):this.readyGeometryNode(t,r,e.response))},loadNodeTexture:function(e,t,r,o){if(0==t.status[r])throw"Should not load texture twice";let s=e.response,i=e=>{0!=t.status[r]&&(t.createTexture(o,e),t.status[r]--,2==t.status[r]&&this.readyNode(t,r))};if("undefined"!=typeof createImageBitmap)try{createImageBitmap(s,{imageOrientation:"flipY"}).then(i)}catch(e){createImageBitmap(s).then(i)}else{var n=window.URL||window.webkitURL,a=document.createElement("img");a.onerror=function(e){console.log("Texture loading error!")},a.src=n.createObjectURL(s),a.onload=function(){n.revokeObjectURL(a.src),i(a)}}},removeNode:function(e,t){if(this.nodes.get(e).delete(t),0==e.status[t])return void(this.debug.verbose&&console.log("Double remove due to abort."));if(e.status[t]=0,t in e.georeq&&4!=e.georeq[t].readyState&&(e.georeq[t].abort(),delete e.georeq[t]),e.availableNodes--,this.cacheSize-=e.nsize[t],e.deleteNodeGeometry(t),!e.vertex.texCoord)return;const r=e.patches[3*e.nfirstpatch[t]+2];r in e.texreq&&4!=e.texreq[r].readyState&&(e.texreq[r].abort(),delete e.texreq[r]),e.texref[r]--,0==e.texref[r]&&e.deleteTexture(r)},readyGeometryNode:function(e,t,r){if(0==e.status[t])return;const o=e.nvertices[t],s=e.nfaces[t];let i={};if(e.corto)i=r;else{i.index=new Uint16Array(r,o*e.vsize,3*s),i.position=new Float32Array(r,0,3*o);var n=12*o;e.vertex.texCoord&&(i.uv=new Float32Array(r,n,2*o),n+=8*o),e.vertex.normal&&(i.normal=new Int16Array(r,n,3*o),n+=6*o),e.vertex.color&&(i.color=new Uint8Array(r,n,4*o),n+=4*o)}e.createNodeGeometry(t,i),e.status[t]--,2==e.status[t]&&this.readyNode(e,t)},readyNode:function(e,t){if(e.status[t]--,1!=e.status[t])throw"A ready node should have status ==1";e.reqAttempt[t]=0,e.createNode(t),e.availableNodes++;for(let t of e.onUpdate)t();this.update()},flush:function(e){if(this.nodes.has(e)){for(let t of this.nodes.get(e))this.removeNode(e,t);this.nodes.delete(e)}},update:function(){if(this.pending>=3)return;let e=null;for(let t of this.candidates)0==t.mesh.status[t.id]&&(!e||t.error>e.error)&&(e=t);if(e){for(;this.cacheSize>this.maxCacheSize;){let t=null;for(let[e,r]of this.nodes)for(let o of r){let r=e.errors[o],s=e.frames[o];(!t||r=.9*e.error)return;this.removeNode(t.mesh,t.id)}this.totswapped+=e.mesh.nsize[e.id],this.candidates=this.candidates.filter((t=>t.mesh==e.mesh&&t.id==e.id)),this.requestNode(e.mesh,e.id),this.update()}}};let b=new v;class g extends o.Mesh{constructor(e,t,r){if(super(),"function"==typeof t)throw"Nexus3D constructor has changed: Nexus3D(url, renderer, options) where options include: onLoad, onUpdate, onProgress and material";this.patchWebGLRenderer(t),Object.assign(this,{isNXS:!0,type:"NXS",url:e,gl:t.getContext(),material:null,autoUpdate:!0,mesh:new x,vbo:[],ibo:[],vao:[],textures:[],attributes:{},basemesh:null}),"material"in r&&(this.material=r.material),this.material||(this.material=new o.MeshStandardMaterial);for(let e of["onLoad","onUpdate","onProgress"])this["_"+e]=[],e in r&&this["_"+e].push(r[e]);this.url&&("object"==typeof e?(this.nxs=this.url,this.nxs.onLoad.push((e=>{this.mesh=this.nxs.mesh,this.traversal=this.nxs.traversal,this.cache=this.nxs.cache,this.vbo=this.nxs.vbo,this.ibo=this.nxs.ibo,this.vao=this.nxs.vao,this.textures=this.nxs.textures,this.onLoadCallback(this)}))):this.open(this.url))}copy(e){throw Object3D.prototype.copy.call(this,e,!1),"Can't really copy."}open(e){let t=this;this.mesh.open(e),this.mesh.createNode=e=>{},this.mesh.createNodeGeometry=(e,r)=>{t.createNodeGeometry(e,r)},this.mesh.createTexture=(e,r)=>{t.createTexture(e,r)},this.mesh.deleteNodeGeometry=e=>{t.deleteNodeGeometry(e)},this.mesh.deleteTexture=e=>{t.deleteTexture(e)},this.mesh.onLoad.push((()=>{t.onLoadCallback()})),this.mesh.onUpdate.push((()=>{for(let e of t._onUpdate)e(this);for(let e of t._onProgress)e(this,this.mesh.availableNodes,this.mesh.nodesCount)})),this.traversal=new d,this.cache=b,this.textures={}}set onLoad(e){this._onLoad.push(e)}set onUpdate(e){this._onUpdate.push(e)}set onProgress(e){this._onProgress.push(e)}updateMaterials(){!1!==this.material.map&&this.mesh.vertex.texCoord&&(this.material.map=this.material_texture),this.mesh.vertex.color&&(this.material.vertexColors=o.VertexColors),this.material.needsUpdate=!0}onLoadCallback(){const e=this.mesh.sphere.center,t=new o.Vector3(e[0],e[1],e[2]),r=this.mesh.sphere.radius;this.boundingSphere=new o.Sphere(t,r);var s=new o.BufferGeometry;s.setAttribute("position",new o.BufferAttribute(new Float32Array(3),3)),this.mesh.vertex.normal&&s.setAttribute("normal",new o.BufferAttribute(new Float32Array(3),3)),this.mesh.vertex.color&&s.setAttribute("color",new o.BufferAttribute(new Float32Array(4),4)),this.mesh.vertex.texCoord&&s.setAttribute("uv",new o.BufferAttribute(new Float32Array(2),2)),this.mesh.vertex.texCoord&&(this.material_texture=new o.DataTexture(new Uint8Array([1,1,1]),1,1,o.RGBFormat),this.material_texture.needsUpdate=!0),this.updateMaterials(),this.geometry=s,this.frustumCulled=!1;for(let e of this._onLoad)e(this)}renderBufferDirect(e,t,r,s,i,n){let a=new o.Vector2;e.getSize(a),this.modelViewMatrix.multiplyMatrices(r.matrixWorldInverse,this.matrixWorld),this.traversal.updateView([0,0,a.width,a.height],r.projectionMatrix.elements,this.modelViewMatrix.elements),this.instance_errors=this.traversal.traverse(this.mesh,this.cache);let h=this.gl,l=h.getParameter(h.CURRENT_PROGRAM),u=this.attributes;u.position=h.getAttribLocation(l,"position"),u.normal=h.getAttribLocation(l,"normal"),u.color=h.getAttribLocation(l,"color"),u.uv=h.getAttribLocation(l,"uv"),u.size=h.getUniformLocation(l,"size"),u.scale=h.getUniformLocation(l,"scale");let d=h.getUniformLocation(l,"map");u.map=d?h.getUniform(l,d):null,this.setVisibility()}setVisibility(){let e=this.traversal,t=this.mesh;if(!t.isReady)return;let r=0,o=this.attributes,s=this.mesh,i=this.gl,n=i instanceof WebGL2RenderingContext,a=o.uv+10*o.color+100*o.normal;for(let x=0;x=0&&(i.vertexAttribPointer(o.uv,2,i.FLOAT,!1,8,t),i.enableVertexAttribArray(o.uv)),t+=8*e),s.vertex.color&&(o.color>=0&&(i.vertexAttribPointer(o.color,4,i.UNSIGNED_BYTE,!0,4,t),i.enableVertexAttribArray(o.color)),t+=4*e),s.vertex.normal&&o.normal>=0&&(i.vertexAttribPointer(o.normal,3,i.SHORT,!0,6,t),i.enableVertexAttribArray(o.normal)),this.cache.debug.nodes){i.disableVertexAttribArray(o.color);var c=this.instance_errors[x],f=[[1,1,1,1],[1,1,1,1],[0,1,0,1],[0,1,1,1],[1,1,0,1],[1,0,1,1],[1,0,0,1]];let e=Math.min(5.99,Math.max(0,Math.log2(c)/2)),t=Math.floor(e);e-=t;let r=[];for(let o=0;o<4;o++)r[o]=f[t][o]*(1-e)+f[t+1][o]*e;i.vertexAttrib4fv(o.color,r)}}this.cache.realError=Math.min(this.mesh.errors[x],this.cache.realError);let b=0,g=0,w=t.nfirstpatch[x+1]-1;for(let s=t.nfirstpatch[x];sb){if(t.vertex.texCoord&&o.uv>=0){var m=t.patches[3*s+2];if(-1!=m){var p=this.textures[m];i.activeTexture(i.TEXTURE0+o.map),i.bindTexture(i.TEXTURE_2D,p)}}let e=this.material.wireframe?i.LINE_STRIP:i.TRIANGLES;i.drawElements(e,3*(g-b),i.UNSIGNED_SHORT,6*b),r+=g-b}b=t.patches[3*s+1]}}}this.cache.rendered+=r}createNodeGeometry(e,t){let r=this.mesh;var s=r.nvertices[e];r.nfaces[e];let i=t.index,n=new ArrayBuffer(s*r.vsize);new Float32Array(n,0,3*s).set(t.position);var a=12*s;r.vertex.texCoord&&(new Float32Array(n,a,2*s).set(t.uv),a+=8*s);r.vertex.color&&(new Uint8Array(n,a,4*s).set(t.color),a+=4*s);r.vertex.normal&&(new Int16Array(n,a,3*s).set(t.normal),a+=6*s);if(e{t(r,o,s,i,n,a),n.renderBufferDirect&&n.renderBufferDirect(e,o,r,s,i,a)},e.originalRender=e.render,e.render=(t,r)=>{b.beginFrame(30),e.originalRender(t,r),b.endFrame()},e.nexusPatched=!0}}e.Cache=b,e.Nexus3D=g,Object.defineProperty(e,"__esModule",{value:!0})})); +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("three")):"function"==typeof define&&define.amd?define(["exports","three"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).Nexus3D={},e.THREE)}(this,function(e,t){"use strict";function r(e){var t=e.getUint32(e.offset,!0),r=e.getUint32(e.offset+4,!0);return e.offset+=8,1*r+t}function o(e){var t=e.getUint32(e.offset,!0);return e.offset+=4,t}function s(e){var t=e.getUint16(e.offset,!0);return e.offset+=2,t}function i(e){var t=e.getFloat32(e.offset,!0);return e.offset+=4,t}function a(e,t){var r=1/(e[12]*e[9]*e[6]*e[3]-e[8]*e[13]*e[6]*e[3]-e[12]*e[5]*e[10]*e[3]+e[4]*e[13]*e[10]*e[3]+e[8]*e[5]*e[14]*e[3]-e[4]*e[9]*e[14]*e[3]-e[12]*e[9]*e[2]*e[7]+e[8]*e[13]*e[2]*e[7]+e[12]*e[1]*e[10]*e[7]-e[0]*e[13]*e[10]*e[7]-e[8]*e[1]*e[14]*e[7]+e[0]*e[9]*e[14]*e[7]+e[12]*e[5]*e[2]*e[11]-e[4]*e[13]*e[2]*e[11]-e[12]*e[1]*e[6]*e[11]+e[0]*e[13]*e[6]*e[11]+e[4]*e[1]*e[14]*e[11]-e[0]*e[5]*e[14]*e[11]-e[8]*e[5]*e[2]*e[15]+e[4]*e[9]*e[2]*e[15]+e[8]*e[1]*e[6]*e[15]-e[0]*e[9]*e[6]*e[15]-e[4]*e[1]*e[10]*e[15]+e[0]*e[5]*e[10]*e[15]);t[0]=(e[9]*e[14]*e[7]-e[13]*e[10]*e[7]+e[13]*e[6]*e[11]-e[5]*e[14]*e[11]-e[9]*e[6]*e[15]+e[5]*e[10]*e[15])*r,t[1]=(e[13]*e[10]*e[3]-e[9]*e[14]*e[3]-e[13]*e[2]*e[11]+e[1]*e[14]*e[11]+e[9]*e[2]*e[15]-e[1]*e[10]*e[15])*r,t[2]=(e[5]*e[14]*e[3]-e[13]*e[6]*e[3]+e[13]*e[2]*e[7]-e[1]*e[14]*e[7]-e[5]*e[2]*e[15]+e[1]*e[6]*e[15])*r,t[3]=(e[9]*e[6]*e[3]-e[5]*e[10]*e[3]-e[9]*e[2]*e[7]+e[1]*e[10]*e[7]+e[5]*e[2]*e[11]-e[1]*e[6]*e[11])*r,t[4]=(e[12]*e[10]*e[7]-e[8]*e[14]*e[7]-e[12]*e[6]*e[11]+e[4]*e[14]*e[11]+e[8]*e[6]*e[15]-e[4]*e[10]*e[15])*r,t[5]=(e[8]*e[14]*e[3]-e[12]*e[10]*e[3]+e[12]*e[2]*e[11]-e[0]*e[14]*e[11]-e[8]*e[2]*e[15]+e[0]*e[10]*e[15])*r,t[6]=(e[12]*e[6]*e[3]-e[4]*e[14]*e[3]-e[12]*e[2]*e[7]+e[0]*e[14]*e[7]+e[4]*e[2]*e[15]-e[0]*e[6]*e[15])*r,t[7]=(e[4]*e[10]*e[3]-e[8]*e[6]*e[3]+e[8]*e[2]*e[7]-e[0]*e[10]*e[7]-e[4]*e[2]*e[11]+e[0]*e[6]*e[11])*r,t[8]=(e[8]*e[13]*e[7]-e[12]*e[9]*e[7]+e[12]*e[5]*e[11]-e[4]*e[13]*e[11]-e[8]*e[5]*e[15]+e[4]*e[9]*e[15])*r,t[9]=(e[12]*e[9]*e[3]-e[8]*e[13]*e[3]-e[12]*e[1]*e[11]+e[0]*e[13]*e[11]+e[8]*e[1]*e[15]-e[0]*e[9]*e[15])*r,t[10]=(e[4]*e[13]*e[3]-e[12]*e[5]*e[3]+e[12]*e[1]*e[7]-e[0]*e[13]*e[7]-e[4]*e[1]*e[15]+e[0]*e[5]*e[15])*r,t[11]=(e[8]*e[5]*e[3]-e[4]*e[9]*e[3]-e[8]*e[1]*e[7]+e[0]*e[9]*e[7]+e[4]*e[1]*e[11]-e[0]*e[5]*e[11])*r,t[12]=(e[12]*e[9]*e[6]-e[8]*e[13]*e[6]-e[12]*e[5]*e[10]+e[4]*e[13]*e[10]+e[8]*e[5]*e[14]-e[4]*e[9]*e[14])*r,t[13]=(e[8]*e[13]*e[2]-e[12]*e[9]*e[2]+e[12]*e[1]*e[10]-e[0]*e[13]*e[10]-e[8]*e[1]*e[14]+e[0]*e[9]*e[14])*r,t[14]=(e[12]*e[5]*e[2]-e[4]*e[13]*e[2]-e[12]*e[1]*e[6]+e[0]*e[13]*e[6]+e[4]*e[1]*e[14]-e[0]*e[5]*e[14])*r,t[15]=(e[4]*e[9]*e[2]-e[8]*e[5]*e[2]+e[8]*e[1]*e[6]-e[0]*e[9]*e[6]-e[4]*e[1]*e[10]+e[0]*e[5]*e[10])*r}function h(e){this.error=new Float32Array(e),this.data=new Int32Array(e),this.size=0}function l(){let e=this;e.maxBlocked=30,e.modelMatrix=new Float32Array(16),e.viewMatrix=new Float32Array(16),e.projectionMatrix=new Float32Array(16),e.modelView=new Float32Array(16),e.modelViewInv=new Float32Array(16),e.modelViewProj=new Float32Array(16),e.modelViewProjInv=new Float32Array(16),e.planes=new Float32Array(24),e.viewport=new Float32Array(4),e.viewpoint=new Float32Array(4)}h.prototype={push:function(e,t){this.data[this.size]=e,this.error[this.size]=t,this.bubbleUp(this.size),this.size++},pop:function(){var e=this.data[0];return this.size--,this.size>0&&(this.data[0]=this.data[this.size],this.error[0]=this.error[this.size],this.sinkDown(0)),e},bubbleUp:function(e){for(var t=this.data[e],r=this.error[e];e>0;){var o=(e+1>>1)-1,s=this.error[o];if(s>r)break;this.data[e]=this.data[o],this.error[e]=s,this.data[o]=t,this.error[o]=r,e=o}},sinkDown:function(e){for(var t=this.data[e],r=this.error[e];;){var o=2*(e+1),s=o-1,i=-1;if(sr&&(i=s)}if(o(-1==i?r:n)&&(i=o);if(-1==i)break;this.data[e]=this.data[i],this.error[e]=this.error[i],this.data[i]=t,this.error[i]=r,e=i}}},l.prototype={updateView:function(e,t,r){let o=this;for(let e=0;e<16;e++)o.projectionMatrix[e]=t[e],o.modelView[e]=r[e];for(let t=0;t<4;t++)o.viewport[t]=e[t];var s,i,n;s=o.projectionMatrix,i=o.modelView,(n=o.modelViewProj)[0]=s[0]*i[0]+s[4]*i[1]+s[8]*i[2]+s[12]*i[3],n[1]=s[1]*i[0]+s[5]*i[1]+s[9]*i[2]+s[13]*i[3],n[2]=s[2]*i[0]+s[6]*i[1]+s[10]*i[2]+s[14]*i[3],n[3]=s[3]*i[0]+s[7]*i[1]+s[11]*i[2]+s[15]*i[3],n[4]=s[0]*i[4]+s[4]*i[5]+s[8]*i[6]+s[12]*i[7],n[5]=s[1]*i[4]+s[5]*i[5]+s[9]*i[6]+s[13]*i[7],n[6]=s[2]*i[4]+s[6]*i[5]+s[10]*i[6]+s[14]*i[7],n[7]=s[3]*i[4]+s[7]*i[5]+s[11]*i[6]+s[15]*i[7],n[8]=s[0]*i[8]+s[4]*i[9]+s[8]*i[10]+s[12]*i[11],n[9]=s[1]*i[8]+s[5]*i[9]+s[9]*i[10]+s[13]*i[11],n[10]=s[2]*i[8]+s[6]*i[9]+s[10]*i[10]+s[14]*i[11],n[11]=s[3]*i[8]+s[7]*i[9]+s[11]*i[10]+s[15]*i[11],n[12]=s[0]*i[12]+s[4]*i[13]+s[8]*i[14]+s[12]*i[15],n[13]=s[1]*i[12]+s[5]*i[13]+s[9]*i[14]+s[13]*i[15],n[14]=s[2]*i[12]+s[6]*i[13]+s[10]*i[14]+s[14]*i[15],n[15]=s[3]*i[12]+s[7]*i[13]+s[11]*i[14]+s[15]*i[15],a(o.modelViewProj,o.modelViewProjInv),a(o.modelView,o.modelViewInv),o.viewpoint[0]=o.modelViewInv[12],o.viewpoint[1]=o.modelViewInv[13],o.viewpoint[2]=o.modelViewInv[14],o.viewpoint[3]=1;const h=o.modelViewProj,l=o.modelViewProjInv;let u=o.planes;u[0]=h[0]+h[3],u[1]=h[4]+h[7],u[2]=h[8]+h[11],u[3]=h[12]+h[15],u[4]=-h[0]+h[3],u[5]=-h[4]+h[7],u[6]=-h[8]+h[11],u[7]=-h[12]+h[15],u[8]=h[1]+h[3],u[9]=h[5]+h[7],u[10]=h[9]+h[11],u[11]=h[13]+h[15],u[12]=-h[1]+h[3],u[13]=-h[5]+h[7],u[14]=-h[9]+h[11],u[15]=-h[13]+h[15],u[16]=-h[2]+h[3],u[17]=-h[6]+h[7],u[18]=-h[10]+h[11],u[19]=-h[14]+h[15],u[20]=-h[2]+h[3],u[21]=-h[6]+h[7],u[22]=-h[10]+h[11],u[23]=-h[14]+h[15];for(let e=0;e<24;e+=4){let t=Math.sqrt(u[e]*u[e]+u[e+1]*u[e+1]+u[e+2]*u[e+2]);u[e]/=t,u[e+1]/=t,u[e+2]/=t,u[e+3]/=t}const d=l[3]+l[15],c=(l[0]+l[12])/d,f=(l[1]+l[13])/d,m=(l[2]+l[14])/d,p=-l[3]+l[15],x=(-l[0]+l[12])/p-c,v=(-l[1]+l[13])/p-f,b=(-l[2]+l[14])/p-m,g=Math.sqrt(x*x+v*v+b*b),w=l[12]/l[15]-o.viewpoint[0],A=l[13]/l[15]-o.viewpoint[1],y=l[14]/l[15]-o.viewpoint[2],E=2*g/Math.sqrt(w*w+A*A+y*y)/o.viewport[2];o.currentResolution==E?o.sameResolution=!0:o.sameResolution=!1,o.currentResolution=E},traverse:function(e,t){let r=this;if(r.mesh=e,!e.isReady)return;const o=e.nodesCount;r.visited=new Uint8Array(o),r.blocked=new Uint8Array(o),r.selected=new Uint8Array(o),r.frame=t.frame,r.instance_errors=new Float32Array(o),r.frame>e.frame&&(e.errors=new Float32Array(o),e.frame=r.frame),r.visitQueue=new h(o);for(var s=0;s0&&tr.drawBudget)return!1;if(1!=r.mesh.status[e])return!1;const o=r.mesh.nspheres,s=5*e;return r.isVisible(o[s],o[s+1],o[s+2],o[s+3])&&(r.drawSize+=.8*r.mesh.nvertices[e]),!0},nodeError:function(e,t){let r=this;const o=r.mesh.nspheres,s=r.viewpoint,i=5*e,n=o[i+0],a=o[i+1],h=o[i+2];let l=o[i+3];t&&(l=o[i+4]);const u=s[0]-n,d=s[1]-a,c=s[2]-h;let f=Math.sqrt(u*u+d*d+c*c)-l;f<.1&&(f=.1);let m=r.mesh.nerrors[e]/(r.currentResolution*f),p=r.distance(n,a,h,o[i+4]);return p<-l?m/=101:p<0&&(m/=1-p/l*100),m},distance:function(e,t,r,o){const s=this.planes;let i=1e20;for(let n=0;n<24;n+=4){let a=s[n]*e+s[n+1]*t+s[n+2]*r+s[n+3]+o;a1.1&&(t.currentError*=1.05),r<.9&&(t.currentError*=.95),t.currentError=Math.max(t.targetError,Math.min(t.maxError,t.currentError))}else t.currentError=t.targetError;t.rendered=0,t.realError=1e20,t.totswapped=0},endFrame:function(){this.update()},requestNode:function(e,t){e.status[t]=2,this.cacheSize+=e.nsize[t],e.reqAttempt[t]=0,this.nodes.has(e)||this.nodes.set(e,new Set),this.nodes.get(e).add(t),this.requestNodeGeometry(e,t),this.requestNodeTexture(e,t)},requestNodeGeometry:function(e,t){this.pending++,e.status[t]++;let r=e.deepzoom?e.baseurl+t+".nxn":e.url,o=e.deepzoom?0:e.noffsets[t],s=e.deepzoom?0:e.noffsets[t+1],i=e.georeq[t]=e.httpRequest(r,o,s,()=>{delete e.georeq[t],this.loadNodeGeometry(i,e,t),this.pending--},()=>{delete e.georeq[t],this.debug.verbose&&console.log("Geometry request error!"),this.recoverNode(e,t,0),this.pending--},()=>{delete e.georeq[t],this.debug.verbose&&console.log("Geometry request abort!"),this.removeNode(e,t),this.pending--},"arraybuffer")},requestNodeTexture:function(e,t){if(!e.vertex.texCoord)return;let r=e.patches[3*e.nfirstpatch[t]+2];e.texref[r]++,e.status[t]++;let o=e.deepzoom?e.baseurl+r+".jpg":e.url,s=e.deepzoom?0:e.textures[r],i=e.deepzoom?0:e.textures[r+1],n=e.texreq[r]=e.httpRequest(o,s,i,()=>{delete e.texreq[r],this.loadNodeTexture(n,e,t,r)},()=>{delete e.texreq[r],this.debug.verbose&&console.log("Texture request error!"),this.recoverNode(e,t,1)},()=>{delete e.texreq[r],this.debug.verbose&&console.log("Texture request abort!"),this.removeNode(e,t)},"blob")},recoverNode:function(e,t,r){if(0==e.status[t])return;e.status[t]--;let o=this;if(e.reqAttempt[t]>2)return this.debug.verbose&&console.log("Max request limit for "+m.url+" node: "+n),void o.removeNode(e,t);switch(e.reqAttempt[t]++,r){case 0:o.requestNodeGeometry(e,t),this.debug.verbose&&console.log("Recovering geometry for "+m.url+" node: "+n);break;case 1:o.requestNodeTexture(e,t),this.debug.verbose&&console.log("Recovering texture for "+m.url+" node: "+n)}},loadNodeGeometry:function(e,t,r){0!=t.status[r]&&(t.compressed?(this.corto||this.loadCorto(),this.corto.postRequest({mesh:t,id:r,buffer:e.response,cache:this})):this.readyGeometryNode(t,r,e.response))},loadNodeTexture:function(e,t,r,o){if(0==t.status[r])throw"Should not load texture twice";let s=e.response,i=e=>{0!=t.status[r]&&(t.createTexture(o,e),t.status[r]--,2==t.status[r]&&this.readyNode(t,r))};if("undefined"!=typeof createImageBitmap)try{createImageBitmap(s,{imageOrientation:"flipY"}).then(i)}catch(e){createImageBitmap(s).then(i)}else{var n=window.URL||window.webkitURL,a=document.createElement("img");a.onerror=function(e){console.log("Texture loading error!")},a.src=n.createObjectURL(s),a.onload=function(){n.revokeObjectURL(a.src),i(a)}}},removeNode:function(e,t){if(this.nodes.get(e).delete(t),0==e.status[t])return void(this.debug.verbose&&console.log("Double remove due to abort."));if(e.status[t]=0,t in e.georeq&&4!=e.georeq[t].readyState&&(e.georeq[t].abort(),delete e.georeq[t]),e.availableNodes--,this.cacheSize-=e.nsize[t],e.deleteNodeGeometry(t),!e.vertex.texCoord)return;const r=e.patches[3*e.nfirstpatch[t]+2];r in e.texreq&&4!=e.texreq[r].readyState&&(e.texreq[r].abort(),delete e.texreq[r]),e.texref[r]--,0==e.texref[r]&&e.deleteTexture(r)},readyGeometryNode:function(e,t,r){if(0==e.status[t])return;const o=e.nvertices[t],s=e.nfaces[t];let i={};if(e.corto)i=r;else{i.index=new Uint16Array(r,o*e.vsize,3*s),i.position=new Float32Array(r,0,3*o);var n=12*o;e.vertex.texCoord&&(i.uv=new Float32Array(r,n,2*o),n+=8*o),e.vertex.normal&&(i.normal=new Int16Array(r,n,3*o),n+=6*o),e.vertex.color&&(i.color=new Uint8Array(r,n,4*o),n+=4*o)}e.createNodeGeometry(t,i),e.status[t]--,2==e.status[t]&&this.readyNode(e,t)},readyNode:function(e,t){if(e.status[t]--,1!=e.status[t])throw"A ready node should have status ==1";e.reqAttempt[t]=0,e.createNode(t),e.availableNodes++;for(let t of e.onUpdate)t();this.update()},flush:function(e){if(this.nodes.has(e)){for(let t of this.nodes.get(e))this.removeNode(e,t);this.nodes.delete(e)}},update:function(){if(this.pending>=3)return;let e=null;for(let t of this.candidates)0==t.mesh.status[t.id]&&(!e||t.error>e.error)&&(e=t);if(e){for(;this.cacheSize>this.maxCacheSize;){let t=null;for(let[e,r]of this.nodes)for(let o of r){let r=e.errors[o],s=e.frames[o];(!t||r=.9*e.error)return;this.removeNode(t.mesh,t.id)}this.totswapped+=e.mesh.nsize[e.id],this.candidates=this.candidates.filter(t=>t.mesh==e.mesh&&t.id==e.id),this.requestNode(e.mesh,e.id),this.update()}}};let x=new p;class v extends t.Mesh{constructor(e,r,o={}){super(),this.patchWebGLRenderer(r),Object.assign(this,{isNXS:!0,type:"NXS",url:e,gl:r.getContext(),XRMode:!1,material:null,autoUpdate:!0,mesh:new f,vbo:[],ibo:[],vao:[],textures:[],attributes:{},basemesh:null}),"material"in o&&(this.material=o.material),this.material||(this.material=new t.MeshStandardMaterial);for(let e of["onLoad","onUpdate","onProgress"])this["_"+e]=[],e in o&&this["_"+e].push(o[e]);this.url&&("object"==typeof e?(this.nxs=this.url,this.nxs.onLoad.push(e=>{this.mesh=this.nxs.mesh,this.traversal=this.nxs.traversal,this.cache=this.nxs.cache,this.vbo=this.nxs.vbo,this.ibo=this.nxs.ibo,this.vao=this.nxs.vao,this.textures=this.nxs.textures,this.onLoadCallback(this)})):this.open(this.url))}copy(e){throw t.Object3D.prototype.copy.call(this,e,!1),new Error("Can't really copy a Nexus3D object.")}open(e){let t=this;this.mesh.open(e),this.mesh.createNode=e=>{},this.mesh.createNodeGeometry=(e,r)=>{t.createNodeGeometry(e,r)},this.mesh.createTexture=(e,r)=>{t.createTexture(e,r)},this.mesh.deleteNodeGeometry=e=>{t.deleteNodeGeometry(e)},this.mesh.deleteTexture=e=>{t.deleteTexture(e)},this.mesh.onLoad.push(()=>{t.onLoadCallback()}),this.mesh.onUpdate.push(()=>{for(let e of t._onUpdate)e(this);for(let e of t._onProgress)e(this,this.mesh.availableNodes,this.mesh.nodesCount)}),this.traversal=new l,this.cache=x,this.textures={}}set onLoad(e){this._onLoad.push(e)}set onUpdate(e){this._onUpdate.push(e)}set onProgress(e){this._onProgress.push(e)}updateMaterials(){!1!==this.material.map&&this.mesh.vertex.texCoord&&(this.material.map=this.material_texture),this.mesh.vertex.color&&(this.material.vertexColors=!0),this.material.needsUpdate=!0}onLoadCallback(){const e=this.mesh.sphere.center,r=new t.Vector3(e[0],e[1],e[2]),o=this.mesh.sphere.radius;this.boundingSphere=new t.Sphere(r,o);var s=new t.BufferGeometry;s.setAttribute("position",new t.BufferAttribute(new Float32Array(3),3)),this.mesh.vertex.normal&&s.setAttribute("normal",new t.BufferAttribute(new Float32Array(3),3)),this.mesh.vertex.color&&s.setAttribute("color",new t.BufferAttribute(new Float32Array(4),4)),this.mesh.vertex.texCoord&&s.setAttribute("uv",new t.BufferAttribute(new Float32Array(2),2)),this.mesh.vertex.texCoord&&(this.material_texture=new t.DataTexture(new Uint8Array([1,1,1,1]),1,1,t.RGBAFormat),this.material_texture.needsUpdate=!0),this.updateMaterials(),this.geometry=s,this.frustumCulled=!1;for(let e of this._onLoad)e(this)}renderBufferDirect(e,r,o,s,i,n){let a=new t.Vector4;e.getViewport(a),this.modelViewMatrix.multiplyMatrices(o.matrixWorldInverse,this.matrixWorld),0==a.x&&this.XRMode&&(this.traversal.updateView(a,o.projectionMatrix.elements,this.modelViewMatrix.elements),this.instance_errors=this.traversal.traverse(this.mesh,this.cache));let h=this.gl,l=h.getParameter(h.CURRENT_PROGRAM),u=this.attributes;u.position=h.getAttribLocation(l,"position"),u.normal=h.getAttribLocation(l,"normal"),u.color=h.getAttribLocation(l,"color"),u.uv=h.getAttribLocation(l,"uv"),u.size=h.getUniformLocation(l,"size"),u.scale=h.getUniformLocation(l,"scale");let d=h.getUniformLocation(l,"map");u.map=d?h.getUniform(l,d):null,this.setVisibility()}setVisibility(){let e=this.traversal,t=this.mesh;if(!t.isReady)return;let r=0,o=this.attributes,s=this.mesh,i=this.gl,n=i instanceof WebGL2RenderingContext,a=o.uv+10*o.color+100*o.normal;for(let x=0;x=0&&(i.vertexAttribPointer(o.uv,2,i.FLOAT,!1,8,t),i.enableVertexAttribArray(o.uv)),t+=8*e),s.vertex.color&&(o.color>=0&&(i.vertexAttribPointer(o.color,4,i.UNSIGNED_BYTE,!0,4,t),i.enableVertexAttribArray(o.color)),t+=4*e),s.vertex.normal&&o.normal>=0&&(i.vertexAttribPointer(o.normal,3,i.SHORT,!0,6,t),i.enableVertexAttribArray(o.normal)),this.cache.debug.nodes){i.disableVertexAttribArray(o.color);var c=this.instance_errors[x],f=[[1,1,1,1],[1,1,1,1],[0,1,0,1],[0,1,1,1],[1,1,0,1],[1,0,1,1],[1,0,0,1]];let e=Math.min(5.99,Math.max(0,Math.log2(c)/2)),t=Math.floor(e);e-=t;let r=[];for(let o=0;o<4;o++)r[o]=f[t][o]*(1-e)+f[t+1][o]*e;i.vertexAttrib4fv(o.color,r)}}this.cache.realError=Math.min(this.mesh.errors[x],this.cache.realError);let b=0,g=0,w=t.nfirstpatch[x+1]-1;for(let s=t.nfirstpatch[x];sb){if(t.vertex.texCoord&&o.uv>=0){var m=t.patches[3*s+2];if(-1!=m){var p=this.textures[m];i.activeTexture(i.TEXTURE0+o.map),i.bindTexture(i.TEXTURE_2D,p)}}let e=this.material.wireframe?i.LINE_STRIP:i.TRIANGLES;i.drawElements(e,3*(g-b),i.UNSIGNED_SHORT,6*b),r+=g-b}b=t.patches[3*s+1]}}}this.cache.rendered+=r}createNodeGeometry(e,r){let o=this.mesh;var s=o.nvertices[e];o.nfaces[e];let i=r.index,n=new ArrayBuffer(s*o.vsize);new Float32Array(n,0,3*s).set(r.position);var a=12*s;o.vertex.texCoord&&(new Float32Array(n,a,2*s).set(r.uv),a+=8*s);o.vertex.color&&(new Uint8Array(n,a,4*s).set(r.color),a+=4*s);o.vertex.normal&&(new Int16Array(n,a,3*s).set(r.normal),a+=6*s);if(e{t(r,o,s,i,n,a),n.renderBufferDirect&&n.renderBufferDirect(e,o,r,s,i,a)},e.originalRender=e.render,e.render=(t,r)=>{x.beginFrame(30),e.originalRender(t,r),x.endFrame()},e.nexusPatched=!0}}e.Cache=x,e.Nexus3D=v,Object.defineProperty(e,"__esModule",{value:!0})}); diff --git a/nexus3d/dist/index.html b/nexus3d/dist/index.html index 58d148e1..e0b38a30 100644 --- a/nexus3d/dist/index.html +++ b/nexus3d/dist/index.html @@ -23,7 +23,6 @@

- - + diff --git a/nexus3d/package.json b/nexus3d/package.json index 2c3476b0..c1109923 100644 --- a/nexus3d/package.json +++ b/nexus3d/package.json @@ -2,23 +2,26 @@ "name": "nexus3d", "version": "1.0.10", "description": "Streaming 3D on the web (threejs supported)", - "author": "", - "license": "ISC", + "author": "Federico Ponchio", + "license": "MIT", "private": false, "browser": "build/nexus3D.min.js", "main": "build/nexus3D.min.js", + "module": "build/nexus3D.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "start": "webpack-dev-server --hot", "build": "webpack --mode development", "rollup": "rollup -c rollup.config.js", - "buildlib": "webpack -c webpacklib.config.js --mode development" + "buildlib": "webpack -c webpacklib.config.js --mode development", + "prepare": "npm run buildlib" }, "repository": { "type": "git", "url": "https://github.com/cnr-isti-vclab/nexus" }, - "keywords": [], + "keywords": ["threejs","streaming","nexus3d"], + "homepage": "https://github.com/cnr-isti-vclab/nexus", "devDependencies": { "html-webpack-plugin": "^5.5.4", "rollup-plugin-terser": "^7.0.2", @@ -27,7 +30,7 @@ "webpack-dev-server": "^4.15.1" }, "dependencies": { - "three": "^0.159.0" + "three": "^0.181.0" }, "files": [ "build/nexus3D.min.js", diff --git a/nexus3d/src/Monitor.js b/nexus3d/src/Monitor.js index fa25bdfe..05ba3e02 100644 --- a/nexus3d/src/Monitor.js +++ b/nexus3d/src/Monitor.js @@ -288,7 +288,7 @@ class Monitor { let dx = 10; for(let i = 0; i < nodes.length; i++) { let n = nodes[i]; - let selected = this.model.traversal.selected[n]; + let selected = this.model.traversal.selected ? this.model.traversal.selected[n] : false; let status = this.mesh.status[n]; let color = 'red'; if(selected) diff --git a/nexus3d/src/Nexus3D.js b/nexus3d/src/Nexus3D.js index f255adcf..8e492b4f 100644 --- a/nexus3d/src/Nexus3D.js +++ b/nexus3d/src/Nexus3D.js @@ -1,15 +1,25 @@ -import * as THREE from 'three' +import { + Mesh as THREEMesh, + MeshStandardMaterial, + Object3D, + Vector3, + Vector4, + Sphere, + BufferGeometry, + BufferAttribute, + DataTexture, + RGBAFormat, + LinearSRGBColorSpace +} from 'three' import { Traversal } from './Traversal.js' import { Mesh } from './Mesh.js' import { Cache } from './Cache.js' -class Nexus3D extends THREE.Mesh { +class Nexus3D extends THREEMesh { - constructor(url, renderer, options) { + constructor(url, renderer, options = {}) { super(); - if(typeof renderer == 'function') - throw "Nexus3D constructor has changed: Nexus3D(url, renderer, options) where options include: onLoad, onUpdate, onProgress and material" this.patchWebGLRenderer(renderer); @@ -35,7 +45,7 @@ class Nexus3D extends THREE.Mesh { if('material' in options) this.material = options.material; if(!this.material) - this.material = new THREE.MeshStandardMaterial(); + this.material = new MeshStandardMaterial(); for(let call of ['onLoad', 'onUpdate', 'onProgress']) { this['_' + call] = []; @@ -62,8 +72,8 @@ class Nexus3D extends THREE.Mesh { } copy(source) { - Object3D.prototype.copy.call( this, source, false ); - throw "Can't really copy." + Object3D.prototype.copy.call(this, source, false); + throw new Error("Can't really copy a Nexus3D object."); return this; } @@ -116,23 +126,23 @@ class Nexus3D extends THREE.Mesh { onLoadCallback() { const c = this.mesh.sphere.center; - const center = new THREE.Vector3(c[0], c[1], c[2]); + const center = new Vector3(c[0], c[1], c[2]); const radius = this.mesh.sphere.radius; - this.boundingSphere = new THREE.Sphere(center, radius); + this.boundingSphere = new Sphere(center, radius); - var geometry = new THREE.BufferGeometry(); + var geometry = new BufferGeometry(); - geometry.setAttribute( 'position', new THREE.BufferAttribute(new Float32Array(3), 3)); + geometry.setAttribute( 'position', new BufferAttribute(new Float32Array(3), 3)); if(this.mesh.vertex.normal) - geometry.setAttribute( 'normal', new THREE.BufferAttribute(new Float32Array(3), 3)); + geometry.setAttribute( 'normal', new BufferAttribute(new Float32Array(3), 3)); if(this.mesh.vertex.color) - geometry.setAttribute( 'color', new THREE.BufferAttribute(new Float32Array(4), 4)); + geometry.setAttribute( 'color', new BufferAttribute(new Float32Array(4), 4)); if(this.mesh.vertex.texCoord) - geometry.setAttribute( 'uv', new THREE.BufferAttribute(new Float32Array(2), 2)); + geometry.setAttribute( 'uv', new BufferAttribute(new Float32Array(2), 2)); if(this.mesh.vertex.texCoord) { - this.material_texture = new THREE.DataTexture( new Uint8Array([1, 1, 1, 1]), 1, 1, THREE.RGBAFormat ); + this.material_texture = new DataTexture( new Uint8Array([1, 1, 1, 1]), 1, 1, RGBAFormat ); this.material_texture.needsUpdate = true; } @@ -146,14 +156,14 @@ class Nexus3D extends THREE.Mesh { } renderBufferDirect(renderer, scene, camera, geometry, material, group) { - let s = new THREE.Vector4(); + let s = new Vector4(); renderer.getViewport(s); //object modelview is multiplied by camera during rendering, we need to do it here for visibility computations this.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, this.matrixWorld ); - if(s.x() == 0 && this.XRMode) { //hack to only traverse on the left eye + if(s.x == 0 || !this.XRMode) { //hack to only traverse on the left eye this.traversal.updateView(s, camera.projectionMatrix.elements, this.modelViewMatrix.elements); - this.instance_errors = this.traversal.traverse(this.mesh, this.cache); + this.instance_errors = this.traversal.traverse(this.mesh, this.cache); } //threejs increments version when setting neeedsUpdate /*if(this.material.version > 0) { @@ -358,12 +368,12 @@ class Nexus3D extends THREE.Mesh { //needed for approximate picking. if(id < this.mesh.nroots) { - let basegeometry = new THREE.BufferGeometry(); - basegeometry.setAttribute( 'position', new THREE.BufferAttribute(data.position, 3 ) ); - basegeometry.setAttribute( 'normal', new THREE.BufferAttribute(data.normal, 3 ) ); - basegeometry.setIndex(new THREE.BufferAttribute( data.index, 1 ) ); + let basegeometry = new BufferGeometry(); + basegeometry.setAttribute( 'position', new BufferAttribute(data.position, 3 ) ); + basegeometry.setAttribute( 'normal', new BufferAttribute(data.normal, 3 ) ); + basegeometry.setIndex(new BufferAttribute( data.index, 1 ) ); - this.basemesh = new THREE.Mesh(basegeometry, this.material); + this.basemesh = new THREEMesh(basegeometry, this.material); this.basemesh.visible = false; this.add(this.basemesh); } @@ -388,8 +398,15 @@ class Nexus3D extends THREE.Mesh { gl.bindTexture(gl.TEXTURE_2D, tex); //TODO some textures might be alpha only! save space - let internalFormat = this.material.map.colorSpace == THREE.LinearSRGBColorSpace ? gl.RGBA : gl.SRGB8_ALPHA8; - let s = gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, gl.RGBA, gl.UNSIGNED_BYTE, image); + let internalFormat; + if (this.material && this.material.map && this.material.map.colorSpace === LinearSRGBColorSpace) { + internalFormat = gl.RGBA; + } else if (typeof gl.SRGB8_ALPHA8 !== 'undefined') { + internalFormat = gl.SRGB8_ALPHA8; + } else { + internalFormat = gl.RGBA; + } + gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, gl.RGBA, gl.UNSIGNED_BYTE, image); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); @@ -437,7 +454,7 @@ class Nexus3D extends THREE.Mesh { } toJSON(meta) { - throw "Can't"; + throw new Error("Can't convert to json."); } patchWebGLRenderer(renderer) { diff --git a/nexus3d/src/index.js b/nexus3d/src/index.js index 980ecbcf..73de1465 100644 --- a/nexus3d/src/index.js +++ b/nexus3d/src/index.js @@ -24,7 +24,7 @@ import { } from 'three' import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'; -import * as Nexus3D from './Nexus3D.js' +import { Nexus3D, Cache } from './Nexus3D.js' import { Monitor } from './Monitor.js' @@ -83,7 +83,7 @@ renderer.setPixelRatio( window.devicePixelRatio ); renderer.shadowMap.enabled = true; renderer.shadowMap.type = BasicShadowMap; -container.append(renderer.domElement); +container.appendChild(renderer.domElement); function onNexusLoad(nexus) { @@ -103,12 +103,12 @@ function onNexusLoad(nexus) { var url = "models/gargo.nxz"; //onUpdate parameter here is used to trigger a redraw -let nexus1 = new Nexus3D.Nexus3D(url, renderer, { onLoad: onNexusLoad, onUpdate: () => { redraw = true; }} ); +let nexus1 = new Nexus3D(url, renderer, { onLoad: onNexusLoad, onUpdate: () => { redraw = true; }} ); //nexus1.castShadow = true; nexus1.receiveShadow = true; //create a second instance and position it. -let nexus2 = new Nexus3D.Nexus3D(url, renderer, { onLoad: onNexusLoad, onUpdate: () => { redraw = true; }} ); +let nexus2 = new Nexus3D(url, renderer, { onLoad: onNexusLoad, onUpdate: () => { redraw = true; }} ); nexus2.castShadow = true; nexus2.receiveShadow = true; @@ -119,7 +119,7 @@ nexus2.receiveShadow = true; //nexus.material.color = new Color(1, 0, 0)); //nexus.material.wireframe = true; -let monitor = new Monitor(Nexus3D.Cache, nexus2); +let monitor = new Monitor(Cache, nexus2); scene.add(nexus1); scene.add(nexus2); @@ -143,7 +143,7 @@ scene.add( ground ); window.addNexus = () => { - nexus2 = new Nexus3D.Nexus3D(url, renderer, { onLoad: onNexusLoad, onUpdate: () => { redraw = true; }} ); + nexus2 = new Nexus3D(url, renderer, { onLoad: onNexusLoad, onUpdate: () => { redraw = true; }} ); scene.add(nexus2); redraw = true; }; @@ -180,7 +180,7 @@ function onWindowResize() { const clock = new Clock(); var redraw = true; -renderer.setAnimationLoop(()=> { +function animate() { const delta = clock.getDelta() controls.update(delta); @@ -202,6 +202,8 @@ renderer.setAnimationLoop(()=> { } renderer.render( scene, camera ); } -}) +} + +renderer.setAnimationLoop(animate); onWindowResize(); diff --git a/nexus3d/webpack.config.js b/nexus3d/webpack.config.js index d9f7e36f..a2ad944c 100644 --- a/nexus3d/webpack.config.js +++ b/nexus3d/webpack.config.js @@ -4,29 +4,52 @@ const path = require('path') module.exports = { - mode: 'production', + mode: 'development', entry: { main: './src/index.js', }, output: { path: path.resolve(__dirname + '/dist'), - filename: 'main.js', + filename: '[name].bundle.js', }, devServer: { - static: './dist' + static: './dist', + client: { + overlay: false + } + }, + + resolve: { + alias: { + 'three$': path.resolve(__dirname, './node_modules/three/build/three.module.js') + } }, optimization: { - minimize: false + minimize: false, + usedExports: true, + splitChunks: { + chunks: 'all', + cacheGroups: { + vendor: { + test: /[\\/]node_modules[\\/]/, + name: 'vendors', + priority: 10 + } + } + } + }, + performance: { + hints: false }, plugins: [ new webpack.HotModuleReplacementPlugin(), new HtmlWebpackPlugin({ title: 'Nexus3D', template: __dirname + '/dist/index.html', - inject: false + inject: 'body' }), ], } diff --git a/nexus3d/webpacklib.config.js b/nexus3d/webpacklib.config.js index ed9cda2f..2ba6fcc3 100644 --- a/nexus3d/webpacklib.config.js +++ b/nexus3d/webpacklib.config.js @@ -3,14 +3,24 @@ var webpack = require('webpack') module.exports = { entry: { - Nexus3D: './Nexus3D.js', + Nexus3D: './src/Nexus3D.js', }, output: { - path: __dirname + '/dist', - filename: '[name].js', - libraryTarget: 'var', - library: '[name]' + path: __dirname + '/build', + filename: 'nexus3D.js', + libraryTarget: 'umd', + library: 'Nexus3D', + globalObject: 'this' + }, + + externals: { + three: { + commonjs: 'three', + commonjs2: 'three', + amd: 'three', + root: 'THREE' + } }, optimization: { diff --git a/src/corto b/src/corto index c0c80fe9..d880519c 160000 --- a/src/corto +++ b/src/corto @@ -1 +1 @@ -Subproject commit c0c80fe9357cd4f5c3428a2fe73361a6e9ea40c3 +Subproject commit d880519c490c88a39d12c31a914b6a687a7019c3 From c368b0046e4acda6854c88d695c251d602a51f2b Mon Sep 17 00:00:00 2001 From: ponchio Date: Fri, 12 Dec 2025 10:40:29 +0100 Subject: [PATCH 29/33] updated corto submodule --- src/corto | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/corto b/src/corto index d880519c..d240e693 160000 --- a/src/corto +++ b/src/corto @@ -1 +1 @@ -Subproject commit d880519c490c88a39d12c31a914b6a687a7019c3 +Subproject commit d240e69306a39417c098475d7002eb16b7264248 From f4f31b0f5716b66c64f7b7eb701eb41f0d913880 Mon Sep 17 00:00:00 2001 From: ponchio Date: Fri, 2 Jan 2026 16:28:06 +0100 Subject: [PATCH 30/33] Added deepzoom nxsedit support. --- src/common/nexus.cpp | 13 +++++-- src/common/nexusdata.cpp | 58 +++++++++++++++++++--------- src/common/nexusfile.h | 7 ++++ src/common/qtnexusfile.cpp | 46 ++++++++++++++++++++++- src/common/qtnexusfile.h | 11 +++++- src/common/signature.h | 1 + src/nxsedit/extractor.cpp | 77 +++++++++++++++++++++++++++++++++++--- 7 files changed, 186 insertions(+), 27 deletions(-) diff --git a/src/common/nexus.cpp b/src/common/nexus.cpp index 626d3a22..9817b3e2 100644 --- a/src/common/nexus.cpp +++ b/src/common/nexus.cpp @@ -117,14 +117,22 @@ void Nexus::loadIndex(char *buffer) { void nx::Nexus::loadImageFromData(nx::TextureData& data, int t) { Texture& texture = textures[t]; - data.memory = (char*)file->map(texture.getBeginOffset(), texture.getSize()); + if(header.signature.isDeepzoom()) { + data.memory = file->loadDZTex(t); + } else { + data.memory = (char*)file->map(texture.getBeginOffset(), texture.getSize()); + } if (!data.memory) { std::cerr << "Failed mapping texture data" << std::endl; exit(0); } QImage img; bool success = img.loadFromData((uchar*)data.memory, texture.getSize()); - file->unmap((uchar*)data.memory); + if(header.signature.isDeepzoom()) { + file->dropDZTex(t); + } else { + file->unmap((uchar*)data.memory); + } if (!success) { std::cerr << "Failed loading texture" << std::endl; @@ -148,7 +156,6 @@ void nx::Nexus::loadImageFromData(nx::TextureData& data, int t) } void Nexus::loadIndex() { NexusData::loadIndex(); - loaded = true; } diff --git a/src/common/nexusdata.cpp b/src/common/nexusdata.cpp index ea481f78..2f3a4c10 100644 --- a/src/common/nexusdata.cpp +++ b/src/common/nexusdata.cpp @@ -149,6 +149,7 @@ uint32_t NexusData::size(uint32_t node) { return nodes[node].getSize(); } + uint64_t NexusData::loadRam(uint32_t n) { Signature &sign = header.signature; @@ -160,21 +161,28 @@ uint64_t NexusData::loadRam(uint32_t n) { uint64_t size = node.nvert*sign.vertex.size() + node.nface*sign.face.size(); - if(!sign.isCompressed()) { - d.memory = (char *)file->map(offset, size); + + if(!sign.isCompressed()) { + if(sign.isDeepzoom()) + d.memory = file->loadDZNode(n); + else + d.memory = (char *)file->map(offset, size); } else { - char *buffer = new char[compressed_size]; - file->seek(offset); - int64_t r = file->read(buffer, compressed_size); - assert(r == (int64_t)compressed_size); + char *buffer = nullptr; + if(sign.isDeepzoom()) { + buffer = file->loadDZNode(n); + } else { + buffer = new char[compressed_size]; + file->seek(offset); + int64_t r = file->read(buffer, compressed_size); + assert(r == (int64_t)compressed_size); + } d.memory = new char[size]; - int iterations = 1; - if(sign.flags & Signature::MECO) { meco::MeshDecoder coder(node, d, patches, sign); coder.decode(compressed_size, (unsigned char *)buffer); @@ -195,6 +203,10 @@ uint64_t NexusData::loadRam(uint32_t n) { decoder.decode(); } + if(sign.isDeepzoom()) + file->dropDZNode(buffer); + else + delete []buffer; //Shuffle points in compressed point clouds if(!sign.face.hasIndex()) { @@ -228,6 +240,7 @@ uint64_t NexusData::loadRam(uint32_t n) { } assert(d.memory); + // If DEEPZOOM mode, textures are stored externally (in _files/) — skip texture loading here. if(header.n_textures) { //be sure to load images for(uint32_t p = node.first_patch; p < node.last_patch(); p++) { @@ -240,13 +253,17 @@ uint64_t NexusData::loadRam(uint32_t n) { continue; Texture &texture = textures[t]; - data.memory = (char *)file->map(texture.getBeginOffset(), texture.getSize()); + if(sign.isDeepzoom()) { + data.memory = file->loadDZTex(n); + } else { + data.memory = (char *)file->map(texture.getBeginOffset(), texture.getSize()); + } if(!data.memory) { cerr << "Failed mapping texture data" << endl; exit(0); } - loadImageFromData(data, t); + //loadImageFromData(data, t); /* QImage img; @@ -288,16 +305,21 @@ uint64_t NexusData::dropRam(uint32_t n, bool write) { assert(!data.vbo); assert(data.memory); - if(!header.signature.isCompressed()) //not compressed. - file->unmap((unsigned char *)data.memory); - else + auto &sign = header.signature; + if(!sign.isCompressed()) { + if(sign.isDeepzoom()) + file->dropDZNode(data.memory); + else + file->unmap((unsigned char *)data.memory); + } else { delete []data.memory; + } data.memory = NULL; //report RAM size for compressed meshes. - uint32_t size = node.nvert * header.signature.vertex.size() + - node.nface * header.signature.face.size(); + uint32_t size = node.nvert * sign.vertex.size() + + node.nface * sign.face.size(); if(header.n_textures) { for(uint32_t p = node.first_patch; p < node.last_patch(); p++) { @@ -307,9 +329,11 @@ uint64_t NexusData::dropRam(uint32_t n, bool write) { TextureData &tdata = texturedata[t]; tdata.count_ram--; if(tdata.count_ram != 0) continue; + if(sign.isDeepzoom()) + file->dropDZTex(tdata.memory); + else + file->unmap((unsigned char *)tdata.memory); - file->unmap((unsigned char *)tdata.memory); - //delete []tdata.memory; tdata.memory = NULL; size += tdata.width*tdata.height*4; } diff --git a/src/common/nexusfile.h b/src/common/nexusfile.h index d7dca9b6..b9200a82 100644 --- a/src/common/nexusfile.h +++ b/src/common/nexusfile.h @@ -2,6 +2,7 @@ #define NX_NEXUSFILE_H #include +#include namespace nx { class NexusFile { public: @@ -13,6 +14,7 @@ namespace nx { }; virtual ~NexusFile() {} virtual void setFileName(const char* uri) = 0; + virtual const char *fileName() = 0; virtual bool open(OpenMode mode) = 0; virtual long long int read(char* where, size_t length) = 0; virtual long long int write(char* from, size_t length) = 0; @@ -20,6 +22,11 @@ namespace nx { virtual void* map(size_t from, size_t size) = 0; virtual bool unmap(void* mapped) = 0; virtual bool seek(size_t to) = 0; + + virtual char *loadDZNode(uint32_t n) = 0; + virtual void dropDZNode(char *data) = 0; + virtual char *loadDZTex(uint32_t n) = 0; + virtual void dropDZTex(char *data) = 0; }; } diff --git a/src/common/qtnexusfile.cpp b/src/common/qtnexusfile.cpp index 8b9ff977..7cb8bfe8 100644 --- a/src/common/qtnexusfile.cpp +++ b/src/common/qtnexusfile.cpp @@ -1,10 +1,15 @@ #include "qtnexusfile.h" +#include namespace nx { void nx::QTNexusFile::setFileName(const char* uri) { file.setFileName(uri); } + const char *nx::QTNexusFile::fileName() { + QByteArray ba = file.fileName().toUtf8(); + return ba.constData(); + } bool nx::QTNexusFile::open(OpenMode openmode) { @@ -46,4 +51,43 @@ namespace nx { { return file.seek(to); } -} \ No newline at end of file + + char *nx::QTNexusFile::loadDZNode(uint32_t n) { + QString fname = file.fileName(); + // expect a companion folder named "_files" like the saver produces + if(fname.size() < 5) + return nullptr; + QString basename = fname.left(fname.size()-4) + "_files"; + QString nodefile = QString("%1/%2.nxn").arg(basename).arg(n); + return readDZFile(nodefile); + } + + void nx::QTNexusFile::dropDZNode(char *data) { + delete [] data; + } + + char *nx::QTNexusFile::loadDZTex(uint32_t n) { + QString fname = file.fileName(); + if(fname.size() < 5) + return nullptr; + QString basename = fname.left(fname.size()-4) + "_files"; + QString texfile = QString("%1/%2.jpg").arg(basename).arg(n); + return readDZFile(texfile); + } + + void nx::QTNexusFile::dropDZTex(char *data) { + delete [] data; + } + + char *nx::QTNexusFile::readDZFile(const QString &path) { + QFile f(path); + if(!f.open(QIODevice::ReadOnly)) + return nullptr; + QByteArray ba = f.readAll(); + char *buf = new char[ba.size()]; + if(ba.size()) + memcpy(buf, ba.constData(), ba.size()); + return buf; + } + +} diff --git a/src/common/qtnexusfile.h b/src/common/qtnexusfile.h index b1e51b27..320f575b 100644 --- a/src/common/qtnexusfile.h +++ b/src/common/qtnexusfile.h @@ -3,14 +3,17 @@ #include "nexusfile.h" #include +#include namespace nx { class QTNexusFile : public NexusFile { private: QFile file; + char *readDZFile(const QString &path); public: void setFileName(const char* uri) override; + const char *fileName() override; bool open(OpenMode openmode) override; long long int read(char* where, size_t length) override; long long int write(char* from, size_t length) override; @@ -18,7 +21,13 @@ namespace nx { void* map(size_t from, size_t size) override; bool unmap(void* mapped) override; bool seek(size_t to) override; + + char *loadDZNode(uint32_t n) override; + void dropDZNode(char *data) override; + char *loadDZTex(uint32_t n) override; + void dropDZTex(char *data) override; + }; } -#endif // NX_QTNEXUSFILE_H \ No newline at end of file +#endif // NX_QTNEXUSFILE_H diff --git a/src/common/signature.h b/src/common/signature.h index 2996ba4d..bedf2620 100644 --- a/src/common/signature.h +++ b/src/common/signature.h @@ -97,6 +97,7 @@ class Signature { bool hasPTextures() { return (bool)(flags | PTEXTURE); } bool isCompressed() { return (bool)(flags & (MECO|CORTO)); } + bool isDeepzoom() { return (bool)(flags & (DEEPZOOM)); } Signature(): flags(0) {} }; diff --git a/src/nxsedit/extractor.cpp b/src/nxsedit/extractor.cpp index 155499bf..db39ed44 100644 --- a/src/nxsedit/extractor.cpp +++ b/src/nxsedit/extractor.cpp @@ -16,6 +16,9 @@ GNU General Public License (http://www.gnu.org/licenses/gpl.txt) for more details. */ #include +#include +#include +#include #include "extractor.h" #include "../nxszip/meshcoder.h" @@ -26,6 +29,28 @@ for more details. using namespace std; using namespace nx; +namespace { +QString deepzoomFolderFromOutput(const QString &output) { + QFileInfo info(output); + QString dirPath = info.absolutePath(); + if(dirPath.isEmpty()) + dirPath = "."; + return dirPath + "/" + info.completeBaseName() + "_files"; +} + +QString deepzoomFilePath(const QString &folder, uint32_t index, const QString &ext) { + return QString("%1/%2.%3").arg(folder).arg(index).arg(ext); +} + +void ensureDeepzoomFolder(const QString &folder) { + QDir dir; + if(dir.exists(folder)) + return; + if(!dir.mkpath(folder)) + throw QString("could not create deepzoom folder %1").arg(folder); +} +} + Extractor::Extractor(NexusData *nx): transform(false), max_size(0), current_size(0), @@ -99,6 +124,13 @@ void Extractor::save(QString output, nx::Signature &signature) { file.setFileName(output); if(!file.open(QIODevice::WriteOnly | QFile::Truncate)) throw QString("could not open file " + output + " for writing"); + + const bool saveAsDeepzoom = signature.isDeepzoom(); + QString deepzoomFolder; + if(saveAsDeepzoom) { + deepzoomFolder = deepzoomFolderFromOutput(output); + ensureDeepzoomFolder(deepzoomFolder); + } nx::Header header = nexus->header; header.signature = signature; @@ -196,9 +228,25 @@ void Extractor::save(QString output, nx::Signature &signature) { } } if(signature.isCompressed()) { - compress(file, signature, node, nexus->nodedata[i], &*patches.begin()); + if(saveAsDeepzoom) { + QFile nodeFile(deepzoomFilePath(deepzoomFolder, n, QStringLiteral("nxn"))); + if(!nodeFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) + throw QString("could not open node file %1 for writing").arg(nodeFile.fileName()); + compress(nodeFile, signature, node, nexus->nodedata[i], &*patches.begin()); + nodeFile.close(); + } else { + compress(file, signature, node, nexus->nodedata[i], &*patches.begin()); + } } else { - file.write(memory, data_size); + if(saveAsDeepzoom) { + QFile nodeFile(deepzoomFilePath(deepzoomFolder, n, QStringLiteral("nxn"))); + if(!nodeFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) + throw QString("could not open node file %1 for writing").arg(nodeFile.fileName()); + nodeFile.write(memory, data_size); + nodeFile.close(); + } else { + file.write(memory, data_size); + } } if(transform) delete []memory; @@ -209,16 +257,35 @@ void Extractor::save(QString output, nx::Signature &signature) { //save textures if(textures.size()) { - for(uint i = 0; i < textures.size()-1; i++) { + for(uint32_t i = 0; i < textures.size()-1; i++) { Texture &in = nexus->textures[i]; Texture &out = textures[i] = in; quint64 start = in.getBeginOffset(); quint64 size = in.getSize(); - char *memory = (char *)nexus->file->map(start, size); + char *memory = 0; + if(nexus->header.signature.isDeepzoom()) { + memory = nexus->file->loadDZTex(i); + } else { + memory = (char *)nexus->file->map(start, size); + } out.offset = file.pos()/NEXUS_PADDING; - file.write(memory, size); + if(saveAsDeepzoom) { + QFile texFile(deepzoomFilePath(deepzoomFolder, i, QStringLiteral("jpg"))); + if(!texFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) + throw QString("could not open texture file %1 for writing").arg(texFile.fileName()); + texFile.write(memory, size); + texFile.close(); + } else { + file.write(memory, size); + } + + if(nexus->header.signature.isDeepzoom()) { + nexus->file->dropDZTex(memory); + } else { + nexus->file->unmap(memory); + } } textures.back().offset = file.pos()/NEXUS_PADDING; } From 3f5178ae41e33866f3fa652ef3827f4d41a1a7d6 Mon Sep 17 00:00:00 2001 From: ponchio Date: Fri, 2 Jan 2026 17:08:32 +0100 Subject: [PATCH 31/33] indexedDB works also for textures --- html/js/nexus.js | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/html/js/nexus.js b/html/js/nexus.js index 8a7e0ae8..c4c9bdd4 100644 --- a/html/js/nexus.js +++ b/html/js/nexus.js @@ -319,7 +319,7 @@ Mesh.prototype = { let request = indexedDB.open(url); request.onsuccess = () => { this.db = request.result; }; request.onupgradeneeded = (event) => { - let db = event.target.result; + let db = event.target.result; this.meshStore = db.createObjectStore('mesh'); this.texStore = db.createObjectStore('tex'); }; @@ -1093,6 +1093,11 @@ function httpRequestNodeGeometry(context, node) { let request = { load:function() { delete m.georeq[n]; + if(m.db) { + let transaction = m.db.transaction('mesh', "readwrite"); + transaction.objectStore('mesh').put(this.response, n); + transaction.commit(); + } loadNodeGeometry(this, context, node); }, error:function() { delete m.georeq[n]; @@ -1137,7 +1142,7 @@ function requestNodeTexture(context, node) { let request = transaction.objectStore('tex').get(node.id); request.onsuccess = (e) => { if(request.result) { - loadNodeTecture({ response: request.result}, context, node); + loadNodeTexture({ response: request.result}, context, node, tex); } else { httpRequestNodeTexture(context, node, tex); } @@ -1155,7 +1160,13 @@ function httpRequestNodeTexture(context, node, tex) { let request = { load:function() { delete m.texreq[n]; - loadNodeTexture(this, context, node, tex); }, + if(m.db) { + let transaction = m.db.transaction('tex', "readwrite"); + transaction.objectStore('tex').put(this.response, tex); + transaction.commit(); + } + loadNodeTexture(this, context, node, tex); + }, error:function() { if(Debug.verbose) console.log("Texture request error!"); delete m.texreq[n]; @@ -1215,12 +1226,6 @@ function loadNodeGeometry(request, context, node) { node.buffer = request.response; - if(m.db) { - let transaction = m.db.transaction('mesh', "readwrite"); - transaction.objectStore('mesh').put(node.buffer, n); - transaction.commit(); - } - if(!m.compressed) readyNode(node); else if(m.meco) { From 2bc373988741fa1954029d91d20b0b4a4c0564f3 Mon Sep 17 00:00:00 2001 From: ponchio Date: Thu, 12 Feb 2026 11:01:33 +0100 Subject: [PATCH 32/33] updated npm package nexus3d to modern standards --- nexus3d/package.json | 21 ++++++++------ nexus3d/rollup.config.js | 61 ++++++++++++++++++++++------------------ nexus3d/src/Cache.js | 8 +++++- 3 files changed, 52 insertions(+), 38 deletions(-) diff --git a/nexus3d/package.json b/nexus3d/package.json index c1109923..2b2b402b 100644 --- a/nexus3d/package.json +++ b/nexus3d/package.json @@ -32,13 +32,16 @@ "dependencies": { "three": "^0.181.0" }, - "files": [ - "build/nexus3D.min.js", - "build/nexus3D.js", - "dist/corto.em.js", - "LICENSE", - "package.json", - "README.md", - "src" - ] +"files": [ + "build/nexus3D.js", + "build/nexus3D.min.js", + "build/corto.worker.js" +], +"exports": { + ".": { + "import": "./build/nexus3D.js", + "require": "./build/nexus3D.min.js" + }, + "./worker": "./build/corto.worker.js" +} } diff --git a/nexus3d/rollup.config.js b/nexus3d/rollup.config.js index c7c5b557..d21bd0f9 100644 --- a/nexus3d/rollup.config.js +++ b/nexus3d/rollup.config.js @@ -1,32 +1,37 @@ import { terser } from "rollup-plugin-terser"; -function header() { - return { - - renderChunk( code ) { - return "// vcg.isti.cnr.it/nexus/license\n" + code; - } - }; - -} +// Helper for license header +const license = "// vcg.isti.cnr.it/nexus/license\n"; export default [ - { - input: './src/Nexus3D.js', - output: [{ - format: 'umd', - name: 'Nexus3D', - file: 'build/nexus3D.min.js', - plugins: [terser()], - globals: { three: 'THREE' } - }, - { - format: 'umd', - name: 'Nexus3D', - file: 'build/nexus3D.js', - globals: { three: 'THREE' } - }], - external: [ 'three' ] - } - -]; + // Build the Main Library (Nexus3D) + { + input: './src/Nexus3D.js', + output: [ + { + format: 'es', + file: 'build/nexus3D.js', + }, + { + format: 'umd', + name: 'Nexus3D', + file: 'build/nexus3D.min.js', + plugins: [terser()], + globals: { three: 'THREE' } + } + ], + external: ['three'] + }, + // Build the Worker (Corto) + { + input: './dist/corto.em.js', + output: { + format: 'iife', // Classic worker format is safest for wide compatibility + file: 'build/corto.worker.js', + plugins: [ + terser(), + { renderChunk: (code) => license + code } + ] + } + } +]; \ No newline at end of file diff --git a/nexus3d/src/Cache.js b/nexus3d/src/Cache.js index 3b1504f1..1fc9aee4 100644 --- a/nexus3d/src/Cache.js +++ b/nexus3d/src/Cache.js @@ -56,7 +56,13 @@ _Cache.prototype = { setTargetError: function(error) { this.targetError = error; }, loadCorto: function() { - let corto = new Worker(this.cortopath + '/corto.em.js'); + let workerPath = this.cortopath + 'corto.worker.js'; + try { + workerPath = new URL('./corto.worker.js', import.meta.url).href; + } catch (e) { + //reverrt to es if import.meta.url is not available + } + let corto = new Worker(workerPath); corto.requests = {}; corto.count = 0; corto.postRequest = function(node) { From e416db8fae78410507675b2bf9254c34450b8186 Mon Sep 17 00:00:00 2001 From: Federico Date: Thu, 23 Apr 2026 13:18:54 +0200 Subject: [PATCH 33/33] ported to qt6 --- CMakeLists.txt | 2 +- nexus3d/build/nexus3D.js | 81 +++++++++---- nexus3d/dist/index.html | 6 +- nexus3d/src/corto.worker.js | 221 ++++++++++++++++++++++++++++++++++++ nexus3d/webpack.config.js | 4 +- src/CMakeLists.txt | 2 +- src/common/controller.cpp | 2 +- src/common/controller.h | 4 +- src/common/nexus.cpp | 4 +- src/common/qcontroller.h | 4 +- src/nxsbuild/objloader.cpp | 13 ++- src/nxsbuild/tsloader.cpp | 2 +- src/nxsview/CMakeLists.txt | 6 +- src/nxsview/gl_nxsview.cpp | 10 +- src/nxsview/gl_nxsview.h | 8 +- src/nxsview/main.cpp | 2 + 16 files changed, 317 insertions(+), 54 deletions(-) create mode 100644 nexus3d/src/corto.worker.js diff --git a/CMakeLists.txt b/CMakeLists.txt index de83ca86..2f455906 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,7 +13,7 @@ set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) -find_package(Qt5 COMPONENTS Widgets REQUIRED) +find_package(Qt6 COMPONENTS Widgets REQUIRED) if (BUILD_NXS_VIEW) include(${CMAKE_CURRENT_SOURCE_DIR}/src/cmake/tools.cmake) diff --git a/nexus3d/build/nexus3D.js b/nexus3d/build/nexus3D.js index 24915944..20e9655a 100644 --- a/nexus3d/build/nexus3D.js +++ b/nexus3d/build/nexus3D.js @@ -20,75 +20,75 @@ return /******/ (() => { // webpackBootstrap /******/ "use strict"; /******/ var __webpack_modules__ = ({ -/***/ "./src/Binary.js": +/***/ "./src/Binary.js" /*!***********************!*\ !*** ./src/Binary.js ***! \***********************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { +(__unused_webpack_module, __webpack_exports__, __webpack_require__) { eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ getFloat32: () => (/* binding */ getFloat32),\n/* harmony export */ getUint16: () => (/* binding */ getUint16),\n/* harmony export */ getUint32: () => (/* binding */ getUint32),\n/* harmony export */ getUint64: () => (/* binding */ getUint64),\n/* harmony export */ matInv: () => (/* binding */ matInv),\n/* harmony export */ matMul: () => (/* binding */ matMul),\n/* harmony export */ vecMul: () => (/* binding */ vecMul)\n/* harmony export */ });\n/* UTILITIES */\n \nfunction getUint64(view) {\n var s = 0;\n var lo = view.getUint32(view.offset, true);\n var hi = view.getUint32(view.offset + 4, true);\n view.offset += 8;\n return ((hi * (1 << 32)) + lo);\n}\n\nfunction getUint32(view) {\n var s = view.getUint32(view.offset, true);\n view.offset += 4;\n return s;\n}\n\nfunction getUint16(view) {\n var s = view.getUint16(view.offset, true);\n view.offset += 2;\n return s;\n}\n\nfunction getFloat32(view) {\n var s = view.getFloat32(view.offset, true);\n view.offset += 4;\n return s;\n}\n\n/* MATRIX STUFF */\n\nfunction vecMul(m, v, r) {\n var w = m[3]*v[0] + m[7]*v[1] + m[11]*v[2] + m[15];\n\n r[0] = (m[0]*v[0] + m[4]*v[1] + m[8 ]*v[2] + m[12 ])/w;\n r[1] = (m[1]*v[0] + m[5]*v[1] + m[9 ]*v[2] + m[13 ])/w;\n r[2] = (m[2]*v[0] + m[6]*v[1] + m[10]*v[2] + m[14])/w;\n}\n\n\nfunction matMul(a, b, r) {\n r[ 0] = a[0]*b[0] + a[4]*b[1] + a[8]*b[2] + a[12]*b[3];\n r[ 1] = a[1]*b[0] + a[5]*b[1] + a[9]*b[2] + a[13]*b[3];\n r[ 2] = a[2]*b[0] + a[6]*b[1] + a[10]*b[2] + a[14]*b[3];\n r[ 3] = a[3]*b[0] + a[7]*b[1] + a[11]*b[2] + a[15]*b[3];\n\n r[ 4] = a[0]*b[4] + a[4]*b[5] + a[8]*b[6] + a[12]*b[7];\n r[ 5] = a[1]*b[4] + a[5]*b[5] + a[9]*b[6] + a[13]*b[7];\n r[ 6] = a[2]*b[4] + a[6]*b[5] + a[10]*b[6] + a[14]*b[7];\n r[ 7] = a[3]*b[4] + a[7]*b[5] + a[11]*b[6] + a[15]*b[7];\n\n r[ 8] = a[0]*b[8] + a[4]*b[9] + a[8]*b[10] + a[12]*b[11];\n r[ 9] = a[1]*b[8] + a[5]*b[9] + a[9]*b[10] + a[13]*b[11];\n r[10] = a[2]*b[8] + a[6]*b[9] + a[10]*b[10] + a[14]*b[11];\n r[11] = a[3]*b[8] + a[7]*b[9] + a[11]*b[10] + a[15]*b[11];\n\n r[12] = a[0]*b[12] + a[4]*b[13] + a[8]*b[14] + a[12]*b[15];\n r[13] = a[1]*b[12] + a[5]*b[13] + a[9]*b[14] + a[13]*b[15];\n r[14] = a[2]*b[12] + a[6]*b[13] + a[10]*b[14] + a[14]*b[15];\n r[15] = a[3]*b[12] + a[7]*b[13] + a[11]*b[14] + a[15]*b[15];\n}\n\nfunction matInv(m, t) {\n var s = 1.0/(\n m[12]* m[9]*m[6]*m[3]-m[8]*m[13]*m[6]*m[3]-m[12]*m[5]*m[10]*m[3]+m[4]*m[13]*m[10]*m[3]+\n m[8]*m[5]*m[14]*m[3]-m[4]*m[9]*m[14]*m[3]-m[12]*m[9]*m[2]*m[7]+m[8]*m[13]*m[2]*m[7]+\n m[12]*m[1]*m[10]*m[7]-m[0]*m[13]*m[10]*m[7]-m[8]*m[1]*m[14]*m[7]+m[0]*m[9]*m[14]*m[7]+\n m[12]*m[5]*m[2]*m[11]-m[4]*m[13]*m[2]*m[11]-m[12]*m[1]*m[6]*m[11]+m[0]*m[13]*m[6]*m[11]+\n m[4]*m[1]*m[14]*m[11]-m[0]*m[5]*m[14]*m[11]-m[8]*m[5]*m[2]*m[15]+m[4]*m[9]*m[2]*m[15]+\n m[8]*m[1]*m[6]*m[15]-m[0]*m[9]*m[6]*m[15]-m[4]*m[1]*m[10]*m[15]+m[0]*m[5]*m[10]*m[15]\n );\n\n t[ 0] = (m[9]*m[14]*m[7]-m[13]*m[10]*m[7]+m[13]*m[6]*m[11]-m[5]*m[14]*m[11]-m[9]*m[6]*m[15]+m[5]*m[10]*m[15])*s;\n t[ 1] = (m[13]*m[10]*m[3]-m[9]*m[14]*m[3]-m[13]*m[2]*m[11]+m[1]*m[14]*m[11]+m[9]*m[2]*m[15]-m[1]*m[10]*m[15])*s;\n t[ 2] = (m[5]*m[14]*m[3]-m[13]*m[6]*m[3]+m[13]*m[2]*m[7]-m[1]*m[14]*m[7]-m[5]*m[2]*m[15]+m[1]*m[6]*m[15])*s;\n t[ 3] = (m[9]*m[6]*m[3]-m[5]*m[10]*m[3]-m[9]*m[2]*m[7]+m[1]*m[10]*m[7]+m[5]*m[2]*m[11]-m[1]*m[6]*m[11])*s;\n\n t[ 4] = (m[12]*m[10]*m[7]-m[8]*m[14]*m[7]-m[12]*m[6]*m[11]+m[4]*m[14]*m[11]+m[8]*m[6]*m[15]-m[4]*m[10]*m[15])*s;\n t[ 5] = (m[8]*m[14]*m[3]-m[12]*m[10]*m[3]+m[12]*m[2]*m[11]-m[0]*m[14]*m[11]-m[8]*m[2]*m[15]+m[0]*m[10]*m[15])*s;\n t[ 6] = (m[12]*m[6]*m[3]-m[4]*m[14]*m[3]-m[12]*m[2]*m[7]+m[0]*m[14]*m[7]+m[4]*m[2]*m[15]-m[0]*m[6]*m[15])*s;\n t[ 7] = (m[4]*m[10]*m[3]-m[8]*m[6]*m[3]+m[8]*m[2]*m[7]-m[0]*m[10]*m[7]-m[4]*m[2]*m[11]+m[0]*m[6]*m[11])*s;\n\n t[ 8] = (m[8]*m[13]*m[7]-m[12]*m[9]*m[7]+m[12]*m[5]*m[11]-m[4]*m[13]*m[11]-m[8]*m[5]*m[15]+m[4]*m[9]*m[15])*s;\n t[ 9] = (m[12]*m[9]*m[3]-m[8]*m[13]*m[3]-m[12]*m[1]*m[11]+m[0]*m[13]*m[11]+m[8]*m[1]*m[15]-m[0]*m[9]*m[15])*s;\n t[10] = (m[4]*m[13]*m[3]-m[12]*m[5]*m[3]+m[12]*m[1]*m[7]-m[0]*m[13]*m[7]-m[4]*m[1]*m[15]+m[0]*m[5]*m[15])*s;\n t[11] = (m[8]*m[5]*m[3]-m[4]*m[9]*m[3]-m[8]*m[1]*m[7]+m[0]*m[9]*m[7]+m[4]*m[1]*m[11]-m[0]*m[5]*m[11])*s;\n\n t[12] = (m[12]*m[9]*m[6]-m[8]*m[13]*m[6]-m[12]*m[5]*m[10]+m[4]*m[13]*m[10]+m[8]*m[5]*m[14]-m[4]*m[9]*m[14])*s;\n t[13] = (m[8]*m[13]*m[2]-m[12]*m[9]*m[2]+m[12]*m[1]*m[10]-m[0]*m[13]*m[10]-m[8]*m[1]*m[14]+m[0]*m[9]*m[14])*s;\n t[14] = (m[12]*m[5]*m[2]-m[4]*m[13]*m[2]-m[12]*m[1]*m[6]+m[0]*m[13]*m[6]+m[4]*m[1]*m[14]-m[0]*m[5]*m[14])*s;\n t[15] = (m[4]*m[9]*m[2]-m[8]*m[5]*m[2]+m[8]*m[1]*m[6]-m[0]*m[9]*m[6]-m[4]*m[1]*m[10]+m[0]*m[5]*m[10])*s;\n}\n\nlet binary = { \n getUint64: getUint64,\n getUint32: getUint32,\n getUint16: getUint16,\n getFloat32: getFloat32,\n vecMul: vecMul,\n matMul: matMul,\n matInv: matInv\n}\n\n\n\n\n\n//# sourceURL=webpack://Nexus3D/./src/Binary.js?\n}"); -/***/ }), +/***/ }, -/***/ "./src/Cache.js": +/***/ "./src/Cache.js" /*!**********************!*\ !*** ./src/Cache.js ***! \**********************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { +(__unused_webpack_module, __webpack_exports__, __webpack_require__) { -eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ Cache: () => (/* binding */ Cache)\n/* harmony export */ });\n\nfunction powerOf2(n) {\n\treturn n && (n & (n - 1)) === 0;\n}\n\nvar targetError = 2.0; //error won't go lower than this if we reach it\nvar maxError = 15; //error won't go over this even if fps is low\nvar minFps = 15;\nvar maxPending = 3;\nvar maxBlocked = 3;\nvar maxReqAttempt = 2;\nvar maxCacheSize = 1024 *(1<<20); \nvar drawBudget = 5*(1<<20);\n\nfunction _Cache() {\n let t = this;\n t.cortopath = '.';\n\tt.frame = 0; //keep track of the time\n\n t.maxCacheSize = maxCacheSize;\n t.minFps = minFps;\n t.currentFps = 0;\n t.targetError = targetError;\n t.currentError = targetError;\n t.maxError = maxError;\n t.realError = 0;\n\n t.pending = 0;\n t.maxPending = 3;\n t.cacheSize = 0;\n t.candidates = []; //list of nodes to be loaded\n t.nodes = new Map(); //for each mesh a list of node ids.\n \n t.last_frametime = 0;\n t.frametime = 0;\n t.end_frametime = 0;\n\n t.debug = { \n verbose : false, //debug messages\n nodes : false, //color each node\n draw : false, //final rendering call disabled\n extract : false, //extraction disabled}\n }\n\n t.totswapped = 0; //in the last frame.\n t.swaprate = 0;\n t.lastupdate = performance.now();\n}\n\n_Cache.prototype = {\n getTargetError: function() { return this.targetError; },\n getMinFps: function() { return this.minFps; },\n setMinFps: function(fps) { this.minFps = fps; },\n getMaxCacheSize: function() { return this.maxCacheSize; },\n setMaxCacheSize: function(size) { this.maxCacheSize = size; },\n setTargetError: function(error) { this.targetError = error; },\n \n loadCorto: function() {\n let corto = new Worker(this.cortopath + '/corto.em.js');\n corto.requests = {};\n corto.count = 0;\n corto.postRequest = function(node) {\n corto.postMessage({ buffer: node.buffer, request:this.count, rgba_colors: true, short_index: true, short_normals: true});\n node.buffer = null;\n this.requests[this.count++] = node;\n }\n corto.onmessage = function(e) {\n var request = e.data.request;\n var node = this.requests[request];\n delete this.requests[request];\n node.model = e.data.model;\n node.cache.readyGeometryNode(node.mesh, node.id, node.model);\n };\n this.corto = corto;\n },\n\n \n beginFrame: function(fps) { //each context has a separate frame count.\n let c = this;\n c.frametime = performance.now();\n let elapsed = c.frametime - c.last_frametime;\n c.last_frametime = c.frametime;\n if(elapsed < 500)\n c.currentFps = 0.9*c.currentFps + 0.1*(1000/elapsed);\n\n fps = c.currentFps;\n\n\t c.frame++;\n\t c.candidates = [];\n\t if(fps && c.minFps) {\n\t\t c.currentFps = fps;\n\t\t const r = c.minFps/fps;\n\t\t if(r > 1.1)\n\t\t\t c.currentError *= 1.05;\n\t\t if(r < 0.9)\n\t\t\t c.currentError *= 0.95;\n\n\t\t c.currentError = Math.max(c.targetError, Math.min(c.maxError, c.currentError));\n\n\t } else\n\t\t c.currentError = c.targetError;\n\n\t c.rendered = 0;\n\t c.realError = 1e20;\n c.totswapped = 0;\n },\n\n endFrame: function() {\n\t this.update();\n },\n\n\n\n requestNode: function(mesh, id) {\n\t mesh.status[id] = 2; \n\n\t \n\t this.cacheSize += mesh.nsize[id];\n\t mesh.reqAttempt[id] = 0;\n\n\t\tif(!this.nodes.has(mesh))\n\t\t\tthis.nodes.set(mesh, new Set());\n\t\tthis.nodes.get(mesh).add(id);\n\n\t this.requestNodeGeometry(mesh, id);\n\t this.requestNodeTexture(mesh, id);\n },\n\n requestNodeGeometry: function(mesh, id) {\n\t\tthis.pending++;\n\t mesh.status[id]++;\n\t\tlet url = mesh.deepzoom ? mesh.baseurl + id + \".nxn\" : mesh.url;\n\t\tlet start = mesh.deepzoom ? 0 : mesh.noffsets[id];\n\t\tlet end = mesh.deepzoom ? 0 : mesh.noffsets[id+1];\n\t let request = mesh.georeq[id] = mesh.httpRequest(url,\n\t\t start,\n\t\t end, \n\t\t ()=>{\n delete mesh.georeq[id];\n this.loadNodeGeometry(request, mesh, id);\n\t\t\t\tthis.pending--;\n },\n\t\t ()=>{\n delete mesh.georeq[id];\n\t\t\t if(this.debug.verbose) console.log(\"Geometry request error!\");\n\t\t\t this.recoverNode(mesh, id, 0);\n\t\t\t\tthis.pending--;\n\t\t },\n\t\t ()=>{\n delete mesh.georeq[id];\n\t\t\t if(this.debug.verbose) console.log(\"Geometry request abort!\");\n\t\t\t this.removeNode(mesh, id);\n\t\t\t\tthis.pending--;\n\t\t },\n\t\t 'arraybuffer'\n );\n },\n\n requestNodeTexture: function(mesh, id) {\n\n\t if(!mesh.vertex.texCoord) return;\n\n\t let tex = mesh.patches[mesh.nfirstpatch[id]*3+2];\n\t mesh.texref[tex]++;\n\t mesh.status[id]++;\n\n\t\tlet url = mesh.deepzoom ? mesh.baseurl + tex + \".jpg\" : mesh.url;\n\t\tlet start = mesh.deepzoom ? 0 : mesh.textures[tex];\n\t\tlet end = mesh.deepzoom ? 0 : mesh.textures[tex+1];\n\t let request = mesh.texreq[tex] = mesh.httpRequest(url,\n\t\t start,\n\t\t end,\n\t\t ()=>{ \n delete mesh.texreq[tex];\n this.loadNodeTexture(request, mesh, id, tex); \n\n },\n\t\t ()=>{\n delete mesh.texreq[tex];\n\t\t \tif(this.debug.verbose) console.log(\"Texture request error!\");\n\t\t\t this.recoverNode(mesh, id, 1);\n\t\t },\n\t\t ()=>{\n delete mesh.texreq[tex];\n\t\t \tif(this.debug.verbose) console.log(\"Texture request abort!\");\n\t\t \tthis.removeNode(mesh, id);\n\t\t },\n\t\t 'blob'\n\t );\n },\n\n recoverNode: function(mesh, id, mode) {\n\t if(mesh.status[id] == 0) return;\n\n\t mesh.status[id]--;\n\n let t = this;\n\n\t if(mesh.reqAttempt[id] > maxReqAttempt) {\n\t\t if(this.debug.verbose) console.log(\"Max request limit for \" + m.url + \" node: \" + n);\n\t\t t.removeNode(mesh, id);\n\t\t return;\n\t }\n\n\t mesh.reqAttempt[id]++;\n\n\t switch (mode){\n\t\tcase 0:\n\t\t t.requestNodeGeometry(mesh, id);\n\t\t if(this.debug.verbose) console.log(\"Recovering geometry for \" + m.url + \" node: \" + n);\n\t\t break;\n\t\tcase 1:\n\t\t\tt.requestNodeTexture(mesh, id);\n\t\t\tif(this.debug.verbose) console.log(\"Recovering texture for \" + m.url + \" node: \" + n);\n\t\t\tbreak;\n\t }\n },\n\n loadNodeGeometry: function(request, mesh, id) {\n\t if(mesh.status[id] == 0) return;\n \n\t if(!mesh.compressed)\n\t\t this.readyGeometryNode(mesh, id, request.response);\n\t else {\n if(!this.corto) this.loadCorto();\n\t\t this.corto.postRequest( { mesh:mesh, id:id, buffer: request.response, cache: this });\n }\n },\n\n\n loadNodeTexture: function(request, mesh, id, texid) {\n if(mesh.status[id] == 0) {\n throw \"Should not load texture twice\";\n }\n\n\t let blob = request.response;\n let callback = (img) => {\n if(mesh.status[id] == 0) //call was aborted.\n return;\n mesh.createTexture(texid, img);\n\n\t\t mesh.status[id]--;\n\n if(mesh.status[id] == 2)\n this.readyNode(mesh, id);\n\t\t }\n\n if(typeof createImageBitmap != 'undefined') {\n\t\t\ttry {\n\t\t\t\tcreateImageBitmap(blob, { imageOrientation: 'flipY' }).then(callback);\n\t\t\t} catch(error) { //old version of firefox do not support options (but flip anyway)\n\t\t\t\tcreateImageBitmap(blob).then(callback);\n\t\t\t}\n\n } else { //fallback for IOS\n var urlCreator = window.URL || window.webkitURL;\n var img = document.createElement('img');\n img.onerror = function(e) { console.log(\"Texture loading error!\"); };\n img.src = urlCreator.createObjectURL(blob);\n \n img.onload = function() {\n urlCreator.revokeObjectURL(img.src);\n callback(img);\n }\n }\n },\n\n removeNode: function(mesh, id) {\n this.nodes.get(mesh).delete(id);\n\n if(mesh.status[id] == 0) {\n\t\t\tif(this.debug.verbose)\n\t\t\t\tconsole.log(\"Double remove due to abort.\");\n return; //TODO\n\t\t}\n\n\t mesh.status[id] = 0;\n\t if (id in mesh.georeq && mesh.georeq[id].readyState != 4) {\n mesh.georeq[id].abort();\n delete mesh.georeq[id];\n\t\t //this.pending--;\n\t }\n mesh.availableNodes--;\n this.cacheSize -= mesh.nsize[id];\n mesh.deleteNodeGeometry(id);\n\n\t if(!mesh.vertex.texCoord) return;\n\n\t const tex = mesh.patches[mesh.nfirstpatch[id]*3+2]; //TODO assuming one texture per node\n\n\t if (tex in mesh.texreq && mesh.texreq[tex].readyState != 4) {\n mesh.texreq[tex].abort();\n delete mesh.texreq[tex];\n }\n\n\t mesh.texref[tex]--;\n \tif(mesh.texref[tex] == 0) {\n mesh.deleteTexture(tex);\n }\n },\n\n readyGeometryNode: function(mesh, id, buffer) {\n if(mesh.status[id] == 0) //call was aborted\n return;\n const nv = mesh.nvertices[id];\n const nf = mesh.nfaces[id];\n\t let geometry = {};\n\n\t \n\t if(!mesh.corto) {\n\t\t geometry.index = new Uint16Array(buffer, nv*mesh.vsize, nf*3);\n geometry.position = new Float32Array(buffer, 0, nv*3);\n\n\t\t var off = nv*12;\n\t\t if(mesh.vertex.texCoord) {\n geometry.uv = new Float32Array(buffer, off, nv*2);\n off += nv*8;\n }\n if(mesh.vertex.normal) {\n\t\t\t\tgeometry.normal = new Int16Array(buffer, off, nv*3);\n off += nv*6;\n\t\t\t}\n\n if(mesh.vertex.color) {\n geometry.color = new Uint8Array(buffer, off, nv*4);\n off += nv*4;\n\t\t\t}\n\n\t\t\n\t } else {\n geometry = buffer;\n\t }\n\n\t //if(nf == 0)\n \t//\tscramble(nv, v, no, co);\n\n mesh.createNodeGeometry(id, geometry);\n\t mesh.status[id]--;\n\n\t if(mesh.status[id] == 2) {\n this.readyNode(mesh, id);\n\t }\n },\n\n //the node is finished, add to cache, and update counters\n readyNode: function(mesh, id) {\n\n\t mesh.status[id]--;\n if(mesh.status[id] != 1) throw \"A ready node should have status ==1\"\n\n\t\t\n\t\tmesh.reqAttempt[id] = 0;\n //this.pending--;\n mesh.createNode(id);\n\t\tmesh.availableNodes++;\n\n for(let callback of mesh.onUpdate)\n callback();\n this.update();\n },\n\n flush: function(mesh) {\n if(!this.nodes.has(mesh)) return;\n\n for(let id of this.nodes.get(mesh))\n this.removeNode(mesh, id);\n this.nodes.delete(mesh);\n }, \n\n update: function() {\n if(this.pending >= maxPending)\n return;\n\n //the best candidate has the highest error\n let best = null;\n for(let c of this.candidates) {\n if(c.mesh.status[c.id] == 0 && (!best || c.error > best.error)) \n best = c;\n\t }\n if(!best) return;\n\n\t// record amount of data transfer per second.\n /*\n let now = performance.now();\n if(Math.floor(now/1000) > Math.floor(this.lastupdate/1000)) { //new second\n this.swaprate = (this.totswapped/1000)/(now - this.lastupdate); //transfer in mb/s\n if(this.debug.verbose)\n console.log(\"Memory loaded in GPU: \", this.swaprate);\n this.totswapped = 0;\n this.lastupdate = now;\n }\n */\n \n \n //make room for new nodes!\n \n\t while(this.cacheSize > this.maxCacheSize) {\n let worst = null;\n \n //find node with smallest error in cache and remove it if worse than the best candidate.\n for(let [mesh, ids] of this.nodes) {\n for(let id of ids) {\n //we need to recompute the errors for the cache, as if not traversed doesn't get updated.\n let error = mesh.errors[id];\n let frame = mesh.frames[id];\n if( !worst || error < worst.error) {\n worst = { id: id, frame: frame, error: error, mesh: mesh }\n }\n }\n }\n if(!worst || worst.error >= best.error*0.9) {\n //(worst.frame + 30 >= best.frame && )) //dont' remove if the best candidate is not good enogh\n return;\n }\n\t\t this.removeNode(worst.mesh, worst.id);\n\t }\n this.totswapped += best.mesh.nsize[best.id];\n this.candidates = this.candidates.filter(e => e.mesh == best.mesh && e.id == best.id);\n \tthis.requestNode(best.mesh, best.id);\n\t this.update(); //try again.\n }\n};\n\nlet Cache = new _Cache;\n\n\n\n\n//# sourceURL=webpack://Nexus3D/./src/Cache.js?\n}"); +eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ Cache: () => (/* binding */ Cache)\n/* harmony export */ });\n\nfunction powerOf2(n) {\n\treturn n && (n & (n - 1)) === 0;\n}\n\nvar targetError = 2.0; //error won't go lower than this if we reach it\nvar maxError = 15; //error won't go over this even if fps is low\nvar minFps = 15;\nvar maxPending = 3;\nvar maxBlocked = 3;\nvar maxReqAttempt = 2;\nvar maxCacheSize = 1024 *(1<<20); \nvar drawBudget = 5*(1<<20);\n\nfunction _Cache() {\n let t = this;\n t.cortopath = '.';\n\tt.frame = 0; //keep track of the time\n\n t.maxCacheSize = maxCacheSize;\n t.minFps = minFps;\n t.currentFps = 0;\n t.targetError = targetError;\n t.currentError = targetError;\n t.maxError = maxError;\n t.realError = 0;\n\n t.pending = 0;\n t.maxPending = 3;\n t.cacheSize = 0;\n t.candidates = []; //list of nodes to be loaded\n t.nodes = new Map(); //for each mesh a list of node ids.\n \n t.last_frametime = 0;\n t.frametime = 0;\n t.end_frametime = 0;\n\n t.debug = { \n verbose : false, //debug messages\n nodes : false, //color each node\n draw : false, //final rendering call disabled\n extract : false, //extraction disabled}\n }\n\n t.totswapped = 0; //in the last frame.\n t.swaprate = 0;\n t.lastupdate = performance.now();\n}\n\n_Cache.prototype = {\n getTargetError: function() { return this.targetError; },\n getMinFps: function() { return this.minFps; },\n setMinFps: function(fps) { this.minFps = fps; },\n getMaxCacheSize: function() { return this.maxCacheSize; },\n setMaxCacheSize: function(size) { this.maxCacheSize = size; },\n setTargetError: function(error) { this.targetError = error; },\n \n loadCorto: function() {\n let workerPath = this.cortopath + 'corto.worker.js'; \n try {\n workerPath = new URL(/* asset import */ Object(function webpackMissingModule() { var e = new Error(\"Cannot find module './corto.worker.js'\"); e.code = 'MODULE_NOT_FOUND'; throw e; }()), __webpack_require__.b).href;\n } catch (e) {\n //reverrt to es if import.meta.url is not available\n }\n let corto = new Worker(workerPath);\n corto.requests = {};\n corto.count = 0;\n corto.postRequest = function(node) {\n corto.postMessage({ buffer: node.buffer, request:this.count, rgba_colors: true, short_index: true, short_normals: true});\n node.buffer = null;\n this.requests[this.count++] = node;\n }\n corto.onmessage = function(e) {\n var request = e.data.request;\n var node = this.requests[request];\n delete this.requests[request];\n node.model = e.data.model;\n node.cache.readyGeometryNode(node.mesh, node.id, node.model);\n };\n this.corto = corto;\n },\n\n \n beginFrame: function(fps) { //each context has a separate frame count.\n let c = this;\n c.frametime = performance.now();\n let elapsed = c.frametime - c.last_frametime;\n c.last_frametime = c.frametime;\n if(elapsed < 500)\n c.currentFps = 0.9*c.currentFps + 0.1*(1000/elapsed);\n\n fps = c.currentFps;\n\n\t c.frame++;\n\t c.candidates = [];\n\t if(fps && c.minFps) {\n\t\t c.currentFps = fps;\n\t\t const r = c.minFps/fps;\n\t\t if(r > 1.1)\n\t\t\t c.currentError *= 1.05;\n\t\t if(r < 0.9)\n\t\t\t c.currentError *= 0.95;\n\n\t\t c.currentError = Math.max(c.targetError, Math.min(c.maxError, c.currentError));\n\n\t } else\n\t\t c.currentError = c.targetError;\n\n\t c.rendered = 0;\n\t c.realError = 1e20;\n c.totswapped = 0;\n },\n\n endFrame: function() {\n\t this.update();\n },\n\n\n\n requestNode: function(mesh, id) {\n\t mesh.status[id] = 2; \n\n\t \n\t this.cacheSize += mesh.nsize[id];\n\t mesh.reqAttempt[id] = 0;\n\n\t\tif(!this.nodes.has(mesh))\n\t\t\tthis.nodes.set(mesh, new Set());\n\t\tthis.nodes.get(mesh).add(id);\n\n\t this.requestNodeGeometry(mesh, id);\n\t this.requestNodeTexture(mesh, id);\n },\n\n requestNodeGeometry: function(mesh, id) {\n\t\tthis.pending++;\n\t mesh.status[id]++;\n\t\tlet url = mesh.deepzoom ? mesh.baseurl + id + \".nxn\" : mesh.url;\n\t\tlet start = mesh.deepzoom ? 0 : mesh.noffsets[id];\n\t\tlet end = mesh.deepzoom ? 0 : mesh.noffsets[id+1];\n\t let request = mesh.georeq[id] = mesh.httpRequest(url,\n\t\t start,\n\t\t end, \n\t\t ()=>{\n delete mesh.georeq[id];\n this.loadNodeGeometry(request, mesh, id);\n\t\t\t\tthis.pending--;\n },\n\t\t ()=>{\n delete mesh.georeq[id];\n\t\t\t if(this.debug.verbose) console.log(\"Geometry request error!\");\n\t\t\t this.recoverNode(mesh, id, 0);\n\t\t\t\tthis.pending--;\n\t\t },\n\t\t ()=>{\n delete mesh.georeq[id];\n\t\t\t if(this.debug.verbose) console.log(\"Geometry request abort!\");\n\t\t\t this.removeNode(mesh, id);\n\t\t\t\tthis.pending--;\n\t\t },\n\t\t 'arraybuffer'\n );\n },\n\n requestNodeTexture: function(mesh, id) {\n\n\t if(!mesh.vertex.texCoord) return;\n\n\t let tex = mesh.patches[mesh.nfirstpatch[id]*3+2];\n\t mesh.texref[tex]++;\n\t mesh.status[id]++;\n\n\t\tlet url = mesh.deepzoom ? mesh.baseurl + tex + \".jpg\" : mesh.url;\n\t\tlet start = mesh.deepzoom ? 0 : mesh.textures[tex];\n\t\tlet end = mesh.deepzoom ? 0 : mesh.textures[tex+1];\n\t let request = mesh.texreq[tex] = mesh.httpRequest(url,\n\t\t start,\n\t\t end,\n\t\t ()=>{ \n delete mesh.texreq[tex];\n this.loadNodeTexture(request, mesh, id, tex); \n\n },\n\t\t ()=>{\n delete mesh.texreq[tex];\n\t\t \tif(this.debug.verbose) console.log(\"Texture request error!\");\n\t\t\t this.recoverNode(mesh, id, 1);\n\t\t },\n\t\t ()=>{\n delete mesh.texreq[tex];\n\t\t \tif(this.debug.verbose) console.log(\"Texture request abort!\");\n\t\t \tthis.removeNode(mesh, id);\n\t\t },\n\t\t 'blob'\n\t );\n },\n\n recoverNode: function(mesh, id, mode) {\n\t if(mesh.status[id] == 0) return;\n\n\t mesh.status[id]--;\n\n let t = this;\n\n\t if(mesh.reqAttempt[id] > maxReqAttempt) {\n\t\t if(this.debug.verbose) console.log(\"Max request limit for \" + m.url + \" node: \" + n);\n\t\t t.removeNode(mesh, id);\n\t\t return;\n\t }\n\n\t mesh.reqAttempt[id]++;\n\n\t switch (mode){\n\t\tcase 0:\n\t\t t.requestNodeGeometry(mesh, id);\n\t\t if(this.debug.verbose) console.log(\"Recovering geometry for \" + m.url + \" node: \" + n);\n\t\t break;\n\t\tcase 1:\n\t\t\tt.requestNodeTexture(mesh, id);\n\t\t\tif(this.debug.verbose) console.log(\"Recovering texture for \" + m.url + \" node: \" + n);\n\t\t\tbreak;\n\t }\n },\n\n loadNodeGeometry: function(request, mesh, id) {\n\t if(mesh.status[id] == 0) return;\n \n\t if(!mesh.compressed)\n\t\t this.readyGeometryNode(mesh, id, request.response);\n\t else {\n if(!this.corto) this.loadCorto();\n\t\t this.corto.postRequest( { mesh:mesh, id:id, buffer: request.response, cache: this });\n }\n },\n\n\n loadNodeTexture: function(request, mesh, id, texid) {\n if(mesh.status[id] == 0) {\n throw \"Should not load texture twice\";\n }\n\n\t let blob = request.response;\n let callback = (img) => {\n if(mesh.status[id] == 0) //call was aborted.\n return;\n mesh.createTexture(texid, img);\n\n\t\t mesh.status[id]--;\n\n if(mesh.status[id] == 2)\n this.readyNode(mesh, id);\n\t\t }\n\n if(typeof createImageBitmap != 'undefined') {\n\t\t\ttry {\n\t\t\t\tcreateImageBitmap(blob, { imageOrientation: 'flipY' }).then(callback);\n\t\t\t} catch(error) { //old version of firefox do not support options (but flip anyway)\n\t\t\t\tcreateImageBitmap(blob).then(callback);\n\t\t\t}\n\n } else { //fallback for IOS\n var urlCreator = window.URL || window.webkitURL;\n var img = document.createElement('img');\n img.onerror = function(e) { console.log(\"Texture loading error!\"); };\n img.src = urlCreator.createObjectURL(blob);\n \n img.onload = function() {\n urlCreator.revokeObjectURL(img.src);\n callback(img);\n }\n }\n },\n\n removeNode: function(mesh, id) {\n this.nodes.get(mesh).delete(id);\n\n if(mesh.status[id] == 0) {\n\t\t\tif(this.debug.verbose)\n\t\t\t\tconsole.log(\"Double remove due to abort.\");\n return; //TODO\n\t\t}\n\n\t mesh.status[id] = 0;\n\t if (id in mesh.georeq && mesh.georeq[id].readyState != 4) {\n mesh.georeq[id].abort();\n delete mesh.georeq[id];\n\t\t //this.pending--;\n\t }\n mesh.availableNodes--;\n this.cacheSize -= mesh.nsize[id];\n mesh.deleteNodeGeometry(id);\n\n\t if(!mesh.vertex.texCoord) return;\n\n\t const tex = mesh.patches[mesh.nfirstpatch[id]*3+2]; //TODO assuming one texture per node\n\n\t if (tex in mesh.texreq && mesh.texreq[tex].readyState != 4) {\n mesh.texreq[tex].abort();\n delete mesh.texreq[tex];\n }\n\n\t mesh.texref[tex]--;\n \tif(mesh.texref[tex] == 0) {\n mesh.deleteTexture(tex);\n }\n },\n\n readyGeometryNode: function(mesh, id, buffer) {\n if(mesh.status[id] == 0) //call was aborted\n return;\n const nv = mesh.nvertices[id];\n const nf = mesh.nfaces[id];\n\t let geometry = {};\n\n\t \n\t if(!mesh.corto) {\n\t\t geometry.index = new Uint16Array(buffer, nv*mesh.vsize, nf*3);\n geometry.position = new Float32Array(buffer, 0, nv*3);\n\n\t\t var off = nv*12;\n\t\t if(mesh.vertex.texCoord) {\n geometry.uv = new Float32Array(buffer, off, nv*2);\n off += nv*8;\n }\n if(mesh.vertex.normal) {\n\t\t\t\tgeometry.normal = new Int16Array(buffer, off, nv*3);\n off += nv*6;\n\t\t\t}\n\n if(mesh.vertex.color) {\n geometry.color = new Uint8Array(buffer, off, nv*4);\n off += nv*4;\n\t\t\t}\n\n\t\t\n\t } else {\n geometry = buffer;\n\t }\n\n\t //if(nf == 0)\n \t//\tscramble(nv, v, no, co);\n\n mesh.createNodeGeometry(id, geometry);\n\t mesh.status[id]--;\n\n\t if(mesh.status[id] == 2) {\n this.readyNode(mesh, id);\n\t }\n },\n\n //the node is finished, add to cache, and update counters\n readyNode: function(mesh, id) {\n\n\t mesh.status[id]--;\n if(mesh.status[id] != 1) throw \"A ready node should have status ==1\"\n\n\t\t\n\t\tmesh.reqAttempt[id] = 0;\n //this.pending--;\n mesh.createNode(id);\n\t\tmesh.availableNodes++;\n\n for(let callback of mesh.onUpdate)\n callback();\n this.update();\n },\n\n flush: function(mesh) {\n if(!this.nodes.has(mesh)) return;\n\n for(let id of this.nodes.get(mesh))\n this.removeNode(mesh, id);\n this.nodes.delete(mesh);\n }, \n\n update: function() {\n if(this.pending >= maxPending)\n return;\n\n //the best candidate has the highest error\n let best = null;\n for(let c of this.candidates) {\n if(c.mesh.status[c.id] == 0 && (!best || c.error > best.error)) \n best = c;\n\t }\n if(!best) return;\n\n\t// record amount of data transfer per second.\n /*\n let now = performance.now();\n if(Math.floor(now/1000) > Math.floor(this.lastupdate/1000)) { //new second\n this.swaprate = (this.totswapped/1000)/(now - this.lastupdate); //transfer in mb/s\n if(this.debug.verbose)\n console.log(\"Memory loaded in GPU: \", this.swaprate);\n this.totswapped = 0;\n this.lastupdate = now;\n }\n */\n \n \n //make room for new nodes!\n \n\t while(this.cacheSize > this.maxCacheSize) {\n let worst = null;\n \n //find node with smallest error in cache and remove it if worse than the best candidate.\n for(let [mesh, ids] of this.nodes) {\n for(let id of ids) {\n //we need to recompute the errors for the cache, as if not traversed doesn't get updated.\n let error = mesh.errors[id];\n let frame = mesh.frames[id];\n if( !worst || error < worst.error) {\n worst = { id: id, frame: frame, error: error, mesh: mesh }\n }\n }\n }\n if(!worst || worst.error >= best.error*0.9) {\n //(worst.frame + 30 >= best.frame && )) //dont' remove if the best candidate is not good enogh\n return;\n }\n\t\t this.removeNode(worst.mesh, worst.id);\n\t }\n this.totswapped += best.mesh.nsize[best.id];\n this.candidates = this.candidates.filter(e => e.mesh == best.mesh && e.id == best.id);\n \tthis.requestNode(best.mesh, best.id);\n\t this.update(); //try again.\n }\n};\n\nlet Cache = new _Cache;\n\n\n\n\n//# sourceURL=webpack://Nexus3D/./src/Cache.js?\n}"); -/***/ }), +/***/ }, -/***/ "./src/Mesh.js": +/***/ "./src/Mesh.js" /*!*********************!*\ !*** ./src/Mesh.js ***! \*********************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { +(__unused_webpack_module, __webpack_exports__, __webpack_require__) { eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ Mesh: () => (/* binding */ Mesh)\n/* harmony export */ });\n/* harmony import */ var _Binary_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./Binary.js */ \"./src/Binary.js\");\n\n\n/* MESH DEFINITION */\nlet Debug = { verbose: true };\n\nlet glP = WebGLRenderingContext.prototype;\nlet attrGlMap = [glP.NONE, glP.BYTE, glP.UNSIGNED_BYTE, glP.SHORT, glP.UNSIGNED_SHORT, glP.INT, glP.UNSIGNED_INT, glP.FLOAT, glP.DOUBLE];\nlet attrSizeMap = [0, 1, 1, 2, 2, 4, 4, 4, 8];\n\n//All addresses in the file are n*256 so, 256 * 2^32 is the max size of a Nxs file \nvar padding = 256;\n\nlet Mesh = function(url) {\n var t = this;\n t.isReady = false;\n t.onLoad = [];\n t.onUpdate = [];\n t.reqAttempt = 0;\n t.georeq = {}; //keeps track of existing httprequests\n t.texreq = {};\n t.frame = 0; //last time this mesh was traversed in rendering.\n t.availableNodes = 0\n if(url)\n t.open(url);\n}\n\nMesh.prototype = {\n open: function(url) {\n let mesh = this;\n mesh.url = url;\n mesh.httpRequest(url,\n 0,\n 88,\n function() {\n if(Debug.verbose) console.log(\"Loading header for \" + mesh.url);\n let view = new DataView(this.response);\n view.offset = 0;\n mesh.reqAttempt++;\n const header = mesh.importHeader(view);\n if(!header) {\n if(Debug.verbose) console.log(\"Empty header!\");\n if(mesh.reqAttempt < maxReqAttempt) mesh.open(mesh.url + '?' + Math.random()); // BLINK ENGINE CACHE BUG PATCH\n return;\n }\n mesh.reqAttempt = 0;\n for(let i in header)\n mesh[i] = header[i];\n mesh.vertex = mesh.signature.vertex;\n mesh.face = mesh.signature.face;\n mesh.renderMode = mesh.face.index?[\"FILL\", \"POINT\"]:[\"POINT\"];\n mesh.compressed = (mesh.signature.flags & (2 | 4)); //meco or corto\n mesh.meco = (mesh.signature.flags & 2);\n mesh.corto = (mesh.signature.flags & 4);\n\n\t\t\t\tmesh.deepzoom = (mesh.signature.flags & 8);\n\t\t\t\tif(mesh.deepzoom)\n\t\t\t\t\tmesh.baseurl = url.substr(0, url.length -4) + '_files/';\n\n\n mesh.requestIndex();\n },\n function() { console.log(\"Open request error!\");},\n function() { console.log(\"Open request abort!\");}\n );\n },\n\n httpRequest: function(url, start, end, load, error, abort, type) {\n if(!type) type = 'arraybuffer';\n var r = new XMLHttpRequest();\n r.open('GET', url, true);\n r.responseType = type;\n\t\tif(end)\n\t\t\tr.setRequestHeader(\"Range\", \"bytes=\" + start + \"-\" + (end -1));\n r.onload = function(){\n switch (this.status){\n case 0:\n//\t\t\t\t\tconsole.log(\"0 response: server unreachable.\");//returned in chrome for local files\n\t\t\t\t\terror();\n\t\t\t\t\tbreak;\n case 206:\n//\t\t\t\t\tconsole.log(\"206 response: partial content loaded.\");\n load.bind(this)();\n break;\n case 200:\n//\t\t\t\t\tconsole.log(\"200 response: server does not support byte range requests.\");\n\t\t\t\t\tif(end == 0)\n\t\t\t\t\t\tload.bind(this)();\n\t\t\t\t\telse\n\t\t\t\t\t\terror();\n\t\t\t\t\tbreak;\n }\n };\n r.onerror = error;\n r.onabort = abort;\n r.send();\n return r;\n },\n\n requestIndex: function() {\n var mesh = this;\n var end = 88 + mesh.nodesCount*44 + mesh.patchesCount*12 + mesh.texturesCount*68;\n mesh.httpRequest(this.url,\n 88,\n end,\n function() { if(Debug.verbose) console.log(\"Loading index for \" + mesh.url); mesh.handleIndex(this.response); },\n function() { console.log(\"Index request error!\");},\n function() { console.log(\"Index request abort!\");}\n );\n },\n\n handleIndex: function(buffer) {\n let t = this;\n let view = new DataView(buffer);\n view.offset = 0;\n\n const n = t.nodesCount;\n\n t.noffsets = new Uint32Array(n);\n t.nvertices = new Uint32Array(n);\n t.nfaces = new Uint32Array(n);\n t.nerrors = new Float32Array(n);\n t.nspheres = new Float32Array(n*5);\n t.nsize = new Float32Array(n);\n t.nfirstpatch = new Uint32Array(n);\n\n for(let i = 0; i < n; i++) {\n t.noffsets[i] = padding*(0,_Binary_js__WEBPACK_IMPORTED_MODULE_0__.getUint32)(view); //offset\n t.nvertices[i] = (0,_Binary_js__WEBPACK_IMPORTED_MODULE_0__.getUint16)(view); //verticesCount\n t.nfaces[i] = (0,_Binary_js__WEBPACK_IMPORTED_MODULE_0__.getUint16)(view); //facesCount\n t.nerrors[i] = (0,_Binary_js__WEBPACK_IMPORTED_MODULE_0__.getFloat32)(view);\n view.offset += 8; //skip cone\n for(let k = 0; k < 5; k++)\n t.nspheres[i*5+k] = (0,_Binary_js__WEBPACK_IMPORTED_MODULE_0__.getFloat32)(view); //sphere + tight\n t.nfirstpatch[i] = (0,_Binary_js__WEBPACK_IMPORTED_MODULE_0__.getUint32)(view); //first patch\n }\n t.sink = n -1;\n\n t.patches = new Uint32Array(view.buffer, view.offset, t.patchesCount*3); //noded, lastTriangle, texture\n t.nroots = t.nodesCount;\n for(let j = 0; j < t.nroots; j++) {\n for(let i = t.nfirstpatch[j]; i < t.nfirstpatch[j+1]; i++) {\n if(t.patches[i*3] < t.nroots)\n t.nroots = t.patches[i*3];\n }\n }\n\n view.offset += t.patchesCount*12;\n\n t.textures = new Uint32Array(t.texturesCount);\n t.texref = new Uint32Array(t.texturesCount);\n for(let i = 0; i < t.texturesCount; i++) {\n t.textures[i] = padding*(0,_Binary_js__WEBPACK_IMPORTED_MODULE_0__.getUint32)(view);\n view.offset += 16*4; //skip proj matrix\n }\n\n t.vsize = 12 + (t.vertex.normal?6:0) + (t.vertex.color?4:0) + (t.vertex.texCoord?8:0);\n t.fsize = 6;\n\n //problem: I have no idea how much space a texture is needed in GPU. 10x factor assumed.\n let tmptexsize = new Uint32Array(n-1);\n let tmptexcount = new Uint32Array(n-1);\n for(let i = 0; i < n-1; i++) {\n for(let p = t.nfirstpatch[i]; p != t.nfirstpatch[i+1]; p++) {\n let tex = t.patches[p*3+2];\n tmptexsize[i] += t.textures[tex+1] - t.textures[tex];\n tmptexcount[i]++;\n }\n t.nsize[i] = t.vsize*t.nvertices[i] + t.fsize*t.nfaces[i];\n }\n for(let i = 0; i < n-1; i++) {\n t.nsize[i] += 10*tmptexsize[i]/tmptexcount[i];\n }\n\n t.status = new Uint8Array(n); //0 for none, 1 for ready, 2+ for waiting data\n t.frames = new Uint32Array(n);\n t.errors = new Float32Array(n); //biggest error of instances\n t.reqAttempt = new Uint8Array(n);\n \n t.isReady = true;\n for(let callback of t.onLoad)\n callback(this);\n },\n\n importAttribute: function(view) {\n let a = {};\n a.type = view.getUint8(view.offset++, true);\n a.size = view.getUint8(view.offset++, true);\n a.glType = attrGlMap[a.type];\n a.normalized = a.type < 7;\n a.stride = attrSizeMap[a.type]*a.size;\n if(a.size == 0) return null;\n return a;\n },\n\n importElement: function(view) {\n let e = [];\n for(let i = 0; i < 8; i++)\n e[i] = this.importAttribute(view);\n return e;\n },\n\n importVertex: function(view) {\t//enum POSITION, NORMAL, COLOR, TEXCOORD, DATA0\n const e = this.importElement(view);\n let color = e[2];\n if(color) {\n color.type = 2; //unsigned byte\n color.glType = attrGlMap[2];\n }\n return { position: e[0], normal: e[1], color: e[2], texCoord: e[3], data: e[4] };\n },\n\n //enum INDEX, NORMAL, COLOR, TEXCOORD, DATA0\n importFace: function(view) {\n const e = this.importElement(view);\n let color = e[2];\n if(color) {\n color.type = 2; //unsigned byte\n color.glType = attrGlMap[2];\n }\n return { index: e[0], normal: e[1], color: e[2], texCoord: e[3], data: e[4] };\n },\n\n importSignature: function(view) {\n let s = {};\n s.vertex = this.importVertex(view);\n s.face = this.importFace(view);\n s.flags = (0,_Binary_js__WEBPACK_IMPORTED_MODULE_0__.getUint32)(view);\n return s;\n },\n\n importHeader: function(view) {\n const magic = (0,_Binary_js__WEBPACK_IMPORTED_MODULE_0__.getUint32)(view);\n if(magic != 0x4E787320) return null;\n let h = {};\n h.version = (0,_Binary_js__WEBPACK_IMPORTED_MODULE_0__.getUint32)(view);\n h.verticesCount = (0,_Binary_js__WEBPACK_IMPORTED_MODULE_0__.getUint64)(view);\n h.facesCount = (0,_Binary_js__WEBPACK_IMPORTED_MODULE_0__.getUint64)(view);\n h.signature = this.importSignature(view);\n h.nodesCount = (0,_Binary_js__WEBPACK_IMPORTED_MODULE_0__.getUint32)(view);\n h.patchesCount = (0,_Binary_js__WEBPACK_IMPORTED_MODULE_0__.getUint32)(view);\n h.texturesCount = (0,_Binary_js__WEBPACK_IMPORTED_MODULE_0__.getUint32)(view);\n h.sphere = {\n center: [(0,_Binary_js__WEBPACK_IMPORTED_MODULE_0__.getFloat32)(view), (0,_Binary_js__WEBPACK_IMPORTED_MODULE_0__.getFloat32)(view), (0,_Binary_js__WEBPACK_IMPORTED_MODULE_0__.getFloat32)(view)],\n radius: (0,_Binary_js__WEBPACK_IMPORTED_MODULE_0__.getFloat32)(view)\n };\n return h;\n },\n //OVERRIDE THESE METHOS\n\n //assemble node and geometry\n createNode: function(id) {},\n\n createNodeGeometry: function(id, data) {},\n deleteNodeGeometry: function(id) {},\n\n createTexture: function(id, image) {},\n deleteTexture: function(id) {},\n};\n\n\n\n\n\n//# sourceURL=webpack://Nexus3D/./src/Mesh.js?\n}"); -/***/ }), +/***/ }, -/***/ "./src/Nexus3D.js": +/***/ "./src/Nexus3D.js" /*!************************!*\ !*** ./src/Nexus3D.js ***! \************************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { +(__unused_webpack_module, __webpack_exports__, __webpack_require__) { -eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ Cache: () => (/* reexport safe */ _Cache_js__WEBPACK_IMPORTED_MODULE_3__.Cache),\n/* harmony export */ Nexus3D: () => (/* binding */ Nexus3D)\n/* harmony export */ });\n/* harmony import */ var three__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! three */ \"three\");\n/* harmony import */ var three__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(three__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _Traversal_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./Traversal.js */ \"./src/Traversal.js\");\n/* harmony import */ var _Mesh_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./Mesh.js */ \"./src/Mesh.js\");\n/* harmony import */ var _Cache_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./Cache.js */ \"./src/Cache.js\");\n\n\n\n\n\nclass Nexus3D extends three__WEBPACK_IMPORTED_MODULE_0__.Mesh {\n\n\tconstructor(url, renderer, options = {}) {\n\n\t\tsuper();\n\n\t\tthis.patchWebGLRenderer(renderer);\n\n\t\tObject.assign(this, {\n\t\t\tisNXS: true,\n\t\t\ttype:'NXS',\n\t\t\turl: url,\n\t\t\tgl: renderer.getContext(),\n\t\t\tXRMode: false, //set to true to avoid flickering due to double eye rendering.\n\t\t\tmaterial: null,\n\n\t\t\tautoUpdate: true,\n\t\t\tmesh: new _Mesh_js__WEBPACK_IMPORTED_MODULE_2__.Mesh(),\n\t\t\tvbo: [],\n\t\t\tibo: [],\n\t\t\tvao: [],\n\t\t\ttextures: [],\n\t\t\tattributes: {}, //here we store the uniform attributes of the shader.\n\n\t\t\tbasemesh: null, //highest level of the nexus, for picking\n\t\t});\n\n\t\tif('material' in options)\n\t\t\tthis.material = options.material;\n\t\tif(!this.material) \n\t\t\tthis.material = new three__WEBPACK_IMPORTED_MODULE_0__.MeshStandardMaterial();\n\t\t\t\n\t\tfor(let call of ['onLoad', 'onUpdate', 'onProgress']) {\n\t\t\tthis['_' + call] = [];\n\t\t\tif(call in options)\n\t\t\t\tthis['_' + call].push(options[call])\n\t\t}\n\n\t\tif(this.url) {\n\t\t\tif(typeof url == 'object') {\n\t\t\t\tthis.nxs = this.url;\n\t\t\t\tthis.nxs.onLoad.push((m) => { \n\t\t\t\t\tthis.mesh = this.nxs.mesh;\n\t\t\t\t\tthis.traversal = this.nxs.traversal;\n\t\t\t\t\tthis.cache = this.nxs.cache;\n\t\t\t\t\tthis.vbo = this.nxs.vbo;\n\t\t\t\t\tthis.ibo = this.nxs.ibo;\n\t\t\t\t\tthis.vao = this.nxs.vao;\n\t\t\t\t\tthis.textures = this.nxs.textures;\n\t\t\t\t\tthis.onLoadCallback(this); \n\t\t\t\t});\n\t\t\t} else\n\t\t\t\tthis.open(this.url);\n\t\t}\n\t}\n\n\tcopy(source) {\n\t\tthree__WEBPACK_IMPORTED_MODULE_0__.Object3D.prototype.copy.call(this, source, false);\n\t\tthrow new Error(\"Can't really copy a Nexus3D object.\");\n\t\t// removed by dead control flow\n\n\t}\n\n\t\n\topen(url) {\n\t\tlet t = this;\n\t\tthis.mesh.open(url);\n\t\tthis.mesh.createNode = (id) => { };\n\t\tthis.mesh.createNodeGeometry = (id, geometry) => { t.createNodeGeometry(id, geometry); };\n\t\tthis.mesh.createTexture = (id, image) => { t.createTexture(id, image); };\n\t\tthis.mesh.deleteNodeGeometry = (id) => { t.deleteNodeGeometry(id); };\n\t\tthis.mesh.deleteTexture = (id) => { t.deleteTexture(id); };\n\t\tthis.mesh.onLoad.push(() => { t.onLoadCallback(); });\n\t\tthis.mesh.onUpdate.push(() => { \n\t\t\tfor(let callback of t._onUpdate) callback(this); \n\t\t\tfor(let callback of t._onProgress) callback(this, this.mesh.availableNodes, this.mesh.nodesCount); \n\t\t});\n\n\t\tthis.traversal = new _Traversal_js__WEBPACK_IMPORTED_MODULE_1__.Traversal();\n\t\tthis.cache = _Cache_js__WEBPACK_IMPORTED_MODULE_3__.Cache; //new Cache();\n\t\tthis.textures = {}; \n\t}\n\n\tset onLoad(callback) {\n\t\tthis._onLoad.push(callback);\n\t}\n\n\tset onUpdate(callback) {\n\t\tthis._onUpdate.push(callback);\n\t}\n\n\tset onProgress(callback) {\n\t\tthis._onProgress.push(callback);\n\t}\n\n\t//TODO this is not really needed, we might just conform to THREEJS standard of updating.\n\t/*set material(material) {\n\t\tthis.material = material;\n\t\tthis.material.needsUpdate = true;\n\t}*/\n\t\n\tupdateMaterials() {\n\t\tif(this.material.map !== false && this.mesh.vertex.texCoord)\n\t\t\tthis.material.map = this.material_texture;\n\n\t\tif(this.mesh.vertex.color)\n\t\t\tthis.material.vertexColors = true; \n\t\tthis.material.needsUpdate = true; \n\t}\n\n\tonLoadCallback() {\n\t\tconst c = this.mesh.sphere.center;\n\t\tconst center = new three__WEBPACK_IMPORTED_MODULE_0__.Vector3(c[0], c[1], c[2]);\n\t\tconst radius = this.mesh.sphere.radius;\n\t\tthis.boundingSphere = new three__WEBPACK_IMPORTED_MODULE_0__.Sphere(center, radius);\n\n\t\tvar geometry = new three__WEBPACK_IMPORTED_MODULE_0__.BufferGeometry();\n\n\t\tgeometry.setAttribute( 'position', new three__WEBPACK_IMPORTED_MODULE_0__.BufferAttribute(new Float32Array(3), 3));\n\t\t\n\t\tif(this.mesh.vertex.normal)\n\t\t\tgeometry.setAttribute( 'normal', new three__WEBPACK_IMPORTED_MODULE_0__.BufferAttribute(new Float32Array(3), 3));\n\t\tif(this.mesh.vertex.color)\n\t\t\tgeometry.setAttribute( 'color', new three__WEBPACK_IMPORTED_MODULE_0__.BufferAttribute(new Float32Array(4), 4));\n\t\tif(this.mesh.vertex.texCoord)\n\t\t\tgeometry.setAttribute( 'uv', new three__WEBPACK_IMPORTED_MODULE_0__.BufferAttribute(new Float32Array(2), 2));\n\n\t\tif(this.mesh.vertex.texCoord) {\n\t\t\tthis.material_texture = new three__WEBPACK_IMPORTED_MODULE_0__.DataTexture( new Uint8Array([1, 1, 1, 1]), 1, 1, three__WEBPACK_IMPORTED_MODULE_0__.RGBAFormat );\n\t\t\tthis.material_texture.needsUpdate = true;\n\t\t}\n\n\t\tthis.updateMaterials();\n\t\tthis.geometry = geometry;\n\t\t\n\t\tthis.frustumCulled = false;\n\t\t\t\n\t\tfor(let callback of this._onLoad)\n\t\t\tcallback(this);\n\t}\n\n\trenderBufferDirect(renderer, scene, camera, geometry, material, group) {\n\t\tlet s = new three__WEBPACK_IMPORTED_MODULE_0__.Vector4();\n\t\trenderer.getViewport(s);\n\n\t \t//object modelview is multiplied by camera during rendering, we need to do it here for visibility computations\n\t\tthis.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, this.matrixWorld );\n\t\tif(s.x == 0 && this.XRMode) { //hack to only traverse on the left eye\n\t\t\tthis.traversal.updateView(s, camera.projectionMatrix.elements, this.modelViewMatrix.elements);\n\t\tthis.instance_errors = this.traversal.traverse(this.mesh, this.cache);\n\t\t}\n\t\t//threejs increments version when setting neeedsUpdate\n\t\t/*if(this.material.version > 0) {\n\t\t\tthis.updateMaterials();\n\t\t\tthis.material.version = 0;\n\t\t\tfor(let callback of this.onUpdate) \n\t\t\t\tcallback(this); \n\t\t}*/\n\t\tlet gl = this.gl;\n\t\tlet program = gl.getParameter(gl.CURRENT_PROGRAM);\n\n\t\t//TODO these calls could be cached saving attrs per each material.\n\t\tlet attr = this.attributes;\n\t\tattr.position = gl.getAttribLocation(program, \"position\");\n\t\tattr.normal = gl.getAttribLocation(program, \"normal\");\n\t\tattr.color = gl.getAttribLocation(program, \"color\");\n\t\tattr.uv = gl.getAttribLocation(program, \"uv\");\n\t\tattr.size = gl.getUniformLocation(program, \"size\");\n\t\tattr.scale = gl.getUniformLocation(program, \"scale\");\n\n\t\tlet map_location = gl.getUniformLocation(program, \"map\"); \n\t\tattr.map = map_location ? gl.getUniform(program, map_location) : null;\n\t\t\n\t\n\t\t//hack to detect if threejs using point or triangle shaders\n\t\t//instance.mode = attr.size ? \"POINT\" : \"FILL\";\n\t\t//if(attr.size != -1) \n\t\t// instance.pointsize = material.size;\n\t\n\t\t//can't find docs or code on how material.scale is computed in threejs.\n\t\t//if(attr.scale != -1)\n\t\t// instance.pointscale = 2.0;\n\n\t\tthis.setVisibility();\n\t}\n\n\tsetVisibility() {\n\t\t//set visibile what is visible!\n\t\tlet t = this.traversal;\n\t\tlet m = this.mesh;\n\t\n\t\tif(!m.isReady)\n\t\t\treturn;\n\t\tlet rendered = 0;\n\t\t\n\t\tlet attr = this.attributes;\n\t\tlet mesh = this.mesh;\n\t\tlet gl = this.gl;\n\t\tlet gl2 = gl instanceof WebGL2RenderingContext;\n\t\tlet state = attr.uv + 10*attr.color + 100*attr.normal;\n\t\t\t\n\t\tfor(let id = 0; id < m.nodesCount; id++) {\n// let err = m.nerrors[id];\n\n\t\t\tif(!t.selected[id]) continue;\n\n\t\t\t//check for children: if all are selected, bail out.\n\t\t\t{\n\t\t\t\tlet visible = false;\n\t\t\t\tlet offset = 0;\n\t\t\t\tlet end = 0;\n\t\t\t\tlet last = m.nfirstpatch[id+1]-1;\n\t\t\t\tfor (var p = m.nfirstpatch[id]; p < m.nfirstpatch[id+1]; ++p) {\n\t\t\t\t\tvar child = m.patches[p*3];\n\t\n\t\t\t\t\tif(!t.selected[child]) {\n\t\t\t\t\t\tvisible = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif(!visible) continue;\n\t\t\t}\n\n\t\t\tvar sp = m.nspheres;\n\t\t\tvar off = id*5;\n\t\t\tif(!t.isVisible(sp[off], sp[off+1], sp[off+2], sp[off+4])) //tight radius\n\t\t\t\tcontinue;\n\t\n\t\t\tlet doBind = true;\n\n\t\t\tif(gl2) {\n\t\t\t\tif(state in this.vao[id])\n\t\t\t\t\tdoBind = false;\n\t\t\t\telse\n\t\t\t\t\tthis.vao[id][state] = gl.createVertexArray();\n\t\t\t\t\t\n\t\t\t \tgl.bindVertexArray(this.vao[id][state]);\n\t\t\t}\n\t\t\t\n\t\t\tif(doBind) { \n\t\t\t\tgl.bindBuffer(gl.ARRAY_BUFFER, this.vbo[id]);\n\t\t\t \tgl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.ibo[id]);\n\t\n\t\t\t\tgl.vertexAttribPointer(attr.position, 3, gl.FLOAT, false, 12, 0);\n\t\t\t\tgl.enableVertexAttribArray(attr.position);\n\t\n\t\t\t\tlet nv = this.mesh.nvertices[id];\n\t\t\t\tlet offset = nv*12;\n\t\n\t\t\t\tif(mesh.vertex.texCoord) {\n\t\t\t\t\tif(attr.uv >= 0) {\n\t\t\t\t\t\tgl.vertexAttribPointer(attr.uv, 2, gl.FLOAT, false, 8, offset);\n\t\t\t\t\t\tgl.enableVertexAttribArray(attr.uv);\n\t\t\t\t\t}\n\t\t\t\t\toffset += nv*8;\n\t\t\t\t}\n\t\t\t\tif(mesh.vertex.color) {\n\t\t\t\t\tif(attr.color >= 0) {\n\t\t\t\t\t\tgl.vertexAttribPointer(attr.color, 4, gl.UNSIGNED_BYTE, true, 4, offset);\n\t\t\t\t\t\tgl.enableVertexAttribArray(attr.color);\n\t\t\t\t\t}\n\t\t\t\t\toffset += nv*4;\n\t\t\t\t}\n\t\t\t\tif(mesh.vertex.normal) {\n\t\t\t\t\tif(attr.normal >= 0) {\n\t\t\t\t\t\tgl.vertexAttribPointer(attr.normal, 3, gl.SHORT, true, 6, offset);\n\t\t\t\t\t\tgl.enableVertexAttribArray(attr.normal);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif(this.cache.debug.nodes) {\n\t\t\t\t\tgl.disableVertexAttribArray(attr.color);\n\n\t\t\t\t\tvar error = this.instance_errors[id]; //this.mesh.errors[id];\n\t\t\t\t\tvar palette = [\n\t\t\t\t\t\t[1, 1, 1, 1], //white\n\t\t\t\t\t\t[1, 1, 1, 1], //white\n\t\t\t\t\t\t[0, 1, 0, 1], //green\n\t\t\t\t\t\t[0, 1, 1, 1], //cyan\n\t\t\t\t\t\t[1, 1, 0, 1], //yellow\n\t\t\t\t\t\t[1, 0, 1, 1], //magenta\n\t\t\t\t\t\t[1, 0, 0, 1] //red\n\t\t\t\t\t];\n\t\t\t\t\tlet w = Math.min(5.99, Math.max(0, Math.log2(error)/2));\n\t\t\t\t\tlet low = Math.floor(w);\n\t\t\t\t\tw -= low;\n\t\t\t\t\tlet color = [];\n\t\t\t\t\tfor( let k = 0; k < 4; k++)\n\t\t\t\t\t\tcolor[k] = palette[low][k]*(1-w) + palette[low+1][k]*w;\n\t\t\t\t\t\n\t\t\t\t\tgl.vertexAttrib4fv(attr.color, color);\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.cache.realError = Math.min(this.mesh.errors[id], this.cache.realError);\n\t\t\t\n\t\t\tlet offset = 0;\n\t\t\tlet end = 0;\n\t\t\tlet last = m.nfirstpatch[id+1]-1;\n\t\t\tfor (let p = m.nfirstpatch[id]; p < m.nfirstpatch[id+1]; ++p) {\n\t\t\t\tlet child = m.patches[p*3];\n\t\n\t\t\t\tif(!t.selected[child]) {\n\t\t\t\t\tend = m.patches[p*3+1];\n\t\t\t\t\tif(p < last) //we join patches if possible.\n\t\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif(end > offset) {\n\t\t\t\t\tif(m.vertex.texCoord && attr.uv >= 0) {\n\t\t\t\t\t\tvar tex = m.patches[p*3+2];\n\t\t\t\t\t\tif(tex != -1) { //bind texture\n\t\t\t\t\t\t\tvar texid = this.textures[tex];\n\t\t\t\t\t\t\tgl.activeTexture(gl.TEXTURE0 + attr.map);\n\t\t\t\t\t\t\tgl.bindTexture(gl.TEXTURE_2D, texid);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tlet mode = this.material.wireframe ? gl.LINE_STRIP : gl.TRIANGLES;\n\n\t\t\t\t\tgl.drawElements(mode, (end - offset) * 3, gl.UNSIGNED_SHORT, offset * 6);\n\t\t\t\t\trendered += end - offset;\n\t\t\t\t}\n\t\t\t\toffset = m.patches[p*3+1];\n\t\t\t}\n\t\t}\n\t\tthis.cache.rendered += rendered;\n\t}\n\n\tcreateNodeGeometry(id, data) {\n\t\tlet m = this.mesh;\n\t\tvar nv = m.nvertices[id];\n\t\tvar nf = m.nfaces[id];\n\t\tlet indices = data.index;\n\t\tlet vertices = new ArrayBuffer(nv*m.vsize);\n\t\tvar position = new Float32Array(vertices, 0, nv*3);\n\t\tposition.set(data.position);\n\t\tvar off = nv*12;\n\t\tif(m.vertex.texCoord) {\n\t\t\tvar uv = new Float32Array(vertices, off, nv*2);\n\t\t\tuv.set(data.uv);\n\t\t\toff += nv*8;\n\t\t}\n\t\tif(m.vertex.color) {\n\t\t\tvar color = new Uint8Array(vertices, off, nv*4);\n\t\t\tcolor.set(data.color);\n\t\t\toff += nv*4;\n\t\t}\n\t\tif(m.vertex.normal) {\n\t\t\tvar normal = new Int16Array(vertices, off, nv*3);\n\t\t\tnormal.set(data.normal);\n\t\t\toff += nv*6;\n\t\t}\n\t\t\n\t\t//needed for approximate picking.\n\t\tif(id < this.mesh.nroots) {\n\t\t\tlet basegeometry = new three__WEBPACK_IMPORTED_MODULE_0__.BufferGeometry();\n\t\t\tbasegeometry.setAttribute( 'position', new three__WEBPACK_IMPORTED_MODULE_0__.BufferAttribute(data.position, 3 ) );\n\t\t\tbasegeometry.setAttribute( 'normal', new three__WEBPACK_IMPORTED_MODULE_0__.BufferAttribute(data.normal, 3 ) );\n\t\t\tbasegeometry.setIndex(new three__WEBPACK_IMPORTED_MODULE_0__.BufferAttribute( data.index, 1 ) );\n\n\t\t\tthis.basemesh = new three__WEBPACK_IMPORTED_MODULE_0__.Mesh(basegeometry, this.material);\n\t\t\tthis.basemesh.visible = false;\n\t\t\tthis.add(this.basemesh);\n\t\t}\n\t\t\n\t\tvar gl = this.gl\n\t\tthis.vao[id] = {}; //one for each attrib combination in use (casting shadows for example).\n\t\tgl.bindVertexArray(null);\n\t\t\n\t\tvar vbo = this.vbo[id] = gl.createBuffer();\n\t\tgl.bindBuffer(gl.ARRAY_BUFFER, vbo);\n\t\tgl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);\n\t\tvar ibo = this.ibo[id] = gl.createBuffer();\n\t\tgl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo);\n\t\tgl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);\n\t}\n\n\tcreateTexture(id, image) {\n\t\tlet gl = this.gl;\n\t\tvar flip = gl.getParameter(gl.UNPACK_FLIP_Y_WEBGL);\n\t\tgl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);\n\t\tlet tex = this.textures[id] = gl.createTexture();\n\t\tgl.bindTexture(gl.TEXTURE_2D, tex);\n\n\t\t//TODO some textures might be alpha only! save space\n\t\tlet internalFormat;\n\t\tif (this.material && this.material.map && this.material.map.colorSpace === three__WEBPACK_IMPORTED_MODULE_0__.LinearSRGBColorSpace) {\n\t\t\tinternalFormat = gl.RGBA;\n\t\t} else if (typeof gl.SRGB8_ALPHA8 !== 'undefined') {\n\t\t\tinternalFormat = gl.SRGB8_ALPHA8;\n\t\t} else {\n\t\t\tinternalFormat = gl.RGBA;\n\t\t}\n\t\tgl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, gl.RGBA, gl.UNSIGNED_BYTE, image);\n\t\tgl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n\t\tgl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n\t\tgl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);\n\n\t\tfunction powerOf2(n) { return n && (n & (n - 1)) === 0; }\n\t\tif(!(gl instanceof WebGLRenderingContext) || (powerOf2(image.width) && powerOf2(image.height))) {\n\t\t\tgl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST_MIPMAP_LINEAR);\n\t\t\tgl.generateMipmap(gl.TEXTURE_2D);\n\t\t} else {\n\t\t\tgl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);\n\t\t}\n\n\t\tgl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flip); \n\t}\n\t//schedule for removal of this node( might not want to delete it in the middle of something.\n\t//TODO check if this is really needed!\n\tdeleteNodeGeometry(id) {\n\t\tthis.gl.deleteBuffer(this.vbo[id]);\n\t\tthis.gl.deleteBuffer(this.ibo[id]);\n\n\t\tthis.vbo[id] = this.ibo[id] = null;\n\t\tif(this.vao[id]) //node might have been loaded and unloaded before a rendering\n\t\t\tfor(const [state, vao] of Object.entries(this.vao[id]))\n\t\t\t\tthis.gl.deleteVertexArray(vao);\n\t\tthis.vao[id] = null;\n\t}\n\n\tdeleteTexture(tex) {\n\t\tif(!this.textures[tex])\n\t\t\treturn;\n\t\t//\tthrow \"Deleting missing texture!\"\n\n\t\tthis.gl.deleteTexture(this.textures[tex]);\n\t\tthis.textures[tex] = 0;\n\t}\n\n\tflush() {\n\t\tthis.cache.flush(this.mesh);\n\t}\n\n\tdispose() {\n\t\tthis.flush();\n\t\tfor(let child of this.children)\n\t\t\tchild.geometry.dispose();\n\t}\n\n\ttoJSON(meta) {\n\t\tthrow new Error(\"Can't convert to json.\");\n\t}\n\n\tpatchWebGLRenderer(renderer) {\n\t\tif(renderer.nexusPatched) return;\n\t\tlet f = renderer.renderBufferDirect;\n\t\trenderer.renderBufferDirect = ( camera, scene, geometry, material, object, group) => { \n\t\t\tf( camera, scene, geometry, material, object, group );\n\t\t\tif ( object.renderBufferDirect)\n\t\t\t\tobject.renderBufferDirect(renderer, scene, camera, geometry, material, group);\n\t\t};\n\t\trenderer.originalRender = renderer.render;\n\t\trenderer.render = (scene, camera) => {\n\t\t\t_Cache_js__WEBPACK_IMPORTED_MODULE_3__.Cache.beginFrame(30);\n\t\t\trenderer.originalRender(scene, camera);\n\t\t\t_Cache_js__WEBPACK_IMPORTED_MODULE_3__.Cache.endFrame();\n\t\t}\n\t\trenderer.nexusPatched = true;\n\t} \n\n}\n\n\n\n\n\n\n\n//# sourceURL=webpack://Nexus3D/./src/Nexus3D.js?\n}"); +eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ Cache: () => (/* reexport safe */ _Cache_js__WEBPACK_IMPORTED_MODULE_3__.Cache),\n/* harmony export */ Nexus3D: () => (/* binding */ Nexus3D)\n/* harmony export */ });\n/* harmony import */ var three__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! three */ \"three\");\n/* harmony import */ var three__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(three__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _Traversal_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./Traversal.js */ \"./src/Traversal.js\");\n/* harmony import */ var _Mesh_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./Mesh.js */ \"./src/Mesh.js\");\n/* harmony import */ var _Cache_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./Cache.js */ \"./src/Cache.js\");\n\n\n\n\n\nclass Nexus3D extends three__WEBPACK_IMPORTED_MODULE_0__.Mesh {\n\n\tconstructor(url, renderer, options = {}) {\n\n\t\tsuper();\n\n\t\tthis.patchWebGLRenderer(renderer);\n\n\t\tObject.assign(this, {\n\t\t\tisNXS: true,\n\t\t\ttype:'NXS',\n\t\t\turl: url,\n\t\t\tgl: renderer.getContext(),\n\t\t\tXRMode: false, //set to true to avoid flickering due to double eye rendering.\n\t\t\tmaterial: null,\n\n\t\t\tautoUpdate: true,\n\t\t\tmesh: new _Mesh_js__WEBPACK_IMPORTED_MODULE_2__.Mesh(),\n\t\t\tvbo: [],\n\t\t\tibo: [],\n\t\t\tvao: [],\n\t\t\ttextures: [],\n\t\t\tattributes: {}, //here we store the uniform attributes of the shader.\n\n\t\t\tbasemesh: null, //highest level of the nexus, for picking\n\t\t});\n\n\t\tif('material' in options)\n\t\t\tthis.material = options.material;\n\t\tif(!this.material) \n\t\t\tthis.material = new three__WEBPACK_IMPORTED_MODULE_0__.MeshStandardMaterial();\n\t\t\t\n\t\tfor(let call of ['onLoad', 'onUpdate', 'onProgress']) {\n\t\t\tthis['_' + call] = [];\n\t\t\tif(call in options)\n\t\t\t\tthis['_' + call].push(options[call])\n\t\t}\n\n\t\tif(this.url) {\n\t\t\tif(typeof url == 'object') {\n\t\t\t\tthis.nxs = this.url;\n\t\t\t\tthis.nxs.onLoad.push((m) => { \n\t\t\t\t\tthis.mesh = this.nxs.mesh;\n\t\t\t\t\tthis.traversal = this.nxs.traversal;\n\t\t\t\t\tthis.cache = this.nxs.cache;\n\t\t\t\t\tthis.vbo = this.nxs.vbo;\n\t\t\t\t\tthis.ibo = this.nxs.ibo;\n\t\t\t\t\tthis.vao = this.nxs.vao;\n\t\t\t\t\tthis.textures = this.nxs.textures;\n\t\t\t\t\tthis.onLoadCallback(this); \n\t\t\t\t});\n\t\t\t} else\n\t\t\t\tthis.open(this.url);\n\t\t}\n\t}\n\n\tcopy(source) {\n\t\tthree__WEBPACK_IMPORTED_MODULE_0__.Object3D.prototype.copy.call(this, source, false);\n\t\tthrow new Error(\"Can't really copy a Nexus3D object.\");\n\t\t// removed by dead control flow\n\n\t}\n\n\t\n\topen(url) {\n\t\tlet t = this;\n\t\tthis.mesh.open(url);\n\t\tthis.mesh.createNode = (id) => { };\n\t\tthis.mesh.createNodeGeometry = (id, geometry) => { t.createNodeGeometry(id, geometry); };\n\t\tthis.mesh.createTexture = (id, image) => { t.createTexture(id, image); };\n\t\tthis.mesh.deleteNodeGeometry = (id) => { t.deleteNodeGeometry(id); };\n\t\tthis.mesh.deleteTexture = (id) => { t.deleteTexture(id); };\n\t\tthis.mesh.onLoad.push(() => { t.onLoadCallback(); });\n\t\tthis.mesh.onUpdate.push(() => { \n\t\t\tfor(let callback of t._onUpdate) callback(this); \n\t\t\tfor(let callback of t._onProgress) callback(this, this.mesh.availableNodes, this.mesh.nodesCount); \n\t\t});\n\n\t\tthis.traversal = new _Traversal_js__WEBPACK_IMPORTED_MODULE_1__.Traversal();\n\t\tthis.cache = _Cache_js__WEBPACK_IMPORTED_MODULE_3__.Cache; //new Cache();\n\t\tthis.textures = {}; \n\t}\n\n\tset onLoad(callback) {\n\t\tthis._onLoad.push(callback);\n\t}\n\n\tset onUpdate(callback) {\n\t\tthis._onUpdate.push(callback);\n\t}\n\n\tset onProgress(callback) {\n\t\tthis._onProgress.push(callback);\n\t}\n\n\t//TODO this is not really needed, we might just conform to THREEJS standard of updating.\n\t/*set material(material) {\n\t\tthis.material = material;\n\t\tthis.material.needsUpdate = true;\n\t}*/\n\t\n\tupdateMaterials() {\n\t\tif(this.material.map !== false && this.mesh.vertex.texCoord)\n\t\t\tthis.material.map = this.material_texture;\n\n\t\tif(this.mesh.vertex.color)\n\t\t\tthis.material.vertexColors = true; \n\t\tthis.material.needsUpdate = true; \n\t}\n\n\tonLoadCallback() {\n\t\tconst c = this.mesh.sphere.center;\n\t\tconst center = new three__WEBPACK_IMPORTED_MODULE_0__.Vector3(c[0], c[1], c[2]);\n\t\tconst radius = this.mesh.sphere.radius;\n\t\tthis.boundingSphere = new three__WEBPACK_IMPORTED_MODULE_0__.Sphere(center, radius);\n\n\t\tvar geometry = new three__WEBPACK_IMPORTED_MODULE_0__.BufferGeometry();\n\n\t\tgeometry.setAttribute( 'position', new three__WEBPACK_IMPORTED_MODULE_0__.BufferAttribute(new Float32Array(3), 3));\n\t\t\n\t\tif(this.mesh.vertex.normal)\n\t\t\tgeometry.setAttribute( 'normal', new three__WEBPACK_IMPORTED_MODULE_0__.BufferAttribute(new Float32Array(3), 3));\n\t\tif(this.mesh.vertex.color)\n\t\t\tgeometry.setAttribute( 'color', new three__WEBPACK_IMPORTED_MODULE_0__.BufferAttribute(new Float32Array(4), 4));\n\t\tif(this.mesh.vertex.texCoord)\n\t\t\tgeometry.setAttribute( 'uv', new three__WEBPACK_IMPORTED_MODULE_0__.BufferAttribute(new Float32Array(2), 2));\n\n\t\tif(this.mesh.vertex.texCoord) {\n\t\t\tthis.material_texture = new three__WEBPACK_IMPORTED_MODULE_0__.DataTexture( new Uint8Array([1, 1, 1, 1]), 1, 1, three__WEBPACK_IMPORTED_MODULE_0__.RGBAFormat );\n\t\t\tthis.material_texture.needsUpdate = true;\n\t\t}\n\n\t\tthis.updateMaterials();\n\t\tthis.geometry = geometry;\n\t\t\n\t\tthis.frustumCulled = false;\n\t\t\t\n\t\tfor(let callback of this._onLoad)\n\t\t\tcallback(this);\n\t}\n\n\trenderBufferDirect(renderer, scene, camera, geometry, material, group) {\n\t\tlet s = new three__WEBPACK_IMPORTED_MODULE_0__.Vector4();\n\t\trenderer.getViewport(s);\n\n\t \t//object modelview is multiplied by camera during rendering, we need to do it here for visibility computations\n\t\tthis.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, this.matrixWorld );\n\t\tif(s.x == 0 || !this.XRMode) { //hack to only traverse on the left eye\n\t\t\tthis.traversal.updateView(s, camera.projectionMatrix.elements, this.modelViewMatrix.elements);\n\t\t\tthis.instance_errors = this.traversal.traverse(this.mesh, this.cache);\n\t\t}\n\t\t//threejs increments version when setting neeedsUpdate\n\t\t/*if(this.material.version > 0) {\n\t\t\tthis.updateMaterials();\n\t\t\tthis.material.version = 0;\n\t\t\tfor(let callback of this.onUpdate) \n\t\t\t\tcallback(this); \n\t\t}*/\n\t\tlet gl = this.gl;\n\t\tlet program = gl.getParameter(gl.CURRENT_PROGRAM);\n\n\t\t//TODO these calls could be cached saving attrs per each material.\n\t\tlet attr = this.attributes;\n\t\tattr.position = gl.getAttribLocation(program, \"position\");\n\t\tattr.normal = gl.getAttribLocation(program, \"normal\");\n\t\tattr.color = gl.getAttribLocation(program, \"color\");\n\t\tattr.uv = gl.getAttribLocation(program, \"uv\");\n\t\tattr.size = gl.getUniformLocation(program, \"size\");\n\t\tattr.scale = gl.getUniformLocation(program, \"scale\");\n\n\t\tlet map_location = gl.getUniformLocation(program, \"map\"); \n\t\tattr.map = map_location ? gl.getUniform(program, map_location) : null;\n\t\t\n\t\n\t\t//hack to detect if threejs using point or triangle shaders\n\t\t//instance.mode = attr.size ? \"POINT\" : \"FILL\";\n\t\t//if(attr.size != -1) \n\t\t// instance.pointsize = material.size;\n\t\n\t\t//can't find docs or code on how material.scale is computed in threejs.\n\t\t//if(attr.scale != -1)\n\t\t// instance.pointscale = 2.0;\n\n\t\tthis.setVisibility();\n\t}\n\n\tsetVisibility() {\n\t\t//set visibile what is visible!\n\t\tlet t = this.traversal;\n\t\tlet m = this.mesh;\n\t\n\t\tif(!m.isReady)\n\t\t\treturn;\n\t\tlet rendered = 0;\n\t\t\n\t\tlet attr = this.attributes;\n\t\tlet mesh = this.mesh;\n\t\tlet gl = this.gl;\n\t\tlet gl2 = gl instanceof WebGL2RenderingContext;\n\t\tlet state = attr.uv + 10*attr.color + 100*attr.normal;\n\t\t\t\n\t\tfor(let id = 0; id < m.nodesCount; id++) {\n// let err = m.nerrors[id];\n\n\t\t\tif(!t.selected[id]) continue;\n\n\t\t\t//check for children: if all are selected, bail out.\n\t\t\t{\n\t\t\t\tlet visible = false;\n\t\t\t\tlet offset = 0;\n\t\t\t\tlet end = 0;\n\t\t\t\tlet last = m.nfirstpatch[id+1]-1;\n\t\t\t\tfor (var p = m.nfirstpatch[id]; p < m.nfirstpatch[id+1]; ++p) {\n\t\t\t\t\tvar child = m.patches[p*3];\n\t\n\t\t\t\t\tif(!t.selected[child]) {\n\t\t\t\t\t\tvisible = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif(!visible) continue;\n\t\t\t}\n\n\t\t\tvar sp = m.nspheres;\n\t\t\tvar off = id*5;\n\t\t\tif(!t.isVisible(sp[off], sp[off+1], sp[off+2], sp[off+4])) //tight radius\n\t\t\t\tcontinue;\n\t\n\t\t\tlet doBind = true;\n\n\t\t\tif(gl2) {\n\t\t\t\tif(state in this.vao[id])\n\t\t\t\t\tdoBind = false;\n\t\t\t\telse\n\t\t\t\t\tthis.vao[id][state] = gl.createVertexArray();\n\t\t\t\t\t\n\t\t\t \tgl.bindVertexArray(this.vao[id][state]);\n\t\t\t}\n\t\t\t\n\t\t\tif(doBind) { \n\t\t\t\tgl.bindBuffer(gl.ARRAY_BUFFER, this.vbo[id]);\n\t\t\t \tgl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.ibo[id]);\n\t\n\t\t\t\tgl.vertexAttribPointer(attr.position, 3, gl.FLOAT, false, 12, 0);\n\t\t\t\tgl.enableVertexAttribArray(attr.position);\n\t\n\t\t\t\tlet nv = this.mesh.nvertices[id];\n\t\t\t\tlet offset = nv*12;\n\t\n\t\t\t\tif(mesh.vertex.texCoord) {\n\t\t\t\t\tif(attr.uv >= 0) {\n\t\t\t\t\t\tgl.vertexAttribPointer(attr.uv, 2, gl.FLOAT, false, 8, offset);\n\t\t\t\t\t\tgl.enableVertexAttribArray(attr.uv);\n\t\t\t\t\t}\n\t\t\t\t\toffset += nv*8;\n\t\t\t\t}\n\t\t\t\tif(mesh.vertex.color) {\n\t\t\t\t\tif(attr.color >= 0) {\n\t\t\t\t\t\tgl.vertexAttribPointer(attr.color, 4, gl.UNSIGNED_BYTE, true, 4, offset);\n\t\t\t\t\t\tgl.enableVertexAttribArray(attr.color);\n\t\t\t\t\t}\n\t\t\t\t\toffset += nv*4;\n\t\t\t\t}\n\t\t\t\tif(mesh.vertex.normal) {\n\t\t\t\t\tif(attr.normal >= 0) {\n\t\t\t\t\t\tgl.vertexAttribPointer(attr.normal, 3, gl.SHORT, true, 6, offset);\n\t\t\t\t\t\tgl.enableVertexAttribArray(attr.normal);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif(this.cache.debug.nodes) {\n\t\t\t\t\tgl.disableVertexAttribArray(attr.color);\n\n\t\t\t\t\tvar error = this.instance_errors[id]; //this.mesh.errors[id];\n\t\t\t\t\tvar palette = [\n\t\t\t\t\t\t[1, 1, 1, 1], //white\n\t\t\t\t\t\t[1, 1, 1, 1], //white\n\t\t\t\t\t\t[0, 1, 0, 1], //green\n\t\t\t\t\t\t[0, 1, 1, 1], //cyan\n\t\t\t\t\t\t[1, 1, 0, 1], //yellow\n\t\t\t\t\t\t[1, 0, 1, 1], //magenta\n\t\t\t\t\t\t[1, 0, 0, 1] //red\n\t\t\t\t\t];\n\t\t\t\t\tlet w = Math.min(5.99, Math.max(0, Math.log2(error)/2));\n\t\t\t\t\tlet low = Math.floor(w);\n\t\t\t\t\tw -= low;\n\t\t\t\t\tlet color = [];\n\t\t\t\t\tfor( let k = 0; k < 4; k++)\n\t\t\t\t\t\tcolor[k] = palette[low][k]*(1-w) + palette[low+1][k]*w;\n\t\t\t\t\t\n\t\t\t\t\tgl.vertexAttrib4fv(attr.color, color);\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.cache.realError = Math.min(this.mesh.errors[id], this.cache.realError);\n\t\t\t\n\t\t\tlet offset = 0;\n\t\t\tlet end = 0;\n\t\t\tlet last = m.nfirstpatch[id+1]-1;\n\t\t\tfor (let p = m.nfirstpatch[id]; p < m.nfirstpatch[id+1]; ++p) {\n\t\t\t\tlet child = m.patches[p*3];\n\t\n\t\t\t\tif(!t.selected[child]) {\n\t\t\t\t\tend = m.patches[p*3+1];\n\t\t\t\t\tif(p < last) //we join patches if possible.\n\t\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif(end > offset) {\n\t\t\t\t\tif(m.vertex.texCoord && attr.uv >= 0) {\n\t\t\t\t\t\tvar tex = m.patches[p*3+2];\n\t\t\t\t\t\tif(tex != -1) { //bind texture\n\t\t\t\t\t\t\tvar texid = this.textures[tex];\n\t\t\t\t\t\t\tgl.activeTexture(gl.TEXTURE0 + attr.map);\n\t\t\t\t\t\t\tgl.bindTexture(gl.TEXTURE_2D, texid);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tlet mode = this.material.wireframe ? gl.LINE_STRIP : gl.TRIANGLES;\n\n\t\t\t\t\tgl.drawElements(mode, (end - offset) * 3, gl.UNSIGNED_SHORT, offset * 6);\n\t\t\t\t\trendered += end - offset;\n\t\t\t\t}\n\t\t\t\toffset = m.patches[p*3+1];\n\t\t\t}\n\t\t}\n\t\tthis.cache.rendered += rendered;\n\t}\n\n\tcreateNodeGeometry(id, data) {\n\t\tlet m = this.mesh;\n\t\tvar nv = m.nvertices[id];\n\t\tvar nf = m.nfaces[id];\n\t\tlet indices = data.index;\n\t\tlet vertices = new ArrayBuffer(nv*m.vsize);\n\t\tvar position = new Float32Array(vertices, 0, nv*3);\n\t\tposition.set(data.position);\n\t\tvar off = nv*12;\n\t\tif(m.vertex.texCoord) {\n\t\t\tvar uv = new Float32Array(vertices, off, nv*2);\n\t\t\tuv.set(data.uv);\n\t\t\toff += nv*8;\n\t\t}\n\t\tif(m.vertex.color) {\n\t\t\tvar color = new Uint8Array(vertices, off, nv*4);\n\t\t\tcolor.set(data.color);\n\t\t\toff += nv*4;\n\t\t}\n\t\tif(m.vertex.normal) {\n\t\t\tvar normal = new Int16Array(vertices, off, nv*3);\n\t\t\tnormal.set(data.normal);\n\t\t\toff += nv*6;\n\t\t}\n\t\t\n\t\t//needed for approximate picking.\n\t\tif(id < this.mesh.nroots) {\n\t\t\tlet basegeometry = new three__WEBPACK_IMPORTED_MODULE_0__.BufferGeometry();\n\t\t\tbasegeometry.setAttribute( 'position', new three__WEBPACK_IMPORTED_MODULE_0__.BufferAttribute(data.position, 3 ) );\n\t\t\tbasegeometry.setAttribute( 'normal', new three__WEBPACK_IMPORTED_MODULE_0__.BufferAttribute(data.normal, 3 ) );\n\t\t\tbasegeometry.setIndex(new three__WEBPACK_IMPORTED_MODULE_0__.BufferAttribute( data.index, 1 ) );\n\n\t\t\tthis.basemesh = new three__WEBPACK_IMPORTED_MODULE_0__.Mesh(basegeometry, this.material);\n\t\t\tthis.basemesh.visible = false;\n\t\t\tthis.add(this.basemesh);\n\t\t}\n\t\t\n\t\tvar gl = this.gl\n\t\tthis.vao[id] = {}; //one for each attrib combination in use (casting shadows for example).\n\t\tgl.bindVertexArray(null);\n\t\t\n\t\tvar vbo = this.vbo[id] = gl.createBuffer();\n\t\tgl.bindBuffer(gl.ARRAY_BUFFER, vbo);\n\t\tgl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);\n\t\tvar ibo = this.ibo[id] = gl.createBuffer();\n\t\tgl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo);\n\t\tgl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);\n\t}\n\n\tcreateTexture(id, image) {\n\t\tlet gl = this.gl;\n\t\tvar flip = gl.getParameter(gl.UNPACK_FLIP_Y_WEBGL);\n\t\tgl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);\n\t\tlet tex = this.textures[id] = gl.createTexture();\n\t\tgl.bindTexture(gl.TEXTURE_2D, tex);\n\n\t\t//TODO some textures might be alpha only! save space\n\t\tlet internalFormat;\n\t\tif (this.material && this.material.map && this.material.map.colorSpace === three__WEBPACK_IMPORTED_MODULE_0__.LinearSRGBColorSpace) {\n\t\t\tinternalFormat = gl.RGBA;\n\t\t} else if (typeof gl.SRGB8_ALPHA8 !== 'undefined') {\n\t\t\tinternalFormat = gl.SRGB8_ALPHA8;\n\t\t} else {\n\t\t\tinternalFormat = gl.RGBA;\n\t\t}\n\t\tgl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, gl.RGBA, gl.UNSIGNED_BYTE, image);\n\t\tgl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n\t\tgl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n\t\tgl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);\n\n\t\tfunction powerOf2(n) { return n && (n & (n - 1)) === 0; }\n\t\tif(!(gl instanceof WebGLRenderingContext) || (powerOf2(image.width) && powerOf2(image.height))) {\n\t\t\tgl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST_MIPMAP_LINEAR);\n\t\t\tgl.generateMipmap(gl.TEXTURE_2D);\n\t\t} else {\n\t\t\tgl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);\n\t\t}\n\n\t\tgl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flip); \n\t}\n\t//schedule for removal of this node( might not want to delete it in the middle of something.\n\t//TODO check if this is really needed!\n\tdeleteNodeGeometry(id) {\n\t\tthis.gl.deleteBuffer(this.vbo[id]);\n\t\tthis.gl.deleteBuffer(this.ibo[id]);\n\n\t\tthis.vbo[id] = this.ibo[id] = null;\n\t\tif(this.vao[id]) //node might have been loaded and unloaded before a rendering\n\t\t\tfor(const [state, vao] of Object.entries(this.vao[id]))\n\t\t\t\tthis.gl.deleteVertexArray(vao);\n\t\tthis.vao[id] = null;\n\t}\n\n\tdeleteTexture(tex) {\n\t\tif(!this.textures[tex])\n\t\t\treturn;\n\t\t//\tthrow \"Deleting missing texture!\"\n\n\t\tthis.gl.deleteTexture(this.textures[tex]);\n\t\tthis.textures[tex] = 0;\n\t}\n\n\tflush() {\n\t\tthis.cache.flush(this.mesh);\n\t}\n\n\tdispose() {\n\t\tthis.flush();\n\t\tfor(let child of this.children)\n\t\t\tchild.geometry.dispose();\n\t}\n\n\ttoJSON(meta) {\n\t\tthrow new Error(\"Can't convert to json.\");\n\t}\n\n\tpatchWebGLRenderer(renderer) {\n\t\tif(renderer.nexusPatched) return;\n\t\tlet f = renderer.renderBufferDirect;\n\t\trenderer.renderBufferDirect = ( camera, scene, geometry, material, object, group) => { \n\t\t\tf( camera, scene, geometry, material, object, group );\n\t\t\tif ( object.renderBufferDirect)\n\t\t\t\tobject.renderBufferDirect(renderer, scene, camera, geometry, material, group);\n\t\t};\n\t\trenderer.originalRender = renderer.render;\n\t\trenderer.render = (scene, camera) => {\n\t\t\t_Cache_js__WEBPACK_IMPORTED_MODULE_3__.Cache.beginFrame(30);\n\t\t\trenderer.originalRender(scene, camera);\n\t\t\t_Cache_js__WEBPACK_IMPORTED_MODULE_3__.Cache.endFrame();\n\t\t}\n\t\trenderer.nexusPatched = true;\n\t} \n\n}\n\n\n\n\n\n\n\n//# sourceURL=webpack://Nexus3D/./src/Nexus3D.js?\n}"); -/***/ }), +/***/ }, -/***/ "./src/PriorityQueue.js": +/***/ "./src/PriorityQueue.js" /*!******************************!*\ !*** ./src/PriorityQueue.js ***! \******************************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { +(__unused_webpack_module, __webpack_exports__, __webpack_require__) { eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ PriorityQueue: () => (/* binding */ PriorityQueue)\n/* harmony export */ });\n \n function PriorityQueue(max_length) {\n this.error = new Float32Array(max_length);\n this.data = new Int32Array(max_length);\n this.size = 0;\n}\n\nPriorityQueue.prototype = {\n push: function(data, error) {\n this.data[this.size] = data;\n this.error[this.size] = error;\n this.bubbleUp(this.size);\n this.size++;\n },\n\n pop: function() {\n var result = this.data[0];\n this.size--;\n if(this.size > 0) {\n this.data[0] = this.data[this.size];\n this.error[0] = this.error[this.size];\n this.sinkDown(0);\n }\n return result;\n },\n\n bubbleUp: function(n) {\n var data = this.data[n];\n var error = this.error[n];\n while (n > 0) {\n var pN = ((n+1)>>1) -1;\n var pError = this.error[pN];\n if(pError > error)\n break;\n //swap\n this.data[n] = this.data[pN];\n this.error[n] = pError;\n this.data[pN] = data;\n this.error[pN] = error;\n n = pN;\n }\n },\n\n sinkDown: function(n) {\n var data = this.data[n];\n var error = this.error[n];\n\n while(true) {\n var child2N = (n + 1) * 2;\n var child1N = child2N - 1;\n var swap = -1;\n if (child1N < this.size) {\n var child1Error = this.error[child1N];\n if(child1Error > error)\n swap = child1N;\n }\n if (child2N < this.size) {\n var child2Error = this.error[child2N];\n if (child2Error > (swap == -1 ? error : child1Error))\n swap = child2N;\n }\n\n if (swap == -1) break;\n\n this.data[n] = this.data[swap];\n this.error[n] = this.error[swap];\n this.data[swap] = data;\n this.error[swap] = error;\n n = swap;\n }\n }\n};\n\n\n\n\n\n\n//# sourceURL=webpack://Nexus3D/./src/PriorityQueue.js?\n}"); -/***/ }), +/***/ }, -/***/ "./src/Traversal.js": +/***/ "./src/Traversal.js" /*!**************************!*\ !*** ./src/Traversal.js ***! \**************************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { +(__unused_webpack_module, __webpack_exports__, __webpack_require__) { eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ Traversal: () => (/* binding */ Traversal)\n/* harmony export */ });\n/* harmony import */ var _Binary_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./Binary.js */ \"./src/Binary.js\");\n/* harmony import */ var _PriorityQueue_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./PriorityQueue.js */ \"./src/PriorityQueue.js\");\n\n\n\nfunction Traversal() {\n let t = this;\n t.maxBlocked = 30;\n\n t.modelMatrix = new Float32Array(16);\n t.viewMatrix = new Float32Array(16);\n t.projectionMatrix = new Float32Array(16);\n t.modelView = new Float32Array(16);\n t.modelViewInv = new Float32Array(16);\n t.modelViewProj = new Float32Array(16);\n t.modelViewProjInv = new Float32Array(16);\n t.planes = new Float32Array(24);\n t.viewport = new Float32Array(4);\n t.viewpoint = new Float32Array(4);\n}\n\nTraversal.prototype = {\n updateView: function(viewport, projection, modelView) {\n let t = this;\n\n for(let i = 0; i < 16; i++) {\n t.projectionMatrix[i] = projection[i];\n t.modelView[i] = modelView[i];\n }\n for(let i = 0; i < 4; i++)\n t.viewport[i] = viewport[i];\n\n (0,_Binary_js__WEBPACK_IMPORTED_MODULE_0__.matMul)(t.projectionMatrix, t.modelView, t.modelViewProj);\n (0,_Binary_js__WEBPACK_IMPORTED_MODULE_0__.matInv)(t.modelViewProj, t.modelViewProjInv);\n\n (0,_Binary_js__WEBPACK_IMPORTED_MODULE_0__.matInv)(t.modelView, t.modelViewInv);\n t.viewpoint[0] = t.modelViewInv[12];\n t.viewpoint[1] = t.modelViewInv[13];\n t.viewpoint[2] = t.modelViewInv[14];\n t.viewpoint[3] = 1.0;\n\n\n const m = t.modelViewProj;\n const mi = t.modelViewProjInv;\n let p = t.planes;\n\n //frustum planes Ax + By + Cz + D = 0;\n p[0] = m[0] + m[3]; p[1] = m[4] + m[7]; p[2] = m[8] + m[11]; p[3] = m[12] + m[15]; //left\n p[4] = -m[0] + m[3]; p[5] = -m[4] + m[7]; p[6] = -m[8] + m[11]; p[7] = -m[12] + m[15]; //right\n p[8] = m[1] + m[3]; p[9] = m[5] + m[7]; p[10] = m[9] + m[11]; p[11] = m[13] + m[15]; //bottom\n p[12] = -m[1] + m[3]; p[13] = -m[5] + m[7]; p[14] = -m[9] + m[11]; p[15] = -m[13] + m[15]; //top\n p[16] = -m[2] + m[3]; p[17] = -m[6] + m[7]; p[18] = -m[10] + m[11]; p[19] = -m[14] + m[15]; //near\n p[20] = -m[2] + m[3]; p[21] = -m[6] + m[7]; p[22] = -m[10] + m[11]; p[23] = -m[14] + m[15]; //far\n\n //normalize planes to get also correct distances\n for(let i = 0; i < 24; i+= 4) {\n let l = Math.sqrt(p[i]*p[i] + p[i+1]*p[i+1] + p[i+2]*p[i+2]);\n p[i] /= l; p[i+1] /= l; p[i+2] /= l; p[i+3] /= l;\n }\n \n //side is M'(1,0,0,1) - M'(-1,0,0,1) and they lie on the planes\n const r3 = mi[3] + mi[15];\n const r0 = (mi[0] + mi[12 ])/r3;\n const r1 = (mi[1] + mi[13 ])/r3;\n const r2 = (mi[2] + mi[14 ])/r3;\n\n const l3 = -mi[3] + mi[15];\n const l0 = (-mi[0] + mi[12 ])/l3 - r0;\n const l1 = (-mi[1] + mi[13 ])/l3 - r1;\n const l2 = (-mi[2] + mi[14 ])/l3 - r2;\n\n const side = Math.sqrt(l0*l0 + l1*l1 + l2*l2);\n\n //center of the scene is M'*(0, 0, 0, 1)\n const c0 = mi[12]/mi[15] - t.viewpoint[0];\n const c1 = mi[13]/mi[15] - t.viewpoint[1];\n const c2 = mi[14]/mi[15] - t.viewpoint[2];\n const dist = Math.sqrt(c0*c0 + c1*c1 + c2*c2);\n\n const resolution = (2*side/dist)/ t.viewport[2];\n t.currentResolution == resolution ? t.sameResolution = true : t.sameResolution = false;\n t.currentResolution = resolution;\n },\n\n traverse : function (mesh, cache) {\n let t = this;\n t.mesh = mesh;\n// if(Debug.extract == true)\n // return;\n\n if(!mesh.isReady) return;\n \n const n = mesh.nodesCount;\n t.visited = new Uint8Array(n);\n t.blocked = new Uint8Array(n);\n t.selected = new Uint8Array(n);\n\n t.frame = cache.frame;\n\n t.instance_errors = new Float32Array(n);\n\n if(t.frame > mesh.frame) { //clean the errors.\n mesh.errors = new Float32Array(n); \n mesh.frame = t.frame;\n }\n\n t.visitQueue = new _PriorityQueue_js__WEBPACK_IMPORTED_MODULE_1__.PriorityQueue(n);\n for(var i = 0; i < mesh.nroots; i++)\n t.insertNode(i);\n \n t.currentError = cache.currentError;\n t.drawSize = 0;\n t.nblocked = 0;\n\n var requested = 0;\n while(t.visitQueue.size && t.nblocked < t.maxBlocked) {\n var error = t.visitQueue.error[0];\n var id = t.visitQueue.pop();\n\n //if not loaded and the queue is not full add to the candidates.\n if (mesh.status[id] == 0 && requested < cache.maxPending) {\n cache.candidates.push({id: id, mesh:mesh, frame:t.frame, error:error});\n requested++;\n }\n /* we don't want to stop as soon as a node is not availabe, because the nodes are sorted by error, \n this could cause a large drop in quality elsewere.\n we still need to mark all children as blocked, to prevent including them in the cut of the dag. */\n var blocked = t.blocked[id] || !t.expandNode(id, error);\n if (blocked)\n t.nblocked++;\n else {\n t.selected[id] = 1;\n //cache.realError = Math.min(error, cache.realError);\n }\n t.insertChildren(id, blocked);\n }\n\n //update remaining errors in the cache\n if(cache.nodes.has(mesh)) {\n for(let id of cache.nodes.get(mesh)) {\n let error = t.nodeError(id);\n if(t.instance_errors[id] == 0) {\n t.instance_errors[i] = error;\n mesh.errors[id] = Math.max(mesh.errors[id], error);\n }\n }\n }\n\n t.mesh = null;\n return t.instance_errors;\n },\n\n insertNode: function (node) {\n let t = this;\n t.visited[node] = 1;\n\n const error = t.nodeError(node);\n\n t.instance_errors[node] = error;\n t.mesh.errors[node] = Math.max(error, t.mesh.errors[node]);\n t.mesh.frames[node] = t.frame;\n\n// if(node > 0 && error < t.currentError) return; //2% speed TODO check if needed\n\n t.visitQueue.push(node, error);\n },\n\n insertChildren : function (node, block) {\n let t = this;\n for(let i = t.mesh.nfirstpatch[node]; i < t.mesh.nfirstpatch[node+1]; ++i) {\n const child = t.mesh.patches[i*3];\n if (child == t.mesh.sink) return;\n if (block) t.blocked[child] = 1;\n if (!t.visited[child])\n t.insertNode(child);\n }\n },\n\n expandNode : function (node, error) {\n let t = this;\n if(node > 0 && error < t.currentError) {\n//\t\t\tconsole.log(\"Reached error\", error, t.currentError);\n return false;\n }\n\n if(t.drawSize > t.drawBudget) {\n//\t\t\tconsole.log(\"Reached drawsize\", t.drawSize, t.drawBudget);\n return false;\n }\n\n if(t.mesh.status[node] != 1) { //not ready\n//\t\t\tconsole.log(\"Node \" + node + \" still not loaded (cache?)\");\n return false;\n }\n\n const sp = t.mesh.nspheres;\n const off = node*5;\n if(t.isVisible(sp[off], sp[off+1], sp[off+2], sp[off+3])) //expanded radius\n t.drawSize += t.mesh.nvertices[node]*0.8;\n //we are adding half of the new faces. (but we are using the vertices so *2)\n\n return true;\n },\n\n nodeError : function (n, tight) {\n let t = this;\n const spheres = t.mesh.nspheres;\n const b = t.viewpoint;\n const off = n*5;\n const cx = spheres[off+0];\n const cy = spheres[off+1];\n const cz = spheres[off+2];\n let r = spheres[off+3];\n if(tight)\n r = spheres[off+4];\n const d0 = b[0] - cx;\n const d1 = b[1] - cy;\n const d2 = b[2] - cz;\n let dist = Math.sqrt(d0*d0 + d1*d1 + d2*d2) - r;\n if (dist < 0.1)\n dist = 0.1;\n\n //resolution is how long is a pixel at distance 1.\n let error = t.mesh.nerrors[n]/(t.currentResolution*dist); //in pixels\n\n //causes flickering due to things popping in and out of visibility, causes a huge resorting.\n /*if (!t.isVisible(cx, cy, cz, spheres[off+4]))\n error /= 100.0; */\n\n //more stable, at least it's continuous.\n let d = t.distance(cx, cy, cz, spheres[off+4]);\n if(d < -r) {\n error /= 101.0;\n } else if(d < 0) {\n error /= 1 -( d/r)*100.0\n } \n return error;\n },\n\n distance: function(x, y, z, r) {\n const p = this.planes;\n let min_distance = 1e20;\n for (let i = 0; i < 24; i +=4) {\n let d = p[i]*x + p[i+1]*y + p[i+2]*z + p[i+3] + r;\n if(d < min_distance)\n min_distance = d;\n }\n return min_distance;\n },\n\n isVisible : function (x, y, z, r) {\n const p = this.planes;\n for (let i = 0; i < 24; i +=4) {\n if(p[i]*x + p[i+1]*y + p[i+2]*z + p[i+3] + r < 0) //plane is ax+by+cz+d = 0; \n return false;\n }\n return true;\n }\n}; \n\n\n\n\n//# sourceURL=webpack://Nexus3D/./src/Traversal.js?\n}"); -/***/ }), +/***/ }, -/***/ "three": +/***/ "three" /*!**************************************************************************************!*\ !*** external {"commonjs":"three","commonjs2":"three","amd":"three","root":"THREE"} ***! \**************************************************************************************/ -/***/ ((module) => { +(module) { module.exports = __WEBPACK_EXTERNAL_MODULE_three__; -/***/ }) +/***/ } /******/ }); /************************************************************************/ @@ -110,12 +110,21 @@ module.exports = __WEBPACK_EXTERNAL_MODULE_three__; /******/ }; /******/ /******/ // Execute the module function +/******/ if (!(moduleId in __webpack_modules__)) { +/******/ delete __webpack_module_cache__[moduleId]; +/******/ var e = new Error("Cannot find module '" + moduleId + "'"); +/******/ e.code = 'MODULE_NOT_FOUND'; +/******/ throw e; +/******/ } /******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = __webpack_modules__; +/******/ /************************************************************************/ /******/ /* webpack/runtime/compat get default export */ /******/ (() => { @@ -157,6 +166,32 @@ module.exports = __WEBPACK_EXTERNAL_MODULE_three__; /******/ }; /******/ })(); /******/ +/******/ /* webpack/runtime/jsonp chunk loading */ +/******/ (() => { +/******/ __webpack_require__.b = (typeof document !== 'undefined' && document.baseURI) || self.location.href; +/******/ +/******/ // object to store loaded and loading chunks +/******/ // undefined = chunk not loaded, null = chunk preloaded/prefetched +/******/ // [resolve, reject, Promise] = chunk loading, 0 = chunk loaded +/******/ var installedChunks = { +/******/ "Nexus3D": 0 +/******/ }; +/******/ +/******/ // no chunk on demand loading +/******/ +/******/ // no prefetching +/******/ +/******/ // no preloaded +/******/ +/******/ // no HMR +/******/ +/******/ // no HMR manifest +/******/ +/******/ // no on chunks loaded +/******/ +/******/ // no jsonp function +/******/ })(); +/******/ /************************************************************************/ /******/ /******/ // startup diff --git a/nexus3d/dist/index.html b/nexus3d/dist/index.html index e0b38a30..1dc8d236 100644 --- a/nexus3d/dist/index.html +++ b/nexus3d/dist/index.html @@ -23,6 +23,10 @@
- + + + diff --git a/nexus3d/src/corto.worker.js b/nexus3d/src/corto.worker.js new file mode 100644 index 00000000..bcb41e5e --- /dev/null +++ b/nexus3d/src/corto.worker.js @@ -0,0 +1,221 @@ +/* +Corto +Copyright (c) 2017-2020, Visual Computing Lab, ISTI - CNR +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +onmessage = async function(job) { + if(typeof(job.data) == "string") return; + + var buffer = job.data.buffer; + if(!buffer) return; + if(!CortoDecoder.instance) + await CortoDecoder.ready; + + var model = CortoDecoder.decode(buffer, job.data.short_index, job.data.short_normals, job.data.rgba_colors ? 4 : 3); + postMessage({ model: model, buffer: buffer, request: job.data.request}); +}; + +var CortoDecoder = (function() { + "use strict"; + var wasm_base = "0061736D01000000014E0D60027F7F0060037F7F7F0060017F017F60017F0060057F7F7F7F7F0060037F7F7F017F60047F7F7F7F0060027F7F017F60067F7F7F7F7F7F006000006000017F60017E017F60047F7F7F7F017F024A0203656E761F656D736372697074656E5F6E6F746966795F6D656D6F72795F67726F777468000316776173695F736E617073686F745F70726576696577310970726F635F657869740003037D7C09070200020207070202020000000100000003030B010000000701030107000300010C0701040500040601000106000105010400020302010002030204000101010102010101000000060700020301000101010005090A050502020203020902000200000203010000010002030505050106060601040408080A03020405017001333305060101108080020608017F0141D0D2010B07D4021C066D656D6F727902000A6E65774465636F6465720003076E67726F75707300040667726F7570730005056E766572740006056E66616365000707686173417474720008096861734E6F726D616C000A08686173436F6C6F72000B056861735576000C0C736574506F736974696F6E73000D0C7365744E6F726D616C733332000E0C7365744E6F726D616C733136000F09736574436F6C6F727300100673657455767300110A736574496E646578313600120A736574496E64657833320013066465636F646500140D64656C6574654465636F6465720015195F5F696E6469726563745F66756E6374696F6E5F7461626C6501000B5F696E697469616C697A650002105F5F6572726E6F5F6C6F636174696F6E0058066D616C6C6F63005D0466726565005E047362726B005C09737461636B53617665007B0C737461636B526573746F7265007C0A737461636B416C6C6F63007D0938010041010B3236373826272D2E313334353B3C3D393E3F4041433E3A3C44454647484E4F44505152535455026D6E6767707A78736E7977740ADFD6037C0300010B850B010A7F027F418801105F2102230041306B220324002002410C6A22004200370200200241186A220442003702002002200036020820022004360214200241206A410041D40010591A20024100360284012002410136027820024180016A2200200141096A3602002002200136027C200220012D00083602782001280009210520002001410D6A2201360200024020050440200241086A2106200341106A210703402002200141026A220420012F00006A2201360280012002200141026A220820012F00006A360280012004105B220141704F0D02024002402001410B4F0440200141106A4170712209105F21002003200941808080807872360210200320003602082003200136020C0C010B200320013A0013200341086A21002001450D010B20002004200110561A0B200020016A41003A00002006200341286A200341086A1028220028020022014504404128105F22012007280200360218200120032903083702102007410036020020034200370308200141003602242001420037021C20012003280228360208200142003702002000200136020020062802002802002204047F2006200436020020002802000520010B2100200228020C200010292002200228021041016A3602100B2001411C6A2008106C20032C001341004804402003280208105E0B2002280280012101200A41016A220A2005470D000B0B200128000021092002200141046A2201360280014100210A200941004A0440200241146A210703402002200141026A220420012F00006A360280012004105B220141704F0D02024002402001410B4F0440200141106A4170712208105F21002003200841808080807872360210200320003602082003200136020C0C010B200320013A0013200341086A21002001450D010B20002004200110561A0B200020016A41003A0000200228028001220128000021002002200141046A36028001200128000421042002200141096A3602800120012D0008210520022001410A6A3602800120012D0009210820022001410B6A3602800120012D000A21060240024002400240200041026B0E020001020B41C800105F220142003702042001420137021420014200370224200141AC0A3602002001410C6A220042003702002001420037022C200142003702342001420037023C20014100360244200141033602082001410036022020004300000044380200200120012802104102723602100C020B41CC00105F22014200370204200142003702202001420137021420014104360248200141800B36020020014284808080C0003702382001420037020C200142003702282001420037023020012005360208200141406B428480808080013702000C010B4138105F220142003702042001420137021420014200370220200141AC0C3602002001420037020C2001420037022820014200370230200120053602080B200120083602142001200436020C200120063602102003200341086A360220200341206A2100230041106B22062400200341286A220B20072006410C6A200341086A102822082802002204047F4100054120105F220441106A21050240200028020022002C000B41004E044020052000290200370200200520002802083602080C010B200520002802002000280204106B0B2004410036021C2004200628020C360208200442003702002008200436020020072802002802002200047F2007200036020020082802000520040B21002007280204200010292007200728020841016A36020841010B3A0004200B2004360200200641106A24002003280228200136021C20032C001341004804402003280208105E0B2002280280012101200A41016A220A2009470D000B0B200220012800003602002002200141046A36028001200220012800043602042002200141086A36028001200341306A240020020C010B1057000B0B13002000280244200041406B2802006B4104750BF60101047F02402000280244200041406B28020022026B220041004C0D00200041047622004101200041014A1B2203410371210541002100200341016B41034F0440200341FCFFFFFF007121030340200120004102746A200220004104746A2802003602002001200041017222044102746A200220044104746A2802003602002001200041027222044102746A200220044104746A2802003602002001200041037222044102746A200220044104746A280200360200200041046A2100200341046B22030D000B0B2005450D000340200120004102746A200220004104746A280200360200200041016A2100200541016B22050D000B0B0B070020002802000B070020002802040B08002000200110090BF90201077F230041106B220424002001105B22024170490440024002402002410B4F0440200241106A4170712205105F2103200420054180808080787236020820042003360200200420023602040C010B200420023A000B200421032002450D010B20032001200210561A0B200220036A41003A0000027F20042101200041146A2802042203047F2001280200200120012D000B220041187441187541004822021B21072001280204200020021B21010340024002400240200328021420032D001B2200200041187441187541004822051B22002001200020014922081B2202044002402007200341106A2206280200200620051B22062002105A2205450440200020014D0D010C050B20054100480D040B200620072002105A2200450D0120004100480D0241010C060B200020014B0D020B20080D0041010C040B200341046A21030B200328020022030D000B41000541000B0B210220042C000B41004804402004280200105E0B200441106A240020024100470F0B1057000B0900200041920810090B0900200041830810090B0900200041800810090B0D00200041890820014106104B0B0D00200041920820014106104B0B0D00200041920820014103104B0BDE0201067F230041106B22032400200341053A000B200341003A0005200341830828000036020020034187082D00003A00042000220541146A22002003104C210420032C000B41004804402003280200105E0B2004200541186A470440200341053A000B200341003A0005200341830828000036020020034187082D00003A000420002003410C6A20031028220828020022044504404120105F2204200341086A22072802003602182004200329030037021020074100360200200342003703002004410036021C2004200328020C360208200442003702002008200436020020002802002802002207047F2000200736020020082802000520040B21002005280218200010292005411C6A2205200528020041016A3602000B200428021C22050440200541F80941E00B107121060B20032C000B41004804402003280200105E0B2006200236024820062001360204200641043602140B200341106A24000B0D00200041800820014106104B0B0900200020013602240B0900200020013602200B9430012B7F024020002802040440230041206B221124002000220941206A2224200041F8006A2215104D2015280208220028000021022015200041046A3602082024200236025020152024412C6A101A2015280208220028000021022015200041046A2200360208200020152802046B410371220404402015200020046B41046A22003602080B202441386A20022000101C2015201528020820024102746A36020820092802142200200941186A221F47044003400240200022022C001B41004E04402011200241106A2205280208360218201120052902003703100C010B201141106A20022802102002280214106B0B2011200228021C220536021C200520092802002015200528020028021C11010020112C001B41004804402011280210105E0B024020022802042205450440200228020822002802002002460D01200241086A210203402002280200220541086A2102200520052802082200280200470D000B0C010B03402005220028020022050D000B0B2000201F470D000B0B0240200928020022052009280238200941346A221528020022026B410C6D22004B0440200520006B220A20152802082202201528020422046B410C6D4D044020152004200A410C6C410C6E410C6C6A3602040C020B02402004201528020022006B2204410C6D2213200A6A221741D6AAD5AA014F0D002017200220006B410C6D2202410174220320032017491B41D5AAD5AA01200241AAD5AAD500491B2202047F200241D6AAD5AA014F0D012002410C6C105F0541000B22172013410C6C6A2203200441746D410C6C6A21132003200A410C6C410C6E410C6C6A210A20172002410C6C6A2102200441004A044020132000200410561A0B201520023602082015200A36020420152013360200200004402000105E0B0C020B1057000B200020054D0D00200920022005410C6C6A3602380B410021002011410036020C200941406B2802002205200928024422254704400340200041036C2119200528020041036C21262011410C6A21274100211B4100211C410021174100210A410021204100212141002128410021224100212B02400240200928027022000440200041ABD5AAD5004F0D01200041186C2200105F220A20006A21200B4101211D202620196B220041024F044020004100480D0120004101764102742200105F221720006A21210B2009350200101621000240201920264F0440200A2104201721130C010B200941D8006A2123200041016A2129417F21144101211E200A210420172113024003402019211A20202106200A210120042100202121072017210820132102034002400240027F0240027F02400240201E4101714504402019211A20202106200A210120042100202121072017210820132102201421220C010B201D2028200820026B2212410275220F4F2203710440200928028401210320272027280200220D41016A360200200341016B21034100210B027F0240200D200928024C6A2D00004106470D0020234103101F220B410171450D0020232029101F0C010B2009280234200928028401410C6C6A220D2003360208200D2003360204200D20033602002009200928028401220341016A3602840120030B210C02402009280224220D0440200D201A4101746A200C3B01000C010B2009280220201A4102746A200C3602000B200B410271450D0220232029101F0C030B02402003450440200220284102746A2103202841016A21280C010B201B201C460D01201C41046B2203211C0B200328020021220B417F21142000202241186C6A22032D00140D022003280210210C200328020C210E200328020821162003280204210D2003280200210B20272027280200220341016A3602002003200928024C6A2D000022104104460D02200120006B221841186D21142000200C41186C6A221241146A21042000200E41186C6A220F41106A212A2012280210211D200F28020C211E200F2802002119417F210302400240024002400240024002400240024020100E0701030406080500080B20232029101F21030C010B2009280234200928028401410C6C6A220320163602082003200B3602042003200D3602002009200928028401220341016A360284010B202A20143602002012201441016A220F36020C024020012006490440200141003A00142001200F3602102001200E36020C2001200D360208200120033602042001200B3602002000210E0C010B200F41ABD5AAD5004F0D11200F200620006B41186D220141017422062006200F491B41AAD5AAD500200141D5AAD52A491B220141ABD5AAD5004F0D11200141186C2206105F2210201441186C6A220141003A00142001200F3602102001200E36020C2001200D360208200120033602042001200B3602002001201841686D41186C6A210E201841004A0440200E2000201810561A0B200620106A21062000105E0B200141186A2212200E6B220F41186D21000240200720084B044020082000360200200841046A21080C010B200820026B2216410275221841016A22084180808080044F0D112008200720026B22074101752210200820104B1B41FFFFFFFF03200741027541FFFFFFFF01491B2207047F20074180808080044F0D122007410274105F0541000B221020184102746A2208200036020020074102742107201641004A044020102002201610561A0B200720106A2107200841046A2108200204402002105E0B201021020B200620124B044020012003360218200141003A002C2001200C360228200120143602242001200B3602202001200D36021C200141306A2101200E21000C060B200041016A220141ABD5AAD5004F0D1020012006200E6B41186D22064101742210200120104B1B41AAD5AAD500200641D5AAD52A491B220141ABD5AAD5004F0D10200141186C2206105F2210200041186C6A220141003A00142001200C3602102001201436020C2001200B3602082001200D360204200120033602002001200F41686D41186C6A2100200F41004A04402000200E200F10561A0B200620106A2106200141186A2101200E105E0C050B200F41013A00142000201E41186C6A20143602102012201436020C20012006490440200141003A00142001200C3602102001201E36020C2001200B3602082001200D36020420012019360200200141186A21010C040B201441016A220341ABD5AAD5004F0D0F2003200620006B41186D22014101742206200320064B1B41AAD5AAD500200141D5AAD52A491B220341ABD5AAD5004F0D0F200341186C2201105F2206201441186C6A220341003A00142003200C3602102003201E36020C2003200B3602082003200D360204200320193602002003201841686D41186C6A210C201841004A0440200C2000201810561A0B200120066A2106200341186A21012000105E20192103200C21000C040B20122802042103200441013A00002000201D41186C6A201436020C202A201436020020012006490440200141003A00142001201D3602102001200E36020C2001200D360208200120033602042001200B360200200141186A21010C040B201441016A220141ABD5AAD5004F0D0E2001200620006B41186D2206410174220C2001200C4B1B41AAD5AAD500200641D5AAD52A491B220141ABD5AAD5004F0D0E200141186C2206105F220F201441186C6A220141003A00142001201D3602102001200E36020C2001200D360208200120033602042001200B3602002001201841686D41186C6A210C201841004A0440200C2000201810561A0B2006200F6A2106200141186A21012000105E200C21000C030B201C202B470440201C2022360200201C41046A211C417F21140C060B202B201B6B220D410275220C41016A22034180808080044F0D0D2003200D410175220B2003200B4B1B41FFFFFFFF03200C41FFFFFFFF01491B220B047F200B4180808080044F0D0E200B410274105F0541000B2203200C4102746A220C2022360200200B410274210B200D41004A04402003201B200D10561A0B2003200B6A212B200C41046A211C417F2114201B4504402003211B0C060B201B105E2003211B0C050B200F41013A0014200441013A00002000201E41186C6A201D3602102000201D41186C6A201E36020C417F21140B201921030B02402009280224220C0440200C201A4101746A220C200D3B0100200C200B3B0102200C20033B01040C010B2009280220201A4102746A220C200D360200200C2003360208200C200B3602040B200221132008211720072121200021042001210A20062120201A41036A0C030B2009280234200928028401410C6C6A220D2003360208200D2003360204200D20033602002009200928028401220341016A3602840120030B210D201A41016A210E02402009280224221004402010200E4101746A200D3B01000C010B2009280220200E4102746A200D3602000B0240200B410471044020232029101F210B0C010B2009280234200928028401410C6C6A220B2003360208200B2003360204200B20033602002009200928028401220B41016A360284010B201A41026A210302402009280224220E0440200E20034101746A200B3B01000C010B200928022020034102746A200B3602000B200120006B221041186D2103200720084D0D0220082003360200200841046A21080C030B200221132008211720072121200021042001210A20062120201A0B2119201B201C46211D2014417F46211E20192026490D030C040B200F41016A220E4180808080044F0D05200E200720026B220741017522082008200E491B41FFFFFFFF03200741027541FFFFFFFF01491B2207047F20074180808080044F0D062007410274105F0541000B220E200F4102746A2208200336020020074102742107201241004A0440200E2002201210561A0B2007200E6A2107200841046A2108200204402002105E0B200E21020B200341016A210E200341026A210F024020012006490440200141003A00142001200E3602102001200F36020C2001200C3602082001200B3602042001200D360200200141186A21010C010B200E41ABD5AAD5004F0D05200E200620006B41186D220141017422062006200E491B41AAD5AAD500200141D5AAD52A491B220141ABD5AAD5004F0D05200141186C2206105F2216200341186C6A220141003A00142001200E3602102001200F36020C2001200C3602082001200B3602042001200D3602002001201041686D41186C6A2112201041004A044020122000201010561A0B200620166A2106200141186A2101200004402000105E0B201221000B200120006B221241186D21100240200720084B044020082010360200200841046A21080C010B200820026B2218410275222A41016A22084180808080044F0D052008200720026B22074101752216200820164B1B41FFFFFFFF03200741027541FFFFFFFF01491B2207047F20074180808080044F0D062007410274105F0541000B2216202A4102746A2208201036020020074102742107201841004A044020162002201810561A0B200720166A2107200841046A2108200204402002105E0B201621020B024020012006490440200141003A00142001200F3602102001200336020C2001200D3602082001200C3602042001200B360200200141186A21010C010B201041016A220141ABD5AAD5004F0D052001200620006B41186D22064101742216200120164B1B41AAD5AAD500200641D5AAD52A491B220141ABD5AAD5004F0D05200141186C2206105F2216201041186C6A220141003A00142001200F3602102001200336020C2001200D3602082001200C3602042001200B3602002001201241686D41186C6A210F201241004A0440200F2000201210561A0B200620166A2106200141186A2101200004402000105E0B200F21000B200120006B221041186D210F0240200720084B04402008200F360200200841046A21080C010B200820026B2216410275221841016A22084180808080044F0D052008200720026B22074101752212200820124B1B41FFFFFFFF03200741027541FFFFFFFF01491B2207047F20074180808080044F0D062007410274105F0541000B221220184102746A2208200F36020020074102742107201641004A044020122002201610561A0B200720126A2107200841046A2108200204402002105E0B201221020B024020012006490440200141003A0014200120033602102001200E36020C2001200B3602082001200D3602042001200C360200200141186A21010C010B200F41016A220141ABD5AAD5004F0D052001200620006B41186D22064101742212200120124B1B41AAD5AAD500200641D5AAD52A491B220141ABD5AAD5004F0D05200141186C2206105F2212200F41186C6A220141003A0014200120033602102001200E36020C2001200B3602082001200D3602042001200C3602002001201041686D41186C6A2103201041004A044020032000201010561A0B200620126A2106200141186A2101200004402000105E0B200321000B201A41036A221A2026490D000B0B20022113200021040B201B450D00201B105E0B201304402013105E0B200404402004105E0B0C010B1057000B20052802002100200541106A2202210520022025470D000B0B0240200941146A22252802002200201F460D0003400240200022022C001B41004E04402011200241106A2205280208360218201120052902003703100C010B201141106A20022802102002280214106B0B2011200228021C220536021C200520092802002015200528020028022011010020112C001B41004804402011280210105E0B024020022802042205450440200228020822002802002002460D01200241086A210203402002280200220541086A2102200520052802082200280200470D000B0C010B03402005220028020022050D000B0B2000201F470D000B20092802142200201F460D0003400240200022022C001B41004E04402011200241106A2205280208360218201120052902003703100C010B201141106A20022802102002280214106B0B2011200228021C220536021C20052009280200200928020420252024200528020028022411040020112C001B41004804402011280210105E0B024020022802042205450440200228020822002802002002460D01200241086A210203402002280200220541086A2102200520052802082200280200470D000B0C010B03402005220028020022050D000B0B2000201F470D000B20252802002200201F460D0003400240200022022C001B41004E04402011200241106A2205280208360218201120052902003703100C010B201141106A20022802102002280214106B0B2011200228021C220536021C20052009280200200528020028022811000020112C001B41004804402011280210105E0B024020022802042205450440200228020822002802002002460D01200241086A210203402002280200220541086A2102200520052802082200280200470D000B0C010B03402005220028020022050D000B0B2000201F470D000B0B201141206A24000C010B230041206B220A2400200A4100360218200A42003703102000221341206A200041F8006A2203104D20002802142200201341186A221747044003400240200022042C001B41004E0440200A200441106A2200280208360208200A20002902003703000C010B200A20042802102004280214106B0B200A200428021C220036020C200020132802002003200028020028021C110100200A2C000B4100480440200A280200105E0B024020042802042202450440200428020822002802002004460D01200441086A210403402004280200220241086A2104200220022802082200280200470D000B0C010B03402002220028020022020D000B0B20002017470D000B201328021421000B024020002017460D0003400240200022042C001B41004E0440200A200441106A2200280208360208200A20002902003703000C010B200A20042802102004280214106B0B200A200428021C220036020C20002013280200200A41106A2000280200280220110100200A2C000B4100480440200A280200105E0B024020042802042202450440200428020822002802002004460D01200441086A210403402004280200220241086A2104200220022802082200280200470D000B0C010B03402002220028020022020D000B0B20002017470D000B201328021422022017460D0003400240200222042C001B41004E0440200A200441106A2200280208360208200A20002902003703000C010B200A20042802102004280214106B0B200A200428021C220036020C200020132802002000280200280228110000200A2C000B4100480440200A280200105E0B024020042802042200450440200428020822022802002004460D01200441086A210403402004280200220041086A2104200020002802082202280200470D000B0C010B03402000220228020022000D000B0B20022017470D000B0B200A28021022000440200A20003602142000105E0B200A41206A24000B0BA60301057F20000440230041106B22042400200022022802142200200241186A220547044003400240200022032C001B41004E04402004200341106A2201280208360208200420012902003703000C010B200420032802102003280214106B0B2004200328021C220136020C20010440200120012802002802041103000B20042C000B41004804402004280200105E0B024020032802042201450440200328020822002802002003460D01200341086A210303402003280200220141086A2103200120012802082200280200470D000B0C010B03402001220028020022010D000B0B20002005470D000B0B200241D8006A101D200228024C22010440200220013602502001105E0B200241406B2802002200044020002002280244220146047F20000503402001410C6B200141086B2802001049200141106B22012000470D000B20022802400B2101200220003602442001105E0B200228023422010440200220013602382001105E0B200241146A21002002280228220104402002200136022C2001105E0B20002002280218104A200241086A200228020C1049200441106A24002002105E0B0B2A01027F200042025A04400340200141016A2101200042035621022000420188210020020D000B0B20010BC21D01147F024002400240024020002802000E020001020B200041046A2111024020002802082204200028020422066B2208417B4D0440200028020C220D20046B41044F0440200441003600002000200441046A2204360208201128020021060C020B200841046A22044100480D042004200D20066B220D410174220B2004200B4B1B41FFFFFFFF07200D41FFFFFFFF03491B220404402004105F21160B200820166A220D4100360000200420166A210B200D41046A2104200841004A044020162006200810561A0B2000200B36020C2000200436020820002016360204200604402006105E200028020821040B201128020021060C010B2000200620086A41046A22043602080B200620086A20013602000240200420066B2208200120086A2204490440201120011018201128020021060C010B200420084F0D002000200420066A3602080B200620086A2002200110561A0F0B230041E0006B220424002004420037021C200442003702242004420037022C200442003702342004420037023C200442003703502004420037035820044200370214200442FF8180808001370308200442003703482004410236024420022103200441086A2206200628020C360210418008105F4100418008105921110240200141004C0D0020014103712108200141016B41034F04402001417C71210D034020112003200E6A2D00004102746A220F200F28020041016A36020020112003200E4101726A2D00004102746A220F200F28020041016A36020020112003200E4102726A2D00004102746A220F200F28020041016A36020020112003200E4103726A2D00004102746A220F200F28020041016A360200200E41046A210E200D41046B220D0D000B0B2008450D00034020112003200E6A2D00004102746A220D200D28020041016A360200200E41016A210E200841016B22080D000B0B02400240034002402011200B4102746A280200220E41004C0D00200E41FF016C20016D21082006280210220E2006280214220F490440200E200B41FF01712008410874723B00002006200E41026A3602100C010B200E200628020C22036B220D417D4C0D02200D410175221041016A2214200F20036B220E200E2014491B41FFFFFFFF07200E41017541FFFFFFFF03491B220F047F200F4100480D03200F410174105F0541000B210E200E20104101746A2214200B41FF01712008410874723B0000200E200F4101746A2108201441026A210F200D41004A0440200E2003200D10561A0B200620083602142006200F3602102006200E36020C2003450D002003105E0B200B41016A220B418002470D000B200628020C200628021010202011105E0C010B1057000B200041046A2116200441086A102141002108230041106B220D24000240200441086A2207280210200728020C6B220B4104480D004101210E200B41017521050240200728023C220A41004C0D00200A4107712109200A41016B41074F0440200A417871210A03402005200E6C20056C20056C20056C20056C20056C20056C20056C210E200A41086B220A0D000B0B2009450D0003402005200E6C210E200941016B22090D000B0B200D41003A000F02402007280250200728024C220A6B220941FF014D0440200D410F6A210C41800220096B2213200741CC006A22152802082203201528020422146B4D044002402013450D00201341016B21102014210320134107712212044003402003200C2D00003A0000200341016A2103201241016B22120D000B0B201320146A211420104107490D0003402003200C2D00003A00002003200C2D00003A00012003200C2D00003A00022003200C2D00003A00032003200C2D00003A00042003200C2D00003A00052003200C2D00003A00062003200C2D00003A0007200341086A22032014470D000B0B201520143602040C020B2014201528020022106B220F20136A221241004E0440200F2012200320106B2203410174221020102012491B41FFFFFFFF07200341FFFFFFFF03491B220F047F200F105F0541000B22116A221021032013410771221204402010210303402003200C2D00003A0000200341016A2103201241016B22120D000B0B201020136A2112201341016B41074F044003402003200C2D00003A00002003200C2D00003A00012003200C2D00003A00022003200C2D00003A00032003200C2D00003A00042003200C2D00003A00052003200C2D00003A00062003200C2D00003A0007200341086A22032012470D000B0B200F20116A2113201020142015280200220C6B22036B2114200341004A04402014200C200310561A0B201520133602082015201236020420152014360200200C0440200C105E0B0C020B1057000B2009418002460D002007200A4180026A3602500B0240200B41004C0D0020054101200541014A1B220A410371210941002105200A41016B41034F0440200A41FCFFFFFF0771210A0340200728024C200728020C20054101746A2D00006A20053A0000200728024C200728020C2005410172220B4101746A2D00006A200B3A0000200728024C200728020C2005410272220B4101746A2D00006A200B3A0000200728024C200728020C2005410372220B4101746A2D00006A200B3A0000200541046A2105200A41046B220A0D000B0B2009450D000340200728024C200728020C20054101746A2D00006A20053A0000200541016A2105200941016B22090D000B0B2007280200200728020C2D0001480D0020072007280240360244200D41FFFFFF07360208200741406B2113200E04402013200E200D41086A10230B2007280218220F200728021C460D000340410021124100211503402007280210200728020C6B4101752105200728023C210C02402008410274220920072802246A280200221020126B220341004C0440410021090C010B2009200F6A28020020126A2106200728024C2111200728023021144100210A200C4100200C41004A1B210B410021090340200A200B460D01201120142006200A6A6A2D00006A2D0000200520096C6A2109200A41016A220A2003470D000B0B200941016A210A02402003200C4E0D00200C20126A20106B220641016B21112003210B2006410771220604400340200B41016A210B2005200A6C210A200520096C2109200641016B22060D000B0B20114107490D0003402005200A6C20056C20056C20056C20056C20056C20056C20056C210A200520096C20056C20056C20056C20056C20056C20056C20056C2109200B41086A220B200C470D000B0B02402003200C4C04402009200A4E0D012013280200210B200A2009417F736A2106200A20096B410371220504400340200B200920156A4102746A2008360200200941016A2109200541016B22050D000B0B200641024D0D010340200B200920156A4102746A220520083602002005200836020C2005200836020820052008360204200941046A2209200A470D000B0C010B200D2013280200220A200920156A41027422096A220B2802002205360204024020054100480D00200B41002007280244200A6B41027522056B3602002005200E6A220B20054B04402013200E200D41046A10232007280218210F0C010B2005200B4D0D002007200A200B4102746A3602440B200728023C20126A21124100200728024020096A2802006B21150C010B0B200841016A2208200728021C200F6B410275490D000B0B200D41106A2400027F200441046A21154100210A410021034100210F4100210D200441086A2213280210201328020C6B41024604402015410036020041000C010B2001410174417F200141004E1B105F210E201541003602000240200141004C0D002013280210201328020C6B4101752107201328022421112013280240210B0340410021100240201328023C22092001200F6B2208200820094A1B220C41004C0D00201328024C21144100211220094100200941004A1B2106034020062012460D0120142002200F20126A6A2D00006A2D0000200720106C6A2110201241016A2212200C470D000B0B0240200820094E0D002009200C417F736A21062009200C6B410771221204400340200C41016A210C200720106C2110201241016B22120D000B0B20064107490D000340200720106C20076C20076C20076C20076C20076C20076C20076C2110200C41086A220C2009470D000B0B027F200B2010200A6B4102746A280200220A41004E04402015200341016A22103602002003200E6A200A3A00002011200A4102746A280200200D6B2109201021034100210A41000C010B2009200D6A0B210D2009200F6A220F2001480D000B200A41004E0D002013280240210703402007200A4102746B280200220A4100480D000B2015200341016A3602002003200E6A200A3A00000B200E0B2114200428021821102004280214210B02400240024020002802082206200028020422026B2208417F4704402006200028020C220D470440200641003A00002000200641016A360208201628020021020C020B200841016A22114100480D0241002106200D20026B220D41017422032011200320084B1B41FFFFFFFF07200D41FFFFFFFF03491B220D0440200D105F21060B200620086A221141003A00002006200D6A210D201141016A2111200841004A044020062002200810561A0B2000200D36020C2000201136020820002006360204200204402002105E0B201628020021020C010B200020023602080B200220086A2010200B6B4101763A000002402000280208200028020422086B220220022004280218200428021422106B22066A220B490440201620061018201628020021080C010B2002200B4D0D0020002008200B6A3602080B200220086A2010200610561A20002001101920002004280204101902402000280208200028020422026B22012001200428020422086A2206490440201620081018201628020021020C010B200120064D0D002000200220066A3602080B200120026A2014200810561A201404402014105E0B20042802041A20042802141A20042802181A200428025422000440200420003602582000105E0B2004280248220004402004200036024C2000105E0B2004280238220004402004200036023C2000105E0B200428022C22000440200420003602302000105E0B200428022022000440200420003602242000105E0B200428021422000440200420003602182000105E0B200441E0006A24000C010B1057000B0B0F0B1057000BC60101067F200120002802082203200028020422026B4D044020002001047F200241002001105920016A0520020B3602040F0B2002200028020022046B220520016A220641004E0440410021022006200320046B22034101742207200620074B1B41FFFFFFFF07200341FFFFFFFF03491B220304402003105F21020B200220036A2103200220056A41002001105920016A2101200541004A044020022004200510561A0B200020033602082000200136020420002002360200200404402004105E0B0F0B1057000BF70101067F0240024020002802082202200028020422036B2205417B4D0440200028020C220420026B41044F0440200241003600002000200241046A360208200028020421030C020B200541046A22064100480D02410021022006200420036B22044101742207200620074B1B41FFFFFFFF07200441FFFFFFFF03491B220404402004105F21020B200220056A22064100360000200220046A2104200641046A2106200541004A044020022003200510561A0B2000200436020C2000200636020820002002360204200304402003105E0B200028020421030C010B2000200320056A41046A3602080B200320056A20013602000F0B1057000B8A0701097F02400240024020002802000E020001020B2000280208220328000021022000200341046A220336020802402001280204200128020022046B220620024904402001200220066B101820012802002104200028020821030C010B200220064F0D002001200220046A3602040B2000200220036A36020820042003200210561A0F0B230041E0006B220224002002420037021C200242003702242002420037022C200242003702342002420037023C200242003703502002420037035820024200370214200242FF8180808001370308200242003703482002410236024420002000280208220341016A22043602082000200420032D0000220341017422066A3602082003047F02402003200241146A22082802082207200828020422056B4101754D04402008200520034101746A3602040C010B02402005200828020022096B220A41017520036A22054100480D002005200720096B220320032005491B41FFFFFFFF07200341017541FFFFFFFF03491B2207047F20074100480D012007410174105F0541000B2103200320074101746A2107200320054101746A2105200A41004A044020032009200A10561A0B200820073602082008200536020420082003360200200904402009105E0B0C010B1057000B20022802140541000B2004200610561A200241086A10212000280208220328000021042000200341046A220336020802402001280204200128020022056B220620044904402001200420066B1018200028020821030C010B200420064F0D002001200420056A3602040B2000200341046A2206200328000022036A36020820040440024020012802002100200241086A2201280210200128020C22056B4102470440200020046A2107200341014A0440200320066A41016B2105034020062D0000410274220320012802246A280200210420002001280230200128021820036A2802006A2004105620046A2100200641016A22062005490D000B0B20002001280230200128021820062D00004102746A2802006A200720006B10561A0C010B200020052D0000200410591A0B0B200228025422000440200220003602582000105E0B2002280248220004402002200036024C2000105E0B2002280238220004402002200036023C2000105E0B200228022C22000440200220003602302000105E0B200228022022000440200220003602242000105E0B200228021422000440200220003602182000105E0B200241E0006A24000B0B440020002001360208417F2001410274200141FFFFFFFF03712001471B105F210120004280808080800437021020004100360200200020013602042000200136020C20000B2500200042003702102000410036020820002001360200200020023602042000200236020C0B1B0002402000280208450D0020002802042200450D002000105E0B0BC50201067F027F20002802082204044020002802140C010B41800221042000418002360208418008105F210320004280808080800437021020004100360200200020033602042000200336020C41200B220320024C0440200020002802102003742001200220036B7672220836021002402004200028020022034A0440200028020421060C010B417F20044103742004410174220541FEFFFFFF03712005471B105F220620002802042207200441027410562104200704402007105E20002802084101742105200028020021030B20002005360208200020043602040B2000200341016A360200200620034102746A200836020020004100360210200028021421044120210320004120360214200220046B220241027441E0086A28020020017121010B200241004A04402000200320026B360214200020002802102002742001723602100B0B810101037F027F2001200028021422024A044020004120200120026B22036B22013602142000200028020C220241046A36020C200041106A2104200028021020037420022802002202200176720C010B2000200220016B2201360214200041106A2104200028021022022001760B210320042002417F200174417F737136020020030BDE0E01097F0340200141016B210A200141026B21090340024002400240024002400240200120006B220241017522030E06050500010203040B200141016B2D000020002D00014D0D0420002F000021022000200141026B22032F00003B0000200320023B00000F0B200141026B2102200141016B22082D0000210320002D0003220420002D00014D0440200341FF017120044D0D0420002F00022103200020022F00003B0002200220033B000020002D000320002D00014D0D04200020002800004110773600000F0B20002F000021052004200341FF0171490440200020022F00003B0000200220053B00000F0B20002F00022103200020053B0002200020033B000020082D000020054180FE03714108764D0D03200020022F00003B0002200220053B00000F0B2000200041026A200041046A200141026B10241A0F0B2000200041026A200041046A200041066A10241A200141016B2D000020002D00074D0D0120002F000621022000200141026B22032F00003B0006200320023B000020002D000720002D00054D0D0120002F00062102200020002F00043B0006200020023B00042002410876220320002D00034D0D01200020002F00023B0004200020023B000220002D000120034F0D01200020002F00003B0002200020023B00000C010B2002413D4C044020002D00052102024020002D0003220320002D000122044D0440200241FF017120034D0D0120002F00042102200020002F00023B0004200020023B0002200420024108764F0D01200020002F00003B0002200020023B00000C010B20002F000021042003200241FF0171220249044020002F00042102200020043B0004200020023B00000C010B20002F00022103200020043B0002200020033B0000200220044180FE03714108764D0D0020002F00042102200020043B0004200020023B00020B200041066A22022001460D01200041046A21030340200222082D000120032D00014B044020082F00002206410876210520082104034002402004200322022F00003B000020002002460440200021020C010B200241026B210320022104200241016B2D00002005490D010B0B200220063B00000B20012008220341026A2202470D000B0C010B2000200341026D41017422046A21060240200241CF0F4E044020002000200341046D41017422026A22032006200220066A220210242107200A2D000020022D00014D0D0120022F00002105200220092F00003B0000200920053B000020022D0001200020046A22052D00014D0440200741016A21070C020B20062F00002108200620022F00003B0000200220083B000020052D000120032D00014D0440200741026A21070C020B20032F00002102200320062F00003B0000200620023B000020032D000120002D00014D0440200741036A21070C020B20002F00002102200020032F00003B0000200320023B0000200741046A21070C010B200A2D000021020240200020046A22052D0001220320002D00014D044041002107200241FF017120034D0D0220062F00002102200620092F00003B0000200920023B00004101210720052D000120002D00014D0D0220002F00002102200020062F00003B0000200620023B00000C010B20002F000021052003200241FF0171490440200020092F00003B0000200920053B0000410121070C020B200020062F00003B0000200620053B000041012107200A2D000020054180FE03714108764D0D01200620092F00003B0000200920053B00000B410221070B20092103027F0240024020002D00012208200020046A2D000122054B0440200921020C010B0340200341026B22022000460440200041026A21042008200A2D00004B0D0320042009460D05034020042D0001200849044020042F00002102200420092F00003B0000200920023B0000200441026A21040C050B2009200441026A2204470D000B0C050B200341016B21042002210320042D000020054D0D000B20002F00002103200020022F00003B0000200220033B0000200741016A21070B2002200041026A22044B0440037F20062D0001210803402004220341026A2104200841FF0171220520032D0001490D000B0340200241016B2108200241026B210220082D000020054D0D000B2002200349047F20030520032F00002105200320022F00003B0000200220053B00002002200620032006461B2106200741016A21070C010B0B21040B024020042006460D0020062D000120042D00014D0D0020042F00002102200420062F00003B0000200620023B0000200741016A21070B20074504402000200410252105200441026A2203200110250440200421012005450D060C040B410220050D021A0B200420006B200120046B480440200020041020200441026A21000C040B200441026A20011020200421010C040B200420092202460D01037F20002D0001210803402004220341026A2104200841FF0171220520032D00014D0D000B0340200241016B2108200241026B2102200520082D00004B0D000B200220034D047F41040520032F00002105200320022F00003B0000200220053B00000C010B0B0B21022003210020024104460D0120024102460D010B0B0B0BFF0F01197F02402000280210200028020C6B221641017522074102490D00027F0240024002404101200028020474220A4101742201044020014180808080044F0D01200A4103742204105F220B4100200410591A0B200041186A210E0240200028021C200028021822026B41027522042001490440200E200120046B10220C010B200120044F0D002000200220014102746A36021C0B200041246A210F02402000280228200028022422026B41027522042001490440200F200120046B10220C010B200120044F0D002000200220014102746A3602280B200041306A210602402000280234200028023022046B220141FF3F4D044020064180C00020016B10180C010B20014180C000460D00200020044180406B3602340B20074180808080044F0D0020164101742201105F4100200110592109200A41016B2218200741016B22146E2102200028020C22052D000341087421044102210120052D000122034108742213210C0340024020012108200C20136C411076220C20044D0D00200841016A2101200220084B0D010B0B2008410F4D044020160D024100210441000C040B200628020020052D00003A0000200841016B2106200741024F044020064103712103200841026B410349210D41012104410121050340200421012003220204400340200028023020016A200028020C2D00003A0000200141016A2101200241016B22020D000B0B200420066A2102200D4504400340200028023020016A200028020C2D00003A0000200120002802306A200028020C2D00003A0001200120002802306A200028020C2D00003A0002200120002802306A200028020C2D00003A0003200141046A22012002470D000B0B200028023020026A200028020C20054101746A2D00003A0000200420086A2104200541016A22052007470D000B2009200620076C221536020020074102490D032014410771210241012101200741026B41074F0440201441787121050340200920014102746A20013602002009200141016A22034102746A20033602002009200141026A22034102746A20033602002009200141036A22034102746A20033602002009200141046A22034102746A20033602002009200141056A22034102746A20033602002009200141066A22034102746A20033602002009200141076A22034102746A2003360200200141086A2101200541086B22050D000B0B2002450D030340200920014102746A2001360200200141016A2101200241016B22020D000B0C030B2009200620076C2215360200410121040C020B1057000B20074101200741014B1B21044100210103402009200141027422026A20013602002002200B6A200341FF0171410874360200200E28020020026A2001360200200F28020020026A4101360200200628020020016A200520014101746A2D00003A00002004200141016A22014604402007211020040C0305200028020C220520014101746A2D000121030C010B000B000B410021050340200541016A2106200741014B0440200C410874210D200520076C21112000280224211220002802182110200028020C2117410121010340200B200120116A41027422026A200D201720014101746A2D000122036C411076200341087420051B360200200220106A200120086C20056B360200200220126A2006360200200141016A22012007470D000B0B200C20136C411076201320051B210C200820062205470D000B200B201541027422016A200C360200200E28020020016A4100360200200F28020020016A2008360200200820146C41016A2110200720086C0B21050240200A20104D0D0020074101200741014B1B220C417E712115200C41017121190340410021014100210602402016450D00410021024100210320152108200741024F04400340200B20092002410172220D4102746A2802004102746A2802002211200B200920024102746A2802004102746A28020022122003200320124922121B2203200320114922111B2103200D2002200620121B20111B2106200241026A2102200841026B22080D000B0B2019450D0020022006200B200920024102746A2802004102746A28020020034B1B21060B200F2802002208200920064102746A221328020041027422026A280200220341016A21112002200B6A2802004108742112200E280200220620026A28020021170340200B200541027422026A20122001410174220D200028020C6A2D00016C411076360200200220066A2004360200200220086A20113602002000280230220220046A200220176A200310561A200320046A220420002802306A200028020C200D6A2D00003A0000200541016A2105200441016A210402402018200120106A47047F200141016A2201200C470D01200C0520010B20074604402013201328020020076A3602000B201020146A2210200A490D020C030B200F2802002108200E28020021060C000B000B000B200E280200210320050440200F28020021084100210441002101410021020340200120092004410020042007491B22044102746A2802004F04402003200241027422066A20032001410274220D6A280200360200200620086A2008200D6A280200360200200241016A21020B200441016A2104200141016A22012005470D000B200E28020021030B0240200028021C20036B4102752201200A490440200E200A20016B10220C010B2001200A4D0D0020002003200A4102746A36021C0B02402000280228200028022422046B4102752201200A490440200F200A20016B10220C010B2001200A4D0D0020002004200A4102746A3602280B2009105E200B450D00200B105E0B0BF80101067F200120002802082202200028020422036B4102754D044020002001047F2003410020014102742201105920016A0520030B3602040F0B02402003200028020022046B2206410275220520016A22074180808080044F0D00410021032005410274027F2007200220046B2202410175220520052007491B41FFFFFFFF03200241027541FFFFFFFF01491B2202044020024180808080044F0D022002410274105F21030B20030B6A410020014102742201105920016A2101200320024102746A2102200641004A044020032004200610561A0B200020023602082000200136020420002003360200200404402004105E0B0F0B1057000BAE0401087F200120002802082203200028020422046B4102754D044002402001450D002001410274210620042103200141027441046B220541027641016A41077122010440034020032002280200360200200341046A2103200141016B22010D000B0B200420066A21042005411C490D0003402003200228020036020020032002280200360204200320022802003602082003200228020036020C2003200228020036021020032002280200360214200320022802003602182003200228020036021C200341206A22032004470D000B0B200020043602040F0B02402004200028020022066B2208410275220720016A22044180808080044F0D002004200320066B22034101752205200420054B1B41FFFFFFFF03200341027541FFFFFFFF01491B2205047F20054180808080044F0D012005410274105F0541000B220420074102746A220721032001410274220941046B220A41027641016A4107712201044020072103034020032002280200360200200341046A2103200141016B22010D000B0B200720096A2101200A411C4F044003402003200228020036020020032002280200360204200320022802003602082003200228020036020C2003200228020036021020032002280200360214200320022802003602182003200228020036021C200341206A22032001470D000B0B200420054102746A2102200841004A044020042006200810561A0B200020023602082000200136020420002004360200200604402006105E0B0F0B1057000BF90201037F20022D00012104027F20012D0001220520002D00014D04404100200441FF017120054D0D011A20012F00002104200120022F00003B0000200220043B000020012D000120002D00014D04402004410876210441010C020B20002F00002104200020012F00003B0000200120043B000020022D0001210441020C010B20002F000021062005200441FF0171490440200020022F00003B0000200220063B000020064180FE0371410876210441010C010B200020012F00003B0000200120063B0000410120022D00012204200641087622054D0D001A200120022F00003B0000200220063B00002005210441020B210520032D0001200441FF01714B047F20022F00002104200220032F00003B0000200320043B000020022D000120012D00014D0440200541016A0F0B20012F00002104200120022F00003B0000200220043B000020012D000120002D00014D0440200541026A0F0B20002F00002102200020012F00003B0000200120023B0000200541036A0520050B0BBF0601077F41012106024002400240024002400240200120006B4101750E06050500010203040B200141016B2D000020002D00014D0D0420002F000021022000200141026B22062F00003B0000200620023B000041010F0B200141026B2102200141016B22012D0000210320002D0003220420002D00014D0440200341FF017120044D0D0420002F00022103200020022F00003B0002200220033B000020002D000320002D00014D0D042000200028000041107736000041010F0B20002F000021052004200341FF0171490440200020022F00003B0000200220053B000041010F0B20002F00022103200020053B0002200020033B000020012D000020054180FE03714108764D0D03200020022F00003B0002200220053B000041010F0B2000200041026A200041046A200141026B10241A41010F0B2000200041026A200041046A200041066A10241A200141016B2D000020002D00074D0D0120002F000621022000200141026B22032F00003B0006200320023B000020002D000720002D00054D0D0120002F00062102200020002F00043B0006200020023B00042002410876220320002D00034D0D01200020002F00023B0004200020023B000220002D000120034F0D01200020002F00003B0002200020023B000041010F0B20002D00052102024020002D0003220320002D000122044D0440200241FF017120034D0D0120002F00042102200020002F00023B0004200020023B0002200420024108764F0D01200020002F00003B0002200020023B00000C010B20002F000021042003200241FF0171220249044020002F00042102200020043B0004200020023B00000C010B20002F00022103200020043B0002200020033B0000200220044180FE03714108764D0D0020002F00042102200020043B0004200020023B00020B200041066A22022001460D00200041046A210602400340200222052D000120062D00014B044020052F00002208410876210420052103034002402003200622022F00003B000020002002460440200021020C010B200241026B210620022103200241016B2D00002004490D010B0B200220083B0000200741016A22074108460D020B20052106200541026A22022001470D000B41010F0B200541026A20014621060B20060BDC0D040A7F047D027C017E200041306A210B0240200141017422092000280234200028023022036B41027522064B0440200B200920066B10220C010B200620094D0D002000200320094102746A3602340B0240200041406B280200200028023C22036B410275220620094904402000413C6A200920066B10220C010B200620094D0D002000200320094102746A3602400B2000280230210902400240024002400240024002400240200028021441016B0E06000401040203040B2001450D0441002106034020022006410C6C6A220328020422052005411F7522046A200473200328020022042004411F7522076A2007736A200328020822072007411F7522036A2003736A2103027F20002A020C220D8B430000004F5D0440200DA80C010B4180808080780B2108200920064103746A027F20034504404100210A41000C010B200520086C20036D210A200420086C20036D2203200741004E0D001A200541004821074100027F2008B722112003B799A122129944000000000000E0416304402012AA0C010B4180808080780B22036B210820044100482104027F2011200AB799A122119944000000000000E0416304402011AA0C010B4180808080780B21052008200320071B210A410020056B200520041B0BAD200AAD42208684370200200641016A22062001470D000B0C030B2001450D034100210603402002200641066C6A22032E010222052005411F7522046A20047320032E010022042004411F7522076A2007736A20032E010422072007411F7522036A20037341FFFF03716A2103027F20002A020C220D8B430000004F5D0440200DA80C010B4180808080780B2108200920064103746A027F20034504404100210A41000C010B200520086C20036D210A200420086C20036D2203200741004E0D001A200541004821074100027F2008B722112003B799A122129944000000000000E0416304402012AA0C010B4180808080780B22036B210820044100482104027F2011200AB799A122119944000000000000E0416304402011AA0C010B4180808080780B21052008200320071B210A410020056B200520041B0BAD200AAD42208684370200200641016A22062001470D000B0C020B2001450D024100210603402002200641036C6A22032C000122052005411F7522046A20047320032C000022042004411F7522076A2007736A20032C000222072007411F7522036A20037341FF01716A2103027F20002A020C220D8B430000004F5D0440200DA80C010B4180808080780B2108200920064103746A027F20034504404100210A41000C010B200520086C20036D210A200420086C20036D2203200741004E0D001A200541004821074100027F2008B722112003B799A122129944000000000000E0416304402012AA0C010B4180808080780B22036B210820044100482104027F2011200AB799A122119944000000000000E0416304402011AA0C010B4180808080780B21052008200320071B210A410020056B200520041B0BAD200AAD42208684370200200641016A22062001470D000B0C010B2001450D0141002106034020022006410C6C6A22032A0200220F8B20032A020422108B9220032A0208220D8B92210E200D43000000005D2103027F20002A020C220D8B430000004F5D0440200DA80C010B4180808080780B21052010200E95210D200F200E95210E02402003450440200E210F0C010B430000803F200D8B93220D8C200D200F43000000005D1B210F430000803F200E8B93210D201043000000005D450D00200D8C210D0B027F200D2005B2220E94220D8B430000004F5D0440200DA80C010B4180808080780BAD4220862113200920064103746A2013027F200F200E94220D8B430000004F5D0440200DA80C010B4180808080780BAD84370200200641016A22062001470D000B0B200B28020022022802042105200228020021042001410249044020052107200421080C030B4101210A200141016B2202410171210C2001410246044020042108200521070C020B2002417E71210B200421082005210703402009200A4103746A220128020C220220012802042206200520052006481B2203200220034A1B210520012802082203200128020022012004200120044A1B2204200320044A1B210420022006200720062007481B220620022006481B210720032001200820012008481B2201200120034A1B2108200A41026A210A200B41026B220B0D000B0C010B200B2802002209280204220721052009280200220821040C010B200C450D002009200A4103746A220128020022092008200820094A1B210820012802042201200720012007481B21072009200420042009481B210420012005200120054A1B21050B20042008417F736AAC10162109200020052007417F736AAC101622012009200120094A1B41016A36021C0BEF0402057F067D230041106B2206240002402000280220450D00200641003A0008200642F0DECDCBC6AEDAB7EE00370300200641083A000B20032006410C6A20061028220728020022054504404120105F2205200641086A22082802003602182005200629030037021020084100360200200642003703002005410036021C2005200628020C360208200542003702002007200536020020032802002802002208047F2003200836020020072802000520050B21072003280204200710292003200328020841016A3602080B200528021C22050440200541F80941980A107121090B20062C000B41004804402006280200105E0B20042802082105200641003602082006420037030020012009280220200220052006102A20002802204102460440200120022005200041246A102B0B20002802302104027F20002A020C220A8B430000004F5D0440200AA80C010B4180808080780B210020062802002102200104402000B2210E41002100034020022000410C6C6A22052A0204220C20052A0200220B8B200C8B9220052A0208220F8B92220D95210A200B200D95210D0240200F43000000005D450440200D210B0C010B430000803F200A8B93220A8C200A200B43000000005D1B210B430000803F200D8B93210A200C43000000005D450D00200A8C210A0B200420004103746A22052005280200027F200B200E94220C8B430000004F5D0440200CA80C010B4180808080780B6B36020020052005280204027F200A200E94220A8B430000004F5D0440200AA80C010B4180808080780B6B360204200041016A22002001470D000B200628020021020B2002450D00200620023602042002105E0B200641106A24000B950201077F200041046A210402402000280204220004402002280200200220022D000B220341187441187541004822051B21082002280204200320051B21020340024002400240024002400240200028021420002D001B2203200341187441187541004822061B22032002200220034B22091B220504402008200041106A2207280200200720061B22072005105A220645044020022003490D020C030B200641004E0D020C010B200220034F0D020B200028020022030D042001200036020020000F0B200720082005105A22030D010B20090D010C050B200341004E0D040B200041046A210420002802042203450D03200421000B20002104200321000C000B000B2001200436020020040F0B2001200036020020040BA20401037F2001200020014622033A000C024020030D000340200128020822022D000C0D010240027F2002200228020822032802002204460440024020032802042204450D0020042D000C0D002004410C6A21040C030B024020012002280200460440200221040C010B200220022802042204280200220136020420042001047F2001200236020820022802080520030B3602082002280208220320032802002002474102746A20043602002004200236020020022004360208200428020821030B200441013A000C200341003A000C200320032802002202280204220436020020040440200420033602080B200220032802083602082003280208220420042802002003474102746A200236020020022003360204200341086A0C010B02402004450D0020042D000C0D002004410C6A21040C020B024020012002280200470440200221010C010B20022001280204220436020020012004047F2004200236020820022802080520030B3602082002280208220320032802002002474102746A20013602002001200236020420022001360208200128020821030B200141013A000C200341003A000C200320032802042202280200220436020420040440200420033602080B200220032802083602082003280208220420042802002003474102746A200236020020022003360200200341086A0B20023602000C020B200241013A000C2003200020034622023A000C200441013A0000200321012002450D000B0B0B8A0302077D097F230041106B220D240020042004280200220C360204200D4100360208200D42003703002000044020042000200D102C2004280200210C0B200241036C220441004A0440200320044102746A210F034020012003280208410C6C22106A2200280200211120012003280204410C6C22126A2202280200211320012003280200410C6C22046A220E28020021142004200C6A220420042A02002002280204B2200E280204B222069322072000280208B2200E280208B22205932209942002280208B220059322052000280204B2200693220A9493220692380200200420052011B22014B2220893220B942013B2200893220820099493220520042A02049238020420042008200A942007200B9493220720042A020892380208200C20126A2204200620042A0200923802002004200520042A0204923802042004200720042A020892380208200C20106A2204200620042A0200923802002004200520042A0204923802042004200720042A0208923802082003410C6A2203200F490D000B0B200D41106A24000B860201027F230041106B220524002003200328020022043602042005410036020C20000440200320002005410C6A1023200328020021040B200141036C220341004A0440200220034102746A21000340200420022802004102746A22032003280200200228020473360200200420022802004102746A22032003280200200228020873360200200420022802044102746A22032003280200200228020873360200200420022802044102746A22032003280200200228020073360200200420022802084102746A22032003280200200228020073360200200420022802084102746A220320032802002002280204733602002002410C6A22022000490D000B0B200541106A24000BE10401077F200120002802082203200028020422066B410C6D4D044002402001450D002001410C6C2104200621032001410C6C410C6B2205410C6E41016A41037122010440034020032002290200370200200320022802083602082003410C6A2103200141016B22010D000B0B200420066A210620054124490D000340200320022902003702002003200241086A2201280200360208200320012802003602142003200229020037020C2003200128020036022020032002290200370218200320022902003702242003200128020036022C200341306A22032006470D000B0B200020063602040F0B02402006200028020022056B410C6D220820016A220441D6AAD5AA014F0D002004200320056B410C6D22034101742205200420054B1B41D5AAD5AA01200341AAD5AAD500491B2207047F200741D6AAD5AA014F0D012007410C6C105F0541000B22092008410C6C6A220521032001410C6C2204410C6B2208410C6E41016A4103712201044020052103034020032002290200370200200320022802083602082003410C6A2103200141016B22010D000B0B200420056A2104200841244F04400340200320022902003702002003200241086A2201280200360208200320012802003602142003200229020037020C2003200128020036022020032002290200370218200320022902003702242003200128020036022C200341306A22032004470D000B0B20092007410C6C6A210720052006200028020022036B220241746D410C6C6A2101200241004A044020012003200210561A0B200020073602082000200436020420002001360200200304402003105E0B0F0B1057000BF10301097F024002400240200028022022040440200028023C21072001280204200128020022086B22020D010C020B200028023C220520002802302202200128020022062802004103746A2802003602002005200220062802004103746A28020436020441012103200128020420066B2209410475220841014B04400340200520034103746A22042002200620034104746A22012802004103746A280200200220012802044103746A2802006B3602002004200220012802004103746A280204200220012802044103746A2802046B360204200341016A22032008470D000B0B20094103752202200041406B28020020056B41027522034B04402000413C6A200220036B10220F0B200220034F0D022000200520024102746A3602400F0B200028023021062000280224210A200241047522024101200241014B1B2109410021020340200820024104746A22052802002101024020044102460440200A20014102746A280200450D010B200720034103746A2204200620014103746A2802003602002004200620052802004103746A280204360204200341016A21030B200241016A22022009460D01200028022021040C000B000B20034101742202200041406B28020020076B41027522034B04402000413C6A200220036B10220F0B200220034F0D002000200720024102746A3602400B0BC60201067F200028022021070240024020022802082203200228020422016B2204417F4704402003200228020C2205470440200341003A00002002200341016A360208200228020421010C020B200441016A22064100480D0241002103200520016B22054101742208200620042008491B41FFFFFFFF07200541FFFFFFFF03491B220504402005105F21030B200320046A220641003A0000200320056A2105200641016A2106200441004A044020032001200410561A0B2002200536020C2002200636020820022003360204200104402001105E0B200228020421010C010B200220013602080B200120046A20073A00002002200228020820022802046B3602102002200041406B280200200028023C22016B41027541017620014102102F200228021021012002200228020820022802046B22043602102000200420016B3602180F0B1057000B9A03010C7F230041206B220C2400200C41086A2001101B210702402001450D00200141004E04402001105F2208410020011059220E20016A210F034020022003200A6C4102746A220D28020022092105024002400240200941016A0E020002010B410121050C010B410221052009411F7520097322044102490D000340200541016A21052004410175220441014B0D000B0B4101210B200341014A04400340024002400240200D200B4102746A280200220441016A0E020002010B410121040C010B2004411F7520047321064102210420064102490D000340200441016A21042006410175220641014B0D000B0B20042005200420054A1B2105200B41016A220B2003470D000B0B200A200E6A20053A000002402005450D00200341004C0D00200720094101200541016B7422066A2005101E4101210420034101460D0003402007200D20044102746A28020020066A2005101E200441016A22042003470D000B0B2001200A41016A220A470D000B0C010B1057000B2000200710302000200F20086B20081017200804402008105E0B2007101D200C41206A24000BD60401087F2001280214220341204704402001280210200374210702402001280200220320012802082202480440200128020421050C010B417F20024103742002410174220441FEFFFFFF03712004471B105F220520012802042206200241027410562102200604402006105E20012802084101742104200128020021030B20012004360208200120023602040B2001200341016A360200200520034102746A20073602002001428080808080043702100B200041046A2106200128020021050240024020002802082202200028020422036B2204417B4D0440200028020C220720026B41044F0440200241003600002000200241046A2202360208200628020021030C020B200441046A22024100480D022002200720036B22074101742209200220094B1B41FFFFFFFF07200741FFFFFFFF03491B220204402002105F21080B200420086A22074100360000200220086A2109200741046A2102200441004A044020082003200410561A0B2000200936020C2000200236020820002008360204200304402003105E200028020821020B200628020021030C010B2000200320046A41046A22023602080B200320046A20053602000240200220036B220420044104200441037122056B410020051B22086A220549044020062008101820002802042103200028020821020C010B200420054D0D002000200320056A22023602080B200128020421040240200220036B22022002200128020041027422016A2205490440200620011018200628020021030C010B200220054D0D002000200320056A3602080B200220036A2004200110561A0F0B1057000BBF0101037F20022002280208220341016A360208200020032D00003602202000413C6A2103024020014101742204200041406B280200200028023C22016B41027522054B04402003200420056B1022200328020021010C010B200420054F0D002000200120044102746A3602400B20022001410210322102024020002802204102470D00200241017422022000280240200028023C22046B41027522014B04402003200220016B10220F0B200120024D0D002000200420024102746A3602400B0B8A0301087F230041306B220324002003420037032820034200370320200342003703182000280208220428000021062000200441046A2204360208200420002802046B410371220504402000200420056B41046A22043602080B200341186A20062004101C2000200028020820064102746A3602084100210420034100360210200342003703082000200341086A101A024020010440200328020C220820032802082200460D012002410274210A200241004C210903402001200220076C4102746A21040240200020076A22062D0000220545044020090D0120044100200A10591A0C010B20090D002004200341186A2005101F410120057441017522056B36020041012100200241014704400340200420004102746A200341186A20062D0000101F20056B360200200041016A22002002470D000B0B20032802082100200328020C21080B200741016A2207200820006B2204490D000B0C010B200328020C200328020822006B21040B200004402003200036020C2000105E0B200341186A101D200341306A240020040BF70101037F02402000280204450D0020002802200D002002280204200228020022056B2202410C6D21042002044041012102200441014D0D01200028023C21030340200320024103746A22002000280200200320052002410C6C6A22012802004103746A2802006A360200200041046A22002000280200200320012802004103746A2802046A360200200241016A22022004470D000B0C010B410221032001410174220441024D0D00200028023C210003402000200341027422016A22022002280200200241086B2802006A360200200020014104726A22012001280200200241046B2802006A360200200341026A22032004470D000B0B0BB91302077D0E7F230041106B220E2400024002402000280204450D002000280220450D00200E41003A0008200E42F0DECDCBC6AEDAB7EE00370300200E41083A000B2003200E410C6A200E10282210280200220F4504404120105F220F200E41086A2211280200360218200F200E29030037021020114100360200200E4200370300200F410036021C200F200E28020C360208200F42003702002010200F36020020032802002802002211047F20032011360200201028020005200F0B21102003280204201010292003200328020841016A3602080B200F28021C22030440200341F80941980A1071210D0B200E2C000B4100480440200E280200105E0B200E4100360208200E420037030020010440200141D6AAD5AA014F0D02200E2001410C6C2203105F220F360200200E2003200F6A2210360208200F41002003410C6B22032003410C706B410C6A10591A200E20103602040B200D280204210302402004280200220F0440200120032002200F200E102A0C010B2002210F20042802042115230041106B22172400200E220D200E28020022163602042017410036020820174200370300200122100440200D20102017102C200D28020021160B200F41036C220D41004A04402015200D4101746A21190340200320152F0104410C6C22146A2210280200210C200320152F0102410C6C22126A220F2802002113200320152F0100410C6C220D6A22182802002111200D20166A220D200D2A0200200F280204B22018280204B2220A93220B2010280208B22018280208B2220993220894200F280208B220099322092010280204B2200A9322069493220A92380200200D2009200CB22011B22205932207942013B22005932205200894932209200D2A020492380204200D2005200694200B20079493220B200D2A020892380208201220166A220D200A200D2A020092380200200D2009200D2A020492380204200D200B200D2A020892380208201420166A220D200A200D2A020092380200200D2009200D2A020492380204200D200B200D2A020892380208201541066A22152019490D000B0B201741106A24000B024020002802204102470D00200428020022030440200120022003200041246A102B0C010B20042802042103200020002802242204360228200E410036020C20010440200041246A22042001200E410C6A1023200428020021040B200241036C220141004C0D00200320014101746A21100340200420032F010022014102746A220F20032F01042202200F28020020032F0102220F73733602002004200F4102746A220D200D280200200120027373360200200420024102746A220220022802002001200F7373360200200341066A22032010490D000B0B024002400240200028021441036B0E0401020200020B200028020421014100210D410021100240200E280204200E28020022146B220C450D00200028023C2104200C410C6D220C4101200C41014B1B210303402001200D410C6C22126A210C201220146A211402400240200028022041014704402000280224200D4102746A280200450D010B20142A020022078B20142A020422088B9220142A020822058B922106200543000000005D2114027F20002A020C22058B430000004F5D04402005A80C010B4180808080780B2113200820069521052007200695210602402014450440200621070C010B430000803F20058B9322058C2005200743000000005D1B2107430000803F20068B932105200843000000005D450D0020058C21050B2013027F20052013B222069422058B430000004F5D04402005A80C010B4180808080780B200420104103746A22122802046A22142014411F75220F6A200F73220F027F200720069422058B430000004F5D04402005A80C010B4180808080780B20122802006A22122012411F7522116A20117322026A6B2211B22105201041016A2110200C2011410048047F4100201320026B22116B2011201441004C1B211441002013200F6B22136B2013201241004C1B0520120BB2220620052005942014B222072007942006200694929291220695380200200C2005200695380208200C20072006953802040C010B200C20142A02002205200520059420142A020422052005949220142A020822062006949291220795380200200C2006200795380208200C20052007953802040B200D41016A220D2003460D01200E28020021140C000B000B0C010B200028020421044100210D0240200E280204200E280200220C6B2213450D00200028023C21032013410C6D22134101201341014B1B2102410021130340200C2013410C6C6A210C0240024020002802204101470440200028022420134102746A280200450D010B200C2A020022078B200C2A020422088B92200C2A020822058B922106200543000000005D2111027F20002A020C22058B430000004F5D04402005A80C010B4180808080780B210C200820069521052007200695210602402011450440200621070C010B430000803F20058B9322058C2005200743000000005D1B2107430000803F20068B932105200843000000005D450D0020058C21050B200C027F2005200CB222069422058B430000004F5D04402005A80C010B4180808080780B2003200D4103746A22122802046A4110742211411075220F2011411F7522116A20117341FFFF03712211027F200720069422058B430000004F5D04402005A80C010B4180808080780B20122802006A411074221241107522102012411F7522126A20127341FFFF037122016A6B2212B22105027D201241004E0440200FB221062010B20C010B200C20016B2212410020126B200F41004A1BB22106200C20116B220C4100200C6B201041004A1BB20B21072004201341066C6A220C027F20052005200594200620069420072007949292912208954300FEFF469422058B430000004F5D04402005A80C010B4180808080780B3B0104200D41016A210D200C027F20062008954300FEFF469422058B430000004F5D04402005A80C010B4180808080780B3B010220072008954300FEFF469422058B430000004F5D0440200C2005A83B01000C020B200C41003B01000C010B200C2A02002205200594200C2A0204220620069492200C2A020822072007949291220843ACC527375D0440200C4100360200200C4100360204200C41808080FC033602080C010B2004201341066C6A220C027F4300FEFF46200895220820079422078B430000004F5D04402007A80C010B4180808080780B3B0104200C027F200820069422068B430000004F5D04402006A80C010B4180808080780B3B0102200C027F200820059422058B430000004F5D04402005A80C010B4180808080780B3B01000B201341016A22132002460D01200E280200210C0C000B000B0B200E2802002203450D00200E20033602042003105E0B200E41106A24000F0B1057000BFA0402077F047D02402000280204450D0020002802200D0002400240200028021441036B0E0400020201020B2001450D010340200028023C20064103746A2202280200411074220341107522042003411F7522036A20037341FFFF037122072002280204411074220241107522032002411F7522026A20027341FFFF037122086A2105027F20002A020C22098B430000004F5D04402009A80C010B4180808080780B220220056B2205B22109027D200541004E04402003B2210A2004B20C010B200220076B2205410020056B200341004A1BB2210A200220086B2202410020026B200441004A1BB20B210B2000280204200641066C6A2202027F20092009200994200A200A94200B200B94929291220C954300FEFF469422098B430000004F5D04402009A80C010B4180808080780B3B01042002027F200A200C954300FEFF469422098B430000004F5D04402009A80C010B4180808080780B3B01022002027F200B200C954300FEFF469422098B430000004F5D04402009A80C010B4180808080780B3B0100200641016A22062001470D000B0C010B2001450D000340200028023C20064103746A220328020422022002411F7522046A2004732207200328020022032003411F7522046A20047322086A2105027F20002A020C22098B430000004F5D04402009A80C010B4180808080780B220420056B2205B22109200541004804404100200420086B22056B2005200241004C1B21024100200420076B22046B2004200341004C1B21030B20002802042006410C6C6A2204200920092009942002B2220B200B942003B2220A200A94929291220C953802082004200B200C953802042004200A200C95380200200641016A22062001470D000B0B0B5001017F200041AC0A360200200028023C22010440200041406B20013602002001105E0B200028023022010440200020013602342001105E0B200028022422010440200020013602282001105E0B20000B5201017F200041AC0A360200200028023C22010440200041406B20013602002001105E0B200028023022010440200020013602342001105E0B200028022422010440200020013602282001105E0B2000105E0B040041020BE807020A7F017D230041106B22052400200041206A21070240200028020820016C22032000280224200028022022066B22044B04402007200320046B10180C010B200320044F0D002000200320066A3602240B02402000280230200028022C22066B220420034904402000412C6A200320046B10180C010B200320044F0D002000200320066A3602300B024002400240200028021441046B0E03000201020B2001450D0120072802002106200028020821070340200741004A04402007410171210B41002103200741014704402007417E71210803402005410C6A20036A200220036A2D0000200041386A220920034102746A2802006D3A0000200341017222042005410C6A6A200220046A2D0000200920044102746A2802006D3A0000200341026A2103200841026B22080D000B0B200B04402005410C6A20036A200220036A2D0000200020034102746A2802386D3A00000B20052D000F210C20052D000E210820052D000D210420052D000C21030B2005200441FF0171200C411874200320046B220941FF017141107472200820046B220B41FF0171410874727236020C41002103200741004A04400340200320066A2005410C6A20036A2D00003A0000200341016A220320002802082207480D000B0B200620076A2106200220076A21022004210320092108200B2104200A41016A220A2001470D000B0C010B200541FF013A000B2001450D00200028020821042000280220210741FF01210C0340200441004A04402004410171210C41002103200441014704402004417E7121060340200041386A2208200341027422096A280200210A200541086A20036A027F200220096A2A02004300007F4394220D8B430000004F5D0440200DA80C010B4180808080780B200A6D3A0000200820034101722209410274220A6A2802002108200541086A20096A027F2002200A6A2A02004300007F4394220D8B430000004F5D0440200DA80C010B4180808080780B20086D3A0000200341026A2103200641026B22060D000B0B200C04402000200341027422066A2802382108200541086A20036A027F200220066A2A02004300007F4394220D8B430000004F5D0440200DA80C010B4180808080780B20086D3A00000B20052D000B210C20052D000A210820052D0009210620052D000821030B2005200641FF0171200C411874200320066B220941FF017141107472200820066B220A41FF0171410874727236020841002103200441004A04400340200320076A200541086A20036A2D00003A0000200341016A220320002802082204480D000B0B200420076A2107200220044102746A21022006210320092108200A2106200B41016A220B2001470D000B0B2000410036021C200541106A24000B8605010C7F230041106B220524000240024020002802042204450D0002400240200028021441046B0E03000201020B2000280248210320002802082102200541FF013A000F200120026C220741004C0D01200420076A21092004200120036C6A210741FF01210A0340200920026B2109200720036B21072005200241004A047F2005410C6A2009200210561A20052D000F210A20052D000D210B20052D000C210620052D000E0520080B20066A220841FF0171200A4118742006200B6A220141FF017141107472200641FF0171410874727236020C41002102200341004A04400340200220076A2005410C6A20026A2D0000200020024102746A2D00386C3A0000200241016A220220002802482203480D000B200028020421040B200420094F0D02200028020821022006210B20082106200121080C000B000B200145044041002004200141027410561A0C010B20014180808080044F0D0120014102742202105F200420021056210A20002802482208417E71210C2008410171210D0340200A200B4102746A220220022D000341187420022D0000220320022D00016A41FF017141107472200341087472200320022D00026A41FF0171723600000240200841004C0D0041002102200C21072008410147044003402004200241027422036A220620062A0200200041386A220620036A280200B2944300007F43953802002004200341047222036A220920092A0200200320066A280200B2944300007F4395380200200241026A2102200741026B22070D000B0B200D450D002004200241027422026A220320032A0200200020026A280238B2944300007F43953802000B200420084102746A2104200B41016A220B2001470D000B200A105E0B200541106A24000F0B1057000B3801017F200041F40B360200200028022C22010440200020013602302001105E0B200028022022010440200020013602242001105E0B20000B3A01017F200041F40B360200200028022C22010440200020013602302001105E0B200028022022010440200020013602242001105E0B2000105E0B040041030B0300010BE003010A7F2000280208220341004A04400340200028022C20026A2000280220200128020028020020036C20026A6A2D00003A0000200241016A220220002802082203480D000B0B410121062001280204200128020022026B410475220441014B04400340200220064104746A220541046A2108024002402005280204220420052802082209460D0020002D0010410171450D00200341004C0D01200541086A210A2005410C6A210B410021020340200028022C200320066C20026A6A20002802202207200528020020036C20026A6A2D00002007200320046C20026A6A2D00002007200320096C20026A6A2D00006A6B2007200B28020020036C20026A6A2D00006A3A0000200241016A2202200028020822034E0D02200A2802002109200828020021040C000B000B41002102200341004C0D000340200028022C200320066C20026A6A20002802202207200528020020036C20026A6A2D00002007200320046C20026A6A2D00006B3A0000200241016A2202200028020822034E0D01200828020021040C000B000B200641016A22062001280204200128020022026B4104752204490D000B0B200320046C22032000280230200028022C22046B22024B04402000412C6A200320026B10180F0B200220034B04402000200320046A3602300B0BFE05010B7F200220022802082204200228020422056B360210024002402000280208220641004C0D000340200020034102746A28023821090240200420056B2206417F4704402004200228020C2208470440200441003A00002002200441016A360208200228020421050C020B200641016A22044100480D042006200820056B2208410174220A20042006200A491B41FFFFFFFF07200841FFFFFFFF03491B2208047F2008105F0541000B22046A220A41003A0000200420086A2108200A41016A210A200641004A044020042005200610561A0B2002200836020C2002200A36020820022004360204200504402005105E0B200228020421050C010B200220053602080B200520066A20093A0000200341016A2203200028020822064E0D0120022802042105200228020821040C000B000B200028022C210A41002104230041206B22082400200841086A20012209101B21050240200622010440200141D6AAD5AA014904402001410C6C2203105F41002003410C6B410C6E410C6C410C6A22031059220C20036A2106034002402009200C2004410C6C6A220D280204200D28020022076B22034B0440200D200920036B10180C010B200320094D0D00200D200720096A3602040B410021072009044003400240200A200120076C20046A6A2C00002203450440200D28020020076A41003A00000C010B20032003411F75220B6A200B73AD1016210B200D28020020076A200B41016A220B3A0000200541004101200B7441017520036A6B200320034100481B200B101E0B200741016A22072009470D000B0B200441016A22042001470D000B2002200510304100210303402002200C2003410C6C6A2207280204200728020022076B20071017200341016A22032001470D000B2006200C47044003402006410C6B220328020022070440200641086B20073602002007105E0B200321062003200C470D000B0B200C105E0C020B1057000B2002200510300B2005101D200841206A2400200228021021052002200228020820022802046B22043602102000200420056B3602180F0B1057000B890201077F02402000280208220541004C0D0020054103712107200228020821010240200541016B4103490440200121040C010B2005417C712109200041386A210603402002200141016A3602082006200341027422086A20012D00003602002002200141026A360208200620084104726A20012D00013602002002200141036A360208200620084108726A20012D00023602002002200141046A220436020820062008410C726A20012D0003360200200341046A210320042101200941046B22090D000B0B2007450D0003402002200441016A2201360208200020034102746A20042D0000360238200341016A210320012104200741016B22070D000B0B20022000280204200510420BE10201077F230041306B220324002003420037032820034200370320200342003703182000280208220428000021052000200441046A2204360208200420002802046B410371220604402000200420066B41046A22043602080B200341186A20052004101C2000200028020820054102746A36020820034100360210200342003703080240200241004C0D0003402000200341086A101A02402001450D0041002104200328020C220620032802082205460D0003400240200420056A22082D000022094504402001200220046C20076A6A41003A00000C010B2001200220046C20076A6A4100200341186A2009101F2205410120082D000041016B7422066A6B200520052006481B3A000020032802082105200328020C21060B200441016A2204200620056B490D000B0B200741016A22072002470D000B200328020C1A20032802082204450D002003200436020C2004105E0B200341186A101D200341306A24000BC90301077F024020002802042207450D0020002D0010410171044020022802042206200228020022056B410C6D4102490D012000280208210341012101034041002104200341004A044020052001410C6C6A220641086A2108200641046A210903402007200120036C20046A6A220520052D00002007200928020020036C20046A6A2D00002007200628020020036C20046A6A2D00006A2007200828020020036C20046A6A2D00006B6A3A0000200441016A220420002802082203480D000B20022802002105200228020421060B200141016A2201200620056B410C6D490D000B0C010B2002280204220620022802002205470440200620056B410C6D4102490D012000280208210341012101034041002104200341004A044020052001410C6C6A210603402007200120036C20046A6A220520052D00002007200628020020036C20046A6A2D00006A3A0000200441016A220420002802082203480D000B20022802002105200228020421060B200141016A2201200620056B410C6D490D000B0C010B20002802082203200120036C4F0D00200321040340200420076A220520052D00002007200420036B6A2D00006A3A0000200441016A22042000280208220320016C490D000B0B0B040041010B9E0C03077F017D017C200041206A21090240200028020820016C22042000280224200028022022036B22014B04402009200420016B10180C010B200120044D0D002000200320046A3602240B02402000280230200028022C22036B220120044904402000412C6A200420016B10180C010B200120044D0D002000200320046A3602300B024002400240024002400240200028021441016B0E0701050205030400050B2004450D042004410171210641002101200441014704402004417E7121030340200028022020016A027F200220014103746A2B030020002A020CBBA3220B44000000000000F04163200B44000000000000000066710440200BAB0C010B41000B3A00002001410172220520002802206A027F200220054103746A2B030020002A020CBBA3220B44000000000000F04163200B44000000000000000066710440200BAB0C010B41000B3A0000200141026A2101200341026B22030D000B0B2006450D04200028022020016A027F200220014103746A2B030020002A020CBBA3220B44000000000000F04163200B44000000000000000066710440200BAB0C010B41000B3A00000C040B2004450D032004410171210641002101200441014704402004417E7121030340200028022020016A027F200220014102746A280200B220002A020C95220A430000804F5D200A430000000060710440200AA90C010B41000B3A00002001410172220520002802206A027F200220054102746A280200B220002A020C95220A430000804F5D200A430000000060710440200AA90C010B41000B3A0000200141026A2101200341026B22030D000B0B2006450D03200028022020016A2103200220014102746A280200B220002A020C95220A430000804F5D200A4300000000607104402003200AA93A00000C040B200341003A00000C030B2004450D022004410171210641002101200441014704402004417E7121030340200028022020016A027F200220014101746A2E0100B220002A020C95220A430000804F5D200A430000000060710440200AA90C010B41000B3A00002001410172220520002802206A027F200220054101746A2E0100B220002A020C95220A430000804F5D200A430000000060710440200AA90C010B41000B3A0000200141026A2101200341026B22030D000B0B2006450D02200028022020016A2103200220014101746A2E0100B220002A020C95220A430000804F5D200A4300000000607104402003200AA93A00000C030B200341003A00000C020B2004450D012004410171210641002101200441014704402004417E7121030340200028022020016A027F200120026A2C0000B220002A020C95220A430000804F5D200A430000000060710440200AA90C010B41000B3A00002001410172220520002802206A027F200220056A2C0000B220002A020C95220A430000804F5D200A430000000060710440200AA90C010B41000B3A0000200141026A2101200341026B22030D000B0B2006450D01200028022020016A2103200120026A2C0000B220002A020C95220A430000804F5D200A4300000000607104402003200AA93A00000C020B200341003A00000C010B2004450D002004410171210641002101200441014704402004417E7121030340200028022020016A027F200220014102746A2A020020002A020C95220A430000804F5D200A430000000060710440200AA90C010B41000B3A00002001410172220520002802206A027F200220054102746A2A020020002A020C95220A430000804F5D200A430000000060710440200AA90C010B41000B3A0000200141026A2101200341026B22030D000B0B2006450D00200028022020016A2103200220014102746A2A020020002A020C95220A430000804F5D200A4300000000607104402003200AA93A00000C010B200341003A00000B2000410036021C2000280208220841004A044003402009280200220620076A2D000021010240200420074D0440200121030C010B20012103200720086A220520044F0D000340200520066A2D000022022003200220034B1B210320022001200120024A1B2101200520086A22052004490D000B0B20032001417F736AAC101621022000200028021C2201200241016A200120024A1B36021C200741016A220720002802082208480D000B0B0B9A0801157F2002200228020820022802046B36021020002802082108200028022C2116024020002D0010410271044020082109230041206B220E2400200E41086A20012203101B211002402003450D00200341004E0440200941016B2204417E712101200441017121072003105F2211410020031059220C20036A21172009410248210D0340410021060240024002402016200920146C6A22152D0000220B0E020201000B41022106200B21040340200641016A210620044104492105200441017621042005450D000B0C010B410221060B0240200D0D004101211220012108200941024704400340410021054100210A024002400240201220156A220F2D000022040E020201000B4102210A0340200A41016A210A20044104492113200441017621042013450D000B0C010B4102210A0B200A20062006200A491B2113024002400240200F2D000122040E020200010B410221050C010B410221050340200541016A2105200441034B210A20044101762104200A0D000B0B20052013200520134B1B2106201241026A2112200841026B22080D000B0B2007450D0041002105024002400240201220156A2D000022040E020200010B410221050C010B410221050340200541016A2105200441034B210A20044101762104200A0D000B0B20052006200520064B1B21060B200C20146A20063A000002402006450D00200941004C0D0020104101200641016B742205200B6A2006101E4101210420094101460D00034020102005200420156A2D00006A2006101E200441016A22042009470D000B0B2003201441016A2214470D000B0C010B1057000B2002201010302002201720116B20111017201104402011105E0B2010101D200E41206A24000C010B230041206B22092400200941086A2001220B101B2104024020080440200841D6AAD5AA014904402008410C6C2203105F41002003410C6B410C6E410C6C410C6A22031059220C20036A210103400240200B200C200F410C6C6A220D280204200D28020022076B22034B0440200D200B20036B10180C010B2003200B4D0D00200D2007200B6A3602040B41002103200B0440034002402016200320086C200F6A6A2D00002207450440200D28020020036A41003A00000C010B2007AD1016210E200D28020020036A200E41016A220E3A000020042007200E101E0B200341016A2203200B470D000B0B200F41016A220F2008470D000B2002200410304100210303402002200C2003410C6C6A2207280204200728020022076B20071017200341016A22032008470D000B2001200C47044003402001410C6B220328020022070440200141086B20073602002007105E0B200321012003200C470D000B0B200C105E0C020B1057000B2002200410300B2004101D200941206A24000B200228021021012002200228020820022802046B22083602102000200820016B3602180B960301077F200028020821012000280204210620002D00104102710440230041306B220024002000420037032820004200370320200042003703182002280208220328000021052002200341046A2203360208200320022802046B410371220404402002200320046B41046A22033602080B200041186A20052003101C2002200228020820054102746A36020820004100360210200042003703082002200041086A101A024020060440200028020C220820002802082202460D01200141004C210903402006200120076C6A21030240200220076A22052D0000220445044020090D0120034100200110591A0C010B20090D002003200041186A2004101F410120047441017622046B3A000041012102200141014704400340200220036A200041186A20052D0000101F20046B3A0000200241016A22022001470D000B0B20002802082102200028020C21080B200741016A2207200820026B490D000B0C010B200028020C200028020822026B1A0B200204402000200236020C2002105E0B200041186A101D200041306A24000F0B20022006200110420BC21202057F027D024020002802042203450D00200028020820016C21020240024002400240024002400240024020002802140E080703060200040105080B2002450D072003027F20002A020C20032C0000B29422088B430000004F5D04402008A80C010B4180808080780B3A00004101210320024101460D07200241016B22014101712104200241024704402001417E7121010340027F20002A020C200028020420036A22022C0000B29422088B430000004F5D04402008A80C010B4180808080780B2105200220053A0000027F20002A020C200320002802046A41016A22022C0000B29422088B430000004F5D04402008A80C010B4180808080780B2105200220053A0000200341026A2103200141026B22010D000B0B2004450D07027F20002A020C200028020420036A22002C0000B29422088B430000004F5D04402008A80C010B4180808080780B2103200020033A00000C070B2002450D062002410171210441002101200241014704402002417E7121020340200320014102746A20002A020C200120036A2D0000B3943802002003200141017222054102746A20002A020C200320056A2D0000B394380200200141026A2101200241026B22020D000B0B2004450D06200320014102746A20002A020C200120036A2D0000B3943802000F0B2002450D0520002A020C21082002410371210541002100200241016B41034F04402002417C7121020340027F20082003200041017422016A22042F0100B3942207430000804F5D20074300000000607104402007A90C010B41000B2106200420063B0100027F2008200320014102726A22042F0100B3942207430000804F5D20074300000000607104402007A90C010B41000B2106200420063B0100027F2008200320014104726A22042F0100B3942207430000804F5D20074300000000607104402007A90C010B41000B2106200420063B0100027F2008200320014106726A22012F0100B3942207430000804F5D20074300000000607104402007A90C010B41000B2104200120043B0100200041046A2100200241046B22020D000B0B2005450D050340027F2008200320004101746A22012F0100B3942207430000804F5D20074300000000607104402007A90C010B41000B2102200120023B0100200041016A2100200541016B22050D000B0C050B2002450D0420002A020C21082002410371210541002100200241016B41034F04402002417C7121020340027F20082003200041027422016A2204280200B3942207430000804F5D20074300000000607104402007A90C010B41000B210620042006360200027F2008200320014104726A2204280200B3942207430000804F5D20074300000000607104402007A90C010B41000B210620042006360200027F2008200320014108726A2204280200B3942207430000804F5D20074300000000607104402007A90C010B41000B210620042006360200027F200820032001410C726A2201280200B3942207430000804F5D20074300000000607104402007A90C010B41000B210420012004360200200041046A2100200241046B22020D000B0B2005450D040340027F2008200320004102746A2201280200B3942207430000804F5D20074300000000607104402007A90C010B41000B210220012002360200200041016A2100200541016B22050D000B0C040B2002450D032003027F20002A020C20032C0000B29422088B430000004F5D04402008A80C010B4180808080780B3A00004101210320024101460D03200241016B22014101712104200241024704402001417E7121010340027F20002A020C200028020420036A22022C0000B29422088B430000004F5D04402008A80C010B4180808080780B2105200220053A0000027F20002A020C200320002802046A41016A22022C0000B29422088B430000004F5D04402008A80C010B4180808080780B2105200220053A0000200341026A2103200141026B22010D000B0B2004450D0320002A020C200028020420036A22032C0000B29422088B430000004F5D044020032008A83A00000F0B200341003A00000F0B2002450D0220002A020C21082002410171210541002100200241014704402002417E7121010340200320004103746A2008200020036A2D0000B394BB3903002003200041017222024103746A2008200220036A2D0000B394BB390300200041026A2100200141026B22010D000B0B2005450D02200320004103746A2008200020036A2D0000B394BB3903000F0B2002450D0120002A020C21082002410371210541002100200241016B41034F04402002417C7121020340027F20082003200041017422016A22042F0100B3942207430000804F5D20074300000000607104402007A90C010B41000B2106200420063B0100027F2008200320014102726A22042F0100B3942207430000804F5D20074300000000607104402007A90C010B41000B2106200420063B0100027F2008200320014104726A22042F0100B3942207430000804F5D20074300000000607104402007A90C010B41000B2106200420063B0100027F2008200320014106726A22012F0100B3942207430000804F5D20074300000000607104402007A90C010B41000B2104200120043B0100200041046A2100200241046B22020D000B0B2005450D010340027F2008200320004101746A22012F0100B3942207430000804F5D20074300000000607104402007A90C010B41000B2102200120023B0100200041016A2100200541016B22050D000B0C010B2002450D0020002A020C21082002410371210541002100200241016B41034F04402002417C7121020340027F20082003200041027422016A2204280200B3942207430000804F5D20074300000000607104402007A90C010B41000B210620042006360200027F2008200320014104726A2204280200B3942207430000804F5D20074300000000607104402007A90C010B41000B210620042006360200027F2008200320014108726A2204280200B3942207430000804F5D20074300000000607104402007A90C010B41000B210620042006360200027F200820032001410C726A2201280200B3942207430000804F5D20074300000000607104402007A90C010B41000B210420012004360200200041046A2100200241046B22020D000B0B2005450D000340027F2008200320004102746A2201280200B3942207430000804F5D20074300000000607104402007A90C010B41000B210220012002360200200041016A2100200541016B22050D000B0B0B41002001044020002001280200104920002001280204104920012C00274100480440200128021C105E0B20012C001B41004804402001280210105E0B2001105E0B0B2F002001044020002001280200104A20002001280204104A20012C001B41004804402001280210105E0B2001105E0B0BDB0301057F230041106B2205240002402001105B220441704F0D00200041146A2108024002402004410B4F0440200441106A4170712207105F2106200520074180808080787236020820052006360200200520043602040C010B200520043A000B200521062004450D010B20062001200410561A0B200420066A41003A0000200041186A210620082005104C210720052C000B41004804402005280200105E0B200620074704402001105B220441704F0D01024002402004410B4F0440200441106A4170712207105F2106200520074180808080787236020820052006360200200520043602040C010B200520043A000B200521062004450D010B20062001200410561A0B200420066A41003A000020082005410C6A20051028220128020022044504404120105F2204200541086A22062802003602182004200529030037021020064100360200200542003703002004410036021C2004200528020C360208200442003702002001200436020020082802002802002206047F2008200636020020012802000520040B21012000280218200110292000411C6A2201200128020041016A3602000B200428021C210420052C000B41004804402005280200105E0B20042002360204200420033602140B200541106A24000F0B1057000B9D0201087F200041046A21050240024020002802042200450D002001280200200120012D000B220241187441187541004822041B21072001280204200220041B210220052103034002402002200028021420002D001B2201200141187441187541004822011B2204200220044922081B22060440200041106A2209280200200920011B20072006105A22010D010B417F2008200220044B1B21010B2003200020014100481B210320002001411D764104716A28020022000D000B20032005460D000240200328021420032D001B2200200041187441187541004822011B2200200220002002491B220404402007200341106A2206280200200620011B2004105A22010D010B200020024B0D010C020B200141004E0D010B200521030B20030BA90C010C7F230041106B220924002001280208220228000021032001200241046A360208200041206A2106024020002802242202200028022022076B410475220820034904400240200320086B220320062802082202200628020422046B4104754D044002402003450D002003410474210720042102200341047441106B220841047641016A410771220304400340200241086A2205420037020020022005360204200241106A2102200341016B22030D000B0B200420076A2104200841F000490D000340200241086A22034200370200200241186A2205420037020020022003360204200241286A2203420037020020022005360214200241386A2205420037020020022003360224200241C8006A2203420037020020022005360234200241D8006A2205420037020020022003360244200241E8006A2203420037020020022005360254200241F8006A22054200370200200220033602642002200536027420024180016A22022004470D000B0B200620043602040C010B0240024002402004200628020022076B410475220420036A22054180808080014F0D002005200220076B22024103752207200520074B1B41FFFFFFFF00200241047541FFFFFF3F491B2207047F20074180808080014F0D012007410474105F0541000B220820044104746A220521022003410474220A41106B220B41047641016A41077122030440200521020340200241086A2204420037020020022004360204200241106A2102200341016B22030D000B0B2005200A6A210C200B41F0004F04400340200241086A22034200370200200241186A2204420037020020022003360204200241286A2203420037020020022004360214200241386A2204420037020020022003360224200241C8006A2203420037020020022004360234200241D8006A2204420037020020022003360244200241E8006A2203420037020020022004360254200241F8006A22044200370200200220033602642002200436027420024180016A2202200C470D000B0B200820074104746A210D2006280204220420062802002208460D010340200541106B2203200441106B22022802003602002003200228020436020420032002280208220A36020820032002410C6A220B280200220736020C024020074504402003200541086B3602040C010B200A200541086B3602082002200441086B220536020420054100360200200B41003602000B200321052002210420022008470D000B2006200D360208200628020421022006200C360204200628020021082006200336020020022008460D0203402002410C6B200241086B2802001049200241106B22022008470D000B0C020B1057000B2006200D3602082006200C360204200620053602000B200804402008105E0B0B200028022421050C010B200320084F0440200221050C010B200720034104746A2205200247044003402002410C6B200241086B2802001049200241106B22022005470D000B0B200020053602240B200520062802002204470440200941086A210A03402004200128020822022800003602002001200241056A360208024020022D0004220B450D002001200241076A220320022F00056A22023602082001200241026A220820022F00006A3602082003105B2202416F4D0440200441046A2107410021060340024002402002410B4F0440200241106A417071220C105F21002009200C4180808080787236020820092000360200200920023602040C010B200920023A000B200921002002450D010B20002003200210561A0B200020026A41003A000020072009410C6A20091028220028020022024504404128105F2202200A28020036021820022009290300370210200A410036020020094200370300200241003602242002420037021C2002200928020C360208200242003702002000200236020020072802002802002203047F2007200336020020002802000520020B21002004280208200010292004200428020C41016A36020C0B2002411C6A2008106C20092C000B41004804402009280200105E0B200641016A2206200B460D0220012001280208220241026A220320022F00006A22023602082001200241026A220820022F00006A3602082003105B2202416F4D0D000B0B1057000B200441106A22042005470D000B0B200941106A24000B3801017F200041AC0C360200200028022C22010440200020013602302001105E0B200028022022010440200020013602242001105E0B20000B3A01017F200041AC0C360200200028022C22010440200020013602302001105E0B200028022022010440200020013602242001105E0B2000105E0BF30B03077F027D027C200041206A21080240200028020820016C22042000280224200028022022036B41027522014B04402008200420016B10220C010B200120044D0D002000200320044102746A3602240B02402000280230200028022C22036B410275220120044904402000412C6A200420016B10220C010B200120044D0D002000200320044102746A3602300B024002400240024002400240200028021441016B0E0701050205030400050B2004450D042000280220210320002A020CBB210C2004410171210641002101200441014704402004417E7121050340200320014102746A027F200220014103746A2B0300200CA3220D9944000000000000E041630440200DAA0C010B4180808080780B3602002003200141017222074102746A027F200220074103746A2B0300200CA3220D9944000000000000E041630440200DAA0C010B4180808080780B360200200141026A2101200541026B22050D000B0B2006450D04200320014102746A027F200220014103746A2B0300200CA3220C9944000000000000E041630440200CAA0C010B4180808080780B3602000C040B2004450D032000280220210520002A020C210A2004410171210641002101200441014704402004417E71210703402005200141027422036A027F200220036A280200B2200A95220B8B430000004F5D0440200BA80C010B4180808080780B3602002005200341047222036A027F200220036A280200B2200A95220B8B430000004F5D0440200BA80C010B4180808080780B360200200141026A2101200741026B22070D000B0B2006450D032005200141027422016A2103200120026A280200B2200A95220A8B430000004F5D04402003200AA83602000C040B20034180808080783602000C030B2004450D022000280220210320002A020C210A2004410171210641002101200441014704402004417E7121050340200320014102746A027F200220014101746A2E0100B2200A95220B8B430000004F5D0440200BA80C010B4180808080780B3602002003200141017222074102746A027F200220074101746A2E0100B2200A95220B8B430000004F5D0440200BA80C010B4180808080780B360200200141026A2101200541026B22050D000B0B2006450D02200320014102746A2103200220014101746A2E0100B2200A95220A8B430000004F5D04402003200AA83602000C030B20034180808080783602000C020B2004450D012000280220210320002A020C210A2004410171210641002101200441014704402004417E7121050340200320014102746A027F200120026A2C0000B2200A95220B8B430000004F5D0440200BA80C010B4180808080780B3602002003200141017222074102746A027F200220076A2C0000B2200A95220B8B430000004F5D0440200BA80C010B4180808080780B360200200141026A2101200541026B22050D000B0B2006450D01200320014102746A2103200120026A2C0000B2200A95220A8B430000004F5D04402003200AA83602000C020B20034180808080783602000C010B2004450D002000280220210520002A020C210A2004410171210641002101200441014704402004417E71210703402005200141027422036A027F200220036A2A0200200A95220B8B430000004F5D0440200BA80C010B4180808080780B3602002005200341047222036A027F200220036A2A0200200A95220B8B430000004F5D0440200BA80C010B4180808080780B360200200141026A2101200741026B22070D000B0B2006450D002005200141027422016A2103200120026A2A0200200A95220A8B430000004F5D04402003200AA83602000C010B20034180808080783602000B410021062000410036021C2000280208220741004A044003402008280200220920064102746A28020021010240200420064D0440200121030C010B20012103200620076A220520044F0D000340200920054102746A28020022022003200220034A1B210320022001200120024A1B2101200520076A22052004490D000B0B20032001417F736AAC101621022000200028021C2201200241016A200120024A1B36021C200641016A220620002802082207480D000B0B0B8204010C7F200028022C2104200128020021082000280208220341004A0440200028022021050340200420024102746A2005200828020020036C20026A4102746A280200360200200241016A220220002802082203480D000B0B41012107200128020420086B410475220941014B04400340200820074104746A220641046A210A02400240200628020422052006280208220B460D0020002D0010410171450D00200028022C2104200341004C0D01200641086A210C2006410C6A210D200028022021014100210203402004200320076C20026A4102746A2001200D28020020036C20026A4102746A2802002001200628020020036C20026A4102746A2802002001200320056C20026A4102746A28020020012003200B6C20026A4102746A2802006A6B6A360200200241016A2202200028020822034E0D02200C280200210B200A28020021050C000B000B200028022C2104200341004C0D00200028022021014100210203402004200320076C20026A4102746A2001200628020020036C20026A4102746A2802002001200320056C20026A4102746A2802006B360200200241016A2202200028020822034E0D01200A28020021050C000B000B200741016A22072009470D000B0B200320096C2203200028023020046B41027522024B04402000412C6A200320026B10220F0B200220034B04402000200420034102746A3602300B0BFA03010B7F2002200228020820022802046B36021020002802082105200028022C210C024020002D0010410271044020022001200C2005102F0C010B230041206B220D2400200D41086A20012206101B210A024020050440200541D6AAD5AA014904402005410C6C2203105F41002003410C6B410C6E410C6C410C6A22031059220820036A21010340024020062008200B410C6C6A2209280204200928020022046B22034B04402009200620036B10180C010B200320064D0D002009200420066A3602040B410021042006044003400240200C200420056C200B6A4102746A2802002203450440200928020020046A41003A00000C010B20032003411F7522076A200773AD10162107200928020020046A200741016A22073A0000200A4100200341012007744101756A6B200320034100481B2007101E0B200441016A22042006470D000B0B200B41016A220B2005470D000B2002200A1030410021030340200220082003410C6C6A2204280204200428020022046B20041017200341016A22032005470D000B2001200847044003402001410C6B220328020022040440200141086B20043602002004105E0B2003210120032008470D000B0B2008105E0C020B1057000B2002200A10300B200A101D200D41206A24000B200228021021012002200228020820022802046B22053602102000200520016B3602180B8A0301077F200028020821012000280204210720002D0010410271044020022007200110321A0F0B230041306B220324002003420037032820034200370320200342003703182002280208220428000021052002200441046A2204360208200420022802046B410371220604402002200420066B41046A22043602080B200341186A20052004101C2002200228020820054102746A36020820034100360210200342003703080240200141004C0D0003402002200341086A101A02402007450D0041002104200328020C220620032802082205460D0003400240200420056A22092D000022004504402007200120046C20086A4102746A41003602000C010B2007200120046C20086A4102746A4100200341186A2000101F2205410120092D000041016B7422066A6B200520052006481B36020020032802082105200328020C21060B200441016A2204200620056B490D000B0B200841016A22082001470D000B200328020C1A20032802082204450D002003200436020C2004105E0B200341186A101D200341306A24000BAA0301087F024020002802042204450D002002280204200228020022086B2202410C6D210620002D0010410171044020064102490D012000280208210341012101034041002102200341004A044020082001410C6C6A220741086A2109200741046A210A03402004200120036C20026A4102746A220520052802002004200A28020020036C20026A4102746A2802002004200728020020036C20026A4102746A2802006A2004200928020020036C20026A4102746A2802006B6A360200200241016A220220002802082203480D000B0B200141016A22012006470D000B0C010B2002044020064102490D012000280208210341012101034041002102200341004A044020082001410C6C6A210703402004200120036C20026A4102746A220520052802002004200728020020036C20026A4102746A2802006A360200200241016A220220002802082203480D000B0B200141016A22012006470D000B0C010B20002802082203200120036C4F0D00200321020340200420024102746A220520052802002004200220036B4102746A2802006A360200200241016A22022000280208220320016C490D000B0B0BC51202057F027D024020002802042203450D00200028020820016C21020240024002400240024002400240024020002802140E080703060200040105080B2002450D072003027F20002A020C20032C0000B29422088B430000004F5D04402008A80C010B4180808080780B3A00004101210320024101460D07200241016B22014101712104200241024704402001417E7121010340027F20002A020C200028020420036A22022C0000B29422088B430000004F5D04402008A80C010B4180808080780B2105200220053A0000027F20002A020C200320002802046A41016A22022C0000B29422088B430000004F5D04402008A80C010B4180808080780B2105200220053A0000200341026A2103200141026B22010D000B0B2004450D07027F20002A020C200028020420036A22002C0000B29422088B430000004F5D04402008A80C010B4180808080780B2103200020033A00000C070B2002450D062002410171210641002101200241014704402002417E71210203402003200141027422056A220420002A020C2004280200B294380200200320054104726A220520002A020C2005280200B294380200200141026A2101200241026B22020D000B0B2006450D06200320014102746A220320002A020C2003280200B2943802000F0B2002450D0520002A020C21082002410371210541002100200241016B41034F04402002417C7121020340027F20082003200041017422016A22042F0100B3942207430000804F5D20074300000000607104402007A90C010B41000B2106200420063B0100027F2008200320014102726A22042F0100B3942207430000804F5D20074300000000607104402007A90C010B41000B2106200420063B0100027F2008200320014104726A22042F0100B3942207430000804F5D20074300000000607104402007A90C010B41000B2106200420063B0100027F2008200320014106726A22012F0100B3942207430000804F5D20074300000000607104402007A90C010B41000B2104200120043B0100200041046A2100200241046B22020D000B0B2005450D050340027F2008200320004101746A22012F0100B3942207430000804F5D20074300000000607104402007A90C010B41000B2102200120023B0100200041016A2100200541016B22050D000B0C050B2002450D0420002A020C21082002410371210541002100200241016B41034F04402002417C7121020340027F20082003200041027422016A2204280200B3942207430000804F5D20074300000000607104402007A90C010B41000B210620042006360200027F2008200320014104726A2204280200B3942207430000804F5D20074300000000607104402007A90C010B41000B210620042006360200027F2008200320014108726A2204280200B3942207430000804F5D20074300000000607104402007A90C010B41000B210620042006360200027F200820032001410C726A2201280200B3942207430000804F5D20074300000000607104402007A90C010B41000B210420012004360200200041046A2100200241046B22020D000B0B2005450D040340027F2008200320004102746A2201280200B3942207430000804F5D20074300000000607104402007A90C010B41000B210220012002360200200041016A2100200541016B22050D000B0C040B2002450D032003027F20002A020C20032C0000B29422088B430000004F5D04402008A80C010B4180808080780B3A00004101210320024101460D03200241016B22014101712104200241024704402001417E7121010340027F20002A020C200028020420036A22022C0000B29422088B430000004F5D04402008A80C010B4180808080780B2105200220053A0000027F20002A020C200320002802046A41016A22022C0000B29422088B430000004F5D04402008A80C010B4180808080780B2105200220053A0000200341026A2103200141026B22010D000B0B2004450D0320002A020C200028020420036A22032C0000B29422088B430000004F5D044020032008A83A00000F0B200341003A00000F0B2002450D0220002A020C21082002410171210541002100200241014704402002417E7121010340200320004103746A2008200320004102746A280200B294BB3903002003200041017222024103746A2008200320024102746A280200B294BB390300200041026A2100200141026B22010D000B0B2005450D02200320004103746A2008200320004102746A280200B294BB3903000F0B2002450D0120002A020C21082002410371210541002100200241016B41034F04402002417C7121020340027F20082003200041017422016A22042F0100B3942207430000804F5D20074300000000607104402007A90C010B41000B2106200420063B0100027F2008200320014102726A22042F0100B3942207430000804F5D20074300000000607104402007A90C010B41000B2106200420063B0100027F2008200320014104726A22042F0100B3942207430000804F5D20074300000000607104402007A90C010B41000B2106200420063B0100027F2008200320014106726A22012F0100B3942207430000804F5D20074300000000607104402007A90C010B41000B2104200120043B0100200041046A2100200241046B22020D000B0B2005450D010340027F2008200320004101746A22012F0100B3942207430000804F5D20074300000000607104402007A90C010B41000B2102200120023B0100200041016A2100200541016B22050D000B0C010B2002450D0020002A020C21082002410371210541002100200241016B41034F04402002417C7121020340027F20082003200041027422016A2204280200B3942207430000804F5D20074300000000607104402007A90C010B41000B210620042006360200027F2008200320014104726A2204280200B3942207430000804F5D20074300000000607104402007A90C010B41000B210620042006360200027F2008200320014108726A2204280200B3942207430000804F5D20074300000000607104402007A90C010B41000B210620042006360200027F200820032001410C726A2201280200B3942207430000804F5D20074300000000607104402007A90C010B41000B210420012004360200200041046A2100200241046B22020D000B0B2005450D000340027F2008200320004102746A2201280200B3942207430000804F5D20074300000000607104402007A90C010B41000B210220012002360200200041016A2100200541016B22050D000B0B0BE40301037F200020026A210302400240024020002001734103714504402000410371450D01200241004C0D01200021020340200220012D00003A0000200141016A2101200241016A2202410371450D0320022003490D000B0C020B024020034104490D00200341046B22042000490D00200021020340200220012D00003A0000200220012D00013A0001200220012D00023A0002200220012D00033A0003200141046A2101200241046A220220044D0D000B0C030B200021020C020B200021020B02402003417C71220441C000490D002002200441406A22054B0D0003402002200128020036020020022001280204360204200220012802083602082002200128020C36020C2002200128021036021020022001280214360214200220012802183602182002200128021C36021C2002200128022036022020022001280224360224200220012802283602282002200128022C36022C2002200128023036023020022001280234360234200220012802383602382002200128023C36023C200141406B2101200241406B220220054D0D000B0B200220044F0D00034020022001280200360200200141046A2101200241046A22022004490D000B0B200220034904400340200220012D00003A0000200141016A2101200241016A22022003470D000B0B20000B070041011001000B050041D80E0BF20202027F017E02402002450D00200020013A0000200020026A220341016B20013A000020024103490D00200020013A0002200020013A0001200341036B20013A0000200341026B20013A000020024107490D00200020013A0003200341046B20013A000020024109490D002000410020006B41037122046A2203200141FF017141818284086C22013602002003200220046B417C7122046A220241046B200136020020044109490D002003200136020820032001360204200241086B20013602002002410C6B200136020020044119490D002003200136021820032001360214200320013602102003200136020C200241106B2001360200200241146B2001360200200241186B20013602002002411C6B20013602002004200341047141187222046B22024120490D002001AD4281808080107E2105200320046A2101034020012005370318200120053703102001200537030820012005370300200141206A2101200241206B2202411F4B0D000B0B20000B810101027F02400240200241044F044020002001724103710D01034020002802002001280200470D02200141046A2101200041046A2100200241046B220241034B0D000B0B2002450D010B034020002D0000220320012D00002204460440200141016A2101200041016A2100200241016B22020D010C020B0B200320046B0F0B41000B7F01037F20002101024020004103710440034020012D0000450D02200141016A22014103710D000B0B03402001220241046A210120022802002203417F73200341818284086B7141808182847871450D000B200341FF0171450440200220006B0F0B034020022D00012103200241016A2201210220030D000B0B200120006B0B6C01027F41D40E2802002201200041036A417C7122026A2100024020024100200020014D1B0D0020003F004110744B044020003F004110746B41FFFF036A4110764000417F46047F4100054100100041010B450D010B41D40E200036020020010F0B41D80E4130360200417F0BA72D010B7F230041106B220B240002400240024002400240024002400240024002400240200041F4014D044041DC0E280200220641102000410B6A4178712000410B491B2205410376220176220041037104402000417F7341017120016A22034103742202418C0F6A280200220141086A2100024020012802082205200241840F6A220246044041DC0E2006417E200377713602000C010B2005200236020C200220053602080B200120034103742203410372360204200120036A41046A220120012802004101723602000C0C0B200541E40E28020022084D0D01200004400240200020017441022001742200410020006B72712200410020006B7141016B22002000410C764110712200762201410576410871220320007220012003762200410276410471220172200020017622004101764102712201722000200176220041017641017122017220002001766A22034103742202418C0F6A28020022012802082200200241840F6A220246044041DC0E2006417E2003777122063602000C010B2000200236020C200220003602080B200141086A210020012005410372360204200120056A22022003410374220420056B2203410172360204200120046A2003360200200804402008410376220441037441840F6A210541F00E2802002101027F2006410120047422047145044041DC0E200420067236020020050C010B20052802080B2104200520013602082004200136020C2001200536020C200120043602080B41F00E200236020041E40E20033602000C0C0B41E00E2802002209450D012009410020096B7141016B22002000410C764110712200762201410576410871220320007220012003762200410276410471220172200020017622004101764102712201722000200176220041017641017122017220002001766A410274418C116A280200220228020441787120056B210120022103034002402003280210220045044020032802142200450D010B200028020441787120056B22032001200120034B22031B21012000200220031B2102200021030C010B0B2002280218210A2002200228020C22044704402002280208220041EC0E280200491A2000200436020C200420003602080C0B0B200241146A2203280200220045044020022802102200450D03200241106A21030B0340200321072000220441146A220328020022000D00200441106A2103200428021022000D000B200741003602000C0A0B417F2105200041BF7F4B0D002000410B6A2200417871210541E00E2802002208450D00027F41002005418002490D001A411F200541FFFFFF074B0D001A2000410876220020004180FE3F6A411076410871220074220120014180E01F6A411076410471220174220320034180800F6A411076410271220374410F7620002001722003726B22004101742005200041156A7641017172411C6A0B2107410020056B21010240024002402007410274418C116A2802002203450440410021000C010B4100210020054100411920074101766B2007411F461B74210203400240200328020441787120056B220620014F0D0020032104200622010D0041002101200321000C030B200020032802142206200620032002411D764104716A2802102203461B200020061B21002002410174210220030D000B0B20002004724504404100210441022007742200410020006B722008712200450D032000410020006B7141016B22002000410C764110712200762203410576410871220220007220032002762200410276410471220372200020037622004101764102712203722000200376220041017641017122037220002003766A410274418C116A28020021000B2000450D010B0340200028020441787120056B220620014921022006200120021B21012000200420021B210420002802102203047F20030520002802140B22000D000B0B2004450D00200141E40E28020020056B4F0D00200428021821072004200428020C22024704402004280208220041EC0E280200491A2000200236020C200220003602080C090B200441146A2203280200220045044020042802102200450D03200441106A21030B0340200321062000220241146A220328020022000D00200241106A2103200228021022000D000B200641003602000C080B200541E40E28020022004D044041F00E28020021010240200020056B220341104F044041E40E200336020041F00E200120056A220236020020022003410172360204200020016A2003360200200120054103723602040C010B41F00E410036020041E40E410036020020012000410372360204200020016A41046A220020002802004101723602000B200141086A21000C0A0B200541E80E280200220249044041E80E200220056B220136020041F40E41F40E280200220020056A22033602002003200141017236020420002005410372360204200041086A21000C0A0B410021002005412F6A2208027F41B412280200044041BC122802000C010B41C012427F37020041B8124280A0808080800437020041B412200B410C6A41707141D8AAD5AA057336020041C812410036020041981241003602004180200B22016A2206410020016B220771220420054D0D0941941228020022010440418C12280200220320046A220920034D0D0A20012009490D0A0B4198122D00004104710D040240024041F40E28020022010440419C12210003402001200028020022034F0440200320002802046A20014B0D030B200028020822000D000B0B4100105C2202417F460D052004210641B812280200220041016B22012002710440200420026B200120026A410020006B716A21060B200520064F0D05200641FEFFFFFF074B0D0541941228020022000440418C12280200220120066A220320014D0D0620002003490D060B2006105C22002002470D010C070B200620026B200771220641FEFFFFFF074B0D042006105C2202200028020020002802046A460D03200221000B02402000417F460D00200541306A20064D0D0041BC122802002201200820066B6A410020016B71220141FEFFFFFF074B0440200021020C070B2001105C417F470440200120066A2106200021020C070B410020066B105C1A0C040B200021022000417F470D050C030B410021040C070B410021020C050B2002417F470D020B4198124198122802004104723602000B200441FEFFFFFF074B0D012004105C21024100105C21002002417F460D012000417F460D01200020024D0D01200020026B2206200541286A4D0D010B418C12418C1228020020066A2200360200419012280200200049044041901220003602000B02400240024041F40E28020022010440419C1221000340200220002802002203200028020422046A460D02200028020822000D000B0C020B41EC0E28020022004100200020024D1B45044041EC0E20023602000B4100210041A0122006360200419C12200236020041FC0E417F36020041800F41B41228020036020041A8124100360200034020004103742201418C0F6A200141840F6A2203360200200141900F6A2003360200200041016A22004120470D000B41F40E2002417820026B4107714100200241086A4107711B22006A220136020041E80E200620006B41286B220036020020012000410172360204200220066A41246B412836020041F80E41C4122802003602000C020B20002D000C4108710D0020012003490D00200120024F0D002000200420066A36020441F40E2001417820016B4107714100200141086A4107711B22006A220336020041E80E41E80E28020020066A220220006B220036020020032000410172360204200120026A412836020441F80E41C4122802003602000C010B41EC0E28020020024B044041EC0E20023602000B200220066A2104419C122100024002400240024002400240034020042000280200470440200028020822000D010C020B0B20002D000C410871450D010B419C12210003402001200028020022034F0440200320002802046A220320014B0D030B200028020821000C000B000B200020023602002000200028020420066A3602042002417820026B4107714100200241086A4107711B6A220620054103723602042004417820046B4107714100200441086A4107711B6A2204200520066A22056B21032001200446044041F40E200536020041E80E41E80E28020020036A2200360200200520004101723602040C030B200441F00E28020046044041F00E200536020041E40E41E40E28020020036A220036020020052000410172360204200020056A20003602000C030B200428020422004103714101460440200041787121080240200041FF014D0440200428020822012000410376220741037441840F6A461A2001200428020C220046044041DC0E41DC0E280200417E200777713602000C020B2001200036020C200020013602080C010B2004280218210902402004200428020C220247044020042802082200200236020C200220003602080C010B0240200441146A220028020022010D00200441106A220028020022010D00410021020C010B0340200021072001220241146A220028020022010D00200241106A2100200228021022010D000B200741003602000B2009450D0002402004200428021C2201410274418C116A22002802004604402000200236020020020D0141E00E41E00E280200417E200177713602000C020B20094110411420092802102004461B6A20023602002002450D010B2002200936021820042802102200044020022000360210200020023602180B20042802142200450D0020022000360214200020023602180B200420086A2104200320086A21030B20042004280204417E7136020420052003410172360204200320056A2003360200200341FF014D04402003410376220141037441840F6A2100027F41DC0E2802002203410120017422017145044041DC0E200120037236020020000C010B20002802080B2101200020053602082001200536020C2005200036020C200520013602080C030B411F2100200341FFFFFF074D04402003410876220020004180FE3F6A411076410871220074220120014180E01F6A411076410471220174220220024180800F6A411076410271220274410F7620002001722002726B22004101742003200041156A7641017172411C6A21000B2005200036021C200542003702102000410274418C116A2101024041E00E2802002202410120007422047145044041E00E200220047236020020012005360200200520013602180C010B20034100411920004101766B2000411F461B742100200128020021020340200222012802044178712003460D032000411D76210220004101742100200120024104716A41106A220428020022020D000B20042005360200200520013602180B2005200536020C200520053602080C020B41F40E2002417820026B4107714100200241086A4107711B22006A220736020041E80E200620006B41286B220036020020072000410172360204200441246B412836020041F80E41C41228020036020020012003412720036B4107714100200341276B4107711B6A412F6B22002000200141106A491B2204411B360204200441A4122902003702102004419C1229020037020841A412200441086A36020041A0122006360200419C12200236020041A8124100360200200441186A2100034020004107360204200041086A2102200041046A210020022003490D000B20012004460D0320042004280204417E713602042001200420016B220641017236020420042006360200200641FF014D04402006410376220341037441840F6A2100027F41DC0E2802002202410120037422037145044041DC0E200220037236020020000C010B20002802080B2103200020013602082003200136020C2001200036020C200120033602080C040B411F210020014200370210200641FFFFFF074D04402006410876220020004180FE3F6A411076410871220074220320034180E01F6A411076410471220374220220024180800F6A411076410271220274410F7620002003722002726B22004101742006200041156A7641017172411C6A21000B2001200036021C2000410274418C116A2103024041E00E2802002202410120007422047145044041E00E200220047236020020032001360200200120033602180C010B20064100411920004101766B2000411F461B742100200328020021020340200222032802044178712006460D042000411D76210220004101742100200320024104716A41106A220428020022020D000B20042001360200200120033602180B2001200136020C200120013602080C030B20012802082200200536020C20012005360208200541003602182005200136020C200520003602080B200641086A21000C050B20032802082200200136020C20032001360208200141003602182001200336020C200120003602080B41E80E280200220020054D0D0041E80E200020056B220136020041F40E41F40E280200220020056A22033602002003200141017236020420002005410372360204200041086A21000C030B41D80E4130360200410021000C020B02402007450D000240200428021C2203410274418C116A220028020020044604402000200236020020020D0141E00E2008417E2003777122083602000C020B20074110411420072802102004461B6A20023602002002450D010B2002200736021820042802102200044020022000360210200020023602180B20042802142200450D0020022000360214200020023602180B02402001410F4D04402004200120056A2200410372360204200020046A41046A220020002802004101723602000C010B20042005410372360204200420056A22022001410172360204200120026A2001360200200141FF014D04402001410376220141037441840F6A2100027F41DC0E2802002203410120017422017145044041DC0E200120037236020020000C010B20002802080B2101200020023602082001200236020C2002200036020C200220013602080C010B411F2100200141FFFFFF074D04402001410876220020004180FE3F6A411076410871220074220320034180E01F6A411076410471220374220520054180800F6A411076410271220574410F7620002003722005726B22004101742001200041156A7641017172411C6A21000B2002200036021C200242003702102000410274418C116A2103024002402008410120007422057145044041E00E200520087236020020032002360200200220033602180C010B20014100411920004101766B2000411F461B742100200328020021050340200522032802044178712001460D022000411D76210520004101742100200320054104716A41106A220628020022050D000B20062002360200200220033602180B2002200236020C200220023602080C010B20032802082200200236020C20032002360208200241003602182002200336020C200220003602080B200441086A21000C010B0240200A450D000240200228021C2203410274418C116A220028020020024604402000200436020020040D0141E00E2009417E200377713602000C020B200A41104114200A2802102002461B6A20043602002004450D010B2004200A36021820022802102200044020042000360210200020043602180B20022802142200450D0020042000360214200020043602180B02402001410F4D04402002200120056A2200410372360204200020026A41046A220020002802004101723602000C010B20022005410372360204200220056A22032001410172360204200120036A2001360200200804402008410376220441037441840F6A210541F00E2802002100027F4101200474220420067145044041DC0E200420067236020020050C010B20052802080B2104200520003602082004200036020C2000200536020C200020043602080B41F00E200336020041E40E20013602000B200241086A21000B200B41106A240020000BA70C01077F02402000450D00200041086B2202200041046B280200220141787122006A2105024020014101710D002001410371450D012002200228020022016B220241EC0E280200490D01200020016A2100200241F00E280200470440200141FF014D0440200228020822042001410376220741037441840F6A461A2004200228020C220146044041DC0E41DC0E280200417E200777713602000C030B2004200136020C200120043602080C020B2002280218210602402002200228020C220347044020022802082201200336020C200320013602080C010B0240200241146A220128020022040D00200241106A220128020022040D00410021030C010B0340200121072004220341146A220128020022040D00200341106A2101200328021022040D000B200741003602000B2006450D0102402002200228021C2204410274418C116A22012802004604402001200336020020030D0141E00E41E00E280200417E200477713602000C030B20064110411420062802102002461B6A20033602002003450D020B2003200636021820022802102201044020032001360210200120033602180B20022802142201450D0120032001360214200120033602180C010B200528020422014103714103470D0041E40E200036020020052001417E7136020420022000410172360204200020026A20003602000F0B200220054F0D0020052802042201410171450D0002402001410271450440200541F40E28020046044041F40E200236020041E80E41E80E28020020006A220036020020022000410172360204200241F00E280200470D0341E40E410036020041F00E41003602000F0B200541F00E28020046044041F00E200236020041E40E41E40E28020020006A220036020020022000410172360204200020026A20003602000F0B200141787120006A21000240200141FF014D0440200528020822042001410376220741037441840F6A461A2004200528020C220146044041DC0E41DC0E280200417E200777713602000C020B2004200136020C200120043602080C010B2005280218210602402005200528020C22034704402005280208220141EC0E280200491A2001200336020C200320013602080C010B0240200541146A220128020022040D00200541106A220128020022040D00410021030C010B0340200121072004220341146A220128020022040D00200341106A2101200328021022040D000B200741003602000B2006450D0002402005200528021C2204410274418C116A22012802004604402001200336020020030D0141E00E41E00E280200417E200477713602000C020B20064110411420062802102005461B6A20033602002003450D010B2003200636021820052802102201044020032001360210200120033602180B20052802142201450D0020032001360214200120033602180B20022000410172360204200020026A2000360200200241F00E280200470D0141E40E20003602000F0B20052001417E7136020420022000410172360204200020026A20003602000B200041FF014D04402000410376220141037441840F6A2100027F41DC0E2802002204410120017422017145044041DC0E200120047236020020000C010B20002802080B2101200020023602082001200236020C2002200036020C200220013602080F0B411F210120024200370210200041FFFFFF074D04402000410876220120014180FE3F6A411076410871220174220420044180E01F6A411076410471220474220320034180800F6A411076410271220374410F7620012004722003726B22014101742000200141156A7641017172411C6A21010B2002200136021C2001410274418C116A210402400240024041E00E2802002203410120017422057145044041E00E200320057236020020042002360200200220043602180C010B20004100411920014101766B2001411F461B742101200428020021030340200322042802044178712000460D022001411D76210320014101742101200420034104716A41106A220528020022030D000B20052002360200200220043602180B2002200236020C200220023602080C010B20042802082200200236020C20042002360208200241003602182002200436020C200220003602080B41FC0E41FC0E28020041016B2202417F20021B3602000B0B3201017F2000410120001B2101024003402001105D22000D0141CC122802002200044020001109000C010B0B1057000B20000B05001057000B110020001063044020002802000F0B20000B0C00200020012D00003A00000B0A0020002D000B4107760B0900200020013602040B0900200020013A000B0B24002000410B4F047F200041106A4170712200200041016B22002000410B461B05410A0B0B0300010B10002002044020002001200210561A0B0B0900200020013602000B100020002001418080808078723602080B5101027F024002402002410A4D0440200021032000200210650C010B2002416F4B0D0120002002106641016A2204105F2203106920002004106A2000200210640B20032001200241016A10680F0B1060000BEA05010A7F2001105B2105230041106B220A240002402005200022041063047F200428020841FFFFFFFF077141016B05410A0B22004D04402004106121002005220204400240200122032000460D002003200020026A22016B410020024101746B4D044020002003200210561A0C010B20002003734103712108024002402000200349044020080440200021010C030B2000410371450440200021010C020B2000210103402002450D04200120032D00003A0000200341016A2103200241016B2102200141016A22014103710D000B0C010B024020080D002001410371044003402002450D052000200241016B22026A2201200220036A2D00003A000020014103710D000B0B200241034D0D0003402000200241046B22026A200220036A280200360200200241034B0D000B0B2002450D0203402000200241016B22026A200220036A2D00003A000020020D000B0C020B200241034D0D00034020012003280200360200200341046A2103200141046A2101200241046B220241034B0D000B0B2002450D000340200120032D00003A0000200141016A2101200341016A2103200241016B22020D000B0B0B200A41003A000F200020056A200A410F6A106202402004106304402004200510640C010B2004200510650B0C010B20002103200520006B2106027F20041063044020042802040C010B20042D000B0B2100230041106B22072400024020062003417F7341116B4D0440200410612108027F200341E7FFFFFF07490440200720034101743602082007200320066A36020C230041106B220624002007410C6A2209280200200741086A220B280200492102200641106A2400200B200920021B28020010660C010B416E0B41016A220B105F21062005044020062001200510680B410022090440200520066A200020086A200910680B2003410A4704402008105E0B2004200610692004200B106A2004200520096A22041064200741003A0007200420066A200741076A1062200741106A24000C010B1060000B0B200A41106A24000B040020000B06002000105E0B7401017F200245044020002802042001280204460F0B2000200146044041010F0B200128020422022D000021010240200028020422032D00002200450D0020002001470D00034020022D0001210120032D00012200450D01200241016A2102200341016A210320002001460D000B0B20002001460BA20101017F230041406A22032400027F4101200020014100106F0D001A41002001450D001A4100200141FC0C41AC0D10712201450D001A200341086A4104724100413410591A200341013602382003417F36021420032000360210200320013602082001200341086A20022802004101200128020028021C110600200328022022004101460440200220032802183602000B20004101460B2100200341406B240020000BA10201037F230041406A220324002000280200220541046B2802002104200541086B280200210520034100360214200320013602102003200036020C2003200236020841002101200341186A4100412710591A200020056A21000240200420024100106F0440200341013602382004200341086A200020004101410020042802002802141108002000410020032802204101461B21010C010B2004200341086A200041014100200428020028021811040002400240200328022C0E020001020B200328021C410020032802284101461B410020032802244101461B410020032802304101461B21010C010B2003280220410147044020032802300D0120032802244101470D0120032802284101470D010B200328021821010B200341406B240020010B5D01017F200028021022034504402000410136022420002002360218200020013602100F0B02402001200346044020002802184102470D01200020023602180F0B200041013A0036200041023602182000200028022441016A3602240B0B1800200020012802084100106F044020012002200310720B0B3100200020012802084100106F044020012002200310720F0B20002802082200200120022003200028020028021C1106000B9A0100200041013A0035024020002802042002470D00200041013A003402402000280210220245044020004101360224200020033602182000200136021020002802304101470D0220034101460D010C020B2001200246044020002802182202410246044020002003360218200321020B20002802304101470D0220024101460D010C020B2000200028022441016A3602240B200041013A00360B0B2000024020002802042001470D00200028021C4101460D002000200236021C0B0BF20100200020012802082004106F044020012002200310760F0B0240200020012802002004106F044002402002200128021047044020012802142002470D010B20034101470D02200141013602200F0B200120033602200240200128022C4104460D00200141003B01342000280208220020012002200241012004200028020028021411080020012D003504402001410336022C20012D0034450D010C030B2001410436022C0B200120023602142001200128022841016A36022820012802244101470D0120012802184102470D01200141013A00360F0B20002802082200200120022003200420002802002802181104000B0B910100200020012802082004106F044020012002200310760F0B0240200020012802002004106F450D0002402002200128021047044020012802142002470D010B20034101470D01200141013602200F0B20012002360214200120033602202001200128022841016A360228024020012802244101470D0020012802184102470D00200141013A00360B2001410436022C0B0B3700200020012802082005106F0440200120022003200410750F0B200028020822002001200220032004200520002802002802141108000B1A00200020012802082005106F0440200120022003200410750B0B040023000B0600200024000B1000230020006B4170712200240020000B0BDE0603004180080B9D04757600636F6C6F7200706F736974696F6E006E6F726D616C00616C6C6F6361746F723C543E3A3A616C6C6F636174652873697A655F74206E2920276E272065786365656473206D6178696D756D20737570706F727465642073697A6500000000000000000100000003000000070000000F0000001F0000003F0000007F000000FF000000FF010000FF030000FF070000FF0F0000FF1F0000FF3F0000FF7F0000FFFF0000FFFF0100FFFF0300FFFF0700FFFF0F00FFFF1F00FFFF3F00FFFF7F00FFFFFF00FFFFFF01FFFFFF03FFFFFF07FFFFFF0FFFFFFF1FFFFFFF3FFFFFFF7F4E3363727431355665727465784174747269627574654500C0060000E00400004E33637274313147656E6572696341747472496945450000E806000000050000F8040000000000006C0500000100000002000000030000000400000005000000060000000700000008000000090000000A0000000B0000004E3363727431304E6F726D616C41747472450000E806000058050000F804000000000000E00500000C0000000D0000000E0000000F000000100000001100000012000000130000001400000015000000160000004E3363727439436F6C6F724174747245004E33637274313147656E65726963417474724968454500E8060000BD050000F8040000E8060000AC050000D405000000000000D40500000C00000017000000180000001900000010000000110000001A0000001B00000014000000150000001C0041A80C0BAA02180500001D0000001E0000001F00000020000000100000002100000022000000230000002400000015000000250000004E31305F5F637878616269763131365F5F7368696D5F747970655F696E666F4500000000E8060000580600004C0700004E31305F5F637878616269763131375F5F636C6173735F747970655F696E666F45000000E8060000880600007C06000000000000AC0600002700000028000000290000002A0000002B0000002C0000002D0000002E0000000000000030070000270000002F000000290000002A0000002B0000003000000031000000320000004E31305F5F637878616269763132305F5F73695F636C6173735F747970655F696E666F4500000000E806000008070000AC060000537439747970655F696E666F00000000C00600003C070041D40E0B025069"; +/* var wasm_simd = ""; + var detector = new Uint8Array([0,97,115,109,1,0,0,0,1,4,1,96,0,0,3,3,2,0,0,5,3,1,0,1,12,1,0,10,22,2,12,0,65,0,65,0,65,0,252,10,0,0,11,7,0,65,0,253,4,26,11]); + + var wasm = wasm_base; + + if (WebAssembly.validate(detector)) { + wasm = wasm_simd; + console.log("Warning: corto is using experimental SIMD support"); + }*/ + var instance, heap; + + var env = { + emscripten_notify_memory_growth: function(index) { + heap = new Uint8Array(instance.exports.memory.buffer); + }, + proc_exit: function(rval) { return 52; }, //WASI_ENOSYS + }; + + function unhex(data) { + var bytes = new Uint8Array(data.length / 2); + for (var i = 0; i < data.length; i += 2) { + bytes[i / 2] = parseInt(data.substr(i, 2), 16); + } + return bytes.buffer; + } + + + var promise = + WebAssembly.instantiate(unhex(wasm_base), { env:env, wasi_unstable:env, "wasi_snapshot_preview1": { + proc_exit() { + return 0; + } + } }) + .then(function(result) { + instance = result.instance; + //instance.exports._start(); + env.emscripten_notify_memory_growth(0); + }); + + function pad() { + return; + var s = instance.exports.sbrk(0); + var t = s & 0x3; + if(t) + instance.exports.sbrk(4 -t); + } + + function decode(source, shortIndex = false, shortNormal = false, colorComponents = 4) { + if(!source.length) + source = new Uint8Array(source); + var len = source.length; + var exports = instance.exports; + var sbrk = exports.malloc; //yes I am a criminal. + var free = exports.free; + + +//copy source to heap. we could use directly source, but that is good only for the first em call. + + + //We could use the heap instead of malloc, don't know cost (not much probably), and can't debug properly. + //set initial heap position, to be restored at the end of the deconding. + var pos = sbrk(0); + + var sptr = sbrk(len); + heap.set(source, sptr); + + var decoder = exports.newDecoder(len, sptr); + var nvert = exports.nvert(decoder); + var nface = exports.nface(decoder); + + var geometry = { + nvert: nvert, + nface: nface, + } + + var ngroups = exports.ngroups(decoder); + if(ngroups > 0) { + pad(); + var gp = sbrk(4*ngroups); + exports.groups(decoder, gp); + geometry.groups = new Uint32Array( ngroups*4); + geometry.grous.set(gp); + free(gp); + } + + var hasNormal = exports.hasNormal(decoder); + var hasColor = exports.hasColor(decoder); + var hasUv = exports.hasUv(decoder); + + var iptr = 0, pptr = 0, nptr = 0, cptr = 0, uptr = 0; + if(nface) { + pad(); //memory align needed for int, short, floats arrays if using sbrk + if(shortIndex) { + iptr = sbrk(nface * 6); + exports.setIndex16(decoder, iptr); + } else { + iptr = sbrk(nface * 12); + exports.setIndex32(decoder, iptr); + } + } + + pptr = sbrk(nvert * 12); + exports.setPositions(decoder, pptr); + + if(hasUv) { + pad(); + uptr = sbrk(nvert * 8); + exports.setUvs(decoder, uptr); + } + + if(hasNormal) { + pad(); + if(shortNormal) { + nptr = sbrk(nvert * 6); + exports.setNormals16(decoder, nptr); + } else { + pptr = sbrk(nvert * 12); + exports.setNormals32(decoder, nptr); + } + } + + if(hasColor) { + pad(); + cptr = sbrk(nvert * colorComponents); + exports.setColors(decoder, cptr, colorComponents); + } + pad(); + exports.decode(decoder); + + + //typed arrays needs to be created in javascript space, not as views of heap (next call will overwrite them!) + //hence the double typed array. + if(nface) { + if(shortIndex) + geometry.index = new Uint16Array(new Uint16Array(heap.buffer, iptr, nface*3)); + else + geometry.index = new Uint32Array(new Uint32Array(heap.buffer, iptr, nface*3)); + } + + geometry.position = new Float32Array(new Float32Array(heap.buffer, pptr, nvert*3)); + + if(hasNormal) { + if(shortNormal) + geometry.normal = new Int16Array(new Int16Array(heap.buffer, nptr, nvert*3)); + else + geometry.normal = new Float32Array(new Float32Array(heap.buffer, nptr, nvert*3)); + } + + if(hasColor) + geometry.color = new Uint8Array(new Uint8Array(heap.buffer, cptr, nvert*colorComponents)); + + if(hasUv) + geometry.uv = new Float32Array(new Float32Array(heap.buffer, uptr, nvert*2)); + + exports.deleteDecoder(decoder); + + //restore heap position if using sbrk + //sbrk(pos - sbrk(0)); + free(sptr); + if(iptr) free(iptr); + if(pptr) free(pptr); + if(cptr) free(cptr); + if(nptr) free(nptr); + if(uptr) free(uptr); + + return geometry; + }; + + return { + ready: promise, + decode: decode + }; +})(); + +if (typeof exports === 'object' && typeof module === 'object') + module.exports = CortoDecoder; +else if (typeof define === 'function' && define['amd']) + define([], function() { + return CortoDecoder; + }); +else if (typeof exports === 'object') + exports["CortoDecoder"] = CortoDecoder; diff --git a/nexus3d/webpack.config.js b/nexus3d/webpack.config.js index a2ad944c..272f1f23 100644 --- a/nexus3d/webpack.config.js +++ b/nexus3d/webpack.config.js @@ -10,7 +10,7 @@ module.exports = { }, output: { - path: path.resolve(__dirname + '/dist'), + path: path.resolve(__dirname + 'dist'), filename: '[name].bundle.js', }, @@ -48,7 +48,7 @@ module.exports = { new webpack.HotModuleReplacementPlugin(), new HtmlWebpackPlugin({ title: 'Nexus3D', - template: __dirname + '/dist/index.html', + template: path.resolve(__dirname + '/src/index.html'), inject: 'body' }), ], diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9b035938..fb42c5b3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -66,7 +66,7 @@ target_include_directories(nexus PUBLIC $ ) -target_link_libraries(nexus PUBLIC vcglib corto Qt5::Widgets) +target_link_libraries(nexus PUBLIC vcglib corto Qt6::Widgets) if (WIN32) target_compile_definitions(nexus PUBLIC NOMINMAX) diff --git a/src/common/controller.cpp b/src/common/controller.cpp index 99ea0e2e..046e9171 100644 --- a/src/common/controller.cpp +++ b/src/common/controller.cpp @@ -29,7 +29,7 @@ Controller::Controller(): max_tokens(300), max_ram(512*1000*1000), max_gpu(256*1 //setMaxTokens(max_tokens); } -void Controller::setWidget(QGLWidget *widget) { +void Controller::setWidget(QOpenGLWidget *widget) { //TODO check destruction of widget #ifdef SHARED_CONTEXT gpu_cache.shared = widget; //new QGLWidget(NULL, widget); diff --git a/src/common/controller.h b/src/common/controller.h index 5da11eb1..3bdb2309 100644 --- a/src/common/controller.h +++ b/src/common/controller.h @@ -25,7 +25,7 @@ for more details. #include "gpu_cache.h" -class QGLWidget; +class QOpenGLWidget; namespace nx { @@ -50,7 +50,7 @@ class Controller: public vcg::Controller { ~Controller() { finish(); } - void setWidget(QGLWidget *widget); + void setWidget(QOpenGLWidget *widget); void setGpu(uint64_t size) { max_gpu = size; gpu_cache.setCapacity(size); } uint64_t maxGpu() const { return max_gpu; } diff --git a/src/common/nexus.cpp b/src/common/nexus.cpp index 9817b3e2..6e1eb1ca 100644 --- a/src/common/nexus.cpp +++ b/src/common/nexus.cpp @@ -22,7 +22,7 @@ for more details. #include "globalgl.h" #include "qtnexusfile.h" -#include +#include using namespace nx; @@ -129,7 +129,7 @@ void nx::Nexus::loadImageFromData(nx::TextureData& data, int t) QImage img; bool success = img.loadFromData((uchar*)data.memory, texture.getSize()); if(header.signature.isDeepzoom()) { - file->dropDZTex(t); + file->dropDZTex(data.memory); } else { file->unmap((uchar*)data.memory); } diff --git a/src/common/qcontroller.h b/src/common/qcontroller.h index d974895a..3160399a 100644 --- a/src/common/qcontroller.h +++ b/src/common/qcontroller.h @@ -5,11 +5,11 @@ #include #include "controller.h" -class QGLWidget; +class QOpenGLWidget; class QController: public QObject, public Controller { Q_OBJECT public: - void start(QGLWidget *shared = NULL); + void start(QOpenGLWidget *shared = NULL); signals: ///emitted at most every (1/fps)s if the cache is changed diff --git a/src/nxsbuild/objloader.cpp b/src/nxsbuild/objloader.cpp index fe43c2cd..09b9b6f9 100644 --- a/src/nxsbuild/objloader.cpp +++ b/src/nxsbuild/objloader.cpp @@ -19,6 +19,7 @@ for more details. #include #include #include +#include #include @@ -176,8 +177,8 @@ void ObjLoader::readMTL(QFile &file) { } if(str.startsWith("Map_Kd", Qt::CaseInsensitive)){ txtfname = str.mid(7).trimmed(); - txtfname = txtfname.remove(QRegExp("^(\")")); - txtfname = txtfname.remove(QRegExp("(\")$")); + txtfname = txtfname.remove(QRegularExpression("^(\")")); + txtfname = txtfname.remove(QRegularExpression("(\")$")); continue; } if(str.startsWith("Kd", Qt::CaseInsensitive)){ @@ -285,8 +286,8 @@ void ObjLoader::cacheVertices() { QFileInfo info = QFileInfo(fname); mtl = QString(buffer).mid(7).trimmed(); - mtl = mtl.remove(QRegExp("^(\")")); - mtl = mtl.remove(QRegExp("(\")$")); + mtl = mtl.remove(QRegularExpression("^(\")")); + mtl = mtl.remove(QRegularExpression("(\")$")); mtl = info.dir().filePath(mtl); } } @@ -363,8 +364,8 @@ quint32 ObjLoader::getTriangles(quint32 size, Triangle *faces) { QString str = QString(buffer).simplified(); - QRegExp rx("[ ]");// match a space - QStringList list = str.split(rx, QString::SkipEmptyParts); + QRegularExpression rx("[ ]");// match a space + QStringList list = str.split(rx, Qt::SkipEmptyParts); list.removeFirst(); //'f' if (list.last().startsWith('\n') || list.last().startsWith('\r')) list.removeLast(); diff --git a/src/nxsbuild/tsloader.cpp b/src/nxsbuild/tsloader.cpp index 276afa07..f02791f2 100644 --- a/src/nxsbuild/tsloader.cpp +++ b/src/nxsbuild/tsloader.cpp @@ -77,7 +77,7 @@ bool TsLoader::useColormapFor(const QString &_property, const QString &palette) colormap.maxValue = line.split(" ").back().toFloat(&ok); if(!ok) throw QString("Proble parsing line: %1").arg(line); } - if(line[0] == "}") { + if(line[0] == '}') { break; } } diff --git a/src/nxsview/CMakeLists.txt b/src/nxsview/CMakeLists.txt index 4500e2f7..fdc871f0 100644 --- a/src/nxsview/CMakeLists.txt +++ b/src/nxsview/CMakeLists.txt @@ -46,15 +46,15 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOUIC ON) -find_package(Qt5 COMPONENTS OpenGL REQUIRED) +find_package(Qt6 COMPONENTS OpenGL OpenGLWidgets REQUIRED) include(${CMAKE_CURRENT_SOURCE_DIR}/glew.cmake) add_executable(nxsview MACOSX_BUNDLE ${SOURCES} ${HEADERS}) if (WIN32) - target_link_libraries(nxsview PUBLIC nexus Qt5::OpenGL external-glew glu32 OpenGL32) + target_link_libraries(nxsview PUBLIC nexus Qt6::OpenGL Qt6::OpenGLWidgets external-glew glu32 OpenGL32) else() - target_link_libraries(nxsview PUBLIC nexus Qt5::OpenGL external-glew OpenGL::GLU OpenGL::GL) + target_link_libraries(nxsview PUBLIC nexus Qt6::OpenGL Qt6::OpenGLWidgets external-glew OpenGL::GLU OpenGL::GL) endif() target_compile_definitions(nxsview PRIVATE GL_COMPATIBILITY) diff --git a/src/nxsview/gl_nxsview.cpp b/src/nxsview/gl_nxsview.cpp index ca1e6934..12955c10 100644 --- a/src/nxsview/gl_nxsview.cpp +++ b/src/nxsview/gl_nxsview.cpp @@ -37,7 +37,7 @@ int current_texture = 0; void glV(Point3f &p) { glVertex3f(p[0], p[1], p[2]); } GLNxsview::GLNxsview(QWidget *parent, bool _share): - QGLWidget(parent), share(_share), + QOpenGLWidget(parent), share(_share), default_trackball(true), extracting(true), current_viewpoint(-1), current_time(-1), playing(false), hasToPick(false), fov(60), info(false) { @@ -133,7 +133,7 @@ void GLNxsview::setColorBottom() { } void GLNxsview::getSnapshot() { - QImage im = grabFrameBuffer(); + QImage im = grabFramebuffer(); QString file = QFileDialog::getSaveFileName(this, tr("Select .png filename to save shapshot"), "", @@ -352,7 +352,7 @@ void GLNxsview::paintEvent(QPaintEvent * /*event*/) { if(extracting) renderer.endFrame(); - static QTime time; + static QElapsedTimer time; static bool first_time = true; if(first_time) time.start(); @@ -406,7 +406,7 @@ Trackball::Button QT2VCG(Qt::MouseButton qtbt, Qt::KeyboardModifiers modifiers) int vcgbt=Trackball::BUTTON_NONE; if(qtbt & Qt::LeftButton) vcgbt |= Trackball::BUTTON_LEFT; if(qtbt & Qt::RightButton) vcgbt |= Trackball::BUTTON_RIGHT; - if(qtbt & Qt::MidButton) vcgbt |= Trackball::BUTTON_MIDDLE; + if(qtbt & Qt::MiddleButton) vcgbt |= Trackball::BUTTON_MIDDLE; if(modifiers & Qt::ShiftModifier) vcgbt |= Trackball::KEY_SHIFT; if(modifiers & Qt::ControlModifier) vcgbt |= Trackball::KEY_CTRL; if(modifiers & Qt::AltModifier) vcgbt |= Trackball::KEY_ALT; @@ -414,7 +414,7 @@ Trackball::Button QT2VCG(Qt::MouseButton qtbt, Qt::KeyboardModifiers modifiers) } void GLNxsview::wheelEvent(QWheelEvent *e) { - if(e->delta() > 0) + if(e->angleDelta().y() > 0) trackball.MouseWheel(1); else trackball.MouseWheel(-1); diff --git a/src/nxsview/gl_nxsview.h b/src/nxsview/gl_nxsview.h index ba849540..4f70b8ee 100644 --- a/src/nxsview/gl_nxsview.h +++ b/src/nxsview/gl_nxsview.h @@ -20,9 +20,9 @@ for more details. #include #include -#include +#include #include -#include +#include #include #include @@ -32,7 +32,7 @@ for more details. #include "../common/renderer.h" #include "scene.h" -class GLNxsview : public QGLWidget { +class GLNxsview : public QOpenGLWidget { Q_OBJECT public: @@ -88,7 +88,7 @@ public slots: std::vector viewpoints; quint32 current_viewpoint; float current_time; //this will be modified by velocity - QTime time; + QElapsedTimer time; bool playing; void init(QString file); diff --git a/src/nxsview/main.cpp b/src/nxsview/main.cpp index 5fc48340..a827c10b 100644 --- a/src/nxsview/main.cpp +++ b/src/nxsview/main.cpp @@ -59,7 +59,9 @@ class QMyWindow: public QWidget { int main(int argc, char *argv[]) { +#if QT_VERSION < 0x060000 QApplication::setAttribute(Qt::AA_X11InitThreads); +#endif QApplication app(argc, argv); QMyWindow *window = new QMyWindow(NULL);