/*
Mesh2Blobby.cpp
Refer to,
www.fundza.com/devkit/rifplugins/mesh2blobby/index.html
for information about this rif.
Malcolm Kesson: Jan 28 2016
Modified: July 25th 2018. See line 131. With RfM22 vertex data
is now tagged "vertex point P" rather than plain old "P".
*/
#include <RifPlugin.h>
#include <RixInterfaces.h>
#include <RifFilter.h>
#include <ri.h>
#include <list>
#include <string>
#include <cstdlib>
class Mesh2Blobby : public RifPlugin {
public:
Mesh2Blobby(RtFloat scale);
virtual ~Mesh2Blobby() { }
virtual RifFilter &GetFilter();
private:
RifFilter m_filters;
RtFloat m_default_blobsize;
RtUString m_blobby_ustr;
RtUString m_blob_min_ustr;
RtUString m_blob_max_ustr;
RixRenderState *m_rstate;
// Utilities_____________________________________________________________
static RtPoint *getVerticesPtr(RtInt paramNum, RtToken paramNames[], RtPointer paramVals[]);
static RtFloat getPrimVar(RtUString name, RtInt paramNum, RtToken paramNames[], RtPointer paramVals[]);
static RtInt countVertices(RtInt npolys, RtInt nvertices[], RtInt vertices[]);
static RtVoid writeBlobby(RtInt numverts, RtPoint *vertices, RtFloat minSize, RtFloat maxSize);
virtual RtFloat getIdentifierId();
static RtFloat randBetween(RtFloat min, RtFloat max);
// Callbacks_____________________________________________________________
static RtVoid pointsGeneralPolygonsV(RtInt npolys, RtInt *nloops, RtInt *nverts,
RtInt* verts, RtInt n, RtToken nms[], RtPointer vals[]);
static RtVoid hierarchicalSubdivisionMeshV(RtToken mask, RtInt nf,
RtInt nverts[], RtInt verts[], RtInt nt, RtToken tags[],
RtInt nargs[], RtInt intargs[], RtFloat floatargs[],
RtToken stringargs[], RtInt, RtToken[], RtPointer[]);
};
// Entry point called by PRMan.
RifPlugin *RifPluginManufacture(int argc, char **argv) {
RtFloat defaultsize = 0.5;
if(argc == 1 && *argv[0] != '\0')
defaultsize = atof(argv[0]);
return new Mesh2Blobby(defaultsize);
}
// Constructor
Mesh2Blobby::Mesh2Blobby(RtFloat size) {
m_default_blobsize = size;
m_filters.PointsGeneralPolygonsV = pointsGeneralPolygonsV;
m_filters.HierarchicalSubdivisionMeshV = hierarchicalSubdivisionMeshV;
m_filters.Filtering = RifFilter::k_Continue;
RixContext *rixContext = RixGetContext();
m_rstate = (RixRenderState*) rixContext->GetRixInterface(k_RixRenderState);
m_blobby_ustr = RtUString("constant float blobby");
m_blob_min_ustr = RtUString("constant float blob_min");
m_blob_max_ustr = RtUString("constant float blob_max");
}
RifFilter& Mesh2Blobby::GetFilter() {
return m_filters;
}
//___________________________________________________________________
// Callback:
// Handles the pointsGeneralPolygonsV primitive.
RtVoid Mesh2Blobby::pointsGeneralPolygonsV(RtInt npolys, RtInt* nloops, RtInt nverts[], RtInt verts[],
RtInt paramNum, RtToken paramNames[], RtPointer paramVals[]) {
Mesh2Blobby *self = static_cast<Mesh2Blobby*> (RifGetCurrentPlugin());
RtBoolean convertToBlobby = (getPrimVar(self->m_blobby_ustr, paramNum, paramNames, paramVals) > 0) ? true : false;
if(convertToBlobby == false) {
RiPointsGeneralPolygonsV(npolys, nloops, nverts, verts, paramNum, paramNames, paramVals);
return;
}
RtFloat blobMin = self->getPrimVar(self->m_blob_min_ustr, paramNum, paramNames, paramVals);
RtFloat blobMax = self->getPrimVar(self->m_blob_max_ustr, paramNum, paramNames, paramVals);
if(blobMin == -1)
blobMin = self->m_default_blobsize;
if(blobMax == -1)
blobMax = self->m_default_blobsize;
int numverts = countVertices(npolys, nverts, verts);
RtPoint *vertices = getVerticesPtr(paramNum, paramNames, paramVals);
writeBlobby(numverts, vertices, blobMin, blobMax);
}
//___________________________________________________________________
// Callback:
// Handles the HierarchicalSubdivisionMesh primitive.
RtVoid Mesh2Blobby::hierarchicalSubdivisionMeshV(RtToken mask,
RtInt nf, RtInt nverts[], RtInt verts[],
RtInt nt, RtToken tags[], RtInt nargs[], RtInt intargs[], RtFloat floatargs[],
RtToken stringargs[],
RtInt paramNum, RtToken paramNames[], RtPointer paramVals[]) {
Mesh2Blobby *self = static_cast<Mesh2Blobby*> (RifGetCurrentPlugin());
RtBoolean convertToBlobby = (getPrimVar(self->m_blobby_ustr, paramNum, paramNames, paramVals) > 0) ? true : false;
if(convertToBlobby == false) {
RiHierarchicalSubdivisionMeshV(mask, nf, nverts, verts, nt, tags, nargs, intargs, floatargs,
stringargs, paramNum, paramNames, paramVals);
return;
}
RtFloat blobMin = self->getPrimVar(self->m_blob_min_ustr, paramNum, paramNames, paramVals);
RtFloat blobMax = self->getPrimVar(self->m_blob_max_ustr, paramNum, paramNames, paramVals);
if(blobMin == -1)
blobMin = self->m_default_blobsize;
if(blobMax == -1)
blobMax = self->m_default_blobsize;
int numverts = countVertices(nf, nverts, verts);
RtPoint *vertices = getVerticesPtr(paramNum, paramNames, paramVals);
writeBlobby(numverts, vertices, blobMin, blobMax);
}
//___________________________________________________________________
// Utility:
// Given the number of points in a vertex array and min and max blob sizes this
// method emits a RiBlobby. For example, if "numverts" is 100 this method
// will output a blobby consisting of 100 merged blobs.
void Mesh2Blobby::writeBlobby(RtInt numverts, RtPoint *vertices, RtFloat minSize, RtFloat maxSize) {
Mesh2Blobby *self = static_cast<Mesh2Blobby*> (RifGetCurrentPlugin());
RtFloat id = self->getIdentifierId();
std::srand((int)id);
RtInt numBlobs = numverts;
RtInt numEllipsoidCodes = numBlobs * 2;
RtInt numBlendingCodes = 2 + numBlobs;
RtInt numTotalCodes = numEllipsoidCodes + numBlendingCodes;
RtInt codes[numTotalCodes];
int mat_index = 0, n = 0;
while(n < numEllipsoidCodes) {
codes[n] = 1001;
codes[n+1] = mat_index;
mat_index += 16;
n += 2;
}
// Blending
codes[n++] = 0; // opcode to additively blend all blobs
codes[n++] = numBlobs; // number of blobs to blend
RtInt index = 0;
while(n < numTotalCodes)
codes[n++] = index++;
RtInt numFloats = 16 * numBlobs;
RtFloat mat[numFloats]; // 16 values per matrix
RtFloat *fPtr = (RtFloat*)vertices;
RtFloat size;
for(int n = 0; n < numBlobs * 16; n += 16) {
size = randBetween(minSize, maxSize);
mat[n] = size; mat[n+1] = mat[n+2] = mat[n+3] = 0;
mat[n+4] = 0; mat[n+5] = size; mat[n+6] = mat[n+7] = 0;
mat[n+8] = mat[n+9] = 0; mat[n+10] = size; mat[n+11] = 0;
mat[n+12] = *fPtr++;
mat[n+13] = *fPtr++;
mat[n+14] = *fPtr++;
mat[n+15] = 1;
}
RtInt nstrings = 1;
const char *str[1] = {"\0"};
RiBlobby(numBlobs, numTotalCodes, codes, numFloats, mat, nstrings, str, RI_NULL);
}
//___________________________________________________________________
// Utility:
// The writeBlobby() method must know the number of number of blobs - a value we derive
// from the number of unique vertices specified by the rib statements "PointsGeneralPolygons"
// and "HierarchicalSubdivisionMesh". Many of the indices in their nvertices array
// are duplicates but the use of std::list.unique() ensures we return a correct count.
RtInt Mesh2Blobby::countVertices(RtInt npolys, RtInt* nvertices, RtInt* vertices) {
// Get the raw indices:
int vertCount = 0;
for(int n = 0; n < npolys; n++)
vertCount += nvertices[n];
// Extract the unique indices:
std::list<int> int_list;
for(int n = 0; n < vertCount; n++)
int_list.push_back(vertices[n]);
int_list.sort();
int_list.unique();
return int_list.size();
}
//___________________________________________________________________
// Utility:
// Scans the paramNames (token names) until RI_P ("P") is found RfM21, or until "vertex point P"
// RfM22 is found. Returns a pointer (address) to the first vertex of the points array.
RtPoint* Mesh2Blobby::getVerticesPtr(RtInt paramNum, RtToken paramNames[], RtPointer paramVals[]) {
for(int n = 0; n < paramNum; n++) {
if(paramNames[n] == RI_P || strcmp(paramNames[n], "vertex point P") == 0)
return (RtPoint*)paramVals[n];
}
return NULL;
}
//___________________________________________________________________
// Utility:
RtFloat Mesh2Blobby::getPrimVar(RtUString name, RtInt paramNum, RtToken paramNames[], RtPointer paramVals[]) {
for(int n = 0; n < paramNum; n++)
if(strcmp(paramNames[n], name.CStr()) == 0)
return *(RtFloat*)paramVals[n];
return -1;
}
//_______________________________________________________________________
// Utility:
// Returns the value of,
// Attribute "identifier" "float id" [1234567]
RtFloat Mesh2Blobby::getIdentifierId() {
RtUString identifier_name = RtUString( "identifier:id");
RtFloat result_id = -1;
RtInt resultCount, resultError, resultLen = sizeof(int*);
RixRenderState::Type resultType;
resultError = m_rstate->GetAttribute(identifier_name, &result_id, resultLen,
&resultType, &resultCount);
if(resultError == 0)
return result_id;
return 1; // an arbitrary value for the random seed
}
//___________________________________________________________________
// Utility:
RtFloat Mesh2Blobby::randBetween(RtFloat min, RtFloat max) {
return ((RtFloat)rand()/RAND_MAX) * (max - min) + min;
}