/*
Mesh2Blobby_Rif.cpp
Malcolm Kesson
Jan 28 2016
Modified Nov 21 2017 to read a custom attribute for the randomization
of the sizes of the blobs. The attribute can be assigned to the shape
node of a mesh as a Pre-Shape MEL command. For example,
RiAttribute("user", "float[2] _blob_range", 0.1, 0.4);
Refer to,
http://fundza.com/devkit/rifplugins/mesh2blobby/index.html
for information about using this rif.
*/
#include <RifPlugin.h>
#include <RixInterfaces.h>
#include <RifFilter.h>
#include <ri.h>
#include <list>
#include <string>
#include <cstdlib>
#include <RiTypesHelper.h>
class Mesh2Blobby_Rif : public RifPlugin {
public:
Mesh2Blobby_Rif();
virtual ~Mesh2Blobby_Rif() { }
virtual RifFilter &GetFilter();
private:
RifFilter m_filters;
RixRenderState *m_rstate;
// 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[]);
// Utilities_____________________________________________________________
static RtPoint *getVerticesPtr(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 int getBlobRange(std::string name, RtFloat result[2]);
virtual RtFloat getIdentifierId();
static RtFloat randBetween(RtFloat min, RtFloat max);
};
//_______________________________________________________________________
// Entry point called by PRMan.
RifPlugin *RifPluginManufacture(int argc, char **argv) {
return new Mesh2Blobby_Rif();
}
//_______________________________________________________________________
// Constructor
Mesh2Blobby_Rif::Mesh2Blobby_Rif() {
m_filters.PointsGeneralPolygonsV = pointsGeneralPolygonsV;
m_filters.HierarchicalSubdivisionMeshV = hierarchicalSubdivisionMeshV;
m_filters.Filtering = RifFilter::k_Continue;
RixContext *rixContext = RixGetContext();
m_rstate = (RixRenderState*) rixContext->GetRixInterface(k_RixRenderState);
}
//_______________________________________________________________________
RifFilter& Mesh2Blobby_Rif::GetFilter() {
return m_filters;
}
//_______________________________________________________________________
// Callback:
RtVoid Mesh2Blobby_Rif::pointsGeneralPolygonsV(RtInt npolys, RtInt* nloops, RtInt nverts[], RtInt verts[],
RtInt paramNum, RtToken paramNames[], RtPointer paramVals[]) {
Mesh2Blobby_Rif *self = static_cast<Mesh2Blobby_Rif*> (RifGetCurrentPlugin());
RtFloat blobsizes[2];
int result = self->getBlobRange("user:blob_range", blobsizes);
//if(result)
// std::cout << "user:blob_range = " << blobsizes[0] << " " << blobsizes[1] << "\n";
if(result == 0) {
RiPointsGeneralPolygonsV(npolys, nloops, nverts, verts,
paramNum, paramNames, paramVals);
return;
}
int numverts = countVertices(npolys, nverts, verts);
RtPoint *vertices = getVerticesPtr(paramNum, paramNames, paramVals);
writeBlobby(numverts, vertices, blobsizes[0], blobsizes[1]);
}
//_______________________________________________________________________
// Callback:
RtVoid Mesh2Blobby_Rif::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_Rif *self = static_cast<Mesh2Blobby_Rif*> (RifGetCurrentPlugin());
RtFloat blobsizes[2];
int result = self->getBlobRange("user:blob_range", blobsizes);
//if(result)
// std::cout << "user:blob_range = " << blobsizes[0] << " " << blobsizes[1] << "\n";
if(result == 0) {
RiHierarchicalSubdivisionMeshV(mask, nf, nverts, verts, nt, tags, nargs, intargs, floatargs,
stringargs, paramNum, paramNames, paramVals);
return;
}
int numverts = countVertices(nf, nverts, verts);
RtPoint *vertices = getVerticesPtr(paramNum, paramNames, paramVals);
writeBlobby(numverts, vertices, blobsizes[0], blobsizes[1]);
}
//_______________________________________________________________________
// Utility:
// Given the number of points in a vertex array and a scaling factor this
// method emits a RiBlobby. For example, if "numverts" is 100 this method
// will output a blobby consisting of 100 merged blobs.
void Mesh2Blobby_Rif::writeBlobby(RtInt numverts, RtPoint *vertices, RtFloat minSize, RtFloat maxSize) {
Mesh2Blobby_Rif *self = static_cast<Mesh2Blobby_Rif*> (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;
//char *str[1];
//str[0] = (char*)"\0";
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 a correct count.
RtInt Mesh2Blobby_Rif::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_Rif::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:
// Returns two values of the "user:blob_range" attribute. For example, in RIB
// the attribute might be,
// Attribute "user" "float[2] blob_range" [0.9 1]
// This attr can be added via a Pre-Shape MEL, for example,
// RiAttribute("user", "float[2] blob_range", 0.1, 0.4);
int Mesh2Blobby_Rif::getBlobRange(std::string name, RtFloat result_range[2]) {
RtInt resultCount, resultError, resultLen = sizeof(int*);
RixRenderState::Type resultType;
RtUString _name_ = RtUString(name.c_str());
resultError = m_rstate->GetAttribute(_name_, result_range, resultLen,
&resultType, &resultCount);
if(resultError == 0)
return 1;
return 0;
}
//_______________________________________________________________________
// Utility:
// Returns the value of,
// Attribute "identifier" "float id" [2]
RtFloat Mesh2Blobby_Rif::getIdentifierId() {
//std::string name = "identifier:id";
RtUString _name_ = RtUString( "identifier:id");
RtFloat result_id = -1;
RtInt resultCount, resultError, resultLen = sizeof(int*);
RixRenderState::Type resultType;
resultError = m_rstate->GetAttribute(_name_, &result_id, resultLen,
&resultType, &resultCount);
if(resultError == 0)
return result_id;
return 1; // an arbitrary value for the random seed
}
RtFloat Mesh2Blobby_Rif::randBetween(RtFloat min, RtFloat max) {
return ((RtFloat)rand()/RAND_MAX) * (max - min) + min;
}