Octree* Octree::Render(Tree* t, const Region& r, uint32_t flags, bool multithread) { auto rp = r.powerOfTwo().view(); if (multithread && rp.canOctsect()) { std::list<std::future<Octree*>> futures; // Start up a set of future rendering every branch of the octree for (auto region : rp.octsect()) { auto e = new Evaluator(t); futures.push_back(std::async(std::launch::async, [e, region, flags](){ auto out = new Octree(e, region, flags); delete e; return out;})); } // Wait for all of the tasks to finish running in the background std::array<Octree*, 8> sub; int index = 0; for (auto& f : futures) { f.wait(); sub[index++] = f.get(); } Evaluator e(t); return new Octree(&e, sub, rp, flags); } else { Evaluator e(t); return new Octree(&e, rp, flags); } }
_STATIC_ void triangulate_voxel(MathTree* tree, const Region r, float** const verts, unsigned* const count, unsigned* const allocated, Region packed, const float* data) { // If we can calculate all of the points in this region with a single // eval_r call, then do so. This large chunk will be used in future // recursive calls to make things more efficient. const unsigned voxels = (r.ni+1)*(r.nj+1)*(r.nk+1); if (!data && voxels < MIN_VOLUME) { packed = r; // Copy the X, Y, Z vectors into a flattened matrix form. packed.X = malloc(voxels*sizeof(float)); packed.Y = malloc(voxels*sizeof(float)); packed.Z = malloc(voxels*sizeof(float)); int q = 0; for (int k = 0; k <= r.nk; ++k) { for (int j = 0; j <= r.nj; ++j) { for (int i = 0; i <= r.ni; ++i) { packed.X[q] = r.X[i]; packed.Y[q] = r.Y[j]; packed.Z[q] = r.Z[k]; q++; } } } // Update the voxel count packed.voxels = voxels; data = eval_r(tree, packed); // Free the allocated matrices free(packed.X); free(packed.Y); free(packed.Z); } // If we have greater than one voxel, subdivide and recurse. if (r.voxels > 1) { Region octants[8]; const uint8_t split = octsect(r, octants); for (int i=0; i < 8; ++i) { if (split & (1 << i)) { triangulate_voxel(tree, octants[i], verts, count, allocated, packed, data); } } return; } // Find corner distance values for this voxel float d[8]; for (int i=0; i < 8; ++i) { // Figure out where this bit of data lives in the larger eval_r array. const unsigned index = (r.imin - packed.imin + ((i & 4) ? r.ni : 0)) + (r.jmin - packed.jmin + ((i & 2) ? r.nj : 0)) * (packed.ni+1) + (r.kmin - packed.kmin + ((i & 1) ? r.nk : 0)) * (packed.ni+1) * (packed.nj+1); d[i] = data[index]; } // Loop over the six tetrahedra that make up a voxel cell for (int t=0; t < 6; ++t) { // Find vertex positions for this tetrahedron uint8_t vertices[] = {0, 7, VERTEX_LOOP[t], VERTEX_LOOP[t+1]}; // Figure out which of the sixteen possible combinations // we're currently experiencing. uint8_t lookup = 0; for (int v=3; v>=0; --v) { lookup = (lookup << 1) + (d[vertices[v]] < 0); } // Iterate over (up to) two triangles in this tetrahedron for (int i=0; i < 2; ++i) { if (EDGE_MAP[lookup][i][0][0] == -1) break; // Do we need to allocate more space for incoming vertices? // If so, double the buffer size. if ((*count) + 9 >= (*allocated)) { *allocated *= 2; *verts = realloc(*verts, sizeof(float)*(*allocated)); } // ...and insert vertices into the mesh. for (int v=0; v < 3; ++v) { const Vec3f vertex = zero_crossing(d, vertices[EDGE_MAP[lookup][i][v][0]], vertices[EDGE_MAP[lookup][i][v][1]]); (*verts)[(*count)++] = vertex.x * (r.X[1] - r.X[0]) + r.X[0]; (*verts)[(*count)++] = vertex.y * (r.Y[1] - r.Y[0]) + r.Y[0]; (*verts)[(*count)++] = vertex.z * (r.Z[1] - r.Z[0]) + r.Z[0]; } } } }