# 機能設計書: 2Dレンダリング

## 1. 機能概要

### 1.1 機能の目的

2Dレンダリングシステムは、Godot Engineにおける2次元グラフィックスの描画基盤を提供する。CanvasItemを基底クラスとして、スプライト、テキスト、図形などの2D要素をキャンバス上に描画し、変換、可視性、描画順序を管理する。RenderingServerと連携して効率的なGPUレンダリングを実現する。

### 1.2 主要な責務

| 責務 | 説明 |
|------|------|
| キャンバスアイテム管理 | 2D描画要素のRID管理とRenderingServer連携 |
| 変換処理 | ローカル/グローバル座標変換の計算とキャッシュ |
| 可視性制御 | 表示/非表示、レイヤー、Zインデックスの管理 |
| 描画コマンド | draw_*メソッドによる描画プリミティブの発行 |
| テクスチャ描画 | Sprite2Dによるテクスチャ表示とアニメーション |
| 再描画管理 | queue_redraw()による効率的な再描画スケジューリング |

### 1.3 アーキテクチャ上の位置づけ

```
+------------------+
|   Game Scripts   |
+------------------+
         |
         v
+------------------+     +------------------+
|   Sprite2D /     |---->|   Texture2D      |
|   Node2D         |     +------------------+
+------------------+
         |
         v
+------------------+
|   CanvasItem     |  <-- 描画基盤クラス
+------------------+
         |
         v
+------------------+     +------------------+
|   CanvasLayer    |<--->|   Viewport       |
+------------------+     +------------------+
         |
         v
+------------------+
| RenderingServer  |  <-- GPU描画コマンド
+------------------+
```

## 2. クラス構造

### 2.1 クラス階層

```
Node
└── CanvasItem (scene/main/canvas_item.h)
    ├── Control (UI要素)
    └── Node2D (scene/2d/node_2d.h)
        └── Sprite2D (scene/2d/sprite_2d.h)
            ├── texture: Ref<Texture2D>
            ├── centered: bool
            ├── offset: Point2
            ├── hflip/vflip: bool
            ├── region_enabled: bool
            ├── region_rect: Rect2
            ├── frame: int
            └── hframes/vframes: int
```

### 2.2 CanvasItemの主要メンバ

```cpp
// scene/main/canvas_item.h 78-132行目
class CanvasItem : public Node {
private:
    RID canvas_item;                    // RenderingServerの描画アイテム
    StringName canvas_group;            // キャンバスグループ名
    CanvasLayer *canvas_layer;          // 所属レイヤー

    Color modulate = Color(1, 1, 1, 1); // モジュレートカラー
    Color self_modulate;                // 自身のモジュレート

    int light_mask = 1;                 // ライトマスク
    uint32_t visibility_layer = 1;      // 可視性レイヤー
    int z_index = 0;                    // Zインデックス
    bool z_relative = true;             // 相対Zインデックス

    bool visible = true;                // 可視状態
    bool pending_update = false;        // 再描画保留中
    bool drawing = false;               // 描画中フラグ
    bool top_level = false;             // トップレベルフラグ

    mutable Transform2D global_transform;  // グローバル変換キャッシュ
    mutable MTFlag global_invalid;         // キャッシュ無効フラグ
};
```

### 2.3 TextureFilter/TextureRepeat列挙型

```cpp
// scene/main/canvas_item.h 52-69行目
enum TextureFilter {
    TEXTURE_FILTER_PARENT_NODE,    // 親ノードから継承
    TEXTURE_FILTER_NEAREST,        // 最近傍補間
    TEXTURE_FILTER_LINEAR,         // 線形補間
    TEXTURE_FILTER_NEAREST_WITH_MIPMAPS,
    TEXTURE_FILTER_LINEAR_WITH_MIPMAPS,
    TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC,
    TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC,
    TEXTURE_FILTER_MAX
};

enum TextureRepeat {
    TEXTURE_REPEAT_PARENT_NODE,    // 親ノードから継承
    TEXTURE_REPEAT_DISABLED,       // リピート無効
    TEXTURE_REPEAT_ENABLED,        // リピート有効
    TEXTURE_REPEAT_MIRROR,         // ミラーリピート
    TEXTURE_REPEAT_MAX,
};
```

## 3. 処理フロー

### 3.1 描画フロー

