mirror of
https://bitbucket.org/mfeemster/fractorium.git
synced 2025-08-14 11:24:59 -04:00

-Add new variations: bubbleT3D, crob, hexaplay3D, hexcrop, hexes, hexnix3D, loonie2, loonie3, nBlur, octapol and synth. -Allow for pre/post versions of dc_bubble, dc_cylinder and dc_linear whereas before they were omitted. -When saving a file with multiple embers in it, detect if time values are all the same and if so, start them at zero and increment by 1 for each ember. -Allow for numerous quality increases to be coalesced into one. It will pick up at the end of the current render. -Show selection highlight on variations tree in response to mouse hover. This makes it easier to see for which variation or param the current mouse wheel action will apply. -Make default temporal samples be 100, whereas before it was 1000 which was overkill. -Require the shift key to be held with delete for deleting an ember to prevent it from triggering when the user enters delete in the edit box. -This wasn't otherwise fixable without writing a lot more code. --Bug fixes -EmberGenome was crashing when generating a sequence from a source file with more than 2 embers in it. -EmberGenome was improperly handling the first frame of a merge after the last frame of the loop. -These bugs were due to a previous commit. Revert parts of that commit. -Prevent a zoom value of less than 0 when reading from xml. -Slight optimization of the crescents, and mask variations, if the compiler wasn't doing it already. -Unique file naming was broken because it was looking for _# and the default names ended with -#. -Disallow renaming of an ember in the library tree to an empty string. -Severe bug that prevented some variations from being read correctly from params generated outside this program. -Severe OpenCL randomization bug. The first x coordinates of the first points in the first kernel call of the first ember of a render since the OpenCL renderer object was created were not random and were mostly -1. -Severe bug when populating xform selection distributions that could sometimes cause a crash due to roundoff error. Fix by using double. -Limit the max number of variations in a random ember to MAX_CL_VARS, which is 8. This ensures they'll look the same on CPU and GPU. -Prevent user from saving stylesheet to default.qss, it's a special reserved filename. --Code changes -Generalize using the running sum output point inside of a variation for all cases: pre, reg and post. -Allow for array variables in variations where the address of each element is stored in m_Params. -Qualify all math functions with std:: -No longer use our own Clamp() in OpenCL, instead use the standard clamp(). -Redesign how functions are used in the variations OpenCL code. -Add tests to EmberTester to verify some of the new functionality. -Place more const and override qualifiers on functions where appropriate. -Add a global rand with a lock to be used very sparingly. -Use a map instead of a vector for bad param names in Xml parsing. -Prefix affine interpolation mode defines with "AFFINE_" to make their purpose more clear. -Allow for variations that change state during iteration by sending a separate copy of the ember to each rendering thread. -Implement this same functionality with a local struct in OpenCL. It's members are the total of all variables that need to change state within an ember. -Add Contains() function to Utils.h. -EmberRender: print names of kernels being printed with --dump_kernel option. -Clean up EmberTester to handle some of the recent changes. -Fix various casts. -Replace % 2 with & 1, even though the compiler was likely doing this already. -Add new file Variations06.h to accommodate new variations. -General cleanup.
370 lines
10 KiB
C++
370 lines
10 KiB
C++
#pragma once
|
|
|
|
#include "EmberDefines.h"
|
|
|
|
/// <summary>
|
|
/// TemporalFilter base, derived and factory classes.
|
|
/// </summary>
|
|
|
|
namespace EmberNs
|
|
{
|
|
/// <summary>
|
|
/// The types of temporal filters available.
|
|
/// </summary>
|
|
enum eTemporalFilterType
|
|
{
|
|
BOX_TEMPORAL_FILTER = 0,
|
|
GAUSSIAN_TEMPORAL_FILTER = 1,
|
|
EXP_TEMPORAL_FILTER = 2
|
|
};
|
|
|
|
/// <summary>
|
|
/// g++ needs a forward declaration here.
|
|
/// </summary>
|
|
template <typename T> class TemporalFilterCreator;
|
|
|
|
#define TEMPORALFILTERUSINGS \
|
|
using TemporalFilter<T>::m_Filter; \
|
|
using TemporalFilter<T>::m_FilterExp; \
|
|
using TemporalFilter<T>::Size; \
|
|
using TemporalFilter<T>::FinishFilter;
|
|
|
|
/// <summary>
|
|
/// Temporal filter is for doing motion blur while rendering a series of frames for animation.
|
|
/// The filter created is used as a vector of scalar values to multiply the time value by in between embers.
|
|
/// There are three possible types: Gaussian, Box and Exp.
|
|
/// Template argument expected to be float or double.
|
|
/// </summary>
|
|
template <typename T>
|
|
class EMBER_API TemporalFilter
|
|
{
|
|
public:
|
|
/// <summary>
|
|
/// Constructor to set up basic filtering parameters, allocate buffers and calculate deltas.
|
|
/// Derived class constructors will complete the final part of filter setup.
|
|
/// </summary>
|
|
/// <param name="filterType">Type of the filter.</param>
|
|
/// <param name="temporalSamples">The number of temporal samples in the ember being rendered</param>
|
|
/// <param name="filterWidth">The width of the filter.</param>
|
|
TemporalFilter(eTemporalFilterType filterType, size_t temporalSamples, T filterWidth)
|
|
{
|
|
size_t i, steps = temporalSamples;
|
|
|
|
m_TemporalSamples = temporalSamples;
|
|
m_FilterWidth = filterWidth;
|
|
m_Deltas.resize(steps);
|
|
m_Filter.resize(steps);
|
|
m_FilterType = filterType;
|
|
m_FilterExp = 1;
|
|
|
|
if (steps == 1)
|
|
{
|
|
m_SumFilt = 1;
|
|
m_Deltas[0] = 0;
|
|
m_Filter[0] = 1;
|
|
}
|
|
else
|
|
{
|
|
//Define the temporal deltas.
|
|
for (i = 0; i < steps; i++)
|
|
m_Deltas[i] = (T(i) / T(steps - 1) - T(0.5)) * filterWidth;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Copy constructor.
|
|
/// </summary>
|
|
/// <param name="filter">The TemporalFilter object to copy</param>
|
|
TemporalFilter(const TemporalFilter<T>& filter)
|
|
{
|
|
*this = filter;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Virtual destructor so derived class destructors get called.
|
|
/// </summary>
|
|
virtual ~TemporalFilter()
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Assignment operator.
|
|
/// </summary>
|
|
/// <param name="filter">The TemporalFilter object to copy.</param>
|
|
/// <returns>Reference to updated self</returns>
|
|
TemporalFilter<T>& operator = (const TemporalFilter<T>& filter)
|
|
{
|
|
if (this != &filter)
|
|
{
|
|
m_TemporalSamples = filter.m_TemporalSamples;
|
|
m_FilterWidth = filter.m_FilterWidth;
|
|
m_FilterExp = filter.m_FilterExp;
|
|
m_SumFilt = filter.m_SumFilt;
|
|
m_Deltas = filter.m_Deltas;
|
|
m_Filter = filter.m_Filter;
|
|
m_FilterType = filter.m_FilterType;
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Return a string representation of this filter.
|
|
/// </summary>
|
|
/// <returns>The string representation of this filter</returns>
|
|
string ToString() const
|
|
{
|
|
size_t i;
|
|
stringstream ss;
|
|
|
|
ss << "Temporal Filter:" << endl
|
|
<< " Size: " << Size() << endl
|
|
<< " Type: " << TemporalFilterCreator<T>::ToString(m_FilterType) << endl
|
|
<< " Sum Filt: " << SumFilt() << endl;
|
|
|
|
ss << "Deltas: " << endl;
|
|
|
|
for (i = 0; i < m_Deltas.size(); i++)
|
|
{
|
|
ss << "Deltas[" << i << "]: " << m_Deltas[i] << endl;
|
|
}
|
|
|
|
ss << "Filter: " << endl;
|
|
|
|
for (i = 0; i < m_Filter.size(); i++)
|
|
{
|
|
ss << "Filter[" << i << "]: " << m_Filter[i] << endl;
|
|
}
|
|
|
|
return ss.str();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Accessors.
|
|
/// </summary>
|
|
size_t Size() const { return m_Filter.size(); }
|
|
size_t TemporalSamples() const { return m_TemporalSamples; }
|
|
T FilterWidth() const { return m_FilterWidth; }
|
|
T FilterExp() const { return m_FilterExp; }
|
|
T SumFilt() const { return m_SumFilt; }
|
|
T* Deltas() { return &m_Deltas[0]; }
|
|
T* Filter() { return &m_Filter[0]; }
|
|
eTemporalFilterType FilterType() const { return m_FilterType; }
|
|
|
|
protected:
|
|
/// <summary>
|
|
/// Normalize the filter and the sum filt.
|
|
/// </summary>
|
|
/// <param name="maxFilt">The maximum filter value contained in the filter vector after it was created</param>
|
|
void FinishFilter(T maxFilt)
|
|
{
|
|
m_SumFilt = 0;
|
|
|
|
for (size_t i = 0; i < Size(); i++)
|
|
{
|
|
m_Filter[i] /= maxFilt;
|
|
m_SumFilt += m_Filter[i];
|
|
}
|
|
|
|
m_SumFilt /= Size();
|
|
}
|
|
|
|
T m_SumFilt;//The sum of all filter values.
|
|
T m_FilterWidth;
|
|
T m_FilterExp;
|
|
size_t m_TemporalSamples;
|
|
vector<T> m_Deltas;//Delta vector.
|
|
vector<T> m_Filter;//Filter vector.
|
|
eTemporalFilterType m_FilterType;//The type of filter this is.
|
|
};
|
|
|
|
/// <summary>
|
|
/// Derivation which implements the Exp filter.
|
|
/// </summary>
|
|
template <typename T>
|
|
class EMBER_API ExpTemporalFilter : public TemporalFilter<T>
|
|
{
|
|
TEMPORALFILTERUSINGS
|
|
public:
|
|
/// <summary>
|
|
/// Constructor to create an Exp filter.
|
|
/// </summary>
|
|
/// <param name="temporalSamples">The number of temporal samples in the ember being rendered</param>
|
|
/// <param name="filterWidth">The width of the filter.</param>
|
|
/// <param name="filterExp">The filter exp.</param>
|
|
ExpTemporalFilter(size_t temporalSamples, T filterWidth, T filterExp)
|
|
: TemporalFilter<T>(BOX_TEMPORAL_FILTER, temporalSamples, filterWidth)
|
|
{
|
|
if (Size() > 1)
|
|
{
|
|
T slpx, maxFilt = 0;
|
|
|
|
for (size_t i = 0; i < Size(); i++)
|
|
{
|
|
if (filterExp >= 0)
|
|
slpx = (T(i) + 1) / Size();
|
|
else
|
|
slpx = T(Size() - i) / Size();
|
|
|
|
//Scale the color based on these values.
|
|
m_Filter[i] = std::pow(slpx, fabs(filterExp));
|
|
|
|
//Keep the max.
|
|
if (m_Filter[i] > maxFilt)
|
|
maxFilt = m_Filter[i];
|
|
}
|
|
|
|
m_FilterExp = filterExp;
|
|
FinishFilter(maxFilt);
|
|
}
|
|
}
|
|
};
|
|
|
|
/// <summary>
|
|
/// Derivation which implements the Gaussian filter.
|
|
/// </summary>
|
|
template <typename T>
|
|
class EMBER_API GaussianTemporalFilter : public TemporalFilter<T>
|
|
{
|
|
TEMPORALFILTERUSINGS
|
|
public:
|
|
/// <summary>
|
|
/// Constructor to create a Gaussian filter.
|
|
/// </summary>
|
|
/// <param name="temporalSamples">The number of temporal samples in the ember being rendered</param>
|
|
/// <param name="filterWidth">The width of the filter.</param>
|
|
GaussianTemporalFilter(size_t temporalSamples, T filterWidth)
|
|
: TemporalFilter<T>(GAUSSIAN_TEMPORAL_FILTER, temporalSamples, filterWidth)
|
|
{
|
|
if (Size() > 1)
|
|
{
|
|
T maxFilt = 0, halfSteps = T(Size()) / T(2);
|
|
GaussianFilter<T> gaussian(1, 1);//Just pass dummy values, they are unused in this case.
|
|
|
|
for (size_t i = 0; i < Size(); i++)
|
|
{
|
|
m_Filter[i] = gaussian.Filter(gaussian.Support() * fabs(i - halfSteps) / halfSteps);
|
|
|
|
//Keep the max.
|
|
if (m_Filter[i] > maxFilt)
|
|
maxFilt = m_Filter[i];
|
|
}
|
|
|
|
FinishFilter(maxFilt);
|
|
}
|
|
}
|
|
};
|
|
|
|
/// <summary>
|
|
/// Derivation which implements the Box filter.
|
|
/// </summary>
|
|
template <typename T>
|
|
class EMBER_API BoxTemporalFilter : public TemporalFilter<T>
|
|
{
|
|
TEMPORALFILTERUSINGS
|
|
public:
|
|
/// <summary>
|
|
/// Constructor to create a Box filter.
|
|
/// </summary>
|
|
/// <param name="temporalSamples">The number of temporal samples in the ember being rendered</param>
|
|
/// <param name="filterWidth">The width of the filter.</param>
|
|
BoxTemporalFilter(size_t temporalSamples, T filterWidth)
|
|
: TemporalFilter<T>(BOX_TEMPORAL_FILTER, temporalSamples, filterWidth)
|
|
{
|
|
if (Size() > 1)
|
|
{
|
|
for (size_t i = 0; i < Size(); i++)
|
|
m_Filter[i] = 1;
|
|
|
|
FinishFilter(1);
|
|
}
|
|
}
|
|
};
|
|
|
|
/// <summary>
|
|
/// Convenience class to assist in converting between filter names and the filter objects themselves.
|
|
/// </summary>
|
|
template <typename T>
|
|
class EMBER_API TemporalFilterCreator
|
|
{
|
|
public:
|
|
/// <summary>
|
|
/// Creates the specified filter type based on the filterType enum parameter.
|
|
/// </summary>
|
|
/// <param name="filterType">Type of the filter</param>
|
|
/// <param name="temporalSamples">The number of temporal samples in the ember being rendered</param>
|
|
/// <param name="filterWidth">The width of the filter</param>
|
|
/// <param name="filterExp">The filter exp, only used with Exp filter, otherwise ignored.</param>
|
|
/// <returns>A pointer to the newly created filter object</returns>
|
|
static TemporalFilter<T>* Create(eTemporalFilterType filterType, size_t temporalSamples, T filterWidth, T filterExp = 1)
|
|
{
|
|
TemporalFilter<T>* filter = nullptr;
|
|
|
|
if (filterType == BOX_TEMPORAL_FILTER)
|
|
filter = new BoxTemporalFilter<T>(temporalSamples, filterWidth);
|
|
else if (filterType == GAUSSIAN_TEMPORAL_FILTER)
|
|
filter = new GaussianTemporalFilter<T>(temporalSamples, filterWidth);
|
|
else if (filterType == EXP_TEMPORAL_FILTER)
|
|
filter = new ExpTemporalFilter<T>(temporalSamples, filterWidth, filterExp);
|
|
else
|
|
filter = new BoxTemporalFilter<T>(temporalSamples, filterWidth);//Default to box if bad enum passed in.
|
|
|
|
return filter;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Return a string vector of the available filter types.
|
|
/// </summary>
|
|
/// <returns>A vector of strings populated with the available filter types</returns>
|
|
static vector<string> FilterTypes()
|
|
{
|
|
vector<string> v;
|
|
|
|
v.reserve(3);
|
|
v.push_back("Box");
|
|
v.push_back("Gaussian");
|
|
v.push_back("Exp");
|
|
|
|
return v;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Convert between the filter name string and its type enum.
|
|
/// </summary>
|
|
/// <param name="filterType">The string name of the filter</param>
|
|
/// <returns>The filter type enum</returns>
|
|
static eTemporalFilterType FromString(const string& filterType)
|
|
{
|
|
if (!_stricmp(filterType.c_str(), "box"))
|
|
return BOX_TEMPORAL_FILTER;
|
|
else if (!_stricmp(filterType.c_str(), "gaussian"))
|
|
return GAUSSIAN_TEMPORAL_FILTER;
|
|
else if (!_stricmp(filterType.c_str(), "exp"))
|
|
return EXP_TEMPORAL_FILTER;
|
|
else
|
|
return BOX_TEMPORAL_FILTER;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Convert between the filter type enum and its name string.
|
|
/// </summary>
|
|
/// <param name="eTemporalFilterType">The filter type enum</param>
|
|
/// <returns>The string name of the filter</returns>
|
|
static string ToString(eTemporalFilterType filterType)
|
|
{
|
|
string filter;
|
|
|
|
if (filterType == BOX_TEMPORAL_FILTER)
|
|
filter = "Box";
|
|
else if (filterType == GAUSSIAN_TEMPORAL_FILTER)
|
|
filter = "Gaussian";
|
|
else if (filterType == EXP_TEMPORAL_FILTER)
|
|
filter = "Exp";
|
|
else
|
|
filter = "Box";
|
|
|
|
return filter;
|
|
}
|
|
};
|
|
}
|