Using Binary Curve Collection (BCC) files
BCC stands for "Binary Curve Collection." I use the BCC file format for yarn-level cloth models. You can find the detailed description of the BCC file format on the Yarn-level Cloth Models page on my website. This solution presents a way of easily loading these files.
Loading the BCC file header
For easily loading the BCC files, I will use the following struct
.
struct BCCHeader { char sign[3]; unsigned char byteCount; char curveType[2]; char dimensions; char upDimension; uint64_t curveCount; uint64_t totalControlPointCount; char fileInfo[40]; };
Using this struct
loading the header is quite simple.
BCCHeader header; FILE *pFile = fopen("filename.bcc", "rb"); fread(&header, sizeof(header), 1, pFile);
Then, we should check if this was a valid BCC file.
if ( header.sign[0] != 'B' ) return -1; // Invalid file signature if ( header.sign[1] != 'C' ) return -1; // Invalid file signature if ( header.sign[2] != 'C' ) return -1; // Invalid file signature if ( header.byteCount != 0x44 ) return -1; // Only supporting 4-byte integers and floats
You can also check the curve type to make sure that it is what you expect.
if ( header.curveType[0] != 'C' ) return -1; // Not a Catmull-Rom curve if ( header.curveType[1] != '0' ) return -1; // Not uniform parameterization if ( header.dimensions != 3 ) return -1; // Only curves in 3D
You can find more information on Camtull-Rom curves and their parameterization here.
Loading the BCC file data block
The data block consists of entrees for each curve. The entrees begin with an integer that indicates the number of control points for the curve, followed by the control points of the curve.
It might be a good idea to allocate space for all control points at once (the number is provided in the header). Then, we must also keep the index of the first control point per curve.
IMPORTANT: The number of control points for a curve CAN be negative. This means that the curve represents a closed loop.
std::vector<cy::Vec3f> controlPoints( header.totalControlPointCount ); std::vector<int> firstControlPoint( header.curveCount + 1 ); std::vector<char> isCurveLoop( header.curveCount ); float *cp = controlPoints.data(); int prevCP = 0; for ( uint64_t i=0; i<header.curveCount; i++ ) { int curveControlPointCount; fread(&curveControlPointCount, sizeof(int), 1, pFile); isCurveLoop[i] = curveControlPointCount < 0; if ( curveControlPointCount < 0 ) curveControlPointCount = -curveControlPointCount; fread(cp, sizeof(float), curveControlPointCount, pFile); cp += curveControlPointCount; firstControlPoint[i] = prevCP; prevCP += curveControlPointCount; } firstControlPoint[header.curveCount] = prevCP;
Drawing the curves
Here I provide a simple (incomplete) code for drawing these curves as line segments (not as Catmull-Rom curves, as they should be drawn). Also note that the following code uses the old OpenGL standard (GL version 3.2 or earlier).
for ( uint64_t i=0; i<header.curveCount; i++ ) { glDrawArrays( isCurveLoop[i] ? GL_LINE_LOOP : GL_LINE_STRIP, controlPoints.data() + fistControlPoint[i], firstControlPoint[i+1] - firstControlPoint[i] ); }