SOP/SOP_Flatten.C

/*
 * Copyright (c) 2013
 *      Side Effects Software Inc.  All rights reserved.
 *
 * Redistribution and use of Houdini Development Kit samples in source and
 * binary forms, with or without modification, are permitted provided that the
 * following conditions are met:
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. The name of Side Effects Software may not be used to endorse or
 *    promote products derived from this software without specific prior
 *    written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY SIDE EFFECTS SOFTWARE `AS IS' AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
 * NO EVENT SHALL SIDE EFFECTS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *----------------------------------------------------------------------------
 * The Flatten SOP.  This SOP Flattens the geometry onto a plane.
 */

#include <UT/UT_DSOVersion.h>
#include <UT/UT_Interrupt.h>
#include <UT/UT_Math.h>
#include <UT/UT_Matrix3.h>
#include <UT/UT_Matrix4.h>
#include <GU/GU_Detail.h>
#include <GU/GU_PrimPoly.h>
#include <PRM/PRM_Include.h>
#include <OP/OP_Operator.h>
#include <OP/OP_OperatorTable.h>
#include <SOP/SOP_Guide.h>
#include "SOP_Flatten.h"

using namespace HDK_Sample;

void
newSopOperator(OP_OperatorTable *table)
{
     table->addOperator(new OP_Operator("hdk_flatten",
                                        "Flatten",
                                         SOP_Flatten::myConstructor,
                                         SOP_Flatten::myTemplateList,
                                         1,
                                         1,
                                         0));
}

static PRM_Name        names[] = {
    PRM_Name("usedir",  "Use Direction Vector"),
    PRM_Name("dist",    "Distance"),
};

PRM_Template
SOP_Flatten::myTemplateList[] = {
    PRM_Template(PRM_STRING,    1, &PRMgroupName, 0, &SOP_Node::pointGroupMenu),
    PRM_Template(PRM_FLT_J,     1, &names[1], PRMzeroDefaults, 0,
                                   &PRMscaleRange),
    PRM_Template(PRM_TOGGLE,    1, &names[0]),
    PRM_Template(PRM_ORD,       1, &PRMorientName, 0, &PRMplaneMenu),
    PRM_Template(PRM_DIRECTION, 3, &PRMdirectionName, PRMzaxisDefaults),
    PRM_Template(),
};


OP_Node *
SOP_Flatten::myConstructor(OP_Network *net, const char *name, OP_Operator *op)
{
    return new SOP_Flatten(net, name, op);
}

SOP_Flatten::SOP_Flatten(OP_Network *net, const char *name, OP_Operator *op)
        : SOP_Node(net, name, op), myGroup(0)
{
    // Make sure to flag that we can supply a guide geometry
    mySopFlags.setNeedGuide1(1);
}

SOP_Flatten::~SOP_Flatten() {}

unsigned
SOP_Flatten::disableParms()
{
    unsigned changed = 0;

    changed  = enableParm(3, !DIRPOP());
    changed += enableParm(4,  DIRPOP());

    return changed;
}


OP_ERROR
SOP_Flatten::cookInputGroups(OP_Context &context, int alone)
{
    // If we are called by the handle, then "alone" equals 1.  In that
    // case, we have to lock the inputs oursevles, and unlock them
    // before exiting this method.
    if (alone) if (lockInputs(context) >= UT_ERROR_ABORT) return error();

    UT_String    grp_name;

    // The "gdp" variable is only available if we are called from the SOP
    // itself.  So, if we are called by a handle, we have to get the
    // geometry oursevles.
    GU_Detail   *pgdp = alone ? (GU_Detail *)inputGeo(0, context) : gdp;

    myGroup = 0;

    getGroups(grp_name);                // Get the group string.

    // If the group string is not null, then we try to parse the group.
    if (grp_name.isstring())
    {
        myGroup = parsePointGroups((const char *)grp_name, pgdp);

        // If the group is not valid, then the group string is invalid
        // as well.  Thus, we add an error to this SOP.
        if (!myGroup)
        {
            addError(SOP_ERR_BADGROUP, grp_name);
        }
        else if (!alone)
        {
            // If the parsed group is valid, then we want to highlight
            // only the group.  The second argument of "1" means that
            // we want the selection to have the same type as our group.
            select(*(GB_BaseGroup *)myGroup, 1);
        }
    }
    else if (!alone)
    {
        // If no group string is specified, then we operate on the entire
        // geometry, so we highlight every point for this SOP.
        select(GU_SPoint);
    }

    // This is where we notify our handles (if any) if the inputs have changed.
    checkInputChanged(0, -1, myDetailGroupPair, pgdp, myGroup);

    // If we are called by the handles, then we have to unlock our inputs.
    if (alone)
    {
        destroyAdhocGroups();
        unlockInputs();
    }

    return error();
}


