New version of Awesomium (Now UltraLight)

Anything and everything that's related to OGRE or the wider graphics field that doesn't fit into the other forums.
Post Reply
User avatar
AshMcConnell
Silver Sponsor
Silver Sponsor
Posts: 605
Joined: Fri Dec 14, 2007 11:44 am
Location: Northern Ireland
x 16
Contact:

New version of Awesomium (Now UltraLight)

Post by AshMcConnell »

Hi Folks,

Just a headsup that the excellent Awesomium (using HTML/JS/CSS as a game UI) has been rewritten to be really fast.

https://ultralig.ht/

Has anyone taken a look yet? Has anyone got it working with Ogre yet? I would like to give it a go, but I'm not sure I'll have the required knowledge.

All the best,
Ash
Iwamori
Gnoblar
Posts: 2
Joined: Sun Oct 14, 2018 9:21 pm
x 1

Re: New version of Awesomium (Now UltraLight)

Post by Iwamori »

I know that question was posted a long time ago, but maybe it will help someone.

I managed to copy bitmap rendered by Ultralight in to Ogre texture and display it as overlay. I am pasting source code, but it is a proof of concept rather than final solution, so it may be buggy.

1. First, implement file system class to load images, CSS or JavaScript files.
2. Then initialize Ultralight library.
3. Then update Ultralight view each frame.
4. Finally, render view to bitmap and copy it to Ogre texture.

Code: Select all

    class MyFileSystem : public ultralight::FileSystem
    {
    public:
        bool FileExists(const ultralight::String16& path) override;
        bool DeleteFile_(const ultralight::String16& path) override;
        bool DeleteEmptyDirectory(const ultralight::String16& path) override;
        bool MoveFile_(const ultralight::String16& old_path, const ultralight::String16& new_path) override;
        bool GetFileSize(const ultralight::String16& path, int64_t& result) override;
        bool GetFileSize(ultralight::FileHandle handle, int64_t& result) override;
        bool GetFileMimeType(const ultralight::String16& path, ultralight::String16& result) override;
        bool GetFileModificationTime(const ultralight::String16& path, time_t& result) override;
        bool GetFileCreationTime(const ultralight::String16& path, time_t& result) override;
        ultralight::MetadataType GetMetadataType(const ultralight::String16& path) override;
        ultralight::String16 GetPathByAppendingComponent(const ultralight::String16& path, const ultralight::String16& component) override;
        bool CreateDirectory_(const ultralight::String16& path) override;
        ultralight::String16 GetHomeDirectory() override;
        ultralight::String16 GetFilenameFromPath(const ultralight::String16& path) override;
        ultralight::String16 GetDirectoryNameFromPath(const ultralight::String16& path) override;
        bool GetVolumeFreeSpace(const ultralight::String16& path, uint64_t& result) override;
        int32_t GetVolumeId(const ultralight::String16& path) override;
        ultralight::Ref<ultralight::String16Vector> ListDirectory(const ultralight::String16& path, const ultralight::String16& filter) override;
        ultralight::String16 OpenTemporaryFile(const ultralight::String16& prefix, ultralight::FileHandle& handle) override;
        ultralight::FileHandle OpenFile(const ultralight::String16& path, bool open_for_writing) override;
        void CloseFile(ultralight::FileHandle& handle) override;
        int64_t SeekFile(ultralight::FileHandle handle, int64_t offset, ultralight::FileSeekOrigin origin) override;
        bool TruncateFile(ultralight::FileHandle handle, int64_t offset) override;
        int64_t WriteToFile(ultralight::FileHandle handle, const char* data, int64_t length) override;
        int64_t ReadFromFile(ultralight::FileHandle handle, char* data, int64_t length) override;
        bool CopyFile_(const ultralight::String16& source_path, const ultralight::String16& destination_path) override;

    private:
        ultralight::FileHandle last_file_handle = 0;

        std::map<ultralight::FileHandle, std::shared_ptr<std::fstream>> file_handles;
    };