```
[queue_redraw()呼び出し]
        |
        v
+------------------+
| pending_update   |  --> true: 既にキューにある
| チェック         |
+------------------+
        |
        v
+------------------+
| _redraw_callback |  <-- call_deferred()でスケジュール
| をキューに追加   |
+------------------+

        ...次フレーム...

+------------------+
| _redraw_callback |
| 実行             |
+------------------+
        |
        v
+------------------+
| canvas_item_clear|  <-- 描画コマンドクリア
| (RenderingServer)|
+------------------+
        |
        v
+------------------+
| drawing = true   |
| NOTIFICATION_DRAW|
| emit "draw"      |
| _draw() 仮想関数 |
| drawing = false  |
+------------------+
        |
        v
[描画完了]
```

### 3.2 Sprite2D描画フロー

```
[NOTIFICATION_DRAW]
        |
        v
+------------------+
| texture.is_null()|  --> true: 終了
| チェック         |
+------------------+
        |
        v
+------------------+
| _get_rects()     |  <-- ソース/デスト矩形計算
| ・region処理     |
| ・frame計算      |
| ・centered処理   |
| ・flip処理       |
+------------------+
        |
        v
+------------------+
| texture->        |
| draw_rect_region |  <-- テクスチャ描画
| (ci, dst, src)   |
+------------------+
        |
        v
[描画完了]
```

### 3.3 変換伝播フロー

```
[変換変更]
        |
        v
+------------------+
| _notify_transform|
+------------------+
        |
        v
+------------------+
| global_invalid   |  <-- 無効フラグセット
| = true           |
+------------------+
        |
        v
+------------------+
| 子ノードへ       |  <-- 再帰的に伝播
| 伝播             |
+------------------+
```

## 4. 主要メソッド詳細

### 4.1 queue_redraw（再描画リクエスト）

```cpp
// scene/main/canvas_item.cpp 472-484行目
void CanvasItem::queue_redraw() {
    ERR_THREAD_GUARD;  // スレッドセーフ
    if (!is_inside_tree()) {
        return;
    }
    if (pending_update) {
        return;  // 既にキューにある
    }

    pending_update = true;
    callable_mp(this, &CanvasItem::_redraw_callback).call_deferred();
}
```

### 4.2 _redraw_callback（描画コールバック）

```cpp
// scene/main/canvas_item.cpp 133-157行目
void CanvasItem::_redraw_callback() {
    if (!is_inside_tree()) {
        pending_update = false;
        return;
    }

    if (draw_commands_dirty) {
        RenderingServer::get_singleton()->canvas_item_clear(get_canvas_item());
        draw_commands_dirty = false;
    }

    if (is_visible_in_tree()) {
        drawing = true;
        TextServer::set_current_drawn_item_oversampling(get_viewport()->get_oversampling());
        current_item_drawn = this;
        notification(NOTIFICATION_DRAW);
        emit_signal(SceneStringName(draw));
        GDVIRTUAL_CALL(_draw);
        current_item_drawn = nullptr;
        TextServer::set_current_drawn_item_oversampling(0.0);
        drawing = false;
        draw_commands_dirty = true;
    }
    pending_update = false;
}
```

### 4.3 get_global_transform（グローバル変換取得）

```cpp
// scene/main/canvas_item.cpp 176-194行目
Transform2D CanvasItem::get_global_transform() const {
    ERR_READ_THREAD_GUARD_V(Transform2D());

    if (_is_global_invalid()) {
        const CanvasItem *pi = get_parent_item();
        Transform2D new_global;
        if (pi) {
            new_global = pi->get_global_transform() * get_transform();
        } else {
            new_global = get_transform();
        }

        global_transform = new_global;
        _set_global_invalid(false);
    }

    return global_transform;
}
```

### 4.4 Sprite2D::_notification（描画処理）

```cpp
// scene/2d/sprite_2d.cpp 145-172行目
void Sprite2D::_notification(int p_what) {
    switch (p_what) {
        case NOTIFICATION_DRAW: {
            if (texture.is_null()) {
                return;
            }

            RID ci = get_canvas_item();

            Rect2 src_rect, dst_rect;
            bool filter_clip_enabled;
            _get_rects(src_rect, dst_rect, filter_clip_enabled);

            texture->draw_rect_region(ci, dst_rect, src_rect, Color(1, 1, 1), false, filter_clip_enabled);
        } break;
    }
}
```

### 4.5 Sprite2D::_get_rects（矩形計算）

