aether-engine 1.4.0__tar.gz → 1.7.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.
- {aether_engine-1.4.0 → aether_engine-1.7.0}/MANIFEST.in +1 -0
- {aether_engine-1.4.0/aether_engine.egg-info → aether_engine-1.7.0}/PKG-INFO +16 -17
- {aether_engine-1.4.0 → aether_engine-1.7.0}/README.md +11 -12
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/__init__.py +9 -3
- aether_engine-1.7.0/aether/accelerators/__init__.py +30 -0
- aether_engine-1.7.0/aether/accelerators/gameplay.py +54 -0
- aether_engine-1.4.0/aether/graphics/voxel_mesh.py → aether_engine-1.7.0/aether/accelerators/voxel.py +101 -47
- aether_engine-1.7.0/aether/assets/__pycache__/asset_pipeline.cpython-314.pyc +0 -0
- aether_engine-1.7.0/aether/assets/fonts/AetherSans.ttf +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/cli.py +35 -13
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/core/_identity.py +6 -7
- aether_engine-1.7.0/aether/core/boot_splash.py +208 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/core/engine.py +13 -17
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/core/input.py +5 -0
- aether_engine-1.7.0/aether/editor/__init__.py +3 -0
- aether_engine-1.7.0/aether/graphics/voxel_mesh.py +4 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/graphics2d/__init__.py +7 -0
- aether_engine-1.7.0/aether/resources/asset_loader.py +143 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/resources/loaders/model_loader.py +14 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/scene/component.py +8 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/utils/fonts.py +8 -0
- aether_engine-1.7.0/aether/web/__init__.py +4 -0
- aether_engine-1.7.0/aether/web/bot_detect.py +53 -0
- aether_engine-1.7.0/aether/web/config.py +61 -0
- aether_engine-1.7.0/aether/web/content.py +427 -0
- aether_engine-1.7.0/aether/web/notifications.py +43 -0
- aether_engine-1.7.0/aether/web/plain.py +57 -0
- aether_engine-1.7.0/aether/web/plain_content.py +166 -0
- aether_engine-1.7.0/aether/web/registry.py +42 -0
- aether_engine-1.7.0/aether/web/secrets.py +29 -0
- aether_engine-1.7.0/aether/web/server.py +165 -0
- aether_engine-1.7.0/aether/web/templates.py +198 -0
- aether_engine-1.7.0/aether/web/urls.py +20 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0/aether_engine.egg-info}/PKG-INFO +16 -17
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether_engine.egg-info/SOURCES.txt +18 -4
- aether_engine-1.7.0/aether_engine.egg-info/entry_points.txt +2 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/examples/03_first_person.py +1 -1
- {aether_engine-1.4.0 → aether_engine-1.7.0}/setup.py +1 -2
- aether_engine-1.4.0/aether/core/boot_splash.py +0 -217
- aether_engine-1.4.0/aether/editor/__init__.py +0 -5
- aether_engine-1.4.0/aether/editor/__main__.py +0 -6
- aether_engine-1.4.0/aether/editor/gizmo.py +0 -346
- aether_engine-1.4.0/aether/editor/imgui_integration.py +0 -468
- aether_engine-1.4.0/aether/editor/model_editor.py +0 -253
- aether_engine-1.4.0/aether_engine.egg-info/entry_points.txt +0 -3
- {aether_engine-1.4.0 → aether_engine-1.7.0}/LICENSE +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/__main__.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/ai/behavior_tree.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/ai/navmesh.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/animation/skeleton.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/assets/asset_pipeline.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/audio/__init__.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/audio/audio_manager.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/audio/music.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/audio/sfx.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/audio/sound.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/core/__init__.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/core/achievement_system.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/core/application.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/core/branding_guard.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/core/config.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/core/console.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/core/event_bus.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/core/plugin_system.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/core/save_system.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/core/time_manager.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/core/window.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/environment/environment_system.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/graphics/__init__.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/graphics/camera.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/graphics/first_person.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/graphics/framebuffer.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/graphics/light.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/graphics/lod_system.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/graphics/material.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/graphics/mesh.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/graphics/mesh_factory.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/graphics/particle_system.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/graphics/post_process.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/graphics/renderer.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/graphics/shader.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/graphics/shader_library.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/graphics/sky_atmosphere.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/graphics/terrain.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/graphics/texture.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/graphics/water.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/network/network_manager.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/physics/__init__.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/physics/bullet_backend.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/physics/collider.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/physics/physics2d.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/physics/physics3d.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/physics/physics_world.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/physics/raycast.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/physics/rigidbody.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/platform/steam_api.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/resources/__init__.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/resources/asset_database.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/resources/loaders/obj_loader.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/resources/loaders/texture_loader.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/resources/resource_manager.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/resources/save_load.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/resources/serialization.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/scene/__init__.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/scene/entity.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/scene/scene.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/scene/scene_graph.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/scene/scene_manager.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/tools/__init__.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/tools/packager.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/ui/__init__.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/ui/button.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/ui/canvas.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/ui/hud.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/ui/text.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/utils/__init__.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/utils/logger.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/utils/math_utils.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/utils/object_pool.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/utils/profiler.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether_engine.egg-info/dependency_links.txt +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether_engine.egg-info/requires.txt +4 -4
- {aether_engine-1.4.0 → aether_engine-1.7.0}/aether_engine.egg-info/top_level.txt +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/examples/01_hello_cube.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/examples/02_sphere_scene.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/examples/04_physics2d.py +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/pyproject.toml +0 -0
- {aether_engine-1.4.0 → aether_engine-1.7.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: aether-engine
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.7.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:
|
|
35
|
-
Requires-Dist: pygame-ce>=2.5; extra == "full"
|
|
34
|
+
Requires-Dist: freetype-py>=2.4; extra == "full"
|
|
36
35
|
Requires-Dist: imageio>=2.31; extra == "full"
|
|
36
|
+
Requires-Dist: pygame-ce>=2.5; extra == "full"
|
|
37
|
+
Requires-Dist: pyyaml>=6.0; extra == "full"
|
|
37
38
|
Requires-Dist: trimesh>=3.21; extra == "full"
|
|
38
|
-
Requires-Dist:
|
|
39
|
+
Requires-Dist: pymunk>=6.0; extra == "full"
|
|
39
40
|
Requires-Dist: pyinstaller>=6.0; extra == "full"
|
|
40
|
-
Requires-Dist: pyyaml>=6.0; extra == "full"
|
|
41
41
|
Dynamic: author
|
|
42
42
|
Dynamic: classifier
|
|
43
43
|
Dynamic: description
|
|
@@ -52,7 +52,7 @@ Dynamic: summary
|
|
|
52
52
|
# Aether Engine
|
|
53
53
|
|
|
54
54
|
**3D / 2D игровой движок на Python + OpenGL**
|
|
55
|
-
**Разработчик:** entiti937 · **Версия:** 1.
|
|
55
|
+
**Разработчик:** entiti937 · **Версия:** 1.7.0 · **Лицензия:** MIT
|
|
56
56
|
**PyPI:** https://pypi.org/project/aether-engine/
|
|
57
57
|
|
|
58
58
|
> Полное руководство: [`docs/GUIDE.md`](docs/GUIDE.md)
|
|
@@ -65,13 +65,11 @@ Dynamic: summary
|
|
|
65
65
|
pip install -U "aether-engine[full]"
|
|
66
66
|
```
|
|
67
67
|
|
|
68
|
-
## Что нового в 1.
|
|
68
|
+
## Что нового в 1.7.0
|
|
69
69
|
|
|
70
|
-
-
|
|
71
|
-
- `
|
|
72
|
-
-
|
|
73
|
-
- Исправлен текст splash (кириллица через Pillow)
|
|
74
|
-
- Убран постоянный водяной знак во время геймплея
|
|
70
|
+
- **AssetLoader** — загрузка любых моделей, спрайтов и текстур (OBJ/GLB/FBX/PNG/WebP/…)
|
|
71
|
+
- `Sprite.from_image(path)` — быстрый импорт 2D-графики
|
|
72
|
+
- Встроенный редактор **убран** (может вернуться в будущих версиях)
|
|
75
73
|
|
|
76
74
|
## Showcase-игра
|
|
77
75
|
|
|
@@ -88,10 +86,10 @@ python games/aether_craft.py
|
|
|
88
86
|
## Другие запуски
|
|
89
87
|
|
|
90
88
|
```bash
|
|
91
|
-
python -m aether.editor
|
|
92
89
|
python games/orb_rush.py
|
|
93
90
|
python examples/03_first_person.py
|
|
94
91
|
python examples/04_physics2d.py
|
|
92
|
+
aether website
|
|
95
93
|
```
|
|
96
94
|
|
|
97
95
|
## Минимальный код
|
|
@@ -116,15 +114,16 @@ engine.enable_first_person(camera)
|
|
|
116
114
|
engine.run()
|
|
117
115
|
```
|
|
118
116
|
|
|
119
|
-
## Возможности
|
|
117
|
+
## Возможности
|
|
120
118
|
|
|
121
119
|
- PBR 3D + sprites 2D + HUD overlay
|
|
122
120
|
- FPS (`enable_first_person`) и free-look камера
|
|
123
121
|
- Физика 3D/2D + raycast
|
|
124
|
-
- `ModelLoader`
|
|
125
|
-
- Встроенный voxel model editor
|
|
122
|
+
- `AssetLoader` / `ModelLoader` — OBJ/GLB/glTF/STL/FBX/STEP/… + PNG/WebP спрайты
|
|
126
123
|
- Procedural SFX, JSON save/load
|
|
127
|
-
- Примеры и
|
|
124
|
+
- Примеры и игры в репозитории
|
|
125
|
+
|
|
126
|
+
> Встроенный редактор моделей/пиксель-арт **временно убран** — планируется вернуть позже. Сейчас используйте Blender, Aseprite и т.д., файлы подгружайте через `AssetLoader`.
|
|
128
127
|
|
|
129
128
|
## Документация
|
|
130
129
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Aether Engine
|
|
2
2
|
|
|
3
3
|
**3D / 2D игровой движок на Python + OpenGL**
|
|
4
|
-
**Разработчик:** entiti937 · **Версия:** 1.
|
|
4
|
+
**Разработчик:** entiti937 · **Версия:** 1.7.0 · **Лицензия:** MIT
|
|
5
5
|
**PyPI:** https://pypi.org/project/aether-engine/
|
|
6
6
|
|
|
7
7
|
> Полное руководство: [`docs/GUIDE.md`](docs/GUIDE.md)
|
|
@@ -14,13 +14,11 @@
|
|
|
14
14
|
pip install -U "aether-engine[full]"
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
## Что нового в 1.
|
|
17
|
+
## Что нового в 1.7.0
|
|
18
18
|
|
|
19
|
-
-
|
|
20
|
-
- `
|
|
21
|
-
-
|
|
22
|
-
- Исправлен текст splash (кириллица через Pillow)
|
|
23
|
-
- Убран постоянный водяной знак во время геймплея
|
|
19
|
+
- **AssetLoader** — загрузка любых моделей, спрайтов и текстур (OBJ/GLB/FBX/PNG/WebP/…)
|
|
20
|
+
- `Sprite.from_image(path)` — быстрый импорт 2D-графики
|
|
21
|
+
- Встроенный редактор **убран** (может вернуться в будущих версиях)
|
|
24
22
|
|
|
25
23
|
## Showcase-игра
|
|
26
24
|
|
|
@@ -37,10 +35,10 @@ python games/aether_craft.py
|
|
|
37
35
|
## Другие запуски
|
|
38
36
|
|
|
39
37
|
```bash
|
|
40
|
-
python -m aether.editor
|
|
41
38
|
python games/orb_rush.py
|
|
42
39
|
python examples/03_first_person.py
|
|
43
40
|
python examples/04_physics2d.py
|
|
41
|
+
aether website
|
|
44
42
|
```
|
|
45
43
|
|
|
46
44
|
## Минимальный код
|
|
@@ -65,15 +63,16 @@ engine.enable_first_person(camera)
|
|
|
65
63
|
engine.run()
|
|
66
64
|
```
|
|
67
65
|
|
|
68
|
-
## Возможности
|
|
66
|
+
## Возможности
|
|
69
67
|
|
|
70
68
|
- PBR 3D + sprites 2D + HUD overlay
|
|
71
69
|
- FPS (`enable_first_person`) и free-look камера
|
|
72
70
|
- Физика 3D/2D + raycast
|
|
73
|
-
- `ModelLoader`
|
|
74
|
-
- Встроенный voxel model editor
|
|
71
|
+
- `AssetLoader` / `ModelLoader` — OBJ/GLB/glTF/STL/FBX/STEP/… + PNG/WebP спрайты
|
|
75
72
|
- Procedural SFX, JSON save/load
|
|
76
|
-
- Примеры и
|
|
73
|
+
- Примеры и игры в репозитории
|
|
74
|
+
|
|
75
|
+
> Встроенный редактор моделей/пиксель-арт **временно убран** — планируется вернуть позже. Сейчас используйте Blender, Aseprite и т.д., файлы подгружайте через `AssetLoader`.
|
|
77
76
|
|
|
78
77
|
## Документация
|
|
79
78
|
|
|
@@ -23,17 +23,19 @@ from aether.graphics.material import Material
|
|
|
23
23
|
from aether.graphics.camera import Camera
|
|
24
24
|
from aether.graphics.first_person import FirstPersonController
|
|
25
25
|
from aether.resources.loaders.model_loader import ModelLoader
|
|
26
|
+
from aether.resources.asset_loader import AssetLoader
|
|
26
27
|
from aether.physics import PhysicsWorld2D, PhysicsWorld3D, BoxCollider, SphereCollider
|
|
27
28
|
from aether.graphics2d import Renderer2D, Sprite, Camera2D
|
|
28
|
-
from aether.editor import ModelEditor
|
|
29
29
|
from aether.ui.hud import Hud, HudBar
|
|
30
30
|
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.
|
|
34
|
+
__version__ = "1.7.0"
|
|
35
35
|
|
|
36
36
|
from aether.graphics.voxel_mesh import ChunkedVoxelMesher
|
|
37
|
+
from aether.accelerators import is_native_available, backend_name
|
|
38
|
+
from aether.accelerators.gameplay import npc_move_toward, roll_loot
|
|
37
39
|
|
|
38
40
|
__all__ = [
|
|
39
41
|
"Engine",
|
|
@@ -50,6 +52,7 @@ __all__ = [
|
|
|
50
52
|
"Camera",
|
|
51
53
|
"FirstPersonController",
|
|
52
54
|
"ModelLoader",
|
|
55
|
+
"AssetLoader",
|
|
53
56
|
"PhysicsWorld2D",
|
|
54
57
|
"PhysicsWorld3D",
|
|
55
58
|
"BoxCollider",
|
|
@@ -57,7 +60,6 @@ __all__ = [
|
|
|
57
60
|
"Renderer2D",
|
|
58
61
|
"Sprite",
|
|
59
62
|
"Camera2D",
|
|
60
|
-
"ModelEditor",
|
|
61
63
|
"Hud",
|
|
62
64
|
"HudBar",
|
|
63
65
|
"SfxBank",
|
|
@@ -65,5 +67,9 @@ __all__ = [
|
|
|
65
67
|
"load_json",
|
|
66
68
|
"Raycaster",
|
|
67
69
|
"ChunkedVoxelMesher",
|
|
70
|
+
"is_native_available",
|
|
71
|
+
"backend_name",
|
|
72
|
+
"npc_move_toward",
|
|
73
|
+
"roll_loot",
|
|
68
74
|
"__version__",
|
|
69
75
|
]
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""Rust/C++ accelerators — prebuilt binaries, no user toolchain required."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from typing import Optional
|
|
5
|
+
|
|
6
|
+
_NATIVE = None
|
|
7
|
+
_NATIVE_TRIED = False
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def is_native_available() -> bool:
|
|
11
|
+
"""True when the precompiled Rust extension is loaded."""
|
|
12
|
+
global _NATIVE, _NATIVE_TRIED
|
|
13
|
+
if _NATIVE_TRIED:
|
|
14
|
+
return _NATIVE is not None
|
|
15
|
+
_NATIVE_TRIED = True
|
|
16
|
+
try:
|
|
17
|
+
import aether_native as mod # type: ignore
|
|
18
|
+
|
|
19
|
+
_NATIVE = mod
|
|
20
|
+
except ImportError:
|
|
21
|
+
_NATIVE = None
|
|
22
|
+
return _NATIVE is not None
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def native_module():
|
|
26
|
+
return _NATIVE
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def backend_name() -> str:
|
|
30
|
+
return "rust" if is_native_available() else "python"
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""Game systems accelerated by Rust when the prebuilt extension is present."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from typing import List, Tuple
|
|
5
|
+
|
|
6
|
+
from aether.accelerators import is_native_available, native_module
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def npc_move_toward(
|
|
10
|
+
pos: Tuple[float, float],
|
|
11
|
+
target: Tuple[float, float],
|
|
12
|
+
speed: float,
|
|
13
|
+
attack_range: float = 1.5,
|
|
14
|
+
) -> Tuple[float, float, int]:
|
|
15
|
+
"""
|
|
16
|
+
Move NPC toward target. Returns (x, z, action).
|
|
17
|
+
action: 0=idle, 1=chase, 2=attack
|
|
18
|
+
"""
|
|
19
|
+
px, pz = pos
|
|
20
|
+
tx, tz = target
|
|
21
|
+
if is_native_available():
|
|
22
|
+
nx, nz, act = native_module().npc_tick(px, pz, tx, tz, speed, attack_range)
|
|
23
|
+
return nx, nz, int(act)
|
|
24
|
+
|
|
25
|
+
dx, dz = tx - px, tz - pz
|
|
26
|
+
dist = (dx * dx + dz * dz) ** 0.5
|
|
27
|
+
if dist < 0.05:
|
|
28
|
+
return px, pz, 0
|
|
29
|
+
if dist <= attack_range:
|
|
30
|
+
return px, pz, 2
|
|
31
|
+
step = min(speed, dist)
|
|
32
|
+
return px + dx / dist * step, pz + dz / dist * step, 1
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def roll_loot(weights: List[float], seed: int = 0) -> int:
|
|
36
|
+
"""Weighted random loot index, or -1."""
|
|
37
|
+
if not weights:
|
|
38
|
+
return -1
|
|
39
|
+
if is_native_available():
|
|
40
|
+
return int(native_module().loot_roll(weights, seed & 0xFFFFFFFFFFFFFFFF))
|
|
41
|
+
|
|
42
|
+
import random
|
|
43
|
+
|
|
44
|
+
rng = random.Random(seed)
|
|
45
|
+
total = sum(weights)
|
|
46
|
+
if total <= 0:
|
|
47
|
+
return -1
|
|
48
|
+
pick = rng.uniform(0, total)
|
|
49
|
+
acc = 0.0
|
|
50
|
+
for i, w in enumerate(weights):
|
|
51
|
+
acc += w
|
|
52
|
+
if pick <= acc:
|
|
53
|
+
return i
|
|
54
|
+
return len(weights) - 1
|
aether_engine-1.4.0/aether/graphics/voxel_mesh.py → aether_engine-1.7.0/aether/accelerators/voxel.py
RENAMED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Voxel mesh acceleration — Rust when available, pure Python fallback."""
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
|
|
4
|
-
from typing import
|
|
4
|
+
from typing import Dict, List, Tuple
|
|
5
5
|
|
|
6
6
|
import numpy as np
|
|
7
7
|
|
|
8
|
+
from aether.accelerators import is_native_available, native_module
|
|
8
9
|
from aether.graphics.mesh import Mesh
|
|
9
10
|
|
|
10
11
|
Vec3i = Tuple[int, int, int]
|
|
@@ -19,6 +20,84 @@ DEFAULT_FACES = [
|
|
|
19
20
|
]
|
|
20
21
|
|
|
21
22
|
|
|
23
|
+
def _python_build_chunk(
|
|
24
|
+
get_block,
|
|
25
|
+
x0: int,
|
|
26
|
+
y0: int,
|
|
27
|
+
z0: int,
|
|
28
|
+
x1: int,
|
|
29
|
+
y1: int,
|
|
30
|
+
z1: int,
|
|
31
|
+
block_types: set | None,
|
|
32
|
+
faces,
|
|
33
|
+
) -> Dict[int, Mesh]:
|
|
34
|
+
buckets: Dict[int, list] = {}
|
|
35
|
+
for x in range(x0, x1):
|
|
36
|
+
for y in range(y0, y1):
|
|
37
|
+
for z in range(z0, z1):
|
|
38
|
+
bid = get_block(x, y, z)
|
|
39
|
+
if bid == 0:
|
|
40
|
+
continue
|
|
41
|
+
if block_types and bid not in block_types:
|
|
42
|
+
continue
|
|
43
|
+
if bid not in buckets:
|
|
44
|
+
buckets[bid] = [[], []]
|
|
45
|
+
verts, inds = buckets[bid]
|
|
46
|
+
index = len(verts) // 8
|
|
47
|
+
for normal, corners in faces:
|
|
48
|
+
nx, ny, nz = normal
|
|
49
|
+
if get_block(x + nx, y + ny, z + nz) != 0:
|
|
50
|
+
continue
|
|
51
|
+
face = []
|
|
52
|
+
for px, py, pz in corners:
|
|
53
|
+
verts.extend([x + px, y + py, z + pz, float(nx), float(ny), float(nz), 0.0, 0.0])
|
|
54
|
+
face.append(index)
|
|
55
|
+
index += 1
|
|
56
|
+
inds.extend([face[0], face[1], face[2], face[0], face[2], face[3]])
|
|
57
|
+
|
|
58
|
+
meshes = {}
|
|
59
|
+
for bid, (verts, inds) in buckets.items():
|
|
60
|
+
if inds:
|
|
61
|
+
meshes[bid] = Mesh(
|
|
62
|
+
np.array(verts, dtype=np.float32),
|
|
63
|
+
np.array(inds, dtype=np.uint32),
|
|
64
|
+
)
|
|
65
|
+
return meshes
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _rust_build_chunk(
|
|
69
|
+
get_block,
|
|
70
|
+
x0: int,
|
|
71
|
+
y0: int,
|
|
72
|
+
z0: int,
|
|
73
|
+
x1: int,
|
|
74
|
+
y1: int,
|
|
75
|
+
z1: int,
|
|
76
|
+
block_types: set | None,
|
|
77
|
+
) -> Dict[int, Mesh]:
|
|
78
|
+
mod = native_module()
|
|
79
|
+
blocks: List[Tuple[int, int, int, int]] = []
|
|
80
|
+
for x in range(x0, x1):
|
|
81
|
+
for y in range(y0, y1):
|
|
82
|
+
for z in range(z0, z1):
|
|
83
|
+
bid = get_block(x, y, z)
|
|
84
|
+
if bid == 0:
|
|
85
|
+
continue
|
|
86
|
+
if block_types and bid not in block_types:
|
|
87
|
+
continue
|
|
88
|
+
blocks.append((x, y, z, int(bid)))
|
|
89
|
+
|
|
90
|
+
raw = mod.build_chunk_mesh(blocks, x0, y0, z0, x1, y1, z1)
|
|
91
|
+
meshes = {}
|
|
92
|
+
for bid, (verts, inds) in raw.items():
|
|
93
|
+
if inds:
|
|
94
|
+
meshes[int(bid)] = Mesh(
|
|
95
|
+
np.array(verts, dtype=np.float32),
|
|
96
|
+
np.array(inds, dtype=np.uint32),
|
|
97
|
+
)
|
|
98
|
+
return meshes
|
|
99
|
+
|
|
100
|
+
|
|
22
101
|
class VoxelChunk:
|
|
23
102
|
__slots__ = ("cx", "cy", "cz", "meshes", "dirty")
|
|
24
103
|
|
|
@@ -31,15 +110,15 @@ class VoxelChunk:
|
|
|
31
110
|
class ChunkedVoxelMesher:
|
|
32
111
|
"""
|
|
33
112
|
Incrementally rebuilds only dirty 16³ chunks instead of the whole world.
|
|
34
|
-
|
|
113
|
+
Uses Rust mesh builder when the prebuilt extension is installed.
|
|
35
114
|
"""
|
|
36
115
|
|
|
37
116
|
CHUNK_SIZE = 16
|
|
38
117
|
|
|
39
118
|
def __init__(
|
|
40
119
|
self,
|
|
41
|
-
get_block
|
|
42
|
-
block_types
|
|
120
|
+
get_block,
|
|
121
|
+
block_types=None,
|
|
43
122
|
chunk_size: int = 16,
|
|
44
123
|
):
|
|
45
124
|
self.get_block = get_block
|
|
@@ -48,13 +127,13 @@ class ChunkedVoxelMesher:
|
|
|
48
127
|
self.chunks: Dict[Vec3i, VoxelChunk] = {}
|
|
49
128
|
self._dirty: set[Vec3i] = set()
|
|
50
129
|
self.faces = DEFAULT_FACES
|
|
130
|
+
self._use_rust = is_native_available()
|
|
51
131
|
|
|
52
132
|
def _chunk_of(self, x: int, y: int, z: int) -> Vec3i:
|
|
53
133
|
s = self.chunk_size
|
|
54
134
|
return (x // s, y // s, z // s)
|
|
55
135
|
|
|
56
136
|
def mark_dirty_at(self, x: int, y: int, z: int):
|
|
57
|
-
"""Mark chunk + neighbors dirty (face culling crosses boundaries)."""
|
|
58
137
|
cx, cy, cz = self._chunk_of(x, y, z)
|
|
59
138
|
for dx in (-1, 0, 1):
|
|
60
139
|
for dy in (-1, 0, 1):
|
|
@@ -66,8 +145,6 @@ class ChunkedVoxelMesher:
|
|
|
66
145
|
|
|
67
146
|
def rebuild_all(self):
|
|
68
147
|
self._dirty.clear()
|
|
69
|
-
seen = set()
|
|
70
|
-
# Caller must iterate blocks and call mark_dirty_at OR we expose full rebuild:
|
|
71
148
|
for key in list(self.chunks.keys()):
|
|
72
149
|
self._dirty.add(key)
|
|
73
150
|
|
|
@@ -84,48 +161,27 @@ class ChunkedVoxelMesher:
|
|
|
84
161
|
x0, y0, z0 = cx * s, cy * s, cz * s
|
|
85
162
|
x1, y1, z1 = x0 + s, y0 + s, z0 + s
|
|
86
163
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
if self.block_types and bid not in self.block_types:
|
|
96
|
-
continue
|
|
97
|
-
if bid not in buckets:
|
|
98
|
-
buckets[bid] = [[], []]
|
|
99
|
-
verts, inds = buckets[bid]
|
|
100
|
-
index = len(verts) // 8
|
|
101
|
-
for normal, corners in self.faces:
|
|
102
|
-
nx, ny, nz = normal
|
|
103
|
-
if self.get_block(x + nx, y + ny, z + nz) != 0:
|
|
104
|
-
continue
|
|
105
|
-
face = []
|
|
106
|
-
for px, py, pz in corners:
|
|
107
|
-
verts.extend([x + px, y + py, z + pz, float(nx), float(ny), float(nz), 0.0, 0.0])
|
|
108
|
-
face.append(index)
|
|
109
|
-
index += 1
|
|
110
|
-
inds.extend([face[0], face[1], face[2], face[0], face[2], face[3]])
|
|
164
|
+
if self._use_rust:
|
|
165
|
+
fresh = _rust_build_chunk(
|
|
166
|
+
self.get_block, x0, y0, z0, x1, y1, z1, self.block_types or None
|
|
167
|
+
)
|
|
168
|
+
else:
|
|
169
|
+
fresh = _python_build_chunk(
|
|
170
|
+
self.get_block, x0, y0, z0, x1, y1, z1, self.block_types or None, self.faces
|
|
171
|
+
)
|
|
111
172
|
|
|
173
|
+
chunk = self.chunks.get((cx, cy, cz))
|
|
112
174
|
meshes = {}
|
|
113
|
-
for bid,
|
|
114
|
-
if
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
if old is not None:
|
|
121
|
-
old.update_data(v_arr, i_arr)
|
|
122
|
-
meshes[bid] = old
|
|
123
|
-
else:
|
|
124
|
-
meshes[bid] = Mesh(v_arr, i_arr)
|
|
175
|
+
for bid, mesh in fresh.items():
|
|
176
|
+
old = chunk.meshes.get(bid) if chunk else None
|
|
177
|
+
if old is not None:
|
|
178
|
+
old.update_data(mesh.vertices, mesh.indices)
|
|
179
|
+
meshes[bid] = old
|
|
180
|
+
else:
|
|
181
|
+
meshes[bid] = mesh
|
|
125
182
|
return meshes
|
|
126
183
|
|
|
127
184
|
def flush_dirty(self):
|
|
128
|
-
"""Rebuild dirty chunks. Returns (meshes, removed_keys)."""
|
|
129
185
|
out = {}
|
|
130
186
|
removed = []
|
|
131
187
|
for key in list(self._dirty):
|
|
@@ -152,7 +208,6 @@ class ChunkedVoxelMesher:
|
|
|
152
208
|
return out, removed
|
|
153
209
|
|
|
154
210
|
def get_all_meshes(self) -> Dict[Tuple[int, int, int, int], Mesh]:
|
|
155
|
-
"""Return all chunk meshes (for initial scene build)."""
|
|
156
211
|
result = {}
|
|
157
212
|
for key, ch in self.chunks.items():
|
|
158
213
|
for bid, mesh in ch.meshes.items():
|
|
@@ -167,7 +222,6 @@ class ChunkedVoxelMesher:
|
|
|
167
222
|
self._dirty.add(key)
|
|
168
223
|
|
|
169
224
|
def prime_chunks(self, blocks: Dict[Vec3i, int]):
|
|
170
|
-
"""Register all block positions and mark their chunks dirty."""
|
|
171
225
|
for (x, y, z) in blocks.keys():
|
|
172
226
|
self.ensure_chunk(x, y, z)
|
|
173
227
|
self.mark_dirty_at(x, y, z)
|
|
Binary file
|
|
@@ -43,15 +43,24 @@ FPS / КАМЕРА
|
|
|
43
43
|
engine.set_mode("2d")
|
|
44
44
|
engine.renderer2d.add_sprite(Sprite(x=0, y=0, width=40, height=40, color=(1,0,0,1)))
|
|
45
45
|
|
|
46
|
-
МОДЕЛИ
|
|
47
|
-
from aether import ModelLoader
|
|
48
|
-
mesh =
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
46
|
+
МОДЕЛИ И АССЕТЫ
|
|
47
|
+
from aether import AssetLoader, ModelLoader
|
|
48
|
+
mesh = AssetLoader.load_model("hero.glb") # obj/stl/glb/gltf/fbx/step/…
|
|
49
|
+
sprite = AssetLoader.load_sprite("icon.png") # png/jpg/webp/gif/…
|
|
50
|
+
tex_id = AssetLoader.load_texture("wall.jpg")
|
|
51
|
+
engine.renderer2d.add_sprite(sprite)
|
|
52
|
+
|
|
53
|
+
ВОКСЕЛИ (ОПТИМИЗАЦИЯ + RUST)
|
|
54
|
+
from aether import ChunkedVoxelMesher, is_native_available, backend_name
|
|
55
|
+
print(backend_name()) # "rust" if prebuilt extension installed, else "python"
|
|
52
56
|
mesher = ChunkedVoxelMesher(world.get_block)
|
|
53
57
|
mesher.notify_block_change(x, y, z)
|
|
54
|
-
meshes = mesher.flush_dirty()
|
|
58
|
+
meshes, removed = mesher.flush_dirty()
|
|
59
|
+
|
|
60
|
+
NPC / НАГРАДЫ (RUST, опционально)
|
|
61
|
+
from aether import npc_move_toward, roll_loot
|
|
62
|
+
x, z, action = npc_move_toward((0,0), (10,5), speed=0.15)
|
|
63
|
+
item = roll_loot([50, 30, 20], seed=42)
|
|
55
64
|
|
|
56
65
|
ФИЗИКА
|
|
57
66
|
engine.physics3d.create_body_for_entity(ent, collider=BoxCollider())
|
|
@@ -72,7 +81,8 @@ FPS / КАМЕРА
|
|
|
72
81
|
ИНСТРУМЕНТЫ
|
|
73
82
|
aether help — эта справка
|
|
74
83
|
aether docs — краткий гайд по API
|
|
75
|
-
aether
|
|
84
|
+
aether website — documentation site (domain in bell notifications)
|
|
85
|
+
python -m aether website
|
|
76
86
|
python games/aether_craft.py
|
|
77
87
|
|
|
78
88
|
ПОЛНАЯ ДОКУМЕНТАЦИЯ: docs/GUIDE.md
|
|
@@ -106,11 +116,10 @@ def cmd_build(args):
|
|
|
106
116
|
)
|
|
107
117
|
|
|
108
118
|
|
|
109
|
-
def
|
|
110
|
-
from aether.
|
|
119
|
+
def cmd_website(args):
|
|
120
|
+
from aether.web.server import run_server
|
|
111
121
|
|
|
112
|
-
|
|
113
|
-
return 0
|
|
122
|
+
return run_server(host=args.host, port=args.port, open_browser=not args.no_browser)
|
|
114
123
|
|
|
115
124
|
|
|
116
125
|
def main(argv=None):
|
|
@@ -124,7 +133,6 @@ def main(argv=None):
|
|
|
124
133
|
|
|
125
134
|
sub.add_parser("help", help="Краткая справка по API и командам").set_defaults(func=cmd_help)
|
|
126
135
|
sub.add_parser("docs", help="Полная документация").set_defaults(func=cmd_docs)
|
|
127
|
-
sub.add_parser("editor", help="3D редактор моделей").set_defaults(func=cmd_editor)
|
|
128
136
|
|
|
129
137
|
p_build = sub.add_parser("build", help="Собрать игру в .exe (PyInstaller)")
|
|
130
138
|
p_build.add_argument("script", help="Путь к main.py игры")
|
|
@@ -134,6 +142,20 @@ def main(argv=None):
|
|
|
134
142
|
p_build.add_argument("--icon", help="Иконка .ico", default=None)
|
|
135
143
|
p_build.set_defaults(func=cmd_build)
|
|
136
144
|
|
|
145
|
+
p_web = sub.add_parser(
|
|
146
|
+
"website",
|
|
147
|
+
help="Локальный сайт документации (градиент, хеш-URL, настройки домена)",
|
|
148
|
+
)
|
|
149
|
+
p_web.add_argument(
|
|
150
|
+
"--port",
|
|
151
|
+
type=int,
|
|
152
|
+
default=9999,
|
|
153
|
+
help="TCP порт (1–65535; по умолчанию 9999)",
|
|
154
|
+
)
|
|
155
|
+
p_web.add_argument("--host", default="0.0.0.0", help="Адрес привязки (0.0.0.0 = доступ из сети)")
|
|
156
|
+
p_web.add_argument("--no-browser", action="store_true", help="Не открывать браузер")
|
|
157
|
+
p_web.set_defaults(func=cmd_website)
|
|
158
|
+
|
|
137
159
|
args = parser.parse_args(argv)
|
|
138
160
|
if not args.command:
|
|
139
161
|
cmd_help(args)
|
|
@@ -7,13 +7,12 @@ _B = {
|
|
|
7
7
|
"n": bytes([230, 194, 211, 207, 194, 213, 135, 226, 201, 192, 206, 201, 194]),
|
|
8
8
|
"c": bytes([194, 201, 211, 206, 211, 206, 158, 148, 144]),
|
|
9
9
|
"l1": bytes([
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
230, 194, 211, 207, 194, 213, 135, 226, 201, 192, 206, 201, 194,
|
|
10
|
+
247, 200, 208, 194, 213, 194, 195, 135, 197, 222, 135, 230, 194, 211, 207, 194,
|
|
11
|
+
213, 135, 226, 201, 192, 206, 201, 194,
|
|
13
12
|
]),
|
|
14
13
|
"l2": bytes([
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
228, 213, 194, 198, 211, 194, 195, 135, 197, 222, 135, 194, 201, 211, 206, 211,
|
|
15
|
+
206, 158, 148, 144,
|
|
17
16
|
]),
|
|
18
17
|
"w": bytes([
|
|
19
18
|
230, 194, 211, 207, 194, 213, 135, 226, 201, 192, 206, 201, 194,
|
|
@@ -21,11 +20,11 @@ _B = {
|
|
|
21
20
|
]),
|
|
22
21
|
"sig": bytes([
|
|
23
22
|
230, 194, 211, 207, 194, 213, 135, 226, 201, 192, 206, 201, 194,
|
|
24
|
-
135, 150, 137,
|
|
23
|
+
135, 150, 137, 144, 137, 151,
|
|
25
24
|
]),
|
|
26
25
|
}
|
|
27
26
|
|
|
28
|
-
_EXPECTED =
|
|
27
|
+
_EXPECTED = 0xD5E52D08
|
|
29
28
|
|
|
30
29
|
|
|
31
30
|
def _decode(blob: bytes) -> str:
|