Init commit

This commit is contained in:
2026-08-17 06:31:43 +09:00
commit 348d9275f6
428 changed files with 61806 additions and 0 deletions
+103
View File
@@ -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 ()
+62
View File
@@ -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;
}
//
+538
View File
@@ -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
//
+107
View File
@@ -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
+69
View File
@@ -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;
}
}
//
+82
View File
@@ -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
+129
View File
@@ -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
//
+74
View File
@@ -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
+129
View File
@@ -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);
}
}
//
+31
View File
@@ -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
+188
View File
@@ -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;
}
}
//
+31
View File
@@ -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
+70
View File
@@ -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();
}
}
//
+28
View File
@@ -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
+5
View File
@@ -0,0 +1,5 @@
#include "wnd_debug.h"
WndDebug::WndDebug(QWidget *parent) : QWidget(parent) {
setAttribute(Qt::WA_StyledBackground, true);
}
+18
View File
@@ -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
+146
View File
@@ -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);
}
//
+30
View File
@@ -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
+526
View File
@@ -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
//
+53
View File
@@ -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
+161
View File
@@ -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});
}
//
+47
View File
@@ -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
+416
View File
@@ -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);
}
//
+70
View File
@@ -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