This commit is contained in:
HP 2026-06-07 18:31:58 +08:00
parent 8d8c9ed9d1
commit b26a6a78fa
5 changed files with 109312 additions and 129 deletions

View File

@ -67,9 +67,9 @@ cmake --build build-win --config Release --target IMX6U-Game
cmake --build build-win --config Release --target IMX6U-Demo cmake --build build-win --config Release --target IMX6U-Demo
``` ```
如果 Tom 的 `.sprite` 资源缺失,先运行资源转换目标 如果修改了 Tom 的原始 PNG 资源,先重新生成 atlas 头文件
```bash ```bash
cmake --build build-win --config Release --target ConvertTomSprites cmake --build build-win --config Release --target GenerateTomAtlasHeader
``` ```
运行: 运行:
@ -106,9 +106,9 @@ cmake --build build-linux --target IMX6U-Game
cmake --build build-linux --target IMX6U-Demo cmake --build build-linux --target IMX6U-Demo
``` ```
如果 Tom 的 `.sprite` 资源缺失,先运行资源转换目标 如果修改了 Tom 的原始 PNG 资源,先重新生成 atlas 头文件
```bash ```bash
cmake --build build-linux --target ConvertTomSprites cmake --build build-linux --target GenerateTomAtlasHeader
``` ```
运行: 运行:
@ -193,6 +193,20 @@ test_sprite_pixels
透明像素仍保留 alpha当前 demo 通过 `RenderData::Image(..., 0x00000000)` 把全透明像素作为 color key 跳过。 透明像素仍保留 alpha当前 demo 通过 `RenderData::Image(..., 0x00000000)` 把全透明像素作为 color key 跳过。
Tom 游戏资源使用 `SpriteAssetTool --atlas-header` 从原始 PNG 一步生成 atlas 头文件:
```bash
cmake --build build-win --config Release --target GenerateTomAtlasHeader
```
生成结果位于:
```text
src/Apps/Game/generated/tom_atlas.h
```
该头文件包含 `tom_atlas_pixels` 和每张图的 `RenderData::SpriteRegion`,因此板端运行 Tom 游戏时不需要额外部署 `.sprite` 文件。
### Bitmap Font 转换 ### Bitmap Font 转换
像素字体图集使用 `tools/gen_font_atlas.py` 生成: 像素字体图集使用 `tools/gen_font_atlas.py` 生成:

View File

