aether-engine 1.0.0__py3-none-any.whl

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 (64) hide show
  1. aether/__init__.py +54 -0
  2. aether/audio/__init__.py +0 -0
  3. aether/audio/audio_manager.py +211 -0
  4. aether/audio/music.py +6 -0
  5. aether/audio/sound.py +6 -0
  6. aether/core/__init__.py +211 -0
  7. aether/core/achievement_system.py +304 -0
  8. aether/core/application.py +19 -0
  9. aether/core/config.py +75 -0
  10. aether/core/console.py +366 -0
  11. aether/core/engine.py +50 -0
  12. aether/core/event_bus.py +24 -0
  13. aether/core/input.py +58 -0
  14. aether/core/plugin_system.py +93 -0
  15. aether/core/save_system.py +409 -0
  16. aether/core/time_manager.py +25 -0
  17. aether/core/window.py +57 -0
  18. aether/graphics/__init__.py +0 -0
  19. aether/graphics/camera.py +69 -0
  20. aether/graphics/framebuffer.py +119 -0
  21. aether/graphics/light.py +32 -0
  22. aether/graphics/lod_system.py +170 -0
  23. aether/graphics/material.py +25 -0
  24. aether/graphics/mesh.py +83 -0
  25. aether/graphics/mesh_factory.py +67 -0
  26. aether/graphics/particle_system.py +268 -0
  27. aether/graphics/post_process.py +279 -0
  28. aether/graphics/renderer.py +135 -0
  29. aether/graphics/shader.py +61 -0
  30. aether/graphics/shader_library.py +157 -0
  31. aether/graphics/sky_atmosphere.py +309 -0
  32. aether/graphics/terrain.py +408 -0
  33. aether/graphics/texture.py +34 -0
  34. aether/graphics/water.py +432 -0
  35. aether/physics/__init__.py +0 -0
  36. aether/physics/bullet_backend.py +395 -0
  37. aether/physics/collider.py +22 -0
  38. aether/physics/physics_world.py +436 -0
  39. aether/physics/raycast.py +25 -0
  40. aether/physics/rigidbody.py +7 -0
  41. aether/resources/__init__.py +0 -0
  42. aether/resources/asset_database.py +76 -0
  43. aether/resources/resource_manager.py +147 -0
  44. aether/resources/serialization.py +114 -0
  45. aether/scene/__init__.py +0 -0
  46. aether/scene/component.py +368 -0
  47. aether/scene/entity.py +46 -0
  48. aether/scene/scene.py +33 -0
  49. aether/scene/scene_graph.py +252 -0
  50. aether/scene/scene_manager.py +191 -0
  51. aether/ui/__init__.py +0 -0
  52. aether/ui/button.py +297 -0
  53. aether/ui/canvas.py +11 -0
  54. aether/ui/text.py +286 -0
  55. aether/utils/__init__.py +0 -0
  56. aether/utils/logger.py +16 -0
  57. aether/utils/math_utils.py +31 -0
  58. aether/utils/object_pool.py +137 -0
  59. aether/utils/profiler.py +187 -0
  60. aether_engine-1.0.0.dist-info/METADATA +71 -0
  61. aether_engine-1.0.0.dist-info/RECORD +64 -0
  62. aether_engine-1.0.0.dist-info/WHEEL +5 -0
  63. aether_engine-1.0.0.dist-info/licenses/LICENSE +21 -0
  64. aether_engine-1.0.0.dist-info/top_level.txt +1 -0
aether/__init__.py ADDED
@@ -0,0 +1,54 @@
1
+ """
2
+ Aether Engine — полноценный 3D игровой движок на Python с OpenGL
3
+ """
4
+
5
+ __version__ = "1.0.0"
6
+ __author__ = "entiti937"
7
+
8
+ # Ядро
9
+ from aether.core.engine import Engine
10
+ from aether.core.application import Application
11
+ from aether.core.config import EngineConfig, GraphicsConfig, PhysicsConfig, AudioConfig
12
+
13
+ # Сцена
14
+ from aether.scene.entity import Entity
15
+ from aether.scene.component import (
16
+ Transform, MeshRenderer, CameraComponent,
17
+ LightComponent, RigidbodyComponent, ScriptComponent, AnimatorComponent
18
+ )
19
+ from aether.scene.scene import Scene
20
+ from aether.scene.scene_manager import SceneManager
21
+
22
+ # Графика
23
+ from aether.graphics.shader import Shader
24
+ from aether.graphics.shader_library import ShaderLibrary
25
+ from aether.graphics.mesh import Mesh
26
+ from aether.graphics.mesh_factory import MeshFactory
27
+ from aether.graphics.material import Material
28
+ from aether.graphics.texture import Texture
29
+ from aether.graphics.camera import Camera
30
+ from aether.graphics.light import DirectionalLight, PointLight, SpotLight
31
+ from aether.graphics.renderer import Renderer
32
+ from aether.graphics.particle_system import ParticleEmitter, ParticleRenderer
33
+
34
+ # Физика
35
+ from aether.physics.physics_world import PhysicsWorld
36
+ from aether.physics.collider import BoxCollider, SphereCollider
37
+ from aether.physics.rigidbody import Rigidbody
38
+
39
+ # Ресурсы
40
+ from aether.resources.resource_manager import ResourceManager
41
+ from aether.resources.asset_database import AssetDatabase
42
+ from aether.resources.loaders.obj_loader import OBJLoader
43
+ from aether.resources.loaders.texture_loader import TextureLoader
44
+
45
+ # UI
46
+ from aether.ui.canvas import Canvas
47
+ from aether.ui.button import Button, Panel, Label
48
+ from aether.ui.text import Font, TextRenderer
49
+
50
+ # Аудио
51
+ from aether.audio.audio_manager import AudioManager, Sound, Music
52
+
53
+ # Утилиты
54
+ from aether.utils.profiler import Profiler, ScopedProfiler
File without changes
@@ -0,0 +1,211 @@
1
+ """Менеджер звука на pygame.mixer"""
2
+ import numpy as np
3
+ from typing import Dict, Optional
4
+ import os
5
+
6
+
7
+ class Sound:
8
+ """Звуковой эффект"""
9
+
10
+ def __init__(self, path: str = ""):
11
+ self.path = path
12
+ self.volume = 1.0
13
+ self.pitch = 1.0
14
+ self.loop = False
15
+ self._sound = None
16
+ self._channel = None
17
+
18
+ if path:
19
+ self.load(path)
20
+
21
+ def load(self, path: str):
22
+ """Загружает звук"""
23
+ try:
24
+ import pygame
25
+ self._sound = pygame.mixer.Sound(path)
26
+ self.path = path
27
+ except Exception as e:
28
+ print(f"[Sound] Failed to load {path}: {e}")
29
+
30
+ def play(self, volume: float = 1.0, pitch: float = 1.0):
31
+ """Проигрывает звук"""
32
+ if self._sound:
33
+ self._sound.set_volume(volume * self.volume)
34
+ self._channel = self._sound.play()
35
+
36
+ def stop(self):
37
+ """Останавливает звук"""
38
+ if self._channel:
39
+ self._channel.stop()
40
+
41
+ def set_volume(self, volume: float):
42
+ """Устанавливает громкость"""
43
+ self.volume = max(0.0, min(1.0, volume))
44
+ if self._channel:
45
+ self._channel.set_volume(self.volume)
46
+
47
+
48
+ class Music:
49
+ """Фоновая музыка"""
50
+
51
+ def __init__(self, path: str = ""):
52
+ self.path = path
53
+ self.volume = 1.0
54
+ self.loop = True
55
+ self._loaded = False
56
+
57
+ def load(self, path: str):
58
+ """Загружает музыку"""
59
+ try:
60
+ import pygame
61
+ pygame.mixer.music.load(path)
62
+ self.path = path
63
+ self._loaded = True
64
+ except Exception as e:
65
+ print(f"[Music] Failed to load {path}: {e}")
66
+
67
+ def play(self, volume: float = 1.0, loop: bool = True):
68
+ """Проигрывает музыку"""
69
+ if self._loaded:
70
+ try:
71
+ import pygame
72
+ pygame.mixer.music.set_volume(volume * self.volume)
73
+ pygame.mixer.music.play(-1 if loop else 0)
74
+ except Exception as e:
75
+ print(f"[Music] Failed to play: {e}")
76
+
77
+ def pause(self):
78
+ """Ставит на паузу"""
79
+ try:
80
+ import pygame
81
+ pygame.mixer.music.pause()
82
+ except:
83
+ pass
84
+
85
+ def resume(self):
86
+ """Продолжает проигрывание"""
87
+ try:
88
+ import pygame
89
+ pygame.mixer.music.unpause()
90
+ except:
91
+ pass
92
+
93
+ def stop(self):
94
+ """Останавливает музыку"""
95
+ try:
96
+ import pygame
97
+ pygame.mixer.music.stop()
98
+ except:
99
+ pass
100
+
101
+
102
+ class AudioManager:
103
+ """Менеджер звука"""
104
+
105
+ _instance = None
106
+
107
+ def __new__(cls):
108
+ if cls._instance is None:
109
+ cls._instance = super().__new__(cls)
110
+ cls._instance._initialize()
111
+ return cls._instance
112
+
113
+ def _initialize(self):
114
+ """Инициализация аудио системы"""
115
+ self.sounds: Dict[str, Sound] = {}
116
+ self.current_music: Optional[Music] = None
117
+
118
+ self.master_volume = 1.0
119
+ self.sfx_volume = 1.0
120
+ self.music_volume = 0.8
121
+
122
+ self._initialized = False
123
+
124
+ try:
125
+ import pygame
126
+ pygame.mixer.init(frequency=44100, size=-16, channels=32, buffer=512)
127
+ self._initialized = True
128
+ print("[Audio] Initialized (pygame.mixer)")
129
+ except ImportError:
130
+ print("[Audio] pygame not available, audio disabled")
131
+ except Exception as e:
132
+ print(f"[Audio] Failed to initialize: {e}")
133
+
134
+ @property
135
+ def initialized(self) -> bool:
136
+ return self._initialized
137
+
138
+ def load_sound(self, name: str, path: str) -> Sound:
139
+ """Загружает звуковой эффект"""
140
+ sound = Sound(path)
141
+ self.sounds[name] = sound
142
+ return sound
143
+
144
+ def play_sound(self, name: str, volume: float = 1.0, pitch: float = 1.0):
145
+ """Проигрывает звук по имени"""
146
+ if not self._initialized:
147
+ return
148
+
149
+ sound = self.sounds.get(name)
150
+ if sound:
151
+ sound.play(volume * self.sfx_volume * self.master_volume, pitch)
152
+
153
+ def stop_sound(self, name: str):
154
+ """Останавливает звук"""
155
+ sound = self.sounds.get(name)
156
+ if sound:
157
+ sound.stop()
158
+
159
+ def play_music(self, path: str, volume: float = 1.0, loop: bool = True):
160
+ """Проигрывает фоновую музыку"""
161
+ if not self._initialized:
162
+ return
163
+
164
+ if self.current_music:
165
+ self.current_music.stop()
166
+
167
+ self.current_music = Music(path)
168
+ self.current_music.play(volume * self.music_volume * self.master_volume, loop)
169
+
170
+ def pause_music(self):
171
+ """Ставит музыку на паузу"""
172
+ if self.current_music:
173
+ self.current_music.pause()
174
+
175
+ def resume_music(self):
176
+ """Продолжает музыку"""
177
+ if self.current_music:
178
+ self.current_music.resume()
179
+
180
+ def stop_music(self):
181
+ """Останавливает музыку"""
182
+ if self.current_music:
183
+ self.current_music.stop()
184
+ self.current_music = None
185
+
186
+ def set_master_volume(self, volume: float):
187
+ """Устанавливает общую громкость"""
188
+ self.master_volume = max(0.0, min(1.0, volume))
189
+
190
+ def set_sfx_volume(self, volume: float):
191
+ """Устанавливает громкость эффектов"""
192
+ self.sfx_volume = max(0.0, min(1.0, volume))
193
+
194
+ def set_music_volume(self, volume: float):
195
+ """Устанавливает громкость музыки"""
196
+ self.music_volume = max(0.0, min(1.0, volume))
197
+ if self.current_music:
198
+ self.current_music.play(self.music_volume * self.master_volume)
199
+
200
+ def cleanup(self):
201
+ """Очищает аудио систему"""
202
+ self.stop_music()
203
+ self.sounds.clear()
204
+
205
+ try:
206
+ import pygame
207
+ pygame.mixer.quit()
208
+ except:
209
+ pass
210
+
211
+ print("[Audio] Cleanup complete")
aether/audio/music.py ADDED
@@ -0,0 +1,6 @@
1
+ """Фоновая музыка"""
2
+ class Music:
3
+ def __init__(self, path=""):
4
+ self.path = path
5
+ self.volume = 1.0
6
+ self.loop = True
aether/audio/sound.py ADDED
@@ -0,0 +1,6 @@
1
+ """Звуковой эффект"""
2
+ class Sound:
3
+ def __init__(self, path=""):
4
+ self.path = path
5
+ self.volume = 1.0
6
+ self.pitch = 1.0
@@ -0,0 +1,211 @@
1
+ """Обработка ввода: клавиатура, мышь, геймпад"""
2
+ import glfw
3
+ import numpy as np
4
+
5
+
6
+ class Input:
7
+ """Система ввода"""
8
+
9
+ # Константы кнопок мыши
10
+ MOUSE_LEFT = glfw.MOUSE_BUTTON_LEFT
11
+ MOUSE_RIGHT = glfw.MOUSE_BUTTON_RIGHT
12
+ MOUSE_MIDDLE = glfw.MOUSE_BUTTON_MIDDLE
13
+
14
+ # Константы геймпада
15
+ GAMEPAD_A = 0
16
+ GAMEPAD_B = 1
17
+ GAMEPAD_X = 2
18
+ GAMEPAD_Y = 3
19
+ GAMEPAD_LB = 4
20
+ GAMEPAD_RB = 5
21
+ GAMEPAD_START = 7
22
+ GAMEPAD_LEFT_STICK_X = 0
23
+ GAMEPAD_LEFT_STICK_Y = 1
24
+ GAMEPAD_RIGHT_STICK_X = 2
25
+ GAMEPAD_RIGHT_STICK_Y = 3
26
+ GAMEPAD_LEFT_TRIGGER = 4
27
+ GAMEPAD_RIGHT_TRIGGER = 5
28
+
29
+ def __init__(self, window):
30
+ self.window = window
31
+
32
+ # Клавиатура
33
+ self._keys = {}
34
+ self._keys_prev = {}
35
+
36
+ # Мышь
37
+ self._mouse_buttons = {}
38
+ self._mouse_buttons_prev = {}
39
+ self.mouse_x = 0.0
40
+ self.mouse_y = 0.0
41
+ self.mouse_dx = 0.0
42
+ self.mouse_dy = 0.0
43
+ self.mouse_scroll = 0.0
44
+ self._first_mouse = True
45
+ self._cursor_locked = False
46
+
47
+ # Геймпад
48
+ self._gamepad_connected = False
49
+ self._gamepad_id = None
50
+ self._gamepad_buttons = {}
51
+ self._gamepad_buttons_prev = {}
52
+ self._gamepad_axes = {}
53
+ self._gamepad_axes_prev = {}
54
+
55
+ # Настройка коллбэков
56
+ glfw.set_key_callback(window.handle, self._key_callback)
57
+ glfw.set_mouse_button_callback(window.handle, self._mouse_button_callback)
58
+ glfw.set_cursor_pos_callback(window.handle, self._cursor_callback)
59
+ glfw.set_scroll_callback(window.handle, self._scroll_callback)
60
+
61
+ # Проверяем наличие геймпада
62
+ self._check_gamepad()
63
+
64
+ def _key_callback(self, window, key, scancode, action, mods):
65
+ """Коллбэк клавиатуры"""
66
+ if action == glfw.PRESS:
67
+ self._keys[key] = True
68
+ elif action == glfw.RELEASE:
69
+ self._keys[key] = False
70
+
71
+ def _mouse_button_callback(self, window, button, action, mods):
72
+ """Коллбэк кнопок мыши"""
73
+ if action == glfw.PRESS:
74
+ self._mouse_buttons[button] = True
75
+ elif action == glfw.RELEASE:
76
+ self._mouse_buttons[button] = False
77
+
78
+ def _cursor_callback(self, window, xpos, ypos):
79
+ """Коллбэк движения мыши"""
80
+ if self._first_mouse:
81
+ self.mouse_x = xpos
82
+ self.mouse_y = ypos
83
+ self._first_mouse = False
84
+
85
+ self.mouse_dx = xpos - self.mouse_x
86
+ self.mouse_dy = ypos - self.mouse_y
87
+ self.mouse_x = xpos
88
+ self.mouse_y = ypos
89
+
90
+ def _scroll_callback(self, window, xoffset, yoffset):
91
+ """Коллбэк колёсика мыши"""
92
+ self.mouse_scroll = yoffset
93
+
94
+ def _check_gamepad(self):
95
+ """Проверяет подключение геймпада"""
96
+ for jid in range(glfw.JOYSTICK_1, glfw.JOYSTICK_16):
97
+ if glfw.joystick_present(jid):
98
+ self._gamepad_connected = True
99
+ self._gamepad_id = jid
100
+ name = glfw.get_joystick_name(jid)
101
+ print(f"[Input] Gamepad connected: {name}")
102
+ return
103
+
104
+ self._gamepad_connected = False
105
+ self._gamepad_id = None
106
+
107
+ def update(self):
108
+ """Обновляет состояние ввода"""
109
+ # Сохраняем предыдущее состояние
110
+ self._keys_prev = self._keys.copy()
111
+ self._mouse_buttons_prev = self._mouse_buttons.copy()
112
+ self._gamepad_buttons_prev = self._gamepad_buttons.copy()
113
+ self._gamepad_axes_prev = self._gamepad_axes.copy()
114
+
115
+ # Сбрасываем дельты мыши
116
+ self.mouse_dx = 0.0
117
+ self.mouse_dy = 0.0
118
+ self.mouse_scroll = 0.0
119
+
120
+ # Обновляем геймпад
121
+ if self._gamepad_connected and self._gamepad_id is not None:
122
+ if not glfw.joystick_present(self._gamepad_id):
123
+ self._gamepad_connected = False
124
+ self._gamepad_id = None
125
+ print("[Input] Gamepad disconnected")
126
+ else:
127
+ buttons = glfw.get_joystick_buttons(self._gamepad_id)
128
+ for i, state in enumerate(buttons):
129
+ self._gamepad_buttons[i] = (state == glfw.PRESS)
130
+
131
+ axes = glfw.get_joystick_axes(self._gamepad_id)
132
+ for i, value in enumerate(axes):
133
+ self._gamepad_axes[i] = value
134
+
135
+ # ---- Клавиатура ----
136
+
137
+ def get_key(self, key) -> bool:
138
+ """Клавиша нажата (удерживается)"""
139
+ return self._keys.get(key, False)
140
+
141
+ def get_key_down(self, key) -> bool:
142
+ """Клавиша нажата в этом кадре"""
143
+ return self._keys.get(key, False) and not self._keys_prev.get(key, False)
144
+
145
+ def get_key_up(self, key) -> bool:
146
+ """Клавиша отпущена в этом кадре"""
147
+ return not self._keys.get(key, False) and self._keys_prev.get(key, False)
148
+
149
+ # ---- Мышь ----
150
+
151
+ def get_mouse_button(self, button) -> bool:
152
+ """Кнопка мыши нажата (удерживается)"""
153
+ return self._mouse_buttons.get(button, False)
154
+
155
+ def get_mouse_button_down(self, button) -> bool:
156
+ """Кнопка мыши нажата в этом кадре"""
157
+ return self._mouse_buttons.get(button, False) and not self._mouse_buttons_prev.get(button, False)
158
+
159
+ def get_mouse_button_up(self, button) -> bool:
160
+ """Кнопка мыши отпущена в этом кадре"""
161
+ return not self._mouse_buttons.get(button, False) and self._mouse_buttons_prev.get(button, False)
162
+
163
+ def get_mouse_position(self) -> tuple:
164
+ """Возвращает позицию мыши"""
165
+ return (self.mouse_x, self.mouse_y)
166
+
167
+ def get_mouse_delta(self) -> tuple:
168
+ """Возвращает дельту движения мыши"""
169
+ return (self.mouse_dx, self.mouse_dy)
170
+
171
+ def get_mouse_scroll(self) -> float:
172
+ """Возвращает значение колёсика"""
173
+ return self.mouse_scroll
174
+
175
+ # ---- Геймпад ----
176
+
177
+ def get_gamepad_button(self, button) -> bool:
178
+ """Кнопка геймпада нажата (удерживается)"""
179
+ return self._gamepad_buttons.get(button, False)
180
+
181
+ def get_gamepad_button_down(self, button) -> bool:
182
+ """Кнопка геймпада нажата в этом кадре"""
183
+ return self._gamepad_buttons.get(button, False) and not self._gamepad_buttons_prev.get(button, False)
184
+
185
+ def get_gamepad_axis(self, axis) -> float:
186
+ """Возвращает значение оси геймпада"""
187
+ value = self._gamepad_axes.get(axis, 0.0)
188
+ # Мёртвая зона
189
+ if abs(value) < 0.15:
190
+ return 0.0
191
+ return value
192
+
193
+ def is_gamepad_connected(self) -> bool:
194
+ """Подключён ли геймпад"""
195
+ return self._gamepad_connected
196
+
197
+ # ---- Управление курсором ----
198
+
199
+ def lock_cursor(self):
200
+ """Захватывает курсор (для FPS)"""
201
+ glfw.set_input_mode(self.window.handle, glfw.CURSOR, glfw.CURSOR_DISABLED)
202
+ self._cursor_locked = True
203
+
204
+ def unlock_cursor(self):
205
+ """Освобождает курсор"""
206
+ glfw.set_input_mode(self.window.handle, glfw.CURSOR, glfw.CURSOR_NORMAL)
207
+ self._cursor_locked = False
208
+
209
+ def is_cursor_locked(self) -> bool:
210
+ """Захвачен ли курсор"""
211
+ return self._cursor_locked