428 lines
17 KiB
C++
428 lines
17 KiB
C++
#include <basalt/char_types.hpp>
|
||
#include <basalt/export_macro.hpp>
|
||
#include <basalt/engine.hpp>
|
||
#include <basalt/object_loader.hpp>
|
||
#include <basalt/anime_loader.hpp>
|
||
#include <spdlog/spdlog.h>
|
||
#include <windows.h>
|
||
#include <d3d11.h>
|
||
#include <d3dcompiler.h>
|
||
#include <wrl/client.h>
|
||
#include <vector>
|
||
#include <iostream>
|
||
#include <cmath>
|
||
#include <print>
|
||
|
||
using Microsoft::WRL::ComPtr;
|
||
#define DXCHK(condition) \
|
||
if (FAILED(condition)) { \
|
||
std::println("bad DirectX calling with code {}", condition); \
|
||
throw std::runtime_error("bad DirectX calling"); \
|
||
}
|
||
|
||
#pragma region Win32 Window Creation
|
||
|
||
// Window procedure for the render window
|
||
static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
|
||
switch (msg) {
|
||
case WM_CLOSE:
|
||
PostQuitMessage(0);
|
||
return 0;
|
||
default:
|
||
break;
|
||
}
|
||
return DefWindowProcW(hwnd, msg, wParam, lParam);
|
||
}
|
||
|
||
// Create a render window for DirectX
|
||
static HWND CreateRenderWindow(std::uint32_t width, std::uint32_t height) {
|
||
static bool g_CLSREG = false;
|
||
constexpr wchar_t class_name[] = L"DirectXRenderWindowClass";
|
||
|
||
if (!g_CLSREG) {
|
||
WNDCLASSEXW wc = {0};
|
||
wc.cbSize = sizeof(WNDCLASSEXW);
|
||
wc.style = CS_HREDRAW | CS_VREDRAW;
|
||
wc.lpfnWndProc = WndProc;
|
||
wc.hInstance = GetModuleHandleW(nullptr);
|
||
wc.hCursor = LoadCursorW(nullptr, IDC_ARROW);
|
||
wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
|
||
wc.lpszClassName = class_name;
|
||
|
||
if (!RegisterClassExW(&wc)) {
|
||
throw std::runtime_error("Failed to register window class");
|
||
}
|
||
g_CLSREG = true;
|
||
}
|
||
|
||
// Calculate window size including borders
|
||
RECT rect = {0, 0, static_cast<LONG>(width), static_cast<LONG>(height)};
|
||
AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, FALSE);
|
||
|
||
HWND hwnd = CreateWindowExW(0,
|
||
class_name,
|
||
L"DirectXRenderWindow",
|
||
WS_OVERLAPPEDWINDOW ^ WS_THICKFRAME ^ WS_MAXIMIZEBOX,
|
||
CW_USEDEFAULT,
|
||
CW_USEDEFAULT,
|
||
rect.right - rect.left,
|
||
rect.bottom - rect.top,
|
||
nullptr,
|
||
nullptr,
|
||
GetModuleHandleW(nullptr),
|
||
nullptr);
|
||
if (!hwnd) {
|
||
throw std::runtime_error("Failed to create render window");
|
||
}
|
||
|
||
return hwnd;
|
||
}
|
||
|
||
static void DestroyRenderWindow(HWND hwnd) {
|
||
DestroyWindow(hwnd);
|
||
}
|
||
|
||
/**
|
||
* @brief Non-block event loop.
|
||
* @return True for indicate active exit.
|
||
*/
|
||
static bool EventLoop() {
|
||
MSG msg;
|
||
if (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE) == 0) {
|
||
// No msg
|
||
return false;
|
||
} else {
|
||
if (msg.message == WM_QUIT) {
|
||
return true;
|
||
} else {
|
||
TranslateMessage(&msg);
|
||
DispatchMessageW(&msg);
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
|
||
#pragma endregion
|
||
|
||
// 立方体顶点(位置)
|
||
struct Vertex {
|
||
float x, y, z;
|
||
};
|
||
|
||
// 简单顶点着色器(只传位置)
|
||
const char* g_VS = R"(
|
||
float4 main(float3 pos : POSITION) : SV_POSITION {
|
||
return float4(pos, 1.0f);
|
||
}
|
||
)";
|
||
|
||
// 简单像素着色器(返回固定颜色)
|
||
const char* g_PS = R"(
|
||
float4 main() : SV_TARGET {
|
||
return float4(0.2f, 0.4f, 0.8f, 1.0f);
|
||
}
|
||
)";
|
||
|
||
namespace guid = ::basalt::shared::guid;
|
||
namespace engine = ::basalt::shared::engine;
|
||
using ::basalt::shared::math::Vector3;
|
||
using engine::EngineConfig;
|
||
using engine::IEngine;
|
||
|
||
class DirectX11Engine : public IEngine {
|
||
public:
|
||
DirectX11Engine() : IEngine() {}
|
||
virtual ~DirectX11Engine() {}
|
||
|
||
private:
|
||
HWND window; ///< Win32窗口
|
||
ComPtr<IDXGIFactory> factory; ///< 适配器工厂
|
||
ComPtr<IDXGIAdapter> adapter; ///< 适配器
|
||
ComPtr<ID3D11Device> device; ///< 设备
|
||
ComPtr<ID3D11DeviceContext> context; ///< 上下文
|
||
ComPtr<IDXGISwapChain> swap_chain; ///< 交换链
|
||
ComPtr<ID3D11RenderTargetView> rtv; ///< 渲染目标视图
|
||
ComPtr<ID3D11Texture2D> back_buffer; ///< 后缓冲
|
||
ComPtr<ID3D11Texture2D> depth_buffer; ///< 深度缓冲
|
||
ComPtr<ID3D11DepthStencilView> dsv; ///< 深度模板视图
|
||
ComPtr<ID3D11Texture2D> depth_staging; ///< 用于CPU读取的深度暂存纹理
|
||
ComPtr<ID3D11VertexShader> vs; ///< 顶点着色器
|
||
ComPtr<ID3D11PixelShader> ps; ///< 像素着色器
|
||
ComPtr<ID3DBlob> vs_blob; ///< 顶点着色器字节码
|
||
ComPtr<ID3DBlob> ps_blob; ///< 像素着色器字节码
|
||
ComPtr<ID3D11InputLayout> input_layout; ///< 输入布局
|
||
ComPtr<ID3D11Buffer> vertex_buffer; ///< 顶点缓冲
|
||
ComPtr<ID3D11DepthStencilState> depth_state; ///< 深度状态
|
||
ComPtr<ID3D11RasterizerState> rasterizer_state; ///< 光栅化状态
|
||
std::vector<BYTE> depth_data; ///< 深度数据
|
||
UINT vertex_count; ///< 顶点数量
|
||
|
||
public:
|
||
virtual guid::Guid get_guid() const { return guid::DIRECTX11_ENGINE; }
|
||
virtual void startup(EngineConfig&& config) override {
|
||
IEngine::startup(std::move(config));
|
||
|
||
// 创建Win32窗口并显示
|
||
if (this->config.headless) {
|
||
window = NULL;
|
||
} else {
|
||
window = CreateRenderWindow(this->config.width, this->config.height);
|
||
ShowWindow(window, SW_SHOW);
|
||
UpdateWindow(window);
|
||
}
|
||
|
||
// 初始化 COM
|
||
DXCHK(CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED));
|
||
|
||
// 创建Factory和Adapter,枚举并选择设备
|
||
DXCHK(CreateDXGIFactory(IID_PPV_ARGS(&factory)));
|
||
spdlog::info(BSTEXT("Available DirectX Devices:"));
|
||
{
|
||
UINT i = 0;
|
||
while (factory->EnumAdapters(i, &adapter) != DXGI_ERROR_NOT_FOUND) {
|
||
DXGI_ADAPTER_DESC desc;
|
||
DXCHK(adapter->GetDesc(&desc));
|
||
spdlog::info(BSTEXT("\t{}\t{}"), i, desc.Description);
|
||
++i;
|
||
}
|
||
}
|
||
DXCHK(factory->EnumAdapters(static_cast<UINT>(this->config.device), &adapter));
|
||
|
||
// 创建设备和上下文
|
||
DXCHK(D3D11CreateDevice(adapter.Get(),
|
||
D3D_DRIVER_TYPE_UNKNOWN,
|
||
nullptr,
|
||
0,//D3D11_CREATE_DEVICE_DEBUG,
|
||
nullptr,
|
||
0,
|
||
D3D11_SDK_VERSION,
|
||
&device,
|
||
nullptr,
|
||
&context));
|
||
|
||
// 创建交换链(窗口可选,这里仅用于渲染上下文)
|
||
DXGI_SWAP_CHAIN_DESC sd = {};
|
||
sd.BufferDesc.Width = this->config.width;
|
||
sd.BufferDesc.Height = this->config.height;
|
||
sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||
sd.SampleDesc.Count = 1;
|
||
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
|
||
sd.BufferCount = 1;
|
||
sd.OutputWindow = window;
|
||
sd.Windowed = TRUE;
|
||
DXCHK(factory->CreateSwapChain(device.Get(), &sd, &swap_chain));
|
||
|
||
// 创建渲染目标
|
||
DXCHK(swap_chain->GetBuffer(0, IID_PPV_ARGS(&back_buffer)));
|
||
DXCHK(device->CreateRenderTargetView(back_buffer.Get(), nullptr, &rtv));
|
||
|
||
// 创建深度缓冲(D32_FLOAT)
|
||
D3D11_TEXTURE2D_DESC depth_desc = {};
|
||
depth_desc.Width = this->config.width;
|
||
depth_desc.Height = this->config.height;
|
||
depth_desc.MipLevels = 1;
|
||
depth_desc.ArraySize = 1;
|
||
depth_desc.Format = DXGI_FORMAT_D32_FLOAT;
|
||
depth_desc.SampleDesc.Count = 1;
|
||
depth_desc.Usage = D3D11_USAGE_DEFAULT;
|
||
depth_desc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
|
||
DXCHK(device->CreateTexture2D(&depth_desc, nullptr, &depth_buffer));
|
||
|
||
DXCHK(device->CreateDepthStencilView(depth_buffer.Get(), nullptr, &dsv));
|
||
|
||
// 创建 staging texture 用于 CPU 读取深度(R32_FLOAT)
|
||
D3D11_TEXTURE2D_DESC staging_desc = depth_desc;
|
||
staging_desc.Format = DXGI_FORMAT_R32_FLOAT; // 注意:DSV 用 D32_FLOAT,staging 用 R32_FLOAT
|
||
staging_desc.BindFlags = 0;
|
||
staging_desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
|
||
staging_desc.Usage = D3D11_USAGE_STAGING;
|
||
DXCHK(device->CreateTexture2D(&staging_desc, nullptr, &depth_staging));
|
||
|
||
// 编译并创建着色器
|
||
DXCHK(D3DCompile(g_VS, static_cast<UINT>(strlen(g_VS)), nullptr, nullptr, nullptr, "main", "vs_4_0", 0, 0, &vs_blob, nullptr));
|
||
DXCHK(D3DCompile(g_PS, static_cast<UINT>(strlen(g_PS)), nullptr, nullptr, nullptr, "main", "ps_4_0", 0, 0, &ps_blob, nullptr));
|
||
DXCHK(device->CreateVertexShader(vs_blob->GetBufferPointer(), vs_blob->GetBufferSize(), nullptr, &vs));
|
||
DXCHK(device->CreatePixelShader(ps_blob->GetBufferPointer(), ps_blob->GetBufferSize(), nullptr, &ps));
|
||
|
||
// 输入布局
|
||
D3D11_INPUT_ELEMENT_DESC layout[] = {{"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0}};
|
||
DXCHK(device->CreateInputLayout(layout, 1, vs_blob->GetBufferPointer(), vs_blob->GetBufferSize(), &input_layout));
|
||
|
||
// Create vertex buffer from object loader data
|
||
std::vector<Vertex> vertices;
|
||
size_t total_vertices = 0;
|
||
// Count total vertices from all objects
|
||
for (size_t i = 0; i < this->config.object_loader->get_object_count(); ++i) {
|
||
const auto& obj = this->config.object_loader->get_object(i);
|
||
total_vertices += obj.get_vertices_count();
|
||
}
|
||
// Collect all vertices from all objects
|
||
for (size_t i = 0; i < this->config.object_loader->get_object_count(); ++i) {
|
||
const auto& obj = this->config.object_loader->get_object(i);
|
||
const auto* obj_vertices = obj.get_vertices();
|
||
size_t obj_vertex_count = obj.get_vertices_count();
|
||
|
||
for (size_t j = 0; j < obj_vertex_count; ++j) {
|
||
Vertex v;
|
||
v.x = obj_vertices[j].x;
|
||
v.y = obj_vertices[j].y;
|
||
v.z = obj_vertices[j].z;
|
||
vertices.push_back(v);
|
||
}
|
||
}
|
||
// Create vertex buffer with dynamic size
|
||
D3D11_BUFFER_DESC vb_desc = {};
|
||
vb_desc.ByteWidth = static_cast<UINT>(vertices.size() * sizeof(Vertex));
|
||
vb_desc.Usage = D3D11_USAGE_DEFAULT;
|
||
vb_desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
|
||
D3D11_SUBRESOURCE_DATA vb_data = {vertices.data()};
|
||
DXCHK(device->CreateBuffer(&vb_desc, &vb_data, &vertex_buffer));
|
||
// Update draw call to use actual vertex count
|
||
vertex_count = vertices.size();
|
||
|
||
// 深度测试启用
|
||
D3D11_DEPTH_STENCIL_DESC ds_desc = {};
|
||
ds_desc.DepthEnable = TRUE;
|
||
ds_desc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
|
||
ds_desc.DepthFunc = D3D11_COMPARISON_LESS;
|
||
DXCHK(device->CreateDepthStencilState(&ds_desc, &depth_state));
|
||
|
||
// 设置光栅化模式
|
||
D3D11_RASTERIZER_DESC rasterizer_desc;
|
||
ZeroMemory(&rasterizer_desc, sizeof(D3D11_RASTERIZER_DESC));
|
||
rasterizer_desc.FillMode = D3D11_FILL_SOLID;
|
||
rasterizer_desc.CullMode = D3D11_CULL_NONE;
|
||
DXCHK(device->CreateRasterizerState(&rasterizer_desc, &rasterizer_state));
|
||
|
||
// 视口
|
||
D3D11_VIEWPORT vp;
|
||
ZeroMemory(&vp, sizeof(D3D11_VIEWPORT));
|
||
vp.TopLeftX = 0;
|
||
vp.TopLeftY = 0;
|
||
vp.Width = (float) this->config.width;
|
||
vp.Height = (float) this->config.height;
|
||
vp.MinDepth = 0.0f;
|
||
vp.MaxDepth = 1.0f;
|
||
context->RSSetViewports(1, &vp); // RSSetViewports doesn't return HRESULT, so no need to wrap
|
||
|
||
//// 打开命名管道(需另一进程已创建)
|
||
//constexpr const wchar_t* PipeName = L"\\\\.\\pipe\\54da494c-301a-47a9-9d67-4fbebaeda8cf";
|
||
//hPipe = CreateFileW(PipeName, GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, nullptr);
|
||
|
||
//if (hPipe == INVALID_HANDLE_VALUE) {
|
||
// std::cerr << "Failed to open named pipe. Ensure another process created it.\n";
|
||
// return;
|
||
//}
|
||
|
||
// 设置管线
|
||
context->OMSetRenderTargets(1, rtv.GetAddressOf(), dsv.Get());
|
||
context->OMSetDepthStencilState(depth_state.Get(), 1);
|
||
context->RSSetState(rasterizer_state.Get());
|
||
context->VSSetShader(vs.Get(), nullptr, 0);
|
||
context->PSSetShader(ps.Get(), nullptr, 0);
|
||
context->IASetInputLayout(input_layout.Get());
|
||
UINT stride = sizeof(Vertex), offset = 0;
|
||
context->IASetVertexBuffers(0, 1, vertex_buffer.GetAddressOf(), &stride, &offset);
|
||
context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
||
|
||
// 缩放深度数据数组到指定大小
|
||
depth_data.resize(this->config.width * this->config.height * sizeof(float));
|
||
|
||
//frameCount = 0;
|
||
}
|
||
|
||
virtual bool tick() override {
|
||
if (IEngine::tick()) return true;
|
||
|
||
// Event loop
|
||
if (EventLoop()) return true;
|
||
|
||
// 获取动画关键帧数据
|
||
basalt::shared::anime_loader::KeyFrameSpan keyframe_span;
|
||
if (this->config.anime_loader) {
|
||
keyframe_span = this->config.anime_loader->tick();
|
||
|
||
// Blender坐标系到DirectX坐标系的转换
|
||
// Blender (右手坐标系): 右+x, 上+y, 前-z
|
||
// DirectX (左手坐标系): 右+x, 上+y, 前+z
|
||
|
||
// 位置变换: X不变, Y不变, Z取反
|
||
float pos_x = keyframe_span.prev_position.x * keyframe_span.prev_time + keyframe_span.next_position.x * keyframe_span.next_time;
|
||
float pos_y = keyframe_span.prev_position.y * keyframe_span.prev_time + keyframe_span.next_position.y * keyframe_span.next_time;
|
||
float pos_z = -(keyframe_span.prev_position.z * keyframe_span.prev_time
|
||
+ keyframe_span.next_position.z * keyframe_span.next_time);
|
||
|
||
// 旋转四元数变换: X和Z取反, Y和W保持不变
|
||
float rot_x = -(keyframe_span.prev_rotation.x * keyframe_span.prev_time
|
||
+ keyframe_span.next_rotation.x * keyframe_span.next_time);
|
||
float rot_y = keyframe_span.prev_rotation.y * keyframe_span.prev_time + keyframe_span.next_rotation.y * keyframe_span.next_time;
|
||
float rot_z = -(keyframe_span.prev_rotation.z * keyframe_span.prev_time
|
||
+ keyframe_span.next_rotation.z * keyframe_span.next_time);
|
||
float rot_w = keyframe_span.prev_rotation.w * keyframe_span.prev_time + keyframe_span.next_rotation.w * keyframe_span.next_time;
|
||
|
||
// 归一化四元数
|
||
float length = std::sqrt(rot_x * rot_x + rot_y * rot_y + rot_z * rot_z + rot_w * rot_w);
|
||
if (length > 0.0f) {
|
||
rot_x /= length;
|
||
rot_y /= length;
|
||
rot_z /= length;
|
||
rot_w /= length;
|
||
}
|
||
|
||
// 这里可以应用相机变换,但目前我们只是获取了动画数据
|
||
// 在实际渲染中,这些值将用于构建视图矩阵
|
||
}
|
||
|
||
// 清屏
|
||
float clear_color[] = {0.8f, 0.1f, 0.1f, 1.0f};
|
||
context->ClearRenderTargetView(rtv.Get(), clear_color);
|
||
context->ClearDepthStencilView(dsv.Get(), D3D11_CLEAR_DEPTH, 1.0f, 0);
|
||
|
||
// Draw vertices from object loader or fallback cube
|
||
context->Draw(vertex_count, 0);
|
||
|
||
//// 复制深度缓冲到 staging texture
|
||
//context->CopyResource(depth_staging.Get(), depth_buffer.Get());
|
||
|
||
//// Map 获取数据
|
||
//D3D11_MAPPED_SUBRESOURCE mapped;
|
||
//DXCHK(context->Map(depth_staging.Get(), 0, D3D11_MAP_READ, 0, &mapped));
|
||
//memcpy(depth_data.data(), mapped.pData, this->config.width * this->config.height * sizeof(float));
|
||
//context->Unmap(depth_staging.Get(), 0);
|
||
|
||
//// 写入命名管道
|
||
//DWORD written;
|
||
//if (!WriteFile(hPipe, depth_data.data(), static_cast<DWORD>(this->config.width * this->config.height * sizeof(float)), &written, nullptr) || written != this->config.width * this->config.height * sizeof(float)) {
|
||
// std::cerr << "WriteFile failed or incomplete.\n";
|
||
//}
|
||
|
||
// 呈现(可选)
|
||
DXCHK(swap_chain->Present(0, 0));
|
||
|
||
//frameCount++;
|
||
|
||
return false;
|
||
}
|
||
|
||
virtual void shutdown() override {
|
||
IEngine::shutdown();
|
||
|
||
//if (hPipe != INVALID_HANDLE_VALUE) {
|
||
// CloseHandle(hPipe);
|
||
//}
|
||
CoUninitialize();
|
||
if (!this->config.headless) {
|
||
CloseWindow(window);
|
||
DestroyRenderWindow(window);
|
||
}
|
||
}
|
||
};
|
||
|
||
BS_EXPORT void* BSCreateInstance() {
|
||
return static_cast<IEngine*>(new DirectX11Engine());
|
||
}
|
||
|
||
BS_EXPORT void BSDestroyInstance(void* instance) {
|
||
delete dynamic_cast<DirectX11Engine*>(static_cast<IEngine*>(instance));
|
||
}
|