#include <UT/UT_DSOVersion.h>
#include <GEO/GEO_AttributeHandle.h>
#include <GU/GU_Detail.h>
#include <PRM/PRM_Include.h>
#include <OP/OP_Channels.h>
#include <OP/OP_Operator.h>
#include <OP/OP_Director.h>
#include <OP/OP_OperatorTable.h>
#include <OP/OP_Caller.h>
#include <UT/UT_Interrupt.h>
#include <SHOP/SHOP_Node.h>
#include "SOP_PrimVOP.h"
#include <VEX/VEX_Error.h>
#include <CVEX/CVEX_Context.h>
#include <CVEX/CVEX_Value.h>
#include <PRM/PRM_DialogScript.h>
#include <PRM/PRM_Include.h>
#include <PRM/PRM_SpareData.h>
#include <VOP/VOP_CodeCompilerArgs.h>
#include <VOP/VOP_LanguageContextTypeList.h>
using namespace HDK_Sample;
void
newSopOperator(OP_OperatorTable *table)
{
table->addOperator(new OP_Operator("hdk_primvop",
"PrimVOP",
SOP_PrimVOP::myConstructor,
SOP_PrimVOP::myTemplateList,
1,
1,
VOP_CodeGenerator::theLocalVariables));
}
static PRM_Default scriptDefault(PRM_DialogSopVex, "null");
static UT_String theVexError;
static UT_String theVexWarning;
static void
vexErrorHandler(const char *msg)
{
if (!theVexError.isstring())
theVexError = "VEX Error:";
theVexError += "\n";
theVexError += msg;
theVexError.harden();
}
static void
vexWarningHandler(const char *msg)
{
if (!theVexWarning.isstring())
theVexWarning = "VEX_Warning:";
theVexWarning += "\n";
theVexWarning += msg;
theVexWarning.harden();
}
static PRM_Name names[] = {
PRM_Name("script", "Script"),
PRM_Name("clear", "Re-load VEX Functions"),
PRM_Name("autobind", "Autobind by Name"),
PRM_Name("bindings", "Number of Bindings"),
PRM_Name("shoppath", "Shop Path"),
PRM_Name("vexsrc", "Vex Source"),
};
static PRM_Name vexsrcNames[] =
{
PRM_Name("myself", "Myself"),
PRM_Name("shop", "Shop"),
PRM_Name("script", "Script"),
PRM_Name(0)
};
static PRM_ChoiceList vexsrcMenu(PRM_CHOICELIST_SINGLE, vexsrcNames);
PRM_Template
SOP_PrimVOP::myTemplateList[]=
{
PRM_Template(PRM_ORD, PRM_Template::PRM_EXPORT_MAX, 1,
&names[5], 0, &vexsrcMenu),
PRM_Template(PRM_STRING, PRM_TYPE_DYNAMIC_PATH,
PRM_Template::PRM_EXPORT_MAX, 1,
&names[4], 0, 0, 0, 0,
&PRM_SpareData::shopCVEX),
PRM_Template(PRM_COMMAND, PRM_Template::PRM_EXPORT_TBX,
1, &names[0], &scriptDefault),
PRM_Template(PRM_STRING, 1, &VOP_CodeGenerator::theVopCompilerName,
&VOP_CodeGenerator::theVopCompilerVexDefault),
PRM_Template(PRM_CALLBACK, 1, &VOP_CodeGenerator::theVopForceCompileName,
0, 0, 0, VOP_CodeGenerator::forceCompile),
PRM_Template()
};
OP_Node *
SOP_PrimVOP::myConstructor(OP_Network *net,const char *name,OP_Operator *entry)
{
return new SOP_PrimVOP(net, name, entry);
}
SOP_PrimVOP::SOP_PrimVOP(OP_Network *net, const char *name, OP_Operator *entry)
: SOP_Node(net, name, entry) ,
myCodeGenerator(this, new VOP_LanguageContextTypeList(
VOP_LANGUAGE_VEX, VOPconvertToContextType( VEX_CVEX_CONTEXT )),
1, 1)
{
setOperatorTable(getOperatorTable(VOP_TABLE_NAME));
}
SOP_PrimVOP::~SOP_PrimVOP()
{
}
unsigned
SOP_PrimVOP::disableParms()
{
int changed = 0;
return changed;
}
OP_ERROR
SOP_PrimVOP::cookMySop(OP_Context &context)
{
double t = context.getTime();
UT_String script;
UT_Interrupt *boss = UTgetInterrupt();
if (lockInputs(context) >= UT_ERROR_ABORT)
return error();
duplicateSource(0, context);
buildScript(script, context.getTime());
VEXerrorHandler prevHandler;
VEXerrorHandler prevWHandler;
prevHandler = VEXgetErrorHandler();
prevWHandler = VEXgetWarningHandler();
theVexError = 0;
theVexWarning = 0;
VEXsetErrorHandler(vexErrorHandler);
VEXsetWarningHandler(vexWarningHandler);
if (!script.isstring())
{
script = "null";
addWarning(SOP_VEX_ERROR,
"No script specified. Using null.");
}
int argc;
char *argv[4096];
argc = script.parse(argv, 4096);
if (!theVexError.isstring())
{
int id;
if (boss->opStart("Executing Volume VEX", 0, 0, &id))
{
OP_Caller caller(this);
executeVex(argc, argv, t, caller);
}
boss->opEnd(id);
}
VEXsetErrorHandler(prevHandler);
VEXsetErrorHandler(prevWHandler);
if (theVexError.isstring())
addError(SOP_VEX_ERROR, (const char *)theVexError);
if (theVexWarning.isstring())
addWarning(SOP_VEX_ERROR, (const char *)theVexWarning);
unlockInputs();
return error();
}
void
SOP_PrimVOP::executeVex(int argc, char **argv,
fpreal t,
OP_Caller &opcaller)
{
CVEX_Context context;
int chunksize, n;
int stid = UTgetSTID();
CH_Manager *chman = OPgetDirector()->getChannelManager();
CH_Collection *pushed = chman->getEvalCollection(stid);
chman->setEvalCollection(getChannels(), stid);
context.setOpCaller(&opcaller);
chunksize = 16384;
int *primid = new int[chunksize];
GEO_Primitive *prim;
n = 0;
FOR_ALL_PRIMITIVES(gdp, prim)
{
primid[n] = prim->getNum();
n++;
if (n >= chunksize)
{
processVexBlock(context, argc, argv, primid, n, t);
n = 0;
}
}
if (n)
processVexBlock(context, argc, argv, primid, n, t);
delete [] primid;
chman->setEvalCollection(pushed, stid);
}
namespace HDK_Sample {
class sop_bindparms
{
public:
const static int NUM_BUFFERS = 2;
const static int INPUT_BUFFER = 0;
const static int OUTPUT_BUFFER = 1;
sop_bindparms()
{
clear();
}
sop_bindparms(const char *name, CVEX_Type type)
{
clear();
myName.harden(name);
myType = type;
}
sop_bindparms(const sop_bindparms &src)
{
clear();
*this = src;
}
void clear()
{
myType = CVEX_TYPE_INVALID;
for (int i = 0; i < NUM_BUFFERS; i++)
{
myBuffer[i] = 0;
myBufLen[i] = 0;
}
}
sop_bindparms &operator=(const sop_bindparms &src)
{
myName.harden(src.name());
myType = src.type();
for (int i = 0; i < NUM_BUFFERS; i++)
{
delete [] myBuffer[i];
myBufLen[i] = src.myBufLen[i];
myBuffer[i] = 0;
if (src.buffer(i) && src.myBufLen[i])
{
myBuffer[i] = new char[myBufLen[i]];
memcpy(myBuffer[i], src.buffer(i), src.myBufLen[i]);
}
}
return *this;
}
~sop_bindparms()
{
for (int i = 0; i < NUM_BUFFERS; i++)
{
delete [] myBuffer[i];
}
}
void allocateBuffer(int bufnum, int n)
{
delete [] myBuffer[bufnum];
switch (myType)
{
case CVEX_TYPE_INTEGER:
myBuffer[bufnum] = new char [sizeof(int) * n];
break;
case CVEX_TYPE_FLOAT:
myBuffer[bufnum] = new char [sizeof(float) * n];
break;
case CVEX_TYPE_VECTOR3:
myBuffer[bufnum] = new char [3*sizeof(float) * n];
break;
case CVEX_TYPE_VECTOR4:
myBuffer[bufnum] = new char [4*sizeof(float) * n];
break;
default:
UT_ASSERT(0);
break;
}
myBufLen[bufnum] = n;
}
void marshallIntoBuffer(int bufnum, GU_Detail *gdp, int *primid, int n)
{
GEO_AttributeHandle gah;
int i;
allocateBuffer(bufnum, n);
gah = gdp->getPrimAttribute(name());
if (gah.isAttributeValid())
{
for (i = 0; i < n; i++)
{
gah.setElement(gdp->primitives()(primid[i]));
switch (myType)
{
case CVEX_TYPE_INTEGER:
((int *)myBuffer[bufnum])[i] = gah.getI();
break;
case CVEX_TYPE_FLOAT:
((float *)myBuffer[bufnum])[i] = gah.getF();
break;
case CVEX_TYPE_VECTOR3:
((UT_Vector3 *)myBuffer[bufnum])[i] = gah.getV3();
break;
case CVEX_TYPE_VECTOR4:
((UT_Vector4 *)myBuffer[bufnum])[i] = gah.getV4();
break;
default:
UT_ASSERT(0);
break;
}
}
}
}
void marshallDataToGdp(int bufnum, GU_Detail *gdp, int *primid, int n, int inc)
{
GEO_AttributeHandle gah;
int i;
int src = 0;
gah = gdp->getPrimAttribute(name());
if (gah.isAttributeValid())
{
for (i = 0; i < n; i++)
{
gah.setElement(gdp->primitives()(primid[i]));
switch (myType)
{
case CVEX_TYPE_INTEGER:
gah.setI(((int *)myBuffer[bufnum])[src]);
break;
case CVEX_TYPE_FLOAT:
gah.setF(((float *)myBuffer[bufnum])[src]);
break;
case CVEX_TYPE_VECTOR3:
gah.setV3(((UT_Vector3 *)myBuffer[bufnum])[src]);
break;
case CVEX_TYPE_VECTOR4:
gah.setV4(((UT_Vector4 *)myBuffer[bufnum])[src]);
break;
default:
UT_ASSERT(0);
break;
}
src += inc;
}
}
}
const char *name() const { return myName; };
CVEX_Type type() const { return myType; }
const char *buffer(int bufnum) const { return myBuffer[bufnum]; }
char *buffer(int bufnum) { return myBuffer[bufnum]; }
private:
UT_String myName;
CVEX_Type myType;
char *myBuffer[NUM_BUFFERS];
int myBufLen[NUM_BUFFERS];
};
}
void
SOP_PrimVOP::processVexBlock(CVEX_Context &context,
int argc, char **argv,
int *primid, int n,
fpreal t)
{
CVEX_Value *var;
GB_Attribute *attrib;
UT_Vector3 *P = 0;
int i, j;
UT_RefArray<sop_bindparms> bindlist;
context.addInput("primid", CVEX_TYPE_INTEGER, primid, n);
context.addInput("P",
CVEX_TYPE_VECTOR3,
true);
context.addInput("Time", CVEX_TYPE_FLOAT, false);
context.addInput("Timeinc", CVEX_TYPE_FLOAT, false);
context.addInput("Frame", CVEX_TYPE_FLOAT, false);
for (attrib = gdp->primitiveAttribs().getHead();
attrib;
attrib = (GB_Attribute *) attrib->next())
{
CVEX_Type type = CVEX_TYPE_INVALID;
switch (attrib->getType())
{
case GB_ATTRIB_FLOAT:
if (attrib->getSize() < sizeof(float)*3)
type = CVEX_TYPE_FLOAT;
else if (attrib->getSize() < sizeof(float)*4)
type = CVEX_TYPE_VECTOR3;
else
type = CVEX_TYPE_VECTOR4;
break;
case GB_ATTRIB_INT:
type = CVEX_TYPE_INTEGER;
break;
case GB_ATTRIB_VECTOR:
type = CVEX_TYPE_VECTOR3;
break;
default:
type = CVEX_TYPE_INVALID;
break;
}
if (type == CVEX_TYPE_INVALID)
continue;
context.addInput(attrib->getName(), type, true);
bindlist.append( sop_bindparms(attrib->getName(),
type) );
}
context.setTime(t);
context.load(argc, argv,
0
);
if (theVexError.isstring())
goto error;
var = context.findInput("P", CVEX_TYPE_VECTOR3);
if (var)
{
P = new UT_Vector3[n];
for (i = 0; i < n; i++)
{
P[i] = gdp->primitives()(primid[i])->baryCenter();
}
var->setData(P, n);
}
for (j = 0; j < bindlist.entries(); j++)
{
var = context.findInput(bindlist(j).name(), bindlist(j).type());
if (var)
{
bindlist(j).marshallIntoBuffer(sop_bindparms::INPUT_BUFFER,
gdp, primid, n);
var->setData(bindlist(j).buffer(sop_bindparms::INPUT_BUFFER), n);
}
if (var = context.findOutput(bindlist(j).name(), bindlist(j).type()))
{
bindlist(j).allocateBuffer(sop_bindparms::OUTPUT_BUFFER, n);
if (var->isVarying())
var->setData(bindlist(j).buffer(sop_bindparms::OUTPUT_BUFFER), n);
else
var->setData(bindlist(j).buffer(sop_bindparms::OUTPUT_BUFFER), 1);
}
}
fpreal32 curtime, curtimeinc, curframe;
var = context.findInput("Time", CVEX_TYPE_FLOAT);
if (var)
{
OP_Node::flags().timeDep = true;
curtime = t;
var->setData(&curtime, 1);
}
var = context.findInput("Timeinc", CVEX_TYPE_FLOAT);
if (var)
{
OP_Node::flags().timeDep = true;
curtimeinc = 1.0f/OPgetDirector()->getChannelManager()->getSamplesPerSec();
var->setData(&curtimeinc, 1);
}
var = context.findInput("Frame", CVEX_TYPE_FLOAT);
if (var)
{
OP_Node::flags().timeDep = true;
curframe = OPgetDirector()->getChannelManager()->getSample(t);
var->setData(&curframe, 1);
}
context.clearFlag(0x1000);
context.run(n, true);
if (context.getFlag(0x1000))
OP_Node::flags().timeDep = 1;
for (j = 0; j < bindlist.entries(); j++)
{
var = context.findOutput(bindlist(j).name(), bindlist(j).type());
if (var)
{
bindlist(j).marshallDataToGdp(sop_bindparms::OUTPUT_BUFFER, gdp, primid, n, (var->isVarying() ? 1 : 0));
}
}
error:
delete [] P;
}
void
SOP_PrimVOP::buildScript(UT_String &script, float t)
{
UT_String shoppath;
int vexsrc = VEXSRC(t);
script = "";
switch (vexsrc)
{
case 0:
{
getFullPath(shoppath);
script = "op:";
script += shoppath;
buildVexCommand(script, getSpareParmTemplates(), t);
break;
}
case 2:
SCRIPT(script, t);
break;
case 1:
{
SHOPPATH(shoppath, t);
SHOP_Node *shop;
shop = findSHOPNode(shoppath);
if (shop)
{
shop->buildVexCommand(script, shop->getSpareParmTemplates(), t);
addExtraInput(shop, OP_INTEREST_DATA);
shop->buildShaderString(script, t, 0, 0, 0, SHOP_CVEX);
}
break;
}
}
}
void
SOP_PrimVOP::getVariableString(int index, UT_String &value, int thread)
{
if( !myCodeGenerator.getVariableString(index, value) )
SOP_Node::getVariableString(index, value, thread);
}
OP_OperatorFilter *
SOP_PrimVOP::getOperatorFilter()
{
return myCodeGenerator.getOperatorFilter();
}
VOP_CodeGenerator *
SOP_PrimVOP::getVopCodeGenerator()
{
return &myCodeGenerator;
}
const char *
SOP_PrimVOP::getChildType() const
{
return VOP_OPTYPE_NAME;
}
OP_OpTypeId
SOP_PrimVOP::getChildTypeID() const
{
return VOP_OPTYPE_ID;
}
void
SOP_PrimVOP::opChanged(OP_EventType reason, void *data)
{
SOP_Node::opChanged(reason, data);
myCodeGenerator.ownerChanged(reason, data);
}
void
SOP_PrimVOP::finishedLoadingNetwork(bool is_child_call)
{
myCodeGenerator.ownerFinishedLoadingNetwork();
SOP_Node::finishedLoadingNetwork(is_child_call);
}
void
SOP_PrimVOP::addNode(OP_Node *node, int notify, int explicitly)
{
myCodeGenerator.beforeAddNode(node);
SOP_Node::addNode(node, notify, explicitly);
myCodeGenerator.afterAddNode(node);
}
void
SOP_PrimVOP::getNodeSpecificInfoText(OP_Context &context,
int verbose,
UT_WorkBuffer &text)
{
SOP_Node::getNodeSpecificInfoText(context, verbose, text);
myCodeGenerator.appendCompileErrors(text, verbose, false);
}