Rebuild trinity visualizer from bare SDL and OpenGL to using Raylib. Added Code for serial parsing on linux. Current functionality reads incoming quaternion packet data coming in over serial and displays the values and also visualizes with a cube, connect and disconnect is implemented. Essentially rebuild the functionality of the old version with added linux support.

This commit is contained in:
2026-09-13 19:44:26 +02:00
commit 62abf4d5d6
821 changed files with 239694 additions and 0 deletions
@@ -0,0 +1,280 @@
/*******************************************************************************************
*
* raylib-extras [ImGui] example - asset browser
*
* This is a more complex ImGui Integration
* It shows how to build windows on top of 2d and 3d views using a render texture
*
* Copyright (c) 2024 Jeffery Myers
*
********************************************************************************************/
#define _CRT_NONSTDC_NO_WARNINGS
#include "asset_browser.h"
#include "imgui_utils.h"
#include "imgui.h"
#include "imgui_internal.h"
#include "raylib.h"
#include "extras/IconsFontAwesome6.h"
#include <algorithm>
AssetBrowserPanel::AssetBrowserPanel()
{
AssetRoot = GetWorkingDirectory();
RebuildFolderTree();
SetCurrentFolder(&FolderRoot);
CurrentView = &ListView;
}
void AssetBrowserPanel::Show()
{
ShowHeader();
ImGuiTableFlags flags = ImGuiTableFlags_Resizable | ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_Borders;
if (ImGui::BeginTable("AssetBrowserTab", 2, flags, ImGui::GetContentRegionAvail()))
{
ImGui::TableSetupColumn("FolderView", ImGuiTableColumnFlags_None, 0.25f);
ImGui::TableSetupColumn("AssetView", ImGuiTableColumnFlags_None, 0.75f);
ImGui::TableNextRow();
ImGui::TableNextColumn();
if (ImGui::BeginChild("FolderList", ImGui::GetContentRegionAvail(),ImGuiChildFlags_None, ImGuiWindowFlags_None))
{
ShowFolderTree();
ImGui::EndChild();
}
ImGui::TableNextColumn();
ShowFilePane();
ImGui::EndTable();
}
}
void AssetBrowserPanel::RebuildFolderTree()
{
FolderRoot.Children.clear();
FolderRoot.FullPath = AssetRoot;
FolderRoot.Name = GetFileNameWithoutExt(AssetRoot.c_str());
FolderRoot.Parent = nullptr;
FolderRoot.Icon = ICON_FA_SERVER;
FolderRoot.ForceOpenNextFrame = true;
FolderRoot.PopulateChildren();
}
void AssetBrowserPanel::SetCurrentFolder(FolderInfo* folder)
{
if (CurrentFolderContents.Folder == folder)
return;
CurrentFolderContents.Folder = folder;
CurrentFolderContents.Files.clear();
if (folder == nullptr)
return;
FolderInfo* openFolder = folder;
while (openFolder != nullptr)
{
openFolder->ForceOpenNextFrame = true;
openFolder = openFolder->Parent;
}
auto files = LoadDirectoryFiles(CurrentFolderContents.Folder->FullPath.c_str());
for (unsigned int i = 0; i < files.count; i++)
{
if (DirectoryExists(files.paths[i]))
continue;
const char* name = GetFileName(files.paths[i]);
if (!name || *name == '.')
continue;
FileInfo& file = CurrentFolderContents.Files.emplace_back();
file.FullPath = files.paths[i];
file.Name = name;
file.Icon = GetFileIcon(name);
}
UnloadDirectoryFiles(files);
}
void AssetBrowserPanel::FolderInfo::PopulateChildren()
{
constexpr Color folderColor = { 255,255,145,255 };
auto folders = LoadDirectoryFiles(FullPath.c_str());
for (unsigned int i = 0; i < folders.count; i++)
{
if (DirectoryExists(folders.paths[i]))
{
const char* name = GetFileNameWithoutExt(folders.paths[i]);
if (!name || *name == '.')
continue;
FolderInfo& child = Children.emplace_back();
child.FullPath = folders.paths[i];
child.Name = name;
child.Parent = this;
child.Tint = folderColor;
child.Icon = ICON_FA_FOLDER;
child.PopulateChildren();
}
}
UnloadDirectoryFiles(folders);
}
bool AssetBrowserPanel::ShowFolderTreeNode(FolderInfo& info)
{
ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick;
if (info.Children.empty())
flags |= ImGuiTreeNodeFlags_Leaf;
if (CurrentFolderContents.Folder == &info)
flags |= ImGuiTreeNodeFlags_Selected;
if (info.ForceOpenNextFrame)
{
ImGui::SetNextItemOpen(true);
}
bool open = ImGui::TreeNodeEx(info.Name.c_str(), flags, "%s %s", info.Icon.c_str(), info.Name.c_str());
if (info.ForceOpenNextFrame && CurrentFolderContents.Folder == &info)
ImGui::ScrollToItem(ImGuiScrollFlags_KeepVisibleCenterY);
info.ForceOpenNextFrame = false;
if (ImGui::IsItemClicked())
SetCurrentFolder(&info);
if (open)
{
for (auto& node : info.Children)
ShowFolderTreeNode(node);
ImGui::TreePop();
}
return CurrentFolderContents.Folder == &info;
}
void AssetBrowserPanel::ShowHeader()
{
ImGui::PushStyleVar(ImGuiStyleVar_ChildBorderSize, 1);
if (ImGui::BeginChild("Header", ImVec2{ ImGui::GetContentRegionAvail().x, ImGui::GetFrameHeight() }))
{
ImGui::Text("%s Root", ICON_FA_FOLDER_OPEN);
ImGui::SameLine();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x * 0.33f);
ImGui::InputText("###Path", (char*)(CurrentFolderContents.Folder->FullPath.c_str() + AssetRoot.size()), CurrentFolderContents.Folder->FullPath.size(), ImGuiInputTextFlags_ReadOnly);
ImGui::EndChild();
}
ImGui::PopStyleVar();
}
void AssetBrowserPanel::ShowFolderTree()
{
ShowFolderTreeNode(FolderRoot);
}
void AssetBrowserPanel::ShowFilePane()
{
if (ImGui::BeginChild("FileList", ImGui::GetContentRegionAvail(), ImGuiChildFlags_None, ImGuiWindowFlags_None))
{
if (CurrentView)
{
auto *item = CurrentView->Show(CurrentFolderContents);
if (item)
{
AssetItemInfo* assetItem = static_cast<AssetItemInfo*>(item);
if (!assetItem->IsFile())
SetCurrentFolder(static_cast<FolderInfo*>(item));
}
}
ImGui::EndChild();
}
}
ViewableItem* AssetBrowserPanel::AssetContainer::Reset()
{
FileItr = Files.begin();
FolderItr = Folder->Children.begin();
if (FileItr != Files.end())
return &(*FileItr);
if (FolderItr != Folder->Children.end())
return &(*FolderItr);
return nullptr;
}
size_t AssetBrowserPanel::AssetContainer::Count()
{
return Files.size() + Folder->Children.size();
}
ViewableItem* AssetBrowserPanel::AssetContainer::Next()
{
if (FileItr != Files.end())
{
FileItr++;
if (FileItr != Files.end())
return &(*FileItr);
else
{
if (FolderItr != Folder->Children.end())
return &(*FolderItr);
else
return nullptr;
}
}
if (FolderItr != Folder->Children.end())
{
FolderItr++;
if (FolderItr != Folder->Children.end())
return &(*FolderItr);
}
return nullptr;
}
const char* AssetBrowserPanel::GetFileIcon(const char* filename)
{
const char* e = GetFileExtension(filename);
if (e == nullptr)
return ICON_FA_FILE;
std::string ext = e;
std::transform(ext.begin(), ext.end(), ext.begin(),
[](unsigned char c) { return std::tolower(c); } // correct
);
if (!ext.empty())
{
if (ext==".png")
return ICON_FA_FILE_IMAGE;
if (ext==".wav" || ext==".mp3" || ext==".oog")
return ICON_FA_FILE_AUDIO;
if (ext==".ttf" || ext==".otf" || ext==".fnt")
return ICON_FA_FONT;
if (ext==".txt" || ext==".md")
return ICON_FA_FILE_LINES;
if (ext==".lua" || ext==".c" || ext==".h" || ext==".cpp")
return ICON_FA_FILE_CODE;
}
return ICON_FA_FILE;
}
@@ -0,0 +1,95 @@
/*******************************************************************************************
*
* raylib-extras [ImGui] example - asset browser
*
* This is a more complex ImGui Integration
* It shows how to build windows on top of 2d and 3d views using a render texture
*
* Copyright (c) 2024 Jeffery Myers
*
********************************************************************************************/
#pragma once
#include "item_view.h"
#include "raylib.h"
#include <string>
#include <vector>
#include <list>
class AssetBrowserPanel
{
public:
AssetBrowserPanel();
void Show();
private:
std::string AssetRoot;
class AssetItemInfo : public ViewableItem
{
protected:
bool File = false;
public:
AssetItemInfo(bool file) : File(file) {}
bool IsFile() const { return File; }
};
class FileInfo : public AssetItemInfo
{
public:
FileInfo() : AssetItemInfo(true) {}
std::string FullPath;
};
class FolderInfo : public AssetItemInfo
{
public:
FolderInfo() : AssetItemInfo(false) {}
std::string FullPath;
FolderInfo* Parent = nullptr;
std::list<FolderInfo> Children;
bool ForceOpenNextFrame = false;
void PopulateChildren();
};
FolderInfo FolderRoot;
class AssetContainer : public ViewableItemContainer
{
public:
ViewableItem* Reset() override;
size_t Count() override;
ViewableItem* Next() override;
FolderInfo* Folder = nullptr;
std::vector<FileInfo> Files;
std::vector<FileInfo>::iterator FileItr;
std::list<FolderInfo>::iterator FolderItr;
};
AssetContainer CurrentFolderContents;
ListItemView ListView;
ItemView* CurrentView = nullptr;
void RebuildFolderTree();
void SetCurrentFolder(FolderInfo* folder);
bool ShowFolderTreeNode(FolderInfo& folder);
void ShowFolderTree();
void ShowFilePane();
void ShowHeader();
const char* GetFileIcon(const char* filename);
};
@@ -0,0 +1,94 @@
/*******************************************************************************************
*
* raylib-extras [ImGui] example - asset browser
*
* This is a more complex ImGui Integration
* It shows how to build windows on top of 2d and 3d views using a render texture
*
* Copyright (c) 2024 Jeffery Myers
*
********************************************************************************************/
#include "imgui_utils.h"
#include "imgui.h"
#include "imgui_internal.h"
namespace ImGuiUtils
{
bool IsSpace(char aCharacter)
{
// all space characters are values 32 or less (space is 32)
// so we can convert them to a bitmask and use a single condition
const int mask = (1 << (' ' - 1)) | (1 << ('\f' - 1)) | (1 << ('\n' - 1)) | (1 << ('\r' - 1)) | (1 << ('\t' - 1)) | (1 << ('\v' - 1));
return (mask & (1 << ((aCharacter && aCharacter <= 32) * (aCharacter - 1)))) != 0;
}
//-------------------------------------------------------------------------------------------------
// Todo: Add support for soft-hyphens when using word boundaries?
//-------------------------------------------------------------------------------------------------
void TextWithEllipsis(const char* string, float aMaxWidth, bool useWordBoundaries, float aSpacing)
{
char const* partStart = string;
char const* partEnd = string;
ImWchar elipsisChar = ImGui::GetFont()->EllipsisChar;
char elipsisText[8];
ImTextStrToUtf8(elipsisText, sizeof(elipsisText), &elipsisChar, (&elipsisChar) + 1);
if (aSpacing < 0.0f) aSpacing = ImGui::GetStyle().ItemSpacing.x;
float const ellipsisWidth = ImGui::CalcTextSize(elipsisText).x + aSpacing;
float width = 0;
bool addElipsis = false;
while (*partStart != 0 )
{
// Add space to next segment
while (IsSpace(*partEnd))
partEnd++;
if (useWordBoundaries)
{
// get next 'word' by looking for space after non-space
while (*partEnd != 0 && !IsSpace(*partEnd))
++partEnd;
}
else
{
if (*partEnd != 0)
++partEnd;
}
ImVec2 const wordSize = ImGui::CalcTextSize(partStart, partEnd);
// Clearly we have space for this word so just add it
if (wordSize.x + width + ellipsisWidth < aMaxWidth)
{
width += wordSize.x;
partStart = partEnd;
}
// If we're just at the end of the word and we just fit then we can commit here
else if (*partEnd == 0 && wordSize.x + width < aMaxWidth)
{
width += wordSize.x;
partStart = partEnd;
}
// we're done so add elipsis where the current segment starts
else
{
addElipsis = true;
break;
}
}
ImGui::TextUnformatted(string, partStart);
if (addElipsis)
{
ImGui::SameLine(0.0f, aSpacing);
ImGui::TextUnformatted(elipsisText);
}
}
}
@@ -0,0 +1,33 @@
/*******************************************************************************************
*
* raylib-extras [ImGui] example - asset browser
*
* This is a more complex ImGui Integration
* It shows how to build windows on top of 2d and 3d views using a render texture
*
* Copyright (c) 2024 Jeffery Myers
*
********************************************************************************************/
#pragma once
#include <string>
#include "raylib.h"
namespace ImGuiUtils
{
void TextWithEllipsis(const char* string, float maxWidth, bool useWordBoundaries = false, float aSpacing = 0);
// DPI scaling functions
inline float ScaleToDPIF(float value)
{
return GetWindowScaleDPI().x * value;
}
inline int ScaleToDPII(int value)
{
return int(GetWindowScaleDPI().x * value);
}
}
@@ -0,0 +1,47 @@
/*******************************************************************************************
*
* raylib-extras [ImGui] example - asset browser
*
* This is a more complex ImGui Integration
* It shows how to build windows on top of 2d and 3d views using a render texture
*
* Copyright (c) 2024 Jeffery Myers
*
********************************************************************************************/
#pragma once
#include <string>
#include "raylib.h"
class ViewableItem
{
public:
virtual ~ViewableItem() = default;
std::string Name;
std::string Icon;
Color Tint = BLANK;
};
class ViewableItemContainer
{
public:
virtual ~ViewableItemContainer() = default;
virtual ViewableItem* Reset() = 0;
virtual size_t Count() = 0;
virtual ViewableItem* Next() = 0;
};
class ItemView
{
public:
virtual ~ItemView() = default;
virtual ViewableItem* Show(ViewableItemContainer& container) = 0;
};
class ListItemView : public ItemView
{
public:
ViewableItem* Show(ViewableItemContainer& container) override;
};
@@ -0,0 +1,53 @@
/*******************************************************************************************
*
* raylib-extras [ImGui] example - asset browser
*
* This is a more complex ImGui Integration
* It shows how to build windows on top of 2d and 3d views using a render texture
*
* Copyright (c) 2024 Jeffery Myers
*
********************************************************************************************/
#include "item_view.h"
#include "imgui.h"
#include "imgui_utils.h"
#include "rlImGuiColors.h"
#include "raylib.h"
extern ImFont* IconFont;
ViewableItem* ListItemView::Show(ViewableItemContainer& container)
{
ViewableItem* item = container.Reset();
ViewableItem* selected = nullptr;
while (item)
{
float x = ImGui::GetCursorPosX();
const char* name = TextFormat("###%s", item->Name.c_str());
if (item->Tint.a > 0)
ImGui::TextColored(rlImGuiColors::Convert(item->Tint), " %s", item->Icon.c_str());
else
ImGui::Text(" %s", item->Icon.c_str());
ImGui::SameLine(0, 0);
ImGui::Text(" %s", item->Name.c_str());
ImGui::SameLine(0, 0);
ImGui::SetCursorPosX(x);
//ImGui::SetItemAllowOverlap();
ImGui::Selectable(name);
if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(0))
{
selected = item;
}
item = container.Next();
}
return selected;
}
@@ -0,0 +1,95 @@
/*******************************************************************************************
*
* raylib-extras [ImGui] example - asset browser
*
* This is a more complex ImGui Integration
* It shows how to build windows on top of 2d and 3d views using a render texture
*
* Copyright (c) 2024 Jeffery Myers
*
********************************************************************************************/
#include "raylib.h"
#include "raymath.h"
#include "imgui.h"
#include "rlImGui.h"
#include "rlImGuiColors.h"
#include "extras/FA6FreeSolidFontData.h"
#include "asset_browser.h"
#include <limits>
ImFont* IconFont = nullptr;
int main(int argc, char* argv[])
{
// Initialization
//--------------------------------------------------------------------------------------
int screenWidth = 1280;
int screenHeight = 800;
SetConfigFlags(FLAG_MSAA_4X_HINT | FLAG_VSYNC_HINT);
InitWindow(screenWidth, screenHeight, "raylib-Extras [ImGui] example - Asset browser");
SetTargetFPS(144);
rlImGuiBeginInitImGui();
ImGui::StyleColorsDark();
static const ImWchar icons_ranges[] = { ICON_MIN_FA, ICON_MAX_FA, 0 };
ImFontConfig icons_config;
icons_config.MergeMode = true;
icons_config.PixelSnapH = true;
icons_config.FontDataOwnedByAtlas = false;
icons_config.GlyphMaxAdvanceX = std::numeric_limits<float>::max();
icons_config.RasterizerMultiply = 1.0f;
icons_config.OversampleH = 2;
icons_config.OversampleV = 1;
icons_config.GlyphRanges = icons_ranges;
ImGuiIO& io = ImGui::GetIO();
io.Fonts->AddFontFromMemoryCompressedTTF((void*)fa_solid_900_compressed_data, fa_solid_900_compressed_size, 12, &icons_config, icons_ranges);
icons_config.MergeMode = false;
IconFont = io.Fonts->AddFontFromMemoryCompressedTTF((void*)fa_solid_900_compressed_data, fa_solid_900_compressed_size, 72, &icons_config, icons_ranges);
rlImGuiEndInitImGui();
AssetBrowserPanel assetBrowser;
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
{
BeginDrawing();
ClearBackground(DARKGRAY);
rlImGuiBegin();
ImGui::SetNextWindowPos(ImVec2(0, 0));
ImGui::SetNextWindowSize(ImVec2(float(GetScreenWidth()), float(GetScreenHeight())));
if (ImGui::Begin("Frame", 0, ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoSavedSettings))
{
assetBrowser.Show();
}
ImGui::End();
rlImGuiEnd();
EndDrawing();
//----------------------------------------------------------------------------------
}
rlImGuiShutdown();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}
@@ -0,0 +1,117 @@
/*******************************************************************************************
*
* raylib-extras [ImGui] example - Docking example
*
* This is an example of using the ImGui docking features that are part of docking branch
* You must replace the default imgui with the code from the docking branch for this to work
* https://github.com/ocornut/imgui/tree/docking
*
* Copyright (c) 2024 Jeffery Myers
*
********************************************************************************************/
#include "raylib.h"
#include "raymath.h"
#include "imgui.h"
#include "rlImGui.h"
// DPI scaling functions
float ScaleToDPIF(float value)
{
return GetWindowScaleDPI().x * value;
}
int ScaleToDPII(int value)
{
return int(GetWindowScaleDPI().x * value);
}
int main(int argc, char* argv[])
{
// Initialization
//--------------------------------------------------------------------------------------
int screenWidth = 1280;
int screenHeight = 800;
// do not set the FLAG_WINDOW_HIGHDPI flag, that scales a low res framebuffer up to the native resolution.
// use the native resolution and scale your geometry.
SetConfigFlags(FLAG_MSAA_4X_HINT | FLAG_VSYNC_HINT | FLAG_WINDOW_RESIZABLE);
InitWindow(screenWidth, screenHeight, "raylib-Extras [ImGui] example - Docking");
SetTargetFPS(144);
rlImGuiSetup(true);
bool run = true;
bool showDemoWindow = true;
// if the linked ImGui has docking, enable it.
// this will only be true if you use the docking branch of ImGui.
#ifdef IMGUI_HAS_DOCK
ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_DockingEnable;
#endif
// Main game loop
while (!WindowShouldClose() && run) // Detect window close button or ESC key, or a quit from the menu
{
BeginDrawing();
ClearBackground(DARKGRAY);
// draw something to the raylib window below the GUI.
DrawCircle(GetScreenWidth() / 2, GetScreenHeight() / 2, GetScreenHeight() * 0.45f, DARKGREEN);
// start ImGui content
rlImGuiBegin();
// if you want windows to dock to the viewport, call this.
#ifdef IMGUI_HAS_DOCK
ImGui::DockSpaceOverViewport(0, NULL, ImGuiDockNodeFlags_PassthruCentralNode); // set ImGuiDockNodeFlags_PassthruCentralNode so that we can see the raylib contents behind the dockspace
#endif
// show a simple menu bar
if (ImGui::BeginMainMenuBar())
{
if (ImGui::BeginMenu("File"))
{
if (ImGui::MenuItem("Quit"))
run = false;
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Window"))
{
if (ImGui::MenuItem("Demo Window", nullptr, showDemoWindow))
showDemoWindow = !showDemoWindow;
ImGui::EndMenu();
}
ImGui::EndMainMenuBar();
}
// show some windows
if (showDemoWindow)
ImGui::ShowDemoWindow(&showDemoWindow);
if (ImGui::Begin("Test Window"))
{
ImGui::TextUnformatted("Another window");
}
ImGui::End();
// end ImGui Content
rlImGuiEnd();
EndDrawing();
//----------------------------------------------------------------------------------
}
rlImGuiShutdown();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}
@@ -0,0 +1,405 @@
/*******************************************************************************************
*
* raylib-extras [ImGui] example - editor
*
* This is a more complex ImGui Integration
* It shows how to build windows on top of 2d and 3d views using a render texture
*
* Copyright (c) 2021 Jeffery Myers
*
********************************************************************************************/
#include "raylib.h"
#include "raymath.h"
#include "imgui.h"
#include "rlImGui.h"
#include "rlImGuiColors.h"
bool Quit = false;
bool ImGuiDemoOpen = false;
// DPI scaling functions
float ScaleToDPIF(float value)
{
return GetWindowScaleDPI().x * value;
}
int ScaleToDPII(int value)
{
return int(GetWindowScaleDPI().x * value);
}
class DocumentWindow
{
public:
bool Open = false;
RenderTexture ViewTexture;
virtual void Setup() = 0;
virtual void Shutdown() = 0;
virtual void Show() = 0;
virtual void Update() = 0;
bool Focused = false;
Rectangle ContentRect = { 0 };
};
class ImageViewerWindow : public DocumentWindow
{
public:
void Setup() override
{
Camera.zoom = 1;
Camera.target.x = 0;
Camera.target.y = 0;
Camera.rotation = 0;
Camera.offset.x = GetScreenWidth() / 2.0f;
Camera.offset.y = GetScreenHeight() / 2.0f;
ViewTexture = LoadRenderTexture(GetScreenWidth(), GetScreenHeight());
ImageTexture = LoadTexture("resources/parrots.png");
UpdateRenderTexture();
}
void Show() override
{
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
ImGui::SetNextWindowSizeConstraints(ImVec2(ScaleToDPIF(400.0f), ScaleToDPIF(400.0f)), ImVec2(float(GetScreenWidth()), float(GetScreenHeight())));
Focused = false;
if (ImGui::Begin("Image Viewer", &Open, ImGuiWindowFlags_NoScrollbar))
{
// save off the screen space content rectangle
ContentRect = { ImGui::GetWindowPos().x + ImGui::GetCursorScreenPos().x, ImGui::GetWindowPos().y + ImGui::GetCursorScreenPos().y, ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y };
Focused = ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows);
ImVec2 size = ImGui::GetContentRegionAvail();
// center the scratch pad in the view
Rectangle viewRect = { 0 };
viewRect.x = ViewTexture.texture.width / 2 - size.x / 2;
viewRect.y = ViewTexture.texture.height / 2 - size.y / 2;
viewRect.width = size.x;
viewRect.height = -size.y;
if (ImGui::BeginChild("Toolbar", ImVec2(ImGui::GetContentRegionAvail().x, 25)))
{
ImGui::SetCursorPosX(2);
ImGui::SetCursorPosY(3);
if (ImGui::Button("None"))
{
CurrentToolMode = ToolMode::None;
}
ImGui::SameLine();
if (ImGui::Button("Move"))
{
CurrentToolMode = ToolMode::Move;
}
ImGui::SameLine();
switch (CurrentToolMode)
{
case ToolMode::None:
ImGui::TextUnformatted("No Tool");
break;
case ToolMode::Move:
ImGui::TextUnformatted("Move Tool");
break;
default:
break;
}
ImGui::SameLine();
ImGui::TextUnformatted(TextFormat("camera target X%f Y%f", Camera.target.x, Camera.target.y));
ImGui::EndChild();
}
rlImGuiImageRect(&ViewTexture.texture, int(size.x), int(size.y), viewRect);
}
ImGui::End();
ImGui::PopStyleVar();
}
void Update() override
{
if (!Open)
return;
if (IsWindowResized())
{
UnloadRenderTexture(ViewTexture);
ViewTexture = LoadRenderTexture(GetScreenWidth(), GetScreenHeight());
Camera.offset.x = GetScreenWidth() / 2.0f;
Camera.offset.y = GetScreenHeight() / 2.0f;
}
Vector2 mousePos = GetMousePosition();
if (Focused)
{
if (CurrentToolMode == ToolMode::Move)
{
// only do this tool when the mouse is in the content area of the window
if (IsMouseButtonDown(0) && CheckCollisionPointRec(mousePos, ContentRect))
{
if (!Dragging)
{
LastMousePos = mousePos;
LastTarget = Camera.target;
}
Dragging = true;
Vector2 mouseDelta = Vector2Subtract(LastMousePos, mousePos);
mouseDelta.x /= Camera.zoom;
mouseDelta.y /= Camera.zoom;
Camera.target = Vector2Add(LastTarget, mouseDelta);
DirtyScene = true;
}
else
{
Dragging = false;
}
}
}
else
{
Dragging = false;
}
if (DirtyScene)
{
DirtyScene = false;
UpdateRenderTexture();
}
}
Texture ImageTexture;
Camera2D Camera = { 0 };
Vector2 LastMousePos = { 0 };
Vector2 LastTarget = { 0 };
bool Dragging = false;
bool DirtyScene = false;
enum class ToolMode
{
None,
Move,
};
ToolMode CurrentToolMode = ToolMode::None;
void UpdateRenderTexture()
{
BeginTextureMode(ViewTexture);
ClearBackground(BLUE);
// camera with our view offset with a world origin of 0,0
BeginMode2D(Camera);
// center the image at 0,0
DrawTexture(ImageTexture, ImageTexture.width / -2, ImageTexture.height / -2, WHITE);
EndMode2D();
EndTextureMode();
}
void Shutdown() override
{
UnloadRenderTexture(ViewTexture);
UnloadTexture(ImageTexture);
}
};
class SceneViewWindow : public DocumentWindow
{
public:
Camera3D Camera = { 0 };
void Setup() override
{
ViewTexture = LoadRenderTexture(GetScreenWidth(), GetScreenHeight());
Camera.fovy = 45;
Camera.up.y = 1;
Camera.position.y = 3;
Camera.position.z = -25;
Image img = GenImageChecked(ScaleToDPII(256), ScaleToDPII(256), ScaleToDPII(32), ScaleToDPII(32), DARKGRAY, WHITE);
GridTexture = LoadTextureFromImage(img);
UnloadImage(img);
GenTextureMipmaps(&GridTexture);
SetTextureFilter(GridTexture, TEXTURE_FILTER_ANISOTROPIC_16X);
SetTextureWrap(GridTexture, TEXTURE_WRAP_CLAMP);
}
void Shutdown() override
{
UnloadRenderTexture(ViewTexture);
UnloadTexture(GridTexture);
}
void Show() override
{
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
ImGui::SetNextWindowSizeConstraints(ImVec2(ScaleToDPIF(400.0f), ScaleToDPIF(400.0f)), ImVec2((float)GetScreenWidth(), (float)GetScreenHeight()));
if (ImGui::Begin("3D View", &Open, ImGuiWindowFlags_NoScrollbar))
{
Focused = ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows);
// draw the view
rlImGuiImageRenderTextureFit(&ViewTexture, true);
}
ImGui::End();
ImGui::PopStyleVar();
}
void Update() override
{
if (!Open)
return;
if (IsWindowResized())
{
UnloadRenderTexture(ViewTexture);
ViewTexture = LoadRenderTexture(GetScreenWidth(), GetScreenHeight());
}
float period = 10;
float magnitude = 25;
Camera.position.x = sinf(float(GetTime() / period)) * magnitude;
BeginTextureMode(ViewTexture);
ClearBackground(SKYBLUE);
BeginMode3D(Camera);
// grid of cube trees on a plane to make a "world"
DrawPlane(Vector3{ 0, 0, 0 }, Vector2{ 50, 50 }, BEIGE); // simple world plane
float spacing = 4;
int count = 5;
for (float x = -count * spacing; x <= count * spacing; x += spacing)
{
for (float z = -count * spacing; z <= count * spacing; z += spacing)
{
Vector3 pos = { x, 0.5f, z };
Vector3 min = { x - 0.5f,0,z - 0.5f };
Vector3 max = { x + 0.5f,1,z + 0.5f };
DrawCube(Vector3{ x, 1.5f, z }, 1, 1, 1, GREEN);
DrawCube(Vector3{ x, 0.5f, z }, 0.25f, 1, 0.25f, BROWN);
}
}
EndMode3D();
EndTextureMode();
}
Texture2D GridTexture = { 0 };
};
ImageViewerWindow ImageViewer;
SceneViewWindow SceneView;
void DoMainMenu()
{
if (ImGui::BeginMainMenuBar())
{
if (ImGui::BeginMenu("File"))
{
if (ImGui::MenuItem("Exit"))
Quit = true;
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Window"))
{
ImGui::MenuItem("ImGui Demo", nullptr, &ImGuiDemoOpen);
ImGui::MenuItem("Image Viewer", nullptr, &ImageViewer.Open);
ImGui::MenuItem("3D View", nullptr, &SceneView.Open);
ImGui::EndMenu();
}
ImGui::EndMainMenuBar();
}
}
int main(int argc, char* argv[])
{
// Initialization
//--------------------------------------------------------------------------------------
int screenWidth = 1900;
int screenHeight = 900;
// do not set the FLAG_WINDOW_HIGHDPI flag, that scales a low res framebuffer up to the native resolution.
// use the native resolution and scale your geometry.
SetConfigFlags(FLAG_MSAA_4X_HINT | FLAG_VSYNC_HINT);
InitWindow(screenWidth, screenHeight, "raylib-Extras [ImGui] example - Editor Example");
SetTargetFPS(144);
rlImGuiSetup(true);
ImGui::GetIO().ConfigWindowsMoveFromTitleBarOnly = true;
ImageViewer.Setup();
ImageViewer.Open = true;
SceneView.Setup();
SceneView.Open = true;
// Main game loop
while (!WindowShouldClose() && !Quit) // Detect window close button or ESC key
{
ImageViewer.Update();
SceneView.Update();
BeginDrawing();
ClearBackground(DARKGRAY);
rlImGuiBegin();
DoMainMenu();
if (ImGuiDemoOpen)
ImGui::ShowDemoWindow(&ImGuiDemoOpen);
if (ImageViewer.Open)
ImageViewer.Show();
if (SceneView.Open)
SceneView.Show();
rlImGuiEnd();
EndDrawing();
//----------------------------------------------------------------------------------
}
rlImGuiShutdown();
ImageViewer.Shutdown();
SceneView.Shutdown();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}
@@ -0,0 +1,161 @@
// Dear ImGui: standalone example application for Raylib with OpenGL
// (Raylib is a simple learning library for game development)
// Learn about Dear ImGui:
// - FAQ https://dearimgui.com/faq
// - Getting Started https://dearimgui.com/getting-started
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
// - Introduction, links and more at the top of imgui.cpp
#include "imgui.h"
#include "imgui_impl_raylib.h"
#include "raylib.h"
#include <cstdint>
// DPI scaling functions
// Use these to scale any hardcoded values to the native display resolution
float ScaleToDPIF(float value)
{
return GetWindowScaleDPI().x * value;
}
int ScaleToDPII(int value)
{
return int(GetWindowScaleDPI().x * value);
}
// Main code
int main(int, char**)
{
// Setup raylib window
// do not set the FLAG_WINDOW_HIGHDPI flag, that scales a low res framebuffer up to the native resolution.
// use the native resolution and scale your geometry.
SetConfigFlags(FLAG_WINDOW_RESIZABLE);
InitWindow(1280, 720, "Dear ImGui Raylib(OpenGL) example");
// Setup Dear ImGui context
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO(); (void)io;
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
// tell ImGui the display scale
if (!IsWindowState(FLAG_WINDOW_HIGHDPI))
{
io.DisplayFramebufferScale.x = GetWindowScaleDPI().x;
io.DisplayFramebufferScale.y = GetWindowScaleDPI().y;
}
// Setup Dear ImGui style
ImGui::StyleColorsDark();
//ImGui::StyleColorsLight();
// Setup Platform/Renderer backends
ImGui_ImplRaylib_Init();
// Load Fonts
// - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them.
// - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple.
// - If the file cannot be loaded, the function will return a nullptr. Please handle those errors in your application (e.g. use an assertion, or display an error and quit).
// - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call.
// - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use Freetype for higher quality font rendering.
// - Read 'docs/FONTS.md' for more instructions and details.
// - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ !
// to properly scale for high DPI displays, we need to cale the font size by the dpi sale
static constexpr int DefaultFonSize = 13;
ImFontConfig defaultConfig;
defaultConfig.SizePixels = DefaultFonSize;
if (!IsWindowState(FLAG_WINDOW_HIGHDPI))
{
defaultConfig.SizePixels = defaultConfig.SizePixels * GetWindowScaleDPI().y;
defaultConfig.RasterizerMultiply = GetWindowScaleDPI().y;
}
defaultConfig.PixelSnapH = true;
io.Fonts->AddFontDefault(&defaultConfig);
//io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf", 18.0f);
//io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f);
//io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f);
//io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f);
// ImFont* font = io.Fonts->AddFontFromFileTTF("resources/driusstraight.ttf", ScaleToDPIF(18.0f), nullptr, io.Fonts->GetGlyphRangesJapanese());
// IM_ASSERT(font != nullptr);
// Our state
bool show_demo_window = true;
bool show_another_window = false;
ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
// Main loop
bool done = false;
while (!done)
{
// Poll and handle events (inputs, window resize, etc.)
// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data.
// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data.
// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
ImGui_ImplRaylib_ProcessEvents();
// Start the Dear ImGui frame
ImGui_ImplRaylib_NewFrame();
ImGui::NewFrame();
// 1. Show the big demo window (Most of the sample code is in ImGui::ShowDemoWindow()! You can browse its code to learn more about Dear ImGui!).
if (show_demo_window)
ImGui::ShowDemoWindow(&show_demo_window);
// 2. Show a simple window that we create ourselves. We use a Begin/End pair to create a named window.
{
static float f = 0.0f;
static int counter = 0;
ImGui::Begin("Hello, world!"); // Create a window called "Hello, world!" and append into it.
ImGui::Text("This is some useful text."); // Display some text (you can use a format strings too)
ImGui::Checkbox("Demo Window", &show_demo_window); // Edit bools storing our window open/close state
ImGui::Checkbox("Another Window", &show_another_window);
ImGui::SliderFloat("float", &f, 0.0f, 1.0f); // Edit 1 float using a slider from 0.0f to 1.0f
ImGui::ColorEdit3("clear color", &clear_color.x); // Edit 3 floats representing a color
if (ImGui::Button("Button")) // Buttons return true when clicked (most widgets return true when edited/activated)
counter++;
ImGui::SameLine();
ImGui::Text("counter = %d", counter);
ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
ImGui::End();
}
// 3. Show another simple window.
if (show_another_window)
{
ImGui::Begin("Another Window", &show_another_window); // Pass a pointer to our bool variable (the window will have a closing button that will clear the bool when clicked)
ImGui::Text("Hello from another window!");
if (ImGui::Button("Close Me"))
show_another_window = false;
ImGui::End();
}
// Rendering
ImGui::Render();
BeginDrawing();
ClearBackground(Color{ uint8_t(clear_color.x * 255), uint8_t(clear_color.y * 255), uint8_t(clear_color.z * 255), uint8_t(clear_color.w * 255) });
ImGui_ImplRaylib_RenderDrawData(ImGui::GetDrawData());
EndDrawing();
done = WindowShouldClose();
}
// Cleanup
ImGui_ImplRaylib_Shutdown();
ImGui::DestroyContext();
CloseWindow();
return 0;
}
@@ -0,0 +1,92 @@
/*******************************************************************************************
*
* raylib-extras [ImGui] example - Simple Integration
*
* This is a simple ImGui Integration
* It is done using C++ but with C style code
* It can be done in C as well if you use the C ImGui wrapper
* https://github.com/cimgui/cimgui
*
* Copyright (c) 2021 Jeffery Myers
*
********************************************************************************************/
#include "raylib.h"
#include "raymath.h"
#include "imgui.h"
#include "rlImGui.h"
// DPI scaling functions
float ScaleToDPIF(float value)
{
return GetWindowScaleDPI().x * value;
}
int ScaleToDPII(int value)
{
return int(GetWindowScaleDPI().x * value);
}
int main(int argc, char* argv[])
{
// Initialization
//--------------------------------------------------------------------------------------
int screenWidth = 1280;
int screenHeight = 800;
SetConfigFlags(FLAG_MSAA_4X_HINT | FLAG_VSYNC_HINT | FLAG_WINDOW_RESIZABLE);
InitWindow(screenWidth, screenHeight, "raylib-Extras [ImGui] example - simple ImGui Demo");
SetTargetFPS(144);
rlImGuiSetup(true);
Texture image = LoadTexture("resources/parrots.png");
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
{
BeginDrawing();
ClearBackground(DARKGRAY);
// start ImGui Conent
rlImGuiBegin();
// show ImGui Content
bool open = true;
ImGui::ShowDemoWindow(&open);
open = true;
if (ImGui::Begin("Test Window", &open))
{
ImGui::TextUnformatted(ICON_FA_JEDI);
rlImGuiImage(&image);
}
ImGui::End();
// end ImGui Content
rlImGuiEnd();
if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT))
DrawText("Prssed", 0, 0, 20, RED);
if (IsMouseButtonDown(MOUSE_BUTTON_LEFT))
DrawText("Down", 0, 20, 20, GREEN);
if (IsWindowFocused())
DrawText("Focused", 100, 20, 20, WHITE);
EndDrawing();
//----------------------------------------------------------------------------------
}
// De-Initialization
//--------------------------------------------------------------------------------------
rlImGuiShutdown();
UnloadTexture(image);
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}