Skip to content

Commit 4cec921

Browse files
committed
♻️ refactor asset image_parser -> image_codec; add gfx copy/blit commands and geometry math
- Rename image_parser to image_codec with expanded codec interface - Add copy texture region and blit commands across gfx backends (DX12, Vulkan, mock) - Add geometry.cppm with ray, plane, frustum, AABB, and intersection tests - Consolidate gfx tests (desc_hash_test -> gfx_test) and asset tests (parser_test -> codec_test) - Rename CLAUDE.md to AGENT.md Made-with: Cursor
1 parent 83ded53 commit 4cec921

41 files changed

Lines changed: 1357 additions & 214 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.cursor/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
plans/

.cursor/skills/commit/skill.md

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
---
2+
description: Using git to commit current changes
3+
match:
4+
- commit
5+
- git commit
6+
---
7+
8+
Using git to commit current changes with the Commit Rule following these steps, and as fast as possible without any explanations.
9+
10+
# Steps
11+
12+
1. According to the staged changes existing or not,
13+
- If there are staged changes, read the staged changes using `git --no-pager diff --cached`
14+
- If there are not staged changes, read the changes in the current branch using `git --no-pager diff`
15+
2. According to the changes content, the changes may be committed with one or more commits.
16+
3. For each commit, generate a commit message using the Commit Rule.
17+
4. Commit the changes with the generated commit message.
18+
19+
# Commit Rule
20+
21+
Follow the commit message format: `<gitmoji> <scope?> <message>`
22+
23+
## Format Structure
24+
25+
- **gitmoji**: A required gitmoji indicating the type of change
26+
- **scope**: Optional module/area name (e.g., `gfx:`, `asset:`, `render_graph:`)
27+
- **message**: Descriptive commit message in present tense
28+
29+
## Gitmoji Reference
30+
31+
| Gitmoji | Code | Description |
32+
| ------- | --------------- | ---------------------------------- |
33+
|| `:sparkles:` | New feature |
34+
| 🐛 | `:bug:` | Bug fix |
35+
| ♻️ | `:recycle:` | Refactor / restructure code |
36+
|| `:zap:` | Performance improvement |
37+
| ⬆️ | `:arrow_up:` | Upgrade dependencies |
38+
| 💚 | `:green_heart:` | Fix CI/build |
39+
| ✏️ | `:pencil2:` | Fix typo |
40+
| 🔧 | `:wrench:` | Configuration changes |
41+
| 📝 | `:memo:` | Documentation |
42+
| 🎨 | `:art:` | Improve structure / format of code |
43+
| 🔥 | `:fire:` | Remove code or files |
44+
| 🏗️ | `:building_construction:` | Architectural changes |
45+
46+
## Examples
47+
48+
- `✨ simple material viewer`
49+
- `🐛 fix uninitialized RT/DS on D3D12MA heaps with CREATE_NOT_ZEROED`
50+
- `♻️ modernize gfx: Vulkan submit2, std::ranges, scoped_lock; fix DX12 barriers`
51+
- `⬆️ update sdl2 to sdl3`
52+
- `♻️ refactor ecs interface`
53+
- `⚡ use GPU Upload Heap (DX12) and VK_EXT_host_image_copy (Vulkan) for resource init`
54+
55+
## Rules
56+
57+
1. Always start with an appropriate gitmoji
58+
2. Optionally include a scope prefix (e.g., `gfx:`, `asset:`) when the change affects a specific module
59+
3. Keep the message concise but descriptive
60+
4. Use present tense ("Add feature" not "Added feature")
61+
5. Lowercase the message (except proper nouns like API names, DX12, Vulkan, etc.)

CLAUDE.md renamed to AGENT.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
# CLAUDE.md
1+
# AGENT.md
22

3-
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
3+
This file provides guidance to AI coding agents when working with code in this repository.
44

55
## Build System
66

