Init commit
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_AUTORCC ON)
|
||||
set(CMAKE_AUTOUIC ON)
|
||||
|
||||
add_executable(${TG_APP}
|
||||
src/main.cpp
|
||||
src/wnd_main.cpp
|
||||
src/wnd_main.h
|
||||
src/resource.cpp
|
||||
src/resource.h
|
||||
src/wnd_resource.cpp
|
||||
src/wnd_resource.h
|
||||
src/wnd_inspector.cpp
|
||||
src/wnd_inspector.h
|
||||
src/wnd_hierachy.cpp
|
||||
src/wnd_hierachy.h
|
||||
src/wnd_debug.cpp
|
||||
src/wnd_debug.h
|
||||
src/states.h
|
||||
src/states.cpp
|
||||
src/wgt_listitem.cpp
|
||||
src/wgt_listitem.h
|
||||
src/wnd_create_mat.cpp
|
||||
src/wnd_create_mat.h
|
||||
src/wgt_prop_input.cpp
|
||||
src/wgt_prop_input.h
|
||||
src/wnd_add_tex.cpp
|
||||
src/wnd_add_tex.h
|
||||
)
|
||||
|
||||
set(INC_QTLIB
|
||||
Qt::Core
|
||||
Qt::Gui
|
||||
Qt::Widgets
|
||||
)
|
||||
target_link_libraries(${TG_APP}
|
||||
PUBLIC
|
||||
${INC_QTLIB}
|
||||
${TG_RENDER}
|
||||
${TG_CORE}
|
||||
${TG_ZASSIMP}
|
||||
${TG_TOOL}
|
||||
ole32
|
||||
windowscodecs
|
||||
)
|
||||
|
||||
set_target_properties(${TG_APP}
|
||||
PROPERTIES
|
||||
WIN32_EXECUTABLE FALSE
|
||||
)
|
||||
|
||||
# 拷贝 assets 目录
|
||||
add_custom_command(TARGET ${TG_APP} POST_BUILD
|
||||
COMMAND "${CMAKE_COMMAND}"
|
||||
"-DSRC_DIR=${CMAKE_SOURCE_DIR}/assets"
|
||||
"-DDST_DIR=$<TARGET_FILE_DIR:${TG_APP}>/assets"
|
||||
-P "${CMAKE_SOURCE_DIR}/cmake/copy_assets_to_output.cmake"
|
||||
|
||||
COMMENT "Copying assets..."
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
# 拷贝 assimp相关依赖
|
||||
add_custom_command(TARGET ${TG_APP} POST_BUILD
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy_if_different
|
||||
"$<TARGET_FILE:${TG_IMPORTED}>"
|
||||
"$<TARGET_FILE_DIR:${TG_APP}>"
|
||||
)
|
||||
|
||||
# 拷贝 QT相关依赖
|
||||
#if (WIN32 AND NOT DEFINED CMAKE_TOOLCHAIN_FILE)
|
||||
if (WIN32)
|
||||
set(DEBUG_SUFFIX)
|
||||
if (MSVC AND CMAKE_BUILD_TYPE MATCHES "Debug")
|
||||
set(DEBUG_SUFFIX "d")
|
||||
endif ()
|
||||
set(QT_INSTALL_PATH "${PATH_QT}")
|
||||
if (NOT EXISTS "${QT_INSTALL_PATH}/bin")
|
||||
set(QT_INSTALL_PATH "${QT_INSTALL_PATH}/..")
|
||||
if (NOT EXISTS "${QT_INSTALL_PATH}/bin")
|
||||
set(QT_INSTALL_PATH "${QT_INSTALL_PATH}/..")
|
||||
endif ()
|
||||
endif ()
|
||||
if (EXISTS "${QT_INSTALL_PATH}/plugins/platforms/qwindows${DEBUG_SUFFIX}.dll")
|
||||
add_custom_command(TARGET ${TG_APP} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory
|
||||
"$<TARGET_FILE_DIR:${TG_APP}>/plugins/platforms/")
|
||||
add_custom_command(TARGET ${TG_APP} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy
|
||||
"${QT_INSTALL_PATH}/plugins/platforms/qwindows${DEBUG_SUFFIX}.dll"
|
||||
"$<TARGET_FILE_DIR:${TG_APP}>/plugins/platforms/")
|
||||
endif ()
|
||||
foreach (QTLIB ${INC_QTLIB})
|
||||
string(REPLACE "Qt::" "" VAR ${QTLIB})
|
||||
list(APPEND _INC_QTLIB ${VAR})
|
||||
endforeach (QTLIB)
|
||||
foreach (QT_LIB ${_INC_QTLIB})
|
||||
add_custom_command(TARGET ${TG_APP} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy
|
||||
"${QT_INSTALL_PATH}/bin/Qt6${QT_LIB}${DEBUG_SUFFIX}.dll"
|
||||
"$<TARGET_FILE_DIR:${TG_APP}>")
|
||||
endforeach (QT_LIB)
|
||||
endif ()
|
||||
@@ -0,0 +1,62 @@
|
||||
#include <QApplication>
|
||||
#include <Windows.h>
|
||||
#include "wnd_main.h"
|
||||
#include "states.h"
|
||||
#include <shlobj.h>
|
||||
namespace {
|
||||
void EnableAnsiColor() {
|
||||
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
|
||||
if (hOut == INVALID_HANDLE_VALUE)
|
||||
return;
|
||||
|
||||
DWORD dwMode = 0;
|
||||
if (!GetConsoleMode(hOut, &dwMode))
|
||||
return;
|
||||
|
||||
dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
|
||||
SetConsoleMode(hOut, dwMode);
|
||||
}
|
||||
}
|
||||
|
||||
class MainCppHelper {
|
||||
public:
|
||||
static void InitStateFromMain() {
|
||||
States::desktopWidth = GetSystemMetrics(SM_CXSCREEN);
|
||||
States::desktopHeight = GetSystemMetrics(SM_CYSCREEN);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
SetConsoleOutputCP(CP_UTF8);
|
||||
EnableAnsiColor();
|
||||
MainCppHelper::InitStateFromMain();
|
||||
HRESULT hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
|
||||
if (FAILED(hr)) {
|
||||
MessageBoxW(nullptr, L"CoInitalizeEx Failed", L"main.cpp :: main()",MB_OK);
|
||||
return 886;
|
||||
}
|
||||
|
||||
QApplication app{argc, argv};
|
||||
WndMain *wm = new WndMain{};
|
||||
int rqt = QApplication::exec();
|
||||
|
||||
CoUninitialize();
|
||||
return rqt;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
@@ -0,0 +1,538 @@
|
||||
#include "resource.h"
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <DirectXMath.h>
|
||||
#include <dxgi.h>
|
||||
#include <d3d11.h>
|
||||
#include <wrl/client.h>
|
||||
#include <wincodec.h>
|
||||
#include <zassimp/importer.h>
|
||||
#include <iostream>
|
||||
#include <render/task.h>
|
||||
#include <queue>
|
||||
#include <mutex>
|
||||
#include <math.hpp>
|
||||
|
||||
namespace CB {
|
||||
// callback wait
|
||||
std::mutex mtx_cb{};
|
||||
std::condition_variable cv_cb{};
|
||||
Z::ResultMessage g_CallbackMessage{};
|
||||
|
||||
void CallBack_WakeThreadUp(Z::ResultMessage msg) { // 被Renderer回调
|
||||
g_CallbackMessage = msg;
|
||||
cv_cb.notify_all();
|
||||
}
|
||||
void WaitForResult() {
|
||||
std::unique_lock lk(mtx_cb);
|
||||
cv_cb.wait(lk);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace LoadedResource {
|
||||
std::list<Z::Model *> g_LoadedModels{};
|
||||
Z::Model *GetLoadedModel(std::filesystem::path &path) {
|
||||
auto it = std::ranges::find_if(g_LoadedModels,
|
||||
[&path](auto &i) { return i->path == path; });
|
||||
if (it == g_LoadedModels.end()) return nullptr;
|
||||
return *it;
|
||||
}
|
||||
void RemoveLoadedModel(std::filesystem::path &path) {
|
||||
size_t ret = g_LoadedModels.remove_if([&path](auto &i) {
|
||||
if (i->path == path) {
|
||||
Importer imp{};
|
||||
imp.ReleaseLoadedModel(i);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
std::cout << "RemoveLoadedModel count = " << ret << std::endl;
|
||||
}
|
||||
|
||||
std::list<Z::TextureData *> g_LoadedTexture{};
|
||||
Z::TextureData *GetLoadedTexture(std::filesystem::path &path) {
|
||||
auto it = std::ranges::find_if(g_LoadedTexture,
|
||||
[&path](auto &item) { return item->path == path; });
|
||||
if (it == g_LoadedTexture.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
return *it;
|
||||
}
|
||||
void RemoveLoadedTexture(std::filesystem::path &path) {
|
||||
size_t ret = g_LoadedTexture.remove_if([&path](auto &i) {
|
||||
if (i->path == path) {
|
||||
delete i;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
std::cout << "RemoveLoadedTexture count = " << ret << std::endl;
|
||||
}
|
||||
|
||||
std::list<SHADER::ShaderInfo *> g_LoadedShaderDesc{};
|
||||
SHADER::ShaderInfo *GetLoadedShader(std::string &name) {
|
||||
auto it = std::ranges::find_if(g_LoadedShaderDesc, [&name](auto &item) {
|
||||
return item->name == name;
|
||||
});
|
||||
if (it != g_LoadedShaderDesc.end()) return *it;
|
||||
return nullptr;
|
||||
}
|
||||
void RemoveLoadedShader(std::string &name) {
|
||||
size_t ret = g_LoadedTexture.remove_if([&name](auto &i) {
|
||||
if (i->name == name) {
|
||||
delete i;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
std::cout << "RemoveLoadedShader count = " << ret << std::endl;
|
||||
}
|
||||
|
||||
|
||||
std::list<MAT::MatInstance *> g_LoadedMatInstance{};
|
||||
MAT::MatInstance *GetLoadedMaterial(std::string &name) {
|
||||
auto it = std::ranges::find_if(g_LoadedMatInstance, [&name](auto &i) {
|
||||
return i->name == name;
|
||||
});
|
||||
if (it != g_LoadedMatInstance.end()) return *it;
|
||||
return nullptr;
|
||||
}
|
||||
void RemoveLoadedMaterial(std::string &name) {
|
||||
size_t ret = g_LoadedMatInstance.remove_if([&name](auto &i) {
|
||||
if (i->name == name) {
|
||||
delete i;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
std::cout << "RemoveLoadedMaterial count = " << ret << std::endl;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
//region MAT
|
||||
MAT::MatInstance::MatInstance(std::string name, const SHADER::ShaderInfo *shader) :
|
||||
name(std::move(name)), info(*shader) {}
|
||||
bool MAT::MatInstance::Update(int index) {
|
||||
std::cout << "app Resource:: MatName = " << this->name << " PropIndex = " << index << std::endl;
|
||||
Z::Task task{};
|
||||
task.type = Z::CmdType::SetMaterialProp;
|
||||
task.cmd.set_material_prop.matName = name.c_str();
|
||||
task.cmd.set_material_prop.propIndex = index;
|
||||
SHADER::PropertyInfo &propinfo = info.cbuffer[info.idx_cbPerMat].props[index]; //
|
||||
task.cmd.set_material_prop.value = propinfo.value;
|
||||
task.fpCallBack = CB::CallBack_WakeThreadUp;
|
||||
Z::Task::AddCommand(task);
|
||||
CB::WaitForResult();
|
||||
return CB::g_CallbackMessage.res == Z::ResultType::OK;
|
||||
};
|
||||
|
||||
//endregion
|
||||
|
||||
|
||||
//region RENDEROBJ
|
||||
bool RENDEROBJ::Object::Update(float *pos, float *rot, float *scale) {
|
||||
auto quatrot = math::quaternion_yaw_pitch_roll(math::radians(rot[1]),
|
||||
math::radians(rot[0]), math::radians(rot[2]));
|
||||
auto matrix = math::compose_RowVec({pos[0], pos[1], pos[2]},
|
||||
quatrot,
|
||||
{scale[0], scale[1], scale[2]});
|
||||
// 向renderer提交修改
|
||||
Z::Task task{};
|
||||
task.type = Z::CmdType::SetRenderObjMatrix;
|
||||
task.fpCallBack = CB::CallBack_WakeThreadUp;
|
||||
task.cmd.set_render_obj_matrix.id = this->id;
|
||||
std::memcpy(&task.cmd.set_render_obj_matrix.matrix, &matrix, 64);
|
||||
Z::Task::AddCommand(task);
|
||||
CB::WaitForResult();
|
||||
if (CB::g_CallbackMessage.res != Z::ResultType::OK) {
|
||||
return false;
|
||||
}
|
||||
std::memcpy(position, pos, 12);
|
||||
std::memcpy(rotation, rot, 12);
|
||||
std::memcpy(this->scale, scale, 12);
|
||||
return true;
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
|
||||
//region MODEL
|
||||
bool ModelImporter::LoadFromFile(const std::filesystem::path &path, Z::Model **out) {
|
||||
|
||||
Importer imp{};
|
||||
Z::Model *model{};
|
||||
if (imp.ImportFromFile(path, &model)) {
|
||||
auto name = model->name;
|
||||
auto it = std::ranges::find_if(
|
||||
LoadedResource::g_LoadedModels, [&name](auto &item) {
|
||||
return name == item->name;
|
||||
});
|
||||
if (it != LoadedResource::g_LoadedModels.end()) {
|
||||
MessageBoxW(nullptr, L"模型name重复,导入失败",
|
||||
L"zassimp::LoadFromFile",MB_OK);
|
||||
imp.ReleaseLoadedModel(model);
|
||||
return false;
|
||||
}
|
||||
|
||||
LoadedResource::g_LoadedModels.push_back(model);
|
||||
*out = model;
|
||||
return true;
|
||||
} else {
|
||||
MessageBoxW(nullptr, L"载入模型失败", L"app resource.cpp LoadFromFile()",MB_OK);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
|
||||
//region TEXTURE
|
||||
|
||||
namespace {
|
||||
[[maybe_unused]] HRESULT LoadTextureFromWIC(
|
||||
ID3D11Device *device,
|
||||
const wchar_t *filename,
|
||||
ID3D11ShaderResourceView **outSRV,
|
||||
ID3D11Texture2D **outTexture = nullptr,
|
||||
bool srgb = false
|
||||
) {
|
||||
using namespace Microsoft::WRL;
|
||||
|
||||
if (!device || !filename || !outSRV) return E_INVALIDARG;
|
||||
|
||||
*outSRV = nullptr;
|
||||
if (outTexture) *outTexture = nullptr;
|
||||
|
||||
ComPtr<IWICImagingFactory> factory;
|
||||
HRESULT hr = CoCreateInstance(
|
||||
CLSID_WICImagingFactory,
|
||||
nullptr,
|
||||
CLSCTX_INPROC_SERVER,
|
||||
IID_PPV_ARGS(&factory)
|
||||
);
|
||||
if (FAILED(hr)) return hr;
|
||||
|
||||
ComPtr<IWICBitmapDecoder> decoder;
|
||||
hr = factory->CreateDecoderFromFilename(
|
||||
filename,
|
||||
nullptr,
|
||||
GENERIC_READ,
|
||||
WICDecodeMetadataCacheOnLoad,
|
||||
&decoder
|
||||
);
|
||||
if (FAILED(hr)) return hr;
|
||||
|
||||
ComPtr<IWICBitmapFrameDecode> frame;
|
||||
hr = decoder->GetFrame(0, &frame);
|
||||
if (FAILED(hr)) return hr;
|
||||
|
||||
UINT width = 0;
|
||||
UINT height = 0;
|
||||
hr = frame->GetSize(&width, &height);
|
||||
if (FAILED(hr)) return hr;
|
||||
|
||||
ComPtr<IWICFormatConverter> converter;
|
||||
hr = factory->CreateFormatConverter(&converter);
|
||||
if (FAILED(hr)) return hr;
|
||||
|
||||
hr = converter->Initialize(
|
||||
frame.Get(),
|
||||
GUID_WICPixelFormat32bppRGBA,
|
||||
WICBitmapDitherTypeNone,
|
||||
nullptr,
|
||||
0.0,
|
||||
WICBitmapPaletteTypeCustom
|
||||
);
|
||||
if (FAILED(hr)) return hr;
|
||||
|
||||
const UINT bytesPerPixel = 4;
|
||||
const UINT rowPitch = width * bytesPerPixel;
|
||||
const UINT imageSize = rowPitch * height;
|
||||
|
||||
std::vector<unsigned char> pixels(imageSize);
|
||||
|
||||
hr = converter->CopyPixels(
|
||||
nullptr,
|
||||
rowPitch,
|
||||
imageSize,
|
||||
pixels.data()
|
||||
);
|
||||
if (FAILED(hr)) return hr;
|
||||
|
||||
// 写入 DirectX 资源
|
||||
|
||||
DXGI_FORMAT format = srgb ? DXGI_FORMAT_R8G8B8A8_UNORM_SRGB : DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
|
||||
D3D11_TEXTURE2D_DESC texDesc = {};
|
||||
texDesc.Width = width;
|
||||
texDesc.Height = height;
|
||||
texDesc.MipLevels = 1;
|
||||
texDesc.ArraySize = 1;
|
||||
texDesc.Format = format;
|
||||
texDesc.SampleDesc.Count = 1;
|
||||
texDesc.SampleDesc.Quality = 0;
|
||||
texDesc.Usage = D3D11_USAGE_DEFAULT;
|
||||
texDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
|
||||
texDesc.CPUAccessFlags = 0;
|
||||
texDesc.MiscFlags = 0;
|
||||
|
||||
D3D11_SUBRESOURCE_DATA initData = {};
|
||||
initData.pSysMem = pixels.data();
|
||||
initData.SysMemPitch = rowPitch;
|
||||
initData.SysMemSlicePitch = imageSize;
|
||||
|
||||
ComPtr<ID3D11Texture2D> texture;
|
||||
hr = device->CreateTexture2D(&texDesc, &initData, &texture);
|
||||
if (FAILED(hr)) return hr;
|
||||
|
||||
D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
|
||||
srvDesc.Format = format;
|
||||
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
|
||||
srvDesc.Texture2D.MostDetailedMip = 0;
|
||||
srvDesc.Texture2D.MipLevels = 1;
|
||||
|
||||
hr = device->CreateShaderResourceView(
|
||||
texture.Get(),
|
||||
&srvDesc,
|
||||
outSRV
|
||||
);
|
||||
if (FAILED(hr)) return hr;
|
||||
|
||||
if (outTexture) {
|
||||
*outTexture = texture.Detach();
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
//分SRGB(待处理,在render模块)和 REVY(OK)重新导入
|
||||
Z::TextureData *ImgImporter::ImportFromFile(const std::filesystem::path &path, bool revG,
|
||||
Z::TextureType type) {
|
||||
using namespace Microsoft::WRL;
|
||||
|
||||
if (!std::filesystem::exists(path)) return nullptr;
|
||||
|
||||
ComPtr<IWICImagingFactory> factory;
|
||||
HRESULT hr = CoCreateInstance(CLSID_WICImagingFactory, nullptr,
|
||||
CLSCTX_INPROC_SERVER,
|
||||
IID_PPV_ARGS(&factory));
|
||||
if (FAILED(hr)) { return nullptr; }
|
||||
|
||||
ComPtr<IWICBitmapDecoder> decoder;
|
||||
hr = factory->CreateDecoderFromFilename(path.c_str(), nullptr,
|
||||
GENERIC_READ,
|
||||
WICDecodeMetadataCacheOnLoad,
|
||||
&decoder);
|
||||
if (FAILED(hr)) { return nullptr; }
|
||||
|
||||
ComPtr<IWICBitmapFrameDecode> frame;
|
||||
hr = decoder->GetFrame(0, &frame);
|
||||
if (FAILED(hr)) { return nullptr; }
|
||||
|
||||
UINT width;
|
||||
UINT height;
|
||||
hr = frame->GetSize(&width, &height);
|
||||
if (FAILED(hr)) { return nullptr; }
|
||||
|
||||
ComPtr<IWICFormatConverter> converter;
|
||||
hr = factory->CreateFormatConverter(&converter);
|
||||
if (FAILED(hr)) { return nullptr; }
|
||||
|
||||
std::unique_ptr<Z::TextureData> _tex = std::make_unique<Z::TextureData>();
|
||||
auto u8str = path.stem().u8string();
|
||||
_tex->name.assign(reinterpret_cast<const char *>(u8str.data()), u8str.size());
|
||||
_tex->path = path;
|
||||
_tex->type = type;
|
||||
|
||||
//1 初始化,统一成RGBA8
|
||||
_tex->bytesPerPixel = 4;
|
||||
hr = converter->Initialize(frame.Get(), GUID_WICPixelFormat32bppRGBA,
|
||||
WICBitmapDitherTypeNone, nullptr, 0.0,
|
||||
WICBitmapPaletteTypeCustom);
|
||||
if (FAILED(hr)) { return nullptr; }
|
||||
|
||||
//2 计算CPU内存布局
|
||||
_tex->rowPitch = width * _tex->bytesPerPixel;
|
||||
_tex->size = _tex->rowPitch * height;
|
||||
_tex->data.resize(_tex->size);
|
||||
|
||||
//3 解码,写入像素
|
||||
hr = converter->CopyPixels(nullptr, _tex->rowPitch, _tex->size,
|
||||
reinterpret_cast<BYTE *>(_tex->data.data()));
|
||||
if (FAILED(hr))return nullptr;
|
||||
|
||||
// 当前引擎需要 DirectX 风格的 -G,
|
||||
// 如果源法线贴图使用 OpenGL 风格的 +G,就反转绿色通道。
|
||||
if (type == Z::TextureType::NORMALS && revG) {
|
||||
auto *pixels = reinterpret_cast<std::uint8_t *>(_tex->data.data());
|
||||
for (std::size_t i = 0; i < _tex->data.size(); i += 4) {
|
||||
pixels[i + 1] = static_cast<std::uint8_t>(255u - pixels[i + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Z::TextureData *result = _tex.release();
|
||||
LoadedResource::g_LoadedTexture.push_back(result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
|
||||
//region Shader
|
||||
namespace {
|
||||
// callback wait
|
||||
std::mutex mtx_cb{};
|
||||
std::condition_variable cv_cb{};
|
||||
Z::ResultMessage g_CallbackMessage{};
|
||||
|
||||
std::queue<SHADER::ShaderInfo> g_WriteBackShaderInfo;
|
||||
|
||||
SHADER::CbufferInfo BuildCbufferDesc(const Z::ShaderInfo::CbufferInfo &c, int index) {
|
||||
SHADER::CbufferInfo cbuf{index, c.name, {}};
|
||||
cbuf.props.reserve(c.c_prop);
|
||||
for (int i = 0; i < c.c_prop; ++i) {
|
||||
SHADER::PropertyInfo prop{};
|
||||
prop.index = c.ar_prop[i].index;
|
||||
prop.type = c.ar_prop[i].type;
|
||||
prop.name = c.ar_prop[i].name;
|
||||
std::memcpy(&prop.value, &c.ar_prop[i].value, sizeof(Z::ShaderInfo::Value));
|
||||
cbuf.props.push_back(std::move(prop));
|
||||
}
|
||||
return cbuf;
|
||||
}
|
||||
SHADER::TextureInfo BuildTexDesc(const Z::ShaderInfo::TexInfo &t, int index) {
|
||||
return SHADER::TextureInfo{
|
||||
t.index,
|
||||
std::string(t.name)
|
||||
};
|
||||
}
|
||||
|
||||
void BuildShaderDesc() { // 在renderer线程被调用,在此构造APP端使用的shader描述
|
||||
Z::ShaderInfo::ShaderInfo *s;
|
||||
std::memcpy(&s, g_CallbackMessage.data, sizeof(g_CallbackMessage.data));
|
||||
if (s == nullptr) return;
|
||||
|
||||
SHADER::ShaderInfo shader{};
|
||||
shader.name = s->name;
|
||||
|
||||
|
||||
|
||||
shader.cbuffer.reserve(s->c_cbuffer);
|
||||
for (int i = 0; i < s->c_cbuffer; ++i) {
|
||||
shader.cbuffer.push_back(BuildCbufferDesc(s->ar_cbuffer[i], i));
|
||||
}
|
||||
|
||||
shader.idx_cbPerMat = -1;
|
||||
for (auto &item: shader.cbuffer) {
|
||||
if (item.name == "CBPerMat") {
|
||||
shader.idx_cbPerMat = item.index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
shader.textures.reserve(s->c_tex);
|
||||
for (int i = 0; i < s->c_tex; ++i) {
|
||||
shader.textures.push_back(BuildTexDesc(s->ar_tex[i], i));
|
||||
}
|
||||
|
||||
g_WriteBackShaderInfo.push(std::move(shader));
|
||||
}
|
||||
|
||||
void CallBack_CompileShaderWakeThreadUp(Z::ResultMessage msg) { // 被Renderer回调
|
||||
g_CallbackMessage = msg;
|
||||
|
||||
BuildShaderDesc();
|
||||
|
||||
cv_cb.notify_all();
|
||||
}
|
||||
void WaitForResult() {
|
||||
std::unique_lock lk(mtx_cb);
|
||||
cv_cb.wait(lk);
|
||||
}
|
||||
}
|
||||
|
||||
SHADER::ShaderInfo *ShaderImporter::ImportFromFile(const std::filesystem::path &path) {
|
||||
if (!std::filesystem::exists(path)) {
|
||||
MessageBoxW(nullptr, L"文件路径不存在", L"resource ShaderImporter",MB_OK);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//让renderer去编译,解释返回的DESC构成
|
||||
Z::Task::Command cmd{};
|
||||
std::u8string u8path = path.u8string();
|
||||
std::string pathstr = {reinterpret_cast<const char *>(u8path.c_str()), u8path.size()};
|
||||
cmd.compile_shader.path = pathstr.c_str();
|
||||
|
||||
Z::Task task{};
|
||||
task.cmd = cmd;
|
||||
task.type = Z::CmdType::CompileShader;
|
||||
task.fpCallBack = CallBack_CompileShaderWakeThreadUp;
|
||||
|
||||
Z::Task::AddCommand(task);
|
||||
WaitForResult();
|
||||
if (g_CallbackMessage.res == Z::ResultType::FAILED) {
|
||||
MessageBoxW(nullptr, L"编译shader返回错误", L"resource ShaderImporter",MB_OK);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// RENDER线程已返回,数据在g_WriteBackShaderInfo的第一个元素内
|
||||
auto *sdesc = new SHADER::ShaderInfo(std::move(g_WriteBackShaderInfo.front()));
|
||||
g_WriteBackShaderInfo.pop();
|
||||
|
||||
sdesc->path = path;
|
||||
|
||||
// std::cout << "APP线程::返回成功\n" <<
|
||||
// "shader name = " << sdesc->name <<
|
||||
// "\n c_cbuffer = " << sdesc->cbuffer.size() <<
|
||||
// "\n cbuf[0].name = " << sdesc->cbuffer[0].name <<
|
||||
// "\n c_tex = " << sdesc->textures.size() <<
|
||||
// "\n tex[0].name = " << sdesc->textures[0].name << std::endl;
|
||||
|
||||
LoadedResource::g_LoadedShaderDesc.push_back(sdesc);
|
||||
return sdesc;
|
||||
}
|
||||
|
||||
|
||||
//endregion
|
||||
|
||||
|
||||
//region MatCreater
|
||||
MAT::MatInstance *MatCreater::CreateMat(const std::string &name, const SHADER::ShaderInfo *shaderInfo) {
|
||||
MAT::MatInstance *m = new MAT::MatInstance(name, shaderInfo);
|
||||
LoadedResource::g_LoadedMatInstance.push_back(m);
|
||||
return m;
|
||||
}
|
||||
//endregion
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
@@ -0,0 +1,107 @@
|
||||
#ifndef RENDER_IMG_IMPORTER_H
|
||||
#define RENDER_IMG_IMPORTER_H
|
||||
#include <render_type_def.h>
|
||||
|
||||
|
||||
namespace SHADER {
|
||||
struct PropertyInfo {
|
||||
int index;
|
||||
Z::ShaderInfo::PropType type{};
|
||||
std::string name;
|
||||
Z::ShaderInfo::Value value{};
|
||||
};
|
||||
struct CbufferInfo {
|
||||
int index;
|
||||
std::string name;
|
||||
std::vector<PropertyInfo> props;
|
||||
};
|
||||
struct TextureInfo {
|
||||
int index; // 该texinfo在ShaderInfo里array的index
|
||||
std::string name; //renderer里通过name索引picture
|
||||
std::string cur_tex; // UTF8,默认为空,当前选择的texture的path字符串
|
||||
};
|
||||
struct ShaderInfo {
|
||||
std::string name;
|
||||
std::filesystem::path path;
|
||||
std::vector<CbufferInfo> cbuffer;
|
||||
int idx_cbPerMat{-1};
|
||||
std::vector<TextureInfo> textures;
|
||||
};
|
||||
}
|
||||
|
||||
namespace MAT {
|
||||
struct MatInstance {
|
||||
std::string name;
|
||||
SHADER::ShaderInfo info;
|
||||
|
||||
explicit MatInstance(std::string name, const SHADER::ShaderInfo *shader);
|
||||
bool Update(int index);
|
||||
};
|
||||
}
|
||||
|
||||
namespace RENDEROBJ {
|
||||
class Object {
|
||||
public:
|
||||
bool Update(float *pos, float *rot, float *scale);
|
||||
|
||||
private:
|
||||
|
||||
public:
|
||||
std::string name; // 显示在面板上的
|
||||
int id; // 用来找renderer的索引
|
||||
float position[3]{};
|
||||
float rotation[3]{};
|
||||
float scale[3]{1.0f, 1.0f, 1.0f};
|
||||
std::vector<MAT::MatInstance *> m_mats;
|
||||
};
|
||||
}
|
||||
|
||||
namespace LoadedResource {
|
||||
Z::Model *GetLoadedModel(std::filesystem::path &path);
|
||||
void RemoveLoadedModel(std::filesystem::path &path);
|
||||
|
||||
Z::TextureData *GetLoadedTexture(std::filesystem::path &path);
|
||||
void RemoveLoadedTexture(std::filesystem::path &path);
|
||||
|
||||
SHADER::ShaderInfo *GetLoadedShader(std::string &name);
|
||||
void RemoveLoadedShader(std::string &name);
|
||||
|
||||
MAT::MatInstance *GetLoadedMaterial(std::string &name);
|
||||
void RemoveLoadedMaterial(std::string &name);
|
||||
}
|
||||
|
||||
|
||||
class ModelImporter { //=
|
||||
public:
|
||||
static bool LoadFromFile(const std::filesystem::path &path, Z::Model **out);
|
||||
};
|
||||
|
||||
class ImgImporter { //=
|
||||
public:
|
||||
static Z::TextureData *ImportFromFile(const std::filesystem::path &path,
|
||||
bool revG, Z::TextureType type);
|
||||
};
|
||||
|
||||
class ShaderImporter { //=
|
||||
public:
|
||||
static SHADER::ShaderInfo *ImportFromFile(const std::filesystem::path &path);
|
||||
};
|
||||
|
||||
class MatCreater { //=
|
||||
public:
|
||||
static MAT::MatInstance *CreateMat(const std::string &name, const SHADER::ShaderInfo *shaderInfo);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif //RENDER_IMG_IMPORTER_H
|
||||
@@ -0,0 +1,69 @@
|
||||
#include "states.h"
|
||||
#include "wgt_listitem.h"
|
||||
|
||||
//-blue app states
|
||||
|
||||
unsigned int States::desktopWidth{};
|
||||
unsigned int States::desktopHeight{};
|
||||
|
||||
unsigned int States::DesktopWidth() { return desktopWidth; }
|
||||
unsigned int States::DesktopHeight() { return desktopHeight; }
|
||||
|
||||
|
||||
//-blue SelectedItemWidgetGroup
|
||||
namespace {
|
||||
std::vector<SelectedItemWidgetGroup *> ar_ExistItemGroup{};
|
||||
}
|
||||
SelectedItemWidgetGroup *SelectedItemWidgetGroup::GetGroup(const char *groupName) {
|
||||
std::string name{groupName};
|
||||
auto it = std::ranges::find_if(ar_ExistItemGroup, [&name](auto *item) {
|
||||
return item->m_GroupName == name;
|
||||
});
|
||||
if (it != ar_ExistItemGroup.end()) return *it;
|
||||
auto *grp = new SelectedItemWidgetGroup{groupName};
|
||||
ar_ExistItemGroup.push_back(grp);
|
||||
return grp;
|
||||
}
|
||||
//=public
|
||||
ListItemWidget *SelectedItemWidgetGroup::GetCurSelected() const { return cur_Selected; }
|
||||
void SelectedItemWidgetGroup::Select(ListItemWidget *item) {
|
||||
for (auto member: m_MemberItems) {
|
||||
member->SetSelectedFromGroup_(member == item);
|
||||
}
|
||||
cur_Selected = item;
|
||||
}
|
||||
void SelectedItemWidgetGroup::UnSelectAll() {
|
||||
for (auto member: m_MemberItems) {
|
||||
member->SetSelectedFromGroup_(false);
|
||||
}
|
||||
cur_Selected = nullptr;
|
||||
}
|
||||
|
||||
//=private
|
||||
SelectedItemWidgetGroup::SelectedItemWidgetGroup(const char *name) : m_GroupName(name) {}
|
||||
void SelectedItemWidgetGroup::Add(ListItemWidget *item) {
|
||||
m_MemberItems.push_back(item);
|
||||
}
|
||||
void SelectedItemWidgetGroup::Remove(ListItemWidget *item) {
|
||||
m_MemberItems.remove(item);
|
||||
}
|
||||
void SelectedItemWidgetGroup::UnSelectFromItem(ListItemWidget *item) {
|
||||
if (cur_Selected == item) {
|
||||
item->SetSelectedFromGroup_(false);
|
||||
cur_Selected = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
@@ -0,0 +1,82 @@
|
||||
#ifndef RENDER_STATES_H
|
||||
#define RENDER_STATES_H
|
||||
|
||||
#include <string>
|
||||
#include <list>
|
||||
// #include <filesystem>
|
||||
#include <event.hpp>
|
||||
|
||||
|
||||
enum SelectableItem {
|
||||
None = 0, RenderObj = 1, Model = 2, Material = 3, Texture = 4, Shader = 5
|
||||
};
|
||||
|
||||
class ListItemWidget;
|
||||
class States { //=
|
||||
public:
|
||||
States() = delete;
|
||||
~States() = delete;
|
||||
|
||||
static unsigned int DesktopWidth();
|
||||
static unsigned int DesktopHeight();
|
||||
|
||||
private:
|
||||
friend class MainCppHelper;
|
||||
static unsigned int desktopWidth;
|
||||
static unsigned int desktopHeight;
|
||||
};
|
||||
|
||||
|
||||
class SelectedItemWidgetGroup { //=
|
||||
public:
|
||||
static SelectedItemWidgetGroup *GetGroup(const char *groupName);
|
||||
|
||||
public:
|
||||
[[nodiscard]] ListItemWidget *GetCurSelected() const;
|
||||
void Select(ListItemWidget *item);
|
||||
void UnSelectAll();
|
||||
|
||||
private:
|
||||
explicit SelectedItemWidgetGroup(const char *name);
|
||||
|
||||
friend class ListItemWidget;
|
||||
void Add(ListItemWidget *item);
|
||||
void Remove(ListItemWidget *item);
|
||||
void UnSelectFromItem(ListItemWidget *item);
|
||||
|
||||
std::string m_GroupName;
|
||||
std::list<ListItemWidget *> m_MemberItems;
|
||||
ListItemWidget *cur_Selected{};
|
||||
|
||||
};
|
||||
|
||||
namespace EVENT { //=
|
||||
struct InspecItem {
|
||||
SelectableItem type{};
|
||||
std::string path;
|
||||
void *resource{};
|
||||
};
|
||||
|
||||
inline Z::Event< InspecItem> OnSelectItem;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif //RENDER_STATES_H
|
||||
@@ -0,0 +1,129 @@
|
||||
#include "wgt_listitem.h"
|
||||
#include <QHBoxLayout>
|
||||
#include <QApplication>
|
||||
#include <QCloseEvent>
|
||||
#include <QDrag>
|
||||
#include <QMimeData>
|
||||
#include "states.h"
|
||||
namespace {
|
||||
|
||||
}
|
||||
|
||||
//region ListItemWidget
|
||||
//= public
|
||||
ListItemWidget::ListItemWidget(QWidget *inner, QWidget *parent, SelectedItemWidgetGroup *group)
|
||||
: QWidget(parent), m_group(group), m_inner(inner) {
|
||||
m_inner->setParent(this);
|
||||
setAttribute(Qt::WA_StyledBackground, true);
|
||||
auto *layout = new QHBoxLayout(this);
|
||||
layout->setContentsMargins(4, 4, 4, 4);
|
||||
layout->addWidget(inner);
|
||||
|
||||
setAutoFillBackground(true);
|
||||
SetSelected(false);
|
||||
if (group) group->Add(this);
|
||||
}
|
||||
ListItemWidget::~ListItemWidget() {
|
||||
if (m_group) m_group->Remove(this);
|
||||
}
|
||||
void ListItemWidget::SetMime(const char *mimeType, const char *mimeData) {
|
||||
m_MimeType = mimeType;
|
||||
m_mimeData = mimeData;
|
||||
}
|
||||
std::string ListItemWidget::GetMimeData() {
|
||||
return m_mimeData;
|
||||
}
|
||||
|
||||
void ListItemWidget::SetSelected(bool selected) {
|
||||
if (m_group) {
|
||||
if (!m_selected && selected) {
|
||||
m_group->Select(this);
|
||||
}
|
||||
} else {
|
||||
SetSelectedFromGroup_(selected);
|
||||
}
|
||||
}
|
||||
bool ListItemWidget::IsSelected() const {
|
||||
return m_selected;
|
||||
}
|
||||
|
||||
//= protected
|
||||
void ListItemWidget::mousePressEvent(QMouseEvent *e) {
|
||||
if (e->button() != Qt::MouseButton::LeftButton) return;
|
||||
m_prepareClick = true;
|
||||
m_pressPos = e->pos();
|
||||
}
|
||||
void ListItemWidget::mouseMoveEvent(QMouseEvent *e) {
|
||||
if (!(e->buttons() & Qt::LeftButton)) {
|
||||
return;
|
||||
}
|
||||
int distance = (e->position().toPoint() - m_pressPos).manhattanLength();
|
||||
if (distance >= QApplication::startDragDistance()) {
|
||||
m_prepareClick = false;
|
||||
TryStartDrag();
|
||||
}
|
||||
}
|
||||
void ListItemWidget::mouseReleaseEvent(QMouseEvent *event) {
|
||||
if (event->button() != Qt::MouseButton::LeftButton) return;
|
||||
if (m_prepareClick) {
|
||||
emit Clicked(this);
|
||||
}
|
||||
}
|
||||
void ListItemWidget::TryStartDrag() {}
|
||||
|
||||
//=private
|
||||
void ListItemWidget::SetSelectedFromGroup_(bool selected) {
|
||||
m_selected = selected;
|
||||
if (selected) {
|
||||
setStyleSheet("background-color: #3a75c4;");
|
||||
} else {
|
||||
setStyleSheet("background-color: transparent;");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//endregion
|
||||
|
||||
|
||||
//region DragItemWidget
|
||||
//= public
|
||||
DragItemWidget::DragItemWidget(QWidget *inner, QWidget *parent, SelectedItemWidgetGroup *group)
|
||||
: ListItemWidget(inner, parent, group) {}
|
||||
|
||||
void DragItemWidget::TryStartDrag() {
|
||||
if (m_dragging) return;
|
||||
m_dragging = true;
|
||||
auto *drag = new QDrag{this};
|
||||
auto *mime = new QMimeData{};
|
||||
|
||||
QByteArray arr{m_mimeData.data(), static_cast<int>(m_mimeData.size())};
|
||||
mime->setData(m_MimeType, arr);
|
||||
|
||||
drag->setMimeData(mime);
|
||||
Qt::DropAction result = drag->exec(Qt::MoveAction);
|
||||
(void)result;
|
||||
|
||||
m_dragging = false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//endregion
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
@@ -0,0 +1,74 @@
|
||||
#ifndef RENDER_LISTITEMWIDGET_H
|
||||
#define RENDER_LISTITEMWIDGET_H
|
||||
#include <QWidget>
|
||||
|
||||
class SelectedItemWidgetGroup;
|
||||
//-blue
|
||||
class ListItemWidget : public QWidget { //可选中的
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ListItemWidget(QWidget *inner, QWidget *parent = nullptr, SelectedItemWidgetGroup *group = nullptr);
|
||||
~ListItemWidget() override;
|
||||
void SetMime(const char *mimeType, const char *mimeData);
|
||||
std::string GetMimeData();
|
||||
void SetSelected(bool selected);
|
||||
[[nodiscard]] bool IsSelected() const;
|
||||
std::byte m_data[8]{};
|
||||
|
||||
signals:
|
||||
void Clicked(ListItemWidget *item);
|
||||
|
||||
protected:
|
||||
QString m_MimeType{};
|
||||
std::string m_mimeData{}; //这个buffer就放path的utf8
|
||||
void mousePressEvent(QMouseEvent *e) override;
|
||||
void mouseMoveEvent(QMouseEvent *e) override;
|
||||
void mouseReleaseEvent(QMouseEvent *event) override;
|
||||
virtual void TryStartDrag();
|
||||
|
||||
private:
|
||||
friend class SelectedItemWidgetGroup;
|
||||
void SetSelectedFromGroup_(bool selected);
|
||||
SelectedItemWidgetGroup *const m_group{};
|
||||
QWidget *m_inner{};
|
||||
bool m_selected{};
|
||||
bool m_prepareClick{};
|
||||
QPoint m_pressPos;
|
||||
|
||||
};
|
||||
|
||||
|
||||
//-blue
|
||||
class DragItemWidget : public ListItemWidget { //可拖拽的
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit DragItemWidget(QWidget *inner, QWidget *parent = nullptr,
|
||||
SelectedItemWidgetGroup *group = nullptr);
|
||||
|
||||
signals:
|
||||
void StartDrag(DragItemWidget *item);
|
||||
|
||||
protected:
|
||||
void TryStartDrag() override;
|
||||
|
||||
private:
|
||||
bool m_dragging{};
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif //RENDER_LISTITEMWIDGET_H
|
||||
@@ -0,0 +1,129 @@
|
||||
#include "wgt_prop_input.h"
|
||||
|
||||
#include <QHBoxLayout>
|
||||
#include <QLineEdit>
|
||||
#include <QSpinBox>
|
||||
#include <QDoubleSpinBox>
|
||||
#include <QLabel>
|
||||
|
||||
namespace {
|
||||
|
||||
|
||||
|
||||
QWidget *CreateRowFloat(int column, float *ar_data, WgtPropInput *p,
|
||||
void (WgtPropInput::*fpCB)()) {
|
||||
QWidget *w = new QWidget{};
|
||||
w->setLayout(new QHBoxLayout);
|
||||
w->layout()->setSpacing(10);
|
||||
w->layout()->setContentsMargins(0, 0, 0, 0);
|
||||
for (int i = 0; i < column; ++i) {
|
||||
QDoubleSpinBox *box = new QDoubleSpinBox{};
|
||||
float *pdata = &ar_data[i];
|
||||
box->setValue(*pdata);
|
||||
box->setMinimumWidth(40);
|
||||
box->setButtonSymbols(QAbstractSpinBox::NoButtons);
|
||||
QWidget::connect(box, &QDoubleSpinBox::valueChanged, [pdata,p,fpCB](double v) {
|
||||
*pdata = static_cast<float>(v);
|
||||
(p->*fpCB)();
|
||||
});
|
||||
w->layout()->addWidget(box);
|
||||
}
|
||||
return w;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
WgtPropInput::WgtPropInput(SHADER::PropertyInfo &prop, void (*fpCallback)(void *, int), void *parameter, int para2) :
|
||||
fpCallback(fpCallback), callBackOBJ(parameter), para2(para2), m_Prop(&prop) {
|
||||
int row{1};
|
||||
int column{1};
|
||||
bool bFloat{true};
|
||||
switch (prop.type) {
|
||||
case Z::ShaderInfo::PropType::Matrix3x3: {
|
||||
row = 3;
|
||||
column = 3;
|
||||
break;
|
||||
}
|
||||
case Z::ShaderInfo::PropType::Matrix4x4: {
|
||||
row = 4;
|
||||
column = 4;
|
||||
break;
|
||||
}
|
||||
case Z::ShaderInfo::PropType::Float: {
|
||||
break;
|
||||
}
|
||||
case Z::ShaderInfo::PropType::Float2: {
|
||||
column = 2;
|
||||
break;
|
||||
}
|
||||
case Z::ShaderInfo::PropType::Float3: {
|
||||
column = 3;
|
||||
break;
|
||||
}
|
||||
case Z::ShaderInfo::PropType::Float4: {
|
||||
column = 4;
|
||||
break;
|
||||
}
|
||||
case Z::ShaderInfo::PropType::Int: {
|
||||
bFloat = false;
|
||||
break;
|
||||
}
|
||||
case Z::ShaderInfo::PropType::Int2: {
|
||||
column = 2;
|
||||
bFloat = false;
|
||||
break;
|
||||
}
|
||||
case Z::ShaderInfo::PropType::Int3: {
|
||||
column = 3;
|
||||
bFloat = false;
|
||||
break;
|
||||
}
|
||||
case Z::ShaderInfo::PropType::Int4: {
|
||||
column = 4;
|
||||
bFloat = false;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
new QLabel("WgtPropInput: 错误的item类型", this);
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
this->setLayout(new QVBoxLayout);
|
||||
this->layout()->setSpacing(0);
|
||||
this->layout()->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
std::byte *pstart = reinterpret_cast<std::byte *>(&prop.value);
|
||||
|
||||
for (int i = 0; i < row; ++i) {
|
||||
void *p = pstart + (4 * i * column);
|
||||
this->layout()->addWidget(CreateRowFloat(column, static_cast<float *>(p),
|
||||
this, &WgtPropInput::OnValueChanged));
|
||||
}
|
||||
|
||||
}
|
||||
// 在子框框内数值变动时回调
|
||||
// 此时对应的数值已经在子框内填好,不需要再填
|
||||
// 直接通知外部update
|
||||
void WgtPropInput::OnValueChanged() {
|
||||
if (fpCallback) {
|
||||
(*fpCallback)(callBackOBJ, para2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
@@ -0,0 +1,31 @@
|
||||
#ifndef RENDER_WGT_PROP_INPUT_H
|
||||
#define RENDER_WGT_PROP_INPUT_H
|
||||
|
||||
#include <QWidget>
|
||||
#include "resource.h"
|
||||
|
||||
class WgtPropInput : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/**
|
||||
* 创建一个Material属性输入框(支持float,int,2,3,4,4x4等格式)
|
||||
* @param prop shader属性描述符
|
||||
* @param fpCallback 可选-UI变化时的回调,可携带两个参数(void*,int)
|
||||
* @param parameter 可选-UI变化时回调携带参数1
|
||||
* @param para2 可选-UI变化时回调携带参数2
|
||||
*/
|
||||
explicit WgtPropInput(SHADER::PropertyInfo &prop,
|
||||
void (*fpCallback)(void *, int) = nullptr, void *parameter = nullptr, int para2 = 0);
|
||||
|
||||
private:
|
||||
void (*fpCallback)(void *, int){};
|
||||
void *callBackOBJ{};
|
||||
int para2;
|
||||
SHADER::PropertyInfo *m_Prop{};
|
||||
void OnValueChanged();
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif //RENDER_WGT_PROP_INPUT_H
|
||||
@@ -0,0 +1,188 @@
|
||||
#include "wnd_add_tex.h"
|
||||
|
||||
#include <QLabel>
|
||||
#include <QVBoxLayout>
|
||||
#include <QPushButton>
|
||||
#include <QCheckBox>
|
||||
#include <QStyle>
|
||||
#include <QComboBox>
|
||||
|
||||
namespace {
|
||||
[[maybe_unused]] QString TexType2String_Full(Z::TextureType type) {
|
||||
switch (type) {
|
||||
case Z::TextureType::NONE: return "None";
|
||||
case Z::TextureType::DIFFUSE: return "Diffuse";
|
||||
case Z::TextureType::SPECULAR: return "Specular";
|
||||
case Z::TextureType::AMBIENT: return "Ambient";
|
||||
case Z::TextureType::EMISSIVE: return "Emissive";
|
||||
case Z::TextureType::HEIGHT: return "Height";
|
||||
case Z::TextureType::NORMALS: return "Normals";
|
||||
case Z::TextureType::SHININESS: return "Shiness";
|
||||
case Z::TextureType::OPACITY: return "Opacity";
|
||||
case Z::TextureType::DISPLACEMENT: return "Displacement";
|
||||
case Z::TextureType::LIGHTMAP: return "Lightmap";
|
||||
case Z::TextureType::REFLECTION: return "Reflection";
|
||||
case Z::TextureType::BASE_COLOR: return "BaseColor";
|
||||
case Z::TextureType::NORMAL_CAMERA: return "NormalCamera";
|
||||
case Z::TextureType::EMISSION_COLOR: return "EmissionColor";
|
||||
case Z::TextureType::METALNESS: return "Metalness";
|
||||
case Z::TextureType::DIFFUSE_ROUGHNESS: return "DiffuseRoughness";
|
||||
case Z::TextureType::AMBIENT_OCCLUSION: return "AmbientOcclusion";
|
||||
case Z::TextureType::UNKNOWN: return "Unknow";
|
||||
case Z::TextureType::SHEEN: return "Sheen";
|
||||
case Z::TextureType::CLEARCOAT: return "Clearcoat";
|
||||
case Z::TextureType::TRANSMISSION: return "Transmission";
|
||||
case Z::TextureType::MAYA_BASE: return "MayaBase";
|
||||
case Z::TextureType::MAYA_SPECULAR: return "MayaSpecular";
|
||||
case Z::TextureType::SPECULAR_COLOR: return "SpecularColor";
|
||||
case Z::TextureType::MAYA_SPECULAR_ROUGHNESS: return "MayaSpecularRoughness";
|
||||
case Z::TextureType::ANISOTROPY: return "Anisotropy";
|
||||
case Z::TextureType::GLTF_METALLIC_ROUGHNESS: return "GltfMetallicRoughness";
|
||||
default: return "None";
|
||||
}
|
||||
}
|
||||
}
|
||||
class WndAddTex::WgtConfig : public QWidget {
|
||||
public :
|
||||
explicit WgtConfig(ReturnData &outData) : m_outData(outData) {
|
||||
this->setLayout(new QVBoxLayout);
|
||||
layout()->setAlignment(Qt::AlignTop);
|
||||
}
|
||||
|
||||
void DisplaySRGB() {
|
||||
PurgeWgts();
|
||||
m_outData.SRGB = true;
|
||||
QCheckBox *cb = new QCheckBox{"16 bytes per pixel"};
|
||||
cb->setCheckState(m_outData.bpp_16 ? Qt::CheckState::Checked : Qt::CheckState::Unchecked);
|
||||
connect(cb, &QCheckBox::checkStateChanged, this, [this](bool checked) {
|
||||
this->m_outData.bpp_16 = checked;
|
||||
});
|
||||
layout()->addWidget(cb);
|
||||
b_clear = false;
|
||||
}
|
||||
void DisplayLinear() {
|
||||
PurgeWgts();
|
||||
m_outData.SRGB = false;
|
||||
QCheckBox *cb = new QCheckBox{"16 bytes per pixel"};
|
||||
cb->setCheckState(m_outData.bpp_16 ? Qt::CheckState::Checked : Qt::CheckState::Unchecked);
|
||||
connect(cb, &QCheckBox::checkStateChanged, this, [this](bool checked) {
|
||||
this->m_outData.bpp_16 = checked;
|
||||
});
|
||||
layout()->addWidget(cb);
|
||||
b_clear = false;
|
||||
}
|
||||
void DisplayNormal() {
|
||||
PurgeWgts();
|
||||
m_outData.SRGB = false;
|
||||
QCheckBox *cb = new QCheckBox{"Reverse G Channel"};
|
||||
cb->setCheckState(Qt::CheckState::Unchecked);
|
||||
connect(cb, &QCheckBox::checkStateChanged, this, [this](bool checked) {
|
||||
this->m_outData.reverse_Y = checked;
|
||||
});
|
||||
layout()->addWidget(cb);
|
||||
b_clear = false;
|
||||
}
|
||||
|
||||
private:
|
||||
void PurgeWgts() {
|
||||
if (b_clear)return;
|
||||
while (QLayoutItem *item = layout()->takeAt(0)) {
|
||||
if (QWidget *widget = item->widget()) {
|
||||
// 延迟删除控件
|
||||
widget->deleteLater();
|
||||
}
|
||||
// 删除 QLayoutItem、Spacer 或已经清空的子布局
|
||||
delete item;
|
||||
}
|
||||
b_clear = true;
|
||||
}
|
||||
|
||||
private:
|
||||
ReturnData &m_outData;
|
||||
bool b_clear{true};
|
||||
};
|
||||
WndAddTex::WndAddTex(ReturnData &outData) : m_outData(outData) {
|
||||
outData = {};
|
||||
|
||||
resize(250, 300);
|
||||
this->setWindowTitle("Text Config");
|
||||
this->setWindowIcon(QIcon{});
|
||||
|
||||
QVBoxLayout *lay = new QVBoxLayout(this);
|
||||
this->setLayout(lay);
|
||||
lay->setAlignment(Qt::AlignJustify);
|
||||
|
||||
this->setStyleSheet(".QCheckBox{font-size:18px;}.QCheckBox::indicator{width: 20px;height: 20px;}");
|
||||
|
||||
// 下拉菜单选项
|
||||
QComboBox *box = new QComboBox{};
|
||||
box->addItem("SRGB"); //0
|
||||
box->addItem("Linear"); //1
|
||||
box->addItem("Normal"); //2
|
||||
lay->addWidget(box);
|
||||
|
||||
// 具体内部选项
|
||||
m_config = new WgtConfig{outData};
|
||||
lay->addWidget(m_config);
|
||||
connect(box, &QComboBox::currentIndexChanged, this, &WndAddTex::OnSelectChanged);
|
||||
OnSelectChanged(0);
|
||||
|
||||
// 最下方按钮
|
||||
lay->addStretch();
|
||||
QWidget *btnarea = new QWidget;
|
||||
btnarea->setLayout(new QHBoxLayout);
|
||||
QPushButton *btn_accept = new QPushButton{"Confirm", this};
|
||||
connect(btn_accept, &QPushButton::clicked, this, [this,&outData]() {
|
||||
outData.valid = true;
|
||||
this->accept();
|
||||
});
|
||||
QPushButton *btn_cancel = new QPushButton{"Cancel", this};
|
||||
connect(btn_cancel, &QPushButton::clicked, this, [this,&outData]() {
|
||||
outData.valid = false;
|
||||
this->reject();
|
||||
});
|
||||
btnarea->layout()->addWidget(btn_accept);
|
||||
btnarea->layout()->addWidget(btn_cancel);
|
||||
lay->addWidget(btnarea);
|
||||
}
|
||||
WndAddTex::~WndAddTex() = default;
|
||||
void WndAddTex::OnSelectChanged(int index) {
|
||||
switch (index) {
|
||||
case 0: {
|
||||
this->m_outData.textureType = Z::TextureType::DIFFUSE;
|
||||
this->m_config->DisplaySRGB();
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
this->m_outData.textureType = Z::TextureType::NONE;
|
||||
this->m_config->DisplayLinear();
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
this->m_outData.textureType = Z::TextureType::NORMALS;
|
||||
this->m_config->DisplayNormal();
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
@@ -0,0 +1,31 @@
|
||||
#ifndef RENDER_WND_ADD_TEX_H
|
||||
#define RENDER_WND_ADD_TEX_H
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
#include "render_type_def.h"
|
||||
|
||||
class WndAddTex : public QDialog {
|
||||
|
||||
public:
|
||||
struct ReturnData {
|
||||
bool valid = false;
|
||||
bool SRGB = true;
|
||||
bool reverse_Y = false;
|
||||
bool bpp_16 = false;
|
||||
Z::TextureType textureType = Z::TextureType::NONE;
|
||||
};
|
||||
|
||||
explicit WndAddTex(ReturnData &outData);
|
||||
~WndAddTex() override;
|
||||
|
||||
private:
|
||||
void OnSelectChanged(int index);
|
||||
class WgtConfig;
|
||||
ReturnData &m_outData;
|
||||
WgtConfig *m_config;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif //RENDER_WND_ADD_TEX_H
|
||||
@@ -0,0 +1,70 @@
|
||||
#include "wnd_create_mat.h"
|
||||
|
||||
#include <qevent.h>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <qmessagebox.h>
|
||||
#include <QPushButton>
|
||||
#include <QScrollArea>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
|
||||
|
||||
WndCreateMat::WndCreateMat(QWidget *parent, const std::vector<std::string> &items,
|
||||
std::string &outShader, std::string &outMatName) :
|
||||
QDialog(parent), out_shader(&outShader), out_matName(&outMatName), m_tex(new QLineEdit) {
|
||||
this->setWindowFlag(Qt::WindowType::WindowCloseButtonHint, true);
|
||||
this->setLayout(new QVBoxLayout);
|
||||
this->layout()->setAlignment(Qt::AlignTop);
|
||||
this->layout()->addWidget(new QLabel{"Material Name"});
|
||||
this->layout()->addWidget(m_tex);
|
||||
this->layout()->addWidget(new QLabel{"Select Shader"});
|
||||
QScrollArea *scroll = new QScrollArea;
|
||||
scroll->setLayout(new QVBoxLayout);
|
||||
QWidget *w = new QWidget;
|
||||
scroll->layout()->addWidget(w);
|
||||
scroll->setWidget(w);
|
||||
scroll->setWidgetResizable(true);
|
||||
QVBoxLayout *lay = new QVBoxLayout{};
|
||||
w->setLayout(lay);
|
||||
lay->setAlignment(Qt::AlignTop);
|
||||
this->layout()->addWidget(scroll);
|
||||
|
||||
for (auto &i: items) {
|
||||
QPushButton *btn = new QPushButton{i.c_str()};
|
||||
lay->addWidget(btn);
|
||||
connect(btn, &QPushButton::clicked, this, &WndCreateMat::OnBtnClicked);
|
||||
}
|
||||
this->resize(230, 430);
|
||||
}
|
||||
void WndCreateMat::keyPressEvent(QKeyEvent *event) {
|
||||
event->accept();
|
||||
// QMessageBox::keyPressEvent(event);
|
||||
}
|
||||
void WndCreateMat::OnBtnClicked() {
|
||||
auto *btn = qobject_cast<QPushButton *>(this->sender());
|
||||
if (!btn) { return; }
|
||||
|
||||
if (m_tex->text().isEmpty()) {
|
||||
QMessageBox::about(this, "错误", "请输入Material Name");
|
||||
} else {
|
||||
*out_matName = m_tex->text().toUtf8().toStdString();
|
||||
*out_shader = btn->text().toUtf8().toStdString();
|
||||
this->accept();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
@@ -0,0 +1,28 @@
|
||||
#ifndef RENDER_WND_SELECT_SHADER_H
|
||||
#define RENDER_WND_SELECT_SHADER_H
|
||||
#include <QWidget>
|
||||
#include <QDialog>
|
||||
|
||||
class QLineEdit;
|
||||
class WndCreateMat : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit WndCreateMat(QWidget *parent, const std::vector<std::string> &items,
|
||||
std::string &outShader,std::string& outMatName);
|
||||
|
||||
protected:
|
||||
void keyPressEvent(QKeyEvent *event) override;
|
||||
|
||||
private:
|
||||
void OnBtnClicked();
|
||||
|
||||
private:
|
||||
std::string *out_shader{};
|
||||
std::string * out_matName{};
|
||||
QLineEdit *m_tex{};
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif //RENDER_WND_SELECT_SHADER_H
|
||||
@@ -0,0 +1,5 @@
|
||||
#include "wnd_debug.h"
|
||||
WndDebug::WndDebug(QWidget *parent) : QWidget(parent) {
|
||||
setAttribute(Qt::WA_StyledBackground, true);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#ifndef RENDER_WND_DEBUG_H
|
||||
#define RENDER_WND_DEBUG_H
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
|
||||
class WndDebug:public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit WndDebug(QWidget*parent);
|
||||
|
||||
private:
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif //RENDER_WND_DEBUG_H
|
||||
@@ -0,0 +1,146 @@
|
||||
#include "wnd_hierachy.h"
|
||||
|
||||
#include <render_type_def.h>
|
||||
#include <qevent.h>
|
||||
#include <QVBoxLayout>
|
||||
#include <QScrollArea>
|
||||
#include <QLabel>
|
||||
#include <QMimeData>
|
||||
#include "resource.h"
|
||||
#include <tool.h>
|
||||
#include <render/task.h>
|
||||
#include <iostream>
|
||||
#include <QMessageBox>
|
||||
|
||||
#include "wgt_listitem.h"
|
||||
#include "states.h"
|
||||
#include <mutex>
|
||||
|
||||
namespace {
|
||||
|
||||
std::mutex mtx_cb{};
|
||||
std::condition_variable cv_cb{};
|
||||
Z::ResultMessage g_CallbackMessage{};
|
||||
void CallBack_WakeThreadUp(Z::ResultMessage msg) { // 被Renderer回调
|
||||
g_CallbackMessage = msg;
|
||||
cv_cb.notify_all();
|
||||
}
|
||||
void WaitForResult() {
|
||||
std::unique_lock lk(mtx_cb);
|
||||
cv_cb.wait(lk);
|
||||
}
|
||||
|
||||
|
||||
void OnItemClicked(ListItemWidget *item) {
|
||||
item->SetSelected(true);
|
||||
EVENT::InspecItem it{};
|
||||
it.type = RenderObj;
|
||||
std::memcpy(&it.resource, &item->m_data, 8);
|
||||
EVENT::OnSelectItem.EmitEvent(it);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//=public
|
||||
WndHierachy::WndHierachy(QWidget *parent) : QWidget(parent) {
|
||||
setAttribute(Qt::WA_StyledBackground, true);
|
||||
setAcceptDrops(true);
|
||||
setLayout(new QVBoxLayout());
|
||||
|
||||
|
||||
ShowPanel();
|
||||
}
|
||||
|
||||
//=protected
|
||||
void WndHierachy::dragEnterEvent(QDragEnterEvent *event) {
|
||||
if (event->mimeData()->hasFormat("application/x-model")) {
|
||||
event->acceptProposedAction(); // 接受:后续才会有 dragMove/drop/leave
|
||||
event->accept();
|
||||
} else {
|
||||
event->ignore();
|
||||
}
|
||||
}
|
||||
|
||||
void WndHierachy::dropEvent(QDropEvent *event) {
|
||||
event->accept();
|
||||
QByteArray baa{event->mimeData()->data("application/x-model")};
|
||||
const char *str = baa.data();
|
||||
std::string path_str{str};
|
||||
std::filesystem::path path{std::u8string(reinterpret_cast<const char8_t *>(path_str.c_str()), path_str.size())};
|
||||
Z::Model *model = LoadedResource::GetLoadedModel(path);
|
||||
if (model == nullptr) {
|
||||
MessageBoxW(nullptr, Z::u8_to_wstring(path_str).c_str(),
|
||||
L"未加载模型",MB_OK);
|
||||
return;
|
||||
}
|
||||
// 向 GRAPHIC 添加渲染
|
||||
Z::Task t{};
|
||||
t.type = Z::CmdType::AddRenderObj;
|
||||
t.cmd.add_render_obj.model = model;
|
||||
t.fpCallBack = CallBack_WakeThreadUp;
|
||||
Z::Task::AddCommand(t);
|
||||
WaitForResult();
|
||||
if (g_CallbackMessage.res != Z::ResultType::OK) {
|
||||
return;
|
||||
}
|
||||
int id{-2};
|
||||
|
||||
// 返回的 .data 是 Graphic里的RenderObj的ID
|
||||
std::memcpy(&id, g_CallbackMessage.data, sizeof(int));
|
||||
// 创建 render object 由此窗口管理
|
||||
std::u8string u8objname{path.stem().u8string()};
|
||||
RENDEROBJ::Object *obj = new RENDEROBJ::Object{
|
||||
std::string{reinterpret_cast<const char *>(u8objname.c_str()), u8objname.size()}, id};
|
||||
obj->m_mats.resize(model->ar_meshDatas.size(), nullptr);
|
||||
|
||||
m_lstItems.push_back(obj);
|
||||
|
||||
ShowPanel();
|
||||
}
|
||||
|
||||
|
||||
//=private
|
||||
void WndHierachy::ShowPanel() {
|
||||
if (m_scroll) {
|
||||
layout()->removeWidget(m_scroll);
|
||||
m_scroll->deleteLater();
|
||||
};
|
||||
|
||||
m_scroll = new QScrollArea{};
|
||||
layout()->addWidget(m_scroll);
|
||||
|
||||
QWidget *content = new QWidget(m_scroll);
|
||||
QVBoxLayout *layout = new QVBoxLayout{content};
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
layout->setSpacing(0);
|
||||
|
||||
SelectedItemWidgetGroup *group = SelectedItemWidgetGroup::GetGroup("inspector");
|
||||
|
||||
for (auto item: m_lstItems) {
|
||||
QLabel *lb = new QLabel{item->name.c_str()};
|
||||
ListItemWidget *w = new ListItemWidget{lb, this, group};
|
||||
w->SetMime("application/x-renderobj-id", std::to_string(item->id).c_str());
|
||||
std::memcpy(&w->m_data, &item, 8);
|
||||
connect(w, &ListItemWidget::Clicked, this, OnItemClicked);
|
||||
layout->addWidget(w);
|
||||
}
|
||||
|
||||
layout->addStretch();
|
||||
|
||||
m_scroll->setWidget(content);
|
||||
m_scroll->setWidgetResizable(true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
@@ -0,0 +1,30 @@
|
||||
#ifndef RENDER_WND_HIERACHY_H
|
||||
#define RENDER_WND_HIERACHY_H
|
||||
#include <QWidget>
|
||||
#include <list>
|
||||
#include "resource.h"
|
||||
|
||||
|
||||
class QScrollArea;
|
||||
class ListItemWidget;
|
||||
class WndHierachy : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit WndHierachy(QWidget *parent);
|
||||
|
||||
protected:
|
||||
void dragEnterEvent(QDragEnterEvent *event) override;
|
||||
void dropEvent(QDropEvent *event) override;
|
||||
|
||||
private:
|
||||
void ShowPanel();
|
||||
|
||||
private:
|
||||
QScrollArea *m_scroll{};
|
||||
std::list<RENDEROBJ::Object *> m_lstItems;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif //RENDER_WND_HIERACHY_H
|
||||
@@ -0,0 +1,526 @@
|
||||
#include "wnd_inspector.h"
|
||||
|
||||
#include <QLabel>
|
||||
#include <QStackedLayout>
|
||||
#include <QScrollArea>
|
||||
#include <iostream>
|
||||
#include <QDoubleSpinBox>
|
||||
#include <QLineEdit>
|
||||
#include <QMessageBox>
|
||||
#include "wgt_prop_input.h"
|
||||
#include <limits>
|
||||
#include <qevent.h>
|
||||
#include <QMimeData>
|
||||
|
||||
#include "render/task.h"
|
||||
#include <mutex>
|
||||
|
||||
#include "../../render/src/shader.h"
|
||||
|
||||
namespace {
|
||||
std::mutex _g_mtx{};
|
||||
std::condition_variable _cv{};
|
||||
Z::ResultMessage g_msg{};
|
||||
void Callback_Wakeup(Z::ResultMessage msg) {
|
||||
g_msg = msg;
|
||||
_cv.notify_one();
|
||||
}
|
||||
void WaitForResult() {
|
||||
std::unique_lock lk{_g_mtx};
|
||||
_cv.wait(lk);
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
WndInspector *_inspector{};
|
||||
void DisplayItem_(EVENT::InspecItem item) {
|
||||
if (_inspector) _inspector->DisplayItem(item);
|
||||
}
|
||||
void InitSpinBox(QDoubleSpinBox *box) {
|
||||
box->setMaximumWidth(80);
|
||||
box->setMinimum(std::numeric_limits<float>::lowest());
|
||||
box->setMaximum(std::numeric_limits<float>::max());
|
||||
box->setButtonSymbols(QAbstractSpinBox::NoButtons);
|
||||
}
|
||||
|
||||
class Row : public QWidget {
|
||||
public:
|
||||
explicit Row(float *v3, float step = 0.5f, void (*callback)(void *) = nullptr, void *parameter = nullptr) :
|
||||
values{v3, v3 + 1, v3 + 2}, temp_value{*v3, *(v3 + 1), *(v3 + 2)},
|
||||
para(parameter), fpCallback(callback) {
|
||||
this->setLayout(new QHBoxLayout);
|
||||
this->layout()->setSpacing(10);
|
||||
|
||||
mx = new QDoubleSpinBox{};
|
||||
InitSpinBox(mx);
|
||||
mx->setValue(*values[0]);
|
||||
mx->setSingleStep(step);
|
||||
connect(mx, &QDoubleSpinBox::valueChanged, this, &Row::OnValueChangedX);
|
||||
|
||||
my = new QDoubleSpinBox{};
|
||||
InitSpinBox(my);
|
||||
my->setValue(*values[1]);
|
||||
my->setSingleStep(step);
|
||||
connect(my, &QDoubleSpinBox::valueChanged, this, &Row::OnValueChangedY);
|
||||
|
||||
mz = new QDoubleSpinBox{};
|
||||
InitSpinBox(mz);
|
||||
mz->setValue(*values[2]);
|
||||
mz->setSingleStep(step);
|
||||
connect(mz, &QDoubleSpinBox::valueChanged, this, &Row::OnValueChangedZ);
|
||||
|
||||
this->layout()->addWidget(mx);
|
||||
this->layout()->addWidget(my);
|
||||
this->layout()->addWidget(mz);
|
||||
};
|
||||
|
||||
float *GetFloat3() {
|
||||
return temp_value;
|
||||
}
|
||||
|
||||
private:
|
||||
void OnValueChangedX(double v) {
|
||||
temp_value[0] = static_cast<float>(v);
|
||||
if (fpCallback) {
|
||||
fpCallback(para);
|
||||
}
|
||||
}
|
||||
void OnValueChangedY(double v) {
|
||||
temp_value[1] = static_cast<float>(v);
|
||||
if (fpCallback) {
|
||||
fpCallback(para);
|
||||
}
|
||||
}
|
||||
void OnValueChangedZ(double v) {
|
||||
temp_value[2] = static_cast<float>(v);
|
||||
if (fpCallback) {
|
||||
fpCallback(para);
|
||||
}
|
||||
}
|
||||
|
||||
float *values[3];
|
||||
float temp_value[3];
|
||||
|
||||
QDoubleSpinBox *mx;
|
||||
QDoubleSpinBox *my;
|
||||
QDoubleSpinBox *mz;
|
||||
|
||||
void *para;
|
||||
void (*fpCallback)(void *para);
|
||||
};
|
||||
|
||||
//region RenderObject 展示面板
|
||||
class WgtMaterial : public QWidget {
|
||||
public:
|
||||
explicit WgtMaterial(MAT::MatInstance *mat, RENDEROBJ::Object *obj, int modelIdx, int meshIdx) :
|
||||
m_renderObj(obj), m_modelIdx(modelIdx), m_meshIdx(meshIdx), m_mat(mat) {
|
||||
// 这里获取的mat可能是nullptr,此时用的是default material
|
||||
setAcceptDrops(true);
|
||||
setLayout(new QVBoxLayout);
|
||||
setStyleSheet("background-color:#445522;");
|
||||
resize(100, 30);
|
||||
QString disp = mat ? QString{mat->name.c_str()} : QString{"Default"};
|
||||
m_label = new QLabel{disp};
|
||||
layout()->addWidget(m_label);
|
||||
}
|
||||
|
||||
public:
|
||||
RENDEROBJ::Object *m_renderObj{};
|
||||
int m_modelIdx;
|
||||
int m_meshIdx;
|
||||
MAT::MatInstance *m_mat;
|
||||
QLabel *m_label{};
|
||||
|
||||
protected:
|
||||
void dragEnterEvent(QDragEnterEvent *event) override {
|
||||
const QMimeData *mime = event->mimeData();
|
||||
if (mime->hasFormat("application/x-material")) {
|
||||
event->acceptProposedAction();
|
||||
event->accept();
|
||||
} else {
|
||||
event->ignore();
|
||||
}
|
||||
}
|
||||
void dropEvent(QDropEvent *event) override {
|
||||
QByteArray data = event->mimeData()->data("application/x-material");
|
||||
std::string matNameStr{data.data()};
|
||||
std::cout << "接收到str:" << matNameStr << std::endl; // 这里的data_str是material的名字
|
||||
// 通知 renderer 设置材质
|
||||
Z::Task task{};
|
||||
task.type = Z::CmdType::SetRenderObjMaterial;
|
||||
task.fpCallBack = Callback_Wakeup;
|
||||
task.cmd.set_render_obj_material.matName = matNameStr.c_str();
|
||||
task.cmd.set_render_obj_material.meshIndex = m_meshIdx;
|
||||
task.cmd.set_render_obj_material.objIndex = m_modelIdx;
|
||||
Z::Task::AddCommand(task);
|
||||
WaitForResult();
|
||||
if (g_msg.res != Z::ResultType::OK) {
|
||||
QMessageBox::about(nullptr, "错误", "设置material失败");
|
||||
return;
|
||||
}
|
||||
// 改app端renderObj的material表
|
||||
MAT::MatInstance *_mat = LoadedResource::GetLoadedMaterial(matNameStr);
|
||||
m_renderObj->m_mats[m_meshIdx] = _mat;
|
||||
m_mat = _mat;
|
||||
// 改ui
|
||||
m_label->setText(QString::fromUtf8(matNameStr.c_str()));
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
void RenderObjPanelCallback(void *panel);
|
||||
class RenderObjPanel : public QWidget {
|
||||
public:
|
||||
explicit RenderObjPanel(RENDEROBJ::Object *obj) : obj(obj) {
|
||||
setLayout(new QVBoxLayout);
|
||||
layout()->setSpacing(0);
|
||||
layout()->setAlignment(Qt::AlignTop);
|
||||
|
||||
layout()->addWidget(new QLabel("position"));
|
||||
rowPos = new Row(obj->position, 0.5f, RenderObjPanelCallback, this);
|
||||
layout()->addWidget(rowPos);
|
||||
|
||||
layout()->addWidget(new QLabel("rotation"));
|
||||
rowRot = new Row(obj->rotation, 15.0f, RenderObjPanelCallback, this);
|
||||
layout()->addWidget(rowRot);
|
||||
|
||||
layout()->addWidget(new QLabel("scale"));
|
||||
rowScale = new Row(obj->scale, 0.1f, RenderObjPanelCallback, this);
|
||||
layout()->addWidget(rowScale);
|
||||
|
||||
layout()->addWidget(new QLabel("Materials"));
|
||||
for (int i = 0; i < obj->m_mats.size(); ++i) {
|
||||
// 这里的m_mats[i]可能是nullptr(用的default)
|
||||
layout()->addWidget(new WgtMaterial{obj->m_mats[i], obj, obj->id, i});
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateValues() const {
|
||||
bool ret = obj->Update(rowPos->GetFloat3(), rowRot->GetFloat3(), rowScale->GetFloat3());
|
||||
(void)ret;
|
||||
}
|
||||
|
||||
public:
|
||||
RENDEROBJ::Object *obj;
|
||||
Row *rowPos;
|
||||
Row *rowRot;
|
||||
Row *rowScale;
|
||||
};
|
||||
void RenderObjPanelCallback(void *panel) {
|
||||
static_cast<RenderObjPanel *>(panel)->UpdateValues();
|
||||
}
|
||||
//endregion
|
||||
|
||||
|
||||
//region Material 展示面板
|
||||
class WgtTexBox : public QWidget {
|
||||
public:
|
||||
explicit WgtTexBox(MAT::MatInstance *mat, int matTexIdx) :
|
||||
m_mat(mat), m_MatTexIdx(matTexIdx) {
|
||||
setAcceptDrops(true);
|
||||
setAttribute(Qt::WidgetAttribute::WA_StyledBackground, true);
|
||||
setStyleSheet("background-color:#666666;");
|
||||
m_texInfo = &mat->info.textures[matTexIdx];
|
||||
|
||||
QVBoxLayout *lay = new QVBoxLayout{this};
|
||||
lay->setSpacing(0);
|
||||
lay->setContentsMargins(0, 0, 0, 0);
|
||||
setLayout(lay);
|
||||
|
||||
QString texName{m_texInfo->cur_tex.empty() ? "" : m_texInfo->cur_tex.c_str()};
|
||||
|
||||
m_label = new QLabel{texName};
|
||||
lay->addWidget(m_label);
|
||||
}
|
||||
|
||||
protected:
|
||||
void dragEnterEvent(QDragEnterEvent *event) override {
|
||||
if (event->mimeData()->hasFormat("application/x-texture")) {
|
||||
event->acceptProposedAction();
|
||||
event->accept();
|
||||
} else {
|
||||
event->ignore();
|
||||
}
|
||||
}
|
||||
void dropEvent(QDropEvent *event) override {
|
||||
auto *mime = event->mimeData();
|
||||
auto data = mime->data("application/x-texture");
|
||||
// 获取的data是Res::path转换为UTF8的str
|
||||
std::u8string u8pathstr{reinterpret_cast<const char8_t *>(data.data()),
|
||||
static_cast<unsigned long long>(data.size())};
|
||||
// std::string pathstr{data.data(), static_cast<unsigned long long>(data.size())};
|
||||
// std::cout << "获取dropevent,data=" << pathstr << std::endl;
|
||||
|
||||
std::filesystem::path path{u8pathstr};
|
||||
std::u8string u8texName = path.stem().u8string();
|
||||
|
||||
// 向Renderer发起请求,设置texture给material
|
||||
Z::Task task{};
|
||||
task.type = Z::CmdType::AssignTextureToMaterial;
|
||||
task.fpCallBack = Callback_Wakeup;
|
||||
task.cmd.assign_texture_to_material.texName = reinterpret_cast<const char *>(u8texName.c_str());
|
||||
task.cmd.assign_texture_to_material.matName = m_mat->name.c_str();
|
||||
task.cmd.assign_texture_to_material.matTexIdx = m_MatTexIdx;
|
||||
Z::Task::AddCommand(task);
|
||||
WaitForResult();
|
||||
if (g_msg.res != Z::ResultType::OK) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 更新UI和APP端数据
|
||||
m_mat->info.textures[m_MatTexIdx].cur_tex.assign(
|
||||
reinterpret_cast<const char *>(u8texName.c_str()), u8pathstr.size());
|
||||
m_label->setText(QString{u8texName.c_str()});
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
QLabel *m_label{};
|
||||
|
||||
MAT::MatInstance *m_mat;
|
||||
int m_MatTexIdx;
|
||||
const SHADER::TextureInfo *m_texInfo{};
|
||||
|
||||
void (*fpOnValueChange)(MAT::MatInstance *, int, SHADER::TextureInfo *){nullptr};
|
||||
}; //class WgtTexBox
|
||||
|
||||
class WgtTexture : public QWidget {
|
||||
public:
|
||||
explicit WgtTexture(MAT::MatInstance *mat, int matTexIdx) : m_mat(mat), m_matTexIdx(matTexIdx) {
|
||||
this->setLayout(new QHBoxLayout);
|
||||
|
||||
auto *tex = &mat->info.textures[matTexIdx];
|
||||
|
||||
QLabel *lab = new QLabel(tex->name.c_str());
|
||||
lab->setMaximumWidth(80);
|
||||
WgtTexBox *box = new WgtTexBox(mat, matTexIdx);
|
||||
layout()->addWidget(lab);
|
||||
layout()->addWidget(box);
|
||||
}
|
||||
|
||||
public:
|
||||
MAT::MatInstance *m_mat;
|
||||
|
||||
int m_matTexIdx;
|
||||
|
||||
}; //class WgtTexture
|
||||
|
||||
void UpdateMaterial(void *pmat, int index) {
|
||||
MAT::MatInstance *mat = static_cast<MAT::MatInstance *>(pmat);
|
||||
mat->Update(index);
|
||||
}
|
||||
|
||||
class CBufferPanel : public QWidget {
|
||||
public:
|
||||
explicit CBufferPanel(SHADER::CbufferInfo *cb, MAT::MatInstance *mat) : m_cbInfo(cb) {
|
||||
QVBoxLayout *lay = new QVBoxLayout{};
|
||||
lay->setContentsMargins(0, 5, 0, 5);
|
||||
setLayout(lay);
|
||||
lay->addWidget(new QLabel{QString{"CBuffer Name: "} + QString{cb->name.c_str()}
|
||||
+ QString{" OdrInArr: "} + std::to_string(cb->index).c_str()});
|
||||
|
||||
for (auto &prop: cb->props) {
|
||||
lay->addWidget(new QLabel{std::to_string(prop.index).c_str() + QString{" : "} +
|
||||
QString{prop.name.c_str()}});
|
||||
WgtPropInput *panelProp = new WgtPropInput{prop, UpdateMaterial, mat, prop.index};
|
||||
lay->addWidget(panelProp);
|
||||
}
|
||||
}
|
||||
|
||||
SHADER::CbufferInfo *m_cbInfo;
|
||||
}; // class CBufferPanel
|
||||
|
||||
class MaterialPanel : public QWidget {
|
||||
public:
|
||||
explicit MaterialPanel(MAT::MatInstance *mat) : m_material(mat) {
|
||||
this->setContentsMargins(0, 0, 0, 0);
|
||||
setLayout(new QVBoxLayout);
|
||||
layout()->setAlignment(Qt::AlignTop);
|
||||
layout()->addWidget(new QLabel("Shader: " + QString{mat->info.name.c_str()}));
|
||||
layout()->addWidget(new QLabel("Material: " + QString{mat->name.c_str()}));
|
||||
|
||||
// 添加 CBUFFER PerMat 的展示面板
|
||||
for (SHADER::CbufferInfo &cb: mat->info.cbuffer) {
|
||||
if (cb.name == "CBPerMat") {
|
||||
CBufferPanel *cbpanel = new CBufferPanel{&cb, mat};
|
||||
layout()->addWidget(cbpanel);
|
||||
}
|
||||
}
|
||||
|
||||
// 添加贴图框框
|
||||
for (SHADER::TextureInfo &texb: mat->info.textures) {
|
||||
std::cout << "texture的名字:" << texb.name << std::endl;
|
||||
WgtTexture *texwgt = new WgtTexture(mat, texb.index);
|
||||
layout()->addWidget(texwgt);
|
||||
}
|
||||
}
|
||||
|
||||
MAT::MatInstance *m_material{};
|
||||
};
|
||||
|
||||
//endregion
|
||||
|
||||
|
||||
//region Shader 展示面板
|
||||
|
||||
std::string PropTypeToStrhelper(Z::ShaderInfo::PropType type) {
|
||||
switch (type) {
|
||||
case Z::ShaderInfo::PropType::Matrix3x3: return {"Matrix3x3"};
|
||||
case Z::ShaderInfo::PropType::Matrix4x4: return {"Matrix4x4"};
|
||||
case Z::ShaderInfo::PropType::Float: return {"Float"};
|
||||
case Z::ShaderInfo::PropType::Float2: return {"Float2"};
|
||||
case Z::ShaderInfo::PropType::Float3: return {"Float3"};
|
||||
case Z::ShaderInfo::PropType::Float4: return {"Float4"};
|
||||
case Z::ShaderInfo::PropType::Int: return {"Int"};
|
||||
case Z::ShaderInfo::PropType::Int2: return {"Int2"};
|
||||
case Z::ShaderInfo::PropType::Int3: return {"Int3"};
|
||||
case Z::ShaderInfo::PropType::Int4: return {"Int4"};
|
||||
default: return {"None"};
|
||||
}
|
||||
}
|
||||
class DisplayLR : public QWidget {
|
||||
public:
|
||||
explicit DisplayLR(const std::string &left, const std::string &right, int leftMaxPx = 120) {
|
||||
QHBoxLayout *lay = new QHBoxLayout{this};
|
||||
lay->setContentsMargins(10, 0, 10, 0);
|
||||
this->setLayout(lay);
|
||||
QLabel *l = new QLabel{QString::fromStdString(left)};
|
||||
l->setMaximumWidth(leftMaxPx);
|
||||
lay->addWidget(l);
|
||||
lay->addWidget(new QLabel{QString::fromStdString(right)});
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
}; // class DisplayLR
|
||||
|
||||
class ShaderPanel : public QWidget {
|
||||
public:
|
||||
explicit ShaderPanel(const SHADER::ShaderInfo *shader) : m_shader(shader) {
|
||||
// 读取shader desc,显示在面板上
|
||||
QVBoxLayout *lay = new QVBoxLayout{this};
|
||||
this->setLayout(lay);
|
||||
lay->setContentsMargins(0, 0, 0, 0);
|
||||
lay->setSpacing(0);
|
||||
lay->setAlignment(Qt::AlignTop);
|
||||
|
||||
// name
|
||||
lay->addWidget(new QLabel{QString{"ShaderName:"} + QString::fromStdString(shader->name)});
|
||||
|
||||
// cbuffers
|
||||
lay->addSpacing(10);
|
||||
for (const SHADER::CbufferInfo &item: shader->cbuffer) {
|
||||
lay->addWidget(new QLabel{QString{"CBuffer Name:"} + QString::fromStdString(item.name)});
|
||||
for (const SHADER::PropertyInfo &prop: item.props) {
|
||||
auto *wgt = new DisplayLR(prop.name, PropTypeToStrhelper(prop.type), 150);
|
||||
lay->addWidget(wgt);
|
||||
}
|
||||
}
|
||||
|
||||
// texture buffer
|
||||
lay->addSpacing(10);
|
||||
if (shader->textures.size() > 0) {
|
||||
lay->addWidget(new QLabel{QString{"Textures"}});
|
||||
}
|
||||
for (const SHADER::TextureInfo &item: shader->textures) {
|
||||
lay->addWidget(new QLabel{QString::fromStdString(item.name)});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
const SHADER::ShaderInfo *const m_shader;
|
||||
|
||||
}; // class ShaderPanel
|
||||
|
||||
//endregion Shader 展示面板
|
||||
|
||||
|
||||
}
|
||||
|
||||
//-blue class WndInspector
|
||||
//= public
|
||||
WndInspector::WndInspector(QWidget *parent) : QWidget(parent) {
|
||||
setAttribute(Qt::WA_StyledBackground, true);
|
||||
_inspector = this;
|
||||
m_Connection = std::move(EVENT::OnSelectItem.Subscribe(&DisplayItem_));
|
||||
this->setLayout(new QVBoxLayout);
|
||||
m_scroll = new QScrollArea;
|
||||
m_scroll->setLayout(new QVBoxLayout);
|
||||
this->layout()->addWidget(m_scroll);
|
||||
m_scroll->layout()->setAlignment(Qt::AlignTop);
|
||||
}
|
||||
void WndInspector::DisplayItem(EVENT::InspecItem &item) {
|
||||
std::cout << "WndInspector::DisplayItem Item.Type = " << item.type << std::endl;
|
||||
m_Item = item;
|
||||
|
||||
// 删除旧面板
|
||||
if (m_content) {
|
||||
m_content->deleteLater();
|
||||
m_content = nullptr;
|
||||
}
|
||||
|
||||
// 创建新面板 赋值给m_content
|
||||
switch (item.type) {
|
||||
case SelectableItem::RenderObj: {
|
||||
RENDEROBJ::Object *obj{};
|
||||
std::memcpy(&obj, &item.resource, 8);
|
||||
m_content = new RenderObjPanel{obj};
|
||||
|
||||
break;
|
||||
}
|
||||
case SelectableItem::Model: {
|
||||
break;
|
||||
}
|
||||
case SelectableItem::Material: {
|
||||
MAT::MatInstance *mat{};
|
||||
std::memcpy(&mat, &item.resource, 8);
|
||||
m_content = new MaterialPanel{mat};
|
||||
break;
|
||||
}
|
||||
case SelectableItem::Texture: {
|
||||
break;
|
||||
}
|
||||
case SelectableItem::Shader: {
|
||||
SHADER::ShaderInfo *shader{};
|
||||
std::memcpy(&shader, &item.resource, 8);
|
||||
m_content = new ShaderPanel{shader};
|
||||
break;
|
||||
}
|
||||
default: { break; }
|
||||
}
|
||||
|
||||
// 将新的panel添加进显示区域
|
||||
if (!m_content) {
|
||||
// QMessageBox::about(this, "WndInspector::DisplayItem", "未初始化m_content");
|
||||
return;
|
||||
}
|
||||
m_scroll->layout()->addWidget(m_content);
|
||||
m_scroll->setWidget(m_content);
|
||||
m_scroll->setWidgetResizable(true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//= private
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
@@ -0,0 +1,53 @@
|
||||
#ifndef RENDER_WND_INSPECTOR_H
|
||||
#define RENDER_WND_INSPECTOR_H
|
||||
#include <QWidget>
|
||||
|
||||
#include "states.h"
|
||||
|
||||
|
||||
class QScrollArea;
|
||||
class QStackedLayout;
|
||||
class WndInspector : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
struct Item {
|
||||
std::string path;
|
||||
void *resource{};
|
||||
};
|
||||
|
||||
public:
|
||||
explicit WndInspector(QWidget *parent);
|
||||
void DisplayItem(EVENT::InspecItem &item);
|
||||
|
||||
private:
|
||||
|
||||
private:
|
||||
// struct InspecItem
|
||||
// InsItemType type{};
|
||||
// std::string path;
|
||||
// void *resource{};
|
||||
EVENT::InspecItem m_Item{};
|
||||
|
||||
QScrollArea *m_scroll{};
|
||||
QWidget *m_content{};
|
||||
Z::Event<EVENT::InspecItem>::Connection m_Connection{};
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif //RENDER_WND_INSPECTOR_H
|
||||
@@ -0,0 +1,161 @@
|
||||
#include "wnd_main.h"
|
||||
#include <QPushButton>
|
||||
#include <iostream>
|
||||
#include <render/render.h>
|
||||
#include <thread>
|
||||
#include <QCloseEvent>
|
||||
#include <QFileDialog>
|
||||
#include <mutex>
|
||||
#include <QWidget>
|
||||
#include <QLayout>
|
||||
#include "states.h"
|
||||
#include <QMenuBar>
|
||||
#include <QMenu>
|
||||
#include <QAction>
|
||||
#include <QSplitter>
|
||||
|
||||
namespace {
|
||||
//region 渲染窗口
|
||||
std::atomic<bool> b_RenderWndPrepared{false};
|
||||
std::thread tr_render;
|
||||
Render *g_render;
|
||||
std::atomic<HWND> hwnd_RenderWnd{nullptr};
|
||||
DWORD renderWindowRet = 0;
|
||||
|
||||
void RenderWindow() {
|
||||
g_render = new Render{};
|
||||
hwnd_RenderWnd.store(g_render->InitWindow(GetModuleHandle(nullptr), L"DirectX"),
|
||||
std::memory_order_release);
|
||||
b_RenderWndPrepared.store(true, std::memory_order::release);
|
||||
if (hwnd_RenderWnd == nullptr) return;
|
||||
renderWindowRet = g_render->ExecWindow();
|
||||
hwnd_RenderWnd.store(nullptr, std::memory_order_release);
|
||||
b_RenderWndPrepared.store(false, std::memory_order_release);
|
||||
delete g_render;
|
||||
|
||||
}
|
||||
//endregion
|
||||
|
||||
|
||||
QString GetMainWindowStyleSheet() {
|
||||
return {"#MainCentralWidget{background-color:#223344;}"
|
||||
"#MainCentralLeftWidget{background-color:#333322;}"
|
||||
"#MainCentralRightWidget{background-color:#332233;}"
|
||||
".WndResource{background-color:#222233;}"
|
||||
".WndHierachy{background-color:#112233;}"
|
||||
".WndInspector{background-color:#221144;}"
|
||||
".WndDebug{background-color:#442211;}"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
//= public
|
||||
WndMain::WndMain() {
|
||||
|
||||
InitRenderWindow();
|
||||
InitDesign();
|
||||
this->setWindowState(Qt::WindowActive);
|
||||
this->show();
|
||||
}
|
||||
WndMain::~WndMain() {
|
||||
if (tr_render.joinable())tr_render.join();
|
||||
qDebug("Render Thread Returns:%d", renderWindowRet);
|
||||
}
|
||||
|
||||
//= protected
|
||||
void WndMain::closeEvent(QCloseEvent *event) {
|
||||
QMainWindow::closeEvent(event);
|
||||
PostMessageW(hwnd_RenderWnd.load(std::memory_order_acquire),WM_CLOSE, 0, 0);
|
||||
while (hwnd_RenderWnd.load(std::memory_order::acquire)) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
}
|
||||
if (tr_render.joinable())tr_render.join();
|
||||
event->accept();
|
||||
}
|
||||
|
||||
//= private
|
||||
void WndMain::InitRenderWindow() const {
|
||||
tr_render = std::thread(RenderWindow);
|
||||
while (!b_RenderWndPrepared.load(std::memory_order::acquire)) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
}
|
||||
}
|
||||
|
||||
void WndMain::InitDesign() {
|
||||
const int w = 720;
|
||||
const int h = 820;
|
||||
this->resize(w, h);
|
||||
this->move(w, States::DesktopHeight() - h - 80);
|
||||
this->setStyleSheet(GetMainWindowStyleSheet());
|
||||
|
||||
// menu bar
|
||||
QMenuBar *menubar = menuBar();
|
||||
QMenu *menu_file = menubar->addMenu("File");
|
||||
QAction *act_test = menu_file->addAction("test");
|
||||
|
||||
// central widget
|
||||
m_central = new QWidget(this);
|
||||
m_central->setObjectName("MainCentralWidget");
|
||||
setCentralWidget(m_central);
|
||||
|
||||
// central widget layout
|
||||
QHBoxLayout *lay = new QHBoxLayout();
|
||||
m_central->setLayout(lay);
|
||||
|
||||
// main splitter
|
||||
QSplitter *sp = new QSplitter(m_central);
|
||||
sp->setOrientation(Qt::Orientation::Horizontal);
|
||||
lay->addWidget(sp);
|
||||
sp->setChildrenCollapsible(false);
|
||||
lay->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
// left area
|
||||
QWidget *leftWgt = new QWidget(this);
|
||||
leftWgt->setObjectName("MainCentralLeftWidget");
|
||||
leftWgt->setLayout(new QVBoxLayout());
|
||||
QSplitter *spl = new QSplitter(leftWgt);
|
||||
spl->setChildrenCollapsible(false);
|
||||
spl->setOrientation(Qt::Vertical);
|
||||
m_WndHierachy = new WndHierachy(this);
|
||||
spl->addWidget(m_WndHierachy);
|
||||
m_WndResource = new WndResource(this);
|
||||
spl->addWidget(m_WndResource);
|
||||
leftWgt->layout()->addWidget(spl);
|
||||
|
||||
spl->setSizes({500, 500});
|
||||
|
||||
// right area
|
||||
QWidget *rightWgt = new QWidget(this);
|
||||
rightWgt->setObjectName("MainCentralRightWidget");
|
||||
rightWgt->setLayout(new QVBoxLayout());
|
||||
QSplitter *spr = new QSplitter(rightWgt);
|
||||
spr->setChildrenCollapsible(false);
|
||||
spr->setOrientation(Qt::Orientation::Vertical);
|
||||
m_WndInspector = new WndInspector(this);
|
||||
spr->addWidget(m_WndInspector);
|
||||
m_WndDebug = new WndDebug(this);
|
||||
spr->addWidget(m_WndDebug);
|
||||
rightWgt->layout()->addWidget(spr);
|
||||
|
||||
spr->setSizes({500, 500});
|
||||
|
||||
sp->addWidget(leftWgt);
|
||||
sp->addWidget(rightWgt);
|
||||
sp->setMinimumWidth(100);
|
||||
sp->setSizes({500, 500});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
@@ -0,0 +1,47 @@
|
||||
#ifndef RENDER_WND_MAIN_H
|
||||
#define RENDER_WND_MAIN_H
|
||||
#include <QMainWindow>
|
||||
#include "wnd_resource.h"
|
||||
#include "wnd_hierachy.h"
|
||||
#include "wnd_inspector.h"
|
||||
#include "wnd_debug.h"
|
||||
|
||||
|
||||
class QBoxLayout;
|
||||
class WndMain : QMainWindow {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
WndMain();
|
||||
~WndMain() override;
|
||||
|
||||
protected:
|
||||
void closeEvent(QCloseEvent *event) override;
|
||||
|
||||
private:
|
||||
void InitRenderWindow() const;
|
||||
void InitDesign();
|
||||
|
||||
private:
|
||||
QWidget *m_central{};
|
||||
WndResource *m_WndResource{};
|
||||
WndHierachy *m_WndHierachy{};
|
||||
WndDebug *m_WndDebug{};
|
||||
WndInspector *m_WndInspector{};
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif //RENDER_WND_MAIN_H
|
||||
@@ -0,0 +1,416 @@
|
||||
#include "wnd_resource.h"
|
||||
#include <QStackedWidget>
|
||||
#include <QVBoxLayout>
|
||||
#include <QPushButton>
|
||||
#include <QButtonGroup>
|
||||
#include <QScrollArea>
|
||||
#include <QLabel>
|
||||
#include <filesystem>
|
||||
#include <QFileDialog>
|
||||
|
||||
#include "wgt_listitem.h"
|
||||
#include "resource.h"
|
||||
#include <zassimp/importer.h>
|
||||
|
||||
#include "states.h"
|
||||
#include <QMessageBox>
|
||||
#include "render/task.h"
|
||||
#include <QStandardPaths>
|
||||
#include <QPointer>
|
||||
#include <mutex>
|
||||
#include "wnd_create_mat.h"
|
||||
#include <iostream>
|
||||
#include "wnd_add_tex.h"
|
||||
|
||||
namespace {
|
||||
// callback wait
|
||||
std::mutex mtx_cb{};
|
||||
std::condition_variable cv_cb{};
|
||||
Z::ResultMessage g_CallbackMessage{};
|
||||
void CallBack_WakeThreadUp(Z::ResultMessage msg) { // 被Renderer回调
|
||||
g_CallbackMessage = msg;
|
||||
cv_cb.notify_all();
|
||||
}
|
||||
void WaitForResult() {
|
||||
std::unique_lock lk(mtx_cb);
|
||||
cv_cb.wait(lk);
|
||||
}
|
||||
|
||||
// open file window
|
||||
QString lastFiledir;
|
||||
std::filesystem::path OpenQFileSelector(const QString &caption,
|
||||
const QString *suffixs = nullptr) {
|
||||
QString ret;
|
||||
QString dir = lastFiledir.isEmpty() ? QStandardPaths::writableLocation(QStandardPaths::DesktopLocation) : lastFiledir;
|
||||
if (suffixs) {
|
||||
ret = QFileDialog::getOpenFileName(nullptr, caption, dir, *suffixs);
|
||||
} else {
|
||||
ret = QFileDialog::getOpenFileName(nullptr, caption, dir);
|
||||
}
|
||||
|
||||
std::filesystem::path path{ret.toStdU16String()};
|
||||
lastFiledir = ret;
|
||||
return path;
|
||||
}
|
||||
|
||||
SelectableItem EventType(WndResource::Mode mode) {
|
||||
switch (mode) {
|
||||
case WndResource::Mode::Model:
|
||||
return SelectableItem::Model;
|
||||
|
||||
case WndResource::Mode::Material:
|
||||
return SelectableItem::Material;
|
||||
|
||||
case WndResource::Mode::Texture:
|
||||
return SelectableItem::Texture;
|
||||
|
||||
case WndResource::Mode::Shader:
|
||||
return SelectableItem::Shader;
|
||||
|
||||
default: return SelectableItem::None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-purple
|
||||
//= public
|
||||
WndResource::WndResource(QWidget *parent) : QWidget(parent) {
|
||||
setAttribute(Qt::WA_StyledBackground, true);
|
||||
setLayout(new QVBoxLayout(this));
|
||||
layout()->setSpacing(0);
|
||||
setMinimumWidth(100);
|
||||
|
||||
|
||||
// btn list
|
||||
QWidget *btnGrp = new QWidget(this);
|
||||
btnGrp->setFixedHeight(40);
|
||||
btnGrp->setLayout(new QHBoxLayout(btnGrp));
|
||||
btnGrp->layout()->setContentsMargins(0, 0, 0, 0);
|
||||
btnGrp->layout()->setSpacing(0);
|
||||
|
||||
QPushButton *btn_model = new QPushButton("Model", btnGrp);
|
||||
btn_model->setCheckable(true);
|
||||
connect(btn_model, &QPushButton::clicked,
|
||||
this, [this]() { SelectMode(Mode::Model); });
|
||||
QPushButton *btn_material = new QPushButton("Material", btnGrp);
|
||||
btn_material->setCheckable(true);
|
||||
connect(btn_material, &QPushButton::clicked,
|
||||
this, [this]() { SelectMode(Mode::Material); });
|
||||
QPushButton *btn_texture = new QPushButton("Texture", btnGrp);
|
||||
btn_texture->setCheckable(true);
|
||||
connect(btn_texture, &QPushButton::clicked,
|
||||
this, [this]() { SelectMode(Mode::Texture); });
|
||||
QPushButton *btn_shader = new QPushButton("Shader", btnGrp);
|
||||
btn_shader->setCheckable(true);
|
||||
connect(btn_shader, &QPushButton::clicked,
|
||||
this, [this]() { SelectMode(Mode::Shader); });
|
||||
|
||||
QButtonGroup *grp = new QButtonGroup(this);
|
||||
grp->addButton(btn_model);
|
||||
grp->addButton(btn_material);
|
||||
grp->addButton(btn_texture);
|
||||
grp->addButton(btn_shader);
|
||||
|
||||
btnGrp->layout()->addWidget(btn_model);
|
||||
btnGrp->layout()->addWidget(btn_material);
|
||||
btnGrp->layout()->addWidget(btn_texture);
|
||||
btnGrp->layout()->addWidget(btn_shader);
|
||||
|
||||
|
||||
// statcked widget
|
||||
m_stack = new QStackedWidget(this);
|
||||
|
||||
// load button
|
||||
QPushButton *btn_load = new QPushButton("Load Resource", this);
|
||||
btn_load->setFixedHeight(45);
|
||||
connect(btn_load, &QPushButton::clicked, this, [this]() {
|
||||
OnBtnLoadResource();
|
||||
});
|
||||
|
||||
layout()->addWidget(btnGrp);
|
||||
layout()->addWidget(m_stack);
|
||||
dynamic_cast<QVBoxLayout *>(layout())->addSpacing(8);
|
||||
layout()->addWidget(btn_load);
|
||||
btn_model->click();
|
||||
}
|
||||
|
||||
//= private
|
||||
void WndResource::SelectMode(Mode mode) {
|
||||
if (m_Mode == mode) return;
|
||||
m_Mode = mode;
|
||||
switch (mode) {
|
||||
case Mode::Model: {
|
||||
ShowModel();
|
||||
break;
|
||||
}
|
||||
case Mode::Material: {
|
||||
ShowMat();
|
||||
break;
|
||||
}
|
||||
case Mode::Texture: {
|
||||
ShowTex();
|
||||
break;
|
||||
}
|
||||
case Mode::Shader: {
|
||||
ShowShader();
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
void WndResource::ShowModel() {
|
||||
UnselectAllItem();
|
||||
if (m_model) {
|
||||
m_stack->removeWidget(m_model);
|
||||
m_model->deleteLater();
|
||||
}
|
||||
m_model = new QScrollArea{this};
|
||||
InitPanel(m_model, m_lstModel, "application/x-model");
|
||||
m_stack->addWidget(m_model);
|
||||
m_stack->setCurrentWidget(m_model);
|
||||
}
|
||||
void WndResource::ShowMat() {
|
||||
UnselectAllItem();
|
||||
if (m_material) {
|
||||
m_stack->removeWidget(m_material);
|
||||
m_material->deleteLater();
|
||||
}
|
||||
m_material = new QScrollArea{this};
|
||||
InitPanel(m_material, m_lstMaterial, "application/x-material");
|
||||
m_stack->addWidget(m_material);
|
||||
m_stack->setCurrentWidget(m_material);
|
||||
}
|
||||
void WndResource::ShowTex() {
|
||||
UnselectAllItem();
|
||||
if (m_texture) {
|
||||
m_stack->removeWidget(m_texture);
|
||||
m_texture->deleteLater();
|
||||
}
|
||||
m_texture = new QScrollArea{this};
|
||||
InitPanel(m_texture, m_lstTexture, "application/x-texture");
|
||||
m_stack->addWidget(m_texture);
|
||||
m_stack->setCurrentWidget(m_texture);
|
||||
}
|
||||
void WndResource::ShowShader() {
|
||||
UnselectAllItem();
|
||||
if (m_shader) {
|
||||
m_stack->removeWidget(m_shader);
|
||||
m_shader->deleteLater();
|
||||
}
|
||||
m_shader = new QScrollArea{this};
|
||||
InitPanel(m_shader, m_lstShader, "application/x-shader");
|
||||
m_stack->addWidget(m_shader);
|
||||
m_stack->setCurrentWidget(m_shader);
|
||||
}
|
||||
|
||||
void WndResource::UnselectAllItem() {
|
||||
if (m_selectedItem) m_selectedItem->SetSelected(false);
|
||||
m_selectedItem = nullptr;
|
||||
}
|
||||
|
||||
void WndResource::OnBtnLoadResource() {
|
||||
switch (m_Mode) {
|
||||
case Mode::Model: {
|
||||
QString sfx = "*.fbx";
|
||||
auto path = OpenQFileSelector("Open FBX File", &sfx);
|
||||
if (path.empty()) return;
|
||||
//开始导入模型
|
||||
Z::Model *model{};
|
||||
if (!ModelImporter::LoadFromFile(path, &model)) return;
|
||||
//导入成功,加入资源,渲染
|
||||
std::u8string u8path{};
|
||||
u8path.reserve(model->name.size());
|
||||
for (unsigned char ch: model->name) {
|
||||
u8path.push_back(static_cast<char8_t>(ch));
|
||||
}
|
||||
m_lstModel.push_back({u8path});
|
||||
ShowModel();
|
||||
|
||||
break;
|
||||
}
|
||||
case Mode::Material: {
|
||||
std::string res_shader{};
|
||||
std::string res_matName{};
|
||||
std::vector<std::string> inputs{};
|
||||
inputs.reserve(m_lstShader.size());
|
||||
for (auto &item: m_lstShader) {
|
||||
std::u8string u8name = item.path.filename().u8string();
|
||||
inputs.emplace_back(reinterpret_cast<const char *>(u8name.c_str()), u8name.size());
|
||||
}
|
||||
WndCreateMat dia{this, inputs, res_shader, res_matName};
|
||||
dia.exec();
|
||||
|
||||
if (res_matName.empty() || res_shader.empty()) {
|
||||
break;
|
||||
}
|
||||
if (res_matName == "Default") {
|
||||
QMessageBox::about(this, "错误", "Material Name can't be \"Default\"");
|
||||
break;
|
||||
}
|
||||
std::cout << "\033[33m" << "WndResource::OnBtnLoadResource::创建Material\n" <<
|
||||
"输入的matname=" << res_matName << " 选中的shadername=" << res_shader << "\033[0m" << std::endl;
|
||||
SHADER::ShaderInfo *shader = LoadedResource::GetLoadedShader(res_shader);
|
||||
if (!shader) {
|
||||
QMessageBox::about(this, "错误", "Shader未载入");
|
||||
break;
|
||||
}
|
||||
if (LoadedResource::GetLoadedMaterial(res_matName)) {
|
||||
QMessageBox::about(this, "错误", "Material Name重复\n未添加");
|
||||
break;
|
||||
}
|
||||
|
||||
// 让 RENDERER 创建材质
|
||||
Z::Task task{};
|
||||
task.type = Z::CmdType::CreateMaterial;
|
||||
task.cmd.create_material.fileName = res_shader.c_str();
|
||||
task.cmd.create_material.matName = res_matName.c_str();
|
||||
task.fpCallBack = CallBack_WakeThreadUp;
|
||||
|
||||
Z::Task::AddCommand(task);
|
||||
WaitForResult();
|
||||
if (g_CallbackMessage.res != Z::ResultType::OK) {
|
||||
QMessageBox::about(this, "错误", "创建material失败");
|
||||
break;
|
||||
}
|
||||
// APP UI 渲染MAT
|
||||
MAT::MatInstance *_mat = MatCreater::CreateMat(res_matName, shader);
|
||||
std::u8string u8matname{reinterpret_cast<const char8_t *>(res_matName.c_str())};
|
||||
m_lstMaterial.push_back({u8matname, _mat});
|
||||
ShowMat();
|
||||
|
||||
break;
|
||||
}
|
||||
case Mode::Texture: {
|
||||
QString suffix{"*.jpg *.png"};
|
||||
auto path = OpenQFileSelector("Select Image File", &suffix);
|
||||
if (path.empty()) break;
|
||||
|
||||
if (LoadedResource::GetLoadedTexture(path) != nullptr) {
|
||||
QMessageBox::about(this, "WndResource", "已经加载此贴图\n");
|
||||
break;
|
||||
}
|
||||
|
||||
// 根据Ret的配置指派图像加载路径
|
||||
WndAddTex::ReturnData ret{};
|
||||
WndAddTex waddt{ret};
|
||||
waddt.exec();
|
||||
std::cout << "Ret.srgb = " << std::boolalpha << ret.SRGB <<
|
||||
"\nRet.ReverseY = " << std::boolalpha << ret.reverse_Y <<
|
||||
"\nRet.bpp16 = " << std::boolalpha << ret.bpp_16 <<
|
||||
"\nRet.Type = " << static_cast<int>(ret.textureType) <<
|
||||
std::endl;
|
||||
if (!ret.valid) { break; }
|
||||
|
||||
Z::TextureData *tex = ImgImporter::ImportFromFile(path, ret.reverse_Y, ret.textureType);
|
||||
if (tex == nullptr) {
|
||||
QMessageBox::about(this, "WndResource", "加载本地图片失败");
|
||||
break;
|
||||
}
|
||||
|
||||
// 给GPU端发送信息,初始化该TEXTURE的资源
|
||||
Z::Task t{};
|
||||
t.type = Z::CmdType::AddTextureResource;
|
||||
t.cmd.add_texture_resource.texName = tex->name.c_str();
|
||||
t.cmd.add_texture_resource.type = tex->type;
|
||||
t.cmd.add_texture_resource.dataptr = tex->data.data();
|
||||
t.cmd.add_texture_resource.bytesPerPixel = tex->bytesPerPixel;
|
||||
t.cmd.add_texture_resource.rowPitch = tex->rowPitch;
|
||||
t.cmd.add_texture_resource.size = tex->size;
|
||||
t.cmd.add_texture_resource.bpp16 = ret.bpp_16;
|
||||
t.cmd.add_texture_resource.SRGB = ret.SRGB;
|
||||
t.fpCallBack = CallBack_WakeThreadUp;
|
||||
Z::Task::AddCommand(t);
|
||||
WaitForResult();
|
||||
if (g_CallbackMessage.res != Z::ResultType::OK) {
|
||||
delete tex;
|
||||
QMessageBox::about(this, "WndResource", "创建 GPU Tex 资源失败");
|
||||
break;
|
||||
}
|
||||
// 渲染UI
|
||||
m_lstTexture.emplace_back(path, nullptr);
|
||||
ShowTex();
|
||||
|
||||
break;
|
||||
}
|
||||
case Mode::Shader: {
|
||||
QString suffix{"*.hlsl"};
|
||||
auto path = OpenQFileSelector("Select .hlsl Shader File", &suffix);
|
||||
if (path.empty()) return;
|
||||
|
||||
std::filesystem::path fileName = path.filename();
|
||||
std::u8string u8name = fileName.u8string();
|
||||
std::string name{reinterpret_cast<const char *>(u8name.c_str()), u8name.size()};
|
||||
|
||||
if (LoadedResource::GetLoadedShader(name)) {
|
||||
QMessageBox::about(this, "WndResource OnBtnLoadResource", "重复的shader名");
|
||||
break;
|
||||
}
|
||||
SHADER::ShaderInfo *shaderInfo = ShaderImporter::ImportFromFile(path);
|
||||
if (shaderInfo) {
|
||||
m_lstShader.push_back({fileName, shaderInfo});
|
||||
ShowShader();
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
void WndResource::InitPanel(QScrollArea *scroll, const std::list<Res> &list, const char *type) {
|
||||
QWidget *content = new QWidget{scroll};
|
||||
QVBoxLayout *layout = new QVBoxLayout{content};
|
||||
content->layout()->setContentsMargins(0, 0, 0, 0);
|
||||
content->layout()->setSpacing(0);
|
||||
|
||||
auto *itemGroup = SelectedItemWidgetGroup::GetGroup("inspector");
|
||||
// 根据具体资源list渲染列表
|
||||
for (auto &item: list) {
|
||||
auto *label = new QLabel{QString::fromStdWString(item.path.stem().wstring())};
|
||||
auto *itm = new DragItemWidget{label, content, itemGroup};
|
||||
|
||||
std::u8string u8mime{item.path.u8string()};
|
||||
std::string mime = {reinterpret_cast<const char *>(u8mime.c_str()), u8mime.size()};
|
||||
itm->SetMime(type, mime.c_str());
|
||||
|
||||
connect(itm, &ListItemWidget::Clicked, itm, [this,&item](ListItemWidget *i) {
|
||||
if (this->m_selectedItem != nullptr) this->m_selectedItem->SetSelected(false);
|
||||
this->m_selectedItem = i;
|
||||
i->SetSelected(true);
|
||||
EVENT::InspecItem ev{};
|
||||
ev.type = EventType(m_Mode);
|
||||
ev.path = std::string{reinterpret_cast<const char *>(item.path.u8string().c_str())};
|
||||
ev.resource = item.data;
|
||||
EVENT::OnSelectItem.EmitEvent(std::move(ev));
|
||||
|
||||
});
|
||||
|
||||
content->layout()->addWidget(itm);
|
||||
}
|
||||
|
||||
layout->addStretch();
|
||||
|
||||
scroll->setWidget(content);
|
||||
scroll->setWidgetResizable(true);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
@@ -0,0 +1,70 @@
|
||||
#ifndef RENDER_WND_RESOURCE_H
|
||||
#define RENDER_WND_RESOURCE_H
|
||||
#include <QWidget>
|
||||
#include <list>
|
||||
#include <filesystem>
|
||||
|
||||
#include "states.h"
|
||||
|
||||
|
||||
class QLabel;
|
||||
class QScrollArea;
|
||||
class ListItemWidget;
|
||||
class QStackedWidget;
|
||||
class WndResource : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum struct Mode {
|
||||
None = 0, Model = 1, Material = 2, Texture = 3, Shader = 4
|
||||
};
|
||||
struct Res {
|
||||
std::filesystem::path path;
|
||||
void *data;
|
||||
};
|
||||
explicit WndResource(QWidget *parent);
|
||||
|
||||
private:
|
||||
|
||||
void SelectMode(Mode mode);
|
||||
|
||||
void ShowModel();
|
||||
void ShowMat();
|
||||
void ShowTex();
|
||||
void ShowShader();
|
||||
|
||||
void UnselectAllItem();
|
||||
void OnBtnLoadResource();
|
||||
void InitPanel(QScrollArea *scroll, const std::list<Res> &list, const char *type);
|
||||
|
||||
private:
|
||||
Mode m_Mode{Mode::None};
|
||||
|
||||
QStackedWidget *m_stack{};
|
||||
|
||||
QScrollArea *m_model{};
|
||||
QScrollArea *m_material{};
|
||||
QScrollArea *m_texture{};
|
||||
QScrollArea *m_shader{};
|
||||
std::list<Res> m_lstModel{};
|
||||
std::list<Res> m_lstMaterial;
|
||||
std::list<Res> m_lstTexture{};
|
||||
std::list<Res> m_lstShader{};
|
||||
|
||||
|
||||
ListItemWidget *m_selectedItem{};
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif //RENDER_WND_RESOURCE_H
|
||||
Reference in New Issue
Block a user