5#define IMGUI_DEFINE_MATH_OPERATORS
12#include "ui/BaseBackend.h"
20constexpr float kAssetBodyHeight = 132.0f;
22inline float AssetCardHeight() {
23 const ImGuiStyle& style = ImGui::GetStyle();
24 return ImGui::GetTextLineHeightWithSpacing() + style.ItemSpacing.y * 3 + kAssetBodyHeight;
28inline void AssetHeader(
const std::string& name,
const std::string& type) {
29 ImGui::TextUnformatted(name.c_str());
30 const ImVec2 typeSize = ImGui::CalcTextSize(type.c_str());
31 ImGui::SameLine(ImGui::GetContentRegionAvail().x - typeSize.x);
32 ImGui::TextDisabled(
"%s", type.c_str());
38inline void BeginAssetBody() {
39 ImGui::BeginChild(
"##assetBody", ImVec2(0, kAssetBodyHeight),
false);
42inline void EndAssetBody() {
47inline void KV(
const char* label,
const std::string& value) {
48 ImGui::TextDisabled(
"%-16s", label);
50 ImGui::TextUnformatted(value.c_str());
62inline int ShadeSetupCount() {
66inline const char* ShadeSetupName(
int idx) {
67 static const char* kNames[] = {
"auto",
"textured",
"textured + lit",
"vertex color",
"vertex color + lit",
69 return kNames[idx >= 0 && idx < ShadeSetupCount() ? idx : 0];
75inline int ShadeSetupIndexByName(
const std::string& raw) {
84 s.push_back((
char)std::tolower((
unsigned char)c));
86 for (
int i = 0; i < ShadeSetupCount(); ++i) {
87 if (s == ShadeSetupName(i)) {
94inline ShadeSetup ShadeSetupFor(
int idx) {
96 case 1:
return { 1,
true,
false };
97 case 2:
return { 1,
false,
true };
98 case 3:
return { 0,
true,
false };
99 case 4:
return { 0,
false,
true };
100 case 5:
return { 2,
true,
false };
101 default:
return { 3,
false,
false };
106inline bool ShadeSetupCombo(
const char*
id,
int& idx) {
107 idx = std::clamp(idx, 0, ShadeSetupCount() - 1);
108 bool changed =
false;
109 ImGui::SetNextItemWidth(150.0f);
110 if (ImGui::BeginCombo(
id, ShadeSetupName(idx))) {
111 for (
int i = 0; i < ShadeSetupCount(); ++i) {
112 if (ImGui::Selectable(ShadeSetupName(i), i == idx)) {
123inline void LightingControls() {
124 if (ImGui::SmallButton(
"light")) {
125 ImGui::OpenPopup(
"##previewlight");
127 if (ImGui::BeginPopup(
"##previewlight")) {
129 ImGui::Checkbox(
"enabled", &light.enabled);
130 ImGui::ColorEdit3(
"ambient", light.ambient, ImGuiColorEditFlags_NoInputs);
131 ImGui::ColorEdit3(
"color", light.color, ImGuiColorEditFlags_NoInputs);
132 ImGui::SetNextItemWidth(200.0f);
133 ImGui::SliderFloat3(
"position", light.position, -4.0f, 4.0f,
"%.2f");
134 ImGui::SetNextItemWidth(200.0f);
135 ImGui::SliderFloat(
"intensity", &light.intensity, 0.0f, 3.0f,
"%.2f");
136 ImGui::SetNextItemWidth(200.0f);
137 ImGui::SliderFloat(
"falloff", &light.falloff, 0.0f, 2.0f,
"%.2f");
140 ImGui::Checkbox(
"fog", &atmo.fogEnabled);
141 ImGui::ColorEdit3(
"fog color", atmo.fogColor, ImGuiColorEditFlags_NoInputs);
142 ImGui::SetNextItemWidth(200.0f);
143 ImGui::DragIntRange2(
"fog range", &atmo.fogStart, &atmo.fogEnd, 2, 0, 1000);
144 if (atmo.fogEnd <= atmo.fogStart) {
145 atmo.fogEnd = atmo.fogStart + 1;
147 if (ImGui::SmallButton(
"reset##light")) {
157inline void OrbitControls(
OrbitView& view) {
158 ImGuiIO& io = ImGui::GetIO();
159 const bool hovered = ImGui::IsItemHovered();
161 const bool panning = (ImGui::IsItemActive() && io.KeyShift) ||
162 (hovered && ImGui::IsMouseDragging(ImGuiMouseButton_Middle));
164 view.panX += io.MouseDelta.x;
165 view.panY += io.MouseDelta.y;
166 }
else if (ImGui::IsItemActive()) {
167 view.yaw -= io.MouseDelta.x * 0.01f;
168 view.pitch = std::clamp(view.pitch + io.MouseDelta.y * 0.01f, -1.55f, 1.55f);
171 if (hovered && io.MouseWheel != 0.0f && (io.KeyCtrl || io.KeySuper)) {
172 view.zoom = std::clamp(view.zoom * (1.0f + io.MouseWheel * 0.1f), 0.02f, 50.0f);
173 io.MouseWheel = 0.0f;
175 if (hovered && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
182struct PreviewCanvas {
188inline PreviewCanvas BeginPreviewCanvas(
const char* strId,
float height,
OrbitView& view) {
189 const float availW = ImGui::GetContentRegionAvail().x;
190 const float vw = std::min(availW, height * 3.0f);
191 ImGui::SetCursorPosX(ImGui::GetCursorPosX() + (availW - vw) * 0.5f);
192 const ImVec2 origin = ImGui::GetCursorScreenPos();
193 ImGui::InvisibleButton(strId, ImVec2(vw, height));
194 const float winTop = ImGui::GetWindowPos().y;
195 const float winBot = winTop + ImGui::GetWindowHeight();
196 const bool visible = ImGui::GetItemRectMax().y > winTop && ImGui::GetItemRectMin().y < winBot;
199 ImGui::GetWindowDrawList()->AddRectFilled(origin, ImVec2(origin.x + vw, origin.y + height),
200 IM_COL32(18, 18, 22, 255));
202 return { origin, ImVec2(vw, height), visible };
210constexpr float kPreviewDefaultHeight = 320.0f;
211constexpr float kPreviewMinHeight = 140.0f;
212constexpr float kPreviewMaxHeight = 1400.0f;
213constexpr float kPreviewGripHeight = 11.0f;
215inline std::map<std::string, float>& PreviewHeights() {
216 static std::map<std::string, float> heights;
220inline float PreviewHeight(
const std::string& key) {
221 const auto it = PreviewHeights().find(key);
222 if (it != PreviewHeights().end()) {
225 if (
const char* forced = std::getenv(
"TORCH_UI_CANVASH")) {
226 const float h = (float)atof(forced);
227 if (h >= kPreviewMinHeight && h <= kPreviewMaxHeight) {
231 return kPreviewDefaultHeight;
235inline float PreviewBlockHeight(
const std::string& key) {
236 return PreviewHeight(key) + kPreviewGripHeight;
240inline PreviewCanvas BeginResizableCanvas(
const char* strId,
const std::string& key,
OrbitView& view) {
241 const float height = PreviewHeight(key);
242 const float availW = ImGui::GetContentRegionAvail().x;
244 const float vw = std::min(availW, kPreviewDefaultHeight * 3.0f);
245 ImGui::SetCursorPosX(ImGui::GetCursorPosX() + (availW - vw) * 0.5f);
246 const ImVec2 origin = ImGui::GetCursorScreenPos();
247 ImGui::InvisibleButton(strId, ImVec2(vw, height));
248 const float winTop = ImGui::GetWindowPos().y;
249 const float winBot = winTop + ImGui::GetWindowHeight();
250 const bool visible = ImGui::GetItemRectMax().y > winTop && ImGui::GetItemRectMin().y < winBot;
253 ImGui::GetWindowDrawList()->AddRectFilled(origin, ImVec2(origin.x + vw, origin.y + height),
254 IM_COL32(18, 18, 22, 255));
257 ImGui::SetCursorScreenPos(ImVec2(origin.x, origin.y + height));
258 ImGui::PushID(strId);
259 ImGui::InvisibleButton(
"##canvasgrip", ImVec2(vw, kPreviewGripHeight));
261 const bool hovered = ImGui::IsItemHovered();
262 const bool active = ImGui::IsItemActive();
263 if (hovered || active) {
264 ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeNS);
267 const float dy = ImGui::GetIO().MouseDelta.y;
269 PreviewHeights()[key] = std::clamp(height + dy, kPreviewMinHeight, kPreviewMaxHeight);
272 if (hovered && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
273 PreviewHeights().erase(key);
277 const ImVec2 mid(origin.x + vw * 0.5f, origin.y + height + kPreviewGripHeight * 0.5f);
278 const ImU32 col = active ? IM_COL32(160, 160, 170, 255)
279 : hovered ? IM_COL32(120, 120, 130, 255)
280 : IM_COL32(70, 70, 78, 255);
281 ImGui::GetWindowDrawList()->AddRectFilled(ImVec2(mid.x - 24.0f, mid.y - 1.5f),
282 ImVec2(mid.x + 24.0f, mid.y + 1.5f), col, 1.5f);
284 return { origin, ImVec2(vw, height), visible };
Definition BaseBackend.h:100
Definition BaseBackend.h:127
Definition BaseBackend.h:111