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 @@ <item>"STD_iwa_SpectrumFx.RGamma" "R Gamma" </item> <item>"STD_iwa_SpectrumFx.GGamma" "G Gamma" </item> <item>"STD_iwa_SpectrumFx.BGamma" "B Gamma" </item> + <item>"STD_iwa_SpectrumFx.loopSpectrumFadeWidth" "Loop Spectrum Fade Width" </item> + <item>"STD_iwa_SpectrumFx.spectrumShift" "Spectrum Shift" </item> <item>"STD_iwa_SpectrumFx.lensFactor" "Lens Factor" </item> <item>"STD_iwa_SpectrumFx.lightThres" "Light Threshod"</item> <item>"STD_iwa_SpectrumFx.lightIntensity" "Light Intensity"</item> @@ -1175,6 +1177,7 @@ <item>"STD_iwa_PNPerspectiveFx.waveHeight" "Wave Height"</item> <item>"STD_iwa_SoapBubbleFx" "SoapBubble Iwa" </item> + <item>"STD_iwa_SoapBubbleFx.renderMode" "Render Mode" </item> <item>"STD_iwa_SoapBubbleFx.intensity" "Intensity" </item> <item>"STD_iwa_SoapBubbleFx.refractiveIndex" "Refractive Index" </item> <item>"STD_iwa_SoapBubbleFx.thickMax" "Thick Max" </item> @@ -1182,9 +1185,13 @@ <item>"STD_iwa_SoapBubbleFx.RGamma" "R Gamma" </item> <item>"STD_iwa_SoapBubbleFx.GGamma" "G Gamma" </item> <item>"STD_iwa_SoapBubbleFx.BGamma" "B Gamma" </item> + <item>"STD_iwa_SoapBubbleFx.loopSpectrumFadeWidth" "Loop Spectrum Fade Width" </item> + <item>"STD_iwa_SoapBubbleFx.spectrumShift" "Spectrum Shift" </item> <item>"STD_iwa_SoapBubbleFx.binarizeThresold" "Threshold" </item> <item>"STD_iwa_SoapBubbleFx.multiSource" "Multiple Bubbles in Shape Image" </item> <item>"STD_iwa_SoapBubbleFx.maskCenter" "Mask Center of the Bubble" </item> + <item>"STD_iwa_SoapBubbleFx.centerOpacity" "Opacity of Bubble's Center" </item> + <item>"STD_iwa_SoapBubbleFx.fitThickness" "Fit Thickness Image to Each Bubble" </item> <item>"STD_iwa_SoapBubbleFx.shapeAspectRatio" "Shape Aspect Ratio" </item> <item>"STD_iwa_SoapBubbleFx.blurRadius" "Blur Radius" </item> <item>"STD_iwa_SoapBubbleFx.blurPower" "Power" </item> 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 @@ <fxlayout> <page name="Color and Shape"> <vbox> + <control>renderMode</control> <separator label="Bubble Color"/> <control>intensity</control> <control>refractiveIndex</control> @@ -9,12 +10,15 @@ <control>RGamma</control> <control>GGamma</control> <control>BGamma</control> + <control>loopSpectrumFadeWidth</control> + <control>spectrumShift</control> </vbox> <vbox> <separator label="Shape"/> <control>binarizeThresold</control> <control>multiSource</control> - <control>maskCenter</control> + <control>centerOpacity</control> + <control>fitThickness</control> <control>shapeAspectRatio</control> <control>blurRadius</control> <control>blurPower</control> 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 @@ <control>RGamma</control> <control>GGamma</control> <control>BGamma</control> + <control>loopSpectrumFadeWidth</control> + <control>spectrumShift</control> <control>lensFactor</control> <control>lightThres</control> <control>lightIntensity</control> 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<std::string, TParamVar *>::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 <class T> -void bindParam(TFx *fx, std::string name, T &var, bool hidden = false) { - fx->getParams()->add(new TParamVarT<T>(name, var, hidden)); +void bindParam(TFx *fx, std::string name, T &var, bool hidden = false, + bool obsolete = false) { + fx->getParams()->add(new TParamVarT<T>(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<T>(getName(), m_var, isHidden()); + return new TParamVarT<T>(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 <QList> #include <QPoint> #include <QSize> +#include <QRect> 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<TRasterGR8P> 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<QRect> 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<int>(thickBBox.getLx() + 0.5), + static_cast<int>(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<TRaster32P, TPixel32>(ras32, thickness_map_p, nullptr, dim); @@ -253,6 +308,7 @@ template <typename RASTER, typename PIXEL> 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<QRect>& 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<QRect>& 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<int> 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<QRect>& regionBoundingRects) { + int regionCount = regionBoundingRects.size() - 1; + + // compute resized thickness rasters + QList<TRasterGR16P> 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 <typename RASTER, typename PIXEL> 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<QRect>& 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<QRect>& 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<QRect>& 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;