Code: Select all

    bool MyFileSystem::FileExists(const ultralight::String16& path)
    {
        throw Exception("Not implemented.");
    }

    bool MyFileSystem::DeleteFile_(const ultralight::String16& path)
    {
        throw Exception("Not implemented.");
    }

    bool MyFileSystem::DeleteEmptyDirectory(const ultralight::String16& path)
    {
        throw Exception("Not implemented.");
    }

    bool MyFileSystem::MoveFile_(const ultralight::String16& old_path, const ultralight::String16& new_path)
    {
        throw Exception("Not implemented.");
    }

    bool MyFileSystem::GetFileSize(const ultralight::String16& path, int64_t& result)
    {
        throw Exception("Not implemented.");
    }

    bool MyFileSystem::GetFileSize(ultralight::FileHandle handle, int64_t& result)
    {
        result = file_handles[handle]->tellg();

        file_handles[handle]->seekg(0, std::ios::end);
        
        result = file_handles[handle]->tellg() - result;

        return true;
    }

    bool MyFileSystem::GetFileMimeType(const ultralight::String16& path, ultralight::String16& result)
    {
        static std::map<std::string, std::string> types =
        {
            { ".css" , "text/css"        },
            { ".html", "text/html"       },
            { ".jpg" , "image/jpeg"      },
            { ".jpeg", "image/jpeg"      },
            { ".js"  , "text/javascript" },
            { ".png" , "image/png"       }
        };

        auto extension = std::filesystem::path(path.data()).extension();
        auto type = types.find(extension.string());

        if (type == types.end())
        {
            return false;
        }

        result = type->second.c_str();

        return true;
    }

    bool MyFileSystem::GetFileModificationTime(const ultralight::String16& path, time_t& result)
    {
        throw Exception("Not implemented.");
    }

    bool MyFileSystem::GetFileCreationTime(const ultralight::String16& path, time_t& result)
    {
        throw Exception("Not implemented.");
    }

    ultralight::MetadataType MyFileSystem::GetMetadataType(const ultralight::String16& path)
    {
        throw Exception("Not implemented.");
    }

    ultralight::String16 MyFileSystem::GetPathByAppendingComponent(const ultralight::String16& path, const ultralight::String16& component)
    {
        throw Exception("Not implemented.");
    }

    bool MyFileSystem::CreateDirectory_(const ultralight::String16& path)
    {
        throw Exception("Not implemented.");
    }

    ultralight::String16 MyFileSystem::GetHomeDirectory()
    {
        throw Exception("Not implemented.");
    }

    ultralight::String16 MyFileSystem::GetFilenameFromPath(const ultralight::String16& path)
    {
        throw Exception("Not implemented.");
    }

    ultralight::String16 MyFileSystem::GetDirectoryNameFromPath(const ultralight::String16& path)
    {
        throw Exception("Not implemented.");
    }

    bool MyFileSystem::GetVolumeFreeSpace(const ultralight::String16& path, uint64_t& result)
    {
        throw Exception("Not implemented.");
    }

    int32_t MyFileSystem::GetVolumeId(const ultralight::String16& path)
    {
        throw Exception("Not implemented.");
    }

    ultralight::Ref<ultralight::String16Vector> MyFileSystem::ListDirectory(const ultralight::String16& path, const ultralight::String16& filter)
    {
        throw Exception("Not implemented.");
    }

    ultralight::String16 MyFileSystem::OpenTemporaryFile(const ultralight::String16& prefix, ultralight::FileHandle& handle)
    {
        throw Exception("Not implemented.");
    }

    ultralight::FileHandle MyFileSystem::OpenFile(const ultralight::String16& path, bool open_for_writing)
    {
        last_file_handle += 1;

        file_handles[last_file_handle] = std::make_shared<std::fstream>();
        file_handles[last_file_handle]->open(path.data());

        auto is_opened = file_handles[last_file_handle]->is_open();

        return last_file_handle;
    }

    void MyFileSystem::CloseFile(ultralight::FileHandle& handle)
    {
        L_TRACE << "CloseFile()";

        file_handles.erase(handle);
    }

    int64_t MyFileSystem::SeekFile(ultralight::FileHandle handle, int64_t offset, ultralight::FileSeekOrigin origin)
    {
        throw Exception("Not implemented.");
    }

    bool MyFileSystem::TruncateFile(ultralight::FileHandle handle, int64_t offset)
    {
        throw Exception("Not implemented.");
    }

    int64_t MyFileSystem::WriteToFile(ultralight::FileHandle handle, const char* data, int64_t length)
    {
        throw Exception("Not implemented.");
    }

    int64_t MyFileSystem::ReadFromFile(ultralight::FileHandle handle, char* data, int64_t length)
    {
        auto s = stream_to_buffer(file_handles[handle]);

        std::memcpy(data, s.data(), length);

        return length;
    }

    bool MyFileSystem::CopyFile_(const ultralight::String16& source_path, const ultralight::String16& destination_path)
    {
        throw Exception("Not implemented.");
    }

Code: Select all

    bool UI_new::init()
    {
        ogre_surface = new OgreSurface("uitex", 1024, 1024);

        ultralight::Config config;

        config.device_scale_hint = 1.0;
        config.font_family_standard = "Arial";

        ultralight::Platform::instance().set_config(config);
        ultralight::Platform::instance().set_file_system(new MyFileSystem());

        renderer = ultralight::Renderer::Create();

        view = renderer->CreateView(1024, 768, true);
        view->set_load_listener(this);
        view->set_view_listener(this);
        view->LoadURL("file:///D:/Projects/rts/bin/ui/ui.html");

        renderer->Update();

        return true;
    }

Code: Select all

        ui_new.renderer->Update();
        ui_new.renderer->Render();
        
        if (ui_new.view->is_bitmap_dirty())
        {
            // ui_new.view->bitmap()->WritePNG("../cache/result.png");

            ui_new.view->bitmap()->LockPixels();
            ui_new.ogre_surface->update(ui_new.view->bitmap());
            ui_new.view->bitmap()->UnlockPixels();
        }

Code: Select all

