diff --git a/stuff/config/current.txt b/stuff/config/current.txt
index a630fd0..c0f90d1 100644
--- a/stuff/config/current.txt
+++ b/stuff/config/current.txt
@@ -1430,7 +1430,26 @@
- "STD_iwa_LinearGradientFx.wave_amplitude" "Amplitude"
- "STD_iwa_LinearGradientFx.wave_frequency" "Frequency"
- "STD_iwa_LinearGradientFx.wave_phase" "Phase"
-
+
+ - "STD_iwa_FloorBumpFx" "Floor Bump Iwa"
+ - "STD_iwa_FloorBumpFx.renderMode" "Render Mode"
+ - "STD_iwa_FloorBumpFx.fov" "Fov"
+ - "STD_iwa_FloorBumpFx.cameraAltitude" "Camera Altitude"
+ - "STD_iwa_FloorBumpFx.eyeLevel" "Eye Level"
+ - "STD_iwa_FloorBumpFx.drawLevel" "Draw Level"
+ - "STD_iwa_FloorBumpFx.waveHeight" "Wave Height"
+ - "STD_iwa_FloorBumpFx.textureOffsetAmount" "Amount"
+ - "STD_iwa_FloorBumpFx.textureOffsetSpread" "Spread"
+ - "STD_iwa_FloorBumpFx.sourcePrecision" "Precision"
+ - "STD_iwa_FloorBumpFx.souceMargin" "Margin"
+ - "STD_iwa_FloorBumpFx.lightAzimuth" "Azimuth"
+ - "STD_iwa_FloorBumpFx.lightElevation" "Elevation"
+ - "STD_iwa_FloorBumpFx.depth" "Depth"
+ - "STD_iwa_FloorBumpFx.refractiveIndex" "Refractive Index"
+ - "STD_iwa_FloorBumpFx.distanceLevel" "Distance Level"
+ - "STD_iwa_FloorBumpFx.differenceMode" "Render difference from unbumped state"
+ - "STD_iwa_FloorBumpFx.displacement" "Displacement"
+
- "STD_iwa_GlareFx" "Glare Iwa"
- "STD_iwa_GlareFx.renderMode" "Render Mode"
- "STD_iwa_GlareFx.irisMode" "Iris Shape"
diff --git a/stuff/profiles/layouts/fxs/STD_iwa_FloorBumpFx.xml b/stuff/profiles/layouts/fxs/STD_iwa_FloorBumpFx.xml
new file mode 100644
index 0000000..23fddf1
--- /dev/null
+++ b/stuff/profiles/layouts/fxs/STD_iwa_FloorBumpFx.xml
@@ -0,0 +1,39 @@
+
+
+ renderMode
+ fov
+ cameraAltitude
+ eyeLevel
+ drawLevel
+ waveHeight
+ souceMargin
+ displacement
+
+
+ sourcePrecision
+
+
+
+ textureOffsetAmount
+ textureOffsetSpread
+
+
+
+ lightAzimuth
+ lightElevation
+
+
+
+ depth
+ refractiveIndex
+
+
+
+ distanceLevel
+
+
+
+ differenceMode
+
+
+
diff --git a/stuff/profiles/layouts/fxs/fxs.lst b/stuff/profiles/layouts/fxs/fxs.lst
index ff93317..0af39ce 100644
--- a/stuff/profiles/layouts/fxs/fxs.lst
+++ b/stuff/profiles/layouts/fxs/fxs.lst
@@ -35,6 +35,7 @@
STD_warpFx
STD_inoWarphvFx
STD_iwa_BarrelDistortFx
+ STD_iwa_FloorBumpFx
STD_diamondGradientFx
diff --git a/toonz/sources/include/toonzqt/paramfield.h b/toonz/sources/include/toonzqt/paramfield.h
index 5a0b4c3..5c2a20a 100644
--- a/toonz/sources/include/toonzqt/paramfield.h
+++ b/toonz/sources/include/toonzqt/paramfield.h
@@ -546,6 +546,8 @@ public:
QSize getPreferredSize() override { return QSize(150, 20); }
+ int getValue() const;
+
protected slots:
void onChange(const QString &str);
};
diff --git a/toonz/sources/include/tparamuiconcept.h b/toonz/sources/include/tparamuiconcept.h
index 167008e..51595c8 100644
--- a/toonz/sources/include/tparamuiconcept.h
+++ b/toonz/sources/include/tparamuiconcept.h
@@ -72,6 +72,8 @@ public:
ELLIPSE, // used in spin blur ino and radial blur ino
+ VERTICAL_POS, // A horizontal line at given height
+ // { [TDoubleParamP] }
TYPESCOUNT
};
diff --git a/toonz/sources/stdfx/CMakeLists.txt b/toonz/sources/stdfx/CMakeLists.txt
index b29fd9a..ed196a7 100644
--- a/toonz/sources/stdfx/CMakeLists.txt
+++ b/toonz/sources/stdfx/CMakeLists.txt
@@ -86,6 +86,7 @@ set(HEADERS
iwa_bokeh_advancedfx.h
iwa_bokeh_util.h
globalcontrollablefx.h
+ iwa_floorbumpfx.h
)
if(OpenCV_FOUND)
@@ -280,6 +281,7 @@ set(SOURCES
iwa_rainbowfx.cpp
iwa_bokeh_advancedfx.cpp
iwa_bokeh_util.cpp
+ iwa_floorbumpfx.cpp
)
if(OpenCV_FOUND)
diff --git a/toonz/sources/stdfx/iwa_floorbumpfx.cpp b/toonz/sources/stdfx/iwa_floorbumpfx.cpp
new file mode 100644
index 0000000..903f0bd
--- /dev/null
+++ b/toonz/sources/stdfx/iwa_floorbumpfx.cpp
@@ -0,0 +1,1475 @@
+#include "iwa_floorbumpfx.h"
+
+#include "tparamuiconcept.h"
+#include "iwa_fresnel.h"
+
+#include
+
+namespace {
+
+inline double cross(const QPointF a, const QPointF b) {
+ return a.x() * b.y() - a.y() * b.x();
+};
+
+inline float lerp(const float v1, const float v2, const float r) {
+ return v1 * (1.0f - r) + v2 * r;
+};
+inline QPointF lerpUV(const QPointF p1, const QPointF p2, const float r) {
+ return p1 * (1.0 - r) + p2 * r;
+};
+float4 lerp4(const float4 v1, const float4 v2, const float r) {
+ return float4{v1.x * (1.0f - r) + v2.x * r, v1.y * (1.0f - r) + v2.y * r,
+ v1.z * (1.0f - r) + v2.z * r, v1.w * (1.0f - r) + v2.w * r};
+};
+
+inline QVector3D lerpPos(const QVector3D v1, const QVector3D v2,
+ const float r) {
+ return (v1 * (1.0 - r) + v2 * r);
+};
+
+inline QVector3D lerpNormal(const QVector3D v1, const QVector3D v2,
+ const float r) {
+ return lerpPos(v1, v2, r).normalized();
+};
+
+inline float fresnelFactor(const float radian) {
+ float degree = radian / M_PI_180;
+ if (degree < 0.0f)
+ return fresnel[0];
+ else if (degree >= 90.0f)
+ return fresnel[90];
+ int id = int(std::floor(degree));
+ float ratio = degree - float(id);
+ return lerp(fresnel[id], fresnel[id + 1], ratio);
+};
+
+inline bool isTextureUsed(int renderMode) {
+ return renderMode == Iwa_FloorBumpFx::TextureMode // Texture
+ || renderMode == Iwa_FloorBumpFx::RefractionMode // Refraction
+ || renderMode == Iwa_FloorBumpFx::ReflectionMode; // Reflection
+}
+
+// pixel height projected on the projection plane
+inline double getVPos(const QPointF &zyPos,
+ const Iwa_FloorBumpFx::FloorBumpVars &vars) {
+ QPointF a1 = QPointF(0, vars.P_y) - zyPos;
+ QPointF a2 = vars.A - vars.B;
+ QPointF b1 = vars.A - zyPos;
+ QPointF b2 = vars.B - zyPos;
+ double s1 = cross(b2, a1) / 2.0;
+ double s2 = cross(a1, b1) / 2.0;
+ double ratio = s1 / (s1 + s2);
+ return vars.H * ratio;
+};
+
+inline float4 getSourcePix(QPointF p, float4 *source_host,
+ const Iwa_FloorBumpFx::FloorBumpVars &vars) {
+ auto source = [&](QPoint p) {
+ return source_host[p.y() * vars.sourceDim.lx + p.x()];
+ };
+
+ QPointF uv = (p + QPointF(vars.margin, vars.margin)) * vars.precision;
+ int inc_x = 1, inc_y = 1;
+ if (uv.x() < 0.0)
+ uv.setX(0.0);
+ else if (uv.x() >= vars.sourceDim.lx - 1) {
+ uv.setX(vars.sourceDim.lx - 1);
+ inc_x = 0;
+ }
+ if (uv.y() < 0.0)
+ uv.setY(0.0);
+ else if (uv.y() >= vars.sourceDim.ly - 1) {
+ uv.setY(vars.sourceDim.ly - 1);
+ inc_y = 0;
+ }
+ QPoint p00(int(std::floor(uv.x())), int(std::floor(uv.y())));
+ QPointF frac = uv - QPointF(p00);
+ if (frac.isNull()) return source(p00);
+ QPoint p01 = p00 + QPoint(inc_x, 0);
+ QPoint p10 = p00 + QPoint(0, inc_y);
+ QPoint p11 = p00 + QPoint(inc_x, inc_y);
+
+ return lerp4(lerp4(source(p00), source(p01), frac.x()),
+ lerp4(source(p10), source(p11), frac.x()), frac.y());
+};
+
+inline float getMapValue(QPointF p, float *map_host,
+ const Iwa_FloorBumpFx::FloorBumpVars &vars) {
+ auto map = [&](QPoint p) { return map_host[p.y() * vars.refDim.lx + p.x()]; };
+ QPointF uv = p + QPointF(vars.margin, vars.margin);
+ int inc_x = 1, inc_y = 1;
+ if (uv.x() < 0.0)
+ uv.setX(0.0);
+ else if (uv.x() >= vars.refDim.lx - 1) {
+ uv.setX(vars.refDim.lx - 1);
+ inc_x = 0;
+ }
+ if (uv.y() < 0.0)
+ uv.setY(0.0);
+ else if (uv.y() >= vars.refDim.ly - 1) {
+ uv.setY(vars.refDim.ly - 1);
+ inc_y = 0;
+ }
+ QPoint p00(int(std::floor(uv.x())), int(std::floor(uv.y())));
+ QPointF frac = uv - QPointF(p00);
+ if (frac.isNull()) return map(p00);
+ QPoint p01 = p00 + QPoint(inc_x, 0);
+ QPoint p10 = p00 + QPoint(0, inc_y);
+ QPoint p11 = p00 + QPoint(inc_x, inc_y);
+
+ return lerp(lerp(map(p00), map(p01), frac.x()),
+ lerp(map(p10), map(p11), frac.x()), frac.y());
+};
+
+// compute horizontal position of the projected point on the projection
+// plane
+inline double getXPos(QVector3D pos,
+ const Iwa_FloorBumpFx::FloorBumpVars &vars) {
+ double angle_Q = vars.angle_el - atan((vars.P_y - pos.y()) / pos.z());
+ double dist =
+ vars.d_PT * cos(vars.angle_el - angle_Q) / (cos(angle_Q) * pos.z());
+ return pos.x() * dist + double(vars.resultDim.lx) * 0.5;
+};
+
+inline float4 getTextureOffsetColor(double preTexOff, double texOff,
+ float ratio) {
+ float val = lerp(preTexOff, texOff, ratio);
+ return float4{val, val, val, 1.0};
+};
+inline float4 getDiffuseColor(QVector3D n,
+ const Iwa_FloorBumpFx::FloorBumpVars &vars) {
+ float val = QVector3D::dotProduct(n, vars.sunVec);
+ if (val < 0.0) val = 0.0;
+ return float4{val, val, val, 1.0};
+};
+inline float4 getDiffuseDifferenceColor(
+ QVector3D n, QVector3D base_n, const Iwa_FloorBumpFx::FloorBumpVars &vars) {
+ float val = QVector3D::dotProduct(n, vars.sunVec);
+ float base_val = QVector3D::dotProduct(base_n, vars.sunVec);
+ val = (val - base_val) * 0.5 + 0.5;
+ if (val < 0.0)
+ val = 0.0;
+ else if (val > 1.0)
+ val = 1.0;
+ return float4{val, val, val, 1.0};
+};
+inline float4 getSpecularColor(QVector3D n, QVector3D pos,
+ const Iwa_FloorBumpFx::FloorBumpVars &vars) {
+ QVector3D halfVector =
+ ((vars.eyePos - pos).normalized() + vars.sunVec).normalized();
+ float val = QVector3D::dotProduct(n, halfVector);
+ if (val < 0.0) val = 0.0;
+ val = std::pow(val, 50.0);
+ return float4{val, val, val, 1.0};
+};
+inline float4 getSpecularDifferenceColor(
+ QVector3D n, QVector3D base_n, QVector3D pos, QVector3D base_pos,
+ const Iwa_FloorBumpFx::FloorBumpVars &vars) {
+ QVector3D halfVector =
+ ((vars.eyePos - pos).normalized() + vars.sunVec).normalized();
+ float ang = std::acos(QVector3D::dotProduct(n, halfVector));
+ halfVector =
+ ((vars.eyePos - base_pos).normalized() + vars.sunVec).normalized();
+ float base_ang = std::acos(QVector3D::dotProduct(base_n, halfVector));
+ float val = std::abs(ang - base_ang) / M_PI_2;
+ if (val > 1.0) val = 1.0;
+ return float4{val, val, val, 1.0};
+};
+inline float4 getFresnelColor(QVector3D n, QVector3D pos,
+ const Iwa_FloorBumpFx::FloorBumpVars &vars) {
+ float angle =
+ acos(QVector3D::dotProduct(n, (vars.eyePos - pos).normalized()));
+ float val = (fresnelFactor(angle) - vars.base_fresnel_ref) /
+ (1.0f - vars.base_fresnel_ref);
+ if (val < 0.0) val = 0.0;
+ return float4{val, val, val, 1.0};
+};
+inline float4 getFresnelDifferenceColor(
+ QVector3D n, QVector3D base_n, QVector3D pos,
+ const Iwa_FloorBumpFx::FloorBumpVars &vars) {
+ float val =
+ getFresnelColor(n, pos, vars).x - getFresnelColor(base_n, pos, vars).x;
+ if (val < 0.0) val = 0.0;
+ return float4{val, val, val, 1.0};
+};
+// currently the texture is assumed as an image of the bottom WITHOUT WATER.
+// would it be nice to have a mode to handle the texture as
+// an image of the bottom refracted on the surface WITHOUT WAVES?
+inline float4 getRefractionColor(QVector3D n, QVector3D pos,
+ float4 *source_host,
+ const Iwa_FloorBumpFx::FloorBumpVars &vars) {
+ QVector3D eyeVec = (vars.eyePos - pos).normalized();
+ // incident angle
+ float n_eye = QVector3D::dotProduct(n, eyeVec);
+ double angle_inci = acos(n_eye);
+ // refraction angle
+ double angle_refr = asin(sin(angle_inci) / vars.r_index);
+ QVector3D refrRay =
+ sin(angle_refr) * (n_eye * n - eyeVec).normalized() - cos(angle_refr) * n;
+ double travelLength = -(vars.depth + pos.y()) / refrRay.y();
+ QVector3D bottomPos = pos + refrRay * travelLength;
+ return getSourcePix(
+ QPointF(getXPos(bottomPos, vars),
+ getVPos(QPointF(bottomPos.z(), bottomPos.y()), vars)),
+ source_host, vars);
+};
+
+inline float4 getReflectionColor(QVector3D n, QVector3D pos,
+ float4 *source_host,
+ const Iwa_FloorBumpFx::FloorBumpVars &vars) {
+ // draw nothing behind the reflected object
+ if (vars.distance > 0.0 && pos.z() >= vars.distance)
+ return float4{0.0, 0.0, 0.0, 0.0};
+ QVector3D eyeVec = (vars.eyePos - pos).normalized();
+ QVector3D refVec = -eyeVec + 2.0 * QVector3D::dotProduct(eyeVec, n) * n;
+ if (refVec.z() <= 0.0) return float4{0.0, 0.0, 0.0, 0.0};
+ // if the object is at infinite length, compute the reflected UV by
+ // using only the angles of reflection vector
+ if (vars.distance < 0.0) {
+ double angle_ref_azim = asin(refVec.x());
+ double angle_ref_elev = asin(refVec.y());
+ double ref_v =
+ vars.d_PT * tan(vars.angle_el - angle_ref_elev) + vars.H / 2.0;
+ double ref_u =
+ vars.d_PT * tan(angle_ref_azim) / cos(vars.angle_el - angle_ref_elev) +
+ vars.W / 2.0;
+ return getSourcePix(QPointF(ref_u, ref_v), source_host, vars);
+ }
+ // compute the reflected UV
+ double length = (vars.distance - pos.z()) / refVec.z();
+ QVector3D boardPos = pos + length * refVec;
+ // invert Y coordinate since the texture image is already vertically
+ // "reflected"
+ boardPos.setY(-boardPos.y());
+ return getSourcePix(
+ QPointF(getXPos(boardPos, vars),
+ getVPos(QPointF(boardPos.z(), boardPos.y()), vars)),
+ source_host, vars);
+};
+
+inline float4 getColor(QVector3D pre_n, QVector3D cur_n, QVector3D pre_p,
+ QVector3D cur_p, float ratio, float4 *source_host,
+ const Iwa_FloorBumpFx::FloorBumpVars &vars,
+ QVector3D pre_base_n = QVector3D(0, 1, 0),
+ QVector3D cur_base_n = QVector3D(0, 1, 0),
+ QVector3D pre_base_pos = QVector3D(),
+ QVector3D cur_base_pos = QVector3D()) {
+ if (vars.renderMode == Iwa_FloorBumpFx::DiffuseMode) {
+ if (vars.differenceMode)
+ return getDiffuseDifferenceColor(
+ lerpNormal(pre_n, cur_n, ratio),
+ lerpNormal(pre_base_n, cur_base_n, ratio), vars);
+ else
+ return getDiffuseColor(lerpNormal(pre_n, cur_n, ratio), vars);
+ } else if (vars.renderMode == Iwa_FloorBumpFx::SpecularMode) {
+ if (vars.differenceMode)
+ return getSpecularDifferenceColor(
+ lerpNormal(pre_n, cur_n, ratio),
+ lerpNormal(pre_base_n, cur_base_n, ratio),
+ lerpPos(pre_p, cur_p, ratio),
+ lerpPos(pre_base_pos, cur_base_pos, ratio), vars);
+ else
+ return getSpecularColor(lerpNormal(pre_n, cur_n, ratio),
+ lerpPos(pre_p, cur_p, ratio), vars);
+ } else if (vars.renderMode == Iwa_FloorBumpFx::FresnelMode) {
+ if (vars.differenceMode)
+ return getFresnelDifferenceColor(
+ lerpNormal(pre_n, cur_n, ratio),
+ lerpNormal(pre_base_n, cur_base_n, ratio),
+ lerpPos(pre_p, cur_p, ratio), vars);
+ else
+ return getFresnelColor(lerpNormal(pre_n, cur_n, ratio),
+ lerpPos(pre_p, cur_p, ratio), vars);
+ } else if (vars.renderMode == Iwa_FloorBumpFx::RefractionMode)
+ return getRefractionColor(lerpNormal(pre_n, cur_n, ratio),
+ lerpPos(pre_p, cur_p, ratio), source_host, vars);
+ else if (vars.renderMode == Iwa_FloorBumpFx::ReflectionMode)
+ return getReflectionColor(lerpNormal(pre_n, cur_n, ratio),
+ lerpPos(pre_p, cur_p, ratio), source_host, vars);
+};
+
+QList getSubPointsList(int subAmount,
+ const Iwa_FloorBumpFx::FloorBumpVars &vars) {
+ QList ret;
+ if (!areAlmostEqual(vars.textureOffsetAmount, 0.0)) {
+ for (int su = -subAmount; su <= subAmount; su++) {
+ double sub_u = float(su) / float(subAmount);
+ for (int sv = -subAmount; sv <= subAmount; sv++) {
+ double sub_v = float(sv) / float(subAmount);
+ // 円の外ならcontinue
+ if (sub_u * sub_u + sub_v * sub_v > 1.0) continue;
+ if (su == 0 && sv == 0) continue;
+ ret.append(QPointF(vars.spread * sub_u, vars.spread * sub_v));
+ }
+ }
+ }
+ return ret;
+}
+
+} // namespace
+
+//------------------------------------
+
+Iwa_FloorBumpFx::Iwa_FloorBumpFx()
+ : m_renderMode(new TIntEnumParam(TextureMode, "Texture"))
+ , m_eyeLevel(0.0)
+ , m_drawLevel(-50.0)
+ , m_fov(30)
+ , m_cameraAltitude(0.0)
+ , m_waveHeight(10.0)
+ , m_differenceMode(false)
+ , m_textureOffsetAmount(0.0)
+ , m_textureOffsetSpread(10.0)
+ , m_sourcePrecision(300.0 / 162.5)
+ , m_souceMargin(0.0)
+ , m_displacement(0.0)
+ , m_lightAzimuth(-135.0)
+ , m_lightElevation(30.0) // default angle_elev will shade horizontal plane
+ // 50% gray (cos(60)=0.5)
+ , m_depth(30.0)
+ , m_refractiveIndex(1.33333)
+ , m_distanceLevel(-100.0) {
+ addInputPort("Height", m_heightRef);
+ addInputPort("Texture", m_texture);
+ addInputPort("Displacement", m_dispRef);
+ bindParam(this, "renderMode", m_renderMode);
+ bindParam(this, "fov", m_fov);
+ bindParam(this, "cameraAltitude", m_cameraAltitude);
+ bindParam(this, "eyeLevel", m_eyeLevel);
+ bindParam(this, "drawLevel", m_drawLevel);
+ bindParam(this, "waveHeight", m_waveHeight);
+ bindParam(this, "differenceMode", m_differenceMode);
+ bindParam(this, "textureOffsetAmount", m_textureOffsetAmount);
+ bindParam(this, "textureOffsetSpread", m_textureOffsetSpread);
+ bindParam(this, "sourcePrecision", m_sourcePrecision);
+ bindParam(this, "souceMargin", m_souceMargin);
+ bindParam(this, "displacement", m_displacement);
+ bindParam(this, "lightAzimuth", m_lightAzimuth);
+ bindParam(this, "lightElevation", m_lightElevation);
+ bindParam(this, "depth", m_depth);
+ bindParam(this, "refractiveIndex", m_refractiveIndex);
+ bindParam(this, "distanceLevel", m_distanceLevel);
+
+ m_renderMode->addItem(DiffuseMode, "Diffuse");
+ m_renderMode->addItem(SpecularMode, "Specular");
+ m_renderMode->addItem(FresnelMode, "Fresnel reflectivity");
+ m_renderMode->addItem(RefractionMode, "Refraction");
+ m_renderMode->addItem(ReflectionMode, "Reflection");
+
+ m_fov->setValueRange(10, 90);
+ m_cameraAltitude->setMeasureName("fxLength");
+ m_cameraAltitude->setValueRange(0.0, 300.0);
+ m_eyeLevel->setMeasureName("fxLength");
+ m_drawLevel->setMeasureName("fxLength");
+
+ m_waveHeight->setMeasureName("fxLength");
+ m_waveHeight->setValueRange(-1000.0, 1000.0);
+ m_textureOffsetAmount->setMeasureName("fxLength");
+ m_textureOffsetAmount->setValueRange(-2000.0, 2000.0);
+ m_textureOffsetSpread->setMeasureName("fxLength");
+ m_textureOffsetSpread->setValueRange(1.0, 300.0);
+
+ m_sourcePrecision->setValueRange(1.0, 2.0);
+ m_souceMargin->setMeasureName("fxLength");
+ m_souceMargin->setValueRange(0.0, 100.0);
+ m_displacement->setMeasureName("fxLength");
+ m_displacement->setValueRange(-50.0, 50.0);
+
+ m_lightAzimuth->setValueRange(-360.0, 360.0);
+ m_lightElevation->setValueRange(0.0, 90.0);
+
+ m_depth->setMeasureName("fxLength");
+ m_depth->setValueRange(0.0, 1000.0);
+ m_refractiveIndex->setValueRange(1.0, 3.0);
+
+ m_distanceLevel->setMeasureName("fxLength");
+}
+
+//------------------------------------
+
+bool Iwa_FloorBumpFx::doGetBBox(double frame, TRectD &bBox,
+ const TRenderSettings &info) {
+ if (m_heightRef.isConnected()) {
+ bool ret = m_heightRef->doGetBBox(frame, bBox, info);
+ if (ret) bBox = TConsts::infiniteRectD;
+ return ret;
+ }
+ return false;
+}
+
+//------------------------------------
+
+bool Iwa_FloorBumpFx::canHandle(const TRenderSettings &info, double frame) {
+ return false;
+}
+
+//------------------------------------------------------------
+// convert output values (in float4) to channel value
+//------------------------------------------------------------
+template
+void Iwa_FloorBumpFx::setOutputRaster(float4 *srcMem, const RASTER dstRas,
+ TDimensionI dim, int drawLevel) {
+ typename PIXEL::Channel halfChan =
+ (typename PIXEL::Channel)(PIXEL::maxChannelValue / 2);
+
+ dstRas->fill(PIXEL::Transparent);
+
+ for (int j = 0; j < drawLevel; j++) {
+ if (j >= dstRas->getLy()) break;
+
+ PIXEL *pix = dstRas->pixels(j);
+ float4 *chan_p = &srcMem[j];
+ for (int i = 0; i < dstRas->getLx(); i++, chan_p += drawLevel, pix++) {
+ float val;
+ val = (*chan_p).x * (float)PIXEL::maxChannelValue + 0.5f;
+ pix->r = (typename PIXEL::Channel)((val > (float)PIXEL::maxChannelValue)
+ ? (float)PIXEL::maxChannelValue
+ : val);
+ val = (*chan_p).y * (float)PIXEL::maxChannelValue + 0.5f;
+ pix->g = (typename PIXEL::Channel)((val > (float)PIXEL::maxChannelValue)
+ ? (float)PIXEL::maxChannelValue
+ : val);
+ val = (*chan_p).z * (float)PIXEL::maxChannelValue + 0.5f;
+ pix->b = (typename PIXEL::Channel)((val > (float)PIXEL::maxChannelValue)
+ ? (float)PIXEL::maxChannelValue
+ : val);
+ val = (*chan_p).w * (float)PIXEL::maxChannelValue + 0.5f;
+ pix->m = (typename PIXEL::Channel)((val > (float)PIXEL::maxChannelValue)
+ ? (float)PIXEL::maxChannelValue
+ : val);
+ }
+ }
+}
+
+//------------------------------------
+
+inline void Iwa_FloorBumpFx::initVars(FloorBumpVars &vars, TTile &tile,
+ const TRenderSettings &settings,
+ double frame) {
+ TAffine aff = settings.m_affine;
+ double factor = sqrt(std::abs(settings.m_affine.det()));
+ double eyeLevel = m_eyeLevel->getValue(frame) * factor;
+ double refLevel = m_drawLevel->getValue(frame) * factor;
+ vars.waveHeight = m_waveHeight->getValue(frame) * factor;
+ vars.displacement = m_displacement->getValue(frame) * factor;
+ if (eyeLevel - 1.0 < refLevel) refLevel = eyeLevel - 1.0;
+ int drawHeight = std::max(refLevel, vars.waveHeight) - tile.m_pos.y;
+ vars.refHeight = int(refLevel - tile.m_pos.y);
+ // convert the pixel coordinate from the bottom-left of the camera box
+ eyeLevel = eyeLevel - (tile.m_pos.y + tile.getRaster()->getCenterD().y) +
+ settings.m_cameraBox.getLy() / 2.0;
+ refLevel = refLevel - (tile.m_pos.y + tile.getRaster()->getCenterD().y) +
+ settings.m_cameraBox.getLy() / 2.0;
+
+ TRectD rectOut(tile.m_pos, TDimensionD(tile.getRaster()->getLx(),
+ tile.getRaster()->getLy()));
+ vars.outDim = TDimensionI(rectOut.getLx(), rectOut.getLy());
+ vars.resultDim = TDimensionI(rectOut.getLx(),
+ std::min(drawHeight, tile.getRaster()->getLy()));
+
+ // get texture image
+ vars.margin = tceil(m_souceMargin->getValue(frame) * factor);
+ vars.precision = m_sourcePrecision->getValue(frame);
+
+ // add margins to all ends and multiply by precision value
+ vars.sourceDim = TDimensionI(
+ tceil(vars.precision * double(vars.resultDim.lx + vars.margin * 2)),
+ tceil(vars.precision * double(vars.resultDim.ly + vars.margin * 2)));
+ // only add margins for height image
+ vars.refDim = TDimensionI(vars.resultDim.lx + vars.margin * 2,
+ vars.refHeight + vars.margin * 2);
+
+ // collecting parameters
+ double angle_halfFov = m_fov->getValue(frame) * M_PI_180 / 2.0;
+ vars.textureOffsetAmount =
+ 100.0 * m_textureOffsetAmount->getValue(frame) * factor * factor;
+ vars.spread = m_textureOffsetSpread->getValue(frame) * factor;
+ if (vars.spread < 1.0) vars.spread = 1.0;
+ vars.camAltitude = m_cameraAltitude->getValue(frame) * factor;
+ vars.differenceMode = m_differenceMode->getValue();
+
+ // making pixels in gray128 to be zero level height
+ // ( 128/255. IT'S NOT 0.5! )
+ vars.zeroLevel = double(128) / double(TPixel32::maxChannelValue);
+
+ vars.H = settings.m_cameraBox.getLy();
+ vars.W = settings.m_cameraBox.getLx();
+
+ double el = eyeLevel - vars.H / 2.0;
+ // angle between the optical axis and the horizontal axis
+ vars.angle_el =
+ std::atan(2.0 * el * tan(angle_halfFov) / double(vars.outDim.ly));
+ // distance between the Eye (P) and
+ // the center of bottom edge of the projection plane (B)
+ double d_PB = double(vars.outDim.ly) / (2.0 * sin(angle_halfFov));
+ // Y coordinate of the Eye position (P)
+ vars.P_y = vars.camAltitude + d_PB * sin((angle_halfFov) + vars.angle_el);
+ // distance from the Eye (P) to the center of the projection plane (T)
+ vars.d_PT = vars.H / (2.0 * tan(angle_halfFov));
+
+ QMatrix cam_tilt;
+ cam_tilt.rotate(-vars.angle_el / M_PI_180);
+ // Z-Y position of the center of top edge of the projection plane (A)
+ vars.A =
+ cam_tilt.map(QPointF(vars.d_PT, vars.H / 2.0)) + QPointF(0, vars.P_y);
+ // Z-Y position of the center of bottom edge of the projection plane (B)
+ vars.B =
+ cam_tilt.map(QPointF(vars.d_PT, -vars.H / 2.0)) + QPointF(0, vars.P_y);
+
+ vars.C_z = vars.P_y / tan(angle_halfFov + vars.angle_el);
+ vars.eyePos = QVector3D(0.0, vars.P_y, 0.0);
+
+ switch (vars.renderMode) {
+ // for shading modes ( diffuse & specular )
+ case DiffuseMode:
+ case SpecularMode: {
+ double angle_azim = m_lightAzimuth->getValue(frame) * M_PI_180;
+ double angle_elev = m_lightElevation->getValue(frame) * M_PI_180;
+ vars.sunVec = QVector3D(sin(angle_azim) * cos(angle_elev), sin(angle_elev),
+ cos(angle_azim) * cos(angle_elev));
+ break;
+ }
+ case FresnelMode: { // for fresnel mode
+ double angle_base = 0.5 * M_PI - (angle_halfFov + vars.angle_el);
+ vars.base_fresnel_ref = fresnelFactor(angle_base);
+ break;
+ }
+ case RefractionMode: {
+ // for refraction mode
+ vars.depth = m_depth->getValue(frame) * factor;
+ vars.r_index = m_refractiveIndex->getValue(frame);
+ break;
+ }
+ case ReflectionMode: {
+ // for reflection mode
+ double distanceLevel = m_distanceLevel->getValue(frame) * factor;
+ double angle_dl = atan(distanceLevel / vars.d_PT);
+ // Z-axis distance to the reflected object.
+ // distance = -1 means the object is at infinity
+ vars.distance = (angle_dl >= vars.angle_el)
+ ? -1
+ : vars.P_y / tan(vars.angle_el - angle_dl);
+ break;
+ }
+ }
+}
+//------------------------------------
+
+void Iwa_FloorBumpFx::doCompute_CPU(TTile &tile, const double frame,
+ const TRenderSettings &settings,
+ const FloorBumpVars &vars,
+ float4 *source_host, float *ref_host,
+ float4 *result_host) {
+ //-----------
+ // Texture Mode
+ if (vars.renderMode == TextureMode) {
+ // テクスチャの勾配を調べるサンプリング点
+ int subAmount = 10; // これ、パラメータ化するか…?
+ QList subPoints = getSubPointsList(subAmount, vars);
+
+ // render pixels in column-major order, from bottom to top
+ for (int i = 0; i < vars.resultDim.lx; i++) {
+ // cancel check
+ if (settings.m_isCanceled && *settings.m_isCanceled) return;
+
+ double maxVPos = 0.0; // maximum height of drawn pixels
+ double preVPos = 0.0; // height of the previous segment
+ QPointF preUV = QPointF(i, -vars.margin);
+ float4 *result_p = &result_host[i * vars.resultDim.ly];
+ float4 *end_p = &result_host[(i + 1) * vars.resultDim.ly - 1];
+ float preTextureOffset = 0.0;
+
+ bool backled = false;
+ double preGrad = 100.0;
+
+ // get height pixels from bottom to top
+ for (int j = -vars.margin; j < vars.refHeight; j++) {
+ // angle between the light axis and the line between (Q) and the eye (P)
+ // (Q) is a point on the projection plane at distance of j from (B)
+ double angle_Q =
+ std::atan((float(j) - (vars.outDim.ly / 2.0)) / vars.d_PT);
+ // (R) is an intersection between the XZ plane and the line P->Q
+ double R_z = vars.P_y / tan(vars.angle_el - angle_Q);
+
+ // compute (S), a point on the bumped surface in Y-axis direction from
+ // (R)
+ float refVal =
+ getMapValue(QPointF(double(i), double(j)), ref_host, vars);
+ QPointF S(R_z, vars.waveHeight * (refVal - vars.zeroLevel) * 2.0);
+ // height of (S) projected on the projection plane
+ double vPos = getVPos(S, vars);
+
+ // UV coordinate of the texture image
+ QPointF currentUV;
+ float currentTextureOffset = 0.0;
+ if (areAlmostEqual(vars.textureOffsetAmount, 0.0))
+ currentUV = QPointF(double(i), getVPos(QPointF(R_z, 0), vars));
+ else {
+ // length of (1,0,0) vector on the XZ plane, projected on the
+ // projection plane
+ double dist = (vars.d_PT * sin(vars.angle_el - angle_Q)) / vars.P_y *
+ cos(angle_Q);
+ // approximate the gradient by difference of the heights of neighbor
+ // points
+
+ // compute gradient of the height image
+ QPointF offset;
+ for (const QPointF &subPoint : subPoints) {
+ // この地点のRefの値を取る
+ QPointF subRefPos(double(i) + dist * subPoint.x(),
+ getVPos(QPointF(R_z + subPoint.y(), 0), vars));
+
+ int sign_u = (subPoint.x() > 0) - (subPoint.x() < 0); // -1, 0, 1
+ int sign_v = (subPoint.y() > 0) - (subPoint.y() < 0); // -1, 0, 1
+
+ float subRefValue = getMapValue(subRefPos, ref_host, vars);
+ offset += subRefValue * QPointF(sign_u, sign_v);
+ }
+
+ offset *= 1.0 / float(subPoints.count() - subAmount); // サンプル数
+ offset *= vars.textureOffsetAmount /
+ (vars.spread * (0.5 + 0.5 / float(subAmount))); // 距離
+ currentUV = QPointF(double(i) + offset.x() * dist,
+ getVPos(QPointF(R_z + offset.y(), 0), vars));
+ if (vars.differenceMode)
+ currentTextureOffset =
+ std::sqrt(offset.x() * offset.x() + offset.y() * offset.y()) /
+ vars.textureOffsetAmount;
+ }
+
+ // continue if the current point is behind the forward bumps
+ if (vPos <= maxVPos) {
+ preVPos = vPos;
+ preUV = currentUV;
+ preTextureOffset = currentTextureOffset;
+ continue;
+ }
+
+ // putting colors on pixels
+ int currentY = int(std::floor(maxVPos));
+ float current_frac = maxVPos - float(currentY);
+ int toY = int(std::floor(vPos));
+ float to_frac = vPos - float(toY);
+ // if the entire current segment is in the same pixel,
+ // then add colors with percentage of the fraction part
+ if (currentY == toY) {
+ float ratio = to_frac - current_frac;
+ if (vars.differenceMode)
+ *result_p += getTextureOffsetColor(preTextureOffset,
+ currentTextureOffset, 0.5) *
+ ratio;
+ else
+ *result_p +=
+ getSourcePix(lerpUV(preUV, currentUV, 0.5), source_host, vars) *
+ ratio;
+ preVPos = vPos;
+ maxVPos = vPos;
+ preUV = currentUV;
+ preTextureOffset = currentTextureOffset;
+ continue;
+ }
+
+ // fill fraction part of the start pixel (currentY)
+ float k =
+ (maxVPos + (1.0 - current_frac) / 2.0 - preVPos) / (vPos - preVPos);
+ float ratio = 1.0 - current_frac;
+ if (vars.differenceMode)
+ *result_p +=
+ getTextureOffsetColor(preTextureOffset, currentTextureOffset, k) *
+ ratio;
+ else
+ *result_p +=
+ getSourcePix(lerpUV(preUV, currentUV, k), source_host, vars) *
+ ratio;
+
+ if (result_p == end_p) break;
+ result_p++;
+
+ // fill pixels between currentY and toY
+ for (int y = currentY + 1; y < toY; y++) {
+ k = (float(y) + 0.5 - preVPos) / (vPos - preVPos);
+ if (vars.differenceMode)
+ *result_p += getTextureOffsetColor(preTextureOffset,
+ currentTextureOffset, k);
+ else
+ *result_p =
+ getSourcePix(lerpUV(preUV, currentUV, k), source_host, vars);
+ if (result_p == end_p) break;
+ result_p++;
+ }
+
+ // fill fraction part of the end pixel (toY)
+ k = (float(toY) + to_frac * 0.5 - preVPos) / (vPos - preVPos);
+ if (vars.differenceMode)
+ *result_p +=
+ getTextureOffsetColor(preTextureOffset, currentTextureOffset, k) *
+ to_frac;
+ else
+ *result_p +=
+ getSourcePix(lerpUV(preUV, currentUV, k), source_host, vars) *
+ to_frac;
+
+ preVPos = vPos;
+ maxVPos = vPos;
+ preUV = currentUV;
+ preTextureOffset = currentTextureOffset;
+ }
+ }
+ }
+ // Other modes
+ // (Diffuse, Specular, Fresnel, Refraction, Reflection)
+ else {
+ // render pixels in column-major order, from bottom to top
+ for (int i = 0; i < vars.resultDim.lx; i++) {
+ // cancel check
+ if (settings.m_isCanceled && *settings.m_isCanceled) return;
+
+ double maxVPos = 0.0; // maximum height of drawn pixels
+ double preVPos = 0.0; // height of the previous segment
+ QVector3D preNormal(0, 1, 0);
+ QVector3D prePos(i, 0,
+ vars.C_z); // actually the x coordinate is not i at the
+ // initial position.., but no problem.
+ float4 *result_p = &result_host[i * vars.resultDim.ly];
+ float4 *end_p = &result_host[(i + 1) * vars.resultDim.ly - 1];
+ // get height pixels from bottom to top
+ for (int j = -vars.margin; j < vars.refHeight; j++) {
+ // angle between the light axis and the line between (Q) and the eye (P)
+ // (Q) is a point on the projection plane at distance of j from (B)
+ double angle_Q =
+ std::atan((float(j) - (vars.outDim.ly / 2.0)) / vars.d_PT);
+ // (R) is an intersection between the XZ plane and the line P->Q
+ double R_z = vars.P_y / tan(vars.angle_el - angle_Q);
+ // In reflection mode, do not draw surface behind the Distance Level
+ if (vars.renderMode == ReflectionMode && vars.distance > 0.0 &&
+ R_z >= vars.distance)
+ break;
+ // compute normal vector from cross-product of surface vectors
+ QVector3D currentNormal;
+ // length of (1,0,0) vector on the XZ plane, projected on the projection
+ // plane
+ double dist = (vars.d_PT * sin(vars.angle_el - angle_Q)) / vars.P_y *
+ cos(angle_Q);
+ double xRefGrad =
+ getMapValue(QPointF(double(i) + dist, double(j)), ref_host, vars) -
+ getMapValue(QPointF(double(i) - dist, double(j)), ref_host, vars);
+ double zRefGrad =
+ getMapValue(QPointF(double(i), getVPos(QPointF(R_z + 1, 0), vars)),
+ ref_host, vars) -
+ getMapValue(QPointF(double(i), getVPos(QPointF(R_z - 1, 0), vars)),
+ ref_host, vars);
+ currentNormal.setX(-xRefGrad * vars.waveHeight * 2.0);
+ currentNormal.setY(4.0);
+ currentNormal.setZ(-zRefGrad * vars.waveHeight * 2.0);
+ currentNormal.normalize();
+
+ // compute (S), a point on the bumped surface in Y-axis direction from
+ // (R)
+ float refVal =
+ getMapValue(QPointF(double(i), double(j)), ref_host, vars);
+ QPointF S(R_z, vars.waveHeight * (refVal - vars.zeroLevel) * 2.0);
+ // height of (S) projected on the projection plane
+ double vPos = getVPos(S, vars);
+ QVector3D currentPos(double(i - vars.resultDim.lx / 2) / dist, S.y(),
+ S.x());
+
+ // continue if the current point is behind the forward bumps
+ if (vPos <= maxVPos) {
+ preVPos = vPos;
+ preNormal = currentNormal;
+ prePos = currentPos;
+ continue;
+ }
+
+ // putting colors on pixels
+ int currentY = int(std::floor(maxVPos));
+ float current_frac = maxVPos - float(currentY);
+ int toY = int(std::floor(vPos));
+ float to_frac = vPos - float(toY);
+ // if the entire current segment is in the same pixel,
+ // then add colors with percentage of the fraction part
+ if (currentY == toY) {
+ float ratio = to_frac - current_frac;
+ *result_p += getColor(preNormal, currentNormal, prePos, currentPos,
+ 0.5, source_host, vars) *
+ ratio;
+ preVPos = vPos;
+ maxVPos = vPos;
+ preNormal = currentNormal;
+ prePos = currentPos;
+ continue;
+ }
+
+ // fill fraction part of the start pixel (currentY)
+ float k =
+ (maxVPos + (1.0 - current_frac) / 2.0 - preVPos) / (vPos - preVPos);
+ float4 color = getColor(preNormal, currentNormal, prePos, currentPos, k,
+ source_host, vars);
+ float ratio = 1.0 - current_frac;
+ *result_p += color * ratio;
+
+ if (result_p == end_p) break;
+ result_p++;
+
+ // fill pixels between currentY and toY
+ for (int y = currentY + 1; y < toY; y++) {
+ k = (float(y) + 0.5 - preVPos) / (vPos - preVPos);
+ *result_p = getColor(preNormal, currentNormal, prePos, currentPos, k,
+ source_host, vars);
+ if (result_p == end_p) break;
+ result_p++;
+ }
+
+ // fill fraction part of the end pixel (toY)
+ k = (float(toY) + to_frac * 0.5 - preVPos) / (vPos - preVPos);
+ color = getColor(preNormal, currentNormal, prePos, currentPos, k,
+ source_host, vars);
+ *result_p += color * to_frac;
+
+ preVPos = vPos;
+ maxVPos = vPos;
+ preNormal = currentNormal;
+ prePos = currentPos;
+ }
+ }
+ }
+}
+
+//------------------------------------
+
+void Iwa_FloorBumpFx::doCompute_with_Displacement(
+ TTile &tile, const double frame, const TRenderSettings &settings,
+ const FloorBumpVars &vars, float4 *source_host, float *ref_host,
+ float *disp_host, float4 *result_host) {
+ //----- Lambdas ------
+
+ // テクスチャの勾配を調べるサンプリング点
+ int subAmount = 10; // これ、パラメータ化するか…?
+ QList subPoints = getSubPointsList(subAmount, vars);
+
+ //-----------
+ // Texture Mode
+ if (vars.renderMode == TextureMode) {
+ // render pixels in column-major order, from bottom to top
+ for (int i = 0; i < vars.resultDim.lx; i++) {
+ // cancel check
+ if (settings.m_isCanceled && *settings.m_isCanceled) return;
+
+ double maxVPos = 0.0; // maximum height of drawn pixels
+ double preVPos = 0.0; // height of the previous segment
+ QPointF preUV = QPointF(i, -vars.margin);
+ float4 *result_p = &result_host[i * vars.resultDim.ly];
+ float4 *end_p = &result_host[(i + 1) * vars.resultDim.ly - 1];
+ float preTextureOffset = 0.0;
+ // get height pixels from bottom to top
+ for (int j = -vars.margin; j < vars.refHeight; j++) {
+ // angle between the light axis and the line between (Q) and the eye (P)
+ // (Q) is a point on the projection plane at distance of j from (B)
+ double angle_Q =
+ std::atan((float(j) - (vars.outDim.ly / 2.0)) / vars.d_PT);
+ // (R) is an intersection between the XZ plane and the line P->Q
+ double R_z = vars.P_y / tan(vars.angle_el - angle_Q);
+
+ // compute (S), a point on the bumped surface in Y-axis direction from
+ // (R)
+ float refVal =
+ getMapValue(QPointF(double(i), double(j)), ref_host, vars);
+ QPointF S(R_z, vars.waveHeight * (refVal - vars.zeroLevel) * 2.0);
+
+ // length of (1,0,0) vector on the XZ plane, projected on the
+ // projection plane
+ double dist = (vars.d_PT * sin(vars.angle_el - angle_Q)) / vars.P_y *
+ cos(angle_Q);
+
+ // UV coordinate of the texture image
+ QVector3D uvOffset;
+ float currentTextureOffset = 0.0;
+ if (vars.textureOffsetAmount == 0.0) {
+ // currentUV = QPointF(double(i), getVPos(QPointF(R_z, 0)));
+ } else {
+ // approximate the gradient by difference of the heights of neighbor
+ // points
+
+ // compute gradient of the height image
+ QPointF offset;
+ for (const QPointF &subPoint : subPoints) {
+ // この地点のRefの値を取る
+ QPointF subRefPos(double(i) + dist * subPoint.x(),
+ getVPos(QPointF(R_z + subPoint.y(), 0), vars));
+
+ int sign_u = (subPoint.x() > 0) - (subPoint.x() < 0); // -1, 0, 1
+ int sign_v = (subPoint.y() > 0) - (subPoint.y() < 0); // -1, 0, 1
+
+ float subRefValue = getMapValue(subRefPos, ref_host, vars);
+ offset += subRefValue * QPointF(sign_u, sign_v);
+ }
+ offset *= 1.0 / float(subPoints.count() - subAmount); // サンプル数
+ offset *= vars.textureOffsetAmount /
+ (vars.spread * (0.5 + 0.5 / float(subAmount))); // 距離
+
+ uvOffset.setX(offset.x() * dist);
+ uvOffset.setZ(offset.y());
+ // currentUV = QPointF(double(i) + offset.x() * dist,
+ // getVPos(QPointF(R_z + offset.y(), 0)));
+ if (vars.differenceMode)
+ currentTextureOffset =
+ 1000 *
+ std::sqrt(offset.x() * offset.x() + offset.y() * offset.y()) /
+ vars.textureOffsetAmount;
+ }
+ QPointF currentDispUV =
+ QPointF(double(i) + uvOffset.x(),
+ getVPos(QPointF(R_z + uvOffset.z(), 0), vars));
+
+ // この地点の変位の値を取得する
+ float dispValue = getMapValue(currentDispUV, disp_host, vars);
+ float dispHeight = dispValue * vars.displacement;
+
+ // この地点の法線を取得
+ QVector3D currentNormal;
+ double xRefGrad =
+ getMapValue(QPointF(double(i) + dist, double(j)), ref_host, vars) -
+ getMapValue(QPointF(double(i) - dist, double(j)), ref_host, vars);
+ double zRefGrad =
+ getMapValue(QPointF(double(i), getVPos(QPointF(R_z + 1, 0), vars)),
+ ref_host, vars) -
+ getMapValue(QPointF(double(i), getVPos(QPointF(R_z - 1, 0), vars)),
+ ref_host, vars);
+ currentNormal.setX(-xRefGrad * vars.waveHeight * 2.0);
+ currentNormal.setY(4.0);
+ currentNormal.setZ(-zRefGrad * vars.waveHeight * 2.0);
+ currentNormal.normalize();
+
+ // 変位後の表面座標 (M)
+ // ※法線のX成分は強引にY座標の変位に持ち込んで近似する
+ float dispXY = std::sqrt(currentNormal.x() * currentNormal.x() +
+ currentNormal.y() * currentNormal.y());
+ QPointF M = S + QPointF(currentNormal.z(), dispXY) * dispHeight;
+ // float dispXRatio = std::sqrt(currentNormal.x()*currentNormal.x() +
+ // currentNormal.y()*currentNormal.y()) / currentNormal.y(); QPointF M =
+ // S + QPointF(currentNormal.z(), currentNormal.y()* dispXRatio) *
+ // dispHeight;
+
+ // height of (M) projected on the projection plane
+ double vPos = getVPos(M, vars);
+
+ uvOffset.setY(dispHeight);
+ QPointF currentUV =
+ QPointF(double(i) + uvOffset.x(),
+ getVPos(QPointF(R_z + uvOffset.z(), uvOffset.y()), vars));
+
+ // 手前のDisplacementで隠されている部分かどうかの判定
+ bool isBehind = false;
+
+ if (preUV.y() > currentUV.y()) isBehind = true;
+ /*
+ if ((vars.displacement > 0.0 && dispValue < 1.0) ||
+ (vars.displacement < 0.0 && dispValue > 0.0)) {
+ QPointF dUV = (currentDispUV - currentUV) * 0.1;
+ QPointF tmpUV = currentUV + dUV;
+ float dHeight = dispHeight * 0.1;
+ float tmpHeight = dHeight;
+ for (int k = 1; k < 10; k++, tmpUV += dUV, tmpHeight += dHeight) {
+ if (getMapValue(tmpUV, disp_host, vars) * vars.displacement >
+ tmpHeight) {
+ isBehind = true;
+ break;
+ }
+ }
+ }*/
+
+ // continue if the current point is behind the forward bumps
+ if (vPos <= maxVPos) {
+ preVPos = vPos;
+ if (!isBehind) preUV = currentUV;
+ preTextureOffset = currentTextureOffset;
+ continue;
+ }
+
+ // putting colors on pixels
+ int currentY = int(std::floor(maxVPos));
+ float current_frac = maxVPos - float(currentY);
+ int toY = int(std::floor(vPos));
+ float to_frac = vPos - float(toY);
+ // if the entire current segment is in the same pixel,
+ // then add colors with percentage of the fraction part
+ if (currentY == toY) {
+ if (!isBehind) {
+ float ratio = to_frac - current_frac;
+ if (vars.differenceMode)
+ *result_p += getTextureOffsetColor(preTextureOffset,
+ currentTextureOffset, 0.5) *
+ ratio;
+ else
+ *result_p += getSourcePix(lerpUV(preUV, currentUV, 0.5),
+ source_host, vars) *
+ ratio;
+ preUV = currentUV;
+ }
+ preVPos = vPos;
+ maxVPos = vPos;
+ preTextureOffset = currentTextureOffset;
+ continue;
+ }
+
+ // fill fraction part of the start pixel (currentY)
+ if (!isBehind) {
+ float k = (maxVPos + (1.0 - current_frac) / 2.0 - preVPos) /
+ (vPos - preVPos);
+ float ratio = 1.0 - current_frac;
+ if (vars.differenceMode)
+ *result_p += getTextureOffsetColor(preTextureOffset,
+ currentTextureOffset, k) *
+ ratio;
+ else
+ *result_p +=
+ getSourcePix(lerpUV(preUV, currentUV, k), source_host, vars) *
+ ratio;
+ }
+
+ if (result_p == end_p) break;
+ result_p++;
+
+ // fill pixels between currentY and toY
+ for (int y = currentY + 1; y < toY; y++) {
+ if (!isBehind) {
+ float k = (float(y) + 0.5 - preVPos) / (vPos - preVPos);
+ if (vars.differenceMode)
+ *result_p += getTextureOffsetColor(preTextureOffset,
+ currentTextureOffset, k);
+ else
+ *result_p =
+ getSourcePix(lerpUV(preUV, currentUV, k), source_host, vars);
+ }
+ if (result_p == end_p) break;
+ result_p++;
+ }
+
+ // fill fraction part of the end pixel (toY)
+ if (!isBehind) {
+ float k = (float(toY) + to_frac * 0.5 - preVPos) / (vPos - preVPos);
+ if (vars.differenceMode)
+ *result_p += getTextureOffsetColor(preTextureOffset,
+ currentTextureOffset, k) *
+ to_frac;
+ else
+ *result_p +=
+ getSourcePix(lerpUV(preUV, currentUV, k), source_host, vars) *
+ to_frac;
+ preUV = currentUV;
+ }
+
+ preVPos = vPos;
+ maxVPos = vPos;
+ preTextureOffset = currentTextureOffset;
+ }
+ }
+ }
+ // Other modes
+ // (Diffuse, Specular, Fresnel, Refraction, Reflection)
+ else {
+ // render pixels in column-major order, from bottom to top
+ for (int i = 0; i < vars.resultDim.lx; i++) {
+ // cancel check
+ if (settings.m_isCanceled && *settings.m_isCanceled) return;
+
+ double maxVPos = 0.0; // maximum height of drawn pixels
+ double preVPos = 0.0; // height of the previous segment
+ QVector3D preDispNormal(0, 1, 0);
+ QVector3D preBaseNormal(0, 1, 0);
+ QVector3D prePos(i, 0,
+ vars.C_z); // actually the x coordinate is not i at the
+ // initial position.., but no problem.
+ QVector3D preBasePos(i, 0, vars.C_z);
+
+ float4 *result_p = &result_host[i * vars.resultDim.ly];
+ float4 *end_p = &result_host[(i + 1) * vars.resultDim.ly - 1];
+ // get height pixels from bottom to top
+ for (int j = -vars.margin; j < vars.refHeight; j++) {
+ // angle between the light axis and the line between (Q) and the eye (P)
+ // (Q) is a point on the projection plane at distance of j from (B)
+ double angle_Q =
+ std::atan((float(j) - (vars.outDim.ly / 2.0)) / vars.d_PT);
+ // (R) is an intersection between the XZ plane and the line P->Q
+ double R_z = vars.P_y / tan(vars.angle_el - angle_Q);
+ // In reflection mode, do not draw surface behind the Distance Level
+ if (vars.renderMode == ReflectionMode && vars.distance > 0.0 &&
+ R_z >= vars.distance)
+ break;
+
+ // compute (S), a point on the bumped surface in Y-axis direction from
+ // (R)
+ float refVal =
+ getMapValue(QPointF(double(i), double(j)), ref_host, vars);
+ QPointF S(R_z, vars.waveHeight * (refVal - vars.zeroLevel) * 2.0);
+ // length of (1,0,0) vector on the XZ plane, projected on the projection
+ // plane
+ double dist = (vars.d_PT * sin(vars.angle_el - angle_Q)) / vars.P_y *
+ cos(angle_Q);
+
+ // UV coordinate of the texture image
+ QVector3D uvOffset;
+ if (vars.textureOffsetAmount == 0.0) {
+ // currentUV = QPointF(double(i), getVPos(QPointF(R_z, 0)));
+ } else {
+ // approximate the gradient by difference of the heights of neighbor
+ // points
+
+ // compute gradient of the height image
+ QPointF offset;
+ for (const QPointF &subPoint : subPoints) {
+ // この地点のRefの値を取る
+ QPointF subRefPos(double(i) + dist * subPoint.x(),
+ getVPos(QPointF(R_z + subPoint.y(), 0), vars));
+
+ int sign_u = (subPoint.x() > 0) - (subPoint.x() < 0); // -1, 0, 1
+ int sign_v = (subPoint.y() > 0) - (subPoint.y() < 0); // -1, 0, 1
+
+ float subRefValue = getMapValue(subRefPos, ref_host, vars);
+ offset += subRefValue * QPointF(sign_u, sign_v);
+ }
+ offset *= 1.0 / float(subPoints.count() - subAmount); // サンプル数
+ offset *= vars.textureOffsetAmount /
+ (vars.spread * (0.5 + 0.5 / float(subAmount))); // 距離
+
+ uvOffset.setX(offset.x() * dist);
+ uvOffset.setZ(offset.y());
+ // currentUV = QPointF(double(i) + offset.x() * dist,
+ // getVPos(QPointF(R_z + offset.y(), 0)));
+ }
+ QPointF currentDispUV =
+ QPointF(double(i) + uvOffset.x(),
+ getVPos(QPointF(R_z + uvOffset.z(), 0), vars));
+
+ // この地点の変位の値を取得する
+ float dispHeight =
+ getMapValue(currentDispUV, disp_host, vars) * vars.displacement;
+
+ // compute normal vector from cross-product of surface vectors
+ QVector3D rightPos(
+ 1.0,
+ getMapValue(QPointF(double(i) + dist, double(j)), ref_host, vars) *
+ vars.waveHeight,
+ 0.0);
+ QVector3D leftPos(
+ -1.0,
+ getMapValue(QPointF(double(i) - dist, double(j)), ref_host, vars) *
+ vars.waveHeight,
+ 0.0);
+ QVector3D farPos(
+ 0.0,
+ getMapValue(QPointF(double(i), getVPos(QPointF(R_z + 1, 0), vars)),
+ ref_host, vars) *
+ vars.waveHeight,
+ 1.0);
+ QVector3D nearPos(
+ 0.0,
+ getMapValue(QPointF(double(i), getVPos(QPointF(R_z - 1, 0), vars)),
+ ref_host, vars) *
+ vars.waveHeight,
+ -1.0);
+ // 左手系座標なので外積を反転する
+ QVector3D currentNormal =
+ -QVector3D::crossProduct(rightPos - leftPos, farPos - nearPos);
+ currentNormal.normalize();
+
+ // 変位後の表面座標 (M)
+ // ※法線のX成分は強引にY座標の変位に持ち込んで近似する
+ float dispXY = std::sqrt(currentNormal.x() * currentNormal.x() +
+ currentNormal.y() * currentNormal.y());
+ QPointF M = S + QPointF(currentNormal.z(), dispXY) * dispHeight;
+
+ // 前後左右の座標に変位を加える
+ QPointF rightDispUV = currentDispUV + QPointF(dist, 0);
+ QPointF leftDispUV = currentDispUV + QPointF(-dist, 0);
+ QPointF farDispUV(currentDispUV.x(),
+ getVPos(QPointF(R_z + uvOffset.z() + 1, 0), vars));
+ QPointF nearDispUV(currentDispUV.x(),
+ getVPos(QPointF(R_z + uvOffset.z() - 1, 0), vars));
+ float rightDispHeight =
+ getMapValue(rightDispUV, disp_host, vars) * vars.displacement;
+ float leftDispHeight =
+ getMapValue(leftDispUV, disp_host, vars) * vars.displacement;
+ float farDispHeight =
+ getMapValue(farDispUV, disp_host, vars) * vars.displacement;
+ float nearDispHeight =
+ getMapValue(nearDispUV, disp_host, vars) * vars.displacement;
+
+ rightPos += currentNormal * rightDispHeight;
+ leftPos += currentNormal * leftDispHeight;
+ farPos += currentNormal * farDispHeight;
+ nearPos += currentNormal * nearDispHeight;
+
+ // 外積で法線を求める 左手系座標なので外積を反転する
+ QVector3D dispNormal =
+ -QVector3D::crossProduct(rightPos - leftPos, farPos - nearPos);
+ dispNormal.normalize();
+
+ // height of (M) projected on the projection plane
+ double vPos = getVPos(M, vars);
+ QVector3D currentPos(double(i - vars.resultDim.lx / 2) / dist, M.y(),
+ M.x());
+
+ // Diffuse差分取得のために、変形無しの状態の法線を取得する
+ QVector3D baseNormal;
+ // Specular差分取得のために、変形無しの状態の位置も取得
+ QVector3D basePos;
+ if (vars.differenceMode) {
+ baseNormal = -QVector3D::crossProduct(
+ QVector3D(2.0, rightDispHeight - leftDispHeight, 0.0),
+ QVector3D(0.0, farDispHeight - nearDispHeight, 2.0));
+ basePos = QVector3D(currentPos.x() + uvOffset.x() / dist, dispHeight,
+ currentPos.z() + uvOffset.z());
+ }
+
+ // continue if the current point is behind the forward bumps
+ if (vPos <= maxVPos) {
+ preVPos = vPos;
+ preDispNormal = dispNormal;
+ preBaseNormal = baseNormal;
+ prePos = currentPos;
+ preBasePos = basePos;
+ continue;
+ }
+
+ // putting colors on pixels
+ int currentY = int(std::floor(maxVPos));
+ float current_frac = maxVPos - float(currentY);
+ int toY = int(std::floor(vPos));
+ float to_frac = vPos - float(toY);
+ // if the entire current segment is in the same pixel,
+ // then add colors with percentage of the fraction part
+ if (currentY == toY) {
+ float ratio = to_frac - current_frac;
+ *result_p += getColor(preDispNormal, dispNormal, prePos, currentPos,
+ 0.5, source_host, vars, preBaseNormal,
+ baseNormal, preBasePos, basePos) *
+ ratio;
+ preVPos = vPos;
+ maxVPos = vPos;
+ preDispNormal = dispNormal;
+ preBaseNormal = baseNormal;
+ prePos = currentPos;
+ preBasePos = basePos;
+ continue;
+ }
+
+ // fill fraction part of the start pixel (currentY)
+ float k =
+ (maxVPos + (1.0 - current_frac) / 2.0 - preVPos) / (vPos - preVPos);
+ float4 color = getColor(preDispNormal, dispNormal, prePos, currentPos,
+ k, source_host, vars, preBaseNormal, baseNormal,
+ preBasePos, basePos);
+ float ratio = 1.0 - current_frac;
+ *result_p += color * ratio;
+
+ if (result_p == end_p) break;
+ result_p++;
+
+ // fill pixels between currentY and toY
+ for (int y = currentY + 1; y < toY; y++) {
+ k = (float(y) + 0.5 - preVPos) / (vPos - preVPos);
+ *result_p = getColor(preDispNormal, dispNormal, prePos, currentPos, k,
+ source_host, vars, preBaseNormal, baseNormal,
+ preBasePos, basePos);
+ if (result_p == end_p) break;
+ result_p++;
+ }
+
+ // fill fraction part of the end pixel (toY)
+ k = (float(toY) + to_frac * 0.5 - preVPos) / (vPos - preVPos);
+ color = getColor(preDispNormal, dispNormal, prePos, currentPos, k,
+ source_host, vars, preBaseNormal, baseNormal,
+ preBasePos, basePos);
+ *result_p += color * to_frac;
+
+ preVPos = vPos;
+ maxVPos = vPos;
+ preDispNormal = dispNormal;
+ preBaseNormal = baseNormal;
+ prePos = currentPos;
+ preBasePos = basePos;
+ }
+ }
+ }
+}
+
+//------------------------------------------------------------
+// convert input tile's channel values to float4 values
+//------------------------------------------------------------
+
+template
+void Iwa_FloorBumpFx::setSourceRaster(const RASTER srcRas, float4 *srcMem,
+ TDimensionI dim) {
+ float4 *s_p = srcMem;
+ for (int j = 0; j < dim.ly; j++) {
+ PIXEL *s_pix = srcRas->pixels(j);
+ for (int i = 0; i < dim.lx; i++, s_pix++, s_p++) {
+ (*s_p).x = (float)s_pix->r / (float)PIXEL::maxChannelValue;
+ (*s_p).y = (float)s_pix->g / (float)PIXEL::maxChannelValue;
+ (*s_p).z = (float)s_pix->b / (float)PIXEL::maxChannelValue;
+ (*s_p).w = (float)s_pix->m / (float)PIXEL::maxChannelValue;
+ }
+ }
+}
+
+void Iwa_FloorBumpFx::setRefRaster(const TRaster64P refRas, float *refMem,
+ TDimensionI dim, bool isRef) {
+ float zeroLevel =
+ (isRef) ? float(128) / float(TPixel32::maxChannelValue) : 0.0;
+ float *r_p = refMem;
+ for (int j = 0; j < dim.ly; j++) {
+ TPixel64 *r_pix = refRas->pixels(j);
+ for (int i = 0; i < dim.lx; i++, r_pix++, r_p++) {
+ float r = (float)r_pix->r / (float)TPixel64::maxChannelValue;
+ float g = (float)r_pix->g / (float)TPixel64::maxChannelValue;
+ float b = (float)r_pix->b / (float)TPixel64::maxChannelValue;
+ float m = (float)r_pix->m / (float)TPixel64::maxChannelValue;
+ // taking brightness
+ float brightness = 0.298912f * r + 0.586610f * g + 0.114478f * b;
+ (*r_p) = brightness * m + zeroLevel * (1.0 - m);
+ }
+ }
+}
+
+//------------------------------------
+
+void Iwa_FloorBumpFx::doCompute(TTile &tile, double frame,
+ const TRenderSettings &rend_sets) {
+ FloorBumpVars vars;
+ vars.renderMode = m_renderMode->getValue();
+ if ((isTextureUsed(vars.renderMode) && !m_texture.isConnected()) ||
+ !m_heightRef.isConnected()) {
+ tile.getRaster()->clear();
+ return;
+ }
+
+ initVars(vars, tile, rend_sets, frame);
+
+ // メモリの確保
+ float4 *source_host = nullptr;
+ float *ref_host;
+ float *disp_host = nullptr;
+
+ TRasterGR8P source_host_ras;
+ if (isTextureUsed(vars.renderMode)) {
+ source_host_ras =
+ TRasterGR8P(vars.sourceDim.lx * sizeof(float4), vars.sourceDim.ly);
+ source_host_ras->lock();
+ source_host = (float4 *)source_host_ras->getRawData();
+ }
+
+ TRasterGR8P ref_host_ras(vars.refDim.lx * sizeof(float), vars.refDim.ly);
+ ref_host_ras->lock();
+ ref_host = (float *)ref_host_ras->getRawData();
+
+ TRasterGR8P disp_host_ras;
+ if (vars.displacement != 0.0 &&
+ m_dispRef.isConnected()) { // renderMode すべてに対応させるかはTBD
+ disp_host_ras = TRasterGR8P(vars.refDim.lx * sizeof(float), vars.refDim.ly);
+ disp_host_ras->lock();
+ disp_host = (float *)disp_host_ras->getRawData();
+ }
+
+ // 画像の取得、格納
+ {
+ if (isTextureUsed(vars.renderMode)) {
+ TRenderSettings source_sets(rend_sets);
+ TPointD sourceTilePos =
+ (tile.m_pos - TPointD(vars.margin, vars.margin)) * vars.precision;
+ source_sets.m_affine *= TScale(vars.precision, vars.precision);
+
+ TTile sourceTile;
+ m_texture->allocateAndCompute(sourceTile, sourceTilePos, vars.sourceDim,
+ tile.getRaster(), frame, source_sets);
+
+ TRaster32P ras32 = (TRaster32P)sourceTile.getRaster();
+ TRaster64P ras64 = (TRaster64P)sourceTile.getRaster();
+ if (ras32)
+ setSourceRaster(ras32, source_host,
+ vars.sourceDim);
+ else if (ras64)
+ setSourceRaster(ras64, source_host,
+ vars.sourceDim);
+ }
+ // height image is always computed in 16bpc regardless of the current render
+ // settings in order to get smooth gradient
+ TRenderSettings ref_sets(rend_sets);
+ TPointD refTilePos = tile.m_pos - TPointD(vars.margin, vars.margin);
+ ref_sets.m_bpp = 64;
+ TTile refTile;
+ m_heightRef->allocateAndCompute(refTile, refTilePos, vars.refDim,
+ TRasterP(), frame, ref_sets);
+ setRefRaster((TRaster64P)refTile.getRaster(), ref_host, vars.refDim, true);
+
+ // disp imageも同様
+ if (disp_host) {
+ TTile dispTile;
+ m_dispRef->allocateAndCompute(dispTile, refTilePos, vars.refDim,
+ TRasterP(), frame, ref_sets);
+ setRefRaster((TRaster64P)dispTile.getRaster(), disp_host, vars.refDim,
+ false);
+ }
+ }
+
+ // prepare the memory for result image
+ TRasterGR8P result_host_ras(vars.resultDim.lx * sizeof(float4),
+ vars.resultDim.ly);
+ float4 *result_host;
+ result_host_ras->lock();
+ result_host = (float4 *)result_host_ras->getRawData();
+
+ // compute
+ if (disp_host) {
+ doCompute_with_Displacement(tile, frame, rend_sets, vars, source_host,
+ ref_host, disp_host, result_host);
+ } else {
+ doCompute_CPU(tile, frame, rend_sets, vars, source_host, ref_host,
+ result_host);
+ }
+
+ if (isTextureUsed(vars.renderMode)) source_host_ras->unlock();
+ ref_host_ras->unlock();
+ if (disp_host) disp_host_ras->unlock();
+
+ // cancel check
+ if (rend_sets.m_isCanceled && *rend_sets.m_isCanceled) {
+ tile.getRaster()->clear();
+ return;
+ }
+
+ // output the result
+ TRaster32P outRas32 = (TRaster32P)tile.getRaster();
+ TRaster64P outRas64 = (TRaster64P)tile.getRaster();
+ if (outRas32)
+ setOutputRaster(result_host, outRas32, vars.outDim,
+ vars.resultDim.ly);
+ else if (outRas64)
+ setOutputRaster(result_host, outRas64, vars.outDim,
+ vars.resultDim.ly);
+
+ result_host_ras->unlock();
+}
+
+//------------------------------------
+
+void Iwa_FloorBumpFx::getParamUIs(TParamUIConcept *&concepts, int &length) {
+ concepts = new TParamUIConcept[length = 3];
+
+ concepts[0].m_type = TParamUIConcept::VERTICAL_POS;
+ concepts[0].m_label = "Eye Level";
+ concepts[0].m_params.push_back(m_eyeLevel);
+
+ concepts[1].m_type = TParamUIConcept::VERTICAL_POS;
+ concepts[1].m_label = "Draw Level";
+ concepts[1].m_params.push_back(m_drawLevel);
+
+ concepts[2].m_type = TParamUIConcept::VERTICAL_POS;
+ concepts[2].m_label = "Distance Level";
+ concepts[2].m_params.push_back(m_distanceLevel);
+ concepts[2].m_params.push_back(m_renderMode);
+}
+
+//------------------------------------
+
+FX_PLUGIN_IDENTIFIER(Iwa_FloorBumpFx, "iwa_FloorBumpFx")
diff --git a/toonz/sources/stdfx/iwa_floorbumpfx.h b/toonz/sources/stdfx/iwa_floorbumpfx.h
new file mode 100644
index 0000000..3a47d51
--- /dev/null
+++ b/toonz/sources/stdfx/iwa_floorbumpfx.h
@@ -0,0 +1,163 @@
+#pragma once
+
+#ifndef IWA_FLOORBUMPFX_H
+#define IWA_FLOORBUMPFX_H
+
+#include "tfxparam.h"
+#include "stdfx.h"
+#include "tparamset.h"
+
+#include
+struct float4 {
+ float x, y, z, w;
+ float4 operator*(const float &v) const {
+ return float4{x * v, y * v, z * v, w * v};
+ }
+ float4 &operator+=(const float4 &v) {
+ x += v.x;
+ y += v.y;
+ z += v.z;
+ w += v.w;
+ return *this;
+ }
+};
+
+class Iwa_FloorBumpFx final : public TStandardRasterFx {
+ FX_PLUGIN_DECLARATION(Iwa_FloorBumpFx)
+public:
+ enum RenderMode {
+ TextureMode = 0,
+ DiffuseMode,
+ SpecularMode,
+ FresnelMode,
+ RefractionMode,
+ ReflectionMode
+ };
+
+ struct FloorBumpVars {
+ double waveHeight;
+ double displacement;
+ int refHeight;
+ TDimensionI outDim;
+ TDimensionI resultDim;
+ int margin;
+ double precision;
+ // add margins to all ends and multiply by precision value
+ TDimensionI sourceDim; // u
+ // only add margins for height image
+ TDimensionI refDim; // u
+
+ // collecting parameters
+ double textureOffsetAmount; // u
+ double spread; // u
+ double camAltitude;
+ int renderMode; // u
+ bool differenceMode; // u
+
+ // making pixels in gray128 to be zero level height
+ // ( 128/255. IT'S NOT 0.5! )
+ double zeroLevel; // u
+ double H; // u
+ double W; // u
+ // angle between the optical axis and the horizontal axis
+ double angle_el; // u
+ // Y coordinate of the Eye position (P)
+ double P_y; // u
+ // distance from the Eye (P) to the center of the projection plane (T)
+ double d_PT; // u
+
+ // Z-Y position of the center of top edge of the projection plane (A)
+ QPointF A; // u
+ // Z-Y position of the center of bottom edge of the projection plane (B)
+ QPointF B; // u
+
+ // (C) is an intersection between the XZ plane and the line P->B
+ double C_z; // u
+ QVector3D sunVec; // u
+ double base_fresnel_ref; // u
+ double depth, r_index; // uu
+ double distance; // u
+ QVector3D eyePos; // u
+ };
+
+protected:
+ TRasterFxPort m_heightRef; // height reference image
+ TRasterFxPort m_texture; // texture image
+ TRasterFxPort m_dispRef; // displacement image
+
+ TIntEnumParamP m_renderMode;
+
+ TDoubleParamP m_fov; // camera fov (degrees)
+ TDoubleParamP
+ m_cameraAltitude; // height of the bottom edge of projection plane
+
+ TDoubleParamP m_eyeLevel; // height of the vanishing point
+ TDoubleParamP m_drawLevel; // upper rendering boundary
+
+ TDoubleParamP m_waveHeight; // height of waves to the both sides (i.e.
+ // amplitude becomes 2*waveHeight)
+
+ TBoolParamP
+ m_differenceMode; // available in diffuse and fresnel mode,
+ // render brightness difference from unbumped state
+
+ // Texture mode parameters
+ TDoubleParamP m_textureOffsetAmount; // amount of texture trailing along with
+ // gradient of the bump
+ TDoubleParamP
+ m_textureOffsetSpread; // adding "blur" to the gradient distribution
+
+ TDoubleParamP m_sourcePrecision; // to load the texture with higher dpi
+ TDoubleParamP m_souceMargin; // margins to be added to all edges for both the
+ // height reference and the texture images
+
+ TDoubleParamP m_displacement;
+
+ // Shading (Diffuse and Specular) modes parameters
+ TDoubleParamP m_lightAzimuth; // light is in front of camera with azimuth=0.
+ // clockwise angle (degrees)
+ TDoubleParamP m_lightElevation; // (degrees)
+
+ // Refraction mode parameters
+ TDoubleParamP m_depth; // water depth. the bottom will be placed at -depth
+ TDoubleParamP
+ m_refractiveIndex; // refractive index of the medium under the surface
+
+ // Reflection mode parameter
+ TDoubleParamP m_distanceLevel; // the distance of the reflected object
+ // specified by the postion on the surface
+
+ // convert output values (in float4) to channel value
+ template
+ void setOutputRaster(float4 *srcMem, const RASTER dstRas, TDimensionI dim,
+ int drawLevel);
+
+ // convert input tile's channel values to float4 values
+ template
+ void setSourceRaster(const RASTER srcRas, float4 *srcMem, TDimensionI dim);
+
+ void setRefRaster(const TRaster64P refRas, float *refMem, TDimensionI dim,
+ bool isRef);
+
+ inline void initVars(FloorBumpVars &vars, TTile &tile,
+ const TRenderSettings &settings, double frame);
+
+public:
+ Iwa_FloorBumpFx();
+ bool doGetBBox(double frame, TRectD &bBox,
+ const TRenderSettings &info) override;
+ bool canHandle(const TRenderSettings &info, double frame) override;
+ void doCompute(TTile &tile, double frame,
+ const TRenderSettings &rend_sets) override;
+ void doCompute_CPU(TTile &tile, const double frame,
+ const TRenderSettings &settings, const FloorBumpVars &vars,
+ float4 *source_host, float *ref_host, float4 *result_host);
+ void doCompute_with_Displacement(TTile &tile, const double frame,
+ const TRenderSettings &settings,
+ const FloorBumpVars &vars,
+ float4 *source_host, float *ref_host,
+ float *disp_host, float4 *result_host);
+ void getParamUIs(TParamUIConcept *&concepts, int &length) override;
+};
+
+#endif
\ No newline at end of file
diff --git a/toonz/sources/tnztools/edittoolgadgets.cpp b/toonz/sources/tnztools/edittoolgadgets.cpp
index 5e143b3..5f0252d 100644
--- a/toonz/sources/tnztools/edittoolgadgets.cpp
+++ b/toonz/sources/tnztools/edittoolgadgets.cpp
@@ -2366,6 +2366,78 @@ void EllipseFxGadget::leftButtonDrag(const TPointD &pos, const TMouseEvent &e) {
void EllipseFxGadget::leftButtonUp() { m_handle = None; }
+//=============================================================================
+
+class VerticalPosFxGadget final : public FxGadget {
+ TPointD m_pos;
+ TDoubleParamP m_yParam;
+ TIntEnumParamP m_mode;
+
+public:
+ VerticalPosFxGadget(FxGadgetController *controller,
+ const TDoubleParamP ¶m, const TIntEnumParamP &mode)
+ : FxGadget(controller), m_yParam(param), m_mode(mode) {
+ addParam(m_yParam);
+ }
+
+ void draw(bool picking) override;
+
+ bool isVisible();
+ void leftButtonDown(const TPointD &pos, const TMouseEvent &) override;
+ void leftButtonDrag(const TPointD &pos, const TMouseEvent &) override;
+};
+
+//---------------------------------------------------------------------------
+// Dirty resolution to hide gadget when selecting unrelated modes
+
+bool VerticalPosFxGadget::isVisible() {
+ if (!m_mode) return true;
+ // condition for Distance Level parameter of Iwa_FloorBumpFx
+ if (m_yParam->getName() == "distanceLevel" && m_mode->getValue() != 5)
+ return false;
+ return true;
+}
+
+//---------------------------------------------------------------------------
+
+void VerticalPosFxGadget::draw(bool picking) {
+ if (!isVisible()) return;
+ setPixelSize();
+ if (isSelected())
+ glColor3dv(m_selectedColor);
+ else
+ glColor3d(0, 0, 1);
+ glPushName(getId());
+ double vPos = getValue(m_yParam);
+ double unit = getPixelSize();
+ glPushMatrix();
+ glTranslated(0, vPos, 0);
+ double r = unit * 3;
+ double d = unit * 300;
+ glBegin(GL_LINES);
+ glVertex2d(0, r);
+ glVertex2d(0, -r);
+ glVertex2d(-d, 0);
+ glVertex2d(d, 0);
+ glEnd();
+ drawTooltip(TPointD(7, 7) * unit, getLabel());
+
+ glPopMatrix();
+ glPopName();
+}
+
+//---------------------------------------------------------------------------
+
+void VerticalPosFxGadget::leftButtonDown(const TPointD &pos,
+ const TMouseEvent &) {}
+
+//---------------------------------------------------------------------------
+
+void VerticalPosFxGadget::leftButtonDrag(const TPointD &pos,
+ const TMouseEvent &) {
+ if (m_yParam) setValue(m_yParam, pos.y);
+}
+
//*************************************************************************************
// FxGadgetController implementation
//*************************************************************************************
@@ -2599,6 +2671,15 @@ FxGadget *FxGadgetController::allocateGadget(const TParamUIConcept &uiConcept) {
break;
}
+ case TParamUIConcept::VERTICAL_POS: {
+ assert(uiConcept.m_params.size() >= 1 && uiConcept.m_params.size() <= 2);
+ TIntEnumParamP mode((uiConcept.m_params.size() >= 2)
+ ? (TIntEnumParamP)uiConcept.m_params[1]
+ : TIntEnumParamP());
+ gadget = new VerticalPosFxGadget(this, uiConcept.m_params[0], mode);
+ break;
+ }
+
default:
break;
}
diff --git a/toonz/sources/toonzqt/fxsettings.cpp b/toonz/sources/toonzqt/fxsettings.cpp
index a0e1863..93931b0 100644
--- a/toonz/sources/toonzqt/fxsettings.cpp
+++ b/toonz/sources/toonzqt/fxsettings.cpp
@@ -343,6 +343,7 @@ void ParamsPage::setPageField(TIStream &is, const TFxP &fx, bool isVertical) {
m_mainLayout->setColumnStretch(0, 0);
m_mainLayout->setColumnStretch(1, 1);
setPageField(is, fx, true);
+
tmpWidget->setLayout(m_mainLayout);
// turn back the layout
m_mainLayout = keepMainLay;
@@ -872,7 +873,7 @@ void ParamsPageSet::addParamsPage(ParamsPage *page, const char *name) {
/*-- このFxで最大サイズのページに合わせてダイアログをリサイズ --*/
QSize pagePreferredSize = page->getPreferredSize();
m_preferredSize = m_preferredSize.expandedTo(
- pagePreferredSize + QSize(m_tabBarContainer->height() + 2,
+ pagePreferredSize + QSize(m_tabBarContainer->height() + 2,
2)); /*-- 2は上下左右のマージン --*/
QScrollArea *pane = new QScrollArea(this);
@@ -976,7 +977,7 @@ void ParamsPageSet::createPage(TIStream &is, const TFxP &fx, int index) {
/*-- このFxで最大サイズのページに合わせてダイアログをリサイズ --*/
QSize pagePreferredSize = paramsPage->getPreferredSize();
m_preferredSize = m_preferredSize.expandedTo(
- pagePreferredSize + QSize(m_tabBarContainer->height() + 2,
+ pagePreferredSize + QSize(m_tabBarContainer->height() + 2,
2)); /*-- 2は上下左右のマージン --*/
QScrollArea *scrollAreaPage = new QScrollArea(this);
diff --git a/toonz/sources/toonzqt/paramfield.cpp b/toonz/sources/toonzqt/paramfield.cpp
index e3a1605..f119c45 100644
--- a/toonz/sources/toonzqt/paramfield.cpp
+++ b/toonz/sources/toonzqt/paramfield.cpp
@@ -1386,7 +1386,6 @@ void EnumParamField::onChange(const QString &str) {
emit currentParamChanged();
emit actualParamChanged();
-
emit modeChanged(m_actualParam->getValue());
if (undo) TUndoManager::manager()->add(undo);
@@ -1420,6 +1419,10 @@ void EnumParamField::update(int frame) {
}
}
+//-----------------------------------------------------------------------------
+
+int EnumParamField::getValue() const { return m_actualParam->getValue(); }
+
//=============================================================================
// BoolParamField
//-----------------------------------------------------------------------------