Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions LuaSTG/LuaSTG/GameResource/ResourceManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ namespace luastg
// 纹理
bool LoadTexture(const char* name, const char* path, bool mipmaps = true) noexcept;
bool CreateTexture(const char* name, int width, int height) noexcept;
// 视频纹理
bool LoadVideo(const char* name, const char* path) noexcept;
// 渲染目标
bool CreateRenderTarget(const char* name, int width = 0, int height = 0, bool depth_buffer = false) noexcept;
// 图片精灵
Expand Down
38 changes: 38 additions & 0 deletions LuaSTG/LuaSTG/GameResource/ResourcePool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,44 @@ namespace luastg
return true;
}

bool ResourcePool::LoadVideo(const char* name, const char* path) noexcept
{
if (m_TexturePool.find(std::string_view(name)) != m_TexturePool.end())
{
if (ResourceMgr::GetResourceLoadingLog())
{
spdlog::warn("[luastg] LoadVideo: 纹理 '{}' 已存在,加载操作已取消", name);
}
return true;
}

core::SmartReference<core::ITexture2D> p_texture;
if (!LAPP.getGraphicsDevice()->createVideoTexture(path, p_texture.put()))
{
spdlog::error("[luastg] 从 '{}' 创建视频纹理 '{}' 失败", path, name);
return false;
}

try
{
core::SmartReference<IResourceTexture> tRes;
tRes.attach(new ResourceTextureImpl(name, p_texture.get()));
m_TexturePool.emplace(name, tRes);
}
catch (std::exception const& e)
{
spdlog::error("[luastg] LoadVideo: 创建视频纹理 '{}' 失败 ({})", name, e.what());
return false;
}

if (ResourceMgr::GetResourceLoadingLog())
{
spdlog::info("[luastg] LoadVideo: 已从 '{}' 加载视频 '{}' ({})", path, name, getResourcePoolTypeName());
}

return true;
}

bool ResourcePool::CreateTexture(const char* name, int width, int height) noexcept
{
if (m_TexturePool.find(std::string_view(name)) != m_TexturePool.end())
Expand Down
94 changes: 94 additions & 0 deletions LuaSTG/LuaSTG/LuaBinding/LW_ResourceMgr.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#include "LuaBinding/LuaWrapper.hpp"
#include "lua/plus.hpp"
#include "AppFrame.h"
#include "d3d11/VideoTexture.hpp"
#include "core/VideoDecoder.hpp"

void luastg::binding::ResourceManager::Register(lua_State* L) noexcept
{
Expand Down Expand Up @@ -52,6 +54,18 @@ void luastg::binding::ResourceManager::Register(lua_State* L) noexcept
return luaL_error(L, "can't load texture from file '%s'.", path);
return 0;
}
static int LoadVideo(lua_State* L) noexcept
{
const char* name = luaL_checkstring(L, 1);
const char* path = luaL_checkstring(L, 2);

ResourcePool* pActivedPool = LRES.GetActivedPool();
if (!pActivedPool)
return luaL_error(L, "can't load resource at this time.");
if (!pActivedPool->LoadVideo(name, path))
return luaL_error(L, "can't load video from file '%s'.", path);
return 0;
}
static int LoadSprite(lua_State* L) noexcept
{
const char* name = luaL_checkstring(L, 1);
Expand Down Expand Up @@ -671,13 +685,86 @@ void luastg::binding::ResourceManager::Register(lua_State* L) noexcept
LRES.CacheTTFFontString(luaL_checkstring(L, 1), str, len);
return 0;
}

// 视频控制函数
static core::IVideoDecoder* GetVideoDecoder(const char* name) noexcept {
auto tex = LRES.FindTexture(name);
if (!tex) return nullptr;

auto texture2d = tex->GetTexture();
if (!texture2d) return nullptr;

// 尝试转换为VideoTexture
auto video_texture = dynamic_cast<core::VideoTexture*>(texture2d);
if (!video_texture) return nullptr;

return video_texture->getVideoDecoder();
}

static int VideoSeek(lua_State* L) noexcept {
const char* name = luaL_checkstring(L, 1);
double time = luaL_checknumber(L, 2);
auto decoder = GetVideoDecoder(name);
if (!decoder)
return luaL_error(L, "video texture '%s' not found.", name);
lua_pushboolean(L, decoder->seek(time));
return 1;
}

static int VideoSetLooping(lua_State* L) noexcept {
const char* name = luaL_checkstring(L, 1);
bool loop = lua_toboolean(L, 2) != 0;
auto decoder = GetVideoDecoder(name);
if (!decoder)
return luaL_error(L, "video texture '%s' not found.", name);
decoder->setLooping(loop);
return 0;
}

static int VideoUpdate(lua_State* L) noexcept {
const char* name = luaL_checkstring(L, 1);
double time = luaL_checknumber(L, 2);
auto decoder = GetVideoDecoder(name);
if (!decoder)
return luaL_error(L, "video texture '%s' not found.", name);
lua_pushboolean(L, decoder->updateToTime(time));
return 1;
}

static int VideoGetInfo(lua_State* L) noexcept {
const char* name = luaL_checkstring(L, 1);
auto decoder = GetVideoDecoder(name);
if (!decoder)
return luaL_error(L, "video texture '%s' not found.", name);

lua_createtable(L, 0, 5);

lua_pushnumber(L, decoder->getDuration());
lua_setfield(L, -2, "duration");

lua_pushnumber(L, decoder->getCurrentTime());
lua_setfield(L, -2, "time");

lua_pushboolean(L, decoder->isLooping());
lua_setfield(L, -2, "looping");

auto size = decoder->getVideoSize();
lua_pushinteger(L, size.x);
lua_setfield(L, -2, "width");

lua_pushinteger(L, size.y);
lua_setfield(L, -2, "height");

return 1;
}
};

