Program Listing for File WebScene.h

Program Listing for File WebScene.h#

Return to documentation for file (include/Karana/WebScene/WebScene.h)

/*
 * Copyright (c) 2024-2026 Karana Dynamics Pty Ltd. All rights reserved.
 *
 * NOTICE TO USER:
 *
 * This source code and/or documentation (the "Licensed Materials") is
 * the confidential and proprietary information of Karana Dynamics Inc.
 * Use of these Licensed Materials is governed by the terms and conditions
 * of a separate software license agreement between Karana Dynamics and the
 * Licensee ("License Agreement"). Unless expressly permitted under that
 * agreement, any reproduction, modification, distribution, or disclosure
 * of the Licensed Materials, in whole or in part, to any third party
 * without the prior written consent of Karana Dynamics is strictly prohibited.
 *
 * THE LICENSED MATERIALS ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND.
 * KARANA DYNAMICS DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
 * BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY, NON-INFRINGEMENT, AND
 * FITNESS FOR A PARTICULAR PURPOSE.
 *
 * IN NO EVENT SHALL KARANA DYNAMICS BE LIABLE FOR ANY DAMAGES WHATSOEVER,
 * INCLUDING BUT NOT LIMITED TO LOSS OF PROFITS, DATA, OR USE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGES, WHETHER IN CONTRACT, TORT,
 * OR OTHERWISE ARISING OUT OF OR IN CONNECTION WITH THE LICENSED MATERIALS.
 *
 * U.S. Government End Users: The Licensed Materials are a "commercial item"
 * as defined at 48 C.F.R. 2.101, and are provided to the U.S. Government
 * only as a commercial end item under the terms of this license.
 *
 * Any use of the Licensed Materials in individual or commercial software must
 * include, in the user documentation and internal source code comments,
 * this Notice, Disclaimer, and U.S. Government Use Provision.
 */

/**
 * @file
 * @brief Contains the WebScene class.
 */

#pragma once
#include "Karana/KCore/SharedPointer.h"
#include "Karana/Scene/GraphicalScene.h"
#include "Karana/Scene/Material.h"
#include "Karana/Scene/Projection.h"
#include "Karana/Scene/StaticGeometry.h"
#include "Karana/Scene/Text.h"
#include "Karana/WebScene/WebSceneCamera.h"
#include "Karana/WebScene/WebSceneNode.h"
#include "Karana/WebScene/WebScenePart.h"
#include "Karana/WebUI/Server.h"
#include <future>
#include <string>
#include <unordered_map>

namespace Karana::Scene {
    /**
     * @class WebScene
     * @brief GraphicalScene implementation for web-based graphics
     *
     * See \sref{scene_layer_sec} for more discussion on
     * the scene layer.
     */
    class WebScene : public GraphicalScene {
        // For create/discard
        friend class WebSceneNode;
        friend class WebScenePart;

      public:
        /**
         * @brief WebScene constructor
         * @param name - Name for the WebScene
         * @param server - Server to communicate with frontends
         */
        WebScene(std::string_view name, const Karana::Core::ks_ptr<Karana::WebUI::Server> &server);
        ~WebScene();

        /**
         * @brief Create a WebScene
         * @param name - Name for the WebScene
         * @param server - Server to communicate with frontends
         * @return The created WebScene
         */
        static Karana::Core::ks_ptr<WebScene>
        create(std::string_view name, const Karana::Core::ks_ptr<Karana::WebUI::Server> &server);

        /**
         * @brief Get the server used by WebScene
         * @return The server used to communicate with frontends
         */
        const Karana::Core::ks_ptr<Karana::WebUI::Server> &server() const;

        void renderToFile(const std::filesystem::path &filepath) override;

        Karana::Core::id_t
        addOverlayText(std::string_view message,
                       float x,
                       float y,
                       const TextParameters &parameters = TextParameters{}) override;

        void setOverlayText(const Karana::Core::id_t &id, std::string_view message) override;

        void removeOverlayText(const Karana::Core::id_t &id) override;

        /**
         * @brief Get the default camera
         * @return The camera
         */
        Karana::Core::ks_ptr<GraphicalSceneCamera> defaultCamera() const override;

        /**
         * @brief Set a callback for when a part is clicked on
         * @param on_pick The callback to call with the part's id
         */
        void setOnPick(std::function<void(const Karana::Core::ks_ptr<WebScenePart> &)> on_pick);

        /// Force a render on the frontend
        void _forceRender() const; // NOLINT(readability-identifier-naming)

        /**
         * @brief Configure shadows
         *
         * Note: this is an experimental API subject to removal
         *
         * @param direction - The non-normalized direction to cast shadows or
         *        the zero vector to disable shadows.
         * @param radius - The radius of the area around origin where shadows are cast
         * @param pixels - Pixel dimensions of the square shadow map
         * @param intensity - Shadow intensity in the range [0, 1]
         */
        void
        _setShadows(const Karana::Math::Vec3 &direction, // NOLINT(readability-identifier-naming)
                    float radius = 5.0,
                    int pixels = 512,
                    float intensity = 0.5);

        /**
         * @brief Set the color of the background
         *
         * Note: this is an experimental API subject to removal
         *
         * @param color - The new background color.
         */
        void _setBackgroundColor(const Color &color); // NOLINT(readability-identifier-naming)