OgreSurface::OgreSurface(const std::string& name, int width, int height)
    {
        auto size = Ogre::Bitwise::firstPO2From(std::max(width, height));

        // Create texture.
        texture = Utils::create_texture(name, size, Ogre::PixelFormat::PF_B8G8R8A8);
        pixel_buffer = texture->getBuffer();

        // Create material.
        material = Ogre::MaterialManager::getSingleton().create(
            name,
            Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME
        );

        auto pass = material->getTechnique(0)->getPass(0);
        pass->setAmbient(Ogre::ColourValue(1.0f, 1.0f, 1.0f, 0.5f));
        pass->setDepthWriteEnabled(false);
        pass->setLightingEnabled(false);
        pass->setSceneBlending(Ogre::SceneBlendType::SBT_TRANSPARENT_ALPHA);

        auto baseTexUnit = pass->createTextureUnitState(name);
        baseTexUnit->setTextureFiltering(
            Ogre::FilterOptions::FO_ANISOTROPIC,
            Ogre::FilterOptions::FO_ANISOTROPIC,
            Ogre::FilterOptions::FO_NONE);
        baseTexUnit->setTextureAnisotropy(4);

        auto height_to_size = static_cast<float>(height) / static_cast<float>(size);
        auto width_to_size = static_cast<float>(width) / static_cast<float>(size);

        // Create rectangle.
        manual_object = Game::getSingleton().create_manual_object();
        manual_object->begin(name, Ogre::RenderOperation::OperationType::OT_TRIANGLE_LIST);

        // Triangle #1.
        manual_object->position(-1.0f, 1.0f, 0.0f);
        manual_object->textureCoord(0.0f, 0.0f);
        manual_object->normal(Ogre::Vector3::UNIT_Y);

        manual_object->position(-1.0f, -1.0f, 0.0f);
        manual_object->textureCoord(0.0f, height_to_size);
        manual_object->normal(Ogre::Vector3::UNIT_Y);

        manual_object->position(1.0f, 1.0f, 0.0f);
        manual_object->textureCoord(width_to_size, 0);
        manual_object->normal(Ogre::Vector3::UNIT_Y);

        // Triangle #2.
        manual_object->position(1.0f, 1.0f, 0.0f);
        manual_object->textureCoord(width_to_size, 0);
        manual_object->normal(Ogre::Vector3::UNIT_Y);

        manual_object->position(-1.0f, -1.0f, 0.0f);
        manual_object->textureCoord(0.0f, height_to_size);
        manual_object->normal(Ogre::Vector3::UNIT_Y);

        manual_object->position(1.0f, -1.0f, 0.0f);
        manual_object->textureCoord(width_to_size, height_to_size);
        manual_object->normal(Ogre::Vector3::UNIT_Y);

        manual_object->end();
        manual_object->setBoundingBox(Ogre::AxisAlignedBox::BOX_INFINITE);
        manual_object->setRenderQueueGroup(Ogre::RenderQueueGroupID::RENDER_QUEUE_OVERLAY);
        manual_object->setUseIdentityProjection(true);
        manual_object->setUseIdentityView(true);

        // Create scene node.
        scene_node = Game::getSingleton().create_scene_node();
        scene_node->attachObject(manual_object);
    }

    OgreSurface::~OgreSurface()
    {
        Game::getSingleton().destroy_manual_object(manual_object);
        Game::getSingleton().destroy_scene_node(scene_node);

        Ogre::MaterialManager::getSingleton().remove(material->getHandle());
        Ogre::TextureManager::getSingleton().remove(texture->getHandle());
    }

    void OgreSurface::update(ultralight::RefPtr<ultralight::Bitmap> bitmap)
    {
        pixel_buffer->lock(Ogre::HardwareBuffer::LockOptions::HBL_WRITE_ONLY);

        auto texture_format1 = texture->getFormat();
        auto texture_format2 = pixel_buffer->getFormat();
        auto pixel_box = pixel_buffer->getCurrentLock();
        auto texture_depth = Ogre::PixelUtil::getNumElemBytes(pixel_box.format);
        auto texture_pitch = pixel_box.rowPitch * texture_depth;
        auto destination_buffer = static_cast<Ogre::uint8*>(pixel_box.data);

        auto bounds = bitmap->bounds();
        auto bpp = bitmap->bpp();
        auto format = bitmap->format();
        auto src_buffer = (std::uint8_t*)bitmap->raw_pixels();
        auto size = bitmap->size();
        auto width = bitmap->width();
        auto height = bitmap->height();
        auto row_bytes = bitmap->row_bytes();

        std::size_t bytes_to_copy = row_bytes > texture_pitch ? texture_pitch : row_bytes;

        for (int y = 0; y < bitmap->height(); ++y)
        {
            std::size_t dst_offset = texture_pitch * y;
            std::size_t src_offset = row_bytes * y;

            std::memcpy(destination_buffer + dst_offset, src_buffer + src_offset, bytes_to_copy);
        }

        pixel_buffer->unlock();
    }
Post Reply