/*
Wireframe_Rif.cpp
Refer to,
http://fundza.com/devkit/rifplugins/mesh2wireframe/index.html
for information about this rif.
Malcolm Kesson
Sept 8 2016
Sept 16 2016: Corrected a memory copy error
Nov 9 2017: Added support for subdiv surfaces, but only as a "cage".
In the "Pre Shape MEL" text field enter one of the following MEL commands,
RiAttribute("user", "int wireframe", 0); // mesh only
RiAttribute("user", "int wireframe", 1); // wireframe only
RiAttribute("user", "int wireframe", 2); // mesh and wireframe
*/
#include <RifPlugin.h>
#include <RixInterfaces.h>
#include <RifFilter.h>
#include <ri.h>
#include <rx.h>
#include <string>
#include <sstream>
#include <stdlib.h>
#define MESH 0
#define WIRE 1
#define WIRE_AND_MESH 2
#define DEFAULT_MODE MESH
class Wireframe_Rif : public RifPlugin {
public:
Wireframe_Rif(RtFloat width);
virtual ~Wireframe_Rif() { }
virtual RifFilter &GetFilter();
private:
RixMessages *m_msg;
RixRenderState *m_rstate;
RifFilter m_filters;
RtFloat m_width;
// 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_____________________________________________________________
virtual RtPoint *getPtrToVertexData(RtInt paramNum, RtToken paramNames[], RtPointer paramVals[]);
virtual int getRenderMode(std::string name);
virtual RtVoid addRoundCurveAttr();
virtual RtVoid convertPolygonsToCurves(RtInt npolys, RtInt nloops[], RtInt vertsPerLoop[], RtInt vertsIndices[],
RtInt paramNum, RtToken paramNames[], RtPointer paramVals[]);
virtual RtVoid convertSubdivToCurves(RtInt nfaces, RtInt vertsPerLoop[], RtInt vertsIndices[],
RtInt paramNum, RtToken paramNames[], RtPointer paramVals[]);
};
//-------------------------------------------------------------
// Entry point called by PRMan.
RifPlugin *RifPluginManufacture(int argc, char **argv) {
RtFloat defaultwidth = 0.025;
if(argc == 1 && *argv[0] != '\0')
defaultwidth = atof(argv[0]);
return new Wireframe_Rif(defaultwidth);
}
//-------------------------------------------------------------
// Constructor
Wireframe_Rif::Wireframe_Rif(RtFloat scale) {
m_width = scale;
m_filters.PointsGeneralPolygonsV = pointsGeneralPolygonsV;
m_filters.HierarchicalSubdivisionMeshV = hierarchicalSubdivisionMeshV;
m_filters.Filtering = RifFilter::k_Continue;
RixContext *rixContext = RxGetRixContext();
m_msg = (RixMessages*) rixContext->GetRixInterface(k_RixMessages);
m_rstate = (RixRenderState*) rixContext->GetRixInterface(k_RixRenderState);
}
//-------------------------------------------------------------
RifFilter& Wireframe_Rif::GetFilter() {
return m_filters;
}
//-------------------------------------------------------------
// Callback:
RtVoid Wireframe_Rif::pointsGeneralPolygonsV(RtInt npolys, RtInt nloops[], RtInt vertsPerLoop[], RtInt vertsIndices[],
RtInt paramNum, RtToken paramNames[], RtPointer paramVals[]) {
Wireframe_Rif *self = static_cast<Wireframe_Rif*> (RifGetCurrentPlugin());
int mode = self->getRenderMode("user:wireframe");
/*
std::stringstream msg;
msg << " mode = ";
msg << mode;
self->m_msg->InfoAlways(msg.str().c_str());
*/
if(mode == 0 || mode == 2)
RiPointsGeneralPolygonsV(npolys, nloops, vertsPerLoop, vertsIndices, paramNum, paramNames, paramVals);
if(mode == 1 || mode == 2)
self->convertPolygonsToCurves(npolys, nloops, vertsPerLoop, vertsIndices, paramNum, paramNames, paramVals);
}
//-------------------------------------------------------------
// Callback:
RtVoid Wireframe_Rif::hierarchicalSubdivisionMeshV(RtToken mask,
RtInt nfaces, RtInt vertsPerLoop[], RtInt vertsIndices[],
RtInt nt, RtToken tags[], RtInt nargs[], RtInt intargs[], RtFloat floatargs[],
RtToken stringargs[],
RtInt paramNum, RtToken paramNames[], RtPointer paramVals[]) {
Wireframe_Rif *self = static_cast<Wireframe_Rif*> (RifGetCurrentPlugin());
int mode = self->getRenderMode("user:wireframe");
if(mode == 0 || mode == 2)
RiHierarchicalSubdivisionMeshV( mask, nfaces, vertsPerLoop, vertsIndices,
nt, tags, nargs, intargs, floatargs, stringargs,
paramNum, paramNames, paramVals);
else if(mode == 1)
self->convertSubdivToCurves(nfaces, vertsPerLoop, vertsIndices, paramNum, paramNames, paramVals);
}
//_______________________________________________________________________
// Utility:
// Almost identical to convertSubdivToCurves().
RtVoid Wireframe_Rif::convertPolygonsToCurves(RtInt npolys, RtInt nloops[], RtInt vertsPerLoop[], RtInt vertsIndices[],
RtInt paramNum, RtToken paramNames[], RtPointer paramVals[]) {
Wireframe_Rif *self = static_cast<Wireframe_Rif*> (RifGetCurrentPlugin());
// Step 1: Count the number of source polygon "P" vertices.
int numVerts;
int numLoops;
int vertCount = 0;
int loopCounter = 0;
for(int n = 0; n < npolys; n++) {
numLoops = nloops[n];
for(int j = 0; j < numLoops; j++) {
numVerts = vertsPerLoop[loopCounter++];
vertCount += numVerts;
}
}
// Step 2: Get a pointer to the source "P" vertex data.
RtPoint *polyVertexPtr = getPtrToVertexData(paramNum, paramNames, paramVals);
RtPoint *polyVertexCopy = (RtPoint*) calloc(1, sizeof(RtPoint) * vertCount);
// Step 3: Copy the data.
memcpy(polyVertexCopy, polyVertexPtr, sizeof(RtPoint) * vertCount);
// Step 4: Assign "P" vertex data so that each curve defines the perimeter of each face.
int vertIndex;
int vertCounter = 0;
RtFloat *fPtr;
loopCounter = 0;
for(int n = 0; n < npolys; n++) {
for(int j = 0; j < nloops[n]; j++) {
numVerts = vertsPerLoop[loopCounter++];
for(int k = 0; k < numVerts; k++) {
vertIndex = vertsIndices[vertCounter];
fPtr = (RtFloat*) &polyVertexCopy[vertIndex];
polyVertexPtr[vertCounter][0] = *fPtr++;
polyVertexPtr[vertCounter][1] = *fPtr++;
polyVertexPtr[vertCounter][2] = *fPtr++;
vertCounter++;
}
}
}
// Step 5: Output the RiCurves and free the temporaty copy of the original "P" data.
int numCurves = loopCounter;
RtToken pNames[2] = { RI_P, RI_CONSTANTWIDTH };
RtFloat width = self->m_width;
RtPointer outVals[2] = { polyVertexPtr, &width };
self->addRoundCurveAttr();
RiCurvesV(RI_LINEAR, numCurves, vertsPerLoop, RI_PERIODIC, 2, pNames, outVals);
free(polyVertexCopy);
}
//_______________________________________________________________________
// Utility:
// Almost identical to convertPolygonsToCurves().
RtVoid Wireframe_Rif::convertSubdivToCurves(RtInt nfaces, RtInt vertsPerLoop[], RtInt vertsIndices[],
RtInt paramNum, RtToken paramNames[], RtPointer paramVals[]) {
Wireframe_Rif *self = static_cast<Wireframe_Rif*> (RifGetCurrentPlugin());
// Step 1: Count the number of source subdiv "P" vertices.
int numVerts;
int vertCount = 0;
int loopCounter = 0;
for(int n = 0; n < nfaces; n++) {
numVerts = vertsPerLoop[loopCounter++];
vertCount += numVerts;
}
// Step 2: Get a pointer to the source "P" vertex data.
RtPoint *subdivVertexPtr = getPtrToVertexData(paramNum, paramNames, paramVals);
RtPoint *subdivVertexCopy = (RtPoint*) calloc(1, sizeof(RtPoint) * vertCount);
// Step 3: Copy the data.
memcpy(subdivVertexCopy, subdivVertexPtr, sizeof(RtPoint) * vertCount);
// Step 4: Assign "P" vertex data so that each curve defines the perimeter of each face.
int vertIndex;
int vertCounter = 0;
RtFloat *fPtr;
loopCounter = 0;
for(int n = 0; n < nfaces; n++) {
numVerts = vertsPerLoop[loopCounter++];
for(int k = 0; k < numVerts; k++) {
vertIndex = vertsIndices[vertCounter];
fPtr = (RtFloat*) &subdivVertexCopy[vertIndex];
subdivVertexPtr[vertCounter][0] = *fPtr++;
subdivVertexPtr[vertCounter][1] = *fPtr++;
subdivVertexPtr[vertCounter][2] = *fPtr++;
vertCounter++;
}
}
// Step 5: Output the RiCurves and free the temporaty copy of the original "P" data.
int numCurves = loopCounter;
RtToken pNames[2] = { RI_P, RI_CONSTANTWIDTH };
RtFloat width = self->m_width;
RtPointer outVals[2] = { subdivVertexPtr, &width };
self->addRoundCurveAttr();
RiCurvesV(RI_LINEAR, numCurves, vertsPerLoop, RI_PERIODIC, 2, pNames, outVals);
free(subdivVertexCopy);
}
//_______________________________________________________________________
// Utility:
// Returns a pointer to the first point of the "P" array.
RtPoint* Wireframe_Rif::getPtrToVertexData(RtInt paramNum, RtToken paramNames[], RtPointer paramVals[]) {
for(int n = 0; n < paramNum; n++) {
if(paramNames[n] == RI_P || strcmp(paramNames[n], RI_P) == 0)
return (RtPoint*)paramVals[n];
}
return NULL;
}
//_______________________________________________________________________
// Utility:
// Returns the int value of the "user:wireframe" attribute.
// 0 - use original polymesh
// 1 - use curves for wireframe
// 2 - use both polymesh and curves.
int Wireframe_Rif::getRenderMode(std::string name) {
int result = -1;
RtInt resultCount, resultError, resultLen = sizeof(int*);
RixRenderState::Type resultType;
resultError = m_rstate->GetAttribute(name.c_str(), &result, resultLen,
&resultType, &resultCount);
if(resultError == 0)
return result;
return DEFAULT_MODE;
}
//_______________________________________________________________________
// Utility:
RtVoid Wireframe_Rif::addRoundCurveAttr( ) {
RixContext *rix = RxGetRixContext();
RixTokenStorage *tok_store = static_cast<RixTokenStorage*>(rix->GetRixInterface(k_RixGlobalTokenData));
RtToken tok = (char*)tok_store->GetToken("int roundcurve");
RtToken tokens[] = { tok };
int value = 1;
RtPointer values[] = { &value };
RiAttributeV(RI_DICE, 1, tokens, values);
}