@ -2,6 +2,11 @@ set(TOM_GAME_TARGET IMX6U-Game)
add_executable(${TOM_GAME_TARGET} add_executable(${TOM_GAME_TARGET}
Main.cpp Main.cpp
generated/tom_atlas.h
)
target_include_directories(${TOM_GAME_TARGET} PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/generated
) )
imx6u_configure_app_target(${TOM_GAME_TARGET}) imx6u_configure_app_target(${TOM_GAME_TARGET})
@ -13,7 +18,7 @@ if(NOT USE_FRAMEBUFFER)
) )
if(WIN32) if(WIN32)
add_executable(TomSpriteAssetTool ${SPRITE_ASSET_TOOL_SOURCES}) add_executable(TomSpriteAssetTool EXCLUDE_FROM_ALL ${SPRITE_ASSET_TOOL_SOURCES})
set_target_properties(TomSpriteAssetTool PROPERTIES OUTPUT_NAME SpriteAssetTool) set_target_properties(TomSpriteAssetTool PROPERTIES OUTPUT_NAME SpriteAssetTool)
target_include_directories(TomSpriteAssetTool PRIVATE target_include_directories(TomSpriteAssetTool PRIVATE
${PROJECT_SOURCE_DIR}/src/Core/Asset ${PROJECT_SOURCE_DIR}/src/Core/Asset
@ -38,7 +43,7 @@ if(NOT USE_FRAMEBUFFER)
"$<TARGET_FILE_DIR:TomSpriteAssetTool>" "$<TARGET_FILE_DIR:TomSpriteAssetTool>"
) )
elseif(SDL2_image_FOUND) elseif(SDL2_image_FOUND)
add_executable(TomSpriteAssetTool ${SPRITE_ASSET_TOOL_SOURCES}) add_executable(TomSpriteAssetTool EXCLUDE_FROM_ALL ${SPRITE_ASSET_TOOL_SOURCES})
set_target_properties(TomSpriteAssetTool PROPERTIES OUTPUT_NAME SpriteAssetTool) set_target_properties(TomSpriteAssetTool PROPERTIES OUTPUT_NAME SpriteAssetTool)
target_include_directories(TomSpriteAssetTool PRIVATE target_include_directories(TomSpriteAssetTool PRIVATE
${PROJECT_SOURCE_DIR}/src/Core/Asset ${PROJECT_SOURCE_DIR}/src/Core/Asset
@ -64,13 +69,33 @@ if(NOT USE_FRAMEBUFFER)
add_custom_target(ConvertTomSprites add_custom_target(ConvertTomSprites
COMMAND $<TARGET_FILE:TomSpriteAssetTool> COMMAND $<TARGET_FILE:TomSpriteAssetTool>
--batch --batch
"${CMAKE_CURRENT_SOURCE_DIR}/assets/raw" "src/Apps/Game/assets/raw"
"${CMAKE_CURRENT_SOURCE_DIR}/assets/sprites" "src/Apps/Game/assets/sprites"
--preset tom-800x480 --preset tom-800x480
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}" WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}"
DEPENDS TomSpriteAssetTool DEPENDS TomSpriteAssetTool
COMMENT "Converting Tom PNG assets to board-ready .sprite files" COMMENT "Converting Tom PNG assets to board-ready .sprite files"
VERBATIM VERBATIM
) )
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
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}"
DEPENDS TomSpriteAssetTool
COMMENT "Generating Tom atlas header from PNG assets"
VERBATIM
)
if(NOT CMAKE_CROSSCOMPILING)
add_dependencies(${TOM_GAME_TARGET} GenerateTomAtlasHeader)
endif()
endif() endif()
endif() endif()

View File

