3
\$\begingroup\$

For my chunk generator for my game, it currently uses a triple nested loop, which seems very inefficient. It also seems like I could cut down a lot of code in the function somehow. Does anyone have any ideas?

void Chunk::Generate() {
    const int octaves = 6;
    const float heightMod = 4.0f;
    const float densityMod = 8.0f;
    const float horizontalFrequency = 1.0f / 256.0f;
    const float frontalFrequency = 1.0f / 256.0f;
    const float heightMapSize = 1.0f / 512.0f;
    for (int z = m_chunkZ; z < m_chunkZ + m_chunkDepth; z++) {
        for (int x = m_chunkX; x < m_chunkX + m_chunkWidth; x++) {
            // the last type generated
            VoxelType lastType = VoxelType::SAND;
            // the height used for this part of the chunk
            int height = static_cast<int>(noise_fractal_brownian_motion(m_noise, octaves, x * horizontalFrequency, heightMapSize, z * frontalFrequency) * (m_chunkHeight * heightMod));
            int minY = m_chunkY;
            int maxY = m_chunkY + height;
            for (int y = minY; y < maxY; y++) {
                const float colorMinCap = -0.45f;
                const float colorMaxCap = 0.45f;
                const float densityXFrequency = 1.0f / 64.0f;
                const float densityZFrequency = 1.0f / 256.0f;
                const float verticalFrequency = 1.0f / (static_cast<float>(abs(height)) * heightMod);
                float density = noise_fractal_brownian_motion(m_noise, octaves, x * densityXFrequency, abs(y) * verticalFrequency, z * densityZFrequency) * (abs(height) * densityMod);
                // todo: new noise map for color
                float colorR = noise_fractal_brownian_motion(m_noise, octaves, x * densityXFrequency, 0.0f, z * densityZFrequency - 0.15f);
                float colorG = noise_fractal_brownian_motion(m_noise, octaves, x * densityXFrequency - 0.08f, 0.0f, z * densityZFrequency);
                float colorB = noise_fractal_brownian_motion(m_noise, octaves, x * densityXFrequency - 0.32f, 0.0f, z * densityZFrequency - 0.64f);
                if (colorR >= colorMaxCap)
                    colorR = colorMaxCap;
                if (colorG >= colorMaxCap)
                    colorG = colorMaxCap;
                if (colorB >= colorMaxCap)
                    colorB = colorMaxCap;
                if (colorR <= colorMinCap)
                    colorR = colorMinCap;
                if (colorG <= colorMinCap)
                    colorG = colorMinCap;
                if (colorB <= colorMinCap)
                    colorB = colorMinCap;
                if (density >= 0.0f) {
                    VoxelType type = ((y == maxY - 1 && lastType == VoxelType::DIRT) ? VoxelType::GRASS : _GetTypeFromY(y));
                    // check if we should replace a dirt type with grass
                    if (y == maxY - 1 && type == VoxelType::DIRT) type = VoxelType::GRASS;
                    if (type == VoxelType::WATER) {
                        m_transparentRenderer.AddVoxel(Position(x, y, z), Voxel(VoxelType::WATER, 0));
                    }
                    else {
                        if (y <= m_seaLevel)
                            m_underwaterRenderer.AddVoxel(Position(x, y, z), Voxel(type, colorR, colorG, colorB));
                        else
                            m_opaqueRenderer.AddVoxel(Position(x, y, z), Voxel(type, colorR, colorG, colorB));
                    }
                    lastType = _GetTypeFromY(y);
                }
            }
            // no matter what, add one water voxel at sea level
            m_transparentRenderer.AddVoxel(Position(x, m_seaLevel, z), Voxel(VoxelType::WATER, 0));
            // no matter what, add one sand voxel at 0 (bottom of the water)
            m_underwaterRenderer.AddVoxel(Position(x, 0, z), Voxel(VoxelType::SAND, 0));
            lastType = VoxelType::SAND;
        }
    }
}
\$\endgroup\$
1
\$\begingroup\$

For your:

            if (colorR >= colorMaxCap)
                colorR = colorMaxCap;
            if (colorG >= colorMaxCap)
                colorG = colorMaxCap;
            if (colorB >= colorMaxCap)
                colorB = colorMaxCap;
            if (colorR <= colorMinCap)
                colorR = colorMinCap;
            if (colorG <= colorMinCap)
                colorG = colorMinCap;
            if (colorB <= colorMinCap)
                colorB = colorMinCap;

This can be reduced using a suggestion from here:

inline float clip(float n, float lower, float upper) {
  return std::max(lower, std::min(n, upper));
}

And then do:

clip(colorR, colorMinCap, colorMaxCap);
clip(colorG, colorMinCap, colorMaxCap);
clip(colorB, colorMinCap, colorMaxCap);

I don't believe you will lose any performance from that.

Also, try adding OpenMP to your project and right before the first for loop add:

#pragma omp parallel for

This should parallelize the first loop (don't nest pragmas), and make your code run faster. Although, I am assuming (after a not too thorough review) that the any particular iteration does not depend on a previous iteration. Although it should, as Perlin Noise is "embarrassingly parallel".

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.