lumosquad/QUAD_POISSON.cpp
2025-07-26 22:14:37 +02:00

620 lines
19 KiB
C++

///////////////////////////////////////////////////////////////////////////////////
// File : QUAD_POISSON.cpp
///////////////////////////////////////////////////////////////////////////////////
//
// LumosQuad - A Lightning Generator
// Copyright 2007
// The University of North Carolina at Chapel Hill
//
///////////////////////////////////////////////////////////////////////////////////
//
// 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
//
// The University of North Carolina at Chapel Hill makes no representations
// about the suitability of this software for any purpose. It is provided
// "as is" without express or implied warranty.
//
// Permission to use, copy, modify and distribute this software and its
// documentation for educational, research and non-profit purposes, without
// fee, and without a written agreement is hereby granted, provided that the
// above copyright notice and the following three paragraphs appear in all
// copies.
//
// THE UNIVERSITY OF NORTH CAROLINA SPECIFICALLY DISCLAIM ANY WARRANTIES,
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN
// "AS IS" BASIS, AND THE UNIVERSITY OF NORTH CAROLINA HAS NO OBLIGATION TO
// PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
//
// Please send questions and comments about LumosQuad to kim@cs.unc.edu.
//
///////////////////////////////////////////////////////////////////////////////////
//
// This program uses OpenEXR, which has the following restrictions:
//
// Copyright (c) 2002, Industrial Light & Magic, a division of Lucas
// Digital Ltd. LLC
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Industrial Light & Magic nor the names of
// its contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
#include "QUAD_POISSON.h"
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
QUAD_POISSON::QUAD_POISSON(int xRes, int yRes, int iterations) :
_root(new CELL(1.0f, 1.0f, 0.0f, 0.0f)),
_noise()
{
_root->refine();
// figure out the max depth needed
float xMax = log((float)xRes) / log(2.0f);
float yMax = log((float)yRes) / log(2.0f);
float max = (xMax > yMax) ? xMax : yMax;
if (max - floor(max) > 1e-7)
max = max + 1;
max = floor(max);
_maxRes = pow(2.0f, (float)max);
_maxDepth = max;
// create the blue noise
_noiseFunc = new BLUE_NOISE(5.0f / (float)_maxRes);
_noise = new bool[_maxRes * _maxRes];
_noiseFunc->complete();
_noiseFunc->maximize();
_noiseFunc->writeToBool(_noise, _maxRes);
_solver = new CG_SOLVER(_maxDepth, iterations);
}
QUAD_POISSON::~QUAD_POISSON()
{
deleteGhosts();
delete _root;
delete _solver;
delete _noiseFunc;
delete[] _noise;
}
//////////////////////////////////////////////////////////////////////
// draw boundaries to OGL
//////////////////////////////////////////////////////////////////////
void QUAD_POISSON::draw(CELL* cell)
{
// see if it's the root
if (cell == NULL) {
draw(_root);
return;
}
// draw the current cell
glColor4f(1,1,1,0.1);
glBegin(GL_LINE_STRIP);
glVertex2f(cell->bounds[1], 1.0f - cell->bounds[0]);
glVertex2f(cell->bounds[1], 1.0f - cell->bounds[2]);
glVertex2f(cell->bounds[3], 1.0f - cell->bounds[2]);
glVertex2f(cell->bounds[3], 1.0f - cell->bounds[0]);
glVertex2f(cell->bounds[1], 1.0f - cell->bounds[0]);
glEnd();
// draw the children
for (int x = 0; x < 4; x++)
if (cell->children[x] != NULL)
draw(cell->children[x]);
}
//////////////////////////////////////////////////////////////////////
// draw the given cell
//////////////////////////////////////////////////////////////////////
void QUAD_POISSON::drawCell(CELL* cell, float r, float g, float b)
{
// draw the current cell
glColor4f(r,g,b,1.0f);
glBegin(GL_QUADS);
glVertex2f(cell->bounds[1], 1.0f - cell->bounds[0]);
glVertex2f(cell->bounds[1], 1.0f - cell->bounds[2]);
glVertex2f(cell->bounds[3], 1.0f - cell->bounds[2]);
glVertex2f(cell->bounds[3], 1.0f - cell->bounds[0]);
glEnd();
}
//////////////////////////////////////////////////////////////////////
// subdivide quadtree to max level for (xPos, yPos)
// returns a pointer to the cell that was created
//////////////////////////////////////////////////////////////////////
CELL* QUAD_POISSON::insert(float xPos, float yPos)
{
int currentDepth = 0;
CELL* currentCell = _root;
bool existed = true;
while (currentDepth < _maxDepth) {
// find quadrant of current point
float diff[2];
diff[0] = xPos - currentCell->center[0];
diff[1] = yPos - currentCell->center[1];
int quadrant = 1;
if (diff[0] > 0.0f) {
if (diff[1] < 0.0f)
quadrant = 2;
}
else if (diff[1] < 0.0f)
quadrant = 3;
else
quadrant = 0;
// check if it exists
if (currentCell->children[quadrant] == NULL) {
existed = false;
currentCell->refine();
}
// recurse to next level
currentCell = currentCell->children[quadrant];
// increment depth
currentDepth++;
}
// if we had to subdivide to get the cell, add them to the list
if (!existed)
for (int i = 0; i < 4; i++)
{
_smallestLeaves.push_back(currentCell->parent->children[i]);
setNoise(currentCell->parent->children[i]);
}
///////////////////////////////////////////////////////////////////
// force orthogonal neighbors to be same depth
// I have commented the first block, the rest follow the same flow
///////////////////////////////////////////////////////////////////
// see if neighbor exists
CELL* north = currentCell->northNeighbor();
if (north && north->depth != _maxDepth) {
// while the neighbor needs to be refined
while (north->depth != _maxDepth) {
// refine it
north->refine();
// set to the newly refined neighbor
north = currentCell->northNeighbor();
}
// add newly created nodes to the list
for (int i = 0; i < 4; i++)
{
_smallestLeaves.push_back(north->parent->children[i]);
setNoise(north->parent->children[i]);
}
}
CELL* south = currentCell->southNeighbor();
if (south && south->depth != _maxDepth) {
while (south->depth != _maxDepth) {
south->refine();
south = currentCell->southNeighbor();
}
for (int i = 0; i < 4; i++)
{
_smallestLeaves.push_back(south->parent->children[i]);
setNoise(south->parent->children[i]);
}
}
CELL* west = currentCell->westNeighbor();
if (west && west->depth != _maxDepth) {
while (west->depth != _maxDepth) {
west->refine();
west = currentCell->westNeighbor();
}
for (int i = 0; i < 4; i++)
{
_smallestLeaves.push_back(west->parent->children[i]);
setNoise(west->parent->children[i]);
}
}
CELL* east = currentCell->eastNeighbor();
if (east && east->depth != _maxDepth) {
while (east->depth != _maxDepth) {
east->refine();
east = currentCell->eastNeighbor();
}
for (int i = 0; i < 4; i++)
{
_smallestLeaves.push_back(east->parent->children[i]);
setNoise(east->parent->children[i]);
}
}
///////////////////////////////////////////////////////////////////
// force diagonal neighbors to be same depth
// The same flow follows as above, except that it makes sure that
// the 'north' and 'south' neighbors already exist
///////////////////////////////////////////////////////////////////
if (north) {
CELL* northwest = north->westNeighbor();
if (northwest && northwest->depth != _maxDepth) {
while (northwest->depth != _maxDepth) {
northwest->refine();
northwest = northwest->children[2];
}
for (int i = 0; i < 4; i++)
{
_smallestLeaves.push_back(northwest->parent->children[i]);
setNoise(northwest->parent->children[i]);
}
}
CELL* northeast = north->eastNeighbor();
if (northeast && northeast->depth != _maxDepth) {
while (northeast->depth != _maxDepth) {
northeast->refine();
northeast= northeast->children[3];
}
for (int i = 0; i < 4; i++)
{
_smallestLeaves.push_back(northeast->parent->children[i]);
setNoise(northeast->parent->children[i]);
}
}
}
if (south) {
CELL* southwest = south->westNeighbor();
if (southwest && southwest->depth != _maxDepth) {
while (southwest->depth != _maxDepth) {
southwest->refine();
southwest = southwest->children[1];
}
for (int i = 0; i < 4; i++)
{
_smallestLeaves.push_back(southwest->parent->children[i]);
setNoise(southwest->parent->children[i]);
}
}
CELL* southeast = south->eastNeighbor();
if (southeast && southeast->depth != _maxDepth) {
while (southeast->depth != _maxDepth) {
southeast->refine();
southeast= southeast->children[0];
}
for (int i = 0; i < 4; i++)
{
_smallestLeaves.push_back(southeast->parent->children[i]);
setNoise(southeast->parent->children[i]);
}
}
}
return currentCell;
}
//////////////////////////////////////////////////////////////////////
// check if a cell hits a noise node
//////////////////////////////////////////////////////////////////////
void QUAD_POISSON::setNoise(CELL* cell)
{
if (!(cell->state == EMPTY))
return;
int x = cell->center[0] * _maxRes;
int y = cell->center[1] * _maxRes;
if (_noise[x + y * _maxRes])
{
cell->boundary = true;
cell->state = ATTRACTOR;
cell->potential = 0.5f;
cell->candidate = true;
}
}
//////////////////////////////////////////////////////////////////////
// insert all leaves into a list
//////////////////////////////////////////////////////////////////////
void QUAD_POISSON::getAllLeaves(list<CELL*>& leaves, CELL* currentCell)
{
// if we're at the root
if (currentCell == NULL)
{
getAllLeaves(leaves, _root);
return;
}
// if we're at a leaf, add it to the list
if (currentCell->children[0] == NULL)
{
leaves.push_back(currentCell);
return;
}
// if children exist, call recursively
for (int x = 0; x < 4; x++)
getAllLeaves(leaves, currentCell->children[x]);
}
//////////////////////////////////////////////////////////////////////
// insert all leaves not on the boundary into a list
//////////////////////////////////////////////////////////////////////
void QUAD_POISSON::getEmptyLeaves(list<CELL*>& leaves, CELL* currentCell)
{
// if we're at the root
if (currentCell == NULL) {
getEmptyLeaves(leaves, _root);
return;
}
// if we're at a leaf, check if it's a boundary and then
// add it to the list
if (currentCell->children[0] == NULL) {
if (!(currentCell->boundary))
leaves.push_back(currentCell);
return;
}
// if children exist, call recursively
for (int x = 0; x < 4; x++)
getEmptyLeaves(leaves, currentCell->children[x]);
}
//////////////////////////////////////////////////////////////////////
// balance the current tree
//////////////////////////////////////////////////////////////////////
void QUAD_POISSON::balance()
{
// collect all the leaf nodes
list<CELL*> leaves;
getAllLeaves(leaves);
// while the list is not empty
list<CELL*>::iterator cellIterator = leaves.begin();
for (cellIterator = leaves.begin(); cellIterator != leaves.end(); cellIterator++) {
CELL* currentCell = *cellIterator;
// if a north neighbor exists
CELL* north = currentCell->northNeighbor();
if (north != NULL)
// while the neighbor is not balanced
while (north->depth < currentCell->depth - 1) {
// refine it
north->refine();
// add the newly refined nodes to the list of
// those to be checked
for (int x = 0; x < 4; x++)
leaves.push_back(north->children[x]);
// set the cell to the newly created one
north = currentCell->northNeighbor();
}
// the rest of the blocks flow the same as above
CELL* south = currentCell->southNeighbor();
if (south!= NULL)
while (south->depth < currentCell->depth - 1) {
south->refine();
for (int x = 0; x < 4; x++)
leaves.push_back(south->children[x]);
south = currentCell->southNeighbor();
}
CELL* west = currentCell->westNeighbor();
if (west != NULL)
while (west->depth < currentCell->depth - 1) {
west->refine();
for (int x = 0; x < 4; x++)
leaves.push_back(west->children[x]);
west = currentCell->westNeighbor();
}
CELL* east = currentCell->eastNeighbor();
if (east != NULL)
while (east->depth < currentCell->depth - 1) {
east->refine();
for (int x = 0; x < 4; x++)
leaves.push_back(east->children[x]);
east = currentCell->eastNeighbor();
}
}
}
//////////////////////////////////////////////////////////////////////
// build the neighbor lists of the current quadtree
//////////////////////////////////////////////////////////////////////
void QUAD_POISSON::buildNeighbors()
{
balance();
// collect all the leaf nodes
list<CELL*> leaves;
getAllLeaves(leaves);
list<CELL*>::iterator cellIterator = leaves.begin();
for (cellIterator = leaves.begin(); cellIterator != leaves.end(); cellIterator++)
{
CELL* currentCell = *cellIterator;
// build north neighbors
CELL* north = currentCell->northNeighbor();
if (north != NULL) {
if (north->children[0] == NULL) {
currentCell->neighbors[0] = north;
currentCell->neighbors[1] = NULL;
}
else {
currentCell->neighbors[0] = north->children[3];
currentCell->neighbors[1] = north->children[2];
}
}
// else build a ghost cell
else
currentCell->neighbors[0] = new CELL(currentCell->depth);
// build east neighbors
CELL* east = currentCell->eastNeighbor();
if (east != NULL) {
if (east->children[0] == NULL) {
currentCell->neighbors[2] = east;
currentCell->neighbors[3] = NULL;
}
else {
currentCell->neighbors[2] = east->children[0];
currentCell->neighbors[3] = east->children[3];
}
}
// else build a ghost cell
else
currentCell->neighbors[2] = new CELL(currentCell->depth);
// build south neighbors
CELL* south = currentCell->southNeighbor();
if (south != NULL) {
if (south->children[0] == NULL) {
currentCell->neighbors[4] = south;
currentCell->neighbors[5] = NULL;
}
else {
currentCell->neighbors[4] = south->children[1];
currentCell->neighbors[5] = south->children[0];
}
}
// else build a ghost cell
else
currentCell->neighbors[4] = new CELL(currentCell->depth);
// build west neighbors
CELL* west = currentCell->westNeighbor();
if (west != NULL) {
if (west->children[0] == NULL) {
currentCell->neighbors[6] = west;
currentCell->neighbors[7] = NULL;
}
else {
currentCell->neighbors[6] = west->children[2];
currentCell->neighbors[7] = west->children[1];
}
}
// else build a ghost cell
else
currentCell->neighbors[6] = new CELL(currentCell->depth);
}
}
//////////////////////////////////////////////////////////////////////
// delete ghost cells
//////////////////////////////////////////////////////////////////////
void QUAD_POISSON::deleteGhosts(CELL* currentCell)
{
// if at the base, call on the root
if (currentCell == NULL)
{
deleteGhosts(_root);
return;
}
// if there are children, delete those too
if (currentCell->children[0]) {
// call recursively
for (int x = 0; x < 4; x++)
deleteGhosts(currentCell->children[x]);
return;
}
// check the neighbors for stuff to delete
for (int x = 0; x < 8; x++) {
// if the neighbor exists
if (currentCell->neighbors[x])
// and if it is a ghost cell, delete it
if (currentCell->neighbors[x]->parent == NULL)
delete currentCell->neighbors[x];
}
}
//////////////////////////////////////////////////////////////////////
// solve the Poisson problem
//////////////////////////////////////////////////////////////////////
int QUAD_POISSON::solve() {
// maintain the quadtree
balance();
buildNeighbors();
// retrieve leaves at the lowest level
_emptyLeaves.clear();
getEmptyLeaves(_emptyLeaves);
static bool firstSolve = true;
static int iterations = _solver->iterations();
// do a full precision solve the first time
if (firstSolve)
{
iterations = _solver->iterations();
_solver->iterations() = 10000;
firstSolve = false;
}
else
_solver->iterations() = iterations;
// return the number of iterations
return _solver->solve(_emptyLeaves);
};
//////////////////////////////////////////////////////////////////////
// get the leafnode that corresponds to the coordinate
//////////////////////////////////////////////////////////////////////
CELL* QUAD_POISSON::getLeaf(float xPos, float yPos)
{
CELL* currentCell = _root;
while (currentCell->children[0] != NULL)
{
// find quadrant of current point
float diff[2];
diff[0] = xPos - currentCell->center[0];
diff[1] = yPos - currentCell->center[1];
int quadrant = 1;
if (diff[0] > 0.0f)
{
if (diff[1] < 0.0f)
quadrant = 2;
}
else if (diff[1] < 0.0f)
quadrant = 3;
else
quadrant = 0;
// check if it exists
if (currentCell->children[quadrant] != NULL)
currentCell = currentCell->children[quadrant];
}
return currentCell;
}