diff --git a/docs/interface.md b/docs/interface.md index 78fe116..5130ed5 100644 --- a/docs/interface.md +++ b/docs/interface.md @@ -59,9 +59,17 @@ virtual methods, IMVU's code only uses an engine through helpers that call its v `destroy`, `get_deleter`, `get_untyped_deleter`), the use count and the weak count, changed atomically. `include/t5de/imvu/shared_ptr.hpp` matches it. With non-trivial copy and destruction, it's returned through a hidden pointer and passed on the stack, as IMVU's code expects. -- Still to work out, for a renderer of our own: `Mat4f`, `Transform`, `LightProperties`, `TextureTransform`, - `Pixmap` (`pixmap.dll`), the buffers' contents, `VertexAttributeArray`, `std::vector` as - Visual C++ 2010 lays it out, and `RenderBlendMode`'s values. +- The data types are in `include/t5de/imvu/types.hpp`, worked out from the Direct3D engine: matrices and + transforms in Direct3D's row-vector convention, colors as `0xAARRGGBB`, lights (point or directional), blend + modes, linear fog, texture coordinate transforms, vertex attribute arrays (data, count, stride). `Pixmap` is used + through `pixmap.dll`'s exported methods (`width`, `height`, `pitch`, `pixels`, ...), not its layout. +- `drawTriangles`' vertex buffers are positions (`Vec3f`), normals (`Vec3f`), texture coordinates (`Vec2f`) and + colors; vertex buffers are made with a stride, element size and count, dynamic ones filled later through the + buffer's own `update`. The objects an engine creates (buffers, textures, render targets) are declared in + `include/t5de/imvu/resources.hpp`; IMVU's code deletes them through their virtual destructors. +- Still unknown: point sprites (`drawPointSprites`, particles), render targets, `CompoundRegion` (texture update + regions), `newRenderTexture`'s flags, and what `beginRendering` does with a null target. MSVC orders overloaded + virtual methods unlike their declaration, so our declarations never overload virtual methods. - Memory crosses the boundary only through virtual methods (destructors, control blocks), so each side can use its own C runtime. Exceptions must not cross it. diff --git a/include/t5de/imvu/render_engine.hpp b/include/t5de/imvu/render_engine.hpp index 9d6f563..e6e8c6c 100644 --- a/include/t5de/imvu/render_engine.hpp +++ b/include/t5de/imvu/render_engine.hpp @@ -1,54 +1,16 @@ #pragma once // IMVU's renderer interface, PiRenderEngine (SceneWindow.dll), declared to match its vtable: the order of the methods // and their parameter types are what matter. The names are IMVU's, from SceneWindow.dll's exported RenderEngineDx -// methods; parameter names are ours, and guesses until the calls are traced. Types used only through pointers or -// references are left incomplete until we need their layout. See docs/interface.md. +// methods; parameter names are ours, from what IMVU's Direct3D engine does with them. See docs/interface.md. #include +#include "t5de/imvu/resources.hpp" #include "t5de/imvu/shared_ptr.hpp" +#include "t5de/imvu/types.hpp" namespace t5de::imvu { -template -struct Vec2 { - T x; - T y; -}; - -template -struct Vec3 { - T x; - T y; - T z; -}; - -using Vec2i = Vec2; -using Vec2f = Vec2; -using Vec3f = Vec3; - -struct Mat4f; -struct Transform; -struct LightProperties; -struct TextureTransform; -struct PointSprite; -class Pixmap; -class INativeWindow; -class PiRenderTexture; -class PiInternalIndexBuffer; -class PiInternalVertexBuffer; -class PiRenderTarget; -class PiPixmapRenderTarget; -class PiTextureRenderTarget; -template -class VertexAttributeArray; - -// std::vector as Visual C++ 2010 lays it out. -struct PointSpriteVector; - -// Values not yet known. -enum class RenderBlendMode : int {}; - class PiRenderEngine { public: PiRenderEngine() = default; @@ -59,7 +21,7 @@ public: virtual void resizeFrameBuffer(Vec2i size) = 0; virtual void enable2dFrameBuffer() = 0; - virtual void clear(std::uint32_t flags, std::uint32_t color) = 0; + virtual void clear(std::uint32_t flags, Color color) = 0; // ClearFlags virtual void setProjection(Vec2i viewport, const Mat4f& projection) = 0; virtual void setModelSpace(const Transform& model) = 0; // slot 5 virtual void setViewSpace(const Transform& view) = 0; @@ -75,29 +37,33 @@ public: virtual void setLightingOn(bool on) = 0; virtual void setLightProperties(std::uint32_t index, const LightProperties& light) = 0; virtual void setFogOn(bool on) = 0; - virtual void setFogAttributes(float start, float end, std::uint32_t color) = 0; + virtual void setFogAttributes(float start, float end, Color color) = 0; virtual void setTextureTransformMatrixIdentity(std::uint32_t stage) = 0; // slot 20 virtual void setTextureTransformMatrix(std::uint32_t stage, const TextureTransform& transform) = 0; - virtual void setEnvironmentAmbient(std::uint32_t color) = 0; - virtual PiRenderTexture* newRenderTexture(Pixmap* pixmap, std::uint32_t flags) = 0; + virtual void setEnvironmentAmbient(Color color) = 0; // zero alpha + virtual PiRenderTexture* newRenderTexture(const Pixmap& pixmap, std::uint32_t flags) = 0; virtual PiInternalIndexBuffer* newIndexBuffer(std::uint32_t count, const std::uint16_t* indices) = 0; - virtual PiInternalVertexBuffer* newStaticVertexBuffer(const void* data, std::uint32_t a, std::uint32_t b, - std::uint32_t c) = 0; // slot 25 - virtual PiInternalVertexBuffer* newDynamicVertexBuffer(std::uint32_t a, std::uint32_t b) = 0; + virtual PiInternalVertexBuffer* newStaticVertexBuffer(const void* data, std::uint32_t stride, + std::uint32_t elementSize, + std::uint32_t elementCount) = 0; // slot 25 + // Filled later with PiInternalVertexBuffer::update. + virtual PiInternalVertexBuffer* newDynamicVertexBuffer(std::uint32_t elementSize, std::uint32_t elementCount) = 0; virtual SharedPtr newPixmapRenderTarget(Vec2i size) = 0; virtual SharedPtr newTextureRenderTarget(Vec2i size) = 0; virtual void beginRendering(PiRenderTarget* target) = 0; virtual void endRendering() = 0; // slot 30 + // Vertex buffers: positions (Vec3f), normals (Vec3f), texture coordinates (Vec2f), colors (Color); all but + // positions may be null. virtual void drawTriangles(PiInternalIndexBuffer* indices, PiInternalVertexBuffer* positions, - PiInternalVertexBuffer* normals, PiInternalVertexBuffer* texCoords0, - PiInternalVertexBuffer* texCoords1) = 0; + PiInternalVertexBuffer* normals, PiInternalVertexBuffer* texCoords, + PiInternalVertexBuffer* colors) = 0; virtual void drawLine(Vec3f from, Vec3f to, std::uint32_t color) = 0; virtual void drawPointSprites(const PointSpriteVector& sprites) = 0; virtual void drawTrianglesFromMemory(std::uint32_t vertexCount, std::uint32_t indexCount, const std::uint16_t* indices, const VertexAttributeArray& positions, const VertexAttributeArray& normals, const VertexAttributeArray& texCoords, - const VertexAttributeArray& colors) = 0; // slot 34 + const VertexAttributeArray& colors) = 0; // slot 34 private: // PiRenderEngine's own fields, after the vtable pointer: a magic number IMVU's destructor checks, and a size diff --git a/include/t5de/imvu/resources.hpp b/include/t5de/imvu/resources.hpp new file mode 100644 index 0000000..640c427 --- /dev/null +++ b/include/t5de/imvu/resources.hpp @@ -0,0 +1,46 @@ +#pragma once +// The objects a PiRenderEngine creates and hands to IMVU's code: buffers, textures, render targets. IMVU's code keeps +// them, calls their virtual methods, and deletes them through their virtual destructors. Declared from the vtables in +// SceneWindow.dll; method names are ours, from what IMVU's implementations do. Not yet verified by a renderer of our +// own. + +#include + +#include "t5de/imvu/types.hpp" + +namespace t5de::imvu { + +class PiInternalIndexBuffer { +public: + virtual ~PiInternalIndexBuffer() = default; +}; + +class PiInternalVertexBuffer { +public: + virtual ~PiInternalVertexBuffer() = default; + // Replaces a dynamic buffer's contents: elementCount elements of elementSize bytes, stride bytes apart (a multiple + // of 4, at least elementSize). Element size and count are the ones the buffer was created with. + virtual void update(const void* data, std::uint32_t stride, std::uint32_t elementSize, + std::uint32_t elementCount) = 0; + // Unknown: IMVU's software buffer forgets its data; its Direct3D buffer asserts, so it isn't called there. + virtual void unknown2() = 0; +}; + +class PiRenderTexture { +public: + virtual ~PiRenderTexture() = default; + virtual Vec2i size() const = 0; + // Copies the parts of pixmap in regions into the texture. (Not overloaded with update: MSVC orders overloaded + // virtual methods unlike their declaration.) + virtual void updateRegions(const Pixmap& pixmap, const CompoundRegion& regions) = 0; + // Copies all of pixmap into the texture. + virtual void update(const Pixmap& pixmap) = 0; + // Reads the texture into a new Pixmap, constructed in result (a Pixmap returned by value). + virtual Pixmap* read(Pixmap* result) const = 0; +}; + +class PiRenderTarget; +class PiPixmapRenderTarget; +class PiTextureRenderTarget; + +} // namespace t5de::imvu diff --git a/include/t5de/imvu/types.hpp b/include/t5de/imvu/types.hpp new file mode 100644 index 0000000..c065096 --- /dev/null +++ b/include/t5de/imvu/types.hpp @@ -0,0 +1,99 @@ +#pragma once +// The data types IMVU's renderer interface passes, as SceneWindow.dll's Direct3D 9 engine reads them. Most are passed +// by reference: where IMVU's type might hold more than we read, its size isn't asserted. See docs/interface.md. + +#include +#include + +namespace t5de::imvu { + +template +struct Vec2 { + T x; + T y; +}; + +template +struct Vec3 { + T x; + T y; + T z; +}; + +using Vec2i = Vec2; +using Vec2f = Vec2; +using Vec3f = Vec3; + +// Colors are 0xAARRGGBB (Direct3D's D3DCOLOR). Light and ambient colors must have zero alpha. +using Color = std::uint32_t; + +// A 4x4 matrix in Direct3D's convention: row-major, transforming row vectors (p' = p * M). +struct Mat4f { + float m[4][4]; +}; + +// An affine transform: a 3x3 matrix (row-major), then a translation; p' = p * linear + translation, as in Direct3D. +// The Direct3D engine expands it to [linear 0; translation 1]. (What it reads; IMVU's type may be larger.) +struct Transform { + float linear[3][3]; + Vec3f translation; +}; + +// A texture coordinate transform: u' = u * m[0] + v * m[1] + m[4], v' = u * m[2] + v * m[3] + m[5]. +struct TextureTransform { + float m[6]; +}; + +enum class LightType : int { + Point = 0, // at `vector`; the Direct3D engine sets no attenuation and a fixed range + Directional = 1, // shining along -`vector` (a zero vector means along -x) +}; + +// What the Direct3D engine reads of a light. (IMVU's type may be larger.) +struct LightProperties { + bool enabled; + Color diffuse; // zero alpha + Vec3f vector; + LightType type; +}; + +static_assert(offsetof(LightProperties, diffuse) == 4 && offsetof(LightProperties, vector) == 8 + && offsetof(LightProperties, type) == 0x14); + +// Blend functions, as source and destination factors. +enum class RenderBlendMode : int { + Alpha = 1, // source alpha, inverse source alpha + Additive = 2, // one, one + Premultiplied = 3, // one, inverse source alpha + AdditiveAlpha = 4, // source alpha, one +}; + +// clear()'s flags. +enum ClearFlags : std::uint32_t { + ClearColor = 1, + ClearDepth = 2, // to 1.0 +}; + +// Vertex attributes in memory: count elements, stride bytes apart (0: packed). +template +struct VertexAttributeArray { + const T* data; + std::uint32_t count; + std::uint32_t stride; +}; + +static_assert(sizeof(VertexAttributeArray) == 12); + +// IMVU's image class (pixmap.dll): 32-bit pixels (Color). Its layout isn't needed: pixmap.dll exports its methods +// (width, height, pitch, rowBytes, pixels, ...). +class Pixmap; + +// Unknown so far: used only by drawPointSprites (particles), as a Visual C++ 2010 std::vector. +struct PointSpriteVector; + +// Region lists IMVU passes to texture updates; unknown so far. +class CompoundRegion; + +class INativeWindow; + +} // namespace t5de::imvu diff --git a/src/passthrough_engine.cpp b/src/passthrough_engine.cpp index 8e5c935..72fd516 100644 --- a/src/passthrough_engine.cpp +++ b/src/passthrough_engine.cpp @@ -1,5 +1,8 @@ #include "passthrough_engine.hpp" +#include + +#include #include #include "log.hpp" @@ -49,6 +52,45 @@ constexpr std::array names = { // Frames whose calls are logged: the first few, then one a minute or so at 30 FPS. bool logged(std::uint64_t frame) { return frame < 5 || frame % 1800 == 0; } +// Whether to log a call's arguments: the first few calls of each method, in the whole process. Enough to check that +// our layouts of IMVU's types read sensible values. +bool sampled(std::size_t slot) +{ + static std::array, PassthroughEngine::slots> calls{}; + return calls[slot]++ < 3; +} + +std::string text(const imvu::Vec3f& v) { return std::format("({:.3f}, {:.3f}, {:.3f})", v.x, v.y, v.z); } + +std::string text(const imvu::Transform& t) +{ + const auto& m = t.linear; + return std::format("[{:.3f} {:.3f} {:.3f}; {:.3f} {:.3f} {:.3f}; {:.3f} {:.3f} {:.3f}] + {}", m[0][0], m[0][1], + m[0][2], m[1][0], m[1][1], m[1][2], m[2][0], m[2][1], m[2][2], text(t.translation)); +} + +// A pixmap's size, from pixmap.dll's exported Pixmap::width and Pixmap::height. +imvu::Vec2i pixmap_size(const imvu::Pixmap& pixmap) +{ + using Getter = unsigned(__thiscall*)(const imvu::Pixmap*); + struct Getters { + Getter width; + Getter height; + }; + static const Getters getters = [] { + HMODULE module = GetModuleHandleW(L"pixmap.dll"); + if (module == nullptr) { + return Getters{}; + } + return Getters{reinterpret_cast(GetProcAddress(module, "?width@Pixmap@@QBEIXZ")), + reinterpret_cast(GetProcAddress(module, "?height@Pixmap@@QBEIXZ"))}; + }(); + if (getters.width == nullptr || getters.height == nullptr) { + return {-1, -1}; + } + return {static_cast(getters.width(&pixmap)), static_cast(getters.height(&pixmap))}; +} + } // namespace PassthroughEngine::PassthroughEngine(imvu::SharedPtr inner) : inner_(std::move(inner)) @@ -73,26 +115,42 @@ void PassthroughEngine::enable2dFrameBuffer() inner_->enable2dFrameBuffer(); } -void PassthroughEngine::clear(std::uint32_t flags, std::uint32_t color) +void PassthroughEngine::clear(std::uint32_t flags, imvu::Color color) { + if (sampled(3)) { + log::info("clear(flags {}, color {:08x})", flags, color); + } count(3); inner_->clear(flags, color); } void PassthroughEngine::setProjection(imvu::Vec2i viewport, const imvu::Mat4f& projection) { + if (sampled(4)) { + const auto& m = projection.m; + log::info("setProjection({}x{}, [{:.3f} {:.3f} {:.3f} {:.3f}; {:.3f} {:.3f} {:.3f} {:.3f}; " + "{:.3f} {:.3f} {:.3f} {:.3f}; {:.3f} {:.3f} {:.3f} {:.3f}])", + viewport.x, viewport.y, m[0][0], m[0][1], m[0][2], m[0][3], m[1][0], m[1][1], m[1][2], + m[1][3], m[2][0], m[2][1], m[2][2], m[2][3], m[3][0], m[3][1], m[3][2], m[3][3]); + } count(4); inner_->setProjection(viewport, projection); } void PassthroughEngine::setModelSpace(const imvu::Transform& model) { + if (sampled(5)) { + log::info("setModelSpace({})", text(model)); + } count(5); inner_->setModelSpace(model); } void PassthroughEngine::setViewSpace(const imvu::Transform& view) { + if (sampled(6)) { + log::info("setViewSpace({})", text(view)); + } count(6); inner_->setViewSpace(view); } @@ -117,6 +175,9 @@ void PassthroughEngine::setDepthWritingOn(bool on) void PassthroughEngine::setBlendMode(imvu::RenderBlendMode mode) { + if (sampled(10)) { + log::info("setBlendMode({})", static_cast(mode)); + } count(10); inner_->setBlendMode(mode); } @@ -129,6 +190,9 @@ void PassthroughEngine::setAlphaTestingOn(bool on) void PassthroughEngine::setAlphaTestValue(float value) { + if (sampled(12)) { + log::info("setAlphaTestValue({:.3f})", value); + } count(12); inner_->setAlphaTestValue(value); } @@ -159,6 +223,10 @@ void PassthroughEngine::setLightingOn(bool on) void PassthroughEngine::setLightProperties(std::uint32_t index, const imvu::LightProperties& light) { + if (sampled(17)) { + log::info("setLightProperties({}, enabled {}, diffuse {:08x}, vector {}, type {})", index, + light.enabled, light.diffuse, text(light.vector), static_cast(light.type)); + } count(17); inner_->setLightProperties(index, light); } @@ -169,8 +237,11 @@ void PassthroughEngine::setFogOn(bool on) inner_->setFogOn(on); } -void PassthroughEngine::setFogAttributes(float start, float end, std::uint32_t color) +void PassthroughEngine::setFogAttributes(float start, float end, imvu::Color color) { + if (sampled(19)) { + log::info("setFogAttributes(start {:.3f}, end {:.3f}, color {:08x})", start, end, color); + } count(19); inner_->setFogAttributes(start, end, color); } @@ -183,39 +254,61 @@ void PassthroughEngine::setTextureTransformMatrixIdentity(std::uint32_t stage) void PassthroughEngine::setTextureTransformMatrix(std::uint32_t stage, const imvu::TextureTransform& transform) { + if (sampled(21)) { + const auto& m = transform.m; + log::info("setTextureTransformMatrix({}, [{:.3f} {:.3f} {:.3f} {:.3f} {:.3f} {:.3f}])", stage, m[0], + m[1], m[2], m[3], m[4], m[5]); + } count(21); inner_->setTextureTransformMatrix(stage, transform); } -void PassthroughEngine::setEnvironmentAmbient(std::uint32_t color) +void PassthroughEngine::setEnvironmentAmbient(imvu::Color color) { + if (sampled(22)) { + log::info("setEnvironmentAmbient({:08x})", color); + } count(22); inner_->setEnvironmentAmbient(color); } -imvu::PiRenderTexture* PassthroughEngine::newRenderTexture(imvu::Pixmap* pixmap, std::uint32_t flags) +imvu::PiRenderTexture* PassthroughEngine::newRenderTexture(const imvu::Pixmap& pixmap, std::uint32_t flags) { + if (sampled(23)) { + const auto size = pixmap_size(pixmap); + log::info("newRenderTexture({}x{} pixmap, flags {:x})", size.x, size.y, flags); + } count(23); return inner_->newRenderTexture(pixmap, flags); } imvu::PiInternalIndexBuffer* PassthroughEngine::newIndexBuffer(std::uint32_t indexCount, const std::uint16_t* indices) { + if (sampled(24)) { + log::info("newIndexBuffer({} indices)", indexCount); + } count(24); return inner_->newIndexBuffer(indexCount, indices); } -imvu::PiInternalVertexBuffer* PassthroughEngine::newStaticVertexBuffer(const void* data, std::uint32_t a, - std::uint32_t b, std::uint32_t c) +imvu::PiInternalVertexBuffer* PassthroughEngine::newStaticVertexBuffer(const void* data, std::uint32_t stride, + std::uint32_t elementSize, std::uint32_t elementCount) { + if (sampled(25)) { + log::info("newStaticVertexBuffer(stride {}, element size {}, {} elements)", stride, elementSize, + elementCount); + } count(25); - return inner_->newStaticVertexBuffer(data, a, b, c); + return inner_->newStaticVertexBuffer(data, stride, elementSize, elementCount); } -imvu::PiInternalVertexBuffer* PassthroughEngine::newDynamicVertexBuffer(std::uint32_t a, std::uint32_t b) +imvu::PiInternalVertexBuffer* PassthroughEngine::newDynamicVertexBuffer(std::uint32_t elementSize, std::uint32_t elementCount) { + if (sampled(26)) { + log::info("newDynamicVertexBuffer(element size {}, {} elements)", elementSize, elementCount); + } count(26); - return inner_->newDynamicVertexBuffer(a, b); + return inner_->newDynamicVertexBuffer(elementSize, elementCount); } imvu::SharedPtr PassthroughEngine::newPixmapRenderTarget(imvu::Vec2i size) @@ -257,15 +350,18 @@ void PassthroughEngine::endRendering() } void PassthroughEngine::drawTriangles(imvu::PiInternalIndexBuffer* indices, imvu::PiInternalVertexBuffer* positions, - imvu::PiInternalVertexBuffer* normals, imvu::PiInternalVertexBuffer* texCoords0, - imvu::PiInternalVertexBuffer* texCoords1) + imvu::PiInternalVertexBuffer* normals, imvu::PiInternalVertexBuffer* texCoords, + imvu::PiInternalVertexBuffer* colors) { count(31); - inner_->drawTriangles(indices, positions, normals, texCoords0, texCoords1); + inner_->drawTriangles(indices, positions, normals, texCoords, colors); } void PassthroughEngine::drawLine(imvu::Vec3f from, imvu::Vec3f to, std::uint32_t color) { + if (sampled(32)) { + log::info("drawLine({}, {}, {:08x})", text(from), text(to), color); + } count(32); inner_->drawLine(from, to, color); } @@ -281,8 +377,12 @@ void PassthroughEngine::drawTrianglesFromMemory(std::uint32_t vertexCount, std:: const imvu::VertexAttributeArray& positions, const imvu::VertexAttributeArray& normals, const imvu::VertexAttributeArray& texCoords, - const imvu::VertexAttributeArray& colors) + const imvu::VertexAttributeArray& colors) { + if (sampled(34)) { + log::info("drawTrianglesFromMemory({} vertices, {} indices; positions {} stride {}, colors {} stride {})", + vertexCount, indexCount, positions.count, positions.stride, colors.count, colors.stride); + } count(34); inner_->drawTrianglesFromMemory(vertexCount, indexCount, indices, positions, normals, texCoords, colors); } diff --git a/src/passthrough_engine.hpp b/src/passthrough_engine.hpp index f5b5638..dd65270 100644 --- a/src/passthrough_engine.hpp +++ b/src/passthrough_engine.hpp @@ -16,7 +16,7 @@ public: void resizeFrameBuffer(imvu::Vec2i size) override; void enable2dFrameBuffer() override; - void clear(std::uint32_t flags, std::uint32_t color) override; + void clear(std::uint32_t flags, imvu::Color color) override; void setProjection(imvu::Vec2i viewport, const imvu::Mat4f& projection) override; void setModelSpace(const imvu::Transform& model) override; void setViewSpace(const imvu::Transform& view) override; @@ -32,29 +32,31 @@ public: void setLightingOn(bool on) override; void setLightProperties(std::uint32_t index, const imvu::LightProperties& light) override; void setFogOn(bool on) override; - void setFogAttributes(float start, float end, std::uint32_t color) override; + void setFogAttributes(float start, float end, imvu::Color color) override; void setTextureTransformMatrixIdentity(std::uint32_t stage) override; void setTextureTransformMatrix(std::uint32_t stage, const imvu::TextureTransform& transform) override; - void setEnvironmentAmbient(std::uint32_t color) override; - imvu::PiRenderTexture* newRenderTexture(imvu::Pixmap* pixmap, std::uint32_t flags) override; + void setEnvironmentAmbient(imvu::Color color) override; + imvu::PiRenderTexture* newRenderTexture(const imvu::Pixmap& pixmap, std::uint32_t flags) override; imvu::PiInternalIndexBuffer* newIndexBuffer(std::uint32_t count, const std::uint16_t* indices) override; - imvu::PiInternalVertexBuffer* newStaticVertexBuffer(const void* data, std::uint32_t a, std::uint32_t b, - std::uint32_t c) override; - imvu::PiInternalVertexBuffer* newDynamicVertexBuffer(std::uint32_t a, std::uint32_t b) override; + imvu::PiInternalVertexBuffer* newStaticVertexBuffer(const void* data, std::uint32_t stride, + std::uint32_t elementSize, + std::uint32_t elementCount) override; + imvu::PiInternalVertexBuffer* newDynamicVertexBuffer(std::uint32_t elementSize, + std::uint32_t elementCount) override; imvu::SharedPtr newPixmapRenderTarget(imvu::Vec2i size) override; imvu::SharedPtr newTextureRenderTarget(imvu::Vec2i size) override; void beginRendering(imvu::PiRenderTarget* target) override; void endRendering() override; void drawTriangles(imvu::PiInternalIndexBuffer* indices, imvu::PiInternalVertexBuffer* positions, - imvu::PiInternalVertexBuffer* normals, imvu::PiInternalVertexBuffer* texCoords0, - imvu::PiInternalVertexBuffer* texCoords1) override; + imvu::PiInternalVertexBuffer* normals, imvu::PiInternalVertexBuffer* texCoords, + imvu::PiInternalVertexBuffer* colors) override; void drawLine(imvu::Vec3f from, imvu::Vec3f to, std::uint32_t color) override; void drawPointSprites(const imvu::PointSpriteVector& sprites) override; void drawTrianglesFromMemory(std::uint32_t vertexCount, std::uint32_t indexCount, const std::uint16_t* indices, const imvu::VertexAttributeArray& positions, const imvu::VertexAttributeArray& normals, const imvu::VertexAttributeArray& texCoords, - const imvu::VertexAttributeArray& colors) override; + const imvu::VertexAttributeArray& colors) override; // The interface's methods, by vtable slot (slot 0, the destructor, isn't counted). static constexpr std::size_t slots = 35;