Panagiotis Christopoulos Charitos 2 mesiacov pred
rodič
commit
91f69dab34

+ 1 - 0
AnKi/Core/App.cpp

@@ -225,6 +225,7 @@ Error App::init()
 	nwinit.m_exclusiveFullscreen = g_cvarWindowFullscreen == 2;
 	nwinit.m_targetFps = g_cvarCoreTargetFps;
 	nwinit.m_borderless = g_cvarWindowBorderless;
+	nwinit.m_maximized = g_cvarWindowMaximized;
 	NativeWindow::allocateSingleton();
 	ANKI_CHECK(NativeWindow::getSingleton().init(nwinit));
 

+ 1 - 0
AnKi/Core/App.h

@@ -17,6 +17,7 @@ namespace anki {
 ANKI_CVAR(NumericCVar<U32>, Window, Width, 1920, 16, 16 * 1024, "Width")
 ANKI_CVAR(NumericCVar<U32>, Window, Height, 1080, 16, 16 * 1024, "Height")
 ANKI_CVAR(NumericCVar<U32>, Window, Fullscreen, 1, 0, 2, "0: windowed, 1: borderless fullscreen, 2: exclusive fullscreen")
+ANKI_CVAR(BoolCVar, Window, Maximized, false, "Maximize")
 ANKI_CVAR(BoolCVar, Window, Borderless, false, "Borderless")
 ANKI_CVAR(NumericCVar<U32>, Core, TargetFps, 60u, 1u, kMaxU32, "Target FPS")
 ANKI_CVAR(NumericCVar<U32>, Core, JobThreadCount, clamp(getCpuCoresCount() / 2u, 2u, 16u), 2u, 1024u, "Number of job thread")

+ 426 - 60
AnKi/Editor/EditorUi.cpp

@@ -9,6 +9,8 @@
 #include <AnKi/Resource/ImageResource.h>
 #include <AnKi/Script/ScriptManager.h>
 #include <AnKi/Window/Input.h>
+#include <AnKi/Util/Filesystem.h>
+#include <filesystem>
 #include <ThirdParty/ImGui/Extra/IconsMaterialDesignIcons.h> // See all icons in https://pictogrammers.com/library/mdi/
 
 namespace anki {
@@ -30,15 +32,26 @@ public:
 	}
 };
 
-#define ANKI_IMGUI_PUSH_POP(func, ...) \
+#define ANKI_PUSH_POP(func, ...) \
 	ImGui::Push##func(__VA_ARGS__); \
 	DeferredPop ANKI_CONCATENATE(pop, __LINE__)([] { \
 		ImGui::Pop##func(); \
 	})
 
+/// This pushes a width for text input widgets that leaves "labelSize" chars for the label
+#define ANKI_PUSH_POP_TEXT_INPUT_WIDTH(labelSize) \
+	const F32 labelWidthBase = ImGui::GetFontSize() * labelSize; /* Some amount of width for label, based on font size. */ \
+	const F32 labelWidthMax = ImGui::GetContentRegionAvail().x * 0.40f; /* ...but always leave some room for framed widgets. */ \
+	ANKI_PUSH_POP(ItemWidth, -min(labelWidthBase, labelWidthMax))
+
 EditorUi::EditorUi()
 {
 	Logger::getSingleton().addMessageHandler(this, loggerMessageHandler);
+
+	gatherAssets(m_assetsWindow.m_assetPaths);
+
+	ANKI_CHECKF(ResourceManager::getSingleton().loadResource("EngineAssets/Editor/Material.png", m_materialIcon));
+	ANKI_CHECKF(ResourceManager::getSingleton().loadResource("EngineAssets/Editor/Mesh.png", m_meshIcon));
 }
 
 EditorUi::~EditorUi()
@@ -46,6 +59,73 @@ EditorUi::~EditorUi()
 	Logger::getSingleton().removeMessageHandler(this, loggerMessageHandler);
 }
 
+void EditorUi::listDir(const std::filesystem::path& rootPath, const std::filesystem::path& parentPath, AssetPath& parent, U32& id)
+{
+	for(const auto& entry : std::filesystem::directory_iterator(parentPath))
+	{
+		if(entry.is_directory())
+		{
+			AssetPath& p = *parent.m_children.emplaceBack();
+			const std::filesystem::path rpath = std::filesystem::relative(entry, parentPath);
+			p.m_dirname = rpath.string().c_str();
+			p.m_id = id++;
+
+			listDir(rootPath, entry, p, id);
+		}
+		else if(entry.is_regular_file())
+		{
+			const String extension = entry.path().extension().string().c_str();
+			AssetFile file;
+			if(extension == ".ankitex")
+			{
+				file.m_type = AssetFileType::kTexture;
+			}
+			else if(extension == ".ankimtl")
+			{
+				file.m_type = AssetFileType::kMaterial;
+			}
+			else if(extension == ".ankimesh")
+			{
+				file.m_type = AssetFileType::kMesh;
+			}
+
+			if(file.m_type != AssetFileType::kNone)
+			{
+				String rpath = std::filesystem::relative(entry.path(), rootPath).string().c_str();
+				if(rpath.isEmpty())
+				{
+					// Sometimes it happens with paths that have links, ignore for now
+					continue;
+				}
+
+				rpath.replaceAll("\\", "/");
+
+				const String basefname = entry.path().filename().string().c_str();
+
+				file.m_basename = basefname;
+				file.m_filename = rpath;
+
+				parent.m_files.emplaceBack(file);
+			}
+		}
+	}
+};
+
+void EditorUi::gatherAssets(DynamicArray<AssetPath>& paths)
+{
+	U32 id = 0;
+	ResourceFilesystem::getSingleton().iterateAllResourceBasePaths([&](CString pathname) {
+		AssetPath& path = *paths.emplaceBack();
+		path.m_dirname = pathname;
+		path.m_id = id++;
+
+		std::filesystem::path stdpath(pathname.cstr());
+		listDir(stdpath, stdpath, path, id);
+
+		return FunctorContinue::kContinue;
+	});
+}
+
 void EditorUi::loggerMessageHandler(void* ud, const LoggerMessageInfo& info)
 {
 	EditorUi& self = *static_cast<EditorUi*>(ud);
@@ -88,16 +168,16 @@ void EditorUi::draw(UiCanvas& canvas)
 	ImGui::SetWindowPos(Vec2(0.0f, 0.0f));
 	ImGui::SetWindowSize(canvas.getSizef());
 
-	buildMainMenu();
+	mainMenu();
 
 	ImGui::DockSpaceOverViewport(0, ImGui::GetMainViewport(), ImGuiDockNodeFlags_PassthruCentralNode);
 
-	buildSceneHierarchyWindow();
-	buildSceneNodePropertiesWindow();
-	buildConsoleWindow();
-	buildAssetsWindow();
+	sceneHierarchyWindow();
+	sceneNodePropertiesWindow();
+	consoleWindow();
+	assetsWindow();
 
-	buildCVarsEditorWindow();
+	cVarsWindow();
 
 	ImGui::End();
 
@@ -107,7 +187,7 @@ void EditorUi::draw(UiCanvas& canvas)
 	m_canvas = nullptr;
 }
 
