Metadata-Version: 2.4
Name: jwarol
Version: 0.13.0
Summary: High-performance creative engine (Win32/OpenGL/Python)
Home-page: https://github.com/r9441887-collab/jwarol-code-new
Author: jwarol-team
Author-email: r9441887@gmail.com
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: C++
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: POSIX :: Linux
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: author
Dynamic: author-email
Dynamic: classifier
Dynamic: description
Dynamic: description-content-type
Dynamic: home-page
Dynamic: license-file
Dynamic: requires-python
Dynamic: summary

# jwarol

Движок на C++ для 2D-игр на Python. Win32 API + сырой OpenGL (и X11 на Linux). Включено: рендеринг, шейдеры, текстуры, физика, звук .wav, игровые объекты, шифрование ассетов, **скелетная анимация из Blender**, **3D → 2D проекция**, сборка в **.exe через PyInstaller**.

## Возможности

| Функция | Описание |
|---------|----------|
| **Window** | Создание окна с OpenGL-контекстом (Win32 / X11) |
| **Shader** | Компиляция GLSL-шейдеров из строк или отдельных .glsl файлов |
| **Renderer** | Рендеринг прямоугольников и текстур через GPU |
| **Texture** | Загрузка PNG/JPG/BMP, процедурные текстуры, наложение |
| **Input** | Клавиатурный ввод (GetAsyncKeyState) |
| **Camera** | Следование за игроком, скроллинг мира, smooth-камера |
| **Sprite/Animation** | Спрайт-листы, анимация по кадрам, генерация анимаций |
| **DetailGroup** | Группа деталей — батч-рендеринг тысяч объектов без просадки FPS |
| **AnimatedSprite2D** | Скелетная 2D-анимация из Blender (bones, keyframes, IK) |
| **SkeletonData** | Иерархия костей для скелетной анимации |
| **SkeletalAnimation** | Анимация с интерполяцией ключевых кадров |
| **Blender Export** | Экспорт 2D/3D моделей и анимаций из Blender в движок |
| **3D → 2D Pipeline** | 3D-модели из Blender рендерятся как 2D-спрайты |
| **Physics** | Verlet-интеграция, гравитация, коллизии, типы тел |
| **Sound** | Воспроизведение .wav (winmm) |
| **Object/Player/Character/GUI/Detail** | Система игровых объектов |
| **Texture.encrypt** | Шифрование текстур для скомпилированных игр |
| **PyInstaller Build** | Сборка игры в .exe одной командой |
| **Clock** | Замер дельта-тайма |
| **Vec2/Rect/Mat3** | 2D-математика |

## Установка

```bash
pip install jwarol
```

### Сборка из исходников

```bash
# Windows (MSVC)
python setup.py build

# Linux (GCC)
python setup.py build
```

## Быстрый старт

```python
import jwarol as jl

jl.init_graphics()
win = jl.Window(800, 600, "Hello jwarol")
renderer = jl.Renderer(800, 600)

while win.is_open:
    win.poll_events()
    win.clear(0.1, 0.1, 0.2)

    renderer.draw_rect(100, 100, 200, 150, 1.0, 0.3, 0.5)
    renderer.draw_rect(350, 200, 100, 100, 0.2, 0.7, 1.0)

    win.swap_buffers()
```

## Текстуры

```python
# Загрузка из файла (PNG/JPG/BMP на Windows, BMP на Linux)
tex = jl.Texture("sprite.png")

# Процедурная текстура из пикселей (RGBA)
import struct
w, h = 64, 64
pixels = bytearray()
for y in range(h):
    for x in range(w):
        pixels.extend([255, 0, 0, 255])  # красный пиксель
tex = jl.Texture(w, h, bytes(pixels), 4)

# Рисование с текстурой
renderer.draw_texture(tex, 100, 100, 200, 200)

# Текстура с прозрачностью
renderer.draw_texture(tex, 100, 100, 200, 200, alpha=0.5)

# Спрайт из tileset'а (draw_texture_ex)
renderer.draw_texture_ex(tileset, 0, 0, 32, 32, 0, 0, 32, 32)

# Умная загрузка всех текстур из папки
textures = jl.Texture.load_all("assets/textures/")

# Наложение текстур
combined = tex.overlay(other_tex, 10, 10, alpha=0.7)
```