@ -3,19 +3,15 @@
#include <cstdint> #include <cstdint>
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
#include <fstream>
#include <iostream> #include <iostream>
#include <string>
#include <thread> #include <thread>
#include <vector>
#include "Color.h" #include "Color.h"
#include "Display.h" #include "Display.h"
#include "DrawContext.h" #include "DrawContext.h"
#include "Image.h"
#include "SpriteAssetLoader.h"
#include "TimeSource.h" #include "TimeSource.h"
#include "Timer.h" #include "Timer.h"
#include "tom_atlas.h"
#ifdef USE_FRAMEBUFFER #ifdef USE_FRAMEBUFFER
#include "FBDisplay.h" #include "FBDisplay.h"
@ -40,17 +36,6 @@ namespace
} }
}; };
struct SpriteImage
{
std::vector<uint32_t> pixels;
RenderData::Image image;
bool is_valid() const
{
return image.pixels != nullptr && image.width > 0 && image.height > 0;
}
};
static Platform::IDisplay* CreateDisplay() static Platform::IDisplay* CreateDisplay()
{ {
#ifdef USE_FRAMEBUFFER #ifdef USE_FRAMEBUFFER
@ -124,89 +109,28 @@ namespace
} }
} }
static std::string FindSpriteAssetPath(const std::string& file_name) static const RenderData::SpriteRegion& SelectTomFrame(
{
const char* roots[] = {
"src/Apps/Game/assets/sprites/",
"../src/Apps/Game/assets/sprites/",
"../../src/Apps/Game/assets/sprites/",
"../../../src/Apps/Game/assets/sprites/",
"/usr/local/share/imx6u-game/sprites/"
};
for (size_t i = 0; i < sizeof(roots) / sizeof(roots[0]); ++i)
{
const std::string path = std::string(roots[i]) + file_name;
std::ifstream file(path.c_str(), std::ios::binary);
if (file.good())
{
return path;
}
}
return std::string("src/Apps/Game/assets/sprites/") + file_name;
}
static void EnableAlphaColorKey(SpriteImage& sprite)
{
for (size_t i = 0; i < sprite.pixels.size(); ++i)
{
if ((sprite.pixels[i] & 0xFFu) == 0u)
{
sprite.pixels[i] = 0x00000000u;
}
}
sprite.image = RenderData::Image(
sprite.pixels.data(),
sprite.image.width,
sprite.image.height,
0x00000000u);
}
static bool LoadSpriteAsset(const std::string& file_name, SpriteImage& sprite, bool use_alpha_key)
{
const std::string path = FindSpriteAssetPath(file_name);
if (!Asset::SpriteAssetLoader::Load(path, sprite.pixels, sprite.image))
{
std::cerr << "Load sprite asset failed: " << path << std::endl;
return false;
}
if (use_alpha_key)
{
EnableAlphaColorKey(sprite);
}
return sprite.is_valid();
}
static const RenderData::Image& SelectTomFrame(
uint32_t animation_time_ms, uint32_t animation_time_ms,
const SpriteImage& stand, const RenderData::SpriteRegion* const* speaking_frames,
const SpriteImage& listen,
const SpriteImage* const* speaking_frames,
size_t speaking_frame_count) size_t speaking_frame_count)
{ {
const uint32_t phase = (animation_time_ms / 1000u) % 6u; const uint32_t phase = (animation_time_ms / 1000u) % 6u;
if (phase < 2u) if (phase < 2u)
{ {
return stand.image; return TomAtlas::tom_stand;
} }
if (phase < 3u) if (phase < 3u)
{ {
return listen.image; return TomAtlas::tom_listhen;
} }
const size_t frame_index = (animation_time_ms / 120u) % speaking_frame_count; const size_t frame_index = (animation_time_ms / 120u) % speaking_frame_count;
return speaking_frames[frame_index]->image; return *speaking_frames[frame_index];
} }
} }
int main(int argc, char* argv[]) int main(int argc, char* argv[])
{ {
std::cout << "[INFO] Start make tom game." << std::endl;
const ProgramOptions options = ParseProgramOptions(argc, argv); const ProgramOptions options = ParseProgramOptions(argc, argv);
if (options.show_help) if (options.show_help)
{ {
@ -221,37 +145,13 @@ int main(int argc, char* argv[])
return -1; return -1;
} }
SpriteImage background; const RenderData::SpriteRegion* speaking_frames[] = {
SpriteImage tom_stand; &TomAtlas::tom_say1,
SpriteImage tom_listen; &TomAtlas::tom_say2,
SpriteImage tom_say1; &TomAtlas::tom_say3,
SpriteImage tom_say2; &TomAtlas::tom_say4,
SpriteImage tom_say3; &TomAtlas::tom_say3,
SpriteImage tom_say4; &TomAtlas::tom_say2
SpriteImage record_button;
if (!LoadSpriteAsset("background.sprite", background, false) ||
!LoadSpriteAsset("Tom-stand.sprite", tom_stand, true) ||
!LoadSpriteAsset("Tom-listhen.sprite", tom_listen, true) ||
!LoadSpriteAsset("Tom-say1.sprite", tom_say1, true) ||
!LoadSpriteAsset("Tom-say2.sprite", tom_say2, true) ||
!LoadSpriteAsset("Tom-say3.sprite", tom_say3, true) ||
!LoadSpriteAsset("Tom-say4.sprite", tom_say4, true) ||
!LoadSpriteAsset("ui-record.sprite", record_button, true))
{
std::cerr << "Missing Tom sprite assets. Run the ConvertTomSprites target first.\n";
display->shutdown();
delete display;
return -1;
}
const SpriteImage* speaking_frames[] = {
&tom_say1,
&tom_say2,
&tom_say3,
&tom_say4,
&tom_say3,
&tom_say2
}; };
const size_t speaking_frame_count = sizeof(speaking_frames) / sizeof(speaking_frames[0]); const size_t speaking_frame_count = sizeof(speaking_frames) / sizeof(speaking_frames[0]);
@ -276,22 +176,20 @@ int main(int argc, char* argv[])
} }
ctx.clear(RenderData::Color(18, 18, 24, 255)); ctx.clear(RenderData::Color(18, 18, 24, 255));
ctx.draw_sprite(0, 0, background.image); ctx.draw_sprite_region(0, 0, TomAtlas::background);
const RenderData::Image& tom = SelectTomFrame( const RenderData::SpriteRegion& tom = SelectTomFrame(
animation_time_ms, animation_time_ms,
tom_stand,
tom_listen,
speaking_frames, speaking_frames,
speaking_frame_count); speaking_frame_count);
const int32_t tom_x = (ScreenWidth - tom.width) / 2; const int32_t tom_x = (ScreenWidth - tom.width) / 2;
const int32_t tom_y = std::max(0, ScreenHeight - tom.height - 72); const int32_t tom_y = std::max(0, ScreenHeight - tom.height - 72);
ctx.draw_sprite(tom_x, tom_y, tom); ctx.draw_sprite_region(tom_x, tom_y, tom);
const int32_t button_x = (ScreenWidth - record_button.image.width) / 2; const int32_t button_x = (ScreenWidth - TomAtlas::ui_record.width) / 2;
const int32_t button_y = ScreenHeight - record_button.image.height - 16; const int32_t button_y = ScreenHeight - TomAtlas::ui_record.height - 16;
ctx.draw_sprite(button_x, button_y, record_button.image); ctx.draw_sprite_region(button_x, button_y, TomAtlas::ui_record);
ctx.present(display); ctx.present(display);
SleepRemainingFrameTime(timer, time_source); SleepRemainingFrameTime(timer, time_source);