```cpp
// scene/2d/sprite_2d.cpp 95-130行目
void Sprite2D::_get_rects(Rect2 &r_src_rect, Rect2 &r_dst_rect, bool &r_filter_clip_enabled) const {
    Rect2 base_rect;

    if (region_enabled) {
        r_filter_clip_enabled = region_filter_clip_enabled;
        base_rect = region_rect;
    } else {
        r_filter_clip_enabled = false;
        base_rect = Rect2(0, 0, texture->get_width(), texture->get_height());
    }

    // フレーム計算
    Size2 frame_size = base_rect.size / Size2(hframes, vframes);
    Point2 frame_offset = Point2(frame % hframes, frame / hframes);
    frame_offset *= frame_size;

    r_src_rect.size = frame_size;
    r_src_rect.position = base_rect.position + frame_offset;

    // デスティネーション計算
    Point2 dest_offset = offset;
    if (centered) {
        dest_offset -= frame_size / 2;
    }

    // スナップ処理
    if (get_viewport() && get_viewport()->is_snap_2d_transforms_to_pixel_enabled()) {
        dest_offset = (dest_offset + Point2(0.5, 0.5)).floor();
    }

    r_dst_rect = Rect2(dest_offset, frame_size);

    // フリップ処理
    if (hflip) {
        r_dst_rect.size.x = -r_dst_rect.size.x;
    }
    if (vflip) {
        r_dst_rect.size.y = -r_dst_rect.size.y;
    }
}
```

## 5. 可視性制御

### 5.1 可視性の伝播

```cpp
// scene/main/canvas_item.cpp 67-74行目
void CanvasItem::_propagate_visibility_changed(bool p_parent_visible_in_tree) {
    parent_visible_in_tree = p_parent_visible_in_tree;
    if (!visible) {
        return;  // 自身が非表示なら伝播不要
    }

    _handle_visibility_change(p_parent_visible_in_tree);
}
```

### 5.2 可視性変更処理

```cpp
// scene/main/canvas_item.cpp 92-111行目
void CanvasItem::_handle_visibility_change(bool p_visible) {
    RenderingServer::get_singleton()->canvas_item_set_visible(canvas_item, p_visible);
    notification(NOTIFICATION_VISIBILITY_CHANGED);

    if (p_visible) {
        queue_redraw();
    } else {
        emit_signal(SceneStringName(hidden));
    }

    _block();
    for (int i = 0; i < get_child_count(); i++) {
        CanvasItem *c = Object::cast_to<CanvasItem>(get_child(i));
        if (c) {
            c->_propagate_visibility_changed(p_visible);
        }
    }
    _unblock();
}
```

## 6. キャンバス管理

### 6.1 キャンバス参入

```cpp
// scene/main/canvas_item.cpp 234-285行目
void CanvasItem::_enter_canvas() {
    CanvasItem *parent_item = get_parent_item();

    if (parent_item) {
        // 親がCanvasItemの場合
        canvas_layer = parent_item->canvas_layer;
        RenderingServer::get_singleton()->canvas_item_set_parent(canvas_item, parent_item->get_canvas_item());
    } else {
        // ルートCanvasItem
        Node *n = this;
        canvas_layer = nullptr;

        // CanvasLayerを探索
        while (n) {
            canvas_layer = Object::cast_to<CanvasLayer>(n);
            if (canvas_layer || Object::cast_to<Viewport>(n)) {
                break;
            }
            n = n->get_parent();
        }

        RID canvas;
        if (canvas_layer) {
            canvas = canvas_layer->get_canvas();
        } else {
            canvas = get_viewport()->find_world_2d()->get_canvas();
        }

        RenderingServer::get_singleton()->canvas_item_set_parent(canvas_item, canvas);
        canvas_group = "_root_canvas" + itos(canvas.get_id());
        add_to_group(canvas_group);
    }

    queue_redraw();
    notification(NOTIFICATION_ENTER_CANVAS);
}
```

## 7. 描画プリミティブ

### 7.1 ERR_DRAW_GUARDマクロ

```cpp
// scene/main/canvas_item.cpp 43-44行目
#define ERR_DRAW_GUARD \
    ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside this node's `_draw()`, functions connected to its `draw` signal, or when it receives NOTIFICATION_DRAW.")
```

### 7.2 主要draw_*メソッド（CanvasItem）

| メソッド | 説明 |
|----------|------|
| `draw_line()` | 線分描画 |
| `draw_rect()` | 矩形描画 |
| `draw_circle()` | 円描画 |
| `draw_texture()` | テクスチャ描画 |
| `draw_string()` | 文字列描画 |
| `draw_polygon()` | ポリゴン描画 |
| `draw_mesh()` | メッシュ描画 |

## 8. コードリーディングガイド

### 8.1 推奨読解順序

1. **CanvasItemの基本構造** (canvas_item.h 78-132行目)
   - メンバ変数とその役割を把握

2. **描画サイクル** (canvas_item.cpp 133-157行目)
   - _redraw_callback()の処理フローを理解

3. **変換キャッシュ** (canvas_item.cpp 176-194行目)
   - get_global_transform()の遅延計算

4. **キャンバス管理** (canvas_item.cpp 234-285行目)
   - _enter_canvas()のレイヤー解決

