HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
VOP/VOP_Switch.C
/*
* Copyright (c) 2024
* 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.
*/
#include <stdlib.h>
#include <OP/OP_Input.h>
#include "VOP_Switch.h"
using namespace HDK_Sample;
void
{
// Create a new VOP_Operator which describes the operator we are
// building. The parameters to this function are similar to the
// OP_Operator constructor except for the vopnet mask, and the
// last parameter, which specifies the number of outputs from
// this operator.
op = new VOP_Operator("hdkswitch", // internal name
"HDK Switch", // UI name
VOP_Switch::myConstructor, // How to create one
VOP_Switch::myTemplateList, // parm definitions
0, // Min # of inputs
VOP_VARIABLE_INOUT_MAX, // Max # of inputs
"*", // vopnet mask
0, // Local variables
OP_FLAG_UNORDERED, // Special flags
1); // # of outputs
table->addOperator(op);
}
static const char *theInputRoot = "input";
static const char *theOutputName = "result";
static PRM_Name theSwitcherName("switcher", "Switcher Index");
static PRM_Name theOutOfBoundsName("outofbounds",
"Out Of Bounds Behavior");
static PRM_Name theOutOfBoundsChoices[] =
{
PRM_Name("last", "Output Last Input Value"),
PRM_Name("zero", "Output Zero"),
};
enum
{
};
static PRM_ChoiceList theOutOfBoundsMenu(PRM_CHOICELIST_SINGLE,
theOutOfBoundsChoices);
{
return new VOP_Switch(net, name, entry);
}
{
PRM_Template(PRM_INT, 1, &theSwitcherName, PRMzeroDefaults),
PRM_Template(PRM_ORD, 1, &theOutOfBoundsName, PRMzeroDefaults,
&theOutOfBoundsMenu),
// List terminator
};
VOP_Switch::VOP_Switch(OP_Network *parent, const char *name, OP_Operator *entry)
: VOP_Node(parent, name, entry)
{
}
VOP_Switch::~VOP_Switch()
{
}
bool
{
bool changed = VOP_Node::updateParmsFlags();
// Disable the switcher index parameter if the first input is connected
// because the input value will be used instead of the parameter value.
changed |= enableParm(theSwitcherName.getToken(), getInput(0) == 0);
return changed;
}
void
{
// If there isn't at least one input connected after the switch index
// input, then this node generates no code.
{
UT_String inputName, outputName;
int outofbounds = OUTOFBOUNDS();
int i, j, next, first = 1;
// The code generated by this node is a series of if/then/else
// statements. Each inputs has a chance to be assigned to the
// output value depending on the switch index value. The switch
// index may be defined by an input or by a parameter value if no
// input is connected.
getOutputName(outputName, 0);
for( i = getConnectedInputIndex(0), j = 0; i >= 0; i = next, j++ )
{
// For each connected input after the first one (the switch index
// input), we output code like:
// if( $switcher == 0 )
// $result = $input1;
// else if( $switcher == 1 )
// $result = $input2;
// else
// $result = $input3;
//
// Note that the inputs and output names are preceded by "$" so
// the code generator will use unique variables names, or the
// correct input variable names matching the connected inputs.
// The $switcher value will be expanded by the code generator to
// use either the matching input variable name or the switcher
// parameter value (if no input is connected).
getInputName(inputName, i);
if( next >= 0 || outofbounds == VOP_SWITCH_OOB_ZERO )
{
if( !first )
os << "else ";
os << "if( $" << theSwitcherName.getToken();
os << " == " << j << " )\n";
os << " ";
}
else if( !first )
{
os << "else\n" << " ";
}
os << "$" << outputName << " = $" << inputName << ";\n";
first = 0;
}
// If we are asked to use the first input for out of bounds switch
// indices, we need to append a final else clause to use the first
// input value.
if( outofbounds == VOP_SWITCH_OOB_ZERO )
{
VOP_TypeInfo output_type_info;
UT_String const_val;
getOutputTypeInfo(output_type_info, 0);
myLanguage->getEmptyConstantCode(const_val, output_type_info);
if( !first )
os << "else\n" << " ";
os << "$" << outputName << " = ";
os << const_val.buffer() << ";\n";
}
codestr.harden(os.str().buffer());
}
}
const char *
VOP_Switch::inputLabel(unsigned idx) const
{
if (idx >= orderedInputs())
{
static UT_String theLabel;
char numstr[UT_NUMBUF];
// Inputs after the first one are just numbered starting at 1.
theLabel = "Input Number ";
UT_String::itoa(numstr, idx + 1 - orderedInputs());
theLabel += numstr;
return theLabel;
}
else
return theSwitcherName.getLabel();
}
const char *
VOP_Switch::outputLabel(unsigned idx) const
{
UT_ASSERT(idx == 0);
return "Chosen Value";
}
void
{
if (idx >= orderedInputs())
{
char numstr[UT_NUMBUF];
// Inputs after the first one are just numbered starting at 1.
in = theInputRoot;
UT_String::itoa(numstr, idx + 1 - orderedInputs());
in += numstr;
}
else
in = theSwitcherName.getToken();
}
int
{
int inputnum = -1;
// The switcher input is always first.
if( in == theSwitcherName.getToken() )
return 0;
// Use the numeric suffix on the input name to determine the input index.
if( !strncmp(in, theInputRoot, strlen(theInputRoot)) )
inputnum = ::atoi((const char *)in + strlen(theInputRoot));
return inputnum - 1 + orderedInputs();
}
void
{
// For any input past our ordered inputs, all input types are the
// same - whatever is plugged into the first variable input.
if( idx >= orderedInputs() )
// Helper method, which essentially invokes
// vop->getOutputTypeInfo( type_info, input->getNodeOutputIndex() );
// on the input vop node
else
}
void
VOP_VopTypeInfoArray &type_infos)
{
VOP_Node *vop;
OP_Input *input;
if( idx >= orderedInputs() )
{
// For any input past our ordered inputs, all input types are the
// same - whatever is plugged into the first variable input.
if( (input = getInputReference(orderedInputs(), 0)) )
{
vop = CAST_VOPNODE(input->getNode());
if( vop )
{
VOP_TypeInfo type_info;
vop->getOutputTypeInfo(type_info, input->getNodeOutputIndex());
type_infos.append(type_info);
}
}
}
else
{
type_infos.append(type_info);
}
}
void
{
UT_ASSERT(idx == 0);
name = theOutputName;
}
void
{
UT_ASSERT(idx == 0);
// The output data type is the same as the data type of the first
// input after the switch index.
}
unsigned
{
int max = nInputs();
// Make sure there is always exactly one unconnected unordered input
// visible.
if( max < orderedInputs() )
max = orderedInputs();
return max + 1;
}
unsigned
{
// The first input (corresponding to the switch index) must always be
// visible.
return 1;
}
int
{
return evalInt(theOutOfBoundsName, 0, 0.0f);
}