minetest/src/client/render/pipeline.h

416 lines
11 KiB
C++

/*
Minetest
Copyright (C) 2022 x2048, Dmitry Kostenko <codeforsmile@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
#include "irrlichttypes_extrabloated.h"
#include <vector>
#include <memory>
#include <string>
class RenderSource;
class RenderTarget;
class RenderStep;
class Client;
class Hud;
class ShadowRenderer;
struct PipelineContext
{
PipelineContext(IrrlichtDevice *_device, Client *_client, Hud *_hud, ShadowRenderer *_shadow_renderer, video::SColor _color, v2u32 _target_size)
: device(_device), client(_client), hud(_hud), shadow_renderer(_shadow_renderer), clear_color(_color), target_size(_target_size)
{
}
IrrlichtDevice *device;
Client *client;
Hud *hud;
ShadowRenderer *shadow_renderer;
video::SColor clear_color;
v2u32 target_size;
bool show_hud {true};
bool show_minimap {true};
bool draw_wield_tool {true};
bool draw_crosshair {true};
};
/**
* Base object that can be owned by RenderPipeline
*
*/
class RenderPipelineObject
{
public:
virtual ~RenderPipelineObject() = default;
virtual void reset(PipelineContext &context) {}
};
/**
* Represents a source of rendering information such as textures
*/
class RenderSource : virtual public RenderPipelineObject
{
public:
/**
* Return the number of textures in the source.
*/
virtual u8 getTextureCount() = 0;
/**
* Get a texture by index.
* Returns nullptr is the texture does not exist.
*/
virtual video::ITexture *getTexture(u8 index) = 0;
};
/**
* Represents a render target (screen or framebuffer)
*/
class RenderTarget : virtual public RenderPipelineObject
{
public:
/**
* Activate the render target and configure OpenGL state for the output.
* This is usually done by @see RenderStep implementations.
*/
virtual void activate(PipelineContext &context)
{
m_clear = false;
}
/**
* Resets the state of the object for the next pipeline iteration
*/
virtual void reset(PipelineContext &context) override
{
m_clear = true;
}
protected:
bool m_clear {true};
};
/**
* Texture buffer represents a framebuffer with a multiple attached textures.
*
* @note Use of TextureBuffer requires use of gl_FragData[] in the shader
*/
class TextureBuffer : public RenderSource
{
public:
virtual ~TextureBuffer() override;
/**
* Configure fixed-size texture for the specific index
*
* @param index index of the texture
* @param size width and height of the texture in pixels
* @param height height of the texture in pixels
* @param name unique name of the texture
* @param format color format
*/
void setTexture(u8 index, core::dimension2du size, const std::string& name, video::ECOLOR_FORMAT format);
/**
* Configure relative-size texture for the specific index
*
* @param index index of the texture
* @param scale_factor relation of the texture dimensions to the screen dimensions
* @param name unique name of the texture
* @param format color format
*/
void setTexture(u8 index, v2f scale_factor, const std::string& name, video::ECOLOR_FORMAT format);
virtual u8 getTextureCount() override { return m_textures.size(); }
virtual video::ITexture *getTexture(u8 index) override;
virtual void reset(PipelineContext &context) override;
private:
static const u8 NO_DEPTH_TEXTURE = 255;
struct TextureDefinition
{
bool valid { false };
bool fixed_size { false };
bool dirty { false };
v2f scale_factor;
core::dimension2du size;
std::string name;
video::ECOLOR_FORMAT format;
};
/**
* Make sure the texture in the given slot matches the texture definition given the current context.
* @param textureSlot address of the texture pointer to verify and populate.
* @param definition logical definition of the texture
* @param context current context of the rendering pipeline
* @return true if a new texture was created and put into the slot
* @return false if the slot was not modified
*/
bool ensureTexture(video::ITexture **textureSlot, const TextureDefinition& definition, PipelineContext &context);
video::IVideoDriver *m_driver { nullptr };
std::vector<TextureDefinition> m_definitions;
core::array<video::ITexture *> m_textures;
};
/**
* Targets output to designated texture in texture buffer
*/
class TextureBufferOutput : public RenderTarget
{
public:
TextureBufferOutput(TextureBuffer *buffer, u8 texture_index);
TextureBufferOutput(TextureBuffer *buffer, const std::vector<u8> &texture_map);
TextureBufferOutput(TextureBuffer *buffer, const std::vector<u8> &texture_map, u8 depth_stencil);
virtual ~TextureBufferOutput() override;
void activate(PipelineContext &context) override;
private:
static const u8 NO_DEPTH_TEXTURE = 255;
TextureBuffer *buffer;
std::vector<u8> texture_map;
u8 depth_stencil { NO_DEPTH_TEXTURE };
video::IRenderTarget* render_target { nullptr };
video::IVideoDriver* driver { nullptr };
};
/**
* Allows remapping texture indicies in another RenderSource.
*
* @note all unmapped indexes are passed through to the underlying render source.
*/
class RemappingSource : RenderSource
{
public:
RemappingSource(RenderSource *source)
: m_source(source)
{}
/**
* Maps texture index to a different index in the dependent source.
*
* @param index texture index as requested by the @see RenderStep.
* @param target_index matching texture index in the underlying @see RenderSource.
*/
void setMapping(u8 index, u8 target_index)
{
if (index >= m_mappings.size()) {
u8 start = m_mappings.size();
m_mappings.resize(index);
for (u8 i = start; i < m_mappings.size(); ++i)
m_mappings[i] = i;
}
m_mappings[index] = target_index;
}
virtual u8 getTextureCount() override
{
return m_mappings.size();
}
virtual video::ITexture *getTexture(u8 index) override
{
if (index < m_mappings.size())
index = m_mappings[index];
return m_source->getTexture(index);
}
public:
RenderSource *m_source;
std::vector<u8> m_mappings;
};
class DynamicSource : public RenderSource
{
public:
bool isConfigured() { return upstream != nullptr; }
void setRenderSource(RenderSource *value) { upstream = value; }
/**
* Return the number of textures in the source.
*/
virtual u8 getTextureCount() override;
/**
* Get a texture by index.
* Returns nullptr is the texture does not exist.
*/
virtual video::ITexture *getTexture(u8 index) override;
private:
RenderSource *upstream { nullptr };
};
/**
* Implements direct output to screen framebuffer.
*/
class ScreenTarget : public RenderTarget
{
public:
virtual void activate(PipelineContext &context) override;
virtual void reset(PipelineContext &context) override;
private:
core::dimension2du size;
};
class DynamicTarget : public RenderTarget
{
public:
bool isConfigured() { return upstream != nullptr; }
void setRenderTarget(RenderTarget *value) { upstream = value; }
virtual void activate(PipelineContext &context) override;
private:
RenderTarget *upstream { nullptr };
};
/**
* Base class for rendering steps in the pipeline
*/
class RenderStep : virtual public RenderPipelineObject
{
public:
/**
* Assigns render source to this step.
*
* @param source source of rendering information
*/
virtual void setRenderSource(RenderSource *source) = 0;
/**
* Assigned render target to this step.
*
* @param target render target to send output to.
*/
virtual void setRenderTarget(RenderTarget *target) = 0;
/**
* Runs the step. This method is invoked by the pipeline.
*/
virtual void run(PipelineContext &context) = 0;
};
/**
* Provides default empty implementation of supporting methods in a rendering step.
*/
class TrivialRenderStep : public RenderStep
{
public:
virtual void setRenderSource(RenderSource *source) override {}
virtual void setRenderTarget(RenderTarget *target) override {}
virtual void reset(PipelineContext &) override {}
};
/**
* Dynamically changes render target of another step.
*
* This allows re-running parts of the pipeline with different outputs
*/
class SetRenderTargetStep : public TrivialRenderStep
{
public:
SetRenderTargetStep(RenderStep *step, RenderTarget *target);
virtual void run(PipelineContext &context) override;
private:
RenderStep *step;
RenderTarget *target;
};
/**
* Render Pipeline provides a flexible way to execute rendering steps in the engine.
*
* RenderPipeline also implements @see RenderStep, allowing for nesting of the pipelines.
*/
class RenderPipeline : public RenderStep
{
public:
/**
* Add a step to the end of the pipeline
*
* @param step reference to a @see RenderStep implementation.
*/
RenderStep *addStep(RenderStep *step)
{
m_pipeline.push_back(step);
return step;
}
/**
* Capture ownership of a dynamically created @see RenderStep instance.
*
* RenderPipeline will delete the instance when the pipeline is destroyed.
*
* @param step reference to the instance.
* @return RenderStep* value of the 'step' parameter.
*/
template<typename T>
T *own(std::unique_ptr<T> &&object)
{
T* result = object.release();
m_objects.push_back(std::unique_ptr<RenderPipelineObject>(result));
return result;
}
/**
* Create a new object that will be managed by the pipeline
*
* @tparam T type of the object to be created
* @tparam Args types of constructor arguments
* @param args constructor arguments
* @return T* pointer to the newly created object
*/
template<typename T, typename... Args>
T *createOwned(Args&&... args) {
return own(std::make_unique<T>(std::forward<Args>(args)...));
}
/**
* Create and add a step managed by the pipeline and return a pointer
* to the step for further configuration.
*
* @tparam T Type of the step to be added.
* @tparam Args Types of the constructor parameters
* @param args Constructor parameters
* @return RenderStep* Pointer to the created step for further configuration.
*/
template<typename T, typename... Args>
RenderStep *addStep(Args&&... args) {
T* result = own(std::make_unique<T>(std::forward<Args>(args)...));
return addStep(result);
}
RenderSource *getInput();
RenderTarget *getOutput();
v2f getScale() { return scale; }
void setScale(v2f value) { scale = value; }
virtual void reset(PipelineContext &context) override {}
virtual void run(PipelineContext &context) override;
virtual void setRenderSource(RenderSource *source) override;
virtual void setRenderTarget(RenderTarget *target) override;
private:
std::vector<RenderStep *> m_pipeline;
std::vector< std::unique_ptr<RenderPipelineObject> > m_objects;
DynamicSource m_input;
DynamicTarget m_output;
v2f scale { 1.0f, 1.0f };
};