OP_ERROR
SOP_Flatten::cookMySop(OP_Context &context)
{
    GEO_Point           *ppt;
    double               now;
    int                  plane;
    float                dist;
    UT_Vector3           normal, p;


    // Before we do anything, we must lock our inputs.  Before returning,
    //  we have to make sure that the inputs get unlocked.
    if (lockInputs(context) >= UT_ERROR_ABORT)
        return error();

    now = context.getTime();
    plane = 2;
    duplicateSource(0, context);

    // These three lines enable the local variable support.  This allows
    // $CR to get the red colour, for example, as well as supporting
    // any varmap created by the Attribute Create SOP.
    // Note that if you override getVariableValue for your own
    // local variables (like SOP_Star does) it is essential you
    // still call the SOP_Node::getVariableValue or you'll not
    // get any of the benefit of the built in local variables.

    // The variable order controls precedence for which attribute will be
    // be bound first if the same named variable shows up in multiple
    // places.  This ordering ensures point attributes get precedence.
    setVariableOrder(3, 2, 0, 1);

    // The setCur* functions track which part of the gdp is currently
    // being processed - it is what is used in the getVariableValue
    // callback as the current point.  The 0 is for the first input,
    // you can have two inputs so $CR2 would get the second input's
    // value.
    setCurGdh(0, myGdpHandle);

    // Builds the lookup table matching attributes to the local variables.
    setupLocalVars();

    // Here we determine which groups we have to work on.  We only
    //  handle point groups.
    if (error() < UT_ERROR_ABORT && cookInputGroups(context) < UT_ERROR_ABORT)
    {
        UT_AutoInterrupt progress("Flattening Points");

        FOR_ALL_OPT_GROUP_POINTS(gdp, myGroup, ppt)
        {
            // Check if user requested abort
            if (progress.wasInterrupted())
                break;

            // This sets the current point that is beint processed to
            // ppt.  This means that ppt will be used for any
            // local variable for any parameter evaluation that occurs
            // after this point.
            myCurPt[0] = ppt;
            dist = DIST(now); 
            if (!DIRPOP())
            {
                switch (ORIENT())
                {
                    case 0 : // XY Plane
                        normal.assign(0, 0, 1);
                        break;
                    case 1 : // YZ Plane
                        normal.assign(1, 0, 0);
                        break;
                    case 2 : // XZ Plane
                        normal.assign(0, 1, 0);
                        break;
                }
            }
            else
            {
                normal.assign(NX(now), NY(now), NZ(now));
                normal.normalize();
            }

            p.assign(ppt->getPos()(0), ppt->getPos()(1),
                     ppt->getPos()(2));
            p -= normal * (dot(normal, p) - dist);
            ppt->getPos()(0) = p.x();
            ppt->getPos()(1) = p.y();
            ppt->getPos()(2) = p.z();
        }
    }
    unlockInputs();

    // Clears out all the myCur* variables to ensure we have no
    // stray pointers.  This ensures that if the parameters are
    // evaluated outside of this cook path they don't try to read
    // possibly stale point pointers.
    resetLocalVarRefs();
    
    return error();
}

OP_ERROR
SOP_Flatten::cookMyGuide1(OP_Context &context)
{
    float               now;
    UT_BoundingBox      bbox;
    UT_Matrix3          mat3;
    UT_Matrix4          xform;
    float               dist, nx = 0, ny = 0, nz = 1;
    float               cx, cy, cz;
    float               sx, sy, sz;
    float               size;
    const int           divs = 5;
    UT_Vector3          zaxis(0, 0, 1);

    if (lockInputs(context) >= UT_ERROR_ABORT) return error();

    now = context.getTime();

    myGuide1->clearAndDestroy();

    dist = DIST(now);
    if (!DIRPOP())
    {
        switch (ORIENT())
        {
            case 0 : // XY Plane
                nx = 0; ny = 0; nz = 1; 
                break;
            case 1 : // YZ Plane
                nx = 1; ny = 0; nz = 0; 
                break;
            case 2 : // XZ Plane
                nx = 0; ny = 1; nz = 0; 
                break;
        }
    }
    else
    {
        nx = NX(now); ny = NY(now); nz = NZ(now);
    }

    if (error() < UT_ERROR_ABORT)
    {
        UT_Vector3 normal(nx, ny, nz);
        normal.normalize();

        inputGeo(0, context)->getBBox(&bbox);

        sx = bbox.sizeX();
        sy = bbox.sizeY();
        sz = bbox.sizeZ();
        size = sqrtf(sx*sx + sy*sy + sz*sz);

        cx = normal.x() * dist;
        cy = normal.y() * dist;
        cz = normal.z() * dist;

        myGuide1->meshGrid(divs, divs, size, size);

        mat3.dihedral(zaxis, normal);
        xform = mat3;
        xform.translate(cx, cy, cz);

        myGuide1->transform(xform);
     }

     unlockInputs();
     return error();
}

const char *
SOP_Flatten::inputLabel(unsigned) const
{
    return "Geometry to Flatten";
}

Generated on Mon Jan 28 00:26:25 2013 for HDK by  doxygen 1.5.9