ImageViewerMain.cpp 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. // Copyright (C) 2009-2022, Panagiotis Christopoulos Charitos and contributors.
  2. // All rights reserved.
  3. // Code licensed under the BSD License.
  4. // http://www.anki3d.org/LICENSE
  5. #include <AnKi/AnKi.h>
  6. using namespace anki;
  7. class TextureViewerUiNode : public SceneNode
  8. {
  9. public:
  10. ImageResourcePtr m_imageResource;
  11. TextureViewerUiNode(SceneGraph* scene, CString name)
  12. : SceneNode(scene, name)
  13. {
  14. SpatialComponent* spatialc = newComponent<SpatialComponent>();
  15. spatialc->setAlwaysVisible(true);
  16. UiComponent* uic = newComponent<UiComponent>();
  17. uic->init(
  18. [](CanvasPtr& canvas, void* ud) {
  19. static_cast<TextureViewerUiNode*>(ud)->draw(canvas);
  20. },
  21. this);
  22. ANKI_CHECK_AND_IGNORE(getSceneGraph().getUiManager().newInstance(m_font, "EngineAssets/UbuntuMonoRegular.ttf",
  23. Array<U32, 1>{16}));
  24. ANKI_CHECK_AND_IGNORE(getSceneGraph().getResourceManager().loadResource(
  25. "ShaderBinaries/UiVisualizeImage.ankiprogbin", m_imageProgram));
  26. }
  27. Error frameUpdate([[maybe_unused]] Second prevUpdateTime, [[maybe_unused]] Second crntTime) override
  28. {
  29. if(!m_textureView.isCreated())
  30. {
  31. m_textureView = m_imageResource->getTextureView();
  32. }
  33. return Error::NONE;
  34. }
  35. private:
  36. FontPtr m_font;
  37. ShaderProgramResourcePtr m_imageProgram;
  38. ShaderProgramPtr m_imageGrProgram;
  39. TextureViewPtr m_textureView;
  40. UiImageIdExtra m_imageIdExtra;
  41. U32 m_crntMip = 0;
  42. F32 m_zoom = 1.0f;
  43. F32 m_depth = 0.0f;
  44. Bool m_pointSampling = true;
  45. Array<Bool, 4> m_colorChannel = {true, true, true, true};
  46. F32 m_maxColorValue = 1.0f;
  47. void draw(CanvasPtr& canvas)
  48. {
  49. const Texture& grTex = *m_imageResource->getTexture().get();
  50. const U32 colorComponentCount = getFormatInfo(grTex.getFormat()).m_componentCount;
  51. ANKI_ASSERT(grTex.getTextureType() == TextureType::_2D || grTex.getTextureType() == TextureType::_3D);
  52. if(!m_imageGrProgram.isCreated())
  53. {
  54. ShaderProgramResourceVariantInitInfo variantInit(m_imageProgram);
  55. variantInit.addMutation("TEXTURE_TYPE", (grTex.getTextureType() == TextureType::_2D) ? 0 : 1);
  56. const ShaderProgramResourceVariant* variant;
  57. m_imageProgram->getOrCreateVariant(variantInit, variant);
  58. m_imageGrProgram = variant->getProgram();
  59. }
  60. ImGui::Begin("Console", nullptr,
  61. ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove);
  62. canvas->pushFont(m_font, 16);
  63. ImGui::SetWindowPos(Vec2(0.0f, 0.0f));
  64. ImGui::SetWindowSize(Vec2(F32(canvas->getWidth()), F32(canvas->getHeight())));
  65. ImGui::BeginChild("Tools", Vec2(-1.0f, 30.0f), false, ImGuiWindowFlags_AlwaysAutoResize);
  66. // Zoom
  67. if(ImGui::Button("-"))
  68. {
  69. m_zoom -= 0.1f;
  70. }
  71. ImGui::SameLine();
  72. ImGui::DragFloat("##Zoom", &m_zoom, 0.01f, 0.1f, 20.0f, "Zoom %.3f");
  73. ImGui::SameLine();
  74. if(ImGui::Button("+"))
  75. {
  76. m_zoom += 0.1f;
  77. }
  78. ImGui::SameLine();
  79. ImGui::Spacing();
  80. ImGui::SameLine();
  81. // Sampling
  82. ImGui::Checkbox("Point sampling", &m_pointSampling);
  83. ImGui::SameLine();
  84. ImGui::Spacing();
  85. ImGui::SameLine();
  86. // Colors
  87. ImGui::Checkbox("Red", &m_colorChannel[0]);
  88. ImGui::SameLine();
  89. ImGui::Checkbox("Green", &m_colorChannel[1]);
  90. ImGui::SameLine();
  91. ImGui::Checkbox("Blue", &m_colorChannel[2]);
  92. ImGui::SameLine();
  93. if(colorComponentCount == 4)
  94. {
  95. ImGui::Checkbox("Alpha", &m_colorChannel[3]);
  96. ImGui::SameLine();
  97. }
  98. ImGui::Spacing();
  99. ImGui::SameLine();
  100. // Mips combo
  101. {
  102. StringListAuto mipLabels(getFrameAllocator());
  103. for(U32 mip = 0; mip < grTex.getMipmapCount(); ++mip)
  104. {
  105. mipLabels.pushBackSprintf("Mip %u (%u x %u)", mip, grTex.getWidth() >> mip, grTex.getHeight() >> mip);
  106. }
  107. const U32 lastCrntMip = m_crntMip;
  108. if(ImGui::BeginCombo("##Mipmap", (mipLabels.getBegin() + m_crntMip)->cstr(), ImGuiComboFlags_HeightLarge))
  109. {
  110. for(U32 mip = 0; mip < grTex.getMipmapCount(); ++mip)
  111. {
  112. const Bool isSelected = (m_crntMip == mip);
  113. if(ImGui::Selectable((mipLabels.getBegin() + mip)->cstr(), isSelected))
  114. {
  115. m_crntMip = mip;
  116. }
  117. if(isSelected)
  118. {
  119. ImGui::SetItemDefaultFocus();
  120. }
  121. }
  122. ImGui::EndCombo();
  123. }
  124. if(lastCrntMip != m_crntMip)
  125. {
  126. // Re-create the image view
  127. TextureViewInitInfo viewInitInf(m_imageResource->getTexture());
  128. viewInitInf.m_firstMipmap = m_crntMip;
  129. viewInitInf.m_mipmapCount = 1;
  130. m_textureView = getSceneGraph().getGrManager().newTextureView(viewInitInf);
  131. }
  132. ImGui::SameLine();
  133. }
  134. // Depth
  135. if(grTex.getTextureType() == TextureType::_3D)
  136. {
  137. StringListAuto labels(getFrameAllocator());
  138. for(U32 d = 0; d < grTex.getDepth(); ++d)
  139. {
  140. labels.pushBackSprintf("Depth %u", d);
  141. }
  142. if(ImGui::BeginCombo("##Depth", (labels.getBegin() + U32(m_depth))->cstr(), ImGuiComboFlags_HeightLarge))
  143. {
  144. for(U32 d = 0; d < grTex.getDepth(); ++d)
  145. {
  146. const Bool isSelected = (m_depth == F32(d));
  147. if(ImGui::Selectable((labels.getBegin() + d)->cstr(), isSelected))
  148. {
  149. m_depth = F32(d);
  150. }
  151. if(isSelected)
  152. {
  153. ImGui::SetItemDefaultFocus();
  154. }
  155. }
  156. ImGui::EndCombo();
  157. }
  158. ImGui::SameLine();
  159. }
  160. // Max color slider
  161. ImGui::SliderFloat("##Max color", &m_maxColorValue, 0.0f, 5.0f, "Max color = %.3f");
  162. ImGui::SameLine();
  163. // Next
  164. ImGui::EndChild();
  165. ImGui::BeginChild("Image", Vec2(-1.0f, -1.0f), false,
  166. ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_HorizontalScrollbar);
  167. // Image
  168. {
  169. // Center image
  170. const Vec2 imageSize = Vec2(F32(grTex.getWidth()), F32(grTex.getHeight())) * m_zoom;
  171. class ExtraPushConstants
  172. {
  173. public:
  174. Vec4 m_colorScale;
  175. Vec4 m_depth;
  176. } pc;
  177. pc.m_colorScale.x() = F32(m_colorChannel[0]) / m_maxColorValue;
  178. pc.m_colorScale.y() = F32(m_colorChannel[1]) / m_maxColorValue;
  179. pc.m_colorScale.z() = F32(m_colorChannel[2]) / m_maxColorValue;
  180. pc.m_colorScale.w() = F32(m_colorChannel[3]);
  181. pc.m_depth = Vec4((m_depth + 0.5f) / F32(grTex.getDepth()));
  182. m_imageIdExtra.m_customProgram = m_imageGrProgram;
  183. m_imageIdExtra.m_textureView = m_textureView;
  184. m_imageIdExtra.m_extraPushConstantsSize = U8(sizeof(pc));
  185. m_imageIdExtra.setExtraPushConstants(&pc, sizeof(pc));
  186. ImGui::Image(UiImageId(&m_imageIdExtra, m_pointSampling), imageSize, Vec2(0.0f, 1.0f), Vec2(1.0f, 0.0f),
  187. Vec4(1.0f), Vec4(0.0f, 0.0f, 0.0f, 1.0f));
  188. if(ImGui::IsItemHovered())
  189. {
  190. if(ImGui::GetIO().KeyCtrl)
  191. {
  192. // Zoom
  193. const F32 zoomSpeed = 0.05f;
  194. if(ImGui::GetIO().MouseWheel > 0.0f)
  195. {
  196. m_zoom *= 1.0f + zoomSpeed;
  197. }
  198. else if(ImGui::GetIO().MouseWheel < 0.0f)
  199. {
  200. m_zoom *= 1.0f - zoomSpeed;
  201. }
  202. // Pan
  203. if(ImGui::GetIO().MouseDown[0])
  204. {
  205. const Vec2 velocity = toAnki(ImGui::GetIO().MouseDelta);
  206. if(velocity.x() != 0.0f)
  207. {
  208. ImGui::SetScrollX(ImGui::GetScrollX() - velocity.x());
  209. }
  210. if(velocity.y() != 0.0f)
  211. {
  212. ImGui::SetScrollY(ImGui::GetScrollY() - velocity.y());
  213. }
  214. }
  215. }
  216. }
  217. }
  218. ImGui::EndChild();
  219. canvas->popFont();
  220. ImGui::End();
  221. }
  222. };
  223. class MyApp : public App
  224. {
  225. public:
  226. Error init(ConfigSet* config, int argc, char** argv, [[maybe_unused]] CString appName)
  227. {
  228. if(argc < 2)
  229. {
  230. ANKI_LOGE("Wrong number of arguments");
  231. return Error::USER_DATA;
  232. }
  233. HeapAllocator<U32> alloc(allocAligned, nullptr);
  234. StringAuto mainDataPath(alloc, ANKI_SOURCE_DIRECTORY);
  235. config->setWindowFullscreen(false);
  236. config->setRsrcDataPaths(mainDataPath);
  237. config->setGrValidation(false);
  238. config->setGrDebugMarkers(false);
  239. ANKI_CHECK(config->setFromCommandLineArguments(argc - 2, argv + 2));
  240. ANKI_CHECK(App::init(config, allocAligned, nullptr));
  241. // Load the texture
  242. ImageResourcePtr image;
  243. ANKI_CHECK(getResourceManager().loadResource(argv[1], image, false));
  244. // Change window name
  245. StringAuto title(alloc);
  246. title.sprintf("%s %u x %u Mips %u Format %s", argv[1], image->getWidth(), image->getHeight(),
  247. image->getTexture()->getMipmapCount(), getFormatInfo(image->getTexture()->getFormat()).m_name);
  248. getWindow().setWindowTitle(title);
  249. // Create the node
  250. SceneGraph& scene = getSceneGraph();
  251. TextureViewerUiNode* node;
  252. ANKI_CHECK(scene.newSceneNode("TextureViewer", node));
  253. node->m_imageResource = image;
  254. return Error::NONE;
  255. }
  256. Error userMainLoop(Bool& quit, [[maybe_unused]] Second elapsedTime) override
  257. {
  258. Input& input = getInput();
  259. if(input.getKey(KeyCode::ESCAPE))
  260. {
  261. quit = true;
  262. }
  263. return Error::NONE;
  264. }
  265. };
  266. int main(int argc, char* argv[])
  267. {
  268. Error err = Error::NONE;
  269. ConfigSet config(allocAligned, nullptr);
  270. MyApp* app = new MyApp;
  271. err = app->init(&config, argc, argv, "Texture Viewer");
  272. if(!err)
  273. {
  274. err = app->mainLoop();
  275. }
  276. delete app;
  277. if(err)
  278. {
  279. ANKI_LOGE("Error reported. Bye!!");
  280. }
  281. else
  282. {
  283. ANKI_LOGI("Bye!!");
  284. }
  285. return 0;
  286. }