#pragma once // Carries out the command stream (include/t5de/protocol/commands.hpp) with Diligent, reproducing IMVU's Direct3D 9 // look (shaders/fixed_function.hlsl): the client's textures and buffers by id, its state as it sets it, a pipeline // for each combination of states it draws with. #include #include #include #include #include #include #include #include "EngineFactoryD3D11.h" #include "RefCntAutoPtr.hpp" #include "t5de/protocol/commands.hpp" namespace t5de { class Scene { public: Scene(Diligent::IRenderDevice* device, Diligent::IDeviceContext* context, Diligent::ISwapChain* swapChain); // Carries out one frame's commands. offsetX is where the window starts within the client's 3D view; without draw, // only the frame's changes are made (textures, buffers, state) and frames into the window aren't drawn (frames into // render targets always are). True if the frame was drawn into the window (to present). bool execute(std::span commands, int offsetX, bool draw); // Receives the pixels of frames that read their render target back (protocol::BeginFrame::readBack): rows packed, // 0xAARRGGBB; empty if they couldn't be read. Valid during the call. void setReadBack(std::function)> readBack) { readBack_ = std::move(readBack); } // Replaces the shaders' source (the built-in one by default): for trying changes while the client runs. void setShaderSource(std::string source); // When the adapter sent the last frame carried out (QueryPerformanceCounter). [[nodiscard]] std::uint64_t lastSentAt() const { return lastSentAt_; } private: struct Texture { Diligent::RefCntAutoPtr texture; Diligent::RefCntAutoPtr depth; // render targets only std::uint32_t width = 0; std::uint32_t height = 0; std::uint32_t flags = 0; }; struct Buffer { Diligent::RefCntAutoPtr buffer; protocol::BufferKind kind = protocol::BufferKind::Vertex; std::uint32_t elementSize = 0; std::uint32_t count = 0; }; struct Pipeline { Diligent::RefCntAutoPtr state; Diligent::RefCntAutoPtr bindings; Diligent::IShaderResourceVariable* texture0 = nullptr; Diligent::IShaderResourceVariable* texture1 = nullptr; }; struct Shaders { Diligent::RefCntAutoPtr vertex; Diligent::RefCntAutoPtr pixel; }; // Mirrors the shader's constant buffer. struct Constants { float world[16]; float view[16]; float projection[16]; float texTransform0[8]; float texTransform1[8]; float ambient[4]; float lightColor[8][4]; float lightVector[8][4]; float fogColor[4]; float fog[4]; float flags[4]; float viewport[4]; }; // What a draw needs from the state, as set by the client. struct State { bool depthTest = true; bool depthWrite = true; bool blending = false; std::uint32_t blendMode = 1; bool culling = true; bool normalInverting = false; std::uint32_t texture0 = 0; std::uint32_t texture1 = 0; }; void createTexture(const protocol::Command& command); void createRenderTarget(const protocol::CreateRenderTarget& create); void beginFrame(const protocol::BeginFrame& begin); void endFrame(); // Sets the viewport for the last projection, in what the frame draws into. void setViewport(); // The render target's pixels, rows packed, 0xAARRGGBB; empty if it can't be read. std::span readTarget(const Texture& target); void updateTexture(const protocol::Command& command); void createBuffer(const protocol::Command& command); void updateBuffer(const protocol::Command& command); void setProjection(const protocol::SetProjection& projection); void setState(const protocol::SetState& state); void drawTriangles(const protocol::DrawTriangles& draw); void drawArrays(const protocol::Command& command); void drawPointSprites(const protocol::Command& command); // Draws indexCount indices from index, with vertex streams (positions required, the others may be null). void draw(Diligent::IBuffer* indices, std::uint32_t indexCount, Diligent::IBuffer* positions, Diligent::IBuffer* normals, Diligent::IBuffer* texCoords, Diligent::IBuffer* colors); // The pipeline for the current state, with these vertex streams and textures, for triangles or particles. Pipeline* pipeline(std::uint32_t streams, std::uint32_t textures, bool sprites); const Shaders& shaders(std::uint32_t streams, std::uint32_t textures, bool sprites); // Picks the pipeline for the current state and sets it up for a draw: constants, textures. Null if it can't. Pipeline* prepare(std::uint32_t streams, bool sprites); Diligent::ITextureView* textureView(std::uint32_t id); Diligent::RefCntAutoPtr device_; Diligent::RefCntAutoPtr context_; Diligent::RefCntAutoPtr swapChain_; Diligent::RefCntAutoPtr constantBuffer_; Diligent::RefCntAutoPtr samplers_[4]; // by wrap u (1) and wrap v (2) std::unordered_map textures_; std::unordered_map buffers_; std::unordered_map shaders_; std::unordered_map pipelines_; std::string shaderSource_; Constants constants_{}; State state_; // What the current frame draws into: the window, a render target, or nothing (a frame not drawn). bool onscreen_ = false; // in a frame drawn into the window Texture* target_ = nullptr; // in a frame drawn into this render target bool readsBack_ = false; // the frame's render target is read back at its end Diligent::ITextureView* colorView_ = nullptr; Diligent::ITextureView* depthView_ = nullptr; std::int32_t projectionWidth_ = 0; std::int32_t projectionHeight_ = 0; std::function)> readBack_; std::vector readPixels_; bool draw_ = true; std::uint64_t lastSentAt_ = 0; int offsetX_ = 0; }; } // namespace t5de