        /**
         * @brief Create a line segment connecting a pair of nodes
         *
         * Note: this is an experimental API subject to removal
         *
         * @param node1 - The first node.
         * @param node2 - The second node.
         * @param color1 - The line's color at the first node.
         * @param color2 - The line's color at the second node.
         * @param dashed - Whether the line should be dashed.
         * @return A unique id for the created line
         */
        Karana::Core::id_t _addLineBetween( // NOLINT(readability-identifier-naming)
            const Karana::Core::ks_ptr<WebSceneNode> &node1,
            const Karana::Core::ks_ptr<WebSceneNode> &node2,
            const Color &color1,
            const Color &color2,
            bool dashed = false);

        /**
         * @brief Remove a line segment created by _addLineBetween
         *
         * Note: this is an experimental API subject to removal
         *
         * @param line_id - The id returned prior by _addLineBetween
         */
        void
        _removeLineBetween(Karana::Core::id_t line_id); // NOLINT(readability-identifier-naming)

      protected:
        /**
         * @brief This does the work to discard the WebScene.
         * @param base - A Base pointer to the WebScene to discard.
         */
        virtual void _discard(Karana::Core::ks_ptr<Base> &base) override;

      private:
        static void
        _ioSendCreateSceneNodeMsg(const std::function<void(const uint8_t *data, size_t len)> &send,
                                  long id);
        static void
        _ioSendCreateScenePartMsg(const std::function<void(const uint8_t *data, size_t len)> &send,
                                  long id,
                                  long geometry_id,
                                  long material_id,
                                  layer_t layers);

        static void
        _ioSendDiscardSceneNodeMsg(const std::function<void(const uint8_t *data, size_t len)> &send,
                                   long id);

        static void
        _ioSendForceRenderMsg(const std::function<void(const uint8_t *data, size_t len)> &send);

        static void
        _ioSendSetShadowsMsg(const std::function<void(const uint8_t *data, size_t len)> &send,
                             const Karana::Math::Vec3 &direction,
                             float radius,
                             int pixels,
                             float intensity);

        static void _ioSendSetBackgroundColorMsg(
            const std::function<void(const uint8_t *data, size_t len)> &send, const Color &color);

        static void
        _ioSendAddOverlayTextMsg(const std::function<void(const uint8_t *data, size_t len)> &send,
                                 long id,
                                 std::string_view message,
                                 float x,
                                 float y,
                                 const TextParameters &parameters);

        static void
        _ioSendSetOverlayTextMsg(const std::function<void(const uint8_t *data, size_t len)> &send,
                                 long id,
                                 std::string_view message);

        static void _ioSendRemoveOverlayTextMsg(
            const std::function<void(const uint8_t *data, size_t len)> &send, long id);

        static void
        _ioSendAddLineBetweenMsg(const std::function<void(const uint8_t *data, size_t len)> &send,
                                 long id,
                                 long node1,
                                 long node2,
                                 const Color &color1,
                                 const Color &color2,
                                 bool dashed);

        static void _ioSendRemoveLineBetweenMsg(
            const std::function<void(const uint8_t *data, size_t len)> &send, long id);

        void _ioHandleConn(Karana::WebUI::Connection &conn) const;
        void _ioHandleMessage(Karana::WebUI::Connection &conn, const uint8_t *data, size_t size);
        void _createCamera();

        Karana::Core::ks_ptr<SceneNode> _createNodeBase(std::string_view name) override;
        void _discardNodeBase(Karana::Core::ks_ptr<SceneNode> &node,
                              bool recursive = true) override;

        Karana::Core::ks_ptr<ScenePart> _createPartBase(std::string_view name,
                                                        const VarStaticGeometry &geometry,
                                                        const VarMaterial &material,
                                                        layer_t layers) override;
        void _discardPartBase(Karana::Core::ks_ptr<ScenePart> &part) override;

        std::unordered_map<Karana::Core::id_t, std::weak_ptr<WebScenePart>> _io_parts;
        std::unordered_map<Karana::Core::id_t, std::weak_ptr<WebSceneNode>> _io_nodes;
        Karana::Core::ks_ptr<WebSceneCamera> _camera;
        std::unordered_map<Karana::Core::id_t, class WebSceneOverlayText> _io_overlay_text;
        std::unordered_map<Karana::Core::id_t, class WebSceneBetweenLine> _io_between_lines;

        std::unordered_map<Karana::Core::id_t, std::promise<RGBABuffer>> _io_render_calls;

        // Zero vector disables shadows
        Karana::Math::Vec3 _io_shadow_light_direction = {0, 0, 0};
        float _io_shadow_radius = 5.0;
        int _io_shadow_pixels = 512;
        float _io_shadow_intensity = 0.5;

        // Sky blue (float repr of #87ceeb)
        Color _io_background_color =
            Color::fromRGB(0.24228112245478564f, 0.6172065624120635f, 0.8307698767709715f);

        const Karana::Core::ks_ptr<Karana::WebUI::Server> _server;
        const Karana::Core::ks_ptr<WebResourceManager> _resource_manager;

        std::function<void(const Karana::Core::ks_ptr<WebScenePart> &)> _io_on_pick = {};
    };
} // namespace Karana::Scene