diff --git a/stuff/config/current.txt b/stuff/config/current.txt
index f882acb..d42fcf2 100644
--- a/stuff/config/current.txt
+++ b/stuff/config/current.txt
@@ -1143,6 +1143,8 @@
- "STD_iwa_SpectrumFx.RGamma" "R Gamma"
- "STD_iwa_SpectrumFx.GGamma" "G Gamma"
- "STD_iwa_SpectrumFx.BGamma" "B Gamma"
+ - "STD_iwa_SpectrumFx.loopSpectrumFadeWidth" "Loop Spectrum Fade Width"
+ - "STD_iwa_SpectrumFx.spectrumShift" "Spectrum Shift"
- "STD_iwa_SpectrumFx.lensFactor" "Lens Factor"
- "STD_iwa_SpectrumFx.lightThres" "Light Threshod"
- "STD_iwa_SpectrumFx.lightIntensity" "Light Intensity"
@@ -1175,6 +1177,7 @@
- "STD_iwa_PNPerspectiveFx.waveHeight" "Wave Height"
- "STD_iwa_SoapBubbleFx" "SoapBubble Iwa"
+ - "STD_iwa_SoapBubbleFx.renderMode" "Render Mode"
- "STD_iwa_SoapBubbleFx.intensity" "Intensity"
- "STD_iwa_SoapBubbleFx.refractiveIndex" "Refractive Index"
- "STD_iwa_SoapBubbleFx.thickMax" "Thick Max"
@@ -1182,9 +1185,13 @@
- "STD_iwa_SoapBubbleFx.RGamma" "R Gamma"
- "STD_iwa_SoapBubbleFx.GGamma" "G Gamma"
- "STD_iwa_SoapBubbleFx.BGamma" "B Gamma"
+ - "STD_iwa_SoapBubbleFx.loopSpectrumFadeWidth" "Loop Spectrum Fade Width"
+ - "STD_iwa_SoapBubbleFx.spectrumShift" "Spectrum Shift"
- "STD_iwa_SoapBubbleFx.binarizeThresold" "Threshold"
- "STD_iwa_SoapBubbleFx.multiSource" "Multiple Bubbles in Shape Image"
- "STD_iwa_SoapBubbleFx.maskCenter" "Mask Center of the Bubble"
+ - "STD_iwa_SoapBubbleFx.centerOpacity" "Opacity of Bubble's Center"
+ - "STD_iwa_SoapBubbleFx.fitThickness" "Fit Thickness Image to Each Bubble"
- "STD_iwa_SoapBubbleFx.shapeAspectRatio" "Shape Aspect Ratio"
- "STD_iwa_SoapBubbleFx.blurRadius" "Blur Radius"
- "STD_iwa_SoapBubbleFx.blurPower" "Power"
diff --git a/stuff/profiles/layouts/fxs/STD_iwa_SoapBubbleFx.xml b/stuff/profiles/layouts/fxs/STD_iwa_SoapBubbleFx.xml
index 5803332..e0fa22b 100644
--- a/stuff/profiles/layouts/fxs/STD_iwa_SoapBubbleFx.xml
+++ b/stuff/profiles/layouts/fxs/STD_iwa_SoapBubbleFx.xml
@@ -1,6 +1,7 @@
+ renderMode
intensity
refractiveIndex
@@ -9,12 +10,15 @@
RGamma
GGamma
BGamma
+ loopSpectrumFadeWidth
+ spectrumShift
binarizeThresold
multiSource
- maskCenter
+ centerOpacity
+ fitThickness
shapeAspectRatio
blurRadius
blurPower
diff --git a/stuff/profiles/layouts/fxs/STD_iwa_SpectrumFx.xml b/stuff/profiles/layouts/fxs/STD_iwa_SpectrumFx.xml
index 4fbd999..05d6855 100644
--- a/stuff/profiles/layouts/fxs/STD_iwa_SpectrumFx.xml
+++ b/stuff/profiles/layouts/fxs/STD_iwa_SpectrumFx.xml
@@ -8,6 +8,8 @@
RGamma
GGamma
BGamma
+ loopSpectrumFadeWidth
+ spectrumShift
lensFactor
lightThres
lightIntensity
diff --git a/toonz/sources/common/tfx/tfx.cpp b/toonz/sources/common/tfx/tfx.cpp
index 6750007..125a69e 100644
--- a/toonz/sources/common/tfx/tfx.cpp
+++ b/toonz/sources/common/tfx/tfx.cpp
@@ -710,10 +710,12 @@ void TFx::loadData(TIStream &is) {
while (!is.eos()) {
std::string paramName;
while (is.openChild(paramName)) {
- TParamP param = getParams()->getParam(paramName);
- if (param)
- param->loadData(is);
- else // il parametro non e' presente -> skip
+ TParamVar *paramVar = getParams()->getParamVar(paramName);
+ if (paramVar && paramVar->getParam()) {
+ paramVar->getParam()->loadData(is);
+ if (paramVar->isObsolete())
+ onObsoleteParamLoaded(paramVar->getParam()->getName());
+ } else // il parametro non e' presente -> skip
skipChild(is);
is.closeChild();
@@ -840,10 +842,12 @@ void TFx::saveData(TOStream &os) {
if (linkedSetRoot == this) {
os.openChild("params");
for (int i = 0; i < getParams()->getParamCount(); i++) {
- std::string paramName = getParams()->getParamName(i);
- TParam *param = getParams()->getParam(i);
+ std::string paramName = getParams()->getParamName(i);
+ const TParamVar *paramVar = getParams()->getParamVar(i);
+ // skip saving for the obsolete parameters
+ if (paramVar->isObsolete()) continue;
os.openChild(paramName);
- param->saveData(os);
+ paramVar->getParam()->saveData(os);
os.closeChild();
}
os.closeChild();
diff --git a/toonz/sources/common/tparam/tparamcontainer.cpp b/toonz/sources/common/tparam/tparamcontainer.cpp
index 38548b8..9dce2e7 100644
--- a/toonz/sources/common/tparam/tparamcontainer.cpp
+++ b/toonz/sources/common/tparam/tparamcontainer.cpp
@@ -67,12 +67,17 @@ const TParamVar *TParamContainer::getParamVar(int index) const {
}
TParam *TParamContainer::getParam(std::string name) const {
+ TParamVar *var = getParamVar(name);
+ return (var) ? var->getParam() : 0;
+}
+
+TParamVar *TParamContainer::getParamVar(std::string name) const {
std::map::const_iterator it;
it = m_imp->m_nameTable.find(name);
if (it == m_imp->m_nameTable.end())
return 0;
else
- return it->second->getParam();
+ return it->second;
}
void TParamContainer::unlink() {
diff --git a/toonz/sources/include/tfx.h b/toonz/sources/include/tfx.h
index c26028b..02ea1f2 100644
--- a/toonz/sources/include/tfx.h
+++ b/toonz/sources/include/tfx.h
@@ -499,6 +499,10 @@ public:
virtual void callEndRenderFrameHandler(const TRenderSettings *rs,
double frame) {}
+ // This function will be called in TFx::loadData whenever the obsolete
+ // parameter is loaded. Do nothing by default.
+ virtual void onObsoleteParamLoaded(const std::string ¶mName) {}
+
public:
// Id-related functions
@@ -539,6 +543,7 @@ inline std::string TFx::getFxType() const { return getDeclaration()->getId(); }
//-------------------------------------------------------------------
#define FX_DECLARATION(T) \
+ \
public: \
const TPersistDeclaration *getDeclaration() const override;
diff --git a/toonz/sources/include/tfxparam.h b/toonz/sources/include/tfxparam.h
index be57d23..a698c73 100644
--- a/toonz/sources/include/tfxparam.h
+++ b/toonz/sources/include/tfxparam.h
@@ -9,8 +9,9 @@
#include "tparamcontainer.h"
template
-void bindParam(TFx *fx, std::string name, T &var, bool hidden = false) {
- fx->getParams()->add(new TParamVarT(name, var, hidden));
+void bindParam(TFx *fx, std::string name, T &var, bool hidden = false,
+ bool obsolete = false) {
+ fx->getParams()->add(new TParamVarT(name, var, hidden, obsolete));
var->addObserver(fx);
}
diff --git a/toonz/sources/include/tparamcontainer.h b/toonz/sources/include/tparamcontainer.h
index 8077fd7..a44c3a2 100644
--- a/toonz/sources/include/tparamcontainer.h
+++ b/toonz/sources/include/tparamcontainer.h
@@ -27,18 +27,28 @@ class TParam;
class DVAPI TParamVar {
std::string m_name;
bool m_isHidden;
+ // Flag for an obsolete parameter used for maintaining backward-compatiblity.
+ // - The obsolete parameter will call a special function
+ // (TFx::onObsoleteParameterLoaded) on loaded which enables to do some special
+ // action. (e.g. converting to a new parameter etc.)
+ // - The obsolete parameter will not be saved.
+ bool m_isObsolete;
TParamObserver *m_paramObserver;
public:
- TParamVar(std::string name, bool hidden = false)
- : m_name(name), m_isHidden(hidden), m_paramObserver(0) {}
+ TParamVar(std::string name, bool hidden = false, bool obsolete = false)
+ : m_name(name)
+ , m_isHidden(hidden)
+ , m_isObsolete(obsolete)
+ , m_paramObserver(0) {}
virtual ~TParamVar() {}
virtual TParamVar *clone() const = 0;
std::string getName() const { return m_name; }
bool isHidden() const { return m_isHidden; }
void setIsHidden(bool hidden) { m_isHidden = hidden; }
- virtual void setParam(TParam *param) = 0;
- virtual TParam *getParam() const = 0;
+ bool isObsolete() const { return m_isObsolete; }
+ virtual void setParam(TParam *param) = 0;
+ virtual TParam *getParam() const = 0;
void setParamObserver(TParamObserver *obs);
};
@@ -47,15 +57,17 @@ class TParamVarT final : public TParamVar {
TParamP m_var;
public:
- TParamVarT(std::string name, TParamP var, bool hidden = false)
- : TParamVar(name, hidden), m_var(var) {}
- TParamVarT(std::string name, T *var, bool hidden = false)
- : TParamVar(name, hidden), m_var(var) {}
+ TParamVarT(std::string name, TParamP var, bool hidden = false,
+ bool obsolete = false)
+ : TParamVar(name, hidden, obsolete), m_var(var) {}
+ TParamVarT(std::string name, T *var, bool hidden = false,
+ bool obsolete = false)
+ : TParamVar(name, hidden, obsolete), m_var(var) {}
void setParam(TParam *param) override { m_var = TParamP(param); }
TParam *getParam() const override { return m_var.getPointer(); }
TParamVar *clone() const override {
- return new TParamVarT(getName(), m_var, isHidden());
+ return new TParamVarT(getName(), m_var, isHidden(), isObsolete());
}
};
@@ -76,6 +88,7 @@ public:
TParam *getParam(int index) const;
std::string getParamName(int index) const;
TParam *getParam(std::string name) const;
+ TParamVar *getParamVar(std::string name) const;
const TParamVar *getParamVar(int index) const;
void unlink();
diff --git a/toonz/sources/stdfx/iwa_soapbubblefx.cpp b/toonz/sources/stdfx/iwa_soapbubblefx.cpp
index 27b9395..d759386 100644
--- a/toonz/sources/stdfx/iwa_soapbubblefx.cpp
+++ b/toonz/sources/stdfx/iwa_soapbubblefx.cpp
@@ -10,9 +10,12 @@ Inherits Iwa_SpectrumFx.
#include "iwa_cie_d65.h"
#include "iwa_xyz.h"
+#include "trop.h"
+
#include
#include
#include
+#include
namespace {
const float PI = 3.14159265f;
@@ -63,12 +66,15 @@ static float* dt(float* f, int n, float a = 1.0f) {
Iwa_SoapBubbleFx::Iwa_SoapBubbleFx()
: Iwa_SpectrumFx()
+ , m_renderMode(new TIntEnumParam(RENDER_MODE_BUBBLE, "Bubble"))
, m_binarize_threshold(0.5)
, m_shape_aspect_ratio(1.0)
, m_blur_radius(5.0)
, m_blur_power(0.5)
, m_multi_source(false)
- , m_mask_center(false)
+ , m_mask_center(false) // obsolete
+ , m_center_opacity(1.0)
+ , m_fit_thickness(false)
, m_normal_sample_distance(1)
, m_noise_sub_depth(3)
, m_noise_resolution_s(18.0)
@@ -83,12 +89,18 @@ Iwa_SoapBubbleFx::Iwa_SoapBubbleFx()
addInputPort("Shape", m_shape);
addInputPort("Depth", m_depth);
+ bindParam(this, "renderMode", m_renderMode);
+ m_renderMode->addItem(RENDER_MODE_THICKNESS, "Thickness");
+ m_renderMode->addItem(RENDER_MODE_DEPTH, "Depth");
+
bindParam(this, "binarizeThresold", m_binarize_threshold);
bindParam(this, "shapeAspectRatio", m_shape_aspect_ratio);
bindParam(this, "blurRadius", m_blur_radius);
bindParam(this, "blurPower", m_blur_power);
bindParam(this, "multiSource", m_multi_source);
- bindParam(this, "maskCenter", m_mask_center);
+ bindParam(this, "maskCenter", m_mask_center, false, true); // obsolete
+ bindParam(this, "centerOpacity", m_center_opacity);
+ bindParam(this, "fitThickness", m_fit_thickness);
bindParam(this, "normalSampleDistance", m_normal_sample_distance);
bindParam(this, "noiseSubDepth", m_noise_sub_depth);
bindParam(this, "noiseResolutionS", m_noise_resolution_s);
@@ -103,6 +115,7 @@ Iwa_SoapBubbleFx::Iwa_SoapBubbleFx()
m_blur_radius->setMeasureName("fxLength");
m_blur_radius->setValueRange(0.0, 25.0);
m_blur_power->setValueRange(0.01, 5.0);
+ m_center_opacity->setValueRange(0.0, 1.0);
m_normal_sample_distance->setValueRange(1, 20);
m_noise_sub_depth->setValueRange(1, 5);
@@ -124,12 +137,19 @@ void Iwa_SoapBubbleFx::doCompute(TTile& tile, double frame,
TRectD bBox(tile.m_pos, TPointD(dim.lx, dim.ly));
QList allocatedRasList;
+ if (m_renderMode->getValue() == RENDER_MODE_DEPTH && m_depth.isConnected()) {
+ m_depth->allocateAndCompute(tile, bBox.getP00(), dim, tile.getRaster(),
+ frame, settings);
+ return;
+ }
+
/* soap bubble color map */
TRasterGR8P bubbleColor_ras(sizeof(float3) * 256 * 256, 1);
bubbleColor_ras->lock();
allocatedRasList.append(bubbleColor_ras);
float3* bubbleColor_p = (float3*)bubbleColor_ras->getRawData();
- calcBubbleMap(bubbleColor_p, frame, true);
+ if (m_renderMode->getValue() == RENDER_MODE_BUBBLE)
+ calcBubbleMap(bubbleColor_p, frame, true);
if (checkCancelAndReleaseRaster(allocatedRasList, tile, settings)) return;
@@ -145,6 +165,14 @@ void Iwa_SoapBubbleFx::doCompute(TTile& tile, double frame,
allocatedRasList.append(alpha_map_ras);
float* alpha_map_p = (float*)alpha_map_ras->getRawData();
+ /* region indices */
+ TRasterGR8P regionIds_ras(sizeof(USHORT) * dim.lx * dim.ly, 1);
+ regionIds_ras->lock();
+ regionIds_ras->clear();
+ allocatedRasList.append(regionIds_ras);
+ USHORT* regionIds_p = (USHORT*)regionIds_ras->getRawData();
+ QList regionBoundingRects;
+
/* if the depth image is connected, use it */
if (m_depth.isConnected()) {
TTile depth_tile;
@@ -167,6 +195,9 @@ void Iwa_SoapBubbleFx::doCompute(TTile& tile, double frame,
alpha_map_p, dim);
}
depthRas->unlock();
+
+ // set one region covering whole camera rect
+ regionBoundingRects.append(QRect(0, 0, dim.lx, dim.ly));
}
/* or, use the shape image to obtain pseudo depth */
else { /* m_shape.isConnected */
@@ -180,24 +211,48 @@ void Iwa_SoapBubbleFx::doCompute(TTile& tile, double frame,
if (checkCancelAndReleaseRaster(allocatedRasList, tile, settings)) return;
- processShape(frame, shape_tile, depth_map_p, alpha_map_p, dim, settings);
+ processShape(frame, shape_tile, depth_map_p, alpha_map_p, regionIds_p,
+ regionBoundingRects, dim, settings);
}
if (checkCancelAndReleaseRaster(allocatedRasList, tile, settings)) return;
- /* compute the thickness input and temporarily store to the tile */
- m_input->compute(tile, frame, settings);
-
- if (checkCancelAndReleaseRaster(allocatedRasList, tile, settings)) return;
-
+ // conpute thickness
TRasterGR8P thickness_map_ras(sizeof(float) * dim.lx * dim.ly, 1);
thickness_map_ras->lock();
allocatedRasList.append(thickness_map_ras);
float* thickness_map_p = (float*)thickness_map_ras->getRawData();
- TRasterP thicknessRas = tile.getRaster();
- TRaster32P ras32 = (TRaster32P)thicknessRas;
- TRaster64P ras64 = (TRaster64P)thicknessRas;
- {
+
+ TRasterP tileRas = tile.getRaster();
+ TRaster32P ras32 = (TRaster32P)tileRas;
+ TRaster64P ras64 = (TRaster64P)tileRas;
+
+ if (m_fit_thickness->getValue()) {
+ // Get the original bbox of thickness image
+ TRectD thickBBox;
+ m_input->getBBox(frame, thickBBox, settings);
+ if (thickBBox == TConsts::infiniteRectD)
+ thickBBox = TRectD(tile.m_pos, TDimensionD(tile.getRaster()->getLx(),
+ tile.getRaster()->getLy()));
+ // Compute the thickenss tile.
+ TTile thicknessTile;
+ TDimension thickDim(static_cast(thickBBox.getLx() + 0.5),
+ static_cast(thickBBox.getLy() + 0.5));
+ m_input->allocateAndCompute(thicknessTile, thickBBox.getP00(), thickDim,
+ tile.getRaster(), frame, settings);
+
+ if (checkCancelAndReleaseRaster(allocatedRasList, tile, settings)) return;
+
+ TRasterP thickRas = thicknessTile.getRaster();
+
+ fitThicknessPatches(thickRas, thickDim, thickness_map_p, dim, regionIds_p,
+ regionBoundingRects);
+ } else {
+ /* compute the thickness input and temporarily store to the tile */
+ m_input->compute(tile, frame, settings);
+
+ if (checkCancelAndReleaseRaster(allocatedRasList, tile, settings)) return;
+
if (ras32)
convertToBrightness(ras32, thickness_map_p, nullptr,
dim);
@@ -253,6 +308,7 @@ template
void Iwa_SoapBubbleFx::convertToRaster(const RASTER ras, float* thickness_map_p,
float* depth_map_p, float* alpha_map_p,
TDimensionI dim, float3* bubbleColor_p) {
+ int renderMode = m_renderMode->getValue();
float* depth_p = depth_map_p;
float* thickness_p = thickness_map_p;
float* alpha_p = alpha_map_p;
@@ -260,12 +316,33 @@ void Iwa_SoapBubbleFx::convertToRaster(const RASTER ras, float* thickness_map_p,
PIXEL* pix = ras->pixels(j);
for (int i = 0; i < dim.lx;
i++, depth_p++, thickness_p++, alpha_p++, pix++) {
- float alpha = (*alpha_p) * (float)pix->m / (float)PIXEL::maxChannelValue;
+ float alpha = (*alpha_p);
+ if (!m_fit_thickness->getValue())
+ alpha *= (float)pix->m / (float)PIXEL::maxChannelValue;
if (alpha == 0.0f) { /* no change for the transparent pixels */
pix->m = (typename PIXEL::Channel)0;
continue;
}
+ // thickness and depth render mode
+ if (renderMode != RENDER_MODE_BUBBLE) {
+ float val = alpha * (float)PIXEL::maxChannelValue + 0.5f;
+ pix->m = (typename PIXEL::Channel)((val > (float)PIXEL::maxChannelValue)
+ ? (float)PIXEL::maxChannelValue
+ : val);
+ float mapVal =
+ (renderMode == RENDER_MODE_THICKNESS) ? (*thickness_p) : (*depth_p);
+ val = alpha * mapVal * (float)PIXEL::maxChannelValue + 0.5f;
+ typename PIXEL::Channel chanVal =
+ (typename PIXEL::Channel)((val > (float)PIXEL::maxChannelValue)
+ ? (float)PIXEL::maxChannelValue
+ : val);
+ pix->r = chanVal;
+ pix->g = chanVal;
+ pix->b = chanVal;
+ continue;
+ }
+
float coordinate[2];
coordinate[0] = 256.0f * std::min(1.0f, *depth_p);
coordinate[1] = 256.0f * std::min(1.0f, *thickness_p);
@@ -331,43 +408,40 @@ void Iwa_SoapBubbleFx::convertToRaster(const RASTER ras, float* thickness_map_p,
void Iwa_SoapBubbleFx::processShape(double frame, TTile& shape_tile,
float* depth_map_p, float* alpha_map_p,
+ USHORT* regionIds_p,
+ QList& regionBoundingRects,
TDimensionI dim,
const TRenderSettings& settings) {
TRaster32P shapeRas = shape_tile.getRaster();
shapeRas->lock();
- /* binarize the shape image */
- TRasterGR8P binarized_ras(sizeof(USHORT) * dim.lx * dim.ly, 1);
- binarized_ras->lock();
- USHORT* binarized_p = (USHORT*)binarized_ras->getRawData();
-
TRasterGR8P distance_ras(sizeof(float) * dim.lx * dim.ly, 1);
distance_ras->lock();
float* distance_p = (float*)distance_ras->getRawData();
float binarize_thres = (float)m_binarize_threshold->getValue(frame);
- int regionCount = do_binarize(shapeRas, binarized_p, binarize_thres,
- distance_p, alpha_map_p, dim);
+ int regionCount =
+ do_binarize(shapeRas, regionIds_p, binarize_thres, distance_p,
+ alpha_map_p, regionBoundingRects, dim);
shapeRas->unlock();
if (settings.m_isCanceled && *settings.m_isCanceled) {
- binarized_ras->unlock();
distance_ras->unlock();
return;
}
- do_distance_transform(distance_p, binarized_p, regionCount, dim, frame);
+ do_distance_transform(distance_p, regionIds_p, regionCount, dim, frame);
if (settings.m_isCanceled && *settings.m_isCanceled) {
- binarized_ras->unlock();
distance_ras->unlock();
return;
}
- if (m_mask_center->getValue())
- applyDistanceToAlpha(distance_p, alpha_map_p, dim);
+ float center_opacity = (float)m_center_opacity->getValue(frame);
+ if (center_opacity != 1.0f)
+ applyDistanceToAlpha(distance_p, alpha_map_p, dim, center_opacity);
/* create blur filter */
float blur_radius = (float)m_blur_radius->getValue(frame) *
@@ -378,16 +452,15 @@ void Iwa_SoapBubbleFx::processShape(double frame, TTile& shape_tile,
float power = (float)m_blur_power->getValue(frame);
float* tmp_depth = depth_map_p;
float* tmp_dist = distance_p;
- USHORT* bin_p = binarized_p;
+ USHORT* rid_p = regionIds_p;
for (int i = 0; i < dim.lx * dim.ly;
- i++, tmp_depth++, tmp_dist++, bin_p++) {
- if (*bin_p == 0)
+ i++, tmp_depth++, tmp_dist++, rid_p++) {
+ if (*rid_p == 0)
*tmp_depth = 0.0f;
else
*tmp_depth = 1.0f - std::pow(*tmp_dist, power);
}
distance_ras->unlock();
- binarized_ras->unlock();
return;
}
@@ -401,17 +474,15 @@ void Iwa_SoapBubbleFx::processShape(double frame, TTile& shape_tile,
if (settings.m_isCanceled && *settings.m_isCanceled) {
blur_filter_ras->unlock();
- binarized_ras->unlock();
distance_ras->unlock();
return;
}
/* blur filtering, normarize & power */
- do_applyFilter(depth_map_p, dim, distance_p, binarized_p, blur_filter_p,
+ do_applyFilter(depth_map_p, dim, distance_p, regionIds_p, blur_filter_p,
blur_filter_size, frame, settings);
distance_ras->unlock();
- binarized_ras->unlock();
blur_filter_ras->unlock();
}
@@ -419,6 +490,7 @@ void Iwa_SoapBubbleFx::processShape(double frame, TTile& shape_tile,
int Iwa_SoapBubbleFx::do_binarize(TRaster32P srcRas, USHORT* dst_p, float thres,
float* distance_p, float* alpha_map_p,
+ QList& regionBoundingRects,
TDimensionI dim) {
TPixel32::Channel channelThres =
(TPixel32::Channel)(thres * (float)TPixel32::maxChannelValue);
@@ -435,7 +507,27 @@ int Iwa_SoapBubbleFx::do_binarize(TRaster32P srcRas, USHORT* dst_p, float thres,
}
// label regions when multi bubble option is on
- if (!m_multi_source->getValue()) return 1;
+ if (!m_multi_source->getValue()) {
+ if (m_fit_thickness->getValue()) {
+ regionBoundingRects.append(QRect());
+ // calc boundingRect of the bubble
+ QPoint topLeft(dim.lx, dim.ly);
+ QPoint bottomRight(0, 0);
+ USHORT* tmp_p = dst_p;
+ for (int j = 0; j < dim.ly; j++) {
+ for (int i = 0; i < dim.lx; i++, tmp_p++) {
+ if ((*tmp_p) == 0) continue;
+ if (topLeft.x() > i) topLeft.setX(i);
+ if (bottomRight.x() < i) bottomRight.setX(i);
+ if (topLeft.y() > j) topLeft.setY(j);
+ if (bottomRight.y() < j) bottomRight.setY(j);
+ }
+ }
+ regionBoundingRects.append(QRect(topLeft, bottomRight));
+ }
+
+ return 1;
+ }
QList lut;
for (int i = 0; i < 65536; i++) lut.append(i);
@@ -481,13 +573,43 @@ int Iwa_SoapBubbleFx::do_binarize(TRaster32P srcRas, USHORT* dst_p, float thres,
lut[convIndex.at(i)] = lut.at(lut.at(convIndex.at(i)));
// apply lut
- tmp_p = dst_p;
+ int maxRegionIndex = 0;
+ tmp_p = dst_p;
for (int j = 0; j < dim.ly; j++) {
for (int i = 0; i < dim.lx; i++, tmp_p++) {
- (*tmp_p) = lut[*tmp_p];
+ (*tmp_p) = lut[*tmp_p];
+ if (maxRegionIndex < (*tmp_p)) maxRegionIndex = (*tmp_p);
+ }
+ }
+
+ // compute bounding boxes of each bubble
+ if (m_fit_thickness->getValue()) {
+ regionBoundingRects.append(QRect());
+ USHORT* tmp_p = dst_p;
+ for (int j = 0; j < dim.ly; j++) {
+ for (int i = 0; i < dim.lx; i++, tmp_p++) {
+ int rId = (*tmp_p);
+ if (rId == 0) continue;
+ while (regionBoundingRects.size() <= rId)
+ regionBoundingRects.append(QRect());
+
+ if (regionBoundingRects.at(rId).isNull())
+ regionBoundingRects[rId].setRect(i, j, 1, 1);
+ else {
+ if (regionBoundingRects[rId].left() > i)
+ regionBoundingRects[rId].setLeft(i);
+ if (regionBoundingRects[rId].right() < i)
+ regionBoundingRects[rId].setRight(i);
+ if (regionBoundingRects[rId].top() > j)
+ regionBoundingRects[rId].setTop(j);
+ if (regionBoundingRects[rId].bottom() < j)
+ regionBoundingRects[rId].setBottom(j);
+ }
+ }
}
}
- return regionCount;
+
+ return maxRegionIndex;
}
//------------------------------------
@@ -859,12 +981,83 @@ bool Iwa_SoapBubbleFx::checkCancelAndReleaseRaster(
//------------------------------------
void Iwa_SoapBubbleFx::applyDistanceToAlpha(float* distance_p,
- float* alpha_map_p,
- TDimensionI dim) {
+ float* alpha_map_p, TDimensionI dim,
+ float center_opacity) {
+ float da = 1.0f - center_opacity;
float* d_p = distance_p;
float* a_p = alpha_map_p;
- for (int i = 0; i < dim.lx * dim.ly; i++, d_p++, a_p++)
- (*a_p) *= 1.0f - (*d_p);
+ for (int i = 0; i < dim.lx * dim.ly; i++, d_p++, a_p++) {
+ (*a_p) *= 1.0f - (*d_p) * da;
+ }
+}
+
+//------------------------------------
+// This will be called in TFx::loadData when obsolete "mask center" value is
+// loaded
+void Iwa_SoapBubbleFx::onObsoleteParamLoaded(const std::string& paramName) {
+ if (paramName != "maskCenter") return;
+ // if "mask center" was ON, set a key frame to the center opacity in order to
+ // get the same result.
+ if (m_mask_center->getValue()) m_center_opacity->setValue(0.0, 0.0);
+}
+
+//------------------------------------
+// patch the thickness images to each bounding box of the bubble
+void Iwa_SoapBubbleFx::fitThicknessPatches(TRasterP thickRas,
+ TDimensionI thickDim,
+ float* thickness_map_p,
+ TDimensionI dim, USHORT* regionIds_p,
+ QList& regionBoundingRects) {
+ int regionCount = regionBoundingRects.size() - 1;
+
+ // compute resized thickness rasters
+ QList resizedThicks;
+ resizedThicks.append(TRasterGR16P());
+ for (int r = 1; r <= regionCount; r++) {
+ QRect regionRect = regionBoundingRects.at(r);
+ TRaster64P resizedThickness(
+ TDimension(regionRect.width(), regionRect.height()));
+ resizedThickness->lock();
+
+ TAffine aff = TScale((double)regionRect.width() / (double)thickDim.lx,
+ (double)regionRect.height() / (double)thickDim.ly);
+
+ // resample the thickenss
+ TRop::resample(resizedThickness, thickRas, aff);
+
+ for (int ry = 0; ry < regionRect.height(); ry++) {
+ TPixel64* p = resizedThickness->pixels(ry);
+ for (int rx = 0; rx < regionRect.width(); rx++, p++) {
+ double val = (double)((*p).r) / (double)(TPixel64::maxChannelValue);
+ }
+ }
+
+ TRasterGR16P thickRas_gray(
+ TDimension(regionRect.width(), regionRect.height()));
+ thickRas_gray->lock();
+ TRop::convert(thickRas_gray, resizedThickness);
+
+ resizedThickness->unlock();
+ resizedThicks.append(thickRas_gray);
+ }
+
+ float* out_p = thickness_map_p;
+ USHORT* rId_p = regionIds_p;
+ for (int j = 0; j < dim.ly; j++) {
+ for (int i = 0; i < dim.lx; i++, out_p++, rId_p++) {
+ if ((*rId_p) == 0) {
+ (*out_p) = 0.0f;
+ continue;
+ }
+ QRect regionBBox = regionBoundingRects.at((int)(*rId_p));
+ QPoint coordInRegion(i - regionBBox.left(), j - regionBBox.top());
+ TPixelGR16 pix = resizedThicks.at((int)(*rId_p))
+ ->pixels(coordInRegion.y())[coordInRegion.x()];
+ (*out_p) = (float)pix.value / (float)TPixelGR16::maxChannelValue;
+ }
+ }
+
+ for (int r = 1; r <= regionCount; r++) resizedThicks.at(r)->unlock();
}
//==============================================================================
diff --git a/toonz/sources/stdfx/iwa_soapbubblefx.h b/toonz/sources/stdfx/iwa_soapbubblefx.h
index 7e9aa3d..7735b43 100644
--- a/toonz/sources/stdfx/iwa_soapbubblefx.h
+++ b/toonz/sources/stdfx/iwa_soapbubblefx.h
@@ -21,12 +21,18 @@ protected:
TRasterFxPort m_shape;
/* another option, to input a depth map directly */
TRasterFxPort m_depth;
+ // rendering mode
+ TIntEnumParamP m_renderMode;
// shape parameters
TDoubleParamP m_binarize_threshold;
TDoubleParamP m_shape_aspect_ratio;
TDoubleParamP m_blur_radius;
TDoubleParamP m_blur_power;
TBoolParamP m_multi_source;
+ TDoubleParamP m_center_opacity;
+ TBoolParamP m_fit_thickness;
+
+ // obsolete parameter. to be conerted to m_center_opacity
TBoolParamP m_mask_center;
// noise parameters
@@ -39,6 +45,8 @@ protected:
TDoubleParamP m_noise_depth_mix_ratio;
TDoubleParamP m_noise_thickness_mix_ratio;
+ enum { RENDER_MODE_BUBBLE, RENDER_MODE_THICKNESS, RENDER_MODE_DEPTH };
+
template
void convertToBrightness(const RASTER srcRas, float* dst, float* alpha,
TDimensionI dim);
@@ -49,11 +57,13 @@ protected:
float3* bubbleColor_p);
void processShape(double frame, TTile& shape_tile, float* depth_map_p,
- float* alpha_map_p, TDimensionI dim,
+ float* alpha_map_p, USHORT* regionIds_p,
+ QList& regionBoundingRects, TDimensionI dim,
const TRenderSettings& settings);
int do_binarize(TRaster32P srcRas, USHORT* dst_p, float thres,
- float* distance_p, float* alpha_map_p, TDimensionI dim);
+ float* distance_p, float* alpha_map_p,
+ QList& regionBoundingRects, TDimensionI dim);
void do_createBlurFilter(float* dst_p, int size, float radius);
@@ -89,13 +99,22 @@ protected:
const TRenderSettings&);
void applyDistanceToAlpha(float* distance_p, float* alpha_map_p,
- TDimensionI dim);
+ TDimensionI dim, float center_opacity);
+
+ void fitThicknessPatches(TRasterP thickRas, TDimensionI thickDim,
+ float* thickness_map_p, TDimensionI dim,
+ USHORT* regionIds_p,
+ QList& regionBoundingRects);
public:
Iwa_SoapBubbleFx();
void doCompute(TTile& tile, double frame,
const TRenderSettings& settings) override;
+
+ // This will be called in TFx::loadData when obsolete "mask center" value is
+ // loaded
+ void onObsoleteParamLoaded(const std::string& paramName) override;
};
#endif
\ No newline at end of file
diff --git a/toonz/sources/stdfx/iwa_spectrumfx.cpp b/toonz/sources/stdfx/iwa_spectrumfx.cpp
index d743405..f706c7a 100644
--- a/toonz/sources/stdfx/iwa_spectrumfx.cpp
+++ b/toonz/sources/stdfx/iwa_spectrumfx.cpp
@@ -31,8 +31,6 @@ void Iwa_SpectrumFx::calcBubbleMap(float3 *bubbleColor, double frame,
float phi; /* phase */
float color_x, color_y, color_z; /* xyz color channels */
- float temp_rgb_f[3];
-
/* obtain parameters */
float intensity = (float)m_intensity->getValue(frame);
float refractiveIndex = (float)m_refractiveIndex->getValue(frame);
@@ -42,6 +40,8 @@ void Iwa_SpectrumFx::calcBubbleMap(float3 *bubbleColor, double frame,
(float)m_GGamma->getValue(frame),
(float)m_BGamma->getValue(frame)};
float lensFactor = (float)m_lensFactor->getValue(frame);
+ float shift = (float)m_spectrumShift->getValue(frame);
+ float fadeWidth = (float)m_loopSpectrumFadeWidth->getValue(frame) / 2.0f;
/* for Iwa_SpectrumFx, incident angle is fixed to 0,
for Iwa_SoapBubbleFx, compute for all discrete incident angles*/
@@ -74,64 +74,97 @@ void Iwa_SpectrumFx::calcBubbleMap(float3 *bubbleColor, double frame,
/* for each discrete thickness */
for (j = 0; j < 256; j++) {
- /* calculate the thickness of film (μm) */
- d = thickMin +
- (thickMax - thickMin) * powf(((float)j / 255.0f), lensFactor);
-
- /* there may be a case that the thickness is smaller than 0 */
- if (d < 0.0f) d = 0.0f;
-
- /* initialize XYZ color channels */
- color_x = 0.0f;
- color_y = 0.0f;
- color_z = 0.0f;
-
- /* for each wavelength (in the range of visible light, 380nm-710nm) */
- for (ram = 0; ram < 34; ram++) {
- /* wavelength `λ` (μm) */
- rambda = 0.38f + 0.01f * (float)ram;
- /* phase of light */
- phi = 4.0f * PI * refractiveIndex * d * cos_re / rambda;
- /* reflection amplitude of the film for each polarization */
- // P-polarized light
- p.r_real = p.r_ab + p.t_ab * p.r_ba * p.t_ba * cosf(phi);
- p.r_img = p.t_ab * p.r_ba * p.t_ba * sinf(phi);
- // S-polarized light
- s.r_real = s.r_ab + s.t_ab * s.r_ba * s.t_ba * cosf(phi);
- s.r_img = s.t_ab * s.r_ba * s.t_ba * sinf(phi);
-
- p.R = p.r_real * p.r_real + p.r_img * p.r_img;
- s.R = s.r_real * s.r_real + s.r_img * s.r_img;
-
- /* combined energy reflectance */
- R_final = (p.R + s.R) / 2.0f;
-
- /* accumulate XYZ channel values */
- color_x += intensity * cie_d65[ram] * R_final * xyz[ram * 3 + 0];
- color_y += intensity * cie_d65[ram] * R_final * xyz[ram * 3 + 1];
- color_z += intensity * cie_d65[ram] * R_final * xyz[ram * 3 + 2];
-
- } /* next wavelength (ram) */
-
- temp_rgb_f[0] =
- 3.240479f * color_x - 1.537150f * color_y - 0.498535f * color_z;
- temp_rgb_f[1] =
- -0.969256f * color_x + 1.875992f * color_y + 0.041556f * color_z;
- temp_rgb_f[2] =
- 0.055648f * color_x - 0.204043f * color_y + 1.057311f * color_z;
-
- /* clamp overflows */
- for (k = 0; k < 3; k++) {
- if (temp_rgb_f[k] < 0.0f) temp_rgb_f[k] = 0.0f;
-
- /* gamma adjustment */
- temp_rgb_f[k] = powf((temp_rgb_f[k] / 255.0f), rgbGamma[k]);
-
- if (temp_rgb_f[k] >= 1.0f) temp_rgb_f[k] = 1.0f;
+ // normalize within 0-1 and shift
+ float t = (float)j / 255.0f + shift;
+ // get fractional part
+ t -= std::floor(t);
+ // apply lens factor
+ t = powf(t, lensFactor);
+
+ float tmp_rgb[2][3];
+ float tmp_t[2];
+ float tmp_ratio[2];
+
+ if (t < fadeWidth) {
+ tmp_t[0] = t;
+ tmp_t[1] = t + 1.0f;
+ tmp_ratio[0] = 0.5f + 0.5f * t / fadeWidth;
+ tmp_ratio[1] = 1.0f - tmp_ratio[0];
+ } else if (t > 1.0f - fadeWidth) {
+ tmp_t[0] = t;
+ tmp_t[1] = t - 1.0f;
+ tmp_ratio[0] = 0.5f + 0.5f * (1.0f - t) / fadeWidth;
+ tmp_ratio[1] = 1.0f - tmp_ratio[0];
+ } else { // no fade
+ tmp_t[0] = t;
+ tmp_t[1] = 0; // unused
+ tmp_ratio[0] = 1.0f;
+ tmp_ratio[1] = 0.0f;
+ }
+
+ /* compute colors for two thickness values and fade them*/
+ for (int fadeId = 0; fadeId < 2; fadeId++) {
+ // if composit ratio is 0, skip computing
+ if (tmp_ratio[fadeId] == 0.0f) continue;
+
+ /* calculate the thickness of film (μm) */
+ d = thickMin + (thickMax - thickMin) * tmp_t[fadeId];
+
+ /* there may be a case that the thickness is smaller than 0 */
+ if (d < 0.0f) d = 0.0f;
+
+ /* initialize XYZ color channels */
+ color_x = 0.0f;
+ color_y = 0.0f;
+ color_z = 0.0f;
+
+ /* for each wavelength (in the range of visible light, 380nm-710nm) */
+ for (ram = 0; ram < 34; ram++) {
+ /* wavelength `λ` (μm) */
+ rambda = 0.38f + 0.01f * (float)ram;
+ /* phase of light */
+ phi = 4.0f * PI * refractiveIndex * d * cos_re / rambda;
+ /* reflection amplitude of the film for each polarization */
+ // P-polarized light
+ p.r_real = p.r_ab + p.t_ab * p.r_ba * p.t_ba * cosf(phi);
+ p.r_img = p.t_ab * p.r_ba * p.t_ba * sinf(phi);
+ // S-polarized light
+ s.r_real = s.r_ab + s.t_ab * s.r_ba * s.t_ba * cosf(phi);
+ s.r_img = s.t_ab * s.r_ba * s.t_ba * sinf(phi);
+
+ p.R = p.r_real * p.r_real + p.r_img * p.r_img;
+ s.R = s.r_real * s.r_real + s.r_img * s.r_img;
+
+ /* combined energy reflectance */
+ R_final = (p.R + s.R) / 2.0f;
+
+ /* accumulate XYZ channel values */
+ color_x += intensity * cie_d65[ram] * R_final * xyz[ram * 3 + 0];
+ color_y += intensity * cie_d65[ram] * R_final * xyz[ram * 3 + 1];
+ color_z += intensity * cie_d65[ram] * R_final * xyz[ram * 3 + 2];
+
+ } /* next wavelength (ram) */
+
+ tmp_rgb[fadeId][0] =
+ 3.240479f * color_x - 1.537150f * color_y - 0.498535f * color_z;
+ tmp_rgb[fadeId][1] =
+ -0.969256f * color_x + 1.875992f * color_y + 0.041556f * color_z;
+ tmp_rgb[fadeId][2] =
+ 0.055648f * color_x - 0.204043f * color_y + 1.057311f * color_z;
+
+ /* clamp overflows */
+ for (k = 0; k < 3; k++) {
+ if (tmp_rgb[fadeId][k] < 0.0f) tmp_rgb[fadeId][k] = 0.0f;
+
+ /* gamma adjustment */
+ tmp_rgb[fadeId][k] = powf((tmp_rgb[fadeId][k] / 255.0f), rgbGamma[k]);
+
+ if (tmp_rgb[fadeId][k] >= 1.0f) tmp_rgb[fadeId][k] = 1.0f;
+ }
}
- bubble_p->x = temp_rgb_f[0];
- bubble_p->y = temp_rgb_f[1];
- bubble_p->z = temp_rgb_f[2];
+ bubble_p->x = tmp_rgb[0][0] * tmp_ratio[0] + tmp_rgb[1][0] * tmp_ratio[1];
+ bubble_p->y = tmp_rgb[0][1] * tmp_ratio[0] + tmp_rgb[1][1] * tmp_ratio[1];
+ bubble_p->z = tmp_rgb[0][2] * tmp_ratio[0] + tmp_rgb[1][2] * tmp_ratio[1];
bubble_p++;
} /*- next thickness d (j) -*/
@@ -149,7 +182,9 @@ Iwa_SpectrumFx::Iwa_SpectrumFx()
, m_BGamma(1.0)
, m_lensFactor(1.0)
, m_lightThres(1.0)
- , m_lightIntensity(1.0) {
+ , m_lightIntensity(1.0)
+ , m_loopSpectrumFadeWidth(0.0)
+ , m_spectrumShift(0.0) {
addInputPort("Source", m_input);
addInputPort("Light", m_light);
bindParam(this, "intensity", m_intensity);
@@ -162,6 +197,8 @@ Iwa_SpectrumFx::Iwa_SpectrumFx()
bindParam(this, "lensFactor", m_lensFactor);
bindParam(this, "lightThres", m_lightThres);
bindParam(this, "lightIntensity", m_lightIntensity);
+ bindParam(this, "loopSpectrumFadeWidth", m_loopSpectrumFadeWidth);
+ bindParam(this, "spectrumShift", m_spectrumShift);
m_intensity->setValueRange(0.0, 8.0);
m_refractiveIndex->setValueRange(1.0, 3.0);
@@ -173,6 +210,8 @@ Iwa_SpectrumFx::Iwa_SpectrumFx()
m_lensFactor->setValueRange(0.01, 10.0);
m_lightThres->setValueRange(-5.0, 1.0);
m_lightIntensity->setValueRange(0.0, 1.0);
+ m_loopSpectrumFadeWidth->setValueRange(0.0, 1.0);
+ m_spectrumShift->setValueRange(-10.0, 10.0);
}
//------------------------------------
diff --git a/toonz/sources/stdfx/iwa_spectrumfx.h b/toonz/sources/stdfx/iwa_spectrumfx.h
index e0ddf70..ff29c49 100644
--- a/toonz/sources/stdfx/iwa_spectrumfx.h
+++ b/toonz/sources/stdfx/iwa_spectrumfx.h
@@ -35,6 +35,10 @@ protected:
TDoubleParamP m_RGamma;
TDoubleParamP m_GGamma;
TDoubleParamP m_BGamma;
+
+ TDoubleParamP m_loopSpectrumFadeWidth;
+ TDoubleParamP m_spectrumShift;
+
TDoubleParamP m_lensFactor;
TDoubleParamP m_lightThres;
TDoubleParamP m_lightIntensity;