@@ -116,7 +116,7 @@ Following the layered architecture from *Game Engine Architecture* (Jason Gregor
116116

117117
**`hitagi/asset`**`AssetManager` for loading scenes, meshes, materials, textures. Assimp for model import; custom parsers for PNG, JPEG, BMP, TGA. Materials are defined with named instances (e.g., `"Phong"`).
118118

119-
**`hitagi/render`**`IRenderer` interface; `ForwardRenderer` is the concrete implementation. The renderer owns the swapchain and render graph instance.
119+
**`hitagi/render`**`IRenderer` interface with two concrete implementations: `ForwardRenderer` (single-pass Phong shading) and `DeferredRenderer` (G-Buffer MRT pass + fullscreen lighting pass). Both renderers own the swapchain and render graph instance. The engine defaults to `ForwardRenderer`; switch via `Engine::SetRenderer()`.
120120

121121
**`hitagi/engine`** — Top-level `Engine` class that composes everything. Initialized from `hitagi.json` at the project root. Usage pattern: construct `Engine`, call `engine.Tick()` in the game loop.
122122

hitagi/asset/asset.cppm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export import :meta_info;
1212
export import :transform;
1313
export import :scene;
1414
export import :mesh_factory;
15-
export import :image_parser;
15+
export import :image_codec;
1616
export import :scene_parser;
1717
export import :material_parser;
1818
export import :asset_manager;

hitagi/asset/asset_manager.cpp

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,15 @@ AssetManager::AssetManager(std::filesystem::path asset_base_path)
1919

2020
m_MaterialParser = std::make_shared<MaterialJSONParser>();
2121

22-
m_ImageParsers[ImageFormat::PNG] = std::make_shared<PngParser>(m_Logger);
23-
m_ImageParsers[ImageFormat::JPEG] = std::make_shared<JpegParser>(m_Logger);
24-
m_ImageParsers[ImageFormat::TGA] = std::make_shared<TgaParser>(m_Logger);
25-
m_ImageParsers[ImageFormat::BMP] = std::make_shared<BmpParser>(m_Logger);
22+
m_ImageDecoders[ImageFormat::PNG] = std::make_shared<PngDecoder>(m_Logger);
23+
m_ImageDecoders[ImageFormat::JPEG] = std::make_shared<JpegDecoder>(m_Logger);
24+
m_ImageDecoders[ImageFormat::TGA] = std::make_shared<TgaDecoder>(m_Logger);
25+
m_ImageDecoders[ImageFormat::BMP] = std::make_shared<BmpDecoder>(m_Logger);
26+
27+
m_ImageEncoders[ImageFormat::PNG] = std::make_shared<PngEncoder>(m_Logger);
2628

2729
m_SceneParsers[SceneFormat::UNKOWN] = std::make_shared<AssimpParser>(
28-
m_ImageParsers,
30+
m_ImageDecoders,
2931
[this](auto name) { return GetMaterial(name); },
3032
m_Logger);
3133
m_SceneParsers[SceneFormat::GLTF] = m_SceneParsers[SceneFormat::UNKOWN];
@@ -49,7 +51,7 @@ std::shared_ptr<Scene> AssetManager::ImportScene(const std::filesystem::path& pa
4951

5052
std::shared_ptr<Texture> AssetManager::ImportTexture(const std::filesystem::path& path) {
5153
auto format = get_image_format(path.extension().string());
52-
auto image = m_ImageParsers[format]->Parse(path);
54+
auto image = m_ImageDecoders[format]->Decode(path);
5355
AddTexture(image);
5456
return image;
5557
}

hitagi/asset/asset_manager.cppm

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import :camera;
1212
import :light;
1313
import :skeleton;
1414
import :scene;
15-
import :image_parser;
15+
import :image_codec;
1616
import :scene_parser;
1717
import :material_parser;
1818
import core;
@@ -50,7 +50,8 @@ private:
5050

5151
// Parser
5252
std::shared_ptr<MaterialParser> m_MaterialParser;
53-
utils::EnumArray<std::shared_ptr<ImageParser>, ImageFormat> m_ImageParsers;
53+
utils::EnumArray<std::shared_ptr<ImageDecoder>, ImageFormat> m_ImageDecoders;
54+
utils::EnumArray<std::shared_ptr<ImageEncoder>, ImageFormat> m_ImageEncoders;
5455
utils::EnumArray<std::shared_ptr<SceneParser>, SceneFormat> m_SceneParsers;
5556

5657
struct Assets {

hitagi/asset/assimp.cpp

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -227,14 +227,14 @@ auto AssimpParser::Parse(const std::filesystem::path& path, const std::filesyste
227227
logger->trace("texture path: {}", _texture->mFilename.C_Str());
228228
const auto compressed_buffer = core::Buffer(_texture->mWidth, reinterpret_cast<const std::byte*>(_texture->pcData));
229229

230-
if (_texture->CheckFormat("jpg") && m_ImageParsers[ImageFormat::JPEG]) {
231-
texture = m_ImageParsers[ImageFormat::JPEG]->Parse(compressed_buffer);
232-
} else if (_texture->CheckFormat("png") && m_ImageParsers[ImageFormat::PNG]) {
233-
texture = m_ImageParsers[ImageFormat::PNG]->Parse(compressed_buffer);
234-
} else if (_texture->CheckFormat("bmp") && m_ImageParsers[ImageFormat::BMP]) {
235-
texture = m_ImageParsers[ImageFormat::BMP]->Parse(compressed_buffer);
236-
} else if (_texture->CheckFormat("tga") && m_ImageParsers[ImageFormat::TGA]) {
237-
texture = m_ImageParsers[ImageFormat::TGA]->Parse(compressed_buffer);
230+
if (_texture->CheckFormat("jpg") && m_ImageDecoders[ImageFormat::JPEG]) {
231+
texture = m_ImageDecoders[ImageFormat::JPEG]->Decode(compressed_buffer);
232+
} else if (_texture->CheckFormat("png") && m_ImageDecoders[ImageFormat::PNG]) {
233+
texture = m_ImageDecoders[ImageFormat::PNG]->Decode(compressed_buffer);
234+
} else if (_texture->CheckFormat("bmp") && m_ImageDecoders[ImageFormat::BMP]) {
235+
texture = m_ImageDecoders[ImageFormat::BMP]->Decode(compressed_buffer);
236+
} else if (_texture->CheckFormat("tga") && m_ImageDecoders[ImageFormat::TGA]) {
237+
texture = m_ImageDecoders[ImageFormat::TGA]->Decode(compressed_buffer);
238238
} else {
239239
logger->warn("Unsupported texture format: {}", _texture->achFormatHint);
240240
}

hitagi/asset/bmp.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ using BITMAP_HEADER = struct BitmapHeader {
3333
};
3434
#pragma pack(pop)
3535

36-
std::shared_ptr<Texture> BmpParser::Parse(const core::Buffer& buffer) {
36+
std::shared_ptr<Texture> BmpDecoder::Decode(const core::Buffer& buffer) {
3737
auto logger = m_Logger ? m_Logger : spdlog::default_logger();
3838
if (buffer.Empty()) {
3939
logger->warn("[BMP] Parsing a empty buffer will return nullptr");

hitagi/asset/image_codec.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
module;
2+
3+
module asset;
4+
import :image_codec;
5+
import std;
6+
7+
namespace hitagi::asset {
8+
auto ImageDecoder::Decode(const std::filesystem::path& path) -> std::shared_ptr<Texture> {
9+
if (core::FileIOManager::Get())
10+
return Decode(core::FileIOManager::Get()->SyncOpenAndReadBinary(path));
11+
else
12+
return nullptr;
13+
}
14+
15+
auto ImageEncoder::Encode(const Texture& texture, const std::filesystem::path& path) -> bool {
16+
auto buffer = Encode(texture);
17+
if (buffer.Empty()) return false;
18+
if (core::FileIOManager::Get()) {
19+
core::FileIOManager::Get()->SaveBuffer(buffer, path);
20+
return true;
21+
}
22+
return false;
23+
}
24+
} // namespace hitagi::asset

hitagi/asset/image_codec.cppm

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
module;
2+
3+
#include <spdlog/logger.h>
4+
5+
export module asset:image_codec;
6+
import std;
7+
import :texture;
8+
import core;
9+
10+
export namespace hitagi::asset {
11+
12+
enum struct ImageFormat : std::uint8_t {
13+
UNKOWN,
14+
PNG,
15+
JPEG,
16+
TGA,
17+
BMP,
18+
};
19+
20+
inline constexpr ImageFormat get_image_format(std::string_view ext) noexcept {
21+
if (ext == ".jpeg" || ext == ".jpg")
22+
return ImageFormat::JPEG;
23+
else if (ext == ".bmp")
24+
return ImageFormat::BMP;
25+
else if (ext == ".tga")
26+
return ImageFormat::TGA;
27+
else if (ext == ".png")
28+
return ImageFormat::PNG;
29+
return ImageFormat::UNKOWN;
30+
}
31+
32+
class ImageDecoder {
33+
public:
34+
ImageDecoder(std::shared_ptr<spdlog::logger> logger = nullptr) : m_Logger(std::move(logger)) {}
35+
36+
virtual auto Decode(const std::filesystem::path& path) -> std::shared_ptr<Texture>;
37+
virtual auto Decode(const core::Buffer& buffer) -> std::shared_ptr<Texture> = 0;
38+
virtual ~ImageDecoder() = default;
39+
40+
protected:
41+
std::shared_ptr<spdlog::logger> m_Logger;
42+
};
43+
44+
class ImageEncoder {
45+
public:
46+
ImageEncoder(std::shared_ptr<spdlog::logger> logger = nullptr) : m_Logger(std::move(logger)) {}
47+
48+
virtual auto Encode(const Texture& texture, const std::filesystem::path& path) -> bool;
49+
virtual auto Encode(const Texture& texture) -> core::Buffer = 0;
50+
virtual ~ImageEncoder() = default;
51+
52+
protected:
53+
std::shared_ptr<spdlog::logger> m_Logger;
54+
};
55+
56+
class PngDecoder : public ImageDecoder {
57+
public:
58+
using ImageDecoder::ImageDecoder;
59+
using ImageDecoder::Decode;
60+
auto Decode(const core::Buffer& buffer) -> std::shared_ptr<Texture> final;
61+
};
62+
63+
class PngEncoder : public ImageEncoder {
64+
public:
65+
using ImageEncoder::ImageEncoder;
66+
using ImageEncoder::Encode;
67+
auto Encode(const Texture& texture) -> core::Buffer final;
68+
};
69+
70+
class JpegDecoder : public ImageDecoder {
71+
public:
72+
using ImageDecoder::ImageDecoder;
73+
using ImageDecoder::Decode;
74+
auto Decode(const core::Buffer& buffer) -> std::shared_ptr<Texture> final;
75+
};
76+
77+
class BmpDecoder : public ImageDecoder {
78+
public:
79+
using ImageDecoder::ImageDecoder;
80+
using ImageDecoder::Decode;
81+
auto Decode(const core::Buffer& buffer) -> std::shared_ptr<Texture> final;
82+
};
83+
84+
class TgaDecoder : public ImageDecoder {
85+
public:
86+
using ImageDecoder::ImageDecoder;
87+
using ImageDecoder::Decode;
88+
auto Decode(const core::Buffer& buffer) -> std::shared_ptr<Texture> final;
89+
};
90+
91+
} // namespace hitagi::asset

0 commit comments

Comments
 (0)