5. **Sprite2D実装** (sprite_2d.cpp 145-172行目)
   - 具体的な描画処理の実装例

### 8.2 読解のコツ

- `RID canvas_item`はRenderingServerへのハンドル
- `pending_update`で再描画をデバウンス（1フレーム1回）
- `drawing`フラグはdraw_*メソッドのガード用
- `global_invalid`は変換キャッシュの無効化管理
- `top_level`は親の変換を無視するフラグ

### 8.3 プログラム呼び出し階層図

```
Sprite2D::set_texture()
    |
    +---> queue_redraw()
    |         |
    |         +---> call_deferred(_redraw_callback)
    |
    +---> emit_signal("texture_changed")
    |
    +---> item_rect_changed()

_redraw_callback() [次フレーム]
    |
    +---> RenderingServer::canvas_item_clear()
    |
    +---> notification(NOTIFICATION_DRAW)
    |         |
    |         +---> Sprite2D::_notification()
    |                   |
    |                   +---> _get_rects()
    |                   |
    |                   +---> texture->draw_rect_region()
    |
    +---> emit_signal("draw")
    |
    +---> GDVIRTUAL_CALL(_draw)
```

### 8.4 データフロー図

```
+----------------+     set_texture()    +----------------+
|   GDScript     |-------------------->|   Sprite2D     |
+----------------+                      +----------------+
                                              |
                                              v
                                        queue_redraw()
                                              |
                                              v
+----------------+     canvas_item      +----------------+
|RenderingServer |<--------------------|   CanvasItem   |
|                |     RID              |                |
+----------------+                      +----------------+
        |
        | draw_rect_region()
        v
+----------------+
|     GPU        |
+----------------+
```

### 8.5 関連ファイル一覧

| ファイル | 役割 |
|----------|------|
| `scene/main/canvas_item.h` | CanvasItem基底クラス定義 |
| `scene/main/canvas_item.cpp` | CanvasItem実装 |
| `scene/2d/node_2d.h/cpp` | Node2D（2D位置/回転/スケール） |
| `scene/2d/sprite_2d.h/cpp` | Sprite2D（テクスチャ表示） |
| `scene/main/canvas_layer.h/cpp` | キャンバスレイヤー |
| `scene/resources/texture.h` | Texture2Dリソース |
| `servers/rendering/rendering_server.h` | レンダリングサーバーAPI |

## 9. 設計上の考慮点

### 9.1 パフォーマンス最適化

1. **遅延描画**: queue_redraw()でフレームごとに1回だけ描画
2. **変換キャッシュ**: global_transformを遅延計算
3. **階層最適化**: canvas_item_childrenで高速子要素走査
4. **RID管理**: RenderingServerへの直接描画コマンド

### 9.2 スレッド安全性

1. **ERR_THREAD_GUARD**: メインスレッド専用メソッドの保護
2. **ERR_READ_THREAD_GUARD_V**: 読み取り専用メソッドの保護
3. **MTFlag**: マルチスレッド対応フラグ型

### 9.3 柔軟性

1. **継承ポイント**: _draw()仮想関数でカスタム描画
2. **シグナル**: "draw"シグナルで外部からの描画追加
3. **テクスチャ設定**: フィルター/リピートモードの継承

## 10. 通知定数

```cpp
// scene/main/canvas_item.h 209-217行目
enum {
    NOTIFICATION_TRANSFORM_CHANGED = 2000,  // 変換変更
    NOTIFICATION_DRAW = 30,                  // 描画タイミング
    NOTIFICATION_VISIBILITY_CHANGED = 31,   // 可視性変更
    NOTIFICATION_ENTER_CANVAS = 32,         // キャンバス参入
    NOTIFICATION_EXIT_CANVAS = 33,          // キャンバス退出
    NOTIFICATION_LOCAL_TRANSFORM_CHANGED = 35,  // ローカル変換変更
    NOTIFICATION_WORLD_2D_CHANGED = 36,     // World2D変更
};
```

## 11. 使用例

### 11.1 GDScriptでのカスタム描画

```gdscript
extends Node2D

func _draw():
    draw_circle(Vector2.ZERO, 50, Color.RED)
    draw_rect(Rect2(-25, -25, 50, 50), Color.BLUE, false, 2.0)
    draw_line(Vector2(-50, 0), Vector2(50, 0), Color.GREEN, 3.0)
```

### 11.2 Sprite2Dの使用

```gdscript
extends Sprite2D

func _ready():
    texture = preload("res://icon.png")
    centered = true
    hframes = 4
    vframes = 2
    frame = 0

func _process(delta):
    frame = (frame + 1) % (hframes * vframes)
```