File diff suppressed because it is too large Load Diff

View File

@ -6,6 +6,8 @@
#include <climits> #include <climits>
#include <cstdint> #include <cstdint>
#include <cstdlib> #include <cstdlib>
#include <fstream>
#include <iomanip>
#include <iostream> #include <iostream>
#include <string> #include <string>
#include <vector> #include <vector>
@ -58,6 +60,34 @@ namespace
} }
}; };
struct AtlasEntry
{
std::string file_name;
std::string identifier;
int32_t x;
int32_t y;
int32_t width;
int32_t height;
std::vector<uint32_t> pixels;
AtlasEntry()
: x(0), y(0), width(0), height(0)
{
}
};
struct AtlasHeaderOptions
{
int32_t atlas_width;
TransformOptions transform;
AtlasHeaderOptions()
: atlas_width(1024),
transform()
{
}
};
static bool IsPathSeparator(char value) static bool IsPathSeparator(char value)
{ {
return value == '/' || value == '\\'; return value == '/' || value == '\\';
@ -124,6 +154,42 @@ namespace
return fileName + Asset::SpriteAssetLoader::GetFileExtension(); return fileName + Asset::SpriteAssetLoader::GetFileExtension();
} }
static std::string RemoveExtension(const std::string& fileName)
{
const size_t slash = fileName.find_last_of("/\\");
const size_t dot = fileName.find_last_of('.');
if (dot != std::string::npos && (slash == std::string::npos || dot > slash))
{
return fileName.substr(0, dot);
}
return fileName;
}
static std::string ToIdentifier(const std::string& text)
{
std::string result;
for (size_t i = 0; i < text.size(); ++i)
{
const unsigned char ch = static_cast<unsigned char>(text[i]);
if (std::isalnum(ch))
{
result.push_back(static_cast<char>(std::tolower(ch)));
}
else
{
result.push_back('_');
}
}
if (result.empty() || !(std::isalpha(static_cast<unsigned char>(result[0])) || result[0] == '_'))
{
result.insert(result.begin(), '_');
}
return result;
}
static bool HasPngExtension(const std::string& path) static bool HasPngExtension(const std::string& path)
{ {
const std::string lower = ToLower(path); const std::string lower = ToLower(path);
@ -428,6 +494,19 @@ static bool ApplyTransform(const std::string& inputPath, const TransformOptions&
return false; return false;
} }
static void NormalizeTransparentPixels(LoadedImage& image)
{
for (size_t i = 0; i < image.pixels.size(); ++i)
{
if ((image.pixels[i] & 0xFFu) == 0u)
{
image.pixels[i] = 0x00000000u;
}
}
image.image = RenderData::Image(image.pixels.data(), image.image.width, image.image.height);
}
static bool ConvertFile( static bool ConvertFile(
const std::string& inputPath, const std::string& inputPath,
const std::string& outputPath, const std::string& outputPath,
@ -493,6 +572,322 @@ static bool ConvertBatch(
return successCount == files.size(); return successCount == files.size();
} }
static bool ContainsAtlasIdentifier(const std::vector<AtlasEntry>& entries, const std::string& identifier)
{
for (size_t i = 0; i < entries.size(); ++i)
{
if (entries[i].identifier == identifier)
{
return true;
}
}
return false;
}
static bool LoadAtlasEntry(
const std::string& inputDirectory,
const std::string& fileName,
const TransformOptions& transformOptions,
AtlasEntry& entry)
{
const std::string inputPath = JoinPath(inputDirectory, fileName);
LoadedImage image;
if (!LoadPngImage(inputPath, image))
{
return false;
}
if (!ApplyTransform(inputPath, transformOptions, image))
{
std::cerr << "Image transform failed: " << inputPath << std::endl;
return false;
}
NormalizeTransparentPixels(image);
entry.file_name = fileName;
entry.identifier = ToIdentifier(RemoveExtension(fileName));
entry.width = image.image.width;
entry.height = image.image.height;
entry.pixels = image.pixels;
return entry.width > 0 && entry.height > 0 && !entry.pixels.empty();
}
static bool PackAtlasEntries(std::vector<AtlasEntry>& entries, int32_t atlasWidth, int32_t& atlasHeight)
{
if (atlasWidth <= 0 || entries.empty())
{
return false;
}
const int32_t padding = 1;
int32_t cursorX = 0;
int32_t cursorY = 0;
int32_t rowHeight = 0;
for (size_t i = 0; i < entries.size(); ++i)
{
AtlasEntry& entry = entries[i];
if (entry.width > atlasWidth)
{
std::cerr << "Atlas width " << atlasWidth << " is smaller than " << entry.file_name
<< " width " << entry.width << std::endl;
return false;
}
if (cursorX > 0 && cursorX + entry.width > atlasWidth)
{
cursorY += rowHeight + padding;
cursorX = 0;
rowHeight = 0;
}
entry.x = cursorX;
entry.y = cursorY;
cursorX += entry.width + padding;
rowHeight = std::max(rowHeight, entry.height);
}
atlasHeight = cursorY + rowHeight;
return atlasHeight > 0;
}
static void WriteHexPixel(std::ofstream& file, uint32_t pixel)
{
file << "0x"
<< std::uppercase
<< std::hex
<< std::setw(8)
<< std::setfill('0')
<< pixel
<< std::dec
<< std::nouppercase
<< std::setfill(' ');
}
static bool WriteAtlasHeader(
const std::string& outputHeaderPath,
const std::string& variablePrefix,
const std::vector<AtlasEntry>& entries,
const std::vector<uint32_t>& atlasPixels,
int32_t atlasWidth,
int32_t atlasHeight)
{
if (!EnsureParentDirectory(outputHeaderPath))
{
return false;
}
std::ofstream file(outputHeaderPath.c_str());
if (!file.good())
{
std::cerr << "Open header failed: " << outputHeaderPath << std::endl;
return false;
}
file << "// Auto-generated by SpriteAssetTool --atlas-header\n";
file << "#pragma once\n";
file << "#include <cstdint>\n";
file << "#include \"Image.h\"\n";
file << "#include \"SpriteRegion.h\"\n\n";
file << "namespace TomAtlas\n";
file << "{\n";
file << "\tstatic const int32_t " << variablePrefix << "_width = " << atlasWidth << ";\n";
file << "\tstatic const int32_t " << variablePrefix << "_height = " << atlasHeight << ";\n\n";
file << "\tstatic const uint32_t " << variablePrefix << "_pixels[] = {\n";
for (size_t i = 0; i < atlasPixels.size(); i += 12)
{
file << "\t\t";
const size_t end = std::min(i + 12, atlasPixels.size());
for (size_t j = i; j < end; ++j)
{
WriteHexPixel(file, atlasPixels[j]);
file << ", ";
}
file << "\n";
}
file << "\t};\n\n";
file << "\tstatic const RenderData::Image image("
<< variablePrefix << "_pixels, "
<< variablePrefix << "_width, "
<< variablePrefix << "_height, 0x00000000u);\n\n";
for (size_t i = 0; i < entries.size(); ++i)
{
const AtlasEntry& entry = entries[i];
file << "\tstatic const RenderData::SpriteRegion " << entry.identifier
<< "(&image, "
<< entry.x << ", "
<< entry.y << ", "
<< entry.width << ", "
<< entry.height << ");\n";
}
file << "}\n";
return file.good();
}
static bool ConvertAtlasHeader(
const std::string& inputDirectory,
const std::string& outputHeaderPath,
const std::string& variablePrefix,
const AtlasHeaderOptions& options)
{
std::vector<std::string> files;
if (!ListPngFiles(inputDirectory, files) || files.empty())
{
std::cerr << "No PNG files found: " << inputDirectory << std::endl;
return false;
}
std::vector<AtlasEntry> entries;
for (size_t i = 0; i < files.size(); ++i)
{
AtlasEntry entry;
if (!LoadAtlasEntry(inputDirectory, files[i], options.transform, entry))
{
return false;
}
if (ContainsAtlasIdentifier(entries, entry.identifier))
{
std::cerr << "Duplicate atlas identifier: " << entry.identifier << std::endl;
return false;
}
entries.push_back(entry);
}
int32_t atlasHeight = 0;
if (!PackAtlasEntries(entries, options.atlas_width, atlasHeight))
{
return false;
}
std::vector<uint32_t> atlasPixels(static_cast<size_t>(options.atlas_width) * static_cast<size_t>(atlasHeight), 0);
for (size_t i = 0; i < entries.size(); ++i)
{
const AtlasEntry& entry = entries[i];
for (int32_t y = 0; y < entry.height; ++y)
{
const size_t dstOffset =
static_cast<size_t>(entry.y + y) * static_cast<size_t>(options.atlas_width) +
static_cast<size_t>(entry.x);
const size_t srcOffset = static_cast<size_t>(y) * static_cast<size_t>(entry.width);
std::copy(
entry.pixels.begin() + static_cast<std::vector<uint32_t>::difference_type>(srcOffset),
entry.pixels.begin() + static_cast<std::vector<uint32_t>::difference_type>(srcOffset + static_cast<size_t>(entry.width)),
atlasPixels.begin() + static_cast<std::vector<uint32_t>::difference_type>(dstOffset));
}
}
if (!WriteAtlasHeader(outputHeaderPath, variablePrefix, entries, atlasPixels, options.atlas_width, atlasHeight))
{
return false;
}
std::cout << "Generated atlas header: " << outputHeaderPath
<< " (" << options.atlas_width << "x" << atlasHeight
<< ", " << entries.size() << " regions)" << std::endl;
return true;
}
static bool ParseTransformModeAt(
int argc,
char* argv[],
int& index,
TransformOptions& options)
{
const std::string mode = argv[index];
if (mode == "--resize" || mode == "--fit")
{
if (index + 2 >= argc)
{
return false;
}
int32_t width = 0;
int32_t height = 0;
if (!ParsePositiveInt(argv[index + 1], width) ||
!ParsePositiveInt(argv[index + 2], height))
{
return false;
}
options.mode = mode == "--resize" ? TransformMode::Resize : TransformMode::Fit;
options.width = width;
options.height = height;
index += 3;
return true;
}
if (mode == "--preset")
{
if (index + 1 >= argc)
{
return false;
}
const std::string presetName = argv[index + 1];
if (presetName != "tom-800x480")
{
std::cerr << "Unknown preset: " << presetName << std::endl;
return false;
}
options.mode = TransformMode::Tom800x480Preset;
index += 2;
return true;
}
return false;
}
static bool ParseAtlasHeaderOptions(
int argc,
char* argv[],
int startIndex,
AtlasHeaderOptions& options)
{
options = AtlasHeaderOptions();
int index = startIndex;
bool hasTransform = false;
while (index < argc)
{
const std::string option = argv[index];
if (option == "--atlas-width")
{
if (index + 1 >= argc || !ParsePositiveInt(argv[index + 1], options.atlas_width))
{
return false;
}
index += 2;
continue;
}
if (option == "--resize" || option == "--fit" || option == "--preset")
{
if (hasTransform)
{
std::cerr << "Only one transform option is allowed for atlas header generation." << std::endl;
return false;
}
if (!ParseTransformModeAt(argc, argv, index, options.transform))
{
return false;
}
hasTransform = true;
continue;
}
return false;
}
return true;
}
static bool ParseTransformOptions(int argc, char* argv[], int startIndex, TransformOptions& options) static bool ParseTransformOptions(int argc, char* argv[], int startIndex, TransformOptions& options)
{ {
if (startIndex >= argc) if (startIndex >= argc)
@ -553,6 +948,7 @@ static void PrintUsage()
std::cout << " SpriteAssetTool --batch inputDir outputDir [--resize width height]" << std::endl; std::cout << " SpriteAssetTool --batch inputDir outputDir [--resize width height]" << std::endl;
std::cout << " SpriteAssetTool --batch inputDir outputDir [--fit maxWidth maxHeight]" << std::endl; std::cout << " SpriteAssetTool --batch inputDir outputDir [--fit maxWidth maxHeight]" << std::endl;
std::cout << " SpriteAssetTool --batch src/Apps/Game/assets/raw src/Apps/Game/assets/sprites --preset tom-800x480" << std::endl; std::cout << " SpriteAssetTool --batch src/Apps/Game/assets/raw src/Apps/Game/assets/sprites --preset tom-800x480" << std::endl;
std::cout << " SpriteAssetTool --atlas-header inputDir output.h var_prefix [--atlas-width width] [--preset tom-800x480]" << std::endl;
} }
int main(int argc, char* argv[]) int main(int argc, char* argv[])
@ -584,6 +980,25 @@ int main(int argc, char* argv[])
ok = ConvertBatch(argv[2], argv[3], transformOptions); ok = ConvertBatch(argv[2], argv[3], transformOptions);
} }
else if (command == "--atlas-header")
{
if (argc < 5)
{
PrintUsage();
IMG_Quit();
return 1;
}
AtlasHeaderOptions atlasOptions;
if (!ParseAtlasHeaderOptions(argc, argv, 5, atlasOptions))
{
PrintUsage();
IMG_Quit();
return 1;
}
ok = ConvertAtlasHeader(argv[2], argv[3], argv[4], atlasOptions);
}
else else
{ {
if (!ParseTransformOptions(argc, argv, 3, transformOptions)) if (!ParseTransformOptions(argc, argv, 3, transformOptions))