BRE Architecture Series Part 5 – Scene Generation

In this opportunity, we are going to talk about the scene generation process since it is loaded from a file stored on the disk until the data is ready and the command lists are recorded to feed the GPU.

How is a scene file processed?

First, we have a YAML file that is the format we use for scenes in BRE. If you want to have more details about YAML format and BRE scenes you should read BRE Architecture Series Part 4 – Scene Format. In BRE, we have a class named SceneLoader that loads the scene file from disk and invokes the different loaders for each scene category to process the file. The Loaders diagram is the following

LoadersDiagram

What is the result of the processing of the scene file?

Once SceneLoader (and all the loaders that it has) process the scene, an instance of a class named Scene, that contains the necessary data to be consumed by the RenderManager, is generated. The implementation of Scene is the following

Scene.h

#pragma once

#include <vector>

#include <Camera\Camera.h>
#include <GeometryPass/GeometryCommandListRecorder.h>

namespace BRE {
///
/// @brief Represents a scene (geometry pass command list recorders, lighting configurations, camera, etc)
///
class Scene {
public:
    Scene() = default;
    Scene(const Scene&) = delete;
    const Scene& operator=(const Scene&) = delete;
    Scene(Scene&&) = delete;
    Scene& operator=(Scene&&) = delete;

    ///
    /// @brief Get geometry pass command list recorders
    /// @return Geometry pass command list recorders
    ///
    GeometryCommandListRecorders& GetGeometryCommandListRecorders() noexcept;

    ///
    /// @brief Get sky box cube map resource
    /// @return Sky box cube map resource
    ///
    ID3D12Resource* &GetSkyBoxCubeMap() noexcept;

    ///
    /// @brief Get diffuse irradiance environment cube map
    /// @return Diffuse irradiance environment cube map
    ///
    ID3D12Resource* &GetDiffuseIrradianceCubeMap() noexcept;

    ///
    /// @brief Get specular pre convolved environment cube map
    /// @return Specular pre convolved environment cube map
    ///
    ID3D12Resource* &GetSpecularPreConvolvedCubeMap() noexcept;

    Camera& GetCamera() noexcept
    {
        return mCamera;
    }

private:
    GeometryCommandListRecorders mGeometryCommandListRecorders;

    ID3D12Resource* mSkyBoxCubeMap{ nullptr };
    ID3D12Resource* mDiffuseIrradianceCubeMap{ nullptr };
    ID3D12Resource* mSpecularPreConvolvedCubeMap{ nullptr };

    Camera mCamera;
};
}

Scene.cpp

#include "Scene.h"

namespace BRE {
GeometryCommandListRecorders&
Scene::GetGeometryCommandListRecorders() noexcept
{
    return mGeometryCommandListRecorders;
}

ID3D12Resource*
&Scene::GetSkyBoxCubeMap() noexcept
{
    return mSkyBoxCubeMap;
}

ID3D12Resource*
&Scene::GetDiffuseIrradianceCubeMap() noexcept
{
    return mDiffuseIrradianceCubeMap;
}

ID3D12Resource*
&Scene::GetSpecularPreConvolvedCubeMap() noexcept
{
    return mSpecularPreConvolvedCubeMap;
}
}

As we can see, it has information about the camera, about the environment and a list of GeometryCommandListRecorders that are command list recorders related with the geometry pass. In BRE Architecture Series Part 7 – Geometry Pass you can see all the geometry pass related stuff. To give you a brief description, a geometry command list recorder generates command lists that draw geometry using a particular technique like texture mapping, normal mapping, height mapping, etc.

Now, we can see the implementation of SceneLoader

SceneLoader.h

#pragma once

#include <memory>

#include <GeometryPass\GeometryCommandListRecorder.h>
#include <SceneLoader\CameraLoader.h>
#include <SceneLoader\DrawableObjectLoader.h>
#include <SceneLoader\EnvironmentLoader.h>
#include <SceneLoader\MaterialPropertiesLoader.h>
#include <SceneLoader\MaterialTechniqueLoader.h>
#include <SceneLoader\ModelLoader.h>
#include <SceneLoader\TextureLoader.h>

namespace BRE {
class Scene;

///
/// @brief Responsible to load scene file
///
class SceneLoader {
public:
    SceneLoader();
    SceneLoader(const SceneLoader&) = delete;
    const SceneLoader& operator=(const SceneLoader&) = delete;
    SceneLoader(SceneLoader&&) = delete;
    SceneLoader& operator=(SceneLoader&&) = delete;

