Divide Framework 0.1
A free and open-source 3D Framework under heavy development
Loading...
Searching...
No Matches
ContentExplorerWindow.cpp
Go to the documentation of this file.
1
2
4
10
15
16#include <filesystem>
17#include <imgui_internal.h>
18#include <imgui_stdlib.h>
19
20namespace Divide {
21 namespace {
22 constexpr const char* g_extensions[] = {
23 "glsl", "cmn", "frag", "vert", "cmp", "geom", "tesc", "tese", //Shaders
24 "ogg", "wav", //Sounds
25 "chai", //Scripts
26 "layout", "looknfeel", "scheme", "xsd", "imageset", "xcf", "txt", "anims", //CEGUI
27 "ttf", "font", //Fonts
28 "mtl", "md5anim", "bvh", //Geometry support
29 "xml" //General
30 };
31
32 constexpr const char* g_imageExtensions[] = {
33 "png", "jpg", "jpeg", "tga", "raw", "dds"
34 };
35
36 constexpr const char* g_soundExtensions[] = {
37 "wav", "ogg", "mp3", "mid"
38 };
39
40 constexpr const char* g_shaderExtensions[] = {
41 "glsl", "vert", "frag", "geom", "comp", "cmn", "tesc", "tese"
42 };
43
44 bool IsValidFile(const ResourcePath& name)
45 {
46 for (const char* extension : g_extensions)
47 {
48 if (hasExtension(name, extension))
49 {
50 return true;
51 }
52 }
53 for (const char* extension : g_geometryExtensions)
54 {
55 if (hasExtension(name, extension))
56 {
57 return true;
58 }
59 }
60 for (const char* extension : g_imageExtensions)
61 {
62 if (hasExtension(name, extension))
63 {
64 return true;
65 }
66 }
67 for (const char* extension : g_soundExtensions)
68 {
69 if (hasExtension(name, extension))
70 {
71 return true;
72 }
73 }
74
75 return false;
76 }
77 }
78
80 : DockedWindow(parent, descriptor)
81 {
82 }
83
85 {
89 for ( Handle<Texture>& icon : _geometryIcons )
90 {
91 DestroyResource( icon );
92 }
93 for ( auto& icon : _loadedTextures)
94 {
95 DestroyResource( icon.second );
96 }
99 }
100
102 {
103 _currentDirectories.resize(2);
104
105 getDirectoryStructureForPath(Paths::g_assetsLocation, _currentDirectories[0]);
106 _currentDirectories[0]._name = Paths::g_assetsLocation;
107
108 getDirectoryStructureForPath(Paths::g_xmlDataLocation, _currentDirectories[1]);
109 _currentDirectories[1]._name = Paths::g_xmlDataLocation;
110
111 _fileIcon = getTextureForPath(Paths::g_iconsPath, "file_icon.png");
112 _soundIcon = getTextureForPath( Paths::g_iconsPath, "sound_icon.png");
113 _shaderIcon = getTextureForPath( Paths::g_iconsPath, "shader_icon.png");
114
115 _geometryIcons[to_base(GeometryFormat::_3DS)] = getTextureForPath(Paths::g_iconsPath, "3ds_icon.png");
116 _geometryIcons[to_base(GeometryFormat::ASE)] = getTextureForPath(Paths::g_iconsPath, "ase_icon.png");
117 _geometryIcons[to_base(GeometryFormat::FBX)] = getTextureForPath(Paths::g_iconsPath, "fbx_icon.png");
118 _geometryIcons[to_base(GeometryFormat::MD2)] = getTextureForPath(Paths::g_iconsPath, "md2_icon.png");
119 _geometryIcons[to_base(GeometryFormat::MD5)] = getTextureForPath(Paths::g_iconsPath, "md5_icon.png");
120 _geometryIcons[to_base(GeometryFormat::OBJ)] = getTextureForPath(Paths::g_iconsPath, "obj_icon.png");
121 _geometryIcons[to_base(GeometryFormat::DAE)] = getTextureForPath(Paths::g_iconsPath, "collada.png");
122 _geometryIcons[to_base(GeometryFormat::GLTF)] = getTextureForPath(Paths::g_iconsPath, "gltf.png");
123 _geometryIcons[to_base(GeometryFormat::X)] = getTextureForPath(Paths::g_iconsPath, "x_icon.png");
124 _geometryIcons[to_base(GeometryFormat::DVD_ANIM)] = getTextureForPath(Paths::g_iconsPath, "divide.png");
125 _geometryIcons[to_base(GeometryFormat::DVD_GEOM)] = getTextureForPath(Paths::g_iconsPath, "divide.png");
126 _geometryIcons[to_base(GeometryFormat::COUNT)] = getTextureForPath(Paths::g_iconsPath, "file_icon.png");
127 }
128
129 void ContentExplorerWindow::update([[maybe_unused]] const U64 deltaTimeUS) {
130
131 // One per frame to avoid massive stutters.
132 if (!_textureLoadQueue.empty())
133 {
135 _textureLoadQueue.pop();
136 _loadedTextures[_ID((entry._path / entry._file._path).string())] = getTextureForPath( entry._path, entry._file._path.string());
137 }
138
140 }
141
142 void ContentExplorerWindow::getDirectoryStructureForPath(const ResourcePath& directoryPath, Directory& directoryOut) const
143 {
144 const std::filesystem::path p(directoryPath.string());
145 if ( std::filesystem::is_directory(p))
146 {
147 directoryOut._name = getTopLevelFolderName(directoryPath);
148 directoryOut._path = directoryPath;
149
150 for (auto&& x : std::filesystem::directory_iterator(p))
151 {
152 if (std::filesystem::is_regular_file(x.path()))
153 {
154 if (IsValidFile(ResourcePath { x.path().string() }))
155 {
156 const ResourcePath filename{ x.path().filename().string() };
157 directoryOut._files.emplace_back(filename, getExtension(filename).substr(1).c_str());
158
159 }
160 }
161 else if (std::filesystem::is_directory(x.path()))
162 {
163 auto& childDirectory = directoryOut._children.emplace_back(std::make_unique<Directory>());
164 getDirectoryStructureForPath(ResourcePath(x.path().string()), *childDirectory);
165 }
166 }
167 }
168 }
169
170 void ContentExplorerWindow::printDirectoryStructure(const Directory& dir, const bool open) const
171 {
172 ImGuiTreeNodeFlags nodeFlags = (open ? ImGuiTreeNodeFlags_DefaultOpen : 0);
173
174 if (dir._children.empty()) {
175 nodeFlags |= ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen;
176 }
177
178 if (ImGui::TreeNodeEx(dir._name.string().c_str(), nodeFlags))
179 {
180 if (ImGui::IsItemClicked() || ImGui::IsItemToggledOpen()) {
181 _selectedDir = &dir;
182 }
183
184 for (const auto& childDirectory : dir._children) {
185 printDirectoryStructure(*childDirectory, false);
186 }
187
188 if (!dir._children.empty()) {
189 ImGui::TreePop();
190 }
191 }
192 }
193
196
197 static bool previewTexture = false;
198 static bool spawnMesh = false;
199
201
202 const auto isSoundFile = [](const char* extension) {
203 for (const char* ext : g_soundExtensions) {
204 if (Util::CompareIgnoreCase(extension, ext)) {
205 return true;
206 }
207 }
208
209 return false;
210 };
211
212 const auto isShaderFile = [](const char* extension) {
213 for (const char* ext : g_shaderExtensions) {
214 if (Util::CompareIgnoreCase(extension, ext)) {
215 return true;
216 }
217 }
218
219 return false;
220 };
221
222 const auto openFileInEditor = [&](const ResourcePath& path, const File& file) {
224
225 if (textEditor.empty())
226 {
227 Attorney::EditorGeneralWidget::showStatusMessage(_parent, "ERROR: No text editor specified!", Time::SecondsToMilliseconds<F32>(3), true);
228 }
229 else
230 {
231 if (openFile(textEditor.string(), path, file._path.string()) != FileError::NONE)
232 {
233 Attorney::EditorGeneralWidget::showStatusMessage(_parent, "ERROR: Couldn't open specified source file!", Time::SecondsToMilliseconds<F32>(3), true);
234 }
235 }
236 };
237
238 constexpr U8 buttonSize = 64u;
239
240 const bool flipImages = !ImageTools::UseUpperLeftOrigin();
241 const ImVec2 uv0{ 0.f, flipImages ? 1.f : 0.f };
242 const ImVec2 uv1{ 1.f, flipImages ? 0.f : 1.f };
243
244 ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 5.0f);
245
246 {
247 ImGui::BeginChild("Folders", ImVec2(ImGui::GetContentRegionAvail().x * 0.3f, -1), true, ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_HorizontalScrollbar);
248 if (ImGui::BeginMenuBar()) {
249 if (ImGui::BeginMenu("Menu")) {
250 ImGui::MenuItem("Refresh", nullptr, nullptr, false);
251 ImGui::EndMenu();
252 }
253 ImGui::EndMenuBar();
254 }
255
256 for (const Directory& dir : _currentDirectories) {
257 printDirectoryStructure(dir, true);
258 }
259
260 ImGui::EndChild();
261 }
262 ImGui::SameLine();
263 {
264 ImGui::BeginChild("Contents", ImVec2(0, -1), true, ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_HorizontalScrollbar);
265 if (ImGui::BeginMenuBar()) {
266 if (ImGui::BeginMenu("Menu")) {
267 ImGui::MenuItem("Refresh", nullptr, nullptr, false);
268 ImGui::EndMenu();
269 }
270 ImGui::EndMenuBar();
271 }
272
273 if (_selectedDir != nullptr)
274 {
275 ImGui::Columns(CLAMPED(to_I32(_selectedDir->_files.size()), 1, 4));
276 bool lockTextureQueue = false;
277
278 for (const auto& file : _selectedDir->_files)
279 {
280 Handle<Texture> tex = INVALID_HANDLE<Texture>;
281 { // Textures
282 for (const char* extension : g_imageExtensions)
283 {
284 if (Util::CompareIgnoreCase(file._extension.c_str(), extension))
285 {
286 const string path = (_selectedDir->_path / file._path).string();
287
288 const auto it = _loadedTextures.find(_ID(path));
289 if (it == std::cend(_loadedTextures) || it->second == INVALID_HANDLE<Texture>)
290 {
292 {
294 {
295 ._path = _selectedDir->_path,
296 ._file =
297 {
298 ._path = file._path
299 }
300 };
301 _textureLoadQueue.push( entry );
302 lockTextureQueue = true;
303 }
304 }
305 else if (Get(it->second)->getState() == ResourceState::RES_LOADED)
306 {
307 tex = it->second;
308 }
309 break;
310 }
311 }
312 }
313
314 ImGui::PushID( _selectedDir->_path.fileSystemPath().c_str() );
315
316 const GeometryFormat format = tex != INVALID_HANDLE<Texture> ? GeometryFormat::COUNT : GetGeometryFormatForExtension(file._extension.c_str());
317
318 bool hasTooltip = false;
319 if (tex != INVALID_HANDLE<Texture>)
320 {
321 const U16 w = Get(tex)->width();
322 const U16 h = Get(tex)->height();
323 const F32 aspect = w / to_F32(h);
324
325 if (ImGui::ImageButton(Get(tex)->resourceName().c_str(), to_TexID(tex), ImVec2(buttonSize, buttonSize / aspect), uv0, uv1))
326 {
328 _previewTexture = tex;
329 previewTexture = true;
330 }
331 }
332 else if (format != GeometryFormat::COUNT)
333 {
334 const Handle<Texture> icon = _geometryIcons[to_base(format)];
335 const U16 w = Get(icon)->width();
336 const U16 h = Get(icon)->height();
337 const F32 aspect = w / to_F32(h);
338
339 const bool modifierPressed = imguiContext.IO.KeyShift;
340 const ImVec4 bgColour(modifierPressed ? 1.f : 0.f, 0.f, 0.f, modifierPressed ? 1.f : 0.f);
341 if (ImGui::ImageButton(Get(icon)->resourceName().c_str(), to_TexID(icon), ImVec2(buttonSize, buttonSize / aspect), uv0, uv1, bgColour, ImVec4(1, 1, 1, 1)))
342 {
345 if ( _spawnMesh == INVALID_HANDLE<Mesh>)
346 {
347 Attorney::EditorGeneralWidget::showStatusMessage(_parent, "ERROR: Couldn't load specified mesh!", Time::SecondsToMilliseconds<F32>(3), true);
348 }
349 }
350 hasTooltip = true;
351 if (ImGui::IsItemHovered())
352 {
353 ImGui::SetTooltip("Hold down [Shift] to spawn directly at the camera position");
354 }
355 }
356 else if (isSoundFile(file._extension.c_str()))
357 {
358 const U16 w = Get(_soundIcon)->width();
359 const U16 h = Get(_soundIcon)->height();
360 const F32 aspect = w / to_F32(h);
361
362 if (ImGui::ImageButton(Get(_soundIcon)->resourceName().c_str(), to_TexID(_soundIcon), ImVec2(buttonSize, buttonSize / aspect), uv0, uv1))
363 {
364 //ToDo: Play sound file -Ionut
365 }
366 } else if (isShaderFile(file._extension.c_str())) {
367 const U16 w = Get(_shaderIcon)->width();
368 const U16 h = Get(_shaderIcon)->height();
369 const F32 aspect = w / to_F32(h);
370
371 if (ImGui::ImageButton(Get(_shaderIcon)->resourceName().c_str(), to_TexID(_shaderIcon), ImVec2(buttonSize, buttonSize / aspect), uv0, uv1))
372 {
373 openFileInEditor(_selectedDir->_path, file);
374 }
375 }
376 else
377 {
378 const U16 w = Get(_fileIcon)->width();
379 const U16 h = Get(_fileIcon)->height();
380 const F32 aspect = w / to_F32(h);
381
382 if (ImGui::ImageButton(Get(_fileIcon)->resourceName().c_str(), to_TexID(_fileIcon), ImVec2(buttonSize, buttonSize / aspect), uv0, uv1))
383 {
384 openFileInEditor( _selectedDir->_path, file);
385 }
386 }
387
388 static string pathString = "";
389
390 pathString = file._path.string();
391 if (!hasTooltip && ImGui::IsItemHovered())
392 {
393 ImGui::SetTooltip( pathString.c_str() );
394 }
395
396 ImGui::Text( pathString.c_str() );
397
398 ImGui::PopID();
399 ImGui::NextColumn();
400 if (lockTextureQueue)
401 {
403 }
404 }
405 }
406 ImGui::EndChild();
407 }
408
409 if ( previewTexture && Attorney::EditorGeneralWidget::modalTextureView(_parent, "Image Preview", _previewTexture, vec2<F32>(512, 512), true, true)) {
410 previewTexture = false;
411 }
412
413 const Camera* playerCam = Attorney::ProjectManagerCameraAccessor::playerCamera(_parent.context().kernel().projectManager().get());
414 if ( spawnMesh && Attorney::EditorGeneralWidget::modalModelSpawn(_parent, _spawnMesh, imguiContext.IO.KeyShift, VECTOR3_UNIT, playerCam->snapshot()._eye)) {
415 spawnMesh = false;
416 }
417
418 ImGui::PopStyleVar();
419 }
420
421 Handle<Texture> ContentExplorerWindow::getTextureForPath(const ResourcePath& texturePath, const std::string_view textureName) const
422 {
423 ResourceDescriptor<Texture> textureResource(textureName);
424 textureResource.assetName( textureName );
425 textureResource.assetLocation(texturePath);
426
428 descriptor._textureOptions._useDDSCache = false;
429
430 return CreateResource(textureResource);
431 }
432
433 Handle<Mesh> ContentExplorerWindow::getModelForPath(const ResourcePath& modelPath, const std::string_view modelName) const
434 {
435 ResourceDescriptor<Mesh> model(modelName);
436 model.assetLocation(modelPath);
437 model.assetName( modelName );
438 model.flag(true);
439
440 return CreateResource(model);
441 }
442} //namespace Divide
#define PROFILE_SCOPE_AUTO(CATEGORY)
Definition: Profiler.h:87
static bool modalTextureView(const Editor &editor, const std::string_view modalName, Handle< Texture > tex, const vec2< F32 > dimensions, const bool preserveAspect, const bool useModal)
Definition: Editor.h:676
static const ResourcePath & externalTextEditorPath(const Editor &editor) noexcept
Definition: Editor.h:716
static void showStatusMessage(const Editor &editor, const string &message, const F32 durationMS, const bool error)
Definition: Editor.h:711
static bool modalModelSpawn(Editor &editor, Handle< Mesh > mesh, bool quick, const vec3< F32 > &scale=VECTOR3_UNIT, const vec3< F32 > &position=VECTOR3_ZERO)
Definition: Editor.h:681
static ImGuiContext & getImGuiContext(Editor &editor, const Editor::ImGuiContextType type) noexcept
Definition: Editor.h:686
static Camera * playerCamera(const Divide::ProjectManager *mgr, const bool skipOverride=false) noexcept
const CameraSnapshot & snapshot() const noexcept
Returns the internal camera snapshot data (eye, orientation, etc)
Definition: Camera.inl:43
eastl::stack< EditorFileEntry > _textureLoadQueue
std::array< Handle< Texture >, to_base(GeometryFormat::COUNT)+1 > _geometryIcons
void getDirectoryStructureForPath(const ResourcePath &directoryPath, Directory &directoryOut) const
ContentExplorerWindow(Editor &parent, const Descriptor &descriptor)
vector< Directory > _currentDirectories
void printDirectoryStructure(const Directory &dir, bool open) const
Handle< Texture > getTextureForPath(const ResourcePath &texturePath, std::string_view textureName) const
Handle< Mesh > getModelForPath(const ResourcePath &modelPath, std::string_view modelName) const
hashMap< size_t, Handle< Texture > > _loadedTextures
const Descriptor & descriptor() const noexcept
Definition: DockedWindow.h:69
PlatformContext & context() noexcept
Kernel & kernel() noexcept
bool UseUpperLeftOrigin() noexcept
Definition: ImageTools.cpp:202
constexpr Optick::Category::Type GUI
Definition: Profiler.h:64
bool CompareIgnoreCase(const char *a, const char *b) noexcept
Handle console commands that start with a forward slash.
Definition: AIProcessor.cpp:7
ImTextureID to_TexID(Handle< Texture > handle)
Definition: Editor.cpp:116
FileError openFile(const std::string_view cmd, const ResourcePath &filePath, const std::string_view fileName)
FORCE_INLINE void DestroyResource(Handle< T > &handle, const bool immediate=false)
ResourcePath getTopLevelFolderName(const ResourcePath &filePath)
bool hasExtension(const ResourcePath &filePath, const std::string_view extensionNoDot)
const char *const g_geometryExtensions[]
Definition: MeshImporter.h:60
uint8_t U8
@ RES_LOADED
The resource is available for usage.
constexpr F32 to_F32(const T value)
GeometryFormat
Definition: MeshImporter.h:43
string getExtension(const std::string_view fileName)
GeometryFormat GetGeometryFormatForExtension(const char *extension) noexcept
static const vec3< F32 > VECTOR3_UNIT
Definition: MathVectors.h:1437
Project & parent
Definition: DefaultScene.h:41
uint16_t U16
constexpr U64 _ID(const char *const str, const U64 value=val_64_const) noexcept
FORCE_INLINE Handle< T > CreateResource(const ResourceDescriptor< T > &descriptor, bool &wasInCache, std::atomic_uint &taskCounter)
::value constexpr T CLAMPED(T n, T min, T max) noexcept
Definition: MathHelper.inl:126
constexpr I32 to_I32(const T value)
FORCE_INLINE T * Get(const Handle< T > handle)
Project const SceneEntry & entry
Definition: DefaultScene.h:41
uint64_t U64
constexpr auto to_base(const Type value) -> Type
vector< Directory_uptr > _children
PropertyDescriptor< T > _propertyDescriptor
Definition: Resource.h:151
StringReturnType< N > string() const noexcept
Definition: ResourcePath.h:64
bool empty() const noexcept