## Физика

```python
world = jl.World()
world.gravity = 980.0

# Verlet-точки
p = world.add_point(400, 100)  # падает вниз

# Физические тела (прямоугольники)
body = world.add_body(300, 0, 50, 50, jl.BodyType.DYNAMIC)
static = world.add_body(0, 650, 800, 50, jl.BodyType.STATIC)

# Исключение из физики
body.enabled = False  # не участвует в симуляции

# Шаг физики
dt = clock.tick()
world.step(dt)

# Чтение позиции
x, y = body.pos.x, body.pos.y
```

## Ввод и время

```python
input = jl.Input()
clock = jl.Clock()

while win.is_open:
    dt = clock.tick()
    input.update()

    if input.is_down(ord('W')): pass      # клавиша зажата
    if input.is_pressed(ord(' ')): pass   # только что нажали
    if input.is_released(ord('A')): pass  # только что отпустили
```

## Шейдеры

```python
# Из строк
shader = jl.Shader("""
#version 330 core
layout(location = 0) in vec2 aPos;
void main() { gl_Position = vec4(aPos, 0.0, 1.0); }
""", """
#version 330 core
uniform vec4 uColor;
out vec4 FragColor;
void main() { FragColor = uColor; }
""")
shader.use()

# Из отдельных файлов (любые названия)
shader = jl.Shader.from_file("shaders/vertex.glsl", "shaders/fragment.glsl")
shader = jl.Shader.from_file("custom.vert", "custom.frag")

# Из одного файла с маркерами (#shader vertex / #shader fragment)
shader = jl.Shader.from_single_file("shader.glsl")

# Ошибки обрабатываются без краша:
# - файл не найден → RuntimeError
# - файл пустой → RuntimeError
# - ошибка компиляции → RuntimeError с логом
try:
    s = jl.Shader.from_file("bad.glsl", "empty.glsl")
except RuntimeError as e:
    print("Shader error:", e)
```

## Звук

```python
# Из папки
sound = jl.Sound("assets/sounds/effect.wav")
sound.play()
sound.set_volume(500)  # 0-1000
sound.stop()

# Быстрое воспроизведение без создания объекта
jl.Sound.play_file("assets/sounds/click.wav")
```

## Камера

```python
cam = jl.Camera(800, 600)

# Следование за игроком (с плавностью)
cam.follow(player)
cam.smooth = 0.1  # 0 = мгновенно, 1 = очень плавно

# Применить к рендереру перед отрисовкой
cam.update(dt)
cam.apply_to(renderer)

# Все draw_rect/draw_texture теперь сдвинуты камерой
# Объекты за экраном не рисуются (FPS не проседает)
renderer.draw_rect(0, 0, 2000, 2000, 0.2, 0.2, 0.3)  # большой мир
```

## Спрайты и анимация

```python
# Загрузка спрайт-листа (одна картинка с кадрами)
sheet = jl.Texture("player_walk.png")  # 4 колонки x 2 строки = 8 кадров

sprite = jl.Sprite()
sprite.set_texture(sheet, 4, 2)  # 4 колонки, 2 строки

# Анимация из последовательности кадров
anim = jl.Animation()
anim.from_range(0, 3)  # кадры 0,1,2,3
anim.speed = 0.1       # сек/кадр
anim.loop = True
sprite.set_animation(anim)

# В игровом цикле
sprite.update(dt)
sprite.draw_at(renderer, player.x, player.y, 64, 64)

# Генерация простых анимаций
idle = jl.Animation.generate("idle", 8)
walk = jl.Animation.generate("walk", 8)
run = jl.Animation.generate("run", 8)

# Рендерер сам обрезает невидимые объекты
renderer.set_camera(cam.x, cam.y)
renderer.is_visible(x, y, w, h)  # проверка видимости вручную
```

## Группа деталей (DetailGroup)

Тысячи мелких объектов без просадки FPS — все рендерятся за один draw call.

```python
details = jl.DetailGroup()

# Цветные прямоугольники
details.add(100, 200, 32, 32, 1.0, 0.5, 0.0, 0.8)

# С текстурой (атлас: одна картинка с кадрами)
tileset = jl.Texture("tileset.png")
details.set_texture(tileset, 4, 4)  # 4x4 сетка кадров

# Фрейм из атласа (col=0, row=0 — первый кадр)
details.add_frame(100, 100, 32, 32, 0, 0)
details.add_frame(140, 100, 32, 32, 1, 0)

# С произвольными UV-координатами
details.add_uv(200, 200, 64, 64, 0.0, 0.0, 0.25, 0.5)

# Дистанция — объекты дальше не рисуются
details.set_max_distance(500)

# Всё одним draw call
details.draw(cam.x, cam.y, 800, 600)

# Culling ON  → рисует только видимое (FPS стабильный)
# Culling OFF → рисует всё подряд (можно сделать просадку FPS)
details.set_culling(False)
```

## Скелетная анимация из Blender (AnimatedSprite2D)

Создайте 2D персонажа в Blender с арматурой, анимируйте — и экспортируйте в движок. Поддерживаются:
- 2D-скелеты из Blender (bones, parent hierarchy)
- Ключевые кадры с интерполяцией (position, rotation, scale)
- Множественные анимации (idle, walk, run, jump, attack...)
- Аттачмент спрайтов к костям
- Зацикленные и одноразовые анимации
- 3D-модели, спроецированные в 2D (Orthographic render)

### Экспорт из Blender

```bash
# Установка: pip install Pillow
blender --background character.blend --python tools/blender_export.py -- --output ./export
```

Результат:
- `character.json` — скелет, анимации, аттачменты
- `character.png` — спрайт-лист со всеми кадрами анимаций

### Использование в движке

```python
# Загрузка из Blender-экспорта
hero = jl.AnimatedSprite2D.from_blender(
    "export/character.json",
    "export/character.png"
)

# Проигрывание анимации
hero.play("idle")   # название анимации — имя Action в Blender

# В игровом цикле:
while win.is_open:
    dt = clock.tick()
    hero.update(dt)
    hero.draw(renderer, player.x, player.y, scale=1.0)
```

### Ручное создание скелета

```python
# Создание скелета программно
skel = jl.SkeletonData()
skel.add_bone("root")           # индекс 0
skel.add_bone("body", 0)        # индекс 1, parent = root
skel.add_bone("head", 1)        # индекс 2, parent = body
skel.add_bone("arm_l", 1)       # индекс 3, parent = body
skel.add_bone("arm_r", 1)       # индекс 4, parent = body

# Позиционирование костей
skel.bones[1].y = -30    # body ниже root
skel.bones[2].y = -30    # head выше body
skel.bones[3].x = -20    # arm_l левее body
skel.bones[4].x = 20     # arm_r правее body

# Создание анимации
walk = jl.SkeletalAnimation()
walk.name = "walk"
walk.duration = 1.0
walk.speed = 1.0
walk.loop = True

# Создание трека для bone[3] (arm_l)
track = jl.BoneTrack()
track.bone_index = 3

# Ключевые кадры вращения
kf1 = jl.BoneKeyframe()
kf1.time = 0.0; kf1.rotation = -30
kf2 = jl.BoneKeyframe()
kf2.time = 0.5; kf2.rotation = 30
kf3 = jl.BoneKeyframe()
kf3.time = 1.0; kf3.rotation = -30

track.keyframes = [kf1, kf2, kf3]
walk.tracks = [track]

# Прикрепление спрайтов к костям
att = jl.BoneAttachment()
att.bone_index = 1           # body
att.ox = 0; att.oy = 0
att.w = 64; att.h = 64
att.u0 = 0; att.v0 = 0       # UV в текстуре
att.u1 = 0.5; att.v1 = 0.5

# Создание анимированного спрайта
sprite = jl.AnimatedSprite2D()
sprite.skeleton = skel
sprite.add_animation(walk)
sprite.attachments = [att]
sprite.set_texture(jl.Texture("character.png"))
sprite.play("walk")

# В цикле:
sprite.update(dt)
sprite.draw(renderer, x, y)
```

### 3D → 2D Pipeline

3D-модели из Blender автоматически проецируются в 2D:

```python
# Та же функция from_blender(), но модель 3D
# Blender экспортирует ортографические рендеры как спрайт-лист
mech = jl.AnimatedSprite2D.from_blender("mech.json", "mech.png")
mech.play("walk")
mech.draw(renderer, 400, 300, scale=2.0)
```

## Игровые объекты

```python
# Базовый объект
obj = jl.Object(100, 100, 64, 64)
obj.active = True
obj.set_texture(texture)

# Игрок
player = jl.Player(400, 300, 32, 32)
player.speed = 250.0

# Персонаж
char = jl.Character(200, 200, 32, 48)
char.hp = 100

# GUI-элемент
gui = jl.GUI(10, 10, 200, 30)
gui.text = "Score: 0"

# Мелкая деталь
detail = jl.Detail(500, 400, 16, 16)
detail.alpha = 0.5
```

## Шифрование текстур

```python
# Зашифровать текстуру (создаёт .enc файл)
jl.Texture.encrypt("sprite.png", "my_secret_key")

# Загрузить зашифрованную текстуру
tex = jl.Texture.load_encrypted("sprite.png.enc", "my_secret_key")
```

## Сборка в .exe (PyInstaller)

Автоматическая сборка игры с C++-расширением jwarol в .exe:

```bash
# Базовая сборка (папка с файлами)
python build_exe.py game.py

# Один exe-файл
python build_exe.py game.py --onefile

# С иконкой и именем
python build_exe.py game.py --onefile --icon icon.ico --name "MyGame"

# С папкой ассетов
python build_exe.py game.py --assets assets/

# С консолью (для отладки)
python build_exe.py game.py --console

# Полная очистка
python build_exe.py game.py --clean
```

Результат в папке `dist/`.

## Структура проекта

```
jwarol/
├── include/           # C++ заголовки
│   ├── Window.hpp     # Окно (Win32 / X11)
│   ├── Shader.hpp     # GLSL-шейдеры
│   ├── Renderer.hpp   # 2D-рендерер (OpenGL)
│   ├── Texture.hpp    # Текстуры (PNG/JPG/BMP/WIC)
│   ├── Input.hpp      # Клавиатурный ввод
│   ├── Physics.hpp    # Verlet-физика
│   ├── Sound.hpp      # WAV-звук (winmm)
│   ├── Camera.hpp     # Камера с follow
│   ├── Sprite.hpp     # Спрайты и анимация
│   ├── DetailGroup.hpp# Батч-рендеринг
│   ├── Anim2D.hpp     # Скелетная 2D-анимация
│   ├── GameObject.hpp # Игровые объекты
│   ├── Math.hpp       # Vec2, Rect, Mat3
│   ├── Clock.hpp      # Дельта-таймер
│   └── GLFunctions.hpp# OpenGL-функции
├── src/               # C++ исходники
├── tools/             # Инструменты
│   └── blender_export.py  # Экспорт из Blender
├── build_exe.py       # PyInstaller сборщик
├── setup.py           # setup.py для build
└── README.md
```

## Репозиторий

https://github.com/r9441887-collab/jwarol-code-new

## Лицензия

MIT License — Руслан (r9441887@gmail.com)
