This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/source/blender/freestyle/intern/geometry/Grid.cpp
Tamito Kajiyama 4569f9ae4e Optimized view map calculation by Alexander Beels.
* View map calculation has been intensively optimized for speed by
means of:

1) new spatial grid data structures (SphericalGrid for perspective
cameras and BoxGrid for orthographic cameras; automatically switched
based on the camera type);

2) a heuristic grid density calculation algorithm; and

3) new line visibility computation algorithms: A "traditional"
algorithm for emulating old visibility algorithms, and a "cumulative"
algorithm for improved, more consistent line visibility, both exploiting
the new spatial grid data structures for fast ray casting.

A new option "Raycasting Algorithm" was added to allow users to choose
a ray casting (line visibility) algorithm.  Available choices are:

- Normal Ray Casting
- Fast Ray Casting
- Very Fast Ray Casting
- Culled Traditional Visibility Detection
- Unculled Traditional Visibility Detection
- Culled Cumulative Visibility Detection
- Unculled Cumulative Visibility Detection

The first three algorithms are those available in the original
Freestyle (the "normal" ray casting was used unconditionally, though).
The "fast" and "very fast" ray casting algorithms achieve a faster
calculation at the cost of less visibility accuracy.

The last four are newly introduced optimized options.  The culled
versions of the new algorithms will exclude from visibility
calculation those faces that lay outside the camera, which leads to a
faster view map construction.  The unculled counterparts will take all
faces into account.  The unculled visibility algorithms are useful
when culling affects stroke chaining.

The recommended options for users are the culled/unculled cumulative
visibility algorithms.  These options are meant to replace the old
algorithms in the future.

Performance improvements over the old algorithms depend on the scenes
to be rendered.

* Silhouette detection has also been considerably optimized for speed.

Performance gains by this optimization do not depend on scenes.

* Improper handling of error conditions in the view map construction
was fixed.
2011-03-14 00:36:27 +00:00

393 lines
11 KiB
C++
Executable File

//
// Copyright (C) : Please refer to the COPYRIGHT file distributed
// with this source distribution.
//
// 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 for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//
///////////////////////////////////////////////////////////////////////////////
#include "Grid.h"
#include "BBox.h"
#include <cassert>
#include <stdexcept>
// Grid Visitors
/////////////////
void allOccludersGridVisitor::examineOccluder(Polygon3r *occ){
occluders_.push_back(occ);
}
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_)){
//Vec3d bboxdiag(_scene3d->bbox().getMax()-_scene3d->bbox().getMin());
//if ((t>1.0E-06*(min(min(bboxdiag.x(),bboxdiag.y()),bboxdiag.z()))) && (t<raylength)){
if(tmp_t < t_){
occluder_ = occ;
u_ = tmp_u;
v_ = tmp_v;
t_ = tmp_t;
}
}else{
occ->userdata2 = 0;
}
}
}
}
bool firstIntersectionGridVisitor::stop(){
if(occluder_)
return true;
return false;
}
// Grid
/////////////////
void Grid::clear() {
if (_occluders.size() != 0) {
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<Vec3r> vertices = occluder->getVertices();
if (vertices.size() == 0)
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 cordinates 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<Vec3r>::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& current_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 coresponding
// 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 = 0;
Vec3r end = Vec3r(orig + FLT_MAX * dir / dir.norm());
bool inter = initInfiniteRay(orig, dir, timestamp);
if(!inter){
return 0;
}
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<Vec3r> box(boxMin, boxMax);
if(box.inside(orig)){
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];
}
}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)){
assert(tmin != -1.0);
Vec3r newOrig = orig + tmin*_ray_dir;
for(unsigned 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;
}