luaL_Reg const lib[] = {
{ "SetResLoadInfo", &Wrapper::SetResLoadInfo },
{ "SetResourceStatus", &Wrapper::SetResourceStatus },
{ "GetResourceStatus", &Wrapper::GetResourceStatus },
{ "LoadTexture", &Wrapper::LoadTexture },
{ "LoadVideo", &Wrapper::LoadVideo },
{ "LoadImage", &Wrapper::LoadSprite },
{ "LoadAnimation", &Wrapper::LoadAnimation },
{ "LoadPS", &Wrapper::LoadPS },
Expand Down Expand Up @@ -710,6 +797,13 @@ void luastg::binding::ResourceManager::Register(lua_State* L) noexcept
{ "SetFontState", &Wrapper::SetFontState },

{ "CacheTTFString", &Wrapper::CacheTTFString },

// 视频控制函数
{ "VideoSeek", &Wrapper::VideoSeek },
{ "VideoSetLooping", &Wrapper::VideoSetLooping },
{ "VideoUpdate", &Wrapper::VideoUpdate },
{ "VideoGetInfo", &Wrapper::VideoGetInfo },

{ NULL, NULL },
};

Expand Down
26 changes: 26 additions & 0 deletions data/example/video_example.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
-- 视频播放示例
-- 使用 StopWatch 提供绝对时间,手动调用 VideoUpdate 更新画面

-- 在资源池中加载视频
-- 参数:名称,路径
LoadVideo('video1', 'test_video.mp4')

-- 创建计时器并设置循环播放
local video_clock = lstg.StopWatch()
VideoSetLooping('video1', true)

-- 像普通纹理一样创建精灵
LoadImage('video_sprite', 'video1', 0, 0, 640, 480)

-- 在 RenderFunc 中渲染
function RenderFunc()
-- 以秒表绝对时间驱动视频更新
VideoUpdate('video1', video_clock:GetElapsed())
Render('video_sprite', 100, 100)
end

