HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
APEX/apex_external_test.C
/*
* Copyright (c) 2025
* 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.
*
*/
// UT_DSOVersion.h is needed to mark the shared library as compiled with the
// HDK. Houdini will refuse to load shared libraries built without it.
#include <GU/GU_Detail.h>
#include <GA/GA_Names.h>
using namespace apex;
class MyCallback
: public APEX_GenericFunction<MyCallback, Float, const Matrix4>
{
public:
static constexpr UT_StringLit funcname = "hdk::MyCallbackHDK";
static constexpr const char *argnames[] = {"det", "matrix"};
void operator()(Float &det, const Matrix4 &m) const
{
det = m.determinant();
}
};
class SetPointTransforms : public APEX_GenericFunctionRunData<
SetPointTransforms,
Geometry,
const VariadicArg<Matrix4>>
{
public:
static constexpr UT_StringLit funcname = "hdk::SetPointTransformsHDK";
static constexpr const char *argnames[] = {"*geo", "transforms"};
struct RunData
{
RunData() = default;
~RunData() = default;
RunData(const RunData &other) = delete;
const RunData &operator=(const RunData &other) = delete;
GA_DataId myNameDataId = GA_INVALID_DATAID;
UT_Array<exint> myLookupHandle;
bool needsRecache(GU_Detail *gdp)
{
if (name_h.isInvalid())
{
if (myNameDataId == GA_INVALID_DATAID)
return false;
myNameDataId = GA_INVALID_DATAID;
return true;
}
return name_h.getDataId() != myNameDataId;
}
void cacheLookup(GU_Detail *gdp, const UT_StringArray &names)
{
myLookupHandle.clear();
myLookupHandle.setSize(exint(gdp->getNumPointOffsets()));
myLookupHandle.constant(-1);
if (name_h.isValid())
{
exint idx = 0;
for (auto &pt_name : names)
{
auto ptoff = lut->getStringOffset(pt_name);
if (ptoff != GA_INVALID_OFFSET)
myLookupHandle[ptoff] = idx;
idx++;
}
myNameDataId = name_h->getDataId();
}
else
myNameDataId = GA_INVALID_DATAID;
}
};
APEX_DEF_TYPE_RUNDATA(SetPointTransforms)
void operator()(RunData &rundata, Geometry &geo, const VariadicArg<Matrix4> &transforms) const
{
GU_Detail *gdp = geo.gdp();
GA_RWHandleM3D transform_h(gdp, GA_ATTRIB_POINT, "transform");
if (transform_h.isInvalid())
return;
if (rundata.needsRecache(gdp))
rundata.cacheLookup(gdp, transforms.names());
if (rundata.myNameDataId == GA_INVALID_DATAID
|| rundata.myLookupHandle.size() < exint(gdp->getNumPointOffsets()))
return;
auto body = [&](const GA_Range &r)
{
for (auto ri = r.begin(); ri != r.end(); ++ri)
{
GA_Offset ptoff = *ri;
exint idx = rundata.myLookupHandle[ptoff];
if (idx < 0)
continue;
const Matrix4 &xform = *transforms[idx];
Vector3 pos;
xform.getTranslates(pos);
gdp->setPos3(ptoff, pos);
Matrix3 m3 = Matrix3(xform);
transform_h.set(ptoff, m3);
}
};
transform_h->bumpDataId();
gdp->getP()->bumpDataId();
}
};
class GetPointTransforms : public APEX_GenericFunctionRunData<
GetPointTransforms,
VariadicArg<Matrix4>,
const Geometry>
{
public:
static constexpr UT_StringLit funcname = "hdk::GetPointTransformsHDK";
static constexpr const char *argnames[] = {"transforms", "geo"};
struct RunData
{
GA_OffsetArray myPointOffsets;
GA_DataId myNameDataId = GA_INVALID_DATAID;
bool needsRecache(const GU_Detail *gdp)
{
if (name_h.isInvalid())
{
if (myNameDataId == GA_INVALID_DATAID)
return false;
myNameDataId = GA_INVALID_DATAID;
return true;
}
return name_h.getDataId() != myNameDataId;
}
void cacheLookup(const GU_Detail *gdp, const UT_StringArray &names)
{
myPointOffsets.clear();
myPointOffsets.setCapacity(names.size());
if (name_h.isValid())
{
for (auto &pt_name : names)
{
auto ptoff = lut->getStringOffset(pt_name);
myPointOffsets.append(ptoff);
}
myNameDataId = name_h->getDataId();
}
else
myNameDataId = GA_INVALID_DATAID;
}
};
APEX_DEF_TYPE_RUNDATA(GetPointTransforms)
void operator()(
RunData &rundata,
VariadicArg<Matrix4> &transforms,
const Geometry &geo) const
{
const GU_Detail *gdp = &*geo;
if (rundata.needsRecache(gdp))
rundata.cacheLookup(gdp, transforms.names());
int idx = 0;
GA_ROHandleM3D h_transform(gdp, GA_ATTRIB_POINT, "transform");
if (h_transform.isInvalid())
return;
for (auto &ptoff : rundata.myPointOffsets)
{
if (ptoff == GA_INVALID_OFFSET)
{
++idx;
continue;
}
Vector3 P = gdp->getPos3(ptoff);
Matrix3 m3 = h_transform.get(ptoff);
Matrix4 m = Matrix4(m3);
UT_ASSERT(idx < transforms.size());
*transforms[idx++] = m;
}
}
};
{
static MyCallback my_callback;
static SetPointTransforms set_point_transforms;
static GetPointTransforms get_point_transforms;
reg.addCallback(&my_callback);
reg.addCallback(&set_point_transforms);
reg.addCallback(&get_point_transforms);
}