diff --git a/stuff/config/current.txt b/stuff/config/current.txt
index 1bf1c92..8c0cb2d 100644
--- a/stuff/config/current.txt
+++ b/stuff/config/current.txt
@@ -187,6 +187,7 @@
- "STD_colorRaylitFx.intensity" "Intensity"
- "STD_colorRaylitFx.decay" "Decay"
- "STD_colorRaylitFx.smoothness" "Smoothness"
+ - "STD_colorRaylitFx.radius" "Radius"
- "STD_colorRaylitFx.includeInput" "Keep Image"
- "STD_erodeDilateFx" "Erode/Dilate"
@@ -370,6 +371,7 @@
- "STD_raylitFx.intensity" "Intensity"
- "STD_raylitFx.decay" "Decay"
- "STD_raylitFx.smoothness" "Smoothness"
+ - "STD_raylitFx.radius" "Radius"
- "STD_raylitFx.includeInput" "Keep Image"
- "STD_raylitFx.invert" "Invert"
diff --git a/stuff/profiles/layouts/fxs/STD_colorRaylitFx.xml b/stuff/profiles/layouts/fxs/STD_colorRaylitFx.xml
index b103cff..7f842bf 100644
--- a/stuff/profiles/layouts/fxs/STD_colorRaylitFx.xml
+++ b/stuff/profiles/layouts/fxs/STD_colorRaylitFx.xml
@@ -5,6 +5,7 @@
intensity
decay
smoothness
+ radius
includeInput
diff --git a/stuff/profiles/layouts/fxs/STD_raylitFx.xml b/stuff/profiles/layouts/fxs/STD_raylitFx.xml
index 307e02f..76fbcad 100644
--- a/stuff/profiles/layouts/fxs/STD_raylitFx.xml
+++ b/stuff/profiles/layouts/fxs/STD_raylitFx.xml
@@ -6,6 +6,7 @@
intensity
decay
smoothness
+ radius
includeInput
invert
diff --git a/toonz/sources/common/trop/traylit.cpp b/toonz/sources/common/trop/traylit.cpp
index 7785ed1..a0a09c9 100644
--- a/toonz/sources/common/trop/traylit.cpp
+++ b/toonz/sources/common/trop/traylit.cpp
@@ -47,22 +47,24 @@ of the ray we're tracing
/*-- 8bit/16bitの違いを吸収する係数 --*/
double factor = max / 255.0;
- double scale =
- params.m_scale; // NOTE: These variable initializations are, well,
- double decay = log(params.m_decay / 100.0 + 1.0) +
- 1.0; // heuristic at least. They were probably tested
- double intensity =
- 1e8 * log(params.m_intensity / 100.0 + 1.0) /
- scale; // to be good, but didn't quite make any REAL sense.
- double smoothness = log(params.m_smoothness * 5.0 / 100.0 + 1.0); //
+ // NOTE: These variable initializations are, well,
+ // heuristic at least. They were probably tested
+ // to be good, but didn't quite make any REAL sense.
// They could be done MUCH better, but changing them
+ // would alter the way raylit has been applied until now.
+ // Should be changed at some point, though...
+
+ double scale = params.m_scale;
+ double decay = log(params.m_decay / 100.0 + 1.0) + 1.0;
+ double intensity = 1e8 * log(params.m_intensity / 100.0 + 1.0) / scale;
+ double smoothness = log(params.m_smoothness * 5.0 / 100.0 + 1.0);
+ double radius = params.m_radius;
+
/*-- 1ステップ進んだ時、次のピクセルで光源が無かったときの光の弱まる割合 --*/
- double neg_delta_p =
- smoothness *
- intensity; // would alter the way raylit has been applied until now.
+ double neg_delta_p = smoothness * intensity;
/*-- 1ステップ進んだ時、次のピクセルで光源が有ったときの光の強まる割合 --*/
double quot_delta_p = intensity / max; //
- // Should be changed at some point, though...
+
/*--
* m_colorはRaylitFxのColor値。r_fac、g_fac、b_facは各チャンネルをPremultiplyした値
* --*/
@@ -136,13 +138,23 @@ of the ray we're tracing
bool insideDst = (x >= 0) && (y >= 0);
if (insideDst) {
// Write the corresponding destination pixel
- if (lightness > 0.0)
- value = (int)(factor * lightness /
- (rayPos.x *
- pow((double)(sq(rayPos.x) + sq(rayPos.y) + sq_z),
- decay)) +
- 0.5); // * ^-d... 0.5 rounds
- else
+ if (lightness > 0.0) {
+ if (radius == 0.0) {
+ value = (int)(factor * lightness /
+ (rayPos.x *
+ pow((double)(sq(rayPos.x) + sq(rayPos.y) + sq_z),
+ decay)) +
+ 0.5); // * ^-d... 0.5 rounds
+ } else {
+ double ratio = std::max(0.001, 1.0 - radius / norm(rayPos));
+ value = (int)(factor * lightness /
+ (rayPos.x * ratio *
+ pow((double)(sq(rayPos.x * ratio) +
+ sq(rayPos.y * ratio) + sq_z),
+ decay)) +
+ 0.5); // * ^-d... 0.5 rounds
+ }
+ } else
value = 0;
// NOTE: pow() could be slow. If that is the case, it could be cached
@@ -188,15 +200,19 @@ void performColorRaylit(T *bufIn, T *bufOut, int dxIn, int dyIn, int dxOut,
double lightness_r, lightness_g, lightness_b;
double factor = max / 255.0;
- double scale =
- params.m_scale; // NOTE: These variable initializations are, well,
- double decay = log(params.m_decay / 100.0 + 1.0) +
- 1.0; // heuristic at least. They were probably tested
- double intensity =
- 1e8 * log(params.m_intensity / 100.0 + 1.0) /
- scale; // to be good, but didn't quite make any REAL sense.
- double smoothness = log(params.m_smoothness * 5.0 / 100.0 + 1.0); //
+ // NOTE: These variable initializations are, well,
+ // heuristic at least. They were probably tested
+ // to be good, but didn't quite make any REAL sense.
// They could be done MUCH better, but changing them
+ // would alter the way raylit has been applied until now.
+ // Should be changed at some point, though...
+
+ double scale = params.m_scale;
+ double decay = log(params.m_decay / 100.0 + 1.0) + 1.0;
+ double intensity = 1e8 * log(params.m_intensity / 100.0 + 1.0) / scale;
+ double smoothness = log(params.m_smoothness * 5.0 / 100.0 + 1.0);
+ double radius = params.m_radius;
+
double neg_delta_p =
smoothness *
intensity; // would alter the way raylit has been applied until now.
@@ -260,9 +276,18 @@ void performColorRaylit(T *bufIn, T *bufOut, int dxIn, int dyIn, int dxOut,
bool insideDst = (x >= 0) && (y >= 0);
if (insideDst) {
// Write the corresponding destination pixel
- fac =
- factor / (rayPos.x *
- pow((double)(sq(rayPos.x) + sq(rayPos.y) + sq_z), decay));
+ if (radius == 0.0) {
+ fac = factor /
+ (rayPos.x *
+ pow((double)(sq(rayPos.x) + sq(rayPos.y) + sq_z), decay));
+ } else {
+ double ratio = std::max(0.001, 1.0 - radius / norm(rayPos));
+ fac =
+ factor /
+ (rayPos.x * ratio *
+ pow((double)(sq(rayPos.x * ratio) + sq(rayPos.y * ratio) + sq_z),
+ decay));
+ }
// NOTE: pow() could be slow. If that is the case, it could be cached
// for the whole octant along the longest ray at integer positions,
@@ -343,12 +368,12 @@ void computeOctant(const TRasterPT &src, const TRasterPT &dst, int octant,
dyIn = srcWrap, dyOut = dstWrap, y0 = tfloor(pOut.y), y1 = lyOut;
if (octant == 5 || octant == 8)
dyIn = -srcWrap, dyOut = -dstWrap, y0 = lyOut - tfloor(pOut.y) - 1,
- y1 = lyOut, std::swap(srcRect.y0, srcRect.y1), srcRect.y0 = lyOut - srcRect.y0,
- srcRect.y1 = lyOut - srcRect.y1;
+ y1 = lyOut, std::swap(srcRect.y0, srcRect.y1),
+ srcRect.y0 = lyOut - srcRect.y0, srcRect.y1 = lyOut - srcRect.y1;
if (octant == 6 || octant == 7)
dxIn = -srcWrap, dxOut = -dstWrap, x0 = lyOut - tfloor(pOut.y) - 1,
- x1 = lyOut, std::swap(srcRect.y0, srcRect.y1), srcRect.y0 = lyOut - srcRect.y0,
- srcRect.y1 = lyOut - srcRect.y1;
+ x1 = lyOut, std::swap(srcRect.y0, srcRect.y1),
+ srcRect.y0 = lyOut - srcRect.y0, srcRect.y1 = lyOut - srcRect.y1;
/*-- 縦向きのピザ領域を計算する場合は、90度回転してから --*/
// Swap x and y axis where necessary
diff --git a/toonz/sources/include/tparamuiconcept.h b/toonz/sources/include/tparamuiconcept.h
index b72a278..3d0fbc8 100644
--- a/toonz/sources/include/tparamuiconcept.h
+++ b/toonz/sources/include/tparamuiconcept.h
@@ -66,6 +66,8 @@ public:
LINEAR_RANGE, // A band-like range between two points.
// { [2 TPointParamP] }
+ RAYLIT,
+
TYPESCOUNT
};
diff --git a/toonz/sources/include/trop.h b/toonz/sources/include/trop.h
index 6602e34..6c80b10 100644
--- a/toonz/sources/include/trop.h
+++ b/toonz/sources/include/trop.h
@@ -350,6 +350,7 @@ struct RaylitParams {
double m_scale;
bool m_invert;
bool m_includeInput;
+ double m_radius;
};
//! Make raylit effect on \b srcRas raster and put the result in \b dstRas.
diff --git a/toonz/sources/stdfx/raylitfx.cpp b/toonz/sources/stdfx/raylitfx.cpp
index 34d6f3c..08c9260 100644
--- a/toonz/sources/stdfx/raylitfx.cpp
+++ b/toonz/sources/stdfx/raylitfx.cpp
@@ -19,6 +19,7 @@ protected:
TDoubleParamP m_decay;
TDoubleParamP m_smoothness;
TBoolParamP m_includeInput;
+ TDoubleParamP m_radius;
public:
BaseRaylitFx()
@@ -27,18 +28,22 @@ public:
, m_intensity(60)
, m_decay(1.0)
, m_smoothness(100)
- , m_includeInput(false) {
+ , m_includeInput(false)
+ , m_radius(0.0) {
m_p->getX()->setMeasureName("fxLength");
m_p->getY()->setMeasureName("fxLength");
+ m_radius->setMeasureName("fxLength");
bindParam(this, "p", m_p);
bindParam(this, "z", m_z);
bindParam(this, "intensity", m_intensity);
bindParam(this, "decay", m_decay);
bindParam(this, "smoothness", m_smoothness);
bindParam(this, "includeInput", m_includeInput);
+ bindParam(this, "radius", m_radius);
addInputPort("Source", m_input);
+ m_radius->setValueRange(0.0, std::numeric_limits::max());
}
~BaseRaylitFx() {}
@@ -56,11 +61,19 @@ public:
const TRenderSettings &info) override;
void getParamUIs(TParamUIConcept *&concepts, int &length) override {
- concepts = new TParamUIConcept[length = 1];
+ concepts = new TParamUIConcept[length = 3];
concepts[0].m_type = TParamUIConcept::POINT;
concepts[0].m_label = "Center";
concepts[0].m_params.push_back(m_p);
+
+ concepts[1].m_type = TParamUIConcept::RADIUS;
+ concepts[1].m_label = "Radius";
+ concepts[1].m_params.push_back(m_radius);
+ concepts[1].m_params.push_back(m_p);
+
+ concepts[2].m_type = TParamUIConcept::RAYLIT;
+ concepts[2].m_params.push_back(m_p);
}
};
@@ -69,7 +82,7 @@ public:
bool BaseRaylitFx::doGetBBox(double frame, TRectD &bBox,
const TRenderSettings &info) {
if (m_input.isConnected()) {
- bool ret = m_input->doGetBBox(frame, bBox, info);
+ bool ret = m_input->doGetBBox(frame, bBox, info);
if (ret) bBox = TConsts::infiniteRectD;
return ret;
} else {
@@ -171,6 +184,7 @@ void RaylitFx::doCompute(TTile &tileOut, double frame,
params.m_lightOriginSrc.y -= (int)tileIn.m_pos.y;
params.m_lightOriginDst.x -= (int)tileOut.m_pos.x;
params.m_lightOriginDst.y -= (int)tileOut.m_pos.y;
+ params.m_radius = m_radius->getValue(frame);
TRop::raylit(tileOut.getRaster(), tileIn.getRaster(), params);
}
@@ -229,6 +243,7 @@ void ColorRaylitFx::doCompute(TTile &tileOut, double frame,
params.m_lightOriginSrc.y -= (int)tileIn.m_pos.y;
params.m_lightOriginDst.x -= (int)tileOut.m_pos.x;
params.m_lightOriginDst.y -= (int)tileOut.m_pos.y;
+ params.m_radius = m_radius->getValue(frame);
TRop::glassRaylit(tileOut.getRaster(), tileIn.getRaster(), params);
}
diff --git a/toonz/sources/tnztools/edittoolgadgets.cpp b/toonz/sources/tnztools/edittoolgadgets.cpp
index eb95383..ed86a52 100644
--- a/toonz/sources/tnztools/edittoolgadgets.cpp
+++ b/toonz/sources/tnztools/edittoolgadgets.cpp
@@ -1529,6 +1529,176 @@ void LinearRangeFxGadget::leftButtonUp(const TPointD &pos,
const TMouseEvent &) {
m_handle = None;
}
+
+//=============================================================================
+
+class RayLitFxGadget final : public FxGadget {
+ TPointParamP m_center;
+
+ enum HANDLE { Body = 0, Near, Far, None } m_handle = None;
+
+ TPointD m_clickedPos, m_mousePos;
+ TPointD m_targetPos, m_anotherPos;
+
+public:
+ RayLitFxGadget(FxGadgetController *controller,
+ const TPointParamP ¢erPoint);
+
+ void draw(bool picking) override;
+
+ void leftButtonDown(const TPointD &pos, const TMouseEvent &) override;
+ void leftButtonDrag(const TPointD &pos, const TMouseEvent &) override;
+ void leftButtonUp(const TPointD &pos, const TMouseEvent &) override;
+};
+
+//---------------------------------------------------------------------------
+
+RayLitFxGadget::RayLitFxGadget(FxGadgetController *controller,
+ const TPointParamP ¢erPoint)
+ : FxGadget(controller, 3), m_center(centerPoint) {
+ addParam(centerPoint->getX());
+ addParam(centerPoint->getY());
+}
+
+//---------------------------------------------------------------------------
+
+void RayLitFxGadget::draw(bool picking) {
+ auto setColorById = [&](int id) {
+ if (isSelected(id))
+ glColor3dv(m_selectedColor);
+ else
+ glColor3d(0, 0, 1);
+ };
+
+ auto drawArrow = [&]() {
+ double arrowLength = getPixelSize() * 20;
+ double arrowTip = getPixelSize() * 5;
+
+ glBegin(GL_LINES);
+ glVertex2d(-arrowLength, 0.0);
+ glVertex2d(arrowLength, 0.0);
+
+ glVertex2d(-arrowLength + arrowTip, arrowTip);
+ glVertex2d(-arrowLength, 0.0);
+
+ glVertex2d(-arrowLength + arrowTip, -arrowTip);
+ glVertex2d(-arrowLength, 0.0);
+
+ glVertex2d(arrowLength - arrowTip, arrowTip);
+ glVertex2d(arrowLength, 0.0);
+
+ glVertex2d(arrowLength - arrowTip, -arrowTip);
+ glVertex2d(arrowLength, 0.0);
+ glEnd();
+ };
+
+ setPixelSize();
+ double lineHalf = getPixelSize() * 100;
+ double lineInterval = getPixelSize() * 50;
+ double r = getPixelSize() * 3;
+
+ glPushMatrix();
+
+ TPointD center = getValue(m_center);
+ double dCenter = norm(center);
+ TPointD handleVec;
+ if (dCenter > lineHalf) {
+ handleVec = normalize(center) * lineHalf;
+ setColorById(Body);
+ glPushName(getId() + Body);
+ glBegin(GL_LINES);
+ glVertex2d(handleVec.x * 0.95, handleVec.y * 0.95);
+ glVertex2d(-handleVec.x * 0.95, -handleVec.y * 0.95);
+ glEnd();
+ glPopName();
+
+ double angle = std::atan2(-center.y, -center.x) * M_180_PI;
+ double theta = M_180_PI * lineInterval / dCenter;
+
+ glColor3d(0, 0, 1);
+ glLineStipple(1, 0x00FF);
+ glEnable(GL_LINE_STIPPLE);
+ glPushMatrix();
+ glTranslated(center.x, center.y, 0);
+ glRotated(angle, 0, 0, 1);
+ for (int i = -3; i <= 3; i++) {
+ if (i == 0) continue;
+ glPushMatrix();
+ glRotated(theta * (double)i, 0, 0, 1);
+ glBegin(GL_LINES);
+ glVertex2d(dCenter - lineHalf, 0.0);
+ glVertex2d(dCenter + lineHalf, 0.0);
+ glEnd();
+ glPopMatrix();
+ }
+
+ glPopMatrix();
+ glDisable(GL_LINE_STIPPLE);
+
+ for (int id = Near; id <= Far; id++) {
+ TPointD hPos = (id == Near) ? handleVec : -handleVec;
+ setColorById(id);
+ glPushName(getId() + id);
+ glPushMatrix();
+ glTranslated(hPos.x, hPos.y, 0);
+ tglDrawRect(-r, -r, r, r);
+ glPopMatrix();
+ glPopName();
+ }
+ }
+
+ if (m_handle == Body) {
+ glPushMatrix();
+ TPointD centerOffset = center - m_targetPos;
+ handleVec = normalize(m_targetPos) * lineHalf;
+ glTranslated(centerOffset.x, centerOffset.y, 0);
+ glBegin(GL_LINES);
+ glVertex2d(handleVec.x, handleVec.y);
+ glVertex2d(-handleVec.x, -handleVec.y);
+ glEnd();
+ glPopMatrix();
+ }
+ glPopMatrix();
+}
+
+//---------------------------------------------------------------------------
+
+void RayLitFxGadget::leftButtonDown(const TPointD &pos, const TMouseEvent &) {
+ m_handle = (HANDLE)m_selected;
+ if (m_handle == None) return;
+ m_clickedPos = pos;
+ m_targetPos = getValue(m_center);
+}
+
+//---------------------------------------------------------------------------
+
+void RayLitFxGadget::leftButtonDrag(const TPointD &pos, const TMouseEvent &e) {
+ if (m_handle == None) return;
+ TPointD d = pos - m_clickedPos;
+
+ if (m_handle == Body) {
+ setValue(m_center, m_targetPos + d);
+ return;
+ }
+
+ double angle =
+ std::atan2(pos.y, pos.x) - std::atan2(m_clickedPos.y, m_clickedPos.x);
+ double scale = norm(pos) / norm(m_clickedPos);
+
+ QTransform transform;
+ QPointF p = transform.rotateRadians(angle)
+ .scale(scale, scale)
+ .map(QPointF(m_targetPos.x, m_targetPos.y));
+
+ setValue(m_center, TPointD(p.x(), p.y()));
+}
+
+//---------------------------------------------------------------------------
+
+void RayLitFxGadget::leftButtonUp(const TPointD &pos, const TMouseEvent &) {
+ m_handle = None;
+}
+
//*************************************************************************************
// FxGadgetController implementation
//*************************************************************************************
@@ -1713,6 +1883,12 @@ FxGadget *FxGadgetController::allocateGadget(const TParamUIConcept &uiConcept) {
uiConcept.m_params[1]);
break;
}
+
+ case TParamUIConcept::RAYLIT: {
+ assert(uiConcept.m_params.size() == 1);
+ gadget = new RayLitFxGadget(this, uiConcept.m_params[0]);
+ break;
+ }
default:
break;
}