I haven't programmed anything for 20 years, but I want the ability in Cinema 4D to save out limit surfaces as an IGES file, so I started creating a plug-in. I'm using the libIGES library to handle the file output.
I'm looping through the patchtable and saving out each 4x4 surface (I think). The overall shape of the result looks like I'm going in the right direction, but the patches don't seem to have the correct control points.
I have attached the IGES file and a screenshot of it if anybody would care to look at it.
If anybody can point me in the right direction I would deeply appreciate it! Thank you in advance!
#define _USE_MATH_DEFINES // for C++
#include <cmath>
#include <cassert>
#include <cstring>
#include <cfloat>
#include <api/dll_iges.h>
#include <api/all_api_entities.h>
#include <opensubdiv/far/topologyDescriptor.h>
#include <opensubdiv/far/primvarRefiner.h>
#include <opensubdiv/far/patchTableFactory.h>
#include <opensubdiv/far/patchMap.h>
#include <opensubdiv/far/ptexIndices.h>
#include "helpers.h"
using namespace OpenSubdiv;
static int const g_nverts = 5;
static double const g_verts[24] = { 0.0f, 0.0f, 20.0f,
0.0f, -20.0f, 0.0f,
20.0f, 0.0f, 0.0f,
0.0f, 20.0f, 0.0f,
-20.0f, 0.0f, 0.0f, };
static int const g_vertsperface[5] = { 3, 3, 3, 3, 4 };
static int const g_nfaces = 5;
static int const g_faceverts[16] = { 0, 1, 2,
0, 2, 3,
0, 3, 4,
0, 4, 1,
4, 3, 2, 1 };
static int const g_ncreases = 4;
static int const g_creaseverts[8] = { 4, 3, 3, 2, 2, 1, 1, 4 };
static float const g_creaseweights[4] = { 3.0f, 3.0f, 3.0f, 3.0f };
// Creates a Far::TopologyRefiner from the pyramid shape above
static Far::TopologyRefiner* createTopologyRefiner();
//------------------------------------------------------------------------------
int main(int, char**) {
// Generate a FarTopologyRefiner (see far_tutorial_0 for details).
Far::TopologyRefiner* refiner = createTopologyRefiner();
// Adaptively refine the topology with an isolation level capped at 3
// because the sharpest crease in the shape is 3.0f (in g_creaseweights[])
int maxIsolation = 3;
refiner->RefineAdaptive(
Far::TopologyRefiner::AdaptiveOptions(maxIsolation));
// Generate a set of Far::PatchTable that we will use to evaluate the
// surface limit
Far::PatchTableFactory::Options patchOptions;
patchOptions.endCapType =
Far::PatchTableFactory::Options::ENDCAP_GREGORY_BASIS;
Far::PatchTable const* patchTable =
Far::PatchTableFactory::Create(*refiner, patchOptions);
// Compute the total number of points we need to evaluate patchtable.
// we use local points around extraordinary features.
int nRefinerVertices = refiner->GetNumVerticesTotal();
int nLocalPoints = patchTable->GetNumLocalPoints();
// Create a buffer to hold the position of the refined verts and
// local points, then copy the coarse positions at the beginning.
std::vector<Vertex> verts(nRefinerVertices + nLocalPoints);
memcpy(&verts[0], g_verts, g_nverts * 3 * sizeof(double));
// Adaptive refinement may result in fewer levels than maxIsolation.
int nRefinedLevels = refiner->GetNumLevels();
// Interpolate vertex primvar data : they are the control vertices
// of the limit patches (see far_tutorial_0 for details)
Vertex* src = &verts[0];
for (int level = 1; level < nRefinedLevels; ++level)
{
Vertex* dst = src + refiner->GetLevel(level - 1).GetNumVertices();
Far::PrimvarRefiner(*refiner).Interpolate(level, src, dst);
src = dst;
}
// Evaluate local points from interpolated vertex primvars.
patchTable->ComputeLocalPointValues(&verts[0], &verts[nRefinerVertices]);
// instantiate the IGES data object ///////////////////////////////////////
DLL_IGES model;
// Create knot vector
double knots[] = { 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0 };
// Create a surface object
DLL_IGES_ENTITY_128 surf(model, true);
// IGES object created ////////////////////////////////////////////////////
// Loop through each patch and save out 4x4 vertices each
int na = patchTable->GetNumPatchArrays();
bool error;
for (int i = 0; i < na; i++)
{
Far::PatchDescriptor pd = patchTable->GetPatchArrayDescriptor(i);
if (pd == 6) // Type::REGULAR
{
Far::ConstIndexArray arraycvs = patchTable->GetPatchArrayVertices(i);
int np = patchTable->GetNumPatches(i);
for (int patch = 0; patch < np; patch++)
{
Far::ConstIndexArray cvs = patchTable->GetPatchVertices(i, patch);
int cvCount = cvs.size();
std::vector<double> surfVerts;
for (int cv = 0; cv < cvCount; cv++)
{
surfVerts.push_back(verts[cvs[cv]].point[0]);
surfVerts.push_back(verts[cvs[cv]].point[1]);
surfVerts.push_back(verts[cvs[cv]].point[2]);
}
if (patch != 0)
error = surf.NewEntity();
error = surf.SetNURBSData(4, 4, 4, 4, knots, knots, &surfVerts[0], false, false, false, 0.0, 1.0, 0.0, 1.0);
surfVerts.clear();
}
}
}
model.Write("test.igs", true);
return 0;
}
static Far::TopologyRefiner* createTopologyRefiner()
{
typedef Far::TopologyDescriptor Descriptor;
Sdc::SchemeType type = OpenSubdiv::Sdc::SCHEME_CATMARK;
Sdc::Options options;
options.SetVtxBoundaryInterpolation(Sdc::Options::VTX_BOUNDARY_EDGE_ONLY);
Descriptor desc;
desc.numVertices = g_nverts;
desc.numFaces = g_nfaces;
desc.numVertsPerFace = g_vertsperface;
desc.vertIndicesPerFace = g_faceverts;
desc.numCreases = g_ncreases;
desc.creaseVertexIndexPairs = g_creaseverts;
desc.creaseWeights = g_creaseweights;
// Instantiate a FarTopologyRefiner from the descriptor.
Far::TopologyRefiner* refiner =
Far::TopologyRefinerFactory<Descriptor>::Create(desc,
Far::TopologyRefinerFactory<Descriptor>::Options(type, options));
return refiner;
}
using namespace std;