void Terrain::setNormalsBezier() { Vector3 v1, v2, v3, v4; int indexW, indexH; for(int h = 0; h < (height/PORTION) - 4; h += 3){ for(int w = 0; w < (width/PORTION) - 4; w += 3){ BezierPatch p = BezierPatch(coords[w][h], coords[w+1][h], coords[w+2][h], coords[w+3][h], coords[w][h+1], coords[w+1][h+1], coords[w+2][h+1], coords[w+3][h+1], coords[w][h+2], coords[w+1][h+2], coords[w+2][h+2], coords[w+3][h+2], coords[w][h+3], coords[w+1][h+3], coords[w+2][h+3], coords[w+3][h+3]); for(float u = 0.0; u <= 1.0 - BEZ_ACC; u += BEZ_ACC) { for(float v = 0.0; v <= 1.0 - BEZ_ACC; v += BEZ_ACC) { v1 = p.getPoint(u, v); v2 = p.getPoint(u, v+1); v3 = p.getPoint(u+1, v); v4 = p.getPoint(u+1, v+1); indexW = w + (int)(BEZ_POINTS*u); indexH = h + (int)(BEZ_POINTS*v); normal_bezier[indexW][indexH] = (v2-v1).cross(v3-v1).normalize(); normal_bezier[indexW][indexH+1] = (v3-v4).cross(v2-v4).normalize(); } } } } }
void parsePatch(ifstream &is) { double x,y,z; Vector3d v; BezierPatch bp = BezierPatch(); do { is >> z >> x >> y; v = Vector3d(x,y,z); for (int j = 0; j < 3; j++) { min_v(j) = min(min_v(j), v(j)); max_v(j) = max(max_v(j), v(j)); } } while( bp.AddPoint(v) ); patches.push_back(bp); }
void Terrain::drawBezier(Matrix4 c) { //glDisable(GL_LIGHTING); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glMultMatrixf(c.ptr()); // Globals::shader->bind(); // GLint loc = glGetUniformLocationARB(Globals::shader->getPid(), "water_level"); // glUniform1f(loc, Shader::water_level); // // GLint col = glGetAttribLocationARB(Globals::shader->getPid(), "gl_Color"); // GLint bandLoc = glGetUniformLocationARB(Globals::shader->getPid(), "band_width"); // glUniform1f(bandLoc, Shader::band_width); // glDisable(GL_LIGHTING); int indexW, indexH; for(int h = 0; h < (height/PORTION) - 4; h += 3){ for(int w = 0; w < (width/PORTION) - 4; w += 3){ BezierPatch p = BezierPatch(coords[w][h], coords[w+1][h], coords[w+2][h], coords[w+3][h], coords[w][h+1], coords[w+1][h+1], coords[w+2][h+1], coords[w+3][h+1], coords[w][h+2], coords[w+1][h+2], coords[w+2][h+2], coords[w+3][h+2], coords[w][h+3], coords[w+1][h+3], coords[w+2][h+3], coords[w+3][h+3]); for(float u = 0.0; u <= 1.0 - BEZ_ACC; u += BEZ_ACC) { glBegin(GL_TRIANGLE_STRIP); for(float v = 0.0; v <= 1.0; v += BEZ_ACC) { glColor3f(terrain_color[0], terrain_color[1], terrain_color[2]); glVertex3fv(p.getPoint(u, v).ptr()); glVertex3fv(p.getPoint(u + BEZ_ACC, v).ptr()); indexW = w + (int)(BEZ_POINTS*u); indexH = h + (int)(BEZ_POINTS*v); glNormal3fv(normal_bezier[indexW][indexH+1].ptr()); } glEnd(); } } } //Globals::shader->unbind(); glPopMatrix(); //glEnable(GL_LIGHTING); }
void BccWorld::createRandomCurveGeometry() { const unsigned n = 15 * 15; m_allGeo = new GeometryArray; m_allGeo->create(n); BezierPatch bp; bp.resetCvs(); int i=0; bp._contorlPoints[0].y += -.2f; bp._contorlPoints[1].y += -.4f; bp._contorlPoints[2].y += -.4f; bp._contorlPoints[3].y += -.5f; bp._contorlPoints[4].y += -.5f; bp._contorlPoints[5].y += .1f; bp._contorlPoints[6].y += .5f; bp._contorlPoints[7].y += .1f; bp._contorlPoints[9].y += .5f; bp._contorlPoints[10].y += .5f; bp._contorlPoints[13].y += -.4f; bp._contorlPoints[14].y += -.85f; bp._contorlPoints[15].y += -.21f; i=0; for(;i<16;i++) { bp._contorlPoints[i] *= 80.f; bp._contorlPoints[i].y += 10.f; bp._contorlPoints[i].z -= 10.f; } RandomCurve rc; rc.create(m_allGeo, 15, 15, &bp, Vector3F(-.15f, 1.f, 0.33f), 11, 21, .9f); }
void FitTest::createRandomCurves() { const unsigned n = 15 * 15; m_allGeo->create(n); BezierPatch bp; bp.resetCvs(); int i=0; bp._contorlPoints[0].y += -.2f; bp._contorlPoints[1].y += -.4f; bp._contorlPoints[2].y += -.4f; bp._contorlPoints[3].y += -.5f; bp._contorlPoints[4].y += -.5f; bp._contorlPoints[5].y += .1f; bp._contorlPoints[6].y += .5f; bp._contorlPoints[7].y += .1f; bp._contorlPoints[9].y += .5f; bp._contorlPoints[10].y += .5f; bp._contorlPoints[13].y += -.4f; bp._contorlPoints[14].y += -.85f; bp._contorlPoints[15].y += -.21f; i=0; for(;i<16;i++) { bp._contorlPoints[i] *= 80.f; bp._contorlPoints[i].y += 10.f; bp._contorlPoints[i].z -= 10.f; } RandomCurve rc; rc.create(m_allGeo, 15, 15, &bp, Vector3F(.035f, 1.84f, 0.03f), 11, 17, .89f); }
vector<Triangle*>* BezierPatchTesselator::tesselate(int mode, bool center_test, float threshold) { if (mode == UNIFORM_MODE) { cout << "uniform\n"; vector<Triangle*>* triangles = new vector<Triangle*>(); for (int patchNum = 0; patchNum < patches.size(); patchNum++) { BezierPatch p = *patches[patchNum]; float u = 0; while (u < 1) { float v = 0; while (v < 1) { Vector2f a, b, c; a = (Vector2f() << u, v).finished(); b = (Vector2f() << u + threshold, v).finished(); c = (Vector2f() << u, v + threshold).finished(); Vector3f norm = (p.evaluate(a) - p.evaluate(b)).cross(p.evaluate(a) - p.evaluate(c)).normalized(); triangles->push_back(new Triangle(p.evaluate(a), p.evaluate(b), p.evaluate(c), norm, norm, norm)); // triangles->push_back(new Triangle(p.evaluate(a), p.evaluate(b), p.evaluate(c), p.findNormal(a), p.findNormal(b), p.findNormal(c))); a = (Vector2f() << u + threshold, v + threshold).finished(); b = (Vector2f() << u + threshold, v).finished(); c = (Vector2f() << u, v + threshold).finished(); norm = (p.evaluate(a) - p.evaluate(b)).cross(p.evaluate(a) - p.evaluate(c)).normalized(); triangles->push_back(new Triangle(p.evaluate(a), p.evaluate(b), p.evaluate(c), norm, norm, norm)); // triangles->push_back(new Triangle(p.evaluate(c), p.evaluate(b), p.evaluate(a), p.findNormal(c), p.findNormal(b), p.findNormal(a))); v += threshold; } u += threshold; } } return triangles; } vector<Triangle*>* triangles = new vector<Triangle*>(); for (int patchNum = 0; patchNum < patches.size(); patchNum++) { Vector2f vertices[3]; vertices[0] = (Vector2f() << 0, 0).finished(); vertices[1] = (Vector2f() << 0, 1).finished(); vertices[2] = (Vector2f() << 1, 0).finished(); vector<Triangle*>* temp = tesselateTriangle(mode, center_test, vertices, *(patches[patchNum]), threshold); append(triangles, temp); vertices[0] = (Vector2f() << 1, 1).finished(); vertices[1] = (Vector2f() << 0, 1).finished(); vertices[2] = (Vector2f() << 1, 0).finished(); temp = tesselateTriangle(mode, center_test, vertices, *(patches[patchNum]), threshold); append(triangles, temp); } return triangles; }
static void _BezierPatchHighQuality(u8 *&dest, u16 *&indices, int &count, int tess_u, int tess_v, const BezierPatch &patch, u32 origVertType, int maxVertices) { const float third = 1.0f / 3.0f; // Downsample until it fits, in case crazy tesselation factors are sent. while ((tess_u + 1) * (tess_v + 1) > maxVertices) { tess_u /= 2; tess_v /= 2; } // First compute all the vertices and put them in an array SimpleVertex *&vertices = (SimpleVertex*&)dest; Vec3Packedf *horiz = new Vec3Packedf[(tess_u + 1) * 4]; Vec3Packedf *horiz2 = horiz + (tess_u + 1) * 1; Vec3Packedf *horiz3 = horiz + (tess_u + 1) * 2; Vec3Packedf *horiz4 = horiz + (tess_u + 1) * 3; Vec3Packedf *derivU1 = new Vec3Packedf[(tess_u + 1) * 4]; Vec3Packedf *derivU2 = derivU1 + (tess_u + 1) * 1; Vec3Packedf *derivU3 = derivU1 + (tess_u + 1) * 2; Vec3Packedf *derivU4 = derivU1 + (tess_u + 1) * 3; bool computeNormals = patch.computeNormals; // Precompute the horizontal curves to we only have to evaluate the vertical ones. for (int i = 0; i < tess_u + 1; i++) { float u = ((float)i / (float)tess_u); horiz[i] = Bernstein3D(patch.points[0]->pos, patch.points[1]->pos, patch.points[2]->pos, patch.points[3]->pos, u); horiz2[i] = Bernstein3D(patch.points[4]->pos, patch.points[5]->pos, patch.points[6]->pos, patch.points[7]->pos, u); horiz3[i] = Bernstein3D(patch.points[8]->pos, patch.points[9]->pos, patch.points[10]->pos, patch.points[11]->pos, u); horiz4[i] = Bernstein3D(patch.points[12]->pos, patch.points[13]->pos, patch.points[14]->pos, patch.points[15]->pos, u); if (computeNormals) { derivU1[i] = Bernstein3DDerivative(patch.points[0]->pos, patch.points[1]->pos, patch.points[2]->pos, patch.points[3]->pos, u); derivU2[i] = Bernstein3DDerivative(patch.points[4]->pos, patch.points[5]->pos, patch.points[6]->pos, patch.points[7]->pos, u); derivU3[i] = Bernstein3DDerivative(patch.points[8]->pos, patch.points[9]->pos, patch.points[10]->pos, patch.points[11]->pos, u); derivU4[i] = Bernstein3DDerivative(patch.points[12]->pos, patch.points[13]->pos, patch.points[14]->pos, patch.points[15]->pos, u); } } for (int tile_v = 0; tile_v < tess_v + 1; ++tile_v) { for (int tile_u = 0; tile_u < tess_u + 1; ++tile_u) { float u = ((float)tile_u / (float)tess_u); float v = ((float)tile_v / (float)tess_v); float bu = u; float bv = v; // TODO: Should be able to precompute the four curves per U, then just Bernstein per V. Will benefit large tesselation factors. const Vec3Packedf &pos1 = horiz[tile_u]; const Vec3Packedf &pos2 = horiz2[tile_u]; const Vec3Packedf &pos3 = horiz3[tile_u]; const Vec3Packedf &pos4 = horiz4[tile_u]; SimpleVertex &vert = vertices[tile_v * (tess_u + 1) + tile_u]; if (computeNormals) { const Vec3Packedf &derivU1_ = derivU1[tile_u]; const Vec3Packedf &derivU2_ = derivU2[tile_u]; const Vec3Packedf &derivU3_ = derivU3[tile_u]; const Vec3Packedf &derivU4_ = derivU4[tile_u]; Vec3Packedf derivU = Bernstein3D(derivU1_, derivU2_, derivU3_, derivU4_, bv); Vec3Packedf derivV = Bernstein3DDerivative(pos1, pos2, pos3, pos4, bv); vert.nrm = Cross(derivU, derivV).Normalized(); if (patch.patchFacing) vert.nrm *= -1.0f; } else { vert.nrm.SetZero(); } vert.pos = Bernstein3D(pos1, pos2, pos3, pos4, bv); if ((origVertType & GE_VTYPE_TC_MASK) == 0) { // Generate texcoord vert.uv[0] = u + patch.u_index * third; vert.uv[1] = v + patch.v_index * third; } else { // Sample UV from control points patch.sampleTexUV(u, v, vert.uv[0], vert.uv[1]); } if (origVertType & GE_VTYPE_COL_MASK) { patch.sampleColor(u, v, vert.color); } else { memcpy(vert.color, patch.points[0]->color, 4); } } } delete[] derivU1; delete[] horiz; GEPatchPrimType prim_type = patch.primType; // Combine the vertices into triangles. for (int tile_v = 0; tile_v < tess_v; ++tile_v) { for (int tile_u = 0; tile_u < tess_u; ++tile_u) { int total = patch.index * (tess_u + 1) * (tess_v + 1); int idx0 = total + tile_v * (tess_u + 1) + tile_u; int idx1 = total + tile_v * (tess_u + 1) + tile_u + 1; int idx2 = total + (tile_v + 1) * (tess_u + 1) + tile_u; int idx3 = total + (tile_v + 1) * (tess_u + 1) + tile_u + 1; CopyQuadIndex(indices, prim_type, idx0, idx1, idx2, idx3); count += 6; } } dest += (tess_u + 1) * (tess_v + 1) * sizeof(SimpleVertex); }
void _BezierPatchHighQuality(u8 *&dest, int &count, int tess_u, int tess_v, const BezierPatch &patch, u32 origVertType) { const float third = 1.0f / 3.0f; // Full correct tesselation of bezier patches. // Note: Does not handle splines correctly. // First compute all the vertices and put them in an array SimpleVertex *vertices = new SimpleVertex[(tess_u + 1) * (tess_v + 1)]; Vec3Packedf *horiz = new Vec3Packedf[(tess_u + 1) * 4]; Vec3Packedf *horiz2 = horiz + (tess_u + 1) * 1; Vec3Packedf *horiz3 = horiz + (tess_u + 1) * 2; Vec3Packedf *horiz4 = horiz + (tess_u + 1) * 3; // Precompute the horizontal curves to we only have to evaluate the vertical ones. for (int i = 0; i < tess_u + 1; i++) { float u = ((float)i / (float)tess_u); horiz[i] = Bernstein3D(patch.points[0]->pos, patch.points[1]->pos, patch.points[2]->pos, patch.points[3]->pos, u); horiz2[i] = Bernstein3D(patch.points[4]->pos, patch.points[5]->pos, patch.points[6]->pos, patch.points[7]->pos, u); horiz3[i] = Bernstein3D(patch.points[8]->pos, patch.points[9]->pos, patch.points[10]->pos, patch.points[11]->pos, u); horiz4[i] = Bernstein3D(patch.points[12]->pos, patch.points[13]->pos, patch.points[14]->pos, patch.points[15]->pos, u); } bool computeNormals = gstate.isLightingEnabled(); for (int tile_v = 0; tile_v < tess_v + 1; ++tile_v) { for (int tile_u = 0; tile_u < tess_u + 1; ++tile_u) { float u = ((float)tile_u / (float)tess_u); float v = ((float)tile_v / (float)tess_v); float bu = u; float bv = v; // TODO: Should be able to precompute the four curves per U, then just Bernstein per V. Will benefit large tesselation factors. const Vec3Packedf &pos1 = horiz[tile_u]; const Vec3Packedf &pos2 = horiz2[tile_u]; const Vec3Packedf &pos3 = horiz3[tile_u]; const Vec3Packedf &pos4 = horiz4[tile_u]; SimpleVertex &vert = vertices[tile_v * (tess_u + 1) + tile_u]; if (computeNormals) { Vec3Packedf derivU1 = Bernstein3DDerivative(patch.points[0]->pos, patch.points[1]->pos, patch.points[2]->pos, patch.points[3]->pos, bu); Vec3Packedf derivU2 = Bernstein3DDerivative(patch.points[4]->pos, patch.points[5]->pos, patch.points[6]->pos, patch.points[7]->pos, bu); Vec3Packedf derivU3 = Bernstein3DDerivative(patch.points[8]->pos, patch.points[9]->pos, patch.points[10]->pos, patch.points[11]->pos, bu); Vec3Packedf derivU4 = Bernstein3DDerivative(patch.points[12]->pos, patch.points[13]->pos, patch.points[14]->pos, patch.points[15]->pos, bu); Vec3Packedf derivU = Bernstein3D(derivU1, derivU2, derivU3, derivU4, bv); Vec3Packedf derivV = Bernstein3DDerivative(pos1, pos2, pos3, pos4, bv); // TODO: Interpolate normals instead of generating them, if available? vert.nrm = Cross(derivU, derivV).Normalized(); if (gstate.patchfacing & 1) vert.nrm *= -1.0f; } else { vert.nrm.SetZero(); } vert.pos = Bernstein3D(pos1, pos2, pos3, pos4, bv); if ((origVertType & GE_VTYPE_TC_MASK) == 0) { // Generate texcoord vert.uv[0] = u + patch.u_index * third; vert.uv[1] = v + patch.v_index * third; } else { // Sample UV from control points patch.sampleTexUV(u, v, vert.uv[0], vert.uv[1]); } if (origVertType & GE_VTYPE_COL_MASK) { patch.sampleColor(u, v, vert.color); } else { memcpy(vert.color, patch.points[0]->color, 4); } } } delete[] horiz; // Tesselate. TODO: Use indices so we only need to emit 4 vertices per pair of triangles instead of six. for (int tile_v = 0; tile_v < tess_v; ++tile_v) { for (int tile_u = 0; tile_u < tess_u; ++tile_u) { float u = ((float)tile_u / (float)tess_u); float v = ((float)tile_v / (float)tess_v); const SimpleVertex *v0 = &vertices[tile_v * (tess_u + 1) + tile_u]; const SimpleVertex *v1 = &vertices[tile_v * (tess_u + 1) + tile_u + 1]; const SimpleVertex *v2 = &vertices[(tile_v + 1) * (tess_u + 1) + tile_u]; const SimpleVertex *v3 = &vertices[(tile_v + 1) * (tess_u + 1) + tile_u + 1]; CopyQuad(dest, v0, v1, v2, v3); count += 6; } } delete[] vertices; }
void TesselateBezierPatch(u8 *&dest, int &count, const BezierPatch &patch, u32 origVertType) { const float third = 1.0f / 3.0f; if (g_Config.bLowQualitySplineBezier) { // Fast and easy way - just draw the control points, generate some very basic normal vector subsitutes. // Very inaccurate though but okay for Loco Roco. Maybe should keep it as an option. float u_base = patch.u_index / 3.0f; float v_base = patch.v_index / 3.0f; for (int tile_v = 0; tile_v < 3; tile_v++) { for (int tile_u = 0; tile_u < 3; tile_u++) { int point_index = tile_u + tile_v * 4; SimpleVertex v0 = *patch.points[point_index]; SimpleVertex v1 = *patch.points[point_index+1]; SimpleVertex v2 = *patch.points[point_index+4]; SimpleVertex v3 = *patch.points[point_index+5]; // Generate UV. TODO: Do this even if UV specified in control points? if ((origVertType & GE_VTYPE_TC_MASK) == 0) { float u = u_base + tile_u * third; float v = v_base + tile_v * third; v0.uv[0] = u; v0.uv[1] = v; v1.uv[0] = u + third; v1.uv[1] = v; v2.uv[0] = u; v2.uv[1] = v + third; v3.uv[0] = u + third; v3.uv[1] = v + third; } // Generate normal if lighting is enabled (otherwise there's no point). // This is a really poor quality algorithm, we get facet normals. if (gstate.isLightingEnabled()) { Vec3f norm = Cross(v1.pos - v0.pos, v2.pos - v0.pos); norm.Normalize(); if (gstate.patchfacing & 1) norm *= -1.0f; v0.nrm = norm; v1.nrm = norm; v2.nrm = norm; v3.nrm = norm; } CopyQuad(dest, &v0, &v1, &v2, &v3); count += 6; } } } else { // Full correct tesselation of bezier patches. // Note: Does not handle splines correctly. int tess_u = gstate.getPatchDivisionU(); int tess_v = gstate.getPatchDivisionV(); // First compute all the vertices and put them in an array SimpleVertex *vertices = new SimpleVertex[(tess_u + 1) * (tess_v + 1)]; Vec3f *horiz = new Vec3f[(tess_u + 1) * 4]; Vec3f *horiz2 = horiz + (tess_u + 1) * 1; Vec3f *horiz3 = horiz + (tess_u + 1) * 2; Vec3f *horiz4 = horiz + (tess_u + 1) * 3; // Precompute the horizontal curves to we only have to evaluate the vertical ones. for (int i = 0; i < tess_u + 1; i++) { float u = ((float)i / (float)tess_u); horiz[i] = Bernstein3D(patch.points[0]->pos, patch.points[1]->pos, patch.points[2]->pos, patch.points[3]->pos, u); horiz2[i] = Bernstein3D(patch.points[4]->pos, patch.points[5]->pos, patch.points[6]->pos, patch.points[7]->pos, u); horiz3[i] = Bernstein3D(patch.points[8]->pos, patch.points[9]->pos, patch.points[10]->pos, patch.points[11]->pos, u); horiz4[i] = Bernstein3D(patch.points[12]->pos, patch.points[13]->pos, patch.points[14]->pos, patch.points[15]->pos, u); } bool computeNormals = gstate.isLightingEnabled(); for (int tile_v = 0; tile_v < tess_v + 1; ++tile_v) { for (int tile_u = 0; tile_u < tess_u + 1; ++tile_u) { float u = ((float)tile_u / (float)tess_u); float v = ((float)tile_v / (float)tess_v); float bu = u; float bv = v; // TODO: Should be able to precompute the four curves per U, then just Bernstein per V. Will benefit large tesselation factors. const Vec3f &pos1 = horiz[tile_u]; const Vec3f &pos2 = horiz2[tile_u]; const Vec3f &pos3 = horiz3[tile_u]; const Vec3f &pos4 = horiz4[tile_u]; SimpleVertex &vert = vertices[tile_v * (tess_u + 1) + tile_u]; if (computeNormals) { Vec3f derivU1 = Bernstein3DDerivative(patch.points[0]->pos, patch.points[1]->pos, patch.points[2]->pos, patch.points[3]->pos, bu); Vec3f derivU2 = Bernstein3DDerivative(patch.points[4]->pos, patch.points[5]->pos, patch.points[6]->pos, patch.points[7]->pos, bu); Vec3f derivU3 = Bernstein3DDerivative(patch.points[8]->pos, patch.points[9]->pos, patch.points[10]->pos, patch.points[11]->pos, bu); Vec3f derivU4 = Bernstein3DDerivative(patch.points[12]->pos, patch.points[13]->pos, patch.points[14]->pos, patch.points[15]->pos, bu); Vec3f derivU = Bernstein3D(derivU1, derivU2, derivU3, derivU4, bv); Vec3f derivV = Bernstein3DDerivative(pos1, pos2, pos3, pos4, bv); // TODO: Interpolate normals instead of generating them, if available? vert.nrm = Cross(derivU, derivV).Normalized(); if (gstate.patchfacing & 1) vert.nrm *= -1.0f; } else { vert.nrm.SetZero(); } vert.pos = Bernstein3D(pos1, pos2, pos3, pos4, bv); if ((origVertType & GE_VTYPE_TC_MASK) == 0) { // Generate texcoord vert.uv[0] = u + patch.u_index * third; vert.uv[1] = v + patch.v_index * third; } else { // Sample UV from control points patch.sampleTexUV(u, v, vert.uv[0], vert.uv[1]); } if (origVertType & GE_VTYPE_COL_MASK) { patch.sampleColor(u, v, vert.color); } else { memcpy(vert.color, patch.points[0]->color, 4); } } } delete [] horiz; // Tesselate. TODO: Use indices so we only need to emit 4 vertices per pair of triangles instead of six. for (int tile_v = 0; tile_v < tess_v; ++tile_v) { for (int tile_u = 0; tile_u < tess_u; ++tile_u) { float u = ((float)tile_u / (float)tess_u); float v = ((float)tile_v / (float)tess_v); const SimpleVertex *v0 = &vertices[tile_v * (tess_u + 1) + tile_u]; const SimpleVertex *v1 = &vertices[tile_v * (tess_u + 1) + tile_u + 1]; const SimpleVertex *v2 = &vertices[(tile_v + 1) * (tess_u + 1) + tile_u]; const SimpleVertex *v3 = &vertices[(tile_v + 1) * (tess_u + 1) + tile_u + 1]; CopyQuad(dest, v0, v1, v2, v3); count += 6; } } delete [] vertices; } }