// Copyright (c) 2021 The Foundry Visionmongers Ltd. All Rights Reserved. #include "DDImage/ModifyGeomOp.h" #include "usg/geom/PointBasedPrim.h" using namespace DD::Image; namespace NukeExample { static const char* kTwistClass = "GeoTwist"; static const char* kHelp = "@i;GeoTwist@n; " "spins the points around a center point."; const char* const kTwistKnobName = "twist"; const char* const kCenterKnobName = "center"; const char* const kScaleKnobName = "scale"; /*! A demonstration GeomOp applying a simple point location modification algorithm. */ class GeoTwist : public ModifyGeomOp { public: const char* Class() const override { return kTwistClass; } const char* node_help() const override { return kHelp; } GeoTwist(Node* node) : ModifyGeomOp(node, BuildEngine()) {} void knobs(Knob_Callback f) override { ModifyGeomOp::knobs(f); double twist = 0.0; fdk::Vec3f center(0.0f); fdk::Vec3f scale(1.0f); Double_knob(f, &twist, IRange(-180, 180), kTwistKnobName, "Twist"); KnobModifiesAttribValues(f); XYZ_knob(f, ¢er.x, kCenterKnobName, "Rotation Center"); KnobModifiesAttribValues(f); XYZ_knob(f, &scale.x, kScaleKnobName, "Scale"); KnobModifiesAttribValues(f); } static Op* Build(Node* node) { return new GeoTwist(node); } static const Op::Description description; private: class Engine : public ModifyEngine { public: Engine(Op* parent) : ModifyEngine(parent) {} //! Filter for PointBased prims. bool secondaryFilterPrimPaths(const usg::StageRef& srcStage, const usg::PathArray& inPrimPaths, usg::PathArray& outPrimPaths) override { // ModifyEngine caches the filtered paths after this is run: outPrimPaths = srcStage->findPrimsOfType(inPrimPaths, true/*matchDescendants*/); return true; } void processPrim(usg::GeomSceneContext& context, const usg::StageRef& srcStage, const usg::Path& primPath) override { auto inPrim = usg::PointBasedPrim::getInStage(srcStage, primPath); if (!inPrim) { return; // no point data to twiddle } if (auto outPrim = usg::PointBasedPrim::overrideInLayer(editLayer(), inPrim)) { const usg::Attribute pointsAttr = inPrim.getPointsAttr(); if (!pointsAttr) { return; } const auto kTwist = knob(kTwistKnobName); const auto kCenter = knob(kCenterKnobName); const auto kScale = knob(kScaleKnobName); // Are the modify controls or input points animating? const bool isAnimating = (modifyValuesTarget().isAnimating() || pointsAttr.isAnimating()); usg::Vec3fArray points; for (const auto& time : context.processTimes()) { const double twist = getValue(0.0, kTwist, time); const fdk::Vec3f center = getValue(fdk::Vec3f(0.0f), kCenter, time); const auto scale = getValue(fdk::Vec3f(1.0f), kScale, time); if (inPrim.getPointsArray(points, time) > 0) { for (auto& p : points) { const auto pnew = (p - center); // Twist factor * distance from center: const float r = radiansf(twist) * (1.0f - pnew.length() * 2.0f) * 2.0f; // Rotations weighted by distance: const float s = sinf(r); const float c = cosf(r); // Move the point: p.x = (pnew.x * c + pnew.y * -s) * scale.x + center.x; p.y = (pnew.x * s + pnew.y * c) * scale.y + center.y; } const auto outPointsTime = (isAnimating) ? time : fdk::defaultTimeValue(); outPrim.setPoints(points, outPointsTime); outPrim.setBoundsAttr(points, outPointsTime); } } } } }; }; const Op::Description GeoTwist::description(kTwistClass, GeoTwist::Build); }