Thank you, Mael! That was exactly what I was looking for. I didn't understand how masks worked. For anyone else's reference, here is a simple midpoint subdivision mask for CGAL's Surface_mesh class. I did away with the templating for my own needs, but obviously that extra generalizability can be worked back in. All this subdivision does is subdivide triangle faces into 4 equal sub-triangles. There is no interpolation on the vertices whatsoever.

// Includes

#include <CGAL/Surface_mesh.h>

#include <CGAL/subdivision_method_3.h>

// Mask class

class MidpointSubdivisionMask {

typedef SurfaceMesh CGAL::Surface_mesh<CGAL::Simple_cartesian<double>::Point_3>;

typedef typename boost::graph_traits<SurfaceMesh>::vertex_descriptor

vertex_descriptor;

typedef typename boost::graph_traits<SurfaceMesh>::halfedge_descriptor

halfedge_descriptor;

typedef typename boost::property_map<SurfaceMesh, CGAL::vertex_point_t>::type

Vertex_pmap;

typedef typename boost::property_traits<Vertex_pmap>::value_type Point;

typedef typename boost::property_traits<Vertex_pmap>::reference Point_ref;

SurfaceMesh &pmesh;

Vertex_pmap vpm;

public:

MidpointSubdivisionMask(SurfaceMesh &pmesh)

: pmesh(pmesh), vpm(get(CGAL::vertex_point, pmesh)) {}

void edge_node(halfedge_descriptor hd, Point &pt) {

Point_ref p1 = get(vpm, target(hd, pmesh));

Point_ref p2 = get(vpm, target(opposite(hd, pmesh), pmesh));

pt = Point((p1[0] + p2[0]) / 2, (p1[1] + p2[1]) / 2, (p1[2] + p2[2]) / 2);

}

void vertex_node(vertex_descriptor vd, Point &pt) {

Point_ref S = get(vpm, vd);

pt = Point(S[0], S[1], S[2]);

}

void border_node(halfedge_descriptor hd, Point &ept, Point &vpt) {

this->edge_node(hd, ept);

this->vertex_node(target(hd, pmesh), vpt);

}

};

//---------------------------------

// To call it

// Do something to create the mesh

SurfaceMesh mesh;

// Number of subdivisions

const size_t order = 1;

// Subdivide

CGAL::Subdivision_method_3::PTQ(

this->_mesh, MidpointSubdivisionMask(this->_mesh),

CGAL::Subdivision_method_3::parameters::number_of_iterations(order));