SOP/SOP_HDKObject.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.
 *
 *----------------------------------------------------------------------------
 * This is an example of an implementation of the Object Merge SOP to
 * demonstrate using DYNAMIC_PATHs, MULTIPARMS, and proper cooking of other
 * SOPs.
 */

#include <UT/UT_DSOVersion.h>
#include <UT/UT_DirUtil.h>
#include <GU/GU_Detail.h>
#include <PRM/PRM_Include.h>
#include <PRM/PRM_SpareData.h>
#include <PRM/PRM_Parm.h>
#include <OP/OP_Director.h>
#include <OP/OP_Operator.h>
#include <OP/OP_OperatorTable.h>

#include "SOP_HDKObject.h"

using namespace HDK_Sample;

// Define the new sop operator...
void
newSopOperator(OP_OperatorTable *table)
{
    table->addOperator(new OP_Operator("proto_objectmerge",
                                        "HDK Object Merge",
                                         SOP_HDKObject::myConstructor,
                                         SOP_HDKObject::myTemplateList,
                                         0, 0, 0, OP_FLAG_GENERATOR));
}

static PRM_Name         objnames[] =
{
    PRM_Name("numobj",  "Number of Objects"),
    PRM_Name("objpath#","Object #"),
    PRM_Name("xformpath","Transform Object"),
    PRM_Name("enable#", "Enable Merge #"),
    PRM_Name(0)
};

static PRM_Template     theObjectTemplates[] = {
    PRM_Template(PRM_TOGGLE, 1, &objnames[3], PRMoneDefaults),
    PRM_Template(PRM_STRING,    PRM_TYPE_DYNAMIC_PATH, 1,
                 &objnames[1], 0, 0,
                 0, 0, &PRM_SpareData::sopPath),
    PRM_Template()
};

PRM_Template
SOP_HDKObject::myTemplateList[] = 
{
    PRM_Template(PRM_MULTITYPE_LIST, theObjectTemplates, 2, &objnames[0],
                    PRMoneDefaults),
    PRM_Template(PRM_STRING, PRM_TYPE_DYNAMIC_PATH, 1, &objnames[2],
                0, 0, 0, 0, &PRM_SpareData::objPath),
    PRM_Template()
};

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


SOP_HDKObject::SOP_HDKObject(OP_Network *net, const char *name, 
                       OP_Operator *entry) : SOP_Node(net, name, entry) {}
SOP_HDKObject::~SOP_HDKObject() {}

unsigned
SOP_HDKObject::disableParms()
{
    UT_String    objname;
    unsigned     changed;
    int          i, n;

    changed = 0;

    n = NUMOBJ();
    for (i = 1; i <= n; i++)
    {
        changed += enableParmInst(objnames[1].getToken(), &i, ENABLEMERGE(i));
    }

    return changed;
}

int
SOP_HDKObject::getDandROpsEqual()
{
    int         objindex, numobj;
    UT_String   objname;
    OP_Network *objptr;

    // don't do anything if we're locked
    if( flags().getHardLocked() )
        return 1;

    numobj = NUMOBJ();

    // Determine if any of our SOPs are evil.
    for (objindex = 1; objindex <= numobj; objindex++)
    {
        if (!ENABLEMERGE(objindex))             // Ignore disabled ones.
            continue;
        
        SOPPATH(objname, objindex, 0.0f);
        
        objptr = (OP_Network *) findNode((const char *) objname);
        if (!objptr)
            continue;

        // Self-referential nodes are assumed to have equal DandR ops,
        // as it doesn't matter as they will be ignored and flagged
        // as errors during cook anyways.
        if (objptr == this)
        {
            continue;
        }

        if (!objptr->getDandROpsEqual())
        {
            return 0;
        }
    }
    // None of our interests was unequal, thus we are equal!
    return 1;
}

