/* SPDX-License-Identifier: GPL-2.0-or-later */ /** \file * \ingroup freestyle * \brief Base class to define a cell grid surrounding the bounding box of the scene */ #include #include "BBox.h" #include "Grid.h" #include "BLI_utildefines.h" namespace Freestyle { // Grid Visitors ///////////////// void allOccludersGridVisitor::examineOccluder(Polygon3r *occ) { occluders_.push_back(occ); } static bool inBox(const Vec3r &inter, const Vec3r &box_min, const Vec3r &box_max) { if (((inter.x() >= box_min.x()) && (inter.x() < box_max.x())) && ((inter.y() >= box_min.y()) && (inter.y() < box_max.y())) && ((inter.z() >= box_min.z()) && (inter.z() < box_max.z()))) { return true; } return false; } void firstIntersectionGridVisitor::examineOccluder(Polygon3r *occ) { // check whether the edge and the polygon plane are coincident: //------------------------------------------------------------- // first let us compute the plane equation. Vec3r v1(((occ)->getVertices())[0]); Vec3d normal((occ)->getNormal()); // soc unused - double d = -(v1 * normal); double tmp_u, tmp_v, tmp_t; if ((occ)->rayIntersect(ray_org_, ray_dir_, tmp_t, tmp_u, tmp_v)) { if (fabs(ray_dir_ * normal) > 0.0001) { // Check whether the intersection is in the cell: if (inBox(ray_org_ + tmp_t * ray_dir_ / ray_dir_.norm(), current_cell_->getOrigin(), current_cell_->getOrigin() + cell_size_)) { #if 0 Vec3d bboxdiag(_scene3d->bbox().getMax() - _scene3d->bbox().getMin()); if ((t > 1.0e-06 * (min(min(bboxdiag.x(), bboxdiag.y()), bboxdiag.z()))) && (t < raylength)) { #else if (tmp_t < t_) { #endif occluder_ = occ; u_ = tmp_u; v_ = tmp_v; t_ = tmp_t; } } else { occ->userdata2 = nullptr; } } } } // namespace Freestyle bool firstIntersectionGridVisitor::stop() { if (occluder_) { return true; } return false; } // Grid ///////////////// void Grid::clear() { if (!_occluders.empty()) { for (OccludersSet::iterator it = _occluders.begin(); it != _occluders.end(); it++) { delete (*it); } _occluders.clear(); } _size = Vec3r(0, 0, 0); _cell_size = Vec3r(0, 0, 0); _orig = Vec3r(0, 0, 0); _cells_nb = Vec3u(0, 0, 0); //_ray_occluders.clear(); } void Grid::configure(const Vec3r &orig, const Vec3r &size, unsigned nb) { _orig = orig; Vec3r tmpSize = size; // Compute the volume of the desired grid real grid_vol = size[0] * size[1] * size[2]; if (grid_vol == 0) { double min = DBL_MAX; int index = 0; int nzeros = 0; for (int i = 0; i < 3; ++i) { if (size[i] == 0) { ++nzeros; index = i; } if ((size[i] != 0) && (min > size[i])) { min = size[i]; } } if (nzeros > 1) { throw std::runtime_error("Warning: the 3D grid has more than one null dimension"); } tmpSize[index] = min; _orig[index] = _orig[index] - min / 2; } // Compute the desired volume of a single cell real cell_vol = grid_vol / nb; // The edge of such a cubic cell is cubic root of cellVolume real edge = pow(cell_vol, 1.0 / 3.0); // We compute the number of cells par edge such as we cover at least the whole box. unsigned i; for (i = 0; i < 3; i++) { _cells_nb[i] = (unsigned)floor(tmpSize[i] / edge) + 1; } _size = tmpSize; for (i = 0; i < 3; i++) { _cell_size[i] = _size[i] / _cells_nb[i]; } } void Grid::insertOccluder(Polygon3r *occluder) { const vector vertices = occluder->getVertices(); if (vertices.empty()) { return; } // add this occluder to the grid's occluders list addOccluder(occluder); // find the bbox associated to this polygon Vec3r min, max; occluder->getBBox(min, max); // Retrieve the cell x, y, z coordinates associated with these min and max Vec3u imax, imin; getCellCoordinates(max, imax); getCellCoordinates(min, imin); // We are now going to fill in the cells overlapping with the polygon bbox. // If the polygon is a triangle (most of cases), we also check for each of these cells if it is // overlapping with the triangle in order to only fill in the ones really overlapping the // triangle. unsigned i, x, y, z; vector::const_iterator it; Vec3u coord; if (vertices.size() == 3) { // Triangle case Vec3r triverts[3]; i = 0; for (it = vertices.begin(); it != vertices.end(); it++) { triverts[i] = Vec3r(*it); i++; } Vec3r boxmin, boxmax; for (z = imin[2]; z <= imax[2]; z++) { for (y = imin[1]; y <= imax[1]; y++) { for (x = imin[0]; x <= imax[0]; x++) { coord[0] = x; coord[1] = y; coord[2] = z; // We retrieve the box coordinates of the current cell getCellBox(coord, boxmin, boxmax); // We check whether the triangle and the box ovewrlap: Vec3r boxcenter((boxmin + boxmax) / 2.0); Vec3r boxhalfsize(_cell_size / 2.0); if (GeomUtils::overlapTriangleBox(boxcenter, boxhalfsize, triverts)) { // We must then create the Cell and add it to the cells list if it does not exist yet. // We must then add the occluder to the occluders list of this cell. Cell *cell = getCell(coord); if (!cell) { cell = new Cell(boxmin); fillCell(coord, *cell); } cell->addOccluder(occluder); } } } } } else { // The polygon is not a triangle, we add all the cells overlapping the polygon bbox. for (z = imin[2]; z <= imax[2]; z++) { for (y = imin[1]; y <= imax[1]; y++) { for (x = imin[0]; x <= imax[0]; x++) { coord[0] = x; coord[1] = y; coord[2] = z; Cell *cell = getCell(coord); if (!cell) { Vec3r orig; getCellOrigin(coord, orig); cell = new Cell(orig); fillCell(coord, *cell); } cell->addOccluder(occluder); } } } } } bool Grid::nextRayCell(Vec3u ¤t_cell, Vec3u &next_cell) { next_cell = current_cell; real t_min, t; unsigned i; t_min = FLT_MAX; // init tmin with handle of the case where one or 2 _u[i] = 0. unsigned coord = 0; // predominant coord(0=x, 1=y, 2=z) // using a parametric equation of a line : B = A + t u, we find the tx, ty and tz respectively // corresponding to the intersections with the plans: // x = _cell_size[0], y = _cell_size[1], z = _cell_size[2] for (i = 0; i < 3; i++) { if (_ray_dir[i] == 0) { continue; } if (_ray_dir[i] > 0) { t = (_cell_size[i] - _pt[i]) / _ray_dir[i]; } else { t = -_pt[i] / _ray_dir[i]; } if (t < t_min) { t_min = t; coord = i; } } // We use the parametric line equation and the found t (tamx) to compute the B coordinates: Vec3r pt_tmp(_pt); _pt = pt_tmp + t_min * _ray_dir; // We express B coordinates in the next cell coordinates system. We just have to // set the coordinate coord of B to 0 of _CellSize[coord] depending on the sign of _u[coord] if (_ray_dir[coord] > 0) { next_cell[coord]++; _pt[coord] -= _cell_size[coord]; // if we are out of the grid, we must stop if (next_cell[coord] >= _cells_nb[coord]) { return false; } } else { int tmp = next_cell[coord] - 1; _pt[coord] = _cell_size[coord]; if (tmp < 0) { return false; } next_cell[coord]--; } _t += t_min; if (_t >= _t_end) { return false; } return true; } void Grid::castRay(const Vec3r &orig, const Vec3r &end, OccludersSet &occluders, unsigned timestamp) { initRay(orig, end, timestamp); allOccludersGridVisitor visitor(occluders); castRayInternal(visitor); } void Grid::castInfiniteRay(const Vec3r &orig, const Vec3r &dir, OccludersSet &occluders, unsigned timestamp) { Vec3r end = Vec3r(orig + FLT_MAX * dir / dir.norm()); bool inter = initInfiniteRay(orig, dir, timestamp); if (!inter) { return; } allOccludersGridVisitor visitor(occluders); castRayInternal(visitor); } Polygon3r *Grid::castRayToFindFirstIntersection( const Vec3r &orig, const Vec3r &dir, double &t, double &u, double &v, unsigned timestamp) { Polygon3r *occluder = nullptr; Vec3r end = Vec3r(orig + FLT_MAX * dir / dir.norm()); bool inter = initInfiniteRay(orig, dir, timestamp); if (!inter) { return nullptr; } firstIntersectionGridVisitor visitor(orig, dir, _cell_size); castRayInternal(visitor); // ARB: This doesn't work, because occluders are unordered within any cell // visitor.occluder() will be an occluder, but we have no guarantee it will be the *first* // occluder. I assume that is the reason this code is not actually used for FindOccludee. occluder = visitor.occluder(); t = visitor.t_; u = visitor.u_; v = visitor.v_; return occluder; } void Grid::initRay(const Vec3r &orig, const Vec3r &end, unsigned timestamp) { _ray_dir = end - orig; _t_end = _ray_dir.norm(); _t = 0; _ray_dir.normalize(); _timestamp = timestamp; for (unsigned i = 0; i < 3; i++) { _current_cell[i] = (unsigned)floor((orig[i] - _orig[i]) / _cell_size[i]); // soc unused - unsigned u = _current_cell[i]; _pt[i] = orig[i] - _orig[i] - _current_cell[i] * _cell_size[i]; } //_ray_occluders.clear(); } bool Grid::initInfiniteRay(const Vec3r &orig, const Vec3r &dir, unsigned timestamp) { _ray_dir = dir; _t_end = FLT_MAX; _t = 0; _ray_dir.normalize(); _timestamp = timestamp; // check whether the origin is in or out the box: Vec3r boxMin(_orig); Vec3r boxMax(_orig + _size); BBox box(boxMin, boxMax); if (box.inside(orig)) { for (unsigned int i = 0; i < 3; i++) { _current_cell[i] = (unsigned int)floor((orig[i] - _orig[i]) / _cell_size[i]); // soc unused - unsigned u = _current_cell[i]; _pt[i] = orig[i] - _orig[i] - _current_cell[i] * _cell_size[i]; } } else { // is the ray intersecting the box? real tmin(-1.0), tmax(-1.0); if (GeomUtils::intersectRayBBox(orig, _ray_dir, boxMin, boxMax, 0, _t_end, tmin, tmax)) { BLI_assert(tmin != -1.0); Vec3r newOrig = orig + tmin * _ray_dir; for (unsigned int i = 0; i < 3; i++) { _current_cell[i] = (unsigned)floor((newOrig[i] - _orig[i]) / _cell_size[i]); if (_current_cell[i] == _cells_nb[i]) { _current_cell[i] = _cells_nb[i] - 1; } // soc unused - unsigned u = _current_cell[i]; _pt[i] = newOrig[i] - _orig[i] - _current_cell[i] * _cell_size[i]; } } else { return false; } } //_ray_occluders.clear(); return true; } } /* namespace Freestyle */