kagura_engine

    Rendering, presentation, assets, audio and application runtime for Kagura

    Download zip
    Author
    Version
    0.6.0
    License
    Apache-2.0
    Last updated
    15 days ago
    Downloads
    70

    #mizchi/kagura_engine

    描画・表示ツリー・アセット・音声・実行ループの基盤です。core の計算結果から 描画コマンドを作り、platform contract の hook を通して実行します。 ゲームのルールと platform backend 実装には依存しません。

    • application, runtime: 描画・更新 callback、実行オプション、フレーム駆動。
    • scene, scene2d, scene3d: 2D/3D の表示構造とドキュメント。
    • hud, tilemap2d, sprite_packer: 表示部品、タイル描画、アトラス構築。
    • inspection: 状態を表示・編集する型付き接続。ゲームの不変条件は利用側が検証。
    • draw3d, renderer3d, postfx, shadow3d: 描画コマンドとパイプライン。

    物理・IK・地形の計算は core、敵やアイテム・進行は game、wgpu-native と ネイティブキャプチャ IO は platform_native に置きます。 時計・ファイル操作・フレーム予約は platform/services、バイト取得は platform/fetch の契約を使います。 アトラスのデコード・管理とロード待ち行列は engine に残し、通信実装を注入します。 renderer2d, text, widget2d, ui, atlas, asset_loader, audio は独立 module です。

    EngineGame

    pub(open) trait EngineGame {
    fn update_game(Self,
    InputSnapshot
    ) -> Unit
    fn draw_game(Self, EngineContext) -> Array[
    DrawTrianglesCommand
    ]
    }

    High-level game contract for examples and small apps that already model their state as update + draw.

    InitialStateError

    pub suberror InitialStateError {
    InitialStateError(String)
    }

    EngineContext

    pub(all) struct EngineContext {
    dst :
    ImageHandle

    shader :
    ShaderHandle

    screen_w : Int
    screen_h : Int
    }

    FrameProfile

    pub(all) struct FrameProfile {
    fps : Double
    frame_time_ms : Double
    update_ms : Double
    draw_ms : Double
    gpu_frame_ms : Double
    draw_calls : Int
    vertex_count : Int
    asset_inflight : Int
    } derive(
    Debug
    )

    Frame profiler data for lightweight in-game overlays.

    FrameProfile::output

    fn FrameProfile::output(self : FrameProfile, logger : &Logger) -> Unit

    FrameProfile::to_string

    fn FrameProfile::to_string(self : FrameProfile) -> String

    HeadlessFrame

    pub(all) struct HeadlessFrame {
    width : Int
    height : Int
    frames : Int
    pixels : Array[Int]
    skipped_commands : Int
    drawn_triangles : Int
    draw_commands : Int
    }

    A rendered frame plus what the rasterizer had to say about the command stream that produced it.

    HeadlessGamepadStep

    type HeadlessGamepadStep derive(
    FromJson
    )

    HeadlessInput

    pub(all) struct HeadlessInput {
    cursor_x : Double
    cursor_y : Double
    pressed_keys : Array[Int]
    }

    One synthesized input tick. Cursor position and held keys are enough to reach the states a UI actually breaks in -- hover, focus, a held direction -- without pretending to be a full input recording.

    HeadlessInputStep

    type HeadlessInputStep derive(
    FromJson
    )

    HeadlessTexture

    pub(all) struct HeadlessTexture {
    id : Int
    width : Int
    height : Int
    pixels : Array[Int]
    }

    A source image the frame needs, as RGBA8. id matches the values a draw command carries in src_image_ids.

    These do not come from the graphics driver: @gfx.GraphicsDriver has no upload call, because the real backend takes pixels straight from the runtime hooks to the GPU. Without them a textured quad samples the unbound white texture, which is why a sprite used to render as a flat white rectangle.

    InitialStates

    pub struct InitialStates[T] {
    default_factory : (Int, Int) -> T
    named : Array[(String, (Int, Int) -> T)]
    }

    Factories construct fresh game state. They do not perform scene transitions.

    InitialStates::create

    fn[T] InitialStates::create(self : InitialStates[T], name : String?, width~ : Int, height~ : Int) -> T raise InitialStateError

    No fallback for unknown names: callers must know which state they are testing.

    InitialStates::create_for_capture

    fn[T] InitialStates::create_for_capture(self : InitialStates[T], width~ : Int, height~ : Int) -> T

    Resolve the host request before constructing state. Works with EngineGame, SceneGame and custom loops because the result is the game's own T.

    InitialStates::names

    fn[T] InitialStates::names(self : InitialStates[T]) -> Array[String]

    InitialStates::new

    fn[T] InitialStates::new(default~ : (Int, Int) -> T, named? : Array[(String, (Int, Int) -> T)]) -> InitialStates[T] raise InitialStateError

    LifecycleHooks

    pub(all) struct LifecycleHooks {
    on_start : (String, String) -> Unit
    on_stop : () -> Unit
    }

    build_profiler_hud

    fn build_profiler_hud(cmds : Array[
    DrawTrianglesCommand
    ], dst :
    ImageHandle
    , shader :
    ShaderHandle
    , profile : FrameProfile, screen_w : Double, screen_h : Double) -> Unit

    capture_viewport

    fn capture_viewport(width : Int, height : Int) -> (Int, Int)

    default_frame_profile

    fn default_frame_profile() -> FrameProfile

    default_headless_input

    fn default_headless_input() -> HeadlessInput

    headless_frame_png

    fn headless_frame_png(frame : HeadlessFrame) -> Bytes?

    PNG bytes for a rendered frame, or None when the encoder rejects the buffer.

    is_native_gpu_capture

    fn is_native_gpu_capture() -> Bool

    parse_headless_keys

    fn parse_headless_keys(text : String) -> Array[Int]

    Parse the comma-separated key-code list the headless request carries ("9,32"). Anything that is not an integer is skipped rather than aborting the render -- a malformed key list should not cost the frame.

    render_commands

    fn[T :
    GraphicsDriver
    ] render_commands(graphics : T, cmds : Array[
    DrawTrianglesCommand
    ], clear_color? :
    Color
    , clear_enabled? : Bool) -> Unit raise

    render_headless_frame

    fn render_headless_frame(update~ : (
    InputSnapshot
    ) -> Unit, draw~ : (EngineContext) -> Array[
    DrawTrianglesCommand
    ], width~ : Int, height~ : Int, frames~ : Int, input? : HeadlessInput, input_at? : (Int) ->
    InputSnapshot
    , on_frame? : () -> Unit, textures? : Array[HeadlessTexture], textures_at? : () -> Array[HeadlessTexture], clear_color? :
    Color
    ) -> HeadlessFrame

    Drive update for frames ticks, then rasterize one draw into an RGBA buffer. frames of 0 draws the initial state without ever updating. input_at supplies a complete snapshot for each zero-based tick, overriding the held input. It can replay keyboard, pointer and gamepad transitions.

    reset_lifecycle_hooks

    fn reset_lifecycle_hooks() -> Unit

    run

    fn run(update~ : (
    InputSnapshot
    ) -> Unit, draw~ : (EngineContext) -> Array[
    DrawTrianglesCommand
    ], on_frame? : () -> Unit, after_render? : (Double, Double) -> Unit, audio_ctx? :
    MixerAudioContext
    , audio_frames_per_tick? : Int, width? : Int, height? : Int, title? : String, canvas? : String) -> Unit

    run_game

    fn[T : EngineGame] run_game(game : T, on_frame? : () -> Unit, after_render? : (Double, Double) -> Unit, audio_ctx? :
    MixerAudioContext
    , audio_frames_per_tick? : Int, width? : Int, height? : Int, title? : String, canvas? : String) -> Unit

    set_gpu_capture_for_test

    fn set_gpu_capture_for_test(enabled : Bool) -> Unit

    set_headless_texture_provider

    fn set_headless_texture_provider(provider : () -> Array[HeadlessTexture]) -> Unit

    Runtime adapters provide the current CPU copies, including updates made this tick.

    set_lifecycle_hooks

    fn set_lifecycle_hooks(hooks : LifecycleHooks) -> Unit

    set_lifecycle_hooks_if_absent

    fn set_lifecycle_hooks_if_absent(hooks : LifecycleHooks) -> Unit

    Native examples often import both web_runtime_hooks (stub on native) and native_runtime_hooks. Both packages register in init; the stub must not wipe the real GPU install if it happens to run later.