Declare the renderer interface's data types

Transforms, matrices, colors, lights, blend modes, fog, texture transforms, vertex arrays and the engine's buffer and texture objects, worked out from IMVU's Direct3D engine. The pass-through logs the first calls' arguments read through them, to check them in the client.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
dhkatzandClaude Opus 5.5 committed 2026-10-28 04:36:37 -07:00
1 parent 3c28a8794b
commit 83e0665681
6 files changed
+298 -77

No files matched your search

+17 -51
View File
@@ -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 <cstdint>
#include "t5de/imvu/resources.hpp"
#include "t5de/imvu/shared_ptr.hpp"
#include "t5de/imvu/types.hpp"
namespace t5de::imvu {
template <class T>
struct Vec2 {
T x;
T y;
};
template <class T>
struct Vec3 {
T x;
T y;
T z;
};
using Vec2i = Vec2<int>;
using Vec2f = Vec2<float>;
using Vec3f = Vec3<float>;
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 T>
class VertexAttributeArray;
// std::vector<PointSprite> 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<PiPixmapRenderTarget> newPixmapRenderTarget(Vec2i size) = 0;
virtual SharedPtr<PiTextureRenderTarget> 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<Vec3f>& positions,
const VertexAttributeArray<Vec3f>& normals,
const VertexAttributeArray<Vec2f>& texCoords,
const VertexAttributeArray<std::uint32_t>& colors) = 0; // slot 34
const VertexAttributeArray<Color>& colors) = 0; // slot 34
private:
// PiRenderEngine's own fields, after the vtable pointer: a magic number IMVU's destructor checks, and a size
+46
View File
@@ -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 <cstdint>
#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
+99
View File
@@ -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 <cstddef>
#include <cstdint>
namespace t5de::imvu {
template <class T>
struct Vec2 {
T x;
T y;
};
template <class T>
struct Vec3 {
T x;
T y;
T z;
};
using Vec2i = Vec2<int>;
using Vec2f = Vec2<float>;
using Vec3f = Vec3<float>;
// 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 <class T>
struct VertexAttributeArray {
const T* data;
std::uint32_t count;
std::uint32_t stride;
};
static_assert(sizeof(VertexAttributeArray<Vec3f>) == 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<PointSprite>.
struct PointSpriteVector;
// Region lists IMVU passes to texture updates; unknown so far.
class CompoundRegion;
class INativeWindow;
} // namespace t5de::imvu