aether-engine 1.7.0__tar.gz → 1.8.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 (124) hide show
  1. {aether_engine-1.7.0/aether_engine.egg-info → aether_engine-1.8.0}/PKG-INFO +35 -44
  2. aether_engine-1.8.0/README.md +74 -0
  3. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/__init__.py +1 -1
  4. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/cli.py +33 -3
  5. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/core/_identity.py +1 -1
  6. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/graphics/camera.py +38 -9
  7. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/graphics/mesh_factory.py +68 -0
  8. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/scene/component.py +3 -3
  9. aether_engine-1.8.0/aether/scene/scene.py +55 -0
  10. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/ui/hud.py +30 -0
  11. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/web/content.py +204 -53
  12. aether_engine-1.8.0/aether/web/plain_content.py +248 -0
  13. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/web/registry.py +12 -11
  14. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/web/templates.py +5 -2
  15. {aether_engine-1.7.0 → aether_engine-1.8.0/aether_engine.egg-info}/PKG-INFO +35 -44
  16. {aether_engine-1.7.0 → aether_engine-1.8.0}/examples/01_hello_cube.py +1 -2
  17. {aether_engine-1.7.0 → aether_engine-1.8.0}/examples/02_sphere_scene.py +1 -2
  18. {aether_engine-1.7.0 → aether_engine-1.8.0}/examples/03_first_person.py +2 -1
  19. {aether_engine-1.7.0 → aether_engine-1.8.0}/setup.py +1 -1
  20. aether_engine-1.7.0/README.md +0 -83
  21. aether_engine-1.7.0/aether/scene/scene.py +0 -33
  22. aether_engine-1.7.0/aether/web/plain_content.py +0 -166
  23. {aether_engine-1.7.0 → aether_engine-1.8.0}/LICENSE +0 -0
  24. {aether_engine-1.7.0 → aether_engine-1.8.0}/MANIFEST.in +0 -0
  25. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/__main__.py +0 -0
  26. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/accelerators/__init__.py +0 -0
  27. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/accelerators/gameplay.py +0 -0
  28. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/accelerators/voxel.py +0 -0
  29. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/ai/behavior_tree.py +0 -0
  30. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/ai/navmesh.py +0 -0
  31. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/animation/skeleton.py +0 -0
  32. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/assets/__pycache__/asset_pipeline.cpython-314.pyc +0 -0
  33. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/assets/asset_pipeline.py +0 -0
  34. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/assets/fonts/AetherSans.ttf +0 -0
  35. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/audio/__init__.py +0 -0
  36. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/audio/audio_manager.py +0 -0
  37. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/audio/music.py +0 -0
  38. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/audio/sfx.py +0 -0
  39. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/audio/sound.py +0 -0
  40. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/core/__init__.py +0 -0
  41. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/core/achievement_system.py +0 -0
  42. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/core/application.py +0 -0
  43. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/core/boot_splash.py +0 -0
  44. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/core/branding_guard.py +0 -0
  45. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/core/config.py +0 -0
  46. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/core/console.py +0 -0
  47. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/core/engine.py +0 -0
  48. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/core/event_bus.py +0 -0
  49. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/core/input.py +0 -0
  50. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/core/plugin_system.py +0 -0
  51. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/core/save_system.py +0 -0
  52. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/core/time_manager.py +0 -0
  53. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/core/window.py +0 -0
  54. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/editor/__init__.py +0 -0
  55. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/environment/environment_system.py +0 -0
  56. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/graphics/__init__.py +0 -0
  57. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/graphics/first_person.py +0 -0
  58. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/graphics/framebuffer.py +0 -0
  59. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/graphics/light.py +0 -0
  60. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/graphics/lod_system.py +0 -0
  61. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/graphics/material.py +0 -0
  62. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/graphics/mesh.py +0 -0
  63. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/graphics/particle_system.py +0 -0
  64. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/graphics/post_process.py +0 -0
  65. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/graphics/renderer.py +0 -0
  66. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/graphics/shader.py +0 -0
  67. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/graphics/shader_library.py +0 -0
  68. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/graphics/sky_atmosphere.py +0 -0
  69. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/graphics/terrain.py +0 -0
  70. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/graphics/texture.py +0 -0
  71. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/graphics/voxel_mesh.py +0 -0
  72. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/graphics/water.py +0 -0
  73. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/graphics2d/__init__.py +0 -0
  74. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/network/network_manager.py +0 -0
  75. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/physics/__init__.py +0 -0
  76. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/physics/bullet_backend.py +0 -0
  77. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/physics/collider.py +0 -0
  78. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/physics/physics2d.py +0 -0
  79. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/physics/physics3d.py +0 -0
  80. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/physics/physics_world.py +0 -0
  81. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/physics/raycast.py +0 -0
  82. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/physics/rigidbody.py +0 -0
  83. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/platform/steam_api.py +0 -0
  84. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/resources/__init__.py +0 -0
  85. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/resources/asset_database.py +0 -0
  86. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/resources/asset_loader.py +0 -0
  87. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/resources/loaders/model_loader.py +0 -0
  88. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/resources/loaders/obj_loader.py +0 -0
  89. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/resources/loaders/texture_loader.py +0 -0
  90. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/resources/resource_manager.py +0 -0
  91. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/resources/save_load.py +0 -0
  92. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/resources/serialization.py +0 -0
  93. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/scene/__init__.py +0 -0
  94. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/scene/entity.py +0 -0
  95. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/scene/scene_graph.py +0 -0
  96. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/scene/scene_manager.py +0 -0
  97. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/tools/__init__.py +0 -0
  98. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/tools/packager.py +0 -0
  99. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/ui/__init__.py +0 -0
  100. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/ui/button.py +0 -0
  101. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/ui/canvas.py +0 -0
  102. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/ui/text.py +0 -0
  103. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/utils/__init__.py +0 -0
  104. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/utils/fonts.py +0 -0
  105. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/utils/logger.py +0 -0
  106. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/utils/math_utils.py +0 -0
  107. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/utils/object_pool.py +0 -0
  108. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/utils/profiler.py +0 -0
  109. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/web/__init__.py +0 -0
  110. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/web/bot_detect.py +0 -0
  111. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/web/config.py +0 -0
  112. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/web/notifications.py +0 -0
  113. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/web/plain.py +0 -0
  114. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/web/secrets.py +0 -0
  115. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/web/server.py +0 -0
  116. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether/web/urls.py +0 -0
  117. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether_engine.egg-info/SOURCES.txt +0 -0
  118. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether_engine.egg-info/dependency_links.txt +0 -0
  119. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether_engine.egg-info/entry_points.txt +0 -0
  120. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether_engine.egg-info/requires.txt +4 -4
  121. {aether_engine-1.7.0 → aether_engine-1.8.0}/aether_engine.egg-info/top_level.txt +0 -0
  122. {aether_engine-1.7.0 → aether_engine-1.8.0}/examples/04_physics2d.py +0 -0
  123. {aether_engine-1.7.0 → aether_engine-1.8.0}/pyproject.toml +0 -0
  124. {aether_engine-1.7.0 → aether_engine-1.8.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aether-engine
3
- Version: 1.7.0
3
+ Version: 1.8.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
@@ -31,13 +31,13 @@ Requires-Dist: trimesh>=3.21; extra == "models"
31
31
  Provides-Extra: build
32
32
  Requires-Dist: pyinstaller>=6.0; extra == "build"
33
33
  Provides-Extra: full
34
- Requires-Dist: freetype-py>=2.4; extra == "full"
35
- Requires-Dist: imageio>=2.31; extra == "full"
36
34
  Requires-Dist: pygame-ce>=2.5; extra == "full"
37
- Requires-Dist: pyyaml>=6.0; extra == "full"
38
35
  Requires-Dist: trimesh>=3.21; extra == "full"
39
- Requires-Dist: pymunk>=6.0; extra == "full"
36
+ Requires-Dist: imageio>=2.31; extra == "full"
40
37
  Requires-Dist: pyinstaller>=6.0; extra == "full"
38
+ Requires-Dist: pyyaml>=6.0; extra == "full"
39
+ Requires-Dist: pymunk>=6.0; extra == "full"
40
+ Requires-Dist: freetype-py>=2.4; extra == "full"
41
41
  Dynamic: author
42
42
  Dynamic: classifier
43
43
  Dynamic: description
@@ -52,10 +52,10 @@ Dynamic: summary
52
52
  # Aether Engine
53
53
 
54
54
  **3D / 2D игровой движок на Python + OpenGL**
55
- **Разработчик:** entiti937 · **Версия:** 1.7.0 · **Лицензия:** MIT
55
+ **Разработчик:** entiti937 · **Версия:** 1.8.0 · **Лицензия:** MIT
56
56
  **PyPI:** https://pypi.org/project/aether-engine/
57
57
 
58
- > Полное руководство: [`docs/GUIDE.md`](docs/GUIDE.md)
58
+ > Полное руководство: [`docs/GUIDE.md`](docs/GUIDE.md) · сайт: `aether website`
59
59
 
60
60
  ---
61
61
 
@@ -65,38 +65,26 @@ Dynamic: summary
65
65
  pip install -U "aether-engine[full]"
66
66
  ```
67
67
 
68
- ## Что нового в 1.7.0
69
-
70
- - **AssetLoader** — загрузка любых моделей, спрайтов и текстур (OBJ/GLB/FBX/PNG/WebP/…)
71
- - `Sprite.from_image(path)` — быстрый импорт 2D-графики
72
- - Встроенный редактор **убран** (может вернуться в будущих версиях)
73
-
74
- ## Showcase-игра
75
-
76
- Minecraft-like sandbox **на этом же движке**:
77
-
78
- ```bash
79
- aether help
80
- aether build games/aether_craft.py --name AetherCraft
81
- python games/aether_craft.py
68
+ ```python
69
+ import aether # правильно
70
+ # import aether_engine # НЕПРАВИЛЬНО — такого модуля нет
82
71
  ```
83
72
 
84
- Там есть: воксельный мир, вид от 1 лица, ломать/ставить блоки, HUD, звуки, день/ночь, сейвы (F5/F9), урон и респавн.
85
-
86
- ## Другие запуски
73
+ ## Что нового в 1.8.0
87
74
 
88
- ```bash
89
- python games/orb_rush.py
90
- python examples/03_first_person.py
91
- python examples/04_physics2d.py
92
- aether website
93
- ```
75
+ - `Scene.remove()` / `despawn()` / `find()`
76
+ - `MeshFactory.create_cylinder(...)`
77
+ - `Camera.look_at(target)` + безопасное присвоение `cam.position = (x,y,z)`
78
+ - HUD: `draw_rect`, `draw_panel`
79
+ - Полный **API Reference** в документации (и `/plain/all` для ИИ)
94
80
 
95
81
  ## Минимальный код
96
82
 
97
83
  ```python
98
- from aether import Engine, Scene, Entity, MeshFactory, Material, Camera
99
- from aether.scene.component import Transform, MeshRenderer
84
+ from aether import (
85
+ Engine, Scene, Entity, Transform, MeshRenderer,
86
+ MeshFactory, Material, Camera,
87
+ )
100
88
 
101
89
  engine = Engine(1280, 720, "Game by you — engine by entiti937")
102
90
  scene = Scene("Main")
@@ -105,7 +93,8 @@ scene.set_main_camera(camera)
105
93
 
106
94
  cube = Entity("Cube")
107
95
  cube.add_component(Transform(position=(0, 1, 0)))
108
- mat = Material(); mat.albedo[:] = (0.2, 0.85, 1.0)
96
+ mat = Material()
97
+ mat.albedo[:] = (0.2, 0.85, 1.0)
109
98
  cube.add_component(MeshRenderer(mesh=MeshFactory.create_cube(), material=mat))
110
99
  scene.spawn(cube)
111
100
 
@@ -114,20 +103,22 @@ engine.enable_first_person(camera)
114
103
  engine.run()
115
104
  ```
116
105
 
117
- ## Возможности
118
-
119
- - PBR 3D + sprites 2D + HUD overlay
120
- - FPS (`enable_first_person`) и free-look камера
121
- - Физика 3D/2D + raycast
122
- - `AssetLoader` / `ModelLoader` — OBJ/GLB/glTF/STL/FBX/STEP/… + PNG/WebP спрайты
123
- - Procedural SFX, JSON save/load
124
- - Примеры и игры в репозитории
106
+ ## Частые операции
125
107
 
126
- > Встроенный редактор моделей/пиксель-арт **временно убран** — планируется вернуть позже. Сейчас используйте Blender, Aseprite и т.д., файлы подгружайте через `AssetLoader`.
108
+ ```python
109
+ scene.remove(entity) # удалить объект
110
+ MeshFactory.create_cylinder(0.5, 2) # цилиндр
111
+ cam.look_at(player_tf.position) # смотреть на игрока
112
+ engine.free_camera = False # своя камера (не WASD fly)
113
+ ```
127
114
 
128
- ## Документация
115
+ ## Возможности
129
116
 
130
- Все команды, API и туториалы: **[docs/GUIDE.md](docs/GUIDE.md)**
117
+ - PBR 3D + sprites 2D + HUD overlay
118
+ - FPS / free-look / `look_at` follow-камера
119
+ - Физика 2D (pymunk) + лёгкий 3D solver
120
+ - `AssetLoader` — модели и спрайты
121
+ - `aether website` + plain docs для LLM
131
122
 
132
123
  ---
133
124
 
@@ -0,0 +1,74 @@
1
+ # Aether Engine
2
+
3
+ **3D / 2D игровой движок на Python + OpenGL**
4
+ **Разработчик:** entiti937 · **Версия:** 1.8.0 · **Лицензия:** MIT
5
+ **PyPI:** https://pypi.org/project/aether-engine/
6
+
7
+ > Полное руководство: [`docs/GUIDE.md`](docs/GUIDE.md) · сайт: `aether website`
8
+
9
+ ---
10
+
11
+ ## Установка
12
+
13
+ ```bash
14
+ pip install -U "aether-engine[full]"
15
+ ```
16
+
17
+ ```python
18
+ import aether # правильно
19
+ # import aether_engine # НЕПРАВИЛЬНО — такого модуля нет
20
+ ```
21
+
22
+ ## Что нового в 1.8.0
23
+
24
+ - `Scene.remove()` / `despawn()` / `find()`
25
+ - `MeshFactory.create_cylinder(...)`
26
+ - `Camera.look_at(target)` + безопасное присвоение `cam.position = (x,y,z)`
27
+ - HUD: `draw_rect`, `draw_panel`
28
+ - Полный **API Reference** в документации (и `/plain/all` для ИИ)
29
+
30
+ ## Минимальный код
31
+
32
+ ```python
33
+ from aether import (
34
+ Engine, Scene, Entity, Transform, MeshRenderer,
35
+ MeshFactory, Material, Camera,
36
+ )
37
+
38
+ engine = Engine(1280, 720, "Game by you — engine by entiti937")
39
+ scene = Scene("Main")
40
+ camera = Camera(position=(0, 2, 6), yaw=-90)
41
+ scene.set_main_camera(camera)
42
+
43
+ cube = Entity("Cube")
44
+ cube.add_component(Transform(position=(0, 1, 0)))
45
+ mat = Material()
46
+ mat.albedo[:] = (0.2, 0.85, 1.0)
47
+ cube.add_component(MeshRenderer(mesh=MeshFactory.create_cube(), material=mat))
48
+ scene.spawn(cube)
49
+
50
+ engine.load_scene(scene)
51
+ engine.enable_first_person(camera)
52
+ engine.run()
53
+ ```
54
+
55
+ ## Частые операции
56
+
57
+ ```python
58
+ scene.remove(entity) # удалить объект
59
+ MeshFactory.create_cylinder(0.5, 2) # цилиндр
60
+ cam.look_at(player_tf.position) # смотреть на игрока
61
+ engine.free_camera = False # своя камера (не WASD fly)
62
+ ```
63
+
64
+ ## Возможности
65
+
66
+ - PBR 3D + sprites 2D + HUD overlay
67
+ - FPS / free-look / `look_at` follow-камера
68
+ - Физика 2D (pymunk) + лёгкий 3D solver
69
+ - `AssetLoader` — модели и спрайты
70
+ - `aether website` + plain docs для LLM
71
+
72
+ ---
73
+
74
+ Aether Engine © **entiti937**
@@ -31,7 +31,7 @@ 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.7.0"
34
+ __version__ = "1.8.0"
35
35
 
36
36
  from aether.graphics.voxel_mesh import ChunkedVoxelMesher
37
37
  from aether.accelerators import is_native_available, backend_name
@@ -15,10 +15,11 @@ QUICK_REFERENCE = f"""
15
15
 
16
16
  УСТАНОВКА
17
17
  pip install -U "aether-engine[full]"
18
+ # пакет на PyPI: aether-engine
19
+ # импорт в коде: import aether (НЕ aether_engine)
18
20
 
19
21
  МИНИМАЛЬНЫЙ КОД (3D)
20
- from aether import Engine, Scene, Entity, MeshFactory, Material, Camera
21
- from aether.scene.component import Transform, MeshRenderer
22
+ from aether import Engine, Scene, Entity, Transform, MeshRenderer, MeshFactory, Material, Camera
22
23
 
23
24
  engine = Engine(1280, 720, "My Game")
24
25
  scene = Scene("Main")
@@ -27,7 +28,8 @@ QUICK_REFERENCE = f"""
27
28
 
28
29
  cube = Entity("Cube")
29
30
  cube.add_component(Transform(position=(0, 1, 0)))
30
- mat = Material(); mat.albedo[:] = (0.2, 0.9, 0.3)
31
+ mat = Material()
32
+ mat.albedo[:] = (0.2, 0.9, 0.3)
31
33
  cube.add_component(MeshRenderer(mesh=MeshFactory.create_cube(), material=mat))
32
34
  scene.spawn(cube)
33
35
 
@@ -35,6 +37,34 @@ QUICK_REFERENCE = f"""
35
37
  engine.enable_first_person(cam) # FPS
36
38
  engine.run() # ESC — выход
37
39
 
40
+ УДАЛЕНИЕ ОБЪЕКТОВ
41
+ scene.remove(entity) # убрать из сцены + destroy()
42
+ # или entity.destroy() # только компоненты (лучше scene.remove)
43
+
44
+ ПРИМИТИВЫ MeshFactory
45
+ create_cube(size=1)
46
+ create_plane(size=10)
47
+ create_sphere(radius=1, sectors=24, stacks=16)
48
+ create_cylinder(radius=0.5, height=1, segments=24)
49
+ create_pyramid(size=1, height=1)
50
+
51
+ КАМЕРА
52
+ cam.position = (x, y, z) # numpy array под капотом
53
+ cam.yaw, cam.pitch, cam.fov
54
+ cam.look_at(target) # смотреть на точку/игрока
55
+ cam.rotate(dx, dy)
56
+ engine.free_camera = False # если управляете камерой сами
57
+
58
+ TRANSFORM
59
+ tf.position # np.ndarray float32, можно tf.position[:] = (x,y,z)
60
+ tf.set_position(x,y,z) / set_rotation / set_scale
61
+ tf.translate(dx,dy,dz)
62
+
63
+ HUD
64
+ engine.draw_hud = True
65
+ hud = Hud(engine.renderer2d)
66
+ hud.clear(); hud.draw_crosshair(); hud.draw_bar(...); hud.draw_hotbar(...)
67
+
38
68
  FPS / КАМЕРА
39
69
  engine.enable_first_person(camera, eye_height=1.7, walk_speed=6)
40
70
  engine.free_camera = True # WASD + Tab (мышь)
@@ -20,7 +20,7 @@ _B = {
20
20
  ]),
21
21
  "sig": bytes([
22
22
  230, 194, 211, 207, 194, 213, 135, 226, 201, 192, 206, 201, 194,
23
- 135, 150, 137, 144, 137, 151,
23
+ 135, 150, 137, 159, 137, 151,
24
24
  ]),
25
25
  }
26
26
 
@@ -1,10 +1,16 @@
1
1
  """Камера"""
2
+ from __future__ import annotations
3
+
4
+ from typing import Sequence, Union
5
+
2
6
  import numpy as np
3
7
 
8
+ Vec3 = Union[Sequence[float], np.ndarray]
9
+
4
10
 
5
11
  class Camera:
6
12
  def __init__(self, position=(0, 2, 10), yaw=-90, pitch=0, fov=60):
7
- self.position = np.array(position, dtype=np.float32)
13
+ self._position = np.array(position, dtype=np.float32)
8
14
  self.yaw = float(yaw)
9
15
  self.pitch = float(pitch)
10
16
  self.fov = float(fov)
@@ -15,6 +21,17 @@ class Camera:
15
21
 
16
22
  self._update_vectors()
17
23
 
24
+ @property
25
+ def position(self) -> np.ndarray:
26
+ return self._position
27
+
28
+ @position.setter
29
+ def position(self, value: Vec3):
30
+ self._position = np.asarray(value, dtype=np.float32).reshape(3)
31
+
32
+ def set_position(self, x: float, y: float, z: float):
33
+ self._position[:] = (x, y, z)
34
+
18
35
  def _update_vectors(self):
19
36
  front = np.array(
20
37
  [
@@ -36,12 +53,24 @@ class Camera:
36
53
  self.right = right
37
54
  self.up = np.cross(self.right, self.front)
38
55
 
56
+ def look_at(self, target: Vec3, world_up=(0.0, 1.0, 0.0)):
57
+ """Point the camera at a world-space target (updates yaw/pitch)."""
58
+ target = np.asarray(target, dtype=np.float32).reshape(3)
59
+ direction = target - self._position
60
+ length = float(np.linalg.norm(direction))
61
+ if length < 1e-6:
62
+ return
63
+ direction = direction / length
64
+ self.pitch = float(np.degrees(np.arcsin(np.clip(direction[1], -1.0, 1.0))))
65
+ self.yaw = float(np.degrees(np.arctan2(direction[2], direction[0])))
66
+ self.pitch = max(-89.0, min(89.0, self.pitch))
67
+ self._update_vectors()
68
+
39
69
  def get_view_matrix(self) -> np.ndarray:
40
- # Column-vector math matrix (uploaded with GL_TRUE / row-major).
41
70
  s = self.right
42
71
  u = self.up
43
72
  f = self.front
44
- p = self.position
73
+ p = self._position
45
74
 
46
75
  view = np.identity(4, dtype=np.float32)
47
76
  view[0, :3] = s
@@ -67,22 +96,22 @@ class Camera:
67
96
  )
68
97
 
69
98
  def move_forward(self, dt):
70
- self.position += self.front * self.speed * dt
99
+ self._position += self.front * self.speed * dt
71
100
 
72
101
  def move_backward(self, dt):
73
- self.position -= self.front * self.speed * dt
102
+ self._position -= self.front * self.speed * dt
74
103
 
75
104
  def move_left(self, dt):
76
- self.position -= self.right * self.speed * dt
105
+ self._position -= self.right * self.speed * dt
77
106
 
78
107
  def move_right(self, dt):
79
- self.position += self.right * self.speed * dt
108
+ self._position += self.right * self.speed * dt
80
109
 
81
110
  def move_up(self, dt):
82
- self.position[1] += self.speed * dt
111
+ self._position[1] += self.speed * dt
83
112
 
84
113
  def move_down(self, dt):
85
- self.position[1] -= self.speed * dt
114
+ self._position[1] -= self.speed * dt
86
115
 
87
116
  def rotate(self, dx, dy):
88
117
  self.yaw += dx * self.sensitivity
@@ -134,6 +134,74 @@ class MeshFactory:
134
134
 
135
135
  return Mesh(vertices, indices)
136
136
 
137
+ @staticmethod
138
+ def create_cylinder(
139
+ radius: float = 0.5,
140
+ height: float = 1.0,
141
+ segments: int = 24,
142
+ capped: bool = True,
143
+ ) -> Mesh:
144
+ """
145
+ Cylinder along Y axis, centered at origin.
146
+ height is full height (from -h/2 to +h/2).
147
+ """
148
+ segments = max(3, int(segments))
149
+ half = height * 0.5
150
+ vertices: list[float] = []
151
+ indices: list[int] = []
152
+
153
+ # Side rings (top and bottom edge)
154
+ for i in range(segments + 1):
155
+ angle = i * 2.0 * math.pi / segments
156
+ c = math.cos(angle)
157
+ s = math.sin(angle)
158
+ x = radius * c
159
+ z = radius * s
160
+ nx, nz = c, s
161
+ u = i / segments
162
+ # bottom
163
+ vertices.extend([x, -half, z, nx, 0.0, nz, u, 0.0])
164
+ # top
165
+ vertices.extend([x, half, z, nx, 0.0, nz, u, 1.0])
166
+
167
+ for i in range(segments):
168
+ b0 = i * 2
169
+ t0 = b0 + 1
170
+ b1 = (i + 1) * 2
171
+ t1 = b1 + 1
172
+ indices.extend([b0, b1, t1, b0, t1, t0])
173
+
174
+ if capped:
175
+ # Bottom cap center + rim
176
+ center_bottom = len(vertices) // 8
177
+ vertices.extend([0.0, -half, 0.0, 0.0, -1.0, 0.0, 0.5, 0.5])
178
+ rim_start = center_bottom + 1
179
+ for i in range(segments):
180
+ angle = i * 2.0 * math.pi / segments
181
+ x = radius * math.cos(angle)
182
+ z = radius * math.sin(angle)
183
+ vertices.extend([x, -half, z, 0.0, -1.0, 0.0, 0.5 + 0.5 * math.cos(angle), 0.5 + 0.5 * math.sin(angle)])
184
+ for i in range(segments):
185
+ i0 = rim_start + i
186
+ i1 = rim_start + ((i + 1) % segments)
187
+ indices.extend([center_bottom, i1, i0])
188
+
189
+ # Top cap
190
+ center_top = len(vertices) // 8
191
+ vertices.extend([0.0, half, 0.0, 0.0, 1.0, 0.0, 0.5, 0.5])
192
+ rim_start = center_top + 1
193
+ for i in range(segments):
194
+ angle = i * 2.0 * math.pi / segments
195
+ x = radius * math.cos(angle)
196
+ z = radius * math.sin(angle)
197
+ vertices.extend([x, half, z, 0.0, 1.0, 0.0, 0.5 + 0.5 * math.cos(angle), 0.5 + 0.5 * math.sin(angle)])
198
+ for i in range(segments):
199
+ i0 = rim_start + i
200
+ i1 = rim_start + ((i + 1) % segments)
201
+ indices.extend([center_top, i0, i1])
202
+
203
+ return Mesh(vertices, indices)
204
+
137
205
  @staticmethod
138
206
  def create_sphere(radius: float = 1.0, sectors: int = 24, stacks: int = 16) -> Mesh:
139
207
  """
@@ -84,8 +84,8 @@ class Transform(Component):
84
84
  self._matrix_dirty = False
85
85
 
86
86
  def get_model_matrix(self) -> np.ndarray:
87
- if self._matrix_dirty:
88
- self._update_matrix()
87
+ # Always rebuild so in-place edits like tf.position[0] = x stay correct
88
+ self._update_matrix()
89
89
  return self._cached_matrix
90
90
 
91
91
  def set_position(self, x, y, z):
@@ -109,7 +109,7 @@ class Transform(Component):
109
109
  self._matrix_dirty = True
110
110
 
111
111
  def look_at(self, target):
112
- target = np.asarray(target, dtype=np.float32)
112
+ target = np.asarray(target, dtype=np.float32).reshape(3)
113
113
  direction = target - self.position
114
114
  length = np.linalg.norm(direction)
115
115
  if length < 1e-6:
@@ -0,0 +1,55 @@
1
+ """Сцена"""
2
+ from typing import List, Optional
3
+ from aether.scene.entity import Entity
4
+ from aether.graphics.camera import Camera
5
+
6
+
7
+ class Scene:
8
+ def __init__(self, name="Scene"):
9
+ self.name = name
10
+ self.entities: List[Entity] = []
11
+ self.main_camera: Optional[Camera] = None
12
+
13
+ def spawn(self, entity: Entity):
14
+ """Add entity to the scene."""
15
+ if entity not in self.entities:
16
+ self.entities.append(entity)
17
+ return entity
18
+
19
+ def remove(self, entity: Entity) -> bool:
20
+ """Remove entity from the scene and call destroy() on it."""
21
+ if entity not in self.entities:
22
+ return False
23
+ self.entities.remove(entity)
24
+ entity.destroy()
25
+ return True
26
+
27
+ def despawn(self, entity: Entity) -> bool:
28
+ """Alias for remove()."""
29
+ return self.remove(entity)
30
+
31
+ def find(self, name: str) -> Optional[Entity]:
32
+ """Find first entity by name, or None."""
33
+ for ent in self.entities:
34
+ if ent.name == name:
35
+ return ent
36
+ return None
37
+
38
+ def set_main_camera(self, camera: Camera):
39
+ self.main_camera = camera
40
+
41
+ def awake(self):
42
+ pass
43
+
44
+ def start(self):
45
+ pass
46
+
47
+ def update(self, dt: float):
48
+ for entity in list(self.entities):
49
+ if entity.enabled:
50
+ entity.update(dt)
51
+
52
+ def destroy(self):
53
+ for entity in list(self.entities):
54
+ entity.destroy()
55
+ self.entities.clear()
@@ -63,3 +63,33 @@ class Hud:
63
63
  layer=81,
64
64
  )