    ///
    /// @brief Load scene
    /// @param rootNode Scene YAML file root node
    ///
    Scene* LoadScene(const char* sceneFilePath) noexcept;

private:
    ///
    /// @brief Generate geometry pass recorders
    /// @param scene Scene to initialize
    ///
    void GenerateGeometryPassRecorders(Scene& scene) noexcept;

    ///
    /// @brief Generate geometry pass command list recorders for color mapping
    /// @param commandListRecorders Geometry pass command list recorders
    ///
    void GenerateGeometryPassRecordersForColorMapping(GeometryCommandListRecorders& commandListRecorders) noexcept;

    ///
    /// @brief Generate geometry pass command list recorders for color normal mapping
    /// @param commandListRecorders Geometry pass command list recorders
    ///
    void GenerateGeometryPassRecordersForColorNormalMapping(GeometryCommandListRecorders& commandListRecorders) noexcept;

    ///
    /// @brief Generate geometry pass command list recorders for color height mapping
    /// @param commandListRecorders Geometry pass command list recorders
    ///
    void GenerateGeometryPassRecordersForColorHeightMapping(GeometryCommandListRecorders& commandListRecorders) noexcept;

    ///
    /// @brief Generate geometry pass command list recorders for texture mapping
    /// @param commandListRecorders Geometry pass command list recorders
    ///
    void GenerateGeometryPassRecordersForTextureMapping(GeometryCommandListRecorders& commandListRecorders) noexcept;

    ///
    /// @brief Generate geometry pass command list recorders for normal mapping
    /// @param commandListRecorders Geometry pass command list recorders
    ///
    void GenerateGeometryPassRecordersForNormalMapping(GeometryCommandListRecorders& commandListRecorders) noexcept;

    ///
    /// @brief Generate geometry pass command list recorders for height mapping
    /// @param commandListRecorders Geometry pass command list recorders
    ///
    void GenerateGeometryPassRecordersForHeightMapping(GeometryCommandListRecorders& commandListRecorders) noexcept;

    ID3D12CommandAllocator* mCommandAllocator{ nullptr };
    ID3D12GraphicsCommandList* mCommandList{ nullptr };
    ModelLoader mModelLoader;
    TextureLoader mTextureLoader;
    MaterialPropertiesLoader mMaterialPropertiesLoader;
    MaterialTechniqueLoader mMaterialTechniqueLoader;
    DrawableObjectLoader mDrawableObjectLoader;
    EnvironmentLoader mEnvironmentLoader;
    CameraLoader mCameraLoader;
};
}

SceneLoader.cpp

#include "SceneLoader.h"

#include <cstdint>
#include <d3d12.h>
#include <string>
#pragma warning( push )
#pragma warning( disable : 4127)
#include <yaml-cpp/yaml.h>
#pragma warning( pop ) 

#include <CommandManager\CommandAllocatorManager.h>
#include <CommandManager\CommandListManager.h>
#include <GeometryPass\Recorders\ColorMappingCommandListRecorder.h>
#include <GeometryPass\Recorders\ColorHeightMappingCommandListRecorder.h>
#include <GeometryPass\Recorders\ColorNormalMappingCommandListRecorder.h>
#include <GeometryPass\Recorders\HeightMappingCommandListRecorder.h>
#include <GeometryPass\Recorders\NormalMappingCommandListRecorder.h>
#include <GeometryPass\Recorders\TextureMappingCommandListRecorder.h>
#include <MathUtils\MathUtils.h>
#include <ModelManager\Model.h>
#include <Scene\Scene.h>
#include <Utils/DebugUtils.h>

using namespace DirectX;

namespace BRE {
SceneLoader::SceneLoader()
    : mMaterialTechniqueLoader(mTextureLoader)
    , mDrawableObjectLoader(mMaterialPropertiesLoader, mMaterialTechniqueLoader, mModelLoader)
    , mEnvironmentLoader(mTextureLoader)
{

    mCommandAllocator = &CommandAllocatorManager::CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT);
    mCommandList = &CommandListManager::CreateCommandList(D3D12_COMMAND_LIST_TYPE_DIRECT, *mCommandAllocator);
    mCommandList->Close();
};

