Rigs of Rods 2023.09
Soft-body Physics Simulation
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
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)
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(), &cfg,
107 cprangePtr);
108}
109
111{
112 // Build texture atlas
113 ImGuiIO& io = ImGui::GetIO();
114 if (io.Fonts->Fonts.empty())
115 io.Fonts->AddFontDefault();
116
117 unsigned char* pixels;
118 int width, height;
119 io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
120
121 mFontTex = TextureManager::getSingleton().createManual("ImGui/FontTex", RGN_INTERNAL, TEX_TYPE_2D,
122 width, height, 1, 1, PF_BYTE_RGBA);
123
124 mFontTex->getBuffer()->blitFromMemory(PixelBox(Box(0, 0, width, height), PF_BYTE_RGBA, pixels));
125}
126void ImGuiOverlay::NewFrame(const FrameEvent& evt)
127{
128 ImGuiIO& io = ImGui::GetIO();
129 io.DeltaTime = std::max<float>(
130 evt.timeSinceLastFrame,
131 1e-4f); // see https://github.com/ocornut/imgui/commit/3c07ec6a6126fb6b98523a9685d1f0f78ca3c40c
132
133 // Read keyboard modifiers inputs
134 io.KeyAlt = false;
135 io.KeySuper = false;
136
137 OverlayManager& oMgr = OverlayManager::getSingleton();
138
139 // Setup display size (every frame to accommodate for window resizing)
140 io.DisplaySize = ImVec2(oMgr.getViewportWidth(), oMgr.getViewportHeight());
141
142 // Start the frame
143 ImGui::NewFrame();
144}
145
147{
148 if (mMaterial->getSupportedTechniques().empty())
149 {
150 mMaterial->load(); // Support for adding lights run time
151 }
152
153 RenderSystem* rSys = Root::getSingleton().getRenderSystem();
154 OverlayManager& oMgr = OverlayManager::getSingleton();
155
156 // Construct projection matrix, taking texel offset corrections in account (important for DirectX9)
157 // See also:
158 // - OGRE-API specific hint: http://www.ogre3d.org/forums/viewtopic.php?f=5&p=536881#p536881
159 // - IMGUI Dx9 demo solution:
160 // https://github.com/ocornut/imgui/blob/master/examples/directx9_example/imgui_impl_dx9.cpp#L127-L138
161 float texelOffsetX = rSys->getHorizontalTexelOffset();
162 float texelOffsetY = rSys->getVerticalTexelOffset();
163 float L = texelOffsetX;
164 float R = oMgr.getViewportWidth() + texelOffsetX;
165 float T = texelOffsetY;
166 float B = oMgr.getViewportHeight() + texelOffsetY;
167
168 mXform = Matrix4(2.0f / (R - L), 0.0f, 0.0f, (L + R) / (L - R), 0.0f, -2.0f / (B - T), 0.0f,
169 (T + B) / (B - T), 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f);
170}
171
172bool ImGuiOverlay::ImGUIRenderable::preRender(SceneManager* sm, RenderSystem* rsys)
173{
174 Viewport* vp = rsys->_getViewport();
175
176 // Instruct ImGui to Render() and process the resulting CmdList-s
177 // Adopted from https://bitbucket.org/ChaosCreator/imgui-ogre2.1-binding
178 // ... Commentary on OGRE forums: http://www.ogre3d.org/forums/viewtopic.php?f=5&t=89081#p531059
179 ImGui::Render();
180 ImDrawData* draw_data = ImGui::GetDrawData();
181 int vpWidth = vp->getActualWidth();
182 int vpHeight = vp->getActualHeight();
183
184 TextureUnitState* tu = mMaterial->getBestTechnique()->getPass(0)->getTextureUnitState(0);
185
186 for (int i = 0; i < draw_data->CmdListsCount; ++i)
187 {
188 const ImDrawList* draw_list = draw_data->CmdLists[i];
189 updateVertexData(draw_list->VtxBuffer, draw_list->IdxBuffer);
190
191 unsigned int startIdx = 0;
192
193 for (int j = 0; j < draw_list->CmdBuffer.Size; ++j)
194 {
195 // Create a renderable and fill it's buffers
196 const ImDrawCmd* drawCmd = &draw_list->CmdBuffer[j];
197
198 // Set scissoring
199 Rect scissor(drawCmd->ClipRect.x, drawCmd->ClipRect.y, drawCmd->ClipRect.z,
200 drawCmd->ClipRect.w);
201
202 // Clamp bounds to viewport dimensions
203 scissor = scissor.intersect(Rect(0, 0, vpWidth, vpHeight));
204
205 if (drawCmd->TextureId)
206 {
207 auto handle = (ResourceHandle)drawCmd->TextureId;
208 auto tex = static_pointer_cast<Texture>(TextureManager::getSingleton().getByHandle(handle));
209 if (tex)
210 {
211 rsys->_setTexture(0, true, tex);
212 rsys->_setSampler(0, *TextureManager::getSingleton().getDefaultSampler());
213 }
214 }
215
216 rsys->setScissorTest(true, scissor.left, scissor.top, scissor.right, scissor.bottom);
217
218 // Render!
219 mRenderOp.indexData->indexStart = startIdx;
220 mRenderOp.indexData->indexCount = drawCmd->ElemCount;
221
222 rsys->_render(mRenderOp);
223
224 if (drawCmd->TextureId)
225 {
226 // reset to pass state
227 rsys->_setTexture(0, true, mFontTex);
228 rsys->_setSampler(0, *tu->getSampler());
229 }
230
231 // Update counts
232 startIdx += drawCmd->ElemCount;
233 }
234 }
235 rsys->setScissorTest(false);
236 return false;
237}
238
240{
241 // Overlayelements should not be lit by the scene, this will not get called
242 static LightList ll;
243 return ll;
244}
245
247{
248 // default overlays to preserve their own detail level
249 mPolygonModeOverrideable = false;
250
251 // use identity projection and view matrices
252 mUseIdentityProjection = true;
253 mUseIdentityView = true;
254
255 mConvertToBGR = false;
256}
257//-----------------------------------------------------------------------------------
259{
260 createFontTexture();
261 createMaterial();
262
263 mRenderOp.vertexData = OGRE_NEW VertexData();
264 mRenderOp.indexData = OGRE_NEW IndexData();
265
266 mRenderOp.vertexData->vertexCount = 0;
267 mRenderOp.vertexData->vertexStart = 0;
268
269 mRenderOp.indexData->indexCount = 0;
270 mRenderOp.indexData->indexStart = 0;
271 mRenderOp.operationType = RenderOperation::OT_TRIANGLE_LIST;
272 mRenderOp.useIndexes = true;
273 mRenderOp.useGlobalInstancingVertexBufferIsAvailable = false;
274
275 VertexDeclaration* decl = mRenderOp.vertexData->vertexDeclaration;
276
277 // vertex declaration
278 size_t offset = 0;
279 decl->addElement(0, offset, VET_FLOAT2, VES_POSITION);
280 offset += VertexElement::getTypeSize(VET_FLOAT2);
281 decl->addElement(0, offset, VET_FLOAT2, VES_TEXTURE_COORDINATES, 0);
282 offset += VertexElement::getTypeSize(VET_FLOAT2);
283 decl->addElement(0, offset, VET_COLOUR, VES_DIFFUSE);
284
285 if (Root::getSingleton().getRenderSystem()->getName().find("Direct3D9") != String::npos)
286 mConvertToBGR = true;
287}
288//-----------------------------------------------------------------------------------
290{
291 OGRE_DELETE mRenderOp.vertexData;
292 OGRE_DELETE mRenderOp.indexData;
293}
294//-----------------------------------------------------------------------------------
295void ImGuiOverlay::ImGUIRenderable::updateVertexData(const ImVector<ImDrawVert>& vtxBuf,
296 const ImVector<ImDrawIdx>& idxBuf)
297{
298 VertexBufferBinding* bind = mRenderOp.vertexData->vertexBufferBinding;
299
300 if (bind->getBindings().empty() || bind->getBuffer(0)->getNumVertices() != size_t(vtxBuf.size()))
301 {
302 bind->setBinding(0, HardwareBufferManager::getSingleton().createVertexBuffer(
303 sizeof(ImDrawVert), vtxBuf.size(), HardwareBuffer::HBU_WRITE_ONLY));
304 }
305 if (!mRenderOp.indexData->indexBuffer ||
306 mRenderOp.indexData->indexBuffer->getNumIndexes() != size_t(idxBuf.size()))
307 {
308 mRenderOp.indexData->indexBuffer = HardwareBufferManager::getSingleton().createIndexBuffer(
309 HardwareIndexBuffer::IT_32BIT, idxBuf.size(), HardwareBuffer::HBU_WRITE_ONLY);
310 }
311
312 if (mConvertToBGR)
313 {
314 // convert RGBA > BGRA
315 PixelBox src(1, vtxBuf.size(), 1, PF_A8B8G8R8, (char*)vtxBuf.Data + offsetof(ImDrawVert, col));
316 src.rowPitch = sizeof(ImDrawVert) / sizeof(ImU32);
317 PixelBox dst = src;
318 dst.format = PF_A8R8G8B8;
319 PixelUtil::bulkPixelConversion(src, dst);
320 }
321
322 // Copy all vertices
323 bind->getBuffer(0)->writeData(0, vtxBuf.size_in_bytes(), vtxBuf.Data, true);
324 mRenderOp.indexData->indexBuffer->writeData(0, idxBuf.size_in_bytes(), idxBuf.Data, true);
325
326 mRenderOp.vertexData->vertexStart = 0;
327 mRenderOp.vertexData->vertexCount = vtxBuf.size();
328}
329} // 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 _findVisibleObjects(Camera *cam, RenderQueue *queue, Viewport *vp)
ImFont * addFont(const String &name, const String &group OGRE_RESOURCE_GROUP_INIT)
add font from ogre .fontdef file must be called before first show()
static void NewFrame(const FrameEvent &evt)
std::vector< CodePointRange > mCodePointRanges
std::vector< ImWchar > CodePointRange