-void EditorUi::buildMainMenu()
+void EditorUi::mainMenu()
 {
 	if(ImGui::BeginMainMenuBar())
 	{
@@ -120,29 +200,29 @@ void EditorUi::buildMainMenu()
 			ImGui::EndMenu();
 		}
 
-		if(ImGui::BeginMenu(ICON_MDI_EYE " Windows"))
+		if(ImGui::BeginMenu(ICON_MDI_APPLICATION_OUTLINE " Windows"))
 		{
-			if(ImGui::MenuItem(ICON_MDI_EYE " Console"))
+			if(ImGui::MenuItem(ICON_MDI_APPLICATION_OUTLINE " Console"))
 			{
 				m_showConsoleWindow = true;
 			}
 
-			if(ImGui::MenuItem(ICON_MDI_EYE " SceneNode Props"))
+			if(ImGui::MenuItem(ICON_MDI_APPLICATION_OUTLINE " SceneNode Props"))
 			{
 				m_showSceneNodePropsWindow = true;
 			}
 
-			if(ImGui::MenuItem(ICON_MDI_EYE " Scene Hierarchy"))
+			if(ImGui::MenuItem(ICON_MDI_APPLICATION_OUTLINE " Scene Hierarchy"))
 			{
 				m_showSceneHierarcyWindow = true;
 			}
 
-			if(ImGui::MenuItem(ICON_MDI_EYE " Assets"))
+			if(ImGui::MenuItem(ICON_MDI_APPLICATION_OUTLINE " Assets"))
 			{
 				m_showAssetsWindow = true;
 			}
 
-			if(ImGui::MenuItem(ICON_MDI_EYE " CVars Editor"))
+			if(ImGui::MenuItem(ICON_MDI_APPLICATION_OUTLINE " CVars Editor"))
 			{
 				m_showCVarEditorWindow = true;
 			}
@@ -150,6 +230,16 @@ void EditorUi::buildMainMenu()
 			ImGui::EndMenu();
 		}
 
+		// Title
+		{
+			CString text = "AnKi 3D Engine Editor";
+			const F32 menuBarWidth = ImGui::GetWindowWidth();
+			const F32 textWidth = ImGui::CalcTextSize(text.cstr()).x;
+			ImGui::SameLine(menuBarWidth - menuBarWidth / 2.0f - textWidth / 2.0f);
+
+			ImGui::TextUnformatted(text.cstr());
+		}
+
 		// Quit bnt
 		{
 			const Char* text = ICON_MDI_CLOSE_CIRCLE;
@@ -177,7 +267,7 @@ void EditorUi::buildMainMenu()
 	}
 }
 