Scene*
SceneLoader::LoadScene(const char* sceneFilePath) noexcept
{
    BRE_ASSERT(sceneFilePath != nullptr);

    const YAML::Node rootNode = YAML::LoadFile(sceneFilePath);
    const std::wstring errorMsg =
        L"Failed to open yaml file: " + StringUtils::AnsiToWideString(sceneFilePath);
    BRE_CHECK_MSG(rootNode.IsDefined(), errorMsg.c_str());

    mModelLoader.LoadModels(rootNode, *mCommandAllocator, *mCommandList);
    mTextureLoader.LoadTextures(rootNode, *mCommandAllocator, *mCommandList);
    mMaterialPropertiesLoader.LoadMaterialsProperties(rootNode);
    mMaterialTechniqueLoader.LoadMaterialTechniques(rootNode);
    mDrawableObjectLoader.LoadDrawableObjects(rootNode);
    mEnvironmentLoader.LoadEnvironment(rootNode);
    mCameraLoader.LoadCamera(rootNode);

    Scene* scene = new Scene;
    GenerateGeometryPassRecorders(*scene);
    scene->GetCamera() = mCameraLoader.GetCamera();

    return scene;
}

void
SceneLoader::GenerateGeometryPassRecorders(Scene& scene) noexcept
{
    GenerateGeometryPassRecordersForColorMapping(scene.GetGeometryCommandListRecorders());
    GenerateGeometryPassRecordersForColorNormalMapping(scene.GetGeometryCommandListRecorders());
    GenerateGeometryPassRecordersForColorHeightMapping(scene.GetGeometryCommandListRecorders());
    GenerateGeometryPassRecordersForTextureMapping(scene.GetGeometryCommandListRecorders());
    GenerateGeometryPassRecordersForNormalMapping(scene.GetGeometryCommandListRecorders());
    GenerateGeometryPassRecordersForHeightMapping(scene.GetGeometryCommandListRecorders());

    scene.GetSkyBoxCubeMap() = &mEnvironmentLoader.GetSkyBoxTexture();
    scene.GetDiffuseIrradianceCubeMap() = &mEnvironmentLoader.GetDiffuseIrradianceTexture();
    scene.GetSpecularPreConvolvedCubeMap() = &mEnvironmentLoader.GetSpecularPreConvolvedEnvironmentTexture();
}

void
SceneLoader::GenerateGeometryPassRecordersForColorMapping(GeometryCommandListRecorders& commandListRecorders) noexcept
{
    const DrawableObjectLoader::DrawableObjectsByModelName& drawableObjectsByModelName =
        mDrawableObjectLoader.GetDrawableObjectsByModelNameByTechniqueType(MaterialTechnique::COLOR_MAPPING);

    if (drawableObjectsByModelName.empty()) {
        return;
    }

    // Iterate over Drawable objects and fill containers needed
    // to initialize the command list recorder.
    ColorMappingCommandListRecorder* commandListRecorder = new ColorMappingCommandListRecorder;
    std::vector<GeometryCommandListRecorder::GeometryData> geometryDataVector;
    std::vector<MaterialProperties> materialProperties;

    std::size_t geometryDataVectorOffset = 0;
    for (const DrawableObjectLoader::DrawableObjectsByModelName::value_type& pair : drawableObjectsByModelName) {
        const std::vector<DrawableObject>& drawableObjects = pair.second;
        BRE_ASSERT(drawableObjects.empty() == false);

        // Build geometry data vertex and index buffers for all meshes
        const Model& model = drawableObjects[0].GetModel();
        const std::vector<Mesh>& meshes = model.GetMeshes();
        const std::size_t totalDataCount = meshes.size() * drawableObjects.size();
        geometryDataVector.reserve(geometryDataVector.size() + totalDataCount);
        for (std::uint32_t i = 0U; i < meshes.size(); ++i) {
            const Mesh& mesh = meshes[i];
            GeometryCommandListRecorder::GeometryData geometryData;
            geometryData.mVertexBufferData = mesh.GetVertexBufferData();
            geometryData.mIndexBufferData = mesh.GetIndexBufferData();
            geometryData.mWorldMatrices.reserve(drawableObjects.size());
            geometryData.mInverseTransposeWorldMatrices.reserve(drawableObjects.size());
            geometryDataVector.emplace_back(geometryData);
        }

        // Iterate all the meses and store data for all the drawable objects.
        for (std::uint32_t i = 0U; i < meshes.size(); ++i) {
            GeometryCommandListRecorder::GeometryData& geometryData =
                geometryDataVector[geometryDataVectorOffset + i];

            for (const DrawableObject& drawableObject : drawableObjects) {
                // Store material properties
                materialProperties.push_back(drawableObject.GetMaterialProperties());

                // Store matrices
                const XMFLOAT4X4& worldMatrix = drawableObject.GetWorldMatrix();
                XMFLOAT4X4 inverseTransposeWorldMatrix;
                MathUtils::StoreInverseTransposeMatrix(worldMatrix, inverseTransposeWorldMatrix);
                geometryData.mWorldMatrices.push_back(worldMatrix);
                geometryData.mInverseTransposeWorldMatrices.push_back(inverseTransposeWorldMatrix);
            }
        }

        geometryDataVectorOffset += meshes.size();
    }

    commandListRecorder->Init(geometryDataVector,
                              materialProperties);

    commandListRecorders.push_back(std::unique_ptr<GeometryCommandListRecorder>(commandListRecorder));
}

