/* 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; }