HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
karma_procedurals/BRAY_HdSphere.C
/*
* 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.
*
*----------------------------------------------------------------------------
* Karma Procedural Sphere Example
*/
#include "BRAY_HdSphere.h"
#include <UT/UT_Debug.h>
using namespace UT::Literal;
using namespace HDK_Sample;
namespace
{
// For now, replace point clouds with this primitive type
static constexpr UT_StringLit theToken("sphere");
static constexpr UT_StringLit theRadius("radius");
static constexpr UT_StringLit theColor("color");
static constexpr UT_StringLit theDisplayColor("displayColor");
// Simple static factory
static BRAY_HdSphere &
getFactory()
{
static BRAY_HdSphere theProc;
return theProc;
}
// Simple class to build out params
class ParamListBuilder
{
public:
using Params = BRAY_AttribList::Attrib;
ParamListBuilder()
{
Params parms[] = {
{
theRadius.asHolder(),
1,
false
},
{
theColor.asHolder(),
3,
false
},
};
myParms = UTmakeUnique<BRAY_AttribList>(parms, SYSarraySize(parms), false);
}
};
// The actual class that is produced by the factory
class CustomSphere final : public BRAY_Procedural
{
public:
CustomSphere()
, myDisplayColor(0, 0, 0)
, myRadius(1.f)
{
{ theRadius.asHolder(),
1,
false,
},
{ theColor.asHolder(),
3,
false
},
{ theDisplayColor.asHolder(),
3,
false
},
};
// This defines the primvars/attributes which can be bound to
// shader arguments. Note that we double up color and displayColor
myAttribs = BRAY_AttribList::findList(alist, 3, 0);
// Randomly generate the display color of the sphere
myDisplayColor = 0;
uint seed = SYSpointerHash(this);
while (myDisplayColor.maxComponent() < .7)
{
myDisplayColor = UT_Vector3(SYSfastRandom(seed),
SYSfastRandom(seed),
SYSfastRandom(seed));
}
}
/// Default destructor
~CustomSphere() override
{
}
/// Return the attribute list which can be bound to shader attributes
/// These are attributes/primvars that are provided by the procedural
/// for shading/rendering.
const BRAY_AttribList *attribList() const override
{
return myAttribs;
}
/// Return the bounding box for the object at the given time
void bounds(UT_BoundingBox &bounds, BRAYtime time) const override
{
bounds.initBounds(-myRadius, -myRadius, -myRadius);
bounds.enlargeBounds(myRadius, myRadius, myRadius);
}
/// The intersect function of the sphere with a 32-bit ray
HitPtr intersect(const Ray32 &ray) const override
{
return intersectSphere<fpreal32>(ray);
}
/// The intersect function of the sphere with a 64-bit ray
HitPtr intersect(const Ray64 &ray) const override
{
return intersectSphere<fpreal64>(ray);
}
/// Return the display color which is used for low-quality rendering
UT_Vector3 displayColor() const override
{
return myDisplayColor;
}
/// @{
/// A set of virtual functions used by Karma to pass parameters to
/// the procedural. This is implemented for int32, int64, fpreal32,
/// fpreal64, and UT_StringHolder. The list of possible parameters
/// for a procedural is given by it's factory's @c paramList() method.
void doSetParameter(
const UT_StringRef &key,
const int32 *vals,
int n) override
{
}
void doSetParameter(
const UT_StringRef &key,
const int64 *vals,
int n) override
{
}
void doSetParameter(
const UT_StringRef &key,
const fpreal32 *vals,
int n) override
{
if (key == theRadius.asRef())
{
// We are just expecting 1 value.
UT_ASSERT(n == 1);
myRadius = vals[0];
}
else if (key == theColor.asRef())
{
// If we have 1 value, r,g,b = val
// else the three components
// either way we need only 1 or 3 vals.
if (n == 1)
myDisplayColor = UT_Vector3(vals[0]);
else if (n >= 3)
myDisplayColor = UT_Vector3(vals[0], vals[1], vals[2]);
}
}
void doSetParameter(
const UT_StringRef &key,
const fpreal64 *vals,
int n) override
{
}
void doSetParameter(
const UT_StringRef &key,
const UT_StringHolder *vals,
int n) override
{
}
/// @}
/// If the procedural had computation to do due to set
/// parameters it would do so here
void doBeginUpdate() override
{
}
void doEndUpdate() override
{
}
/// Validate the state of the attributes and parameters here
bool checkIsValid() const override { return true; }
/// @{
/// Functions to evaluate the procedural's attributes, these functions
/// are called by Karma when shading and rendering. The attributes are
/// indexed by the value attrib, in the order of the @c attribList().
const int32 *
attribVal(
int attrib,
BRAYtime time,
const Hit &hit_info,
int size) const override
{
return evalAttribute(attrib, time, hit_info, buf, size);
}
const int64 *
attribVal(
int attrib,
BRAYtime time,
const Hit &hit_info,
int64 *buf,
int size) const override
{
return evalAttribute(attrib, time, hit_info, buf, size);
}
const fpreal32 *
attribVal(
int attrib,
BRAYtime time,
const Hit &hit_info,
fpreal32 *buf,
int size) const override
{
// Return attributes based on their the attrib index
switch (attrib)
{
case 0:
return &myRadius;
case 1:
case 2:
return myDisplayColor.data();
default:
}
return buf;
}
const fpreal64 *
attribVal(
int attrib,
BRAYtime time,
const Hit &hit_info,
fpreal64 *buf,
int size) const override
{
// Return attributes based on their the attrib index
// note that it accounts for the 32 -> 64 bit conversion
switch (attrib)
{
case 0:
buf[0] = myRadius;
return buf;
case 1:
case 2:
std::copy(myDisplayColor.data(), myDisplayColor.data()+3, buf);
return buf;
default:
}
return buf;
}
attribVal(
int attrib,
BRAYtime time,
const Hit &hit_info,
int size) const override
{
return evalAttribute(attrib, time, hit_info, buf, size);
}
/// @}
template <typename T>
const T *
evalAttribute(
int attrib,
BRAYtime time,
const Hit &hit,
T *buf,
int size) const
{
return buf;
}
void update(BRAY_EventType event) override
{
UTdebugFormat("Update {}", className());
}
void dumpInfo(UT_JSONWriter &w) const override
{
w.jsonKeyValue("radius", myRadius);
}
protected:
// Templated intersect function for either ray precision
template <typename T> HitPtr
intersectSphere(const Ray<T> &ray) const
{
// Simple spherical intersection
T a = ray.dir.length2();
T b = 2 * dot(ray.org, ray.dir);
T c = ray.org.length2() - myRadius * myRadius;
T d = b*b - 4*a*c;
// No intersection (no real solution to quadratic)
if (d < 0)
return HitPtr();
a = 1.0f / (2.0f * a);
d = SYSsqrt(d);
// Intersection distances
T t0 = a*(-b - d);
T t1 = a*(-b + d);
// If these intersections are smaller than the current
// hit distance, update the hit
T dist = ray.tfar;
if (t0 > 0 && t0 < dist)
dist = t0;
else if (t1 > 0 && t1 < dist)
dist = t1;
else
return HitPtr();
// Update the hit
HitPtr hit = getHit();
hit->distance = dist;
hit->Ng = ray.org + dist * ray.dir;
hit->Ng.normalize();
return hit;
}
private:
const BRAY_AttribList *myAttribs;
float myRadius;
UT_Vector3 myDisplayColor;
};
}
/// Call procedural factory constructor, and name the procedural
BRAY_HdSphere::BRAY_HdSphere()
: BRAY_ProceduralFactory(theToken.asHolder())
{
}
BRAY_HdSphere::~BRAY_HdSphere()
{
}
BRAY_HdSphere::create() const
{
return new CustomSphere;
}
/// Return the list of parameters, which are attributes of the procedural that
/// can be set by Karma. These parameters can control the behaviour of the
/// procedural.
/// In this example, the parameters are the radius and the color of the sphere.
BRAY_HdSphere::paramList() const
{
// Thread safe static initialization
static ParamListBuilder builder;
return builder.myParms.get();
}
void
{
getFactory(); // Instantiate
}