65
65
  )
66
+
67
+ def draw_rect(
68
+ self,
69
+ x: float,
70
+ y: float,
71
+ width: float,
72
+ height: float,
73
+ color=(1.0, 1.0, 1.0, 1.0),
74
+ layer: int = 70,
75
+ ):
76
+ """Draw a solid screen-space rectangle (center at x,y)."""
77
+ self._add(Sprite(x, y, width, height, color=color, layer=layer))
78
+
79
+ def draw_panel(
80
+ self,
81
+ x: float,
82
+ y: float,
83
+ width: float,
84
+ height: float,
85
+ fill=(0.05, 0.06, 0.08, 0.75),
86
+ border=(0.9, 0.9, 0.95, 0.9),
87
+ border_thickness: float = 2.0,
88
+ ):
89
+ """Draw a simple bordered panel (no text — use window title / print for text)."""
90
+ self.draw_rect(x, y, width, height, color=fill, layer=70)
91
+ t = border_thickness
92
+ self.draw_rect(x, y + height * 0.5, width, t, color=border, layer=71)
93
+ self.draw_rect(x, y - height * 0.5, width, t, color=border, layer=71)
94
+ self.draw_rect(x - width * 0.5, y, t, height, color=border, layer=71)
95
+ self.draw_rect(x + width * 0.5, y, t, height, color=border, layer=71)