OP_ERROR
SOP_HDKObject::cookMySop(OP_Context &context)
{
    GU_Detail           *cookedgdp;
    GB_PrimitiveGroup   *xform_primgroup = 0;
    GB_PointGroup       *xform_ptgroup = 0;
    OP_Network          *xformobjptr;
    OP_Network          *objptr;
    SOP_Node            *sopptr;
    GU_Detail            blank_gdp;
    int                  objindex;
    int                  numobj;
    int                  firstmerge = 1;
    UT_String            objname, sopname;
    double               t = context.getTime();

    // These dude's monitor where our offsets in each merge is in the geometry
    int                  start_pt = 0, end_pt = 0;
    int                  start_prim = 0, end_prim = 0;

    gdp->copy(blank_gdp, GB_COPY_START);

    // Get our xform object, if any.
    XFORMPATH(objname, t);
    xformobjptr = (OP_Network *) findNode(objname);
    if (xformobjptr && xformobjptr->getOpTypeID() == SOP_OPTYPE_ID)
    {
        // The user pointed to the SOP.  We silently promote it to the
        // containing object.  This allows the intuitive "." to be used
        // for the path to transform relative to our own op (rather than
        // having to track up an arbitrary number of paths)
        xformobjptr = xformobjptr->getCreator();
    }

    // We must explicitly cast down as OBJ_Node * is unknown here.
    // We also do the cast here instead of in findNode as we want
    // to allow people to reference SOPs.
    xformobjptr = (OP_Network *)CAST_OBJNODE(xformobjptr);

    if (!xformobjptr && objname.isstring())
    {
        // We didn't get an xform object, but they had something typed,
        // badmerge.
        addError(SOP_BAD_SOP_MERGED, objname);
    }
    
    numobj = NUMOBJ();
    
    int         cookrender;

    // Peek as to whether we are a render cook or not.
    cookrender = getCreator()->isCookingRender();
    
    for (objindex = 1; objindex <= numobj; objindex++)
    {
        int     gotsopbyflag, savecookrender;
        
        if (!ENABLEMERGE(objindex))             // Ignore disabled ones.
            continue;

        gotsopbyflag = 0;

        SOPPATH(sopname, objindex, t);

        if (!sopname.isstring())
        {
            continue;                           // Blank means ignore.
        }
        
        sopptr = getSOPNode(sopname, 1);        // We want extra inputs.

        if (sopptr == this)
        {
            // Self-reference.  Special brand of evil.
            addWarning(SOP_ERR_SELFMERGE);
            continue;
        }

        if (!sopptr)
        {
            // Illegal merge.  Just warn so we don't abort everything.
            addWarning(SOP_BAD_SOP_MERGED, sopname);
            continue;
        }

        // Get the creator, which is our objptr.
        objptr = sopptr->getCreator();

        // Change over so any subnet evaluation will properly track...
        savecookrender = objptr->isCookingRender();
        objptr->setCookingRender(cookrender);

        // Actually cook...
        cookedgdp = (GU_Detail *)sopptr->getCookedGeo(context);
        
        // Restore the cooking render state.
        objptr->setCookingRender(savecookrender);

        if (!cookedgdp)
        {
            // Something went wrong with the cooking. Warn the hapless user.
            addWarning(SOP_BAD_SOP_MERGED, sopname);
            continue;
        }

        // Now add the extra inputs...
        addExtraInput(objptr, OP_INTEREST_DATA);
        // The sop extra inputs were set by the getSOPNode

        gdp->copy(*cookedgdp, GB_COPY_ADD);

        // Apply the transform.  
        if (xformobjptr)
        {
            UT_Matrix4           xform, xform2;

            if (firstmerge)
            {
                firstmerge = 0;

                // The first object we merge we can just do a full gdp transform
                // rather than building the subgroup.
                if (!objptr->getWorldTransform(xform, context))
                    addTransformError(*objptr, "world");
                if (!xformobjptr->getIWorldTransform(xform2, context))
                    addTransformError(*xformobjptr, "inverse world");
                xform *= xform2;

                // We want to initialize our start/end arrays so
                // we don't retransform these guys with the next chunk.
                end_prim = cookedgdp->primitives().entries();
                end_pt = cookedgdp->points().entries();
                start_prim = cookedgdp->primitives().entries();
                start_pt = cookedgdp->points().entries();

                gdp->transform(xform, xform_primgroup, xform_ptgroup);
            }
            else
            {
                if (!xform_primgroup)
                    xform_primgroup = gdp->newPrimitiveGroup("__objxform__", 1);

                if (!xform_ptgroup)
                    xform_ptgroup = gdp->newPointGroup("__objptxform__", 1);

                // clear the previous entries

                xform_primgroup->clearEntries();
                xform_ptgroup->clearEntries();

                // set the new entries

                end_prim += cookedgdp->primitives().entries();
                end_pt += cookedgdp->points().entries();

                while (start_prim<end_prim)
                    xform_primgroup->add(start_prim++);

                while (start_pt<end_pt)
                    xform_ptgroup->add(start_pt++);

                if (!objptr->getWorldTransform(xform, context))
                    addTransformError(*objptr, "world");
                if (!xformobjptr->getIWorldTransform(xform2, context))
                    addTransformError(*xformobjptr, "inverse world");
                xform *= xform2;

                gdp->transform(xform, xform_primgroup, xform_ptgroup);
            }
        }
    }

    if (xformobjptr)
    {
        addExtraInput(xformobjptr, OP_INTEREST_DATA);
    }

    // Finish & clean up the copy procedure.
    gdp->copy(blank_gdp, GB_COPY_END);

    if (error() < UT_ERROR_ABORT)
        select(GU_SPrimitive);

    return error();
}

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