├── .gitignore ├── LICENSE ├── README.md ├── blit.hlsl ├── dxutil.cpp ├── dxutil.h ├── imgui ├── imconfig.h ├── imgui.cpp ├── imgui.h ├── imgui_demo.cpp ├── imgui_draw.cpp ├── imgui_impl_dx11.cpp ├── imgui_impl_dx11.h ├── imgui_internal.h ├── stb_rect_pack.h ├── stb_textedit.h └── stb_truetype.h ├── main.cpp ├── scene.cpp ├── scene.h ├── trianglebin.sln ├── trianglebin.vcxproj ├── trianglebin.vcxproj.filters └── triangles.hlsl /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Compiled Dynamic libraries 12 | *.so 13 | *.dylib 14 | *.dll 15 | 16 | # Fortran module files 17 | *.mod 18 | *.smod 19 | 20 | # Compiled Static libraries 21 | *.lai 22 | *.la 23 | *.a 24 | *.lib 25 | 26 | # Executables 27 | *.exe 28 | *.out 29 | *.app 30 | 31 | # VS things 32 | *.VC.db 33 | *.VC.opendb 34 | x64 35 | .vs 36 | 37 | # imgui 38 | imgui.ini -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Nicolas Guillemot 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # trianglebin 2 | Experimenting with how triangles are binned by the rasterizer 3 | 4 | Video preview: 5 | 6 | [![trianglebin](http://i.imgur.com/roZnBir.png)](https://www.youtube.com/watch?v=hS52vE1M1qA "trianglebin") 7 | -------------------------------------------------------------------------------- /blit.hlsl: -------------------------------------------------------------------------------- 1 | struct VS_INPUT 2 | { 3 | uint VertexID : SV_VertexID; 4 | }; 5 | 6 | struct VS_OUTPUT 7 | { 8 | float4 Position : SV_Position; 9 | float2 TexCoord : TEXCOORD; 10 | }; 11 | 12 | struct PS_OUTPUT 13 | { 14 | float4 Color : SV_Target; 15 | }; 16 | 17 | Texture2D TrianglesSRV : register(t0); 18 | SamplerState TrianglesSMP : register(s0); 19 | 20 | VS_OUTPUT VSmain(VS_INPUT input) 21 | { 22 | VS_OUTPUT output; 23 | 24 | output.Position.x = (float)(input.VertexID / 2) * 4.0 - 1.0; 25 | output.Position.y = (float)(input.VertexID % 2) * 4.0 - 1.0; 26 | output.Position.z = 0.0; 27 | output.Position.w = 1.0; 28 | 29 | output.TexCoord.x = (float)(input.VertexID / 2) * 2.0; 30 | output.TexCoord.y = 1.0 - (float)(input.VertexID % 2) * 2.0; 31 | 32 | return output; 33 | } 34 | 35 | PS_OUTPUT PSmain(VS_OUTPUT input) 36 | { 37 | PS_OUTPUT output; 38 | output.Color = TrianglesSRV.Sample(TrianglesSMP, input.TexCoord); 39 | return output; 40 | } -------------------------------------------------------------------------------- /dxutil.cpp: -------------------------------------------------------------------------------- 1 | #include "dxutil.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | std::set> g_IgnoredAsserts; 12 | std::mutex g_IgnoredAssertsMutex; 13 | 14 | std::wstring WideFromMultiByte(const char* s) 15 | { 16 | int bufSize = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, s, -1, NULL, 0); 17 | CHECKWIN32(bufSize != 0); 18 | 19 | std::wstring ws(bufSize, 0); 20 | CHECKWIN32(MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, s, -1, &ws[0], bufSize)); 21 | ws.pop_back(); // remove null terminator 22 | return ws; 23 | } 24 | 25 | std::wstring WideFromMultiByte(const std::string& s) 26 | { 27 | return WideFromMultiByte(s.c_str()); 28 | } 29 | 30 | std::string MultiByteFromWide(const wchar_t* ws) 31 | { 32 | int bufSize = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, ws, -1, NULL, 0, NULL, NULL); 33 | CHECKWIN32(bufSize != 0); 34 | 35 | std::string s(bufSize, 0); 36 | CHECKWIN32(WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, ws, -1, &s[0], bufSize, NULL, NULL)); 37 | s.pop_back(); // remove null terminator 38 | return s; 39 | } 40 | 41 | std::string MultiByteFromWide(const std::wstring& ws) 42 | { 43 | return MultiByteFromWide(ws.c_str()); 44 | } 45 | 46 | std::string MultiByteFromHR(HRESULT hr) 47 | { 48 | _com_error err(hr); 49 | return MultiByteFromWide(err.ErrorMessage()); 50 | } 51 | 52 | bool detail_CheckHR(HRESULT hr, const char* file, const char* function, int line) 53 | { 54 | if (SUCCEEDED(hr)) 55 | { 56 | return true; 57 | } 58 | 59 | std::lock_guard lock(g_IgnoredAssertsMutex); 60 | 61 | if (g_IgnoredAsserts.find(std::make_tuple(file, function, line)) != g_IgnoredAsserts.end()) 62 | { 63 | return false; 64 | } 65 | 66 | std::wstring wfile = WideFromMultiByte(file); 67 | std::wstring wfunction = WideFromMultiByte(function); 68 | _com_error err(hr); 69 | 70 | std::wstring msg = std::wstring() + 71 | L"File: " + wfile + L"\n" + 72 | L"Function: " + wfunction + L"\n" + 73 | L"Line: " + std::to_wstring(line) + L"\n" + 74 | L"ErrorMessage: " + err.ErrorMessage() + L"\n"; 75 | 76 | int result = MessageBoxW(NULL, msg.c_str(), L"Error", MB_ABORTRETRYIGNORE); 77 | if (result == IDABORT) 78 | { 79 | ExitProcess(-1); 80 | } 81 | else if (result == IDRETRY) 82 | { 83 | DebugBreak(); 84 | } 85 | else if (result == IDIGNORE) 86 | { 87 | g_IgnoredAsserts.insert(std::make_tuple(file, function, line)); 88 | } 89 | 90 | return false; 91 | } 92 | 93 | void SimpleMessageBox_FatalError(const char* fmt, ...) 94 | { 95 | va_list vl; 96 | va_start(vl, fmt); 97 | 98 | int nc = _vscprintf(fmt, vl); 99 | std::unique_ptr chars = std::make_unique(nc + 1); 100 | vsnprintf_s(chars.get(), nc + 1, nc, fmt, vl); 101 | 102 | std::wstring wmsg = WideFromMultiByte(chars.get()); 103 | MessageBoxW(NULL, wmsg.c_str(), L"Fatal Error", MB_OK); 104 | 105 | va_end(vl); 106 | 107 | #ifdef _DEBUG 108 | DebugBreak(); 109 | #endif 110 | 111 | ExitProcess(-1); 112 | } 113 | 114 | bool detail_CheckWin32(BOOL okay, const char* file, const char* function, int line) 115 | { 116 | if (okay) 117 | { 118 | return true; 119 | } 120 | 121 | return detail_CheckHR(HRESULT_FROM_WIN32(GetLastError()), file, function, line); 122 | } -------------------------------------------------------------------------------- /dxutil.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define NOMINMAX 4 | #define WIN32_LEAN_AND_MEAN 5 | #include 6 | #undef GetWindowFont // because imgui 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | using Microsoft::WRL::ComPtr; 15 | 16 | std::wstring WideFromMultiByte(const char* s); 17 | std::wstring WideFromMultiByte(const std::string& s); 18 | std::string MultiByteFromWide(const wchar_t* s); 19 | std::string MultiByteFromWide(const std::wstring& s); 20 | 21 | std::string MultiByteFromHR(HRESULT hr); 22 | 23 | void SimpleMessageBox_FatalError(const char* fmt, ...); 24 | 25 | bool detail_CheckHR(HRESULT hr, const char* file, const char* function, int line); 26 | bool detail_CheckWin32(BOOL okay, const char* file, const char* function, int line); 27 | 28 | #define CHECKHR(hr) detail_CheckHR(hr, __FILE__, __FUNCSIG__, __LINE__) 29 | #define CHECKWIN32(okay) detail_CheckWin32(okay, __FILE__, __FUNCSIG__, __LINE__) -------------------------------------------------------------------------------- /imgui/imconfig.h: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // USER IMPLEMENTATION 3 | // This file contains compile-time options for ImGui. 4 | // Other options (memory allocation overrides, callbacks, etc.) can be set at runtime via the ImGuiIO structure - ImGui::GetIO(). 5 | //----------------------------------------------------------------------------- 6 | 7 | #pragma once 8 | 9 | //---- Define assertion handler. Defaults to calling assert(). 10 | //#define IM_ASSERT(_EXPR) MyAssert(_EXPR) 11 | 12 | //---- Define attributes of all API symbols declarations, e.g. for DLL under Windows. 13 | //#define IMGUI_API __declspec( dllexport ) 14 | //#define IMGUI_API __declspec( dllimport ) 15 | 16 | //---- Include imgui_user.h at the end of imgui.h 17 | //#define IMGUI_INCLUDE_IMGUI_USER_H 18 | 19 | //---- Don't implement default handlers for Windows (so as not to link with OpenClipboard() and others Win32 functions) 20 | //#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCS 21 | //#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCS 22 | 23 | //---- Don't implement help and test window functionality (ShowUserGuide()/ShowStyleEditor()/ShowTestWindow() methods will be empty) 24 | //#define IMGUI_DISABLE_TEST_WINDOWS 25 | 26 | //---- Don't define obsolete functions names 27 | //#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS 28 | 29 | //---- Implement STB libraries in a namespace to avoid conflicts 30 | //#define IMGUI_STB_NAMESPACE ImGuiStb 31 | 32 | //---- Define constructor and implicit cast operators to convert back<>forth from your math types and ImVec2/ImVec4. 33 | /* 34 | #define IM_VEC2_CLASS_EXTRA \ 35 | ImVec2(const MyVec2& f) { x = f.x; y = f.y; } \ 36 | operator MyVec2() const { return MyVec2(x,y); } 37 | 38 | #define IM_VEC4_CLASS_EXTRA \ 39 | ImVec4(const MyVec4& f) { x = f.x; y = f.y; z = f.z; w = f.w; } \ 40 | operator MyVec4() const { return MyVec4(x,y,z,w); } 41 | */ 42 | 43 | //---- Tip: You can add extra functions within the ImGui:: namespace, here or in your own headers files. 44 | //---- e.g. create variants of the ImGui::Value() helper for your low-level math types, or your own widgets/helpers. 45 | /* 46 | namespace ImGui 47 | { 48 | void Value(const char* prefix, const MyMatrix44& v, const char* float_format = NULL); 49 | } 50 | */ 51 | 52 | -------------------------------------------------------------------------------- /imgui/imgui_impl_dx11.cpp: -------------------------------------------------------------------------------- 1 | // ImGui Win32 + DirectX11 binding 2 | // In this binding, ImTextureID is used to store a 'ID3D11ShaderResourceView*' texture identifier. Read the FAQ about ImTextureID in imgui.cpp. 3 | 4 | // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. 5 | // If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). 6 | // If you are new to ImGui, see examples/README.txt and documentation at the top of imgui.cpp. 7 | // https://github.com/ocornut/imgui 8 | 9 | #include "imgui.h" 10 | #include "imgui_impl_dx11.h" 11 | 12 | // DirectX 13 | #include 14 | #include 15 | #define DIRECTINPUT_VERSION 0x0800 16 | #include 17 | 18 | // Data 19 | static INT64 g_Time = 0; 20 | static INT64 g_TicksPerSecond = 0; 21 | 22 | static HWND g_hWnd = 0; 23 | static ID3D11Device* g_pd3dDevice = NULL; 24 | static ID3D11DeviceContext* g_pd3dDeviceContext = NULL; 25 | static ID3D11Buffer* g_pVB = NULL; 26 | static ID3D11Buffer* g_pIB = NULL; 27 | static ID3D10Blob * g_pVertexShaderBlob = NULL; 28 | static ID3D11VertexShader* g_pVertexShader = NULL; 29 | static ID3D11InputLayout* g_pInputLayout = NULL; 30 | static ID3D11Buffer* g_pVertexConstantBuffer = NULL; 31 | static ID3D10Blob * g_pPixelShaderBlob = NULL; 32 | static ID3D11PixelShader* g_pPixelShader = NULL; 33 | static ID3D11SamplerState* g_pFontSampler = NULL; 34 | static ID3D11ShaderResourceView*g_pFontTextureView = NULL; 35 | static ID3D11RasterizerState* g_pRasterizerState = NULL; 36 | static ID3D11BlendState* g_pBlendState = NULL; 37 | static ID3D11DepthStencilState* g_pDepthStencilState = NULL; 38 | static int g_VertexBufferSize = 5000, g_IndexBufferSize = 10000; 39 | 40 | struct VERTEX_CONSTANT_BUFFER 41 | { 42 | float mvp[4][4]; 43 | }; 44 | 45 | // This is the main rendering function that you have to implement and provide to ImGui (via setting up 'RenderDrawListsFn' in the ImGuiIO structure) 46 | // If text or lines are blurry when integrating ImGui in your engine: 47 | // - in your Render function, try translating your projection matrix by (0.5f,0.5f) or (0.375f,0.375f) 48 | void ImGui_ImplDX11_RenderDrawLists(ImDrawData* draw_data) 49 | { 50 | ID3D11DeviceContext* ctx = g_pd3dDeviceContext; 51 | 52 | // Create and grow vertex/index buffers if needed 53 | if (!g_pVB || g_VertexBufferSize < draw_data->TotalVtxCount) 54 | { 55 | if (g_pVB) { g_pVB->Release(); g_pVB = NULL; } 56 | g_VertexBufferSize = draw_data->TotalVtxCount + 5000; 57 | D3D11_BUFFER_DESC desc; 58 | memset(&desc, 0, sizeof(D3D11_BUFFER_DESC)); 59 | desc.Usage = D3D11_USAGE_DYNAMIC; 60 | desc.ByteWidth = g_VertexBufferSize * sizeof(ImDrawVert); 61 | desc.BindFlags = D3D11_BIND_VERTEX_BUFFER; 62 | desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; 63 | desc.MiscFlags = 0; 64 | if (g_pd3dDevice->CreateBuffer(&desc, NULL, &g_pVB) < 0) 65 | return; 66 | } 67 | if (!g_pIB || g_IndexBufferSize < draw_data->TotalIdxCount) 68 | { 69 | if (g_pIB) { g_pIB->Release(); g_pIB = NULL; } 70 | g_IndexBufferSize = draw_data->TotalIdxCount + 10000; 71 | D3D11_BUFFER_DESC desc; 72 | memset(&desc, 0, sizeof(D3D11_BUFFER_DESC)); 73 | desc.Usage = D3D11_USAGE_DYNAMIC; 74 | desc.ByteWidth = g_IndexBufferSize * sizeof(ImDrawIdx); 75 | desc.BindFlags = D3D11_BIND_INDEX_BUFFER; 76 | desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; 77 | if (g_pd3dDevice->CreateBuffer(&desc, NULL, &g_pIB) < 0) 78 | return; 79 | } 80 | 81 | // Copy and convert all vertices into a single contiguous buffer 82 | D3D11_MAPPED_SUBRESOURCE vtx_resource, idx_resource; 83 | if (ctx->Map(g_pVB, 0, D3D11_MAP_WRITE_DISCARD, 0, &vtx_resource) != S_OK) 84 | return; 85 | if (ctx->Map(g_pIB, 0, D3D11_MAP_WRITE_DISCARD, 0, &idx_resource) != S_OK) 86 | return; 87 | ImDrawVert* vtx_dst = (ImDrawVert*)vtx_resource.pData; 88 | ImDrawIdx* idx_dst = (ImDrawIdx*)idx_resource.pData; 89 | for (int n = 0; n < draw_data->CmdListsCount; n++) 90 | { 91 | const ImDrawList* cmd_list = draw_data->CmdLists[n]; 92 | memcpy(vtx_dst, &cmd_list->VtxBuffer[0], cmd_list->VtxBuffer.size() * sizeof(ImDrawVert)); 93 | memcpy(idx_dst, &cmd_list->IdxBuffer[0], cmd_list->IdxBuffer.size() * sizeof(ImDrawIdx)); 94 | vtx_dst += cmd_list->VtxBuffer.size(); 95 | idx_dst += cmd_list->IdxBuffer.size(); 96 | } 97 | ctx->Unmap(g_pVB, 0); 98 | ctx->Unmap(g_pIB, 0); 99 | 100 | // Setup orthographic projection matrix into our constant buffer 101 | { 102 | D3D11_MAPPED_SUBRESOURCE mapped_resource; 103 | if (ctx->Map(g_pVertexConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped_resource) != S_OK) 104 | return; 105 | VERTEX_CONSTANT_BUFFER* constant_buffer = (VERTEX_CONSTANT_BUFFER*)mapped_resource.pData; 106 | float L = 0.0f; 107 | float R = ImGui::GetIO().DisplaySize.x; 108 | float B = ImGui::GetIO().DisplaySize.y; 109 | float T = 0.0f; 110 | float mvp[4][4] = 111 | { 112 | { 2.0f/(R-L), 0.0f, 0.0f, 0.0f }, 113 | { 0.0f, 2.0f/(T-B), 0.0f, 0.0f }, 114 | { 0.0f, 0.0f, 0.5f, 0.0f }, 115 | { (R+L)/(L-R), (T+B)/(B-T), 0.5f, 1.0f }, 116 | }; 117 | memcpy(&constant_buffer->mvp, mvp, sizeof(mvp)); 118 | ctx->Unmap(g_pVertexConstantBuffer, 0); 119 | } 120 | 121 | // Backup DX state that will be modified to restore it afterwards (unfortunately this is very ugly looking and verbose. Close your eyes!) 122 | struct BACKUP_DX11_STATE 123 | { 124 | UINT ScissorRectsCount, ViewportsCount; 125 | D3D11_RECT ScissorRects[D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE]; 126 | D3D11_VIEWPORT Viewports[D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE]; 127 | ID3D11RasterizerState* RS; 128 | ID3D11BlendState* BlendState; 129 | FLOAT BlendFactor[4]; 130 | UINT SampleMask; 131 | UINT StencilRef; 132 | ID3D11DepthStencilState* DepthStencilState; 133 | ID3D11ShaderResourceView* PSShaderResource; 134 | ID3D11SamplerState* PSSampler; 135 | ID3D11PixelShader* PS; 136 | ID3D11VertexShader* VS; 137 | UINT PSInstancesCount, VSInstancesCount; 138 | ID3D11ClassInstance* PSInstances[256], *VSInstances[256]; // 256 is max according to PSSetShader documentation 139 | D3D11_PRIMITIVE_TOPOLOGY PrimitiveTopology; 140 | ID3D11Buffer* IndexBuffer, *VertexBuffer, *VSConstantBuffer; 141 | UINT IndexBufferOffset, VertexBufferStride, VertexBufferOffset; 142 | DXGI_FORMAT IndexBufferFormat; 143 | ID3D11InputLayout* InputLayout; 144 | }; 145 | BACKUP_DX11_STATE old; 146 | old.ScissorRectsCount = old.ViewportsCount = D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE; 147 | ctx->RSGetScissorRects(&old.ScissorRectsCount, old.ScissorRects); 148 | ctx->RSGetViewports(&old.ViewportsCount, old.Viewports); 149 | ctx->RSGetState(&old.RS); 150 | ctx->OMGetBlendState(&old.BlendState, old.BlendFactor, &old.SampleMask); 151 | ctx->OMGetDepthStencilState(&old.DepthStencilState, &old.StencilRef); 152 | ctx->PSGetShaderResources(0, 1, &old.PSShaderResource); 153 | ctx->PSGetSamplers(0, 1, &old.PSSampler); 154 | old.PSInstancesCount = old.VSInstancesCount = 256; 155 | ctx->PSGetShader(&old.PS, old.PSInstances, &old.PSInstancesCount); 156 | ctx->VSGetShader(&old.VS, old.VSInstances, &old.VSInstancesCount); 157 | ctx->VSGetConstantBuffers(0, 1, &old.VSConstantBuffer); 158 | ctx->IAGetPrimitiveTopology(&old.PrimitiveTopology); 159 | ctx->IAGetIndexBuffer(&old.IndexBuffer, &old.IndexBufferFormat, &old.IndexBufferOffset); 160 | ctx->IAGetVertexBuffers(0, 1, &old.VertexBuffer, &old.VertexBufferStride, &old.VertexBufferOffset); 161 | ctx->IAGetInputLayout(&old.InputLayout); 162 | 163 | // Setup viewport 164 | D3D11_VIEWPORT vp; 165 | memset(&vp, 0, sizeof(D3D11_VIEWPORT)); 166 | vp.Width = ImGui::GetIO().DisplaySize.x; 167 | vp.Height = ImGui::GetIO().DisplaySize.y; 168 | vp.MinDepth = 0.0f; 169 | vp.MaxDepth = 1.0f; 170 | vp.TopLeftX = vp.TopLeftY = 0.0f; 171 | ctx->RSSetViewports(1, &vp); 172 | 173 | // Bind shader and vertex buffers 174 | unsigned int stride = sizeof(ImDrawVert); 175 | unsigned int offset = 0; 176 | ctx->IASetInputLayout(g_pInputLayout); 177 | ctx->IASetVertexBuffers(0, 1, &g_pVB, &stride, &offset); 178 | ctx->IASetIndexBuffer(g_pIB, sizeof(ImDrawIdx) == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT, 0); 179 | ctx->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); 180 | ctx->VSSetShader(g_pVertexShader, NULL, 0); 181 | ctx->VSSetConstantBuffers(0, 1, &g_pVertexConstantBuffer); 182 | ctx->PSSetShader(g_pPixelShader, NULL, 0); 183 | ctx->PSSetSamplers(0, 1, &g_pFontSampler); 184 | 185 | // Setup render state 186 | const float blend_factor[4] = { 0.f, 0.f, 0.f, 0.f }; 187 | ctx->OMSetBlendState(g_pBlendState, blend_factor, 0xffffffff); 188 | ctx->OMSetDepthStencilState(g_pDepthStencilState, 0); 189 | ctx->RSSetState(g_pRasterizerState); 190 | 191 | // Render command lists 192 | int vtx_offset = 0; 193 | int idx_offset = 0; 194 | for (int n = 0; n < draw_data->CmdListsCount; n++) 195 | { 196 | const ImDrawList* cmd_list = draw_data->CmdLists[n]; 197 | for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.size(); cmd_i++) 198 | { 199 | const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; 200 | if (pcmd->UserCallback) 201 | { 202 | pcmd->UserCallback(cmd_list, pcmd); 203 | } 204 | else 205 | { 206 | const D3D11_RECT r = { (LONG)pcmd->ClipRect.x, (LONG)pcmd->ClipRect.y, (LONG)pcmd->ClipRect.z, (LONG)pcmd->ClipRect.w }; 207 | ctx->PSSetShaderResources(0, 1, (ID3D11ShaderResourceView**)&pcmd->TextureId); 208 | ctx->RSSetScissorRects(1, &r); 209 | ctx->DrawIndexed(pcmd->ElemCount, idx_offset, vtx_offset); 210 | } 211 | idx_offset += pcmd->ElemCount; 212 | } 213 | vtx_offset += cmd_list->VtxBuffer.size(); 214 | } 215 | 216 | // Restore modified DX state 217 | ctx->RSSetScissorRects(old.ScissorRectsCount, old.ScissorRects); 218 | ctx->RSSetViewports(old.ViewportsCount, old.Viewports); 219 | ctx->RSSetState(old.RS); if (old.RS) old.RS->Release(); 220 | ctx->OMSetBlendState(old.BlendState, old.BlendFactor, old.SampleMask); if (old.BlendState) old.BlendState->Release(); 221 | ctx->OMSetDepthStencilState(old.DepthStencilState, old.StencilRef); if (old.DepthStencilState) old.DepthStencilState->Release(); 222 | ctx->PSSetShaderResources(0, 1, &old.PSShaderResource); if (old.PSShaderResource) old.PSShaderResource->Release(); 223 | ctx->PSSetSamplers(0, 1, &old.PSSampler); if (old.PSSampler) old.PSSampler->Release(); 224 | ctx->PSSetShader(old.PS, old.PSInstances, old.PSInstancesCount); if (old.PS) old.PS->Release(); 225 | for (UINT i = 0; i < old.PSInstancesCount; i++) if (old.PSInstances[i]) old.PSInstances[i]->Release(); 226 | ctx->VSSetShader(old.VS, old.VSInstances, old.VSInstancesCount); if (old.VS) old.VS->Release(); 227 | ctx->VSSetConstantBuffers(0, 1, &old.VSConstantBuffer); if (old.VSConstantBuffer) old.VSConstantBuffer->Release(); 228 | for (UINT i = 0; i < old.VSInstancesCount; i++) if (old.VSInstances[i]) old.VSInstances[i]->Release(); 229 | ctx->IASetPrimitiveTopology(old.PrimitiveTopology); 230 | ctx->IASetIndexBuffer(old.IndexBuffer, old.IndexBufferFormat, old.IndexBufferOffset); if (old.IndexBuffer) old.IndexBuffer->Release(); 231 | ctx->IASetVertexBuffers(0, 1, &old.VertexBuffer, &old.VertexBufferStride, &old.VertexBufferOffset); if (old.VertexBuffer) old.VertexBuffer->Release(); 232 | ctx->IASetInputLayout(old.InputLayout); if (old.InputLayout) old.InputLayout->Release(); 233 | } 234 | 235 | IMGUI_API LRESULT ImGui_ImplDX11_WndProcHandler(HWND, UINT msg, WPARAM wParam, LPARAM lParam) 236 | { 237 | ImGuiIO& io = ImGui::GetIO(); 238 | switch (msg) 239 | { 240 | case WM_LBUTTONDOWN: 241 | io.MouseDown[0] = true; 242 | return true; 243 | case WM_LBUTTONUP: 244 | io.MouseDown[0] = false; 245 | return true; 246 | case WM_RBUTTONDOWN: 247 | io.MouseDown[1] = true; 248 | return true; 249 | case WM_RBUTTONUP: 250 | io.MouseDown[1] = false; 251 | return true; 252 | case WM_MBUTTONDOWN: 253 | io.MouseDown[2] = true; 254 | return true; 255 | case WM_MBUTTONUP: 256 | io.MouseDown[2] = false; 257 | return true; 258 | case WM_MOUSEWHEEL: 259 | io.MouseWheel += GET_WHEEL_DELTA_WPARAM(wParam) > 0 ? +1.0f : -1.0f; 260 | return true; 261 | case WM_MOUSEMOVE: 262 | io.MousePos.x = (signed short)(lParam); 263 | io.MousePos.y = (signed short)(lParam >> 16); 264 | return true; 265 | case WM_KEYDOWN: 266 | if (wParam < 256) 267 | io.KeysDown[wParam] = 1; 268 | return true; 269 | case WM_KEYUP: 270 | if (wParam < 256) 271 | io.KeysDown[wParam] = 0; 272 | return true; 273 | case WM_CHAR: 274 | // You can also use ToAscii()+GetKeyboardState() to retrieve characters. 275 | if (wParam > 0 && wParam < 0x10000) 276 | io.AddInputCharacter((unsigned short)wParam); 277 | return true; 278 | } 279 | return 0; 280 | } 281 | 282 | static void ImGui_ImplDX11_CreateFontsTexture() 283 | { 284 | // Build texture atlas 285 | ImGuiIO& io = ImGui::GetIO(); 286 | unsigned char* pixels; 287 | int width, height; 288 | io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); 289 | 290 | // Upload texture to graphics system 291 | { 292 | D3D11_TEXTURE2D_DESC desc; 293 | ZeroMemory(&desc, sizeof(desc)); 294 | desc.Width = width; 295 | desc.Height = height; 296 | desc.MipLevels = 1; 297 | desc.ArraySize = 1; 298 | desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; 299 | desc.SampleDesc.Count = 1; 300 | desc.Usage = D3D11_USAGE_DEFAULT; 301 | desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; 302 | desc.CPUAccessFlags = 0; 303 | 304 | ID3D11Texture2D *pTexture = NULL; 305 | D3D11_SUBRESOURCE_DATA subResource; 306 | subResource.pSysMem = pixels; 307 | subResource.SysMemPitch = desc.Width * 4; 308 | subResource.SysMemSlicePitch = 0; 309 | g_pd3dDevice->CreateTexture2D(&desc, &subResource, &pTexture); 310 | 311 | // Create texture view 312 | D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc; 313 | ZeroMemory(&srvDesc, sizeof(srvDesc)); 314 | srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; 315 | srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; 316 | srvDesc.Texture2D.MipLevels = desc.MipLevels; 317 | srvDesc.Texture2D.MostDetailedMip = 0; 318 | g_pd3dDevice->CreateShaderResourceView(pTexture, &srvDesc, &g_pFontTextureView); 319 | pTexture->Release(); 320 | } 321 | 322 | // Store our identifier 323 | io.Fonts->TexID = (void *)g_pFontTextureView; 324 | 325 | // Create texture sampler 326 | { 327 | D3D11_SAMPLER_DESC desc; 328 | ZeroMemory(&desc, sizeof(desc)); 329 | desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; 330 | desc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP; 331 | desc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP; 332 | desc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP; 333 | desc.MipLODBias = 0.f; 334 | desc.ComparisonFunc = D3D11_COMPARISON_ALWAYS; 335 | desc.MinLOD = 0.f; 336 | desc.MaxLOD = 0.f; 337 | g_pd3dDevice->CreateSamplerState(&desc, &g_pFontSampler); 338 | } 339 | } 340 | 341 | bool ImGui_ImplDX11_CreateDeviceObjects() 342 | { 343 | if (!g_pd3dDevice) 344 | return false; 345 | if (g_pFontSampler) 346 | ImGui_ImplDX11_InvalidateDeviceObjects(); 347 | 348 | // By using D3DCompile() from / d3dcompiler.lib, we introduce a dependency to a given version of d3dcompiler_XX.dll (see D3DCOMPILER_DLL_A) 349 | // If you would like to use this DX11 sample code but remove this dependency you can: 350 | // 1) compile once, save the compiled shader blobs into a file or source code and pass them to CreateVertexShader()/CreatePixelShader() [prefered solution] 351 | // 2) use code to detect any version of the DLL and grab a pointer to D3DCompile from the DLL. 352 | // See https://github.com/ocornut/imgui/pull/638 for sources and details. 353 | 354 | // Create the vertex shader 355 | { 356 | static const char* vertexShader = 357 | "cbuffer vertexBuffer : register(b0) \ 358 | {\ 359 | float4x4 ProjectionMatrix; \ 360 | };\ 361 | struct VS_INPUT\ 362 | {\ 363 | float2 pos : POSITION;\ 364 | float4 col : COLOR0;\ 365 | float2 uv : TEXCOORD0;\ 366 | };\ 367 | \ 368 | struct PS_INPUT\ 369 | {\ 370 | float4 pos : SV_POSITION;\ 371 | float4 col : COLOR0;\ 372 | float2 uv : TEXCOORD0;\ 373 | };\ 374 | \ 375 | PS_INPUT main(VS_INPUT input)\ 376 | {\ 377 | PS_INPUT output;\ 378 | output.pos = mul( ProjectionMatrix, float4(input.pos.xy, 0.f, 1.f));\ 379 | output.col = input.col;\ 380 | output.uv = input.uv;\ 381 | return output;\ 382 | }"; 383 | 384 | D3DCompile(vertexShader, strlen(vertexShader), NULL, NULL, NULL, "main", "vs_4_0", 0, 0, &g_pVertexShaderBlob, NULL); 385 | if (g_pVertexShaderBlob == NULL) // NB: Pass ID3D10Blob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob! 386 | return false; 387 | if (g_pd3dDevice->CreateVertexShader((DWORD*)g_pVertexShaderBlob->GetBufferPointer(), g_pVertexShaderBlob->GetBufferSize(), NULL, &g_pVertexShader) != S_OK) 388 | return false; 389 | 390 | // Create the input layout 391 | D3D11_INPUT_ELEMENT_DESC local_layout[] = { 392 | { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (size_t)(&((ImDrawVert*)0)->pos), D3D11_INPUT_PER_VERTEX_DATA, 0 }, 393 | { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (size_t)(&((ImDrawVert*)0)->uv), D3D11_INPUT_PER_VERTEX_DATA, 0 }, 394 | { "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, (size_t)(&((ImDrawVert*)0)->col), D3D11_INPUT_PER_VERTEX_DATA, 0 }, 395 | }; 396 | if (g_pd3dDevice->CreateInputLayout(local_layout, 3, g_pVertexShaderBlob->GetBufferPointer(), g_pVertexShaderBlob->GetBufferSize(), &g_pInputLayout) != S_OK) 397 | return false; 398 | 399 | // Create the constant buffer 400 | { 401 | D3D11_BUFFER_DESC desc; 402 | desc.ByteWidth = sizeof(VERTEX_CONSTANT_BUFFER); 403 | desc.Usage = D3D11_USAGE_DYNAMIC; 404 | desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; 405 | desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; 406 | desc.MiscFlags = 0; 407 | g_pd3dDevice->CreateBuffer(&desc, NULL, &g_pVertexConstantBuffer); 408 | } 409 | } 410 | 411 | // Create the pixel shader 412 | { 413 | static const char* pixelShader = 414 | "struct PS_INPUT\ 415 | {\ 416 | float4 pos : SV_POSITION;\ 417 | float4 col : COLOR0;\ 418 | float2 uv : TEXCOORD0;\ 419 | };\ 420 | sampler sampler0;\ 421 | Texture2D texture0;\ 422 | \ 423 | float4 main(PS_INPUT input) : SV_Target\ 424 | {\ 425 | float4 out_col = input.col * texture0.Sample(sampler0, input.uv); \ 426 | return out_col; \ 427 | }"; 428 | 429 | D3DCompile(pixelShader, strlen(pixelShader), NULL, NULL, NULL, "main", "ps_4_0", 0, 0, &g_pPixelShaderBlob, NULL); 430 | if (g_pPixelShaderBlob == NULL) // NB: Pass ID3D10Blob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob! 431 | return false; 432 | if (g_pd3dDevice->CreatePixelShader((DWORD*)g_pPixelShaderBlob->GetBufferPointer(), g_pPixelShaderBlob->GetBufferSize(), NULL, &g_pPixelShader) != S_OK) 433 | return false; 434 | } 435 | 436 | // Create the blending setup 437 | { 438 | D3D11_BLEND_DESC desc; 439 | ZeroMemory(&desc, sizeof(desc)); 440 | desc.AlphaToCoverageEnable = false; 441 | desc.RenderTarget[0].BlendEnable = true; 442 | desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA; 443 | desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA; 444 | desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD; 445 | desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA; 446 | desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO; 447 | desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD; 448 | desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; 449 | g_pd3dDevice->CreateBlendState(&desc, &g_pBlendState); 450 | } 451 | 452 | // Create the rasterizer state 453 | { 454 | D3D11_RASTERIZER_DESC desc; 455 | ZeroMemory(&desc, sizeof(desc)); 456 | desc.FillMode = D3D11_FILL_SOLID; 457 | desc.CullMode = D3D11_CULL_NONE; 458 | desc.ScissorEnable = true; 459 | desc.DepthClipEnable = true; 460 | g_pd3dDevice->CreateRasterizerState(&desc, &g_pRasterizerState); 461 | } 462 | 463 | // Create depth-stencil State 464 | { 465 | D3D11_DEPTH_STENCIL_DESC desc; 466 | ZeroMemory(&desc, sizeof(desc)); 467 | desc.DepthEnable = false; 468 | desc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL; 469 | desc.DepthFunc = D3D11_COMPARISON_ALWAYS; 470 | desc.StencilEnable = false; 471 | desc.FrontFace.StencilFailOp = desc.FrontFace.StencilDepthFailOp = desc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP; 472 | desc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS; 473 | desc.BackFace = desc.FrontFace; 474 | g_pd3dDevice->CreateDepthStencilState(&desc, &g_pDepthStencilState); 475 | } 476 | 477 | ImGui_ImplDX11_CreateFontsTexture(); 478 | 479 | return true; 480 | } 481 | 482 | void ImGui_ImplDX11_InvalidateDeviceObjects() 483 | { 484 | if (!g_pd3dDevice) 485 | return; 486 | 487 | if (g_pFontSampler) { g_pFontSampler->Release(); g_pFontSampler = NULL; } 488 | if (g_pFontTextureView) { g_pFontTextureView->Release(); g_pFontTextureView = NULL; ImGui::GetIO().Fonts->TexID = 0; } 489 | if (g_pIB) { g_pIB->Release(); g_pIB = NULL; } 490 | if (g_pVB) { g_pVB->Release(); g_pVB = NULL; } 491 | 492 | if (g_pBlendState) { g_pBlendState->Release(); g_pBlendState = NULL; } 493 | if (g_pDepthStencilState) { g_pDepthStencilState->Release(); g_pDepthStencilState = NULL; } 494 | if (g_pRasterizerState) { g_pRasterizerState->Release(); g_pRasterizerState = NULL; } 495 | if (g_pPixelShader) { g_pPixelShader->Release(); g_pPixelShader = NULL; } 496 | if (g_pPixelShaderBlob) { g_pPixelShaderBlob->Release(); g_pPixelShaderBlob = NULL; } 497 | if (g_pVertexConstantBuffer) { g_pVertexConstantBuffer->Release(); g_pVertexConstantBuffer = NULL; } 498 | if (g_pInputLayout) { g_pInputLayout->Release(); g_pInputLayout = NULL; } 499 | if (g_pVertexShader) { g_pVertexShader->Release(); g_pVertexShader = NULL; } 500 | if (g_pVertexShaderBlob) { g_pVertexShaderBlob->Release(); g_pVertexShaderBlob = NULL; } 501 | } 502 | 503 | bool ImGui_ImplDX11_Init(void* hwnd, ID3D11Device* device, ID3D11DeviceContext* device_context) 504 | { 505 | g_hWnd = (HWND)hwnd; 506 | g_pd3dDevice = device; 507 | g_pd3dDeviceContext = device_context; 508 | 509 | if (!QueryPerformanceFrequency((LARGE_INTEGER *)&g_TicksPerSecond)) 510 | return false; 511 | if (!QueryPerformanceCounter((LARGE_INTEGER *)&g_Time)) 512 | return false; 513 | 514 | ImGuiIO& io = ImGui::GetIO(); 515 | io.KeyMap[ImGuiKey_Tab] = VK_TAB; // Keyboard mapping. ImGui will use those indices to peek into the io.KeyDown[] array that we will update during the application lifetime. 516 | io.KeyMap[ImGuiKey_LeftArrow] = VK_LEFT; 517 | io.KeyMap[ImGuiKey_RightArrow] = VK_RIGHT; 518 | io.KeyMap[ImGuiKey_UpArrow] = VK_UP; 519 | io.KeyMap[ImGuiKey_DownArrow] = VK_DOWN; 520 | io.KeyMap[ImGuiKey_PageUp] = VK_PRIOR; 521 | io.KeyMap[ImGuiKey_PageDown] = VK_NEXT; 522 | io.KeyMap[ImGuiKey_Home] = VK_HOME; 523 | io.KeyMap[ImGuiKey_End] = VK_END; 524 | io.KeyMap[ImGuiKey_Delete] = VK_DELETE; 525 | io.KeyMap[ImGuiKey_Backspace] = VK_BACK; 526 | io.KeyMap[ImGuiKey_Enter] = VK_RETURN; 527 | io.KeyMap[ImGuiKey_Escape] = VK_ESCAPE; 528 | io.KeyMap[ImGuiKey_A] = 'A'; 529 | io.KeyMap[ImGuiKey_C] = 'C'; 530 | io.KeyMap[ImGuiKey_V] = 'V'; 531 | io.KeyMap[ImGuiKey_X] = 'X'; 532 | io.KeyMap[ImGuiKey_Y] = 'Y'; 533 | io.KeyMap[ImGuiKey_Z] = 'Z'; 534 | 535 | io.RenderDrawListsFn = ImGui_ImplDX11_RenderDrawLists; // Alternatively you can set this to NULL and call ImGui::GetDrawData() after ImGui::Render() to get the same ImDrawData pointer. 536 | io.ImeWindowHandle = g_hWnd; 537 | 538 | return true; 539 | } 540 | 541 | void ImGui_ImplDX11_Shutdown() 542 | { 543 | ImGui_ImplDX11_InvalidateDeviceObjects(); 544 | ImGui::Shutdown(); 545 | g_pd3dDevice = NULL; 546 | g_pd3dDeviceContext = NULL; 547 | g_hWnd = (HWND)0; 548 | } 549 | 550 | void ImGui_ImplDX11_NewFrame() 551 | { 552 | if (!g_pFontSampler) 553 | ImGui_ImplDX11_CreateDeviceObjects(); 554 | 555 | ImGuiIO& io = ImGui::GetIO(); 556 | 557 | // Setup display size (every frame to accommodate for window resizing) 558 | RECT rect; 559 | GetClientRect(g_hWnd, &rect); 560 | io.DisplaySize = ImVec2((float)(rect.right - rect.left), (float)(rect.bottom - rect.top)); 561 | 562 | // Setup time step 563 | INT64 current_time; 564 | QueryPerformanceCounter((LARGE_INTEGER *)¤t_time); 565 | io.DeltaTime = (float)(current_time - g_Time) / g_TicksPerSecond; 566 | g_Time = current_time; 567 | 568 | // Read keyboard modifiers inputs 569 | io.KeyCtrl = (GetKeyState(VK_CONTROL) & 0x8000) != 0; 570 | io.KeyShift = (GetKeyState(VK_SHIFT) & 0x8000) != 0; 571 | io.KeyAlt = (GetKeyState(VK_MENU) & 0x8000) != 0; 572 | io.KeySuper = false; 573 | // io.KeysDown : filled by WM_KEYDOWN/WM_KEYUP events 574 | // io.MousePos : filled by WM_MOUSEMOVE events 575 | // io.MouseDown : filled by WM_*BUTTON* events 576 | // io.MouseWheel : filled by WM_MOUSEWHEEL events 577 | 578 | // Hide OS mouse cursor if ImGui is drawing it 579 | SetCursor(io.MouseDrawCursor ? NULL : LoadCursor(NULL, IDC_ARROW)); 580 | 581 | // Start the frame 582 | ImGui::NewFrame(); 583 | } 584 | -------------------------------------------------------------------------------- /imgui/imgui_impl_dx11.h: -------------------------------------------------------------------------------- 1 | // ImGui Win32 + DirectX11 binding 2 | // In this binding, ImTextureID is used to store a 'ID3D11ShaderResourceView*' texture identifier. Read the FAQ about ImTextureID in imgui.cpp. 3 | 4 | // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. 5 | // If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). 6 | // If you are new to ImGui, see examples/README.txt and documentation at the top of imgui.cpp. 7 | // https://github.com/ocornut/imgui 8 | 9 | struct ID3D11Device; 10 | struct ID3D11DeviceContext; 11 | 12 | IMGUI_API bool ImGui_ImplDX11_Init(void* hwnd, ID3D11Device* device, ID3D11DeviceContext* device_context); 13 | IMGUI_API void ImGui_ImplDX11_Shutdown(); 14 | IMGUI_API void ImGui_ImplDX11_NewFrame(); 15 | 16 | // Use if you want to reset your rendering device without losing ImGui state. 17 | IMGUI_API void ImGui_ImplDX11_InvalidateDeviceObjects(); 18 | IMGUI_API bool ImGui_ImplDX11_CreateDeviceObjects(); 19 | 20 | // Handler for Win32 messages, update mouse/keyboard data. 21 | // You may or not need this for your implementation, but it can serve as reference for handling inputs. 22 | // Commented out to avoid dragging dependencies on types. You can copy the extern declaration in your code. 23 | /* 24 | IMGUI_API LRESULT ImGui_ImplDX11_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); 25 | */ 26 | -------------------------------------------------------------------------------- /imgui/imgui_internal.h: -------------------------------------------------------------------------------- 1 | // dear imgui, v1.50 WIP 2 | // (internals) 3 | 4 | // You may use this file to debug, understand or extend ImGui features but we don't provide any guarantee of forward compatibility! 5 | // Implement maths operators for ImVec2 (disabled by default to not collide with using IM_VEC2_CLASS_EXTRA along with your own math types+operators) 6 | // #define IMGUI_DEFINE_MATH_OPERATORS 7 | 8 | #pragma once 9 | 10 | #ifndef IMGUI_VERSION 11 | #error Must include imgui.h before imgui_internal.h 12 | #endif 13 | 14 | #include // FILE* 15 | #include // sqrtf, fabsf, fmodf, powf, floorf, ceilf, cosf, sinf 16 | 17 | #ifdef _MSC_VER 18 | #pragma warning (push) 19 | #pragma warning (disable: 4251) // class 'xxx' needs to have dll-interface to be used by clients of struct 'xxx' // when IMGUI_API is set to__declspec(dllexport) 20 | #endif 21 | 22 | #ifdef __clang__ 23 | #pragma clang diagnostic push 24 | #pragma clang diagnostic ignored "-Wunused-function" // for stb_textedit.h 25 | #pragma clang diagnostic ignored "-Wmissing-prototypes" // for stb_textedit.h 26 | #pragma clang diagnostic ignored "-Wold-style-cast" 27 | #endif 28 | 29 | //----------------------------------------------------------------------------- 30 | // Forward Declarations 31 | //----------------------------------------------------------------------------- 32 | 33 | struct ImRect; 34 | struct ImGuiColMod; 35 | struct ImGuiStyleMod; 36 | struct ImGuiGroupData; 37 | struct ImGuiSimpleColumns; 38 | struct ImGuiDrawContext; 39 | struct ImGuiTextEditState; 40 | struct ImGuiIniData; 41 | struct ImGuiMouseCursorData; 42 | struct ImGuiPopupRef; 43 | struct ImGuiWindow; 44 | 45 | typedef int ImGuiLayoutType; // enum ImGuiLayoutType_ 46 | typedef int ImGuiButtonFlags; // enum ImGuiButtonFlags_ 47 | typedef int ImGuiTreeNodeFlags; // enum ImGuiTreeNodeFlags_ 48 | typedef int ImGuiSliderFlags; // enum ImGuiSliderFlags_ 49 | 50 | //------------------------------------------------------------------------- 51 | // STB libraries 52 | //------------------------------------------------------------------------- 53 | 54 | namespace ImGuiStb 55 | { 56 | 57 | #undef STB_TEXTEDIT_STRING 58 | #undef STB_TEXTEDIT_CHARTYPE 59 | #define STB_TEXTEDIT_STRING ImGuiTextEditState 60 | #define STB_TEXTEDIT_CHARTYPE ImWchar 61 | #define STB_TEXTEDIT_GETWIDTH_NEWLINE -1.0f 62 | #include "stb_textedit.h" 63 | 64 | } // namespace ImGuiStb 65 | 66 | //----------------------------------------------------------------------------- 67 | // Context 68 | //----------------------------------------------------------------------------- 69 | 70 | extern IMGUI_API ImGuiContext* GImGui; // current implicit ImGui context pointer 71 | 72 | //----------------------------------------------------------------------------- 73 | // Helpers 74 | //----------------------------------------------------------------------------- 75 | 76 | #define IM_ARRAYSIZE(_ARR) ((int)(sizeof(_ARR)/sizeof(*_ARR))) 77 | #define IM_PI 3.14159265358979323846f 78 | 79 | // Helpers: UTF-8 <> wchar 80 | IMGUI_API int ImTextStrToUtf8(char* buf, int buf_size, const ImWchar* in_text, const ImWchar* in_text_end); // return output UTF-8 bytes count 81 | IMGUI_API int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end); // return input UTF-8 bytes count 82 | IMGUI_API int ImTextStrFromUtf8(ImWchar* buf, int buf_size, const char* in_text, const char* in_text_end, const char** in_remaining = NULL); // return input UTF-8 bytes count 83 | IMGUI_API int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end); // return number of UTF-8 code-points (NOT bytes count) 84 | IMGUI_API int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end); // return number of bytes to express string as UTF-8 code-points 85 | 86 | // Helpers: Misc 87 | IMGUI_API ImU32 ImHash(const void* data, int data_size, ImU32 seed = 0); // Pass data_size==0 for zero-terminated strings 88 | IMGUI_API void* ImLoadFileToMemory(const char* filename, const char* file_open_mode, int* out_file_size = NULL, int padding_bytes = 0); 89 | IMGUI_API bool ImIsPointInTriangle(const ImVec2& p, const ImVec2& a, const ImVec2& b, const ImVec2& c); 90 | static inline bool ImCharIsSpace(int c) { return c == ' ' || c == '\t' || c == 0x3000; } 91 | static inline int ImUpperPowerOfTwo(int v) { v--; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; v++; return v; } 92 | 93 | // Helpers: String 94 | IMGUI_API int ImStricmp(const char* str1, const char* str2); 95 | IMGUI_API int ImStrnicmp(const char* str1, const char* str2, int count); 96 | IMGUI_API char* ImStrdup(const char* str); 97 | IMGUI_API int ImStrlenW(const ImWchar* str); 98 | IMGUI_API const ImWchar*ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin); // Find beginning-of-line 99 | IMGUI_API const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end); 100 | IMGUI_API int ImFormatString(char* buf, int buf_size, const char* fmt, ...) IM_PRINTFARGS(3); 101 | IMGUI_API int ImFormatStringV(char* buf, int buf_size, const char* fmt, va_list args); 102 | 103 | // Helpers: Math 104 | // We are keeping those not leaking to the user by default, in the case the user has implicit cast operators between ImVec2 and its own types (when IM_VEC2_CLASS_EXTRA is defined) 105 | #ifdef IMGUI_DEFINE_MATH_OPERATORS 106 | static inline ImVec2 operator*(const ImVec2& lhs, const float rhs) { return ImVec2(lhs.x*rhs, lhs.y*rhs); } 107 | static inline ImVec2 operator/(const ImVec2& lhs, const float rhs) { return ImVec2(lhs.x/rhs, lhs.y/rhs); } 108 | static inline ImVec2 operator+(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x+rhs.x, lhs.y+rhs.y); } 109 | static inline ImVec2 operator-(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x-rhs.x, lhs.y-rhs.y); } 110 | static inline ImVec2 operator*(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x*rhs.x, lhs.y*rhs.y); } 111 | static inline ImVec2 operator/(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x/rhs.x, lhs.y/rhs.y); } 112 | static inline ImVec2& operator+=(ImVec2& lhs, const ImVec2& rhs) { lhs.x += rhs.x; lhs.y += rhs.y; return lhs; } 113 | static inline ImVec2& operator-=(ImVec2& lhs, const ImVec2& rhs) { lhs.x -= rhs.x; lhs.y -= rhs.y; return lhs; } 114 | static inline ImVec2& operator*=(ImVec2& lhs, const float rhs) { lhs.x *= rhs; lhs.y *= rhs; return lhs; } 115 | static inline ImVec2& operator/=(ImVec2& lhs, const float rhs) { lhs.x /= rhs; lhs.y /= rhs; return lhs; } 116 | static inline ImVec4 operator-(const ImVec4& lhs, const ImVec4& rhs) { return ImVec4(lhs.x-rhs.x, lhs.y-rhs.y, lhs.z-rhs.z, lhs.w-rhs.w); } 117 | #endif 118 | 119 | static inline int ImMin(int lhs, int rhs) { return lhs < rhs ? lhs : rhs; } 120 | static inline int ImMax(int lhs, int rhs) { return lhs >= rhs ? lhs : rhs; } 121 | static inline float ImMin(float lhs, float rhs) { return lhs < rhs ? lhs : rhs; } 122 | static inline float ImMax(float lhs, float rhs) { return lhs >= rhs ? lhs : rhs; } 123 | static inline ImVec2 ImMin(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(ImMin(lhs.x,rhs.x), ImMin(lhs.y,rhs.y)); } 124 | static inline ImVec2 ImMax(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(ImMax(lhs.x,rhs.x), ImMax(lhs.y,rhs.y)); } 125 | static inline int ImClamp(int v, int mn, int mx) { return (v < mn) ? mn : (v > mx) ? mx : v; } 126 | static inline float ImClamp(float v, float mn, float mx) { return (v < mn) ? mn : (v > mx) ? mx : v; } 127 | static inline ImVec2 ImClamp(const ImVec2& f, const ImVec2& mn, ImVec2 mx) { return ImVec2(ImClamp(f.x,mn.x,mx.x), ImClamp(f.y,mn.y,mx.y)); } 128 | static inline float ImSaturate(float f) { return (f < 0.0f) ? 0.0f : (f > 1.0f) ? 1.0f : f; } 129 | static inline float ImLerp(float a, float b, float t) { return a + (b - a) * t; } 130 | static inline ImVec2 ImLerp(const ImVec2& a, const ImVec2& b, const ImVec2& t) { return ImVec2(a.x + (b.x - a.x) * t.x, a.y + (b.y - a.y) * t.y); } 131 | static inline float ImLengthSqr(const ImVec2& lhs) { return lhs.x*lhs.x + lhs.y*lhs.y; } 132 | static inline float ImLengthSqr(const ImVec4& lhs) { return lhs.x*lhs.x + lhs.y*lhs.y + lhs.z*lhs.z + lhs.w*lhs.w; } 133 | static inline float ImInvLength(const ImVec2& lhs, float fail_value) { float d = lhs.x*lhs.x + lhs.y*lhs.y; if (d > 0.0f) return 1.0f / sqrtf(d); return fail_value; } 134 | static inline float ImFloor(float f) { return (float)(int)f; } 135 | static inline ImVec2 ImFloor(ImVec2 v) { return ImVec2((float)(int)v.x, (float)(int)v.y); } 136 | 137 | // We call C++ constructor on own allocated memory via the placement "new(ptr) Type()" syntax. 138 | // Defining a custom placement new() with a dummy parameter allows us to bypass including which on some platforms complains when user has disabled exceptions. 139 | #ifdef IMGUI_DEFINE_PLACEMENT_NEW 140 | struct ImPlacementNewDummy {}; 141 | inline void* operator new(size_t, ImPlacementNewDummy, void* ptr) { return ptr; } 142 | inline void operator delete(void*, ImPlacementNewDummy, void*) {} 143 | #define IM_PLACEMENT_NEW(_PTR) new(ImPlacementNewDummy(), _PTR) 144 | #endif 145 | 146 | //----------------------------------------------------------------------------- 147 | // Types 148 | //----------------------------------------------------------------------------- 149 | 150 | enum ImGuiButtonFlags_ 151 | { 152 | ImGuiButtonFlags_Repeat = 1 << 0, // hold to repeat 153 | ImGuiButtonFlags_PressedOnClickRelease = 1 << 1, // (default) return pressed on click+release on same item (default if no PressedOn** flag is set) 154 | ImGuiButtonFlags_PressedOnClick = 1 << 2, // return pressed on click (default requires click+release) 155 | ImGuiButtonFlags_PressedOnRelease = 1 << 3, // return pressed on release (default requires click+release) 156 | ImGuiButtonFlags_PressedOnDoubleClick = 1 << 4, // return pressed on double-click (default requires click+release) 157 | ImGuiButtonFlags_FlattenChilds = 1 << 5, // allow interaction even if a child window is overlapping 158 | ImGuiButtonFlags_DontClosePopups = 1 << 6, // disable automatically closing parent popup on press 159 | ImGuiButtonFlags_Disabled = 1 << 7, // disable interaction 160 | ImGuiButtonFlags_AlignTextBaseLine = 1 << 8, // vertically align button to match text baseline - ButtonEx() only 161 | ImGuiButtonFlags_NoKeyModifiers = 1 << 9, // disable interaction if a key modifier is held 162 | ImGuiButtonFlags_AllowOverlapMode = 1 << 10 // require previous frame HoveredId to either match id or be null before being usable 163 | }; 164 | 165 | enum ImGuiSliderFlags_ 166 | { 167 | ImGuiSliderFlags_Vertical = 1 << 0 168 | }; 169 | 170 | enum ImGuiSelectableFlagsPrivate_ 171 | { 172 | // NB: need to be in sync with last value of ImGuiSelectableFlags_ 173 | ImGuiSelectableFlags_Menu = 1 << 3, 174 | ImGuiSelectableFlags_MenuItem = 1 << 4, 175 | ImGuiSelectableFlags_Disabled = 1 << 5, 176 | ImGuiSelectableFlags_DrawFillAvailWidth = 1 << 6 177 | }; 178 | 179 | // FIXME: this is in development, not exposed/functional as a generic feature yet. 180 | enum ImGuiLayoutType_ 181 | { 182 | ImGuiLayoutType_Vertical, 183 | ImGuiLayoutType_Horizontal 184 | }; 185 | 186 | enum ImGuiPlotType 187 | { 188 | ImGuiPlotType_Lines, 189 | ImGuiPlotType_Histogram 190 | }; 191 | 192 | enum ImGuiDataType 193 | { 194 | ImGuiDataType_Int, 195 | ImGuiDataType_Float 196 | }; 197 | 198 | // 2D axis aligned bounding-box 199 | // NB: we can't rely on ImVec2 math operators being available here 200 | struct IMGUI_API ImRect 201 | { 202 | ImVec2 Min; // Upper-left 203 | ImVec2 Max; // Lower-right 204 | 205 | ImRect() : Min(FLT_MAX,FLT_MAX), Max(-FLT_MAX,-FLT_MAX) {} 206 | ImRect(const ImVec2& min, const ImVec2& max) : Min(min), Max(max) {} 207 | ImRect(const ImVec4& v) : Min(v.x, v.y), Max(v.z, v.w) {} 208 | ImRect(float x1, float y1, float x2, float y2) : Min(x1, y1), Max(x2, y2) {} 209 | 210 | ImVec2 GetCenter() const { return ImVec2((Min.x+Max.x)*0.5f, (Min.y+Max.y)*0.5f); } 211 | ImVec2 GetSize() const { return ImVec2(Max.x-Min.x, Max.y-Min.y); } 212 | float GetWidth() const { return Max.x-Min.x; } 213 | float GetHeight() const { return Max.y-Min.y; } 214 | ImVec2 GetTL() const { return Min; } // Top-left 215 | ImVec2 GetTR() const { return ImVec2(Max.x, Min.y); } // Top-right 216 | ImVec2 GetBL() const { return ImVec2(Min.x, Max.y); } // Bottom-left 217 | ImVec2 GetBR() const { return Max; } // Bottom-right 218 | bool Contains(const ImVec2& p) const { return p.x >= Min.x && p.y >= Min.y && p.x < Max.x && p.y < Max.y; } 219 | bool Contains(const ImRect& r) const { return r.Min.x >= Min.x && r.Min.y >= Min.y && r.Max.x < Max.x && r.Max.y < Max.y; } 220 | bool Overlaps(const ImRect& r) const { return r.Min.y < Max.y && r.Max.y > Min.y && r.Min.x < Max.x && r.Max.x > Min.x; } 221 | void Add(const ImVec2& rhs) { if (Min.x > rhs.x) Min.x = rhs.x; if (Min.y > rhs.y) Min.y = rhs.y; if (Max.x < rhs.x) Max.x = rhs.x; if (Max.y < rhs.y) Max.y = rhs.y; } 222 | void Add(const ImRect& rhs) { if (Min.x > rhs.Min.x) Min.x = rhs.Min.x; if (Min.y > rhs.Min.y) Min.y = rhs.Min.y; if (Max.x < rhs.Max.x) Max.x = rhs.Max.x; if (Max.y < rhs.Max.y) Max.y = rhs.Max.y; } 223 | void Expand(const float amount) { Min.x -= amount; Min.y -= amount; Max.x += amount; Max.y += amount; } 224 | void Expand(const ImVec2& amount) { Min.x -= amount.x; Min.y -= amount.y; Max.x += amount.x; Max.y += amount.y; } 225 | void Reduce(const ImVec2& amount) { Min.x += amount.x; Min.y += amount.y; Max.x -= amount.x; Max.y -= amount.y; } 226 | void Clip(const ImRect& clip) { if (Min.x < clip.Min.x) Min.x = clip.Min.x; if (Min.y < clip.Min.y) Min.y = clip.Min.y; if (Max.x > clip.Max.x) Max.x = clip.Max.x; if (Max.y > clip.Max.y) Max.y = clip.Max.y; } 227 | void Floor() { Min.x = (float)(int)Min.x; Min.y = (float)(int)Min.y; Max.x = (float)(int)Max.x; Max.y = (float)(int)Max.y; } 228 | ImVec2 GetClosestPoint(ImVec2 p, bool on_edge) const 229 | { 230 | if (!on_edge && Contains(p)) 231 | return p; 232 | if (p.x > Max.x) p.x = Max.x; 233 | else if (p.x < Min.x) p.x = Min.x; 234 | if (p.y > Max.y) p.y = Max.y; 235 | else if (p.y < Min.y) p.y = Min.y; 236 | return p; 237 | } 238 | }; 239 | 240 | // Stacked color modifier, backup of modified data so we can restore it 241 | struct ImGuiColMod 242 | { 243 | ImGuiCol Col; 244 | ImVec4 PreviousValue; 245 | }; 246 | 247 | // Stacked style modifier, backup of modified data so we can restore it 248 | struct ImGuiStyleMod 249 | { 250 | ImGuiStyleVar Var; 251 | ImVec2 PreviousValue; 252 | }; 253 | 254 | // Stacked data for BeginGroup()/EndGroup() 255 | struct ImGuiGroupData 256 | { 257 | ImVec2 BackupCursorPos; 258 | ImVec2 BackupCursorMaxPos; 259 | float BackupIndentX; 260 | float BackupCurrentLineHeight; 261 | float BackupCurrentLineTextBaseOffset; 262 | float BackupLogLinePosY; 263 | bool AdvanceCursor; 264 | }; 265 | 266 | // Per column data for Columns() 267 | struct ImGuiColumnData 268 | { 269 | float OffsetNorm; // Column start offset, normalized 0.0 (far left) -> 1.0 (far right) 270 | //float IndentX; 271 | }; 272 | 273 | // Simple column measurement currently used for MenuItem() only. This is very short-sighted/throw-away code and NOT a generic helper. 274 | struct IMGUI_API ImGuiSimpleColumns 275 | { 276 | int Count; 277 | float Spacing; 278 | float Width, NextWidth; 279 | float Pos[8], NextWidths[8]; 280 | 281 | ImGuiSimpleColumns(); 282 | void Update(int count, float spacing, bool clear); 283 | float DeclColumns(float w0, float w1, float w2); 284 | float CalcExtraSpace(float avail_w); 285 | }; 286 | 287 | // Internal state of the currently focused/edited text input box 288 | struct IMGUI_API ImGuiTextEditState 289 | { 290 | ImGuiID Id; // widget id owning the text state 291 | ImVector Text; // edit buffer, we need to persist but can't guarantee the persistence of the user-provided buffer. so we copy into own buffer. 292 | ImVector InitialText; // backup of end-user buffer at the time of focus (in UTF-8, unaltered) 293 | ImVector TempTextBuffer; 294 | int CurLenA, CurLenW; // we need to maintain our buffer length in both UTF-8 and wchar format. 295 | int BufSizeA; // end-user buffer size 296 | float ScrollX; 297 | ImGuiStb::STB_TexteditState StbState; 298 | float CursorAnim; 299 | bool CursorFollow; 300 | bool SelectedAllMouseLock; 301 | 302 | ImGuiTextEditState() { memset(this, 0, sizeof(*this)); } 303 | void CursorAnimReset() { CursorAnim = -0.30f; } // After a user-input the cursor stays on for a while without blinking 304 | void CursorClamp() { StbState.cursor = ImMin(StbState.cursor, CurLenW); StbState.select_start = ImMin(StbState.select_start, CurLenW); StbState.select_end = ImMin(StbState.select_end, CurLenW); } 305 | bool HasSelection() const { return StbState.select_start != StbState.select_end; } 306 | void ClearSelection() { StbState.select_start = StbState.select_end = StbState.cursor; } 307 | void SelectAll() { StbState.select_start = 0; StbState.select_end = CurLenW; StbState.cursor = StbState.select_end; StbState.has_preferred_x = false; } 308 | void OnKeyPressed(int key); 309 | }; 310 | 311 | // Data saved in imgui.ini file 312 | struct ImGuiIniData 313 | { 314 | char* Name; 315 | ImGuiID Id; 316 | ImVec2 Pos; 317 | ImVec2 Size; 318 | bool Collapsed; 319 | }; 320 | 321 | // Mouse cursor data (used when io.MouseDrawCursor is set) 322 | struct ImGuiMouseCursorData 323 | { 324 | ImGuiMouseCursor Type; 325 | ImVec2 HotOffset; 326 | ImVec2 Size; 327 | ImVec2 TexUvMin[2]; 328 | ImVec2 TexUvMax[2]; 329 | }; 330 | 331 | // Storage for current popup stack 332 | struct ImGuiPopupRef 333 | { 334 | ImGuiID PopupId; // Set on OpenPopup() 335 | ImGuiWindow* Window; // Resolved on BeginPopup() - may stay unresolved if user never calls OpenPopup() 336 | ImGuiWindow* ParentWindow; // Set on OpenPopup() 337 | ImGuiID ParentMenuSet; // Set on OpenPopup() 338 | ImVec2 MousePosOnOpen; // Copy of mouse position at the time of opening popup 339 | 340 | ImGuiPopupRef(ImGuiID id, ImGuiWindow* parent_window, ImGuiID parent_menu_set, const ImVec2& mouse_pos) { PopupId = id; Window = NULL; ParentWindow = parent_window; ParentMenuSet = parent_menu_set; MousePosOnOpen = mouse_pos; } 341 | }; 342 | 343 | // Main state for ImGui 344 | struct ImGuiContext 345 | { 346 | bool Initialized; 347 | ImGuiIO IO; 348 | ImGuiStyle Style; 349 | ImFont* Font; // (Shortcut) == FontStack.empty() ? IO.Font : FontStack.back() 350 | float FontSize; // (Shortcut) == FontBaseSize * g.CurrentWindow->FontWindowScale == window->FontSize() 351 | float FontBaseSize; // (Shortcut) == IO.FontGlobalScale * Font->Scale * Font->FontSize. Size of characters. 352 | ImVec2 FontTexUvWhitePixel; // (Shortcut) == Font->TexUvWhitePixel 353 | 354 | float Time; 355 | int FrameCount; 356 | int FrameCountEnded; 357 | int FrameCountRendered; 358 | ImVector Windows; 359 | ImVector WindowsSortBuffer; 360 | ImGuiWindow* CurrentWindow; // Being drawn into 361 | ImVector CurrentWindowStack; 362 | ImGuiWindow* FocusedWindow; // Will catch keyboard inputs 363 | ImGuiWindow* HoveredWindow; // Will catch mouse inputs 364 | ImGuiWindow* HoveredRootWindow; // Will catch mouse inputs (for focus/move only) 365 | ImGuiID HoveredId; // Hovered widget 366 | bool HoveredIdAllowOverlap; 367 | ImGuiID HoveredIdPreviousFrame; 368 | ImGuiID ActiveId; // Active widget 369 | ImGuiID ActiveIdPreviousFrame; 370 | bool ActiveIdIsAlive; 371 | bool ActiveIdIsJustActivated; // Set at the time of activation for one frame 372 | bool ActiveIdAllowOverlap; // Set only by active widget 373 | ImVec2 ActiveIdClickOffset; // Clicked offset from upper-left corner, if applicable (currently only set by ButtonBehavior) 374 | ImGuiWindow* ActiveIdWindow; 375 | ImGuiWindow* MovedWindow; // Track the child window we clicked on to move a window. 376 | ImGuiID MovedWindowMoveId; // == MovedWindow->RootWindow->MoveId 377 | ImVector Settings; // .ini Settings 378 | float SettingsDirtyTimer; // Save .ini Settings on disk when time reaches zero 379 | ImVector ColorModifiers; // Stack for PushStyleColor()/PopStyleColor() 380 | ImVector StyleModifiers; // Stack for PushStyleVar()/PopStyleVar() 381 | ImVector FontStack; // Stack for PushFont()/PopFont() 382 | ImVector OpenPopupStack; // Which popups are open (persistent) 383 | ImVector CurrentPopupStack; // Which level of BeginPopup() we are in (reset every frame) 384 | 385 | // Storage for SetNexWindow** and SetNextTreeNode*** functions 386 | ImVec2 SetNextWindowPosVal; 387 | ImVec2 SetNextWindowSizeVal; 388 | ImVec2 SetNextWindowContentSizeVal; 389 | bool SetNextWindowCollapsedVal; 390 | ImGuiSetCond SetNextWindowPosCond; 391 | ImGuiSetCond SetNextWindowSizeCond; 392 | ImGuiSetCond SetNextWindowContentSizeCond; 393 | ImGuiSetCond SetNextWindowCollapsedCond; 394 | ImRect SetNextWindowSizeConstraintRect; // Valid if 'SetNextWindowSizeConstraint' is true 395 | ImGuiSizeConstraintCallback SetNextWindowSizeConstraintCallback; 396 | void* SetNextWindowSizeConstraintCallbackUserData; 397 | bool SetNextWindowSizeConstraint; 398 | bool SetNextWindowFocus; 399 | bool SetNextTreeNodeOpenVal; 400 | ImGuiSetCond SetNextTreeNodeOpenCond; 401 | 402 | // Render 403 | ImDrawData RenderDrawData; // Main ImDrawData instance to pass render information to the user 404 | ImVector RenderDrawLists[3]; 405 | float ModalWindowDarkeningRatio; 406 | ImDrawList OverlayDrawList; // Optional software render of mouse cursors, if io.MouseDrawCursor is set + a few debug overlays 407 | ImGuiMouseCursor MouseCursor; 408 | ImGuiMouseCursorData MouseCursorData[ImGuiMouseCursor_Count_]; 409 | 410 | // Widget state 411 | ImGuiTextEditState InputTextState; 412 | ImFont InputTextPasswordFont; 413 | ImGuiID ScalarAsInputTextId; // Temporary text input when CTRL+clicking on a slider, etc. 414 | ImGuiStorage ColorEditModeStorage; // Store user selection of color edit mode 415 | float DragCurrentValue; // Currently dragged value, always float, not rounded by end-user precision settings 416 | ImVec2 DragLastMouseDelta; 417 | float DragSpeedDefaultRatio; // If speed == 0.0f, uses (max-min) * DragSpeedDefaultRatio 418 | float DragSpeedScaleSlow; 419 | float DragSpeedScaleFast; 420 | ImVec2 ScrollbarClickDeltaToGrabCenter; // Distance between mouse and center of grab box, normalized in parent space. Use storage? 421 | char Tooltip[1024]; 422 | char* PrivateClipboard; // If no custom clipboard handler is defined 423 | ImVec2 OsImePosRequest, OsImePosSet; // Cursor position request & last passed to the OS Input Method Editor 424 | 425 | // Logging 426 | bool LogEnabled; 427 | FILE* LogFile; // If != NULL log to stdout/ file 428 | ImGuiTextBuffer* LogClipboard; // Else log to clipboard. This is pointer so our GImGui static constructor doesn't call heap allocators. 429 | int LogStartDepth; 430 | int LogAutoExpandMaxDepth; 431 | 432 | // Misc 433 | float FramerateSecPerFrame[120]; // calculate estimate of framerate for user 434 | int FramerateSecPerFrameIdx; 435 | float FramerateSecPerFrameAccum; 436 | int CaptureMouseNextFrame; // explicit capture via CaptureInputs() sets those flags 437 | int CaptureKeyboardNextFrame; 438 | char TempBuffer[1024*3+1]; // temporary text buffer 439 | 440 | ImGuiContext() 441 | { 442 | Initialized = false; 443 | Font = NULL; 444 | FontSize = FontBaseSize = 0.0f; 445 | FontTexUvWhitePixel = ImVec2(0.0f, 0.0f); 446 | 447 | Time = 0.0f; 448 | FrameCount = 0; 449 | FrameCountEnded = FrameCountRendered = -1; 450 | CurrentWindow = NULL; 451 | FocusedWindow = NULL; 452 | HoveredWindow = NULL; 453 | HoveredRootWindow = NULL; 454 | HoveredId = 0; 455 | HoveredIdAllowOverlap = false; 456 | HoveredIdPreviousFrame = 0; 457 | ActiveId = 0; 458 | ActiveIdPreviousFrame = 0; 459 | ActiveIdIsAlive = false; 460 | ActiveIdIsJustActivated = false; 461 | ActiveIdAllowOverlap = false; 462 | ActiveIdClickOffset = ImVec2(-1,-1); 463 | ActiveIdWindow = NULL; 464 | MovedWindow = NULL; 465 | MovedWindowMoveId = 0; 466 | SettingsDirtyTimer = 0.0f; 467 | 468 | SetNextWindowPosVal = ImVec2(0.0f, 0.0f); 469 | SetNextWindowSizeVal = ImVec2(0.0f, 0.0f); 470 | SetNextWindowCollapsedVal = false; 471 | SetNextWindowPosCond = 0; 472 | SetNextWindowSizeCond = 0; 473 | SetNextWindowContentSizeCond = 0; 474 | SetNextWindowCollapsedCond = 0; 475 | SetNextWindowFocus = false; 476 | SetNextWindowSizeConstraintCallback = NULL; 477 | SetNextWindowSizeConstraintCallbackUserData = NULL; 478 | SetNextTreeNodeOpenVal = false; 479 | SetNextTreeNodeOpenCond = 0; 480 | 481 | ScalarAsInputTextId = 0; 482 | DragCurrentValue = 0.0f; 483 | DragLastMouseDelta = ImVec2(0.0f, 0.0f); 484 | DragSpeedDefaultRatio = 1.0f / 100.0f; 485 | DragSpeedScaleSlow = 0.01f; 486 | DragSpeedScaleFast = 10.0f; 487 | ScrollbarClickDeltaToGrabCenter = ImVec2(0.0f, 0.0f); 488 | memset(Tooltip, 0, sizeof(Tooltip)); 489 | PrivateClipboard = NULL; 490 | OsImePosRequest = OsImePosSet = ImVec2(-1.0f, -1.0f); 491 | 492 | ModalWindowDarkeningRatio = 0.0f; 493 | OverlayDrawList._OwnerName = "##Overlay"; // Give it a name for debugging 494 | MouseCursor = ImGuiMouseCursor_Arrow; 495 | memset(MouseCursorData, 0, sizeof(MouseCursorData)); 496 | 497 | LogEnabled = false; 498 | LogFile = NULL; 499 | LogClipboard = NULL; 500 | LogStartDepth = 0; 501 | LogAutoExpandMaxDepth = 2; 502 | 503 | memset(FramerateSecPerFrame, 0, sizeof(FramerateSecPerFrame)); 504 | FramerateSecPerFrameIdx = 0; 505 | FramerateSecPerFrameAccum = 0.0f; 506 | CaptureMouseNextFrame = CaptureKeyboardNextFrame = -1; 507 | memset(TempBuffer, 0, sizeof(TempBuffer)); 508 | } 509 | }; 510 | 511 | // Transient per-window data, reset at the beginning of the frame 512 | // FIXME: That's theory, in practice the delimitation between ImGuiWindow and ImGuiDrawContext is quite tenuous and could be reconsidered. 513 | struct IMGUI_API ImGuiDrawContext 514 | { 515 | ImVec2 CursorPos; 516 | ImVec2 CursorPosPrevLine; 517 | ImVec2 CursorStartPos; 518 | ImVec2 CursorMaxPos; // Implicitly calculate the size of our contents, always extending. Saved into window->SizeContents at the end of the frame 519 | float CurrentLineHeight; 520 | float CurrentLineTextBaseOffset; 521 | float PrevLineHeight; 522 | float PrevLineTextBaseOffset; 523 | float LogLinePosY; 524 | int TreeDepth; 525 | ImGuiID LastItemId; 526 | ImRect LastItemRect; 527 | bool LastItemHoveredAndUsable; // Item rectangle is hovered, and its window is currently interactable with (not blocked by a popup preventing access to the window) 528 | bool LastItemHoveredRect; // Item rectangle is hovered, but its window may or not be currently interactable with (might be blocked by a popup preventing access to the window) 529 | bool MenuBarAppending; 530 | float MenuBarOffsetX; 531 | ImVector ChildWindows; 532 | ImGuiStorage* StateStorage; 533 | ImGuiLayoutType LayoutType; 534 | 535 | // We store the current settings outside of the vectors to increase memory locality (reduce cache misses). The vectors are rarely modified. Also it allows us to not heap allocate for short-lived windows which are not using those settings. 536 | float ItemWidth; // == ItemWidthStack.back(). 0.0: default, >0.0: width in pixels, <0.0: align xx pixels to the right of window 537 | float TextWrapPos; // == TextWrapPosStack.back() [empty == -1.0f] 538 | bool AllowKeyboardFocus; // == AllowKeyboardFocusStack.back() [empty == true] 539 | bool ButtonRepeat; // == ButtonRepeatStack.back() [empty == false] 540 | ImVector ItemWidthStack; 541 | ImVector TextWrapPosStack; 542 | ImVector AllowKeyboardFocusStack; 543 | ImVector ButtonRepeatStack; 544 | ImVectorGroupStack; 545 | ImGuiColorEditMode ColorEditMode; 546 | int StackSizesBackup[6]; // Store size of various stacks for asserting 547 | 548 | float IndentX; // Indentation / start position from left of window (increased by TreePush/TreePop, etc.) 549 | float ColumnsOffsetX; // Offset to the current column (if ColumnsCurrent > 0). FIXME: This and the above should be a stack to allow use cases like Tree->Column->Tree. Need revamp columns API. 550 | int ColumnsCurrent; 551 | int ColumnsCount; 552 | float ColumnsMinX; 553 | float ColumnsMaxX; 554 | float ColumnsStartPosY; 555 | float ColumnsCellMinY; 556 | float ColumnsCellMaxY; 557 | bool ColumnsShowBorders; 558 | ImGuiID ColumnsSetId; 559 | ImVector ColumnsData; 560 | 561 | ImGuiDrawContext() 562 | { 563 | CursorPos = CursorPosPrevLine = CursorStartPos = CursorMaxPos = ImVec2(0.0f, 0.0f); 564 | CurrentLineHeight = PrevLineHeight = 0.0f; 565 | CurrentLineTextBaseOffset = PrevLineTextBaseOffset = 0.0f; 566 | LogLinePosY = -1.0f; 567 | TreeDepth = 0; 568 | LastItemId = 0; 569 | LastItemRect = ImRect(0.0f,0.0f,0.0f,0.0f); 570 | LastItemHoveredAndUsable = LastItemHoveredRect = false; 571 | MenuBarAppending = false; 572 | MenuBarOffsetX = 0.0f; 573 | StateStorage = NULL; 574 | LayoutType = ImGuiLayoutType_Vertical; 575 | ItemWidth = 0.0f; 576 | ButtonRepeat = false; 577 | AllowKeyboardFocus = true; 578 | TextWrapPos = -1.0f; 579 | ColorEditMode = ImGuiColorEditMode_RGB; 580 | memset(StackSizesBackup, 0, sizeof(StackSizesBackup)); 581 | 582 | IndentX = 0.0f; 583 | ColumnsOffsetX = 0.0f; 584 | ColumnsCurrent = 0; 585 | ColumnsCount = 1; 586 | ColumnsMinX = ColumnsMaxX = 0.0f; 587 | ColumnsStartPosY = 0.0f; 588 | ColumnsCellMinY = ColumnsCellMaxY = 0.0f; 589 | ColumnsShowBorders = true; 590 | ColumnsSetId = 0; 591 | } 592 | }; 593 | 594 | // Windows data 595 | struct IMGUI_API ImGuiWindow 596 | { 597 | char* Name; 598 | ImGuiID ID; // == ImHash(Name) 599 | ImGuiWindowFlags Flags; // See enum ImGuiWindowFlags_ 600 | int IndexWithinParent; // Order within immediate parent window, if we are a child window. Otherwise 0. 601 | ImVec2 PosFloat; 602 | ImVec2 Pos; // Position rounded-up to nearest pixel 603 | ImVec2 Size; // Current size (==SizeFull or collapsed title bar size) 604 | ImVec2 SizeFull; // Size when non collapsed 605 | ImVec2 SizeContents; // Size of contents (== extents reach of the drawing cursor) from previous frame 606 | ImVec2 SizeContentsExplicit; // Size of contents explicitly set by the user via SetNextWindowContentSize() 607 | ImRect ContentsRegionRect; // Maximum visible content position in window coordinates. ~~ (SizeContentsExplicit ? SizeContentsExplicit : Size - ScrollbarSizes) - CursorStartPos, per axis 608 | ImVec2 WindowPadding; // Window padding at the time of begin. We need to lock it, in particular manipulation of the ShowBorder would have an effect 609 | ImGuiID MoveId; // == window->GetID("#MOVE") 610 | ImVec2 Scroll; 611 | ImVec2 ScrollTarget; // target scroll position. stored as cursor position with scrolling canceled out, so the highest point is always 0.0f. (FLT_MAX for no change) 612 | ImVec2 ScrollTargetCenterRatio; // 0.0f = scroll so that target position is at top, 0.5f = scroll so that target position is centered 613 | bool ScrollbarX, ScrollbarY; 614 | ImVec2 ScrollbarSizes; 615 | float BorderSize; 616 | bool Active; // Set to true on Begin() 617 | bool WasActive; 618 | bool Accessed; // Set to true when any widget access the current window 619 | bool Collapsed; // Set when collapsing window to become only title-bar 620 | bool SkipItems; // == Visible && !Collapsed 621 | int BeginCount; // Number of Begin() during the current frame (generally 0 or 1, 1+ if appending via multiple Begin/End pairs) 622 | ImGuiID PopupId; // ID in the popup stack when this window is used as a popup/menu (because we use generic Name/ID for recycling) 623 | int AutoFitFramesX, AutoFitFramesY; 624 | bool AutoFitOnlyGrows; 625 | int AutoPosLastDirection; 626 | int HiddenFrames; 627 | int SetWindowPosAllowFlags; // bit ImGuiSetCond_*** specify if SetWindowPos() call will succeed with this particular flag. 628 | int SetWindowSizeAllowFlags; // bit ImGuiSetCond_*** specify if SetWindowSize() call will succeed with this particular flag. 629 | int SetWindowCollapsedAllowFlags; // bit ImGuiSetCond_*** specify if SetWindowCollapsed() call will succeed with this particular flag. 630 | bool SetWindowPosCenterWanted; 631 | 632 | ImGuiDrawContext DC; // Temporary per-window data, reset at the beginning of the frame 633 | ImVector IDStack; // ID stack. ID are hashes seeded with the value at the top of the stack 634 | ImRect ClipRect; // = DrawList->clip_rect_stack.back(). Scissoring / clipping rectangle. x1, y1, x2, y2. 635 | ImRect WindowRectClipped; // = WindowRect just after setup in Begin(). == window->Rect() for root window. 636 | int LastFrameActive; 637 | float ItemWidthDefault; 638 | ImGuiSimpleColumns MenuColumns; // Simplified columns storage for menu items 639 | ImGuiStorage StateStorage; 640 | float FontWindowScale; // Scale multiplier per-window 641 | ImDrawList* DrawList; 642 | ImGuiWindow* RootWindow; // If we are a child window, this is pointing to the first non-child parent window. Else point to ourself. 643 | ImGuiWindow* RootNonPopupWindow; // If we are a child window, this is pointing to the first non-child non-popup parent window. Else point to ourself. 644 | ImGuiWindow* ParentWindow; // If we are a child window, this is pointing to our parent window. Else point to NULL. 645 | 646 | // Navigation / Focus 647 | int FocusIdxAllCounter; // Start at -1 and increase as assigned via FocusItemRegister() 648 | int FocusIdxTabCounter; // (same, but only count widgets which you can Tab through) 649 | int FocusIdxAllRequestCurrent; // Item being requested for focus 650 | int FocusIdxTabRequestCurrent; // Tab-able item being requested for focus 651 | int FocusIdxAllRequestNext; // Item being requested for focus, for next update (relies on layout to be stable between the frame pressing TAB and the next frame) 652 | int FocusIdxTabRequestNext; // " 653 | 654 | public: 655 | ImGuiWindow(const char* name); 656 | ~ImGuiWindow(); 657 | 658 | ImGuiID GetID(const char* str, const char* str_end = NULL); 659 | ImGuiID GetID(const void* ptr); 660 | 661 | ImRect Rect() const { return ImRect(Pos.x, Pos.y, Pos.x+Size.x, Pos.y+Size.y); } 662 | float CalcFontSize() const { return GImGui->FontBaseSize * FontWindowScale; } 663 | float TitleBarHeight() const { return (Flags & ImGuiWindowFlags_NoTitleBar) ? 0.0f : CalcFontSize() + GImGui->Style.FramePadding.y * 2.0f; } 664 | ImRect TitleBarRect() const { return ImRect(Pos, ImVec2(Pos.x + SizeFull.x, Pos.y + TitleBarHeight())); } 665 | float MenuBarHeight() const { return (Flags & ImGuiWindowFlags_MenuBar) ? CalcFontSize() + GImGui->Style.FramePadding.y * 2.0f : 0.0f; } 666 | ImRect MenuBarRect() const { float y1 = Pos.y + TitleBarHeight(); return ImRect(Pos.x, y1, Pos.x + SizeFull.x, y1 + MenuBarHeight()); } 667 | }; 668 | 669 | //----------------------------------------------------------------------------- 670 | // Internal API 671 | // No guarantee of forward compatibility here. 672 | //----------------------------------------------------------------------------- 673 | 674 | namespace ImGui 675 | { 676 | // We should always have a CurrentWindow in the stack (there is an implicit "Debug" window) 677 | // If this ever crash because g.CurrentWindow is NULL it means that either 678 | // - ImGui::NewFrame() has never been called, which is illegal. 679 | // - You are calling ImGui functions after ImGui::Render() and before the next ImGui::NewFrame(), which is also illegal. 680 | inline ImGuiWindow* GetCurrentWindowRead() { ImGuiContext& g = *GImGui; return g.CurrentWindow; } 681 | inline ImGuiWindow* GetCurrentWindow() { ImGuiContext& g = *GImGui; g.CurrentWindow->Accessed = true; return g.CurrentWindow; } 682 | IMGUI_API ImGuiWindow* GetParentWindow(); 683 | IMGUI_API ImGuiWindow* FindWindowByName(const char* name); 684 | IMGUI_API void FocusWindow(ImGuiWindow* window); 685 | 686 | IMGUI_API void EndFrame(); // Ends the ImGui frame. Automatically called by Render()! you most likely don't need to ever call that yourself directly. If you don't need to render you can call EndFrame() but you'll have wasted CPU already. If you don't need to render, don't create any windows instead! 687 | 688 | IMGUI_API void SetActiveID(ImGuiID id, ImGuiWindow* window); 689 | IMGUI_API void SetHoveredID(ImGuiID id); 690 | IMGUI_API void KeepAliveID(ImGuiID id); 691 | 692 | IMGUI_API void ItemSize(const ImVec2& size, float text_offset_y = 0.0f); 693 | IMGUI_API void ItemSize(const ImRect& bb, float text_offset_y = 0.0f); 694 | IMGUI_API bool ItemAdd(const ImRect& bb, const ImGuiID* id); 695 | IMGUI_API bool IsClippedEx(const ImRect& bb, const ImGuiID* id, bool clip_even_when_logged); 696 | IMGUI_API bool IsHovered(const ImRect& bb, ImGuiID id, bool flatten_childs = false); 697 | IMGUI_API bool FocusableItemRegister(ImGuiWindow* window, bool is_active, bool tab_stop = true); // Return true if focus is requested 698 | IMGUI_API void FocusableItemUnregister(ImGuiWindow* window); 699 | IMGUI_API ImVec2 CalcItemSize(ImVec2 size, float default_x, float default_y); 700 | IMGUI_API float CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x); 701 | 702 | IMGUI_API void OpenPopupEx(const char* str_id, bool reopen_existing); 703 | 704 | inline IMGUI_API ImU32 GetColorU32(ImGuiCol idx, float alpha_mul) { ImVec4 c = GImGui->Style.Colors[idx]; c.w *= GImGui->Style.Alpha * alpha_mul; return ImGui::ColorConvertFloat4ToU32(c); } 705 | inline IMGUI_API ImU32 GetColorU32(const ImVec4& col) { ImVec4 c = col; c.w *= GImGui->Style.Alpha; return ImGui::ColorConvertFloat4ToU32(c); } 706 | 707 | // NB: All position are in absolute pixels coordinates (not window coordinates) 708 | // FIXME: All those functions are a mess and needs to be refactored into something decent. Avoid use outside of imgui.cpp! 709 | // We need: a sort of symbol library, preferably baked into font atlas when possible + decent text rendering helpers. 710 | IMGUI_API void RenderText(ImVec2 pos, const char* text, const char* text_end = NULL, bool hide_text_after_hash = true); 711 | IMGUI_API void RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width); 712 | IMGUI_API void RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, ImGuiAlign align = ImGuiAlign_Default, const ImVec2* clip_min = NULL, const ImVec2* clip_max = NULL); 713 | IMGUI_API void RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border = true, float rounding = 0.0f); 714 | IMGUI_API void RenderCollapseTriangle(ImVec2 pos, bool is_open, float scale = 1.0f, bool shadow = false); 715 | IMGUI_API void RenderBullet(ImVec2 pos); 716 | IMGUI_API void RenderCheckMark(ImVec2 pos, ImU32 col); 717 | IMGUI_API const char* FindRenderedTextEnd(const char* text, const char* text_end = NULL); // Find the optional ## from which we stop displaying text. 718 | 719 | IMGUI_API bool ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool* out_held, ImGuiButtonFlags flags = 0); 720 | IMGUI_API bool ButtonEx(const char* label, const ImVec2& size_arg = ImVec2(0,0), ImGuiButtonFlags flags = 0); 721 | IMGUI_API bool CloseButton(ImGuiID id, const ImVec2& pos, float radius); 722 | 723 | IMGUI_API bool SliderBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_min, float v_max, float power, int decimal_precision, ImGuiSliderFlags flags = 0); 724 | IMGUI_API bool SliderFloatN(const char* label, float* v, int components, float v_min, float v_max, const char* display_format, float power); 725 | IMGUI_API bool SliderIntN(const char* label, int* v, int components, int v_min, int v_max, const char* display_format); 726 | 727 | IMGUI_API bool DragBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_speed, float v_min, float v_max, int decimal_precision, float power); 728 | IMGUI_API bool DragFloatN(const char* label, float* v, int components, float v_speed, float v_min, float v_max, const char* display_format, float power); 729 | IMGUI_API bool DragIntN(const char* label, int* v, int components, float v_speed, int v_min, int v_max, const char* display_format); 730 | 731 | IMGUI_API bool InputTextEx(const char* label, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback = NULL, void* user_data = NULL); 732 | IMGUI_API bool InputFloatN(const char* label, float* v, int components, int decimal_precision, ImGuiInputTextFlags extra_flags); 733 | IMGUI_API bool InputIntN(const char* label, int* v, int components, ImGuiInputTextFlags extra_flags); 734 | IMGUI_API bool InputScalarEx(const char* label, ImGuiDataType data_type, void* data_ptr, void* step_ptr, void* step_fast_ptr, const char* scalar_format, ImGuiInputTextFlags extra_flags); 735 | IMGUI_API bool InputScalarAsWidgetReplacement(const ImRect& aabb, const char* label, ImGuiDataType data_type, void* data_ptr, ImGuiID id, int decimal_precision); 736 | 737 | IMGUI_API bool TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end = NULL); 738 | IMGUI_API bool TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags = 0); // Consume previous SetNextTreeNodeOpened() data, if any. May return true when logging 739 | IMGUI_API void TreePushRawID(ImGuiID id); 740 | 741 | IMGUI_API void PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size); 742 | 743 | IMGUI_API int ParseFormatPrecision(const char* fmt, int default_value); 744 | IMGUI_API float RoundScalar(float value, int decimal_precision); 745 | 746 | } // namespace ImGui 747 | 748 | #ifdef __clang__ 749 | #pragma clang diagnostic pop 750 | #endif 751 | 752 | #ifdef _MSC_VER 753 | #pragma warning (pop) 754 | #endif 755 | -------------------------------------------------------------------------------- /imgui/stb_rect_pack.h: -------------------------------------------------------------------------------- 1 | // stb_rect_pack.h - v0.08 - public domain - rectangle packing 2 | // Sean Barrett 2014 3 | // 4 | // Useful for e.g. packing rectangular textures into an atlas. 5 | // Does not do rotation. 6 | // 7 | // Not necessarily the awesomest packing method, but better than 8 | // the totally naive one in stb_truetype (which is primarily what 9 | // this is meant to replace). 10 | // 11 | // Has only had a few tests run, may have issues. 12 | // 13 | // More docs to come. 14 | // 15 | // No memory allocations; uses qsort() and assert() from stdlib. 16 | // Can override those by defining STBRP_SORT and STBRP_ASSERT. 17 | // 18 | // This library currently uses the Skyline Bottom-Left algorithm. 19 | // 20 | // Please note: better rectangle packers are welcome! Please 21 | // implement them to the same API, but with a different init 22 | // function. 23 | // 24 | // Credits 25 | // 26 | // Library 27 | // Sean Barrett 28 | // Minor features 29 | // Martins Mozeiko 30 | // Bugfixes / warning fixes 31 | // Jeremy Jaussaud 32 | // 33 | // Version history: 34 | // 35 | // 0.08 (2015-09-13) really fix bug with empty rects (w=0 or h=0) 36 | // 0.07 (2015-09-13) fix bug with empty rects (w=0 or h=0) 37 | // 0.06 (2015-04-15) added STBRP_SORT to allow replacing qsort 38 | // 0.05: added STBRP_ASSERT to allow replacing assert 39 | // 0.04: fixed minor bug in STBRP_LARGE_RECTS support 40 | // 0.01: initial release 41 | // 42 | // LICENSE 43 | // 44 | // This software is in the public domain. Where that dedication is not 45 | // recognized, you are granted a perpetual, irrevocable license to copy, 46 | // distribute, and modify this file as you see fit. 47 | 48 | ////////////////////////////////////////////////////////////////////////////// 49 | // 50 | // INCLUDE SECTION 51 | // 52 | 53 | #ifndef STB_INCLUDE_STB_RECT_PACK_H 54 | #define STB_INCLUDE_STB_RECT_PACK_H 55 | 56 | #define STB_RECT_PACK_VERSION 1 57 | 58 | #ifdef STBRP_STATIC 59 | #define STBRP_DEF static 60 | #else 61 | #define STBRP_DEF extern 62 | #endif 63 | 64 | #ifdef __cplusplus 65 | extern "C" { 66 | #endif 67 | 68 | typedef struct stbrp_context stbrp_context; 69 | typedef struct stbrp_node stbrp_node; 70 | typedef struct stbrp_rect stbrp_rect; 71 | 72 | #ifdef STBRP_LARGE_RECTS 73 | typedef int stbrp_coord; 74 | #else 75 | typedef unsigned short stbrp_coord; 76 | #endif 77 | 78 | STBRP_DEF void stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects); 79 | // Assign packed locations to rectangles. The rectangles are of type 80 | // 'stbrp_rect' defined below, stored in the array 'rects', and there 81 | // are 'num_rects' many of them. 82 | // 83 | // Rectangles which are successfully packed have the 'was_packed' flag 84 | // set to a non-zero value and 'x' and 'y' store the minimum location 85 | // on each axis (i.e. bottom-left in cartesian coordinates, top-left 86 | // if you imagine y increasing downwards). Rectangles which do not fit 87 | // have the 'was_packed' flag set to 0. 88 | // 89 | // You should not try to access the 'rects' array from another thread 90 | // while this function is running, as the function temporarily reorders 91 | // the array while it executes. 92 | // 93 | // To pack into another rectangle, you need to call stbrp_init_target 94 | // again. To continue packing into the same rectangle, you can call 95 | // this function again. Calling this multiple times with multiple rect 96 | // arrays will probably produce worse packing results than calling it 97 | // a single time with the full rectangle array, but the option is 98 | // available. 99 | 100 | struct stbrp_rect 101 | { 102 | // reserved for your use: 103 | int id; 104 | 105 | // input: 106 | stbrp_coord w, h; 107 | 108 | // output: 109 | stbrp_coord x, y; 110 | int was_packed; // non-zero if valid packing 111 | 112 | }; // 16 bytes, nominally 113 | 114 | 115 | STBRP_DEF void stbrp_init_target (stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes); 116 | // Initialize a rectangle packer to: 117 | // pack a rectangle that is 'width' by 'height' in dimensions 118 | // using temporary storage provided by the array 'nodes', which is 'num_nodes' long 119 | // 120 | // You must call this function every time you start packing into a new target. 121 | // 122 | // There is no "shutdown" function. The 'nodes' memory must stay valid for 123 | // the following stbrp_pack_rects() call (or calls), but can be freed after 124 | // the call (or calls) finish. 125 | // 126 | // Note: to guarantee best results, either: 127 | // 1. make sure 'num_nodes' >= 'width' 128 | // or 2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1' 129 | // 130 | // If you don't do either of the above things, widths will be quantized to multiples 131 | // of small integers to guarantee the algorithm doesn't run out of temporary storage. 132 | // 133 | // If you do #2, then the non-quantized algorithm will be used, but the algorithm 134 | // may run out of temporary storage and be unable to pack some rectangles. 135 | 136 | STBRP_DEF void stbrp_setup_allow_out_of_mem (stbrp_context *context, int allow_out_of_mem); 137 | // Optionally call this function after init but before doing any packing to 138 | // change the handling of the out-of-temp-memory scenario, described above. 139 | // If you call init again, this will be reset to the default (false). 140 | 141 | 142 | STBRP_DEF void stbrp_setup_heuristic (stbrp_context *context, int heuristic); 143 | // Optionally select which packing heuristic the library should use. Different 144 | // heuristics will produce better/worse results for different data sets. 145 | // If you call init again, this will be reset to the default. 146 | 147 | enum 148 | { 149 | STBRP_HEURISTIC_Skyline_default=0, 150 | STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default, 151 | STBRP_HEURISTIC_Skyline_BF_sortHeight 152 | }; 153 | 154 | 155 | ////////////////////////////////////////////////////////////////////////////// 156 | // 157 | // the details of the following structures don't matter to you, but they must 158 | // be visible so you can handle the memory allocations for them 159 | 160 | struct stbrp_node 161 | { 162 | stbrp_coord x,y; 163 | stbrp_node *next; 164 | }; 165 | 166 | struct stbrp_context 167 | { 168 | int width; 169 | int height; 170 | int align; 171 | int init_mode; 172 | int heuristic; 173 | int num_nodes; 174 | stbrp_node *active_head; 175 | stbrp_node *free_head; 176 | stbrp_node extra[2]; // we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2' 177 | }; 178 | 179 | #ifdef __cplusplus 180 | } 181 | #endif 182 | 183 | #endif 184 | 185 | ////////////////////////////////////////////////////////////////////////////// 186 | // 187 | // IMPLEMENTATION SECTION 188 | // 189 | 190 | #ifdef STB_RECT_PACK_IMPLEMENTATION 191 | #ifndef STBRP_SORT 192 | #include 193 | #define STBRP_SORT qsort 194 | #endif 195 | 196 | #ifndef STBRP_ASSERT 197 | #include 198 | #define STBRP_ASSERT assert 199 | #endif 200 | 201 | enum 202 | { 203 | STBRP__INIT_skyline = 1 204 | }; 205 | 206 | STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic) 207 | { 208 | switch (context->init_mode) { 209 | case STBRP__INIT_skyline: 210 | STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight); 211 | context->heuristic = heuristic; 212 | break; 213 | default: 214 | STBRP_ASSERT(0); 215 | } 216 | } 217 | 218 | STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem) 219 | { 220 | if (allow_out_of_mem) 221 | // if it's ok to run out of memory, then don't bother aligning them; 222 | // this gives better packing, but may fail due to OOM (even though 223 | // the rectangles easily fit). @TODO a smarter approach would be to only 224 | // quantize once we've hit OOM, then we could get rid of this parameter. 225 | context->align = 1; 226 | else { 227 | // if it's not ok to run out of memory, then quantize the widths 228 | // so that num_nodes is always enough nodes. 229 | // 230 | // I.e. num_nodes * align >= width 231 | // align >= width / num_nodes 232 | // align = ceil(width/num_nodes) 233 | 234 | context->align = (context->width + context->num_nodes-1) / context->num_nodes; 235 | } 236 | } 237 | 238 | STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes) 239 | { 240 | int i; 241 | #ifndef STBRP_LARGE_RECTS 242 | STBRP_ASSERT(width <= 0xffff && height <= 0xffff); 243 | #endif 244 | 245 | for (i=0; i < num_nodes-1; ++i) 246 | nodes[i].next = &nodes[i+1]; 247 | nodes[i].next = NULL; 248 | context->init_mode = STBRP__INIT_skyline; 249 | context->heuristic = STBRP_HEURISTIC_Skyline_default; 250 | context->free_head = &nodes[0]; 251 | context->active_head = &context->extra[0]; 252 | context->width = width; 253 | context->height = height; 254 | context->num_nodes = num_nodes; 255 | stbrp_setup_allow_out_of_mem(context, 0); 256 | 257 | // node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly) 258 | context->extra[0].x = 0; 259 | context->extra[0].y = 0; 260 | context->extra[0].next = &context->extra[1]; 261 | context->extra[1].x = (stbrp_coord) width; 262 | #ifdef STBRP_LARGE_RECTS 263 | context->extra[1].y = (1<<30); 264 | #else 265 | context->extra[1].y = 65535; 266 | #endif 267 | context->extra[1].next = NULL; 268 | } 269 | 270 | // find minimum y position if it starts at x1 271 | static int stbrp__skyline_find_min_y(stbrp_context *, stbrp_node *first, int x0, int width, int *pwaste) 272 | { 273 | //(void)c; 274 | stbrp_node *node = first; 275 | int x1 = x0 + width; 276 | int min_y, visited_width, waste_area; 277 | STBRP_ASSERT(first->x <= x0); 278 | 279 | #if 0 280 | // skip in case we're past the node 281 | while (node->next->x <= x0) 282 | ++node; 283 | #else 284 | STBRP_ASSERT(node->next->x > x0); // we ended up handling this in the caller for efficiency 285 | #endif 286 | 287 | STBRP_ASSERT(node->x <= x0); 288 | 289 | min_y = 0; 290 | waste_area = 0; 291 | visited_width = 0; 292 | while (node->x < x1) { 293 | if (node->y > min_y) { 294 | // raise min_y higher. 295 | // we've accounted for all waste up to min_y, 296 | // but we'll now add more waste for everything we've visted 297 | waste_area += visited_width * (node->y - min_y); 298 | min_y = node->y; 299 | // the first time through, visited_width might be reduced 300 | if (node->x < x0) 301 | visited_width += node->next->x - x0; 302 | else 303 | visited_width += node->next->x - node->x; 304 | } else { 305 | // add waste area 306 | int under_width = node->next->x - node->x; 307 | if (under_width + visited_width > width) 308 | under_width = width - visited_width; 309 | waste_area += under_width * (min_y - node->y); 310 | visited_width += under_width; 311 | } 312 | node = node->next; 313 | } 314 | 315 | *pwaste = waste_area; 316 | return min_y; 317 | } 318 | 319 | typedef struct 320 | { 321 | int x,y; 322 | stbrp_node **prev_link; 323 | } stbrp__findresult; 324 | 325 | static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int width, int height) 326 | { 327 | int best_waste = (1<<30), best_x, best_y = (1 << 30); 328 | stbrp__findresult fr; 329 | stbrp_node **prev, *node, *tail, **best = NULL; 330 | 331 | // align to multiple of c->align 332 | width = (width + c->align - 1); 333 | width -= width % c->align; 334 | STBRP_ASSERT(width % c->align == 0); 335 | 336 | node = c->active_head; 337 | prev = &c->active_head; 338 | while (node->x + width <= c->width) { 339 | int y,waste; 340 | y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste); 341 | if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { // actually just want to test BL 342 | // bottom left 343 | if (y < best_y) { 344 | best_y = y; 345 | best = prev; 346 | } 347 | } else { 348 | // best-fit 349 | if (y + height <= c->height) { 350 | // can only use it if it first vertically 351 | if (y < best_y || (y == best_y && waste < best_waste)) { 352 | best_y = y; 353 | best_waste = waste; 354 | best = prev; 355 | } 356 | } 357 | } 358 | prev = &node->next; 359 | node = node->next; 360 | } 361 | 362 | best_x = (best == NULL) ? 0 : (*best)->x; 363 | 364 | // if doing best-fit (BF), we also have to try aligning right edge to each node position 365 | // 366 | // e.g, if fitting 367 | // 368 | // ____________________ 369 | // |____________________| 370 | // 371 | // into 372 | // 373 | // | | 374 | // | ____________| 375 | // |____________| 376 | // 377 | // then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned 378 | // 379 | // This makes BF take about 2x the time 380 | 381 | if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) { 382 | tail = c->active_head; 383 | node = c->active_head; 384 | prev = &c->active_head; 385 | // find first node that's admissible 386 | while (tail->x < width) 387 | tail = tail->next; 388 | while (tail) { 389 | int xpos = tail->x - width; 390 | int y,waste; 391 | STBRP_ASSERT(xpos >= 0); 392 | // find the left position that matches this 393 | while (node->next->x <= xpos) { 394 | prev = &node->next; 395 | node = node->next; 396 | } 397 | STBRP_ASSERT(node->next->x > xpos && node->x <= xpos); 398 | y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste); 399 | if (y + height < c->height) { 400 | if (y <= best_y) { 401 | if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) { 402 | best_x = xpos; 403 | STBRP_ASSERT(y <= best_y); 404 | best_y = y; 405 | best_waste = waste; 406 | best = prev; 407 | } 408 | } 409 | } 410 | tail = tail->next; 411 | } 412 | } 413 | 414 | fr.prev_link = best; 415 | fr.x = best_x; 416 | fr.y = best_y; 417 | return fr; 418 | } 419 | 420 | static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height) 421 | { 422 | // find best position according to heuristic 423 | stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height); 424 | stbrp_node *node, *cur; 425 | 426 | // bail if: 427 | // 1. it failed 428 | // 2. the best node doesn't fit (we don't always check this) 429 | // 3. we're out of memory 430 | if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) { 431 | res.prev_link = NULL; 432 | return res; 433 | } 434 | 435 | // on success, create new node 436 | node = context->free_head; 437 | node->x = (stbrp_coord) res.x; 438 | node->y = (stbrp_coord) (res.y + height); 439 | 440 | context->free_head = node->next; 441 | 442 | // insert the new node into the right starting point, and 443 | // let 'cur' point to the remaining nodes needing to be 444 | // stiched back in 445 | 446 | cur = *res.prev_link; 447 | if (cur->x < res.x) { 448 | // preserve the existing one, so start testing with the next one 449 | stbrp_node *next = cur->next; 450 | cur->next = node; 451 | cur = next; 452 | } else { 453 | *res.prev_link = node; 454 | } 455 | 456 | // from here, traverse cur and free the nodes, until we get to one 457 | // that shouldn't be freed 458 | while (cur->next && cur->next->x <= res.x + width) { 459 | stbrp_node *next = cur->next; 460 | // move the current node to the free list 461 | cur->next = context->free_head; 462 | context->free_head = cur; 463 | cur = next; 464 | } 465 | 466 | // stitch the list back in 467 | node->next = cur; 468 | 469 | if (cur->x < res.x + width) 470 | cur->x = (stbrp_coord) (res.x + width); 471 | 472 | #ifdef _DEBUG 473 | cur = context->active_head; 474 | while (cur->x < context->width) { 475 | STBRP_ASSERT(cur->x < cur->next->x); 476 | cur = cur->next; 477 | } 478 | STBRP_ASSERT(cur->next == NULL); 479 | 480 | { 481 | stbrp_node *L1 = NULL, *L2 = NULL; 482 | int count=0; 483 | cur = context->active_head; 484 | while (cur) { 485 | L1 = cur; 486 | cur = cur->next; 487 | ++count; 488 | } 489 | cur = context->free_head; 490 | while (cur) { 491 | L2 = cur; 492 | cur = cur->next; 493 | ++count; 494 | } 495 | STBRP_ASSERT(count == context->num_nodes+2); 496 | } 497 | #endif 498 | 499 | return res; 500 | } 501 | 502 | static int rect_height_compare(const void *a, const void *b) 503 | { 504 | stbrp_rect *p = (stbrp_rect *) a; 505 | stbrp_rect *q = (stbrp_rect *) b; 506 | if (p->h > q->h) 507 | return -1; 508 | if (p->h < q->h) 509 | return 1; 510 | return (p->w > q->w) ? -1 : (p->w < q->w); 511 | } 512 | 513 | static int rect_width_compare(const void *a, const void *b) 514 | { 515 | stbrp_rect *p = (stbrp_rect *) a; 516 | stbrp_rect *q = (stbrp_rect *) b; 517 | if (p->w > q->w) 518 | return -1; 519 | if (p->w < q->w) 520 | return 1; 521 | return (p->h > q->h) ? -1 : (p->h < q->h); 522 | } 523 | 524 | static int rect_original_order(const void *a, const void *b) 525 | { 526 | stbrp_rect *p = (stbrp_rect *) a; 527 | stbrp_rect *q = (stbrp_rect *) b; 528 | return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed); 529 | } 530 | 531 | #ifdef STBRP_LARGE_RECTS 532 | #define STBRP__MAXVAL 0xffffffff 533 | #else 534 | #define STBRP__MAXVAL 0xffff 535 | #endif 536 | 537 | STBRP_DEF void stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects) 538 | { 539 | int i; 540 | 541 | // we use the 'was_packed' field internally to allow sorting/unsorting 542 | for (i=0; i < num_rects; ++i) { 543 | rects[i].was_packed = i; 544 | #ifndef STBRP_LARGE_RECTS 545 | STBRP_ASSERT(rects[i].w <= 0xffff && rects[i].h <= 0xffff); 546 | #endif 547 | } 548 | 549 | // sort according to heuristic 550 | STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_height_compare); 551 | 552 | for (i=0; i < num_rects; ++i) { 553 | if (rects[i].w == 0 || rects[i].h == 0) { 554 | rects[i].x = rects[i].y = 0; // empty rect needs no space 555 | } else { 556 | stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h); 557 | if (fr.prev_link) { 558 | rects[i].x = (stbrp_coord) fr.x; 559 | rects[i].y = (stbrp_coord) fr.y; 560 | } else { 561 | rects[i].x = rects[i].y = STBRP__MAXVAL; 562 | } 563 | } 564 | } 565 | 566 | // unsort 567 | STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_original_order); 568 | 569 | // set was_packed flags 570 | for (i=0; i < num_rects; ++i) 571 | rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL); 572 | } 573 | #endif 574 | -------------------------------------------------------------------------------- /imgui/stb_textedit.h: -------------------------------------------------------------------------------- 1 | // [ImGui] this is a slightly modified version of stb_truetype.h 1.8 2 | // [ImGui] - fixed a crash bug in stb_textedit_discard_redo (#681) 3 | // [ImGui] - fixed some minor warnings 4 | // [ImGui] - added STB_TEXTEDIT_MOVEWORDLEFT/STB_TEXTEDIT_MOVEWORDRIGHT custom handler (#473) 5 | 6 | // stb_textedit.h - v1.8 - public domain - Sean Barrett 7 | // Development of this library was sponsored by RAD Game Tools 8 | // 9 | // This C header file implements the guts of a multi-line text-editing 10 | // widget; you implement display, word-wrapping, and low-level string 11 | // insertion/deletion, and stb_textedit will map user inputs into 12 | // insertions & deletions, plus updates to the cursor position, 13 | // selection state, and undo state. 14 | // 15 | // It is intended for use in games and other systems that need to build 16 | // their own custom widgets and which do not have heavy text-editing 17 | // requirements (this library is not recommended for use for editing large 18 | // texts, as its performance does not scale and it has limited undo). 19 | // 20 | // Non-trivial behaviors are modelled after Windows text controls. 21 | // 22 | // 23 | // LICENSE 24 | // 25 | // This software is dual-licensed to the public domain and under the following 26 | // license: you are granted a perpetual, irrevocable license to copy, modify, 27 | // publish, and distribute this file as you see fit. 28 | // 29 | // 30 | // DEPENDENCIES 31 | // 32 | // Uses the C runtime function 'memmove', which you can override 33 | // by defining STB_TEXTEDIT_memmove before the implementation. 34 | // Uses no other functions. Performs no runtime allocations. 35 | // 36 | // 37 | // VERSION HISTORY 38 | // 39 | // 1.8 (2016-04-02) better keyboard handling when mouse button is down 40 | // 1.7 (2015-09-13) change y range handling in case baseline is non-0 41 | // 1.6 (2015-04-15) allow STB_TEXTEDIT_memmove 42 | // 1.5 (2014-09-10) add support for secondary keys for OS X 43 | // 1.4 (2014-08-17) fix signed/unsigned warnings 44 | // 1.3 (2014-06-19) fix mouse clicking to round to nearest char boundary 45 | // 1.2 (2014-05-27) fix some RAD types that had crept into the new code 46 | // 1.1 (2013-12-15) move-by-word (requires STB_TEXTEDIT_IS_SPACE ) 47 | // 1.0 (2012-07-26) improve documentation, initial public release 48 | // 0.3 (2012-02-24) bugfixes, single-line mode; insert mode 49 | // 0.2 (2011-11-28) fixes to undo/redo 50 | // 0.1 (2010-07-08) initial version 51 | // 52 | // ADDITIONAL CONTRIBUTORS 53 | // 54 | // Ulf Winklemann: move-by-word in 1.1 55 | // Fabian Giesen: secondary key inputs in 1.5 56 | // Martins Mozeiko: STB_TEXTEDIT_memmove 57 | // 58 | // Bugfixes: 59 | // Scott Graham 60 | // Daniel Keller 61 | // Omar Cornut 62 | // 63 | // USAGE 64 | // 65 | // This file behaves differently depending on what symbols you define 66 | // before including it. 67 | // 68 | // 69 | // Header-file mode: 70 | // 71 | // If you do not define STB_TEXTEDIT_IMPLEMENTATION before including this, 72 | // it will operate in "header file" mode. In this mode, it declares a 73 | // single public symbol, STB_TexteditState, which encapsulates the current 74 | // state of a text widget (except for the string, which you will store 75 | // separately). 76 | // 77 | // To compile in this mode, you must define STB_TEXTEDIT_CHARTYPE to a 78 | // primitive type that defines a single character (e.g. char, wchar_t, etc). 79 | // 80 | // To save space or increase undo-ability, you can optionally define the 81 | // following things that are used by the undo system: 82 | // 83 | // STB_TEXTEDIT_POSITIONTYPE small int type encoding a valid cursor position 84 | // STB_TEXTEDIT_UNDOSTATECOUNT the number of undo states to allow 85 | // STB_TEXTEDIT_UNDOCHARCOUNT the number of characters to store in the undo buffer 86 | // 87 | // If you don't define these, they are set to permissive types and 88 | // moderate sizes. The undo system does no memory allocations, so 89 | // it grows STB_TexteditState by the worst-case storage which is (in bytes): 90 | // 91 | // [4 + sizeof(STB_TEXTEDIT_POSITIONTYPE)] * STB_TEXTEDIT_UNDOSTATE_COUNT 92 | // + sizeof(STB_TEXTEDIT_CHARTYPE) * STB_TEXTEDIT_UNDOCHAR_COUNT 93 | // 94 | // 95 | // Implementation mode: 96 | // 97 | // If you define STB_TEXTEDIT_IMPLEMENTATION before including this, it 98 | // will compile the implementation of the text edit widget, depending 99 | // on a large number of symbols which must be defined before the include. 100 | // 101 | // The implementation is defined only as static functions. You will then 102 | // need to provide your own APIs in the same file which will access the 103 | // static functions. 104 | // 105 | // The basic concept is that you provide a "string" object which 106 | // behaves like an array of characters. stb_textedit uses indices to 107 | // refer to positions in the string, implicitly representing positions 108 | // in the displayed textedit. This is true for both plain text and 109 | // rich text; even with rich text stb_truetype interacts with your 110 | // code as if there was an array of all the displayed characters. 111 | // 112 | // Symbols that must be the same in header-file and implementation mode: 113 | // 114 | // STB_TEXTEDIT_CHARTYPE the character type 115 | // STB_TEXTEDIT_POSITIONTYPE small type that a valid cursor position 116 | // STB_TEXTEDIT_UNDOSTATECOUNT the number of undo states to allow 117 | // STB_TEXTEDIT_UNDOCHARCOUNT the number of characters to store in the undo buffer 118 | // 119 | // Symbols you must define for implementation mode: 120 | // 121 | // STB_TEXTEDIT_STRING the type of object representing a string being edited, 122 | // typically this is a wrapper object with other data you need 123 | // 124 | // STB_TEXTEDIT_STRINGLEN(obj) the length of the string (ideally O(1)) 125 | // STB_TEXTEDIT_LAYOUTROW(&r,obj,n) returns the results of laying out a line of characters 126 | // starting from character #n (see discussion below) 127 | // STB_TEXTEDIT_GETWIDTH(obj,n,i) returns the pixel delta from the xpos of the i'th character 128 | // to the xpos of the i+1'th char for a line of characters 129 | // starting at character #n (i.e. accounts for kerning 130 | // with previous char) 131 | // STB_TEXTEDIT_KEYTOTEXT(k) maps a keyboard input to an insertable character 132 | // (return type is int, -1 means not valid to insert) 133 | // STB_TEXTEDIT_GETCHAR(obj,i) returns the i'th character of obj, 0-based 134 | // STB_TEXTEDIT_NEWLINE the character returned by _GETCHAR() we recognize 135 | // as manually wordwrapping for end-of-line positioning 136 | // 137 | // STB_TEXTEDIT_DELETECHARS(obj,i,n) delete n characters starting at i 138 | // STB_TEXTEDIT_INSERTCHARS(obj,i,c*,n) insert n characters at i (pointed to by STB_TEXTEDIT_CHARTYPE*) 139 | // 140 | // STB_TEXTEDIT_K_SHIFT a power of two that is or'd in to a keyboard input to represent the shift key 141 | // 142 | // STB_TEXTEDIT_K_LEFT keyboard input to move cursor left 143 | // STB_TEXTEDIT_K_RIGHT keyboard input to move cursor right 144 | // STB_TEXTEDIT_K_UP keyboard input to move cursor up 145 | // STB_TEXTEDIT_K_DOWN keyboard input to move cursor down 146 | // STB_TEXTEDIT_K_LINESTART keyboard input to move cursor to start of line // e.g. HOME 147 | // STB_TEXTEDIT_K_LINEEND keyboard input to move cursor to end of line // e.g. END 148 | // STB_TEXTEDIT_K_TEXTSTART keyboard input to move cursor to start of text // e.g. ctrl-HOME 149 | // STB_TEXTEDIT_K_TEXTEND keyboard input to move cursor to end of text // e.g. ctrl-END 150 | // STB_TEXTEDIT_K_DELETE keyboard input to delete selection or character under cursor 151 | // STB_TEXTEDIT_K_BACKSPACE keyboard input to delete selection or character left of cursor 152 | // STB_TEXTEDIT_K_UNDO keyboard input to perform undo 153 | // STB_TEXTEDIT_K_REDO keyboard input to perform redo 154 | // 155 | // Optional: 156 | // STB_TEXTEDIT_K_INSERT keyboard input to toggle insert mode 157 | // STB_TEXTEDIT_IS_SPACE(ch) true if character is whitespace (e.g. 'isspace'), 158 | // required for default WORDLEFT/WORDRIGHT handlers 159 | // STB_TEXTEDIT_MOVEWORDLEFT(obj,i) custom handler for WORDLEFT, returns index to move cursor to 160 | // STB_TEXTEDIT_MOVEWORDRIGHT(obj,i) custom handler for WORDRIGHT, returns index to move cursor to 161 | // STB_TEXTEDIT_K_WORDLEFT keyboard input to move cursor left one word // e.g. ctrl-LEFT 162 | // STB_TEXTEDIT_K_WORDRIGHT keyboard input to move cursor right one word // e.g. ctrl-RIGHT 163 | // STB_TEXTEDIT_K_LINESTART2 secondary keyboard input to move cursor to start of line 164 | // STB_TEXTEDIT_K_LINEEND2 secondary keyboard input to move cursor to end of line 165 | // STB_TEXTEDIT_K_TEXTSTART2 secondary keyboard input to move cursor to start of text 166 | // STB_TEXTEDIT_K_TEXTEND2 secondary keyboard input to move cursor to end of text 167 | // 168 | // Todo: 169 | // STB_TEXTEDIT_K_PGUP keyboard input to move cursor up a page 170 | // STB_TEXTEDIT_K_PGDOWN keyboard input to move cursor down a page 171 | // 172 | // Keyboard input must be encoded as a single integer value; e.g. a character code 173 | // and some bitflags that represent shift states. to simplify the interface, SHIFT must 174 | // be a bitflag, so we can test the shifted state of cursor movements to allow selection, 175 | // i.e. (STB_TEXTED_K_RIGHT|STB_TEXTEDIT_K_SHIFT) should be shifted right-arrow. 176 | // 177 | // You can encode other things, such as CONTROL or ALT, in additional bits, and 178 | // then test for their presence in e.g. STB_TEXTEDIT_K_WORDLEFT. For example, 179 | // my Windows implementations add an additional CONTROL bit, and an additional KEYDOWN 180 | // bit. Then all of the STB_TEXTEDIT_K_ values bitwise-or in the KEYDOWN bit, 181 | // and I pass both WM_KEYDOWN and WM_CHAR events to the "key" function in the 182 | // API below. The control keys will only match WM_KEYDOWN events because of the 183 | // keydown bit I add, and STB_TEXTEDIT_KEYTOTEXT only tests for the KEYDOWN 184 | // bit so it only decodes WM_CHAR events. 185 | // 186 | // STB_TEXTEDIT_LAYOUTROW returns information about the shape of one displayed 187 | // row of characters assuming they start on the i'th character--the width and 188 | // the height and the number of characters consumed. This allows this library 189 | // to traverse the entire layout incrementally. You need to compute word-wrapping 190 | // here. 191 | // 192 | // Each textfield keeps its own insert mode state, which is not how normal 193 | // applications work. To keep an app-wide insert mode, update/copy the 194 | // "insert_mode" field of STB_TexteditState before/after calling API functions. 195 | // 196 | // API 197 | // 198 | // void stb_textedit_initialize_state(STB_TexteditState *state, int is_single_line) 199 | // 200 | // void stb_textedit_click(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) 201 | // void stb_textedit_drag(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) 202 | // int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) 203 | // int stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int len) 204 | // void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int key) 205 | // 206 | // Each of these functions potentially updates the string and updates the 207 | // state. 208 | // 209 | // initialize_state: 210 | // set the textedit state to a known good default state when initially 211 | // constructing the textedit. 212 | // 213 | // click: 214 | // call this with the mouse x,y on a mouse down; it will update the cursor 215 | // and reset the selection start/end to the cursor point. the x,y must 216 | // be relative to the text widget, with (0,0) being the top left. 217 | // 218 | // drag: 219 | // call this with the mouse x,y on a mouse drag/up; it will update the 220 | // cursor and the selection end point 221 | // 222 | // cut: 223 | // call this to delete the current selection; returns true if there was 224 | // one. you should FIRST copy the current selection to the system paste buffer. 225 | // (To copy, just copy the current selection out of the string yourself.) 226 | // 227 | // paste: 228 | // call this to paste text at the current cursor point or over the current 229 | // selection if there is one. 230 | // 231 | // key: 232 | // call this for keyboard inputs sent to the textfield. you can use it 233 | // for "key down" events or for "translated" key events. if you need to 234 | // do both (as in Win32), or distinguish Unicode characters from control 235 | // inputs, set a high bit to distinguish the two; then you can define the 236 | // various definitions like STB_TEXTEDIT_K_LEFT have the is-key-event bit 237 | // set, and make STB_TEXTEDIT_KEYTOCHAR check that the is-key-event bit is 238 | // clear. 239 | // 240 | // When rendering, you can read the cursor position and selection state from 241 | // the STB_TexteditState. 242 | // 243 | // 244 | // Notes: 245 | // 246 | // This is designed to be usable in IMGUI, so it allows for the possibility of 247 | // running in an IMGUI that has NOT cached the multi-line layout. For this 248 | // reason, it provides an interface that is compatible with computing the 249 | // layout incrementally--we try to make sure we make as few passes through 250 | // as possible. (For example, to locate the mouse pointer in the text, we 251 | // could define functions that return the X and Y positions of characters 252 | // and binary search Y and then X, but if we're doing dynamic layout this 253 | // will run the layout algorithm many times, so instead we manually search 254 | // forward in one pass. Similar logic applies to e.g. up-arrow and 255 | // down-arrow movement.) 256 | // 257 | // If it's run in a widget that *has* cached the layout, then this is less 258 | // efficient, but it's not horrible on modern computers. But you wouldn't 259 | // want to edit million-line files with it. 260 | 261 | 262 | //////////////////////////////////////////////////////////////////////////// 263 | //////////////////////////////////////////////////////////////////////////// 264 | //// 265 | //// Header-file mode 266 | //// 267 | //// 268 | 269 | #ifndef INCLUDE_STB_TEXTEDIT_H 270 | #define INCLUDE_STB_TEXTEDIT_H 271 | 272 | //////////////////////////////////////////////////////////////////////// 273 | // 274 | // STB_TexteditState 275 | // 276 | // Definition of STB_TexteditState which you should store 277 | // per-textfield; it includes cursor position, selection state, 278 | // and undo state. 279 | // 280 | 281 | #ifndef STB_TEXTEDIT_UNDOSTATECOUNT 282 | #define STB_TEXTEDIT_UNDOSTATECOUNT 99 283 | #endif 284 | #ifndef STB_TEXTEDIT_UNDOCHARCOUNT 285 | #define STB_TEXTEDIT_UNDOCHARCOUNT 999 286 | #endif 287 | #ifndef STB_TEXTEDIT_CHARTYPE 288 | #define STB_TEXTEDIT_CHARTYPE int 289 | #endif 290 | #ifndef STB_TEXTEDIT_POSITIONTYPE 291 | #define STB_TEXTEDIT_POSITIONTYPE int 292 | #endif 293 | 294 | typedef struct 295 | { 296 | // private data 297 | STB_TEXTEDIT_POSITIONTYPE where; 298 | short insert_length; 299 | short delete_length; 300 | short char_storage; 301 | } StbUndoRecord; 302 | 303 | typedef struct 304 | { 305 | // private data 306 | StbUndoRecord undo_rec [STB_TEXTEDIT_UNDOSTATECOUNT]; 307 | STB_TEXTEDIT_CHARTYPE undo_char[STB_TEXTEDIT_UNDOCHARCOUNT]; 308 | short undo_point, redo_point; 309 | short undo_char_point, redo_char_point; 310 | } StbUndoState; 311 | 312 | typedef struct 313 | { 314 | ///////////////////// 315 | // 316 | // public data 317 | // 318 | 319 | int cursor; 320 | // position of the text cursor within the string 321 | 322 | int select_start; // selection start point 323 | int select_end; 324 | // selection start and end point in characters; if equal, no selection. 325 | // note that start may be less than or greater than end (e.g. when 326 | // dragging the mouse, start is where the initial click was, and you 327 | // can drag in either direction) 328 | 329 | unsigned char insert_mode; 330 | // each textfield keeps its own insert mode state. to keep an app-wide 331 | // insert mode, copy this value in/out of the app state 332 | 333 | ///////////////////// 334 | // 335 | // private data 336 | // 337 | unsigned char cursor_at_end_of_line; // not implemented yet 338 | unsigned char initialized; 339 | unsigned char has_preferred_x; 340 | unsigned char single_line; 341 | unsigned char padding1, padding2, padding3; 342 | float preferred_x; // this determines where the cursor up/down tries to seek to along x 343 | StbUndoState undostate; 344 | } STB_TexteditState; 345 | 346 | 347 | //////////////////////////////////////////////////////////////////////// 348 | // 349 | // StbTexteditRow 350 | // 351 | // Result of layout query, used by stb_textedit to determine where 352 | // the text in each row is. 353 | 354 | // result of layout query 355 | typedef struct 356 | { 357 | float x0,x1; // starting x location, end x location (allows for align=right, etc) 358 | float baseline_y_delta; // position of baseline relative to previous row's baseline 359 | float ymin,ymax; // height of row above and below baseline 360 | int num_chars; 361 | } StbTexteditRow; 362 | #endif //INCLUDE_STB_TEXTEDIT_H 363 | 364 | 365 | //////////////////////////////////////////////////////////////////////////// 366 | //////////////////////////////////////////////////////////////////////////// 367 | //// 368 | //// Implementation mode 369 | //// 370 | //// 371 | 372 | 373 | // implementation isn't include-guarded, since it might have indirectly 374 | // included just the "header" portion 375 | #ifdef STB_TEXTEDIT_IMPLEMENTATION 376 | 377 | #ifndef STB_TEXTEDIT_memmove 378 | #include 379 | #define STB_TEXTEDIT_memmove memmove 380 | #endif 381 | 382 | 383 | ///////////////////////////////////////////////////////////////////////////// 384 | // 385 | // Mouse input handling 386 | // 387 | 388 | // traverse the layout to locate the nearest character to a display position 389 | static int stb_text_locate_coord(STB_TEXTEDIT_STRING *str, float x, float y) 390 | { 391 | StbTexteditRow r; 392 | int n = STB_TEXTEDIT_STRINGLEN(str); 393 | float base_y = 0, prev_x; 394 | int i=0, k; 395 | 396 | r.x0 = r.x1 = 0; 397 | r.ymin = r.ymax = 0; 398 | r.num_chars = 0; 399 | 400 | // search rows to find one that straddles 'y' 401 | while (i < n) { 402 | STB_TEXTEDIT_LAYOUTROW(&r, str, i); 403 | if (r.num_chars <= 0) 404 | return n; 405 | 406 | if (i==0 && y < base_y + r.ymin) 407 | return 0; 408 | 409 | if (y < base_y + r.ymax) 410 | break; 411 | 412 | i += r.num_chars; 413 | base_y += r.baseline_y_delta; 414 | } 415 | 416 | // below all text, return 'after' last character 417 | if (i >= n) 418 | return n; 419 | 420 | // check if it's before the beginning of the line 421 | if (x < r.x0) 422 | return i; 423 | 424 | // check if it's before the end of the line 425 | if (x < r.x1) { 426 | // search characters in row for one that straddles 'x' 427 | k = i; 428 | prev_x = r.x0; 429 | for (i=0; i < r.num_chars; ++i) { 430 | float w = STB_TEXTEDIT_GETWIDTH(str, k, i); 431 | if (x < prev_x+w) { 432 | if (x < prev_x+w/2) 433 | return k+i; 434 | else 435 | return k+i+1; 436 | } 437 | prev_x += w; 438 | } 439 | // shouldn't happen, but if it does, fall through to end-of-line case 440 | } 441 | 442 | // if the last character is a newline, return that. otherwise return 'after' the last character 443 | if (STB_TEXTEDIT_GETCHAR(str, i+r.num_chars-1) == STB_TEXTEDIT_NEWLINE) 444 | return i+r.num_chars-1; 445 | else 446 | return i+r.num_chars; 447 | } 448 | 449 | // API click: on mouse down, move the cursor to the clicked location, and reset the selection 450 | static void stb_textedit_click(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) 451 | { 452 | state->cursor = stb_text_locate_coord(str, x, y); 453 | state->select_start = state->cursor; 454 | state->select_end = state->cursor; 455 | state->has_preferred_x = 0; 456 | } 457 | 458 | // API drag: on mouse drag, move the cursor and selection endpoint to the clicked location 459 | static void stb_textedit_drag(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) 460 | { 461 | int p = stb_text_locate_coord(str, x, y); 462 | if (state->select_start == state->select_end) 463 | state->select_start = state->cursor; 464 | state->cursor = state->select_end = p; 465 | } 466 | 467 | ///////////////////////////////////////////////////////////////////////////// 468 | // 469 | // Keyboard input handling 470 | // 471 | 472 | // forward declarations 473 | static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state); 474 | static void stb_text_redo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state); 475 | static void stb_text_makeundo_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length); 476 | static void stb_text_makeundo_insert(STB_TexteditState *state, int where, int length); 477 | static void stb_text_makeundo_replace(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length); 478 | 479 | typedef struct 480 | { 481 | float x,y; // position of n'th character 482 | float height; // height of line 483 | int first_char, length; // first char of row, and length 484 | int prev_first; // first char of previous row 485 | } StbFindState; 486 | 487 | // find the x/y location of a character, and remember info about the previous row in 488 | // case we get a move-up event (for page up, we'll have to rescan) 489 | static void stb_textedit_find_charpos(StbFindState *find, STB_TEXTEDIT_STRING *str, int n, int single_line) 490 | { 491 | StbTexteditRow r; 492 | int prev_start = 0; 493 | int z = STB_TEXTEDIT_STRINGLEN(str); 494 | int i=0, first; 495 | 496 | if (n == z) { 497 | // if it's at the end, then find the last line -- simpler than trying to 498 | // explicitly handle this case in the regular code 499 | if (single_line) { 500 | STB_TEXTEDIT_LAYOUTROW(&r, str, 0); 501 | find->y = 0; 502 | find->first_char = 0; 503 | find->length = z; 504 | find->height = r.ymax - r.ymin; 505 | find->x = r.x1; 506 | } else { 507 | find->y = 0; 508 | find->x = 0; 509 | find->height = 1; 510 | while (i < z) { 511 | STB_TEXTEDIT_LAYOUTROW(&r, str, i); 512 | prev_start = i; 513 | i += r.num_chars; 514 | } 515 | find->first_char = i; 516 | find->length = 0; 517 | find->prev_first = prev_start; 518 | } 519 | return; 520 | } 521 | 522 | // search rows to find the one that straddles character n 523 | find->y = 0; 524 | 525 | for(;;) { 526 | STB_TEXTEDIT_LAYOUTROW(&r, str, i); 527 | if (n < i + r.num_chars) 528 | break; 529 | prev_start = i; 530 | i += r.num_chars; 531 | find->y += r.baseline_y_delta; 532 | } 533 | 534 | find->first_char = first = i; 535 | find->length = r.num_chars; 536 | find->height = r.ymax - r.ymin; 537 | find->prev_first = prev_start; 538 | 539 | // now scan to find xpos 540 | find->x = r.x0; 541 | i = 0; 542 | for (i=0; first+i < n; ++i) 543 | find->x += STB_TEXTEDIT_GETWIDTH(str, first, i); 544 | } 545 | 546 | #define STB_TEXT_HAS_SELECTION(s) ((s)->select_start != (s)->select_end) 547 | 548 | // make the selection/cursor state valid if client altered the string 549 | static void stb_textedit_clamp(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) 550 | { 551 | int n = STB_TEXTEDIT_STRINGLEN(str); 552 | if (STB_TEXT_HAS_SELECTION(state)) { 553 | if (state->select_start > n) state->select_start = n; 554 | if (state->select_end > n) state->select_end = n; 555 | // if clamping forced them to be equal, move the cursor to match 556 | if (state->select_start == state->select_end) 557 | state->cursor = state->select_start; 558 | } 559 | if (state->cursor > n) state->cursor = n; 560 | } 561 | 562 | // delete characters while updating undo 563 | static void stb_textedit_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int len) 564 | { 565 | stb_text_makeundo_delete(str, state, where, len); 566 | STB_TEXTEDIT_DELETECHARS(str, where, len); 567 | state->has_preferred_x = 0; 568 | } 569 | 570 | // delete the section 571 | static void stb_textedit_delete_selection(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) 572 | { 573 | stb_textedit_clamp(str, state); 574 | if (STB_TEXT_HAS_SELECTION(state)) { 575 | if (state->select_start < state->select_end) { 576 | stb_textedit_delete(str, state, state->select_start, state->select_end - state->select_start); 577 | state->select_end = state->cursor = state->select_start; 578 | } else { 579 | stb_textedit_delete(str, state, state->select_end, state->select_start - state->select_end); 580 | state->select_start = state->cursor = state->select_end; 581 | } 582 | state->has_preferred_x = 0; 583 | } 584 | } 585 | 586 | // canoncialize the selection so start <= end 587 | static void stb_textedit_sortselection(STB_TexteditState *state) 588 | { 589 | if (state->select_end < state->select_start) { 590 | int temp = state->select_end; 591 | state->select_end = state->select_start; 592 | state->select_start = temp; 593 | } 594 | } 595 | 596 | // move cursor to first character of selection 597 | static void stb_textedit_move_to_first(STB_TexteditState *state) 598 | { 599 | if (STB_TEXT_HAS_SELECTION(state)) { 600 | stb_textedit_sortselection(state); 601 | state->cursor = state->select_start; 602 | state->select_end = state->select_start; 603 | state->has_preferred_x = 0; 604 | } 605 | } 606 | 607 | // move cursor to last character of selection 608 | static void stb_textedit_move_to_last(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) 609 | { 610 | if (STB_TEXT_HAS_SELECTION(state)) { 611 | stb_textedit_sortselection(state); 612 | stb_textedit_clamp(str, state); 613 | state->cursor = state->select_end; 614 | state->select_start = state->select_end; 615 | state->has_preferred_x = 0; 616 | } 617 | } 618 | 619 | #ifdef STB_TEXTEDIT_IS_SPACE 620 | static int is_word_boundary( STB_TEXTEDIT_STRING *_str, int _idx ) 621 | { 622 | return _idx > 0 ? (STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(_str,_idx-1) ) && !STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(_str, _idx) ) ) : 1; 623 | } 624 | 625 | #ifndef STB_TEXTEDIT_MOVEWORDLEFT 626 | static int stb_textedit_move_to_word_previous( STB_TEXTEDIT_STRING *_str, int c ) 627 | { 628 | while( c >= 0 && !is_word_boundary( _str, c ) ) 629 | --c; 630 | 631 | if( c < 0 ) 632 | c = 0; 633 | 634 | return c; 635 | } 636 | #define STB_TEXTEDIT_MOVEWORDLEFT stb_textedit_move_to_word_previous 637 | #endif 638 | 639 | #ifndef STB_TEXTEDIT_MOVEWORDRIGHT 640 | static int stb_textedit_move_to_word_next( STB_TEXTEDIT_STRING *_str, int c ) 641 | { 642 | const int len = STB_TEXTEDIT_STRINGLEN(_str); 643 | while( c < len && !is_word_boundary( _str, c ) ) 644 | ++c; 645 | 646 | if( c > len ) 647 | c = len; 648 | 649 | return c; 650 | } 651 | #define STB_TEXTEDIT_MOVEWORDRIGHT stb_textedit_move_to_word_next 652 | #endif 653 | 654 | #endif 655 | 656 | // update selection and cursor to match each other 657 | static void stb_textedit_prep_selection_at_cursor(STB_TexteditState *state) 658 | { 659 | if (!STB_TEXT_HAS_SELECTION(state)) 660 | state->select_start = state->select_end = state->cursor; 661 | else 662 | state->cursor = state->select_end; 663 | } 664 | 665 | // API cut: delete selection 666 | static int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) 667 | { 668 | if (STB_TEXT_HAS_SELECTION(state)) { 669 | stb_textedit_delete_selection(str,state); // implicity clamps 670 | state->has_preferred_x = 0; 671 | return 1; 672 | } 673 | return 0; 674 | } 675 | 676 | // API paste: replace existing selection with passed-in text 677 | static int stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE const *ctext, int len) 678 | { 679 | STB_TEXTEDIT_CHARTYPE *text = (STB_TEXTEDIT_CHARTYPE *) ctext; 680 | // if there's a selection, the paste should delete it 681 | stb_textedit_clamp(str, state); 682 | stb_textedit_delete_selection(str,state); 683 | // try to insert the characters 684 | if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, len)) { 685 | stb_text_makeundo_insert(state, state->cursor, len); 686 | state->cursor += len; 687 | state->has_preferred_x = 0; 688 | return 1; 689 | } 690 | // remove the undo since we didn't actually insert the characters 691 | if (state->undostate.undo_point) 692 | --state->undostate.undo_point; 693 | return 0; 694 | } 695 | 696 | // API key: process a keyboard input 697 | static void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int key) 698 | { 699 | retry: 700 | switch (key) { 701 | default: { 702 | int c = STB_TEXTEDIT_KEYTOTEXT(key); 703 | if (c > 0) { 704 | STB_TEXTEDIT_CHARTYPE ch = (STB_TEXTEDIT_CHARTYPE) c; 705 | 706 | // can't add newline in single-line mode 707 | if (c == '\n' && state->single_line) 708 | break; 709 | 710 | if (state->insert_mode && !STB_TEXT_HAS_SELECTION(state) && state->cursor < STB_TEXTEDIT_STRINGLEN(str)) { 711 | stb_text_makeundo_replace(str, state, state->cursor, 1, 1); 712 | STB_TEXTEDIT_DELETECHARS(str, state->cursor, 1); 713 | if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, &ch, 1)) { 714 | ++state->cursor; 715 | state->has_preferred_x = 0; 716 | } 717 | } else { 718 | stb_textedit_delete_selection(str,state); // implicity clamps 719 | if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, &ch, 1)) { 720 | stb_text_makeundo_insert(state, state->cursor, 1); 721 | ++state->cursor; 722 | state->has_preferred_x = 0; 723 | } 724 | } 725 | } 726 | break; 727 | } 728 | 729 | #ifdef STB_TEXTEDIT_K_INSERT 730 | case STB_TEXTEDIT_K_INSERT: 731 | state->insert_mode = !state->insert_mode; 732 | break; 733 | #endif 734 | 735 | case STB_TEXTEDIT_K_UNDO: 736 | stb_text_undo(str, state); 737 | state->has_preferred_x = 0; 738 | break; 739 | 740 | case STB_TEXTEDIT_K_REDO: 741 | stb_text_redo(str, state); 742 | state->has_preferred_x = 0; 743 | break; 744 | 745 | case STB_TEXTEDIT_K_LEFT: 746 | // if currently there's a selection, move cursor to start of selection 747 | if (STB_TEXT_HAS_SELECTION(state)) 748 | stb_textedit_move_to_first(state); 749 | else 750 | if (state->cursor > 0) 751 | --state->cursor; 752 | state->has_preferred_x = 0; 753 | break; 754 | 755 | case STB_TEXTEDIT_K_RIGHT: 756 | // if currently there's a selection, move cursor to end of selection 757 | if (STB_TEXT_HAS_SELECTION(state)) 758 | stb_textedit_move_to_last(str, state); 759 | else 760 | ++state->cursor; 761 | stb_textedit_clamp(str, state); 762 | state->has_preferred_x = 0; 763 | break; 764 | 765 | case STB_TEXTEDIT_K_LEFT | STB_TEXTEDIT_K_SHIFT: 766 | stb_textedit_clamp(str, state); 767 | stb_textedit_prep_selection_at_cursor(state); 768 | // move selection left 769 | if (state->select_end > 0) 770 | --state->select_end; 771 | state->cursor = state->select_end; 772 | state->has_preferred_x = 0; 773 | break; 774 | 775 | #ifdef STB_TEXTEDIT_MOVEWORDLEFT 776 | case STB_TEXTEDIT_K_WORDLEFT: 777 | if (STB_TEXT_HAS_SELECTION(state)) 778 | stb_textedit_move_to_first(state); 779 | else { 780 | state->cursor = STB_TEXTEDIT_MOVEWORDLEFT(str, state->cursor-1); 781 | stb_textedit_clamp( str, state ); 782 | } 783 | break; 784 | 785 | case STB_TEXTEDIT_K_WORDLEFT | STB_TEXTEDIT_K_SHIFT: 786 | if( !STB_TEXT_HAS_SELECTION( state ) ) 787 | stb_textedit_prep_selection_at_cursor(state); 788 | 789 | state->cursor = STB_TEXTEDIT_MOVEWORDLEFT(str, state->cursor-1); 790 | state->select_end = state->cursor; 791 | 792 | stb_textedit_clamp( str, state ); 793 | break; 794 | #endif 795 | 796 | #ifdef STB_TEXTEDIT_MOVEWORDRIGHT 797 | case STB_TEXTEDIT_K_WORDRIGHT: 798 | if (STB_TEXT_HAS_SELECTION(state)) 799 | stb_textedit_move_to_last(str, state); 800 | else { 801 | state->cursor = STB_TEXTEDIT_MOVEWORDRIGHT(str, state->cursor+1); 802 | stb_textedit_clamp( str, state ); 803 | } 804 | break; 805 | 806 | case STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT: 807 | if( !STB_TEXT_HAS_SELECTION( state ) ) 808 | stb_textedit_prep_selection_at_cursor(state); 809 | 810 | state->cursor = STB_TEXTEDIT_MOVEWORDRIGHT(str, state->cursor+1); 811 | state->select_end = state->cursor; 812 | 813 | stb_textedit_clamp( str, state ); 814 | break; 815 | #endif 816 | 817 | case STB_TEXTEDIT_K_RIGHT | STB_TEXTEDIT_K_SHIFT: 818 | stb_textedit_prep_selection_at_cursor(state); 819 | // move selection right 820 | ++state->select_end; 821 | stb_textedit_clamp(str, state); 822 | state->cursor = state->select_end; 823 | state->has_preferred_x = 0; 824 | break; 825 | 826 | case STB_TEXTEDIT_K_DOWN: 827 | case STB_TEXTEDIT_K_DOWN | STB_TEXTEDIT_K_SHIFT: { 828 | StbFindState find; 829 | StbTexteditRow row; 830 | int i, sel = (key & STB_TEXTEDIT_K_SHIFT) != 0; 831 | 832 | if (state->single_line) { 833 | // on windows, up&down in single-line behave like left&right 834 | key = STB_TEXTEDIT_K_RIGHT | (key & STB_TEXTEDIT_K_SHIFT); 835 | goto retry; 836 | } 837 | 838 | if (sel) 839 | stb_textedit_prep_selection_at_cursor(state); 840 | else if (STB_TEXT_HAS_SELECTION(state)) 841 | stb_textedit_move_to_last(str,state); 842 | 843 | // compute current position of cursor point 844 | stb_textedit_clamp(str, state); 845 | stb_textedit_find_charpos(&find, str, state->cursor, state->single_line); 846 | 847 | // now find character position down a row 848 | if (find.length) { 849 | float goal_x = state->has_preferred_x ? state->preferred_x : find.x; 850 | float x; 851 | int start = find.first_char + find.length; 852 | state->cursor = start; 853 | STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor); 854 | x = row.x0; 855 | for (i=0; i < row.num_chars; ++i) { 856 | float dx = STB_TEXTEDIT_GETWIDTH(str, start, i); 857 | #ifdef STB_TEXTEDIT_GETWIDTH_NEWLINE 858 | if (dx == STB_TEXTEDIT_GETWIDTH_NEWLINE) 859 | break; 860 | #endif 861 | x += dx; 862 | if (x > goal_x) 863 | break; 864 | ++state->cursor; 865 | } 866 | stb_textedit_clamp(str, state); 867 | 868 | state->has_preferred_x = 1; 869 | state->preferred_x = goal_x; 870 | 871 | if (sel) 872 | state->select_end = state->cursor; 873 | } 874 | break; 875 | } 876 | 877 | case STB_TEXTEDIT_K_UP: 878 | case STB_TEXTEDIT_K_UP | STB_TEXTEDIT_K_SHIFT: { 879 | StbFindState find; 880 | StbTexteditRow row; 881 | int i, sel = (key & STB_TEXTEDIT_K_SHIFT) != 0; 882 | 883 | if (state->single_line) { 884 | // on windows, up&down become left&right 885 | key = STB_TEXTEDIT_K_LEFT | (key & STB_TEXTEDIT_K_SHIFT); 886 | goto retry; 887 | } 888 | 889 | if (sel) 890 | stb_textedit_prep_selection_at_cursor(state); 891 | else if (STB_TEXT_HAS_SELECTION(state)) 892 | stb_textedit_move_to_first(state); 893 | 894 | // compute current position of cursor point 895 | stb_textedit_clamp(str, state); 896 | stb_textedit_find_charpos(&find, str, state->cursor, state->single_line); 897 | 898 | // can only go up if there's a previous row 899 | if (find.prev_first != find.first_char) { 900 | // now find character position up a row 901 | float goal_x = state->has_preferred_x ? state->preferred_x : find.x; 902 | float x; 903 | state->cursor = find.prev_first; 904 | STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor); 905 | x = row.x0; 906 | for (i=0; i < row.num_chars; ++i) { 907 | float dx = STB_TEXTEDIT_GETWIDTH(str, find.prev_first, i); 908 | #ifdef STB_TEXTEDIT_GETWIDTH_NEWLINE 909 | if (dx == STB_TEXTEDIT_GETWIDTH_NEWLINE) 910 | break; 911 | #endif 912 | x += dx; 913 | if (x > goal_x) 914 | break; 915 | ++state->cursor; 916 | } 917 | stb_textedit_clamp(str, state); 918 | 919 | state->has_preferred_x = 1; 920 | state->preferred_x = goal_x; 921 | 922 | if (sel) 923 | state->select_end = state->cursor; 924 | } 925 | break; 926 | } 927 | 928 | case STB_TEXTEDIT_K_DELETE: 929 | case STB_TEXTEDIT_K_DELETE | STB_TEXTEDIT_K_SHIFT: 930 | if (STB_TEXT_HAS_SELECTION(state)) 931 | stb_textedit_delete_selection(str, state); 932 | else { 933 | int n = STB_TEXTEDIT_STRINGLEN(str); 934 | if (state->cursor < n) 935 | stb_textedit_delete(str, state, state->cursor, 1); 936 | } 937 | state->has_preferred_x = 0; 938 | break; 939 | 940 | case STB_TEXTEDIT_K_BACKSPACE: 941 | case STB_TEXTEDIT_K_BACKSPACE | STB_TEXTEDIT_K_SHIFT: 942 | if (STB_TEXT_HAS_SELECTION(state)) 943 | stb_textedit_delete_selection(str, state); 944 | else { 945 | stb_textedit_clamp(str, state); 946 | if (state->cursor > 0) { 947 | stb_textedit_delete(str, state, state->cursor-1, 1); 948 | --state->cursor; 949 | } 950 | } 951 | state->has_preferred_x = 0; 952 | break; 953 | 954 | #ifdef STB_TEXTEDIT_K_TEXTSTART2 955 | case STB_TEXTEDIT_K_TEXTSTART2: 956 | #endif 957 | case STB_TEXTEDIT_K_TEXTSTART: 958 | state->cursor = state->select_start = state->select_end = 0; 959 | state->has_preferred_x = 0; 960 | break; 961 | 962 | #ifdef STB_TEXTEDIT_K_TEXTEND2 963 | case STB_TEXTEDIT_K_TEXTEND2: 964 | #endif 965 | case STB_TEXTEDIT_K_TEXTEND: 966 | state->cursor = STB_TEXTEDIT_STRINGLEN(str); 967 | state->select_start = state->select_end = 0; 968 | state->has_preferred_x = 0; 969 | break; 970 | 971 | #ifdef STB_TEXTEDIT_K_TEXTSTART2 972 | case STB_TEXTEDIT_K_TEXTSTART2 | STB_TEXTEDIT_K_SHIFT: 973 | #endif 974 | case STB_TEXTEDIT_K_TEXTSTART | STB_TEXTEDIT_K_SHIFT: 975 | stb_textedit_prep_selection_at_cursor(state); 976 | state->cursor = state->select_end = 0; 977 | state->has_preferred_x = 0; 978 | break; 979 | 980 | #ifdef STB_TEXTEDIT_K_TEXTEND2 981 | case STB_TEXTEDIT_K_TEXTEND2 | STB_TEXTEDIT_K_SHIFT: 982 | #endif 983 | case STB_TEXTEDIT_K_TEXTEND | STB_TEXTEDIT_K_SHIFT: 984 | stb_textedit_prep_selection_at_cursor(state); 985 | state->cursor = state->select_end = STB_TEXTEDIT_STRINGLEN(str); 986 | state->has_preferred_x = 0; 987 | break; 988 | 989 | 990 | #ifdef STB_TEXTEDIT_K_LINESTART2 991 | case STB_TEXTEDIT_K_LINESTART2: 992 | #endif 993 | case STB_TEXTEDIT_K_LINESTART: { 994 | StbFindState find; 995 | stb_textedit_clamp(str, state); 996 | stb_textedit_move_to_first(state); 997 | stb_textedit_find_charpos(&find, str, state->cursor, state->single_line); 998 | state->cursor = find.first_char; 999 | state->has_preferred_x = 0; 1000 | break; 1001 | } 1002 | 1003 | #ifdef STB_TEXTEDIT_K_LINEEND2 1004 | case STB_TEXTEDIT_K_LINEEND2: 1005 | #endif 1006 | case STB_TEXTEDIT_K_LINEEND: { 1007 | StbFindState find; 1008 | stb_textedit_clamp(str, state); 1009 | stb_textedit_move_to_first(state); 1010 | stb_textedit_find_charpos(&find, str, state->cursor, state->single_line); 1011 | 1012 | state->has_preferred_x = 0; 1013 | state->cursor = find.first_char + find.length; 1014 | if (find.length > 0 && STB_TEXTEDIT_GETCHAR(str, state->cursor-1) == STB_TEXTEDIT_NEWLINE) 1015 | --state->cursor; 1016 | break; 1017 | } 1018 | 1019 | #ifdef STB_TEXTEDIT_K_LINESTART2 1020 | case STB_TEXTEDIT_K_LINESTART2 | STB_TEXTEDIT_K_SHIFT: 1021 | #endif 1022 | case STB_TEXTEDIT_K_LINESTART | STB_TEXTEDIT_K_SHIFT: { 1023 | StbFindState find; 1024 | stb_textedit_clamp(str, state); 1025 | stb_textedit_prep_selection_at_cursor(state); 1026 | stb_textedit_find_charpos(&find, str, state->cursor, state->single_line); 1027 | state->cursor = state->select_end = find.first_char; 1028 | state->has_preferred_x = 0; 1029 | break; 1030 | } 1031 | 1032 | #ifdef STB_TEXTEDIT_K_LINEEND2 1033 | case STB_TEXTEDIT_K_LINEEND2 | STB_TEXTEDIT_K_SHIFT: 1034 | #endif 1035 | case STB_TEXTEDIT_K_LINEEND | STB_TEXTEDIT_K_SHIFT: { 1036 | StbFindState find; 1037 | stb_textedit_clamp(str, state); 1038 | stb_textedit_prep_selection_at_cursor(state); 1039 | stb_textedit_find_charpos(&find, str, state->cursor, state->single_line); 1040 | state->has_preferred_x = 0; 1041 | state->cursor = find.first_char + find.length; 1042 | if (find.length > 0 && STB_TEXTEDIT_GETCHAR(str, state->cursor-1) == STB_TEXTEDIT_NEWLINE) 1043 | --state->cursor; 1044 | state->select_end = state->cursor; 1045 | break; 1046 | } 1047 | 1048 | // @TODO: 1049 | // STB_TEXTEDIT_K_PGUP - move cursor up a page 1050 | // STB_TEXTEDIT_K_PGDOWN - move cursor down a page 1051 | } 1052 | } 1053 | 1054 | ///////////////////////////////////////////////////////////////////////////// 1055 | // 1056 | // Undo processing 1057 | // 1058 | // @OPTIMIZE: the undo/redo buffer should be circular 1059 | 1060 | static void stb_textedit_flush_redo(StbUndoState *state) 1061 | { 1062 | state->redo_point = STB_TEXTEDIT_UNDOSTATECOUNT; 1063 | state->redo_char_point = STB_TEXTEDIT_UNDOCHARCOUNT; 1064 | } 1065 | 1066 | // discard the oldest entry in the undo list 1067 | static void stb_textedit_discard_undo(StbUndoState *state) 1068 | { 1069 | if (state->undo_point > 0) { 1070 | // if the 0th undo state has characters, clean those up 1071 | if (state->undo_rec[0].char_storage >= 0) { 1072 | int n = state->undo_rec[0].insert_length, i; 1073 | // delete n characters from all other records 1074 | state->undo_char_point = state->undo_char_point - (short) n; // vsnet05 1075 | STB_TEXTEDIT_memmove(state->undo_char, state->undo_char + n, (size_t) ((size_t)state->undo_char_point*sizeof(STB_TEXTEDIT_CHARTYPE))); 1076 | for (i=0; i < state->undo_point; ++i) 1077 | if (state->undo_rec[i].char_storage >= 0) 1078 | state->undo_rec[i].char_storage = state->undo_rec[i].char_storage - (short) n; // vsnet05 // @OPTIMIZE: get rid of char_storage and infer it 1079 | } 1080 | --state->undo_point; 1081 | STB_TEXTEDIT_memmove(state->undo_rec, state->undo_rec+1, (size_t) ((size_t)state->undo_point*sizeof(state->undo_rec[0]))); 1082 | } 1083 | } 1084 | 1085 | // discard the oldest entry in the redo list--it's bad if this 1086 | // ever happens, but because undo & redo have to store the actual 1087 | // characters in different cases, the redo character buffer can 1088 | // fill up even though the undo buffer didn't 1089 | static void stb_textedit_discard_redo(StbUndoState *state) 1090 | { 1091 | int k = STB_TEXTEDIT_UNDOSTATECOUNT-1; 1092 | 1093 | if (state->redo_point <= k) { 1094 | // if the k'th undo state has characters, clean those up 1095 | if (state->undo_rec[k].char_storage >= 0) { 1096 | int n = state->undo_rec[k].insert_length, i; 1097 | // delete n characters from all other records 1098 | state->redo_char_point = state->redo_char_point + (short) n; // vsnet05 1099 | STB_TEXTEDIT_memmove(state->undo_char + state->redo_char_point, state->undo_char + state->redo_char_point-n, (size_t) ((size_t)(STB_TEXTEDIT_UNDOCHARCOUNT - state->redo_char_point)*sizeof(STB_TEXTEDIT_CHARTYPE))); 1100 | for (i=state->redo_point; i < k; ++i) 1101 | if (state->undo_rec[i].char_storage >= 0) 1102 | state->undo_rec[i].char_storage = state->undo_rec[i].char_storage + (short) n; // vsnet05 1103 | } 1104 | ++state->redo_point; 1105 | STB_TEXTEDIT_memmove(state->undo_rec + state->redo_point-1, state->undo_rec + state->redo_point, (size_t) ((size_t)(STB_TEXTEDIT_UNDOSTATECOUNT - state->redo_point)*sizeof(state->undo_rec[0]))); 1106 | } 1107 | } 1108 | 1109 | static StbUndoRecord *stb_text_create_undo_record(StbUndoState *state, int numchars) 1110 | { 1111 | // any time we create a new undo record, we discard redo 1112 | stb_textedit_flush_redo(state); 1113 | 1114 | // if we have no free records, we have to make room, by sliding the 1115 | // existing records down 1116 | if (state->undo_point == STB_TEXTEDIT_UNDOSTATECOUNT) 1117 | stb_textedit_discard_undo(state); 1118 | 1119 | // if the characters to store won't possibly fit in the buffer, we can't undo 1120 | if (numchars > STB_TEXTEDIT_UNDOCHARCOUNT) { 1121 | state->undo_point = 0; 1122 | state->undo_char_point = 0; 1123 | return NULL; 1124 | } 1125 | 1126 | // if we don't have enough free characters in the buffer, we have to make room 1127 | while (state->undo_char_point + numchars > STB_TEXTEDIT_UNDOCHARCOUNT) 1128 | stb_textedit_discard_undo(state); 1129 | 1130 | return &state->undo_rec[state->undo_point++]; 1131 | } 1132 | 1133 | static STB_TEXTEDIT_CHARTYPE *stb_text_createundo(StbUndoState *state, int pos, int insert_len, int delete_len) 1134 | { 1135 | StbUndoRecord *r = stb_text_create_undo_record(state, insert_len); 1136 | if (r == NULL) 1137 | return NULL; 1138 | 1139 | r->where = pos; 1140 | r->insert_length = (short) insert_len; 1141 | r->delete_length = (short) delete_len; 1142 | 1143 | if (insert_len == 0) { 1144 | r->char_storage = -1; 1145 | return NULL; 1146 | } else { 1147 | r->char_storage = state->undo_char_point; 1148 | state->undo_char_point = state->undo_char_point + (short) insert_len; 1149 | return &state->undo_char[r->char_storage]; 1150 | } 1151 | } 1152 | 1153 | static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) 1154 | { 1155 | StbUndoState *s = &state->undostate; 1156 | StbUndoRecord u, *r; 1157 | if (s->undo_point == 0) 1158 | return; 1159 | 1160 | // we need to do two things: apply the undo record, and create a redo record 1161 | u = s->undo_rec[s->undo_point-1]; 1162 | r = &s->undo_rec[s->redo_point-1]; 1163 | r->char_storage = -1; 1164 | 1165 | r->insert_length = u.delete_length; 1166 | r->delete_length = u.insert_length; 1167 | r->where = u.where; 1168 | 1169 | if (u.delete_length) { 1170 | // if the undo record says to delete characters, then the redo record will 1171 | // need to re-insert the characters that get deleted, so we need to store 1172 | // them. 1173 | 1174 | // there are three cases: 1175 | // there's enough room to store the characters 1176 | // characters stored for *redoing* don't leave room for redo 1177 | // characters stored for *undoing* don't leave room for redo 1178 | // if the last is true, we have to bail 1179 | 1180 | if (s->undo_char_point + u.delete_length >= STB_TEXTEDIT_UNDOCHARCOUNT) { 1181 | // the undo records take up too much character space; there's no space to store the redo characters 1182 | r->insert_length = 0; 1183 | } else { 1184 | int i; 1185 | 1186 | // there's definitely room to store the characters eventually 1187 | while (s->undo_char_point + u.delete_length > s->redo_char_point) { 1188 | // there's currently not enough room, so discard a redo record 1189 | stb_textedit_discard_redo(s); 1190 | // should never happen: 1191 | if (s->redo_point == STB_TEXTEDIT_UNDOSTATECOUNT) 1192 | return; 1193 | } 1194 | r = &s->undo_rec[s->redo_point-1]; 1195 | 1196 | r->char_storage = s->redo_char_point - u.delete_length; 1197 | s->redo_char_point = s->redo_char_point - (short) u.delete_length; 1198 | 1199 | // now save the characters 1200 | for (i=0; i < u.delete_length; ++i) 1201 | s->undo_char[r->char_storage + i] = STB_TEXTEDIT_GETCHAR(str, u.where + i); 1202 | } 1203 | 1204 | // now we can carry out the deletion 1205 | STB_TEXTEDIT_DELETECHARS(str, u.where, u.delete_length); 1206 | } 1207 | 1208 | // check type of recorded action: 1209 | if (u.insert_length) { 1210 | // easy case: was a deletion, so we need to insert n characters 1211 | STB_TEXTEDIT_INSERTCHARS(str, u.where, &s->undo_char[u.char_storage], u.insert_length); 1212 | s->undo_char_point -= u.insert_length; 1213 | } 1214 | 1215 | state->cursor = u.where + u.insert_length; 1216 | 1217 | s->undo_point--; 1218 | s->redo_point--; 1219 | } 1220 | 1221 | static void stb_text_redo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) 1222 | { 1223 | StbUndoState *s = &state->undostate; 1224 | StbUndoRecord *u, r; 1225 | if (s->redo_point == STB_TEXTEDIT_UNDOSTATECOUNT) 1226 | return; 1227 | 1228 | // we need to do two things: apply the redo record, and create an undo record 1229 | u = &s->undo_rec[s->undo_point]; 1230 | r = s->undo_rec[s->redo_point]; 1231 | 1232 | // we KNOW there must be room for the undo record, because the redo record 1233 | // was derived from an undo record 1234 | 1235 | u->delete_length = r.insert_length; 1236 | u->insert_length = r.delete_length; 1237 | u->where = r.where; 1238 | u->char_storage = -1; 1239 | 1240 | if (r.delete_length) { 1241 | // the redo record requires us to delete characters, so the undo record 1242 | // needs to store the characters 1243 | 1244 | if (s->undo_char_point + u->insert_length > s->redo_char_point) { 1245 | u->insert_length = 0; 1246 | u->delete_length = 0; 1247 | } else { 1248 | int i; 1249 | u->char_storage = s->undo_char_point; 1250 | s->undo_char_point = s->undo_char_point + u->insert_length; 1251 | 1252 | // now save the characters 1253 | for (i=0; i < u->insert_length; ++i) 1254 | s->undo_char[u->char_storage + i] = STB_TEXTEDIT_GETCHAR(str, u->where + i); 1255 | } 1256 | 1257 | STB_TEXTEDIT_DELETECHARS(str, r.where, r.delete_length); 1258 | } 1259 | 1260 | if (r.insert_length) { 1261 | // easy case: need to insert n characters 1262 | STB_TEXTEDIT_INSERTCHARS(str, r.where, &s->undo_char[r.char_storage], r.insert_length); 1263 | } 1264 | 1265 | state->cursor = r.where + r.insert_length; 1266 | 1267 | s->undo_point++; 1268 | s->redo_point++; 1269 | } 1270 | 1271 | static void stb_text_makeundo_insert(STB_TexteditState *state, int where, int length) 1272 | { 1273 | stb_text_createundo(&state->undostate, where, 0, length); 1274 | } 1275 | 1276 | static void stb_text_makeundo_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length) 1277 | { 1278 | int i; 1279 | STB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, length, 0); 1280 | if (p) { 1281 | for (i=0; i < length; ++i) 1282 | p[i] = STB_TEXTEDIT_GETCHAR(str, where+i); 1283 | } 1284 | } 1285 | 1286 | static void stb_text_makeundo_replace(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length) 1287 | { 1288 | int i; 1289 | STB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, old_length, new_length); 1290 | if (p) { 1291 | for (i=0; i < old_length; ++i) 1292 | p[i] = STB_TEXTEDIT_GETCHAR(str, where+i); 1293 | } 1294 | } 1295 | 1296 | // reset the state to default 1297 | static void stb_textedit_clear_state(STB_TexteditState *state, int is_single_line) 1298 | { 1299 | state->undostate.undo_point = 0; 1300 | state->undostate.undo_char_point = 0; 1301 | state->undostate.redo_point = STB_TEXTEDIT_UNDOSTATECOUNT; 1302 | state->undostate.redo_char_point = STB_TEXTEDIT_UNDOCHARCOUNT; 1303 | state->select_end = state->select_start = 0; 1304 | state->cursor = 0; 1305 | state->has_preferred_x = 0; 1306 | state->preferred_x = 0; 1307 | state->cursor_at_end_of_line = 0; 1308 | state->initialized = 1; 1309 | state->single_line = (unsigned char) is_single_line; 1310 | state->insert_mode = 0; 1311 | } 1312 | 1313 | // API initialize 1314 | static void stb_textedit_initialize_state(STB_TexteditState *state, int is_single_line) 1315 | { 1316 | stb_textedit_clear_state(state, is_single_line); 1317 | } 1318 | #endif//STB_TEXTEDIT_IMPLEMENTATION 1319 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include "dxutil.h" 2 | #include "imgui/imgui.h" 3 | #include "imgui/imgui_impl_dx11.h" 4 | #include "scene.h" 5 | 6 | #pragma comment(lib, "d3d11.lib") 7 | #pragma comment(lib, "dxgi.lib") 8 | #pragma comment(lib, "d3dcompiler.lib") 9 | 10 | const D3D_FEATURE_LEVEL kMinFeatureLevel = D3D_FEATURE_LEVEL_11_0; 11 | const int kSwapChainBufferCount = 3; 12 | const DXGI_FORMAT kSwapChainFormat = DXGI_FORMAT_R8G8B8A8_UNORM; 13 | const UINT kSwapChainFlags = DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT; 14 | 15 | static HWND g_hWnd = NULL; 16 | static bool g_ShouldClose = false; 17 | static ID3D11Device* g_Device; 18 | static ID3D11DeviceContext* g_DeviceContext; 19 | static IDXGISwapChain* g_SwapChain; 20 | static HANDLE g_FrameLatencyWaitableObject; 21 | static D3D11_RENDER_TARGET_VIEW_DESC g_SwapChainRTVDesc; 22 | 23 | void RendererResize(int width, int height); 24 | 25 | IMGUI_API LRESULT ImGui_ImplDX11_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); 26 | 27 | static LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) 28 | { 29 | ImGui_ImplDX11_WndProcHandler(hWnd, msg, wParam, lParam); 30 | 31 | switch (msg) 32 | { 33 | case WM_SIZE: 34 | if (g_Device && wParam != SIZE_MINIMIZED) 35 | { 36 | int windowWidth = (int)LOWORD(lParam); 37 | int windowHeight = (int)HIWORD(lParam); 38 | RendererResize(windowWidth, windowHeight); 39 | } 40 | return 0; 41 | case WM_SYSCOMMAND: 42 | if ((wParam & 0xfff0) == SC_KEYMENU) // Disable ALT application menu 43 | return 0; 44 | break; 45 | case WM_CLOSE: 46 | g_ShouldClose = true; 47 | return 0; 48 | } 49 | 50 | return DefWindowProcW(hWnd, msg, wParam, lParam); 51 | } 52 | 53 | void WindowInit(int width, int height, const char* title) 54 | { 55 | WNDCLASSEXW wc = {}; 56 | wc.cbSize = sizeof(wc); 57 | wc.lpfnWndProc = WndProc; 58 | wc.hInstance = GetModuleHandleW(NULL); 59 | wc.hCursor = LoadCursorW(NULL, IDC_ARROW); 60 | wc.lpszClassName = L"WindowClass"; 61 | CHECKWIN32(RegisterClassExW(&wc)); 62 | 63 | DWORD dwStyle = WS_OVERLAPPEDWINDOW; 64 | DWORD dwExStyle = 0; 65 | RECT wr = { 0, 0, width, height }; 66 | CHECKWIN32(AdjustWindowRectEx(&wr, dwStyle, FALSE, dwExStyle)); 67 | 68 | std::wstring wtitle = WideFromMultiByte(title); 69 | 70 | g_hWnd = CreateWindowExW( 71 | dwExStyle, L"WindowClass", wtitle.c_str(), dwStyle, 72 | CW_USEDEFAULT, CW_USEDEFAULT, 73 | wr.right - wr.left, wr.bottom - wr.top, 74 | NULL, NULL, GetModuleHandleW(NULL), NULL); 75 | CHECKWIN32(g_hWnd != NULL); 76 | 77 | ShowWindow(g_hWnd, SW_SHOWDEFAULT); 78 | } 79 | 80 | void RendererInit() 81 | { 82 | ComPtr pDXGIFactory; 83 | CHECKHR(CreateDXGIFactory(IID_PPV_ARGS(&pDXGIFactory))); 84 | 85 | ComPtr pDXGIAdapter; 86 | CHECKHR(pDXGIFactory->EnumAdapters(0, &pDXGIAdapter)); 87 | 88 | DXGI_SWAP_CHAIN_DESC scd = {}; 89 | scd.BufferDesc.Format = kSwapChainFormat; 90 | scd.SampleDesc.Count = 1; 91 | scd.BufferCount = kSwapChainBufferCount; 92 | scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; 93 | scd.OutputWindow = g_hWnd; 94 | scd.Windowed = TRUE; 95 | scd.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; 96 | scd.Flags = DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT; 97 | 98 | UINT flags = 0; 99 | #if _DEBUG 100 | flags |= D3D11_CREATE_DEVICE_DEBUG; 101 | #endif 102 | 103 | ComPtr pDevice; 104 | ComPtr pDeviceContext; 105 | ComPtr pSwapChain; 106 | HANDLE hFrameLatencyWaitableObject; 107 | 108 | D3D_FEATURE_LEVEL kFeatureLevels[] = { 109 | D3D_FEATURE_LEVEL_12_1, 110 | D3D_FEATURE_LEVEL_12_0, 111 | D3D_FEATURE_LEVEL_11_1, 112 | D3D_FEATURE_LEVEL_11_0, 113 | D3D_FEATURE_LEVEL_10_1, 114 | D3D_FEATURE_LEVEL_10_0, 115 | D3D_FEATURE_LEVEL_9_3, 116 | D3D_FEATURE_LEVEL_9_2, 117 | D3D_FEATURE_LEVEL_9_1 118 | }; 119 | 120 | D3D_FEATURE_LEVEL featureLevel; 121 | ComPtr pTmpSwapChain; 122 | CHECKHR(D3D11CreateDeviceAndSwapChain( 123 | pDXGIAdapter.Get(), D3D_DRIVER_TYPE_UNKNOWN, NULL, flags, 124 | kFeatureLevels, _countof(kFeatureLevels), 125 | D3D11_SDK_VERSION, 126 | &scd, &pTmpSwapChain, 127 | &pDevice, &featureLevel, &pDeviceContext)); 128 | CHECKHR(pTmpSwapChain.As(&pSwapChain)); 129 | 130 | if (featureLevel < kMinFeatureLevel) 131 | { 132 | SimpleMessageBox_FatalError( 133 | "Minimum D3D feature level not satisfied:\n" 134 | "Minimum feature level: %d.%d\n" 135 | "Actual feature level: %d.%d\n", 136 | (kMinFeatureLevel >> 12) & 0x0F, (kMinFeatureLevel >> 8) & 0x0F, 137 | (featureLevel >> 12) & 0x0F, (featureLevel >> 8) & 0x0F); 138 | } 139 | 140 | hFrameLatencyWaitableObject = pSwapChain->GetFrameLatencyWaitableObject(); 141 | CHECKHR(pDXGIFactory->MakeWindowAssociation(g_hWnd, DXGI_MWA_NO_WINDOW_CHANGES)); 142 | 143 | g_Device = pDevice.Get(); 144 | g_Device->AddRef(); 145 | g_DeviceContext = pDeviceContext.Get(); 146 | g_DeviceContext->AddRef(); 147 | g_SwapChain = pSwapChain.Get(); 148 | g_SwapChain->AddRef(); 149 | g_FrameLatencyWaitableObject = hFrameLatencyWaitableObject; 150 | } 151 | 152 | void RendererResize(int width, int height) 153 | { 154 | CHECKHR(g_SwapChain->ResizeBuffers( 155 | kSwapChainBufferCount, 156 | width, height, 157 | kSwapChainFormat, kSwapChainFlags)); 158 | 159 | g_SwapChainRTVDesc.Format = kSwapChainFormat; 160 | g_SwapChainRTVDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; 161 | 162 | SceneResize(width, height); 163 | } 164 | 165 | void RendererPaint() 166 | { 167 | ID3D11Device* dev = g_Device; 168 | ID3D11DeviceContext* dc = g_DeviceContext; 169 | IDXGISwapChain* sc = g_SwapChain; 170 | 171 | // Wait until the previous frame is presented before drawing the next frame 172 | CHECKWIN32(WaitForSingleObject(g_FrameLatencyWaitableObject, INFINITE) == WAIT_OBJECT_0); 173 | 174 | // grab the current backbuffer 175 | ComPtr pBackBufferTex2D; 176 | ComPtr pBackBufferRTV; 177 | CHECKHR(sc->GetBuffer(0, IID_PPV_ARGS(&pBackBufferTex2D))); 178 | CHECKHR(dev->CreateRenderTargetView(pBackBufferTex2D.Get(), &g_SwapChainRTVDesc, &pBackBufferRTV)); 179 | 180 | ScenePaint(pBackBufferRTV.Get()); 181 | 182 | // Render ImGui 183 | ID3D11RenderTargetView* imguiRTVs[] = { pBackBufferRTV.Get() }; 184 | dc->OMSetRenderTargets(_countof(imguiRTVs), imguiRTVs, NULL); 185 | ImGui::Render(); 186 | dc->OMSetRenderTargets(0, NULL, NULL); 187 | 188 | // finally present 189 | CHECKHR(sc->Present(0, 0)); 190 | } 191 | 192 | int main() 193 | { 194 | WindowInit(1280, 720, "trianglebin"); 195 | RendererInit(); 196 | ImGui_ImplDX11_Init(g_hWnd, g_Device, g_DeviceContext); 197 | SceneInit(g_Device, g_DeviceContext); 198 | 199 | RECT cr; 200 | CHECKWIN32(GetClientRect(g_hWnd, &cr)); 201 | RendererResize(cr.right - cr.left, cr.bottom - cr.top); 202 | 203 | for (;;) 204 | { 205 | MSG msg; 206 | while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) 207 | { 208 | TranslateMessage(&msg); 209 | DispatchMessageW(&msg); 210 | } 211 | 212 | if (g_ShouldClose) 213 | { 214 | break; 215 | } 216 | 217 | ImGui_ImplDX11_NewFrame(); 218 | 219 | RendererPaint(); 220 | } 221 | } -------------------------------------------------------------------------------- /scene.cpp: -------------------------------------------------------------------------------- 1 | #include "scene.h" 2 | #include "imgui/imgui.h" 3 | 4 | #include 5 | #include "dxutil.h" 6 | 7 | #include 8 | 9 | static ID3D11Device* g_Device; 10 | static ID3D11DeviceContext* g_DeviceContext; 11 | 12 | static ID3D11Texture2D* g_TrianglesTex2DMS; 13 | static ID3D11Texture2D* g_TrianglesTex2D; 14 | static ID3D11RenderTargetView* g_TrianglesRTV; 15 | static ID3D11ShaderResourceView* g_TrianglesSRV; 16 | static ID3D11SamplerState* g_TrianglesSMP; 17 | 18 | static ID3D11RasterizerState* g_TrianglesRasterizerState; 19 | static ID3D11DepthStencilState* g_TrianglesDepthStencilState; 20 | static ID3D11BlendState* g_TrianglesBlendState; 21 | static ID3D11VertexShader* g_TrianglesVS; 22 | static ID3D11PixelShader* g_TrianglesPS; 23 | 24 | static ID3D11RasterizerState* g_BlitRasterizerState; 25 | static ID3D11DepthStencilState* g_BlitDepthStencilState; 26 | static ID3D11BlendState* g_BlitBlendState; 27 | static ID3D11VertexShader* g_BlitVS; 28 | static ID3D11PixelShader* g_BlitPS; 29 | 30 | static ID3D11Buffer* g_PixelCountBuffer; 31 | static ID3D11UnorderedAccessView* g_PixelCountUAV; 32 | 33 | static ID3D11Buffer* g_MaxNumPixelsBuffer; 34 | 35 | static D3D11_VIEWPORT g_Viewport; 36 | 37 | // The vertex shader always outputs at least 8 floats: 38 | // float4 position, float4 color 39 | static const int kNumNonExtraFloats = 8; 40 | 41 | static int g_NumTris; 42 | static float g_MaxNumPixelsPercent; 43 | static int g_NumFloatsPerVertex = kNumNonExtraFloats; 44 | static int g_PixelFormatIndex; 45 | static int g_SampleCountIndex; 46 | 47 | static const char* kPixelFormatNames[] = { 48 | "(32 bpp) R8G8B8A8_UNORM", 49 | "(64 bpp) R16G16B16A16_UNORM", 50 | "(128 bpp) R32G32B32A32_FLOAT" 51 | }; 52 | 53 | static const DXGI_FORMAT kPixelFormatFormats[] = { 54 | DXGI_FORMAT_R8G8B8A8_UNORM, 55 | DXGI_FORMAT_R16G16B16A16_UNORM, 56 | DXGI_FORMAT_R32G32B32A32_FLOAT 57 | }; 58 | 59 | static const char* kSampleCountNames[] = { 60 | "1","2","4","8" 61 | }; 62 | 63 | static const UINT kSampleCountCounts[] = { 64 | 1, 2, 4, 8 65 | }; 66 | 67 | void RebuildShaders() 68 | { 69 | ID3D11Device* dev = g_Device; 70 | ID3D11DeviceContext* dc = g_DeviceContext; 71 | 72 | ComPtr TrianglesVSBlob; 73 | ComPtr TrianglesPSBlob; 74 | ComPtr BlitVSBlob; 75 | ComPtr BlitPSBlob; 76 | 77 | struct ShaderToCompile 78 | { 79 | std::wstring name; 80 | std::string entry; 81 | std::string target; 82 | ID3DBlob** blob; 83 | std::vector defines; 84 | }; 85 | 86 | std::string numExtraFloatsStr = std::to_string(g_NumFloatsPerVertex - kNumNonExtraFloats); 87 | D3D_SHADER_MACRO numExtraFloatsMacro{ "NUM_EXTRA_FLOATs", numExtraFloatsStr.c_str() }; 88 | 89 | ShaderToCompile shadersToCompile[] = { 90 | { L"triangles.hlsl", "VSmain", "vs_5_0", &TrianglesVSBlob, { numExtraFloatsMacro } }, 91 | { L"triangles.hlsl", "PSmain", "ps_5_0", &TrianglesPSBlob, { numExtraFloatsMacro } }, 92 | { L"blit.hlsl", "VSmain", "vs_5_0", &BlitVSBlob,{ } }, 93 | { L"blit.hlsl", "PSmain", "ps_5_0", &BlitPSBlob,{ } }, 94 | }; 95 | 96 | for (ShaderToCompile& s2c : shadersToCompile) 97 | { 98 | UINT flags = 0; 99 | #if _DEBUG 100 | flags |= D3DCOMPILE_DEBUG; 101 | #else 102 | flags |= D3DCOMPILE_OPTIMIZATION_LEVEL3; 103 | #endif 104 | 105 | ComPtr ErrBlob; 106 | s2c.defines.push_back(D3D_SHADER_MACRO{}); 107 | HRESULT hr = D3DCompileFromFile(s2c.name.c_str(), s2c.defines.data(), D3D_COMPILE_STANDARD_FILE_INCLUDE, s2c.entry.c_str(), s2c.target.c_str(), flags, 0, s2c.blob, &ErrBlob); 108 | std::string mbname = MultiByteFromWide(s2c.name); 109 | if (FAILED(hr)) 110 | { 111 | std::string hrs = MultiByteFromHR(hr); 112 | fprintf(stderr, 113 | "Error (%s):\n%s%s%s\n", 114 | mbname.c_str(), 115 | hrs.c_str(), 116 | ErrBlob ? "\n" : "", 117 | ErrBlob ? (const char*)ErrBlob->GetBufferPointer() : ""); 118 | } 119 | 120 | if (ErrBlob) 121 | { 122 | printf("Warning (%s): %s\n", mbname.c_str(), (const char*)ErrBlob->GetBufferPointer()); 123 | } 124 | } 125 | 126 | if (g_TrianglesVS) g_TrianglesVS->Release(); 127 | CHECKHR(dev->CreateVertexShader(TrianglesVSBlob->GetBufferPointer(), TrianglesVSBlob->GetBufferSize(), NULL, &g_TrianglesVS)); 128 | 129 | if (g_TrianglesPS) g_TrianglesPS->Release(); 130 | CHECKHR(dev->CreatePixelShader(TrianglesPSBlob->GetBufferPointer(), TrianglesPSBlob->GetBufferSize(), NULL, &g_TrianglesPS)); 131 | 132 | if (g_BlitVS) g_BlitVS->Release(); 133 | CHECKHR(dev->CreateVertexShader(BlitVSBlob->GetBufferPointer(), BlitVSBlob->GetBufferSize(), NULL, &g_BlitVS)); 134 | 135 | if (g_BlitPS) g_BlitPS->Release(); 136 | CHECKHR(dev->CreatePixelShader(BlitPSBlob->GetBufferPointer(), BlitPSBlob->GetBufferSize(), NULL, &g_BlitPS)); 137 | } 138 | 139 | void SceneInit(ID3D11Device* dev, ID3D11DeviceContext* dc) 140 | { 141 | g_Device = dev; 142 | g_DeviceContext = dc; 143 | 144 | RebuildShaders(); 145 | 146 | // triangles pipeline 147 | { 148 | D3D11_RASTERIZER_DESC trianglesRasterizerDesc = CD3D11_RASTERIZER_DESC(D3D11_DEFAULT); 149 | CHECKHR(dev->CreateRasterizerState(&trianglesRasterizerDesc, &g_TrianglesRasterizerState)); 150 | 151 | D3D11_DEPTH_STENCIL_DESC trianglesDepthStencilDesc = CD3D11_DEPTH_STENCIL_DESC(D3D11_DEFAULT); 152 | trianglesDepthStencilDesc.DepthEnable = FALSE; 153 | CHECKHR(dev->CreateDepthStencilState(&trianglesDepthStencilDesc, &g_TrianglesDepthStencilState)); 154 | 155 | D3D11_BLEND_DESC trianglesBlendDesc = CD3D11_BLEND_DESC(D3D11_DEFAULT); 156 | CHECKHR(dev->CreateBlendState(&trianglesBlendDesc, &g_TrianglesBlendState)); 157 | } 158 | 159 | // blit pipeline 160 | { 161 | D3D11_RASTERIZER_DESC blitRasterizerDesc = CD3D11_RASTERIZER_DESC(D3D11_DEFAULT); 162 | CHECKHR(dev->CreateRasterizerState(&blitRasterizerDesc, &g_BlitRasterizerState)); 163 | 164 | D3D11_DEPTH_STENCIL_DESC blitDepthStencilDesc = CD3D11_DEPTH_STENCIL_DESC(D3D11_DEFAULT); 165 | blitDepthStencilDesc.DepthEnable = FALSE; 166 | CHECKHR(dev->CreateDepthStencilState(&blitDepthStencilDesc, &g_BlitDepthStencilState)); 167 | 168 | D3D11_BLEND_DESC blitBlendDesc = CD3D11_BLEND_DESC(D3D11_DEFAULT); 169 | CHECKHR(dev->CreateBlendState(&blitBlendDesc, &g_BlitBlendState)); 170 | } 171 | 172 | CHECKHR(dev->CreateBuffer( 173 | &CD3D11_BUFFER_DESC(sizeof(UINT32), D3D11_BIND_UNORDERED_ACCESS, D3D11_USAGE_DEFAULT, 0, D3D11_RESOURCE_MISC_BUFFER_STRUCTURED, sizeof(UINT32)), 174 | NULL, 175 | &g_PixelCountBuffer)); 176 | 177 | CHECKHR(dev->CreateUnorderedAccessView( 178 | g_PixelCountBuffer, 179 | &CD3D11_UNORDERED_ACCESS_VIEW_DESC(g_PixelCountBuffer, DXGI_FORMAT_UNKNOWN, 0, 1, D3D11_BUFFER_UAV_FLAG_COUNTER), 180 | &g_PixelCountUAV)); 181 | 182 | CHECKHR(dev->CreateBuffer( 183 | &CD3D11_BUFFER_DESC(16, D3D11_BIND_CONSTANT_BUFFER, D3D11_USAGE_DYNAMIC, D3D11_CPU_ACCESS_WRITE, 0, sizeof(UINT32)), 184 | NULL, 185 | &g_MaxNumPixelsBuffer)); 186 | 187 | CHECKHR(dev->CreateSamplerState( 188 | &CD3D11_SAMPLER_DESC(D3D11_DEFAULT), 189 | &g_TrianglesSMP)); 190 | } 191 | 192 | void SceneResize(int width, int height) 193 | { 194 | ID3D11Device* dev = g_Device; 195 | ID3D11DeviceContext* dc = g_DeviceContext; 196 | 197 | g_Viewport.Width = (float)width; 198 | g_Viewport.Height = (float)height; 199 | g_Viewport.TopLeftX = 0.0f; 200 | g_Viewport.TopLeftY = 0.0f; 201 | g_Viewport.MinDepth = 0.0f; 202 | g_Viewport.MaxDepth = 1.0f; 203 | 204 | DXGI_FORMAT trianglesFormat = kPixelFormatFormats[g_PixelFormatIndex]; 205 | UINT sampleCount = kSampleCountCounts[g_SampleCountIndex]; 206 | 207 | if (g_TrianglesTex2DMS) g_TrianglesTex2DMS->Release(); 208 | CHECKHR(dev->CreateTexture2D( 209 | &CD3D11_TEXTURE2D_DESC(trianglesFormat, width, height, 1, 1, D3D11_BIND_RENDER_TARGET, D3D11_USAGE_DEFAULT, 0, sampleCount, 0, 0), 210 | NULL, 211 | &g_TrianglesTex2DMS)); 212 | 213 | if (g_TrianglesRTV) g_TrianglesRTV->Release(); 214 | CHECKHR(dev->CreateRenderTargetView( 215 | g_TrianglesTex2DMS, 216 | &CD3D11_RENDER_TARGET_VIEW_DESC(D3D11_RTV_DIMENSION_TEXTURE2DMS, trianglesFormat, 0, 1), 217 | &g_TrianglesRTV)); 218 | 219 | if (g_TrianglesTex2D) g_TrianglesTex2D->Release(); 220 | CHECKHR(dev->CreateTexture2D( 221 | &CD3D11_TEXTURE2D_DESC(trianglesFormat, width, height, 1, 1, D3D11_BIND_SHADER_RESOURCE, D3D11_USAGE_DEFAULT, 0, 1, 0, 0), 222 | NULL, 223 | &g_TrianglesTex2D)); 224 | 225 | if (g_TrianglesSRV) g_TrianglesSRV->Release(); 226 | CHECKHR(dev->CreateShaderResourceView( 227 | g_TrianglesTex2D, 228 | &CD3D11_SHADER_RESOURCE_VIEW_DESC(D3D11_SRV_DIMENSION_TEXTURE2D, trianglesFormat, 0, 1), 229 | &g_TrianglesSRV)); 230 | } 231 | 232 | void ScenePaint(ID3D11RenderTargetView* backbufferRTV) 233 | { 234 | ID3D11Device* dev = g_Device; 235 | ID3D11DeviceContext* dc = g_DeviceContext; 236 | 237 | ImGui::SetNextWindowSize(ImVec2(550, 250), ImGuiSetCond_Once); 238 | if (ImGui::Begin("Toolbox")) 239 | { 240 | ImGui::SliderInt("Num triangles", &g_NumTris, 0, 1000); 241 | if (g_NumTris < 0) g_NumTris = 0; 242 | 243 | ImGui::SliderFloat("Num pixels (percent)", &g_MaxNumPixelsPercent, 0.0f, 1.0f); 244 | if (g_MaxNumPixelsPercent < 0.0f) g_MaxNumPixelsPercent = 0.0f; 245 | 246 | if (ImGui::SliderInt("Num floats per vertex ", &g_NumFloatsPerVertex, 8, 32)) 247 | { 248 | if (g_NumFloatsPerVertex < 8) 249 | g_NumFloatsPerVertex = 8; 250 | if (g_NumFloatsPerVertex > 32) 251 | g_NumFloatsPerVertex = 32; 252 | 253 | RebuildShaders(); 254 | } 255 | 256 | if (ImGui::ListBox("Pixel format", &g_PixelFormatIndex, kPixelFormatNames, _countof(kPixelFormatNames))) 257 | { 258 | SceneResize((int)g_Viewport.Width, (int)g_Viewport.Height); 259 | } 260 | 261 | if (ImGui::ListBox("Sample count", &g_SampleCountIndex, kSampleCountNames, _countof(kSampleCountNames))) 262 | { 263 | SceneResize((int)g_Viewport.Width, (int)g_Viewport.Height); 264 | } 265 | } 266 | ImGui::End(); 267 | 268 | { 269 | D3D11_MAPPED_SUBRESOURCE mapped; 270 | CHECKHR(dc->Map(g_MaxNumPixelsBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped)); 271 | 272 | // not exact, but good enough 273 | float pixelsPerTri = 0.5f * g_Viewport.Width * g_Viewport.Height; 274 | 275 | // some fudge factor added to the percent to make 100% always draw all triangles fully and 0% draw nothing 276 | float pixelsPercent = g_MaxNumPixelsPercent; 277 | if (pixelsPercent == 1.0f) 278 | pixelsPercent = 1.01f; 279 | 280 | *(UINT32*)mapped.pData = (UINT32)(pixelsPercent * pixelsPerTri * g_NumTris); 281 | dc->Unmap(g_MaxNumPixelsBuffer, 0); 282 | } 283 | 284 | const float kClearColor[] = { 0, 0, 0, 0 }; 285 | dc->ClearRenderTargetView(g_TrianglesRTV, kClearColor); 286 | 287 | // draw triangles 288 | { 289 | ID3D11RenderTargetView* rtvs[] = { g_TrianglesRTV }; 290 | ID3D11UnorderedAccessView* uavs[] = { g_PixelCountUAV }; 291 | UINT uavCounters[_countof(uavs)] = { 0 }; 292 | dc->OMSetRenderTargetsAndUnorderedAccessViews(_countof(rtvs), rtvs, NULL, _countof(rtvs), _countof(uavs), uavs, uavCounters); 293 | dc->VSSetShader(g_TrianglesVS, NULL, 0); 294 | dc->PSSetShader(g_TrianglesPS, NULL, 0); 295 | dc->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); 296 | dc->IASetInputLayout(NULL); 297 | dc->RSSetState(g_TrianglesRasterizerState); 298 | dc->OMSetDepthStencilState(g_TrianglesDepthStencilState, 0); 299 | dc->OMSetBlendState(g_TrianglesBlendState, NULL, UINT_MAX); 300 | dc->RSSetViewports(1, &g_Viewport); 301 | dc->IASetVertexBuffers(0, 0, NULL, NULL, NULL); 302 | dc->IASetIndexBuffer(NULL, DXGI_FORMAT_UNKNOWN, 0); 303 | dc->PSSetConstantBuffers(0, 1, &g_MaxNumPixelsBuffer); 304 | dc->Draw(g_NumTris * 3, 0); 305 | 306 | dc->OMSetRenderTargets(0, NULL, NULL); 307 | dc->VSSetShader(NULL, NULL, 0); 308 | dc->PSSetShader(NULL, NULL, 0); 309 | } 310 | 311 | dc->ResolveSubresource(g_TrianglesTex2D, 0, g_TrianglesTex2DMS, 0, kPixelFormatFormats[g_PixelFormatIndex]); 312 | 313 | // blit 314 | { 315 | ID3D11RenderTargetView* rtvs[] = { backbufferRTV }; 316 | dc->OMSetRenderTargets(_countof(rtvs), rtvs, NULL); 317 | dc->VSSetShader(g_BlitVS, NULL, 0); 318 | dc->PSSetShader(g_BlitPS, NULL, 0); 319 | dc->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); 320 | dc->IASetInputLayout(NULL); 321 | dc->RSSetState(g_BlitRasterizerState); 322 | dc->OMSetDepthStencilState(g_BlitDepthStencilState, 0); 323 | dc->OMSetBlendState(g_BlitBlendState, NULL, UINT_MAX); 324 | dc->RSSetViewports(1, &g_Viewport); 325 | dc->IASetVertexBuffers(0, 0, NULL, NULL, NULL); 326 | dc->IASetIndexBuffer(NULL, DXGI_FORMAT_UNKNOWN, 0); 327 | dc->PSSetShaderResources(0, 1, &g_TrianglesSRV); 328 | dc->PSSetSamplers(0, 1, &g_TrianglesSMP); 329 | dc->Draw(3, 0); 330 | 331 | ID3D11ShaderResourceView* resetSRV = NULL; 332 | dc->PSSetShaderResources(0, 1, &resetSRV); 333 | dc->OMSetRenderTargets(0, NULL, NULL); 334 | dc->VSSetShader(NULL, NULL, 0); 335 | dc->PSSetShader(NULL, NULL, 0); 336 | } 337 | } -------------------------------------------------------------------------------- /scene.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | void SceneInit(ID3D11Device* dev, ID3D11DeviceContext* dc); 6 | void SceneResize(int width, int height); 7 | void ScenePaint(ID3D11RenderTargetView* backbufferRTV); -------------------------------------------------------------------------------- /trianglebin.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25123.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "trianglebin", "trianglebin.vcxproj", "{C5D9805D-3483-494C-8F17-0583586E04EC}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Release|x64 = Release|x64 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {C5D9805D-3483-494C-8F17-0583586E04EC}.Debug|x64.ActiveCfg = Debug|x64 15 | {C5D9805D-3483-494C-8F17-0583586E04EC}.Debug|x64.Build.0 = Debug|x64 16 | {C5D9805D-3483-494C-8F17-0583586E04EC}.Release|x64.ActiveCfg = Release|x64 17 | {C5D9805D-3483-494C-8F17-0583586E04EC}.Release|x64.Build.0 = Release|x64 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /trianglebin.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | x64 7 | 8 | 9 | Release 10 | x64 11 | 12 | 13 | 14 | {C5D9805D-3483-494C-8F17-0583586E04EC} 15 | trianglebin 16 | 10.0.10586.0 17 | 18 | 19 | 20 | Application 21 | true 22 | v140 23 | Unicode 24 | 25 | 26 | Application 27 | false 28 | v140 29 | true 30 | Unicode 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | Level3 48 | Disabled 49 | true 50 | 51 | 52 | 53 | 54 | Level3 55 | MaxSpeed 56 | true 57 | true 58 | true 59 | 60 | 61 | true 62 | true 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | Document 88 | 89 | 90 | 91 | 92 | Document 93 | 94 | 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /trianglebin.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | imgui 8 | 9 | 10 | imgui 11 | 12 | 13 | imgui 14 | 15 | 16 | imgui 17 | 18 | 19 | 20 | 21 | 22 | 23 | imgui 24 | 25 | 26 | imgui 27 | 28 | 29 | imgui 30 | 31 | 32 | imgui 33 | 34 | 35 | imgui 36 | 37 | 38 | imgui 39 | 40 | 41 | imgui 42 | 43 | 44 | 45 | 46 | 47 | {17981b9f-78c2-4ea7-91c4-3cda0f029253} 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /triangles.hlsl: -------------------------------------------------------------------------------- 1 | struct VS_INPUT 2 | { 3 | uint VertexID : SV_VertexID; 4 | }; 5 | 6 | struct VS_OUTPUT 7 | { 8 | float4 Position : SV_Position; 9 | float4 Color : COLOR; 10 | #if NUM_EXTRA_FLOATs > 0 11 | float ExtraFloats[NUM_EXTRA_FLOATs] : EXTRAFLOATS; 12 | #endif 13 | }; 14 | 15 | struct PS_OUTPUT 16 | { 17 | float4 Color : SV_Target; 18 | }; 19 | 20 | RWStructuredBuffer PixelCounterUAV : register(u1); 21 | cbuffer MaxNumPixelsCBV : register(b0) { uint MaxNumPixels; }; 22 | 23 | VS_OUTPUT VSmain(VS_INPUT input) 24 | { 25 | VS_OUTPUT output; 26 | 27 | if (input.VertexID % 3 == 0) 28 | output.Position = float4(-1, 1, 0, 1); 29 | else if (input.VertexID % 3 == 1) 30 | output.Position = float4(1, 1, 0, 1); 31 | else if (input.VertexID % 3 == 2) 32 | output.Position = float4(-1, -1, 0, 1); 33 | 34 | const float4 colors[7] = { 35 | float4(1,0,0,1), 36 | float4(0,1,0,1), 37 | float4(0,0,1,1), 38 | float4(1,1,0,1), 39 | float4(0,1,1,1), 40 | float4(1,0,1,1), 41 | float4(1,1,1,1) 42 | }; 43 | 44 | output.Color = colors[(input.VertexID / 3) % 7] * float4(0.4,0.4,0.4,1); 45 | 46 | #if NUM_EXTRA_FLOATs > 0 47 | [unroll] 48 | for (int i = 0; i < NUM_EXTRA_FLOATs; i++) 49 | { 50 | output.ExtraFloats[i] = input.VertexID + i; 51 | } 52 | #endif 53 | 54 | return output; 55 | } 56 | 57 | PS_OUTPUT PSmain(VS_OUTPUT input) 58 | { 59 | if (PixelCounterUAV.IncrementCounter() > MaxNumPixels) 60 | { 61 | discard; 62 | } 63 | 64 | PS_OUTPUT output; 65 | output.Color = input.Color; 66 | 67 | // just to force it not to optimize this out 68 | #if NUM_EXTRA_FLOATs > 0 69 | [unroll] 70 | for (int i = 0; i < NUM_EXTRA_FLOATs; i++) 71 | { 72 | output.Color.r += input.ExtraFloats[i] * 0.00001; 73 | } 74 | #endif 75 | 76 | return output; 77 | } --------------------------------------------------------------------------------