void
SceneLoader::GenerateGeometryPassRecordersForColorNormalMapping(GeometryCommandListRecorders& commandListRecorders) noexcept
{
    const DrawableObjectLoader::DrawableObjectsByModelName& drawableObjectsByModelName =
        mDrawableObjectLoader.GetDrawableObjectsByModelNameByTechniqueType(MaterialTechnique::COLOR_NORMAL_MAPPING);

    if (drawableObjectsByModelName.empty()) {
        return;
    }

    // Iterate over Drawable objects and fill containers needed
    // to initialize the command list recorder.
    ColorNormalMappingCommandListRecorder* commandListRecorder = new ColorNormalMappingCommandListRecorder;
    std::vector<GeometryCommandListRecorder::GeometryData> geometryDataVector;
    std::vector<MaterialProperties> materialProperties;
    std::vector<ID3D12Resource*> normalTextures;

    std::size_t geometryDataVectorOffset = 0;
    for (const DrawableObjectLoader::DrawableObjectsByModelName::value_type& pair : drawableObjectsByModelName) {
        const std::vector<DrawableObject>& drawableObjects = pair.second;
        BRE_ASSERT(drawableObjects.empty() == false);

        // Build geometry data vertex and index buffers for all meshes
        const Model& model = drawableObjects[0].GetModel();
        const std::vector<Mesh>& meshes = model.GetMeshes();
        const std::size_t totalDataCount = meshes.size() * drawableObjects.size();
        geometryDataVector.reserve(geometryDataVector.size() + totalDataCount);
        for (std::uint32_t i = 0U; i < meshes.size(); ++i) {
            const Mesh& mesh = meshes[i];
            GeometryCommandListRecorder::GeometryData geometryData;
            geometryData.mVertexBufferData = mesh.GetVertexBufferData();
            geometryData.mIndexBufferData = mesh.GetIndexBufferData();
            geometryData.mWorldMatrices.reserve(drawableObjects.size());
            geometryData.mInverseTransposeWorldMatrices.reserve(drawableObjects.size());
            geometryDataVector.emplace_back(geometryData);
        }

        // Iterate all the meses and store data for all the drawable objects.
        for (std::uint32_t i = 0U; i < meshes.size(); ++i) {
            GeometryCommandListRecorder::GeometryData& geometryData =
                geometryDataVector[geometryDataVectorOffset + i];

            for (const DrawableObject& drawableObject : drawableObjects) {
                // Store material properties
                materialProperties.push_back(drawableObject.GetMaterialProperties());

                // Store textures
                const MaterialTechnique& materialTechnique = drawableObject.GetMaterialTechnique();
                normalTextures.push_back(&materialTechnique.GetNormalTexture());

                // Store matrices
                const XMFLOAT4X4& worldMatrix = drawableObject.GetWorldMatrix();
                XMFLOAT4X4 inverseTransposeWorldMatrix;
                MathUtils::StoreInverseTransposeMatrix(worldMatrix, inverseTransposeWorldMatrix);
                geometryData.mWorldMatrices.push_back(worldMatrix);
                geometryData.mInverseTransposeWorldMatrices.push_back(inverseTransposeWorldMatrix);
            }
        }

        geometryDataVectorOffset += meshes.size();
    }

    commandListRecorder->Init(geometryDataVector,
                              materialProperties,
                              normalTextures);

    commandListRecorders.push_back(std::unique_ptr<GeometryCommandListRecorder>(commandListRecorder));
}

