aether-engine 1.8.1__tar.gz → 1.8.2__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 (125) hide show
  1. {aether_engine-1.8.1/aether_engine.egg-info → aether_engine-1.8.2}/PKG-INFO +4 -4
  2. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/__init__.py +1 -1
  3. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/cli.py +5 -1
  4. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/core/_identity.py +1 -1
  5. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/core/engine.py +4 -0
  6. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/core/input.py +9 -0
  7. aether_engine-1.8.2/aether/docs/GUIDE.md +390 -0
  8. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/web/content.py +96 -10
  9. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/web/plain_content.py +7 -4
  10. {aether_engine-1.8.1 → aether_engine-1.8.2/aether_engine.egg-info}/PKG-INFO +4 -4
  11. aether_engine-1.8.2/docs/GUIDE.md +390 -0
  12. {aether_engine-1.8.1 → aether_engine-1.8.2}/setup.py +1 -1
  13. aether_engine-1.8.1/aether/docs/GUIDE.md +0 -270
  14. aether_engine-1.8.1/docs/GUIDE.md +0 -270
  15. {aether_engine-1.8.1 → aether_engine-1.8.2}/LICENSE +0 -0
  16. {aether_engine-1.8.1 → aether_engine-1.8.2}/MANIFEST.in +0 -0
  17. {aether_engine-1.8.1 → aether_engine-1.8.2}/README.md +0 -0
  18. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/__main__.py +0 -0
  19. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/accelerators/__init__.py +0 -0
  20. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/accelerators/gameplay.py +0 -0
  21. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/accelerators/voxel.py +0 -0
  22. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/ai/behavior_tree.py +0 -0
  23. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/ai/navmesh.py +0 -0
  24. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/animation/skeleton.py +0 -0
  25. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/assets/__pycache__/asset_pipeline.cpython-314.pyc +0 -0
  26. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/assets/asset_pipeline.py +0 -0
  27. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/assets/fonts/AetherSans.ttf +0 -0
  28. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/audio/__init__.py +0 -0
  29. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/audio/audio_manager.py +0 -0
  30. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/audio/music.py +0 -0
  31. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/audio/sfx.py +0 -0
  32. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/audio/sound.py +0 -0
  33. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/core/__init__.py +0 -0
  34. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/core/achievement_system.py +0 -0
  35. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/core/application.py +0 -0
  36. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/core/boot_splash.py +0 -0
  37. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/core/branding_guard.py +0 -0
  38. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/core/config.py +0 -0
  39. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/core/console.py +0 -0
  40. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/core/event_bus.py +0 -0
  41. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/core/plugin_system.py +0 -0
  42. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/core/save_system.py +0 -0
  43. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/core/time_manager.py +0 -0
  44. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/core/window.py +0 -0
  45. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/editor/__init__.py +0 -0
  46. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/environment/environment_system.py +0 -0
  47. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/graphics/__init__.py +0 -0
  48. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/graphics/camera.py +0 -0
  49. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/graphics/first_person.py +0 -0
  50. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/graphics/framebuffer.py +0 -0
  51. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/graphics/light.py +0 -0
  52. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/graphics/lod_system.py +0 -0
  53. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/graphics/material.py +0 -0
  54. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/graphics/mesh.py +0 -0
  55. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/graphics/mesh_factory.py +0 -0
  56. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/graphics/particle_system.py +0 -0
  57. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/graphics/post_process.py +0 -0
  58. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/graphics/renderer.py +0 -0
  59. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/graphics/shader.py +0 -0
  60. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/graphics/shader_library.py +0 -0
  61. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/graphics/sky_atmosphere.py +0 -0
  62. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/graphics/terrain.py +0 -0
  63. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/graphics/texture.py +0 -0
  64. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/graphics/voxel_mesh.py +0 -0
  65. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/graphics/water.py +0 -0
  66. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/graphics2d/__init__.py +0 -0
  67. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/network/network_manager.py +0 -0
  68. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/physics/__init__.py +0 -0
  69. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/physics/bullet_backend.py +0 -0
  70. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/physics/collider.py +0 -0
  71. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/physics/physics2d.py +0 -0
  72. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/physics/physics3d.py +0 -0
  73. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/physics/physics_world.py +0 -0
  74. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/physics/raycast.py +0 -0
  75. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/physics/rigidbody.py +0 -0
  76. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/platform/steam_api.py +0 -0
  77. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/resources/__init__.py +0 -0
  78. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/resources/asset_database.py +0 -0
  79. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/resources/asset_loader.py +0 -0
  80. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/resources/loaders/model_loader.py +0 -0
  81. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/resources/loaders/obj_loader.py +0 -0
  82. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/resources/loaders/texture_loader.py +0 -0
  83. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/resources/resource_manager.py +0 -0
  84. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/resources/save_load.py +0 -0
  85. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/resources/serialization.py +0 -0
  86. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/scene/__init__.py +0 -0
  87. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/scene/component.py +0 -0
  88. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/scene/entity.py +0 -0
  89. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/scene/scene.py +0 -0
  90. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/scene/scene_graph.py +0 -0
  91. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/scene/scene_manager.py +0 -0
  92. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/tools/__init__.py +0 -0
  93. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/tools/packager.py +0 -0
  94. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/ui/__init__.py +0 -0
  95. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/ui/button.py +0 -0
  96. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/ui/canvas.py +0 -0
  97. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/ui/hud.py +0 -0
  98. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/ui/text.py +0 -0
  99. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/utils/__init__.py +0 -0
  100. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/utils/fonts.py +0 -0
  101. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/utils/logger.py +0 -0
  102. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/utils/math_utils.py +0 -0
  103. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/utils/object_pool.py +0 -0
  104. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/utils/profiler.py +0 -0
  105. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/web/__init__.py +0 -0
  106. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/web/bot_detect.py +0 -0
  107. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/web/config.py +0 -0
  108. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/web/notifications.py +0 -0
  109. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/web/plain.py +0 -0
  110. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/web/registry.py +0 -0
  111. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/web/secrets.py +0 -0
  112. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/web/server.py +0 -0
  113. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/web/templates.py +0 -0
  114. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether/web/urls.py +0 -0
  115. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether_engine.egg-info/SOURCES.txt +0 -0
  116. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether_engine.egg-info/dependency_links.txt +0 -0
  117. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether_engine.egg-info/entry_points.txt +0 -0
  118. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether_engine.egg-info/requires.txt +3 -3
  119. {aether_engine-1.8.1 → aether_engine-1.8.2}/aether_engine.egg-info/top_level.txt +0 -0
  120. {aether_engine-1.8.1 → aether_engine-1.8.2}/examples/01_hello_cube.py +0 -0
  121. {aether_engine-1.8.1 → aether_engine-1.8.2}/examples/02_sphere_scene.py +0 -0
  122. {aether_engine-1.8.1 → aether_engine-1.8.2}/examples/03_first_person.py +0 -0
  123. {aether_engine-1.8.1 → aether_engine-1.8.2}/examples/04_physics2d.py +0 -0
  124. {aether_engine-1.8.1 → aether_engine-1.8.2}/pyproject.toml +0 -0
  125. {aether_engine-1.8.1 → aether_engine-1.8.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aether-engine
3
- Version: 1.8.1
3
+ Version: 1.8.2
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: pymunk>=6.0; extra == "full"
34
+ Requires-Dist: freetype-py>=2.4; extra == "full"
35
35
  Requires-Dist: imageio>=2.31; extra == "full"
36
+ Requires-Dist: trimesh>=3.21; extra == "full"
36
37
  Requires-Dist: pyyaml>=6.0; extra == "full"
37
38
  Requires-Dist: pyinstaller>=6.0; extra == "full"
38
- Requires-Dist: freetype-py>=2.4; extra == "full"
39
- Requires-Dist: trimesh>=3.21; extra == "full"
40
39
  Requires-Dist: pygame-ce>=2.5; extra == "full"
40
+ Requires-Dist: pymunk>=6.0; extra == "full"
41
41
  Dynamic: author
42
42
  Dynamic: classifier
43
43
  Dynamic: description
@@ -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.8.1"
34
+ __version__ = "1.8.2"
35
35
 
36
36
  from aether.graphics.voxel_mesh import ChunkedVoxelMesher
37
37
  from aether.accelerators import is_native_available, backend_name
@@ -101,10 +101,14 @@ NPC / НАГРАДЫ (RUST, опционально)
101
101
  engine.physics3d.create_body_for_entity(ent, collider=BoxCollider())
102
102
  engine.physics2d.create_box(x, y, w, h, mass=1.0)
103
103
 
104
- ВВОД
104
+ ВВОД / МЫШЬ
105
105
  import glfw
106
106
  engine.input.get_key(glfw.KEY_W)
107
107
  engine.input.get_key_down(glfw.KEY_SPACE)
108
+ engine.capture_mouse(True) # захват мыши (НЕ glfw на engine.window!)
109
+ dx, dy = engine.input.mouse_delta() # движение мыши за кадр
110
+ engine.input.lock_cursor(False) # отпустить курсор
111
+ # raw GLFW: engine.window.handle
108
112
 
109
113
  АПДЕЙТ
110
114
  engine.on_update(lambda dt: ...)
@@ -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, 159, 137, 150,
23
+ 135, 150, 137, 159, 137, 149,
24
24
  ]),
25
25
  }
26
26
 
@@ -377,6 +377,10 @@ class Engine:
377
377
  self.renderer2d.render()
378
378
  self.renderer2d.end_frame()
379
379
 
380
+ def capture_mouse(self, locked: bool = True):
381
+ """Lock/unlock cursor for custom camera control. Same as input.lock_cursor()."""
382
+ self.input.lock_cursor(locked)
383
+
380
384
  def on_update(self, callback):
381
385
  """Регистрирует callback(dt), вызываемый каждый кадр."""
382
386
  self._update_callbacks.append(callback)
@@ -57,6 +57,7 @@ class Input:
57
57
  self._mouse_buttons_prev = self._mouse_buttons.copy()
58
58
 
59
59
  def lock_cursor(self, locked: bool = True):
60
+ """Capture mouse for FPS / orbit camera. Use engine.input.lock_cursor(True)."""
60
61
  self.cursor_locked = locked
61
62
  mode = glfw.CURSOR_DISABLED if locked else glfw.CURSOR_NORMAL
62
63
  glfw.set_input_mode(self.window.handle, glfw.CURSOR, mode)
@@ -66,6 +67,14 @@ class Input:
66
67
  self.mouse_dx = 0.0
67
68
  self.mouse_dy = 0.0
68
69
 
70
+ def mouse_delta(self) -> tuple:
71
+ """Mouse movement since last frame (dx, dy). Requires lock_cursor(True) for FPS."""
72
+ return (self.mouse_dx, self.mouse_dy)
73
+
74
+ def get_cursor_pos(self) -> tuple:
75
+ """Window cursor position in pixels (x, y)."""
76
+ return (self.mouse_x, self.mouse_y)
77
+
69
78
  def get_key(self, key) -> bool:
70
79
  return self._keys.get(key, False)
71
80
 
@@ -0,0 +1,390 @@
1
+ # Aether Engine — Full Guide (v1.8.2)
2
+
3
+ **PyPI:** `pip install aether-engine` → **import:** `import aether` (NOT `aether_engine`)
4
+
5
+ Commands: `aether help` | `aether docs` (this file) | `aether website`
6
+
7
+ ---
8
+
9
+ ## 1. Engine
10
+
11
+ ```python
12
+ from aether import Engine
13
+
14
+ engine = Engine(width=1280, height=720, title="My Game")
15
+ engine.load_scene(scene)
16
+ engine.on_update(my_update) # def my_update(dt): ...
17
+ engine.run() # blocks until ESC
18
+
19
+ # Properties
20
+ engine.scene # current Scene
21
+ engine.input # keyboard / mouse
22
+ engine.window # Window (title, size, .handle for raw GLFW)
23
+ engine.renderer # 3D renderer
24
+ engine.renderer2d # 2D / HUD sprites
25
+ engine.physics2d # pymunk
26
+ engine.physics3d # lightweight 3D
27
+ engine.time.fps # float
28
+ engine.free_camera # True = built-in WASD fly (set False for custom cam)
29
+ engine.draw_hud # True = render HUD overlay each frame
30
+ engine.mode # "3d" or "2d"
31
+
32
+ # Methods
33
+ engine.set_mode("2d")
34
+ engine.enable_first_person(camera, eye_height=1.7, walk_speed=6)
35
+ engine.capture_mouse(True) # lock cursor — use this, NOT glfw on window directly
36
+ engine.on_update(callback)
37
+ engine.run()
38
+ engine.shutdown()
39
+ ```
40
+
41
+ ---
42
+
43
+ ## 2. Input (keyboard / mouse)
44
+
45
+ **Never pass `engine.window` to glfw directly.** Use `engine.window.handle` only if you need raw GLFW.
46
+
47
+ ```python
48
+ import glfw
49
+
50
+ inp = engine.input
51
+
52
+ # Keyboard
53
+ inp.get_key(glfw.KEY_W) # held
54
+ inp.get_key_down(glfw.KEY_SPACE) # pressed this frame
55
+ inp.get_key_up(glfw.KEY_E)
56
+
57
+ # Mouse buttons
58
+ inp.get_mouse_button(glfw.MOUSE_BUTTON_LEFT)
59
+ inp.get_mouse_button_down(glfw.MOUSE_BUTTON_RIGHT)
60
+
61
+ # Mouse look (FPS / orbit)
62
+ inp.lock_cursor(True) # or engine.capture_mouse(True)
63
+ dx, dy = inp.mouse_delta() # movement since last frame
64
+ # Also: inp.mouse_dx, inp.mouse_dy
65
+
66
+ # Toggle capture (like Tab in free camera)
67
+ if inp.get_key_down(glfw.KEY_TAB):
68
+ inp.lock_cursor(not inp.cursor_locked)
69
+
70
+ # Cursor position in pixels
71
+ x, y = inp.get_cursor_pos()
72
+ ```
73
+
74
+ ---
75
+
76
+ ## 3. Window
77
+
78
+ ```python
79
+ w = engine.window
80
+ w.set_title("HP 80 | score 5")
81
+ w.width, w.height
82
+ w.handle # raw GLFW window — for glfw.get_cursor_pos(engine.window.handle) etc.
83
+ # Do NOT: glfw.set_input_mode(engine.window, ...) — wrong type
84
+ # Do: engine.input.lock_cursor(True)
85
+ ```
86
+
87
+ ---
88
+
89
+ ## 4. Scene
90
+
91
+ ```python
92
+ from aether import Scene
93
+
94
+ scene = Scene("Level1")
95
+ scene.spawn(entity)
96
+ scene.remove(entity) # removes from scene + entity.destroy()
97
+ scene.despawn(entity) # alias
98
+ found = scene.find("Player")
99
+ scene.set_main_camera(camera)
100
+ scene.entities # list
101
+ ```
102
+
103
+ ---
104
+
105
+ ## 5. Entity
106
+
107
+ ```python
108
+ from aether import Entity, Transform, MeshRenderer
109
+
110
+ ent = Entity("Player")
111
+ ent.add_component(Transform(position=(0, 1, 0)))
112
+ ent.add_component(MeshRenderer(mesh=..., material=...))
113
+ ent.get_component("Transform")
114
+ ent.remove_component("MeshRenderer")
115
+ ent.destroy() # prefer scene.remove(ent)
116
+ ent.enabled = True
117
+ ```
118
+
119
+ ---
120
+
121
+ ## 6. Transform
122
+
123
+ `position`, `rotation`, `scale` = **numpy float32[3]**. Rotation in **degrees**.
124
+
125
+ ```python
126
+ tf = Transform(position=(0, 1, 0), rotation=(0, 45, 0), scale=(1, 1, 1))
127
+ tf.set_position(x, y, z)
128
+ tf.position[:] = (1, 2, 3)
129
+ tf.position[1] += 0.1
130
+ tf.set_rotation(rx, ry, rz)
131
+ tf.set_scale(sx, sy, sz)
132
+ tf.translate(dx, dy, dz)
133
+ tf.rotate(0, 90 * dt, 0)
134
+ tf.look_at(target_xyz)
135
+ tf.get_model_matrix()
136
+ ```
137
+
138
+ ---
139
+
140
+ ## 7. Camera
141
+
142
+ ```python
143
+ from aether import Camera
144
+
145
+ cam = Camera(position=(0, 5, 10), yaw=-90, pitch=-20, fov=60)
146
+ cam.position = (x, y, z)
147
+ cam.yaw, cam.pitch, cam.fov, cam.near, cam.far
148
+ cam.front, cam.right, cam.up
149
+ cam.look_at(player_tf.position)
150
+ cam.rotate(mouse_dx, mouse_dy)
151
+ cam.move_forward(dt)
152
+
153
+ # Third-person — disable built-in fly camera first
154
+ engine.free_camera = False
155
+ cam.position = (px, py + 6, pz + 10)
156
+ cam.look_at(target)
157
+ ```
158
+
159
+ ---
160
+
161
+ ## 8. MeshFactory
162
+
163
+ ```python
164
+ from aether import MeshFactory
165
+
166
+ MeshFactory.create_cube(size=1.0)
167
+ MeshFactory.create_plane(size=10.0)
168
+ MeshFactory.create_sphere(radius=1.0, sectors=24, stacks=16)
169
+ MeshFactory.create_cylinder(radius=0.5, height=2.0, segments=24, capped=True)
170
+ MeshFactory.create_pyramid(size=1.0, height=1.0)
171
+ ```
172
+
173
+ ---
174
+
175
+ ## 9. Material
176
+
177
+ ```python
178
+ from aether import Material
179
+
180
+ mat = Material("wood")
181
+ mat.albedo[:] = (0.8, 0.5, 0.2) # RGB 0..1
182
+ mat.metallic = 0.0
183
+ mat.roughness = 0.6
184
+ mat.ao = 1.0
185
+ ```
186
+
187
+ ---
188
+
189
+ ## 10. HUD
190
+
191
+ Origin **(0,0) = center of screen**, +Y up. **No text rendering** — use `engine.window.set_title(...)`.
192
+
193
+ ```python
194
+ from aether import Hud, HudBar
195
+
196
+ engine.draw_hud = True
197
+ hud = Hud(engine.renderer2d)
198
+
199
+ def update(dt):
200
+ hud.clear()
201
+ hud.draw_bar(-500, 300, 200, 20, hp, 100, (0.8, 0.2, 0.2, 1.0))
202
+ hud.draw_crosshair()
203
+ hud.draw_hotbar(7, 0, colors=[(1,0,0),(0,1,0)])
204
+ hud.draw_rect(0, -250, 100, 30, color=(0,0,0,0.5))
205
+ hud.draw_panel(0, 250, 300, 60)
206
+ engine.window.set_title(f"HP {hp}")
207
+ ```
208
+
209
+ ---
210
+
211
+ ## 11. Assets
212
+
213
+ ```python
214
+ from aether import AssetLoader, Sprite
215
+
216
+ mesh = AssetLoader.load_model("hero.glb")
217
+ sprite = Sprite.from_image("icon.png", x=0, y=0)
218
+ ```
219
+
220
+ ---
221
+
222
+ ## 12. Physics
223
+
224
+ **2D:** `engine.physics2d` (pymunk) — Y-up, sync `sprite.x, sprite.y = body.x, body.y`
225
+ **3D:** `engine.physics3d` — lightweight; PyBullet planned
226
+
227
+ ```python
228
+ from aether import BoxCollider
229
+ body = engine.physics3d.create_body_for_entity(ent, mass=1.0, collider=BoxCollider(size=(1,1,1)))
230
+ ```
231
+
232
+ ---
233
+
234
+ # EXAMPLE GAMES (copy-paste)
235
+
236
+ ## Example A — Hello Cube (3D basics)
237
+
238
+ ```python
239
+ from aether import Engine, Scene, Entity, Transform, MeshRenderer, MeshFactory, Material, Camera
240
+
241
+ engine = Engine(1280, 720, "Hello Cube")
242
+ scene = Scene("Main")
243
+ camera = Camera(position=(0, 4, 8), yaw=-90, pitch=-25)
244
+ scene.set_main_camera(camera)
245
+
246
+ floor = Entity("Floor")
247
+ floor.add_component(Transform(position=(0, -1, 0)))
248
+ m = Material(); m.albedo[:] = (0.7, 0.7, 0.8)
249
+ floor.add_component(MeshRenderer(mesh=MeshFactory.create_plane(12), material=m))
250
+ scene.spawn(floor)
251
+
252
+ cube = Entity("Cube")
253
+ cube.add_component(Transform(position=(0, 1.5, 0), scale=(2, 2, 2)))
254
+ m2 = Material(); m2.albedo[:] = (0.1, 1.0, 0.2)
255
+ cube.add_component(MeshRenderer(mesh=MeshFactory.create_cube(), material=m2))
256
+ scene.spawn(cube)
257
+
258
+ engine.load_scene(scene)
259
+ engine.run()
260
+ ```
261
+
262
+ ## Example B — Sphere ring (materials)
263
+
264
+ ```python
265
+ import math
266
+ from aether import Engine, Scene, Entity, Transform, MeshRenderer, MeshFactory, Material, Camera
267
+
268
+ engine = Engine(1280, 720, "Spheres")
269
+ scene = Scene("Spheres")
270
+ scene.set_main_camera(Camera(position=(0, 5, 15), yaw=-90, pitch=-20))
271
+
272
+ floor = Entity("Floor")
273
+ fm = Material(); fm.albedo[:] = (0.55, 0.58, 0.65)
274
+ floor.add_component(Transform(position=(0, -0.5, 0)))
275
+ floor.add_component(MeshRenderer(mesh=MeshFactory.create_plane(14), material=fm))
276
+ scene.spawn(floor)
277
+
278
+ for i in range(12):
279
+ a = i * math.pi * 2 / 12
280
+ mat = Material(); mat.albedo[:] = (0.3 + i*0.05, 0.5, 0.8 - i*0.04)
281
+ s = Entity(f"S{i}")
282
+ s.add_component(Transform(position=(math.cos(a)*4, 0.5, math.sin(a)*4)))
283
+ s.add_component(MeshRenderer(mesh=MeshFactory.create_sphere(0.5), material=mat))
284
+ scene.spawn(s)
285
+
286
+ engine.load_scene(scene)
287
+ engine.run()
288
+ ```
289
+
290
+ ## Example C — First person (FPS)
291
+
292
+ ```python
293
+ from aether import Engine, Scene, Entity, Transform, MeshRenderer, MeshFactory, Material, Camera
294
+
295
+ engine = Engine(1280, 720, "FPS")
296
+ scene = Scene("FPS")
297
+ cam = Camera(position=(0, 1.7, 8), yaw=-90, fov=75)
298
+ scene.set_main_camera(cam)
299
+
300
+ floor = Entity("Floor")
301
+ floor.add_component(Transform())
302
+ m = Material(); m.albedo[:] = (0.3, 0.34, 0.4)
303
+ floor.add_component(MeshRenderer(mesh=MeshFactory.create_plane(40), material=m))
304
+ scene.spawn(floor)
305
+
306
+ engine.load_scene(scene)
307
+ engine.enable_first_person(cam)
308
+ engine.run()
309
+ ```
310
+
311
+ ## Example D — 2D platformer (pymunk)
312
+
313
+ ```python
314
+ import glfw
315
+ from aether import Engine, Sprite
316
+
317
+ engine = Engine(960, 540, "2D")
318
+ engine.set_mode("2d")
319
+ engine.free_camera = False
320
+ r = engine.renderer2d
321
+ r.camera.zoom = 0.035
322
+
323
+ player_sprite = r.add_sprite(Sprite(x=-200, y=0, width=36, height=48, color=(0.2, 0.85, 1, 1)))
324
+ phys = engine.physics2d
325
+ phys.create_box(0, -120, 700, 40, static=True)
326
+ player = phys.create_box(-200, 0, 36, 48, mass=1.0)
327
+
328
+ def update(dt):
329
+ vx = 0
330
+ if engine.input.get_key(glfw.KEY_A): vx -= 260
331
+ if engine.input.get_key(glfw.KEY_D): vx += 260
332
+ phys.set_velocity(player, vx, player.vy)
333
+ if engine.input.get_key_down(glfw.KEY_SPACE) and abs(player.vy) < 20:
334
+ phys.apply_impulse(player, 0, 420)
335
+ player_sprite.x, player_sprite.y = player.x, player.y
336
+
337
+ engine.on_update(update)
338
+ engine.run()
339
+ ```
340
+
341
+ ## Example E — Custom camera + mouse (like your game)
342
+
343
+ ```python
344
+ import glfw
345
+ import math
346
+ from aether import Engine, Scene, Entity, Transform, MeshRenderer, MeshFactory, Material, Camera, Hud
347
+
348
+ engine = Engine(1280, 720, "Third Person")
349
+ scene = Scene("Main")
350
+ cam = Camera(position=(0, 5, 10), yaw=-90, pitch=-30)
351
+ scene.set_main_camera(cam)
352
+ engine.free_camera = False
353
+ engine.capture_mouse(True) # NOT glfw.set_input_mode(engine.window, ...)
354
+
355
+ player = Entity("Player")
356
+ ptf = Transform(position=(0, 0.5, 0))
357
+ player.add_component(ptf)
358
+ m = Material(); m.albedo[:] = (0.2, 0.6, 1.0)
359
+ player.add_component(MeshRenderer(mesh=MeshFactory.create_cube(), material=m))
360
+ scene.spawn(player)
361
+
362
+ yaw, pitch, dist = 0.0, 0.6, 10.0
363
+ engine.draw_hud = True
364
+ hud = Hud(engine.renderer2d)
365
+
366
+ def update(dt):
367
+ global yaw, pitch
368
+ if engine.input.get_key(glfw.KEY_TAB):
369
+ engine.capture_mouse(not engine.input.cursor_locked)
370
+ mdx, mdy = engine.input.mouse_delta()
371
+ yaw += mdx * 0.003
372
+ pitch = max(0.1, min(1.5, pitch - mdy * 0.003))
373
+ t = ptf.position
374
+ cam.position = (
375
+ t[0] + dist * math.cos(pitch) * math.sin(yaw),
376
+ t[1] + dist * math.sin(pitch),
377
+ t[2] + dist * math.cos(pitch) * math.cos(yaw),
378
+ )
379
+ cam.look_at(t)
380
+ hud.clear()
381
+ hud.draw_crosshair()
382
+
383
+ engine.on_update(update)
384
+ engine.load_scene(scene)
385
+ engine.run()
386
+ ```
387
+
388
+ ---
389
+
390
+ Aether Engine © entiti937
@@ -246,6 +246,19 @@ mat.albedo[:] = (0.9, 0.2, 0.2)
246
246
  mat.metallic = 0.8
247
247
  mat.roughness = 0.3</code></pre>
248
248
 
249
+ <h2>Input</h2>
250
+ <pre><code>import glfw
251
+
252
+ engine.input.get_key(glfw.KEY_W)
253
+ engine.input.get_key_down(glfw.KEY_SPACE)
254
+ engine.capture_mouse(True) # lock cursor
255
+ dx, dy = engine.input.mouse_delta()
256
+ engine.input.lock_cursor(False)
257
+
258
+ # WRONG: glfw.set_input_mode(engine.window, ...)
259
+ # RIGHT: engine.capture_mouse(True)
260
+ # Raw GLFW: engine.window.handle</code></pre>
261
+
249
262
  <h2>Hud</h2>
250
263
  <p><strong>Coords:</strong> (0,0) = center of window, +Y up. No text — use <code>engine.window.set_title(...)</code>.</p>
251
264
  <pre><code>from aether import Hud
@@ -498,16 +511,89 @@ aether build my_game.py --name MyGame --console</code></pre>
498
511
  """,
499
512
  "examples": """
500
513
  <section>
501
- <h1>Example Scripts</h1>
502
- <p class="lead">Small standalone demos included with the engine. Run any with <code>python path/to/script.py</code>.</p>
503
- <table class="data">
504
- <tr><th>Script</th><th>Teaches</th></tr>
505
- <tr><td><code>examples/01_hello_cube.py</code></td><td>Window, cube, free camera</td></tr>
506
- <tr><td><code>examples/02_sphere_scene.py</code></td><td>Multiple objects, materials</td></tr>
507
- <tr><td><code>examples/03_first_person.py</code></td><td>FPS controller</td></tr>
508
- <tr><td><code>examples/04_physics2d.py</code></td><td>2D physics playground</td></tr>
509
- </table>
510
- <div class="tip">Use these as templates — copy an example and replace meshes, input, and game rules with your own.</div>
514
+ <h1>Example Games — full code</h1>
515
+ <p class="lead">Copy-paste into <code>my_game.py</code>. Also in repo: <code>examples/</code>. Terminal: <code>aether docs</code></p>
516
+
517
+ <h2>Hello Cube (3D)</h2>
518
+ <pre><code>from aether import Engine, Scene, Entity, Transform, MeshRenderer, MeshFactory, Material, Camera
519
+
520
+ engine = Engine(1280, 720, "Hello")
521
+ scene = Scene("Main")
522
+ scene.set_main_camera(Camera(position=(0, 4, 8), yaw=-90, pitch=-25))
523
+ cube = Entity("Cube")
524
+ cube.add_component(Transform(position=(0, 1.5, 0)))
525
+ m = Material(); m.albedo[:] = (0.1, 1, 0.2)
526
+ cube.add_component(MeshRenderer(mesh=MeshFactory.create_cube(), material=m))
527
+ scene.spawn(cube)
528
+ engine.load_scene(scene)
529
+ engine.run()</code></pre>
530
+
531
+ <h2>FPS</h2>
532
+ <pre><code>from aether import Engine, Scene, Entity, Transform, MeshRenderer, MeshFactory, Material, Camera
533
+
534
+ engine = Engine(1280, 720, "FPS")
535
+ scene = Scene("FPS")
536
+ cam = Camera(position=(0, 1.7, 8), yaw=-90)
537
+ scene.set_main_camera(cam)
538
+ floor = Entity("Floor")
539
+ floor.add_component(Transform())
540
+ m = Material(); m.albedo[:] = (0.3, 0.34, 0.4)
541
+ floor.add_component(MeshRenderer(mesh=MeshFactory.create_plane(40), material=m))
542
+ scene.spawn(floor)
543
+ engine.load_scene(scene)
544
+ engine.enable_first_person(cam)
545
+ engine.run()</code></pre>
546
+
547
+ <h2>2D platformer (pymunk)</h2>
548
+ <pre><code>import glfw
549
+ from aether import Engine, Sprite
550
+
551
+ engine = Engine(960, 540, "2D"); engine.set_mode("2d"); engine.free_camera = False
552
+ r = engine.renderer2d; r.camera.zoom = 0.035
553
+ sp = r.add_sprite(Sprite(x=-200, y=0, width=36, height=48, color=(0.2,0.85,1,1)))
554
+ pw = engine.physics2d
555
+ pw.create_box(0, -120, 700, 40, static=True)
556
+ body = pw.create_box(-200, 0, 36, 48, mass=1.0)
557
+
558
+ def update(dt):
559
+ vx = -260 if engine.input.get_key(glfw.KEY_A) else 260 if engine.input.get_key(glfw.KEY_D) else 0
560
+ pw.set_velocity(body, vx, body.vy)
561
+ if engine.input.get_key_down(glfw.KEY_SPACE) and abs(body.vy) &lt; 20:
562
+ pw.apply_impulse(body, 0, 420)
563
+ sp.x, sp.y = body.x, body.y
564
+
565
+ engine.on_update(update); engine.run()</code></pre>
566
+
567
+ <h2>Third-person + mouse</h2>
568
+ <pre><code>import glfw, math
569
+ from aether import Engine, Scene, Entity, Transform, MeshRenderer, MeshFactory, Material, Camera, Hud
570
+
571
+ engine = Engine(1280, 720, "TP"); scene = Scene("Main")
572
+ cam = Camera(position=(0, 5, 10), yaw=-90, pitch=-30)
573
+ scene.set_main_camera(cam)
574
+ engine.free_camera = False
575
+ engine.capture_mouse(True) # NOT glfw.set_input_mode(engine.window, ...)
576
+
577
+ player = Entity("P"); ptf = Transform(position=(0, 0.5, 0)); player.add_component(ptf)
578
+ m = Material(); m.albedo[:] = (0.2, 0.6, 1)
579
+ player.add_component(MeshRenderer(mesh=MeshFactory.create_cube(), material=m))
580
+ scene.spawn(player)
581
+ yaw, pitch, dist = 0.0, 0.6, 10.0
582
+ hud = Hud(engine.renderer2d); engine.draw_hud = True
583
+
584
+ def update(dt):
585
+ global yaw, pitch
586
+ if engine.input.get_key_down(glfw.KEY_TAB):
587
+ engine.capture_mouse(not engine.input.cursor_locked)
588
+ mdx, mdy = engine.input.mouse_delta()
589
+ yaw += mdx * 0.003
590
+ pitch = max(0.1, min(1.5, pitch - mdy * 0.003))
591
+ t = ptf.position
592
+ cam.position = (t[0]+dist*math.cos(pitch)*math.sin(yaw), t[1]+dist*math.sin(pitch), t[2]+dist*math.cos(pitch)*math.cos(yaw))
593
+ cam.look_at(t)
594
+ hud.clear(); hud.draw_crosshair()
595
+
596
+ engine.on_update(update); engine.load_scene(scene); engine.run()</code></pre>
511
597
  </section>
512
598
  """,
513
599
  "architecture": """
@@ -207,10 +207,13 @@ aether build my_game.py --name MyGame
207
207
  Output: dist/MyGame.exe (Windows, PyInstaller)
208
208
  """,
209
209
  "examples": """
210
- examples/01_hello_cube.py — basics
211
- examples/02_sphere_scene.py — materials
212
- examples/03_first_person.py — FPS
213
- examples/04_physics2d.py — 2D physics
210
+ Full copy-paste games in aether docs (aether docs) — sections Example A through E.
211
+
212
+ Hello cube: Engine + Scene + Entity + MeshFactory.create_cube + engine.run()
213
+ FPS: engine.enable_first_person(camera)
214
+ 2D: engine.set_mode("2d") + physics2d + Sprite sync
215
+ Third-person: engine.free_camera=False + engine.capture_mouse(True) + input.mouse_delta()
216
+ NEVER glfw.set_input_mode(engine.window, ...) — use engine.capture_mouse(True)
214
217
  """,
215
218
  "architecture": """
216
219
  Engine -> GLFW (window/input/OpenGL), Renderer3D, Renderer2D, Physics3D, Physics2D, Audio, Scene
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aether-engine
3
- Version: 1.8.1
3
+ Version: 1.8.2
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: pymunk>=6.0; extra == "full"
34
+ Requires-Dist: freetype-py>=2.4; extra == "full"
35
35
  Requires-Dist: imageio>=2.31; extra == "full"
36
+ Requires-Dist: trimesh>=3.21; extra == "full"
36
37
  Requires-Dist: pyyaml>=6.0; extra == "full"
37
38
  Requires-Dist: pyinstaller>=6.0; extra == "full"
38
- Requires-Dist: freetype-py>=2.4; extra == "full"
39
- Requires-Dist: trimesh>=3.21; extra == "full"
40
39
  Requires-Dist: pygame-ce>=2.5; extra == "full"
40
+ Requires-Dist: pymunk>=6.0; extra == "full"
41
41
  Dynamic: author
42
42
  Dynamic: classifier
43
43
  Dynamic: description