Rigs of Rods 2026.01
Soft-body Physics Simulation
Loading...
Searching...
No Matches
OgreImGuiOverlay.cpp
Go to the documentation of this file.
1// This file is part of the OGRE project.
2// It is subject to the license terms in the LICENSE file found in the top-level directory
3// of this distribution and at https://www.ogre3d.org/licensing.
4
5#include <imgui.h>
6
7#include <OgreImGuiOverlay.h>
8#include <OgreHardwareBufferManager.h>
9#include <OgreHardwarePixelBuffer.h>
10#include <OgreRenderSystem.h>
11#include <OgreTextureManager.h>
12#include <OgreMaterialManager.h>
13#include <OgreOverlayManager.h>
14#include <OgreFontManager.h>
15#include <OgreTechnique.h>
16#include <OgreTextureUnitState.h>
17#include <OgreFont.h>
18#include <OgreRenderQueue.h>
19#include <OgreFrameListener.h>
20#include <OgreRoot.h>
21
22namespace Ogre
23{
24
25ImGuiOverlay::ImGuiOverlay() : Overlay("ImGuiOverlay")
26{
27 ImGui::CreateContext();
28 ImGuiIO& io = ImGui::GetIO();
29
30 io.BackendPlatformName = "OGRE";
31}
33{
34 ImGui::DestroyContext();
35}
36
38{
39 if (!mInitialised)
40 {
42 mCodePointRanges.clear();
43 }
44 mInitialised = true;
45}
46
47//-----------------------------------------------------------------------------------
48void ImGuiOverlay::_findVisibleObjects(Camera* cam, RenderQueue* queue, Viewport* vp)
49{
50 if (!mVisible)
51 return;
52
54 queue->addRenderable(&mRenderable, RENDER_QUEUE_OVERLAY, mZOrder * 100);
55}
56//-----------------------------------------------------------------------------------
58{
59 mMaterial = MaterialManager::getSingleton().create("ImGui/material", RGN_INTERNAL);
60 Pass* mPass = mMaterial->getTechnique(0)->getPass(0);
61 mPass->setCullingMode(CULL_NONE);
62 mPass->setVertexColourTracking(TVC_DIFFUSE);
63 mPass->setSceneBlending(SBT_TRANSPARENT_ALPHA);
64 mPass->setSeparateSceneBlendingOperation(SBO_ADD, SBO_ADD);
65 mPass->setSeparateSceneBlending(SBF_SOURCE_ALPHA, SBF_ONE_MINUS_SOURCE_ALPHA,
66 SBF_ONE_MINUS_SOURCE_ALPHA, SBF_ZERO);
67
68 TextureUnitState* mTexUnit = mPass->createTextureUnitState();
69 mTexUnit->setTexture(mFontTex);
70 mTexUnit->setTextureFiltering(TFO_NONE);
71
72 mMaterial->load();
73 mMaterial->setLightingEnabled(false);
74 mMaterial->setDepthCheckEnabled(false);
75}
76
77ImFont* ImGuiOverlay::addFont(const String& name, const String& group, float size_scale)
78{
79 FontPtr font = FontManager::getSingleton().getByName(name, group);
80 OgreAssert(font, "font does not exist");
81 OgreAssert(font->getType() == FT_TRUETYPE, "font must be of FT_TRUETYPE");
82 DataStreamPtr dataStreamPtr =
83 ResourceGroupManager::getSingleton().openResource(font->getSource(), font->getGroup());
84 MemoryDataStream ttfchunk(dataStreamPtr, false); // transfer ownership to imgui
85
86 // convert codepoint ranges for imgui
87 CodePointRange cprange;
88 for (const auto& r : font->getCodePointRangeList())
89 {
90 cprange.push_back(r.first);
91 cprange.push_back(r.second);
92 }
93
94 ImGuiIO& io = ImGui::GetIO();
95 const ImWchar* cprangePtr = io.Fonts->GetGlyphRangesAll();
96 if (!cprange.empty())
97 {
98 cprange.push_back(0); // terminate
99 mCodePointRanges.push_back(cprange);
100 // ptr must persist until createFontTexture
101 cprangePtr = mCodePointRanges.back().data();
102 }
103
104 ImFontConfig cfg;
105 strncpy(cfg.Name, name.c_str(), 40);
106 return io.Fonts->AddFontFromMemoryTTF(ttfchunk.getPtr(), (int)ttfchunk.size(), font->getTrueTypeSize() * size_scale, &cfg,
107 cprangePtr);
108}
109
111{
112 if (!mInitialised)
113 return; // Will be built by `initialise()`
114
115 TextureManager::getSingleton().remove(mRenderable.mFontTex);
116 mRenderable.mFontTex.reset();
118 mRenderable.mMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTexture(mRenderable.mFontTex);
119 mCodePointRanges.clear();
120}
121
123{
124 // Build texture atlas
125 ImGuiIO& io = ImGui::GetIO();
126 if (io.Fonts->Fonts.empty())
127 io.Fonts->AddFontDefault();
128
129 unsigned char* pixels;
130 int width, height;
131 io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
132
133 mFontTex = TextureManager::getSingleton().createManual("ImGui/FontTex", RGN_INTERNAL, TEX_TYPE_2D,
134 width, height, 1, 1, PF_BYTE_RGBA);
135
136 mFontTex->getBuffer()->blitFromMemory(PixelBox(Box(0, 0, width, height), PF_BYTE_RGBA, pixels));
137}
138void ImGuiOverlay::NewFrame(const FrameEvent& evt)
139{
140 ImGuiIO& io = ImGui::GetIO();
141 io.DeltaTime = std::max<float>(
142 evt.timeSinceLastFrame,
143 1e-4f); // see https://github.com/ocornut/imgui/commit/3c07ec6a6126fb6b98523a9685d1f0f78ca3c40c
144
145 // Read keyboard modifiers inputs
146 io.KeyAlt = false;
147 io.KeySuper = false;
148
149 OverlayManager& oMgr = OverlayManager::getSingleton();
150
151 // Setup display size (every frame to accommodate for window resizing)
152 io.DisplaySize = ImVec2(oMgr.getViewportWidth(), oMgr.getViewportHeight());
153
154 // Start the frame
155 ImGui::NewFrame();
156}
157
159{
160 if (mMaterial->getSupportedTechniques().empty())
161 {
162 mMaterial->load(); // Support for adding lights run time
163 }
164
165 RenderSystem* rSys = Root::getSingleton().getRenderSystem();
166 OverlayManager& oMgr = OverlayManager::getSingleton();
167
168 // Construct projection matrix, taking texel offset corrections in account (important for DirectX9)
169 // See also:
170 // - OGRE-API specific hint: http://www.ogre3d.org/forums/viewtopic.php?f=5&p=536881#p536881
171 // - IMGUI Dx9 demo solution:
172 // https://github.com/ocornut/imgui/blob/master/examples/directx9_example/imgui_impl_dx9.cpp#L127-L138
173 float texelOffsetX = rSys->getHorizontalTexelOffset();
174 float texelOffsetY = rSys->getVerticalTexelOffset();
175 float L = texelOffsetX;
176 float R = oMgr.getViewportWidth() + texelOffsetX;
177 float T = texelOffsetY;
178 float B = oMgr.getViewportHeight() + texelOffsetY;
179
180 mXform = Matrix4(2.0f / (R - L), 0.0f, 0.0f, (L + R) / (L - R), 0.0f, -2.0f / (B - T), 0.0f,
181 (T + B) / (B - T), 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f);
182}
183
184bool ImGuiOverlay::ImGUIRenderable::preRender(SceneManager* sm, RenderSystem* rsys)
185{
186 Viewport* vp = rsys->_getViewport();
187
188 // Instruct ImGui to Render() and process the resulting CmdList-s
189 // Adopted from https://bitbucket.org/ChaosCreator/imgui-ogre2.1-binding
190 // ... Commentary on OGRE forums: http://www.ogre3d.org/forums/viewtopic.php?f=5&t=89081#p531059
191 ImGui::Render();
192 ImDrawData* draw_data = ImGui::GetDrawData();
193 int vpWidth = vp->getActualWidth();
194 int vpHeight = vp->getActualHeight();
195
196 TextureUnitState* tu = mMaterial->getBestTechnique()->getPass(0)->getTextureUnitState(0);
197
198 for (int i = 0; i < draw_data->CmdListsCount; ++i)
199 {
200 const ImDrawList* draw_list = draw_data->CmdLists[i];
201 updateVertexData(draw_list->VtxBuffer, draw_list->IdxBuffer);
202
203 unsigned int startIdx = 0;
204
205 for (int j = 0; j < draw_list->CmdBuffer.Size; ++j)
206 {
207 // Create a renderable and fill it's buffers
208 const ImDrawCmd* drawCmd = &draw_list->CmdBuffer[j];
209
210 // Set scissoring
211 Rect scissor(drawCmd->ClipRect.x, drawCmd->ClipRect.y, drawCmd->ClipRect.z,
212 drawCmd->ClipRect.w);
213
214 // Clamp bounds to viewport dimensions
215 scissor = scissor.intersect(Rect(0, 0, vpWidth, vpHeight));
216
217 if (drawCmd->TextureId)
218 {
219 auto handle = (ResourceHandle)drawCmd->TextureId;
220 auto tex = static_pointer_cast<Texture>(TextureManager::getSingleton().getByHandle(handle));
221 if (tex)
222 {
223 rsys->_setTexture(0, true, tex);
224 rsys->_setSampler(0, *TextureManager::getSingleton().getDefaultSampler());
225 }
226 }
227
228 rsys->setScissorTest(true, scissor.left, scissor.top, scissor.right, scissor.bottom);
229
230 // Render!
231 mRenderOp.indexData->indexStart = startIdx;
232 mRenderOp.indexData->indexCount = drawCmd->ElemCount;
233
234 rsys->_render(mRenderOp);
235
236 if (drawCmd->TextureId)
237 {
238 // reset to pass state
239 rsys->_setTexture(0, true, mFontTex);
240 rsys->_setSampler(0, *tu->getSampler());
241 }
242
243 // Update counts
244 startIdx += drawCmd->ElemCount;
245 }
246 }
247 rsys->setScissorTest(false);
248 return false;
249}
250
252{
253 // Overlayelements should not be lit by the scene, this will not get called
254 static LightList ll;
255 return ll;
256}
257
259{
260 // default overlays to preserve their own detail level
261 mPolygonModeOverrideable = false;
262
263 // use identity projection and view matrices
264 mUseIdentityProjection = true;
265 mUseIdentityView = true;
266
267 mConvertToBGR = false;
268}
269//-----------------------------------------------------------------------------------
271{
272 createFontTexture();
273 createMaterial();
274
275 mRenderOp.vertexData = OGRE_NEW VertexData();
276 mRenderOp.indexData = OGRE_NEW IndexData();
277
278 mRenderOp.vertexData->vertexCount = 0;
279 mRenderOp.vertexData->vertexStart = 0;
280
281 mRenderOp.indexData->indexCount = 0;
282 mRenderOp.indexData->indexStart = 0;
283 mRenderOp.operationType = RenderOperation::OT_TRIANGLE_LIST;
284 mRenderOp.useIndexes = true;
285 mRenderOp.useGlobalInstancingVertexBufferIsAvailable = false;
286
287 VertexDeclaration* decl = mRenderOp.vertexData->vertexDeclaration;
288
289 // vertex declaration
290 size_t offset = 0;
291 decl->addElement(0, offset, VET_FLOAT2, VES_POSITION);
292 offset += VertexElement::getTypeSize(VET_FLOAT2);
293 decl->addElement(0, offset, VET_FLOAT2, VES_TEXTURE_COORDINATES, 0);
294 offset += VertexElement::getTypeSize(VET_FLOAT2);
295 decl->addElement(0, offset, VET_COLOUR, VES_DIFFUSE);
296
297 if (Root::getSingleton().getRenderSystem()->getName().find("Direct3D9") != String::npos)
298 mConvertToBGR = true;
299}
300//-----------------------------------------------------------------------------------
302{
303 OGRE_DELETE mRenderOp.vertexData;
304 OGRE_DELETE mRenderOp.indexData;
305}
306//-----------------------------------------------------------------------------------
307void ImGuiOverlay::ImGUIRenderable::updateVertexData(const ImVector<ImDrawVert>& vtxBuf,
308 const ImVector<ImDrawIdx>& idxBuf)
309{
310 VertexBufferBinding* bind = mRenderOp.vertexData->vertexBufferBinding;
311
312 if (bind->getBindings().empty() || bind->getBuffer(0)->getNumVertices() != size_t(vtxBuf.size()))
313 {
314 bind->setBinding(0, HardwareBufferManager::getSingleton().createVertexBuffer(
315 sizeof(ImDrawVert), vtxBuf.size(), HardwareBuffer::HBU_WRITE_ONLY));
316 }
317 if (!mRenderOp.indexData->indexBuffer ||
318 mRenderOp.indexData->indexBuffer->getNumIndexes() != size_t(idxBuf.size()))
319 {
320 mRenderOp.indexData->indexBuffer = HardwareBufferManager::getSingleton().createIndexBuffer(
321 HardwareIndexBuffer::IT_32BIT, idxBuf.size(), HardwareBuffer::HBU_WRITE_ONLY);
322 }
323
324 if (mConvertToBGR)
325 {
326 // convert RGBA > BGRA
327 PixelBox src(1, vtxBuf.size(), 1, PF_A8B8G8R8, (char*)vtxBuf.Data + offsetof(ImDrawVert, col));
328 src.rowPitch = sizeof(ImDrawVert) / sizeof(ImU32);
329 PixelBox dst = src;
330 dst.format = PF_A8R8G8B8;
331 PixelUtil::bulkPixelConversion(src, dst);
332 }
333
334 // Copy all vertices
335 bind->getBuffer(0)->writeData(0, vtxBuf.size_in_bytes(), vtxBuf.Data, true);
336 mRenderOp.indexData->indexBuffer->writeData(0, idxBuf.size_in_bytes(), idxBuf.Data, true);
337
338 mRenderOp.vertexData->vertexStart = 0;
339 mRenderOp.vertexData->vertexCount = vtxBuf.size();
340}
341} // namespace Ogre
const LightList & getLights(void) const
void updateVertexData(const ImVector< ImDrawVert > &vtxBuf, const ImVector< ImDrawIdx > &idxBuf)
bool preRender(SceneManager *sm, RenderSystem *rsys)
ImGUIRenderable mRenderable
void rebuildFontTexture()
RIGSOFRODS: re-create the font texture after fonts were changed (i.e. io.Fonts->Clear() + addFont())
void _findVisibleObjects(Camera *cam, RenderQueue *queue, Viewport *vp)
static void NewFrame(const FrameEvent &evt)
std::vector< CodePointRange > mCodePointRanges
ImFont * addFont(const String &name, const String &group OGRE_RESOURCE_GROUP_INIT, float size_scale=1.f)
add font from ogre .fontdef file must be called before first show() or followed by rebuildFontTexture...
std::vector< ImWchar > CodePointRange