-- 可用控制:
-- 1. VideoSetLooping(name, bool) -- 设置是否循环播放
-- 2. VideoSeek(name, time) -- 控制时间,但是不更新画面,适合进度条等场景,完成后需要调用 VideoUpdate 来刷新画面
-- 3. VideoUpdate(name, absolute_time) -- 根据绝对时间更新视频画面,适合与 StopWatch 结合使用,确保视频播放与游戏时间同步
-- 4. VideoGetInfo(name) -- 获取视频信息,如帧率、总时长等,返回一个表格
4 changes: 4 additions & 0 deletions engine/graphics/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ target_link_libraries(${lib_name} PUBLIC
dxgi.lib
d3d11.lib
d2d1.lib
# media foundation (for video)
mfplat.lib
mfreadwrite.lib
mfuuid.lib
Microsoft.Windows.ImplementationLibrary
Microsoft::DirectXTexMini
libqoi
Expand Down
3 changes: 3 additions & 0 deletions engine/graphics/core/GraphicsDevice.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "core/ImmutableString.hpp"
#include "core/GraphicsBuffer.hpp"
#include "core/Texture2D.hpp"
#include "core/VideoDecoder.hpp"
#include "core/GraphicsSampler.hpp"
#include "core/RenderTarget.hpp"
#include "core/DepthStencilBuffer.hpp"
Expand Down Expand Up @@ -108,6 +109,8 @@ namespace core {
virtual bool createTextureFromFile(StringView path, bool mipmap, ITexture2D** out_texture) = 0;
virtual bool createTextureFromImage(IImage* image, bool mipmap, ITexture2D** out_texture) = 0;
virtual bool createTexture(Vector2U size, ITexture2D** out_texture) = 0;
virtual bool createVideoTexture(StringView path, ITexture2D** out_texture) = 0;
virtual bool createVideoDecoder(IVideoDecoder** out_decoder) = 0;

virtual bool createSampler(const GraphicsSamplerInfo& info, IGraphicsSampler** out_sampler) = 0;

Expand Down
42 changes: 42 additions & 0 deletions engine/graphics/core/VideoDecoder.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#pragma once
#include "core/ReferenceCounted.hpp"
#include "core/Vector2.hpp"
#include "core/ImmutableString.hpp"

namespace core {
CORE_INTERFACE IVideoDecoder : IReferenceCounted {
// 打开视频文件
virtual bool open(StringView path) = 0;

// 关闭视频
virtual void close() = 0;

// 获取状态
virtual bool hasVideo() const noexcept = 0;

// 视频信息
virtual Vector2U getVideoSize() const noexcept = 0;
virtual double getDuration() const noexcept = 0;
virtual double getCurrentTime() const noexcept = 0; // 返回上次更新的帧时间

// 跳转到指定时间
virtual bool seek(double time_in_seconds) = 0;

// 循环播放设置
virtual void setLooping(bool loop) = 0;
virtual bool isLooping() const noexcept = 0;

// 手动更新到指定时间点
// time_in_seconds: 目标时间(秒)
// 返回是否成功更新(失败可能是因为到达视频结尾且未循环)
virtual bool updateToTime(double time_in_seconds) = 0;

// 获取用于渲染的纹理(返回 ID3D11Texture2D*)
virtual void* getNativeTexture() const noexcept = 0;

// 获取 Shader Resource View(返回 ID3D11ShaderResourceView*)
virtual void* getNativeShaderResourceView() const noexcept = 0;
};

CORE_INTERFACE_ID(IVideoDecoder, "a4b5c6d7-e8f9-1234-5678-9abcdef01234")
}
2 changes: 2 additions & 0 deletions engine/graphics/d3d11/GraphicsDevice.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ namespace core {
bool createTextureFromFile(StringView path, bool mipmap, ITexture2D** out_texture) override;
bool createTexture(Vector2U size, ITexture2D** out_texture) override;
bool createTextureFromImage(IImage* image, bool mipmap, ITexture2D** out_texture) override;
bool createVideoTexture(StringView path, ITexture2D** out_texture) override;
bool createVideoDecoder(IVideoDecoder** out_decoder) override;

bool createSampler(const GraphicsSamplerInfo& info, IGraphicsSampler** out_sampler) override;

Expand Down
Loading