diff --git a/examples/image_viewer/CMakeLists.txt b/examples/image_viewer/CMakeLists.txt new file mode 100644 index 0000000..8719d57 --- /dev/null +++ b/examples/image_viewer/CMakeLists.txt @@ -0,0 +1,7 @@ +add_executable(image_viewer main.cpp) + +target_link_libraries(image_viewer PRIVATE ${PROJECT_NAME}) + +target_compile_definitions(image_viewer PRIVATE + IMAGE_VIEWER_ASSET_DIR="${CMAKE_CURRENT_SOURCE_DIR}" +) diff --git a/examples/image_viewer/dog.jpeg b/examples/image_viewer/dog.jpeg new file mode 100644 index 0000000..26e1725 Binary files /dev/null and b/examples/image_viewer/dog.jpeg differ diff --git a/examples/image_viewer/main.cpp b/examples/image_viewer/main.cpp new file mode 100644 index 0000000..2eae2df --- /dev/null +++ b/examples/image_viewer/main.cpp @@ -0,0 +1,79 @@ +#include + +#include +#include +#include +#include +#include +#include +#include + +// Shows a render target filling the whole panel. +class ViewportPanel : public sophia::Panel { + public: + ViewportPanel(sophia::UI& ui, sophia::RenderTargetHandle target) + : Panel{{.name = "Image", + .windowFlags = ImGuiWindowFlags_NoScrollbar | + ImGuiWindowFlags_NoScrollWithMouse}}, + ui{ui}, + target{target} {} + + void onUpdate(const sophia::FrameInfo&) override { ui.image(target); } + + private: + sophia::UI& ui; + sophia::RenderTargetHandle target; +}; + +// Displays an image file (anything stb_image reads: JPEG, PNG, BMP, ...) as a +// sprite in a 2D scene, scaled to fit the viewport. +// image_viewer [path] +class ImageViewer : public sophia::Application { + public: + ImageViewer() + : Application{{.width = 1000, .height = 750, .name = "ImageViewer"}} {} + + void onCreate() override { + std::string path = + args.size() > 1 ? args[1] : IMAGE_VIEWER_ASSET_DIR "/dog.jpeg"; + + sophia::SceneHandle sceneHandle = scenes().create(); + sophia::Scene& scene = scenes().get(sceneHandle); + + camera = scene.createEntity("Camera"); + camera.addComponent(); + + sophia::TextureHandle texture = resources().load(path); + imageSize = resources().getExtent(texture); + + // One world unit per image pixel; the camera zooms to fit. + sophia::Entity image = scene.createEntity("Image"); + image.addComponent( + sophia::SpriteComponent{.texture = texture, .size = imageSize}); + + target = resources().create(); + views().create({.scene = sceneHandle, + .target = target, + .camera = camera, + .clearColor = {0.1f, 0.1f, 0.15f, 1.f}}); + + panels().emplace(ui(), target); + // panels().setDefaultLayout({.center = {"Image"}}); + } + + void onUpdate(const sophia::FrameInfo&) override { + // Fit the whole image: show enough height that neither dimension of the + // image overflows the viewport. + glm::vec2 viewport = resources().getExtent(target); + float viewportAspect = viewport.x / viewport.y; + camera.getComponent().ortho.height = + std::max(imageSize.y, imageSize.x / viewportAspect); + } + + private: + sophia::Entity camera; + sophia::RenderTargetHandle target{}; + glm::vec2 imageSize{1.f}; +}; + +SOPHIA_MAIN(ImageViewer)