重构SpriteAssertTool.cpp,减少中间文件产生;重构TomGame文件夹,删去不必要的文件;修改CMakeLists,PC重构Game自动重新构建资源头文件;修改文档
This commit is contained in:
parent
08088c8413
commit
1e34e15e04
|
|
@ -127,15 +127,6 @@ function(imx6u_configure_app_target target_name)
|
|||
"${SDL2_DLL}"
|
||||
"$<TARGET_FILE_DIR:${target_name}>"
|
||||
)
|
||||
elseif(SDL2_image_FOUND)
|
||||
add_executable(SpriteAssetTool ${SPRITE_ASSET_TOOL_SOURCES})
|
||||
target_include_directories(SpriteAssetTool PRIVATE
|
||||
src/Core/Asset
|
||||
src/Core/RenderData
|
||||
)
|
||||
target_link_libraries(SpriteAssetTool PRIVATE SDL2::SDL2 SDL2_image::SDL2_image)
|
||||
else()
|
||||
message(STATUS "SpriteAssetTool disabled: SDL2_image was not found")
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
|
|
|
|||
12
README.md
12
README.md
|
|
@ -67,7 +67,7 @@ cmake --build build-win --config Release --target IMX6U-Game
|
|||
cmake --build build-win --config Release --target IMX6U-Demo
|
||||
```
|
||||
|
||||
如果修改了 Tom 的原始 PNG 资源,先重新生成 atlas 头文件:
|
||||
构建 `IMX6U-Game` 时会自动重新生成 Tom 的 atlas 头文件;也可以单独执行:
|
||||
```bash
|
||||
cmake --build build-win --config Release --target GenerateTomAtlasHeader
|
||||
```
|
||||
|
|
@ -106,7 +106,7 @@ cmake --build build-linux --target IMX6U-Game
|
|||
cmake --build build-linux --target IMX6U-Demo
|
||||
```
|
||||
|
||||
如果修改了 Tom 的原始 PNG 资源,先重新生成 atlas 头文件:
|
||||
构建 `IMX6U-Game` 时会自动重新生成 Tom 的 atlas 头文件;也可以单独执行:
|
||||
```bash
|
||||
cmake --build build-linux --target GenerateTomAtlasHeader
|
||||
```
|
||||
|
|
@ -139,6 +139,8 @@ cmake --build build-arm-fb
|
|||
|
||||
说明:单配置生成器(Makefile/Ninja)默认使用 `Release` 构建;ARM / framebuffer 性能测试必须确认 `CMAKE_BUILD_TYPE=Release`,否则逐像素绘制和 `/dev/fb0` 提交会因未优化构建出现数量级偏差。
|
||||
|
||||
注意:Tom 的 atlas 头文件生成工具是主机侧离线工具,依赖 PC/Linux 主机上的 SDL2_image;主机构建 `IMX6U-Game` 时会自动执行,ARM 交叉编译过程不会执行它。如果修改了 `src/Apps/Game/assets/raw/` 里的 PNG,必须先在 Windows 或 Linux x86 构建目录执行 `GenerateTomAtlasHeader` 或构建一次 `IMX6U-Game`,再进行 ARM 交叉编译。ARM 构建只消费已经生成好的 `src/Apps/Game/generated/tom_atlas.h`。
|
||||
|
||||
构建(SDL2 后端,要求工具链/sysroot 可找到目标板 SDL2 开发库):
|
||||
```bash
|
||||
cmake -B build-arm-sdl \
|
||||
|
|
@ -193,7 +195,7 @@ test_sprite_pixels
|
|||
|
||||
透明像素仍保留 alpha;当前 demo 通过 `RenderData::Image(..., 0x00000000)` 把全透明像素作为 color key 跳过。
|
||||
|
||||
Tom 游戏资源使用 `SpriteAssetTool --atlas-header` 从原始 PNG 一步生成 atlas 头文件:
|
||||
Tom 游戏资源使用 `SpriteAssetTool` 从固定目录 `src/Apps/Game/assets/raw/` 读取原始 PNG,一步生成 atlas 头文件。主机侧构建 `IMX6U-Game` 时 CMake 会自动执行 `GenerateTomAtlasHeader`;也可以单独执行:
|
||||
|
||||
```bash
|
||||
cmake --build build-win --config Release --target GenerateTomAtlasHeader
|
||||
|
|
@ -205,7 +207,9 @@ cmake --build build-win --config Release --target GenerateTomAtlasHeader
|
|||
src/Apps/Game/generated/tom_atlas.h
|
||||
```
|
||||
|
||||
该头文件包含 `tom_atlas_pixels` 和每张图的 `RenderData::SpriteRegion`,因此板端运行 Tom 游戏时不需要额外部署 `.sprite` 文件。
|
||||
该头文件包含 `tom_atlas_pixels` 和每张图的 `RenderData::SpriteRegion`,因此板端运行 Tom 游戏时不需要额外部署图片资源文件。尺寸规则、region 名称和 PNG 文件名统一记录在 `src/Apps/Game/tools/asset_pipeline/SpriteAssetTool.cpp` 顶部的 `Sources` 表里;CMake 不再重复维护每张 PNG 的路径。
|
||||
|
||||
`src/Apps/Game/assets/sprites/` 属于旧的 `.sprite` 文件部署流程,Tom 主游戏现在不再依赖它。只有旧的手动测试或未迁移工具还可能引用该目录;清理这些旧入口后可以删除这个目录。
|
||||
|
||||
### Bitmap Font 转换
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
set(TOM_GAME_TARGET IMX6U-Game)
|
||||
set(TOM_ATLAS_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/generated/tom_atlas.h")
|
||||
set_source_files_properties(${TOM_ATLAS_HEADER} PROPERTIES GENERATED TRUE)
|
||||
|
||||
add_executable(${TOM_GAME_TARGET}
|
||||
Main.cpp
|
||||
generated/tom_atlas.h
|
||||
${TOM_ATLAS_HEADER}
|
||||
)
|
||||
|
||||
target_include_directories(${TOM_GAME_TARGET} PRIVATE
|
||||
|
|
@ -11,91 +13,78 @@ target_include_directories(${TOM_GAME_TARGET} PRIVATE
|
|||
|
||||
imx6u_configure_app_target(${TOM_GAME_TARGET})
|
||||
|
||||
if(CMAKE_CROSSCOMPILING AND NOT EXISTS "${TOM_ATLAS_HEADER}")
|
||||
message(FATAL_ERROR
|
||||
"Tom atlas header is missing. Run GenerateTomAtlasHeader in a host build before cross compiling."
|
||||
)
|
||||
endif()
|
||||
|
||||
if(NOT USE_FRAMEBUFFER)
|
||||
set(SPRITE_ASSET_TOOL_SOURCES
|
||||
set(TOM_ATLAS_TOOL_SOURCES
|
||||
tools/asset_pipeline/SpriteAssetTool.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/Core/Asset/SpriteAssetLoader.cpp
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
add_executable(TomSpriteAssetTool EXCLUDE_FROM_ALL ${SPRITE_ASSET_TOOL_SOURCES})
|
||||
set_target_properties(TomSpriteAssetTool PROPERTIES OUTPUT_NAME SpriteAssetTool)
|
||||
target_include_directories(TomSpriteAssetTool PRIVATE
|
||||
${PROJECT_SOURCE_DIR}/src/Core/Asset
|
||||
${PROJECT_SOURCE_DIR}/src/Core/RenderData
|
||||
add_executable(TomAtlasTool EXCLUDE_FROM_ALL ${TOM_ATLAS_TOOL_SOURCES})
|
||||
target_include_directories(TomAtlasTool PRIVATE
|
||||
${PROJECT_SOURCE_DIR}/libs/Win/SDL2/include
|
||||
${PROJECT_SOURCE_DIR}/libs/Win/SDL_image/include
|
||||
)
|
||||
target_link_directories(TomSpriteAssetTool PRIVATE
|
||||
target_link_directories(TomAtlasTool PRIVATE
|
||||
${SDL2_LIB_DIR}
|
||||
${SDL2_IMAGE_LIB_DIR}
|
||||
)
|
||||
target_link_libraries(TomSpriteAssetTool PRIVATE SDL2main SDL2 SDL2_image)
|
||||
target_link_libraries(TomAtlasTool PRIVATE SDL2main SDL2 SDL2_image)
|
||||
|
||||
add_custom_command(TARGET TomSpriteAssetTool POST_BUILD
|
||||
add_custom_command(TARGET TomAtlasTool POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
"${SDL2_DLL}"
|
||||
"$<TARGET_FILE_DIR:TomSpriteAssetTool>"
|
||||
"$<TARGET_FILE_DIR:TomAtlasTool>"
|
||||
)
|
||||
add_custom_command(TARGET TomSpriteAssetTool POST_BUILD
|
||||
add_custom_command(TARGET TomAtlasTool POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
"${SDL2_IMAGE_DLL}"
|
||||
"$<TARGET_FILE_DIR:TomSpriteAssetTool>"
|
||||
"$<TARGET_FILE_DIR:TomAtlasTool>"
|
||||
)
|
||||
elseif(SDL2_image_FOUND)
|
||||
add_executable(TomSpriteAssetTool EXCLUDE_FROM_ALL ${SPRITE_ASSET_TOOL_SOURCES})
|
||||
set_target_properties(TomSpriteAssetTool PROPERTIES OUTPUT_NAME SpriteAssetTool)
|
||||
target_include_directories(TomSpriteAssetTool PRIVATE
|
||||
${PROJECT_SOURCE_DIR}/src/Core/Asset
|
||||
${PROJECT_SOURCE_DIR}/src/Core/RenderData
|
||||
add_executable(TomAtlasTool EXCLUDE_FROM_ALL ${TOM_ATLAS_TOOL_SOURCES})
|
||||
target_include_directories(TomAtlasTool PRIVATE
|
||||
${SDL2_image_INCLUDE_DIRS}
|
||||
${SDL2_IMAGE_INCLUDE_DIRS}
|
||||
)
|
||||
target_link_libraries(TomSpriteAssetTool PRIVATE SDL2::SDL2)
|
||||
target_link_libraries(TomAtlasTool PRIVATE SDL2::SDL2)
|
||||
|
||||
if(TARGET SDL2_image::SDL2_image)
|
||||
target_link_libraries(TomSpriteAssetTool PRIVATE SDL2_image::SDL2_image)
|
||||
target_link_libraries(TomAtlasTool PRIVATE SDL2_image::SDL2_image)
|
||||
else()
|
||||
target_link_libraries(TomSpriteAssetTool PRIVATE
|
||||
target_link_libraries(TomAtlasTool PRIVATE
|
||||
${SDL2_image_LIBRARIES}
|
||||
${SDL2_IMAGE_LIBRARIES}
|
||||
)
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "SpriteAssetTool disabled: SDL2_image was not found")
|
||||
message(STATUS "TomAtlasTool disabled: SDL2_image was not found")
|
||||
endif()
|
||||
|
||||
if(TARGET TomSpriteAssetTool)
|
||||
add_custom_target(ConvertTomSprites
|
||||
COMMAND $<TARGET_FILE:TomSpriteAssetTool>
|
||||
--batch
|
||||
"src/Apps/Game/assets/raw"
|
||||
"src/Apps/Game/assets/sprites"
|
||||
--preset tom-800x480
|
||||
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}"
|
||||
DEPENDS TomSpriteAssetTool
|
||||
COMMENT "Converting Tom PNG assets to board-ready .sprite files"
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
if(TARGET TomAtlasTool)
|
||||
if(NOT CMAKE_CROSSCOMPILING)
|
||||
add_custom_target(GenerateTomAtlasHeader
|
||||
COMMAND $<TARGET_FILE:TomSpriteAssetTool>
|
||||
--atlas-header
|
||||
"src/Apps/Game/assets/raw"
|
||||
"src/Apps/Game/generated/tom_atlas.h"
|
||||
tom_atlas
|
||||
--atlas-width
|
||||
1024
|
||||
--preset
|
||||
tom-800x480
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory
|
||||
"src/Apps/Game/generated"
|
||||
COMMAND $<TARGET_FILE:TomAtlasTool>
|
||||
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}"
|
||||
DEPENDS TomSpriteAssetTool
|
||||
DEPENDS TomAtlasTool
|
||||
COMMENT "Generating Tom atlas header from PNG assets"
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
if(NOT CMAKE_CROSSCOMPILING)
|
||||
add_dependencies(${TOM_GAME_TARGET} GenerateTomAtlasHeader)
|
||||
else()
|
||||
add_custom_target(GenerateTomAtlasHeader
|
||||
COMMAND ${CMAKE_COMMAND} -E echo
|
||||
"GenerateTomAtlasHeader is host-only. Run it in build-win or build-linux before cross compiling."
|
||||
VERBATIM
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -1 +0,0 @@
|
|||
|
||||
|
|
@ -1 +0,0 @@
|
|||
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1 +0,0 @@
|
|||
|
||||
|
|
@ -1,234 +0,0 @@
|
|||
#include <SDL.h>
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "FrameBuffer.h"
|
||||
#include "SDLDisplay.h"
|
||||
#include "SpriteAssetLoader.h"
|
||||
#include "SpriteRasterizer.h"
|
||||
#include "Image.h"
|
||||
#include "Sprite.h"
|
||||
#include "SpriteAnimator.h"
|
||||
#include "AnimationSystem.h"
|
||||
|
||||
static std::string FindAssetPath(const std::string& fileName)
|
||||
{
|
||||
const char* roots[] = {
|
||||
"src/Apps/Game/assets/sprites/",
|
||||
"../src/Apps/Game/assets/sprites/",
|
||||
"../../src/Apps/Game/assets/sprites/",
|
||||
"../../../src/Apps/Game/assets/sprites/"
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < sizeof(roots) / sizeof(roots[0]); ++i)
|
||||
{
|
||||
const std::string path = std::string(roots[i]) + fileName;
|
||||
std::ifstream file(path.c_str(), std::ios::binary);
|
||||
if (file.good())
|
||||
{
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
return std::string("src/Apps/Game/assets/sprites/") + fileName;
|
||||
}
|
||||
|
||||
static bool LoadSpriteImage(const std::string& fileName, RenderData::Image& image)
|
||||
{
|
||||
const std::string path = FindAssetPath(fileName);
|
||||
if (!Asset::SpriteAssetLoader::Load(path, image))
|
||||
{
|
||||
std::cerr << "Load sprite failed: " << path << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static RenderData::Image ResizeImageNearest(const RenderData::Image& source, int32_t width, int32_t height)
|
||||
{
|
||||
if (!source.is_valid() || width <= 0 || height <= 0)
|
||||
{
|
||||
return RenderData::Image();
|
||||
}
|
||||
|
||||
RenderData::Image result(width, height);
|
||||
for (int32_t y = 0; y < height; ++y)
|
||||
{
|
||||
const int32_t sourceY = y * source.get_height() / height;
|
||||
for (int32_t x = 0; x < width; ++x)
|
||||
{
|
||||
const int32_t sourceX = x * source.get_width() / width;
|
||||
result.set_pixel(x, y, source.get_pixel_fast(sourceX, sourceY));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static RenderData::Image ResizeImageToFit(const RenderData::Image& source, int32_t maxWidth, int32_t maxHeight)
|
||||
{
|
||||
if (!source.is_valid() || maxWidth <= 0 || maxHeight <= 0)
|
||||
{
|
||||
return RenderData::Image();
|
||||
}
|
||||
|
||||
const float scaleX = static_cast<float>(maxWidth) / source.get_width();
|
||||
const float scaleY = static_cast<float>(maxHeight) / source.get_height();
|
||||
const float scale = std::min(scaleX, scaleY);
|
||||
|
||||
const int32_t width = std::max(1, static_cast<int32_t>(source.get_width() * scale));
|
||||
const int32_t height = std::max(1, static_cast<int32_t>(source.get_height() * scale));
|
||||
return ResizeImageNearest(source, width, height);
|
||||
}
|
||||
|
||||
static bool LoadFrames(
|
||||
const std::vector<std::string>& fileNames,
|
||||
std::vector<RenderData::Image>& images,
|
||||
std::vector<RenderData::Sprite>& frames,
|
||||
int32_t maxFrameWidth,
|
||||
int32_t maxFrameHeight)
|
||||
{
|
||||
images.clear();
|
||||
frames.clear();
|
||||
images.reserve(fileNames.size());
|
||||
|
||||
for (size_t i = 0; i < fileNames.size(); ++i)
|
||||
{
|
||||
RenderData::Image image;
|
||||
if (!LoadSpriteImage(fileNames[i], image))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
images.push_back(ResizeImageToFit(image, maxFrameWidth, maxFrameHeight));
|
||||
}
|
||||
|
||||
frames.reserve(images.size());
|
||||
for (size_t i = 0; i < images.size(); ++i)
|
||||
{
|
||||
frames.push_back(RenderData::Sprite(&images[i]));
|
||||
}
|
||||
|
||||
return !frames.empty();
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
const int32_t width = 800;
|
||||
const int32_t height = 480;
|
||||
|
||||
Platform::SDLDisplay display;
|
||||
if (!display.init(width, height))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
RenderData::Image originalBackground;
|
||||
if (!LoadSpriteImage("background.sprite", originalBackground))
|
||||
{
|
||||
display.shutdown();
|
||||
return -1;
|
||||
}
|
||||
RenderData::Image background = ResizeImageNearest(originalBackground, width, height);
|
||||
|
||||
std::vector<std::string> frameFiles;
|
||||
frameFiles.push_back("Tom-stand.sprite");
|
||||
frameFiles.push_back("Tom-listhen.sprite");
|
||||
frameFiles.push_back("Tom-openmouse1.sprite");
|
||||
frameFiles.push_back("Tom-openmouse2.sprite");
|
||||
frameFiles.push_back("Tom-say1.sprite");
|
||||
frameFiles.push_back("Tom-say2.sprite");
|
||||
frameFiles.push_back("Tom-say3.sprite");
|
||||
frameFiles.push_back("Tom-say4.sprite");
|
||||
|
||||
std::vector<RenderData::Image> frameImages;
|
||||
std::vector<RenderData::Sprite> frames;
|
||||
if (!LoadFrames(frameFiles, frameImages, frames, width * 7 / 10, height * 9 / 10))
|
||||
{
|
||||
display.shutdown();
|
||||
return -1;
|
||||
}
|
||||
|
||||
Core::FrameBuffer frameBuffer(width, height);
|
||||
Rasterizer::SpriteRasterizer spriteRasterizer(&frameBuffer);
|
||||
|
||||
Game::SpriteAnimator tomAnimator(frames, 0.12f, true);
|
||||
Game::AnimationSystem animationSystem;
|
||||
|
||||
const RenderData::Sprite* firstFrame = tomAnimator.get_current_sprite();
|
||||
const int32_t tomX = firstFrame ? (width - firstFrame->width) / 2 : 0;
|
||||
const int32_t tomY = firstFrame ? height - firstFrame->height - 20 : 0;
|
||||
animationSystem.add(&tomAnimator, tomX, tomY);
|
||||
|
||||
std::cout << "Sprite animation test" << std::endl;
|
||||
std::cout << "Space: play/pause, R: reset, Esc: quit" << std::endl;
|
||||
std::cout << "Background: " << originalBackground.get_width() << "x" << originalBackground.get_height()
|
||||
<< " -> " << background.get_width() << "x" << background.get_height() << std::endl;
|
||||
std::cout << "Tom frame 0: " << frameImages[0].get_width() << "x" << frameImages[0].get_height() << std::endl;
|
||||
|
||||
bool shouldQuit = false;
|
||||
uint32_t lastTime = display.get_time_ms();
|
||||
while (!shouldQuit)
|
||||
{
|
||||
SDL_Event event;
|
||||
while (SDL_PollEvent(&event))
|
||||
{
|
||||
if (event.type == SDL_QUIT)
|
||||
{
|
||||
shouldQuit = true;
|
||||
}
|
||||
else if (event.type == SDL_KEYDOWN)
|
||||
{
|
||||
if (event.key.repeat != 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (event.key.keysym.sym == SDLK_ESCAPE)
|
||||
{
|
||||
shouldQuit = true;
|
||||
}
|
||||
else if (event.key.keysym.sym == SDLK_SPACE)
|
||||
{
|
||||
if (tomAnimator.is_playing())
|
||||
{
|
||||
tomAnimator.stop();
|
||||
}
|
||||
else
|
||||
{
|
||||
tomAnimator.play();
|
||||
}
|
||||
}
|
||||
else if (event.key.keysym.sym == SDLK_r)
|
||||
{
|
||||
tomAnimator.reset();
|
||||
tomAnimator.play();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const uint32_t currentTime = display.get_time_ms();
|
||||
const float deltaTime = static_cast<float>(currentTime - lastTime) * 0.001f;
|
||||
lastTime = currentTime;
|
||||
|
||||
animationSystem.update(deltaTime);
|
||||
|
||||
frameBuffer.clear(RenderData::Color(18, 18, 24, 255));
|
||||
spriteRasterizer.DrawImage(background, 0, 0);
|
||||
animationSystem.draw(spriteRasterizer);
|
||||
display.present(&frameBuffer);
|
||||
|
||||
SDL_Delay(16);
|
||||
}
|
||||
|
||||
display.shutdown();
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
|
||||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue