aether-engine 1.3.1__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.3.1 → aether_engine-1.7.0}/MANIFEST.in +1 -0
- {aether_engine-1.3.1/aether_engine.egg-info → aether_engine-1.7.0}/PKG-INFO +19 -14
- {aether_engine-1.3.1 → aether_engine-1.7.0}/README.md +13 -11
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/__init__.py +12 -3
- aether_engine-1.7.0/aether/__main__.py +6 -0
- 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.7.0/aether/accelerators/voxel.py +227 -0
- 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.7.0/aether/cli.py +167 -0
- {aether_engine-1.3.1 → 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.7.0/aether/core/branding_guard.py +32 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/core/engine.py +16 -36
- {aether_engine-1.3.1 → 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.3.1 → aether_engine-1.7.0}/aether/graphics/mesh.py +21 -0
- aether_engine-1.7.0/aether/graphics/voxel_mesh.py +4 -0
- {aether_engine-1.3.1 → 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.3.1 → aether_engine-1.7.0}/aether/resources/loaders/model_loader.py +14 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/scene/component.py +8 -0
- aether_engine-1.7.0/aether/tools/__init__.py +1 -0
- aether_engine-1.7.0/aether/tools/packager.py +68 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/ui/text.py +8 -2
- {aether_engine-1.3.1 → 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.3.1 → aether_engine-1.7.0/aether_engine.egg-info}/PKG-INFO +19 -14
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether_engine.egg-info/SOURCES.txt +23 -4
- aether_engine-1.7.0/aether_engine.egg-info/entry_points.txt +2 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether_engine.egg-info/requires.txt +6 -2
- {aether_engine-1.3.1 → aether_engine-1.7.0}/examples/03_first_person.py +1 -1
- {aether_engine-1.3.1 → aether_engine-1.7.0}/setup.py +4 -2
- aether_engine-1.3.1/aether/core/boot_splash.py +0 -141
- aether_engine-1.3.1/aether/core/branding_guard.py +0 -103
- aether_engine-1.3.1/aether/editor/__init__.py +0 -5
- aether_engine-1.3.1/aether/editor/__main__.py +0 -6
- aether_engine-1.3.1/aether/editor/gizmo.py +0 -346
- aether_engine-1.3.1/aether/editor/imgui_integration.py +0 -468
- aether_engine-1.3.1/aether/editor/model_editor.py +0 -253
- aether_engine-1.3.1/aether_engine.egg-info/entry_points.txt +0 -2
- {aether_engine-1.3.1 → aether_engine-1.7.0}/LICENSE +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/ai/behavior_tree.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/ai/navmesh.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/animation/skeleton.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/assets/asset_pipeline.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/audio/__init__.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/audio/audio_manager.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/audio/music.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/audio/sfx.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/audio/sound.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/core/__init__.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/core/achievement_system.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/core/application.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/core/config.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/core/console.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/core/event_bus.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/core/plugin_system.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/core/save_system.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/core/time_manager.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/core/window.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/environment/environment_system.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/graphics/__init__.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/graphics/camera.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/graphics/first_person.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/graphics/framebuffer.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/graphics/light.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/graphics/lod_system.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/graphics/material.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/graphics/mesh_factory.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/graphics/particle_system.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/graphics/post_process.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/graphics/renderer.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/graphics/shader.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/graphics/shader_library.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/graphics/sky_atmosphere.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/graphics/terrain.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/graphics/texture.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/graphics/water.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/network/network_manager.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/physics/__init__.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/physics/bullet_backend.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/physics/collider.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/physics/physics2d.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/physics/physics3d.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/physics/physics_world.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/physics/raycast.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/physics/rigidbody.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/platform/steam_api.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/resources/__init__.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/resources/asset_database.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/resources/loaders/obj_loader.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/resources/loaders/texture_loader.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/resources/resource_manager.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/resources/save_load.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/resources/serialization.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/scene/__init__.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/scene/entity.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/scene/scene.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/scene/scene_graph.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/scene/scene_manager.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/ui/__init__.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/ui/button.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/ui/canvas.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/ui/hud.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/utils/__init__.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/utils/logger.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/utils/math_utils.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/utils/object_pool.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether/utils/profiler.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether_engine.egg-info/dependency_links.txt +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/aether_engine.egg-info/top_level.txt +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/examples/01_hello_cube.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/examples/02_sphere_scene.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/examples/04_physics2d.py +0 -0
- {aether_engine-1.3.1 → aether_engine-1.7.0}/pyproject.toml +0 -0
- {aether_engine-1.3.1 → 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
|
|
@@ -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
|
|
32
|
-
Requires-Dist:
|
|
34
|
+
Requires-Dist: freetype-py>=2.4; extra == "full"
|
|
33
35
|
Requires-Dist: imageio>=2.31; extra == "full"
|
|
36
|
+
Requires-Dist: pygame-ce>=2.5; extra == "full"
|
|
34
37
|
Requires-Dist: pyyaml>=6.0; extra == "full"
|
|
35
38
|
Requires-Dist: trimesh>=3.21; extra == "full"
|
|
36
39
|
Requires-Dist: pymunk>=6.0; extra == "full"
|
|
37
|
-
Requires-Dist:
|
|
40
|
+
Requires-Dist: pyinstaller>=6.0; 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.
|
|
55
|
+
**Разработчик:** entiti937 · **Версия:** 1.7.0 · **Лицензия:** MIT
|
|
53
56
|
**PyPI:** https://pypi.org/project/aether-engine/
|
|
54
57
|
|
|
55
58
|
> Полное руководство: [`docs/GUIDE.md`](docs/GUIDE.md)
|
|
@@ -62,18 +65,19 @@ Dynamic: summary
|
|
|
62
65
|
pip install -U "aether-engine[full]"
|
|
63
66
|
```
|
|
64
67
|
|
|
65
|
-
## Что нового в 1.
|
|
68
|
+
## Что нового в 1.7.0
|
|
66
69
|
|
|
67
|
-
-
|
|
68
|
-
-
|
|
69
|
-
-
|
|
70
|
-
- Компиляция защищённых модулей при сборке пакета
|
|
70
|
+
- **AssetLoader** — загрузка любых моделей, спрайтов и текстур (OBJ/GLB/FBX/PNG/WebP/…)
|
|
71
|
+
- `Sprite.from_image(path)` — быстрый импорт 2D-графики
|
|
72
|
+
- Встроенный редактор **убран** (может вернуться в будущих версиях)
|
|
71
73
|
|
|
72
74
|
## Showcase-игра
|
|
73
75
|
|
|
74
76
|
Minecraft-like sandbox **на этом же движке**:
|
|
75
77
|
|
|
76
78
|
```bash
|
|
79
|
+
aether help
|
|
80
|
+
aether build games/aether_craft.py --name AetherCraft
|
|
77
81
|
python games/aether_craft.py
|
|
78
82
|
```
|
|
79
83
|
|
|
@@ -82,10 +86,10 @@ python games/aether_craft.py
|
|
|
82
86
|
## Другие запуски
|
|
83
87
|
|
|
84
88
|
```bash
|
|
85
|
-
python -m aether.editor
|
|
86
89
|
python games/orb_rush.py
|
|
87
90
|
python examples/03_first_person.py
|
|
88
91
|
python examples/04_physics2d.py
|
|
92
|
+
aether website
|
|
89
93
|
```
|
|
90
94
|
|
|
91
95
|
## Минимальный код
|
|
@@ -110,15 +114,16 @@ engine.enable_first_person(camera)
|
|
|
110
114
|
engine.run()
|
|
111
115
|
```
|
|
112
116
|
|
|
113
|
-
## Возможности
|
|
117
|
+
## Возможности
|
|
114
118
|
|
|
115
119
|
- PBR 3D + sprites 2D + HUD overlay
|
|
116
120
|
- FPS (`enable_first_person`) и free-look камера
|
|
117
121
|
- Физика 3D/2D + raycast
|
|
118
|
-
- `ModelLoader`
|
|
119
|
-
- Встроенный voxel model editor
|
|
122
|
+
- `AssetLoader` / `ModelLoader` — OBJ/GLB/glTF/STL/FBX/STEP/… + PNG/WebP спрайты
|
|
120
123
|
- Procedural SFX, JSON save/load
|
|
121
|
-
- Примеры и
|
|
124
|
+
- Примеры и игры в репозитории
|
|
125
|
+
|
|
126
|
+
> Встроенный редактор моделей/пиксель-арт **временно убран** — планируется вернуть позже. Сейчас используйте Blender, Aseprite и т.д., файлы подгружайте через `AssetLoader`.
|
|
122
127
|
|
|
123
128
|
## Документация
|
|
124
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,18 +14,19 @@
|
|
|
14
14
|
pip install -U "aether-engine[full]"
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
## Что нового в 1.
|
|
17
|
+
## Что нового в 1.7.0
|
|
18
18
|
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
-
|
|
22
|
-
- Компиляция защищённых модулей при сборке пакета
|
|
19
|
+
- **AssetLoader** — загрузка любых моделей, спрайтов и текстур (OBJ/GLB/FBX/PNG/WebP/…)
|
|
20
|
+
- `Sprite.from_image(path)` — быстрый импорт 2D-графики
|
|
21
|
+
- Встроенный редактор **убран** (может вернуться в будущих версиях)
|
|
23
22
|
|
|
24
23
|
## Showcase-игра
|
|
25
24
|
|
|
26
25
|
Minecraft-like sandbox **на этом же движке**:
|
|
27
26
|
|
|
28
27
|
```bash
|
|
28
|
+
aether help
|
|
29
|
+
aether build games/aether_craft.py --name AetherCraft
|
|
29
30
|
python games/aether_craft.py
|
|
30
31
|
```
|
|
31
32
|
|
|
@@ -34,10 +35,10 @@ python games/aether_craft.py
|
|
|
34
35
|
## Другие запуски
|
|
35
36
|
|
|
36
37
|
```bash
|
|
37
|
-
python -m aether.editor
|
|
38
38
|
python games/orb_rush.py
|
|
39
39
|
python examples/03_first_person.py
|
|
40
40
|
python examples/04_physics2d.py
|
|
41
|
+
aether website
|
|
41
42
|
```
|
|
42
43
|
|
|
43
44
|
## Минимальный код
|
|
@@ -62,15 +63,16 @@ engine.enable_first_person(camera)
|
|
|
62
63
|
engine.run()
|
|
63
64
|
```
|
|
64
65
|
|
|
65
|
-
## Возможности
|
|
66
|
+
## Возможности
|
|
66
67
|
|
|
67
68
|
- PBR 3D + sprites 2D + HUD overlay
|
|
68
69
|
- FPS (`enable_first_person`) и free-look камера
|
|
69
70
|
- Физика 3D/2D + raycast
|
|
70
|
-
- `ModelLoader`
|
|
71
|
-
- Встроенный voxel model editor
|
|
71
|
+
- `AssetLoader` / `ModelLoader` — OBJ/GLB/glTF/STL/FBX/STEP/… + PNG/WebP спрайты
|
|
72
72
|
- Procedural SFX, JSON save/load
|
|
73
|
-
- Примеры и
|
|
73
|
+
- Примеры и игры в репозитории
|
|
74
|
+
|
|
75
|
+
> Встроенный редактор моделей/пиксель-арт **временно убран** — планируется вернуть позже. Сейчас используйте Blender, Aseprite и т.д., файлы подгружайте через `AssetLoader`.
|
|
74
76
|
|
|
75
77
|
## Документация
|
|
76
78
|
|
|
@@ -23,15 +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
|
+
|
|
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
|
|
35
39
|
|
|
36
40
|
__all__ = [
|
|
37
41
|
"Engine",
|
|
@@ -48,6 +52,7 @@ __all__ = [
|
|
|
48
52
|
"Camera",
|
|
49
53
|
"FirstPersonController",
|
|
50
54
|
"ModelLoader",
|
|
55
|
+
"AssetLoader",
|
|
51
56
|
"PhysicsWorld2D",
|
|
52
57
|
"PhysicsWorld3D",
|
|
53
58
|
"BoxCollider",
|
|
@@ -55,12 +60,16 @@ __all__ = [
|
|
|
55
60
|
"Renderer2D",
|
|
56
61
|
"Sprite",
|
|
57
62
|
"Camera2D",
|
|
58
|
-
"ModelEditor",
|
|
59
63
|
"Hud",
|
|
60
64
|
"HudBar",
|
|
61
65
|
"SfxBank",
|
|
62
66
|
"save_json",
|
|
63
67
|
"load_json",
|
|
64
68
|
"Raycaster",
|
|
69
|
+
"ChunkedVoxelMesher",
|
|
70
|
+
"is_native_available",
|
|
71
|
+
"backend_name",
|
|
72
|
+
"npc_move_toward",
|
|
73
|
+
"roll_loot",
|
|
65
74
|
"__version__",
|
|
66
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
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
"""Voxel mesh acceleration — Rust when available, pure Python fallback."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from typing import Dict, List, Tuple
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
|
|
8
|
+
from aether.accelerators import is_native_available, native_module
|
|
9
|
+
from aether.graphics.mesh import Mesh
|
|
10
|
+
|
|
11
|
+
Vec3i = Tuple[int, int, int]
|
|
12
|
+
|
|
13
|
+
DEFAULT_FACES = [
|
|
14
|
+
((0, 0, 1), [(0, 0, 1), (1, 0, 1), (1, 1, 1), (0, 1, 1)]),
|
|
15
|
+
((0, 0, -1), [(1, 0, 0), (0, 0, 0), (0, 1, 0), (1, 1, 0)]),
|
|
16
|
+
((-1, 0, 0), [(0, 0, 0), (0, 0, 1), (0, 1, 1), (0, 1, 0)]),
|
|
17
|
+
((1, 0, 0), [(1, 0, 1), (1, 0, 0), (1, 1, 0), (1, 1, 1)]),
|
|
18
|
+
((0, 1, 0), [(0, 1, 1), (1, 1, 1), (1, 1, 0), (0, 1, 0)]),
|
|
19
|
+
((0, -1, 0), [(0, 0, 0), (1, 0, 0), (1, 0, 1), (0, 0, 1)]),
|
|
20
|
+
]
|
|
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
|
+
|
|
101
|
+
class VoxelChunk:
|
|
102
|
+
__slots__ = ("cx", "cy", "cz", "meshes", "dirty")
|
|
103
|
+
|
|
104
|
+
def __init__(self, cx, cy, cz):
|
|
105
|
+
self.cx, self.cy, self.cz = cx, cy, cz
|
|
106
|
+
self.meshes: Dict[int, Mesh] = {}
|
|
107
|
+
self.dirty = True
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class ChunkedVoxelMesher:
|
|
111
|
+
"""
|
|
112
|
+
Incrementally rebuilds only dirty 16³ chunks instead of the whole world.
|
|
113
|
+
Uses Rust mesh builder when the prebuilt extension is installed.
|
|
114
|
+
"""
|
|
115
|
+
|
|
116
|
+
CHUNK_SIZE = 16
|
|
117
|
+
|
|
118
|
+
def __init__(
|
|
119
|
+
self,
|
|
120
|
+
get_block,
|
|
121
|
+
block_types=None,
|
|
122
|
+
chunk_size: int = 16,
|
|
123
|
+
):
|
|
124
|
+
self.get_block = get_block
|
|
125
|
+
self.block_types = block_types or set()
|
|
126
|
+
self.chunk_size = chunk_size
|
|
127
|
+
self.chunks: Dict[Vec3i, VoxelChunk] = {}
|
|
128
|
+
self._dirty: set[Vec3i] = set()
|
|
129
|
+
self.faces = DEFAULT_FACES
|
|
130
|
+
self._use_rust = is_native_available()
|
|
131
|
+
|
|
132
|
+
def _chunk_of(self, x: int, y: int, z: int) -> Vec3i:
|
|
133
|
+
s = self.chunk_size
|
|
134
|
+
return (x // s, y // s, z // s)
|
|
135
|
+
|
|
136
|
+
def mark_dirty_at(self, x: int, y: int, z: int):
|
|
137
|
+
cx, cy, cz = self._chunk_of(x, y, z)
|
|
138
|
+
for dx in (-1, 0, 1):
|
|
139
|
+
for dy in (-1, 0, 1):
|
|
140
|
+
for dz in (-1, 0, 1):
|
|
141
|
+
key = (cx + dx, cy + dy, cz + dz)
|
|
142
|
+
self._dirty.add(key)
|
|
143
|
+
if key in self.chunks:
|
|
144
|
+
self.chunks[key].dirty = True
|
|
145
|
+
|
|
146
|
+
def rebuild_all(self):
|
|
147
|
+
self._dirty.clear()
|
|
148
|
+
for key in list(self.chunks.keys()):
|
|
149
|
+
self._dirty.add(key)
|
|
150
|
+
|
|
151
|
+
def mark_all_dirty(self):
|
|
152
|
+
for ch in self.chunks.values():
|
|
153
|
+
ch.dirty = True
|
|
154
|
+
self._dirty = set(self.chunks.keys())
|
|
155
|
+
|
|
156
|
+
def notify_block_change(self, x: int, y: int, z: int):
|
|
157
|
+
self.mark_dirty_at(x, y, z)
|
|
158
|
+
|
|
159
|
+
def _build_chunk_mesh(self, cx: int, cy: int, cz: int) -> Dict[int, Mesh]:
|
|
160
|
+
s = self.chunk_size
|
|
161
|
+
x0, y0, z0 = cx * s, cy * s, cz * s
|
|
162
|
+
x1, y1, z1 = x0 + s, y0 + s, z0 + s
|
|
163
|
+
|
|
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
|
+
)
|
|
172
|
+
|
|
173
|
+
chunk = self.chunks.get((cx, cy, cz))
|
|
174
|
+
meshes = {}
|
|
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
|
|
182
|
+
return meshes
|
|
183
|
+
|
|
184
|
+
def flush_dirty(self):
|
|
185
|
+
out = {}
|
|
186
|
+
removed = []
|
|
187
|
+
for key in list(self._dirty):
|
|
188
|
+
cx, cy, cz = key
|
|
189
|
+
if key not in self.chunks:
|
|
190
|
+
self.chunks[key] = VoxelChunk(cx, cy, cz)
|
|
191
|
+
ch = self.chunks[key]
|
|
192
|
+
old_bids = set(ch.meshes.keys())
|
|
193
|
+
new_meshes = self._build_chunk_mesh(cx, cy, cz)
|
|
194
|
+
new_bids = set(new_meshes.keys())
|
|
195
|
+
for bid in old_bids - new_bids:
|
|
196
|
+
old = ch.meshes.get(bid)
|
|
197
|
+
if old:
|
|
198
|
+
old.destroy()
|
|
199
|
+
removed.append((cx, cy, cz, bid))
|
|
200
|
+
for old in ch.meshes.values():
|
|
201
|
+
if old not in new_meshes.values():
|
|
202
|
+
old.destroy()
|
|
203
|
+
ch.meshes = new_meshes
|
|
204
|
+
ch.dirty = False
|
|
205
|
+
for bid, mesh in new_meshes.items():
|
|
206
|
+
out[(cx, cy, cz, bid)] = mesh
|
|
207
|
+
self._dirty.discard(key)
|
|
208
|
+
return out, removed
|
|
209
|
+
|
|
210
|
+
def get_all_meshes(self) -> Dict[Tuple[int, int, int, int], Mesh]:
|
|
211
|
+
result = {}
|
|
212
|
+
for key, ch in self.chunks.items():
|
|
213
|
+
for bid, mesh in ch.meshes.items():
|
|
214
|
+
cx, cy, cz = key
|
|
215
|
+
result[(cx, cy, cz, bid)] = mesh
|
|
216
|
+
return result
|
|
217
|
+
|
|
218
|
+
def ensure_chunk(self, x: int, y: int, z: int):
|
|
219
|
+
key = self._chunk_of(x, y, z)
|
|
220
|
+
if key not in self.chunks:
|
|
221
|
+
self.chunks[key] = VoxelChunk(*key)
|
|
222
|
+
self._dirty.add(key)
|
|
223
|
+
|
|
224
|
+
def prime_chunks(self, blocks: Dict[Vec3i, int]):
|
|
225
|
+
for (x, y, z) in blocks.keys():
|
|
226
|
+
self.ensure_chunk(x, y, z)
|
|
227
|
+
self.mark_dirty_at(x, y, z)
|
|
Binary file
|