1090 lines
39 KiB
C++
1090 lines
39 KiB
C++
|
#include "iwa_flowpaintbrushfx.h"
|
|||
|
#include "tparamuiconcept.h"
|
|||
|
#include "tlevel.h"
|
|||
|
#include "trop.h"
|
|||
|
|
|||
|
#include <random>
|
|||
|
|
|||
|
#include "tgl.h"
|
|||
|
#include <QOpenGLFramebufferObject>
|
|||
|
#include <QOffscreenSurface>
|
|||
|
#include <QSurfaceFormat>
|
|||
|
#include <QOpenGLContext>
|
|||
|
#include <QImage>
|
|||
|
#include <QOpenGLTexture>
|
|||
|
#include <QColor>
|
|||
|
#include <QPainter>
|
|||
|
|
|||
|
namespace {
|
|||
|
|
|||
|
inline double lerp(DoublePair range, double t) {
|
|||
|
return range.first * (1.0 - t) + range.second * t;
|
|||
|
}
|
|||
|
|
|||
|
bool strokeStackGraterThan(const BrushStroke &stroke1,
|
|||
|
const BrushStroke &stroke2) {
|
|||
|
return stroke1.stack > stroke2.stack;
|
|||
|
}
|
|||
|
|
|||
|
} // namespace
|
|||
|
|
|||
|
//------------------------------------------------------------
|
|||
|
// obtain raster data of brush tips <20>u<EFBFBD><75><EFBFBD>V<EFBFBD>^<5E>b<EFBFBD>`<60>̃<EFBFBD><CC83>X<EFBFBD>^<5E>[<5B>f<EFBFBD>[<5B>^<5E><><EFBFBD>擾
|
|||
|
void Iwa_FlowPaintBrushFx::getBrushRasters(std::vector<TRasterP> &brushRasters,
|
|||
|
TDimension &b_size, int &lastFrame,
|
|||
|
TTile &tile,
|
|||
|
const TRenderSettings &ri) {
|
|||
|
// <20>u<EFBFBD><75><EFBFBD>V<EFBFBD>e<EFBFBD>N<EFBFBD>X<EFBFBD>`<60><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
TPointD b_offset;
|
|||
|
const TFxTimeRegion &tr = m_brush->getTimeRegion();
|
|||
|
lastFrame = tr.getLastFrame() + 1;
|
|||
|
TLevelP partLevel = new TLevel();
|
|||
|
partLevel->setName(m_brush->getAlias(0, ri));
|
|||
|
|
|||
|
// The particles offset must be calculated without considering the
|
|||
|
// affine's translational component
|
|||
|
TRenderSettings riZero(ri);
|
|||
|
riZero.m_affine.a13 = riZero.m_affine.a23 = 0;
|
|||
|
|
|||
|
// Calculate the bboxes union
|
|||
|
TRectD brushBox;
|
|||
|
for (int t = 0; t < lastFrame; ++t) {
|
|||
|
TRectD inputBox;
|
|||
|
m_brush->getBBox(t, inputBox, riZero);
|
|||
|
brushBox += inputBox;
|
|||
|
}
|
|||
|
if (brushBox.isEmpty()) {
|
|||
|
lastFrame = 0;
|
|||
|
return;
|
|||
|
}
|
|||
|
if (brushBox == TConsts::infiniteRectD) brushBox *= ri.m_cameraBox;
|
|||
|
|
|||
|
b_size.lx = (int)brushBox.getLx() + 1;
|
|||
|
b_size.ly = (int)brushBox.getLy() + 1;
|
|||
|
b_offset = TPointD(0.5 * (brushBox.x0 + brushBox.x1),
|
|||
|
0.5 * (brushBox.y0 + brushBox.y1));
|
|||
|
|
|||
|
for (int t = 0; t < lastFrame; ++t) {
|
|||
|
TRasterP ras;
|
|||
|
std::string alias;
|
|||
|
TRasterImageP rimg;
|
|||
|
rimg = partLevel->frame(t);
|
|||
|
if (rimg) {
|
|||
|
ras = rimg->getRaster();
|
|||
|
} else {
|
|||
|
alias = "BRUSH: " + m_brush->getAlias(t, ri);
|
|||
|
rimg = TImageCache::instance()->get(alias, false);
|
|||
|
if (rimg) {
|
|||
|
ras = rimg->getRaster();
|
|||
|
|
|||
|
// Check that the raster resolution is sufficient for our purposes
|
|||
|
if (ras->getLx() < b_size.lx || ras->getLy() < b_size.ly)
|
|||
|
ras = 0;
|
|||
|
else
|
|||
|
b_size = TDimension(ras->getLx(), ras->getLy());
|
|||
|
}
|
|||
|
}
|
|||
|
if (!ras) {
|
|||
|
TTile auxTile;
|
|||
|
TRenderSettings auxRi(ri);
|
|||
|
auxRi.m_bpp = 32;
|
|||
|
m_brush->allocateAndCompute(auxTile, brushBox.getP00(), b_size, 0, t,
|
|||
|
auxRi);
|
|||
|
ras = auxTile.getRaster();
|
|||
|
addRenderCache(alias, TRasterImageP(ras));
|
|||
|
}
|
|||
|
if (ras) brushRasters.push_back(ras);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//------------------------------------------------------------
|
|||
|
|
|||
|
template <typename RASTER, typename PIXEL>
|
|||
|
void Iwa_FlowPaintBrushFx::setFlowTileToBuffer(const RASTER flowRas,
|
|||
|
double2 *buf) {
|
|||
|
double2 *buf_p = buf;
|
|||
|
for (int j = 0; j < flowRas->getLy(); j++) {
|
|||
|
PIXEL *pix = flowRas->pixels(j);
|
|||
|
for (int i = 0; i < flowRas->getLx(); i++, pix++, buf_p++) {
|
|||
|
double val = double(pix->r) / double(PIXEL::maxChannelValue);
|
|||
|
(*buf_p).x = val * 2.0 - 1.0;
|
|||
|
val = double(pix->g) / double(PIXEL::maxChannelValue);
|
|||
|
(*buf_p).y = val * 2.0 - 1.0;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//------------------------------------------------------------
|
|||
|
|
|||
|
template <typename RASTER, typename PIXEL>
|
|||
|
void Iwa_FlowPaintBrushFx::setAreaTileToBuffer(const RASTER areaRas,
|
|||
|
double *buf) {
|
|||
|
double *buf_p = buf;
|
|||
|
for (int j = 0; j < areaRas->getLy(); j++) {
|
|||
|
PIXEL *pix = areaRas->pixels(j);
|
|||
|
for (int i = 0; i < areaRas->getLx(); i++, pix++, buf_p++) {
|
|||
|
(*buf_p) = double(pix->m) / double(PIXEL::maxChannelValue);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//------------------------------------------------------------
|
|||
|
|
|||
|
template <typename RASTER, typename PIXEL>
|
|||
|
void Iwa_FlowPaintBrushFx::setColorTileToBuffer(const RASTER colorRas,
|
|||
|
colorRGBA *buf) {
|
|||
|
colorRGBA *buf_p = buf;
|
|||
|
for (int j = 0; j < colorRas->getLy(); j++) {
|
|||
|
PIXEL *pix = colorRas->pixels(j);
|
|||
|
for (int i = 0; i < colorRas->getLx(); i++, pix++, buf_p++) {
|
|||
|
(*buf_p).r = double(pix->r) / double(PIXEL::maxChannelValue);
|
|||
|
(*buf_p).g = double(pix->g) / double(PIXEL::maxChannelValue);
|
|||
|
(*buf_p).b = double(pix->b) / double(PIXEL::maxChannelValue);
|
|||
|
(*buf_p).a = double(pix->m) / double(PIXEL::maxChannelValue);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//------------------------------------------------------------
|
|||
|
template <typename RASTER, typename PIXEL>
|
|||
|
void Iwa_FlowPaintBrushFx::setOutRaster(const RASTER outRas, double *buf) {
|
|||
|
double *buf_p = buf;
|
|||
|
for (int j = 0; j < outRas->getLy(); j++) {
|
|||
|
PIXEL *pix = outRas->pixels(j);
|
|||
|
for (int i = 0; i < outRas->getLx(); i++, pix++, buf_p++) {
|
|||
|
typename PIXEL::Channel val =
|
|||
|
(typename PIXEL::Channel)(*buf_p * double(PIXEL::maxChannelValue));
|
|||
|
pix->r = val;
|
|||
|
pix->g = val;
|
|||
|
pix->b = val;
|
|||
|
pix->m = PIXEL::maxChannelValue;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//------------------------------------------------------------
|
|||
|
Iwa_FlowPaintBrushFx::Iwa_FlowPaintBrushFx()
|
|||
|
: m_h_density(10.0)
|
|||
|
, m_v_density(10.0)
|
|||
|
, m_pos_randomness(1.0)
|
|||
|
, m_pos_wobble(0.0)
|
|||
|
, m_tip_width(DoublePair(0.02, 0.05))
|
|||
|
, m_tip_length(DoublePair(0.08, 0.2))
|
|||
|
, m_tip_alpha(DoublePair(0.8, 1.0))
|
|||
|
, m_tip_joints(3)
|
|||
|
, m_bidirectional(true)
|
|||
|
, m_width_randomness(0.0)
|
|||
|
, m_length_randomness(0.0)
|
|||
|
, m_angle_randomness(0.0)
|
|||
|
, m_sustain_width_to_skew(0.0)
|
|||
|
, m_anti_jaggy(false)
|
|||
|
, m_origin_pos(TPointD(0.0, 0.0))
|
|||
|
, m_horizontal_pos(TPointD(100.0, 0.0))
|
|||
|
, m_vertical_pos(TPointD(0.0, 100.0))
|
|||
|
, m_curve_point(TPointD(0.0, 0.0))
|
|||
|
, m_fill_gap_size(0.0)
|
|||
|
, m_reference_frame(0.0)
|
|||
|
, m_reference_prevalence(0.0)
|
|||
|
, m_random_seed(1)
|
|||
|
, m_sortBy(new TIntEnumParam(NoSort, "None")) {
|
|||
|
addInputPort("Brush", m_brush);
|
|||
|
addInputPort("Flow", m_flow);
|
|||
|
addInputPort("Area", m_area);
|
|||
|
addInputPort("Color", m_color);
|
|||
|
|
|||
|
bindParam(this, "h_density", m_h_density);
|
|||
|
bindParam(this, "v_density", m_v_density);
|
|||
|
bindParam(this, "pos_randomness", m_pos_randomness);
|
|||
|
bindParam(this, "pos_wobble", m_pos_wobble);
|
|||
|
bindParam(this, "tip_width", m_tip_width);
|
|||
|
bindParam(this, "tip_length", m_tip_length);
|
|||
|
bindParam(this, "tip_alpha", m_tip_alpha);
|
|||
|
bindParam(this, "tip_joints", m_tip_joints);
|
|||
|
bindParam(this, "bidirectional", m_bidirectional);
|
|||
|
|
|||
|
bindParam(this, "width_randomness", m_width_randomness);
|
|||
|
bindParam(this, "length_randomness", m_length_randomness);
|
|||
|
bindParam(this, "angle_randomness", m_angle_randomness);
|
|||
|
bindParam(this, "sustain_width_to_skew", m_sustain_width_to_skew);
|
|||
|
bindParam(this, "anti_jaggy", m_anti_jaggy);
|
|||
|
|
|||
|
bindParam(this, "origin_pos", m_origin_pos);
|
|||
|
bindParam(this, "horizontal_pos", m_horizontal_pos);
|
|||
|
bindParam(this, "vertical_pos", m_vertical_pos);
|
|||
|
bindParam(this, "curve_point", m_curve_point);
|
|||
|
bindParam(this, "fill_gap_size", m_fill_gap_size);
|
|||
|
|
|||
|
bindParam(this, "reference_frame", m_reference_frame);
|
|||
|
bindParam(this, "reference_prevalence", m_reference_prevalence);
|
|||
|
|
|||
|
bindParam(this, "random_seed", m_random_seed);
|
|||
|
bindParam(this, "sort_by", m_sortBy);
|
|||
|
|
|||
|
m_h_density->setValueRange(1.0, 300.0);
|
|||
|
m_v_density->setValueRange(1.0, 300.0);
|
|||
|
m_pos_randomness->setValueRange(0.0, 2.0);
|
|||
|
m_pos_wobble->setValueRange(0.0, 1.0);
|
|||
|
m_tip_width->getMin()->setValueRange(0.0, 1.0);
|
|||
|
m_tip_width->getMax()->setValueRange(0.0, 1.0);
|
|||
|
m_tip_length->getMin()->setValueRange(0.0, 1.0);
|
|||
|
m_tip_length->getMax()->setValueRange(0.0, 1.0);
|
|||
|
m_tip_alpha->getMin()->setValueRange(0.0, 1.0);
|
|||
|
m_tip_alpha->getMax()->setValueRange(0.0, 1.0);
|
|||
|
m_tip_joints->setValueRange(0, 20);
|
|||
|
|
|||
|
m_width_randomness->setValueRange(0.0, 0.9);
|
|||
|
m_length_randomness->setValueRange(0.0, 0.9);
|
|||
|
m_angle_randomness->setValueRange(0.0, 180.0); // degree
|
|||
|
|
|||
|
m_sustain_width_to_skew->setValueRange(0.0, 1.0);
|
|||
|
|
|||
|
m_origin_pos->getX()->setMeasureName("fxLength");
|
|||
|
m_origin_pos->getY()->setMeasureName("fxLength");
|
|||
|
m_horizontal_pos->getX()->setMeasureName("fxLength");
|
|||
|
m_horizontal_pos->getY()->setMeasureName("fxLength");
|
|||
|
m_vertical_pos->getX()->setMeasureName("fxLength");
|
|||
|
m_vertical_pos->getY()->setMeasureName("fxLength");
|
|||
|
m_curve_point->getX()->setValueRange(-0.5, 0.5);
|
|||
|
m_curve_point->getY()->setValueRange(-0.5, 0.5);
|
|||
|
|
|||
|
m_fill_gap_size->setMeasureName("fxLength");
|
|||
|
m_fill_gap_size->setValueRange(0.0, 50.0);
|
|||
|
|
|||
|
m_reference_frame->setValueRange(0, (std::numeric_limits<double>::max)());
|
|||
|
m_reference_prevalence->setValueRange(0.0, 1.0);
|
|||
|
m_random_seed->setValueRange(0, (std::numeric_limits<int>::max)());
|
|||
|
m_sortBy->addItem(Smaller, "Size - Smaller on Top");
|
|||
|
m_sortBy->addItem(Larger, "Size - Larger on Top");
|
|||
|
m_sortBy->addItem(Darker, "Brightness - Darker on Top");
|
|||
|
m_sortBy->addItem(Brighter, "Brightness - Brighter on Top");
|
|||
|
m_sortBy->addItem(Random, "Random");
|
|||
|
|
|||
|
// Version 1 (development version) : strokes are placed upward direction if
|
|||
|
// there is no Flow input.
|
|||
|
// Version 2: strokes are directed parallel to the
|
|||
|
// vertical vector of the parallelogram if there is no Flow input.
|
|||
|
setFxVersion(2);
|
|||
|
}
|
|||
|
|
|||
|
//------------------------------------------------------------
|
|||
|
bool Iwa_FlowPaintBrushFx::doGetBBox(double frame, TRectD &bBox,
|
|||
|
const TRenderSettings &info) {
|
|||
|
if (!m_brush.isConnected()) {
|
|||
|
bBox = TRectD();
|
|||
|
return false;
|
|||
|
}
|
|||
|
TPointD origin_pos = m_origin_pos->getValue(frame);
|
|||
|
TPointD horiz_pos = m_horizontal_pos->getValue(frame);
|
|||
|
TPointD vert_pos = m_vertical_pos->getValue(frame);
|
|||
|
TPointD opposite_pos = horiz_pos + vert_pos - origin_pos;
|
|||
|
bBox = boundingBox(origin_pos, horiz_pos, vert_pos, opposite_pos);
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
//--------------------------------------------------------------
|
|||
|
double Iwa_FlowPaintBrushFx::getSizePixelAmount(const double val,
|
|||
|
const TAffine affine) {
|
|||
|
/*--- Convert to vector --- */
|
|||
|
TPointD vect;
|
|||
|
vect.x = val;
|
|||
|
vect.y = 0.0;
|
|||
|
/*--- Apply geometrical transformation ---*/
|
|||
|
// For the following lines I referred to lines 586-592 of
|
|||
|
// sources/stdfx/motionblurfx.cpp
|
|||
|
TAffine aff(affine);
|
|||
|
aff.a13 = aff.a23 = 0; /* ignore translation */
|
|||
|
vect = aff * vect;
|
|||
|
/*--- return the length of the vector ---*/
|
|||
|
return sqrt(vect.x * vect.x + vect.y * vect.y);
|
|||
|
}
|
|||
|
//------------------------------------------------------------
|
|||
|
|
|||
|
FlowPaintBrushFxParam Iwa_FlowPaintBrushFx::getParam(
|
|||
|
TTile &tile, double frame, const TRenderSettings &ri) {
|
|||
|
FlowPaintBrushFxParam p;
|
|||
|
|
|||
|
TAffine aff = ri.m_affine;
|
|||
|
p.origin_pos = aff * m_origin_pos->getValue(frame);
|
|||
|
p.horiz_pos = aff * m_horizontal_pos->getValue(frame);
|
|||
|
p.vert_pos = aff * m_vertical_pos->getValue(frame);
|
|||
|
TPointD opposite_pos = p.horiz_pos + p.vert_pos - p.origin_pos;
|
|||
|
p.bbox = boundingBox(p.origin_pos, p.horiz_pos, p.vert_pos, opposite_pos);
|
|||
|
p.dim.lx = (int)p.bbox.getLx() + 1;
|
|||
|
p.dim.ly = (int)p.bbox.getLy() + 1;
|
|||
|
p.origin_pos -= p.bbox.getP00();
|
|||
|
p.horiz_pos -= p.bbox.getP00();
|
|||
|
p.vert_pos -= p.bbox.getP00();
|
|||
|
|
|||
|
p.fill_gap_size =
|
|||
|
(int)getSizePixelAmount(m_fill_gap_size->getValue(frame), aff);
|
|||
|
|
|||
|
p.h_density = m_h_density->getValue(frame);
|
|||
|
p.v_density = m_v_density->getValue(frame);
|
|||
|
p.pos_randomness = m_pos_randomness->getValue(frame);
|
|||
|
p.pos_wobble = m_pos_wobble->getValue(frame);
|
|||
|
|
|||
|
p.random_seed = m_random_seed->getValue();
|
|||
|
p.tipLength = m_tip_length->getValue(frame);
|
|||
|
p.tipWidth = m_tip_width->getValue(frame);
|
|||
|
p.tipAlpha = m_tip_alpha->getValue(frame);
|
|||
|
p.width_random = m_width_randomness->getValue(frame);
|
|||
|
p.length_random = m_length_randomness->getValue(frame);
|
|||
|
p.angle_random = m_angle_randomness->getValue(frame);
|
|||
|
|
|||
|
p.reso = m_tip_joints->getValue();
|
|||
|
p.anti_jaggy = m_anti_jaggy->getValue();
|
|||
|
|
|||
|
p.hVec = p.horiz_pos - p.origin_pos;
|
|||
|
p.vVec = p.vert_pos - p.origin_pos;
|
|||
|
p.vVec_unit = double2{normalize(p.vVec).x, normalize(p.vVec).y};
|
|||
|
|
|||
|
p.brushAff = TAffine(p.hVec.x, p.vVec.x, p.origin_pos.x, p.hVec.y, p.vVec.y,
|
|||
|
p.origin_pos.y);
|
|||
|
return p;
|
|||
|
}
|
|||
|
|
|||
|
//------------------------------------------------------------
|
|||
|
|
|||
|
void Iwa_FlowPaintBrushFx::fillGapByDilateAndErode(double *buf,
|
|||
|
const TDimension &dim,
|
|||
|
const int fill_gap_size) {
|
|||
|
TRasterGR8P tmp_buf_ras = TRasterGR8P(dim.lx * dim.ly * sizeof(double), 1);
|
|||
|
tmp_buf_ras->lock();
|
|||
|
double *tmp_buf = (double *)tmp_buf_ras->getRawData();
|
|||
|
|
|||
|
// dilate, then erode
|
|||
|
for (int mode = 0; mode < 2; mode++) {
|
|||
|
for (int i = 0; i < fill_gap_size; i++) {
|
|||
|
double *fromBuf = (i % 2 == 0) ? buf : tmp_buf;
|
|||
|
double *toBuf = (i % 2 == 0) ? tmp_buf : buf;
|
|||
|
|
|||
|
double *to_p = toBuf;
|
|||
|
double *frm_p = fromBuf;
|
|||
|
for (int y = 0; y < dim.ly; y++) {
|
|||
|
double *n_up =
|
|||
|
(y == 0) ? &fromBuf[y * dim.lx] : &fromBuf[(y - 1) * dim.lx];
|
|||
|
double *n_dn = (y == dim.ly - 1) ? &fromBuf[y * dim.lx]
|
|||
|
: &fromBuf[(y + 1) * dim.lx];
|
|||
|
for (int x = 0; x < dim.lx; x++, n_up++, n_dn++, to_p++, frm_p++) {
|
|||
|
if (mode == 0) {
|
|||
|
*to_p = std::max(*frm_p, *n_up);
|
|||
|
*to_p = std::max(*to_p, *n_dn);
|
|||
|
if (x != 0) *to_p = std::max(*to_p, *(frm_p - 1));
|
|||
|
if (x != dim.lx - 1) *to_p = std::max(*to_p, *(frm_p + 1));
|
|||
|
} else {
|
|||
|
*to_p = std::min(*frm_p, *n_up);
|
|||
|
*to_p = std::min(*to_p, *n_dn);
|
|||
|
if (x != 0) *to_p = std::min(*to_p, *(frm_p - 1));
|
|||
|
if (x != dim.lx - 1) *to_p = std::min(*to_p, *(frm_p + 1));
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// <20><EFBFBD><EF9094><EFBFBD>̃t<CC83>B<EFBFBD><42><EFBFBD>^<5E><><EFBFBD><EFBFBD><EFBFBD>O<EFBFBD>̏ꍇ<CC8F>A<EFBFBD>v<EFBFBD>Z<EFBFBD><5A><EFBFBD>ʂ<EFBFBD>tmp_buf<75>ɓ<EFBFBD><C993><EFBFBD><EFBFBD>Ă<EFBFBD><C482><EFBFBD><EFBFBD>̂<EFBFBD>buf<75>ɃR<C983>s<EFBFBD>[<5B><><EFBFBD><EFBFBD>
|
|||
|
if (fill_gap_size % 2 == 1) {
|
|||
|
memcpy(buf, tmp_buf, dim.lx * dim.ly * sizeof(double));
|
|||
|
}
|
|||
|
|
|||
|
tmp_buf_ras->unlock();
|
|||
|
}
|
|||
|
|
|||
|
//------------------------------------------------------------
|
|||
|
void Iwa_FlowPaintBrushFx::computeBrushVertices(
|
|||
|
QVector<BrushVertex> &brushVertices, QList<BrushStroke> &brushStrokes,
|
|||
|
FlowPaintBrushFxParam &p, TTile &tile, double frame,
|
|||
|
const TRenderSettings &ri) {
|
|||
|
// <20>Œ肷<C592><E882B7><EFBFBD>^<5E>b<EFBFBD>`<60>F<EFBFBD><EFBFBD>t<EFBFBD><74><EFBFBD>[<5B><><EFBFBD><EFBFBD>Area<65>AColor<6F><72><EFBFBD>p<EFBFBD><70><EFBFBD>Đ<EFBFBD><C490><EFBFBD><EFBFBD>A<EFBFBD>J<EFBFBD><4A><EFBFBD><EFBFBD><EFBFBD>g<EFBFBD>t<EFBFBD><74><EFBFBD>[<5B><><EFBFBD><EFBFBD>Flow<6F><77><EFBFBD>p<EFBFBD><70><EFBFBD>ė<EFBFBD><C497><EFBFBD>
|
|||
|
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>^<5E>b<EFBFBD>`<60>FArea<65>AColor<6F>AFlow<6F><77><EFBFBD>ׂăJ<C483><4A><EFBFBD><EFBFBD><EFBFBD>g<EFBFBD>t<EFBFBD><74><EFBFBD>[<5B><><EFBFBD><EFBFBD><EFBFBD>p<EFBFBD><70><EFBFBD><EFBFBD><EFBFBD>B
|
|||
|
int referenceFrame = (int)std::round(m_reference_frame->getValue(frame)) - 1;
|
|||
|
double reference_prevalence = m_reference_prevalence->getValue(frame);
|
|||
|
bool bidirectional = m_bidirectional->getValue();
|
|||
|
|
|||
|
FlowPaintBrushFxParam pivP =
|
|||
|
getParam(tile, referenceFrame, ri); // TODO: <20><><EFBFBD><EFBFBD><EFBFBD>A<EFBFBD><41><EFBFBD>x<EFBFBD><78><EFBFBD>Ԗh<D496><68><EFBFBD><EFBFBD>
|
|||
|
pivP.lastFrame = p.lastFrame;
|
|||
|
FlowPaintBrushFxParam *param[2] = {&p, &pivP};
|
|||
|
|
|||
|
double ref_frame[2] = {frame, (double)referenceFrame};
|
|||
|
double2 *flow_buf[2] = {nullptr};
|
|||
|
double *area_buf[2] = {nullptr};
|
|||
|
colorRGBA *color_buf[2] = {nullptr};
|
|||
|
TRasterGR8P flow_buf_ras[2], area_buf_ras[2], color_buf_ras[2];
|
|||
|
|
|||
|
TRaster32P ras32 = tile.getRaster();
|
|||
|
TRaster64P ras64 = tile.getRaster();
|
|||
|
|
|||
|
// <20><><EFBFBD>ꂼ<EFBFBD><EA82BC><EFBFBD>̎Q<CC8E>Ɖ摜<C689><E6919C><EFBFBD>擾
|
|||
|
for (int f = 0; f < 2; f++) {
|
|||
|
// Reference<63>t<EFBFBD><74><EFBFBD>[<5B><><EFBFBD>̓t<CD83><74><EFBFBD>[<5B><><EFBFBD><EFBFBD>-1<><31><EFBFBD><EFBFBD>prevalence<63><65><EFBFBD>O<EFBFBD>̏ꍇ<CC8F>v<EFBFBD>Z<EFBFBD><5A><EFBFBD>Ȃ<EFBFBD>
|
|||
|
int tmp_f = ref_frame[f];
|
|||
|
if (f == 1 && (tmp_f < 0 || reference_prevalence == 0.0)) continue;
|
|||
|
|
|||
|
// <20>܂<EFBFBD><DC82>AFlow<6F><77><EFBFBD>v<EFBFBD>Z
|
|||
|
if (m_flow.isConnected()) {
|
|||
|
// obtain Flow memory buffer (XY)
|
|||
|
TTile flowTile;
|
|||
|
m_flow->allocateAndCompute(flowTile, param[f]->bbox.getP00(),
|
|||
|
param[f]->dim, tile.getRaster(), tmp_f, ri);
|
|||
|
// allocate buffer
|
|||
|
flow_buf_ras[f] =
|
|||
|
TRasterGR8P(param[f]->dim.lx * param[f]->dim.ly * sizeof(double2), 1);
|
|||
|
flow_buf_ras[f]->lock();
|
|||
|
flow_buf[f] = (double2 *)flow_buf_ras[f]->getRawData();
|
|||
|
if (ras32)
|
|||
|
setFlowTileToBuffer<TRaster32P, TPixel32>(flowTile.getRaster(),
|
|||
|
flow_buf[f]);
|
|||
|
else if (ras64)
|
|||
|
setFlowTileToBuffer<TRaster64P, TPixel64>(flowTile.getRaster(),
|
|||
|
flow_buf[f]);
|
|||
|
}
|
|||
|
// <20>J<EFBFBD><4A><EFBFBD><EFBFBD><EFBFBD>g<EFBFBD>t<EFBFBD><74><EFBFBD>[<5B><><EFBFBD><EFBFBD>prevalence<63><65><EFBFBD>P<EFBFBD>̏ꍇFlow<6F>̂v<DD8C>Z
|
|||
|
if (f == 0 && reference_prevalence == 1.0) continue;
|
|||
|
// Area<65><61>Color<6F><72><EFBFBD>v<EFBFBD>Z
|
|||
|
if (m_area.isConnected()) {
|
|||
|
TTile areaTile;
|
|||
|
m_area->allocateAndCompute(areaTile, param[f]->bbox.getP00(),
|
|||
|
param[f]->dim, tile.getRaster(), tmp_f, ri);
|
|||
|
// allocate buffer
|
|||
|
area_buf_ras[f] =
|
|||
|
TRasterGR8P(param[f]->dim.lx * param[f]->dim.ly * sizeof(double), 1);
|
|||
|
area_buf_ras[f]->lock();
|
|||
|
area_buf[f] = (double *)area_buf_ras[f]->getRawData();
|
|||
|
if (ras32)
|
|||
|
setAreaTileToBuffer<TRaster32P, TPixel32>(areaTile.getRaster(),
|
|||
|
area_buf[f]);
|
|||
|
else if (ras64)
|
|||
|
setAreaTileToBuffer<TRaster64P, TPixel64>(areaTile.getRaster(),
|
|||
|
area_buf[f]);
|
|||
|
|
|||
|
// <20>Ԃ<EFBFBD><D482><EFBFBD><EFBFBD>߂<EFBFBD>
|
|||
|
if (param[f]->fill_gap_size > 0)
|
|||
|
fillGapByDilateAndErode(area_buf[f], param[f]->dim,
|
|||
|
param[f]->fill_gap_size);
|
|||
|
}
|
|||
|
if (m_color.isConnected()) {
|
|||
|
TTile colorTile;
|
|||
|
m_color->allocateAndCompute(colorTile, param[f]->bbox.getP00(),
|
|||
|
param[f]->dim, tile.getRaster(), tmp_f, ri);
|
|||
|
// allocate buffer
|
|||
|
color_buf_ras[f] = TRasterGR8P(
|
|||
|
param[f]->dim.lx * param[f]->dim.ly * sizeof(colorRGBA), 1);
|
|||
|
color_buf_ras[f]->lock();
|
|||
|
color_buf[f] = (colorRGBA *)color_buf_ras[f]->getRawData();
|
|||
|
if (ras32)
|
|||
|
setColorTileToBuffer<TRaster32P, TPixel32>(colorTile.getRaster(),
|
|||
|
color_buf[f]);
|
|||
|
else if (ras64)
|
|||
|
setColorTileToBuffer<TRaster64P, TPixel64>(colorTile.getRaster(),
|
|||
|
color_buf[f]);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
enum FRAMETYPE { CURRENT = 0, REFERENCE };
|
|||
|
|
|||
|
auto getFlow = [&](TPointD pos, FRAMETYPE fType) {
|
|||
|
if (!flow_buf[fType]) {
|
|||
|
if (getFxVersion() == 1)
|
|||
|
return double2{0, 1};
|
|||
|
else
|
|||
|
return param[CURRENT]->vVec_unit;
|
|||
|
}
|
|||
|
int u = (int)std::round(pos.x);
|
|||
|
int v = (int)std::round(pos.y);
|
|||
|
if (u < 0 || u >= param[fType]->dim.lx || v < 0 ||
|
|||
|
v >= param[fType]->dim.ly) {
|
|||
|
if (getFxVersion() == 1)
|
|||
|
return double2{0, 1};
|
|||
|
else
|
|||
|
return param[CURRENT]->vVec_unit;
|
|||
|
}
|
|||
|
return flow_buf[fType][v * param[fType]->dim.lx + u];
|
|||
|
};
|
|||
|
auto getArea = [&](TPointD pos, FRAMETYPE fType) {
|
|||
|
if (!area_buf[fType]) return 1.0;
|
|||
|
int u = (int)std::round(pos.x);
|
|||
|
int v = (int)std::round(pos.y);
|
|||
|
if (u < 0 || u >= param[fType]->dim.lx || v < 0 ||
|
|||
|
v >= param[fType]->dim.ly)
|
|||
|
return 0.0;
|
|||
|
return area_buf[fType][v * param[fType]->dim.lx + u];
|
|||
|
};
|
|||
|
auto getColor = [&](TPointD pos, FRAMETYPE fType) {
|
|||
|
if (!color_buf[fType]) return colorRGBA{1.0, 1.0, 1.0, 1.0};
|
|||
|
int u = (int)std::round(pos.x);
|
|||
|
int v = (int)std::round(pos.y);
|
|||
|
if (u < 0 || u >= param[fType]->dim.lx || v < 0 ||
|
|||
|
v >= param[fType]->dim.ly)
|
|||
|
return colorRGBA{1.0, 1.0, 1.0, 0.0};
|
|||
|
return color_buf[fType][v * param[fType]->dim.lx + u];
|
|||
|
};
|
|||
|
|
|||
|
// <20><><EFBFBD><EFBFBD><EFBFBD>_<EFBFBD><5F><EFBFBD>̐ݒ<CC90>
|
|||
|
std::mt19937_64 mt, mt_cur; // , mt_ref;
|
|||
|
mt.seed(p.random_seed);
|
|||
|
mt_cur.seed(
|
|||
|
(unsigned int)(p.random_seed +
|
|||
|
(int)std::round(frame))); // <20>t<EFBFBD><74><EFBFBD>[<5B><><EFBFBD><EFBFBD><EFBFBD>Ƀo<C983><6F><EFBFBD><EFBFBD><C282><EFBFBD><EFBFBD>铮<EFBFBD><E993AE>
|
|||
|
std::uniform_real_distribution<> random01(0.0, 1.0);
|
|||
|
std::uniform_int_distribution<> random_textureId(0, p.lastFrame - 1);
|
|||
|
std::uniform_real_distribution<> random_plusminus1(-1.0, 1.0);
|
|||
|
|
|||
|
// <20>^<5E>b<EFBFBD>`<60>̒<EFBFBD><CC92>_<EFBFBD><5F><EFBFBD>W<EFBFBD><57><EFBFBD>o<EFBFBD>^<5E><><EFBFBD>Ă<EFBFBD><C482><EFBFBD>
|
|||
|
double v_incr = 1.0 / p.v_density;
|
|||
|
double h_incr = 1.0 / p.h_density;
|
|||
|
double v_base_pos = v_incr * 0.5;
|
|||
|
double segmentAmount = (double)p.reso * 2 - 1;
|
|||
|
|
|||
|
int id = 0; // Map<61>̃L<CC83>[<5B>ɂȂ<C982><C882>ʂ<EFBFBD><CA82>ԍ<EFBFBD>
|
|||
|
for (int v = 0; v < (int)p.v_density; v++, v_base_pos += v_incr) {
|
|||
|
double h_base_pos = h_incr * 0.5;
|
|||
|
for (int h = 0; h < (int)p.h_density; h++, h_base_pos += h_incr, id++) {
|
|||
|
BrushStroke brushStroke;
|
|||
|
|
|||
|
// reference<63><65><EFBFBD>J<EFBFBD><4A><EFBFBD><EFBFBD><EFBFBD>g<EFBFBD><67>
|
|||
|
FRAMETYPE frameType =
|
|||
|
(referenceFrame >= 0 && random01(mt) <= reference_prevalence)
|
|||
|
? REFERENCE
|
|||
|
: CURRENT;
|
|||
|
|
|||
|
double pos_x = h_base_pos;
|
|||
|
double pos_y = v_base_pos;
|
|||
|
if (frameType == CURRENT) {
|
|||
|
pos_x += (random01(mt) - 0.5) * p.pos_randomness * h_incr;
|
|||
|
pos_y += (random01(mt) - 0.5) * p.pos_randomness * v_incr;
|
|||
|
} else { // REFERENCE case
|
|||
|
pos_x += (random01(mt) - 0.5) * pivP.pos_randomness * h_incr;
|
|||
|
pos_y += (random01(mt) - 0.5) * pivP.pos_randomness * v_incr;
|
|||
|
}
|
|||
|
if (pos_x < 0.0 || pos_x > 1.0 || pos_y < 0.0 || pos_y > 1.0) {
|
|||
|
mt.discard(7); // <20><><EFBFBD><EFBFBD><EFBFBD>A<EFBFBD><41><EFBFBD>̌<EFBFBD><CC8C>̃<EFBFBD><CC83><EFBFBD><EFBFBD>_<EFBFBD><5F><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><F1909495><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
mt_cur.discard(2);
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
// Area<65>l<EFBFBD><6C><EFBFBD>E<EFBFBD><45><EFBFBD><EFBFBD><EFBFBD>W
|
|||
|
TPointD areaSamplePos =
|
|||
|
param[frameType]->brushAff * TPointD(pos_x, pos_y);
|
|||
|
// <20><><EFBFBD><EFBFBD><EFBFBD>͈͂̎Q<CC8E>Ɖ摜<C689>̋P<CC8B>x<EFBFBD><78><EFBFBD>擾
|
|||
|
double areaVal = getArea(areaSamplePos, frameType);
|
|||
|
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>邩<EFBFBD><E982A9><EFBFBD>Ȃ<EFBFBD><C882><EFBFBD>
|
|||
|
if (random01(mt) > areaVal) {
|
|||
|
mt.discard(6); // <20><><EFBFBD><EFBFBD><EFBFBD>ɂ<EFBFBD><C982>A<EFBFBD><41><EFBFBD>̌<EFBFBD><CC8C>̃<EFBFBD><CC83><EFBFBD><EFBFBD>_<EFBFBD><5F><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><F1909495><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
mt_cur.discard(2);
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
double2 wobble;
|
|||
|
wobble.x = (random01(mt_cur) - 0.5) * p.pos_wobble * h_incr;
|
|||
|
wobble.y = (random01(mt_cur) - 0.5) * p.pos_wobble * v_incr;
|
|||
|
// <20><><EFBFBD>݂̃Z<CC83>O<EFBFBD><4F><EFBFBD><EFBFBD><EFBFBD>g<EFBFBD>̃<EFBFBD><CC83><EFBFBD><EFBFBD>_<EFBFBD><5F><EFBFBD><EFBFBD><EFBFBD>O<EFBFBD><4F><EFBFBD>W
|
|||
|
brushStroke.originPos = TPointD(pos_x + wobble.x, pos_y + wobble.y);
|
|||
|
|
|||
|
brushStroke.color = getColor(areaSamplePos, frameType);
|
|||
|
double alpha = lerp(param[frameType]->tipAlpha, areaVal);
|
|||
|
// premultiply <20>Ȃ̂<C882>RGB<47>l<EFBFBD>ɂ<EFBFBD><C982><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
brushStroke.color.r *= alpha;
|
|||
|
brushStroke.color.g *= alpha;
|
|||
|
brushStroke.color.b *= alpha;
|
|||
|
brushStroke.color.a *= alpha;
|
|||
|
|
|||
|
brushStroke.length =
|
|||
|
lerp(param[frameType]->tipLength, areaVal) *
|
|||
|
(1.0 + random_plusminus1(mt) * param[frameType]->length_random);
|
|||
|
brushStroke.widthHalf =
|
|||
|
lerp(param[frameType]->tipWidth, areaVal) *
|
|||
|
(1.0 + random_plusminus1(mt) * param[frameType]->width_random) * 0.5;
|
|||
|
|
|||
|
if (p.anti_jaggy) brushStroke.widthHalf *= 3.0;
|
|||
|
|
|||
|
brushStroke.angle =
|
|||
|
random_plusminus1(mt) * param[frameType]->angle_random;
|
|||
|
TAffine flow_rot = TRotation(brushStroke.angle);
|
|||
|
|
|||
|
brushStroke.textureId = random_textureId(mt);
|
|||
|
brushStroke.randomVal = random01(mt);
|
|||
|
|
|||
|
bool inv = random01(mt) < 0.5;
|
|||
|
brushStroke.invert = (bidirectional) ? inv : false;
|
|||
|
|
|||
|
// <20>܂<EFBFBD><DC82>A<EFBFBD>c<EFBFBD>ɂȂ镔<C882><E99594><EFBFBD>̍<EFBFBD><CC8D>W<EFBFBD><57><EFBFBD>v<EFBFBD>Z<EFBFBD><5A><EFBFBD><EFBFBD>
|
|||
|
QVector<TPointD> centerPosHalf[2];
|
|||
|
|
|||
|
double segLen =
|
|||
|
brushStroke.length /
|
|||
|
segmentAmount; // <20>Z<EFBFBD>O<EFBFBD><4F><EFBFBD><EFBFBD><EFBFBD>g<EFBFBD>ЂƂ<C682><C295>̒<EFBFBD><CC92><EFBFBD><EFBFBD>B<EFBFBD>i<EFBFBD>u<EFBFBD><75><EFBFBD>V<EFBFBD><56><EFBFBD>W<EFBFBD>n<EFBFBD>j
|
|||
|
|
|||
|
bool tooCurve[2] = {false, false};
|
|||
|
TPointD originFlow;
|
|||
|
// <20>O<EFBFBD><4F><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
for (int dir = 0; dir < 2; dir++) {
|
|||
|
// curPos<6F><73><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʒu<CA92><75>
|
|||
|
TPointD curPos(brushStroke.originPos);
|
|||
|
TPointD preFlowUV;
|
|||
|
TPointD startFlow;
|
|||
|
for (int s = 0; s < param[frameType]->reso; s++) {
|
|||
|
// <20>@<40><><EFBFBD>݂̃Z<CC83>O<EFBFBD><4F><EFBFBD><EFBFBD><EFBFBD>g<EFBFBD>̃<EFBFBD><CC83><EFBFBD><EFBFBD>_<EFBFBD><5F><EFBFBD><EFBFBD><EFBFBD>O<EFBFBD><4F><EFBFBD>W
|
|||
|
TPointD samplePos = param[CURRENT]->brushAff * curPos;
|
|||
|
|
|||
|
// <20><><EFBFBD><EFBFBD><EFBFBD>_<EFBFBD><5F><EFBFBD><EFBFBD><EFBFBD>O<EFBFBD><4F><EFBFBD>W<EFBFBD><57><EFBFBD>痬<EFBFBD><E797AC><EFBFBD>x<EFBFBD>N<EFBFBD>g<EFBFBD><67><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>擾
|
|||
|
double2 tmp_flow = getFlow(samplePos, CURRENT);
|
|||
|
|
|||
|
// <20>@<40><><EFBFBD><EFBFBD><EFBFBD>x<EFBFBD>N<EFBFBD>g<EFBFBD><67><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>u<EFBFBD><75><EFBFBD>V<EFBFBD><56><EFBFBD>W<EFBFBD>n<EFBFBD>ɕϊ<C995>
|
|||
|
TPointD flow_uv =
|
|||
|
param[CURRENT]->brushAff.inv() * TPointD(tmp_flow.x, tmp_flow.y) -
|
|||
|
param[CURRENT]->brushAff.inv() * TPointD(0., 0.);
|
|||
|
if (s == 0 && dir == 0) originFlow = flow_uv;
|
|||
|
|
|||
|
// <20>x<EFBFBD>N<EFBFBD>g<EFBFBD><67><EFBFBD>𐳋K<F090B38B><4B>
|
|||
|
flow_uv = normalize(flow_uv);
|
|||
|
|
|||
|
// <20>߂<EFBFBD><DF82><EFBFBD><EFBFBD><EFBFBD><EFBFBD>̂Ƃ<CC82><C682>̓x<CD83>N<EFBFBD>g<EFBFBD><67><EFBFBD>]
|
|||
|
if (dir == 1) flow_uv = -flow_uv;
|
|||
|
|
|||
|
// <20><><EFBFBD><EFBFBD><EFBFBD>̕<EFBFBD><CC95><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>]
|
|||
|
flow_uv = flow_rot * flow_uv;
|
|||
|
|
|||
|
if (s != 0) {
|
|||
|
double dot = flow_uv.x * preFlowUV.x + flow_uv.y * preFlowUV.y;
|
|||
|
if (dot < 0.0) flow_uv = -flow_uv;
|
|||
|
|
|||
|
double dotVsStart =
|
|||
|
startFlow.x * flow_uv.x + startFlow.y * flow_uv.y;
|
|||
|
if (dotVsStart < 0.3) {
|
|||
|
tooCurve[dir] = true;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
preFlowUV = flow_uv;
|
|||
|
|
|||
|
if (s == 0) startFlow = flow_uv;
|
|||
|
|
|||
|
// <20>x<EFBFBD>N<EFBFBD>g<EFBFBD><67><EFBFBD><EFBFBD><EFBFBD>Z<EFBFBD>O<EFBFBD><4F><EFBFBD><EFBFBD><EFBFBD>g<EFBFBD><67><EFBFBD><EFBFBD><EFBFBD>̂<CC82><CE82>A<EFBFBD><41><EFBFBD>̓_<CC93>Ƃ<EFBFBD><C682><EFBFBD><EFBFBD>B<EFBFBD>ŏ<EFBFBD><C58F>̃Z<CC83>O<EFBFBD><4F><EFBFBD><EFBFBD><EFBFBD>g<EFBFBD>͔<EFBFBD><CD94><EFBFBD><EFBFBD>̒<EFBFBD><CC92><EFBFBD><EFBFBD>B
|
|||
|
TPointD nextPos = curPos + flow_uv * segLen * ((s == 0) ? 0.5 : 1.0);
|
|||
|
|
|||
|
// <20><><EFBFBD>_<EFBFBD><5F><EFBFBD>i<EFBFBD>[
|
|||
|
centerPosHalf[dir].push_back(nextPos);
|
|||
|
|
|||
|
// curPos<6F><73><EFBFBD>i<EFBFBD>߂<EFBFBD>
|
|||
|
curPos = nextPos;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (tooCurve[0] && tooCurve[1]) continue;
|
|||
|
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>炩<EFBFBD>ȏꍇ
|
|||
|
if (!tooCurve[0] && !tooCurve[1]) {
|
|||
|
brushStroke.centerPos = centerPosHalf[1];
|
|||
|
for (auto pos : centerPosHalf[0]) brushStroke.centerPos.push_front(pos);
|
|||
|
}
|
|||
|
// <20>Е<EFBFBD><D095><EFBFBD><EFBFBD><EFBFBD><EFBFBD>}<7D>J<EFBFBD>[<5B>u<EFBFBD>̏ꍇ<CC8F>A<EFBFBD><41><EFBFBD>炩<EFBFBD>ȕ<EFBFBD><C895>̃J<CC83>[<5B>u<EFBFBD>Α<F094BD91><CE91>ɔ<EFBFBD><C994>]<5D><><EFBFBD>ĉ<EFBFBD><C489><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
else {
|
|||
|
int OkId = (tooCurve[0]) ? 1 : 0;
|
|||
|
TPointD origin(pos_x, pos_y);
|
|||
|
TPointD okInitialVec = centerPosHalf[OkId].at(0) - origin;
|
|||
|
double thetaDeg = std::atan2(okInitialVec.y, okInitialVec.x) / M_PI_180;
|
|||
|
TAffine reflectAff = TTranslation(origin) * TRotation(thetaDeg) *
|
|||
|
TScale(-1.0, 1.0) * TRotation(-thetaDeg) *
|
|||
|
TTranslation(-origin);
|
|||
|
brushStroke.centerPos = centerPosHalf[OkId];
|
|||
|
for (auto pos : centerPosHalf[OkId]) {
|
|||
|
TPointD refPos = reflectAff * pos;
|
|||
|
brushStroke.centerPos.push_front(reflectAff * pos);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// <20><><EFBFBD><EFBFBD><EFBFBD>ŁA<C581><EFBFBD>t<EFBFBD><74><EFBFBD>[<5B><><EFBFBD>̗<EFBFBD><CC97><EFBFBD><EFBFBD>̌<EFBFBD><CC8C><EFBFBD><EFBFBD>ɏ]<5D><><EFBFBD>ăe<C483>N<EFBFBD>X<EFBFBD>`<60><><EFBFBD>̌<EFBFBD><CC8C><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>낦<EFBFBD>Ă݂<C482>
|
|||
|
if (referenceFrame >= 0) {
|
|||
|
TPointD referenceSamplePos =
|
|||
|
param[REFERENCE]->brushAff * TPointD(pos_x, pos_y);
|
|||
|
double2 reference_flow = getFlow(referenceSamplePos, REFERENCE);
|
|||
|
// <20>@<40><><EFBFBD><EFBFBD><EFBFBD>x<EFBFBD>N<EFBFBD>g<EFBFBD><67><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>u<EFBFBD><75><EFBFBD>V<EFBFBD><56><EFBFBD>W<EFBFBD>n<EFBFBD>ɕϊ<C995>
|
|||
|
TPointD reference_flow_uv =
|
|||
|
param[REFERENCE]->brushAff.inv() *
|
|||
|
TPointD(reference_flow.x, reference_flow.y) -
|
|||
|
param[REFERENCE]->brushAff.inv() * TPointD(0., 0.);
|
|||
|
// <20><><EFBFBD>ς<EFBFBD><CF82><EFBFBD><EFBFBD><EFBFBD><EFBFBD>A<EFBFBD><41><EFBFBD>]<5D><><EFBFBD>邩<EFBFBD><E982A9><EFBFBD>f<EFBFBD><66><EFBFBD><EFBFBD>
|
|||
|
double dot = originFlow.x * reference_flow_uv.x +
|
|||
|
originFlow.y * reference_flow_uv.y;
|
|||
|
if (dot < 0.0) {
|
|||
|
brushStroke.invert = !brushStroke.invert;
|
|||
|
}
|
|||
|
}
|
|||
|
// <20>o<EFBFBD>^
|
|||
|
brushStrokes.append(brushStroke);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// <20><><EFBFBD>X<EFBFBD>^<5E>[<5B><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
for (int f = 0; f < 2; f++) {
|
|||
|
if (flow_buf[f]) flow_buf_ras[f]->unlock();
|
|||
|
if (area_buf[f]) area_buf_ras[f]->unlock();
|
|||
|
if (color_buf[f]) color_buf_ras[f]->unlock();
|
|||
|
}
|
|||
|
|
|||
|
// <20>\<5C>[<5B>g
|
|||
|
StackMode stackMode = (StackMode)m_sortBy->getValue();
|
|||
|
if (stackMode != NoSort) {
|
|||
|
// <20>傫<EFBFBD><E582AB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>z<EFBFBD><7A><EFBFBD>őO<C591>A<EFBFBD><41><EFBFBD>Ȃ킿<C882><ED82BF><EFBFBD>i<EFBFBD><69><EFBFBD>j<EFBFBD>ɕ`<60><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>悤<EFBFBD>ɂ<EFBFBD><C982><EFBFBD>
|
|||
|
for (auto &stroke : brushStrokes) {
|
|||
|
switch (stackMode) {
|
|||
|
case Smaller:
|
|||
|
stroke.stack = stroke.length * stroke.widthHalf;
|
|||
|
break;
|
|||
|
case Larger:
|
|||
|
stroke.stack = -stroke.length * stroke.widthHalf;
|
|||
|
break;
|
|||
|
// Value = 0.3R 0.59G 0.11B
|
|||
|
case Darker:
|
|||
|
stroke.stack = 0.3 * stroke.color.r + 0.59 * stroke.color.g +
|
|||
|
0.11 * stroke.color.b;
|
|||
|
break;
|
|||
|
case Brighter:
|
|||
|
stroke.stack = -(0.3 * stroke.color.r + 0.59 * stroke.color.g +
|
|||
|
0.11 * stroke.color.b);
|
|||
|
break;
|
|||
|
case Random:
|
|||
|
stroke.stack = stroke.randomVal;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
qSort(brushStrokes.begin(), brushStrokes.end(), strokeStackGraterThan);
|
|||
|
}
|
|||
|
|
|||
|
// <20>^<5E>b<EFBFBD>`<60><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>f<EFBFBD>ɑ<C991><CE82>Ă<EFBFBD><C482><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>đ<EFBFBD><C491><EFBFBD><EFBFBD><EFBFBD><EFBFBD>銄<EFBFBD><E98A84>
|
|||
|
double sustain_width_to_skew = m_sustain_width_to_skew->getValue(frame);
|
|||
|
TPointD curve_point = m_curve_point->getValue(frame);
|
|||
|
// <20><><EFBFBD>_<EFBFBD><5F><EFBFBD>W<EFBFBD><57><EFBFBD>o<EFBFBD>^<5E><><EFBFBD>Ă<EFBFBD><C482><EFBFBD>
|
|||
|
for (auto stroke : brushStrokes) {
|
|||
|
int jointCount = stroke.centerPos.size();
|
|||
|
|
|||
|
// <20><><EFBFBD>_<EFBFBD>ʒu<CA92><75><EFBFBD>J<EFBFBD>[<5B>u<EFBFBD><75><EFBFBD><EFBFBD><EFBFBD>Ă䂪<C482>߂<EFBFBD><DF82>e<EFBFBD>X<EFBFBD>g
|
|||
|
if (curve_point != TPointD()) {
|
|||
|
for (int s = 0; s < stroke.centerPos.size(); s++) {
|
|||
|
TPointD p = stroke.centerPos[s];
|
|||
|
TPointD A = (1.0 - p.x) * (1.0 - p.x) * TPointD(0.0, 0.5) +
|
|||
|
2.0 * (1.0 - p.x) * p.x *
|
|||
|
TPointD(0.5 + curve_point.x, 0.5 + curve_point.y) +
|
|||
|
p.x * p.x * TPointD(1.0, 0.5);
|
|||
|
stroke.centerPos[s] = (1.0 - p.y) * (1.0 - p.y) * TPointD(p.x, 0.0) +
|
|||
|
2.0 * (1.0 - p.y) * p.y * TPointD(A.x, A.y) +
|
|||
|
p.y * p.y * TPointD(p.x, 1.0);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
for (int s = 0; s < jointCount; s++) {
|
|||
|
// <20>O<EFBFBD><4F><EFBFBD>̓_
|
|||
|
TPointD back = (s == 0) ? stroke.centerPos[0] : stroke.centerPos[s - 1];
|
|||
|
TPointD fore = (s == jointCount - 1) ? stroke.centerPos[jointCount - 1]
|
|||
|
: stroke.centerPos[s + 1];
|
|||
|
TPointD vec = normalize(fore - back);
|
|||
|
TPointD n(-vec.y * stroke.widthHalf, vec.x * stroke.widthHalf);
|
|||
|
|
|||
|
// <20><><EFBFBD><EFBFBD><EFBFBD>ŁA<C581>e<EFBFBD>N<EFBFBD>X<EFBFBD>`<60><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ɍ<EFBFBD><C98C><EFBFBD><EFBFBD>鏈<EFBFBD><E98F88><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
if (sustain_width_to_skew > 0.0) {
|
|||
|
// <20><><EFBFBD>ʍ<EFBFBD><CA8D>W<EFBFBD>ɕϊ<C995>
|
|||
|
TPointD nr = param[CURRENT]->brushAff * n -
|
|||
|
param[CURRENT]->brushAff * TPointD(0, 0);
|
|||
|
TPointD vr = param[CURRENT]->brushAff * vec -
|
|||
|
param[CURRENT]->brushAff * TPointD(0, 0);
|
|||
|
// nr <20><> vr<76><72><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ɂ<EFBFBD><C982><EFBFBD><EFBFBD>Â<EFBFBD><C382>悤<EFBFBD>ɁAnr<6E><72><EFBFBD><EFBFBD><EFBFBD>]<5D><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
double nr_len = norm(nr);
|
|||
|
|
|||
|
nr = (1.0 / nr_len) * nr;
|
|||
|
vr = normalize(vr);
|
|||
|
|
|||
|
// <20><><EFBFBD><EFBFBD>
|
|||
|
double theta = std::acos(nr * vr);
|
|||
|
double new_theta = sustain_width_to_skew * M_PI * 0.5 +
|
|||
|
(1.0 - sustain_width_to_skew) * theta;
|
|||
|
// <20>O<EFBFBD>ς̐<CF82><CC90><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Anr<6E><72>vr<76>̂ǂ<CC82><C782><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ɂ<EFBFBD><C982>邩<EFBFBD><E982A9><EFBFBD>f
|
|||
|
if (cross(vr, nr) < 0) new_theta = -new_theta;
|
|||
|
TPointD new_nr(vr.x * cos(new_theta) - vr.y * sin(new_theta),
|
|||
|
vr.x * sin(new_theta) + vr.y * cos(new_theta));
|
|||
|
new_nr = nr_len * new_nr;
|
|||
|
|
|||
|
// <20>u<EFBFBD><75><EFBFBD>V<EFBFBD><56><EFBFBD>W<EFBFBD>ɂ<EFBFBD><C982>ǂ<EFBFBD>
|
|||
|
n = param[CURRENT]->brushAff.inv() *
|
|||
|
(new_nr + param[CURRENT]->brushAff * TPointD(0, 0));
|
|||
|
}
|
|||
|
|
|||
|
// <20>͂<EFBFBD><CD82><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
if (p.anti_jaggy && s == 0) {
|
|||
|
TPointD edgePos = stroke.centerPos[0] * 2.0 - stroke.originPos;
|
|||
|
brushVertices.append(
|
|||
|
BrushVertex(edgePos + n, 0.0, (stroke.invert) ? 1.0 : 0.0));
|
|||
|
brushVertices.append(
|
|||
|
BrushVertex(edgePos - n, 1.0, (stroke.invert) ? 1.0 : 0.0));
|
|||
|
}
|
|||
|
|
|||
|
double texCoord_v = (stroke.invert) ? 1.0 - (double(s) / segmentAmount)
|
|||
|
: double(s) / segmentAmount;
|
|||
|
|
|||
|
if (p.anti_jaggy) texCoord_v = 0.25 + 0.5 * texCoord_v;
|
|||
|
|
|||
|
brushVertices.append(
|
|||
|
BrushVertex(stroke.centerPos[s] + n, 0.0, texCoord_v));
|
|||
|
brushVertices.append(
|
|||
|
BrushVertex(stroke.centerPos[s] - n, 1.0, texCoord_v));
|
|||
|
|
|||
|
// <20><><EFBFBD>̂͂<CC82><CD82><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
if (p.anti_jaggy && s == jointCount - 1) {
|
|||
|
TPointD edgePos =
|
|||
|
stroke.centerPos[jointCount - 1] * 2.0 - stroke.originPos;
|
|||
|
brushVertices.append(
|
|||
|
BrushVertex(edgePos + n, 0.0, (stroke.invert) ? 0.0 : 1.0));
|
|||
|
brushVertices.append(
|
|||
|
BrushVertex(edgePos - n, 1.0, (stroke.invert) ? 0.0 : 1.0));
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
//------------------------------------------------------------
|
|||
|
void Iwa_FlowPaintBrushFx::doCompute(TTile &tile, double frame,
|
|||
|
const TRenderSettings &ri) {
|
|||
|
if (!m_brush.isConnected()) {
|
|||
|
tile.getRaster()->clear();
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
TDimension b_size(0, 0);
|
|||
|
int lastFrame = 0;
|
|||
|
std::vector<TRasterP> brushRasters;
|
|||
|
// <20>u<EFBFBD><75><EFBFBD>V<EFBFBD>^<5E>b<EFBFBD>`<60>̃<EFBFBD><CC83>X<EFBFBD>^<5E>[<5B>f<EFBFBD>[<5B>^<5E><><EFBFBD>擾
|
|||
|
getBrushRasters(brushRasters, b_size, lastFrame, tile, ri);
|
|||
|
|
|||
|
if (lastFrame == 0) {
|
|||
|
tile.getRaster()->clear();
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
// <20>p<EFBFBD><70><EFBFBD><EFBFBD><EFBFBD>[<5B>^<5E>擾
|
|||
|
FlowPaintBrushFxParam p = getParam(tile, frame, ri);
|
|||
|
p.lastFrame = lastFrame;
|
|||
|
|
|||
|
int vCount = p.reso * 4;
|
|||
|
// <20>㉺<EFBFBD>Ƀ}<7D>[<5B>W<EFBFBD><57><EFBFBD>p<EFBFBD>̒<EFBFBD><CC92>_<EFBFBD><5F><EFBFBD>lj<EFBFBD>
|
|||
|
if (p.anti_jaggy) vCount += 4;
|
|||
|
|
|||
|
QVector<BrushVertex> brushVertices;
|
|||
|
QList<BrushStroke> brushStrokes;
|
|||
|
computeBrushVertices(brushVertices, brushStrokes, p, tile, frame, ri);
|
|||
|
|
|||
|
GLfloat m[16] = {(float)p.hVec.x,
|
|||
|
(float)p.hVec.y,
|
|||
|
0.f,
|
|||
|
0.f,
|
|||
|
(float)p.vVec.x,
|
|||
|
(float)p.vVec.y,
|
|||
|
0.f,
|
|||
|
0.f,
|
|||
|
0.f,
|
|||
|
0.f,
|
|||
|
1.f,
|
|||
|
0.f,
|
|||
|
(float)p.origin_pos.x,
|
|||
|
(float)p.origin_pos.y,
|
|||
|
0.f,
|
|||
|
1.f};
|
|||
|
|
|||
|
QOpenGLContext *context = new QOpenGLContext();
|
|||
|
if (QOpenGLContext::currentContext())
|
|||
|
context->setShareContext(QOpenGLContext::currentContext());
|
|||
|
context->setFormat(QSurfaceFormat::defaultFormat());
|
|||
|
context->create();
|
|||
|
context->makeCurrent(ri.m_offScreenSurface.get());
|
|||
|
|
|||
|
// <20>e<EFBFBD>N<EFBFBD>X<EFBFBD>`<60><><EFBFBD>̊m<CC8A><6D>
|
|||
|
std::vector<QOpenGLTexture *> brushTextures;
|
|||
|
for (auto texRas : brushRasters) {
|
|||
|
QImage texImg(texRas->getRawData(), b_size.lx, b_size.ly,
|
|||
|
QImage::Format_RGBA8888);
|
|||
|
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>3<EFBFBD>{<7B>ɂ<EFBFBD><C982><EFBFBD><EFBFBD>i<EFBFBD><69><EFBFBD>E<EFBFBD><45>x1<78>̃}<7D>[<5B>W<EFBFBD><57><EFBFBD>j
|
|||
|
if (p.anti_jaggy) {
|
|||
|
QImage resizedImage(texImg.width() * 3, texImg.height() * 2,
|
|||
|
QImage::Format_RGBA8888);
|
|||
|
QPainter painter(&resizedImage);
|
|||
|
painter.setCompositionMode(QPainter::CompositionMode_Source);
|
|||
|
painter.fillRect(resizedImage.rect(), Qt::transparent);
|
|||
|
painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
|
|||
|
painter.drawImage(QPoint(texImg.width(), texImg.height() / 2), texImg);
|
|||
|
painter.end();
|
|||
|
texImg = resizedImage;
|
|||
|
}
|
|||
|
|
|||
|
QOpenGLTexture *texture = new QOpenGLTexture(texImg.rgbSwapped());
|
|||
|
texture->setMinificationFilter(QOpenGLTexture::LinearMipMapLinear);
|
|||
|
texture->setMagnificationFilter(QOpenGLTexture::Linear);
|
|||
|
brushTextures.push_back(texture);
|
|||
|
}
|
|||
|
|
|||
|
TDimensionI outDim = tile.getRaster()->getSize();
|
|||
|
|
|||
|
// <20>`<60><>
|
|||
|
{
|
|||
|
std::unique_ptr<QOpenGLFramebufferObject> fb(
|
|||
|
new QOpenGLFramebufferObject(p.dim.lx, p.dim.ly));
|
|||
|
|
|||
|
fb->bind();
|
|||
|
|
|||
|
glViewport(0, 0, p.dim.lx, p.dim.ly);
|
|||
|
glClearColor(0, 0, 0, 0);
|
|||
|
glClear(GL_COLOR_BUFFER_BIT);
|
|||
|
|
|||
|
glMatrixMode(GL_PROJECTION);
|
|||
|
glLoadIdentity();
|
|||
|
gluOrtho2D(0, p.dim.lx, 0, p.dim.ly);
|
|||
|
|
|||
|
glMatrixMode(GL_MODELVIEW);
|
|||
|
|
|||
|
glLoadIdentity();
|
|||
|
glLoadMatrixf(m);
|
|||
|
|
|||
|
glDisable(GL_POLYGON_SMOOTH);
|
|||
|
glEnable(GL_BLEND);
|
|||
|
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
|||
|
glEnable(GL_TEXTURE_2D);
|
|||
|
|
|||
|
glEnableClientState(GL_VERTEX_ARRAY);
|
|||
|
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
|||
|
{
|
|||
|
glVertexPointer(2, GL_DOUBLE, sizeof(BrushVertex), brushVertices.data());
|
|||
|
glTexCoordPointer(2, GL_DOUBLE, sizeof(BrushVertex),
|
|||
|
(double *)brushVertices.data() + 2);
|
|||
|
}
|
|||
|
|
|||
|
int first = 0;
|
|||
|
|
|||
|
for (auto stroke : brushStrokes) {
|
|||
|
brushTextures[stroke.textureId]->bind();
|
|||
|
|
|||
|
glColor4d(stroke.color.r, stroke.color.g, stroke.color.b, stroke.color.a);
|
|||
|
glDrawArrays(GL_TRIANGLE_STRIP, first, vCount);
|
|||
|
first += vCount;
|
|||
|
}
|
|||
|
|
|||
|
glFlush();
|
|||
|
|
|||
|
glDisableClientState(GL_VERTEX_ARRAY);
|
|||
|
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
|||
|
|
|||
|
QImage img =
|
|||
|
fb->toImage().scaled(QSize(p.dim.lx, p.dim.ly), Qt::IgnoreAspectRatio,
|
|||
|
Qt::SmoothTransformation);
|
|||
|
// y<><79><EFBFBD>W<EFBFBD>͏㉺<CD8F><E389BA><EFBFBD>]<5D><><EFBFBD>Ă<EFBFBD><C482>邱<EFBFBD>Ƃɒ<C682><C992>ӁI<D381>I
|
|||
|
QRect subRect(tile.m_pos.x - (int)std::round(p.bbox.getP00().x),
|
|||
|
(int)std::round(p.bbox.getP01().y) - tile.m_pos.y - outDim.ly,
|
|||
|
outDim.lx, outDim.ly);
|
|||
|
QImage subImg = img.copy(subRect);
|
|||
|
|
|||
|
TRaster32P ras32 = tile.getRaster();
|
|||
|
TRaster64P ras64 = tile.getRaster();
|
|||
|
TRaster32P resultRas;
|
|||
|
if (ras32)
|
|||
|
resultRas = ras32;
|
|||
|
else if (ras64) {
|
|||
|
resultRas = TRaster32P(outDim.lx, outDim.ly);
|
|||
|
resultRas->lock();
|
|||
|
}
|
|||
|
|
|||
|
for (int y = 0; y < outDim.ly; y++) {
|
|||
|
QRgb *rgb_p =
|
|||
|
reinterpret_cast<QRgb *>(subImg.scanLine(outDim.ly - 1 - y));
|
|||
|
TPixel32 *dst_p = resultRas->pixels(y);
|
|||
|
for (int x = 0; x < outDim.lx; x++, rgb_p++, dst_p++) {
|
|||
|
(*dst_p).r = (unsigned char)qRed(*rgb_p);
|
|||
|
(*dst_p).g = (unsigned char)qGreen(*rgb_p);
|
|||
|
(*dst_p).b = (unsigned char)qBlue(*rgb_p);
|
|||
|
(*dst_p).m = (unsigned char)qAlpha(*rgb_p);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (ras64) {
|
|||
|
TRop::convert(ras64, resultRas);
|
|||
|
resultRas->unlock();
|
|||
|
}
|
|||
|
|
|||
|
fb->release();
|
|||
|
}
|
|||
|
context->deleteLater();
|
|||
|
|
|||
|
for (auto tex : brushTextures) {
|
|||
|
delete tex;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//------------------------------------------------------------
|
|||
|
|
|||
|
void Iwa_FlowPaintBrushFx::getParamUIs(TParamUIConcept *&concepts,
|
|||
|
int &length) {
|
|||
|
concepts = new TParamUIConcept[length = 4];
|
|||
|
|
|||
|
concepts[0].m_type = TParamUIConcept::POINT;
|
|||
|
concepts[0].m_label = "Origin";
|
|||
|
concepts[0].m_params.push_back(m_origin_pos);
|
|||
|
|
|||
|
concepts[1].m_type = TParamUIConcept::POINT;
|
|||
|
concepts[1].m_label = "Horizontal Range";
|
|||
|
concepts[1].m_params.push_back(m_horizontal_pos);
|
|||
|
|
|||
|
concepts[2].m_type = TParamUIConcept::POINT;
|
|||
|
concepts[2].m_label = "Vertical Range";
|
|||
|
concepts[2].m_params.push_back(m_vertical_pos);
|
|||
|
|
|||
|
concepts[3].m_type = TParamUIConcept::PARALLELOGRAM;
|
|||
|
concepts[3].m_params.push_back(m_origin_pos);
|
|||
|
concepts[3].m_params.push_back(m_horizontal_pos);
|
|||
|
concepts[3].m_params.push_back(m_vertical_pos);
|
|||
|
concepts[3].m_params.push_back(m_curve_point);
|
|||
|
}
|
|||
|
|
|||
|
//------------------------------------------------------------
|
|||
|
|
|||
|
std::string Iwa_FlowPaintBrushFx::getAlias(double frame,
|
|||
|
const TRenderSettings &info) const {
|
|||
|
double refFrame = m_reference_frame->getValue(frame);
|
|||
|
double prevalence = m_reference_prevalence->getValue(frame);
|
|||
|
if (refFrame < 0 || prevalence == 0.0) {
|
|||
|
// check if the brush is wobbled
|
|||
|
double wobble = m_pos_wobble->getValue(frame);
|
|||
|
std::string alias = TStandardRasterFx::getAlias(frame, info);
|
|||
|
if (areAlmostEqual(wobble, 0.0))
|
|||
|
return alias;
|
|||
|
else {
|
|||
|
alias.insert(getFxType().length() + 1, std::to_string(frame) + ",");
|
|||
|
return alias;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
std::string alias = getFxType();
|
|||
|
alias += "[";
|
|||
|
|
|||
|
// alias degli effetti connessi alle porte di input separati da virgole
|
|||
|
// una porta non connessa da luogo a un alias vuoto (stringa vuota)
|
|||
|
for (int i = 0; i < getInputPortCount(); ++i) {
|
|||
|
TFxPort *port = getInputPort(i);
|
|||
|
if (port->isConnected()) {
|
|||
|
TRasterFxP ifx = port->getFx();
|
|||
|
assert(ifx);
|
|||
|
alias += ifx->getAlias(frame, info);
|
|||
|
// add alias of flow, area and color map in the reference frame
|
|||
|
if (getInputPortName(i) != "Brush") {
|
|||
|
alias += ",";
|
|||
|
alias += ifx->getAlias(refFrame, info);
|
|||
|
}
|
|||
|
}
|
|||
|
alias += ",";
|
|||
|
}
|
|||
|
|
|||
|
std::string paramalias("");
|
|||
|
for (int i = 0; i < getParams()->getParamCount(); ++i) {
|
|||
|
TParam *param = getParams()->getParam(i);
|
|||
|
paramalias += param->getName() + "=" + param->getValueAlias(frame, 3);
|
|||
|
}
|
|||
|
|
|||
|
return alias + std::to_string(frame) + "," + std::to_string(getIdentifier()) +
|
|||
|
paramalias + "]";
|
|||
|
}
|
|||
|
|
|||
|
FX_PLUGIN_IDENTIFIER(Iwa_FlowPaintBrushFx, "iwa_FlowPaintBrushFx")
|