Initial commit
This commit is contained in:
commit
7cdac8ded9
45 changed files with 7275 additions and 0 deletions
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
build/*
|
||||||
|
html
|
||||||
|
*.exr
|
||||||
272
APSF.cpp
Normal file
272
APSF.cpp
Normal file
|
|
@ -0,0 +1,272 @@
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// File : APSF.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 "APSF.h"
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// Construction/Destruction
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
APSF::APSF(int res) :
|
||||||
|
_res(res)
|
||||||
|
{
|
||||||
|
// make sure kernel is odd dimensions
|
||||||
|
if (!(_res % 2)) _res++;
|
||||||
|
_kernel = new float[_res * _res];
|
||||||
|
|
||||||
|
_q = 0.999;
|
||||||
|
_R = 400.0f;
|
||||||
|
_D = 2000.0f;
|
||||||
|
_T = 1.001f;
|
||||||
|
_sigma = 0.5f;
|
||||||
|
|
||||||
|
_maxTerms = 600;
|
||||||
|
_I0 = 1.0f;
|
||||||
|
_retinaSize = 0.01f;
|
||||||
|
_eyeSize = 0.025f;
|
||||||
|
}
|
||||||
|
|
||||||
|
APSF::~APSF()
|
||||||
|
{
|
||||||
|
delete[] _kernel;
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// Legendre polymonial
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
float APSF::legendreM(int m, float mu)
|
||||||
|
{
|
||||||
|
vector<float> memoized;
|
||||||
|
memoized.push_back(1.0f);
|
||||||
|
memoized.push_back(mu);
|
||||||
|
|
||||||
|
for (int x = 2; x <= m; x++)
|
||||||
|
{
|
||||||
|
float newMemo = ((2.0f * (float)x - 1.0f) * mu * memoized[x - 1] -
|
||||||
|
((float)x - 1.0f) * memoized[x - 2]) / (float)x;
|
||||||
|
memoized.push_back(newMemo);
|
||||||
|
}
|
||||||
|
|
||||||
|
return memoized[m];
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// scattering function at a point
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
float APSF::pointAPSF(float mu)
|
||||||
|
{
|
||||||
|
float total = 0.0f;
|
||||||
|
|
||||||
|
for (int m = 0; m < _maxTerms; m++)
|
||||||
|
total += (gM(_I0, m) + gM(_I0, m + 1)) * legendreM(m, mu);
|
||||||
|
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// generate a convolution kernel
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
void APSF::generateKernelFast()
|
||||||
|
{
|
||||||
|
float dx = _retinaSize / (float)_res;
|
||||||
|
float dy = _retinaSize / (float)_res;
|
||||||
|
int halfRes = _res / 2;
|
||||||
|
float* oneD = new float[_res];
|
||||||
|
|
||||||
|
float max = 0.0f;
|
||||||
|
float min = 1000.0f;
|
||||||
|
int x,y = halfRes;
|
||||||
|
for (x = 0; x < _res; x++)
|
||||||
|
{
|
||||||
|
// calc angle
|
||||||
|
float diffX = (x - halfRes) * dx;
|
||||||
|
float diffY = (y - halfRes) * dy;
|
||||||
|
float distance = sqrt(diffX * diffX + diffY * diffY);
|
||||||
|
if ((distance / _eyeSize) > (_R / _D))
|
||||||
|
oneD[x] = 0.0f;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
float i = -distance * distance * _D * _D + _eyeSize * _eyeSize * _R * _R + distance * distance * _R * _R;
|
||||||
|
i = _eyeSize * _eyeSize * _D - _eyeSize * sqrt(i);
|
||||||
|
i /= _eyeSize * _eyeSize + distance * distance;
|
||||||
|
float mu = M_PI - atan(_retinaSize / distance) - asin((_D - i) / _R);
|
||||||
|
oneD[x] = pointAPSF(cos(mu));
|
||||||
|
|
||||||
|
min = (oneD[x] < min) ? oneD[x] : min;
|
||||||
|
}
|
||||||
|
max = (oneD[x] > max) ? oneD[x] : max;
|
||||||
|
}
|
||||||
|
|
||||||
|
// floor
|
||||||
|
if (min > 0.0f)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < _res; i++)
|
||||||
|
if (oneD[i] > 0.0f)
|
||||||
|
oneD[i] -= min;
|
||||||
|
max -= min;
|
||||||
|
}
|
||||||
|
|
||||||
|
// normalize
|
||||||
|
if (max > 1.0f)
|
||||||
|
{
|
||||||
|
float maxInv = 1.0f / max;
|
||||||
|
for (int i = 0; i < _res; i++)
|
||||||
|
oneD[i] *= maxInv;
|
||||||
|
}
|
||||||
|
|
||||||
|
// interpolate the kernel
|
||||||
|
int index = 0;
|
||||||
|
for (y = 0; y < _res; y++)
|
||||||
|
for (x = 0; x < _res; x++, index++)
|
||||||
|
{
|
||||||
|
float dx = fabs((float)(x - halfRes));
|
||||||
|
float dy = fabs((float)(y - halfRes));
|
||||||
|
float magnitude = sqrtf(dx * dx + dy * dy);
|
||||||
|
|
||||||
|
int lower = floor(magnitude);
|
||||||
|
if (lower > halfRes - 1)
|
||||||
|
{
|
||||||
|
_kernel[index] = 0.0f;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
float lerp = magnitude - lower;
|
||||||
|
_kernel[index] = (1.0f - lerp) * oneD[halfRes + lower] +
|
||||||
|
lerp * oneD[halfRes + lower + 1];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
delete[] oneD;
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// save the kernel in binary
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
void APSF::write(const char* filename)
|
||||||
|
{
|
||||||
|
// open file
|
||||||
|
FILE* file;
|
||||||
|
file = fopen(filename, "wb");
|
||||||
|
|
||||||
|
fwrite((void*)&_res, sizeof(int), 1, file);
|
||||||
|
fwrite((void*)&_q, sizeof(float), 1, file);
|
||||||
|
fwrite((void*)&_T, sizeof(float), 1, file);
|
||||||
|
fwrite((void*)&_I0, sizeof(float), 1, file);
|
||||||
|
fwrite((void*)&_sigma, sizeof(float), 1, file);
|
||||||
|
fwrite((void*)&_R, sizeof(float), 1, file);
|
||||||
|
fwrite((void*)&_D, sizeof(float), 1, file);
|
||||||
|
fwrite((void*)&_retinaSize, sizeof(float), 1, file);
|
||||||
|
fwrite((void*)&_eyeSize, sizeof(float), 1, file);
|
||||||
|
fwrite((void*)&_maxTerms, sizeof(int), 1, file);
|
||||||
|
fwrite((void*)&_kernel, sizeof(float) * _res * _res, 1, file);
|
||||||
|
|
||||||
|
fclose(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// load a binary kernel
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
void APSF::read(const char* filename)
|
||||||
|
{
|
||||||
|
// open file
|
||||||
|
FILE* file;
|
||||||
|
file = fopen(filename, "rb");
|
||||||
|
|
||||||
|
if (_kernel) delete[] _kernel;
|
||||||
|
|
||||||
|
fread((void*)&_res, sizeof(int), 1, file);
|
||||||
|
fread((void*)&_q, sizeof(float), 1, file);
|
||||||
|
fread((void*)&_T, sizeof(float), 1, file);
|
||||||
|
fread((void*)&_I0, sizeof(float), 1, file);
|
||||||
|
fread((void*)&_sigma, sizeof(float), 1, file);
|
||||||
|
fread((void*)&_R, sizeof(float), 1, file);
|
||||||
|
fread((void*)&_D, sizeof(float), 1, file);
|
||||||
|
fread((void*)&_retinaSize, sizeof(float), 1, file);
|
||||||
|
fread((void*)&_eyeSize, sizeof(float), 1, file);
|
||||||
|
fread((void*)&_maxTerms, sizeof(int), 1, file);
|
||||||
|
|
||||||
|
_kernel = new float[_res * _res];
|
||||||
|
fread((void*)&_kernel, sizeof(float) * _res * _res, 1, file);
|
||||||
|
|
||||||
|
fclose(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// write the kernel to a PPM file
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
void APSF::writePPM(const char* filename)
|
||||||
|
{
|
||||||
|
unsigned char* ppm = new unsigned char[3 * _res * _res];
|
||||||
|
|
||||||
|
for (int x = 0; x < _res * _res; x++)
|
||||||
|
{
|
||||||
|
ppm[3 * x] = 255 * _kernel[x];
|
||||||
|
ppm[3 * x + 1] = 255 * _kernel[x];
|
||||||
|
ppm[3 * x + 2] = 255 * _kernel[x];
|
||||||
|
}
|
||||||
|
WritePPM(filename, ppm, _res, _res);
|
||||||
|
|
||||||
|
delete[] ppm;
|
||||||
|
}
|
||||||
155
APSF.h
Normal file
155
APSF.h
Normal file
|
|
@ -0,0 +1,155 @@
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// File : APSF.h
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef APSF_H
|
||||||
|
#define APSF_H
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
#include <GL/glut.h>
|
||||||
|
#include <vector>
|
||||||
|
#include "ppm/ppm.hpp"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
#ifndef M_PI
|
||||||
|
#define M_PI 3.1415926535897931f
|
||||||
|
#endif
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
/// \brief Generates the rendering filter
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
class APSF
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
//! constructor
|
||||||
|
APSF(int res = 512);
|
||||||
|
//! destructor
|
||||||
|
virtual ~APSF();
|
||||||
|
|
||||||
|
//! read in an APSF file
|
||||||
|
void read(const char* filename);
|
||||||
|
//! write out an APSF file
|
||||||
|
void write(const char* filename);
|
||||||
|
//! write out the current kernel to a PPM file
|
||||||
|
void writePPM(const char* filename);
|
||||||
|
|
||||||
|
//! generate one line of the kernel and spin it radially
|
||||||
|
void generateKernelFast();
|
||||||
|
|
||||||
|
//! resolution of current kernel
|
||||||
|
int res() { return _res; };
|
||||||
|
|
||||||
|
//! returns the float array for the kernel
|
||||||
|
float* kernel() { return _kernel; };
|
||||||
|
|
||||||
|
private:
|
||||||
|
//! kernel resolution
|
||||||
|
int _res;
|
||||||
|
//! convolution kernel
|
||||||
|
float* _kernel;
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// APSF components
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// scattering parameters
|
||||||
|
float _q;
|
||||||
|
float _T;
|
||||||
|
float _I0;
|
||||||
|
float _sigma;
|
||||||
|
float _R;
|
||||||
|
float _D;
|
||||||
|
|
||||||
|
float _retinaSize;
|
||||||
|
float _eyeSize;
|
||||||
|
|
||||||
|
//! number of coefficients
|
||||||
|
int _maxTerms;
|
||||||
|
|
||||||
|
//! function value at a point
|
||||||
|
float pointAPSF(float mu);
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// auxiliary functions
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
float legendreM(int m, float mu);
|
||||||
|
float gM(float I0, int m) {
|
||||||
|
return (m == 0) ? 0.0f : exp(-(betaM(m, _q) * _T + alphaM(m) * log(_T)));
|
||||||
|
};
|
||||||
|
float alphaM(float m) {
|
||||||
|
return m + 1.0f;
|
||||||
|
};
|
||||||
|
float betaM(float m, float q) {
|
||||||
|
return ((2.0f * m + 1.0f) / m) * (1.0f - pow(q, (int)m - 1));
|
||||||
|
};
|
||||||
|
float factorial(float x) {
|
||||||
|
return (x <= 1.0f) ? 1.0f : x * factorial(x - 1.0f);
|
||||||
|
};
|
||||||
|
float choose(float x, float y) {
|
||||||
|
return factorial(x) / (factorial(y) * factorial(x - y));
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
448
BlueNoise/BLUE_NOISE.cpp
Normal file
448
BlueNoise/BLUE_NOISE.cpp
Normal file
|
|
@ -0,0 +1,448 @@
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// File : BLUE_NOISE.h
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// This class is a very thin wrapper to Daniel Dunbar's blue noise generator.
|
||||||
|
// With the exception of BLUE_NOISE.h and BLUE_NOISE.cpp, the other files
|
||||||
|
// in this directory are unmodified copies of his code.
|
||||||
|
//
|
||||||
|
// For the original, untainted code, see:
|
||||||
|
// http://www.cs.virginia.edu/~gfx/pubs/antimony/
|
||||||
|
//
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// $Id: PDSampling.cpp,v 1.12 2006/07/11 16:45:22 zr Exp $
|
||||||
|
|
||||||
|
#define _USE_MATH_DEFINES
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
#include "BLUE_NOISE.h"
|
||||||
|
#include "RangeList.h"
|
||||||
|
#include "ScallopedSector.h"
|
||||||
|
#include "WeightedDiscretePDF.h"
|
||||||
|
|
||||||
|
typedef std::vector<int> IntVector;
|
||||||
|
|
||||||
|
///
|
||||||
|
|
||||||
|
BLUE_NOISE::BLUE_NOISE(float _radius, bool _isTiled, bool usesGrid) :
|
||||||
|
m_rng(123456),
|
||||||
|
radius(_radius),
|
||||||
|
isTiled(_isTiled)
|
||||||
|
{
|
||||||
|
if (usesGrid) {
|
||||||
|
// grid size is chosen so that 4*radius search only
|
||||||
|
// requires searching adjacent cells, this also
|
||||||
|
// determines max points per cell
|
||||||
|
m_gridSize = (int) ceil(2./(4.*_radius));
|
||||||
|
if (m_gridSize<2) m_gridSize = 2;
|
||||||
|
|
||||||
|
m_gridCellSize = 2.0f/m_gridSize;
|
||||||
|
m_grid = new int[m_gridSize*m_gridSize][kMaxPointsPerCell];
|
||||||
|
|
||||||
|
for (int y=0; y<m_gridSize; y++) {
|
||||||
|
for (int x=0; x<m_gridSize; x++) {
|
||||||
|
for (int k=0; k<kMaxPointsPerCell; k++) {
|
||||||
|
m_grid[y*m_gridSize + x][k] = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
m_gridSize = 0;
|
||||||
|
m_gridCellSize = 0;
|
||||||
|
m_grid = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BLUE_NOISE::pointInDomain(Vec2 &a)
|
||||||
|
{
|
||||||
|
return -1<=a.x && -1<=a.y && 1>=a.x && 1>=a.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec2 BLUE_NOISE::randomPoint()
|
||||||
|
{
|
||||||
|
return Vec2(2*m_rng.getFloatL()-1, 2*m_rng.getFloatL()-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec2 BLUE_NOISE::getTiled(Vec2 v)
|
||||||
|
{
|
||||||
|
float x = v.x, y = v.y;
|
||||||
|
|
||||||
|
if (isTiled) {
|
||||||
|
if (x<-1) x += 2;
|
||||||
|
else if (x>1) x -= 2;
|
||||||
|
|
||||||
|
if (y<-1) y += 2;
|
||||||
|
else if (y>1) y -= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Vec2(x,y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BLUE_NOISE::getGridXY(Vec2 &v, int *gx_out, int *gy_out)
|
||||||
|
{
|
||||||
|
int gx = *gx_out = (int) floor(.5*(v.x + 1)*m_gridSize);
|
||||||
|
int gy = *gy_out = (int) floor(.5*(v.y + 1)*m_gridSize);
|
||||||
|
if (gx<0 || gx>=m_gridSize || gy<0 || gy>=m_gridSize) {
|
||||||
|
printf("Internal error, point outside grid was generated, ignoring.\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BLUE_NOISE::addPoint(Vec2 pt)
|
||||||
|
{
|
||||||
|
int i, gx, gy, *cell;
|
||||||
|
|
||||||
|
points.push_back(pt);
|
||||||
|
|
||||||
|
if (m_grid) {
|
||||||
|
getGridXY(pt, &gx, &gy);
|
||||||
|
cell = m_grid[gy*m_gridSize + gx];
|
||||||
|
for (i=0; i<kMaxPointsPerCell; i++) {
|
||||||
|
if (cell[i]==-1) {
|
||||||
|
cell[i] = (int) points.size()-1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (i==kMaxPointsPerCell) {
|
||||||
|
printf("Internal error, overflowed max points per grid cell. Exiting.\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int BLUE_NOISE::findNeighbors(Vec2 &pt, float distance)
|
||||||
|
{
|
||||||
|
if (!m_grid) {
|
||||||
|
printf("Internal error, sampler cannot search without grid.\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
float distanceSqrd = distance*distance;
|
||||||
|
int i, j, k, gx, gy, N = (int) ceil(distance/m_gridCellSize);
|
||||||
|
if (N>(m_gridSize>>1)) N = m_gridSize>>1;
|
||||||
|
|
||||||
|
m_neighbors.clear();
|
||||||
|
getGridXY(pt, &gx, &gy);
|
||||||
|
for (j=-N; j<=N; j++) {
|
||||||
|
for (i=-N; i<=N; i++) {
|
||||||
|
int cx = (gx+i+m_gridSize)%m_gridSize;
|
||||||
|
int cy = (gy+j+m_gridSize)%m_gridSize;
|
||||||
|
int *cell = m_grid[cy*m_gridSize + cx];
|
||||||
|
|
||||||
|
for (k=0; k<kMaxPointsPerCell; k++) {
|
||||||
|
if (cell[k]==-1) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
if (getDistanceSquared(pt, points[cell[k]])<distanceSqrd)
|
||||||
|
m_neighbors.push_back(cell[k]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (int) m_neighbors.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
float BLUE_NOISE::findClosestNeighbor(Vec2 &pt, float distance)
|
||||||
|
{
|
||||||
|
if (!m_grid) {
|
||||||
|
printf("Internal error, sampler cannot search without grid.\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
float closestSqrd = distance*distance;
|
||||||
|
int i, j, k, gx, gy, N = (int) ceil(distance/m_gridCellSize);
|
||||||
|
if (N>(m_gridSize>>1)) N = m_gridSize>>1;
|
||||||
|
|
||||||
|
getGridXY(pt, &gx, &gy);
|
||||||
|
for (j=-N; j<=N; j++) {
|
||||||
|
for (i=-N; i<=N; i++) {
|
||||||
|
int cx = (gx+i+m_gridSize)%m_gridSize;
|
||||||
|
int cy = (gy+j+m_gridSize)%m_gridSize;
|
||||||
|
int *cell = m_grid[cy*m_gridSize + cx];
|
||||||
|
|
||||||
|
for (k=0; k<kMaxPointsPerCell; k++) {
|
||||||
|
if (cell[k]==-1) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
float d = getDistanceSquared(pt, points[cell[k]]);
|
||||||
|
|
||||||
|
if (d<closestSqrd)
|
||||||
|
closestSqrd = d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sqrt(closestSqrd);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BLUE_NOISE::findNeighborRanges(int index, RangeList &rl)
|
||||||
|
{
|
||||||
|
if (!m_grid) {
|
||||||
|
printf("Internal error, sampler cannot search without grid.\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec2 &candidate = points[index];
|
||||||
|
float rangeSqrd = 4*4*radius*radius;
|
||||||
|
int i, j, k, gx, gy, N = (int) ceil(4*radius/m_gridCellSize);
|
||||||
|
if (N>(m_gridSize>>1)) N = m_gridSize>>1;
|
||||||
|
|
||||||
|
getGridXY(candidate, &gx, &gy);
|
||||||
|
|
||||||
|
int xSide = (candidate.x - (-1 + gx*m_gridCellSize))>m_gridCellSize*.5;
|
||||||
|
int ySide = (candidate.y - (-1 + gy*m_gridCellSize))>m_gridCellSize*.5;
|
||||||
|
int iy = 1;
|
||||||
|
for (j=-N; j<=N; j++) {
|
||||||
|
int ix = 1;
|
||||||
|
|
||||||
|
if (j==0) iy = ySide;
|
||||||
|
else if (j==1) iy = 0;
|
||||||
|
|
||||||
|
for (i=-N; i<=N; i++) {
|
||||||
|
if (i==0) ix = xSide;
|
||||||
|
else if (i==1) ix = 0;
|
||||||
|
|
||||||
|
// offset to closest cell point
|
||||||
|
float dx = candidate.x - (-1 + (gx+i+ix)*m_gridCellSize);
|
||||||
|
float dy = candidate.y - (-1 + (gy+j+iy)*m_gridCellSize);
|
||||||
|
|
||||||
|
if (dx*dx+dy*dy<rangeSqrd) {
|
||||||
|
int cx = (gx+i+m_gridSize)%m_gridSize;
|
||||||
|
int cy = (gy+j+m_gridSize)%m_gridSize;
|
||||||
|
int *cell = m_grid[cy*m_gridSize + cx];
|
||||||
|
|
||||||
|
for (k=0; k<kMaxPointsPerCell; k++) {
|
||||||
|
if (cell[k]==-1) {
|
||||||
|
break;
|
||||||
|
} else if (cell[k]!=index) {
|
||||||
|
Vec2 &pt = points[cell[k]];
|
||||||
|
Vec2 v = getTiled(pt-candidate);
|
||||||
|
float distSqrd = v.x*v.x + v.y*v.y;
|
||||||
|
|
||||||
|
if (distSqrd<rangeSqrd) {
|
||||||
|
float dist = sqrt(distSqrd);
|
||||||
|
float angle = atan2(v.y,v.x);
|
||||||
|
float theta = acos(.25f*dist/radius);
|
||||||
|
|
||||||
|
rl.subtract(angle-theta, angle+theta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BLUE_NOISE::maximize()
|
||||||
|
{
|
||||||
|
RangeList rl(0,0);
|
||||||
|
int i, N = (int) points.size();
|
||||||
|
|
||||||
|
for (i=0; i<N; i++) {
|
||||||
|
Vec2 &candidate = points[i];
|
||||||
|
|
||||||
|
rl.reset(0, (float) M_PI*2);
|
||||||
|
findNeighborRanges(i, rl);
|
||||||
|
while (rl.numRanges) {
|
||||||
|
RangeEntry &re = rl.ranges[m_rng.getInt31()%rl.numRanges];
|
||||||
|
float angle = re.min + (re.max-re.min)*m_rng.getFloatL();
|
||||||
|
Vec2 pt = getTiled(Vec2(candidate.x + cos(angle)*2*radius,
|
||||||
|
candidate.y + sin(angle)*2*radius));
|
||||||
|
|
||||||
|
addPoint(pt);
|
||||||
|
rl.subtract(angle - (float) M_PI/3, angle + (float) M_PI/3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BLUE_NOISE::relax()
|
||||||
|
{
|
||||||
|
FILE *tmp = fopen("relaxTmpIn.txt","w");
|
||||||
|
int dim, numVerts, numFaces;
|
||||||
|
Vec2 *verts = 0;
|
||||||
|
int numPoints = (int) points.size();
|
||||||
|
|
||||||
|
// will overwrite later
|
||||||
|
fprintf(tmp, "2 \n");
|
||||||
|
for (int i=0; i<(int) points.size(); i++) {
|
||||||
|
Vec2 &pt = points[i];
|
||||||
|
fprintf(tmp, "%f %f\n", pt.x, pt.y);
|
||||||
|
}
|
||||||
|
for (int y=-1; y<=1; y++) {
|
||||||
|
for (int x=-1; x<=1; x++) {
|
||||||
|
if (x || y) {
|
||||||
|
for (int i=0; i<(int) points.size(); i++) {
|
||||||
|
Vec2 &pt = points[i];
|
||||||
|
if (fabs(pt.x+x*2)-1<radius*4 || fabs(pt.y+y*2)-1<radius*4) {
|
||||||
|
fprintf(tmp, "%f %f\n", pt.x+x*2, pt.y+y*2);
|
||||||
|
numPoints++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fseek(tmp, 0, 0);
|
||||||
|
fprintf(tmp, "2 %d", numPoints);
|
||||||
|
fclose(tmp);
|
||||||
|
|
||||||
|
tmp = fopen("relaxTmpOut.txt", "w");
|
||||||
|
fclose(tmp);
|
||||||
|
system("qvoronoi p FN < relaxTmpIn.txt > relaxTmpOut.txt");
|
||||||
|
|
||||||
|
tmp = fopen("relaxTmpOut.txt", "r");
|
||||||
|
fscanf(tmp, "%d\n%d\n", &dim, &numVerts);
|
||||||
|
|
||||||
|
if (dim!=2) {
|
||||||
|
printf("Error calling out to qvoronoi, skipping relaxation.\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
verts = new Vec2[numVerts];
|
||||||
|
for (int i=0; i<numVerts; i++) {
|
||||||
|
fscanf(tmp, "%f %f\n", &verts[i].x, &verts[i].y);
|
||||||
|
}
|
||||||
|
|
||||||
|
fscanf(tmp, "%d\n", &numFaces);
|
||||||
|
|
||||||
|
for (int i=0; i<(int) points.size(); i++) {
|
||||||
|
Vec2 center(0,0);
|
||||||
|
int N, skip=0;
|
||||||
|
|
||||||
|
fscanf(tmp, "%d", &N);
|
||||||
|
for (int j=0; j<N; j++) {
|
||||||
|
int index;
|
||||||
|
|
||||||
|
fscanf(tmp, "%d", &index);
|
||||||
|
if (index<0) {
|
||||||
|
skip = 1;
|
||||||
|
} else {
|
||||||
|
center += verts[index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!skip) {
|
||||||
|
center *= (1.0f/N);
|
||||||
|
points[i] = getTiled(center);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exit:
|
||||||
|
if (verts) delete verts;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
|
||||||
|
typedef std::map<int, ScallopedRegion*> RegionMap;
|
||||||
|
|
||||||
|
void BLUE_NOISE::complete()
|
||||||
|
{
|
||||||
|
RangeList rl(0,0);
|
||||||
|
IntVector candidates;
|
||||||
|
|
||||||
|
addPoint(randomPoint());
|
||||||
|
candidates.push_back((int) points.size()-1);
|
||||||
|
|
||||||
|
while (candidates.size()) {
|
||||||
|
int c = m_rng.getInt32()%candidates.size();
|
||||||
|
int index = candidates[c];
|
||||||
|
Vec2 candidate = points[index];
|
||||||
|
candidates[c] = candidates[candidates.size()-1];
|
||||||
|
candidates.pop_back();
|
||||||
|
|
||||||
|
rl.reset(0, (float) M_PI*2);
|
||||||
|
findNeighborRanges(index, rl);
|
||||||
|
while (rl.numRanges) {
|
||||||
|
RangeEntry &re = rl.ranges[m_rng.getInt32()%rl.numRanges];
|
||||||
|
float angle = re.min + (re.max-re.min)*m_rng.getFloatL();
|
||||||
|
Vec2 pt = getTiled(Vec2(candidate.x + cos(angle)*2*radius,
|
||||||
|
candidate.y + sin(angle)*2*radius));
|
||||||
|
|
||||||
|
addPoint(pt);
|
||||||
|
candidates.push_back((int) points.size()-1);
|
||||||
|
|
||||||
|
rl.subtract(angle - (float) M_PI/3, angle + (float) M_PI/3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BLUE_NOISE::writeToBool(bool* noise, int size)
|
||||||
|
{
|
||||||
|
// wipe
|
||||||
|
int index = 0;
|
||||||
|
for (index = 0; index < size * size; index++)
|
||||||
|
noise[index] = false;
|
||||||
|
|
||||||
|
for (int x = 0; x < points.size(); x++)
|
||||||
|
{
|
||||||
|
int i = (points[x].x + 1.0f) * 0.5f * size;
|
||||||
|
int j = (points[x].y + 1.0f) * 0.5f * size;
|
||||||
|
index = i + j * size;
|
||||||
|
noise[index] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
183
BlueNoise/BLUE_NOISE.h
Normal file
183
BlueNoise/BLUE_NOISE.h
Normal file
|
|
@ -0,0 +1,183 @@
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// File : BLUE_NOISE.h
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// This class is a very thin wrapper to Daniel Dunbar's blue noise generator.
|
||||||
|
// With the exception of BLUE_NOISE.h and BLUE_NOISE.cpp, the other files
|
||||||
|
// in this directory are unmodified copies of his code.
|
||||||
|
//
|
||||||
|
// For the original, untainted code, see:
|
||||||
|
// http://www.cs.virginia.edu/~gfx/pubs/antimony/
|
||||||
|
//
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// $Id: PDSampling.h,v 1.6 2006/07/06 23:13:18 zr Exp $
|
||||||
|
|
||||||
|
#include "RNG.h"
|
||||||
|
#include <cmath>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#define kMaxPointsPerCell 9
|
||||||
|
|
||||||
|
class RangeList;
|
||||||
|
class ScallopedRegion;
|
||||||
|
|
||||||
|
class Vec2 {
|
||||||
|
public:
|
||||||
|
Vec2() {};
|
||||||
|
Vec2(float _x, float _y) : x(_x), y(_y) {};
|
||||||
|
|
||||||
|
float x,y;
|
||||||
|
|
||||||
|
float length() { return sqrt(x*x + y*y); }
|
||||||
|
|
||||||
|
bool operator ==(const Vec2 &b) const { return x==b.x && y==b.y; }
|
||||||
|
Vec2 operator +(Vec2 b) { return Vec2(x+b.x, y+b.y); }
|
||||||
|
Vec2 operator -(Vec2 b) { return Vec2(x-b.x, y-b.y); }
|
||||||
|
Vec2 operator *(Vec2 b) { return Vec2(x*b.x, y*b.y); }
|
||||||
|
Vec2 operator /(Vec2 b) { return Vec2(x/b.x, y*b.y); }
|
||||||
|
|
||||||
|
Vec2 operator +(float n) { return Vec2(x+n, y+n); }
|
||||||
|
Vec2 operator -(float n) { return Vec2(x-n, y-n); }
|
||||||
|
Vec2 operator *(float n) { return Vec2(x*n, y*n); }
|
||||||
|
Vec2 operator /(float n) { return Vec2(x/n, y*n); }
|
||||||
|
|
||||||
|
Vec2 &operator +=(Vec2 b) { x+=b.x; y+=b.y; return *this; }
|
||||||
|
Vec2 &operator -=(Vec2 b) { x-=b.x; y-=b.y; return *this; }
|
||||||
|
Vec2 &operator *=(Vec2 b) { x*=b.x; y*=b.y; return *this; }
|
||||||
|
Vec2 &operator /=(Vec2 b) { x/=b.x; y/=b.y; return *this; }
|
||||||
|
|
||||||
|
Vec2 &operator +=(float n) { x+=n; y+=n; return *this; }
|
||||||
|
Vec2 &operator -=(float n) { x-=n; y-=n; return *this; }
|
||||||
|
Vec2 &operator *=(float n) { x*=n; y*=n; return *this; }
|
||||||
|
Vec2 &operator /=(float n) { x/=n; y/=n; return *this; }
|
||||||
|
};
|
||||||
|
|
||||||
|
/// \brief Daniel Dunbar's blue noise generator
|
||||||
|
///
|
||||||
|
/// The original code has been modified so that the 'boundary sampling'
|
||||||
|
/// method is the only one available.
|
||||||
|
class BLUE_NOISE {
|
||||||
|
protected:
|
||||||
|
RNG m_rng;
|
||||||
|
std::vector<int> m_neighbors;
|
||||||
|
|
||||||
|
int (*m_grid)[kMaxPointsPerCell];
|
||||||
|
int m_gridSize;
|
||||||
|
float m_gridCellSize;
|
||||||
|
|
||||||
|
public:
|
||||||
|
std::vector<Vec2> points;
|
||||||
|
float radius;
|
||||||
|
bool isTiled;
|
||||||
|
|
||||||
|
public:
|
||||||
|
BLUE_NOISE(float radius, bool isTiled=true, bool usesGrid=true);
|
||||||
|
virtual ~BLUE_NOISE() { };
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
bool pointInDomain(Vec2 &a);
|
||||||
|
|
||||||
|
// return shortest distance between _a_
|
||||||
|
// and _b_ (accounting for tiling)
|
||||||
|
float getDistanceSquared(Vec2 &a, Vec2 &b) { Vec2 v = getTiled(b-a); return v.x*v.x + v.y*v.y; }
|
||||||
|
float getDistance(Vec2 &a, Vec2 &b) { return sqrt(getDistanceSquared(a, b)); }
|
||||||
|
|
||||||
|
// generate a random point in square
|
||||||
|
Vec2 randomPoint();
|
||||||
|
|
||||||
|
// return tiled coordinates of _v_
|
||||||
|
Vec2 getTiled(Vec2 v);
|
||||||
|
|
||||||
|
// return grid x,y for point
|
||||||
|
void getGridXY(Vec2 &v, int *gx_out, int *gy_out);
|
||||||
|
|
||||||
|
// add _pt_ to point list and grid
|
||||||
|
void addPoint(Vec2 pt);
|
||||||
|
|
||||||
|
// populate m_neighbors with list of
|
||||||
|
// all points within _radius_ of _pt_
|
||||||
|
// and return number of such points
|
||||||
|
int findNeighbors(Vec2 &pt, float radius);
|
||||||
|
|
||||||
|
// return distance to closest neighbor within _radius_
|
||||||
|
float findClosestNeighbor(Vec2 &pt, float radius);
|
||||||
|
|
||||||
|
// find available angle ranges on boundary for candidate
|
||||||
|
// by subtracting occluded neighbor ranges from _rl_
|
||||||
|
void findNeighborRanges(int index, RangeList &rl);
|
||||||
|
|
||||||
|
// extend point set by boundary sampling until domain is
|
||||||
|
// full
|
||||||
|
void maximize();
|
||||||
|
|
||||||
|
// apply one step of Lloyd relaxation
|
||||||
|
void relax();
|
||||||
|
|
||||||
|
void complete();
|
||||||
|
|
||||||
|
void writeToBool(bool* noise, int size);
|
||||||
|
};
|
||||||
7
BlueNoise/LICENSE.txt
Normal file
7
BlueNoise/LICENSE.txt
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
This code is released into the public domain. You can do whatever
|
||||||
|
you want with it.
|
||||||
|
|
||||||
|
I do ask that you respect the authorship and credit myself (Daniel
|
||||||
|
Dunbar) when referencing the code. Additionally, if you use the
|
||||||
|
code in an interesting or integral manner I would like to hear
|
||||||
|
about it.
|
||||||
101
BlueNoise/README.txt
Normal file
101
BlueNoise/README.txt
Normal file
|
|
@ -0,0 +1,101 @@
|
||||||
|
PDSample - Poisson-Disk sample set generation
|
||||||
|
Daniel Dunbar, daniel@zuster.org
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
Overview
|
||||||
|
--
|
||||||
|
PDSample generates Poisson-disk sampling sets in the domain [-1,1]^2
|
||||||
|
using a variety of methods. See "A Spatial Data Structure for Fast
|
||||||
|
Poisson-Disk Sample Generation", in Proc' of SIGGRAPH 2006 for more
|
||||||
|
information.
|
||||||
|
|
||||||
|
|
||||||
|
Building
|
||||||
|
---
|
||||||
|
The code should be portable to any platform with 32-bit float's and int's.
|
||||||
|
|
||||||
|
Windows: There is an included PDSample.sln for MSVS version 7.
|
||||||
|
Unix: Type 'make' and hope for the best.
|
||||||
|
|
||||||
|
|
||||||
|
Usage
|
||||||
|
---
|
||||||
|
PDSample [-m] [-t] [-r <relax count=0>] [-M <multiplier=1>]
|
||||||
|
[-N <minMaxThrows=1000>] <method> <radius> <output>
|
||||||
|
|
||||||
|
Options
|
||||||
|
--
|
||||||
|
o -t
|
||||||
|
Uses tiled (toroidal) domain for supporting samplers. The resulting point
|
||||||
|
set will be suitable for tiling repeatedly in the x and y directions.
|
||||||
|
|
||||||
|
o -m
|
||||||
|
Maximize the resulting point set. For samplers which do not already produce
|
||||||
|
a maximal point set then this will use the Boundary sampling method to
|
||||||
|
ensure the resulting point set is maximal.
|
||||||
|
|
||||||
|
o -r <relax count>
|
||||||
|
Apply the specified number of relaxations to the resulting point set. This
|
||||||
|
requires that qvoronoi be in the path.
|
||||||
|
|
||||||
|
o -M <multiplier>
|
||||||
|
For DartThrowing and BestCandidate methods this determines the factor to
|
||||||
|
multiply the current number of points by to determine how many samples to
|
||||||
|
take before exiting (DartThrowing) or accepting the best candidate
|
||||||
|
(BestCandidate).
|
||||||
|
|
||||||
|
o -N <minMaxThrows>
|
||||||
|
This specifies a minimum number of samples that will be taken for the
|
||||||
|
DartThrowing sampler. See below.
|
||||||
|
|
||||||
|
|
||||||
|
Available Samplers (for method argument)
|
||||||
|
--
|
||||||
|
o DartThrowing
|
||||||
|
Standard dart throwing. On each iteration the DartThrowing sampler will
|
||||||
|
try min(N*multiplier,minMaxThrows) samples before termination. Note that
|
||||||
|
for regular dart throwing where simply a maximum number of throws is used
|
||||||
|
to determine the termination point, the multiplier should be set to 0.
|
||||||
|
|
||||||
|
o BestCandidate
|
||||||
|
Mitchell's Best Candidate algorithm. Uses the multiplier argument.
|
||||||
|
|
||||||
|
o Boundary
|
||||||
|
Dart throwing by maximizing boundaries.
|
||||||
|
|
||||||
|
o Pure
|
||||||
|
Dart throwing using scalloped sectors.
|
||||||
|
o LinearPure
|
||||||
|
Dart throwing using scalloped sectors but without sampling regions according
|
||||||
|
to their probability of being hit.
|
||||||
|
|
||||||
|
o Penrose
|
||||||
|
Ostromoukhov et al.'s sampling method using their quasisampler_prototype.h
|
||||||
|
|
||||||
|
o Uniform
|
||||||
|
Random point generation. The number of samples to take is calculated as
|
||||||
|
.75/radius^2 to approximately match the density of Poisson-disk sampling.
|
||||||
|
|
||||||
|
|
||||||
|
Output format
|
||||||
|
--
|
||||||
|
Point sets are output in a trivial binary format. The format is not intended
|
||||||
|
for distribution and does not encode the endianness of the generating platform.
|
||||||
|
|
||||||
|
The format matches the pseudo-C struct below:
|
||||||
|
struct {
|
||||||
|
int N; // number of points
|
||||||
|
float t; // generation time
|
||||||
|
float r; // radius used in generation
|
||||||
|
int isTiled; // flag for if the set is tileable
|
||||||
|
float points[N][2];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Acknowledgments
|
||||||
|
--
|
||||||
|
Thanks to Ares Lagae for comments on a preliminary release of the code,
|
||||||
|
Ostromoukhov et al. for making available their quasisampler implementation,
|
||||||
|
as well as Takuji Nishimura and Makoto Matsumoto for their Mersenne
|
||||||
|
Twister random number generator.
|
||||||
174
BlueNoise/RNG.cpp
Normal file
174
BlueNoise/RNG.cpp
Normal file
|
|
@ -0,0 +1,174 @@
|
||||||
|
/*
|
||||||
|
A C-program for MT19937, with initialization improved 2002/1/26.
|
||||||
|
Coded by Takuji Nishimura and Makoto Matsumoto.
|
||||||
|
Modified to be a C++ class by Daniel Dunbar.
|
||||||
|
|
||||||
|
Before using, initialize the state by using init_genrand(seed)
|
||||||
|
or init_by_array(init_key, key_length).
|
||||||
|
|
||||||
|
Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions
|
||||||
|
are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
2. 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.
|
||||||
|
|
||||||
|
3. The names of its contributors may not be used to endorse or promote
|
||||||
|
products derived from this software without specific prior written
|
||||||
|
permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
|
||||||
|
Any feedback is very welcome.
|
||||||
|
http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html
|
||||||
|
email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "RNG.h"
|
||||||
|
|
||||||
|
/* initializes mt[N] with a seed */
|
||||||
|
RNG::RNG(unsigned long s)
|
||||||
|
{
|
||||||
|
seed(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* initialize by an array with array-length */
|
||||||
|
/* init_key is the array for initializing keys */
|
||||||
|
/* key_length is its length */
|
||||||
|
/* slight change for C++, 2004/2/26 */
|
||||||
|
RNG::RNG(unsigned long init_key[], int key_length)
|
||||||
|
{
|
||||||
|
int i, j, k;
|
||||||
|
seed(19650218UL);
|
||||||
|
i=1; j=0;
|
||||||
|
k = (N>key_length ? N : key_length);
|
||||||
|
for (; k; k--) {
|
||||||
|
mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1664525UL))
|
||||||
|
+ init_key[j] + j; /* non linear */
|
||||||
|
mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */
|
||||||
|
i++; j++;
|
||||||
|
if (i>=N) { mt[0] = mt[N-1]; i=1; }
|
||||||
|
if (j>=key_length) j=0;
|
||||||
|
}
|
||||||
|
for (k=N-1; k; k--) {
|
||||||
|
mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1566083941UL))
|
||||||
|
- i; /* non linear */
|
||||||
|
mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */
|
||||||
|
i++;
|
||||||
|
if (i>=N) { mt[0] = mt[N-1]; i=1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
mt[0] = 0x80000000UL; /* MSB is 1; assuring non-zero initial array */
|
||||||
|
}
|
||||||
|
|
||||||
|
void RNG::seed(unsigned long s)
|
||||||
|
{
|
||||||
|
mt[0]= s & 0xffffffffUL;
|
||||||
|
for (mti=1; mti<N; mti++) {
|
||||||
|
mt[mti] =
|
||||||
|
(1812433253UL * (mt[mti-1] ^ (mt[mti-1] >> 30)) + mti);
|
||||||
|
/* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */
|
||||||
|
/* In the previous versions, MSBs of the seed affect */
|
||||||
|
/* only MSBs of the array mt[]. */
|
||||||
|
/* 2002/01/09 modified by Makoto Matsumoto */
|
||||||
|
mt[mti] &= 0xffffffffUL;
|
||||||
|
/* for >32 bit machines */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* generates a random number on [0,0xffffffff]-interval */
|
||||||
|
unsigned long RNG::getInt32()
|
||||||
|
{
|
||||||
|
unsigned long y;
|
||||||
|
static unsigned long mag01[2]={0x0UL, _MATRIX_A};
|
||||||
|
/* mag01[x] = x * _MATRIX_A for x=0,1 */
|
||||||
|
|
||||||
|
if (mti >= N) { /* generate N words at one time */
|
||||||
|
int kk;
|
||||||
|
|
||||||
|
for (kk=0;kk<N-_M;kk++) {
|
||||||
|
y = (mt[kk]&_UPPER_MASK)|(mt[kk+1]&_LOWER_MASK);
|
||||||
|
mt[kk] = mt[kk+_M] ^ (y >> 1) ^ mag01[y & 0x1UL];
|
||||||
|
}
|
||||||
|
for (;kk<N-1;kk++) {
|
||||||
|
y = (mt[kk]&_UPPER_MASK)|(mt[kk+1]&_LOWER_MASK);
|
||||||
|
mt[kk] = mt[kk+(_M-N)] ^ (y >> 1) ^ mag01[y & 0x1UL];
|
||||||
|
}
|
||||||
|
y = (mt[N-1]&_UPPER_MASK)|(mt[0]&_LOWER_MASK);
|
||||||
|
mt[N-1] = mt[_M-1] ^ (y >> 1) ^ mag01[y & 0x1UL];
|
||||||
|
|
||||||
|
mti = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
y = mt[mti++];
|
||||||
|
|
||||||
|
/* Tempering */
|
||||||
|
y ^= (y >> 11);
|
||||||
|
y ^= (y << 7) & 0x9d2c5680UL;
|
||||||
|
y ^= (y << 15) & 0xefc60000UL;
|
||||||
|
y ^= (y >> 18);
|
||||||
|
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* generates a random number on [0,0x7fffffff]-interval */
|
||||||
|
long RNG::getInt31()
|
||||||
|
{
|
||||||
|
return (long)(getInt32()>>1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* generates a random number on [0,1]-real-interval */
|
||||||
|
double RNG::getDoubleLR()
|
||||||
|
{
|
||||||
|
return getInt32()*(1.0/4294967295.0);
|
||||||
|
/* divided by 2^32-1 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* generates a random number on [0,1)-real-interval */
|
||||||
|
double RNG::getDoubleL()
|
||||||
|
{
|
||||||
|
return getInt32()*(1.0/4294967296.0);
|
||||||
|
/* divided by 2^32 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* generates a random number on (0,1)-real-interval */
|
||||||
|
double RNG::getDouble()
|
||||||
|
{
|
||||||
|
return (((double)getInt32()) + 0.5)*(1.0/4294967296.0);
|
||||||
|
/* divided by 2^32 */
|
||||||
|
}
|
||||||
|
|
||||||
|
float RNG::getFloatLR()
|
||||||
|
{
|
||||||
|
return getInt32()*(1.0f/4294967295.0f);
|
||||||
|
/* divided by 2^32-1 */
|
||||||
|
}
|
||||||
|
float RNG::getFloatL()
|
||||||
|
{
|
||||||
|
return getInt32()*(1.0f/4294967296.0f);
|
||||||
|
/* divided by 2^32 */
|
||||||
|
}
|
||||||
|
float RNG::getFloat()
|
||||||
|
{
|
||||||
|
return (getInt32() + 0.5f)*(1.0f/4294967296.0f);
|
||||||
|
/* divided by 2^32 */
|
||||||
|
}
|
||||||
|
|
||||||
37
BlueNoise/RNG.h
Normal file
37
BlueNoise/RNG.h
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
class RNG {
|
||||||
|
private:
|
||||||
|
/* Period parameters */
|
||||||
|
static const long N = 624;
|
||||||
|
static const long _M = 397;
|
||||||
|
static const unsigned long _MATRIX_A = 0x9908b0dfUL; /* constant vector a */
|
||||||
|
static const unsigned long _UPPER_MASK = 0x80000000UL; /* most significant w-r bits */
|
||||||
|
static const unsigned long _LOWER_MASK = 0x7fffffffUL; /* least significant r bits */
|
||||||
|
|
||||||
|
private:
|
||||||
|
unsigned long mt[N]; /* the array for the state vector */
|
||||||
|
int mti;
|
||||||
|
|
||||||
|
public:
|
||||||
|
RNG(unsigned long seed=5489UL);
|
||||||
|
RNG(unsigned long *init_key, int key_length);
|
||||||
|
|
||||||
|
void seed(unsigned long seed);
|
||||||
|
|
||||||
|
/* generates a random number on [0,0xffffffff]-interval */
|
||||||
|
unsigned long getInt32();
|
||||||
|
/* generates a random number on [0,0x7fffffff]-interval */
|
||||||
|
long getInt31();
|
||||||
|
/* generates a random number on [0,1]-real-interval */
|
||||||
|
double getDoubleLR();
|
||||||
|
float getFloatLR();
|
||||||
|
/* generates a random number on [0,1)-real-interval */
|
||||||
|
double getDoubleL();
|
||||||
|
float getFloatL();
|
||||||
|
/* generates a random number on (0,1)-real-interval */
|
||||||
|
double getDouble();
|
||||||
|
float getFloat();
|
||||||
|
};
|
||||||
139
BlueNoise/RangeList.cpp
Normal file
139
BlueNoise/RangeList.cpp
Normal file
|
|
@ -0,0 +1,139 @@
|
||||||
|
// $Id: RangeList.cpp,v 1.4 2006/01/24 03:22:14 zr Exp $
|
||||||
|
|
||||||
|
#define _USE_MATH_DEFINES
|
||||||
|
#include <math.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include "RangeList.h"
|
||||||
|
|
||||||
|
///
|
||||||
|
|
||||||
|
static const float kSmallestRange = .000001f;
|
||||||
|
|
||||||
|
RangeList::RangeList(float min, float max)
|
||||||
|
{
|
||||||
|
numRanges = 0;
|
||||||
|
rangesSize = 8;
|
||||||
|
ranges = new RangeEntry[rangesSize];
|
||||||
|
reset(min, max);
|
||||||
|
}
|
||||||
|
|
||||||
|
RangeList::~RangeList()
|
||||||
|
{
|
||||||
|
delete[] ranges;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RangeList::reset(float min, float max)
|
||||||
|
{
|
||||||
|
numRanges = 1;
|
||||||
|
ranges[0].min = min;
|
||||||
|
ranges[0].max = max;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RangeList::deleteRange(int pos)
|
||||||
|
{
|
||||||
|
if (pos<numRanges-1) {
|
||||||
|
memmove(&ranges[pos], &ranges[pos+1], sizeof(*ranges)*(numRanges-(pos+1)));
|
||||||
|
}
|
||||||
|
numRanges--;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RangeList::insertRange(int pos, float min, float max)
|
||||||
|
{
|
||||||
|
if (numRanges==rangesSize) {
|
||||||
|
RangeEntry *tmp = new RangeEntry[rangesSize];
|
||||||
|
memcpy(tmp, ranges, numRanges*sizeof(*tmp));
|
||||||
|
delete[] ranges;
|
||||||
|
ranges = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pos<numRanges) {
|
||||||
|
memmove(&ranges[pos+1], &ranges[pos], sizeof(*ranges)*(numRanges-pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
ranges[pos].min = min;
|
||||||
|
ranges[pos].max = max;
|
||||||
|
numRanges++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RangeList::subtract(float a, float b)
|
||||||
|
{
|
||||||
|
static const float twoPi = (float) (M_PI*2);
|
||||||
|
|
||||||
|
if (a>twoPi) {
|
||||||
|
subtract(a-twoPi, b-twoPi);
|
||||||
|
} else if (b<0) {
|
||||||
|
subtract(a+twoPi, b+twoPi);
|
||||||
|
} else if (a<0) {
|
||||||
|
subtract(0, b);
|
||||||
|
subtract(a+twoPi,twoPi);
|
||||||
|
} else if (b>twoPi) {
|
||||||
|
subtract(a, twoPi);
|
||||||
|
subtract(0, b-twoPi);
|
||||||
|
} else if (numRanges==0) {
|
||||||
|
;
|
||||||
|
} else {
|
||||||
|
int pos;
|
||||||
|
|
||||||
|
if (a<ranges[0].min) {
|
||||||
|
pos = -1;
|
||||||
|
} else {
|
||||||
|
int lo=0, mid=0, hi=numRanges;
|
||||||
|
|
||||||
|
while (lo<hi-1) {
|
||||||
|
mid = (lo+hi)>>1;
|
||||||
|
if (ranges[mid].min<a) {
|
||||||
|
lo = mid;
|
||||||
|
} else {
|
||||||
|
hi = mid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pos = lo;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pos==-1) {
|
||||||
|
pos = 0;
|
||||||
|
} else if (a<ranges[pos].max) {
|
||||||
|
float c = ranges[pos].min;
|
||||||
|
float d = ranges[pos].max;
|
||||||
|
if (a-c<kSmallestRange) {
|
||||||
|
if (b<d) {
|
||||||
|
ranges[pos].min = b;
|
||||||
|
} else {
|
||||||
|
deleteRange(pos);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ranges[pos].max = a;
|
||||||
|
if (b<d) {
|
||||||
|
insertRange(pos+1, b, d);
|
||||||
|
}
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (pos<numRanges-1 && b>ranges[pos+1].min) {
|
||||||
|
pos++;
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (pos<numRanges && b>ranges[pos].min) {
|
||||||
|
if (ranges[pos].max-b<kSmallestRange) {
|
||||||
|
deleteRange(pos);
|
||||||
|
} else {
|
||||||
|
ranges[pos].min = b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RangeList::print()
|
||||||
|
{
|
||||||
|
printf("[");
|
||||||
|
for (int i=0; i<numRanges; i++) {
|
||||||
|
printf("(%f,%f)%s", ranges[i].min, ranges[i].max, (i==numRanges-1)?"":", ");
|
||||||
|
}
|
||||||
|
printf("]\n");
|
||||||
|
}
|
||||||
27
BlueNoise/RangeList.h
Normal file
27
BlueNoise/RangeList.h
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
// $Id: RangeList.h,v 1.3 2006/01/12 16:39:19 zr Exp $
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
typedef struct _RangeEntry {
|
||||||
|
float min, max;
|
||||||
|
} RangeEntry;
|
||||||
|
|
||||||
|
class RangeList {
|
||||||
|
public:
|
||||||
|
RangeEntry *ranges;
|
||||||
|
int numRanges, rangesSize;
|
||||||
|
|
||||||
|
public:
|
||||||
|
RangeList(float min, float max);
|
||||||
|
~RangeList();
|
||||||
|
|
||||||
|
void reset(float min, float max);
|
||||||
|
|
||||||
|
void print();
|
||||||
|
|
||||||
|
void subtract(float min, float max);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void deleteRange(int pos);
|
||||||
|
void insertRange(int pos, float min, float max);
|
||||||
|
};
|
||||||
289
BlueNoise/ScallopedSector.cpp
Normal file
289
BlueNoise/ScallopedSector.cpp
Normal file
|
|
@ -0,0 +1,289 @@
|
||||||
|
#define _USE_MATH_DEFINES
|
||||||
|
#include <math.h>
|
||||||
|
#include <float.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include "BLUE_NOISE.h"
|
||||||
|
#include "ScallopedSector.h"
|
||||||
|
|
||||||
|
static const float kTwoPi = (float) (M_PI*2);
|
||||||
|
|
||||||
|
static float integralOfDistToCircle(float x, float d, float r, float k)
|
||||||
|
{
|
||||||
|
if (r<FLT_EPSILON)
|
||||||
|
return 0.0;
|
||||||
|
|
||||||
|
float sin_x = sin(x);
|
||||||
|
float d_sin_x = d*sin_x;
|
||||||
|
float y = sin_x*d/r;
|
||||||
|
if (y<-1) y = -1;
|
||||||
|
else if (y>1) y = 1;
|
||||||
|
|
||||||
|
float theta = asin(y);
|
||||||
|
|
||||||
|
return (r*(r*(x +
|
||||||
|
k*theta) +
|
||||||
|
k*cos(theta)*d_sin_x) +
|
||||||
|
d*cos(x)*d_sin_x)*.5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
ScallopedSector::ScallopedSector(Vec2 &_Pt, float _a1, float _a2, Vec2 &P1, float r1, float sign1, Vec2 &P2, float r2, float sign2)
|
||||||
|
{
|
||||||
|
Vec2 v1 = Vec2(P1.x - _Pt.x, P1.y - _Pt.y);
|
||||||
|
Vec2 v2 = Vec2(P2.x - _Pt.x, P2.y - _Pt.y);
|
||||||
|
|
||||||
|
P = _Pt;
|
||||||
|
a1 = _a1;
|
||||||
|
a2 = _a2;
|
||||||
|
|
||||||
|
arcs[0].P = P1;
|
||||||
|
arcs[0].r = r1;
|
||||||
|
arcs[0].sign = sign1;
|
||||||
|
arcs[0].d = sqrt(v1.x*v1.x + v1.y*v1.y);
|
||||||
|
arcs[0].rSqrd = arcs[0].r*arcs[0].r;
|
||||||
|
arcs[0].dSqrd = arcs[0].d*arcs[0].d;
|
||||||
|
arcs[0].theta = atan2(v1.y,v1.x);
|
||||||
|
arcs[0].integralAtStart = integralOfDistToCircle(a1 - arcs[0].theta, arcs[0].d, arcs[0].r, arcs[0].sign);
|
||||||
|
|
||||||
|
arcs[1].P = P2;
|
||||||
|
arcs[1].r = r2;
|
||||||
|
arcs[1].sign = sign2;
|
||||||
|
arcs[1].d = sqrt(v2.x*v2.x + v2.y*v2.y);
|
||||||
|
arcs[1].rSqrd = arcs[1].r*arcs[1].r;
|
||||||
|
arcs[1].dSqrd = arcs[1].d*arcs[1].d;
|
||||||
|
arcs[1].theta = atan2(v2.y,v2.x);
|
||||||
|
arcs[1].integralAtStart = integralOfDistToCircle(a1 - arcs[1].theta, arcs[1].d, arcs[1].r, arcs[1].sign);
|
||||||
|
|
||||||
|
area = calcAreaToAngle(a2);
|
||||||
|
}
|
||||||
|
|
||||||
|
float ScallopedSector::calcAreaToAngle(float angle)
|
||||||
|
{
|
||||||
|
float underInner = integralOfDistToCircle(angle - arcs[0].theta, arcs[0].d, arcs[0].r, arcs[0].sign) - arcs[0].integralAtStart;
|
||||||
|
float underOuter = integralOfDistToCircle(angle - arcs[1].theta, arcs[1].d, arcs[1].r, arcs[1].sign) - arcs[1].integralAtStart;
|
||||||
|
|
||||||
|
return underOuter-underInner;
|
||||||
|
}
|
||||||
|
|
||||||
|
float ScallopedSector::calcAngleForArea(float area, RNG &rng)
|
||||||
|
{
|
||||||
|
float lo = a1, hi = a2, cur = lo + (hi-lo)*rng.getFloat();
|
||||||
|
|
||||||
|
for (int i=0; i<10; i++) {
|
||||||
|
if (calcAreaToAngle(cur)<area) {
|
||||||
|
lo = cur;
|
||||||
|
cur = (cur + hi)*.5f;
|
||||||
|
} else {
|
||||||
|
hi = cur;
|
||||||
|
cur = (lo + cur)*.5f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cur;
|
||||||
|
}
|
||||||
|
|
||||||
|
float ScallopedSector::distToCurve(float angle, int index)
|
||||||
|
{
|
||||||
|
float alpha = angle - arcs[index].theta;
|
||||||
|
float sin_alpha = sin(alpha);
|
||||||
|
float t0 = arcs[index].rSqrd - arcs[index].dSqrd*sin_alpha*sin_alpha;
|
||||||
|
if (t0<0) {
|
||||||
|
return arcs[index].d*cos(alpha);
|
||||||
|
} else {
|
||||||
|
return arcs[index].d*cos(alpha) + arcs[index].sign*sqrt(t0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec2 ScallopedSector::sample(RNG &rng)
|
||||||
|
{
|
||||||
|
float angle = calcAngleForArea(area*rng.getFloatL(), rng);
|
||||||
|
float d1 = distToCurve(angle, 0);
|
||||||
|
float d2 = distToCurve(angle, 1);
|
||||||
|
float d = sqrt(d1*d1 + (d2*d2 - d1*d1)*rng.getFloat());
|
||||||
|
|
||||||
|
return Vec2(P.x + cos(angle)*d, P.y + sin(angle)*d);
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
|
||||||
|
float ScallopedSector::canonizeAngle(float angle)
|
||||||
|
{
|
||||||
|
float delta = fmod(angle - a1, kTwoPi);
|
||||||
|
if (delta<0) delta += kTwoPi;
|
||||||
|
return a1 + delta;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScallopedSector::distToCircle(float angle, Vec2 &C, float r, float *d1_out, float *d2_out)
|
||||||
|
{
|
||||||
|
Vec2 v(C.x - P.x, C.y - P.y);
|
||||||
|
float dSqrd = v.x*v.x + v.y*v.y;
|
||||||
|
float theta = atan2(v.y, v.x);
|
||||||
|
float alpha = angle - theta;
|
||||||
|
float sin_alpha = sin(alpha);
|
||||||
|
float xSqrd = r*r - dSqrd*sin_alpha*sin_alpha;
|
||||||
|
|
||||||
|
if (xSqrd<0) {
|
||||||
|
*d1_out = *d2_out = -10000000;
|
||||||
|
} else {
|
||||||
|
float a = sqrt(dSqrd)*cos(alpha);
|
||||||
|
float x = sqrt(xSqrd);
|
||||||
|
*d1_out = a-x;
|
||||||
|
*d2_out = a+x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScallopedSector::subtractDisk(Vec2 &C, float r, std::vector<ScallopedSector> *regions)
|
||||||
|
{
|
||||||
|
std::vector<float> angles;
|
||||||
|
|
||||||
|
Vec2 v(C.x - P.x, C.y-P.y);
|
||||||
|
float d = sqrt(v.x*v.x + v.y*v.y);
|
||||||
|
|
||||||
|
if (r<d) {
|
||||||
|
float theta = atan2(v.y, v.x);
|
||||||
|
float x = sqrt(d*d-r*r);
|
||||||
|
float angle, alpha = asin(r/d);
|
||||||
|
|
||||||
|
angle = canonizeAngle(theta+alpha);
|
||||||
|
if (a1<angle && angle<a2) {
|
||||||
|
if (distToCurve(angle,0)<x && x<distToCurve(angle,1))
|
||||||
|
angles.push_back(angle);
|
||||||
|
}
|
||||||
|
|
||||||
|
angle = canonizeAngle(theta-alpha);
|
||||||
|
if (a1<angle && angle<a2) {
|
||||||
|
if (distToCurve(angle,0)<x && x<distToCurve(angle,1))
|
||||||
|
angles.push_back(angle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int arcIndex=0; arcIndex<2; arcIndex++) {
|
||||||
|
Vec2 &C2 = arcs[arcIndex].P;
|
||||||
|
float R = arcs[arcIndex].r;
|
||||||
|
Vec2 v(C.x - C2.x, C.y - C2.y);
|
||||||
|
float d = sqrt(v.x*v.x + v.y*v.y);
|
||||||
|
|
||||||
|
if (d>FLT_EPSILON) {
|
||||||
|
float invD = 1.0f/d;
|
||||||
|
float x = (d*d - r*r + R*R)*invD*.5f;
|
||||||
|
float k = R*R - x*x;
|
||||||
|
|
||||||
|
if (k>0) {
|
||||||
|
float y = sqrt(k);
|
||||||
|
float vx = v.x*invD;
|
||||||
|
float vy = v.y*invD;
|
||||||
|
float vx_x = vx*x, vy_x = vy*x;
|
||||||
|
float vx_y = vx*y, vy_y = vy*y;
|
||||||
|
float angle;
|
||||||
|
|
||||||
|
angle = canonizeAngle(atan2(C2.y + vy_x + vx_y - P.y,
|
||||||
|
C2.x + vx_x - vy_y - P.x));
|
||||||
|
if (a1<angle && angle<a2) angles.push_back(angle);
|
||||||
|
|
||||||
|
angle = canonizeAngle(atan2(C2.y + vy_x - vx_y - P.y,
|
||||||
|
C2.x + vx_x + vy_y - P.x));
|
||||||
|
if (a1<angle && angle<a2) angles.push_back(angle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sort(angles.begin(), angles.end());
|
||||||
|
angles.insert(angles.begin(), a1);
|
||||||
|
angles.push_back(a2);
|
||||||
|
|
||||||
|
for (unsigned int i=1; i<angles.size(); i++) {
|
||||||
|
float a1 = angles[i-1], a2 = angles[i];
|
||||||
|
float midA = (a1+a2)*.5f;
|
||||||
|
float inner = distToCurve(midA,0);
|
||||||
|
float outer = distToCurve(midA,1);
|
||||||
|
float d1, d2;
|
||||||
|
|
||||||
|
distToCircle(midA, C, r, &d1, &d2); // d1<=d2
|
||||||
|
|
||||||
|
if (d2<inner || d1>outer) {
|
||||||
|
regions->push_back(ScallopedSector(P, a1, a2, arcs[0].P, arcs[0].r, arcs[0].sign, arcs[1].P, arcs[1].r, arcs[1].sign));
|
||||||
|
} else {
|
||||||
|
if (inner<d1) {
|
||||||
|
regions->push_back(ScallopedSector(P, a1, a2, arcs[0].P, arcs[0].r, arcs[0].sign, C, r, -1));
|
||||||
|
}
|
||||||
|
if (d2<outer) {
|
||||||
|
regions->push_back(ScallopedSector(P, a1, a2, C, r, 1, arcs[1].P, arcs[1].r, arcs[1].sign));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
|
||||||
|
ScallopedRegion::ScallopedRegion(Vec2 &P, float r1, float r2, float _minArea) :
|
||||||
|
minArea(_minArea)
|
||||||
|
{
|
||||||
|
regions = new std::vector<ScallopedSector>;
|
||||||
|
regions->push_back(ScallopedSector(P, 0, kTwoPi, P, r1, 1, P, r2, 1));
|
||||||
|
area = (*regions)[0].area;
|
||||||
|
}
|
||||||
|
|
||||||
|
ScallopedRegion::~ScallopedRegion()
|
||||||
|
{
|
||||||
|
delete regions;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScallopedRegion::subtractDisk(Vec2 C, float r)
|
||||||
|
{
|
||||||
|
std::vector<ScallopedSector> *newRegions = new std::vector<ScallopedSector>;
|
||||||
|
|
||||||
|
area = 0;
|
||||||
|
for (unsigned int i=0; i<regions->size(); i++) {
|
||||||
|
ScallopedSector &ss = (*regions)[i];
|
||||||
|
std::vector<ScallopedSector> *tmp = new std::vector<ScallopedSector>;
|
||||||
|
|
||||||
|
ss.subtractDisk(C, r, tmp);
|
||||||
|
|
||||||
|
for (unsigned int j=0; j<tmp->size(); j++) {
|
||||||
|
ScallopedSector &nss = (*tmp)[j];
|
||||||
|
|
||||||
|
if (nss.area>minArea) {
|
||||||
|
area += nss.area;
|
||||||
|
|
||||||
|
if (newRegions->size()) {
|
||||||
|
ScallopedSector &last = (*newRegions)[newRegions->size()-1];
|
||||||
|
if (last.a2==nss.a1 && (last.arcs[0].P==nss.arcs[0].P && last.arcs[0].r==nss.arcs[0].r && last.arcs[0].sign==nss.arcs[0].sign) &&
|
||||||
|
(last.arcs[1].P==nss.arcs[1].P && last.arcs[1].r==nss.arcs[1].r && last.arcs[1].sign==nss.arcs[1].sign)) {
|
||||||
|
last.a2 = nss.a2;
|
||||||
|
last.area = last.calcAreaToAngle(last.a2);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
newRegions->push_back(nss);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delete tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete regions;
|
||||||
|
regions = newRegions;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec2 ScallopedRegion::sample(RNG &rng)
|
||||||
|
{
|
||||||
|
if (!regions->size()) {
|
||||||
|
printf("Fatal error, sampled from empty region.");
|
||||||
|
exit(1);
|
||||||
|
return Vec2(0,0);
|
||||||
|
} else {
|
||||||
|
float a = area*rng.getFloatL();
|
||||||
|
ScallopedSector &ss = (*regions)[0];
|
||||||
|
|
||||||
|
for (unsigned int i=0; i<regions->size(); i++) {
|
||||||
|
ss = (*regions)[i];
|
||||||
|
if (a<ss.area)
|
||||||
|
break;
|
||||||
|
a -= ss.area;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ss.sample(rng);
|
||||||
|
}
|
||||||
|
}
|
||||||
51
BlueNoise/ScallopedSector.h
Normal file
51
BlueNoise/ScallopedSector.h
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
// $Id: ScallopedSector.h,v 1.3 2006/07/06 23:30:24 zr Exp $
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
Vec2 P;
|
||||||
|
float r, sign, d, theta, integralAtStart;
|
||||||
|
float rSqrd, dSqrd;
|
||||||
|
} ArcData;
|
||||||
|
|
||||||
|
class ScallopedSector
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Vec2 P;
|
||||||
|
float a1, a2, area;
|
||||||
|
|
||||||
|
ArcData arcs[2];
|
||||||
|
|
||||||
|
public:
|
||||||
|
ScallopedSector(Vec2 &_Pt, float _a1, float _a2, Vec2 &P1, float r1, float sign1, Vec2 &P2, float r2, float sign2);
|
||||||
|
|
||||||
|
float calcAreaToAngle(float angle);
|
||||||
|
float calcAngleForArea(float area, RNG &rng);
|
||||||
|
Vec2 sample(RNG &rng);
|
||||||
|
|
||||||
|
float distToCurve(float angle, int index);
|
||||||
|
|
||||||
|
void subtractDisk(Vec2 &C, float r, std::vector<ScallopedSector> *regions);
|
||||||
|
|
||||||
|
private:
|
||||||
|
float canonizeAngle(float angle);
|
||||||
|
|
||||||
|
void distToCircle(float angle, Vec2 &C, float r, float *d1_out, float *d2_out);
|
||||||
|
};
|
||||||
|
|
||||||
|
class ScallopedRegion
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
std::vector<ScallopedSector> *regions;
|
||||||
|
float minArea;
|
||||||
|
float area;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ScallopedRegion(Vec2 &P, float r1, float r2, float minArea=.00000001);
|
||||||
|
~ScallopedRegion();
|
||||||
|
|
||||||
|
bool isEmpty() { return regions->size()==0; }
|
||||||
|
void subtractDisk(Vec2 C, float r);
|
||||||
|
|
||||||
|
Vec2 sample(RNG &rng);
|
||||||
|
};
|
||||||
313
BlueNoise/WeightedDiscretePDF.cpp
Normal file
313
BlueNoise/WeightedDiscretePDF.cpp
Normal file
|
|
@ -0,0 +1,313 @@
|
||||||
|
// $Id: WeightedDiscretePDF.cpp,v 1.4 2006/07/07 05:54:31 zr Exp $
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
#include "WeightedDiscretePDF.h"
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
WDPDF_Node<T>::WDPDF_Node(T key_, float weight_, WDPDF_Node<T> *parent_)
|
||||||
|
{
|
||||||
|
m_mark = false;
|
||||||
|
|
||||||
|
key = key_;
|
||||||
|
weight = weight_;
|
||||||
|
sumWeights = 0;
|
||||||
|
left = right = 0;
|
||||||
|
parent = parent_;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
WDPDF_Node<T>::~WDPDF_Node()
|
||||||
|
{
|
||||||
|
if (left) delete left;
|
||||||
|
if (right) delete right;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
WeightedDiscretePDF<T>::WeightedDiscretePDF()
|
||||||
|
{
|
||||||
|
m_root = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
WeightedDiscretePDF<T>::~WeightedDiscretePDF()
|
||||||
|
{
|
||||||
|
if (m_root) delete m_root;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void WeightedDiscretePDF<T>::insert(T item, float weight)
|
||||||
|
{
|
||||||
|
WDPDF_Node<T> *p=0, *n=m_root;
|
||||||
|
|
||||||
|
while (n) {
|
||||||
|
if (n->leftIsRed() && n->rightIsRed())
|
||||||
|
split(n);
|
||||||
|
|
||||||
|
p = n;
|
||||||
|
if (n->key==item) {
|
||||||
|
throw std::domain_error("insert: argument(item) already in tree");
|
||||||
|
} else {
|
||||||
|
n = (item<n->key)?n->left:n->right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
n = new WDPDF_Node<T>(item, weight, p);
|
||||||
|
|
||||||
|
if (!p) {
|
||||||
|
m_root = n;
|
||||||
|
} else {
|
||||||
|
if (item<p->key) {
|
||||||
|
p->left = n;
|
||||||
|
} else {
|
||||||
|
p->right = n;
|
||||||
|
}
|
||||||
|
|
||||||
|
split(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
propogateSumsUp(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void WeightedDiscretePDF<T>::remove(T item)
|
||||||
|
{
|
||||||
|
WDPDF_Node<T> **np = lookup(item, 0);
|
||||||
|
WDPDF_Node<T> *child, *n = *np;
|
||||||
|
|
||||||
|
if (!n) {
|
||||||
|
throw std::domain_error("remove: argument(item) not in tree");
|
||||||
|
} else {
|
||||||
|
if (n->left) {
|
||||||
|
WDPDF_Node<T> **leftMaxp = &n->left;
|
||||||
|
|
||||||
|
while ((*leftMaxp)->right)
|
||||||
|
leftMaxp = &(*leftMaxp)->right;
|
||||||
|
|
||||||
|
n->key = (*leftMaxp)->key;
|
||||||
|
n->weight = (*leftMaxp)->weight;
|
||||||
|
|
||||||
|
np = leftMaxp;
|
||||||
|
n = *np;
|
||||||
|
}
|
||||||
|
|
||||||
|
// node now has at most one child
|
||||||
|
|
||||||
|
child = n->left?n->left:n->right;
|
||||||
|
*np = child;
|
||||||
|
|
||||||
|
if (child) {
|
||||||
|
child->parent = n->parent;
|
||||||
|
|
||||||
|
if (n->isBlack()) {
|
||||||
|
lengthen(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
propogateSumsUp(n->parent);
|
||||||
|
|
||||||
|
n->left = n->right = 0;
|
||||||
|
delete n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void WeightedDiscretePDF<T>::update(T item, float weight)
|
||||||
|
{
|
||||||
|
WDPDF_Node<T> *n = *lookup(item, 0);
|
||||||
|
|
||||||
|
if (!n) {
|
||||||
|
throw std::domain_error("update: argument(item) not in tree");
|
||||||
|
} else {
|
||||||
|
float delta = weight - n->weight;
|
||||||
|
n->weight = weight;
|
||||||
|
|
||||||
|
for (; n; n=n->parent) {
|
||||||
|
n->sumWeights += delta;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
T WeightedDiscretePDF<T>::choose(float p)
|
||||||
|
{
|
||||||
|
if (p<0.0 || p>=1.0) {
|
||||||
|
throw std::domain_error("choose: argument(p) outside valid range");
|
||||||
|
} else if (!m_root) {
|
||||||
|
throw std::logic_error("choose: choose() called on empty tree");
|
||||||
|
} else {
|
||||||
|
float w = m_root->sumWeights * p;
|
||||||
|
WDPDF_Node<T> *n = m_root;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
if (n->left) {
|
||||||
|
if (w<n->left->sumWeights) {
|
||||||
|
n = n->left;
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
w -= n->left->sumWeights;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (w<n->weight || !n->right) {
|
||||||
|
break; // !n->right condition shouldn't be necessary, just sanity check
|
||||||
|
}
|
||||||
|
w -= n->weight;
|
||||||
|
n = n->right;
|
||||||
|
}
|
||||||
|
|
||||||
|
return n->key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
bool WeightedDiscretePDF<T>::inTree(T item)
|
||||||
|
{
|
||||||
|
WDPDF_Node<T> *n = *lookup(item, 0);
|
||||||
|
|
||||||
|
return !!n;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
WDPDF_Node<T> **WeightedDiscretePDF<T>::lookup(T item, WDPDF_Node<T> **parent_out)
|
||||||
|
{
|
||||||
|
WDPDF_Node<T> *n, *p=0, **np=&m_root;
|
||||||
|
|
||||||
|
while ((n = *np)) {
|
||||||
|
if (n->key==item) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
p = n;
|
||||||
|
if (item<n->key) {
|
||||||
|
np = &n->left;
|
||||||
|
} else {
|
||||||
|
np = &n->right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parent_out)
|
||||||
|
*parent_out = p;
|
||||||
|
return np;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void WeightedDiscretePDF<T>::split(WDPDF_Node<T> *n)
|
||||||
|
{
|
||||||
|
if (n->left) n->left->markBlack();
|
||||||
|
if (n->right) n->right->markBlack();
|
||||||
|
|
||||||
|
if (n->parent) {
|
||||||
|
WDPDF_Node<T> *p = n->parent;
|
||||||
|
|
||||||
|
n->markRed();
|
||||||
|
|
||||||
|
if (p->isRed()) {
|
||||||
|
p->parent->markRed();
|
||||||
|
|
||||||
|
// not same direction
|
||||||
|
if (!( (n==p->left && p==p->parent->left) ||
|
||||||
|
(n==p->right && p==p->parent->right))) {
|
||||||
|
rotate(n);
|
||||||
|
p = n;
|
||||||
|
}
|
||||||
|
|
||||||
|
rotate(p);
|
||||||
|
p->markBlack();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void WeightedDiscretePDF<T>::rotate(WDPDF_Node<T> *n)
|
||||||
|
{
|
||||||
|
WDPDF_Node<T> *p=n->parent, *pp=p->parent;
|
||||||
|
|
||||||
|
n->parent = pp;
|
||||||
|
p->parent = n;
|
||||||
|
|
||||||
|
if (n==p->left) {
|
||||||
|
p->left = n->right;
|
||||||
|
n->right = p;
|
||||||
|
if (p->left) p->left->parent = p;
|
||||||
|
} else {
|
||||||
|
p->right = n->left;
|
||||||
|
n->left = p;
|
||||||
|
if (p->right) p->right->parent = p;
|
||||||
|
}
|
||||||
|
|
||||||
|
n->setSum();
|
||||||
|
p->setSum();
|
||||||
|
|
||||||
|
if (!pp) {
|
||||||
|
m_root = n;
|
||||||
|
} else {
|
||||||
|
if (p==pp->left) {
|
||||||
|
pp->left = n;
|
||||||
|
} else {
|
||||||
|
pp->right = n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void WeightedDiscretePDF<T>::lengthen(WDPDF_Node<T> *n)
|
||||||
|
{
|
||||||
|
if (n->isRed()) {
|
||||||
|
n->markBlack();
|
||||||
|
} else if (n->parent) {
|
||||||
|
WDPDF_Node<T> *sibling = n->sibling();
|
||||||
|
|
||||||
|
if (sibling && sibling->isRed()) {
|
||||||
|
n->parent->markRed();
|
||||||
|
sibling->markBlack();
|
||||||
|
|
||||||
|
rotate(sibling); // node sibling is now old sibling child, must be black
|
||||||
|
sibling = n->sibling();
|
||||||
|
}
|
||||||
|
|
||||||
|
// sibling is black
|
||||||
|
|
||||||
|
if (!sibling) {
|
||||||
|
lengthen(n->parent);
|
||||||
|
} else if (sibling->leftIsBlack() && sibling->rightIsBlack()) {
|
||||||
|
if (n->parent->isBlack()) {
|
||||||
|
sibling->markRed();
|
||||||
|
lengthen(n->parent);
|
||||||
|
} else {
|
||||||
|
sibling->markRed();
|
||||||
|
n->parent->markBlack();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (n==n->parent->left && sibling->rightIsBlack()) {
|
||||||
|
rotate(sibling->left); // sibling->left must be red
|
||||||
|
sibling->markRed();
|
||||||
|
sibling->parent->markBlack();
|
||||||
|
sibling = sibling->parent;
|
||||||
|
} else if (n==n->parent->right && sibling->leftIsBlack()) {
|
||||||
|
rotate(sibling->right); // sibling->right must be red
|
||||||
|
sibling->markRed();
|
||||||
|
sibling->parent->markBlack();
|
||||||
|
sibling = sibling->parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
// sibling is black, and sibling's far child is red
|
||||||
|
|
||||||
|
rotate(sibling);
|
||||||
|
if (n->parent->isRed()) sibling->markRed();
|
||||||
|
sibling->left->markBlack();
|
||||||
|
sibling->right->markBlack();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void WeightedDiscretePDF<T>::propogateSumsUp(WDPDF_Node<T> *n)
|
||||||
|
{
|
||||||
|
for (; n; n=n->parent)
|
||||||
|
n->setSum();
|
||||||
|
}
|
||||||
57
BlueNoise/WeightedDiscretePDF.h
Normal file
57
BlueNoise/WeightedDiscretePDF.h
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
// $Id: WeightedDiscretePDF.h,v 1.4 2006/07/07 05:54:31 zr Exp $
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
class WDPDF_Node
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
bool m_mark;
|
||||||
|
|
||||||
|
public:
|
||||||
|
WDPDF_Node<T> *parent, *left, *right;
|
||||||
|
T key;
|
||||||
|
float weight, sumWeights;
|
||||||
|
|
||||||
|
public:
|
||||||
|
WDPDF_Node(T key_, float weight_, WDPDF_Node<T> *parent_);
|
||||||
|
~WDPDF_Node();
|
||||||
|
|
||||||
|
WDPDF_Node<T> *sibling() { return this==parent->left?parent->right:parent->left; }
|
||||||
|
|
||||||
|
void markRed() { m_mark = true; }
|
||||||
|
void markBlack() { m_mark = false; }
|
||||||
|
bool isRed() { return m_mark; }
|
||||||
|
bool isBlack() { return !m_mark; }
|
||||||
|
bool leftIsBlack() { return !left || left->isBlack(); }
|
||||||
|
bool rightIsBlack() { return !right || right->isBlack(); }
|
||||||
|
bool leftIsRed() { return !leftIsBlack(); }
|
||||||
|
bool rightIsRed() { return !rightIsBlack(); }
|
||||||
|
void setSum() { sumWeights = weight + (left?left->sumWeights:0) + (right?right->sumWeights:0); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
class WeightedDiscretePDF
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
WDPDF_Node<T> *m_root;
|
||||||
|
|
||||||
|
public:
|
||||||
|
WeightedDiscretePDF();
|
||||||
|
~WeightedDiscretePDF();
|
||||||
|
|
||||||
|
void insert(T item, float weight);
|
||||||
|
void update(T item, float newWeight);
|
||||||
|
void remove(T item);
|
||||||
|
bool inTree(T item);
|
||||||
|
|
||||||
|
/* pick a tree element according to its
|
||||||
|
* weight. p should be in [0,1).
|
||||||
|
*/
|
||||||
|
T choose(float p);
|
||||||
|
|
||||||
|
private:
|
||||||
|
WDPDF_Node<T> **lookup(T item, WDPDF_Node<T> **parent_out);
|
||||||
|
void split(WDPDF_Node<T> *node);
|
||||||
|
void rotate(WDPDF_Node<T> *node);
|
||||||
|
void lengthen(WDPDF_Node<T> *node);
|
||||||
|
void propogateSumsUp(WDPDF_Node<T> *n);
|
||||||
|
};
|
||||||
225
CELL.cpp
Normal file
225
CELL.cpp
Normal file
|
|
@ -0,0 +1,225 @@
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// File : CELL.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 "CELL.h"
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// Construction/Destruction
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// normal cell constructor
|
||||||
|
CELL::CELL(float north, float east, float south, float west, CELL* parent, int depth) :
|
||||||
|
parent(parent), depth(depth), index(-1), candidate(false),
|
||||||
|
boundary(false), potential(0.0f), state(EMPTY)
|
||||||
|
{
|
||||||
|
for (int x = 0; x < 4; x++)
|
||||||
|
children[x] = NULL;
|
||||||
|
for (int x = 0; x < 8; x++)
|
||||||
|
neighbors[x] = NULL;
|
||||||
|
|
||||||
|
bounds[0] = north; bounds[1] = east; bounds[2] = south; bounds[3] = west;
|
||||||
|
|
||||||
|
center[0] = (bounds[1] + bounds[3]) * 0.5f;
|
||||||
|
center[1] = (bounds[0] + bounds[2]) * 0.5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ghost cell constructor
|
||||||
|
CELL::CELL(int depth) : parent(NULL), depth(depth), index(-1), candidate(false),
|
||||||
|
boundary(true), potential(0.0f), state(EMPTY)
|
||||||
|
{
|
||||||
|
for (int x = 0; x < 4; x++)
|
||||||
|
children[x] = NULL;
|
||||||
|
for (int x = 0; x < 8; x++)
|
||||||
|
neighbors[x] = NULL;
|
||||||
|
|
||||||
|
bounds[0] = 0.0f; bounds[1] = 0.0f; bounds[2] = 0.0f; bounds[3] = 0.0f;
|
||||||
|
center[0] = 0.0f; center[1] = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
CELL::~CELL() {
|
||||||
|
int x;
|
||||||
|
|
||||||
|
for (x = 0; x < 4; x++)
|
||||||
|
if (children[x] != NULL)
|
||||||
|
{
|
||||||
|
delete children[x];
|
||||||
|
children[x] = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// refine current cell
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
void CELL::refine() {
|
||||||
|
if (children[0] != NULL) return;
|
||||||
|
float center[] = {(bounds[0] + bounds[2]) * 0.5f, (bounds[1] + bounds[3]) * 0.5f};
|
||||||
|
|
||||||
|
children[0] = new CELL(bounds[0], center[1], center[0], bounds[3], this, depth + 1);
|
||||||
|
children[1] = new CELL(bounds[0], bounds[1], center[0], center[1], this, depth + 1);
|
||||||
|
children[2] = new CELL(center[0], bounds[1], bounds[2], center[1], this, depth + 1);
|
||||||
|
children[3] = new CELL(center[0], center[1], bounds[2], bounds[3], this, depth + 1);
|
||||||
|
|
||||||
|
children[0]->potential = potential;
|
||||||
|
children[1]->potential = potential;
|
||||||
|
children[2]->potential = potential;
|
||||||
|
children[3]->potential = potential;
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// return north neighbor to current cell
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
CELL* CELL::northNeighbor()
|
||||||
|
{
|
||||||
|
// if it is the root
|
||||||
|
if (this->parent == NULL) return NULL;
|
||||||
|
|
||||||
|
// if it is the southern child of the parent
|
||||||
|
if (parent->children[3] == this) return parent->children[0];
|
||||||
|
if (parent->children[2] == this) return parent->children[1];
|
||||||
|
|
||||||
|
// else look up higher
|
||||||
|
CELL* mu = parent->northNeighbor();
|
||||||
|
|
||||||
|
// if there are no more children to look at,
|
||||||
|
// this is the answer
|
||||||
|
if (mu == NULL || mu->children[0] == NULL) return mu;
|
||||||
|
// if it is the NW child of the parent
|
||||||
|
else if (parent->children[0] == this) return mu->children[3];
|
||||||
|
// if it is the NE child of the parent
|
||||||
|
else return mu->children[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// return north neighbor to current cell
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
CELL* CELL::southNeighbor()
|
||||||
|
{
|
||||||
|
// if it is the root
|
||||||
|
if (this->parent == NULL) return NULL;
|
||||||
|
|
||||||
|
// if it is the northern child of the parent
|
||||||
|
if (parent->children[0] == this) return parent->children[3];
|
||||||
|
if (parent->children[1] == this) return parent->children[2];
|
||||||
|
|
||||||
|
// else look up higher
|
||||||
|
CELL* mu = parent->southNeighbor();
|
||||||
|
|
||||||
|
// if there are no more children to look at,
|
||||||
|
// this is the answer
|
||||||
|
if (mu == NULL || mu->children[0] == NULL) return mu;
|
||||||
|
// if it is the SW child of the parent
|
||||||
|
else if (parent->children[3] == this) return mu->children[0];
|
||||||
|
// if it is the SE child of the parent
|
||||||
|
else return mu->children[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// return north neighbor to current cell
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
CELL* CELL::westNeighbor()
|
||||||
|
{
|
||||||
|
// if it is the root
|
||||||
|
if (this->parent == NULL) return NULL;
|
||||||
|
|
||||||
|
// if it is the eastern child of the parent
|
||||||
|
if (parent->children[1] == this) return parent->children[0];
|
||||||
|
if (parent->children[2] == this) return parent->children[3];
|
||||||
|
|
||||||
|
// else look up higher
|
||||||
|
CELL* mu = parent->westNeighbor();
|
||||||
|
|
||||||
|
// if there are no more children to look at,
|
||||||
|
// this is the answer
|
||||||
|
if (mu == NULL || mu->children[0] == NULL) return mu;
|
||||||
|
// if it is the NW child of the parent
|
||||||
|
else if (parent->children[0] == this) return mu->children[1];
|
||||||
|
// if it is the SW child of the parent
|
||||||
|
else return mu->children[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// return north neighbor to current cell
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
CELL* CELL::eastNeighbor()
|
||||||
|
{
|
||||||
|
// if it is the root
|
||||||
|
if (this->parent == NULL) return NULL;
|
||||||
|
|
||||||
|
// if it is the western child of the parent
|
||||||
|
if (parent->children[0] == this) return parent->children[1];
|
||||||
|
if (parent->children[3] == this) return parent->children[2];
|
||||||
|
|
||||||
|
// else look up higher
|
||||||
|
CELL* mu = parent->eastNeighbor();
|
||||||
|
|
||||||
|
// if there are no more children to look at,
|
||||||
|
// this is the answer
|
||||||
|
if (mu == NULL || mu->children[0] == NULL) return mu;
|
||||||
|
// if it is the NE child of the parent
|
||||||
|
else if (parent->children[1] == this) return mu->children[0];
|
||||||
|
// if it is the SE child of the parent
|
||||||
|
else return mu->children[3];
|
||||||
|
}
|
||||||
|
|
||||||
194
CELL.h
Normal file
194
CELL.h
Normal file
|
|
@ -0,0 +1,194 @@
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// File : CELL.h
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef CELL_H
|
||||||
|
#define CELL_H
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
/// \enum Possible states of the cell in the DBM simulation
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
enum CELL_STATE {EMPTY, NEGATIVE, POSITIVE, REPULSOR, ATTRACTOR};
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
/// \brief Basic cell data structure of the quadtree
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
class CELL
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
//! normal cell constructor
|
||||||
|
CELL(float north,
|
||||||
|
float east,
|
||||||
|
float south,
|
||||||
|
float west,
|
||||||
|
CELL* parent = NULL,
|
||||||
|
int depth = 0);
|
||||||
|
|
||||||
|
//! ghost cell constructor
|
||||||
|
CELL(int depth = 0);
|
||||||
|
|
||||||
|
//! destructor
|
||||||
|
~CELL();
|
||||||
|
|
||||||
|
//! The children of the node in the quadtree
|
||||||
|
/*!
|
||||||
|
Winding order of children is:
|
||||||
|
|
||||||
|
\verbatim
|
||||||
|
_________
|
||||||
|
| | |
|
||||||
|
| 0 | 1 |
|
||||||
|
|___|___|
|
||||||
|
| | |
|
||||||
|
| 3 | 2 |
|
||||||
|
|___|___|
|
||||||
|
\endverbatim */
|
||||||
|
CELL* children[4];
|
||||||
|
|
||||||
|
//! The physical bounds of the current grid cell
|
||||||
|
/*!
|
||||||
|
Winding order of bounds is:
|
||||||
|
|
||||||
|
\verbatim
|
||||||
|
0 - north
|
||||||
|
1 - east
|
||||||
|
2 - south
|
||||||
|
3 - west
|
||||||
|
\endverbatim */
|
||||||
|
float bounds[4];
|
||||||
|
|
||||||
|
//! The neighbors in the balanced quadtree
|
||||||
|
/*!
|
||||||
|
winding order of the neighbors is:
|
||||||
|
|
||||||
|
\verbatim
|
||||||
|
| 0 | 1 |
|
||||||
|
____|____|____|_____
|
||||||
|
| |
|
||||||
|
7 | | 2
|
||||||
|
____| |_____
|
||||||
|
| |
|
||||||
|
6 | | 3
|
||||||
|
____|_________|_____
|
||||||
|
| | |
|
||||||
|
| 5 | 4 |
|
||||||
|
\endverbatim
|
||||||
|
|
||||||
|
Neighbors 0,2,4,6 should always exist. Depending on
|
||||||
|
if the neighbor is on a lower refinement level,
|
||||||
|
neighbors 1,3,5,7 may or may not exist. If they are not
|
||||||
|
present, the pointer value should ne NULL. */
|
||||||
|
CELL* neighbors[8];
|
||||||
|
|
||||||
|
//! Poisson stencil coefficients
|
||||||
|
/*!
|
||||||
|
winding order of the stencil coefficients:
|
||||||
|
|
||||||
|
\verbatim
|
||||||
|
| 0 | 1 |
|
||||||
|
____|____|____|_____
|
||||||
|
| |
|
||||||
|
7 | | 2
|
||||||
|
____| 8 |_____
|
||||||
|
| |
|
||||||
|
6 | | 3
|
||||||
|
____|_________|_____
|
||||||
|
| | |
|
||||||
|
| 5 | 4 |
|
||||||
|
\endverbatim
|
||||||
|
Stencils 0,2,4,6 should always exist. Depending on
|
||||||
|
if the neighbor is on a lower refinement level,
|
||||||
|
stencils 1,3,5,7 may or may not exist. If they are not
|
||||||
|
present, the pointer value should ne NULL. */
|
||||||
|
float stencil[9];
|
||||||
|
|
||||||
|
float center[2]; ///< center of the cell
|
||||||
|
int depth; ///< current tree depth
|
||||||
|
bool candidate; ///< already a member of candidate list?
|
||||||
|
|
||||||
|
CELL* parent; ///< parent node in the quadtree
|
||||||
|
CELL_STATE state; ///< DBM state of the cell
|
||||||
|
|
||||||
|
void refine(); ///< subdivide the cell
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
// solver-related variables
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
bool boundary; ///< boundary node to include in the solver?
|
||||||
|
float potential; ///< current electric potential
|
||||||
|
float b; ///< rhs of the linear system
|
||||||
|
float residual; ///< residual in the linear solver
|
||||||
|
int index; ///< lexicographic index for the solver
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
// neighbor lookups
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
CELL* northNeighbor(); ///< lookup northern neighbor
|
||||||
|
CELL* southNeighbor(); ///< lookup southern neighbor
|
||||||
|
CELL* westNeighbor(); ///< lookup western neighbor
|
||||||
|
CELL* eastNeighbor(); ///< lookup eastern neighbor
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
309
CG_SOLVER.cpp
Normal file
309
CG_SOLVER.cpp
Normal file
|
|
@ -0,0 +1,309 @@
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// File : CG_SOLVER.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 "CG_SOLVER.h"
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// Construction/Destruction
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
CG_SOLVER::CG_SOLVER(int maxDepth, int iterations, int digits) :
|
||||||
|
_iterations(iterations),
|
||||||
|
_arraySize(0), _listSize(0), _digits(digits),
|
||||||
|
_direction(NULL), _residual(NULL), _q(NULL), _potential(NULL)
|
||||||
|
{
|
||||||
|
// compute the physical size of various grid cells
|
||||||
|
_dx = new float[maxDepth + 1];
|
||||||
|
_dx[0] = 1.0f;
|
||||||
|
for (int x = 1; x <= maxDepth; x++)
|
||||||
|
_dx[x] = _dx[x - 1] * 0.5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
CG_SOLVER::~CG_SOLVER()
|
||||||
|
{
|
||||||
|
if (_direction) delete[] _direction;
|
||||||
|
if (_residual) delete[] _residual;
|
||||||
|
if (_potential) delete[] _potential;
|
||||||
|
if (_q) delete[] _q;
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// reallocate scratch arrays if necessary
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
void CG_SOLVER::reallocate()
|
||||||
|
{
|
||||||
|
// if we have enough size already, return
|
||||||
|
if (_arraySize >= _listSize) return;
|
||||||
|
|
||||||
|
// made sure it SSE aligns okay
|
||||||
|
_arraySize = _listSize * 2;
|
||||||
|
if (_arraySize % 4)
|
||||||
|
_arraySize += 4 - _arraySize % 4;
|
||||||
|
|
||||||
|
// delete the old ones
|
||||||
|
if (_direction) delete[] _direction;
|
||||||
|
if (_residual) delete[] _residual;
|
||||||
|
if (_q) delete[] _q;
|
||||||
|
|
||||||
|
// allocate the new ones
|
||||||
|
_direction = new float[_arraySize];
|
||||||
|
_residual = new float[_arraySize];
|
||||||
|
_q = new float[_arraySize];
|
||||||
|
|
||||||
|
// wipe the new ones
|
||||||
|
for (int x = 0; x < _arraySize; x++)
|
||||||
|
_direction[x] = _residual[x] = _q[x] = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// conjugate gradient solver
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
int CG_SOLVER::solve(list<CELL*> cells)
|
||||||
|
{
|
||||||
|
// counters
|
||||||
|
int x, y, index;
|
||||||
|
list<CELL*>::iterator cellIterator;
|
||||||
|
|
||||||
|
// i = 0
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
// precalculate stencils
|
||||||
|
calcStencils(cells);
|
||||||
|
|
||||||
|
// reallocate scratch arrays if necessary
|
||||||
|
_listSize = cells.size();
|
||||||
|
reallocate();
|
||||||
|
|
||||||
|
// compute a new lexicographical order
|
||||||
|
cellIterator = cells.begin();
|
||||||
|
for (x = 0; x < _listSize; x++, cellIterator++)
|
||||||
|
(*cellIterator)->index = x;
|
||||||
|
|
||||||
|
// r = b - Ax
|
||||||
|
calcResidual(cells);
|
||||||
|
|
||||||
|
// copy residual into easy array
|
||||||
|
// d = r
|
||||||
|
cellIterator = cells.begin();
|
||||||
|
float deltaNew = 0.0f;
|
||||||
|
for (x = 0; x < _listSize; x++, cellIterator++)
|
||||||
|
{
|
||||||
|
_direction[x] = _residual[x];
|
||||||
|
deltaNew += _residual[x] * _residual[x];
|
||||||
|
}
|
||||||
|
|
||||||
|
// delta0 = deltaNew
|
||||||
|
float delta0 = deltaNew;
|
||||||
|
|
||||||
|
// While deltaNew > (eps^2) * delta0
|
||||||
|
float eps = pow(10.0f, (float)-_digits);
|
||||||
|
float maxR = 2.0f * eps;
|
||||||
|
while ((i < _iterations) && (maxR > eps))
|
||||||
|
{
|
||||||
|
// q = Ad
|
||||||
|
cellIterator = cells.begin();
|
||||||
|
for (y = 0; y < _listSize; y++, cellIterator++)
|
||||||
|
{
|
||||||
|
CELL* currentCell = *cellIterator;
|
||||||
|
CELL** neighbors = currentCell->neighbors;
|
||||||
|
|
||||||
|
float neighborSum = 0.0f;
|
||||||
|
for (int x = 0; x < 4; x++)
|
||||||
|
{
|
||||||
|
int j = x * 2;
|
||||||
|
neighborSum += _direction[neighbors[j]->index] * currentCell->stencil[j];
|
||||||
|
if (neighbors[j+1])
|
||||||
|
neighborSum += _direction[neighbors[j+1]->index] * currentCell->stencil[j+1];
|
||||||
|
}
|
||||||
|
_q[y] = -neighborSum + _direction[y] * currentCell->stencil[8];
|
||||||
|
}
|
||||||
|
// alpha = deltaNew / (transpose(d) * q)
|
||||||
|
float alpha = 0.0f;
|
||||||
|
for (x = 0; x < _listSize; x++)
|
||||||
|
alpha += _direction[x] * _q[x];
|
||||||
|
if (fabs(alpha) > 0.0f)
|
||||||
|
alpha = deltaNew / alpha;
|
||||||
|
|
||||||
|
// x = x + alpha * d
|
||||||
|
cellIterator = cells.begin();
|
||||||
|
for (x = 0; x < _listSize; x++, cellIterator++)
|
||||||
|
(*cellIterator)->potential += alpha * _direction[x];
|
||||||
|
|
||||||
|
// r = r - alpha * q
|
||||||
|
maxR = 0.0f;
|
||||||
|
for (x = 0; x < _listSize; x++)
|
||||||
|
{
|
||||||
|
_residual[x] -= _q[x] * alpha;
|
||||||
|
maxR = (_residual[x] > maxR) ? _residual[x] : maxR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// deltaOld = deltaNew
|
||||||
|
float deltaOld = deltaNew;
|
||||||
|
|
||||||
|
// deltaNew = transpose(r) * r
|
||||||
|
deltaNew = 0.0f;
|
||||||
|
for (x = 0; x < _listSize; x++)
|
||||||
|
deltaNew += _residual[x] * _residual[x];
|
||||||
|
|
||||||
|
// beta = deltaNew / deltaOld
|
||||||
|
float beta = deltaNew / deltaOld;
|
||||||
|
|
||||||
|
// d = r + beta * d
|
||||||
|
for (x = 0; x < _listSize; x++)
|
||||||
|
_direction[x] = _residual[x] + beta * _direction[x];
|
||||||
|
|
||||||
|
// i = i + 1
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// calculate the residuals
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
float CG_SOLVER::calcResidual(list<CELL*> cells)
|
||||||
|
{
|
||||||
|
float maxResidual = 0.0f;
|
||||||
|
|
||||||
|
list<CELL*>::iterator cellIterator = cells.begin();
|
||||||
|
for (int i = 0; i < _listSize; i++, cellIterator++)
|
||||||
|
{
|
||||||
|
CELL* currentCell = *cellIterator;
|
||||||
|
float dx = _dx[currentCell->depth];
|
||||||
|
float neighborSum = 0.0f;
|
||||||
|
|
||||||
|
for (int x = 0; x < 4; x++)
|
||||||
|
{
|
||||||
|
int i = x * 2;
|
||||||
|
neighborSum += currentCell->neighbors[i]->potential * currentCell->stencil[i];
|
||||||
|
if (currentCell->neighbors[i+1])
|
||||||
|
neighborSum += currentCell->neighbors[i+1]->potential * currentCell->stencil[i+1];
|
||||||
|
}
|
||||||
|
_residual[i] = currentCell->b - (-neighborSum + currentCell->potential * currentCell->stencil[8]);
|
||||||
|
|
||||||
|
if (fabs(_residual[i]) > maxResidual)
|
||||||
|
maxResidual = fabs(_residual[i]);
|
||||||
|
}
|
||||||
|
return maxResidual;
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// compute stencils once and store
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
void CG_SOLVER::calcStencils(list<CELL*> cells)
|
||||||
|
{
|
||||||
|
list<CELL*>::iterator cellIterator = cells.begin();
|
||||||
|
for (cellIterator = cells.begin(); cellIterator != cells.end(); cellIterator++)
|
||||||
|
{
|
||||||
|
CELL* currentCell = *cellIterator;
|
||||||
|
float invDx = 1.0f / _dx[currentCell->depth];
|
||||||
|
|
||||||
|
// sum over faces
|
||||||
|
float deltaSum = 0.0f;
|
||||||
|
float bSum = 0.0f;
|
||||||
|
|
||||||
|
for (int x = 0; x < 4; x++)
|
||||||
|
{
|
||||||
|
int i = x * 2;
|
||||||
|
currentCell->stencil[i] = 0.0f;
|
||||||
|
currentCell->stencil[i+1] = 0.0f;
|
||||||
|
|
||||||
|
if (currentCell->neighbors[i + 1] == NULL) {
|
||||||
|
// if it is the same refinement level (case 1)
|
||||||
|
if (currentCell->depth == currentCell->neighbors[i]->depth) {
|
||||||
|
deltaSum += invDx;
|
||||||
|
if (!currentCell->neighbors[i]->boundary)
|
||||||
|
currentCell->stencil[i] = invDx;
|
||||||
|
else
|
||||||
|
bSum += (currentCell->neighbors[i]->potential) * invDx;
|
||||||
|
}
|
||||||
|
// else it is less refined (case 3)
|
||||||
|
else {
|
||||||
|
deltaSum += 0.5f * invDx;
|
||||||
|
if (!currentCell->neighbors[i]->boundary)
|
||||||
|
currentCell->stencil[i] = 0.5f * invDx;
|
||||||
|
else
|
||||||
|
bSum += currentCell->neighbors[i]->potential * 0.5f * invDx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if the neighbor is at a lower level (case 2)
|
||||||
|
else {
|
||||||
|
deltaSum += 2.0f * invDx;
|
||||||
|
if (!currentCell->neighbors[i]->boundary)
|
||||||
|
currentCell->stencil[i] = invDx;
|
||||||
|
else
|
||||||
|
bSum += currentCell->neighbors[i]->potential * invDx;
|
||||||
|
if (!currentCell->neighbors[i+1]->boundary)
|
||||||
|
currentCell->stencil[i+1] = invDx;
|
||||||
|
else
|
||||||
|
bSum += currentCell->neighbors[i+1]->potential * invDx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
currentCell->stencil[8] = deltaSum;
|
||||||
|
currentCell->b = bSum;
|
||||||
|
}
|
||||||
|
}
|
||||||
120
CG_SOLVER.h
Normal file
120
CG_SOLVER.h
Normal file
|
|
@ -0,0 +1,120 @@
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// File : CG_SOLVER.h
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef CG_SOLVER_H
|
||||||
|
#define CG_SOLVER_H
|
||||||
|
|
||||||
|
#include "CELL.h"
|
||||||
|
#include <cmath>
|
||||||
|
#include <list>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
/// \brief Conjugate gradient Poisson solver.
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
class CG_SOLVER
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
//! constructor
|
||||||
|
CG_SOLVER(int maxDepth, int iterations = 10, int digits = 8);
|
||||||
|
//! destructor
|
||||||
|
virtual ~CG_SOLVER();
|
||||||
|
|
||||||
|
//! solve the Poisson problem
|
||||||
|
virtual int solve(list<CELL*> cells);
|
||||||
|
|
||||||
|
//! calculate the residual
|
||||||
|
float calcResidual(list<CELL*> cells);
|
||||||
|
|
||||||
|
//! accessor for the maximum number of iterations
|
||||||
|
int& iterations() { return _iterations; };
|
||||||
|
|
||||||
|
protected:
|
||||||
|
int _iterations; ///< maximum number of iterations
|
||||||
|
int _digits; ///< desired digits of precision
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
// conjugate gradient arrays
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
float* _direction; ///< conjugate gradient 'd' array
|
||||||
|
float* _potential; ///< conjugate gradient solution, 'x' array
|
||||||
|
float* _residual; ///< conjugate gradient residual, 'r' array
|
||||||
|
float* _q; ///< conjugate gradient 'q' array
|
||||||
|
|
||||||
|
int _arraySize; ///< currently allocated array size
|
||||||
|
int _listSize; ///< current system size
|
||||||
|
|
||||||
|
//! compute stencils once and store
|
||||||
|
void calcStencils(list<CELL*> cells);
|
||||||
|
|
||||||
|
//! reallocate the scratch arrays
|
||||||
|
virtual void reallocate();
|
||||||
|
|
||||||
|
//! physical lengths of various cell sizes
|
||||||
|
float* _dx;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
416
CG_SOLVER_SSE.cpp
Normal file
416
CG_SOLVER_SSE.cpp
Normal file
|
|
@ -0,0 +1,416 @@
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// File : CG_SOLVER_SSE.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 "CG_SOLVER_SSE.h"
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// Construction/Destruction
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
CG_SOLVER_SSE::CG_SOLVER_SSE(int maxDepth, int iterations, int digits) :
|
||||||
|
CG_SOLVER(maxDepth, iterations, digits)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
CG_SOLVER_SSE::~CG_SOLVER_SSE()
|
||||||
|
{
|
||||||
|
if (_direction) free(_direction);
|
||||||
|
if (_potential) free(_potential);
|
||||||
|
if (_residual) free(_residual);
|
||||||
|
if (_q) free(_q);
|
||||||
|
|
||||||
|
_direction = NULL;
|
||||||
|
_residual = NULL;
|
||||||
|
_q = NULL;
|
||||||
|
_potential = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// reallocate the sse arrays
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
void CG_SOLVER_SSE::reallocate()
|
||||||
|
{
|
||||||
|
if (_arraySize >= _listSize) return;
|
||||||
|
_arraySize = _listSize * 2;
|
||||||
|
|
||||||
|
if (_arraySize % 4)
|
||||||
|
_arraySize += 4 - _arraySize % 4;
|
||||||
|
|
||||||
|
if (_direction) free(_direction);
|
||||||
|
if (_potential) free(_potential);
|
||||||
|
if (_residual) free(_residual);
|
||||||
|
if (_q) free(_q);
|
||||||
|
|
||||||
|
_direction = (float*) aligned_alloc(_arraySize * sizeof(float), 16);
|
||||||
|
_potential = (float*) aligned_alloc(_arraySize * sizeof(float), 16);
|
||||||
|
_residual = (float*) aligned_alloc(_arraySize * sizeof(float), 16);
|
||||||
|
_q = (float*) aligned_alloc(_arraySize * sizeof(float), 16);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// solve the linear system
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
int CG_SOLVER_SSE::solve(list<CELL*> cells)
|
||||||
|
{
|
||||||
|
// counters
|
||||||
|
int x, y, index;
|
||||||
|
list<CELL*>::iterator cellIterator;
|
||||||
|
|
||||||
|
// i = 0
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
// precalculate stencils
|
||||||
|
calcStencils(cells);
|
||||||
|
|
||||||
|
// reallocate scratch arrays if necessary
|
||||||
|
_listSize = cells.size();
|
||||||
|
reallocate();
|
||||||
|
wipeSSE(_potential);
|
||||||
|
wipeSSE(_direction);
|
||||||
|
wipeSSE(_residual);
|
||||||
|
wipeSSE(_q);
|
||||||
|
|
||||||
|
// compute a new lexicographical order
|
||||||
|
cellIterator = cells.begin();
|
||||||
|
for (x = 0; x < _listSize; x++, cellIterator++)
|
||||||
|
{
|
||||||
|
CELL* cell = *cellIterator;
|
||||||
|
cell->index = x;
|
||||||
|
_potential[x] = cell->potential;
|
||||||
|
}
|
||||||
|
|
||||||
|
// r = b - Ax
|
||||||
|
calcResidual(cells);
|
||||||
|
|
||||||
|
// d = r
|
||||||
|
copySSE(_direction, _residual);
|
||||||
|
|
||||||
|
// deltaNew = r^T r
|
||||||
|
float deltaNew = dotSSE(_residual, _residual);
|
||||||
|
|
||||||
|
// delta0 = deltaNew
|
||||||
|
float delta0 = deltaNew;
|
||||||
|
|
||||||
|
// While deltaNew > (eps^2) * delta0
|
||||||
|
float eps = pow(10.0f, (float)-_digits);
|
||||||
|
float maxR = 2.0f * eps;
|
||||||
|
while ((i < _iterations) && (maxR > eps))
|
||||||
|
{
|
||||||
|
// q = Ad
|
||||||
|
cellIterator = cells.begin();
|
||||||
|
for (y = 0; y < _listSize; y++, cellIterator++)
|
||||||
|
{
|
||||||
|
CELL* currentCell = *cellIterator;
|
||||||
|
CELL** neighbors = currentCell->neighbors;
|
||||||
|
float* stencil = currentCell->stencil;
|
||||||
|
|
||||||
|
float neighborSum = 0.0f;
|
||||||
|
for (int x = 0; x < 8; x++)
|
||||||
|
{
|
||||||
|
if (neighbors[x])
|
||||||
|
neighborSum += _direction[neighbors[x]->index] * stencil[x];
|
||||||
|
}
|
||||||
|
_q[y] = -neighborSum + _direction[y] * currentCell->stencil[8];
|
||||||
|
}
|
||||||
|
|
||||||
|
// alpha = deltaNew / (transpose(d) * q)
|
||||||
|
float alpha = dotSSE(_q, _direction);
|
||||||
|
if (fabs(alpha) > 0.0f)
|
||||||
|
alpha = deltaNew / alpha;
|
||||||
|
|
||||||
|
// x = x + alpha * d
|
||||||
|
saxpySSE(alpha, _direction, _potential);
|
||||||
|
|
||||||
|
// r = r - alpha * q
|
||||||
|
saxpySSE(-alpha, _q, _residual);
|
||||||
|
maxR = maxSSE(_residual);
|
||||||
|
|
||||||
|
// deltaOld = deltaNew
|
||||||
|
float deltaOld = deltaNew;
|
||||||
|
|
||||||
|
// deltaNew = transpose(r) * r
|
||||||
|
deltaNew = dotSSE(_residual, _residual);
|
||||||
|
|
||||||
|
// beta = deltaNew / deltaOld
|
||||||
|
float beta = deltaNew / deltaOld;
|
||||||
|
|
||||||
|
// d = r + beta * d
|
||||||
|
saypxSSE(beta, _residual, _direction);
|
||||||
|
|
||||||
|
// i = i + 1
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy back into the tree
|
||||||
|
cellIterator = cells.begin();
|
||||||
|
for (x = 0; x < _listSize; x++, cellIterator++)
|
||||||
|
(*cellIterator)->potential = _potential[x];
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// dot product of two vectors
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
float CG_SOLVER_SSE::dotSSE(float* x, float* y)
|
||||||
|
{
|
||||||
|
__m128 sum = _mm_set_ps1(0.0f);
|
||||||
|
__m128* xSSE = (__m128*)x;
|
||||||
|
__m128* ySSE = (__m128*)y;
|
||||||
|
__m128 temp;
|
||||||
|
for (int index = 0; index < _arraySize / 4; index++)
|
||||||
|
{
|
||||||
|
temp = _mm_mul_ps(*xSSE, *ySSE);
|
||||||
|
sum = _mm_add_ps(sum, temp);
|
||||||
|
xSSE++;
|
||||||
|
ySSE++;
|
||||||
|
}
|
||||||
|
union u {
|
||||||
|
__m128 m;
|
||||||
|
float f[4];
|
||||||
|
} extract;
|
||||||
|
extract.m = sum;
|
||||||
|
return extract.f[0] + extract.f[1] + extract.f[2] + extract.f[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// scalar 'a' x + y
|
||||||
|
// Y = aX + Y
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
void CG_SOLVER_SSE::saxpySSE(float s, float* x, float* y)
|
||||||
|
{
|
||||||
|
__m128* ySSE = (__m128*)y;
|
||||||
|
__m128* xSSE = (__m128*)x;
|
||||||
|
__m128 sSSE = _mm_set_ps1(s);
|
||||||
|
__m128 temp;
|
||||||
|
for (int index = 0; index < _arraySize / 4; index++)
|
||||||
|
{
|
||||||
|
temp = _mm_mul_ps(*xSSE, sSSE);
|
||||||
|
*ySSE = _mm_add_ps(*ySSE, temp);
|
||||||
|
|
||||||
|
xSSE++;
|
||||||
|
ySSE++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// scalar 'a' y + x
|
||||||
|
// Y = aY + X
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
void CG_SOLVER_SSE::saypxSSE(float s, float* x, float* y)
|
||||||
|
{
|
||||||
|
__m128* ySSE = (__m128*)y;
|
||||||
|
__m128* xSSE = (__m128*)x;
|
||||||
|
__m128 sSSE = _mm_set_ps1(s);
|
||||||
|
__m128 temp;
|
||||||
|
for (int index = 0; index < _arraySize / 4; index++)
|
||||||
|
{
|
||||||
|
temp = _mm_mul_ps(*ySSE, sSSE);
|
||||||
|
*ySSE = _mm_add_ps(*xSSE, temp);
|
||||||
|
|
||||||
|
xSSE++;
|
||||||
|
ySSE++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// scalar 'a' y + x
|
||||||
|
// Y = aY + X
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
float CG_SOLVER_SSE::maxSSE(float* x)
|
||||||
|
{
|
||||||
|
__m128 maxFoundSSE= _mm_set_ps1(0.0f);
|
||||||
|
__m128* xSSE = (__m128*)x;
|
||||||
|
for (int index = 0; index < _arraySize / 4; index++)
|
||||||
|
{
|
||||||
|
maxFoundSSE = _mm_max_ps(*xSSE, maxFoundSSE);
|
||||||
|
xSSE++;
|
||||||
|
}
|
||||||
|
union u {
|
||||||
|
__m128 m;
|
||||||
|
float f[4];
|
||||||
|
} extract;
|
||||||
|
extract.m = maxFoundSSE;
|
||||||
|
float maxFound = extract.f[0] > extract.f[1] ? extract.f[0] : extract.f[1];
|
||||||
|
maxFound = maxFound > extract.f[2] ? maxFound : extract.f[2];
|
||||||
|
return maxFound > extract.f[3] ? maxFound : extract.f[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// SSE add
|
||||||
|
// Y = X + Y
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
void CG_SOLVER_SSE::addSSE(float* x, float* y)
|
||||||
|
{
|
||||||
|
__m128* ySSE = (__m128*)y;
|
||||||
|
__m128* xSSE = (__m128*)x;
|
||||||
|
for (int index = 0; index < _arraySize / 4; index++)
|
||||||
|
{
|
||||||
|
*ySSE = _mm_add_ps(*ySSE, *xSSE);
|
||||||
|
xSSE++;
|
||||||
|
ySSE++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// SSE multiply
|
||||||
|
// Y = X * Y
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
void CG_SOLVER_SSE::multiplySSE(float* x, float* y)
|
||||||
|
{
|
||||||
|
__m128* ySSE = (__m128*)y;
|
||||||
|
__m128* xSSE = (__m128*)x;
|
||||||
|
for (int index = 0; index < _arraySize / 4; index++)
|
||||||
|
{
|
||||||
|
*ySSE = _mm_mul_ps(*ySSE, *xSSE);
|
||||||
|
xSSE++;
|
||||||
|
ySSE++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// SSE multiply
|
||||||
|
// Z = X * Y
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
void CG_SOLVER_SSE::multiplySSE(float* x, float* y, float* z)
|
||||||
|
{
|
||||||
|
__m128* zSSE = (__m128*)z;
|
||||||
|
__m128* ySSE = (__m128*)y;
|
||||||
|
__m128* xSSE = (__m128*)x;
|
||||||
|
for (int index = 0; index < _arraySize / 4; index++)
|
||||||
|
{
|
||||||
|
*zSSE = _mm_mul_ps(*ySSE, *xSSE);
|
||||||
|
xSSE++;
|
||||||
|
ySSE++;
|
||||||
|
zSSE++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// SSE multiply
|
||||||
|
// Z = W - X * Y
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
void CG_SOLVER_SSE::multiplySubtractSSE(float* w, float* x, float* y, float* z)
|
||||||
|
{
|
||||||
|
__m128* zSSE = (__m128*)z;
|
||||||
|
__m128* ySSE = (__m128*)y;
|
||||||
|
__m128* xSSE = (__m128*)x;
|
||||||
|
__m128* wSSE = (__m128*)w;
|
||||||
|
for (int index = 0; index < _arraySize / 4; index++)
|
||||||
|
{
|
||||||
|
*zSSE = _mm_mul_ps(*ySSE, *xSSE);
|
||||||
|
*zSSE = _mm_sub_ps(*wSSE, *zSSE);
|
||||||
|
|
||||||
|
xSSE++;
|
||||||
|
ySSE++;
|
||||||
|
zSSE++;
|
||||||
|
wSSE++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// SSE set
|
||||||
|
// X = val
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
void CG_SOLVER_SSE::setSSE(float* x, float val)
|
||||||
|
{
|
||||||
|
__m128* xSSE = (__m128*)x;
|
||||||
|
for (int index = 0; index < _arraySize / 4; index++)
|
||||||
|
{
|
||||||
|
*xSSE = _mm_set_ps1(val);
|
||||||
|
xSSE++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// SSE set
|
||||||
|
// X = 0
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
void CG_SOLVER_SSE::wipeSSE(float* x)
|
||||||
|
{
|
||||||
|
__m128* xSSE = (__m128*)x;
|
||||||
|
for (int index = 0; index < _arraySize / 4; index++)
|
||||||
|
{
|
||||||
|
*xSSE = _mm_setzero_ps();
|
||||||
|
xSSE++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// SSE set
|
||||||
|
// X = Y
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
void CG_SOLVER_SSE::copySSE(float* x, float* y)
|
||||||
|
{
|
||||||
|
__m128* ySSE = (__m128*)y;
|
||||||
|
for (int index = 0; index < _arraySize / 4; index++)
|
||||||
|
{
|
||||||
|
_mm_store_ps(x,*ySSE);
|
||||||
|
x += 4;
|
||||||
|
ySSE++;
|
||||||
|
}
|
||||||
|
}
|
||||||
103
CG_SOLVER_SSE.h
Normal file
103
CG_SOLVER_SSE.h
Normal file
|
|
@ -0,0 +1,103 @@
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// File : CG_SOLVER_SSE.h
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef CG_SOLVER_SSE_H
|
||||||
|
#define CG_SOLVER_SSE_H
|
||||||
|
|
||||||
|
#include "CG_SOLVER.h"
|
||||||
|
#include <xmmintrin.h>
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
/// \brief Conjugate gradient Poisson solver that exploits SSE.
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
class CG_SOLVER_SSE : public CG_SOLVER
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
//! constructor
|
||||||
|
CG_SOLVER_SSE(int maxDepth, int iterations = 10, int digits = 1);
|
||||||
|
//! destructor
|
||||||
|
~CG_SOLVER_SSE();
|
||||||
|
|
||||||
|
//! solve the Poisson problem using SSE
|
||||||
|
virtual int solve(list<CELL*> cells);
|
||||||
|
|
||||||
|
private:
|
||||||
|
//! reallocate the SSE-friendly scratch arrays
|
||||||
|
virtual void reallocate();
|
||||||
|
|
||||||
|
// SSE linear algebra operators
|
||||||
|
inline float dotSSE(float* x, float* y);
|
||||||
|
inline void saxpySSE(float a, float* x, float* y);
|
||||||
|
inline void saypxSSE(float a, float* x, float* y);
|
||||||
|
inline float maxSSE(float* x);
|
||||||
|
inline void addSSE(float* x, float* y);
|
||||||
|
inline void multiplySSE(float* x, float* y);
|
||||||
|
inline void multiplySSE(float* x, float* y, float* z);
|
||||||
|
inline void multiplySubtractSSE(float* w, float* x, float* y, float* z);
|
||||||
|
inline void setSSE(float* x, float val);
|
||||||
|
inline void wipeSSE(float* x);
|
||||||
|
inline void copySSE(float* x, float* y);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
566
DAG.cpp
Normal file
566
DAG.cpp
Normal file
|
|
@ -0,0 +1,566 @@
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// File : DAG.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 "DAG.h"
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// Construction/Destruction
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
DAG::DAG(int xRes, int yRes) :
|
||||||
|
_xRes(xRes),
|
||||||
|
_yRes(yRes),
|
||||||
|
_width(xRes),
|
||||||
|
_height(yRes),
|
||||||
|
_dx(1.0f / xRes),
|
||||||
|
_dy(1.0f / yRes),
|
||||||
|
_root(NULL),
|
||||||
|
_totalNodes(0),
|
||||||
|
_bottomHit(-1),
|
||||||
|
_secondaryIntensity(0.3f),
|
||||||
|
_leaderIntensity(0.75f)
|
||||||
|
{
|
||||||
|
(_dx < _dy) ? _dy = _dx : _dy = _dx;
|
||||||
|
_offscreenBuffer = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
DAG::~DAG()
|
||||||
|
{
|
||||||
|
if (_offscreenBuffer) delete[] _offscreenBuffer;
|
||||||
|
|
||||||
|
deleteNode(_root);
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// delete the nodes
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
void DAG::deleteNode(NODE* root)
|
||||||
|
{
|
||||||
|
if (root == NULL) return;
|
||||||
|
|
||||||
|
for (int x = 0; x < root->neighbors.size(); x++)
|
||||||
|
deleteNode(root->neighbors[x]);
|
||||||
|
|
||||||
|
delete root;
|
||||||
|
root = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// add line segment 'index' to segment list
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
bool DAG::addSegment(int index, int neighbor)
|
||||||
|
{
|
||||||
|
if (_root)
|
||||||
|
{
|
||||||
|
// find corresponding root in DAG
|
||||||
|
map<int, NODE*>::iterator iter = _hash.find(neighbor);
|
||||||
|
NODE* root = (*iter).second;
|
||||||
|
|
||||||
|
if (root == NULL) return false;
|
||||||
|
|
||||||
|
// add to DAG
|
||||||
|
NODE* newNode = new NODE(index);
|
||||||
|
newNode->parent = root;
|
||||||
|
root->neighbors.push_back(newNode);
|
||||||
|
|
||||||
|
// add to hash table
|
||||||
|
_hash.insert(map<int, NODE*>::value_type(index, newNode));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// make the root
|
||||||
|
_root = new NODE(neighbor);
|
||||||
|
_hash.insert(map<int, NODE*>::value_type(neighbor, _root));
|
||||||
|
|
||||||
|
// then do the add
|
||||||
|
NODE* newNode = new NODE(index);
|
||||||
|
newNode->parent = _root;
|
||||||
|
_root->neighbors.push_back(newNode);
|
||||||
|
|
||||||
|
// add to hash table
|
||||||
|
_hash.insert(map<int, NODE*>::value_type(index, newNode));
|
||||||
|
}
|
||||||
|
|
||||||
|
_totalNodes++;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// build the leader chain
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
void DAG::buildLeader(int bottomHit)
|
||||||
|
{
|
||||||
|
_bottomHit = bottomHit;
|
||||||
|
|
||||||
|
// get pointer to bottommost node
|
||||||
|
map<int, NODE*>::iterator iter = _hash.find(bottomHit);
|
||||||
|
NODE* child = (*iter).second;
|
||||||
|
|
||||||
|
// crawl up the tree
|
||||||
|
while (child != NULL)
|
||||||
|
{
|
||||||
|
// tag segment
|
||||||
|
child->leader = true;
|
||||||
|
child->secondary = false;
|
||||||
|
|
||||||
|
// look for side branches
|
||||||
|
for (int x = 0; x < child->neighbors.size(); x++)
|
||||||
|
if (!(child->neighbors[x]->leader))
|
||||||
|
buildBranch(child->neighbors[x], 1);
|
||||||
|
|
||||||
|
// advance child
|
||||||
|
child = child->parent;
|
||||||
|
}
|
||||||
|
buildIntensity(_root);
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// build the side branch
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
void DAG::buildBranch(NODE* node, int depth)
|
||||||
|
{
|
||||||
|
node->depth = depth;
|
||||||
|
node->leader = false;
|
||||||
|
|
||||||
|
// look for side branches
|
||||||
|
for (int x = 0; x < node->neighbors.size(); x++)
|
||||||
|
if (!(node->neighbors[x]->leader))
|
||||||
|
buildBranch(node->neighbors[x], depth + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// draw the DAG segments
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
void DAG::drawNode(NODE* root)
|
||||||
|
{
|
||||||
|
if (root == NULL) return;
|
||||||
|
|
||||||
|
// draw segments
|
||||||
|
int beginIndex = root->index;
|
||||||
|
int begin[2];
|
||||||
|
int end[2];
|
||||||
|
float dWidth = _dx;
|
||||||
|
float dHeight = _dy;
|
||||||
|
|
||||||
|
for (int x = 0; x < root->neighbors.size(); x++)
|
||||||
|
{
|
||||||
|
// get end node
|
||||||
|
NODE* endNode = root->neighbors[x];
|
||||||
|
int endIndex = endNode->index;
|
||||||
|
|
||||||
|
// draw segments
|
||||||
|
begin[1] = beginIndex / _xRes;
|
||||||
|
begin[0] = beginIndex - begin[1] * _xRes;
|
||||||
|
end[1] = endIndex / _xRes;
|
||||||
|
end[0] = endIndex - end[1] * _xRes;
|
||||||
|
|
||||||
|
if (_bottomHit != -1)
|
||||||
|
glColor4f(endNode->intensity,
|
||||||
|
endNode->intensity,
|
||||||
|
endNode->intensity,1.0f);
|
||||||
|
else
|
||||||
|
glColor4f(0.0f, 1.0f, 0.0f, 1.0f);
|
||||||
|
|
||||||
|
glBegin(GL_LINES);
|
||||||
|
glVertex3f(begin[0] * dWidth + dWidth * 0.5f, 1.0f - begin[1] * dHeight + dHeight * 0.5f, 0.1f);
|
||||||
|
glVertex3f(end[0] * dWidth + dWidth * 0.5f, 1.0f - end[1] * dHeight + dHeight * 0.5f, 0.1f);
|
||||||
|
glEnd();
|
||||||
|
|
||||||
|
// call recursively
|
||||||
|
if (endNode->neighbors.size() > 0)
|
||||||
|
drawNode(endNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// set the intensity of the node
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
void DAG::buildIntensity(NODE* root)
|
||||||
|
{
|
||||||
|
if (root == NULL) return;
|
||||||
|
|
||||||
|
// draw segments
|
||||||
|
int beginIndex = root->index;
|
||||||
|
int begin[2];
|
||||||
|
int end[2];
|
||||||
|
float dWidth = _dx;
|
||||||
|
float dHeight = _dy;
|
||||||
|
|
||||||
|
for (int x = 0; x < root->neighbors.size(); x++)
|
||||||
|
{
|
||||||
|
// get end node
|
||||||
|
NODE* endNode = root->neighbors[x];
|
||||||
|
int endIndex = endNode->index;
|
||||||
|
|
||||||
|
// draw segments
|
||||||
|
begin[1] = beginIndex / _xRes;
|
||||||
|
begin[0] = beginIndex - begin[1] * _xRes;
|
||||||
|
end[1] = endIndex / _xRes;
|
||||||
|
end[0] = endIndex - end[1] * _xRes;
|
||||||
|
|
||||||
|
// set color
|
||||||
|
if (endNode->leader)
|
||||||
|
endNode->intensity = _leaderIntensity;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// find max depth of current channel
|
||||||
|
if (endNode->maxDepthNode == NULL)
|
||||||
|
findDeepest(endNode, endNode->maxDepthNode);
|
||||||
|
|
||||||
|
int maxDepth = endNode->maxDepthNode->depth;
|
||||||
|
|
||||||
|
// calc standard deviation
|
||||||
|
float stdDev = -(float)(maxDepth * maxDepth) / (float)(log(_secondaryIntensity) * 2.0f);
|
||||||
|
|
||||||
|
// calc falloff
|
||||||
|
float eTerm = -(float)(endNode->depth) * (float)(endNode->depth);
|
||||||
|
eTerm /= (2.0f * stdDev);
|
||||||
|
eTerm = exp(eTerm) * 0.5f;
|
||||||
|
endNode->intensity = eTerm;
|
||||||
|
}
|
||||||
|
|
||||||
|
// call recursively
|
||||||
|
if (endNode->neighbors.size() > 0)
|
||||||
|
buildIntensity(endNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// draw the tree offscreen
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
float*& DAG::drawOffscreen(int scale)
|
||||||
|
{
|
||||||
|
// allocate buffer
|
||||||
|
_width = _xRes * scale;
|
||||||
|
_height = _yRes * scale;
|
||||||
|
_scale = scale;
|
||||||
|
if (_offscreenBuffer) delete[] _offscreenBuffer;
|
||||||
|
_offscreenBuffer = new float[_width * _height];
|
||||||
|
|
||||||
|
// wipe the buffer
|
||||||
|
for (int x = 0; x < _width * _height; x++)
|
||||||
|
_offscreenBuffer[x] = 0.0f;
|
||||||
|
|
||||||
|
// recursively draw the tree
|
||||||
|
drawOffscreenNode(_root);
|
||||||
|
|
||||||
|
return _offscreenBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// draw the DAG segments
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
void DAG::drawOffscreenNode(NODE* root)
|
||||||
|
{
|
||||||
|
if (root == NULL) return;
|
||||||
|
|
||||||
|
// draw segments
|
||||||
|
int beginIndex = root->index;
|
||||||
|
int begin[2];
|
||||||
|
int end[2];
|
||||||
|
float dWidth = _dx;
|
||||||
|
float dHeight = _dy;
|
||||||
|
|
||||||
|
for (int x = 0; x < root->neighbors.size(); x++)
|
||||||
|
{
|
||||||
|
// get end node
|
||||||
|
NODE* endNode = root->neighbors[x];
|
||||||
|
int endIndex = endNode->index;
|
||||||
|
|
||||||
|
// get endpoints
|
||||||
|
begin[0] = beginIndex % _xRes * _scale;
|
||||||
|
begin[1] = beginIndex / _xRes * _scale;
|
||||||
|
end[0] = endIndex % _xRes * _scale;
|
||||||
|
end[1] = endIndex / _xRes * _scale;
|
||||||
|
|
||||||
|
// make sure the one with the smaller x comes first
|
||||||
|
if (end[0] < begin[0])
|
||||||
|
{
|
||||||
|
int temp[] = {end[0], end[1]};
|
||||||
|
end[0] = begin[0];
|
||||||
|
end[1] = begin[1];
|
||||||
|
|
||||||
|
begin[0] = temp[0];
|
||||||
|
begin[1] = temp[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
// rasterize
|
||||||
|
drawLine(begin, end, endNode->intensity);
|
||||||
|
|
||||||
|
// call recursively
|
||||||
|
if (endNode->neighbors.size() > 0)
|
||||||
|
drawOffscreenNode(endNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// rasterize the line
|
||||||
|
// I assume all the lines are purely horizontal, vertical or diagonal
|
||||||
|
// to avoid using something like Bresenham
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
void DAG::drawLine(int begin[], int end[], float intensity)
|
||||||
|
{
|
||||||
|
int scaledX = _xRes * _scale;
|
||||||
|
|
||||||
|
// if it is a horizontal line
|
||||||
|
if (begin[1] == end[1])
|
||||||
|
{
|
||||||
|
for (int x = begin[0]; x < end[0]; x++)
|
||||||
|
{
|
||||||
|
int index = x + end[1] * scaledX;
|
||||||
|
if (intensity > _offscreenBuffer[index])
|
||||||
|
_offscreenBuffer[index] = intensity;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if it is a vertical line
|
||||||
|
if (begin[0] == end[0])
|
||||||
|
{
|
||||||
|
int bottom = (begin[1] > end[1]) ? end[1] : begin[1];
|
||||||
|
int top = (begin[1] > end[1]) ? begin[1] : end[1];
|
||||||
|
|
||||||
|
for (int y = bottom; y < top; y++)
|
||||||
|
{
|
||||||
|
int index = begin[0] + y * scaledX;
|
||||||
|
if (intensity > _offscreenBuffer[index])
|
||||||
|
_offscreenBuffer[index] = intensity;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// else it is diagonal
|
||||||
|
int slope = (begin[1] < end[1]) ? 1 : -1;
|
||||||
|
int interval = end[0] - begin[0];
|
||||||
|
for (int x = 0; x <= interval; x++)
|
||||||
|
{
|
||||||
|
int index = begin[0] + x + (begin[1] + x * slope) * scaledX;
|
||||||
|
if (intensity > _offscreenBuffer[index])
|
||||||
|
_offscreenBuffer[index] = intensity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// find deepest depth from current root
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
void DAG::findDeepest(NODE* root, NODE*& deepest)
|
||||||
|
{
|
||||||
|
deepest = root;
|
||||||
|
|
||||||
|
for (int x = 0; x < root->neighbors.size(); x++)
|
||||||
|
{
|
||||||
|
NODE* child = root->neighbors[x];
|
||||||
|
NODE* candidate = NULL;
|
||||||
|
findDeepest(child, candidate);
|
||||||
|
if (candidate->depth > deepest->depth)
|
||||||
|
deepest = candidate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// dump out line segments
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
void DAG::write(const char* filename)
|
||||||
|
{
|
||||||
|
// open file
|
||||||
|
FILE* file;
|
||||||
|
file = fopen(filename, "wb");
|
||||||
|
|
||||||
|
// write out total number of DAG nodes
|
||||||
|
fwrite((void*)&_totalNodes, sizeof(int), 1, file);
|
||||||
|
fwrite((void*)&_xRes, sizeof(int), 1, file);
|
||||||
|
fwrite((void*)&_yRes, sizeof(int), 1, file);
|
||||||
|
fwrite((void*)&_dx, sizeof(float), 1, file);
|
||||||
|
fwrite((void*)&_dy, sizeof(float), 1, file);
|
||||||
|
fwrite((void*)&_bottomHit, sizeof(int), 1, file);
|
||||||
|
fwrite((void*)&_inputWidth, sizeof(int), 1, file);
|
||||||
|
fwrite((void*)&_inputHeight, sizeof(int), 1, file);
|
||||||
|
|
||||||
|
// write out nodes
|
||||||
|
writeNode(_root, file);
|
||||||
|
|
||||||
|
fclose(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// read in line segments
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
void DAG::read(const char* filename)
|
||||||
|
{
|
||||||
|
|
||||||
|
// erase old DAG
|
||||||
|
deleteNode(_root);
|
||||||
|
|
||||||
|
// open file
|
||||||
|
FILE* file;
|
||||||
|
file = fopen(filename, "rb");
|
||||||
|
if (file == NULL)
|
||||||
|
{
|
||||||
|
cout << "ERROR: " << filename << " is invalid." << endl;
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// read in total number of DAG nodes
|
||||||
|
fread((void*)&_totalNodes, sizeof(int), 1, file);
|
||||||
|
fread((void*)&_xRes, sizeof(int), 1, file);
|
||||||
|
fread((void*)&_yRes, sizeof(int), 1, file);
|
||||||
|
fread((void*)&_dx, sizeof(float), 1, file);
|
||||||
|
fread((void*)&_dy, sizeof(float), 1, file);
|
||||||
|
fread((void*)&_bottomHit, sizeof(int), 1, file);
|
||||||
|
fread((void*)&_inputWidth, sizeof(int), 1, file);
|
||||||
|
fread((void*)&_inputHeight, sizeof(int), 1, file);
|
||||||
|
|
||||||
|
// clear _hash for use
|
||||||
|
_hash.clear();
|
||||||
|
|
||||||
|
// read in all the DAG nodes
|
||||||
|
for (int x = 0; x <= _totalNodes; x++)
|
||||||
|
readNode(file);
|
||||||
|
|
||||||
|
fclose(file);
|
||||||
|
|
||||||
|
if (_bottomHit != -1)
|
||||||
|
buildLeader(_bottomHit);
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// write out a DAG node
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
void DAG::writeNode(NODE* root, FILE* file)
|
||||||
|
{
|
||||||
|
int x;
|
||||||
|
|
||||||
|
// write out neighbors
|
||||||
|
int numNeighbors = root->neighbors.size();
|
||||||
|
for (x = 0; x < numNeighbors; x++)
|
||||||
|
writeNode(root->neighbors[x], file);
|
||||||
|
|
||||||
|
// write out this node
|
||||||
|
fwrite((void*)&(root->index), sizeof(int), 1, file);
|
||||||
|
int parent = (root->parent == NULL) ? -1 : root->parent->index;
|
||||||
|
fwrite((void*)&parent, sizeof(int), 1, file);
|
||||||
|
fwrite((void*)&(root->leader), sizeof(bool), 1, file);
|
||||||
|
fwrite((void*)&(root->secondary), sizeof(bool), 1, file);
|
||||||
|
fwrite((void*)&(root->depth), sizeof(int), 1, file);
|
||||||
|
fwrite((void*)&numNeighbors, sizeof(int), 1, file);
|
||||||
|
for (x = 0; x < numNeighbors; x++)
|
||||||
|
fwrite((void*)&(root->neighbors[x]->index), sizeof(int), 1, file);
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// read in a DAG node
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
void DAG::readNode(FILE* file)
|
||||||
|
{
|
||||||
|
// read in DAG data
|
||||||
|
int index;
|
||||||
|
int parent;
|
||||||
|
bool leader;
|
||||||
|
bool secondary;
|
||||||
|
int depth;
|
||||||
|
int numNeighbors;
|
||||||
|
float potential;
|
||||||
|
fread((void*)&index, sizeof(int), 1, file);
|
||||||
|
fread((void*)&parent, sizeof(int), 1, file);
|
||||||
|
fread((void*)&leader, sizeof(bool), 1, file);
|
||||||
|
fread((void*)&secondary, sizeof(bool), 1, file);
|
||||||
|
fread((void*)&depth, sizeof(int), 1, file);
|
||||||
|
fread((void*)&numNeighbors, sizeof(int), 1, file);
|
||||||
|
|
||||||
|
// create the node
|
||||||
|
NODE* node = new NODE(index);
|
||||||
|
node->leader = leader;
|
||||||
|
node->secondary = secondary;
|
||||||
|
node->depth = depth;
|
||||||
|
|
||||||
|
// look up neighbors
|
||||||
|
map<int, NODE*>::iterator iter;
|
||||||
|
for (int x = 0; x < numNeighbors; x++)
|
||||||
|
{
|
||||||
|
// read in the child index
|
||||||
|
int neighborIndex;
|
||||||
|
fread((void*)&neighborIndex, sizeof(int), 1, file);
|
||||||
|
|
||||||
|
// look up in hash table
|
||||||
|
iter = _hash.find(neighborIndex);
|
||||||
|
|
||||||
|
// push onto the neighbor vector
|
||||||
|
node->neighbors.push_back((*iter).second);
|
||||||
|
|
||||||
|
// set child's parent
|
||||||
|
(*iter).second->parent = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add to hash table
|
||||||
|
_hash.insert(map<int, NODE*>::value_type(index, node));
|
||||||
|
|
||||||
|
// search for root node
|
||||||
|
if (parent == -1)
|
||||||
|
{
|
||||||
|
node->parent = NULL;
|
||||||
|
_root = node;
|
||||||
|
}
|
||||||
|
}
|
||||||
208
DAG.h
Normal file
208
DAG.h
Normal file
|
|
@ -0,0 +1,208 @@
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// File : DAG.h
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef DAG_H
|
||||||
|
#define DAG_H
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
#include <cmath>
|
||||||
|
#include <GL/glut.h>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
/// \brief Directed acyclic graph that renders the final lightning
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
class DAG
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
//! constructor
|
||||||
|
DAG(int xRes, int yRes);
|
||||||
|
//! destructor
|
||||||
|
virtual ~DAG();
|
||||||
|
|
||||||
|
//! build the stepped ladder
|
||||||
|
void buildLeader(int bottomHit);
|
||||||
|
|
||||||
|
//! add DAG segment
|
||||||
|
bool addSegment(int index, int neighbor);
|
||||||
|
|
||||||
|
//! draw to OpenGL
|
||||||
|
void draw() { drawNode(_root); };
|
||||||
|
|
||||||
|
//! draw to an offscreen buffer
|
||||||
|
float*& drawOffscreen(int scale = 1);
|
||||||
|
|
||||||
|
//! read in a new DAG
|
||||||
|
void read(const char* filename);
|
||||||
|
//! write out the current DAG
|
||||||
|
void write(const char* filename);
|
||||||
|
|
||||||
|
//! quadtree x resolution accessor
|
||||||
|
int xRes() { return _xRes; };
|
||||||
|
//! quadtree y resolution accessor
|
||||||
|
int yRes() { return _yRes; };
|
||||||
|
|
||||||
|
//! input image x resolution accessor
|
||||||
|
int& inputWidth() { return _inputWidth; };
|
||||||
|
//! input image y resolution accessor
|
||||||
|
int& inputHeight() { return _inputHeight; };
|
||||||
|
|
||||||
|
private:
|
||||||
|
//! x resolution of quadtree
|
||||||
|
int _xRes;
|
||||||
|
//! y resolution of quadtree
|
||||||
|
int _yRes;
|
||||||
|
//! physical length of one grid cell
|
||||||
|
float _dx;
|
||||||
|
//! physical length of one grid cell
|
||||||
|
float _dy;
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
/// \brief node for line segment tree
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
struct NODE {
|
||||||
|
int index;
|
||||||
|
vector<NODE*> neighbors;
|
||||||
|
NODE* parent;
|
||||||
|
bool leader;
|
||||||
|
bool secondary;
|
||||||
|
int depth;
|
||||||
|
NODE* maxDepthNode;
|
||||||
|
float intensity;
|
||||||
|
|
||||||
|
NODE(int indexIn) {
|
||||||
|
index = indexIn;
|
||||||
|
parent = NULL;
|
||||||
|
leader = false;
|
||||||
|
depth = 0;
|
||||||
|
maxDepthNode = NULL;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
//! recursive destructor
|
||||||
|
void deleteNode(NODE* root);
|
||||||
|
|
||||||
|
//! root of DAG
|
||||||
|
NODE* _root;
|
||||||
|
|
||||||
|
//! hash table of DAG nodes
|
||||||
|
map<int, NODE*> _hash;
|
||||||
|
|
||||||
|
//! build side branch
|
||||||
|
void buildBranch(NODE* node, int depth);
|
||||||
|
//! draw a node to OpenGL
|
||||||
|
void drawNode(NODE* root);
|
||||||
|
//! find the deepest node in a given subtree
|
||||||
|
void findDeepest(NODE* root, NODE*& deepest);
|
||||||
|
|
||||||
|
//! read in a DAG node
|
||||||
|
void readNode(FILE* file);
|
||||||
|
//! write out a DAG node
|
||||||
|
void writeNode(NODE* root, FILE* file);
|
||||||
|
|
||||||
|
//! total number of nodes in scene
|
||||||
|
int _totalNodes;
|
||||||
|
|
||||||
|
//! node that finally hit bottom
|
||||||
|
int _bottomHit;
|
||||||
|
|
||||||
|
//! set the line segment intensities
|
||||||
|
void buildIntensity(NODE* root);
|
||||||
|
|
||||||
|
//! brightness of secondary branch
|
||||||
|
float _secondaryIntensity;
|
||||||
|
//! brightness of primary branch
|
||||||
|
float _leaderIntensity;
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
// offscreen buffer variables
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
//! offscreen buffer
|
||||||
|
float* _offscreenBuffer;
|
||||||
|
|
||||||
|
//! width of offscreen buffer
|
||||||
|
int _width;
|
||||||
|
|
||||||
|
//! height of offscreen buffer
|
||||||
|
int _height;
|
||||||
|
|
||||||
|
//! scale of offscreen buffer compared to original image
|
||||||
|
int _scale;
|
||||||
|
|
||||||
|
//! draw a given node in the DAG
|
||||||
|
void drawOffscreenNode(NODE* root);
|
||||||
|
|
||||||
|
//! rasterize a single line to the offscreen buffer
|
||||||
|
void drawLine(int begin[], int end[], float intensity);
|
||||||
|
|
||||||
|
//! input image x resolution
|
||||||
|
int _inputWidth;
|
||||||
|
//! input image y resolution
|
||||||
|
int _inputHeight;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
96
EXR.cpp
Normal file
96
EXR.cpp
Normal file
|
|
@ -0,0 +1,96 @@
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// File : EXR.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 "EXR.h"
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// Construction/Destruction
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
EXR::EXR()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
EXR::~EXR()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void EXR::writeEXR(const char filename[], float* image, int width, int height)
|
||||||
|
{
|
||||||
|
Imf::Rgba* exrImage = new Imf::Rgba[width * height];
|
||||||
|
for (int x = 0; x < width * height; x++)
|
||||||
|
{
|
||||||
|
exrImage[x].r = exrImage[x].g = exrImage[x].b = image[x];
|
||||||
|
exrImage[x].a = 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
Imf::RgbaOutputFile file (filename, width, height);
|
||||||
|
file.setFrameBuffer (exrImage, 1, width);
|
||||||
|
file.writePixels (height);
|
||||||
|
|
||||||
|
delete[] exrImage;
|
||||||
|
}
|
||||||
92
EXR.h
Normal file
92
EXR.h
Normal file
|
|
@ -0,0 +1,92 @@
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// File : EXR.h
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef EXR_H
|
||||||
|
#define EXR_H
|
||||||
|
#include <Imath/half.h>
|
||||||
|
#include <OpenEXR/ImfRgbaFile.h>
|
||||||
|
#include <OpenEXR/ImfStringAttribute.h>
|
||||||
|
#include <OpenEXR/ImfMatrixAttribute.h>
|
||||||
|
#include <OpenEXR/ImfArray.h>
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
/// \brief Wrapper for the ILM OpenEXR routines
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
class EXR
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
EXR();
|
||||||
|
virtual ~EXR();
|
||||||
|
|
||||||
|
/// \brief write a float array out to an EXR file
|
||||||
|
///
|
||||||
|
/// \param filename name of output file
|
||||||
|
/// \param image float array to write to an EXR file
|
||||||
|
/// \param width width of float array
|
||||||
|
/// \param height height of float array
|
||||||
|
static void writeEXR(const char* filename, float* image, int width, int height);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
233
FFT.cpp
Normal file
233
FFT.cpp
Normal file
|
|
@ -0,0 +1,233 @@
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// File : FFT.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 "FFT.h"
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// Construction/Destruction
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
FFT::FFT()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
FFT::~FFT()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FFT::convolve(float* source, float* kernel, int xSource, int ySource, int xKernel, int yKernel)
|
||||||
|
{
|
||||||
|
int x, y, index;
|
||||||
|
|
||||||
|
// get normalization params
|
||||||
|
float maxCurrent = 0.0f;
|
||||||
|
for (x = 0; x < xSource * ySource; x++)
|
||||||
|
maxCurrent = (maxCurrent < source[x]) ? source[x] : maxCurrent;
|
||||||
|
float maxKernel = 0.0f;
|
||||||
|
for (x = 0; x < xKernel * yKernel; x++)
|
||||||
|
maxKernel = (maxKernel < kernel[x]) ? kernel[x] : maxKernel;
|
||||||
|
float maxProduct = maxCurrent * maxKernel;
|
||||||
|
|
||||||
|
// retrieve dimensions
|
||||||
|
int xHalf = xKernel / 2;
|
||||||
|
int yHalf = yKernel / 2;
|
||||||
|
int xResPadded = xSource + xKernel;
|
||||||
|
int yResPadded = ySource + yKernel;
|
||||||
|
|
||||||
|
if (xResPadded != yResPadded)
|
||||||
|
(xResPadded > yResPadded) ? yResPadded = xResPadded : xResPadded = yResPadded;
|
||||||
|
|
||||||
|
// create padded field
|
||||||
|
fftw_complex* padded = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * xResPadded * yResPadded);
|
||||||
|
if (!padded)
|
||||||
|
{
|
||||||
|
cout << " IMAGE: Not enough memory! Try a smaller final image size." << endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// init padded field
|
||||||
|
for (index = 0; index < xResPadded * yResPadded; index++)
|
||||||
|
padded[index][0] = padded[index][1] = 0.0f;
|
||||||
|
index = 0;
|
||||||
|
for (y = 0; y < ySource; y++)
|
||||||
|
for (x = 0; x < xSource; x++, index++)
|
||||||
|
{
|
||||||
|
int paddedIndex = (x + xKernel / 2) + (y + yKernel / 2) * xResPadded;
|
||||||
|
padded[paddedIndex][0] = source[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
// create padded filter
|
||||||
|
fftw_complex* filter = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * xResPadded * yResPadded);
|
||||||
|
if (!filter)
|
||||||
|
{
|
||||||
|
cout << " FILTER: Not enough memory! Try a smaller final image size." << endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// init padded filter
|
||||||
|
for (index = 0; index < xResPadded * yResPadded; index++)
|
||||||
|
filter[index][0] = filter[index][1] = 0.0f;
|
||||||
|
|
||||||
|
// quadrant IV
|
||||||
|
for (y = 0; y < (yHalf + 1); y++)
|
||||||
|
for (x = 0; x < (xHalf + 1); x++)
|
||||||
|
{
|
||||||
|
int filterIndex = x + xHalf + y * xKernel;
|
||||||
|
int fieldIndex = x + (y + yResPadded - (yHalf + 1)) * xResPadded;
|
||||||
|
filter[fieldIndex][0] = kernel[filterIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
// quadrant I
|
||||||
|
for (y = 0; y < yHalf; y++)
|
||||||
|
for (x = 0; x < (xHalf + 1); x++)
|
||||||
|
{
|
||||||
|
int filterIndex = (x + xHalf) + (y + yHalf + 1) * xKernel;
|
||||||
|
int fieldIndex = x + y * xResPadded;
|
||||||
|
filter[fieldIndex][0] = filter[fieldIndex][1] = kernel[filterIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
// quadrant III
|
||||||
|
for (y = 0; y < (yHalf + 1); y++)
|
||||||
|
for (x = 0; x < xHalf; x++)
|
||||||
|
{
|
||||||
|
int filterIndex = x + y * xKernel;
|
||||||
|
int fieldIndex = (x + xResPadded - xHalf) + (y + yResPadded - (yHalf + 1)) * xResPadded;
|
||||||
|
filter[fieldIndex][0] = filter[fieldIndex][1] = kernel[filterIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
// quadrant II
|
||||||
|
for (y = 0; y < yHalf; y++)
|
||||||
|
for (x = 0; x < xHalf; x++)
|
||||||
|
{
|
||||||
|
int filterIndex = x + (y + yHalf + 1) * xKernel;
|
||||||
|
int fieldIndex = (x + xResPadded - xHalf) + y * xResPadded;
|
||||||
|
filter[fieldIndex][0] = filter[fieldIndex][1] = kernel[filterIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
// perform forward FFT on field
|
||||||
|
fftw_complex* paddedTransformed = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * xResPadded * yResPadded);
|
||||||
|
if (!paddedTransformed)
|
||||||
|
{
|
||||||
|
cout << " T-IMAGE: Not enough memory! Try a smaller final image size." << endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
fftw_plan forwardField = fftw_plan_dft_2d(xResPadded, yResPadded, padded, paddedTransformed, FFTW_FORWARD, FFTW_ESTIMATE);
|
||||||
|
fftw_execute(forwardField);
|
||||||
|
|
||||||
|
// perform forward FFT on filter
|
||||||
|
fftw_complex* filterTransformed = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * xResPadded * yResPadded);
|
||||||
|
if (!filterTransformed)
|
||||||
|
{
|
||||||
|
cout << " T-FILTER: Not enough memory! Try a smaller final image size." << endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
fftw_plan forwardFilter = fftw_plan_dft_2d(xResPadded, yResPadded, filter, filterTransformed, FFTW_FORWARD, FFTW_ESTIMATE);
|
||||||
|
fftw_execute(forwardFilter);
|
||||||
|
|
||||||
|
// apply frequency space filter
|
||||||
|
for (index = 0; index < xResPadded * yResPadded; index++)
|
||||||
|
{
|
||||||
|
float newReal = paddedTransformed[index][0] * filterTransformed[index][0] -
|
||||||
|
paddedTransformed[index][1] * filterTransformed[index][1];
|
||||||
|
float newIm = paddedTransformed[index][0] * filterTransformed[index][1] +
|
||||||
|
paddedTransformed[index][1] * filterTransformed[index][0];
|
||||||
|
paddedTransformed[index][0] = newReal;
|
||||||
|
paddedTransformed[index][1] = newIm;
|
||||||
|
}
|
||||||
|
|
||||||
|
// transform back
|
||||||
|
fftw_plan backwardField = fftw_plan_dft_2d(xResPadded, yResPadded, paddedTransformed, padded, FFTW_BACKWARD, FFTW_ESTIMATE);
|
||||||
|
fftw_execute(backwardField);
|
||||||
|
|
||||||
|
// copy back into padded
|
||||||
|
index = 0;
|
||||||
|
for (y = 0; y < ySource; y++)
|
||||||
|
for (x = 0; x < xSource; x++, index++)
|
||||||
|
{
|
||||||
|
int paddedIndex = (x + xKernel / 2) + (y + yKernel / 2) * xResPadded;
|
||||||
|
source[index] = padded[paddedIndex][0];
|
||||||
|
}
|
||||||
|
|
||||||
|
// clean up
|
||||||
|
fftw_free(padded);
|
||||||
|
fftw_free(paddedTransformed);
|
||||||
|
fftw_free(filter);
|
||||||
|
fftw_free(filterTransformed);
|
||||||
|
|
||||||
|
// if normalization is exceeded, renormalize
|
||||||
|
float newMax = 0.0f;
|
||||||
|
for (x = 0; x < xSource * ySource; x++)
|
||||||
|
newMax = (newMax < source[x]) ? source[x] : newMax;
|
||||||
|
if (newMax > maxProduct)
|
||||||
|
{
|
||||||
|
float scale = maxProduct / newMax;
|
||||||
|
for (x = 0; x < xSource * ySource; x++)
|
||||||
|
source[x] *= scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
95
FFT.h
Normal file
95
FFT.h
Normal file
|
|
@ -0,0 +1,95 @@
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// File : FFT.h
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef FFT_H
|
||||||
|
#define FFT_H
|
||||||
|
|
||||||
|
#include <fftw3.h>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
/// \brief Wrapper class for FFTW
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
class FFT
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FFT();
|
||||||
|
virtual ~FFT();
|
||||||
|
/// \brief convolve image and filter using FFTW
|
||||||
|
///
|
||||||
|
/// \param source source image
|
||||||
|
/// \param kernel convolution kernel
|
||||||
|
/// \param xSource width of source image
|
||||||
|
/// \param ySource height of source image
|
||||||
|
/// \param xKernel width of kernel
|
||||||
|
/// \param yKernel height yidth of kernel
|
||||||
|
///
|
||||||
|
/// \return Returns the convolved image in the 'image' array. If the convolve fails, returns false
|
||||||
|
static bool convolve(float* source, float* kernel, int xSource, int ySource, int xKernel, int yKernel);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
20
LumosQuad.sln
Normal file
20
LumosQuad.sln
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 9.00
|
||||||
|
# Visual C++ Express 2005
|
||||||
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LumosQuad", "LumosQuad.vcproj", "{5BC95B61-43C1-46C2-BA59-D95F6C336355}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Win32 = Debug|Win32
|
||||||
|
Release|Win32 = Release|Win32
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{5BC95B61-43C1-46C2-BA59-D95F6C336355}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||||
|
{5BC95B61-43C1-46C2-BA59-D95F6C336355}.Debug|Win32.Build.0 = Debug|Win32
|
||||||
|
{5BC95B61-43C1-46C2-BA59-D95F6C336355}.Release|Win32.ActiveCfg = Release|Win32
|
||||||
|
{5BC95B61-43C1-46C2-BA59-D95F6C336355}.Release|Win32.Build.0 = Release|Win32
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
322
LumosQuad.vcproj
Normal file
322
LumosQuad.vcproj
Normal file
|
|
@ -0,0 +1,322 @@
|
||||||
|
<?xml version="1.0" encoding="Windows-1252"?>
|
||||||
|
<VisualStudioProject
|
||||||
|
ProjectType="Visual C++"
|
||||||
|
Version="8.00"
|
||||||
|
Name="LumosQuad"
|
||||||
|
ProjectGUID="{5BC95B61-43C1-46C2-BA59-D95F6C336355}"
|
||||||
|
RootNamespace="LumosQuad"
|
||||||
|
Keyword="Win32Proj"
|
||||||
|
>
|
||||||
|
<Platforms>
|
||||||
|
<Platform
|
||||||
|
Name="Win32"
|
||||||
|
/>
|
||||||
|
</Platforms>
|
||||||
|
<ToolFiles>
|
||||||
|
</ToolFiles>
|
||||||
|
<Configurations>
|
||||||
|
<Configuration
|
||||||
|
Name="Debug|Win32"
|
||||||
|
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
|
||||||
|
IntermediateDirectory="$(ConfigurationName)"
|
||||||
|
ConfigurationType="1"
|
||||||
|
CharacterSet="1"
|
||||||
|
>
|
||||||
|
<Tool
|
||||||
|
Name="VCPreBuildEventTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCCustomBuildTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCXMLDataGeneratorTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCWebServiceProxyGeneratorTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCMIDLTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
Optimization="0"
|
||||||
|
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
|
||||||
|
MinimalRebuild="true"
|
||||||
|
BasicRuntimeChecks="3"
|
||||||
|
RuntimeLibrary="3"
|
||||||
|
UsePrecompiledHeader="0"
|
||||||
|
WarningLevel="3"
|
||||||
|
Detect64BitPortabilityProblems="true"
|
||||||
|
DebugInformationFormat="4"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCManagedResourceCompilerTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCResourceCompilerTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCPreLinkEventTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCLinkerTool"
|
||||||
|
AdditionalDependencies="kernel32.lib $(NoInherit)"
|
||||||
|
LinkIncremental="2"
|
||||||
|
GenerateDebugInformation="true"
|
||||||
|
SubSystem="1"
|
||||||
|
TargetMachine="1"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCALinkTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCManifestTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCXDCMakeTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCBscMakeTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCFxCopTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCAppVerifierTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCWebDeploymentTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCPostBuildEventTool"
|
||||||
|
/>
|
||||||
|
</Configuration>
|
||||||
|
<Configuration
|
||||||
|
Name="Release|Win32"
|
||||||
|
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
|
||||||
|
IntermediateDirectory="$(ConfigurationName)"
|
||||||
|
ConfigurationType="1"
|
||||||
|
CharacterSet="1"
|
||||||
|
WholeProgramOptimization="1"
|
||||||
|
>
|
||||||
|
<Tool
|
||||||
|
Name="VCPreBuildEventTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCCustomBuildTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCXMLDataGeneratorTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCWebServiceProxyGeneratorTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCMIDLTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
FavorSizeOrSpeed="1"
|
||||||
|
PreprocessorDefinitions="GLUT_BUILDING_LIB;_CRT_SECURE_NO_DEPRECATE;NDEBUG;WIN32;_CONSOLE"
|
||||||
|
RuntimeLibrary="2"
|
||||||
|
EnableEnhancedInstructionSet="2"
|
||||||
|
UsePrecompiledHeader="0"
|
||||||
|
WarningLevel="3"
|
||||||
|
Detect64BitPortabilityProblems="true"
|
||||||
|
DebugInformationFormat="3"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCManagedResourceCompilerTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCResourceCompilerTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCPreLinkEventTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCLinkerTool"
|
||||||
|
AdditionalOptions="/FORCE:MULTIPLE /fixed:no"
|
||||||
|
AdditionalDependencies="opengl32.lib glu32.lib glut32.lib IlmThread.lib Iex.lib IlmImf.lib Half.lib zdll.lib"
|
||||||
|
LinkIncremental="1"
|
||||||
|
GenerateDebugInformation="true"
|
||||||
|
SubSystem="1"
|
||||||
|
OptimizeReferences="2"
|
||||||
|
EnableCOMDATFolding="2"
|
||||||
|
TargetMachine="1"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCALinkTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCManifestTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCXDCMakeTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCBscMakeTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCFxCopTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCAppVerifierTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCWebDeploymentTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCPostBuildEventTool"
|
||||||
|
/>
|
||||||
|
</Configuration>
|
||||||
|
</Configurations>
|
||||||
|
<References>
|
||||||
|
</References>
|
||||||
|
<Files>
|
||||||
|
<Filter
|
||||||
|
Name="Source Files"
|
||||||
|
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
|
||||||
|
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
|
||||||
|
>
|
||||||
|
<File
|
||||||
|
RelativePath=".\APSF.cpp"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\APSF.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\BlueNoise\BLUE_NOISE.cpp"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\CELL.cpp"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\CELL.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\CG_SOLVER.cpp"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\CG_SOLVER_SSE.cpp"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\DAG.cpp"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\DAG.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\EXR.cpp"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\EXR.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\FFT.cpp"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\FFT.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\imdebug.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\main.cpp"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\ppm\ppm.cpp"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\ppm\ppm.hpp"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\QUAD_DBM_2D.cpp"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\QUAD_DBM_2D.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\QUAD_POISSON.cpp"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\QUAD_POISSON.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\BlueNoise\RangeList.cpp"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\BlueNoise\RNG.cpp"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\BlueNoise\ScallopedSector.cpp"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
</Filter>
|
||||||
|
<Filter
|
||||||
|
Name="Header Files"
|
||||||
|
Filter="h;hpp;hxx;hm;inl;inc;xsd"
|
||||||
|
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
|
||||||
|
>
|
||||||
|
<File
|
||||||
|
RelativePath=".\BlueNoise\BLUE_NOISE.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\CG_SOLVER.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\CG_SOLVER_SSE.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\BlueNoise\RangeList.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\BlueNoise\RNG.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\BlueNoise\ScallopedSector.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\BlueNoise\WeightedDiscretePDF.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
</Filter>
|
||||||
|
<Filter
|
||||||
|
Name="Resource Files"
|
||||||
|
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
|
||||||
|
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
|
||||||
|
>
|
||||||
|
</Filter>
|
||||||
|
</Files>
|
||||||
|
<Globals>
|
||||||
|
</Globals>
|
||||||
|
</VisualStudioProject>
|
||||||
52
Makefile
Normal file
52
Makefile
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
all: build/lumosquad
|
||||||
|
|
||||||
|
build/lumosquad: build/lumosquad.o build/APSF.o build/CELL.o build/CG_SOLVER_SSE.o build/CG_SOLVER.o build/DAG.o build/EXR.o build/FFT.o build/QUAD_DBM_2D.o build/QUAD_POISSON.o build/ppm.o build/BLUE_NOISE.o build/RangeList.o build/RNG.o build/ScallopedSector.o build/WeightedDiscretePDF.o
|
||||||
|
g++ -g -o build/lumosquad build/*.o -lGL -lglut -lGLU -lfftw3 -lOpenEXR -lImath
|
||||||
|
|
||||||
|
build/lumosquad.o: main.cpp
|
||||||
|
g++ -g -O3 -o build/lumosquad.o -c main.cpp -I/usr/include/Imath
|
||||||
|
|
||||||
|
build/APSF.o:
|
||||||
|
g++ -g -O3 -o build/APSF.o -c APSF.cpp
|
||||||
|
|
||||||
|
build/CELL.o:
|
||||||
|
g++ -g -O3 -o build/CELL.o -c CELL.cpp
|
||||||
|
|
||||||
|
build/CG_SOLVER_SSE.o:
|
||||||
|
g++ -g -O3 -o build/CG_SOLVER_SSE.o -c CG_SOLVER_SSE.cpp
|
||||||
|
|
||||||
|
build/CG_SOLVER.o:
|
||||||
|
g++ -g -O3 -o build/CG_SOLVER.o -c CG_SOLVER.cpp
|
||||||
|
|
||||||
|
build/DAG.o:
|
||||||
|
g++ -g -O3 -o build/DAG.o -c DAG.cpp
|
||||||
|
|
||||||
|
build/EXR.o:
|
||||||
|
g++ -g -O3 -o build/EXR.o -c EXR.cpp -I/usr/include/Imath
|
||||||
|
|
||||||
|
build/FFT.o:
|
||||||
|
g++ -g -O3 -o build/FFT.o -c FFT.cpp
|
||||||
|
|
||||||
|
build/QUAD_DBM_2D.o:
|
||||||
|
g++ -g -O3 -o build/QUAD_DBM_2D.o -c QUAD_DBM_2D.cpp
|
||||||
|
|
||||||
|
build/QUAD_POISSON.o:
|
||||||
|
g++ -g -O3 -o build/QUAD_POISSON.o -c QUAD_POISSON.cpp
|
||||||
|
|
||||||
|
build/ppm.o:
|
||||||
|
g++ -g -O3 -o build/ppm.o -c ppm/ppm.cpp
|
||||||
|
|
||||||
|
build/BLUE_NOISE.o:
|
||||||
|
g++ -g -O3 -o build/BLUE_NOISE.o -c BlueNoise/BLUE_NOISE.cpp
|
||||||
|
|
||||||
|
build/RangeList.o:
|
||||||
|
g++ -g -O3 -o build/RangeList.o -c BlueNoise/RangeList.cpp
|
||||||
|
|
||||||
|
build/RNG.o:
|
||||||
|
g++ -g -O3 -o build/RNG.o -c BlueNoise/RNG.cpp
|
||||||
|
|
||||||
|
build/ScallopedSector.o:
|
||||||
|
g++ -g -O3 -o build/ScallopedSector.o -c BlueNoise/ScallopedSector.cpp
|
||||||
|
|
||||||
|
build/WeightedDiscretePDF.o:
|
||||||
|
g++ -g -O3 -o build/WeightedDiscretePDF.o -c BlueNoise/WeightedDiscretePDF.cpp
|
||||||
461
QUAD_DBM_2D.cpp
Normal file
461
QUAD_DBM_2D.cpp
Normal file
|
|
@ -0,0 +1,461 @@
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// File : QUAD_DBM_2D.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_DBM_2D.h"
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// Construction/Destruction
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
QUAD_DBM_2D::QUAD_DBM_2D(int xRes, int yRes, int iterations) :
|
||||||
|
_xRes(xRes),
|
||||||
|
_yRes(yRes),
|
||||||
|
_bottomHit(0),
|
||||||
|
_iterations(iterations),
|
||||||
|
_quadPoisson(NULL),
|
||||||
|
_dag(NULL),
|
||||||
|
_skips(10),
|
||||||
|
_twister(123456)
|
||||||
|
{
|
||||||
|
allocate();
|
||||||
|
_dag = new DAG(_xRes, _yRes);
|
||||||
|
|
||||||
|
// calculate dimensions
|
||||||
|
_dx = 1.0f / (float)_xRes;
|
||||||
|
_dy = 1.0f / (float)_yRes;
|
||||||
|
if (_dx < _dy) _dy = _dx;
|
||||||
|
else _dx = _dy;
|
||||||
|
|
||||||
|
_maxRes = _xRes * _yRes;
|
||||||
|
}
|
||||||
|
|
||||||
|
QUAD_DBM_2D::~QUAD_DBM_2D()
|
||||||
|
{
|
||||||
|
deallocate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QUAD_DBM_2D::allocate()
|
||||||
|
{
|
||||||
|
_quadPoisson = new QUAD_POISSON(_xRes, _yRes, _iterations);
|
||||||
|
_xRes = _yRes = _quadPoisson->maxRes();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QUAD_DBM_2D::deallocate()
|
||||||
|
{
|
||||||
|
if (_dag) delete _dag;
|
||||||
|
if (_quadPoisson) delete _quadPoisson;
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// check neighbors for any candidate nodes
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
void QUAD_DBM_2D::checkForCandidates(CELL* cell)
|
||||||
|
{
|
||||||
|
int maxDepth = _quadPoisson->maxDepth();
|
||||||
|
|
||||||
|
CELL* north = cell->northNeighbor();
|
||||||
|
if (north) {
|
||||||
|
if (north->depth == maxDepth) {
|
||||||
|
if (!north->candidate) {
|
||||||
|
_candidates.push_back(north);
|
||||||
|
north->candidate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
CELL* northeast = north->eastNeighbor();
|
||||||
|
if (northeast && !northeast->candidate) {
|
||||||
|
_candidates.push_back(northeast);
|
||||||
|
northeast->candidate = true;
|
||||||
|
}
|
||||||
|
CELL* northwest = north->westNeighbor();
|
||||||
|
if (northwest && !northwest->candidate) {
|
||||||
|
_candidates.push_back(northwest);
|
||||||
|
northwest->candidate = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CELL* east = cell->eastNeighbor();
|
||||||
|
if (east && !east->candidate) {
|
||||||
|
_candidates.push_back(east);
|
||||||
|
east->candidate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
CELL* south = cell->southNeighbor();
|
||||||
|
if (south) {
|
||||||
|
if (!south->candidate) {
|
||||||
|
_candidates.push_back(south);
|
||||||
|
south->candidate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
CELL* southeast = south->eastNeighbor();
|
||||||
|
if (southeast && !southeast->candidate) {
|
||||||
|
_candidates.push_back(southeast);
|
||||||
|
southeast->candidate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
CELL* southwest = south->westNeighbor();
|
||||||
|
if (southwest && !southwest->candidate) {
|
||||||
|
_candidates.push_back(southwest);
|
||||||
|
southwest->candidate = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CELL* west = cell->westNeighbor();
|
||||||
|
if (west && !west->candidate) {
|
||||||
|
_candidates.push_back(west);
|
||||||
|
west->candidate = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// add particle to the aggregate
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
bool QUAD_DBM_2D::addParticle()
|
||||||
|
{
|
||||||
|
static float invSqrtTwo = 1.0f / sqrt(2.0f);
|
||||||
|
static int totalParticles = 0;
|
||||||
|
static int skipSolve = 0;
|
||||||
|
|
||||||
|
// compute the potential
|
||||||
|
int iterations = 0;
|
||||||
|
if (!skipSolve)
|
||||||
|
iterations = _quadPoisson->solve();
|
||||||
|
skipSolve++;
|
||||||
|
if (skipSolve == _skips) skipSolve = 0;
|
||||||
|
|
||||||
|
// construct probability distribution
|
||||||
|
vector<float> probabilities;
|
||||||
|
float totalPotential = 0.0f;
|
||||||
|
for (int x = 0; x < _candidates.size(); x++)
|
||||||
|
{
|
||||||
|
if (_candidates[x]->candidate)
|
||||||
|
{
|
||||||
|
probabilities.push_back(_candidates[x]->potential);
|
||||||
|
totalPotential += _candidates[x]->potential;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
probabilities.push_back(0.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
// get all the candidates
|
||||||
|
// if none are left, stop
|
||||||
|
if (_candidates.size() == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if there is not enough potential, go Brownian
|
||||||
|
int toAddIndex = 0;
|
||||||
|
if (totalPotential < 1e-8)
|
||||||
|
toAddIndex = _candidates.size() * _twister.getDoubleLR();
|
||||||
|
// else follow DBM algorithm
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// add a neighbor
|
||||||
|
float random = _twister.getDoubleLR();
|
||||||
|
float invTotalPotential = 1.0f / totalPotential;
|
||||||
|
float potentialSeen = probabilities[0] * invTotalPotential;
|
||||||
|
while ((potentialSeen < random) && (toAddIndex < _candidates.size()))
|
||||||
|
{
|
||||||
|
toAddIndex++;
|
||||||
|
potentialSeen += probabilities[toAddIndex] * invTotalPotential;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_candidates[toAddIndex]->boundary = true;
|
||||||
|
_candidates[toAddIndex]->potential = 0.0f;
|
||||||
|
_candidates[toAddIndex]->state = NEGATIVE;
|
||||||
|
|
||||||
|
CELL* neighbor = NULL;
|
||||||
|
CELL* added = _candidates[toAddIndex];
|
||||||
|
CELL* north = added->northNeighbor();
|
||||||
|
if (north)
|
||||||
|
{
|
||||||
|
if (north->state == NEGATIVE)
|
||||||
|
neighbor = north;
|
||||||
|
CELL* northeast = north->eastNeighbor();
|
||||||
|
if (northeast && northeast->state == NEGATIVE)
|
||||||
|
neighbor = northeast;
|
||||||
|
CELL* northwest = north->westNeighbor();
|
||||||
|
if (northwest && northwest->state == NEGATIVE)
|
||||||
|
neighbor = northwest;
|
||||||
|
}
|
||||||
|
CELL* east = added->eastNeighbor();
|
||||||
|
if (east && east->state == NEGATIVE)
|
||||||
|
neighbor = east;
|
||||||
|
|
||||||
|
CELL* south = added->southNeighbor();
|
||||||
|
if (south)
|
||||||
|
{
|
||||||
|
if (south->state == NEGATIVE)
|
||||||
|
neighbor = south;
|
||||||
|
CELL* southeast = south->eastNeighbor();
|
||||||
|
if (southeast && southeast->state == NEGATIVE)
|
||||||
|
neighbor = southeast;
|
||||||
|
CELL* southwest = south->westNeighbor();
|
||||||
|
if (southwest && southwest->state == NEGATIVE)
|
||||||
|
neighbor = southwest;
|
||||||
|
}
|
||||||
|
CELL* west = added->westNeighbor();
|
||||||
|
if (west && west->state == NEGATIVE)
|
||||||
|
neighbor = west;
|
||||||
|
|
||||||
|
// insert it as a node for bookkeeping
|
||||||
|
_quadPoisson->insert(added->center[0], added->center[1]);
|
||||||
|
checkForCandidates(added);
|
||||||
|
|
||||||
|
// insert into the dag
|
||||||
|
int newIndex = (int)(added->center[0] * _xRes) +
|
||||||
|
(int)(added->center[1] * _yRes) * _xRes;
|
||||||
|
int neighborIndex = (int)(neighbor->center[0] * _xRes) +
|
||||||
|
(int)(neighbor->center[1] * _yRes) * _xRes;
|
||||||
|
_dag->addSegment(newIndex, neighborIndex);
|
||||||
|
|
||||||
|
totalParticles++;
|
||||||
|
if (!(totalParticles % 200))
|
||||||
|
cout << " " << totalParticles;
|
||||||
|
|
||||||
|
hitGround(added);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// hit ground yet?
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
bool QUAD_DBM_2D::hitGround(CELL* cell)
|
||||||
|
{
|
||||||
|
if (_bottomHit)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (!cell)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
bool hit = false;
|
||||||
|
if (cell->northNeighbor())
|
||||||
|
{
|
||||||
|
CELL* north = cell->northNeighbor();
|
||||||
|
if (north->state == POSITIVE)
|
||||||
|
hit = true;
|
||||||
|
if (north->eastNeighbor()->state == POSITIVE)
|
||||||
|
hit = true;
|
||||||
|
if (north->westNeighbor()->state == POSITIVE)
|
||||||
|
hit = true;
|
||||||
|
}
|
||||||
|
if (cell->eastNeighbor())
|
||||||
|
if (cell->eastNeighbor()->state == POSITIVE)
|
||||||
|
hit = true;
|
||||||
|
if (cell->southNeighbor())
|
||||||
|
{
|
||||||
|
CELL* south = cell->southNeighbor();
|
||||||
|
if (south->state == POSITIVE)
|
||||||
|
hit = true;
|
||||||
|
if (south->eastNeighbor()->state == POSITIVE)
|
||||||
|
hit = true;
|
||||||
|
if (south->westNeighbor()->state == POSITIVE)
|
||||||
|
hit = true;
|
||||||
|
}
|
||||||
|
if (cell->westNeighbor())
|
||||||
|
if (cell->westNeighbor()->state == POSITIVE)
|
||||||
|
hit = true;
|
||||||
|
|
||||||
|
if (hit)
|
||||||
|
{
|
||||||
|
_bottomHit = (int)(cell->center[0] * _xRes) +
|
||||||
|
(int)(cell->center[1] * _yRes) * _xRes;
|
||||||
|
_dag->buildLeader(_bottomHit);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// drawing functions
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
void QUAD_DBM_2D::draw() {
|
||||||
|
glPushMatrix();
|
||||||
|
glTranslatef(-0.5, -0.5, 0);
|
||||||
|
|
||||||
|
list<CELL*> leaves;
|
||||||
|
list<CELL*>::iterator cellIterator = leaves.begin();
|
||||||
|
_quadPoisson->getAllLeaves(leaves);
|
||||||
|
for (cellIterator = leaves.begin(); cellIterator != leaves.end(); cellIterator++)
|
||||||
|
{
|
||||||
|
float color = (*cellIterator)->potential;
|
||||||
|
|
||||||
|
if ((*cellIterator)->boundary) {
|
||||||
|
if (color <= 0.0f)
|
||||||
|
_quadPoisson->drawCell(*cellIterator, 0,0,0);
|
||||||
|
else
|
||||||
|
_quadPoisson->drawCell(*cellIterator, 0,0,color);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
_quadPoisson->drawCell(*cellIterator, color, 0,0);
|
||||||
|
}
|
||||||
|
_quadPoisson->draw(NULL);
|
||||||
|
|
||||||
|
glPopMatrix();
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// read in attractors from an image
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
bool QUAD_DBM_2D::readImage(unsigned char* initial,
|
||||||
|
unsigned char* attractors,
|
||||||
|
unsigned char* repulsors,
|
||||||
|
unsigned char* terminators,
|
||||||
|
int xRes, int yRes)
|
||||||
|
{
|
||||||
|
_dag->inputWidth() = xRes;
|
||||||
|
_dag->inputHeight() = yRes;
|
||||||
|
|
||||||
|
bool initialFound = false;
|
||||||
|
bool terminateFound = false;
|
||||||
|
|
||||||
|
int index = 0;
|
||||||
|
for (int y = 0; y < yRes; y++)
|
||||||
|
for (int x = 0; x < xRes; x++, index++)
|
||||||
|
{
|
||||||
|
// insert initial condition
|
||||||
|
if (initial[index])
|
||||||
|
{
|
||||||
|
// insert something
|
||||||
|
CELL* negative = _quadPoisson->insert(x, y);
|
||||||
|
negative->boundary = true;
|
||||||
|
negative->potential = 0.0f;
|
||||||
|
negative->state = NEGATIVE;
|
||||||
|
negative->candidate = true;
|
||||||
|
|
||||||
|
checkForCandidates(negative);
|
||||||
|
|
||||||
|
initialFound = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// insert attractors
|
||||||
|
if (attractors[index])
|
||||||
|
{
|
||||||
|
// insert something
|
||||||
|
CELL* positive = _quadPoisson->insert(x, y);
|
||||||
|
positive->boundary = true;
|
||||||
|
positive->potential = 1.0f;
|
||||||
|
positive->state = ATTRACTOR;
|
||||||
|
positive->candidate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// insert repulsors
|
||||||
|
if (repulsors[index])
|
||||||
|
{
|
||||||
|
// only insert the repulsor if it is the edge of a repulsor
|
||||||
|
bool edge = false;
|
||||||
|
|
||||||
|
if (x != 0)
|
||||||
|
{
|
||||||
|
if (!repulsors[index - 1]) edge = true;
|
||||||
|
if (y != 0 && !repulsors[index - xRes - 1]) edge = true;
|
||||||
|
if (y != yRes - 1 && !repulsors[index + xRes - 1]) edge = true;
|
||||||
|
}
|
||||||
|
if (x != _xRes - 1)
|
||||||
|
{
|
||||||
|
if (!repulsors[index + 1]) edge = true;
|
||||||
|
if (y != 0 && !repulsors[index - xRes + 1]) edge = true;
|
||||||
|
if (y != yRes - 1 && !repulsors[index + xRes + 1]) edge = true;
|
||||||
|
}
|
||||||
|
if (y != 0 && !repulsors[index - xRes]) edge = true;
|
||||||
|
if (y != yRes - 1 && !repulsors[index + xRes]) edge = true;
|
||||||
|
|
||||||
|
if (edge)
|
||||||
|
{
|
||||||
|
// insert something
|
||||||
|
CELL* negative = _quadPoisson->insert(x, y);
|
||||||
|
negative->boundary = true;
|
||||||
|
negative->potential = 0.0f;
|
||||||
|
negative->state = REPULSOR;
|
||||||
|
negative->candidate = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// insert terminators
|
||||||
|
if (terminators[index])
|
||||||
|
{
|
||||||
|
// insert something
|
||||||
|
CELL* positive = _quadPoisson->insert(x, y);
|
||||||
|
positive->boundary = true;
|
||||||
|
positive->potential = 1.0f;
|
||||||
|
positive->state = POSITIVE;
|
||||||
|
positive->candidate = true;
|
||||||
|
|
||||||
|
terminateFound = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!initialFound) {
|
||||||
|
cout << " The lightning does not start anywhere! " << endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!terminateFound) {
|
||||||
|
cout << " The lightning does not end anywhere! " << endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
196
QUAD_DBM_2D.h
Normal file
196
QUAD_DBM_2D.h
Normal file
|
|
@ -0,0 +1,196 @@
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// File : QUAD_DBM_2D.h
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef QUAD_DBM_2D_H
|
||||||
|
#define QUAD_DBM_2D_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <GL/glut.h>
|
||||||
|
#include "DAG.h"
|
||||||
|
#include "QUAD_POISSON.h"
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
/// \brief Quadtree DBM solver. This is the highest level class.
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
class QUAD_DBM_2D
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// \brief DBM constructor
|
||||||
|
///
|
||||||
|
/// \param xRes maximum x resolution
|
||||||
|
/// \param yRes maximum y resolution
|
||||||
|
/// \param iterations maximum conjugate gradient iterations
|
||||||
|
QUAD_DBM_2D(int xRes = 128, int yRes = 128, int iterations = 10);
|
||||||
|
|
||||||
|
//! destructor
|
||||||
|
virtual ~QUAD_DBM_2D();
|
||||||
|
|
||||||
|
//! add to aggregate
|
||||||
|
bool addParticle();
|
||||||
|
|
||||||
|
/// \brief Hit ground yet?
|
||||||
|
/// \return returns true if a terminator as already been hit
|
||||||
|
bool hitGround(CELL* cell = NULL);
|
||||||
|
|
||||||
|
//! draw the quadtree cells to OpenGL
|
||||||
|
void draw();
|
||||||
|
|
||||||
|
//! draw the DAG to OpenGL
|
||||||
|
void drawSegments() {
|
||||||
|
glLineWidth(1.0f);
|
||||||
|
glPushMatrix();
|
||||||
|
glTranslatef(-0.5f, -0.5f, 0.0f);
|
||||||
|
_dag->draw();
|
||||||
|
glPopMatrix();
|
||||||
|
};
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
// file IO
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
//! write everything to a file
|
||||||
|
void writeFields(const char* filename);
|
||||||
|
|
||||||
|
//! read everything from a file
|
||||||
|
void readFields(const char* filename);
|
||||||
|
|
||||||
|
/// \brief read in control parameters from an input file
|
||||||
|
///
|
||||||
|
/// \param initial initial pixels of lightning
|
||||||
|
/// \param attractors pixels that attract the lightning
|
||||||
|
/// \param repulsors pixels that repulse the lightning
|
||||||
|
/// \param terminators pixels that halt the simulation if hit
|
||||||
|
/// \param xRes x resolution of the image
|
||||||
|
/// \param yRes y resolution of the image
|
||||||
|
///
|
||||||
|
/// \return Returns false if it finds something wrong with the images
|
||||||
|
bool readImage(unsigned char* initial,
|
||||||
|
unsigned char* attractors,
|
||||||
|
unsigned char* repulsors,
|
||||||
|
unsigned char* terminators,
|
||||||
|
int xRes, int yRes);
|
||||||
|
|
||||||
|
//! read in a new DAG
|
||||||
|
void readDAG(const char* filename) { _dag->read(filename); };
|
||||||
|
|
||||||
|
//! write out the current DAG
|
||||||
|
void writeDAG(const char* filename) { _dag->write(filename); };
|
||||||
|
|
||||||
|
/// \brief render to a software-only buffer
|
||||||
|
///
|
||||||
|
/// \param scale a (scale * xRes) x (scale * yRes) image is rendered
|
||||||
|
float*& renderOffscreen(int scale = 1) { return _dag->drawOffscreen(scale); };
|
||||||
|
|
||||||
|
//! access the DBM x resolution
|
||||||
|
int xRes() { return _xRes; };
|
||||||
|
//! access the DBM y resolution
|
||||||
|
int yRes() { return _yRes; };
|
||||||
|
//! access the DAG x resolution
|
||||||
|
int xDagRes() { return _dag->xRes(); };
|
||||||
|
//! access the DAG y resolution
|
||||||
|
int yDagRes() { return _dag->yRes(); };
|
||||||
|
//! access the x resolution of the input image
|
||||||
|
int inputWidth() { return _dag->inputWidth(); };
|
||||||
|
//! access the y resolution of the input image
|
||||||
|
int inputHeight() { return _dag->inputHeight(); };
|
||||||
|
|
||||||
|
private:
|
||||||
|
void allocate();
|
||||||
|
void deallocate();
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// dielectric breakdown model components
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// field dimensions
|
||||||
|
int _xRes;
|
||||||
|
int _yRes;
|
||||||
|
int _maxRes;
|
||||||
|
float _dx;
|
||||||
|
float _dy;
|
||||||
|
int _iterations;
|
||||||
|
|
||||||
|
// which cell did it hit bottom with?
|
||||||
|
int _bottomHit;
|
||||||
|
|
||||||
|
DAG* _dag;
|
||||||
|
|
||||||
|
QUAD_POISSON* _quadPoisson;
|
||||||
|
|
||||||
|
// current candidate list
|
||||||
|
vector<CELL*> _candidates;
|
||||||
|
|
||||||
|
// check if any of the neighbors of cell should be added to the
|
||||||
|
// candidate list
|
||||||
|
void checkForCandidates(CELL* cell);
|
||||||
|
|
||||||
|
// number of particles to add before doing another Poisson solve
|
||||||
|
int _skips;
|
||||||
|
|
||||||
|
// Mersenne Twister
|
||||||
|
RNG _twister;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
620
QUAD_POISSON.cpp
Normal file
620
QUAD_POISSON.cpp
Normal file
|
|
@ -0,0 +1,620 @@
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
190
QUAD_POISSON.h
Normal file
190
QUAD_POISSON.h
Normal file
|
|
@ -0,0 +1,190 @@
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// File : QUAD_POISSON.h
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef QUAD_POISSON_H
|
||||||
|
#define QUAD_POISSON_H
|
||||||
|
|
||||||
|
#include <GL/glut.h>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include "CELL.h"
|
||||||
|
#include <list>
|
||||||
|
#include "CG_SOLVER.h"
|
||||||
|
#include "CG_SOLVER_SSE.h"
|
||||||
|
#include "BlueNoise/BLUE_NOISE.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
/// \brief Quadtree Poisson solver
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
class QUAD_POISSON
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// \brief quadtree constructor
|
||||||
|
///
|
||||||
|
/// \param xRes maximum x resolution
|
||||||
|
/// \param yRes maximum y resolution
|
||||||
|
/// \param iterations maximum conjugate gradient iterations
|
||||||
|
QUAD_POISSON(int xRes,
|
||||||
|
int yRes,
|
||||||
|
int iterations = 10);
|
||||||
|
|
||||||
|
//! destructor
|
||||||
|
virtual ~QUAD_POISSON();
|
||||||
|
|
||||||
|
/// \brief OpenGL drawing function
|
||||||
|
///
|
||||||
|
/// \param cell internally used param, should always be NULL externally
|
||||||
|
void draw(CELL* cell = NULL);
|
||||||
|
|
||||||
|
/// \brief Draw a single OpenGL cell
|
||||||
|
///
|
||||||
|
/// \param cell quadtree cell to draw
|
||||||
|
/// \param r red intensity to draw
|
||||||
|
/// \param g green intensity to draw
|
||||||
|
/// \param b blue intensity to draw
|
||||||
|
void drawCell(CELL* cell,
|
||||||
|
float r = 1.0f,
|
||||||
|
float g = 0.0f,
|
||||||
|
float b = 0.0f);
|
||||||
|
|
||||||
|
//! Solve the Poisson problem
|
||||||
|
int solve();
|
||||||
|
|
||||||
|
/// \brief insert point at maximum subdivision level
|
||||||
|
///
|
||||||
|
/// \param xPos x position to insert at
|
||||||
|
/// \param yPos y position to insert at
|
||||||
|
CELL* insert(float xPos, float yPos);
|
||||||
|
|
||||||
|
/// \brief insert point at maximum subdivision level
|
||||||
|
///
|
||||||
|
/// \param xPos grid cell x index to insert
|
||||||
|
/// \param yPos grid cell y index to insert
|
||||||
|
CELL* insert(int xPos, int yPos) {
|
||||||
|
return insert((float)xPos / _maxRes, (float)yPos / _maxRes);
|
||||||
|
};
|
||||||
|
|
||||||
|
/// \brief get all the leaf nodes
|
||||||
|
/// \return Leaves are returned in the 'leaves' param
|
||||||
|
void getAllLeaves(list<CELL*>& leaves, CELL* currentCell = NULL);
|
||||||
|
|
||||||
|
/// \brief get all the leaf nodes at finest subdivision level
|
||||||
|
/// \return Leaves are returned in the 'leaves' param
|
||||||
|
list<CELL*>& getSmallestLeaves() { return _smallestLeaves; };
|
||||||
|
|
||||||
|
//! maximum resolution accessor
|
||||||
|
int& maxRes() { return _maxRes; }
|
||||||
|
|
||||||
|
//! maximum depth accessor
|
||||||
|
int& maxDepth() { return _maxDepth; };
|
||||||
|
|
||||||
|
//! get leaf at coordinate (x,y)
|
||||||
|
CELL* getLeaf(float xPos, float yPos);
|
||||||
|
|
||||||
|
private:
|
||||||
|
//! root of the quadtree
|
||||||
|
CELL* _root;
|
||||||
|
|
||||||
|
//! maximum resolution of quadtree
|
||||||
|
int _maxRes;
|
||||||
|
|
||||||
|
//! maxmimum depth of quadtree
|
||||||
|
int _maxDepth;
|
||||||
|
|
||||||
|
//! dependant leaves
|
||||||
|
list<CELL*> _emptyLeaves;
|
||||||
|
|
||||||
|
//! smallest leaves
|
||||||
|
list<CELL*> _smallestLeaves;
|
||||||
|
|
||||||
|
//! current Poisson solver
|
||||||
|
CG_SOLVER* _solver;
|
||||||
|
|
||||||
|
//! balance quadtree
|
||||||
|
void balance();
|
||||||
|
|
||||||
|
//! get the leaf nodes not on the boundary
|
||||||
|
void getEmptyLeaves(list<CELL*>& leaves, CELL* currentCell = NULL);
|
||||||
|
|
||||||
|
//! build the neighbor lists of the cells
|
||||||
|
void buildNeighbors();
|
||||||
|
|
||||||
|
//! delete ghost cells
|
||||||
|
void deleteGhosts(CELL* currentCell = NULL);
|
||||||
|
|
||||||
|
//! Blue noise function
|
||||||
|
BLUE_NOISE* _noiseFunc;
|
||||||
|
|
||||||
|
//! Blue noise sample locations
|
||||||
|
bool* _noise;
|
||||||
|
|
||||||
|
//! check if a cell hits a noise node
|
||||||
|
void setNoise(CELL* cell);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
10
README.md
Normal file
10
README.md
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
# LumosQuad
|
||||||
|
|
||||||
|
[Original code source](https://gamma.cs.unc.edu/FAST_LIGHTNING/lumos.html) slightly modified to work on Debian.
|
||||||
|
|
||||||
|
Packages to install: `freeglut3-dev libfftw3-dev libopenexr-dev libopengl-dev`
|
||||||
|
|
||||||
|
make
|
||||||
|
./build/lumosquad examples/spine.ppm o.exr
|
||||||
|
|
||||||
|
EXR files are images that can be opened by Gimp.
|
||||||
BIN
examples/hint.ppm
Normal file
BIN
examples/hint.ppm
Normal file
Binary file not shown.
BIN
examples/nopath.ppm
Normal file
BIN
examples/nopath.ppm
Normal file
Binary file not shown.
BIN
examples/spine.lightning
Normal file
BIN
examples/spine.lightning
Normal file
Binary file not shown.
BIN
examples/spine.ppm
Normal file
BIN
examples/spine.ppm
Normal file
Binary file not shown.
BIN
examples/spineless.ppm
Normal file
BIN
examples/spineless.ppm
Normal file
Binary file not shown.
BIN
examples/y-second.ppm
Normal file
BIN
examples/y-second.ppm
Normal file
Binary file not shown.
BIN
examples/y.ppm
Normal file
BIN
examples/y.ppm
Normal file
Binary file not shown.
408
main.cpp
Normal file
408
main.cpp
Normal file
|
|
@ -0,0 +1,408 @@
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// File : main.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.
|
||||||
|
//
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
///
|
||||||
|
/// \mainpage Fast Animation of Lightning Using An Adaptive Mesh
|
||||||
|
/// \section Introduction
|
||||||
|
///
|
||||||
|
/// This project is an implementation of the paper
|
||||||
|
/// <b><em>Fast Animation of Lightning Using An Adaptive Mesh</em></b>. It
|
||||||
|
/// includes both the simulation and rendering components described in that paper.
|
||||||
|
///
|
||||||
|
/// Several pieces of software are used in this project that the respective
|
||||||
|
/// authors were kind enough to make freely available:
|
||||||
|
///
|
||||||
|
/// <UL>
|
||||||
|
/// <LI> <A HREF = "http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html">Mersenne twister</A>
|
||||||
|
/// (Thanks to Makoto Matsumoto)
|
||||||
|
/// <LI> <A HREF = "http://www.fftw.org/">FFTW</A>
|
||||||
|
/// (Thanks to Matteo Frigo and Steven G. Johnson)
|
||||||
|
/// <LI> <A HREF = "http://www.openexr.com/">OpenEXR</A>
|
||||||
|
/// (Thanks to ILM)
|
||||||
|
/// <LI> <A HREF = "http://www.cs.unc.edu/~walk/software/glvu/">GLVU</A>
|
||||||
|
/// (Thanks to Walkthru)
|
||||||
|
/// <LI> <A HREF = "http://www.cs.virginia.edu/~gfx/pubs/antimony/">Antimony</A>
|
||||||
|
/// (Thanks to Daniel Dunbar and Greg Humphreys)
|
||||||
|
/// </UL>
|
||||||
|
///
|
||||||
|
/// <em>Theodore Kim, kim@cs.unc.edu, October 2006</em>
|
||||||
|
///
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#define COMMAND_LINE_VERSION 1
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include "ppm/ppm.hpp"
|
||||||
|
#include "APSF.h"
|
||||||
|
#include "FFT.h"
|
||||||
|
#include "QUAD_DBM_2D.h"
|
||||||
|
#include "EXR.h"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// globals
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
int iterations = 10;
|
||||||
|
static QUAD_DBM_2D* potential = new QUAD_DBM_2D(256, 256, iterations);
|
||||||
|
APSF apsf(512);
|
||||||
|
|
||||||
|
// input image info
|
||||||
|
int inputWidth = -1;
|
||||||
|
int inputHeight = -1;
|
||||||
|
|
||||||
|
// input params
|
||||||
|
string inputFile;
|
||||||
|
string outputFile;
|
||||||
|
|
||||||
|
// image scale
|
||||||
|
float scale = 5;
|
||||||
|
|
||||||
|
// pause the simulation?
|
||||||
|
bool pause = false;
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// render the glow
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
void renderGlow(string filename, int scale = 1)
|
||||||
|
{
|
||||||
|
int w = potential->xDagRes() * scale;
|
||||||
|
int h = potential->yDagRes() * scale;
|
||||||
|
|
||||||
|
// draw the DAG
|
||||||
|
float*& source = potential->renderOffscreen(scale);
|
||||||
|
|
||||||
|
// if there is no input dimensions specified, else there were input
|
||||||
|
// image dimensions, so crop it
|
||||||
|
if (inputWidth == -1)
|
||||||
|
{
|
||||||
|
inputWidth = potential->inputWidth();
|
||||||
|
inputHeight = potential->inputHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy out the cropped version
|
||||||
|
int wCropped = inputWidth * scale;
|
||||||
|
int hCropped = inputHeight * scale;
|
||||||
|
float* cropped = new float[wCropped * hCropped];
|
||||||
|
cout << endl << " Generating EXR image width: " << wCropped << " height: " << hCropped << endl;
|
||||||
|
for (int y = 0; y < hCropped; y++)
|
||||||
|
for (int x = 0; x < wCropped; x++)
|
||||||
|
{
|
||||||
|
int uncroppedIndex = x + y * w;
|
||||||
|
int croppedIndex = x + y * wCropped;
|
||||||
|
cropped[croppedIndex] = source[uncroppedIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
// create the filter
|
||||||
|
apsf.generateKernelFast();
|
||||||
|
|
||||||
|
// convolve with FFT
|
||||||
|
bool success = FFT::convolve(cropped, apsf.kernel(), wCropped, hCropped, apsf.res(), apsf.res());
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
EXR::writeEXR(filename.c_str(), cropped, wCropped, hCropped);
|
||||||
|
cout << " " << filename << " written." << endl;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
cout << " Final image generation failed." << endl;
|
||||||
|
|
||||||
|
delete[] cropped;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// load image file into the DBM simulation
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
bool loadImages(string inputFile)
|
||||||
|
{
|
||||||
|
// load the files
|
||||||
|
unsigned char* input = NULL;
|
||||||
|
LoadPPM(inputFile.c_str(), input, inputWidth, inputHeight);
|
||||||
|
|
||||||
|
unsigned char* start = new unsigned char[inputWidth * inputHeight];
|
||||||
|
unsigned char* repulsor = new unsigned char[inputWidth * inputHeight];
|
||||||
|
unsigned char* attractor = new unsigned char[inputWidth * inputHeight];
|
||||||
|
unsigned char* terminators = new unsigned char[inputWidth * inputHeight];
|
||||||
|
|
||||||
|
// composite RGB channels into one
|
||||||
|
for (int x = 0; x < inputWidth * inputHeight; x++)
|
||||||
|
{
|
||||||
|
start[x] = (input[3 * x] == 255) ? 255 : 0;
|
||||||
|
repulsor[x] = (input[3 * x + 1] == 255) ? 255 : 0;
|
||||||
|
attractor[x] = (input[3 * x + 2] == 255) ? 255 : 0;
|
||||||
|
terminators[x] = 0;
|
||||||
|
|
||||||
|
if (input[3 * x] + input[3 * x + 1] + input[3 * x + 2] == 255 * 3)
|
||||||
|
{
|
||||||
|
terminators[x] = 255;
|
||||||
|
start[x] = repulsor[x] = attractor[x] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (potential) delete potential;
|
||||||
|
potential = new QUAD_DBM_2D(inputWidth, inputHeight, iterations);
|
||||||
|
bool success = potential->readImage(start, attractor, repulsor, terminators, inputWidth, inputHeight);
|
||||||
|
|
||||||
|
// delete the memory
|
||||||
|
delete[] input;
|
||||||
|
delete[] start;
|
||||||
|
delete[] repulsor;
|
||||||
|
delete[] attractor;
|
||||||
|
delete[] terminators;
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
int width = 600;
|
||||||
|
int height = 600;
|
||||||
|
bool animate = false;
|
||||||
|
float camera[2];
|
||||||
|
float translate[2];
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// window Reshape function
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
void Reshape(int w, int h)
|
||||||
|
{
|
||||||
|
if (h == 0) h = 1;
|
||||||
|
|
||||||
|
glViewport(0, 0, w, h);
|
||||||
|
|
||||||
|
glMatrixMode(GL_PROJECTION);
|
||||||
|
glLoadIdentity();
|
||||||
|
gluOrtho2D(-camera[0] - translate[0], camera[0] + translate[0], -camera[1] - translate[1], camera[1] + translate[1]);
|
||||||
|
|
||||||
|
glMatrixMode(GL_MODELVIEW);
|
||||||
|
glLoadIdentity();
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// GLUT Display callback
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
void Display()
|
||||||
|
{
|
||||||
|
glMatrixMode(GL_PROJECTION);
|
||||||
|
glLoadIdentity();
|
||||||
|
gluOrtho2D(-camera[0] + translate[0], camera[0] + translate[0], -camera[1] + translate[1], camera[1] + translate[1]);
|
||||||
|
|
||||||
|
glMatrixMode(GL_MODELVIEW);
|
||||||
|
glLoadIdentity();
|
||||||
|
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||||
|
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
||||||
|
|
||||||
|
potential->draw();
|
||||||
|
potential->drawSegments();
|
||||||
|
|
||||||
|
glutSwapBuffers();
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// GLUT Keyboard callback
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
void Keyboard(unsigned char key, int x, int y)
|
||||||
|
{
|
||||||
|
switch(key)
|
||||||
|
{
|
||||||
|
case 'p':
|
||||||
|
pause = !pause;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'q':
|
||||||
|
cout << " You terminated the simulation prematurely." << endl;
|
||||||
|
exit(0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
glutPostRedisplay();
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// window Reshape function
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
void Idle()
|
||||||
|
{
|
||||||
|
if (!pause)
|
||||||
|
for (int x = 0; x < 100; x++)
|
||||||
|
{
|
||||||
|
bool success = potential->addParticle();
|
||||||
|
|
||||||
|
if (!success)
|
||||||
|
{
|
||||||
|
cout << " No nodes left to add! Is your terminator reachable?" << endl;
|
||||||
|
//exit(1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (potential->hitGround())
|
||||||
|
{
|
||||||
|
glutPostRedisplay();
|
||||||
|
cout << endl << endl;
|
||||||
|
|
||||||
|
// write out the DAG file
|
||||||
|
string lightningFile = inputFile.substr(0, inputFile.size() - 3) + string("lightning");
|
||||||
|
cout << " Intermediate file " << lightningFile << " written." << endl;
|
||||||
|
potential->writeDAG(lightningFile.c_str());
|
||||||
|
|
||||||
|
// render the final EXR file
|
||||||
|
renderGlow(outputFile, scale);
|
||||||
|
delete potential;
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
glutPostRedisplay();
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// GLUT Main
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
int glutMain(int *argc, char **argv)
|
||||||
|
{
|
||||||
|
float smaller = 1.0f;
|
||||||
|
camera[0] = smaller * 0.5f;
|
||||||
|
camera[1] = smaller * 0.5f;
|
||||||
|
translate[0] = 0.0f;
|
||||||
|
translate[1] = 0.0f;
|
||||||
|
|
||||||
|
glutInit(argc, argv);
|
||||||
|
glutInitDisplayMode(GLUT_DOUBLE | GLUT_DEPTH | GLUT_RGBA);
|
||||||
|
glutInitWindowPosition(50, 50);
|
||||||
|
glutInitWindowSize(width, height);
|
||||||
|
glutCreateWindow("Lumos: A Lightning Generator v0.1");
|
||||||
|
|
||||||
|
glutDisplayFunc(Display);
|
||||||
|
glutKeyboardFunc(Keyboard);
|
||||||
|
glutIdleFunc(Idle);
|
||||||
|
glEnable(GL_BLEND);
|
||||||
|
glBlendFunc(GL_SRC_ALPHA,GL_ONE);
|
||||||
|
|
||||||
|
Reshape(width, height);
|
||||||
|
|
||||||
|
glClearColor(0.0, 0.0, 0.0, 1.0);
|
||||||
|
glShadeModel(GL_SMOOTH);
|
||||||
|
|
||||||
|
// Go!
|
||||||
|
glutMainLoop();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Main
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
if (argc < 3)
|
||||||
|
{
|
||||||
|
cout << endl;
|
||||||
|
cout << " LumosQuad <input file> <output file> <scale (optional)>" << endl;
|
||||||
|
cout << " =========================================================" << endl;
|
||||||
|
cout << " <input file> - *.ppm file with input colors" << endl;
|
||||||
|
cout << " --OR--" << endl;
|
||||||
|
cout << " *.lightning file from a previous run" << endl;
|
||||||
|
cout << " <output file> - The OpenEXR file to output" << endl;
|
||||||
|
cout << " <scale> - Scaling constant for final image." << endl;
|
||||||
|
cout << " Press 'q' to terminate the simulation prematurely." << endl;
|
||||||
|
cout << " Send questions and comments to kim@cs.unc.edu" << endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
cout << endl << "Lumos: A lightning generator v0.1" << endl;
|
||||||
|
cout << "------------------------------------------------------" << endl;
|
||||||
|
|
||||||
|
// store the input params
|
||||||
|
inputFile = string(argv[1]);
|
||||||
|
outputFile = string(argv[2]);
|
||||||
|
if (argc > 3) scale = atoi(argv[3]);
|
||||||
|
|
||||||
|
// see if the input is a *.lightning file
|
||||||
|
if (inputFile.size() > 10)
|
||||||
|
{
|
||||||
|
string postfix = inputFile.substr(inputFile.size() - 9, inputFile.size());
|
||||||
|
|
||||||
|
cout << " Using intermediate file " << inputFile << endl;
|
||||||
|
if (postfix == string("lightning"))
|
||||||
|
{
|
||||||
|
potential->readDAG(inputFile.c_str());
|
||||||
|
renderGlow(outputFile, scale);
|
||||||
|
delete potential;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// read in the *.ppm input file
|
||||||
|
if (!loadImages(inputFile))
|
||||||
|
{
|
||||||
|
cout << " ERROR: " << inputFile.c_str() << " is not a valid PPM file." << endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
cout << " " << inputFile << " read." << endl << endl;
|
||||||
|
|
||||||
|
|
||||||
|
// loop simulation until it hits a terminator
|
||||||
|
cout << " Total particles added: ";
|
||||||
|
glutMain(&argc, argv);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
60
ppm/ppm.cpp
Normal file
60
ppm/ppm.cpp
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// File : ppm.cpp
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// GLVU : Copyright 1997 - 2002
|
||||||
|
// The University of North Carolina at Chapel Hill
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Permission to use, copy, modify, distribute and sell this software and its
|
||||||
|
// documentation for any purpose is hereby granted without fee, provided that
|
||||||
|
// the above copyright notice appear in all copies and that both that copyright
|
||||||
|
// notice and this permission notice appear in supporting documentation.
|
||||||
|
// Binaries may be compiled with this software without any royalties or
|
||||||
|
// restrictions.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
//============================================================================
|
||||||
|
// ppm.cpp : Portable Pixel Map image format module
|
||||||
|
//============================================================================
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#define PPM_VERBOSE 0
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
// READS AN IMAGE IN FROM A PPM FILE. RETURNS THE COLOR RGB ARRAY AND DIMENSIONS
|
||||||
|
// PERFORMS AUTO-ALLOCATION OF Color ARRAY IF SET TO NULL BEFORE CALLING; OTHERWISE
|
||||||
|
// ASSUMES THAT COLOR HAS BEEN PRE-ALLOCED.
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void LoadPPM(const char *FileName, unsigned char* &Color, int &Width, int &Height)
|
||||||
|
{
|
||||||
|
FILE* fp = fopen(FileName, "rb");
|
||||||
|
if (fp==NULL)
|
||||||
|
{ printf("PPM ERROR (ReadPPM) : unable to open %s!\n",FileName);
|
||||||
|
Color=NULL; Width=0; Height=0; return; }
|
||||||
|
int c,s;
|
||||||
|
do{ do { s=fgetc(fp); } while (s!='\n'); } while ((c=fgetc(fp))=='#');
|
||||||
|
ungetc(c,fp);
|
||||||
|
fscanf(fp, "%d %d\n255\n", &Width, &Height);
|
||||||
|
#if PPM_VERBOSE
|
||||||
|
printf("Reading %dx%d Texture [%s]. . .\n", Width, Height, FileName);
|
||||||
|
#endif
|
||||||
|
int NumComponents = Width*Height*3;
|
||||||
|
if (Color==NULL) Color = new unsigned char[NumComponents];
|
||||||
|
fread(Color,NumComponents,1,fp);
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
// Writes an unsigned byte RGB color array out to a PPM file.
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void WritePPM(const char *FileName, unsigned char* Color, int Width, int Height)
|
||||||
|
{
|
||||||
|
FILE* fp = fopen(FileName, "wb");
|
||||||
|
if (fp==NULL) { printf("PPM ERROR (WritePPM) : unable to open %s!\n",FileName); return; }
|
||||||
|
fprintf(fp, "P6\n%d %d\n255\n", Width, Height);
|
||||||
|
fwrite(Color,1,Width*Height*3,fp);
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
23
ppm/ppm.hpp
Normal file
23
ppm/ppm.hpp
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// File : ppm.hpp
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// GLVU : Copyright 1997 - 2002
|
||||||
|
// The University of North Carolina at Chapel Hill
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Permission to use, copy, modify, distribute and sell this software and its
|
||||||
|
// documentation for any purpose is hereby granted without fee, provided that
|
||||||
|
// the above copyright notice appear in all copies and that both that copyright
|
||||||
|
// notice and this permission notice appear in supporting documentation.
|
||||||
|
// Binaries may be compiled with this software without any royalties or
|
||||||
|
// restrictions.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
//============================================================================
|
||||||
|
// ppm.hpp : Portable Pixel Map image format module
|
||||||
|
//============================================================================
|
||||||
|
|
||||||
|
void LoadPPM(const char *FileName, unsigned char* &Color, int &Width, int &Height);
|
||||||
|
void WritePPM(const char *FileName, unsigned char* Color, int Width, int Height);
|
||||||
Loading…
Add table
Add a link
Reference in a new issue