void
SceneLoader::GenerateGeometryPassRecordersForColorHeightMapping(GeometryCommandListRecorders& commandListRecorders) noexcept
{
    const DrawableObjectLoader::DrawableObjectsByModelName& drawableObjectsByModelName =
        mDrawableObjectLoader.GetDrawableObjectsByModelNameByTechniqueType(MaterialTechnique::COLOR_HEIGHT_MAPPING);

    if (drawableObjectsByModelName.empty()) {
        return;
    }

    // Iterate over Drawable objects and fill containers needed
    // to initialize the command list recorder.
    ColorHeightMappingCommandListRecorder* commandListRecorder = new ColorHeightMappingCommandListRecorder;
    std::vector<GeometryCommandListRecorder::GeometryData> geometryDataVector;
    std::vector<MaterialProperties> materialProperties;
    std::vector<ID3D12Resource*> normalTextures;
    std::vector<ID3D12Resource*> heightTextures;

    std::size_t geometryDataVectorOffset = 0;
    for (const DrawableObjectLoader::DrawableObjectsByModelName::value_type& pair : drawableObjectsByModelName) {
        const std::vector<DrawableObject>& drawableObjects = pair.second;
        BRE_ASSERT(drawableObjects.empty() == false);

        // Build geometry data vertex and index buffers for all meshes
        const Model& model = drawableObjects[0].GetModel();
        const std::vector<Mesh>& meshes = model.GetMeshes();
        const std::size_t totalDataCount = meshes.size() * drawableObjects.size();
        geometryDataVector.reserve(geometryDataVector.size() + totalDataCount);
        for (std::uint32_t i = 0U; i < meshes.size(); ++i) {
            const Mesh& mesh = meshes[i];
            GeometryCommandListRecorder::GeometryData geometryData;
            geometryData.mVertexBufferData = mesh.GetVertexBufferData();
            geometryData.mIndexBufferData = mesh.GetIndexBufferData();
            geometryData.mWorldMatrices.reserve(drawableObjects.size());
            geometryData.mInverseTransposeWorldMatrices.reserve(drawableObjects.size());
            geometryDataVector.emplace_back(geometryData);
        }

        // Iterate all the meses and store data for all the drawable objects.
        for (std::uint32_t i = 0U; i < meshes.size(); ++i) {
            GeometryCommandListRecorder::GeometryData& geometryData =
                geometryDataVector[geometryDataVectorOffset + i];

            for (const DrawableObject& drawableObject : drawableObjects) {
                // Store material properties
                materialProperties.push_back(drawableObject.GetMaterialProperties());

                // Store textures
                const MaterialTechnique& materialTechnique = drawableObject.GetMaterialTechnique();
                normalTextures.push_back(&materialTechnique.GetNormalTexture());
                heightTextures.push_back(&materialTechnique.GetHeightTexture());

                // Store matrices
                const XMFLOAT4X4& worldMatrix = drawableObject.GetWorldMatrix();
                XMFLOAT4X4 inverseTransposeWorldMatrix;
                MathUtils::StoreInverseTransposeMatrix(worldMatrix, inverseTransposeWorldMatrix);
                geometryData.mWorldMatrices.push_back(worldMatrix);
                geometryData.mInverseTransposeWorldMatrices.push_back(inverseTransposeWorldMatrix);
            }
        }

        geometryDataVectorOffset += meshes.size();
    }

    commandListRecorder->Init(geometryDataVector,
                              materialProperties,
                              normalTextures,
                              heightTextures);

    commandListRecorders.push_back(std::unique_ptr<GeometryCommandListRecorder>(commandListRecorder));
}

