惰性Log

技術系の話題を中心に書いています

Effekseerをemscriptenで動かす その2 ファイルローダ編

前回、ファイルローダがどうのこうの言ってから2ヶ月が経過してしまった。
Effekseerの標準のテクスチャローダではテクスチャを読み込むことができない。が、インターフェースが提供されているので各々で実装することはできる。
NenEngineに実装されている。
インターフェースは以下のようになった。

https://github.com/Astomih/NenEngine/blob/main/src/Nen/Effect/CustomTextureLoader.hgithub.com

#include <Effekseer.h>
class CustomTextureLoader : public Effekseer::TextureLoader
{
private:
    ::Effekseer::Backend::GraphicsDevice* graphicsDevice_ = nullptr;

public:
    CustomTextureLoader(::Effekseer::Backend::GraphicsDevice* graphicsDevice) : graphicsDevice_(graphicsDevice) {}

    ~CustomTextureLoader() = default;

public:
    Effekseer::TextureRef Load(const EFK_CHAR* path, Effekseer::TextureType textureType) override;

    void Unload(Effekseer::TextureRef data) override {}
}

実装は以下。
https://github.com/Astomih/NenEngine/blob/main/src/Nen/Effect/CustomTextureLoader.cppgithub.com

#include "CustomTextureLoader.h"
#include <Nen.hpp>
#include <EffekseerRenderer/EffekseerRendererGL.MaterialLoader.h>
#include <EffekseerRenderer/EffekseerRendererGL.RendererImplemented.h>
#include <EffekseerRenderer/GraphicsDevice.h>
#include <fstream>
#include <SDL.h>
#include <SDL_image.h>

Effekseer::TextureRef CustomTextureLoader::Load(const EFK_CHAR* path, Effekseer::TextureType textureType)
{
    std::array<char, 260> path8;
    Effekseer::ConvertUtf16ToUtf8(path8.data(), static_cast<int32_t>(path8.size()), path);
    SDL_Surface* surf = IMG_Load(path8.data());
    if (!surf)
    {
        nen::Logger::Error("Failed to load \"%s\". IMG_Error: %s", std::string(path8.data()), IMG_GetError());
    }
    ::SDL_LockSurface(surf);
    auto formatbuf = ::SDL_AllocFormat(SDL_PIXELFORMAT_ABGR8888);
    formatbuf->BytesPerPixel = 4;
    auto imagedata = ::SDL_ConvertSurface(surf, formatbuf, 0);
    SDL_UnlockSurface(surf);
    // Generate a GL texture
    GLuint texture;
    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, surf->w, surf->h, 0, GL_RGBA,
        GL_UNSIGNED_BYTE, imagedata->pixels);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
    std::string pathStr = path8.data();
    auto backend = static_cast<EffekseerRendererGL::Backend::GraphicsDevice*>(graphicsDevice_)
        ->CreateTexture(texture, false, [texture, pathStr]() -> void
            {
                glDeleteTextures(1, &texture);
            });
    auto textureData = Effekseer::MakeRefPtr<Effekseer::Texture>();
    textureData->SetBackend(backend);
    nen::Logger::Info("Loaded \"%s\".", pathStr);
    SDL_FreeSurface(surf);
    SDL_FreeSurface(imagedata);
    SDL_FreeFormat(formatbuf);
    return textureData;
}

基本的には、画像やマテリアルをこちら側で読み込み、各種APIの登録を済ませてからCreateTextureを呼ぶ。