aether-engine 1.3.0__tar.gz → 1.4.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. {aether_engine-1.3.0/aether_engine.egg-info → aether_engine-1.4.0}/PKG-INFO +16 -10
  2. {aether_engine-1.3.0 → aether_engine-1.4.0}/README.md +9 -6
  3. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/__init__.py +4 -1
  4. aether_engine-1.4.0/aether/__main__.py +6 -0
  5. aether_engine-1.4.0/aether/cli.py +145 -0
  6. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/core/_identity.py +1 -1
  7. aether_engine-1.4.0/aether/core/boot_splash.py +217 -0
  8. aether_engine-1.4.0/aether/core/branding_guard.py +32 -0
  9. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/core/engine.py +3 -19
  10. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/graphics/mesh.py +21 -0
  11. aether_engine-1.4.0/aether/graphics/voxel_mesh.py +173 -0
  12. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/resources/serialization.py +19 -9
  13. aether_engine-1.4.0/aether/tools/__init__.py +1 -0
  14. aether_engine-1.4.0/aether/tools/packager.py +68 -0
  15. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/ui/text.py +8 -2
  16. {aether_engine-1.3.0 → aether_engine-1.4.0/aether_engine.egg-info}/PKG-INFO +16 -10
  17. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether_engine.egg-info/SOURCES.txt +5 -0
  18. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether_engine.egg-info/entry_points.txt +1 -0
  19. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether_engine.egg-info/requires.txt +7 -3
  20. {aether_engine-1.3.0 → aether_engine-1.4.0}/setup.py +24 -5
  21. aether_engine-1.3.0/aether/core/boot_splash.py +0 -141
  22. aether_engine-1.3.0/aether/core/branding_guard.py +0 -103
  23. {aether_engine-1.3.0 → aether_engine-1.4.0}/LICENSE +0 -0
  24. {aether_engine-1.3.0 → aether_engine-1.4.0}/MANIFEST.in +0 -0
  25. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/ai/behavior_tree.py +0 -0
  26. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/ai/navmesh.py +0 -0
  27. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/animation/skeleton.py +0 -0
  28. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/assets/asset_pipeline.py +0 -0
  29. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/audio/__init__.py +0 -0
  30. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/audio/audio_manager.py +0 -0
  31. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/audio/music.py +0 -0
  32. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/audio/sfx.py +0 -0
  33. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/audio/sound.py +0 -0
  34. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/core/__init__.py +0 -0
  35. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/core/achievement_system.py +0 -0
  36. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/core/application.py +0 -0
  37. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/core/config.py +0 -0
  38. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/core/console.py +0 -0
  39. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/core/event_bus.py +0 -0
  40. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/core/input.py +0 -0
  41. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/core/plugin_system.py +0 -0
  42. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/core/save_system.py +0 -0
  43. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/core/time_manager.py +0 -0
  44. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/core/window.py +0 -0
  45. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/editor/__init__.py +0 -0
  46. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/editor/__main__.py +0 -0
  47. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/editor/gizmo.py +0 -0
  48. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/editor/imgui_integration.py +0 -0
  49. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/editor/model_editor.py +0 -0
  50. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/environment/environment_system.py +0 -0
  51. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/graphics/__init__.py +0 -0
  52. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/graphics/camera.py +0 -0
  53. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/graphics/first_person.py +0 -0
  54. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/graphics/framebuffer.py +0 -0
  55. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/graphics/light.py +0 -0
  56. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/graphics/lod_system.py +0 -0
  57. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/graphics/material.py +0 -0
  58. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/graphics/mesh_factory.py +0 -0
  59. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/graphics/particle_system.py +0 -0
  60. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/graphics/post_process.py +0 -0
  61. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/graphics/renderer.py +0 -0
  62. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/graphics/shader.py +0 -0
  63. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/graphics/shader_library.py +0 -0
  64. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/graphics/sky_atmosphere.py +0 -0
  65. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/graphics/terrain.py +0 -0
  66. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/graphics/texture.py +0 -0
  67. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/graphics/water.py +0 -0
  68. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/graphics2d/__init__.py +0 -0
  69. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/network/network_manager.py +0 -0
  70. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/physics/__init__.py +0 -0
  71. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/physics/bullet_backend.py +0 -0
  72. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/physics/collider.py +0 -0
  73. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/physics/physics2d.py +0 -0
  74. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/physics/physics3d.py +0 -0
  75. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/physics/physics_world.py +0 -0
  76. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/physics/raycast.py +0 -0
  77. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/physics/rigidbody.py +0 -0
  78. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/platform/steam_api.py +0 -0
  79. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/resources/__init__.py +0 -0
  80. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/resources/asset_database.py +0 -0
  81. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/resources/loaders/model_loader.py +0 -0
  82. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/resources/loaders/obj_loader.py +0 -0
  83. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/resources/loaders/texture_loader.py +0 -0
  84. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/resources/resource_manager.py +0 -0
  85. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/resources/save_load.py +0 -0
  86. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/scene/__init__.py +0 -0
  87. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/scene/component.py +0 -0
  88. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/scene/entity.py +0 -0
  89. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/scene/scene.py +0 -0
  90. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/scene/scene_graph.py +0 -0
  91. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/scene/scene_manager.py +0 -0
  92. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/ui/__init__.py +0 -0
  93. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/ui/button.py +0 -0
  94. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/ui/canvas.py +0 -0
  95. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/ui/hud.py +0 -0
  96. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/utils/__init__.py +0 -0
  97. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/utils/fonts.py +0 -0
  98. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/utils/logger.py +0 -0
  99. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/utils/math_utils.py +0 -0
  100. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/utils/object_pool.py +0 -0
  101. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether/utils/profiler.py +0 -0
  102. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether_engine.egg-info/dependency_links.txt +0 -0
  103. {aether_engine-1.3.0 → aether_engine-1.4.0}/aether_engine.egg-info/top_level.txt +0 -0
  104. {aether_engine-1.3.0 → aether_engine-1.4.0}/examples/01_hello_cube.py +0 -0
  105. {aether_engine-1.3.0 → aether_engine-1.4.0}/examples/02_sphere_scene.py +0 -0
  106. {aether_engine-1.3.0 → aether_engine-1.4.0}/examples/03_first_person.py +0 -0
  107. {aether_engine-1.3.0 → aether_engine-1.4.0}/examples/04_physics2d.py +0 -0
  108. {aether_engine-1.3.0 → aether_engine-1.4.0}/pyproject.toml +0 -0
  109. {aether_engine-1.3.0 → aether_engine-1.4.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aether-engine
3
- Version: 1.3.0
3
+ Version: 1.4.0
4
4
  Summary: 3D/2D game engine by entiti937 — OpenGL, physics, FPS, mandatory branding splash
5
5
  Home-page: https://pypi.org/project/aether-engine/
6
6
  Author: entiti937
@@ -28,13 +28,16 @@ Provides-Extra: physics
28
28
  Requires-Dist: pymunk>=6.0; extra == "physics"
29
29
  Provides-Extra: models
30
30
  Requires-Dist: trimesh>=3.21; extra == "models"
31
+ Provides-Extra: build
32
+ Requires-Dist: pyinstaller>=6.0; extra == "build"
31
33
  Provides-Extra: full
34
+ Requires-Dist: pymunk>=6.0; extra == "full"
35
+ Requires-Dist: pygame-ce>=2.5; extra == "full"
36
+ Requires-Dist: imageio>=2.31; extra == "full"
32
37
  Requires-Dist: trimesh>=3.21; extra == "full"
33
38
  Requires-Dist: freetype-py>=2.4; extra == "full"
34
- Requires-Dist: pygame-ce>=2.5; extra == "full"
39
+ Requires-Dist: pyinstaller>=6.0; extra == "full"
35
40
  Requires-Dist: pyyaml>=6.0; extra == "full"
36
- Requires-Dist: pymunk>=6.0; extra == "full"
37
- Requires-Dist: imageio>=2.31; extra == "full"
38
41
  Dynamic: author
39
42
  Dynamic: classifier
40
43
  Dynamic: description
@@ -49,7 +52,7 @@ Dynamic: summary
49
52
  # Aether Engine
50
53
 
51
54
  **3D / 2D игровой движок на Python + OpenGL**
52
- **Разработчик:** entiti937 · **Версия:** 1.3.0 · **Лицензия:** MIT
55
+ **Разработчик:** entiti937 · **Версия:** 1.4.0 · **Лицензия:** MIT
53
56
  **PyPI:** https://pypi.org/project/aether-engine/
54
57
 
55
58
  > Полное руководство: [`docs/GUIDE.md`](docs/GUIDE.md)
@@ -62,18 +65,21 @@ Dynamic: summary
62
65
  pip install -U "aether-engine[full]"
63
66
  ```
64
67
 
65
- ## Что нового в 1.3.0
68
+ ## Что нового в 1.4.0
66
69
 
67
- - **Обязательный splash** при старте любой игры (чёрный экран, белый текст, entiti937)
68
- - **Водяной знак** в каждом кадре + защита от удаления брендинга
69
- - **Кириллица** — системные шрифты, UTF-8 консоль, полный Unicode в UI
70
- - Компиляция защищённых модулей при сборке пакета
70
+ - Splash как в Unreal (fade-in) — **только при загрузке**, не в игре
71
+ - `aether help` / `aether build` — справка и сборка в `.exe`
72
+ - `ChunkedVoxelMesher` — оптимизация вокселей на уровне движка
73
+ - Исправлен текст splash (кириллица через Pillow)
74
+ - Убран постоянный водяной знак во время геймплея
71
75
 
72
76
  ## Showcase-игра
73
77
 
74
78
  Minecraft-like sandbox **на этом же движке**:
75
79
 
76
80
  ```bash
81
+ aether help
82
+ aether build games/aether_craft.py --name AetherCraft
77
83
  python games/aether_craft.py
78
84
  ```
79
85
 
@@ -1,7 +1,7 @@
1
1
  # Aether Engine
2
2
 
3
3
  **3D / 2D игровой движок на Python + OpenGL**
4
- **Разработчик:** entiti937 · **Версия:** 1.3.0 · **Лицензия:** MIT
4
+ **Разработчик:** entiti937 · **Версия:** 1.4.0 · **Лицензия:** MIT
5
5
  **PyPI:** https://pypi.org/project/aether-engine/
6
6
 
7
7
  > Полное руководство: [`docs/GUIDE.md`](docs/GUIDE.md)
@@ -14,18 +14,21 @@
14
14
  pip install -U "aether-engine[full]"
15
15
  ```
16
16
 
17
- ## Что нового в 1.3.0
17
+ ## Что нового в 1.4.0
18
18
 
19
- - **Обязательный splash** при старте любой игры (чёрный экран, белый текст, entiti937)
20
- - **Водяной знак** в каждом кадре + защита от удаления брендинга
21
- - **Кириллица** — системные шрифты, UTF-8 консоль, полный Unicode в UI
22
- - Компиляция защищённых модулей при сборке пакета
19
+ - Splash как в Unreal (fade-in) — **только при загрузке**, не в игре
20
+ - `aether help` / `aether build` — справка и сборка в `.exe`
21
+ - `ChunkedVoxelMesher` — оптимизация вокселей на уровне движка
22
+ - Исправлен текст splash (кириллица через Pillow)
23
+ - Убран постоянный водяной знак во время геймплея
23
24
 
24
25
  ## Showcase-игра
25
26
 
26
27
  Minecraft-like sandbox **на этом же движке**:
27
28
 
28
29
  ```bash
30
+ aether help
31
+ aether build games/aether_craft.py --name AetherCraft
29
32
  python games/aether_craft.py
30
33
  ```
31
34
 
@@ -31,7 +31,9 @@ from aether.audio.sfx import SfxBank
31
31
  from aether.resources.save_load import save_json, load_json
32
32
  from aether.physics.raycast import Raycaster
33
33
 
34
- __version__ = "1.3.0"
34
+ __version__ = "1.4.0"
35
+
36
+ from aether.graphics.voxel_mesh import ChunkedVoxelMesher
35
37
 
36
38
  __all__ = [
37
39
  "Engine",
@@ -62,5 +64,6 @@ __all__ = [
62
64
  "save_json",
63
65
  "load_json",
64
66
  "Raycaster",
67
+ "ChunkedVoxelMesher",
65
68
  "__version__",
66
69
  ]
@@ -0,0 +1,6 @@
1
+ """python -m aether"""
2
+
3
+ from aether.cli import main
4
+
5
+ if __name__ == "__main__":
6
+ raise SystemExit(main())
@@ -0,0 +1,145 @@
1
+ """Aether Engine CLI — help, docs, build."""
2
+ from __future__ import annotations
3
+
4
+ import argparse
5
+ import sys
6
+ from pathlib import Path
7
+
8
+ from aether import __version__
9
+ from aether.core._identity import creator, engine_name
10
+
11
+
12
+ QUICK_REFERENCE = f"""
13
+ {engine_name()} v{__version__} — разработчик: {creator()}
14
+ ================================================================
15
+
16
+ УСТАНОВКА
17
+ pip install -U "aether-engine[full]"
18
+
19
+ МИНИМАЛЬНЫЙ КОД (3D)
20
+ from aether import Engine, Scene, Entity, MeshFactory, Material, Camera
21
+ from aether.scene.component import Transform, MeshRenderer
22
+
23
+ engine = Engine(1280, 720, "My Game")
24
+ scene = Scene("Main")
25
+ cam = Camera(position=(0, 2, 6), yaw=-90)
26
+ scene.set_main_camera(cam)
27
+
28
+ cube = Entity("Cube")
29
+ cube.add_component(Transform(position=(0, 1, 0)))
30
+ mat = Material(); mat.albedo[:] = (0.2, 0.9, 0.3)
31
+ cube.add_component(MeshRenderer(mesh=MeshFactory.create_cube(), material=mat))
32
+ scene.spawn(cube)
33
+
34
+ engine.load_scene(scene)
35
+ engine.enable_first_person(cam) # FPS
36
+ engine.run() # ESC — выход
37
+
38
+ FPS / КАМЕРА
39
+ engine.enable_first_person(camera, eye_height=1.7, walk_speed=6)
40
+ engine.free_camera = True # WASD + Tab (мышь)
41
+
42
+ 2D РЕЖИМ
43
+ engine.set_mode("2d")
44
+ engine.renderer2d.add_sprite(Sprite(x=0, y=0, width=40, height=40, color=(1,0,0,1)))
45
+
46
+ МОДЕЛИ
47
+ from aether import ModelLoader
48
+ mesh = ModelLoader.load("hero.glb") # obj/stl/glb/gltf/fbx...
49
+
50
+ ВОКСЕЛИ (ОПТИМИЗАЦИЯ)
51
+ from aether.graphics.voxel_mesh import ChunkedVoxelMesher
52
+ mesher = ChunkedVoxelMesher(world.get_block)
53
+ mesher.notify_block_change(x, y, z)
54
+ meshes = mesher.flush_dirty() # только изменённые чанки
55
+
56
+ ФИЗИКА
57
+ engine.physics3d.create_body_for_entity(ent, collider=BoxCollider())
58
+ engine.physics2d.create_box(x, y, w, h, mass=1.0)
59
+
60
+ ВВОД
61
+ import glfw
62
+ engine.input.get_key(glfw.KEY_W)
63
+ engine.input.get_key_down(glfw.KEY_SPACE)
64
+
65
+ АПДЕЙТ
66
+ engine.on_update(lambda dt: ...)
67
+
68
+ СБОРКА В EXE
69
+ aether build my_game.py
70
+ aether build my_game.py --name MyGame --console
71
+
72
+ ИНСТРУМЕНТЫ
73
+ aether help — эта справка
74
+ aether docs — краткий гайд по API
75
+ aether-editor — 3D редактор моделей
76
+ python games/aether_craft.py
77
+
78
+ ПОЛНАЯ ДОКУМЕНТАЦИЯ: docs/GUIDE.md
79
+ PyPI: https://pypi.org/project/aether-engine/
80
+ """
81
+
82
+
83
+ def cmd_help(_args):
84
+ print(QUICK_REFERENCE)
85
+ return 0
86
+
87
+
88
+ def cmd_docs(_args):
89
+ guide = Path(__file__).resolve().parents[2] / "docs" / "GUIDE.md"
90
+ if guide.exists():
91
+ print(guide.read_text(encoding="utf-8"))
92
+ else:
93
+ print(QUICK_REFERENCE)
94
+ return 0
95
+
96
+
97
+ def cmd_build(args):
98
+ from aether.tools.packager import build_exe
99
+
100
+ return build_exe(
101
+ args.script,
102
+ name=args.name,
103
+ onefile=not args.dir,
104
+ windowed=not args.console,
105
+ icon=args.icon,
106
+ )
107
+
108
+
109
+ def cmd_editor(_args):
110
+ from aether.editor.model_editor import main as editor_main
111
+
112
+ editor_main()
113
+ return 0
114
+
115
+
116
+ def main(argv=None):
117
+ parser = argparse.ArgumentParser(
118
+ prog="aether",
119
+ description=f"{engine_name()} CLI by {creator()}",
120
+ )
121
+ parser.add_argument("--version", action="version", version=f"{engine_name()} {__version__}")
122
+
123
+ sub = parser.add_subparsers(dest="command")
124
+
125
+ sub.add_parser("help", help="Краткая справка по API и командам").set_defaults(func=cmd_help)
126
+ sub.add_parser("docs", help="Полная документация").set_defaults(func=cmd_docs)
127
+ sub.add_parser("editor", help="3D редактор моделей").set_defaults(func=cmd_editor)
128
+
129
+ p_build = sub.add_parser("build", help="Собрать игру в .exe (PyInstaller)")
130
+ p_build.add_argument("script", help="Путь к main.py игры")
131
+ p_build.add_argument("--name", help="Имя exe", default=None)
132
+ p_build.add_argument("--console", action="store_true", help="Показать консоль")
133
+ p_build.add_argument("--dir", action="store_true", help="Папка вместо onefile")
134
+ p_build.add_argument("--icon", help="Иконка .ico", default=None)
135
+ p_build.set_defaults(func=cmd_build)
136
+
137
+ args = parser.parse_args(argv)
138
+ if not args.command:
139
+ cmd_help(args)
140
+ return 0
141
+ return args.func(args)
142
+
143
+
144
+ if __name__ == "__main__":
145
+ sys.exit(main())
@@ -21,7 +21,7 @@ _B = {
21
21
  ]),
22
22
  "sig": bytes([
23
23
  230, 194, 211, 207, 194, 213, 135, 226, 201, 192, 206, 201, 194,
24
- 135, 150, 137, 148, 137, 151,
24
+ 135, 150, 137, 147, 137, 151,
25
25
  ]),
26
26
  }