void
SceneLoader::GenerateGeometryPassRecordersForTextureMapping(GeometryCommandListRecorders& commandListRecorders) noexcept
{
    const DrawableObjectLoader::DrawableObjectsByModelName& drawableObjectsByModelName =
        mDrawableObjectLoader.GetDrawableObjectsByModelNameByTechniqueType(MaterialTechnique::TEXTURE_MAPPING);

    if (drawableObjectsByModelName.empty()) {
        return;
    }

    // Iterate over Drawable objects and fill containers needed
    // to initialize the command list recorder.
    TextureMappingCommandListRecorder* commandListRecorder = new TextureMappingCommandListRecorder;
    std::vector<GeometryCommandListRecorder::GeometryData> geometryDataVector;
    std::vector<MaterialProperties> materialProperties;
    std::vector<ID3D12Resource*> diffuseTextures;

    std::size_t geometryDataVectorOffset = 0;
    for (const DrawableObjectLoader::DrawableObjectsByModelName::value_type& pair : drawableObjectsByModelName) {
        const std::vector<DrawableObject>& drawableObjects = pair.second;
        BRE_ASSERT(drawableObjects.empty() == false);

        // Build geometry data vertex and index buffers for all meshes
        const Model& model = drawableObjects[0].GetModel();
        const std::vector<Mesh>& meshes = model.GetMeshes();
        const std::size_t totalDataCount = meshes.size() * drawableObjects.size();
        geometryDataVector.reserve(geometryDataVector.size() + totalDataCount);
        for (std::uint32_t i = 0U; i < meshes.size(); ++i) {
            const Mesh& mesh = meshes[i];
            GeometryCommandListRecorder::GeometryData geometryData;
            geometryData.mVertexBufferData = mesh.GetVertexBufferData();
            geometryData.mIndexBufferData = mesh.GetIndexBufferData();
            geometryData.mWorldMatrices.reserve(drawableObjects.size());
            geometryData.mInverseTransposeWorldMatrices.reserve(drawableObjects.size());
            geometryDataVector.emplace_back(geometryData);
        }

        // Iterate all the meses and store data for all the drawable objects.
        for (std::uint32_t i = 0U; i < meshes.size(); ++i) {
            GeometryCommandListRecorder::GeometryData& geometryData =
                geometryDataVector[geometryDataVectorOffset + i];

            for (const DrawableObject& drawableObject : drawableObjects) {
                // Store material properties
                materialProperties.push_back(drawableObject.GetMaterialProperties());

                // Store textures
                const MaterialTechnique& materialTechnique = drawableObject.GetMaterialTechnique();
                diffuseTextures.push_back(&materialTechnique.GetDiffuseTexture());

                // Store matrices
                const XMFLOAT4X4& worldMatrix = drawableObject.GetWorldMatrix();
                XMFLOAT4X4 inverseTransposeWorldMatrix;
                MathUtils::StoreInverseTransposeMatrix(worldMatrix, inverseTransposeWorldMatrix);
                geometryData.mWorldMatrices.push_back(worldMatrix);
                geometryData.mInverseTransposeWorldMatrices.push_back(inverseTransposeWorldMatrix);
            }
        }

        geometryDataVectorOffset += meshes.size();
    }

    commandListRecorder->Init(geometryDataVector,
                              materialProperties,
                              diffuseTextures);

    commandListRecorders.push_back(std::unique_ptr<GeometryCommandListRecorder>(commandListRecorder));
}

void
SceneLoader::GenerateGeometryPassRecordersForNormalMapping(GeometryCommandListRecorders& commandListRecorders) noexcept
{
    const DrawableObjectLoader::DrawableObjectsByModelName& drawableObjectsByModelName =
        mDrawableObjectLoader.GetDrawableObjectsByModelNameByTechniqueType(MaterialTechnique::NORMAL_MAPPING);

    if (drawableObjectsByModelName.empty()) {
        return;
    }

    // Iterate over Drawable objects and fill containers needed
    // to initialize the command list recorder.
    NormalMappingCommandListRecorder* commandListRecorder = new NormalMappingCommandListRecorder;
    std::vector<GeometryCommandListRecorder::GeometryData> geometryDataVector;
    std::vector<MaterialProperties> materialProperties;
    std::vector<ID3D12Resource*> diffuseTextures;
    std::vector<ID3D12Resource*> normalTextures;

    std::size_t geometryDataVectorOffset = 0;
    for (const DrawableObjectLoader::DrawableObjectsByModelName::value_type& pair : drawableObjectsByModelName) {
        const std::vector<DrawableObject>& drawableObjects = pair.second;
        BRE_ASSERT(drawableObjects.empty() == false);

        // Build geometry data vertex and index buffers for all meshes
        const Model& model = drawableObjects[0].GetModel();
        const std::vector<Mesh>& meshes = model.GetMeshes();
        const std::size_t totalDataCount = meshes.size() * drawableObjects.size();
        geometryDataVector.reserve(geometryDataVector.size() + totalDataCount);
        for (std::uint32_t i = 0U; i < meshes.size(); ++i) {
            const Mesh& mesh = meshes[i];
            GeometryCommandListRecorder::GeometryData geometryData;
            geometryData.mVertexBufferData = mesh.GetVertexBufferData();
            geometryData.mIndexBufferData = mesh.GetIndexBufferData();
            geometryData.mWorldMatrices.reserve(drawableObjects.size());
            geometryData.mInverseTransposeWorldMatrices.reserve(drawableObjects.size());
            geometryDataVector.emplace_back(geometryData);
        }

        // Iterate all the meses and store data for all the drawable objects.
        for (std::uint32_t i = 0U; i < meshes.size(); ++i) {
            GeometryCommandListRecorder::GeometryData& geometryData =
                geometryDataVector[geometryDataVectorOffset + i];

            for (const DrawableObject& drawableObject : drawableObjects) {
                // Store material properties
                materialProperties.push_back(drawableObject.GetMaterialProperties());

                // Store textures
                const MaterialTechnique& materialTechnique = drawableObject.GetMaterialTechnique();
                diffuseTextures.push_back(&materialTechnique.GetDiffuseTexture());
                normalTextures.push_back(&materialTechnique.GetNormalTexture());

                // Store matrices
                const XMFLOAT4X4& worldMatrix = drawableObject.GetWorldMatrix();
                XMFLOAT4X4 inverseTransposeWorldMatrix;
                MathUtils::StoreInverseTransposeMatrix(worldMatrix, inverseTransposeWorldMatrix);
                geometryData.mWorldMatrices.push_back(worldMatrix);
                geometryData.mInverseTransposeWorldMatrices.push_back(inverseTransposeWorldMatrix);
            }
        }

        geometryDataVectorOffset += meshes.size();
    }

    commandListRecorder->Init(geometryDataVector,
                              materialProperties,
                              diffuseTextures,
                              normalTextures);

    commandListRecorders.push_back(std::unique_ptr<GeometryCommandListRecorder>(commandListRecorder));
}

