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
@@ -0,0 +1,408 @@
1
+ """Система террайна с LOD и текстурированием"""
2
+ import numpy as np
3
+ from OpenGL.GL import *
4
+ from typing import List, Tuple, Optional
5
+ from aether.graphics.mesh import Mesh
6
+ from aether.graphics.texture import Texture
7
+
8
+
9
+ class TerrainChunk:
10
+ """Чанк террайна"""
11
+
12
+ def __init__(self,
13
+ x: int, z: int,
14
+ size: int,
15
+ resolution: int,
16
+ height_map: np.ndarray):
17
+ self.grid_x = x
18
+ self.grid_z = z
19
+ self.size = size
20
+ self.resolution = resolution
21
+ self.height_map = height_map
22
+
23
+ # Позиция в мире
24
+ self.world_x = x * size
25
+ self.world_z = z * size
26
+
27
+ # Меш
28
+ self.mesh: Optional[Mesh] = None
29
+ self._generate_mesh()
30
+
31
+ def _generate_mesh(self):
32
+ """Генерирует меш чанка"""
33
+ vertices = []
34
+ normals = []
35
+ uvs = []
36
+ indices = []
37
+
38
+ step = self.size / (self.resolution - 1)
39
+
40
+ # Вершины
41
+ for iz in range(self.resolution):
42
+ for ix in range(self.resolution):
43
+ # Локальные координаты
44
+ lx = ix * step
45
+ lz = iz * step
46
+
47
+ # Высота из height_map
48
+ hx = int(ix * self.height_map.shape[0] / self.resolution)
49
+ hz = int(iz * self.height_map.shape[1] / self.resolution)
50
+
51
+ height = 0.0
52
+ if 0 <= hx < self.height_map.shape[0] and 0 <= hz < self.height_map.shape[1]:
53
+ height = self.height_map[hx, hz]
54
+
55
+ vertices.append([lx, height, lz])
56
+
57
+ # UV координаты
58
+ uvs.append([ix / (self.resolution - 1), iz / (self.resolution - 1)])
59
+
60
+ vertices = np.array(vertices, dtype=np.float32)
61
+
62
+ # Нормали (вычисляем из соседних вершин)
63
+ normals = self._calculate_normals(vertices)
64
+
65
+ # Индексы (треугольники)
66
+ for iz in range(self.resolution - 1):
67
+ for ix in range(self.resolution - 1):
68
+ top_left = iz * self.resolution + ix
69
+ top_right = top_left + 1
70
+ bottom_left = (iz + 1) * self.resolution + ix
71
+ bottom_right = bottom_left + 1
72
+
73
+ # Два треугольника
74
+ indices.extend([top_left, bottom_left, top_right])
75
+ indices.extend([top_right, bottom_left, bottom_right])
76
+
77
+ indices = np.array(indices, dtype=np.uint32)
78
+
79
+ # Создаём меш
80
+ self.mesh = Mesh(vertices, indices, normals, np.array(uvs, dtype=np.float32))
81
+
82
+ def _calculate_normals(self, vertices: np.ndarray) -> np.ndarray:
83
+ """Вычисляет нормали из вершин"""
84
+ normals = np.zeros_like(vertices)
85
+
86
+ for iz in range(1, self.resolution - 1):
87
+ for ix in range(1, self.resolution - 1):
88
+ i = iz * self.resolution + ix
89
+
90
+ # Соседние вершины
91
+ left = vertices[iz * self.resolution + (ix - 1)]
92
+ right = vertices[iz * self.resolution + (ix + 1)]
93
+ top = vertices[(iz - 1) * self.resolution + ix]
94
+ bottom = vertices[(iz + 1) * self.resolution + ix]
95
+
96
+ # Вычисляем нормаль
97
+ dx = right - left
98
+ dz = bottom - top
99
+
100
+ normal = np.cross(dz, dx)
101
+ norm = np.linalg.norm(normal)
102
+
103
+ if norm > 0:
104
+ normals[i] = normal / norm
105
+ else:
106
+ normals[i] = np.array([0, 1, 0])
107
+
108
+ return normals
109
+
110
+ def get_height_at(self, world_x: float, world_z: float) -> float:
111
+ """Получает высоту в точке мира"""
112
+ # Локальные координаты
113
+ local_x = world_x - self.world_x
114
+ local_z = world_z - self.world_z
115
+
116
+ if local_x < 0 or local_x > self.size or local_z < 0 or local_z > self.size:
117
+ return 0.0
118
+
119
+ # Нормализованные координаты
120
+ nx = local_x / self.size
121
+ nz = local_z / self.size
122
+
123
+ # Индексы в height_map
124
+ hx = int(nx * (self.height_map.shape[0] - 1))
125
+ hz = int(nz * (self.height_map.shape[1] - 1))
126
+
127
+ if 0 <= hx < self.height_map.shape[0] - 1 and 0 <= hz < self.height_map.shape[1] - 1:
128
+ # Билинейная интерполяция
129
+ fx = nx * (self.height_map.shape[0] - 1) - hx
130
+ fz = nz * (self.height_map.shape[1] - 1) - hz
131
+
132
+ h00 = self.height_map[hx, hz]
133
+ h10 = self.height_map[hx + 1, hz]
134
+ h01 = self.height_map[hx, hz + 1]
135
+ h11 = self.height_map[hx + 1, hz + 1]
136
+
137
+ return (h00 * (1 - fx) * (1 - fz) +
138
+ h10 * fx * (1 - fz) +
139
+ h01 * (1 - fx) * fz +
140
+ h11 * fx * fz)
141
+
142
+ return 0.0
143
+
144
+
145
+ class Terrain:
146
+ """Террайн (ландшафт)"""
147
+
148
+ def __init__(self,
149
+ width: int = 512,
150
+ depth: int = 512,
151
+ max_height: float = 100.0,
152
+ chunk_size: int = 64,
153
+ chunk_resolution: int = 65):
154
+
155
+ self.width = width
156
+ self.depth = depth
157
+ self.max_height = max_height
158
+ self.chunk_size = chunk_size
159
+ self.chunk_resolution = chunk_resolution
160
+
161
+ # Чанки
162
+ self.chunks: dict = {}
163
+ self._chunks_x = width // chunk_size
164
+ self._chunks_z = depth // chunk_size
165
+
166
+ # Текстуры
167
+ self.splat_map: Optional[Texture] = None
168
+ self.detail_textures: List[Texture] = []
169
+
170
+ # Height map всего террайна
171
+ self.height_map = np.zeros((width + 1, depth + 1), dtype=np.float32)
172
+
173
+ # Настройки
174
+ self.tesselation_enabled = False
175
+ self.tesselation_factor = 1.0
176
+ self.wireframe = False
177
+
178
+ print(f"[Terrain] Created {width}x{depth} ({self._chunks_x * self._chunks_z} chunks)")
179
+
180
+ def generate_height_map(self,
181
+ seed: int = 0,
182
+ octaves: int = 6,
183
+ persistence: float = 0.5,
184
+ lacunarity: float = 2.0,
185
+ scale: float = 100.0):
186
+ """Генерирует height map с помощью шума Перлина"""
187
+ try:
188
+ from noise import pnoise2
189
+
190
+ np.random.seed(seed)
191
+
192
+ for x in range(self.width + 1):
193
+ for z in range(self.depth + 1):
194
+ height = pnoise2(
195
+ x / scale,
196
+ z / scale,
197
+ octaves=octaves,
198
+ persistence=persistence,
199
+ lacunarity=lacunarity,
200
+ base=seed
201
+ )
202
+ # Нормализуем от [-1, 1] к [0, max_height]
203
+ self.height_map[x, z] = (height + 1) * 0.5 * self.max_height
204
+
205
+ print(f"[Terrain] Height map generated (seed: {seed})")
206
+
207
+ except ImportError:
208
+ print("[Terrain] 'noise' module not found. Using random heights.")
209
+ self._generate_random_height_map(seed)
210
+
211
+ def _generate_random_height_map(self, seed: int):
212
+ """Генерирует случайную height map"""
213
+ np.random.seed(seed)
214
+
215
+ # Простой шум через интерполяцию
216
+ points = np.random.rand(16, 16) * self.max_height
217
+
218
+ for x in range(self.width + 1):
219
+ for z in range(self.depth + 1):
220
+ fx = (x / self.width) * (points.shape[0] - 1)
221
+ fz = (z / self.depth) * (points.shape[1] - 1)
222
+
223
+ ix, iz = int(fx), int(fz)
224
+ fx -= ix
225
+ fz -= iz
226
+
227
+ ix = min(ix, points.shape[0] - 2)
228
+ iz = min(iz, points.shape[1] - 2)
229
+
230
+ h = (points[ix, iz] * (1 - fx) * (1 - fz) +
231
+ points[ix + 1, iz] * fx * (1 - fz) +
232
+ points[ix, iz + 1] * (1 - fx) * fz +
233
+ points[ix + 1, iz + 1] * fx * fz)
234
+
235
+ self.height_map[x, z] = h
236
+
237
+ def generate_chunks(self):
238
+ """Генерирует все чанки"""
239
+ for cx in range(self._chunks_x):
240
+ for cz in range(self._chunks_z):
241
+ self._generate_chunk(cx, cz)
242
+
243
+ print(f"[Terrain] Generated {len(self.chunks)} chunks")
244
+
245
+ def _generate_chunk(self, cx: int, cz: int):
246
+ """Генерирует один чанк"""
247
+ # Извлекаем часть height map для чанка
248
+ x_start = cx * self.chunk_size
249
+ z_start = cz * self.chunk_size
250
+
251
+ chunk_hmap = np.zeros((self.chunk_size + 1, self.chunk_size + 1), dtype=np.float32)
252
+
253
+ for x in range(self.chunk_size + 1):
254
+ for z in range(self.chunk_size + 1):
255
+ world_x = x_start + x
256
+ world_z = z_start + z
257
+
258
+ if world_x <= self.width and world_z <= self.depth:
259
+ chunk_hmap[x, z] = self.height_map[world_x, world_z]
260
+
261
+ chunk = TerrainChunk(
262
+ cx, cz,
263
+ self.chunk_size,
264
+ self.chunk_resolution,
265
+ chunk_hmap
266
+ )
267
+
268
+ self.chunks[(cx, cz)] = chunk
269
+
270
+ def get_height(self, world_x: float, world_z: float) -> float:
271
+ """Получает высоту в точке мира"""
272
+ # Определяем чанк
273
+ cx = int(world_x // self.chunk_size)
274
+ cz = int(world_z // self.chunk_size)
275
+
276
+ chunk = self.chunks.get((cx, cz))
277
+ if chunk:
278
+ return chunk.get_height_at(world_x, world_z)
279
+
280
+ return 0.0
281
+
282
+ def get_normal(self, world_x: float, world_z: float) -> np.ndarray:
283
+ """Получает нормаль в точке мира"""
284
+ eps = 0.1
285
+
286
+ h_left = self.get_height(world_x - eps, world_z)
287
+ h_right = self.get_height(world_x + eps, world_z)
288
+ h_down = self.get_height(world_x, world_z - eps)
289
+ h_up = self.get_height(world_x, world_z + eps)
290
+
291
+ normal = np.array([
292
+ (h_left - h_right) / (2 * eps),
293
+ 1.0,
294
+ (h_down - h_up) / (2 * eps)
295
+ ], dtype=np.float32)
296
+
297
+ return normal / np.linalg.norm(normal)
298
+
299
+ def add_texture_layer(self, texture: Texture,
300
+ min_height: float = 0.0,
301
+ max_height: float = 1.0,
302
+ min_slope: float = 0.0,
303
+ max_slope: float = 90.0):
304
+ """Добавляет слой текстуры"""
305
+ self.detail_textures.append(texture)
306
+
307
+ def render(self, view: np.ndarray, projection: np.ndarray,
308
+ camera_position: np.ndarray, shader=None):
309
+ """Рендерит террайн"""
310
+ if self.wireframe:
311
+ glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
312
+
313
+ for chunk in self.chunks.values():
314
+ if chunk.mesh:
315
+ # Здесь был бы рендеринг с шейдером
316
+ chunk.mesh.draw()
317
+
318
+ if self.wireframe:
319
+ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
320
+
321
+ def cleanup(self):
322
+ """Очищает ресурсы"""
323
+ for chunk in self.chunks.values():
324
+ if chunk.mesh:
325
+ chunk.mesh.cleanup()
326
+ self.chunks.clear()
327
+ print("[Terrain] Cleanup complete")
328
+
329
+
330
+ class TerrainEditor:
331
+ """Инструменты редактирования террайна"""
332
+
333
+ def __init__(self, terrain: Terrain):
334
+ self.terrain = terrain
335
+ self.brush_size = 5.0
336
+ self.brush_strength = 1.0
337
+ self.brush_falloff = 2.0
338
+
339
+ def raise_terrain(self, world_x: float, world_z: float, amount: float):
340
+ """Поднимает террайн в точке"""
341
+ self._modify_terrain(world_x, world_z, amount)
342
+
343
+ def lower_terrain(self, world_x: float, world_z: float, amount: float):
344
+ """Опускает террайн в точке"""
345
+ self._modify_terrain(world_x, world_z, -amount)
346
+
347
+ def smooth_terrain(self, world_x: float, world_z: float, radius: float):
348
+ """Сглаживает террайн"""
349
+ cx = int(world_x)
350
+ cz = int(world_z)
351
+ r = int(radius)
352
+
353
+ heights = []
354
+
355
+ for x in range(max(0, cx - r), min(self.terrain.width, cx + r + 1)):
356
+ for z in range(max(0, cz - r), min(self.terrain.depth, cz + r + 1)):
357
+ dist = np.sqrt((x - cx)**2 + (z - cz)**2)
358
+ if dist <= r:
359
+ heights.append(self.terrain.height_map[x, z])
360
+
361
+ if heights:
362
+ avg = np.mean(heights)
363
+
364
+ for x in range(max(0, cx - r), min(self.terrain.width, cx + r + 1)):
365
+ for z in range(max(0, cz - r), min(self.terrain.depth, cz + r + 1)):
366
+ dist = np.sqrt((x - cx)**2 + (z - cz)**2)
367
+ if dist <= r:
368
+ t = dist / r
369
+ self.terrain.height_map[x, z] = (
370
+ self.terrain.height_map[x, z] * t + avg * (1 - t)
371
+ )
372
+
373
+ def flatten_terrain(self, world_x: float, world_z: float,
374
+ radius: float, target_height: float):
375
+ """Выравнивает террайн"""
376
+ cx = int(world_x)
377
+ cz = int(world_z)
378
+ r = int(radius)
379
+
380
+ for x in range(max(0, cx - r), min(self.terrain.width, cx + r + 1)):
381
+ for z in range(max(0, cz - r), min(self.terrain.depth, cz + r + 1)):
382
+ dist = np.sqrt((x - cx)**2 + (z - cz)**2)
383
+ if dist <= r:
384
+ t = dist / r
385
+ self.terrain.height_map[x, z] = (
386
+ self.terrain.height_map[x, z] * t + target_height * (1 - t)
387
+ )
388
+
389
+ def _modify_terrain(self, world_x: float, world_z: float, amount: float):
390
+ """Модифицирует террайн в точке"""
391
+ cx = int(world_x)
392
+ cz = int(world_z)
393
+ r = int(self.brush_size)
394
+
395
+ for x in range(max(0, cx - r), min(self.terrain.width, cx + r + 1)):
396
+ for z in range(max(0, cz - r), min(self.terrain.depth, cz + r + 1)):
397
+ dist = np.sqrt((x - cx)**2 + (z - cz)**2)
398
+
399
+ if dist <= r:
400
+ # Функция falloff
401
+ falloff = 1.0 - (dist / r) ** self.brush_falloff
402
+ self.terrain.height_map[x, z] += amount * falloff * self.brush_strength
403
+
404
+ # Ограничиваем высоту
405
+ self.terrain.height_map[x, z] = max(0.0, min(
406
+ self.terrain.max_height,
407
+ self.terrain.height_map[x, z]
408
+ ))
@@ -0,0 +1,34 @@
1
+ """Текстура"""
2
+ from OpenGL.GL import *
3
+ from PIL import Image
4
+ import numpy as np
5
+
6
+
7
+ class Texture:
8
+ def __init__(self, path=None):
9
+ self.id = glGenTextures(1)
10
+ self.width = 0
11
+ self.height = 0
12
+
13
+ if path:
14
+ self.load(path)
15
+
16
+ def load(self, path: str):
17
+ image = Image.open(path).convert("RGBA")
18
+ image = image.transpose(Image.FLIP_TOP_BOTTOM)
19
+ self.width, self.height = image.size
20
+ data = np.array(image, dtype=np.uint8)
21
+
22
+ glBindTexture(GL_TEXTURE_2D, self.id)
23
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, self.width, self.height,
24
+ 0, GL_RGBA, GL_UNSIGNED_BYTE, data)
25
+ glGenerateMipmap(GL_TEXTURE_2D)
26
+
27
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
28
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)
29
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR)
30
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
31
+
32
+ def bind(self, unit=0):
33
+ glActiveTexture(GL_TEXTURE0 + unit)
34
+ glBindTexture(GL_TEXTURE_2D, self.id)