27
27
 
@@ -0,0 +1,217 @@
1
+ """UE-style boot splash — fade-in text on black, preload only."""
2
+ from __future__ import annotations
3
+
4
+ import math
5
+ import time
6
+
7
+ import glfw
8
+ import numpy as np
9
+ from OpenGL.GL import *
10
+ from PIL import Image, ImageDraw, ImageFont
11
+
12
+ from aether.core._identity import creator, splash_line1, splash_line2, verify_token
13
+ from aether.utils.fonts import resolve_system_font
14
+
15
+ _SPLASH_VERT = """
16
+ #version 330 core
17
+ layout(location = 0) in vec2 aPos;
18
+ layout(location = 1) in vec2 aUV;
19
+ out vec2 vUV;
20
+ uniform float uAlpha;
21
+ void main() {
22
+ vUV = aUV;
23
+ gl_Position = vec4(aPos, 0.0, 1.0);
24
+ }
25
+ """
26
+
27
+ _SPLASH_FRAG = """
28
+ #version 330 core
29
+ in vec2 vUV;
30
+ out vec4 FragColor;
31
+ uniform sampler2D uTex;
32
+ uniform float uAlpha;
33
+ void main() {
34
+ vec4 c = texture(uTex, vUV);
35
+ FragColor = vec4(c.rgb, c.a * uAlpha);
36
+ }
37
+ """
38
+
39
+
40
+ class BootSplash:
41
+ """Animated preload splash (Unreal-style fade). Shown ONLY during Engine init."""
42
+
43
+ MIN_SHOW_SEC = 2.4
44
+ FADE_IN_SEC = 0.85
45
+ FADE_OUT_SEC = 0.55
46
+
47
+ def __init__(self, window):
48
+ self.window = window
49
+ self.width = window.width
50
+ self.height = window.height
51
+ self._started = time.monotonic()
52
+ self._progress = 0.0
53
+ self._status = ""
54
+ self._font = None
55
+ self._font_small = None
56
+ self._load_fonts()
57
+ self._init_gl()
58
+ self.draw(0.0, "Загрузка...")
59
+
60
+ def _load_fonts(self):
61
+ path = resolve_system_font()
62
+ if not path:
63
+ return
64
+ try:
65
+ self._font = ImageFont.truetype(path, 36)
66
+ self._font_small = ImageFont.truetype(path, 22)
67
+ except Exception:
68
+ self._font = ImageFont.load_default()
69
+ self._font_small = self._font
70
+
71
+ def _init_gl(self):
72
+ from aether.graphics.shader import Shader
73
+
74
+ self._shader = Shader(_SPLASH_VERT, _SPLASH_FRAG)
75
+ self._vao = glGenVertexArrays(1)
76
+ self._vbo = glGenBuffers(1)
77
+ self._tex = glGenTextures(1)
78
+ quad = np.array(
79
+ [
80
+ -1, -1, 0, 0,
81
+ 1, -1, 1, 0,
82
+ 1, 1, 1, 1,
83
+ -1, -1, 0, 0,
84
+ 1, 1, 1, 1,
85
+ -1, 1, 0, 1,
86
+ ],
87
+ dtype=np.float32,
88
+ )
89
+ glBindVertexArray(self._vao)
90
+ glBindBuffer(GL_ARRAY_BUFFER, self._vbo)
91
+ glBufferData(GL_ARRAY_BUFFER, quad.nbytes, quad, GL_STATIC_DRAW)
92
+ glEnableVertexAttribArray(0)
93
+ glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 16, ctypes.c_void_p(0))
94
+ glEnableVertexAttribArray(1)
95
+ glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 16, ctypes.c_void_p(8))
96
+ glBindVertexArray(0)
97
+
98
+ def _fade_alpha(self) -> float:
99
+ t = time.monotonic() - self._started
100
+ if t < self.FADE_IN_SEC:
101
+ return self._ease_out(t / self.FADE_IN_SEC)
102
+ if self._progress >= 0.99 and t > self.MIN_SHOW_SEC:
103
+ out_t = t - self.MIN_SHOW_SEC
104
+ if out_t < self.FADE_OUT_SEC:
105
+ return 1.0 - self._ease_in(out_t / self.FADE_OUT_SEC)
106
+ return 0.0
107
+ return 1.0
108
+
109
+ @staticmethod
110
+ def _ease_out(x: float) -> float:
111
+ return 1.0 - (1.0 - x) ** 3
112
+
113
+ @staticmethod
114
+ def _ease_in(x: float) -> float:
115
+ return x * x * x
116
+
117
+ def _compose_image(self, alpha: float) -> np.ndarray:
118
+ w, h = self.width, self.height
119
+ img = Image.new("RGBA", (w, h), (0, 0, 0, 255))
120
+
121
+ if self._font:
122
+ overlay = Image.new("RGBA", (w, h), (0, 0, 0, 0))
123
+ draw = ImageDraw.Draw(overlay)
124
+
125
+ line1 = splash_line1()
126
+ line2 = splash_line2()
127
+ a = int(255 * max(0.0, min(1.0, alpha)))
128
+
129
+ # Slight slide-up like UE
130
+ slide = int((1.0 - min(1.0, alpha * 1.2)) * 28)
131
+
132
+ def tw(text, font):
133
+ bb = draw.textbbox((0, 0), text, font=font)
134
+ return bb[2] - bb[0], bb[3] - bb[1]
135
+
136
+ t1w, t1h = tw(line1, self._font)
137
+ t2w, t2h = tw(line2, self._font_small)
138
+ cy = h // 2 - slide
139
+
140
+ draw.text(((w - t1w) // 2, cy - t1h - 8), line1, font=self._font, fill=(255, 255, 255, a))
141
+ draw.text(((w - t2w) // 2, cy + 6), line2, font=self._font_small, fill=(230, 230, 230, a))
142
+
143
+ if self._status:
144
+ sw, sh = tw(self._status, self._font_small)
145
+ draw.text(
146
+ ((w - sw) // 2, cy + t2h + 36),
147
+ self._status,
148
+ font=self._font_small,
149
+ fill=(160, 160, 160, a),
150
+ )
151
+
152
+ # Progress bar
153
+ bar_w = min(420, int(w * 0.5))
154
+ bar_h = 4
155
+ bx = (w - bar_w) // 2
156
+ by = int(h * 0.62)
157
+ draw.rectangle([bx, by, bx + bar_w, by + bar_h], fill=(40, 40, 40, a))
158
+ fill = int(bar_w * self._progress)
159
+ if fill > 0:
160
+ draw.rectangle([bx, by, bx + fill, by + bar_h], fill=(255, 255, 255, a))
161
+
162
+ img = Image.alpha_composite(img, overlay)
163
+
164
+ return np.array(img, dtype=np.uint8)
165
+
166
+ def draw(self, progress: float, status: str = ""):
167
+ if not verify_token():
168
+ raise RuntimeError("Aether Engine identity verification failed")
169
+
170
+ self._progress = max(self._progress, progress)
171
+ self._status = status
172
+ alpha = self._fade_alpha()
173
+
174
+ glViewport(0, 0, self.width, self.height)
175
+ glClearColor(0, 0, 0, 1)
176
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
177
+
178
+ data = self._compose_image(alpha)
179
+ glBindTexture(GL_TEXTURE_2D, self._tex)
180
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
181
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
182
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, self.width, self.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data)
183
+
184
+ glDisable(GL_DEPTH_TEST)
185
+ glEnable(GL_BLEND)
186
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
187
+
188
+ self._shader.use()
189
+ self._shader.set_float("uAlpha", 1.0)
190
+ glActiveTexture(GL_TEXTURE0)
191
+ glBindTexture(GL_TEXTURE_2D, self._tex)
192
+ self._shader.set_int("uTex", 0)
193
+
194
+ glBindVertexArray(self._vao)
195
+ glDrawArrays(GL_TRIANGLES, 0, 6)
196
+ glBindVertexArray(0)
197
+
198
+ glDisable(GL_BLEND)
199
+ glEnable(GL_DEPTH_TEST)
200
+
201
+ self.window.swap_buffers()
202
+ glfw.poll_events()
203
+
204
+ def step(self, progress: float, status: str):
205
+ self.draw(progress, status)
206
+ time.sleep(0.016)
207
+
208
+ def finish(self):
209
+ # Fade-out animation
210
+ t0 = time.monotonic()
211
+ while time.monotonic() - t0 < self.FADE_OUT_SEC + 0.05:
212
+ self._progress = 1.0
213
+ self.draw(1.0, "Готово")
214
+ self.draw(1.0, "")
215
+
216
+
217
+ import ctypes # noqa: E402
@@ -0,0 +1,32 @@
1
+ """Runtime integrity checks — no on-screen watermark during gameplay."""
2
+ from __future__ import annotations
3
+
4
+ from aether.core._identity import verify_token
5
+
6
+
7
+ class BrandingGuard:
8
+ """Silent integrity guard. Branding is shown only on boot splash."""
9
+
10
+ _tamper_count = 0
11
+
12
+ def __init__(self, engine):
13
+ self.engine = engine
14
+ self._frame = 0
15
+
16
+ @classmethod
17
+ def assert_integrity(cls, engine) -> bool:
18
+ if not verify_token():
19
+ cls._tamper_count += 1
20
+ return False
21
+ if getattr(engine, "_brand_bypass", False):
22
+ cls._tamper_count += 1
23
+ engine._brand_bypass = False
24
+ return False
25
+ return True
26
+
27
+ def tick(self):
28
+ self._frame += 1
29
+ if self._frame % 120 == 0 and not verify_token():
30
+ raise RuntimeError(
31
+ "Aether Engine: integrity check failed. Reinstall: pip install -U aether-engine"
32
+ )
@@ -276,7 +276,7 @@ class Engine:
276
276
  traceback.print_exc()
277
277
 
278
278
  if self._frame_count % 180 == 0 and not BrandingGuard.assert_integrity(self):
279
- self._branding._force_overlay = True
279
+ raise RuntimeError("Aether Engine integrity check failed")
280
280
 
281
281
  profiler.end_frame()
282
282
  self._frame_count += 1
@@ -360,7 +360,8 @@ class Engine:
360
360
  self.renderer2d.begin_frame(clear=True)
361
361
  self.renderer2d.render()
362
362
  self.renderer2d.end_frame()
363
- self._render_branding()
363
+ if hasattr(self, "_branding") and self._branding:
364
+ self._branding.tick()
364
365
  return
365
366
 
366
367
  self.renderer.begin_frame()
@@ -380,23 +381,6 @@ class Engine:
380
381
  self.renderer2d.render()
381
382
  self.renderer2d.end_frame()
382
383
 
383
- self._render_branding()
384
-
385
- def _render_branding(self):
386
- """Mandatory watermark — always drawn."""
387
- if not hasattr(self, "_branding") or self._branding is None:
388
- return
389
- try:
390
- from OpenGL.GL import glDisable, glEnable, GL_BLEND, GL_DEPTH_TEST
391
-
392
- glDisable(GL_DEPTH_TEST)
393
- glEnable(GL_BLEND)
394
- self._branding.draw_watermark()
395
- self._branding.tick()
396
- glEnable(GL_DEPTH_TEST)
397
- except Exception:
398
- pass
399
-
400
384
  def on_update(self, callback):
401
385
  """Регистрирует callback(dt), вызываемый каждый кадр."""
402
386
  self._update_callbacks.append(callback)
@@ -75,6 +75,27 @@ class Mesh:
75
75
  glBindBuffer(GL_ARRAY_BUFFER, 0)
76
76
  self._gpu_ready = True
77
77
 
78
+ def update_data(self, vertices, indices=None):
79
+ """Update GPU buffers in-place (fast path for voxel edits)."""
80
+ self.vertices = np.array(vertices, dtype=np.float32).reshape(-1)
81
+ if indices is not None:
82
+ self.indices = np.array(indices, dtype=np.uint32)
83
+ self.vertex_count = len(self.vertices) // 8
84
+ self.index_count = 0 if self.indices is None else len(self.indices)
85
+
86
+ if not self._gpu_ready:
87
+ self._create_gl_buffers()
88
+ return
89
+
90
+ glBindBuffer(GL_ARRAY_BUFFER, self.vbo)
91
+ glBufferData(GL_ARRAY_BUFFER, self.vertices.nbytes, self.vertices, GL_DYNAMIC_DRAW)
92
+ if self.indices is not None and self.index_count > 0:
93
+ if not self.ebo:
94
+ self.ebo = glGenBuffers(1)
95
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self.ebo)
96
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, self.indices.nbytes, self.indices, GL_DYNAMIC_DRAW)
97
+ glBindVertexArray(0)
98
+
78
99
  def draw(self):
79
100
  self.ensure_gpu()
80
101
  if not self.vao: