Fluid3InitializeSource::Fluid3InitializeSource(ProgramFactory& factory,
    int xSize, int ySize, int zSize, int numXThreads, int numYThreads,
    int numZThreads, std::shared_ptr<ConstantBuffer> const& parameters)
    :
    mNumXGroups(xSize/numXThreads),
    mNumYGroups(ySize/numYThreads),
    mNumZGroups(zSize/numZThreads)
{
    // Create the resources for generating velocity from vortices.
    mVortex = std::make_shared<ConstantBuffer>(sizeof(Vortex), true);
    mVelocity0 = std::make_shared<Texture3>(DF_R32G32B32A32_FLOAT, xSize,
        ySize, zSize);
    mVelocity0->SetUsage(Resource::SHADER_OUTPUT);
    mVelocity1 = std::make_shared<Texture3>(DF_R32G32B32A32_FLOAT, xSize,
        ySize, zSize);
    mVelocity1->SetUsage(Resource::SHADER_OUTPUT);

    // Create the resources for generating velocity from wind and gravity.
    mExternal = std::make_shared<ConstantBuffer>(sizeof(External), false);
    External& e = *mExternal->Get<External>();
    e.densityProducer = { 0.5f, 0.5f, 0.5f, 0.0f };
    e.densityPData = { 0.01f, 16.0f, 0.0f, 0.0f };
    e.densityConsumer = { 0.75f, 0.75f, 0.75f, 0.0f };
    e.densityCData = { 0.01f, 0.0f, 0.0f, 0.0f };
    e.gravity = { 0.0f, 0.0f, 0.0f, 0.0f };
    e.windData = { 0.001f, 0.0f, 0.0f, 0.0f };
    mSource = std::make_shared<Texture3>(DF_R32G32B32A32_FLOAT, xSize, ySize,
        zSize);
    mSource->SetUsage(Resource::SHADER_OUTPUT);

    // Create the shader for generating velocity from vortices.
    int i = factory.GetAPI();
    factory.PushDefines();
    factory.defines.Set("NUM_X_THREADS", numXThreads);
    factory.defines.Set("NUM_Y_THREADS", numYThreads);
    factory.defines.Set("NUM_Z_THREADS", numZThreads);
    std::shared_ptr<ComputeShader> cshader;

    mGenerateVortex = factory.CreateFromSource(*msGenerateSource[i]);
    if (mGenerateVortex)
    {
        cshader = mGenerateVortex->GetCShader();
        cshader->Set("Parameters", parameters);
        cshader->Set("Vortex", mVortex);
        cshader->Set("inVelocity", mVelocity0);
        cshader->Set("outVelocity", mVelocity1);
    }

    // Create the shader for generating the sources to the fluid simulation.
    mInitializeSource = factory.CreateFromSource(*msInitializeSource[i]);
    if (mInitializeSource)
    {
        cshader = mInitializeSource->GetCShader();
        cshader->Set("Parameters", parameters);
        cshader->Set("External", mExternal);
        cshader->Set("source", mSource);
    }

    factory.PopDefines();
}
Fluid3ComputeDivergence::Fluid3ComputeDivergence(ProgramFactory& factory,
    int xSize, int ySize, int zSize, int numXThreads, int numYThreads,
    int numZThreads, std::shared_ptr<ConstantBuffer> const& parameters)
    :
    mNumXGroups(xSize/numXThreads),
    mNumYGroups(ySize/numYThreads),
    mNumZGroups(zSize/numZThreads)
{
    mDivergence = std::make_shared<Texture3>(DF_R32_FLOAT, xSize, ySize,
        zSize);
    mDivergence->SetUsage(Resource::SHADER_OUTPUT);

    int i = factory.GetAPI();
    factory.PushDefines();
    factory.defines.Set("NUM_X_THREADS", numXThreads);
    factory.defines.Set("NUM_Y_THREADS", numYThreads);
    factory.defines.Set("NUM_Z_THREADS", numZThreads);
    mComputeDivergence = factory.CreateFromSource(*msSource[i]);
    if (mComputeDivergence)
    {
        mComputeDivergence->GetCShader()->Set("Parameters", parameters);
        mComputeDivergence->GetCShader()->Set("divergence", mDivergence);
    }

    factory.PopDefines();
}
Fluid2SolvePoisson::Fluid2SolvePoisson(ProgramFactory& factory, int xSize,
    int ySize, int numXThreads, int numYThreads,
    std::shared_ptr<ConstantBuffer> const& parameters, int numIterations)
    :
    mNumXGroups(xSize/numXThreads),
    mNumYGroups(ySize/numYThreads),
    mNumIterations(numIterations)
{
    mPoisson0 = std::make_shared<Texture2>(DF_R32_FLOAT, xSize, ySize);
    mPoisson0->SetUsage(Resource::SHADER_OUTPUT);
    mPoisson1 = std::make_shared<Texture2>(DF_R32_FLOAT, xSize, ySize);
    mPoisson1->SetUsage(Resource::SHADER_OUTPUT);

    int i = factory.GetAPI();
    factory.PushDefines();
    factory.defines.Set("NUM_X_THREADS", numXThreads);
    factory.defines.Set("NUM_Y_THREADS", numYThreads);

    // For zeroing mPoisson0 on the GPU.
    mZeroPoisson = factory.CreateFromSource(*msZeroSource[i]);
    if (mZeroPoisson)
    {
        mZeroPoisson->GetCShader()->Set("poisson", mPoisson0);
    }

    // Create the shader for generating velocity from vortices.
    mSolvePoisson = factory.CreateFromSource(*msSolveSource[i]);
    if (mSolvePoisson)
    {
        mSolvePoisson->GetCShader()->Set("Parameters", parameters);
    }

    factory.defines.Clear();
    factory.defines.Set("USE_ZERO_X_EDGE", 1);
    factory.defines.Set("NUM_Y_THREADS", numYThreads);
    mWriteXEdge = factory.CreateFromSource(*msEnforceSource[i]);

    factory.defines.Clear();
    factory.defines.Set("USE_ZERO_Y_EDGE", 1);
    factory.defines.Set("NUM_X_THREADS", numXThreads);
    mWriteYEdge = factory.CreateFromSource(*msEnforceSource[i]);

    factory.PopDefines();
}
Fluid2InitializeState::Fluid2InitializeState(ProgramFactory& factory,
    int xSize, int ySize, int numXThreads, int numYThreads)
    :
    mNumXGroups(xSize/numXThreads),
    mNumYGroups(ySize/numYThreads)
{
    // Use a Mersenne twister engine for random numbers.
    std::mt19937 mte;
    std::uniform_real_distribution<float> unirnd(0.0f, 1.0f);

    // Initial density values are randomly generated.
    mDensity = std::make_shared<Texture2>(DF_R32_FLOAT, xSize, ySize);
    float* data = mDensity->Get<float>();
    for (unsigned int i = 0; i < mDensity->GetNumElements(); ++i, ++data)
    {
        *data = unirnd(mte);
    }

    // Initial velocity values are zero.
    mVelocity = std::make_shared<Texture2>(DF_R32G32_FLOAT, xSize, ySize);
    memset(mVelocity->GetData(), 0, mVelocity->GetNumBytes());

    // The states at time 0 and time -dt are initialized by a compute shader.
    mStateTm1 = std::make_shared<Texture2>(DF_R32G32B32A32_FLOAT, xSize,
        ySize);
    mStateTm1->SetUsage(Resource::SHADER_OUTPUT);

    mStateT = std::make_shared<Texture2>(DF_R32G32B32A32_FLOAT, xSize, ySize);
    mStateT->SetUsage(Resource::SHADER_OUTPUT);

    // Create the shader for initializing velocity and density.
    int i = factory.GetAPI();
    factory.PushDefines();
    factory.defines.Set("NUM_X_THREADS", numXThreads);
    factory.defines.Set("NUM_Y_THREADS", numYThreads);
    mInitializeState = factory.CreateFromSource(*msSource[i]);
    if (mInitializeState)
    {
        std::shared_ptr<ComputeShader> cshader =
            mInitializeState->GetCShader();
        cshader->Set("density", mDensity);
        cshader->Set("velocity", mVelocity);
        cshader->Set("stateTm1", mStateTm1);
        cshader->Set("stateT", mStateT);
    }
    factory.PopDefines();
}
Fluid2AdjustVelocity::Fluid2AdjustVelocity(ProgramFactory& factory,
    int xSize, int ySize, int numXThreads, int numYThreads,
    std::shared_ptr<ConstantBuffer> const& parameters)
    :
    mNumXGroups(xSize/numXThreads),
    mNumYGroups(ySize/numYThreads)
{
    int i = factory.GetAPI();
    factory.PushDefines();
    factory.defines.Set("NUM_X_THREADS", numXThreads);
    factory.defines.Set("NUM_Y_THREADS", numYThreads);

    mAdjustVelocity = factory.CreateFromSource(*msSource[i]);
    if (mAdjustVelocity)
    {
        mAdjustVelocity->GetCShader()->Set("Parameters", parameters);
    }

    factory.PopDefines();
}
Fluid3EnforceStateBoundary::Fluid3EnforceStateBoundary(
    ProgramFactory& factory, int xSize, int ySize, int zSize, int numXThreads,
    int numYThreads, int numZThreads)
    :
    mNumXGroups(xSize/numXThreads),
    mNumYGroups(ySize/numYThreads),
    mNumZGroups(zSize/numZThreads)
{
    mXMin = std::make_shared<Texture2>(DF_R32G32_FLOAT, ySize, zSize);
    mXMin->SetUsage(Resource::SHADER_OUTPUT);
    mXMax = std::make_shared<Texture2>(DF_R32G32_FLOAT, ySize, zSize);
    mXMax->SetUsage(Resource::SHADER_OUTPUT);
    mYMin = std::make_shared<Texture2>(DF_R32G32_FLOAT, xSize, zSize);
    mYMin->SetUsage(Resource::SHADER_OUTPUT);
    mYMax = std::make_shared<Texture2>(DF_R32G32_FLOAT, xSize, zSize);
    mYMax->SetUsage(Resource::SHADER_OUTPUT);
    mZMin = std::make_shared<Texture2>(DF_R32G32_FLOAT, xSize, ySize);
    mZMin->SetUsage(Resource::SHADER_OUTPUT);
    mZMax = std::make_shared<Texture2>(DF_R32G32_FLOAT, xSize, ySize);
    mZMax->SetUsage(Resource::SHADER_OUTPUT);

    int i = factory.GetAPI();
    factory.PushDefines();
    factory.defines.Set("USE_COPY_X_FACE", 1);
    factory.defines.Set("NUM_Y_THREADS", numYThreads);
    factory.defines.Set("NUM_Z_THREADS", numZThreads);
    mCopyXFace = factory.CreateFromSource(*msSource[i]);
    if (mCopyXFace)
    {
        mCopyXFace->GetCShader()->Set("xMin", mXMin);
        mCopyXFace->GetCShader()->Set("xMax", mXMax);
    }

    factory.defines.Clear();
    factory.defines.Set("USE_WRITE_X_FACE", 1);
    factory.defines.Set("NUM_Y_THREADS", numYThreads);
    factory.defines.Set("NUM_Z_THREADS", numZThreads);
    mWriteXFace = factory.CreateFromSource(*msSource[i]);
    if (mWriteXFace)
    {
        mWriteXFace->GetCShader()->Set("xMin", mXMin);
        mWriteXFace->GetCShader()->Set("xMax", mXMax);
    }

    factory.defines.Clear();
    factory.defines.Set("USE_COPY_Y_FACE", 1);
    factory.defines.Set("NUM_X_THREADS", numXThreads);
    factory.defines.Set("NUM_Z_THREADS", numZThreads);
    mCopyYFace = factory.CreateFromSource(*msSource[i]);
    if (mCopyYFace)
    {
        mCopyYFace->GetCShader()->Set("yMin", mYMin);
        mCopyYFace->GetCShader()->Set("yMax", mYMax);
    }

    factory.defines.Clear();
    factory.defines.Set("USE_WRITE_Y_FACE", 1);
    factory.defines.Set("NUM_X_THREADS", numXThreads);
    factory.defines.Set("NUM_Z_THREADS", numZThreads);
    mWriteYFace = factory.CreateFromSource(*msSource[i]);
    if (mWriteYFace)
    {
        mWriteYFace->GetCShader()->Set("yMin", mYMin);
        mWriteYFace->GetCShader()->Set("yMax", mYMax);
    }

    factory.defines.Clear();
    factory.defines.Set("USE_COPY_Z_FACE", 1);
    factory.defines.Set("NUM_X_THREADS", numXThreads);
    factory.defines.Set("NUM_Y_THREADS", numYThreads);
    mCopyZFace = factory.CreateFromSource(*msSource[i]);
    if (mCopyZFace)
    {
        mCopyZFace->GetCShader()->Set("zMin", mZMin);
        mCopyZFace->GetCShader()->Set("zMax", mZMax);
    }

    factory.defines.Clear();
    factory.defines.Set("USE_WRITE_Z_FACE", 1);
    factory.defines.Set("NUM_X_THREADS", numXThreads);
    factory.defines.Set("NUM_Y_THREADS", numYThreads);
    mWriteZFace = factory.CreateFromSource(*msSource[i]);
    if (mWriteZFace)
    {
        mWriteZFace->GetCShader()->Set("zMin", mZMin);
        mWriteZFace->GetCShader()->Set("zMax", mZMax);
    }

    factory.PopDefines();
}