// Copyright (c) 2022 The Foundry Visionmongers Ltd. All Rights Reserved. #include "DDImage/Knobs.h" #include "DDImage/SourceGeomOp.h" #include "usg/geom/MeshPrim.h" #include "usg/geom/GeomTokens.h" #include "ndk/geo/utils/MeshUtils.h" #include using namespace DD::Image; using namespace fdk; using namespace usg; namespace NukeExample { static const char* kTriangleClass = "GeoTriangle"; static const char* kHelp = "Creates a 3D @i;Triangle@n;"; static const char* kPointAKnobName = "point_a"; static const char* kPointBKnobName = "point_b"; static const char* kPointCKnobName = "point_c"; class GeoTriangle : public SourceGeomOp { public: class Engine : public SourceEngine { public: Engine(Op* parent) : SourceEngine(parent) {} void createPrims(usg::GeomSceneContext& context, const usg::Path& path) override { if (context.doGeometryProcessing()) { // Create the mesh prim and the base attributes LayerRef defineLayer = editLayer(); assert(defineLayer); ndk::MeshSample sample; // local temp storage const auto knobPointA = knob(kPointAKnobName); // can animate! const auto knobPointB = knob(kPointBKnobName); // can animate! const auto knobPointC = knob(kPointCKnobName); // can animate! // Are the XYZ knobs animating? Any other knobs that may affect // the triangle's point locations over time should contribute // to this bool. For example if a new 'size' knob is added // that affects the global point locations (not done via xform) // then that knob's isAnimated() value should be OR'd in here: const bool pointsAnimating = knobPointA.isAnimated() || knobPointB.isAnimated() || knobPointC.isAnimated(); MeshPrim mesh = MeshPrim::defineInLayer(defineLayer, path); bool buildTopology = context.doGeometryInitialization(); Vec3f pointA, pointB, pointC; for (const TimeValue& time : context.processTimes()) { if (knobPointA) { pointA = knobPointA.get(time); } if (knobPointB) { pointB = knobPointB.get(time); } if (knobPointC) { pointC = knobPointC.get(time); } if (buildTopology) { sample.faceVertexCounts.clear(); sample.faceVertexIndices.clear(); sample.all_tris = true; sample.all_quads = false; sample.addTri(0, 1, 2); sample.facevert_uvs = {{0, 0}, {1, 0}, {0, 1}}; } sample.points = {pointA, pointB, pointC}; sample.facevert_normals.resize(1, { (pointB - pointA).cross(pointC - pointA).normalized() }); if (buildTopology) { sample.setMeshPrimFaceTopology(mesh); buildTopology = false; } sample.setMeshPrimProperties(mesh, true/*setUvs*/, true/*setNormals*/); const fdk::TimeValue pointsTime = (pointsAnimating) ? time : fdk::defaultTimeValue(); mesh.setPoints(sample.points, pointsTime); mesh.setBoundsAttr(sample.points, pointsTime); } } } }; GeoTriangle(Node* node) : SourceGeomOp(node, BuildEngine()), _aPos(0, 0, 0), _bPos(1, 0, 0), _cPos(0, 1, 0) {} virtual const char* Class() const override { return kTriangleClass; } const char* node_help() const override { return kHelp; } virtual void knobs(Knob_Callback f) override { SourceGeomOp::knobs(f); // Set up the common SourceGeo knobs. XYZ_knob(f, &_aPos[0], kPointAKnobName, "a"); KnobModifiesAttribValues(f); XYZ_knob(f, &_bPos[0], kPointBKnobName, "b"); KnobModifiesAttribValues(f); XYZ_knob(f, &_cPos[0], kPointCKnobName, "c"); KnobModifiesAttribValues(f); makeTransformKnob(f); KnobDefinesGeometry(f); } static Op* Build(Node* node) { return new GeoTriangle(node); } static const Op::Description description; private: Vec3f _aPos; Vec3f _bPos; Vec3f _cPos; }; const Op::Description GeoTriangle::description(kTriangleClass, GeoTriangle::Build); }