Draw the client's scene in the renderer process
The adapter records everything the client draws (state, transforms, lights, fog, textures and buffers) as a command stream, sent in chunks through the shared memory channel, while IMVU's Direct3D renderer still draws the whole view. The renderer process draws it with Diligent, with shaders reproducing IMVU's Direct3D 9 fixed-function setup, over the right half of the 3D view to compare. It skips to the newest frame when it falls behind, and logs its errors and frame times. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
5c35c8b63a
commit
42fc55cb91
23 files changed
+2121
-472
No files matched your search
@@ -0,0 +1,296 @@
|
||||
#include "engine.hpp"
|
||||
|
||||
#include <bit>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
|
||||
#include "log.hpp"
|
||||
#include "resources.hpp"
|
||||
|
||||
namespace t5de {
|
||||
|
||||
using protocol::Op;
|
||||
using protocol::State;
|
||||
|
||||
namespace {
|
||||
|
||||
protocol::SetTransform transform_command(const imvu::Transform& t)
|
||||
{
|
||||
protocol::SetTransform command{};
|
||||
std::memcpy(command.m, &t.linear[0][0], sizeof(float) * 9);
|
||||
command.m[9] = t.translation.x;
|
||||
command.m[10] = t.translation.y;
|
||||
command.m[11] = t.translation.z;
|
||||
return command;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Engine::Engine(imvu::SharedPtr<imvu::PiRenderEngine> inner, std::shared_ptr<Recorder> recorder)
|
||||
: inner_(std::move(inner)), recorder_(std::move(recorder))
|
||||
{
|
||||
}
|
||||
|
||||
Engine::~Engine() { log::info("engine: destroyed after {} frames", frames_); }
|
||||
|
||||
void Engine::setState(State state, std::uint32_t value)
|
||||
{
|
||||
recorder_->add(Op::SetState, protocol::SetState{state, value});
|
||||
}
|
||||
|
||||
void Engine::resizeFrameBuffer(imvu::Vec2i size) { inner_->resizeFrameBuffer(size); }
|
||||
|
||||
void Engine::enable2dFrameBuffer() { inner_->enable2dFrameBuffer(); }
|
||||
|
||||
void Engine::clear(std::uint32_t flags, imvu::Color color)
|
||||
{
|
||||
inner_->clear(flags, color);
|
||||
recorder_->add(Op::Clear, protocol::Clear{flags, color});
|
||||
}
|
||||
|
||||
void Engine::setProjection(imvu::Vec2i viewport, const imvu::Mat4f& projection)
|
||||
{
|
||||
inner_->setProjection(viewport, projection);
|
||||
protocol::SetProjection command{viewport.x, viewport.y, {}};
|
||||
std::memcpy(command.m, &projection.m[0][0], sizeof(command.m));
|
||||
recorder_->add(Op::SetProjection, command);
|
||||
}
|
||||
|
||||
void Engine::setModelSpace(const imvu::Transform& model)
|
||||
{
|
||||
inner_->setModelSpace(model);
|
||||
recorder_->add(Op::SetModel, transform_command(model));
|
||||
}
|
||||
|
||||
void Engine::setViewSpace(const imvu::Transform& view)
|
||||
{
|
||||
inner_->setViewSpace(view);
|
||||
recorder_->add(Op::SetView, transform_command(view));
|
||||
}
|
||||
|
||||
void Engine::setTextures(imvu::PiRenderTexture* texture0, imvu::PiRenderTexture* texture1)
|
||||
{
|
||||
inner_->setTextures(inner(texture0), inner(texture1));
|
||||
recorder_->add(Op::SetTextures, protocol::SetTextures{id(texture0), id(texture1)});
|
||||
}
|
||||
|
||||
void Engine::setDepthTestingOn(bool on)
|
||||
{
|
||||
inner_->setDepthTestingOn(on);
|
||||
setState(State::DepthTest, on);
|
||||
}
|
||||
|
||||
void Engine::setDepthWritingOn(bool on)
|
||||
{
|
||||
inner_->setDepthWritingOn(on);
|
||||
setState(State::DepthWrite, on);
|
||||
}
|
||||
|
||||
void Engine::setBlendMode(imvu::RenderBlendMode mode)
|
||||
{
|
||||
inner_->setBlendMode(mode);
|
||||
setState(State::BlendMode, static_cast<std::uint32_t>(mode));
|
||||
}
|
||||
|
||||
void Engine::setAlphaTestingOn(bool on)
|
||||
{
|
||||
inner_->setAlphaTestingOn(on);
|
||||
setState(State::AlphaTest, on);
|
||||
}
|
||||
|
||||
void Engine::setAlphaTestValue(float value)
|
||||
{
|
||||
inner_->setAlphaTestValue(value);
|
||||
setState(State::AlphaReference, static_cast<std::uint32_t>(std::lround(value * 255.0f)));
|
||||
}
|
||||
|
||||
void Engine::setBlendingOn(bool on)
|
||||
{
|
||||
inner_->setBlendingOn(on);
|
||||
setState(State::Blending, on);
|
||||
}
|
||||
|
||||
void Engine::setBackFaceCullingOn(bool on)
|
||||
{
|
||||
inner_->setBackFaceCullingOn(on);
|
||||
setState(State::Culling, on);
|
||||
}
|
||||
|
||||
void Engine::setNormalInvertingOn(bool on)
|
||||
{
|
||||
inner_->setNormalInvertingOn(on);
|
||||
setState(State::NormalInverting, on);
|
||||
}
|
||||
|
||||
void Engine::setLightingOn(bool on)
|
||||
{
|
||||
inner_->setLightingOn(on);
|
||||
setState(State::Lighting, on);
|
||||
}
|
||||
|
||||
void Engine::setLightProperties(std::uint32_t index, const imvu::LightProperties& light)
|
||||
{
|
||||
inner_->setLightProperties(index, light);
|
||||
protocol::SetLight command{index, light.enabled, light.diffuse,
|
||||
light.type == imvu::LightType::Directional,
|
||||
{light.vector.x, light.vector.y, light.vector.z}};
|
||||
// IMVU's Direct3D renderer shines a directional light along -vector, along -x for a zero vector.
|
||||
if (command.directional && light.vector.x == 0 && light.vector.y == 0 && light.vector.z == 0) {
|
||||
command.vector[0] = 1;
|
||||
}
|
||||
recorder_->add(Op::SetLight, command);
|
||||
}
|
||||
|
||||
void Engine::setFogOn(bool on)
|
||||
{
|
||||
inner_->setFogOn(on);
|
||||
setState(State::Fog, on);
|
||||
}
|
||||
|
||||
void Engine::setFogAttributes(float start, float end, imvu::Color color)
|
||||
{
|
||||
inner_->setFogAttributes(start, end, color);
|
||||
recorder_->add(Op::SetFog, protocol::SetFog{start, end, color});
|
||||
}
|
||||
|
||||
void Engine::setTextureTransformMatrixIdentity(std::uint32_t stage)
|
||||
{
|
||||
inner_->setTextureTransformMatrixIdentity(stage);
|
||||
recorder_->add(Op::SetTextureTransform, protocol::SetTextureTransform{stage, 1, {1, 0, 0, 1, 0, 0}});
|
||||
}
|
||||
|
||||
void Engine::setTextureTransformMatrix(std::uint32_t stage, const imvu::TextureTransform& transform)
|
||||
{
|
||||
inner_->setTextureTransformMatrix(stage, transform);
|
||||
protocol::SetTextureTransform command{stage, 0, {}};
|
||||
std::memcpy(command.m, transform.m, sizeof(command.m));
|
||||
recorder_->add(Op::SetTextureTransform, command);
|
||||
}
|
||||
|
||||
void Engine::setEnvironmentAmbient(imvu::Color color)
|
||||
{
|
||||
inner_->setEnvironmentAmbient(color);
|
||||
setState(State::Ambient, color);
|
||||
}
|
||||
|
||||
imvu::PiRenderTexture* Engine::newRenderTexture(const imvu::Pixmap& pixmap, std::uint32_t flags)
|
||||
{
|
||||
imvu::PiRenderTexture* texture = inner_->newRenderTexture(pixmap, flags);
|
||||
if (texture == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
return new Texture(texture, recorder_, recorder_->createTexture(pixmap, flags));
|
||||
}
|
||||
|
||||
imvu::PiInternalIndexBuffer* Engine::newIndexBuffer(std::uint32_t count, const std::uint16_t* indices)
|
||||
{
|
||||
imvu::PiInternalIndexBuffer* buffer = inner_->newIndexBuffer(count, indices);
|
||||
if (buffer == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
return new IndexBuffer(buffer, recorder_,
|
||||
recorder_->createBuffer(protocol::BufferKind::Index, indices, 2, 2, count));
|
||||
}
|
||||
|
||||
imvu::PiInternalVertexBuffer* Engine::newStaticVertexBuffer(const void* data, std::uint32_t stride,
|
||||
std::uint32_t elementSize, std::uint32_t elementCount)
|
||||
{
|
||||
imvu::PiInternalVertexBuffer* buffer = inner_->newStaticVertexBuffer(data, stride, elementSize, elementCount);
|
||||
if (buffer == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
return new VertexBuffer(
|
||||
buffer, recorder_,
|
||||
recorder_->createBuffer(protocol::BufferKind::Vertex, data, stride, elementSize, elementCount));
|
||||
}
|
||||
|
||||
imvu::PiInternalVertexBuffer* Engine::newDynamicVertexBuffer(std::uint32_t elementSize, std::uint32_t elementCount)
|
||||
{
|
||||
imvu::PiInternalVertexBuffer* buffer = inner_->newDynamicVertexBuffer(elementSize, elementCount);
|
||||
if (buffer == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
return new VertexBuffer(
|
||||
buffer, recorder_,
|
||||
recorder_->createBuffer(protocol::BufferKind::Vertex, nullptr, 0, elementSize, elementCount));
|
||||
}
|
||||
|
||||
imvu::SharedPtr<imvu::PiPixmapRenderTarget> Engine::newPixmapRenderTarget(imvu::Vec2i size)
|
||||
{
|
||||
return inner_->newPixmapRenderTarget(size);
|
||||
}
|
||||
|
||||
imvu::SharedPtr<imvu::PiTextureRenderTarget> Engine::newTextureRenderTarget(imvu::Vec2i size)
|
||||
{
|
||||
return inner_->newTextureRenderTarget(size);
|
||||
}
|
||||
|
||||
void Engine::beginRendering(imvu::PiRenderTarget* target)
|
||||
{
|
||||
inner_->beginRendering(target);
|
||||
recorder_->add(Op::BeginFrame, protocol::BeginFrame{target != nullptr});
|
||||
}
|
||||
|
||||
void Engine::endRendering()
|
||||
{
|
||||
inner_->endRendering();
|
||||
recorder_->add(Op::EndFrame, protocol::EndFrame{});
|
||||
recorder_->flush(true);
|
||||
++frames_;
|
||||
}
|
||||
|
||||
void Engine::drawTriangles(imvu::PiInternalIndexBuffer* indices, imvu::PiInternalVertexBuffer* positions,
|
||||
imvu::PiInternalVertexBuffer* normals, imvu::PiInternalVertexBuffer* texCoords,
|
||||
imvu::PiInternalVertexBuffer* colors)
|
||||
{
|
||||
inner_->drawTriangles(inner(indices), inner(positions), inner(normals), inner(texCoords), inner(colors));
|
||||
recorder_->add(Op::DrawTriangles,
|
||||
protocol::DrawTriangles{id(indices), id(positions), id(normals), id(texCoords), id(colors)});
|
||||
}
|
||||
|
||||
void Engine::drawLine(imvu::Vec3f from, imvu::Vec3f to, imvu::Color color) { inner_->drawLine(from, to, color); }
|
||||
|
||||
void Engine::drawPointSprites(const imvu::PointSpriteVector& sprites) { inner_->drawPointSprites(sprites); }
|
||||
|
||||
void Engine::drawTrianglesFromMemory(std::uint32_t vertexCount, std::uint32_t indexCount,
|
||||
const std::uint16_t* indices,
|
||||
const imvu::VertexAttributeArray<imvu::Vec3f>& positions,
|
||||
const imvu::VertexAttributeArray<imvu::Vec3f>& normals,
|
||||
const imvu::VertexAttributeArray<imvu::Vec2f>& texCoords,
|
||||
const imvu::VertexAttributeArray<imvu::Color>& colors)
|
||||
{
|
||||
inner_->drawTrianglesFromMemory(vertexCount, indexCount, indices, positions, normals, texCoords, colors);
|
||||
if (indices == nullptr || positions.data == nullptr || vertexCount == 0) {
|
||||
return;
|
||||
}
|
||||
const bool hasNormals = normals.data != nullptr && normals.count >= vertexCount;
|
||||
const bool hasTexCoords = texCoords.data != nullptr && texCoords.count >= vertexCount;
|
||||
const bool hasColors = colors.data != nullptr && colors.count >= vertexCount;
|
||||
const std::size_t indexBytes = protocol::padded(std::size_t{indexCount} * 2);
|
||||
const std::size_t size = indexBytes + std::size_t{vertexCount} * (12 + (hasNormals ? 12 : 0) +
|
||||
(hasTexCoords ? 8 : 0) + (hasColors ? 4 : 0));
|
||||
std::byte* payload = recorder_->add(Op::DrawArrays,
|
||||
protocol::DrawArrays{vertexCount, indexCount, hasNormals, hasTexCoords,
|
||||
hasColors},
|
||||
size);
|
||||
if (payload == nullptr) {
|
||||
return;
|
||||
}
|
||||
std::memcpy(payload, indices, std::size_t{indexCount} * 2);
|
||||
std::byte* next = payload + indexBytes;
|
||||
pack(next, positions.data, positions.stride, 12, vertexCount);
|
||||
next += std::size_t{vertexCount} * 12;
|
||||
if (hasNormals) {
|
||||
pack(next, normals.data, normals.stride, 12, vertexCount);
|
||||
next += std::size_t{vertexCount} * 12;
|
||||
}
|
||||
if (hasTexCoords) {
|
||||
pack(next, texCoords.data, texCoords.stride, 8, vertexCount);
|
||||
next += std::size_t{vertexCount} * 8;
|
||||
}
|
||||
if (hasColors) {
|
||||
pack(next, colors.data, colors.stride, 4, vertexCount);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace t5de
|
||||
Reference in new issue
Block a user