[spline/bezier]Improve basic spline/bezier structures.

This commit is contained in:
xebra 2018-09-27 20:57:30 +09:00
parent 8ad38dfaae
commit 10afcf2dbb
3 changed files with 151 additions and 198 deletions

View file

@ -343,37 +343,29 @@ void ControlPoints::Convert(const SimpleVertex *const *points, int size) {
defcolor = points[0]->color_32;
}
template<class Patch>
template<class Surface>
class SubdivisionSurface {
private:
const ControlPoints &points;
const Patch &patch;
const Weight2D &weights;
public:
SubdivisionSurface(const ControlPoints &points, const Patch &patch, const Weight2D &weights)
: points(points), patch(patch), weights(weights) {
}
template <bool sampleNrm, bool sampleCol, bool sampleTex, bool useSSE4, bool patchFacing>
void Tessellate(OutputBuffers &output) {
const float inv_u = 1.0f / (float)patch.tess_u;
const float inv_v = 1.0f / (float)patch.tess_v;
static void Tessellate(OutputBuffers &output, const Surface &surface, const ControlPoints &points, const Weight2D &weights) {
const float inv_u = 1.0f / (float)surface.tess_u;
const float inv_v = 1.0f / (float)surface.tess_v;
for (int patch_u = 0; patch_u < patch.num_patches_u; ++patch_u) {
const int start_u = patch.GetTessStart(patch_u);
for (int patch_v = 0; patch_v < patch.num_patches_v; ++patch_v) {
const int start_v = patch.GetTessStart(patch_v);
for (int patch_u = 0; patch_u < surface.num_patches_u; ++patch_u) {
const int start_u = surface.GetTessStart(patch_u);
for (int patch_v = 0; patch_v < surface.num_patches_v; ++patch_v) {
const int start_v = surface.GetTessStart(patch_v);
// Prepare 4x4 control points to tessellate
const int idx = patch.GetPointIndex(patch_u, patch_v);
const int idx_v[4] = { idx, idx + patch.count_u, idx + patch.count_u * 2, idx + patch.count_u * 3 };
const int idx = surface.GetPointIndex(patch_u, patch_v);
const int idx_v[4] = { idx, idx + surface.num_points_u, idx + surface.num_points_u * 2, idx + surface.num_points_u * 3 };
Tessellator<Vec3f> tess_pos(points.pos, idx_v);
Tessellator<Vec4f> tess_col(points.col, idx_v);
Tessellator<Vec2f> tess_tex(points.tex, idx_v);
Tessellator<Vec3f> tess_nrm(points.pos, idx_v);
for (int tile_u = start_u; tile_u <= patch.tess_u; ++tile_u) {
const int index_u = patch.GetIndexU(patch_u, tile_u);
for (int tile_u = start_u; tile_u <= surface.tess_u; ++tile_u) {
const int index_u = surface.GetIndexU(patch_u, tile_u);
const Weight &wu = weights.u[index_u];
// Pre-tessellate U lines
@ -385,11 +377,11 @@ public:
if (sampleNrm)
tess_nrm.SampleU(wu.deriv);
for (int tile_v = start_v; tile_v <= patch.tess_v; ++tile_v) {
const int index_v = patch.GetIndexV(patch_v, tile_v);
for (int tile_v = start_v; tile_v <= surface.tess_v; ++tile_v) {
const int index_v = surface.GetIndexV(patch_v, tile_v);
const Weight &wv = weights.v[index_v];
SimpleVertex &vert = output.vertices[patch.GetIndex(index_u, index_v, patch_u, patch_v)];
SimpleVertex &vert = output.vertices[surface.GetIndex(index_u, index_v, patch_u, patch_v)];
// Tessellate
vert.pos = tess_pos.SampleV(wv.basis);
@ -420,62 +412,60 @@ public:
}
}
patch.BuildIndex(output.indices, output.count);
surface.BuildIndex(output.indices, output.count);
}
using TessFunc = void(SubdivisionSurface::*)(OutputBuffers &);
using TessFunc = void(*)(OutputBuffers &, const Surface &, const ControlPoints &, const Weight2D &);
TEMPLATE_PARAMETER_DISPATCHER_FUNCTION(Tess, SubdivisionSurface::Tessellate, TessFunc);
void Tessellate(OutputBuffers &output, u32 origVertType) {
constexpr int NumParams = 5;
static TemplateParameterDispatcher<TessFunc, NumParams, Tess> dispatcher; // Initialize only once
const bool params[NumParams] = {
static void Tessellate(OutputBuffers &output, const Surface &surface, const ControlPoints &points, const Weight2D &weights, u32 origVertType) {
const bool params[] = {
(origVertType & GE_VTYPE_NRM_MASK) != 0,
(origVertType & GE_VTYPE_COL_MASK) != 0,
(origVertType & GE_VTYPE_TC_MASK) != 0,
cpu_info.bSSE4_1,
patch.patchFacing,
surface.patchFacing,
};
static TemplateParameterDispatcher<TessFunc, ARRAY_SIZE(params), Tess> dispatcher; // Initialize only once
TessFunc func = dispatcher.GetFunc(params);
(this->*func)(output);
func(output, surface, points, weights);
}
};
template<class Patch>
void SoftwareTessellation(OutputBuffers &output, const Patch &patch, u32 origVertType, const ControlPoints &points) {
using WeightType = typename Patch::WeightType;
u32 key_u = WeightType::ToKey(patch.tess_u, patch.count_u, patch.type_u);
u32 key_v = WeightType::ToKey(patch.tess_v, patch.count_v, patch.type_v);
template<class Surface>
void SoftwareTessellation(OutputBuffers &output, const Surface &surface, u32 origVertType, const ControlPoints &points) {
using WeightType = typename Surface::WeightType;
u32 key_u = WeightType::ToKey(surface.tess_u, surface.num_points_u, surface.type_u);
u32 key_v = WeightType::ToKey(surface.tess_v, surface.num_points_v, surface.type_v);
Weight2D weights(WeightType::weightsCache, key_u, key_v);
SubdivisionSurface<Patch> surface(points, patch, weights);
surface.Tessellate(output, origVertType);
SubdivisionSurface<Surface>::Tessellate(output, surface, points, weights, origVertType);
}
template<class Patch>
static void HardwareTessellation(OutputBuffers &output, const Patch &patch, u32 origVertType,
template<class Surface>
static void HardwareTessellation(OutputBuffers &output, const Surface &surface, u32 origVertType,
const SimpleVertex *const *points, TessellationDataTransfer *tessDataTransfer) {
using WeightType = typename Patch::WeightType;
u32 key_u = WeightType::ToKey(patch.tess_u, patch.count_u, patch.type_u);
u32 key_v = WeightType::ToKey(patch.tess_v, patch.count_v, patch.type_v);
using WeightType = typename Surface::WeightType;
u32 key_u = WeightType::ToKey(surface.tess_u, surface.num_points_u, surface.type_u);
u32 key_v = WeightType::ToKey(surface.tess_v, surface.num_points_v, surface.type_v);
Weight2D weights(WeightType::weightsCache, key_u, key_v);
weights.size_u = WeightType::CalcSize(patch.tess_u, patch.count_u);
weights.size_v = WeightType::CalcSize(patch.tess_v, patch.count_v);
tessDataTransfer->SendDataToShader(points, patch.count_u, patch.count_v, origVertType, weights);
weights.size_u = WeightType::CalcSize(surface.tess_u, surface.num_points_u);
weights.size_v = WeightType::CalcSize(surface.tess_v, surface.num_points_v);
tessDataTransfer->SendDataToShader(points, surface.num_points_u, surface.num_points_v, origVertType, weights);
// Generating simple input vertices for the spline-computing vertex shader.
float inv_u = 1.0f / (float)patch.tess_u;
float inv_v = 1.0f / (float)patch.tess_v;
for (int patch_u = 0; patch_u < patch.num_patches_u; ++patch_u) {
const int start_u = patch.GetTessStart(patch_u);
for (int patch_v = 0; patch_v < patch.num_patches_v; ++patch_v) {
const int start_v = patch.GetTessStart(patch_v);
for (int tile_u = start_u; tile_u <= patch.tess_u; ++tile_u) {
const int index_u = patch.GetIndexU(patch_u, tile_u);
for (int tile_v = start_v; tile_v <= patch.tess_v; ++tile_v) {
const int index_v = patch.GetIndexV(patch_v, tile_v);
SimpleVertex &vert = output.vertices[patch.GetIndex(index_u, index_v, patch_u, patch_v)];
float inv_u = 1.0f / (float)surface.tess_u;
float inv_v = 1.0f / (float)surface.tess_v;
for (int patch_u = 0; patch_u < surface.num_patches_u; ++patch_u) {
const int start_u = surface.GetTessStart(patch_u);
for (int patch_v = 0; patch_v < surface.num_patches_v; ++patch_v) {
const int start_v = surface.GetTessStart(patch_v);
for (int tile_u = start_u; tile_u <= surface.tess_u; ++tile_u) {
const int index_u = surface.GetIndexU(patch_u, tile_u);
for (int tile_v = start_v; tile_v <= surface.tess_v; ++tile_v) {
const int index_v = surface.GetIndexV(patch_v, tile_v);
SimpleVertex &vert = output.vertices[surface.GetIndex(index_u, index_v, patch_u, patch_v)];
// Index for the weights
vert.pos.x = index_u;
vert.pos.y = index_v;
@ -489,7 +479,7 @@ static void HardwareTessellation(OutputBuffers &output, const Patch &patch, u32
}
}
}
patch.BuildIndex(output.indices, output.count);
surface.BuildIndex(output.indices, output.count);
}
void DrawEngineCommon::SubmitSpline(const void *control_points, const void *indices, int tess_u, int tess_v, int count_u, int count_v, int type_u, int type_v, GEPatchPrimType prim_type, bool computeNormals, bool patchFacing, u32 vertType, int *bytesRead) {
@ -525,10 +515,6 @@ void DrawEngineCommon::SubmitSpline(const void *control_points, const void *indi
ERROR_LOG(G3D, "Something went really wrong, vertex size: %i vs %i", vertexSize, (int)sizeof(SimpleVertex));
}
// If specified as 0, uses 1.
if (tess_u < 1) tess_u = 1;
if (tess_v < 1) tess_v = 1;
// Make an array of pointers to the control points, to get rid of indices.
const SimpleVertex **points = (const SimpleVertex **)managedBuf.Allocate(sizeof(SimpleVertex *) * count_u * count_v);
for (int idx = 0; idx < count_u * count_v; idx++)
@ -539,24 +525,24 @@ void DrawEngineCommon::SubmitSpline(const void *control_points, const void *indi
output.indices = quadIndices_;
output.count = 0;
SplinePatchLocal patch;
patch.tess_u = tess_u;
patch.tess_v = tess_v;
patch.type_u = type_u;
patch.type_v = type_v;
patch.count_u = count_u;
patch.count_v = count_v;
patch.num_patches_u = count_u - 3;
patch.num_patches_v = count_v - 3;
patch.primType = prim_type;
patch.patchFacing = patchFacing;
patch.Init(SPLINE_BUFFER_SIZE / vertexSize);
SplineSurface surface;
surface.tess_u = tess_u;
surface.tess_v = tess_v;
surface.type_u = type_u;
surface.type_v = type_v;
surface.num_points_u = count_u;
surface.num_points_v = count_v;
surface.num_patches_u = count_u - 3;
surface.num_patches_v = count_v - 3;
surface.primType = prim_type;
surface.patchFacing = patchFacing;
surface.Init(SPLINE_BUFFER_SIZE / vertexSize);
if (CanUseHardwareTessellation(prim_type)) {
HardwareTessellation(output, patch, origVertType, points, tessDataTransfer);
HardwareTessellation(output, surface, origVertType, points, tessDataTransfer);
} else {
ControlPoints cpoints(points, count_u * count_v, managedBuf);
SoftwareTessellation(output, patch, origVertType, cpoints);
SoftwareTessellation(output, surface, origVertType, cpoints);
}
u32 vertTypeWithIndex16 = (vertType & ~GE_VTYPE_IDX_MASK) | GE_VTYPE_IDX_16BIT;
@ -618,10 +604,6 @@ void DrawEngineCommon::SubmitBezier(const void *control_points, const void *indi
ERROR_LOG(G3D, "Something went really wrong, vertex size: %i vs %i", vertexSize, (int)sizeof(SimpleVertex));
}
// If specified as 0, uses 1.
if (tess_u < 1) tess_u = 1;
if (tess_v < 1) tess_v = 1;
// Make an array of pointers to the control points, to get rid of indices.
const SimpleVertex **points = (const SimpleVertex **)managedBuf.Allocate(sizeof(SimpleVertex *) * count_u * count_v);
for (int idx = 0; idx < count_u * count_v; idx++)
@ -632,22 +614,22 @@ void DrawEngineCommon::SubmitBezier(const void *control_points, const void *indi
output.indices = quadIndices_;
output.count = 0;
BezierPatch patch;
patch.tess_u = tess_u;
patch.tess_v = tess_v;
patch.count_u = count_u;
patch.count_v = count_v;
patch.num_patches_u = (count_u - 1) / 3;
patch.num_patches_v = (count_v - 1) / 3;
patch.primType = prim_type;
patch.patchFacing = patchFacing;
patch.Init(SPLINE_BUFFER_SIZE / vertexSize);
BezierSurface surface;
surface.tess_u = tess_u;
surface.tess_v = tess_v;
surface.num_points_u = count_u;
surface.num_points_v = count_v;
surface.num_patches_u = (count_u - 1) / 3;
surface.num_patches_v = (count_v - 1) / 3;
surface.primType = prim_type;
surface.patchFacing = patchFacing;
surface.Init(SPLINE_BUFFER_SIZE / vertexSize);
if (CanUseHardwareTessellation(prim_type)) {
HardwareTessellation(output, patch, origVertType, points, tessDataTransfer);
HardwareTessellation(output, surface, origVertType, points, tessDataTransfer);
} else {
ControlPoints cpoints(points, count_u * count_v, managedBuf);
SoftwareTessellation(output, patch, origVertType, cpoints);
SoftwareTessellation(output, surface, origVertType, cpoints);
}
u32 vertTypeWithIndex16 = (vertType & ~GE_VTYPE_IDX_MASK) | GE_VTYPE_IDX_16BIT;

View file

@ -50,21 +50,20 @@ class Spline3DWeight;
// We decode all vertices into a common format for easy interpolation and stuff.
// Not fast but can be optimized later.
struct BezierPatch {
using WeightType = Bezier3DWeight;
int tess_u;
int tess_v;
int count_u;
int count_v;
int type_u;
int type_v;
int num_patches_u;
int num_patches_v;
struct SurfaceInfo {
int tess_u, tess_v;
int num_points_u, num_points_v;
int num_patches_u, num_patches_v;
int type_u, type_v;
GEPatchPrimType primType;
bool patchFacing;
void Init(int maxVertices) {
void Init() {
// If specified as 0, uses 1.
if (tess_u < 1) tess_u = 1;
if (tess_v < 1) tess_v = 1;
switch (g_Config.iSplineBezierQuality) {
case LOW_QUALITY:
tess_u = 2;
@ -74,93 +73,77 @@ struct BezierPatch {
// Don't cut below 2, though.
if (tess_u > 2) tess_u = HALF_CEIL(tess_u);
if (tess_v > 2) tess_v = HALF_CEIL(tess_v);
// Pass through
case HIGH_QUALITY:
// Downsample until it fits, in case crazy tessellation factors are sent.
while ((tess_u + 1) * (tess_v + 1) * num_patches_u * num_patches_v > maxVertices) {
tess_u--;
tess_v--;
}
break;
}
}
};
struct BezierSurface : public SurfaceInfo {
using WeightType = Bezier3DWeight;
int num_verts_per_patch;
void Init(int maxVertices) {
SurfaceInfo::Init();
// Downsample until it fits, in case crazy tessellation factors are sent.
while ((tess_u + 1) * (tess_v + 1) * num_patches_u * num_patches_v > maxVertices) {
tess_u--;
tess_v--;
}
num_verts_per_patch = (tess_u + 1) * (tess_v + 1);
}
int GetTessStart(int patch) const { return 0; }
int GetPointIndex(int patch_u, int patch_v) const { return patch_v * 3 * count_u + patch_u * 3;}
int GetPointIndex(int patch_u, int patch_v) const { return patch_v * 3 * num_points_u + patch_u * 3; }
int GetIndexU(int patch_u, int tile_u) const { return tile_u; }
int GetIndexV(int patch_v, int tile_v) const { return tile_v; }
int GetPatchIndex(int patch_u, int patch_v) const { return patch_v * num_patches_u + patch_u;}
int GetIndex(int index_u, int index_v, int patch_u, int patch_v) const {
return index_v * (tess_u + 1) + index_u + (tess_u + 1) * (tess_v + 1) * GetPatchIndex(patch_u, patch_v);
int patch_index = patch_v * num_patches_u + patch_u;
return index_v * (tess_u + 1) + index_u + num_verts_per_patch * patch_index;
}
void BuildIndex(u16 *indices, int &count) const {
for (int patch_u = 0; patch_u < num_patches_u; ++patch_u) {
for (int patch_v = 0; patch_v < num_patches_v; ++patch_v) {
int patch_index = patch_v * num_patches_u + patch_u;
int total = patch_index * (tess_u + 1) * (tess_v + 1);
int total = patch_index * num_verts_per_patch;
::BuildIndex(indices + count, count, tess_u, tess_v, primType, total);
}
}
}
};
struct SplinePatchLocal {
struct SplineSurface : public SurfaceInfo {
using WeightType = Spline3DWeight;
int tess_u;
int tess_v;
int count_u;
int count_v;
int type_u;
int type_v;
int num_patches_u;
int num_patches_v;
int num_divisions_u;
int num_divisions_v;
bool patchFacing;
GEPatchPrimType primType;
int num_vertices_u;
void Init(int maxVertices) {
switch (g_Config.iSplineBezierQuality) {
case LOW_QUALITY:
tess_u = 2;
tess_v = 2;
break;
case MEDIUM_QUALITY:
// Don't cut below 2, though.
if (tess_u > 2) tess_u = HALF_CEIL(tess_u);
if (tess_v > 2) tess_v = HALF_CEIL(tess_v);
// Pass through
case HIGH_QUALITY:
// Downsample until it fits, in case crazy tessellation factors are sent.
while ((num_patches_u * tess_u + 1) * (num_patches_v * tess_v + 1) > maxVertices) {
tess_u--;
tess_v--;
}
break;
SurfaceInfo::Init();
// Downsample until it fits, in case crazy tessellation factors are sent.
while ((num_patches_u * tess_u + 1) * (num_patches_v * tess_v + 1) > maxVertices) {
tess_u--;
tess_v--;
}
num_divisions_u = num_patches_u * tess_u;
num_divisions_v = num_patches_v * tess_v;
num_vertices_u = num_patches_u * tess_u + 1;
}
int GetTessStart(int patch) const { return (patch == 0) ? 0 : 1; }
int GetPointIndex(int patch_u, int patch_v) const { return patch_v * count_u + patch_u;}
int GetPointIndex(int patch_u, int patch_v) const { return patch_v * num_points_u + patch_u; }
int GetIndexU(int patch_u, int tile_u) const { return patch_u * tess_u + tile_u; }
int GetIndexV(int patch_v, int tile_v) const { return patch_v * tess_v + tile_v; }
int GetIndex(int index_u, int index_v, int patch_u, int patch_v) const {
return index_v * (num_divisions_u + 1) + index_u;
return index_v * num_vertices_u + index_u;
}
void BuildIndex(u16 *indices, int &count) const {
::BuildIndex(indices, count, num_divisions_u, num_divisions_v, primType);
::BuildIndex(indices, count, num_patches_u * tess_u, num_patches_v * tess_v, primType);
}
};

View file

@ -171,45 +171,39 @@ static void ExpandBezier(int &count, int op, const std::vector<SimpleVertex> &si
if (count_u < 4 || count_v < 4)
return;
int tess_u = gstate.getPatchDivisionU();
int tess_v = gstate.getPatchDivisionV();
// If specified as 0, uses 1.
if (tess_u < 1) tess_u = 1;
if (tess_v < 1) tess_v = 1;
BezierPatch patch;
patch.count_u = count_u;
patch.count_v = count_v;
patch.tess_u = tess_u;
patch.tess_v = tess_v;
patch.num_patches_u = (count_u - 1) / 3;
patch.num_patches_v = (count_v - 1) / 3;
patch.primType = gstate.getPatchPrimitiveType();
patch.patchFacing = false;
BezierSurface surface;
surface.num_points_u = count_u;
surface.num_points_v = count_v;
surface.tess_u = gstate.getPatchDivisionU();
surface.tess_v = gstate.getPatchDivisionV();
surface.num_patches_u = (count_u - 1) / 3;
surface.num_patches_v = (count_v - 1) / 3;
surface.primType = gstate.getPatchPrimitiveType();
surface.patchFacing = false;
int size = count_u * count_v;
// Make an array of pointers to the control points, to get rid of indices.
std::vector<const SimpleVertex *> points(count_u * count_v);
for (int idx = 0; idx < count_u * count_v; idx++)
std::vector<const SimpleVertex *> points(size);
for (int idx = 0; idx < size; idx++)
points[idx] = simpleVerts.data() + (!indices.empty() ? indices[idx] : idx);
int total_patches = patch.num_patches_u * patch.num_patches_v;
generatedVerts.resize((tess_u + 1) * (tess_v + 1) * total_patches);
generatedInds.resize(tess_u * tess_v * 6 * total_patches);
int total_patches = surface.num_patches_u * surface.num_patches_v;
generatedVerts.resize((surface.tess_u + 1) * (surface.tess_v + 1) * total_patches);
generatedInds.resize(surface.tess_u * surface.tess_v * 6 * total_patches);
OutputBuffers output;
output.vertices = generatedVerts.data();
output.indices = generatedInds.data();
output.count = 0;
int size = count_u * count_v;
ControlPoints cpoints;
cpoints.pos = (Vec3f *)AllocateAlignedMemory(sizeof(Vec3f) * size, 16);
cpoints.tex = (Vec2f *)AllocateAlignedMemory(sizeof(Vec2f) * size, 16);
cpoints.col = (Vec4f *)AllocateAlignedMemory(sizeof(Vec4f) * size, 16);
cpoints.Convert(points.data(), size);
patch.Init(generatedVerts.size());
SoftwareTessellation(output, patch, gstate.vertType, cpoints);
surface.Init(generatedVerts.size());
SoftwareTessellation(output, surface, gstate.vertType, cpoints);
count = output.count;
FreeAlignedMemory(cpoints.pos);
@ -224,31 +218,26 @@ static void ExpandSpline(int &count, int op, const std::vector<SimpleVertex> &si
if (count_u < 4 || count_v < 4)
return;
int tess_u = gstate.getPatchDivisionU();
int tess_v = gstate.getPatchDivisionV();
// If specified as 0, uses 1.
if (tess_u < 1) tess_u = 1;
if (tess_v < 1) tess_v = 1;
SplinePatchLocal patch;
patch.count_u = count_u;
patch.count_v = count_v;
patch.tess_u = tess_u;
patch.tess_v = tess_v;
patch.type_u = (op >> 16) & 0x3;
patch.type_v = (op >> 18) & 0x3;
patch.num_patches_u = count_u - 3;
patch.num_patches_v = count_v - 3;
patch.primType = gstate.getPatchPrimitiveType();
patch.patchFacing = false;
SplineSurface surface;
surface.num_points_u = count_u;
surface.num_points_v = count_v;
surface.tess_u = gstate.getPatchDivisionU();
surface.tess_v = gstate.getPatchDivisionV();
surface.type_u = (op >> 16) & 0x3;
surface.type_v = (op >> 18) & 0x3;
surface.num_patches_u = count_u - 3;
surface.num_patches_v = count_v - 3;
surface.primType = gstate.getPatchPrimitiveType();
surface.patchFacing = false;
int size = count_u * count_v;
// Make an array of pointers to the control points, to get rid of indices.
std::vector<const SimpleVertex *> points(count_u * count_v);
for (int idx = 0; idx < count_u * count_v; idx++)
std::vector<const SimpleVertex *> points(size);
for (int idx = 0; idx < size; idx++)
points[idx] = simpleVerts.data() + (!indices.empty() ? indices[idx] : idx);
int patch_div_s = patch.num_patches_u * patch.tess_u;
int patch_div_t = patch.num_patches_v * patch.tess_v;
int patch_div_s = surface.num_patches_u * surface.tess_u;
int patch_div_t = surface.num_patches_v * surface.tess_v;
generatedVerts.resize((patch_div_s + 1) * (patch_div_t + 1));
generatedInds.resize(patch_div_s * patch_div_t * 6);
@ -257,15 +246,14 @@ static void ExpandSpline(int &count, int op, const std::vector<SimpleVertex> &si
output.indices = generatedInds.data();
output.count = 0;
int size = count_u * count_v;
ControlPoints cpoints;
cpoints.pos = (Vec3f *)AllocateAlignedMemory(sizeof(Vec3f) * size, 16);
cpoints.tex = (Vec2f *)AllocateAlignedMemory(sizeof(Vec2f) * size, 16);
cpoints.col = (Vec4f *)AllocateAlignedMemory(sizeof(Vec4f) * size, 16);
cpoints.Convert(points.data(), size);
patch.Init(generatedVerts.size());
SoftwareTessellation(output, patch, gstate.vertType, cpoints);
surface.Init(generatedVerts.size());
SoftwareTessellation(output, surface, gstate.vertType, cpoints);
count = output.count;
FreeAlignedMemory(cpoints.pos);