-void EditorUi::buildSceneNode(SceneNode& node)
+void EditorUi::sceneNode(SceneNode& node)
 {
 	ImGui::TableNextRow();
 	ImGui::TableNextColumn();
@@ -210,7 +300,7 @@ void EditorUi::buildSceneNode(SceneNode& node)
 	{
 		for(SceneNode* child : node.getChildren())
 		{
-			buildSceneNode(*child);
+			sceneNode(*child);
 		}
 
 		ImGui::TreePop();
@@ -218,7 +308,7 @@ void EditorUi::buildSceneNode(SceneNode& node)
 	ImGui::PopID();
 }
 
-void EditorUi::buildSceneHierarchyWindow()
+void EditorUi::sceneHierarchyWindow()
 {
 	if(!m_showSceneHierarcyWindow)
 	{
@@ -234,9 +324,9 @@ void EditorUi::buildSceneHierarchyWindow()
 		ImGui::SetNextWindowSize(Vec2(400.0f, viewportSize.y() - kConsoleHeight), ImGuiCond_FirstUseEver);
 	}
 
-	if(ImGui::Begin("Scene Hierarchy", &m_showSceneHierarcyWindow, 0))
+	if(ImGui::Begin("Scene Hierarchy", &m_showSceneHierarcyWindow, ImGuiWindowFlags_NoCollapse))
 	{
-		buildFilter(m_sceneHierarchyWindow.m_filter);
+		filter(m_sceneHierarchyWindow.m_filter);
 
 		if(ImGui::BeginChild("##tree", Vec2(0.0f), ImGuiChildFlags_Borders | ImGuiChildFlags_NavFlattened, ImGuiWindowFlags_None))
 		{
@@ -245,7 +335,7 @@ void EditorUi::buildSceneHierarchyWindow()
 				SceneGraph::getSingleton().visitNodes([this](SceneNode& node) {
 					if(!node.getParent() && m_sceneHierarchyWindow.m_filter.PassFilter(node.getName().cstr()))
 					{
-						buildSceneNode(node);
+						sceneNode(node);
 					}
 					return true;
 				});
@@ -258,7 +348,7 @@ void EditorUi::buildSceneHierarchyWindow()
 	ImGui::End();
 }
 
-void EditorUi::buildSceneNodePropertiesWindow()
+void EditorUi::sceneNodePropertiesWindow()
 {
 	if(!m_showSceneNodePropsWindow)
 	{
@@ -272,15 +362,15 @@ void EditorUi::buildSceneNodePropertiesWindow()
 		// Viewport is one frame delay so do that when frame >1
 		const Vec2 viewportSize = ImGui::GetMainViewport()->WorkSize;
 		const Vec2 viewportPos = ImGui::GetMainViewport()->WorkPos;
-		const F32 initialWidth = 400.0f;
+		const F32 initialWidth = 500.0f;
 		ImGui::SetNextWindowPos(Vec2(viewportSize.x() - initialWidth, viewportPos.y()), ImGuiCond_FirstUseEver);
 		ImGui::SetNextWindowSize(Vec2(initialWidth, viewportSize.y() - kConsoleHeight), ImGuiCond_FirstUseEver);
 	}
 
-	if(ImGui::Begin("SceneNode Props", &m_showSceneNodePropsWindow, 0) && m_sceneHierarchyWindow.m_visibleNode)
+	if(ImGui::Begin("SceneNode Props", &m_showSceneNodePropsWindow, ImGuiWindowFlags_NoCollapse) && m_sceneHierarchyWindow.m_visibleNode)
 	{
-		U32 id = 0;
 		SceneNode& node = *m_sceneHierarchyWindow.m_visibleNode;
+		I32 id = 0;
 
 		if(state.m_currentSceneNodeUuid != node.getUuid())
 		{
@@ -291,24 +381,13 @@ void EditorUi::buildSceneNodePropertiesWindow()
 			state.m_textEditorTxt.destroy();
 		}
 
-		auto dummyButton = [&]() {
-			ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.0f);
-			ImGui::PushID(id++);
-			ImGui::Button(ICON_MDI_LOCK);
-			ImGui::PopID();
-			ImGui::PopStyleVar();
-			ImGui::SameLine();
-		};
-
-		const F32 labelWidthBase = ImGui::GetFontSize() * 12; // Some amount of width for label, based on font size.
-		const F32 labelWidthMax = ImGui::GetContentRegionAvail().x * 0.40f; // ...but always leave some room for framed widgets.
-		ANKI_IMGUI_PUSH_POP(ItemWidth, -min(labelWidthBase, labelWidthMax));
+		ANKI_PUSH_POP_TEXT_INPUT_WIDTH(12);
 
 		// Name
 		{
-			dummyButton();
+			dummyButton(id++);
 
-			Array<Char, 256> name;
+			Array<Char, kMaxTextInputLen> name;
 			std::strncpy(name.getBegin(), node.getName().getBegin(), name.getSize());
 			if(ImGui::InputText("Name", name.getBegin(), name.getSize(), ImGuiInputTextFlags_EnterReturnsTrue))
 			{
@@ -318,7 +397,7 @@ void EditorUi::buildSceneNodePropertiesWindow()
 
 		// Local transform
 		{
-			dummyButton();
+			dummyButton(id++);
 
 			F32 localOrigin[3] = {node.getLocalOrigin().x(), node.getLocalOrigin().y(), node.getLocalOrigin().z()};
 			if(ImGui::DragFloat3(ICON_MDI_AXIS_ARROW " Origin", localOrigin, 0.025f, -1000000.0f, 1000000.0f))
@@ -364,7 +443,7 @@ void EditorUi::buildSceneNodePropertiesWindow()
 
 		// Local rotation
 		{
-			dummyButton();
+			dummyButton(id++);
 
 			const Euler rot(node.getLocalRotation());
 			F32 localRotation[3] = {toDegrees(rot.x()), toDegrees(rot.y()), toDegrees(rot.z())};
@@ -424,16 +503,18 @@ void EditorUi::buildSceneNodePropertiesWindow()
 
 		// Components
 		{
-			ANKI_IMGUI_PUSH_POP(StyleVar, ImGuiStyleVar_SeparatorTextAlign, Vec2(0.5f));
+			ANKI_PUSH_POP(StyleVar, ImGuiStyleVar_SeparatorTextAlign, Vec2(0.5f));
 
 			U32 count = 0;
 			node.iterateComponents([&](SceneComponent& comp) {
-				ANKI_IMGUI_PUSH_POP(ID, comp.getUuid());
+				ANKI_PUSH_POP(ID, comp.getUuid());
 				const F32 alpha = 0.1f;
-				ANKI_IMGUI_PUSH_POP(StyleColor, ImGuiCol_ChildBg, (count & 1) ? Vec4(0.0, 0.0f, 1.0f, alpha) : Vec4(1.0, 0.0f, 0.0f, alpha));
+				ANKI_PUSH_POP(StyleColor, ImGuiCol_ChildBg, (count & 1) ? Vec4(0.0, 0.0f, 1.0f, alpha) : Vec4(1.0, 0.0f, 0.0f, alpha));
 
 				if(ImGui::BeginChild("Child", Vec2(0.0f), ImGuiChildFlags_AutoResizeY))
 				{
+					ANKI_PUSH_POP_TEXT_INPUT_WIDTH(12);
+
 					// Find the icon
 					CString icon = ICON_MDI_TOY_BRICK;
 					switch(comp.getType())
@@ -451,7 +532,7 @@ void EditorUi::buildSceneNodePropertiesWindow()
 						label.sprintf(" %s %s (%u)", icon.cstr(), kSceneComponentTypeName[comp.getType()], comp.getUuid());
 						ImGui::SeparatorText(label.cstr());
 
-						if(ImGui::Button(ICON_MDI_DELETE))
+						if(ImGui::Button(ICON_MDI_MINUS_BOX))
 						{
 						}
 						ImGui::SetItemTooltip("Delete Component");
@@ -470,7 +551,10 @@ void EditorUi::buildSceneNodePropertiesWindow()
 						// Nothing
 						break;
 					case SceneComponentType::kScript:
-						buildScriptComponent(static_cast<ScriptComponent&>(comp));
+						scriptComponent(static_cast<ScriptComponent&>(comp));
+						break;
+					case SceneComponentType::kMaterial:
+						materialComponent(static_cast<MaterialComponent&>(comp));
 						break;
 					default:
 						ImGui::Text("TODO");
@@ -486,10 +570,20 @@ void EditorUi::buildSceneNodePropertiesWindow()
 	ImGui::End();
 }
 
-void EditorUi::buildScriptComponent(ScriptComponent& comp)
+void EditorUi::scriptComponent(ScriptComponent& comp)
 {
 	auto& state = m_sceneNodePropsWindow;
 
+	// Play button
+	{
+		ImGui::SameLine();
+		if(ImGui::Button(ICON_MDI_PLAY "##ScriptComponentResourceFilename"))
+		{
+			ANKI_LOGV("TODO");
+		}
+		ImGui::SetItemTooltip("Play script");
+	}
+
 	// Clear button
 	{
 		if(ImGui::Button(ICON_MDI_DELETE "##ScriptComponentResourceFilename"))
@@ -538,7 +632,7 @@ void EditorUi::buildScriptComponent(ScriptComponent& comp)
 
 	if(state.m_textEditorOpen && state.m_scriptComponentThatHasTheTextEditorOpen == comp.getUuid())
 	{
-		if(buildTextEditorWindow(String().sprintf("ScriptComponent %u", comp.getUuid()), &state.m_textEditorOpen, state.m_textEditorTxt))
+		if(textEditorWindow(String().sprintf("ScriptComponent %u", comp.getUuid()), &state.m_textEditorOpen, state.m_textEditorTxt))
 		{
 			ANKI_LOGV("Updating ScriptComponent");
 			comp.setScriptText(state.m_textEditorTxt);
@@ -552,36 +646,93 @@ void EditorUi::buildScriptComponent(ScriptComponent& comp)
 	}
 }
 
-void EditorUi::buildCVarsEditorWindow()
+void EditorUi::materialComponent(MaterialComponent& comp)
+{
+	// Locate button
+	{
+		ImGui::BeginDisabled(!comp.hasMaterialResource());
+		if(ImGui::Button(comp.hasMaterialResource() ? ICON_MDI_MAP_MARKER : ICON_MDI_ALERT_CIRCLE "##MaterialCompBtn"))
+		{
+			ANKI_LOGW("TODO");
+		}
+		ImGui::SetItemTooltip("Locate");
+		ImGui::EndDisabled();
+		ImGui::SameLine();
+	}
+
+	// Filename
+	{
+		ImGui::SetNextItemWidth(-1.0f);
+
+		Char buff[kMaxTextInputLen] = "";
+		if(comp.hasMaterialResource())
+		{
+			std::strncpy(buff, comp.getMaterialFilename().cstr(), sizeof(buff));
+		}
+
+		if(ImGui::InputTextWithHint("##MaterialCompFname", ".ankimtl Filename", buff, sizeof(buff)))
+		{
+			comp.setMaterialFilename(buff);
+		}
+
+		if(comp.hasMaterialResource())
+		{
+			ImGui::SetItemTooltip("%s", comp.getMaterialFilename().cstr());
+		}
+	}
+
+	// Submesh ID
+	{
+		dummyButton(0);
+
+		I32 value = comp.getSubmeshIndex();
+		Char txt[100] = "lala";
+		if(ImGui::InputInt(ICON_MDI_VECTOR_POLYGON " Submesh ID", &value, 1, 1, 0))
+		{
+			comp.setSubmeshIndex(value);
+		}
+	}
+}
+
+void EditorUi::cVarsWindow()
 {
 	if(!m_showCVarEditorWindow)
 	{
 		return;
 	}
 
+	F32 maxCvarTextSize = 0.0f;
+	CVarSet::getSingleton().iterateCVars([&](CVar& cvar) {
+		maxCvarTextSize = max(maxCvarTextSize, ImGui::CalcTextSize(cvar.getName().cstr()).x);
+		return FunctorContinue::kContinue;
+	});
+
 	if(ImGui::GetFrameCount() > 1)
 	{
 		// Viewport is one frame delay so do that when frame >1
 		const Vec2 viewportSize = ImGui::GetMainViewport()->WorkSize;
 		const Vec2 viewportPos = ImGui::GetMainViewport()->WorkPos;
-		const Vec2 initialSize = Vec2(800.0f, m_canvas->getSizef().y() * 0.8f);
+		const Vec2 initialSize = Vec2(900.0f, m_canvas->getSizef().y() * 0.8f);
 		ImGui::SetNextWindowSize(initialSize, ImGuiCond_FirstUseEver);
 		ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), ImGuiCond_Once, Vec2(0.5f));
 	}
 
 	if(ImGui::Begin("CVars Editor", &m_showCVarEditorWindow, 0))
 	{
-		buildFilter(m_cvarsEditorWindow.m_filter);
+		filter(m_cvarsEditorWindow.m_filter);
 
-		if(ImGui::BeginChild("TableInternals", Vec2(0.0f), ImGuiChildFlags_Borders | ImGuiChildFlags_NavFlattened))
+		if(ImGui::BeginChild("##Child", Vec2(0.0f), ImGuiChildFlags_Borders | ImGuiChildFlags_NavFlattened))
 		{
 			if(ImGui::BeginTable("Table", 2, 0))
 			{
+				ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, maxCvarTextSize + 20.0f);
+				ImGui::TableSetupColumn("Value");
+
 				I32 id = 0;
 				CVarSet::getSingleton().iterateCVars([&](CVar& cvar) {
 					if(!m_cvarsEditorWindow.m_filter.PassFilter(cvar.getName().cstr()))
 					{
-						return false;
+						return FunctorContinue::kContinue;
 					}
 
 					ImGui::TableNextRow();
@@ -593,6 +744,8 @@ void EditorUi::buildCVarsEditorWindow()
 
 					ImGui::PushID(id++);
 
+					ImGui::SetNextItemWidth(-1.0f);
+
 					if(cvar.getValueType() == CVarValueType::kBool)
 					{
 						BoolCVar& bcvar = static_cast<BoolCVar&>(cvar);
@@ -658,7 +811,7 @@ void EditorUi::buildCVarsEditorWindow()
 
 					ImGui::PopID();
 
-					return false;
+					return FunctorContinue::kContinue;
 				});
 
 				ImGui::EndTable();
@@ -670,7 +823,7 @@ void EditorUi::buildCVarsEditorWindow()
 	ImGui::End();
 }
 
-void EditorUi::buildConsoleWindow()
+void EditorUi::consoleWindow()
 {
 	if(!m_showConsoleWindow)
 	{
@@ -690,7 +843,7 @@ void EditorUi::buildConsoleWindow()
 		ImGui::SetNextWindowPos(initialPos, ImGuiCond_FirstUseEver);
 	}
 
-	if(ImGui::Begin("Console", &m_showConsoleWindow, 0))
+	if(ImGui::Begin("Console", &m_showConsoleWindow, ImGuiWindowFlags_NoCollapse))
 	{
 		// Log controls
 		{
@@ -754,13 +907,61 @@ void EditorUi::buildConsoleWindow()
 	ImGui::End();
 }
 
-void EditorUi::buildAssetsWindow()
+void EditorUi::dirTree(const AssetPath& path)
+{
+	auto& state = m_assetsWindow;
+
+	ImGui::TableNextRow();
+	ImGui::TableNextColumn();
+
+	ImGuiTreeNodeFlags treeFlags = ImGuiTreeNodeFlags_None;
+	treeFlags |= ImGuiTreeNodeFlags_OpenOnArrow
+				 | ImGuiTreeNodeFlags_OpenOnDoubleClick; // Standard opening mode as we are likely to want to add selection afterwards
+	treeFlags |= ImGuiTreeNodeFlags_NavLeftJumpsToParent; // Left arrow support
+	treeFlags |= ImGuiTreeNodeFlags_SpanFullWidth; // Span full width for easier mouse reach
+	treeFlags |= ImGuiTreeNodeFlags_DrawLinesToNodes; // Always draw hierarchy outlines
+
+	if(state.m_pathSelected == &path)
+	{
+		treeFlags |= ImGuiTreeNodeFlags_Selected;
+	}
+
+	const Bool hasChildren = path.m_children.getSize();
+	if(!hasChildren)
+	{
+		treeFlags |= ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_Bullet;
+	}
+
+	ImGui::PushID(path.m_id);
+	const Bool nodeOpen = ImGui::TreeNodeEx("", treeFlags, "%s", path.m_dirname.cstr());
+	ImGui::PopID();
+	ImGui::SetItemTooltip("%s", path.m_dirname.cstr());
+
+	if(ImGui::IsItemFocused())
+	{
+		state.m_pathSelected = &path;
+	}
+
+	if(nodeOpen)
+	{
+		for(const AssetPath& p : path.m_children)
+		{
+			dirTree(p);
+		}
+
+		ImGui::TreePop();
+	}
+}
+
+void EditorUi::assetsWindow()
 {
 	if(!m_showAssetsWindow)
 	{
 		return;
 	}
 
+	auto& state = m_assetsWindow;
+
 	if(ImGui::GetFrameCount() > 1)
 	{
 		// Viewport is one frame delay so do that when frame >1
@@ -772,14 +973,122 @@ void EditorUi::buildAssetsWindow()
 		ImGui::SetNextWindowPos(initialPos, ImGuiCond_FirstUseEver);
 	}
 
-	if(ImGui::Begin("Assets", &m_showAssetsWindow, 0))
+	if(ImGui::Begin("Assets", &m_showAssetsWindow, ImGuiWindowFlags_NoCollapse))
 	{
-	}
+		// Left side
+		{
+			if(ImGui::BeginChild("Left", Vec2(300.0f, -1.0f), ImGuiChildFlags_ResizeX | ImGuiChildFlags_Borders | ImGuiChildFlags_NavFlattened))
+			{
+				if(ImGui::BeginTable("##bg", 1, ImGuiTableFlags_RowBg))
+				{
+					for(const AssetPath& p : state.m_assetPaths)
+					{
+						dirTree(p);
+					}
+
+					ImGui::EndTable();
+				}
+			}
+			ImGui::EndChild();
+		} // Left side
+
+		ImGui::SameLine();
+
+		// Right side
+		{
+			// Use the filter to gather the files
+			DynamicArray<const AssetFile*> filteredFiles;
+			if(state.m_pathSelected)
+			{
+				for(const AssetFile& f : state.m_pathSelected->m_files)
+				{
+					if(state.m_fileFilter.PassFilter(f.m_basename.cstr()))
+					{
+						filteredFiles.emplaceBack(&f);
+					}
+				}
+			}
+
+			if(ImGui::BeginChild("Right", Vec2(-1.0f, -1.0f), 0))
+			{
+				// Increase/decrease icon size
+				{
+					ImGui::TextUnformatted("Icon Size");
+					ImGui::SameLine();
+					ImGui::SetNextItemWidth(64.0f);
+					ImGui::SliderInt("##Icon Size", &state.m_cellSize, 5, 11, "%d", ImGuiSliderFlags_AlwaysClamp);
+					ImGui::SameLine();
+				}
+
+				filter(state.m_fileFilter);
+
+				if(ImGui::BeginChild("RightBottom", Vec2(-1.0f, -1.0f), 0))
+				{
+					const F32 cellWidth = F32(state.m_cellSize) * 16;
+					const U32 columnCount = U32(ImGui::GetContentRegionAvail().x / cellWidth);
+					ImGui::SetNextItemWidth(-1.0f);
+					const ImGuiTableFlags flags = ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersOuter
+												  | ImGuiTableFlags_BordersV | ImGuiTableFlags_ContextMenuInBody;
+					if(filteredFiles.getSize() && ImGui::BeginTable("Grid", columnCount, flags))
+					{
+						const U32 rowCount = (filteredFiles.getSize() + columnCount - 1) / columnCount;
+
+						for(U32 row = 0; row < rowCount; ++row)
+						{
+							ImGui::TableNextRow();
+							for(U32 column = 0; column < columnCount; ++column)
+							{
+								ImGui::TableNextColumn();
+
+								const U32 idx = row * columnCount + column;
+								if(idx < filteredFiles.getSize())
+								{
+									const AssetFile& file = *filteredFiles[idx];
+
+									if(file.m_type == AssetFileType::kMaterial)
+									{
+										ImTextureID id;
+										id.m_texture = &m_materialIcon->getTexture();
+										ImGui::Image(id, Vec2(cellWidth));
+									}
+									else if(file.m_type == AssetFileType::kMesh)
+									{
+										ImTextureID id;
+										id.m_texture = &m_meshIcon->getTexture();
+										ImGui::Image(id, Vec2(cellWidth));
+									}
+									else if(file.m_type == AssetFileType::kTexture)
+									{
+										ImageResourcePtr img;
+										loadImageToCache(file.m_filename, img);
+										ImTextureID id;
+										id.m_texture = &img->getTexture();
+										id.m_textureSubresource = TextureSubresourceDesc::all();
+										ImGui::Image(id, Vec2(cellWidth));
+									}
+
+									ImGui::TextWrapped("%s", file.m_basename.cstr());
+									ImGui::SetItemTooltip("%s", file.m_filename.cstr());
+								}
+							}
+						}
 
+						ImGui::EndTable();
+					}
+					else
+					{
+						ImGui::TextUnformatted("Empty");
+					}
+				}
+				ImGui::EndChild();
+			}
+			ImGui::EndChild();
+		} // Right side
+	}
 	ImGui::End();
 }
 
-void EditorUi::buildFilter(ImGuiTextFilter& filter)
+void EditorUi::filter(ImGuiTextFilter& filter)
 {
 	ImGui::SetNextItemWidth(-FLT_MIN);
 	ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_F, ImGuiInputFlags_Tooltip);
@@ -792,7 +1101,17 @@ void EditorUi::buildFilter(ImGuiTextFilter& filter)
 	ImGui::PopItemFlag();
 }
 
-Bool EditorUi::buildTextEditorWindow(CString extraWindowTitle, Bool* pOpen, String& inout)
+void EditorUi::dummyButton(I32 id)
+{
+	ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.0f);
+	ImGui::PushID(id);
+	ImGui::Button(ICON_MDI_LOCK);
+	ImGui::PopID();
+	ImGui::PopStyleVar();
+	ImGui::SameLine();
+};
+
+Bool EditorUi::textEditorWindow(CString extraWindowTitle, Bool* pOpen, String& inout) const
 {
 	Bool save = false;
 
@@ -849,4 +1168,51 @@ Bool EditorUi::buildTextEditorWindow(CString extraWindowTitle, Bool* pOpen, Stri
 	return save;
 }
 
+void EditorUi::loadImageToCache(CString fname, ImageResourcePtr& img)
+{
+	// Try to load first
+	ANKI_CHECKF(ResourceManager::getSingleton().loadResource(fname, img));
+
+	// Update the cache
+	const U32 crntFrame = ImGui::GetFrameCount();
+	Bool entryFound = false;
+	DynamicArray<ImageCacheEntry>& cache = m_assetsWindow.m_imageCache;
+	for(ImageCacheEntry& entry : cache)
+	{
+		if(entry.m_image->getUuid() == img->getUuid())
+		{
+			entry.m_lastSeenInFrame = crntFrame;
+			entryFound = true;
+			break;
+		}
+	}
+
+	if(!entryFound)
+	{
+		cache.emplaceBack(ImageCacheEntry{img, crntFrame});
+	}
+
+	// Try to remove stale entries
+	const U32 frameInactivityCount = 60 * 30; // ~30"
+	while(true)
+	{
+		Bool foundStaleEntry = false;
+		for(auto it = cache.getBegin(); it != cache.getEnd(); ++it)
+		{
+			ANKI_ASSERT(crntFrame >= it->m_lastSeenInFrame);
+			if(crntFrame - it->m_lastSeenInFrame > frameInactivityCount)
+			{
+				cache.erase(it);
+				foundStaleEntry = true;
+				break;
+			}
+		}
+
+		if(!foundStaleEntry)
+		{
+			break;
+		}
+	}
+}
+
 } // end namespace anki

+ 76 - 12
AnKi/Editor/EditorUi.h

@@ -7,10 +7,18 @@
 
 #include <AnKi/Ui.h>
 
+namespace std {
+namespace filesystem {
+class path;
+}
+} // namespace std
+
 namespace anki {
 
+// Forward
 class SceneNode;
 class ScriptComponent;
+class MaterialComponent;
 
 /// @addtogroup editor
 /// @{
@@ -29,9 +37,42 @@ public:
 
 private:
 	static constexpr F32 kMargin = 4.0f;
-	static constexpr F32 kConsoleHeight = 300.0f;
+	static constexpr F32 kConsoleHeight = 400.0f;
 	static constexpr U32 kMaxTextInputLen = 256;
 
+	enum class AssetFileType : U32
+	{
+		kNone,
+		kTexture,
+		kMaterial,
+		kMesh,
+		kLua
+	};
+
+	class AssetFile
+	{
+	public:
+		String m_basename;
+		String m_filename;
+		AssetFileType m_type = AssetFileType::kNone;
+	};
+
+	class AssetPath
+	{
+	public:
+		String m_dirname;
+		DynamicArray<AssetPath> m_children;
+		DynamicArray<AssetFile> m_files;
+		U32 m_id = 0;
+	};
+
+	class ImageCacheEntry
+	{
+	public:
+		ImageResourcePtr m_image;
+		U32 m_lastSeenInFrame = 0;
+	};
+
 	UiCanvas* m_canvas = nullptr;
 
 	ImFont* m_font = nullptr;
@@ -44,6 +85,9 @@ private:
 	Bool m_showSceneHierarcyWindow = true;
 	Bool m_showAssetsWindow = true;
 
+	ImageResourcePtr m_materialIcon;
+	ImageResourcePtr m_meshIcon;
+
 	class
 	{
 	public:
@@ -83,24 +127,44 @@ private:
 		U32 m_currentSceneNodeUuid = 0;
 	} m_sceneNodePropsWindow;
 
-	void buildMainMenu();
+	class
+	{
+	public:
+		const AssetPath* m_pathSelected = nullptr;
+		DynamicArray<AssetPath> m_assetPaths;
+
+		DynamicArray<ImageCacheEntry> m_imageCache;
+
+		ImGuiTextFilter m_fileFilter;
+
+		I32 m_cellSize = 8; ///< Icon size
+	} m_assetsWindow;
+
+	void mainMenu();
+
+	void sceneHierarchyWindow();
+	void sceneNode(SceneNode& node);
 
-	void buildSceneHierarchyWindow();
-	void buildSceneNode(SceneNode& node);
+	void sceneNodePropertiesWindow();
+	void scriptComponent(ScriptComponent& comp);
+	void materialComponent(MaterialComponent& comp);
 
-	void buildSceneNodePropertiesWindow();
-	void buildScriptComponent(ScriptComponent& comp);
+	void cVarsWindow();
+	void consoleWindow();
 
-	void buildCVarsEditorWindow();
-	void buildConsoleWindow();
-	void buildAssetsWindow();
+	void assetsWindow();
+	void dirTree(const AssetPath& path);
 
-	// Utils
-	void buildFilter(ImGuiTextFilter& filter);
-	Bool buildTextEditorWindow(CString extraWindowTitle, Bool* pOpen, String& inout);
+	// Widget/UI utils
+	static void filter(ImGuiTextFilter& filter);
+	Bool textEditorWindow(CString extraWindowTitle, Bool* pOpen, String& inout) const;
+	static void dummyButton(I32 id);
 
 	// Misc
 	static void loggerMessageHandler(void* ud, const LoggerMessageInfo& info);
+	static void listDir(const std::filesystem::path& rootPath, const std::filesystem::path& parentPath, AssetPath& parent, U32& id);
+	static void gatherAssets(DynamicArray<AssetPath>& paths);
+	void loadImageToCache(CString fname, ImageResourcePtr& img);
 };
 /// @}
 

+ 2 - 1
AnKi/Renderer/Reflections.cpp

@@ -57,6 +57,8 @@ Error Reflections::init()
 
 		m_sbtRecordSize = getAlignedRoundUp(GrManager::getSingleton().getDeviceCapabilities().m_sbtRecordAlignment,
 											GrManager::getSingleton().getDeviceCapabilities().m_shaderGroupHandleSize + U32(sizeof(UVec4)));
+
+		ANKI_CHECK(loadShaderProgram(kProgFname, mutation, m_mainProg, m_rtMaterialFetchInlineRtGrProg, "RtMaterialFetchInlineRt"));
 	}
 
 	ANKI_CHECK(loadShaderProgram(kProgFname, mutation, m_mainProg, m_spatialDenoisingGrProg, "SpatialDenoise"));
@@ -66,7 +68,6 @@ Error Reflections::init()
 	ANKI_CHECK(loadShaderProgram(kProgFname, mutation, m_mainProg, m_ssrGrProg, "Ssr"));
 	ANKI_CHECK(loadShaderProgram(kProgFname, mutation, m_mainProg, m_probeFallbackGrProg, "ReflectionProbeFallback"));
 	ANKI_CHECK(loadShaderProgram(kProgFname, mutation, m_mainProg, m_tileClassificationGrProg, "Classification"));
-	ANKI_CHECK(loadShaderProgram(kProgFname, mutation, m_mainProg, m_rtMaterialFetchInlineRtGrProg, "RtMaterialFetchInlineRt"));
 
 	m_transientRtDesc1 = getRenderer().create2DRenderTargetDescription(
 		getRenderer().getInternalResolution().x(), getRenderer().getInternalResolution().y(), Format::kR16G16B16A16_Sfloat, "Reflections #1");

+ 14 - 0
AnKi/Resource/ResourceFilesystem.h

@@ -114,6 +114,20 @@ public:
 		return Error::kNone;
 	}
 
+	/// Iterate paths in the DataPaths CVar
+	template<typename TFunc>
+	void iterateAllResourceBasePaths(TFunc func) const
+	{
+		for(const Path& path : m_paths)
+		{
+			const FunctorContinue cont = func(path.m_path);
+			if(cont == FunctorContinue::kStop)
+			{
+				break;
+			}
+		}
+	}
+
 #if !ANKI_TESTS
 private:
 #endif

+ 12 - 0
AnKi/Scene/Components/MaterialComponent.cpp

@@ -47,6 +47,18 @@ MaterialComponent& MaterialComponent::setMaterialFilename(CString fname)
 	return *this;
 }
 
+CString MaterialComponent::getMaterialFilename() const
+{
+	if(m_resource)
+	{
+		return m_resource->getFilename();
+	}
+	else
+	{
+		return "*Error*";
+	}
+}
+
 MaterialComponent& MaterialComponent::setSubmeshIndex(U32 submeshIdx)
 {
 	if(m_submeshIdx != submeshIdx)

+ 12 - 0
AnKi/Scene/Components/MaterialComponent.h

@@ -30,8 +30,20 @@ public:
 
 	MaterialComponent& setMaterialFilename(CString fname);
 
+	CString getMaterialFilename() const;
+
+	Bool hasMaterialResource() const
+	{
+		return !!m_resource;
+	}
+
 	MaterialComponent& setSubmeshIndex(U32 submeshIdx);
 
+	U32 getSubmeshIndex() const
+	{
+		return m_submeshIdx;
+	}
+
 private:
 	GpuSceneArrays::MeshLod::Allocation m_gpuSceneMeshLods;
 	GpuSceneArrays::Renderable::Allocation m_gpuSceneRenderable;

+ 3 - 3
AnKi/Script/Globals.cpp

@@ -20,17 +20,17 @@ static CVar* findCVar(CString name)
 		// Possibly a CVAR
 
 		CString cvarName = name.getBegin() + prefix.getLength();
-		CVarSet::getSingleton().iterateCVars([&](CVar& cvar) -> Bool {
+		CVarSet::getSingleton().iterateCVars([&](CVar& cvar) {
 			ScriptString cvarName2 = cvar.getName();
 			cvarName2.replaceAll(".", "");
 
 			if(cvarName == cvarName2)
 			{
 				foundCVar = &cvar;
-				return true;
+				return FunctorContinue::kStop;
 			}
 
-			return false;
+			return FunctorContinue::kContinue;
 		});
 	}
 

+ 87 - 18
AnKi/Ui/UiCanvas.cpp

@@ -22,15 +22,18 @@ static void setColorStyleAdia()
 	ImVec4* colors = style.Colors;
 
 	// Base Colors
-	const ImVec4 bgColor = ImVec4(0.10f, 0.105f, 0.11f, 1.00f);
-	const ImVec4 lightBgColor = ImVec4(0.15f, 0.16f, 0.17f, 1.00f);
-	const ImVec4 panelColor = ImVec4(0.17f, 0.18f, 0.19f, 1.00f);
-	const ImVec4 panelHoverColor = ImVec4(0.20f, 0.22f, 0.24f, 1.00f);
-	const ImVec4 panelActiveColor = ImVec4(0.23f, 0.26f, 0.29f, 1.00f);
-	const ImVec4 textColor = ImVec4(0.86f, 0.87f, 0.88f, 1.00f);
-	const ImVec4 textDisabledColor = ImVec4(0.50f, 0.50f, 0.50f, 1.00f);
-	const ImVec4 borderColor = ImVec4(0.14f, 0.16f, 0.18f, 1.00f);
-	const ImVec4 blueColor = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
+	const Vec4 bgColor = Vec4(0.10f, 0.105f, 0.11f, 1.00f);
+	const Vec4 lightBgColor = Vec4(0.15f, 0.16f, 0.17f, 1.00f);
+	const Vec4 panelColor = Vec4(0.17f, 0.18f, 0.19f, 1.00f);
+	const Vec4 panelHoverColor = Vec4(0.20f, 0.22f, 0.24f, 1.00f);
+	const Vec4 panelActiveColor = Vec4(0.23f, 0.26f, 0.29f, 1.00f);
+	const Vec4 textColor = Vec4(0.86f, 0.87f, 0.88f, 1.00f);
+	const Vec4 textDisabledColor = Vec4(0.50f, 0.50f, 0.50f, 1.00f);
+	const Vec4 borderColor = Vec4(0.14f, 0.16f, 0.18f, 1.00f);
+	const Vec4 blueColor = Vec4(0.26f, 0.59f, 0.98f, 1.00f);
+
+	// Main menu
+	colors[ImGuiCol_MenuBarBg] = bgColor;
 
 	// Text
 	colors[ImGuiCol_Text] = textColor;
@@ -44,9 +47,9 @@ static void setColorStyleAdia()
 	colors[ImGuiCol_BorderShadow] = borderColor;
 
 	// Headers
-	colors[ImGuiCol_Header] = panelColor;
-	colors[ImGuiCol_HeaderHovered] = panelHoverColor;
-	colors[ImGuiCol_HeaderActive] = panelActiveColor;
+	colors[ImGuiCol_Header] = blueColor;
+	colors[ImGuiCol_HeaderHovered] = blueColor;
+	colors[ImGuiCol_HeaderActive] = blueColor;
 
 	// Buttons
 	colors[ImGuiCol_Button] = panelColor;
@@ -100,10 +103,10 @@ static void setColorStyleAdia()
 	colors[ImGuiCol_PlotHistogramHovered] = panelActiveColor;
 
 	// Text Selected BG
-	colors[ImGuiCol_TextSelectedBg] = panelActiveColor;
+	colors[ImGuiCol_TextSelectedBg] = blueColor;
 
 	// Modal Window Dim Bg
-	colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.10f, 0.105f, 0.11f, 0.5f);
+	colors[ImGuiCol_ModalWindowDimBg] = Vec4(0.10f, 0.105f, 0.11f, 0.5f);
 
 	// Tables
 	colors[ImGuiCol_TableHeaderBg] = panelColor;
@@ -125,10 +128,75 @@ static void setColorStyleAdia()
 	style.TabRounding = 2.0f;
 
 	// Reduced Padding and Spacing
-	style.WindowPadding = ImVec2(5.0f, 5.0f);
-	style.FramePadding = ImVec2(4.0f, 3.0f);
-	style.ItemSpacing = ImVec2(6.0f, 4.0f);
-	style.ItemInnerSpacing = ImVec2(4.0f, 4.0f);
+	style.WindowPadding = Vec2(5.0f, 5.0f);
+	style.FramePadding = Vec2(4.0f, 3.0f);
+	style.ItemSpacing = Vec2(6.0f, 4.0f);
+	style.ItemInnerSpacing = Vec2(4.0f, 4.0f);
+}
+
+[[maybe_unused]] static void setStypeHalfLife()
+{
+	ImVec4* colors = ImGui::GetStyle().Colors;
+	colors[ImGuiCol_Text] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
+	colors[ImGuiCol_TextDisabled] = ImVec4(0.50f, 0.50f, 0.50f, 1.00f);
+	colors[ImGuiCol_WindowBg] = ImVec4(0.29f, 0.34f, 0.26f, 1.00f);
+	colors[ImGuiCol_ChildBg] = ImVec4(0.29f, 0.34f, 0.26f, 1.00f);
+	colors[ImGuiCol_PopupBg] = ImVec4(0.24f, 0.27f, 0.20f, 1.00f);
+	colors[ImGuiCol_Border] = ImVec4(0.54f, 0.57f, 0.51f, 0.50f);
+	colors[ImGuiCol_BorderShadow] = ImVec4(0.14f, 0.16f, 0.11f, 0.52f);
+	colors[ImGuiCol_FrameBg] = ImVec4(0.24f, 0.27f, 0.20f, 1.00f);
+	colors[ImGuiCol_FrameBgHovered] = ImVec4(0.27f, 0.30f, 0.23f, 1.00f);
+	colors[ImGuiCol_FrameBgActive] = ImVec4(0.30f, 0.34f, 0.26f, 1.00f);
+	colors[ImGuiCol_TitleBg] = ImVec4(0.24f, 0.27f, 0.20f, 1.00f);
+	colors[ImGuiCol_TitleBgActive] = ImVec4(0.29f, 0.34f, 0.26f, 1.00f);
+	colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.00f, 0.00f, 0.00f, 0.51f);
+	colors[ImGuiCol_MenuBarBg] = ImVec4(0.24f, 0.27f, 0.20f, 1.00f);
+	colors[ImGuiCol_ScrollbarBg] = ImVec4(0.35f, 0.42f, 0.31f, 1.00f);
+	colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.28f, 0.32f, 0.24f, 1.00f);
+	colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.25f, 0.30f, 0.22f, 1.00f);
+	colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.23f, 0.27f, 0.21f, 1.00f);
+	colors[ImGuiCol_CheckMark] = ImVec4(0.59f, 0.54f, 0.18f, 1.00f);
+	colors[ImGuiCol_SliderGrab] = ImVec4(0.35f, 0.42f, 0.31f, 1.00f);
+	colors[ImGuiCol_SliderGrabActive] = ImVec4(0.54f, 0.57f, 0.51f, 0.50f);
+	colors[ImGuiCol_Button] = ImVec4(0.29f, 0.34f, 0.26f, 0.40f);
+	colors[ImGuiCol_ButtonHovered] = ImVec4(0.35f, 0.42f, 0.31f, 1.00f);
+	colors[ImGuiCol_ButtonActive] = ImVec4(0.54f, 0.57f, 0.51f, 0.50f);
+	colors[ImGuiCol_Header] = ImVec4(0.35f, 0.42f, 0.31f, 1.00f);
+	colors[ImGuiCol_HeaderHovered] = ImVec4(0.35f, 0.42f, 0.31f, 0.6f);
+	colors[ImGuiCol_HeaderActive] = ImVec4(0.54f, 0.57f, 0.51f, 0.50f);
+	colors[ImGuiCol_Separator] = ImVec4(0.14f, 0.16f, 0.11f, 1.00f);
+	colors[ImGuiCol_SeparatorHovered] = ImVec4(0.54f, 0.57f, 0.51f, 1.00f);
+	colors[ImGuiCol_SeparatorActive] = ImVec4(0.59f, 0.54f, 0.18f, 1.00f);
+	colors[ImGuiCol_ResizeGrip] = ImVec4(0.19f, 0.23f, 0.18f, 0.00f); // grip invis
+	colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.54f, 0.57f, 0.51f, 1.00f);
+	colors[ImGuiCol_ResizeGripActive] = ImVec4(0.59f, 0.54f, 0.18f, 1.00f);
+	colors[ImGuiCol_Tab] = ImVec4(0.35f, 0.42f, 0.31f, 1.00f);
+	colors[ImGuiCol_TabHovered] = ImVec4(0.54f, 0.57f, 0.51f, 0.78f);
+	colors[ImGuiCol_TabSelected] = ImVec4(0.59f, 0.54f, 0.18f, 1.00f);
+	colors[ImGuiCol_TabDimmed] = ImVec4(0.24f, 0.27f, 0.20f, 1.00f);
+	colors[ImGuiCol_TabDimmedSelected] = ImVec4(0.35f, 0.42f, 0.31f, 1.00f);
+	colors[ImGuiCol_DockingPreview] = ImVec4(0.59f, 0.54f, 0.18f, 1.00f);
+	colors[ImGuiCol_DockingEmptyBg] = ImVec4(0.20f, 0.20f, 0.20f, 1.00f);
+	colors[ImGuiCol_PlotLines] = ImVec4(0.61f, 0.61f, 0.61f, 1.00f);
+	colors[ImGuiCol_PlotLinesHovered] = ImVec4(0.59f, 0.54f, 0.18f, 1.00f);
+	colors[ImGuiCol_PlotHistogram] = ImVec4(1.00f, 0.78f, 0.28f, 1.00f);
+	colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f);
+	colors[ImGuiCol_TextSelectedBg] = ImVec4(0.59f, 0.54f, 0.18f, 1.00f);
+	colors[ImGuiCol_DragDropTarget] = ImVec4(0.73f, 0.67f, 0.24f, 1.00f);
+	colors[ImGuiCol_NavCursor] = ImVec4(0.59f, 0.54f, 0.18f, 1.00f);
+	colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f);
+	colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f);
+	colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.35f);
+
+	ImGuiStyle& style = ImGui::GetStyle();
+	style.FrameBorderSize = 1.0f;
+	style.WindowRounding = 0.0f;
+	style.ChildRounding = 0.0f;
+	style.FrameRounding = 0.0f;
+	style.PopupRounding = 0.0f;
+	style.ScrollbarRounding = 0.0f;
+	style.GrabRounding = 0.0f;
+	style.TabRounding = 0.0f;
 }
 
 static MouseCursor imguiCursorToAnki(ImGuiMouseCursor imguiCursor)
@@ -218,6 +286,7 @@ Error UiCanvas::init(UVec2 size)
 
 	// ImGui::StyleColorsLight();
 	setColorStyleAdia();
+	// setStypeHalfLife();
 
 	m_defaultFont = addFont("EngineAssets/UbuntuRegular.ttf");
 

+ 1 - 2
AnKi/Util/CVarSet.h

@@ -260,8 +260,7 @@ public:
 	{
 		for(CVar& cvar : m_cvars)
 		{
-			const Bool stop = func(cvar);
-			if(stop)
+			if(func(cvar) == FunctorContinue::kStop)
 			{
 				return;
 			}

+ 7 - 0
AnKi/Util/StdTypes.h

@@ -343,6 +343,13 @@ inline constexpr F32 operator""_degrees(long double x)
 	typedef auto _selfFn##selfType()->decltype(*this); \
 	using _SelfRef##selfType = decltype(((_selfFn##selfType*)0)()); \
 	using selfType = std::remove_reference<_SelfRef##selfType>::type;
+
+/// Instead of using bool to break a loop, which it's difficult to tell if it means stop or continue, use this enum
+enum class FunctorContinue
+{
+	kContinue,
+	kStop
+};
 /// @}
 
 } // end namespace anki

+ 1 - 0
AnKi/Window/NativeWindow.h

@@ -27,6 +27,7 @@ public:
 	Bool m_fullscreenDesktopRez = false;
 	Bool m_exclusiveFullscreen = false;
 	Bool m_borderless = false;
+	Bool m_maximized = false;
 
 	CString m_title = "AnKi";
 };

+ 5 - 0
AnKi/Window/NativeWindowSdl.cpp

@@ -101,6 +101,11 @@ Error NativeWindowSdl::initSdl(const NativeWindowInitInfo& init)
 		flags |= SDL_WINDOW_BORDERLESS;
 	}
 
+	if(init.m_maximized)
+	{
+		flags |= SDL_WINDOW_MAXIMIZED | SDL_WINDOW_RESIZABLE;
+	}
+
 	if(init.m_fullscreenDesktopRez)
 	{
 		if(init.m_exclusiveFullscreen)

BIN
EngineAssets/Editor/Material.png


BIN
EngineAssets/Editor/Mesh.png


+ 3 - 2
Tools/Editor/EditorMain.cpp

@@ -45,9 +45,10 @@ public:
 
 	Error userPreInit() override
 	{
-		ANKI_CHECK(CVarSet::getSingleton().setFromCommandLineArguments(m_argc - 1, m_argv + 1));
-		g_cvarWindowBorderless = true;
 		g_cvarWindowFullscreen = false;
+		g_cvarWindowMaximized = true;
+		g_cvarWindowBorderless = true;
+		ANKI_CHECK(CVarSet::getSingleton().setFromCommandLineArguments(m_argc - 1, m_argv + 1));
 
 		if(CString(g_cvarEditorScene) != "")
 		{