TerrainPatchData::TerrainPatchData(TerrainQuadtree* tree) :
        tree(tree),
        tessellation(tree->getPlanetarySurface()->getTessellation()),
        vertices(tessellation + 3, std::vector<Ogre::Vector3>(tessellation + 3)),
        normals(tessellation + 3, std::vector<Ogre::Vector3>(tessellation + 3)),
        heights(tessellation + 3, std::vector<Ogre::Real>(tessellation + 3, 0.0)) {
    int patchSize = tree->getSize();
    Ogre::Real halfPatchSize = Ogre::Real(patchSize / 2);
    Ogre::Real delta = (Ogre::Real)patchSize * (1.0 / (Ogre::Real)tessellation);

    Ogre::SceneNode* patchSceneNode = tree->getSceneNode();
    Ogre::SceneNode* planetarySceneNode = tree->getPlanetarySurface()->getSceneNode();

    Ogre::Real planetaryRadius = (Ogre::Real)tree->getPlanetarySurface()->getRadius();

    for (int z = -1; z < tessellation + 2; ++z) {
        for (int x = -1; x < tessellation + 2; ++x) {
            Ogre::Vector3 position(-halfPatchSize + delta * x, 0.0, -halfPatchSize + delta * z);
            Ogre::Vector3 planetaryPosition = planetarySceneNode->convertWorldToLocalPosition(patchSceneNode->convertLocalToWorldPosition(position));
            planetaryPosition = mapToSphere(planetaryPosition, planetaryRadius);
            Ogre::Vector3 normal = planetaryPosition.normalisedCopy();
            Ogre::Real height = tree->getPlanetarySurface()->getNoiseFunction()->getValue(planetaryPosition);

            planetaryPosition = planetarySceneNode->convertLocalToWorldPosition(planetaryPosition);
            position = patchSceneNode->convertWorldToLocalPosition(planetaryPosition);

            normal = (tree->getRootSceneNode()->getOrientation().Inverse() * normal).normalisedCopy();

            vertices[z + 1][x + 1] = position;
            normals[z + 1][x + 1] = normal;
            heights[z + 1][x + 1] = height;
        }
    }

    for (int z = 0; z < tessellation + 1; ++z) {
        for (int x = 0; x < tessellation + 1; ++x) {
            processedPositions.push_back(getVertex(x, z));
            processedNormals.push_back(getVertexNormal(x, z));
            processedTextureCoords.push_back(getNormal(x, z));
        }
    }

    // Add triangles.
    for (int z = 0; z < tessellation; ++z) {
        int zOffset = z * (tessellation + 1);
        for (int x = 0; x < tessellation; ++x) {
            int indices[4] = {
                zOffset + x,
                zOffset + x + 1,
                zOffset + tessellation + 1 + x,
                zOffset + tessellation + 1 + x + 1
            };

            processedIndices.push_back(indices[0]);
            processedIndices.push_back(indices[2]);
            processedIndices.push_back(indices[1]);
            processedIndices.push_back(indices[2]);
            processedIndices.push_back(indices[3]);
            processedIndices.push_back(indices[1]);
        }
    }
}