void
SceneLoader::GenerateGeometryPassRecordersForHeightMapping(GeometryCommandListRecorders& commandListRecorders) noexcept
{
    const DrawableObjectLoader::DrawableObjectsByModelName& drawableObjectsByModelName =
        mDrawableObjectLoader.GetDrawableObjectsByModelNameByTechniqueType(MaterialTechnique::HEIGHT_MAPPING);

    if (drawableObjectsByModelName.empty()) {
        return;
    }

    // Iterate over Drawable objects and fill containers needed
    // to initialize the command list recorder.
    HeightMappingCommandListRecorder* commandListRecorder = new HeightMappingCommandListRecorder;
    std::vector<GeometryCommandListRecorder::GeometryData> geometryDataVector;
    std::vector<MaterialProperties> materialProperties;
    std::vector<ID3D12Resource*> diffuseTextures;
    std::vector<ID3D12Resource*> normalTextures;
    std::vector<ID3D12Resource*> heightTextures;

    std::size_t geometryDataVectorOffset = 0;
    for (const DrawableObjectLoader::DrawableObjectsByModelName::value_type& pair : drawableObjectsByModelName) {
        const std::vector<DrawableObject>& drawableObjects = pair.second;
        BRE_ASSERT(drawableObjects.empty() == false);

        // Build geometry data vertex and index buffers for all meshes
        const Model& model = drawableObjects[0].GetModel();
        const std::vector<Mesh>& meshes = model.GetMeshes();
        const std::size_t totalDataCount = meshes.size() * drawableObjects.size();
        geometryDataVector.reserve(geometryDataVector.size() + totalDataCount);
        for (std::uint32_t i = 0U; i < meshes.size(); ++i) {
            const Mesh& mesh = meshes[i];
            GeometryCommandListRecorder::GeometryData geometryData;
            geometryData.mVertexBufferData = mesh.GetVertexBufferData();
            geometryData.mIndexBufferData = mesh.GetIndexBufferData();
            geometryData.mWorldMatrices.reserve(drawableObjects.size());
            geometryData.mInverseTransposeWorldMatrices.reserve(drawableObjects.size());
            geometryDataVector.emplace_back(geometryData);
        }

        // Iterate all the meses and store data for all the drawable objects.
        for (std::uint32_t i = 0U; i < meshes.size(); ++i) {
            GeometryCommandListRecorder::GeometryData& geometryData =
                geometryDataVector[geometryDataVectorOffset + i];

            for (const DrawableObject& drawableObject : drawableObjects) {
                // Store material properties
                materialProperties.push_back(drawableObject.GetMaterialProperties());

                // Store textures
                const MaterialTechnique& materialTechnique = drawableObject.GetMaterialTechnique();
                diffuseTextures.push_back(&materialTechnique.GetDiffuseTexture());
                normalTextures.push_back(&materialTechnique.GetNormalTexture());
                heightTextures.push_back(&materialTechnique.GetHeightTexture());

                // Store matrices
                const XMFLOAT4X4& worldMatrix = drawableObject.GetWorldMatrix();
                XMFLOAT4X4 inverseTransposeWorldMatrix;
                MathUtils::StoreInverseTransposeMatrix(worldMatrix, inverseTransposeWorldMatrix);
                geometryData.mWorldMatrices.push_back(worldMatrix);
                geometryData.mInverseTransposeWorldMatrices.push_back(inverseTransposeWorldMatrix);
            }
        }

        geometryDataVectorOffset += meshes.size();
    }

    commandListRecorder->Init(geometryDataVector,
                              materialProperties,
                              diffuseTextures,
                              normalTextures,
                              heightTextures);

    commandListRecorders.push_back(std::unique_ptr<GeometryCommandListRecorder>(commandListRecorder));
}
}

In the implementation, we can see that we generate GeometryCommandListRecorders per technique. That is, we group all the geometry that should be drawn with texture mapping technique in a single instance of GeometryCommandListRecorder, the geometry that should be drawn with normal mapping technique in another GeometryCommandListRecorder, etc. This strategy is used to minimize the pipeline state object changes, that according to NVIDIA recommendation is a good practice.

For what is the Scene used?

Once the scene file is loaded from disk and processed by the SceneLoader to generate a Scene, it is consumed by the RenderManager. The class that is in charge is the SceneExecutor, and its implementation is the following

SceneExecutor.h

#pragma once

namespace BRE {
class RenderManager;
class Scene;

///
/// @brief Responsible to load and execute the scene
///
class SceneExecutor {
public:
    ///
    /// @brief SceneExecutor constructor
    /// @param sceneFilePath Scene file path. Must be not nullptr.
    ///
    explicit SceneExecutor(const char* sceneFilePath);
    ~SceneExecutor();
    SceneExecutor(const SceneExecutor&) = delete;
    const SceneExecutor& operator=(const SceneExecutor&) = delete;
    SceneExecutor(SceneExecutor&&) = delete;
    SceneExecutor& operator=(SceneExecutor&&) = delete;

    ///
    /// @brief Executes the scene executor.
    ///
    /// This method is going to load the scene and run the main loop.
    ///
    void Execute() noexcept;

private:
    Scene* mScene{ nullptr };

    RenderManager* mRenderManager{ nullptr };
};
}

SceneExecutor.cpp

#include "SceneExecutor.h"

#include <CommandListExecutor\CommandListExecutor.h>
#include <Input/Keyboard.h>
#include <Input/Mouse.h>
#include <RenderManager/RenderManager.h>
#include <Scene/Scene.h>
#include <SceneLoader\SceneLoader.h>
#include <Utils\DebugUtils.h>

using namespace DirectX;

namespace BRE {
namespace {
const std::uint32_t MAX_NUM_CMD_LISTS{ 3U };

void UpdateKeyboardAndMouse() noexcept
{
    Keyboard::Get().UpdateKeysState();
    Mouse::Get().UpdateMouseState();
    if (Keyboard::Get().IsKeyDown(DIK_ESCAPE)) {
        PostQuitMessage(0);
    }
}

// Runs program until Escape key is pressed.
std::int32_t RunMessageLoop() noexcept
{
    MSG message{ nullptr };
    while (message.message != WM_QUIT) {
        if (PeekMessage(&message, nullptr, 0U, 0U, PM_REMOVE)) {
            TranslateMessage(&message);
            DispatchMessage(&message);
        } else {
            UpdateKeyboardAndMouse();
        }
    }

    return static_cast<std::int32_t>(message.wParam);
}
}

using namespace DirectX;

SceneExecutor::~SceneExecutor()
{
    BRE_ASSERT(mRenderManager != nullptr);
    mRenderManager->Terminate();

    delete mScene;
}

void
SceneExecutor::Execute() noexcept
{
    RunMessageLoop();
}

SceneExecutor::SceneExecutor(const char* sceneFilePath)
{
    BRE_ASSERT(sceneFilePath != nullptr);

    CommandListExecutor::Create(MAX_NUM_CMD_LISTS);

    SceneLoader sceneLoader;
    mScene = sceneLoader.LoadScene(sceneFilePath);
    BRE_ASSERT(mScene != nullptr);

    mRenderManager = &RenderManager::Create(*mScene);
}
}

As we can see, between the responsibilities of this class we have the main loop processing (that receives user input from mouse and keyboard), CommandListExecutor creation to being able to send command lists to be executed in the GPU, the SceneLoader creation for scene processing, and the RenderManager creation to consume the Scene to begin with the rendering tasks.

Future Work

  • Load models and textures in several threads to improve performance
  • Execute loaders in parallel if possible
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s