Compare commits

..
Author SHA1 Message Date
anime d76342f3fa fixed rotation moving 2026-02-12 13:55:06 +06:00
anime fb4a860773 added class Camera and fixed control of ship 2026-02-09 21:16:54 +06:00
anime 47da35a401 added new class camera 2026-02-05 15:01:16 +06:00
anime 0a72040110 merge 2026-01-22 14:58:12 +06:00
anime 1dcdf10d6a Merge origin/main into camera2 2026-01-16 18:48:33 +06:00
Vladislav Khorev ed746891b7 Fixing according to Leyla feedback 2026-01-15 10:11:46 +03:00
anime 3bd3202bf8 added joystick and attack button is moved to right 2026-01-14 22:39:38 +06:00
anime b7f5f43777 added to main new featherc 2026-01-12 20:37:21 +06:00
anime 2ad6ebf38d added all 2026-01-12 14:41:24 +06:00
anime 96deb8b393 corrected moving ship added moving camera 2025-12-29 18:59:06 +06:00
anime 47fe0c4719 added ship control 2025-12-11 21:14:39 +06:00
leyla da8946e9ef moving ship 2025-12-11 15:13:35 +06:00
326 changed files with 22291 additions and 9161821 deletions

No files matched your search

-4
View File
@@ -1,7 +1,3 @@
*.bmp filter=lfs diff=lfs merge=lfs -text
*.png filter=lfs diff=lfs merge=lfs -text
*.jpg filter=lfs diff=lfs merge=lfs -text
*.anim filter=lfs diff=lfs merge=lfs -text
*.wav filter=lfs diff=lfs merge=lfs -text
*.ogg filter=lfs diff=lfs merge=lfs -text
*.mp3 filter=lfs diff=lfs merge=lfs -text
BIN
View File
Binary file not shown.
-65
View File
@@ -1,65 +0,0 @@
import bpy
def append_layered_action_5_0(source_obj_name, target_obj_name):
source_obj = bpy.data.objects.get(source_obj_name)
target_obj = bpy.data.objects.get(target_obj_name)
if not (source_obj and target_obj):
print("Ошибка: Объекты не найдены")
return
src_action = source_obj.animation_data.action
tgt_action = target_obj.animation_data.action
# 1. Получаем слои (обычно первый)
src_layer = src_action.layers[0]
tgt_layer = tgt_action.layers[0]
# 2. Получаем стрипы
src_strip = src_layer.strips[0]
tgt_strip = tgt_layer.strips[0]
# Смещение (опираемся на конец диапазона целевого экшена)
offset = tgt_action.frame_range[1]
# 3. Итерируемся по channelbags в исходном стрипе
for src_bag in src_strip.channelbags:
# Ищем или создаем соответствующий bag в целевом стрипе
# Обычно они сопоставляются по имени или типу (например, 'Keyframe Channel Bag')
# В простейшем случае берем первый или сопоставляем по индексу
dst_bag = None
if len(tgt_strip.channelbags) > 0:
# Пытаемся найти по названию (если оно есть) или берем тот же индекс
dst_bag = tgt_strip.channelbags[0]
if not dst_bag:
# Если в целевом стрипе нет сумок, это странно, но можно создать
# (Метод создания может зависеть от конкретного подтипа стрипа в 5.0)
continue
#print(f"Обработка channelbag: {src_bag.name}, кривых: {len(src_bag.fcurves)}")
# 4. Итерируемся по fcurves внутри сумки
for src_fcurve in src_bag.fcurves:
dst_fcurve = dst_bag.fcurves.find(src_fcurve.data_path, index=src_fcurve.array_index)
if not dst_fcurve:
dst_fcurve = dst_bag.fcurves.new(data_path=src_fcurve.data_path, index=src_fcurve.array_index)
# 5. Копируем ключи с офсетом
for keyframe in src_fcurve.keyframe_points:
new_frame = keyframe.co[0] + offset
new_value = keyframe.co[1]
new_kp = dst_fcurve.keyframe_points.insert(new_frame, new_value, options={'FAST'})
new_kp.interpolation = keyframe.interpolation
# Обновляем интерполяцию
for fc in dst_bag.fcurves:
fc.update()
print(f"Анимация успешно дозаписана. Новый конец: {tgt_action.frame_range[1]}")
append_layered_action_5_0('Armature.001', 'Armature')
-192
View File
@@ -1,192 +0,0 @@
import bpy
import bmesh
# Имена mesh и арматуры
mesh_name = "arm"
armature_name = "Reference"
# Находим объект mesh по имени
mesh_obj = bpy.data.objects.get(mesh_name)
# Находим объект арматуры по имени
armature_obj = bpy.data.objects.get(armature_name)
# Устанавливаем текущий кадр на 0
bpy.context.scene.frame_set(0)
# Принудительно обновляем сцену, чтобы применить анимацию
bpy.context.view_layer.update()
# Открываем файл для записи
with open("C:\\Work\\Projects\\witcher001\\resources\\w\\zombie002.txt", "w") as file:
# Обработка арматуры и анимации
if armature_obj and armature_obj.type == 'ARMATURE':
file.write("=== Armature Matrix ===\n")
for row in armature_obj.matrix_world:
file.write(f"{row}\n")
file.write(f"=== Armature Bones: {len(armature_obj.data.bones)}\n")
for bone in armature_obj.data.bones:
# Записываем имя кости, длину и связи
file.write(f"Bone: {bone.name}\n")
file.write(f" HEAD_LOCAL: {bone.head_local}\n")
file.write(f" TAIL_LOCAL: {bone.tail_local}\n")
file.write(f" Length: {(bone.tail_local - bone.head_local).length}\n")
for row in bone.matrix:
file.write(f" {row}\n")
file.write(f" Parent: {bone.parent.name if bone.parent else 'None'}\n")
file.write(f" Children: {[child.name for child in bone.children]}\n")
# Обработка mesh
if mesh_obj and mesh_obj.type == 'MESH':
# Создаем копию mesh, чтобы не изменять оригинал
mesh_copy = mesh_obj.copy()
mesh_copy.data = mesh_obj.data.copy()
bpy.context.collection.objects.link(mesh_copy)
# Убедимся, что объект активен
bpy.context.view_layer.objects.active = mesh_copy
mesh_copy.select_set(True)
# Применяем модификатор Armature (если он есть)
for modifier in mesh_copy.modifiers:
if modifier.type == 'ARMATURE':
# Включаем модификатор, если он отключен
if not modifier.show_viewport:
modifier.show_viewport = True
if not modifier.show_render:
modifier.show_render = True
# Проверяем, что модификатор связан с арматурой
if modifier.object is None:
print(f"Модификатор Armature на объекте {mesh_copy.name} не связан с арматурой. Пропускаем.")
continue
# Временно применяем модификатор, чтобы получить правильные координаты вершин
try:
bpy.ops.object.modifier_apply(modifier=modifier.name)
except RuntimeError as e:
print(f"Ошибка при применении модификатора Armature: {e}")
continue
# Переходим в режим редактирования
bpy.ops.object.mode_set(mode='EDIT')
# Получаем BMesh представление mesh
bm = bmesh.from_edit_mesh(mesh_copy.data)
# Записываем список вершин
file.write(f"===Vertices: {len(bm.verts)}\n")
for vertex in bm.verts:
file.write(f"Vertex {vertex.index}: {vertex.co}\n")
# Убедимся, что у меша есть UV слой
uv_layer = bm.loops.layers.uv.active
if not uv_layer:
file.write("UV слой не найден.\n")
if uv_layer:
file.write(f"===UV Coordinates:\n")
file.write(f"Face count: {len(bm.faces)}\n")
for face in bm.faces:
file.write(f"Face {face.index}\n")
file.write(f"UV Count: {len(face.loops)}\n")
for loop in face.loops:
uv_coords = loop[uv_layer].uv
file.write(f" UV {uv_coords}\n")
# Записываем нормали
file.write(f"===Normals:\n")
for vertex in bm.verts:
file.write(f"Vertex {vertex.index}: Normal {vertex.normal}\n")
# Записываем треугольники (индексы вершин)
file.write(f"===Triangles: {len(bm.faces)}\n")
for face in bm.faces:
if len(face.verts) == 3: # Проверяем, что это треугольник
verts_indices = [vert.index for vert in face.verts]
file.write(f"Triangle: {verts_indices}\n")
# Возвращаемся в объектный режим
bpy.ops.object.mode_set(mode='OBJECT')
# Записываем веса вершин
file.write("=== Vertex Weights ===\n")
for vertex in mesh_copy.data.vertices:
file.write(f"Vertex {vertex.index}:\n")
file.write(f"Vertex groups: {len(vertex.groups)}\n")
for group in vertex.groups:
group_name = mesh_copy.vertex_groups[group.group].name
file.write(f" Group: '{group_name}', Weight: {group.weight}\n")
# Удаляем временную копию mesh
bpy.data.objects.remove(mesh_copy)
else:
file.write(f"Объект с именем '{mesh_name}' не найден или не является mesh.\n")
# Обработка арматуры и анимации
if armature_obj and armature_obj.type == 'ARMATURE':
# Получаем все ключевые кадры для арматуры
file.write("=== Animation Keyframes ===\n")
if armature_obj.animation_data and armature_obj.animation_data.action:
action = armature_obj.animation_data.action
# Собираем все уникальные ключевые кадры
keyframes = set()
# Логика для Blender 5.0 (Strip-based / ChannelBag structure)
if hasattr(action, "layers"):
for layer in action.layers:
if hasattr(layer, "strips"):
for strip in layer.strips:
# Проверяем наличие channelbags (согласно вашему dir(strip))
if hasattr(strip, "channelbags"):
for bag in strip.channelbags:
for fcurve in bag.fcurves:
for keyframe in fcurve.keyframe_points:
keyframes.add(int(keyframe.co[0]))
# На случай, если в этой версии используется единственное число
elif hasattr(strip, "channelbag") and strip.channelbag:
for fcurve in strip.channelbag.fcurves:
for keyframe in fcurve.keyframe_points:
keyframes.add(int(keyframe.co[0]))
# Фоллбек для Legacy экшенов
if not keyframes and hasattr(action, "fcurves"):
for fcurve in action.fcurves:
for keyframe in fcurve.keyframe_points:
keyframes.add(int(keyframe.co[0]))
keyframes = sorted(keyframes)
# Сортируем ключевые кадры
keyframes = sorted(keyframes)
# Сохраняем координаты и матрицы поворота для каждой кости на каждом ключевом кадре
file.write("=== Bone Transforms per Keyframe ===\n")
file.write(f"Keyframes: {len(keyframes)}\n")
for frame in keyframes:
# Устанавливаем текущий кадр
bpy.context.scene.frame_set(frame)
bpy.context.view_layer.update() # Обновляем сцену
file.write(f"Frame: {frame}\n")
for bone in armature_obj.pose.bones:
# Получаем координаты и матрицу поворота кости в мировом пространстве
matrix = bone.matrix
location = matrix.translation
rotation = matrix.to_euler()
# Записываем данные
file.write(f" Bone: {bone.name}\n")
file.write(f" Location: {location}\n")
file.write(f" Rotation: {rotation}\n")
file.write(f" Matrix:\n")
for row in matrix:
file.write(f" {row}\n")
else:
file.write(f"Объект с именем '{armature_name}' не найден или не является арматурой.\n")
print("Данные сохранены в файл 'mesh_armature_and_animation_data.txt'")
-228
View File
@@ -1,228 +0,0 @@
import bpy
import bmesh
#!
# Имена mesh и арматуры
mesh_name = "Joined"
armature_name = "Armature"
# Находим объект mesh по имени
mesh_obj = bpy.data.objects.get(mesh_name)
# Находим объект арматуры по имени
armature_obj = bpy.data.objects.get(armature_name)
# Устанавливаем текущий кадр на 0
bpy.context.scene.frame_set(0)
# Принудительно обновляем сцену, чтобы применить анимацию
bpy.context.view_layer.update()
# Открываем файл для записи
with open("C:\\Work\\Media\\witcher\\2026-04-13\\output\\gg_stand_idle001.txt", "w") as file:
# Обработка арматуры и анимации
if armature_obj and armature_obj.type == 'ARMATURE':
file.write("=== Armature Matrix ===\n")
for row in armature_obj.matrix_world:
file.write(f"{row}\n")
file.write(f"=== Armature Bones: {len(armature_obj.data.bones)}\n")
for bone in armature_obj.data.bones:
# Записываем имя кости, длину и связи
file.write(f"Bone: {bone.name}\n")
file.write(f" HEAD_LOCAL: {bone.head_local}\n")
file.write(f" TAIL_LOCAL: {bone.tail_local}\n")
file.write(f" Length: {(bone.tail_local - bone.head_local).length}\n")
for row in bone.matrix:
file.write(f" {row}\n")
file.write(f" Parent: {bone.parent.name if bone.parent else 'None'}\n")
file.write(f" Children: {[child.name for child in bone.children]}\n")
# Обработка mesh
if mesh_obj and mesh_obj.type == 'MESH':
# Создаем копию mesh, чтобы не изменять оригинал
mesh_copy = mesh_obj.copy()
mesh_copy.data = mesh_obj.data.copy()
bpy.context.collection.objects.link(mesh_copy)
# Убедимся, что объект активен
bpy.context.view_layer.objects.active = mesh_copy
mesh_copy.select_set(True)
# Применяем модификатор Armature (если он есть)
for modifier in mesh_copy.modifiers:
if modifier.type == 'ARMATURE':
# Включаем модификатор, если он отключен
if not modifier.show_viewport:
modifier.show_viewport = True
if not modifier.show_render:
modifier.show_render = True
# Проверяем, что модификатор связан с арматурой
if modifier.object is None:
print(f"Модификатор Armature на объекте {mesh_copy.name} не связан с арматурой. Пропускаем.")
continue
# Временно применяем модификатор, чтобы получить правильные координаты вершин
try:
bpy.ops.object.modifier_apply(modifier=modifier.name)
except RuntimeError as e:
print(f"Ошибка при применении модификатора Armature: {e}")
continue
# Переходим в режим редактирования
bpy.ops.object.mode_set(mode='EDIT')
# Получаем BMesh представление mesh
bm = bmesh.from_edit_mesh(mesh_copy.data)
# Записываем список вершин
file.write(f"===Vertices: {len(bm.verts)}\n")
for vertex in bm.verts:
file.write(f"Vertex {vertex.index}: {vertex.co}\n")
# Убедимся, что у меша есть UV слой
uv_layer = bm.loops.layers.uv.active
if not uv_layer:
file.write("UV слой не найден.\n")
if uv_layer:
file.write(f"===UV Coordinates:\n")
file.write(f"Face count: {len(bm.faces)}\n")
for face in bm.faces:
file.write(f"Face {face.index}\n")
file.write(f"UV Count: {len(face.loops)}\n")
for loop in face.loops:
uv_coords = loop[uv_layer].uv
file.write(f" UV {uv_coords}\n")
# Записываем нормали
file.write(f"===Normals:\n")
for vertex in bm.verts:
file.write(f"Vertex {vertex.index}: Normal {vertex.normal}\n")
# Записываем треугольники (индексы вершин)
file.write(f"===Triangles: {len(bm.faces)}\n")
for face in bm.faces:
if len(face.verts) == 3: # Проверяем, что это треугольник
verts_indices = [vert.index for vert in face.verts]
file.write(f"Triangle: {verts_indices}\n")
# Возвращаемся в объектный режим
bpy.ops.object.mode_set(mode='OBJECT')
# Записываем веса вершин
file.write("=== Vertex Weights (Max 5 bones per vertex) ===\n")
MAX_BONES = 5
for vertex in mesh_copy.data.vertices:
# Извлекаем все группы и веса для текущей вершины
all_weights = []
for group_element in vertex.groups:
all_weights.append({
'index': group_element.group,
'weight': group_element.weight
})
# Если костей больше лимита, фильтруем и перераспределяем
if len(all_weights) > MAX_BONES:
# Сортируем по весу (от большего к меньшему)
all_weights.sort(key=lambda x: x['weight'], reverse=True)
# Берем только топ-5
kept_weights = all_weights[:MAX_BONES]
# Считаем сумму весов оставшихся костей для нормализации
total_weight = sum(gw['weight'] for gw in kept_weights)
if total_weight > 0:
for gw in kept_weights:
gw['weight'] /= total_weight
else:
# На случай, если у всех веса были по 0.0 (редкий баг меша)
kept_weights[0]['weight'] = 1.0
final_weights = kept_weights
else:
final_weights = all_weights
file.write(f"Vertex {vertex.index}:\n")
file.write(f"Vertex groups: {len(final_weights)}\n")
for gw in final_weights:
group_name = mesh_copy.vertex_groups[gw['index']].name
file.write(f" Group: '{group_name}', Weight: {gw['weight']:.6f}\n")
# Удаляем временную копию mesh
bpy.data.objects.remove(mesh_copy)
else:
file.write(f"Объект с именем '{mesh_name}' не найден или не является mesh.\n")
# Обработка арматуры и анимации
if armature_obj and armature_obj.type == 'ARMATURE':
# Получаем все ключевые кадры для арматуры
file.write("=== Animation Keyframes ===\n")
if armature_obj.animation_data and armature_obj.animation_data.action:
action = armature_obj.animation_data.action
# Собираем все уникальные ключевые кадры
keyframes = set()
# Логика для Blender 5.0 (Strip-based / ChannelBag structure)
if hasattr(action, "layers"):
for layer in action.layers:
if hasattr(layer, "strips"):
for strip in layer.strips:
# Проверяем наличие channelbags (согласно вашему dir(strip))
if hasattr(strip, "channelbags"):
for bag in strip.channelbags:
for fcurve in bag.fcurves:
for keyframe in fcurve.keyframe_points:
keyframes.add(int(keyframe.co[0]))
# На случай, если в этой версии используется единственное число
elif hasattr(strip, "channelbag") and strip.channelbag:
for fcurve in strip.channelbag.fcurves:
for keyframe in fcurve.keyframe_points:
keyframes.add(int(keyframe.co[0]))
# Фоллбек для Legacy экшенов
if not keyframes and hasattr(action, "fcurves"):
for fcurve in action.fcurves:
for keyframe in fcurve.keyframe_points:
keyframes.add(int(keyframe.co[0]))
keyframes = sorted(keyframes)
# Сортируем ключевые кадры
keyframes = sorted(keyframes)
# Сохраняем координаты и матрицы поворота для каждой кости на каждом ключевом кадре
file.write("=== Bone Transforms per Keyframe ===\n")
file.write(f"Keyframes: {len(keyframes)}\n")
for frame in keyframes:
# Устанавливаем текущий кадр
bpy.context.scene.frame_set(frame)
bpy.context.view_layer.update() # Обновляем сцену
file.write(f"Frame: {frame}\n")
for bone in armature_obj.pose.bones:
# Получаем координаты и матрицу поворота кости в мировом пространстве
matrix = bone.matrix
location = matrix.translation
rotation = matrix.to_euler()
# Записываем данные
file.write(f" Bone: {bone.name}\n")
file.write(f" Location: {location}\n")
file.write(f" Rotation: {rotation}\n")
file.write(f" Matrix:\n")
for row in matrix:
file.write(f" {row}\n")
else:
file.write(f"Объект с именем '{armature_name}' не найден или не является арматурой.\n")
print("Данные сохранены в файл 'mesh_armature_and_animation_data.txt'")
-111
View File
@@ -1,111 +0,0 @@
import bpy
import bmesh
import mathutils
import random
import math
class SolidTreeGenerator:
def __init__(self, levels=5, length=3.0, radius=0.3):
self.levels = levels
self.base_length = length
self.base_radius = radius
# Хранилище для данных: (start_pos, end_pos, radius_start, radius_end)
self.branches_data = []
def calculate_tree(self, start_pos, direction, length, radius, level):
if level <= 0 or length < 0.1:
return
end_pos = start_pos + direction * length
# Сохраняем данные сегмента
self.branches_data.append({
'start': start_pos.copy(),
'end': end_pos.copy(),
'r_start': radius,
'r_end': radius * 0.7
})
# 1. Основной ствол (продолжение)
trunk_dir = (direction + self.get_random_vector(0.1)).normalized()
self.calculate_tree(end_pos, trunk_dir, length * 0.8, radius * 0.7, level - 1)
# 2. Боковые ветки (ветвление)
if level > 1:
num_sides = random.randint(2, 3) # Минимум 2 ветки для видимости
for _ in range(num_sides):
# Создаем вектор, сильно отклоненный от ствола (30-60 градусов)
axis = self.get_random_vector(1.0).normalized()
angle = math.radians(random.uniform(30, 60))
side_dir = direction.copy()
side_dir.rotate(mathutils.Quaternion(axis, angle))
# Боковые ветки короче
self.calculate_tree(end_pos, side_dir, length * 0.6, radius * 0.5, level - 1)
def get_random_vector(self, intensity):
return mathutils.Vector((
random.uniform(-intensity, intensity),
random.uniform(-intensity, intensity),
random.uniform(-intensity, intensity)
))
def build_mesh(self):
mesh = bpy.data.meshes.new("TreeMesh")
obj = bpy.data.objects.new("Tree", mesh)
bpy.context.collection.objects.link(obj)
bm = bmesh.new()
skin_layer = bm.verts.layers.skin.verify()
# Словарь для предотвращения дублирования вершин в одной точке
# Ключ - кортеж координат, Значение - объект вершины BMesh
vert_map = {}
for b in self.branches_data:
# Превращаем координаты в кортежи для словаря
s_key = tuple(round(v, 4) for v in b['start'])
e_key = tuple(round(v, 4) for v in b['end'])
# Получаем или создаем начальную вершину
if s_key not in vert_map:
v_start = bm.verts.new(b['start'])
v_start[skin_layer].radius = (b['r_start'], b['r_start'])
vert_map[s_key] = v_start
else:
v_start = vert_map[s_key]
# Получаем или создаем конечную вершину
if e_key not in vert_map:
v_end = bm.verts.new(b['end'])
v_end[skin_layer].radius = (b['r_end'], b['r_end'])
vert_map[e_key] = v_end
else:
v_end = vert_map[e_key]
# Создаем ребро, если его еще нет
if not bm.edges.get((v_start, v_end)):
bm.edges.new((v_start, v_end))
# Находим корень (самую нижнюю точку) и помечаем его
root_v = min(bm.verts, key=lambda v: v.co.z)
root_v[skin_layer].use_root = True
bm.to_mesh(mesh)
bm.free()
# Модификаторы
obj.modifiers.new(name="Skin", type='SKIN')
sub = obj.modifiers.new(name="Subdiv", type='SUBSURF')
sub.levels = 1 # Для начала 1, чтобы не тормозило
# Очистка сцены
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete()
# Запуск
generator = SolidTreeGenerator(levels=5, length=3.0, radius=0.4)
generator.calculate_tree(mathutils.Vector((0,0,0)), mathutils.Vector((0,0,1)), 3.0, 0.4, 5)
generator.build_mesh()
+1 -10
View File
@@ -25,7 +25,7 @@ macro(check_and_download URL ARCHIVE_NAME EXTRACTED_DIR_NAME CHECK_FILE)
endmacro()
# 1) ZLIB (Нужна только для инклудов, если не используете emscripten порты)
check_and_download("https://www.zlib.net/zlib132.zip" "zlib132.zip" "zlib-1.3.2" "CMakeLists.txt")
check_and_download("https://www.zlib.net/zlib131.zip" "zlib131.zip" "zlib-1.3.1" "CMakeLists.txt")
# 2) SDL2
check_and_download("https://github.com/libsdl-org/SDL/archive/refs/tags/release-2.32.10.zip" "release-2.32.10.zip" "SDL-release-2.32.10" "CMakeLists.txt")
@@ -47,12 +47,3 @@ check_and_download("https://download.savannah.gnu.org/releases/freetype/freetype
# 8) SDL_ttf
check_and_download("https://github.com/libsdl-org/SDL_ttf/archive/refs/tags/release-2.24.0.zip" "release-2.24.0.zip" "SDL_ttf-release-2.24.0" "CMakeLists.txt")
# 9) Lua
check_and_download("https://github.com/lua/lua/archive/refs/tags/v5.4.8.zip" "lua-v5.4.8.zip" "lua-5.4.8" "lapi.c")
# 10) sol2 (header-only C++ bindings for Lua)
check_and_download("https://github.com/ThePhD/sol2/archive/refs/tags/v3.3.0.zip" "sol2-v3.3.0.zip" "sol2-3.3.0" "include/sol/sol.hpp")
# 11) SDL2_mixer
check_and_download("https://github.com/libsdl-org/SDL_mixer/archive/refs/tags/release-2.8.0.zip" "SDL_mixer-release-2.8.0.zip" "SDL_mixer-release-2.8.0" "CMakeLists.txt")
+10 -191
View File
@@ -8,16 +8,11 @@ endmacro()
set(BUILD_CONFIGS Debug Release)
# Map MinSizeRel and RelWithDebInfo to Release libs for all imported targets.
# Without this CMake warns that IMPORTED_LOCATION is missing for those configs.
set(CMAKE_MAP_IMPORTED_CONFIG_MINSIZEREL Release)
set(CMAKE_MAP_IMPORTED_CONFIG_RELWITHDEBINFO Release)
# ===========================================
# 1) ZLIB (zlib131.zip → zlib-1.3.2) - без изменений
# 1) ZLIB (zlib131.zip → zlib-1.3.1) - без изменений
# ===========================================
set(ZLIB_SRC_DIR "${THIRDPARTY_DIR}/zlib-1.3.2")
set(ZLIB_SRC_DIR "${THIRDPARTY_DIR}/zlib-1.3.1")
set(ZLIB_BUILD_DIR "${ZLIB_SRC_DIR}/build")
set(ZLIB_INSTALL_DIR "${ZLIB_SRC_DIR}/install")
@@ -81,8 +76,8 @@ set_target_properties(zlib_external_lib PROPERTIES
#IMPORTED_LOCATION_RELEASE "${ZLIB_INSTALL_DIR}/lib/zlib.lib"
# Можно также указать статические библиотеки, если вы хотите их использовать
IMPORTED_LOCATION_DEBUG "${ZLIB_INSTALL_DIR}/lib/zsd.lib"
IMPORTED_LOCATION_RELEASE "${ZLIB_INSTALL_DIR}/lib/zs.lib"
IMPORTED_LOCATION_DEBUG "${ZLIB_INSTALL_DIR}/lib/zlibstaticd.lib"
IMPORTED_LOCATION_RELEASE "${ZLIB_INSTALL_DIR}/lib/zlibstatic.lib"
INTERFACE_INCLUDE_DIRECTORIES "${ZLIB_INSTALL_DIR}/include"
)
@@ -402,11 +397,9 @@ endif()
add_library(freetype_external_lib UNKNOWN IMPORTED GLOBAL)
set_target_properties(freetype_external_lib PROPERTIES
IMPORTED_LOCATION_DEBUG "${_ft_debug_lib}"
IMPORTED_LOCATION_RELEASE "${_ft_release_lib}"
)
target_include_directories(freetype_external_lib INTERFACE
"$<IF:$<CONFIG:Debug>,${FREETYPE_BASE_DIR}-Debug/include/freetype2,${FREETYPE_BASE_DIR}-Release/include/freetype2>"
"$<IF:$<CONFIG:Debug>,${FREETYPE_BASE_DIR}-Debug/include,${FREETYPE_BASE_DIR}-Release/include>"
IMPORTED_LOCATION_RELEASE "${_ft_release_lib}"
INTERFACE_INCLUDE_DIRECTORIES
"$<IF:$<CONFIG:Debug>,${FREETYPE_BASE_DIR}-Debug/include,${FREETYPE_BASE_DIR}-Release/include>"
)
# ===========================================
@@ -460,8 +453,8 @@ if(NOT _have_sdl2ttf)
-DSDL2_LIBRARY=${_SDL2_LIB}
-DSDL2_INCLUDE_DIR=${SDL2_INSTALL_DIR}/include/SDL2
-DFREETYPE_LIBRARY=${_FT_LIB}
-DFREETYPE_INCLUDE_DIR=${_FT_PREFIX}/include/freetype2
-DFREETYPE_INCLUDE_DIRS=${_FT_PREFIX}/include/freetype2
-DFREETYPE_INCLUDE_DIR=${_FT_PREFIX}/include
-DFREETYPE_INCLUDE_DIRS=${_FT_PREFIX}/include
-DSDL2TTF_VENDORED=OFF
-DSDL2TTF_SAMPLES=OFF
RESULT_VARIABLE _ttf_cfg_res
@@ -551,178 +544,4 @@ if(NOT TARGET boost_external_lib)
add_library(boost_external_lib INTERFACE)
# Boost заголовки находятся непосредственно в корне распакованной папки
target_include_directories(boost_external_lib INTERFACE "${BOOST_SRC_DIR}")
endif()
# ===========================================
# 9) Lua (5.5.0) - embedded scripting language
# ===========================================
set(LUA_SRC_DIR "${THIRDPARTY_DIR}/lua-5.4.8")
if(NOT TARGET lua_static)
file(GLOB LUA_SOURCES "${LUA_SRC_DIR}/*.c")
# Exclude the standalone interpreter, compiler, and unity-build wrapper.
# onelua.c #includes all other .c files — compiling it alongside them
# causes every symbol to be defined twice.
list(REMOVE_ITEM LUA_SOURCES
"${LUA_SRC_DIR}/lua.c"
"${LUA_SRC_DIR}/luac.c"
"${LUA_SRC_DIR}/onelua.c"
)
add_library(lua_static STATIC ${LUA_SOURCES})
target_include_directories(lua_static PUBLIC "${LUA_SRC_DIR}")
target_compile_definitions(lua_static PRIVATE _CRT_SECURE_NO_WARNINGS)
endif()
# ===========================================
# 10) sol2 (3.3.0) - header-only C++ bindings for Lua
# ===========================================
set(SOL2_SRC_DIR "${THIRDPARTY_DIR}/sol2-3.3.0")
# Apply patch for Clang/Emscripten compatibility in optional<T&>::emplace().
# The sentinel file prevents re-applying on subsequent cmake runs.
set(_sol2_sentinel "${SOL2_SRC_DIR}/.patched")
if(NOT EXISTS "${_sol2_sentinel}")
find_package(Git QUIET)
if(GIT_FOUND)
execute_process(
COMMAND ${GIT_EXECUTABLE} apply --ignore-whitespace
"${CMAKE_CURRENT_LIST_DIR}/patches/sol2-3.3.0-clang-optional.patch"
WORKING_DIRECTORY "${SOL2_SRC_DIR}"
RESULT_VARIABLE _sol2_patch_res
)
if(_sol2_patch_res EQUAL 0)
file(WRITE "${_sol2_sentinel}" "patched\n")
message(STATUS "Applied sol2 Clang optional patch")
else()
message(WARNING "sol2 patch failed (exit ${_sol2_patch_res}) — Clang/Emscripten builds may not compile")
endif()
else()
message(WARNING "Git not found — cannot apply sol2 patch automatically. "
"Apply cmake/patches/sol2-3.3.0-clang-optional.patch manually.")
endif()
endif()
if(NOT TARGET sol2_external_lib)
add_library(sol2_external_lib INTERFACE)
target_include_directories(sol2_external_lib INTERFACE "${SOL2_SRC_DIR}/include")
target_link_libraries(sol2_external_lib INTERFACE lua_static)
endif()
# ===========================================
# 11) SDL2_mixer (2.8.0) – сборка из исходников
# ===========================================
set(SDL2MIXER_SRC_DIR "${THIRDPARTY_DIR}/SDL_mixer-release-2.8.0")
set(SDL2MIXER_BASE_DIR "${SDL2MIXER_SRC_DIR}/install")
set(SDL2MIXER_BASE_DIR "${SDL2MIXER_BASE_DIR}" CACHE PATH "SDL2_mixer install base directory" FORCE)
set(_have_sdl2mixer TRUE)
foreach(cfg IN LISTS BUILD_CONFIGS)
if(NOT EXISTS "${SDL2MIXER_BASE_DIR}-${cfg}/lib/SDL2_mixer.lib" AND
NOT EXISTS "${SDL2MIXER_BASE_DIR}-${cfg}/lib/SDL2_mixerd.lib")
set(_have_sdl2mixer FALSE)
endif()
endforeach()
if(NOT _have_sdl2mixer)
foreach(cfg IN LISTS BUILD_CONFIGS)
if(cfg STREQUAL "Debug")
set(_SDL2_LIB "${SDL2_INSTALL_DIR}/lib/SDL2d.lib")
else()
set(_SDL2_LIB "${SDL2_INSTALL_DIR}/lib/SDL2.lib")
endif()
log("Configuring SDL2_mixer (${cfg}) ...")
execute_process(
COMMAND ${CMAKE_COMMAND}
-G "${CMAKE_GENERATOR}"
-S "${SDL2MIXER_SRC_DIR}"
-B "${SDL2MIXER_SRC_DIR}/build-${cfg}"
-DCMAKE_INSTALL_PREFIX=${SDL2MIXER_BASE_DIR}-${cfg}
-DCMAKE_PREFIX_PATH=${SDL2_INSTALL_DIR}
-DSDL2_LIBRARY=${_SDL2_LIB}
-DSDL2_INCLUDE_DIR=${SDL2_INSTALL_DIR}/include/SDL2
-DSDL2MIXER_DEPS_SHARED=OFF
-DSDL2MIXER_VENDORED=ON
-DSDL2MIXER_SAMPLES=OFF
-DSDL2MIXER_MUSIC_CMD=OFF
-DSDL2MIXER_MOD=OFF
-DSDL2MIXER_MIDI=OFF
-DSDL2MIXER_OPUS=OFF
-DSDL2MIXER_WAVPACK=OFF
-DSDL2MIXER_MP3_MPG123=OFF
-DSDL2MIXER_MP3_DRMP3=ON
-DSDL2MIXER_FLAC_DRFLAC=ON
-DSDL2MIXER_OGG_STB=ON
-DCMAKE_DISABLE_FIND_PACKAGE_OGG=TRUE
-DCMAKE_DISABLE_FIND_PACKAGE_Vorbis=TRUE
-DCMAKE_DISABLE_FIND_PACKAGE_FLAC=TRUE
-DCMAKE_DISABLE_FIND_PACKAGE_MPG123=TRUE
-DCMAKE_DISABLE_FIND_PACKAGE_LibModPlug=TRUE
-DCMAKE_DISABLE_FIND_PACKAGE_FluidLite=TRUE
RESULT_VARIABLE _mixer_cfg_res
OUTPUT_VARIABLE _mixer_cfg_out
ERROR_VARIABLE _mixer_cfg_err
)
if(NOT _mixer_cfg_res EQUAL 0)
message(STATUS "SDL2_mixer configure stdout: ${_mixer_cfg_out}")
message(STATUS "SDL2_mixer configure stderr: ${_mixer_cfg_err}")
message(FATAL_ERROR "SDL2_mixer configure failed for ${cfg}")
endif()
log("Building SDL2_mixer (${cfg}) ...")
execute_process(
COMMAND ${CMAKE_COMMAND}
--build "${SDL2MIXER_SRC_DIR}/build-${cfg}" --config ${cfg}
RESULT_VARIABLE _mixer_build_res
)
if(NOT _mixer_build_res EQUAL 0)
message(FATAL_ERROR "SDL2_mixer build failed for ${cfg}")
endif()
log("Installing SDL2_mixer (${cfg}) ...")
execute_process(
COMMAND ${CMAKE_COMMAND}
--install "${SDL2MIXER_SRC_DIR}/build-${cfg}" --config ${cfg}
RESULT_VARIABLE _mixer_inst_res
)
if(NOT _mixer_inst_res EQUAL 0)
message(FATAL_ERROR "SDL2_mixer install failed for ${cfg}")
endif()
endforeach()
endif()
set(_mixer_debug_lib "")
foreach(cand
"${SDL2MIXER_BASE_DIR}-Debug/lib/SDL2_mixerd.lib"
"${SDL2MIXER_BASE_DIR}-Debug/lib/SDL2_mixer.lib"
)
if(EXISTS "${cand}")
set(_mixer_debug_lib "${cand}")
break()
endif()
endforeach()
set(_mixer_release_lib "")
foreach(cand
"${SDL2MIXER_BASE_DIR}-Release/lib/SDL2_mixer.lib"
)
if(EXISTS "${cand}")
set(_mixer_release_lib "${cand}")
break()
endif()
endforeach()
if(_mixer_debug_lib STREQUAL "" OR _mixer_release_lib STREQUAL "")
message(FATAL_ERROR "SDL2_mixer libs not found in ${SDL2MIXER_BASE_DIR}-Debug/Release")
endif()
add_library(SDL2_mixer_external_lib UNKNOWN IMPORTED GLOBAL)
set_target_properties(SDL2_mixer_external_lib PROPERTIES
IMPORTED_LOCATION_DEBUG "${_mixer_debug_lib}"
IMPORTED_LOCATION_RELEASE "${_mixer_release_lib}"
INTERFACE_INCLUDE_DIRECTORIES
"$<IF:$<CONFIG:Debug>,${SDL2MIXER_BASE_DIR}-Debug/include,${SDL2MIXER_BASE_DIR}-Release/include>"
INTERFACE_LINK_LIBRARIES
"SDL2_external_lib"
)
endif()
@@ -1,14 +0,0 @@
--- a/include/sol/optional_implementation.hpp
+++ b/include/sol/optional_implementation.hpp
@@ -2189,7 +2189,10 @@
template <class... Args>
T& emplace(Args&&... args) noexcept {
static_assert(std::is_constructible<T, Args&&...>::value, "T must be constructible with Args");
*this = nullopt;
- this->construct(std::forward<Args>(args)...);
+ // Reference specialization stores a pointer; set it directly.
+ // construct() only exists in the non-reference specialization.
+ m_value = std::addressof(std::forward<Args>(args)...);
+ return *m_value;
}
+156
View File
@@ -0,0 +1,156 @@
{
"root": {
"type": "FrameLayout",
"x": 0,
"y": 0,
"width": 1280,
"height": 720,
"children": [
{
"type": "FrameLayout",
"name": "leftPanel",
"x": 100,
"y": 100,
"width": 320,
"height": 400,
"children": [
{
"type": "LinearLayout",
"name": "mainButtons",
"orientation": "vertical",
"spacing": 10,
"x": 0,
"y": 0,
"width": 300,
"height": 300,
"children": [
{
"type": "Button",
"name": "playButton",
"x": 100,
"y": 300,
"width": 200,
"height": 50,
"animations": {
"buttonsExit": {
"repeat": false,
"steps": [
{
"type": "move",
"to": [
-400,
0
],
"duration": 1.0,
"easing": "easein"
}
]
}
},
"textures": {
"normal": "./resources/button.png",
"hover": "./resources/sand.png",
"pressed": "./resources/button.png"
}
},
{
"type": "Button",
"name": "settingsButton",
"x": 100,
"y": 200,
"width": 200,
"height": 50,
"animations": {
"buttonsExit": {
"repeat": false,
"steps": [
{
"type": "wait",
"duration": 0.5
},
{
"type": "move",
"to": [
-400,
0
],
"duration": 1.0,
"easing": "easein"
}
]
}
},
"textures": {
"normal": "./resources/sand.png",
"hover": "./resources/button.png",
"pressed": "./resources/sand.png"
}
},
{
"type": "Button",
"name": "exitButton",
"x": 100,
"y": 100,
"width": 200,
"height": 50,
"animations": {
"buttonsExit": {
"repeat": false,
"steps": [
{
"type": "wait",
"duration": 1.0
},
{
"type": "move",
"to": [
-400,
0
],
"duration": 1.0,
"easing": "easein"
}
]
},
"bgScroll": {
"repeat": true,
"steps": [
{
"type": "move",
"to": [
1280,
0
],
"duration": 5.0,
"easing": "linear"
}
]
}
},
"textures": {
"normal": "./resources/rock.png",
"hover": "./resources/button.png",
"pressed": "./resources/rock.png"
}
}
]
}
]
},
{
"type": "Slider",
"name": "musicVolumeSlider",
"x": 1140,
"y": 100,
"width": 10,
"height": 500,
"value": 0.5,
"orientation": "vertical",
"textures": {
"track": "./resources/musicVolumeBarTexture.png",
"knob": "./resources/musicVolumeBarButton.png"
}
}
]
}
}
-323
View File
@@ -1,323 +0,0 @@
#!/usr/bin/env python3
"""
Convert a text-based bone animation file to the BSAF binary format.
Usage:
python convert_anim_to_binary.py <input.txt> <output.bin>
Binary format (BSAF v2) -- all values little-endian:
HEADER
4 bytes magic "BSAF"
uint32 version (2)
BONES
uint32 numBones
per bone:
3 x float boneStartWorld (from HEAD_LOCAL)
float boneLength
9 x float 3x3 rotation matrix (row-major)
int32 parentIndex (-1 if none)
uint32 numChildren
numChildren x int32 childIndices
BONE NAMES (v2+)
per bone:
uint32 nameLen
nameLen bytes UTF-8 name (no terminator)
VERTICES
uint32 numVertices
numVertices x 3 x float positions
UV COORDINATES
uint32 numFaces
numFaces x 6 x float 3 UV pairs per face (u0,v0,u1,v1,u2,v2)
NORMALS
numVertices x 3 x float normals
TRIANGLES
uint32 numTriangles
numTriangles x 3 x int32 vertex indices
VERTEX WEIGHTS
per vertex (numVertices):
uint32 numGroups
numGroups x (int32 boneIndex, float weight)
ANIMATION KEYFRAMES
uint32 numKeyframes
per keyframe:
int32 frameNumber
per bone (numBones, in index order 0..N-1):
3 x float location
16 x float 4x4 matrix (row-major)
"""
import struct
import re
import sys
def parse_floats(line):
return [float(x) for x in re.findall(r'[-]?\d+\.\d+', line)]
def parse_first_int(line):
m = re.search(r'\d+', line)
if m:
return int(m.group())
raise ValueError(f"No integer found in: {line}")
def parse_children(line):
return re.findall(r"'([^']+)'", line)
def convert(input_path, output_path):
with open(input_path, 'r', encoding='utf-8', errors='replace') as f:
lines = f.readlines()
idx = 0
def next_line():
nonlocal idx
line = lines[idx].rstrip()
idx += 1
return line
# --- Skip armature matrix (5 lines) ---
for _ in range(5):
next_line()
# --- Bone count ---
line = next_line() # "=== Armature Bones: 65"
num_bones = parse_first_int(line)
bone_names = []
bones = []
bone_parent_names = []
bone_children_names = []
for _ in range(num_bones):
bone = {}
# "Bone: mixamorig:Hips"
line = next_line()
bone_name = line[6:]
bone_names.append(bone_name)
# " HEAD_LOCAL: <Vector (x, y, z)>"
line = next_line()
bone['head'] = parse_floats(line)[:3]
# " TAIL_LOCAL: ..." -- skip
next_line()
# " Length: 0.123"
line = next_line()
bone['length'] = parse_floats(line)[0]
# 3x3 matrix (3 rows)
mat = []
for _ in range(3):
mat.extend(parse_floats(next_line()))
bone['matrix_3x3'] = mat
# " Parent: None" or " Parent: boneName"
line = next_line()
if line == " Parent: None":
bone_parent_names.append(None)
else:
bone_parent_names.append(line[10:])
# " Children: ['a', 'b'] or []"
line = next_line()
bone_children_names.append(parse_children(line))
bones.append(bone)
# Build name -> index map
name_to_idx = {name: i for i, name in enumerate(bone_names)}
# Resolve parent / child indices
for i in range(num_bones):
if bone_parent_names[i] is None:
bones[i]['parent'] = -1
else:
bones[i]['parent'] = name_to_idx[bone_parent_names[i]]
bones[i]['children'] = [name_to_idx[c] for c in bone_children_names[i]]
# --- Vertices ---
line = next_line() # "===Vertices: 5140"
num_vertices = parse_first_int(line)
vertices = []
for _ in range(num_vertices):
vertices.append(parse_floats(next_line())[:3])
# --- UV Coordinates ---
next_line() # "===UV Coordinates:"
line = next_line() # "Face count: 8602"
num_faces = parse_first_int(line)
uvs = []
for _ in range(num_faces):
next_line() # "Face N"
next_line() # "UV Count: 3"
face_uvs = []
for _ in range(3):
face_uvs.extend(parse_floats(next_line())[:2])
uvs.append(face_uvs) # 6 floats
# --- Normals ---
next_line() # "===Normals:"
normals = []
for _ in range(num_vertices):
normals.append(parse_floats(next_line())[:3])
# --- Triangles ---
line = next_line() # "===Triangles: 8602"
num_triangles = parse_first_int(line)
triangles = []
for _ in range(num_triangles):
line = next_line()
ints = [int(x) for x in re.findall(r'[-]?\d+', line)]
triangles.append(ints[:3])
# --- Vertex Weights ---
next_line() # "=== Vertex Weights ..."
vertex_weights = []
for _ in range(num_vertices):
next_line() # "Vertex N:"
line = next_line() # "Vertex groups: 2"
num_groups = parse_first_int(line)
groups = []
for _ in range(num_groups):
line = next_line()
m = re.search(r"'([^']+)'.*?([-]?\d+\.\d+)", line)
bone_name = m.group(1)
weight = float(m.group(2))
groups.append((name_to_idx[bone_name], weight))
vertex_weights.append(groups)
# --- Animation Keyframes ---
next_line() # "=== Animation Keyframes ==="
next_line() # "=== Bone Transforms per Keyframe ==="
line = next_line() # "Keyframes: 32"
num_keyframes = parse_first_int(line)
keyframes = []
for _ in range(num_keyframes):
line = next_line() # "Frame: 0"
frame_number = parse_first_int(line)
bone_data = {}
for _ in range(num_bones):
line = next_line() # " Bone: mixamorig:Hips"
bone_name = line.strip()
if bone_name.startswith("Bone: "):
bone_name = bone_name[6:]
bone_idx = name_to_idx[bone_name]
# Location
location = parse_floats(next_line())[:3]
# Rotation (skip)
next_line()
# " Matrix:" (skip header)
next_line()
# 4 rows of 4 floats
matrix = []
for _ in range(4):
matrix.extend(parse_floats(next_line()))
bone_data[bone_idx] = {
'location': location,
'matrix': matrix,
}
keyframes.append((frame_number, bone_data))
# ================================================================
# Write binary file
# ================================================================
with open(output_path, 'wb') as out:
# Header
out.write(b'BSAF')
out.write(struct.pack('<I', 2))
# Bones
out.write(struct.pack('<I', num_bones))
for i in range(num_bones):
b = bones[i]
out.write(struct.pack('<3f', *b['head']))
out.write(struct.pack('<f', b['length']))
out.write(struct.pack('<9f', *b['matrix_3x3']))
out.write(struct.pack('<i', b['parent']))
out.write(struct.pack('<I', len(b['children'])))
for c in b['children']:
out.write(struct.pack('<i', c))
# Bone names (v2+)
for name in bone_names:
name_bytes = name.encode('utf-8')
out.write(struct.pack('<I', len(name_bytes)))
out.write(name_bytes)
# Vertices
out.write(struct.pack('<I', num_vertices))
for v in vertices:
out.write(struct.pack('<3f', *v))
# UV Coordinates
out.write(struct.pack('<I', num_faces))
for uv in uvs:
out.write(struct.pack('<6f', *uv))
# Normals
for n in normals:
out.write(struct.pack('<3f', *n))
# Triangles
out.write(struct.pack('<I', num_triangles))
for t in triangles:
out.write(struct.pack('<3i', *t))
# Vertex Weights
for vw in vertex_weights:
out.write(struct.pack('<I', len(vw)))
for bone_idx, weight in vw:
out.write(struct.pack('<if', bone_idx, weight))
# Animation Keyframes
out.write(struct.pack('<I', num_keyframes))
for frame_num, bone_data in keyframes:
out.write(struct.pack('<i', frame_num))
for i in range(num_bones):
bd = bone_data[i]
out.write(struct.pack('<3f', *bd['location']))
out.write(struct.pack('<16f', *bd['matrix']))
input_size = sum(len(l) for l in lines)
import os
output_size = os.path.getsize(output_path)
print(f"Converted: {input_path} ({input_size:,} bytes text) -> {output_path} ({output_size:,} bytes binary)")
print(f" Bones: {num_bones}, Vertices: {num_vertices}, Faces: {num_faces}, "
f"Triangles: {num_triangles}, Keyframes: {num_keyframes}")
if __name__ == '__main__':
if len(sys.argv) != 3:
print(f"Usage: {sys.argv[0]} <input.txt> <output.bin>")
sys.exit(1)
convert(sys.argv[1], sys.argv[2])
-370
View File
@@ -1,370 +0,0 @@
#!/usr/bin/env python3
"""
Convert a text-based multi-mesh bone animation file to the BSMF binary format.
Usage:
python convert_anim_to_binary_new.py <input.txt> <output.bin>
Binary format (BSMF v2) -- all values little-endian:
HEADER
4 bytes magic "BSMF"
uint32 version (2)
ARMATURE MATRIX
16 x float 4x4 matrix (row-major)
BONES
uint32 numBones
per bone:
3 x float boneStartWorld (from HEAD_LOCAL)
float boneLength
9 x float 3x3 rotation matrix (row-major)
int32 parentIndex (-1 if none)
uint32 numChildren
numChildren x int32 childIndices
BONE NAMES
per bone:
uint32 nameLen
nameLen bytes UTF-8 name (no terminator)
MESHES
uint32 numMeshes
per mesh:
uint32 nameLength
nameLength x char meshName (UTF-8, no null terminator)
VERTICES
uint32 numVertices
numVertices x 3 x float positions
UV COORDINATES
uint32 numFaces
numFaces x 6 x float 3 UV pairs per face (u0,v0,u1,v1,u2,v2)
NORMALS
numVertices x 3 x float normals
TRIANGLES
uint32 numTriangles
numTriangles x 3 x int32 vertex indices
VERTEX WEIGHTS
per vertex (numVertices):
uint32 numGroups
numGroups x (int32 boneIndex, float weight)
ANIMATION KEYFRAMES
uint32 numKeyframes
per keyframe:
int32 frameNumber
per bone (numBones, in index order 0..N-1):
3 x float location
16 x float 4x4 matrix (row-major)
"""
import struct
import re
import sys
def parse_floats(line):
return [float(x) for x in re.findall(r'[-]?\d+\.\d+', line)]
def parse_first_int(line):
m = re.search(r'\d+', line)
if m:
return int(m.group())
raise ValueError(f"No integer found in: {line}")
def parse_children(line):
return re.findall(r"'([^']+)'", line)
def convert(input_path, output_path):
with open(input_path, 'r', encoding='utf-8', errors='replace') as f:
lines = f.readlines()
idx = 0
def next_line():
nonlocal idx
line = lines[idx].rstrip()
idx += 1
return line
# --- Armature matrix header + 4 rows ---
next_line() # "=== Armature Matrix ==="
armature_matrix = []
for _ in range(4):
armature_matrix.extend(parse_floats(next_line())[:4])
# --- Bone count ---
line = next_line() # "=== Armature Bones: 65"
num_bones = parse_first_int(line)
bone_names = []
bones = []
bone_parent_names = []
bone_children_names = []
for _ in range(num_bones):
bone = {}
# "Bone: mixamorig:Hips"
line = next_line()
bone_name = line[6:]
bone_names.append(bone_name)
# " HEAD_LOCAL: <Vector (x, y, z)>"
line = next_line()
bone['head'] = parse_floats(line)[:3]
# " TAIL_LOCAL: ..." -- skip
next_line()
# " Length: 0.123"
line = next_line()
bone['length'] = parse_floats(line)[0]
# 3x3 matrix (3 rows)
mat = []
for _ in range(3):
mat.extend(parse_floats(next_line()))
bone['matrix_3x3'] = mat
# " Parent: None" or " Parent: boneName"
line = next_line()
if line == " Parent: None":
bone_parent_names.append(None)
else:
bone_parent_names.append(line[10:])
# " Children: ['a', 'b'] or []"
line = next_line()
bone_children_names.append(parse_children(line))
bones.append(bone)
# Build name -> index map
name_to_idx = {name: i for i, name in enumerate(bone_names)}
# Resolve parent / child indices
for i in range(num_bones):
if bone_parent_names[i] is None:
bones[i]['parent'] = -1
else:
bones[i]['parent'] = name_to_idx[bone_parent_names[i]]
bones[i]['children'] = [name_to_idx[c] for c in bone_children_names[i]]
# --- Multi-mesh header ---
line = next_line() # "=== TOTAL MESHES TO EXPORT: 7 ==="
num_meshes = parse_first_int(line)
meshes = []
for _ in range(num_meshes):
# "=== Mesh Object: Name ==="
line = next_line()
m = re.match(r"===\s*Mesh Object:\s*(.+?)\s*===$", line)
if not m:
raise ValueError(f"Invalid mesh header: {line}")
mesh_name = m.group(1)
# --- Vertices ---
line = next_line() # "===Vertices: N"
num_vertices = parse_first_int(line)
vertices = []
for _ in range(num_vertices):
vertices.append(parse_floats(next_line())[:3])
# --- UV Coordinates ---
next_line() # "===UV Coordinates:"
line = next_line() # "Face count: M"
num_faces = parse_first_int(line)
uvs = []
for _ in range(num_faces):
next_line() # "Face N"
next_line() # "UV Count: 3"
face_uvs = []
for _ in range(3):
face_uvs.extend(parse_floats(next_line())[:2])
uvs.append(face_uvs)
# --- Normals ---
next_line() # "===Normals:"
normals = []
for _ in range(num_vertices):
normals.append(parse_floats(next_line())[:3])
# --- Triangles ---
line = next_line() # "===Triangles: M"
num_triangles = parse_first_int(line)
triangles = []
for _ in range(num_triangles):
line = next_line()
ints = [int(x) for x in re.findall(r'[-]?\d+', line)]
triangles.append(ints[:3])
# --- Vertex Weights ---
next_line() # "=== Vertex Weights (Max 5 bones per vertex) ==="
vertex_weights = []
for _ in range(num_vertices):
next_line() # "Vertex N:"
line = next_line() # "Vertex groups: K"
num_groups = parse_first_int(line)
groups = []
for _ in range(num_groups):
line = next_line()
m = re.search(r"'([^']+)'.*?([-]?\d+\.\d+)", line)
bone_name = m.group(1)
weight = float(m.group(2))
groups.append((name_to_idx[bone_name], weight))
vertex_weights.append(groups)
meshes.append({
'name': mesh_name,
'num_vertices': num_vertices,
'vertices': vertices,
'num_faces': num_faces,
'uvs': uvs,
'normals': normals,
'num_triangles': num_triangles,
'triangles': triangles,
'vertex_weights': vertex_weights,
})
# --- Animation Keyframes ---
next_line() # "=== Animation Keyframes ==="
next_line() # "=== Bone Transforms per Keyframe ==="
line = next_line() # "Keyframes: N"
num_keyframes = parse_first_int(line)
keyframes = []
for _ in range(num_keyframes):
line = next_line() # "Frame: N"
frame_number = parse_first_int(line)
bone_data = {}
for _ in range(num_bones):
line = next_line() # " Bone: mixamorig:Hips"
bone_name = line.strip()
if bone_name.startswith("Bone: "):
bone_name = bone_name[6:]
bone_idx = name_to_idx[bone_name]
# Location
location = parse_floats(next_line())[:3]
# Rotation (skip)
next_line()
# " Matrix:" (skip header)
next_line()
# 4 rows of 4 floats
matrix = []
for _ in range(4):
matrix.extend(parse_floats(next_line()))
bone_data[bone_idx] = {
'location': location,
'matrix': matrix,
}
keyframes.append((frame_number, bone_data))
# ================================================================
# Write binary file
# ================================================================
with open(output_path, 'wb') as out:
# Header
out.write(b'BSMF')
out.write(struct.pack('<I', 2))
# Armature matrix (16 floats, row-major)
out.write(struct.pack('<16f', *armature_matrix))
# Bones
out.write(struct.pack('<I', num_bones))
for i in range(num_bones):
b = bones[i]
out.write(struct.pack('<3f', *b['head']))
out.write(struct.pack('<f', b['length']))
out.write(struct.pack('<9f', *b['matrix_3x3']))
out.write(struct.pack('<i', b['parent']))
out.write(struct.pack('<I', len(b['children'])))
for c in b['children']:
out.write(struct.pack('<i', c))
# Bone names
for name in bone_names:
name_bytes = name.encode('utf-8')
out.write(struct.pack('<I', len(name_bytes)))
out.write(name_bytes)
# Meshes
out.write(struct.pack('<I', num_meshes))
for md in meshes:
name_bytes = md['name'].encode('utf-8')
out.write(struct.pack('<I', len(name_bytes)))
out.write(name_bytes)
# Vertices
out.write(struct.pack('<I', md['num_vertices']))
for v in md['vertices']:
out.write(struct.pack('<3f', *v))
# UV Coordinates
out.write(struct.pack('<I', md['num_faces']))
for uv in md['uvs']:
out.write(struct.pack('<6f', *uv))
# Normals
for n in md['normals']:
out.write(struct.pack('<3f', *n))
# Triangles
out.write(struct.pack('<I', md['num_triangles']))
for t in md['triangles']:
out.write(struct.pack('<3i', *t))
# Vertex weights
for vw in md['vertex_weights']:
out.write(struct.pack('<I', len(vw)))
for bone_idx, weight in vw:
out.write(struct.pack('<if', bone_idx, weight))
# Animation Keyframes
out.write(struct.pack('<I', num_keyframes))
for frame_num, bone_data in keyframes:
out.write(struct.pack('<i', frame_num))
for i in range(num_bones):
bd = bone_data[i]
out.write(struct.pack('<3f', *bd['location']))
out.write(struct.pack('<16f', *bd['matrix']))
input_size = sum(len(l) for l in lines)
import os
output_size = os.path.getsize(output_path)
print(f"Converted: {input_path} ({input_size:,} bytes text) -> {output_path} ({output_size:,} bytes binary)")
print(f" Bones: {num_bones}, Meshes: {num_meshes}, Keyframes: {num_keyframes}")
for md in meshes:
print(f" - {md['name']}: {md['num_vertices']} verts, "
f"{md['num_faces']} faces, {md['num_triangles']} tris")
if __name__ == '__main__':
if len(sys.argv) != 3:
print(f"Usage: {sys.argv[0]} <input.txt> <output.bin>")
sys.exit(1)
convert(sys.argv[1], sys.argv[2])
-54
View File
@@ -1,54 +0,0 @@
#!/usr/bin/env python3
"""
Convert an old single-mesh text animation file to the new multi-mesh text format.
The only structural difference is that the new format wraps the mesh block
between these two extra headers before the "===Vertices:" line:
=== TOTAL MESHES TO EXPORT: 1 ===
=== Mesh Object: Body ===
Usage:
python convert_old_anim_to_new.py <input.txt> <output.txt> [mesh_name]
If mesh_name is omitted, "Body" is used.
"""
import sys
def convert(input_path, output_path, mesh_name="Body"):
with open(input_path, 'r', encoding='utf-8', errors='replace') as f:
lines = f.readlines()
# Find the first "===Vertices:" line -- that's where the bone block ends
# and the mesh block begins in the old format.
insert_at = None
for i, line in enumerate(lines):
if line.lstrip().startswith("===Vertices:"):
insert_at = i
break
if insert_at is None:
raise RuntimeError("Could not find '===Vertices:' line in input file")
header_lines = [
"=== TOTAL MESHES TO EXPORT: 1 ===\n",
f"=== Mesh Object: {mesh_name} ===\n",
]
out_lines = lines[:insert_at] + header_lines + lines[insert_at:]
with open(output_path, 'w', encoding='utf-8') as out:
out.writelines(out_lines)
print(f"Converted: {input_path} -> {output_path} (mesh name: {mesh_name})")
if __name__ == '__main__':
if len(sys.argv) not in (3, 4):
print(f"Usage: {sys.argv[0]} <input.txt> <output.txt> [mesh_name]")
sys.exit(1)
mesh_name = sys.argv[3] if len(sys.argv) == 4 else "Body"
convert(sys.argv[1], sys.argv[2], mesh_name)
-1
View File
@@ -47,7 +47,6 @@ add_library(main SHARED
../../../../src/utils/Perlin.cpp
../../../../src/utils/TaskManager.cpp
../../../../src/utils/Utils.cpp
../../../../src/navigation/PathFinder.cpp
../../../../src/planet/PlanetData.cpp
../../../../src/planet/PlanetObject.cpp
../../../../src/planet/StoneObject.cpp
+41 -134
View File
@@ -8,7 +8,7 @@ if(NOT CMAKE_MAKE_PROGRAM AND WIN32)
endif()
endif()
project(bishkek-witcher LANGUAGES C CXX)
project(space-game001 CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
@@ -17,127 +17,40 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
include("${CMAKE_CURRENT_SOURCE_DIR}/../cmake/FetchDependencies.cmake")
# Теперь гарантированно есть папка ../thirdparty со всеми исходниками
# ===========================================
# Lua (5.5.0) - embedded scripting, pure C, compiles with Emscripten
# ===========================================
set(LUA_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../thirdparty/lua-5.4.8")
file(GLOB LUA_SOURCES "${LUA_SRC_DIR}/*.c")
list(REMOVE_ITEM LUA_SOURCES
"${LUA_SRC_DIR}/lua.c"
"${LUA_SRC_DIR}/luac.c"
"${LUA_SRC_DIR}/onelua.c"
)
add_library(lua_static STATIC ${LUA_SOURCES})
target_include_directories(lua_static PUBLIC "${LUA_SRC_DIR}")
# sol2 (header-only)
set(SOL2_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../thirdparty/sol2-3.3.0")
# Apply patch for Clang/Emscripten compatibility in optional<T&>::emplace().
set(_sol2_sentinel "${SOL2_SRC_DIR}/.patched")
if(NOT EXISTS "${_sol2_sentinel}")
find_package(Git QUIET)
if(GIT_FOUND)
execute_process(
COMMAND ${GIT_EXECUTABLE} apply --ignore-whitespace
"${CMAKE_CURRENT_SOURCE_DIR}/../cmake/patches/sol2-3.3.0-clang-optional.patch"
WORKING_DIRECTORY "${SOL2_SRC_DIR}"
RESULT_VARIABLE _sol2_patch_res
)
if(_sol2_patch_res EQUAL 0)
file(WRITE "${_sol2_sentinel}" "patched\n")
message(STATUS "Applied sol2 Clang optional patch")
else()
message(WARNING "sol2 patch failed (exit ${_sol2_patch_res}) — Clang/Emscripten builds may not compile")
endif()
else()
message(WARNING "Git not found — cannot apply sol2 patch automatically. "
"Apply cmake/patches/sol2-3.3.0-clang-optional.patch manually.")
endif()
endif()
# Список исходных файлов (синхронизирован с proj-windows/CMakeLists.txt)
# Список исходных файлов (без изменений)
set(SOURCES
../src/main.cpp
../src/Game.cpp
../src/Game.h
../src/Character.cpp
../src/Character.h
../src/Environment.cpp
../src/Environment.h
../src/render/Renderer.cpp
../src/render/Renderer.h
../src/render/ShaderManager.cpp
../src/render/ShaderManager.h
../src/render/TextureManager.cpp
../src/render/TextureManager.h
../src/TextModel.cpp
../src/TextModel.h
../src/AudioPlayerAsync.cpp
../src/AudioPlayerAsync.h
../src/BoneAnimatedModel.cpp
../src/BoneAnimatedModel.h
../src/BoneAnimatedModelNew.cpp
../src/BoneAnimatedModelNew.h
../src/render/OpenGlExtensions.cpp
../src/render/OpenGlExtensions.h
../src/utils/Utils.cpp
../src/utils/Utils.h
../src/TextModel.cpp
../src/Projectile.cpp
../src/SparkEmitter.cpp
../src/SparkEmitter.h
../src/utils/Perlin.cpp
../src/utils/Perlin.h
../src/utils/TaskManager.cpp
../src/utils/TaskManager.h
../src/render/FrameBuffer.cpp
../src/render/FrameBuffer.h
../src/render/ShadowMap.cpp
../src/render/ShadowMap.h
../src/UiManager.cpp
../src/UiManager.h
../src/render/TextRenderer.h
../src/render/TextRenderer.cpp
../src/MenuManager.h
../src/MenuManager.cpp
../src/Location.h
../src/Location.cpp
../src/GameConstants.h
../src/GameConstants.cpp
../src/ScriptEngine.h
../src/ScriptEngine.cpp
../src/navigation/PathFinder.h
../src/navigation/PathFinder.cpp
../src/items/GameObjectLoader.h
../src/items/GameObjectLoader.cpp
../src/items/Item.h
../src/items/Item.cpp
../src/items/InteractiveObject.h
../src/items/InteractiveObject.cpp
../src/dialogue/DialogueTypes.h
../src/dialogue/DialogueDatabase.h
../src/dialogue/DialogueDatabase.cpp
../src/dialogue/DialogueRuntime.h
../src/dialogue/DialogueRuntime.cpp
../src/dialogue/DialogueOverlay.h
../src/dialogue/DialogueOverlay.cpp
../src/dialogue/DialogueSystem.h
../src/dialogue/DialogueSystem.cpp
../src/quest/QuestTypes.h
../src/quest/QuestJournal.h
../src/quest/QuestJournal.cpp
../src/render/Renderer.cpp
../src/render/ShaderManager.cpp
../src/render/TextureManager.cpp
../src/render/FrameBuffer.cpp
../src/render/OpenGlExtensions.cpp
../src/utils/Utils.cpp
../src/utils/TaskManager.cpp
../src/utils/Perlin.cpp
../src/planet/PlanetData.cpp
../src/planet/PlanetObject.cpp
../src/planet/StoneObject.cpp
)
add_executable(bishkek-witcher ${SOURCES})
add_executable(space-game001 ${SOURCES})
# Настройка путей к инклудам (используем скачанные исходники)
target_include_directories(bishkek-witcher PRIVATE
target_include_directories(space-game001 PRIVATE
../src
../thirdparty/eigen-5.0.0
../thirdparty/boost_1_90_0
"${SOL2_SRC_DIR}/include"
)
# Сборка libzip через add_subdirectory (Emscripten соберет её из исходников)
# Опции для либзипа, чтобы он не искал лишнего в системе
set(ENABLE_GNUTLS OFF CACHE BOOL "" FORCE)
set(ENABLE_OPENSSL OFF CACHE BOOL "" FORCE)
set(ENABLE_WINDOWS_CRYPTO OFF CACHE BOOL "" FORCE)
@@ -145,45 +58,38 @@ set(ENABLE_COMMONCRYPTO OFF CACHE BOOL "" FORCE)
add_subdirectory("../thirdparty/libzip-1.11.4" libzip-build)
target_link_libraries(bishkek-witcher PRIVATE zip z lua_static websocket.js)
# Линковка:
# 'zip' берется из add_subdirectory
# 'z' - это системный zlib Emscripten-а (флаг -sUSE_ZLIB=1 добавим ниже)
target_link_libraries(space-game001 PRIVATE zip z)
# Эмскриптен-флаги
set(EMSCRIPTEN_FLAGS
"-sUSE_SDL=2"
"-sUSE_SDL_IMAGE=2"
"-sUSE_LIBPNG=1"
"-sUSE_ZLIB=1"
"-sUSE_SDL_TTF=2"
"-sUSE_SDL_MIXER=2"
"-sSDL2_MIXER_FORMATS=[ogg,mp3]"
#"-pthread"
#"-sUSE_PTHREADS=1"
"-sUSE_ZLIB=1" # Добавили zlib порт
"-pthread"
"-sUSE_PTHREADS=1"
"-fexceptions"
"-DNETWORK"
)
target_compile_options(bishkek-witcher PRIVATE ${EMSCRIPTEN_FLAGS} "-O2")
target_compile_options(space-game001 PRIVATE ${EMSCRIPTEN_FLAGS} "-O2")
# Only loading.png and the shaders used before resources.zip is ready are preloaded.
# resources.zip is downloaded asynchronously at runtime and served as a separate file.
set(EMSCRIPTEN_LINK_FLAGS
${EMSCRIPTEN_FLAGS}
"-O2"
#"-sPTHREAD_POOL_SIZE=4"
"-sPTHREAD_POOL_SIZE=4"
"-sALLOW_MEMORY_GROWTH=1"
"-sFULL_ES3=1"
"--preload-file ${CMAKE_CURRENT_SOURCE_DIR}/../resources/loading.png@resources/loading.png"
"--preload-file ${CMAKE_CURRENT_SOURCE_DIR}/../resources/shaders@resources/shaders"
"--preload-file ${CMAKE_CURRENT_SOURCE_DIR}/../resources/start.lua@resources/start.lua"
"--preload-file ${CMAKE_CURRENT_SOURCE_DIR}/../audio@audio"
"--preload-file resources.zip"
)
# Применяем настройки линковки
target_link_options(bishkek-witcher PRIVATE ${EMSCRIPTEN_LINK_FLAGS})
target_link_options(space-game001 PRIVATE ${EMSCRIPTEN_LINK_FLAGS})
# Для совместимости со старыми версиями CMake, если target_link_options недостаточно
string(REPLACE ";" " " EMSCRIPTEN_LINK_FLAGS_STR "${EMSCRIPTEN_LINK_FLAGS}")
set_target_properties(bishkek-witcher PROPERTIES
set_target_properties(space-game001 PROPERTIES
LINK_FLAGS "${EMSCRIPTEN_LINK_FLAGS_STR}"
SUFFIX ".html"
)
@@ -201,33 +107,34 @@ add_custom_command(
)
add_custom_target(pack_resources DEPENDS "${RESOURCES_ZIP}")
add_dependencies(bishkek-witcher pack_resources)
add_dependencies(space-game001 pack_resources)
# Определяем путь к директории установки (относительно папки билда)
set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/public")
# 1. Устанавливаем основной HTML файл
install(TARGETS bishkek-witcher
install(TARGETS space-game001
RUNTIME DESTINATION .
)
# 2. Устанавливаем сопутствующие файлы (JS, WASM и сгенерированный архив данных)
install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/bishkek-witcher.js"
"${CMAKE_CURRENT_BINARY_DIR}/bishkek-witcher.wasm"
"${CMAKE_CURRENT_BINARY_DIR}/bishkek-witcher.data"
# Emscripten создает их в той же папке, что и таргет
install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/space-game001.js"
"${CMAKE_CURRENT_BINARY_DIR}/space-game001.wasm"
"${CMAKE_CURRENT_BINARY_DIR}/space-game001.data"
DESTINATION .
)
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/index.html"
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/space-game001plain.html"
DESTINATION .
)
# resources.zip is served separately and downloaded asynchronously at runtime
install(FILES "${RESOURCES_ZIP}" DESTINATION .)
# Если вам все еще нужен сам resources.zip отдельно в папке public:
#install(FILES "${RESOURCES_ZIP}" DESTINATION .)
add_custom_command(TARGET bishkek-witcher POST_BUILD
add_custom_command(TARGET space-game001 POST_BUILD
COMMAND ${CMAKE_COMMAND} --install .
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
COMMENT "Automatically deploying to public directory..."
+1 -7
View File
@@ -1,13 +1,7 @@
# how to build
If emsdk is not installed, you need to clone it from here: https://github.com/emscripten-core/emsdk
and install:
```
C:\Work\Projects\emsdk\emsdk.bat install latest
```
Then activate the environment:
Activate the environment:
```
C:\Work\Projects\emsdk\emsdk.bat activate latest
C:\Work\Projects\emsdk\emsdk_env.bat
-82
View File
@@ -1,82 +0,0 @@
<!DOCTYPE html>
<html lang="en-us">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
<title>Bishkek Witcher</title>
<style>
body, html {
margin: 0; padding: 0; width: 100%; height: 100%;
overflow: hidden; background-color: #000;
position: fixed;
}
#canvas {
display: block;
position: absolute;
top: 0; left: 0;
width: 100vw; height: 100vh;
border: none;
}
#fs-button {
position: absolute;
top: 10px; right: 10px;
padding: 10px;
z-index: 10;
background: rgba(255,255,255,0.3);
color: white; border: 1px solid white;
cursor: pointer;
font-family: sans-serif;
border-radius: 5px;
}
#status { color: white; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); }
</style>
</head>
<body>
<button id="fs-button">Fullscreen</button>
<div id="status">Downloading...</div>
<canvas id="canvas" oncontextmenu="event.preventDefault()" tabindex="-1"></canvas>
<script>
function prepareModuleEnvironment() {
window.Module = window.Module || {};
var canvasEl = document.getElementById('canvas');
window.Module.canvas = canvasEl;
window.Module.setStatus = window.Module.setStatus || function (text) {
var statusElement = document.getElementById("status");
statusElement.innerHTML = text;
statusElement.style.display = text ? 'block' : 'none';
};
}
function loadGameScript() {
var s = document.createElement('script');
s.src = 'bishkek-witcher.js';
s.async = true;
document.body.appendChild(s);
}
document.getElementById('fs-button').addEventListener('click', function() {
if (!document.fullscreenElement) {
document.documentElement.requestFullscreen().catch(function(e) {
console.error('Fullscreen error: ' + e.message);
});
} else {
document.exitFullscreen();
}
});
document.addEventListener('DOMContentLoaded', function() {
prepareModuleEnvironment();
loadGameScript();
});
window.addEventListener("orientationchange", function() {
setTimeout(() => {
window.dispatchEvent(new Event('resize'));
}, 200);
});
</script>
</body>
</html>
-2
View File
@@ -1,2 +0,0 @@
<!doctypehtml><html lang=en-us><head><meta charset=utf-8><meta content="text/html; charset=utf-8"http-equiv=Content-Type><title>Emscripten-Generated Code</title><style>body{font-family:arial;margin:0;padding:none}.emscripten{padding-right:0;margin-left:auto;margin-right:auto;display:block}div.emscripten{text-align:center}div.emscripten_border{border:1px solid #000}canvas.emscripten{border:0 none;background-color:#000}#emscripten_logo{display:inline-block;margin:0;padding:6px;width:265px}.spinner{height:30px;width:30px;margin:0;margin-top:20px;margin-left:20px;display:inline-block;vertical-align:top;-webkit-animation:rotation .8s linear infinite;-moz-animation:rotation .8s linear infinite;-o-animation:rotation .8s linear infinite;animation:rotation .8s linear infinite;border-left:5px solid #ebebeb;border-right:5px solid #ebebeb;border-bottom:5px solid #ebebeb;border-top:5px solid #787878;border-radius:100%;background-color:#bdd72e}@-webkit-keyframes rotation{from{-webkit-transform:rotate(0)}to{-webkit-transform:rotate(360deg)}}@-moz-keyframes rotation{from{-moz-transform:rotate(0)}to{-moz-transform:rotate(360deg)}}@-o-keyframes rotation{from{-o-transform:rotate(0)}to{-o-transform:rotate(360deg)}}@keyframes rotation{from{transform:rotate(0)}to{transform:rotate(360deg)}}#status{display:inline-block;vertical-align:top;margin-top:30px;margin-left:20px;font-weight:700;color:#787878}#progress{height:20px;width:300px}#controls{display:inline-block;float:right;vertical-align:top;margin-top:30px;margin-right:20px}#output{width:100%;height:200px;margin:0 auto;margin-top:10px;border-left:0;border-right:0px;padding-left:0;padding-right:0;display:block;background-color:#000;color:#fff;font-family:'Lucida Console',Monaco,monospace;outline:0}</style></head><body><script src="https://cdn.jsdelivr.net/npm/eruda"></script>
<script>eruda.init();</script><a href=http://emscripten.org><img id=emscripten_logo src=""></a><div class=spinner id=spinner></div><div class=emscripten id=status>Downloading...</div><span id=controls><span><input type=checkbox id=resize>Resize canvas</span> <span><input type=checkbox id=pointerLock checked>Lock/hide mouse pointer    </span><span><input type=button onclick='Module.requestFullscreen(document.getElementById("pointerLock").checked,document.getElementById("resize").checked)'value=Fullscreen></span></span><div class=emscripten><progress hidden id=progress max=100 value=0></progress></div><div class=emscripten_border><canvas class=emscripten id=canvas oncontextmenu=event.preventDefault() tabindex=-1></canvas></div><textarea id=output rows=8></textarea><script>var statusElement=document.getElementById("status"),progressElement=document.getElementById("progress"),spinnerElement=document.getElementById("spinner"),canvasElement=document.getElementById("canvas"),outputElement=document.getElementById("output");outputElement&&(outputElement.value=""),canvasElement.addEventListener("webglcontextlost",(e=>{alert("WebGL context lost. You will need to reload the page."),e.preventDefault()}),!1);var Module={print(...e){if(console.log(...e),outputElement){var t=e.join(" ");outputElement.value+=t+"\n",outputElement.scrollTop=outputElement.scrollHeight}},canvas:canvasElement,setStatus(e){if(Module.setStatus.last||(Module.setStatus.last={time:Date.now(),text:""}),e!==Module.setStatus.last.text){var t=e.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/),n=Date.now();t&&n-Module.setStatus.last.time<30||(Module.setStatus.last.time=n,Module.setStatus.last.text=e,t?(e=t[1],progressElement.value=100*parseInt(t[2]),progressElement.max=100*parseInt(t[4]),progressElement.hidden=!1,spinnerElement.hidden=!1):(progressElement.value=null,progressElement.max=null,progressElement.hidden=!0,e||(spinnerElement.style.display="none")),statusElement.innerHTML=e)}},totalDependencies:0,monitorRunDependencies(e){this.totalDependencies=Math.max(this.totalDependencies,e),Module.setStatus(e?"Preparing... ("+(this.totalDependencies-e)+"/"+this.totalDependencies+")":"All downloads complete.")}};Module.setStatus("Downloading..."),window.onerror=e=>{Module.setStatus("Exception thrown, see JavaScript console"),spinnerElement.style.display="none",Module.setStatus=e=>{e&&console.error("[post-exception status] "+e)}}</script><script async src="space-game001.js" crossorigin="anonymous"></script></body></html>
+93 -193
View File
@@ -1,200 +1,100 @@
<!DOCTYPE html>
<html lang="en-us">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
<title>Sky Trek Tales</title>
<style>
body, html {
margin: 0; padding: 0; width: 100%; height: 100%;
overflow: hidden; background-color: #000;
position: fixed;
}
#canvas {
display: block;
position: absolute;
top: 0; left: 0;
width: 100vw; height: 100vh;
border: none;
}
<!doctypehtml>
<html lang=en-us>
#fs-button {
position: absolute;
top: 10px; right: 10px;
padding: 10px;
z-index: 10;
background: rgba(255,255,255,0.3);
color: white; border: 1px solid white;
cursor: pointer;
font-family: sans-serif;
border-radius: 5px;
}
#status { color: white; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); }
/* Nick modal */
#nickOverlay {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
background: rgba(0,0,0,0.85);
z-index: 9999;
}
#nickBox {
background: #111;
border: 1px solid #444;
padding: 24px;
width: 320px;
box-shadow: 0 8px 24px rgba(0,0,0,0.6);
text-align: center;
}
#nickBox h2 { margin: 0 0 12px 0; font-size: 18px; color: #eee; }
#nickBox input[type="text"] {
width: 100%;
padding: 10px;
font-size: 16px;
box-sizing: border-box;
margin-bottom: 12px;
border: 1px solid #333;
background: #000;
color: #fff;
}
#nickBox button {
padding: 10px 16px;
font-size: 16px;
background: #2a9fd6;
border: none;
color: #fff;
cursor: pointer;
}
</style>
</head>
<body>
<button id="fs-button">Fullscreen</button>
<div id="status">Downloading...</div>
<canvas id="canvas" oncontextmenu="event.preventDefault()" tabindex="-1"></canvas>
<div id="nickOverlay" style="display:none;">
<div id="nickBox">
<h2>Enter your nickname</h2>
<input id="nickInput" type="text" maxlength="32" placeholder="Player" />
<div>
<button id="nickSubmit">Start</button>
</div>
</div>
</div>
<script>
// Utility: подготовить глобальный Module до загрузки Emscripten-скрипта
function prepareModuleEnvironment() {
window.Module = window.Module || {};
var canvasEl = document.getElementById('canvas');
// Устанавливаем canvas для Emscripten, чтобы createContext не падал
window.Module.canvas = canvasEl;
// Подготовим заглушку setStatus, если ещё нет
window.Module.setStatus = window.Module.setStatus || function (text) {
var statusElement = document.getElementById("status");
statusElement.innerHTML = text;
statusElement.style.display = text ? 'block' : 'none';
};
}
// Show overlay only if no nickname saved.
function loadGameScript() {
var s = document.createElement('script');
s.src = 'space-game001.js';
s.async = true;
document.body.appendChild(s);
}
function showNickOverlay() {
var overlay = document.getElementById('nickOverlay');
overlay.style.display = 'flex';
var input = document.getElementById('nickInput');
input.focus();
}
function hideNickOverlay() {
var overlay = document.getElementById('nickOverlay');
overlay.style.display = 'none';
}
function saveNickAndStart(nick) {
try {
if (!nick || nick.trim() === '') nick = 'Player';
localStorage.setItem('spacegame_nick', nick);
} catch (e) {
console.warn('localStorage not available', e);
}
hideNickOverlay();
// перед загрузкой скрипта гарантируем, что Module.canvas задан
prepareModuleEnvironment();
loadGameScript();
}
document.getElementById('fs-button').addEventListener('click', function() {
if (!document.fullscreenElement) {
document.documentElement.requestFullscreen().catch(function(e) {
console.error('Fullscreen error: ' + e.message);
});
} else {
document.exitFullscreen();
}
});
document.addEventListener('DOMContentLoaded', function() {
// Готовим Module сразу — даже если откроется модалка, поле canvas будет доступно для скрипта (если он загружается позже)
prepareModuleEnvironment();
var stored = null;
try {
stored = localStorage.getItem('spacegame_nick');
} catch (e) {
console.warn('localStorage not available', e);
<head>
<meta charset=utf-8>
<meta content="text/html; charset=utf-8" http-equiv=Content-Type>
<title>Space Game</title>
<style>
body {
font-family: arial;
margin: 0;
overflow: hidden;
padding: 0;
min-height: 100vh;
background-color: #000;
}
if (stored && stored.trim() !== '') {
// Nick is present — start immediately
loadGameScript();
} else {
// Show modal to request nickname before loading WASM
showNickOverlay();
var submit = document.getElementById('nickSubmit');
var input = document.getElementById('nickInput');
submit.addEventListener('click', function() {
saveNickAndStart(input.value);
});
input.addEventListener('input', function() {
// Strip any character that is not a-z, A-Z, 0-9 or space
var pos = this.selectionStart;
var cleaned = this.value.replace(/[^a-zA-Z0-9 ]/g, '');
if (cleaned !== this.value) {
this.value = cleaned;
this.setSelectionRange(pos - 1, pos - 1);
}
});
input.addEventListener('keydown', function(e) {
if (e.key === 'Enter') {
saveNickAndStart(input.value);
}
});
.emscripten {
padding-right: 0;
margin-left: auto;
margin-right: auto;
display: block
}
});
window.addEventListener("orientationchange", function() {
// Chrome на Android обновляет innerWidth/Height не мгновенно.
// Ждем завершения анимации поворота.
setTimeout(() => {
// В Emscripten это вызовет ваш onWindowResized в C++
window.dispatchEvent(new Event('resize'));
}, 200);
});
div.emscripten {
text-align: center
}
</script>
div.emscripten_border {
border: 1px solid #000
}
</body>
</html>
canvas.emscripten {
border: 0 none;
background-color: #000;
width: 90%;
height: 100vh;
}
@-webkit-keyframes rotation {
from {
-webkit-transform: rotate(0)
}
to {
-webkit-transform: rotate(360deg)
}
}
@-moz-keyframes rotation {
from {
-moz-transform: rotate(0)
}
to {
-moz-transform: rotate(360deg)
}
}
@-o-keyframes rotation {
from {
-o-transform: rotate(0)
}
to {
-o-transform: rotate(360deg)
}
}
@keyframes rotation {
from {
transform: rotate(0)
}
to {
transform: rotate(360deg)
}
}
#status {
display: none;
}
#progress {
height: 20px;
width: 300px
}
</style>
</head>
<body>
<div class=emscripten id=status></div>
<div class=emscripten><progress hidden id=progress max=100 value=0></progress></div>
<div class=emscripten_border><canvas class=emscripten id=canvas oncontextmenu=event.preventDefault()
tabindex=-1></canvas></div>
<script>var statusElement = document.getElementById("status"), progressElement = document.getElementById("progress"), spinnerElement = document.getElementById("spinner"), Module = { print: function () { var e = document.getElementById("output"); return e && (e.value = ""), function (t) { arguments.length > 1 && (t = Array.prototype.slice.call(arguments).join(" ")), console.log(t), e && (e.value += t + "\n", e.scrollTop = e.scrollHeight) } }(), canvas: (() => { var e = document.getElementById("canvas"); return e.addEventListener("webglcontextlost", (e => { alert("WebGL context lost. You will need to reload the page."), e.preventDefault() }), !1), e })(), setStatus: e => { if (Module.setStatus.last || (Module.setStatus.last = { time: Date.now(), text: "" }), e !== Module.setStatus.last.text) { var t = e.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/), n = Date.now(); t && n - Module.setStatus.last.time < 30 || (Module.setStatus.last.time = n, Module.setStatus.last.text = e, t ? (e = t[1], progressElement.value = 100 * parseInt(t[2]), progressElement.max = 100 * parseInt(t[4]), progressElement.hidden = !1, spinnerElement.hidden = !1) : (progressElement.value = null, progressElement.max = null, progressElement.hidden = !0, e || (spinnerElement.style.display = "none")), statusElement.innerHTML = e) } }, totalDependencies: 0, monitorRunDependencies: e => { this.totalDependencies = Math.max(this.totalDependencies, e), Module.setStatus(e ? "Preparing... (" + (this.totalDependencies - e) + "/" + this.totalDependencies + ")" : "All downloads complete.") } }; Module.setStatus("Downloading..."), window.onerror = e => { Module.setStatus("Exception thrown, see JavaScript console"), spinnerElement.style.display = "none", Module.setStatus = e => { e && console.error("[post-exception status] " + e) } }</script>
<script async src=space-game001.js></script>
</body>
</html>
+24 -59
View File
@@ -1,14 +1,10 @@
cmake_minimum_required(VERSION 3.16)
project(space-game001 LANGUAGES C CXX)
project(space-game001 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Build option (Windows-only): when ON, the game launches in fullscreen.
# When OFF (default), it launches in a 1280x720 window like before.
option(FULLSCREEN "Launch the game in fullscreen mode" OFF)
include(${CMAKE_CURRENT_SOURCE_DIR}/../cmake/ThirdParty.cmake)
# ===========================================
@@ -18,12 +14,12 @@ add_executable(space-game001
../src/main.cpp
../src/Game.cpp
../src/Game.h
../src/Character.cpp
../src/Character.h
../src/Environment.cpp
../src/Environment.h
../src/render/Renderer.cpp
../src/render/Renderer.h
../src/render/Camera.cpp
../src/render/Camera.h
../src/render/ShaderManager.cpp
../src/render/ShaderManager.h
../src/render/TextureManager.cpp
@@ -34,54 +30,35 @@ add_executable(space-game001
../src/AudioPlayerAsync.h
../src/BoneAnimatedModel.cpp
../src/BoneAnimatedModel.h
../src/BoneAnimatedModelNew.cpp
../src/BoneAnimatedModelNew.h
../src/render/OpenGlExtensions.cpp
../src/render/OpenGlExtensions.h
../src/utils/Utils.cpp
../src/utils/Utils.h
../src/SparkEmitter.cpp
../src/SparkEmitter.h
../src/planet/PlanetObject.cpp
../src/planet/PlanetObject.h
../src/planet/PlanetData.cpp
../src/planet/PlanetData.h
../src/utils/Perlin.cpp
../src/utils/Perlin.h
../src/utils/TaskManager.cpp
../src/utils/TaskManager.h
../src/planet/StoneObject.cpp
../src/planet/StoneObject.h
../src/render/FrameBuffer.cpp
../src/render/FrameBuffer.h
../src/render/ShadowMap.cpp
../src/render/ShadowMap.h
../src/UiManager.cpp
../src/UiManager.h
../src/render/TextRenderer.h
../src/render/TextRenderer.cpp
../src/MenuManager.h
../src/MenuManager.cpp
../src/Location.h
../src/Location.cpp
../src/GameConstants.h
../src/GameConstants.cpp
../src/ScriptEngine.h
../src/ScriptEngine.cpp
../src/navigation/PathFinder.h
../src/navigation/PathFinder.cpp
../src/items/GameObjectLoader.h
../src/items/GameObjectLoader.cpp
../src/items/Item.h
../src/items/Item.cpp
../src/items/InteractiveObject.h
../src/items/InteractiveObject.cpp
../src/dialogue/DialogueTypes.h
../src/dialogue/DialogueDatabase.h
../src/dialogue/DialogueDatabase.cpp
../src/dialogue/DialogueRuntime.h
../src/dialogue/DialogueRuntime.cpp
../src/dialogue/DialogueOverlay.h
../src/dialogue/DialogueOverlay.cpp
../src/dialogue/DialogueSystem.h
../src/dialogue/DialogueSystem.cpp
../src/quest/QuestTypes.h
../src/quest/QuestJournal.h
../src/quest/QuestJournal.cpp
../src/Projectile.h
../src/Projectile.cpp
../src/network/NetworkInterface.h
../src/network/LocalClient.h
../src/network/LocalClient.cpp
../src/network/ClientState.h
../src/network/ClientState.cpp
../src/network/WebSocketClient.h
../src/network/WebSocketClient.cpp
)
# Установка проекта по умолчанию для Visual Studio
@@ -103,14 +80,10 @@ target_compile_definitions(space-game001 PRIVATE
WIN32_LEAN_AND_MEAN
PNG_ENABLED
SDL_MAIN_HANDLED
# DEBUG_LIGHT
# SHOW_PATH
# NETWORK
# SIMPLIFIED
)
if(FULLSCREEN)
target_compile_definitions(space-game001 PRIVATE FULLSCREEN)
endif()
# Линкуем с SDL2main, если он вообще установлен
target_link_libraries(space-game001 PRIVATE SDL2main_external_lib)
@@ -124,8 +97,6 @@ target_link_libraries(space-game001 PRIVATE
SDL2_ttf_external_lib
eigen_external_lib
boost_external_lib
sol2_external_lib
SDL2_mixer_external_lib
)
# Линкуем OpenGL (Windows)
@@ -145,12 +116,11 @@ if (WIN32)
set(LIBZIP_DLL_SRC "$<IF:$<CONFIG:Debug>,${LIBZIP_BASE_DIR}-Debug/bin/zip.dll,${LIBZIP_BASE_DIR}-Release/bin/zip.dll>")
set(ZLIB_DLL_SRC "$<IF:$<CONFIG:Debug>,${ZLIB_INSTALL_DIR}/bin/zd.dll,${ZLIB_INSTALL_DIR}/bin/z.dll>")
set(ZLIB_DLL_DST "$<IF:$<CONFIG:Debug>,$<TARGET_FILE_DIR:space-game001>/zd.dll,$<TARGET_FILE_DIR:space-game001>/z.dll>")
set(ZLIB_DLL_SRC "$<IF:$<CONFIG:Debug>,${ZLIB_INSTALL_DIR}/bin/zlibd.dll,${ZLIB_INSTALL_DIR}/bin/zlib.dll>")
set(ZLIB_DLL_DST "$<IF:$<CONFIG:Debug>,$<TARGET_FILE_DIR:space-game001>/zlibd.dll,$<TARGET_FILE_DIR:space-game001>/zlib.dll>")
set(SDL2TTF_DLL_SRC "$<IF:$<CONFIG:Debug>,${SDL2TTF_BASE_DIR}-Debug/bin/SDL2_ttfd.dll,${SDL2TTF_BASE_DIR}-Release/bin/SDL2_ttf.dll>")
set(SDL2MIXER_DLL_SRC "$<IF:$<CONFIG:Debug>,${SDL2MIXER_BASE_DIR}-Debug/bin/SDL2_mixerd.dll,${SDL2MIXER_BASE_DIR}-Release/bin/SDL2_mixer.dll>")
add_custom_command(TARGET space-game001 POST_BUILD
COMMAND ${CMAKE_COMMAND} -E echo "Copying DLLs to output folder..."
@@ -172,10 +142,6 @@ if (WIN32)
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${SDL2TTF_DLL_SRC}"
"$<TARGET_FILE_DIR:space-game001>"
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${CMAKE_CURRENT_SOURCE_DIR}/../thirdparty/SDL_mixer-release-2.8.0/install-$<CONFIG>/bin/SDL2_mixer$<$<CONFIG:Debug>:d>.dll"
"$<TARGET_FILE_DIR:space-game001>/SDL2_mixer$<$<CONFIG:Debug>:d>.dll"
)
endif()
@@ -186,7 +152,6 @@ endif()
# Какие папки с ресурсами нужно копировать
set(RUNTIME_RESOURCE_DIRS
"resources"
"audio"
)
# Копируем ресурсы и шейдеры в папку exe и в корень build/
@@ -202,4 +167,4 @@ foreach(resdir IN LISTS RUNTIME_RESOURCE_DIRS)
"${CMAKE_SOURCE_DIR}/../${resdir}"
"${CMAKE_BINARY_DIR}/${resdir}"
)
endforeach()
endforeach()
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+29
View File
@@ -0,0 +1,29 @@
===Vertices (Split by UV/Normal): 14
V 0: Pos(1.0, 1.0, 1.0) Norm(0.57735, 0.57735, 0.57735) UV(0.5, 0.5)
V 1: Pos(-1.0, 1.0, 1.0) Norm(-0.57735, 0.57735, 0.57735) UV(0.75, 0.5)
V 2: Pos(-1.0, -1.0, 1.0) Norm(-0.57735, -0.57735, 0.57735) UV(0.75, 0.75)
V 3: Pos(1.0, -1.0, 1.0) Norm(0.57735, -0.57735, 0.57735) UV(0.5, 0.75)
V 4: Pos(1.0, -1.0, -1.0) Norm(0.57735, -0.57735, -0.57735) UV(0.25, 0.75)
V 5: Pos(-1.0, -1.0, 1.0) Norm(-0.57735, -0.57735, 0.57735) UV(0.5, 1.0)
V 6: Pos(-1.0, -1.0, -1.0) Norm(-0.57735, -0.57735, -0.57735) UV(0.25, 1.0)
V 7: Pos(-1.0, -1.0, -1.0) Norm(-0.57735, -0.57735, -0.57735) UV(0.25, 0.0)
V 8: Pos(-1.0, -1.0, 1.0) Norm(-0.57735, -0.57735, 0.57735) UV(0.5, 0.0)
V 9: Pos(-1.0, 1.0, 1.0) Norm(-0.57735, 0.57735, 0.57735) UV(0.5, 0.25)
V 10: Pos(-1.0, 1.0, -1.0) Norm(-0.57735, 0.57735, -0.57735) UV(0.25, 0.25)
V 11: Pos(-1.0, 1.0, -1.0) Norm(-0.57735, 0.57735, -0.57735) UV(0.0, 0.5)
V 12: Pos(1.0, 1.0, -1.0) Norm(0.57735, 0.57735, -0.57735) UV(0.25, 0.5)
V 13: Pos(-1.0, -1.0, -1.0) Norm(-0.57735, -0.57735, -0.57735) UV(0.0, 0.75)
===Triangles (Indices): 12
Tri: 0 1 2
Tri: 0 2 3
Tri: 4 3 5
Tri: 4 5 6
Tri: 7 8 9
Tri: 7 9 10
Tri: 11 12 4
Tri: 11 4 13
Tri: 12 0 3
Tri: 12 3 4
Tri: 10 9 0
Tri: 10 0 12
+15
View File
@@ -0,0 +1,15 @@
{
"emissionPoints": [
{ "position": [0.0, 0.0, 0.0] }
],
"texture": "resources/spark_white.png",
"speedRange": [10.0, 30.0],
"zSpeedRange": [-1.0, 1.0],
"scaleRange": [0.5, 1.0],
"lifeTimeRange": [200.0, 800.0],
"emissionRate": 50.0,
"maxParticles": 5,
"particleSize": 2,
"biasX": 0.1,
"shaderProgramName": "default"
}
+55
View File
@@ -0,0 +1,55 @@
{
"root": {
"type": "LinearLayout",
"orientation": "vertical",
"align": "center",
"x": 0,
"y": 0,
"width": 1920,
"height": 1080,
"background": {
"color": [0, 0, 0, 0.7]
},
"children": [
{
"type": "Button",
"name": "gameOverText",
"x": 350,
"y": 400,
"width": 600,
"height": 150,
"textures": {
"normal": "resources/gameover.png",
"hover": "resources/gameover.png",
"pressed": "resources/gameover.png"
}
},
{
"type": "Button",
"name": "restartButton",
"x": 350,
"y": 300,
"width": 300,
"height": 80,
"textures": {
"normal": "resources/shoot_normal.png",
"hover": "resources/shoot_normal.png",
"pressed": "resources/shoot_normal.png"
}
},
{
"type": "Button",
"name": "gameOverExitButton",
"x": 650,
"y": 300,
"width": 300,
"height": 80,
"textures": {
"normal": "resources/sand2.png",
"hover": "resources/sand2.png",
"pressed": "resources/sand2.png"
}
}
]
}
}
+72
View File
@@ -0,0 +1,72 @@
{
"root": {
"type": "FrameLayout",
"x": 0,
"y": 0,
"width": 1280,
"height": 720,
"children": [
{
"type": "FrameLayout",
"name": "centerPanel",
"x": 480,
"y": 160,
"width": 320,
"height": 400,
"children": [
{
"type": "LinearLayout",
"name": "settingsButtons",
"orientation": "vertical",
"spacing": 10,
"x": 0,
"y": 0,
"width": 300,
"height": 300,
"children": [
{
"type": "Button",
"name": "Opt1",
"x": 100,
"y": 300,
"width": 200,
"height": 50,
"textures": {
"normal": "resources/sand2.png",
"hover": "resources/sand2.png",
"pressed": "resources/sand2.png"
}
},
{
"type": "Button",
"name": "Opt2",
"x": 100,
"y": 200,
"width": 200,
"height": 50,
"textures": {
"normal": "resources/sand2.png",
"hover": "resources/sand2.png",
"pressed": "resources/sand2.png"
}
},
{
"type": "Button",
"name": "backButton",
"x": 100,
"y": 100,
"width": 200,
"height": 50,
"textures": {
"normal": "resources/sand2.png",
"hover": "resources/sand2.png",
"pressed": "resources/sand2.png"
}
}
]
}
]
}
]
}
}
+22
View File
@@ -0,0 +1,22 @@
{
"emissionRate": 100.0,
"maxParticles": 200,
"particleSize": 0.3,
"biasX": 0.3,
"emissionPoints": [
{
"position": [-1.3, 0, 0.0]
},
{
"position": [1.3, 0.0, 0.0]
}
],
"speedRange": [0.5, 2.0],
"zSpeedRange": [1.0, 3.0],
"scaleRange": [0.8, 1.2],
"lifeTimeRange": [600.0, 1400.0],
"texture": "resources/spark.png",
"shaderProgramName": "default",
"vertexShader": "resources/shaders/spark.vertex",
"fragmentShader": "resources/shaders/spark.fragment"
}
@@ -0,0 +1,15 @@
{
"emissionPoints": [
{ "position": [0.0, 0.0, 0.0] }
],
"texture": "resources/spark_white.png",
"speedRange": [10.0, 30.0],
"zSpeedRange": [-1.0, 1.0],
"scaleRange": [0.5, 1.0],
"lifeTimeRange": [200.0, 800.0],
"emissionRate": 50.0,
"maxParticles": 10,
"particleSize": 0.09,
"biasX": 0.1,
"shaderProgramName": "default"
}
+169
View File
@@ -0,0 +1,169 @@
{
"root": {
"type": "FrameLayout",
"x": 0,
"y": 0,
"width": 1280,
"height": 720,
"children": [
{
"type": "FrameLayout",
"name": "leftPanel",
"x": 100,
"y": 100,
"width": 320,
"height": 400,
"children": [
{
"type": "LinearLayout",
"name": "mainButtons",
"orientation": "vertical",
"spacing": 10,
"x": 0,
"y": 0,
"width": 300,
"height": 300,
"children": [
{
"type": "Button",
"name": "playButton",
"x": -1000,
"y": 500,
"width": 200,
"height": 50,
"animations": {
"buttonsExit": {
"repeat": false,
"steps": [
{
"type": "move",
"to": [
-400,
0
],
"duration": 1.0,
"easing": "easein"
}
]
}
},
"textures": {
"normal": "./resources/sand2.png",
"hover": "./resources/sand2.png",
"pressed": "./resources/sand2.png"
}
},
{
"type": "Button",
"name": "settingsButton",
"x": -1000,
"y": 400,
"width": 200,
"height": 50,
"animations": {
"buttonsExit": {
"repeat": false,
"steps": [
{
"type": "wait",
"duration": 0.5
},
{
"type": "move",
"to": [
-400,
0
],
"duration": 1.0,
"easing": "easein"
}
]
}
},
"textures": {
"normal": "./resources/sand2.png",
"hover": "./resources/sand2.png",
"pressed": "./resources/sand2.png"
}
},
{
"type": "Button",
"name": "exitButton",
"x": -1000,
"y": 300,
"width": 200,
"height": 50,
"animations": {
"buttonsExit": {
"repeat": false,
"steps": [
{
"type": "wait",
"duration": 1.0
},
{
"type": "move",
"to": [
-400,
0
],
"duration": 1.0,
"easing": "easein"
}
]
},
"bgScroll": {
"repeat": true,
"steps": [
{
"type": "move",
"to": [
1280,
0
],
"duration": 5.0,
"easing": "linear"
}
]
}
},
"textures": {
"normal": "./resources/sand2.png",
"hover": "./resources/sand2.png",
"pressed": "./resources/sand2.png"
}
}
]
}
]
},
{
"type": "Slider",
"name": "velocitySlider",
"x": 1140,
"y": 100,
"width": 50,
"height": 500,
"value": 0.0,
"orientation": "vertical",
"textures": {
"track": "resources/velocitySliderTexture.png",
"knob": "resources/velocitySliderButton.png"
}
},
{
"type": "Button",
"name": "shootButton",
"x": 1115,
"y": 0,
"width": 100,
"height": 100,
"textures": {
"normal": "resources/shoot_normal.png",
"hover": "resources/shoot_hover.png",
"pressed": "resources/shoot_pressed.png"
}
}
]
}
}
-43
View File
@@ -1,43 +0,0 @@
{
"objects": [
{
"name": "firebox",
"texturePath": "resources/w/Cube001.png",
"meshPath": "resources/w/firebox.txt",
"rotationX": 0.0,
"rotationY": -1.5707963267948966,
"rotationZ": 0.0,
"positionX": 0.0,
"positionY": 0.0,
"positionZ": 0.0,
"scale": 1.0,
"interactive": false
},
{
"name": "inai",
"texturePath": "resources/w/inai001.png",
"meshPath": "resources/w/inai001.txt",
"rotationX": 0.0,
"rotationY": -1.5707963267948966,
"rotationZ": 0.0,
"positionX": 2.5,
"positionY": 1.4,
"positionZ": -9.9,
"scale": 1.0,
"interactive": false
},
{
"name": "bench",
"texturePath": "resources/w/bench001opt.png",
"meshPath": "resources/w/bench002opt.txt",
"rotationX": 0.0,
"rotationY": 3.141592653589793,
"rotationZ": 0.0,
"positionX": -2.1,
"positionY": 0.5,
"positionZ": -7.9,
"scale": 3.0,
"interactive": false
}
]
}
-160
View File
@@ -1,160 +0,0 @@
{
"objects": [
{
"name": "door",
"texturePath": "resources/w/exterior/door002.png",
"meshPath": "resources/w/exterior/door002.txt",
"rotationX": 0.0,
"rotationY": 0.0,
"rotationZ": 0.0,
"positionX": 0.0,
"positionY": 0.0,
"positionZ": 0.0,
"scale": 1.0,
"interactive": false
},
{
"name": "inai",
"texturePath": "resources/w/exterior/Main_Building.png",
"meshPath": "resources/w/exterior/Main_Building.txt",
"rotationX": 0.0,
"rotationY": 0.0,
"rotationZ": 0.0,
"positionX": 0.0,
"positionY": 0.0,
"positionZ": 0.0,
"scale": 1.0,
"interactive": false
},
{
"name": "tree001",
"texturePath": "resources/w/exterior/tree001.png",
"meshPath": "resources/w/exterior/tree002.txt",
"rotationX": 0.0,
"rotationY": -1.5707963267948966,
"rotationZ": 0.0,
"positionX": 10.0,
"positionY": 0.0,
"positionZ": 12.0,
"scale": 1.0,
"interactive": false
},
{
"name": "tree002",
"texturePath": "resources/w/exterior/tree001.png",
"meshPath": "resources/w/exterior/tree002.txt",
"rotationX": 0.0,
"rotationY": 1.5707963267948966,
"rotationZ": 0.0,
"positionX": -12,
"positionY": 0.0,
"positionZ": 19.0,
"scale": 1.0,
"interactive": false
},
{
"name": "tree003",
"texturePath": "resources/w/exterior/tree001.png",
"meshPath": "resources/w/exterior/tree002.txt",
"rotationX": 0.0,
"rotationY": 0.0,
"rotationZ": 0.0,
"positionX": -12.0,
"positionY": 0.0,
"positionZ": 8.0,
"scale": 1.0,
"interactive": false
},
{
"name": "tree004",
"texturePath": "resources/w/exterior/tree001.png",
"meshPath": "resources/w/exterior/tree002.txt",
"rotationX": 0.0,
"rotationY": -1.5707963267948966,
"rotationZ": 0.0,
"positionX": -12.0,
"positionY": 0.0,
"positionZ": 0.0,
"scale": 1.0,
"interactive": false
},
{
"name": "tree005",
"texturePath": "resources/w/exterior/tree001.png",
"meshPath": "resources/w/exterior/tree002.txt",
"rotationX": 0.0,
"rotationY": 1.5707963267948966,
"rotationZ": 0.0,
"positionX": -12.0,
"positionY": 0.0,
"positionZ": -8.0,
"scale": 1.0,
"interactive": false
},
{
"name": "tree006",
"texturePath": "resources/w/exterior/tree001.png",
"meshPath": "resources/w/exterior/tree002.txt",
"rotationX": 0.0,
"rotationY": 0.0,
"rotationZ": 0.0,
"positionX": 8.49915,
"positionY": 0.0,
"positionZ": -2.59884,
"scale": 1.0,
"interactive": false
},
{
"name": "tree007",
"texturePath": "resources/w/exterior/tree001.png",
"meshPath": "resources/w/exterior/tree002.txt",
"rotationX": 0.0,
"rotationY": -1.5707963267948966,
"rotationZ": 0.0,
"positionX": 14.5936,
"positionY": 0.0,
"positionZ": 5.3401,
"scale": 1.0,
"interactive": false
},
{
"name": "tree008",
"texturePath": "resources/w/exterior/tree001.png",
"meshPath": "resources/w/exterior/tree002.txt",
"rotationX": 0.0,
"rotationY": 1.5707963267948966,
"rotationZ": 0.0,
"positionX": 23.9295,
"positionY": 0.0,
"positionZ": 9.00583,
"scale": 1.0,
"interactive": false
},
{
"name": "tree009",
"texturePath": "resources/w/exterior/tree001.png",
"meshPath": "resources/w/exterior/tree002.txt",
"rotationX": 0.0,
"rotationY": 0.0,
"rotationZ": 0.0,
"positionX": 29.8128,
"positionY": 0.0,
"positionZ": -1.45278,
"scale": 1.0,
"interactive": false
},
{
"name": "tree010",
"texturePath": "resources/w/exterior/tree001.png",
"meshPath": "resources/w/exterior/tree002.txt",
"rotationX": 0.0,
"rotationY": -1.5707963267948966,
"rotationZ": 0.0,
"positionX": 33.1771,
"positionY": 0.0,
"positionZ": 14.609,
"scale": 1.0,
"interactive": false
}
]
}
-178
View File
@@ -1,178 +0,0 @@
{
"cellSize": 0.4,
"agentRadius": 0.45,
"floorY": 0.0,
"objectPadding": 0.25,
"areas": [
{
"name": "main_corridor",
"available": true,
"polygon": [
[-2.2, 0.8],
[2.2, 0.8],
[2.2, -40.8],
[-2.2, -40.8]
]
},
{
"name": "left_door_05",
"available": true,
"polygon": [
[-3.4, -4.2],
[-2.0, -4.2],
[-2.0, -5.8],
[-3.4, -5.8]
]
},
{
"name": "right_door_05",
"available": true,
"polygon": [
[2.0, -4.2],
[3.4, -4.2],
[3.4, -5.8],
[2.0, -5.8]
]
},
{
"name": "left_room_05",
"available": true,
"polygon": [
[-8.8, -1.0],
[-3.0, -1.0],
[-3.0, -9.0],
[-8.8, -9.0]
]
},
{
"name": "right_room_05",
"available": true,
"polygon": [
[3.0, -1.0],
[8.8, -1.0],
[8.8, -9.0],
[3.0, -9.0]
]
},
{
"name": "left_door_15",
"available": true,
"polygon": [
[-3.4, -14.2],
[-2.0, -14.2],
[-2.0, -15.8],
[-3.4, -15.8]
]
},
{
"name": "right_door_15",
"available": true,
"polygon": [
[2.0, -14.2],
[3.4, -14.2],
[3.4, -15.8],
[2.0, -15.8]
]
},
{
"name": "left_room_15",
"available": true,
"polygon": [
[-8.8, -11.0],
[-3.0, -11.0],
[-3.0, -19.0],
[-8.8, -19.0]
]
},
{
"name": "right_room_15",
"available": true,
"polygon": [
[3.0, -11.0],
[8.8, -11.0],
[8.8, -19.0],
[3.0, -19.0]
]
},
{
"name": "left_door_25",
"available": true,
"polygon": [
[-3.4, -24.2],
[-2.0, -24.2],
[-2.0, -25.8],
[-3.4, -25.8]
]
},
{
"name": "right_door_25",
"available": true,
"polygon": [
[2.0, -24.2],
[3.4, -24.2],
[3.4, -25.8],
[2.0, -25.8]
]
},
{
"name": "left_room_25",
"available": true,
"polygon": [
[-8.8, -21.0],
[-3.0, -21.0],
[-3.0, -29.0],
[-8.8, -29.0]
]
},
{
"name": "right_room_25",
"available": true,
"polygon": [
[3.0, -21.0],
[8.8, -21.0],
[8.8, -29.0],
[3.0, -29.0]
]
},
{
"name": "left_door_35",
"available": true,
"polygon": [
[-3.4, -34.2],
[-2.0, -34.2],
[-2.0, -35.8],
[-3.4, -35.8]
]
},
{
"name": "right_door_35",
"available": true,
"polygon": [
[2.0, -34.2],
[3.4, -34.2],
[3.4, -35.8],
[2.0, -35.8]
]
},
{
"name": "left_room_35",
"available": true,
"polygon": [
[-8.8, -31.0],
[-3.0, -31.0],
[-3.0, -39.0],
[-8.8, -39.0]
]
},
{
"name": "right_room_35",
"available": true,
"polygon": [
[3.0, -31.0],
[8.8, -31.0],
[8.8, -39.0],
[3.0, -39.0]
]
}
]
}
-18
View File
@@ -1,18 +0,0 @@
{
"cellSize": 0.4,
"agentRadius": 0.45,
"floorY": 0.0,
"objectPadding": 0.25,
"areas": [
{
"name": "main_corridor",
"available": true,
"polygon": [
[-200, 200],
[200, 200],
[200, -200],
[-200, -200]
]
}
]
}
-99
View File
@@ -1,99 +0,0 @@
{
"npcs": [
{
"id": "npc_04_ghost",
"name": "Беспокойный Призрак",
"texturePath": "resources/w/ghost_skin001.png",
"animationIdlePath": "resources/w/default_float001.anim",
"animationWalkPath": "resources/w/default_float001.anim",
"positionX": 0.0,
"positionY": 0.0,
"positionZ": -35.0,
"walkSpeed": 1.5,
"rotationSpeed": 8.0,
"modelScale": 0.01,
"modelCorrectionRotX": 0.0,
"modelCorrectionRotY": 180.0,
"modelCorrectionRotZ": 0.0,
"interactionRadius": 1.0,
"gift": {
"id": "ghost_essence",
"name": "Ghost's Essence",
"description": "A mysterious essence from the Ghost realm",
"icon": "resources/w/red.png"
}
},
{
"id": "ghost_01x",
"name": "Опасный Призрак",
"texturePath": "resources/w/ghost_skin002.png",
"animationIdlePath": "resources/w/default_float001.anim",
"animationWalkPath": "resources/w/default_float001.anim",
"animationActionIdlePath": "resources/w/float_attack003_cut.anim",
"animationActionAttackPath": "resources/w/float_attack003.anim",
"animationStandToActionPath": "resources/w/default_float001_cut.anim",
"animationActionToStandPath": "resources/w/default_float001_cut.anim",
"animationActionToDeathPath": "resources/w/default_float001_cut.anim",
"animationDeathIdlePath": "resources/w/default_float001_cut.anim",
"positionX": 0.0,
"positionY": 0.0,
"positionZ": -20.0,
"walkSpeed": 1.5,
"rotationSpeed": 8.0,
"modelScale": 0.01,
"modelCorrectionRotX": 0.0,
"modelCorrectionRotY": 180.0,
"modelCorrectionRotZ": 0.0,
"hp": 35,
"canAttack": true
},
{
"id": "ghost_02x",
"name": "Злой призрак",
"texturePath": "resources/w/ghost_skin002.png",
"animationIdlePath": "resources/w/default_float001.anim",
"animationWalkPath": "resources/w/default_float001.anim",
"animationActionIdlePath": "resources/w/float_attack003_cut.anim",
"animationActionAttackPath": "resources/w/float_attack003.anim",
"animationStandToActionPath": "resources/w/default_float001_cut.anim",
"animationActionToStandPath": "resources/w/default_float001_cut.anim",
"animationActionToDeathPath": "resources/w/default_float001_cut.anim",
"animationDeathIdlePath": "resources/w/default_float001_cut.anim",
"positionX": -5.91548,
"positionY": 0.0,
"positionZ": -25.1861,
"walkSpeed": 1.5,
"rotationSpeed": 8.0,
"modelScale": 0.01,
"modelCorrectionRotX": 0.0,
"modelCorrectionRotY": 180.0,
"modelCorrectionRotZ": 0.0,
"hp": 25,
"canAttack": true
},
{
"id": "ghost_02x",
"name": "Злой дух",
"texturePath": "resources/w/ghost_skin002.png",
"animationIdlePath": "resources/w/default_float001.anim",
"animationWalkPath": "resources/w/default_float001.anim",
"animationActionIdlePath": "resources/w/float_attack003_cut.anim",
"animationActionAttackPath": "resources/w/float_attack003.anim",
"animationStandToActionPath": "resources/w/default_float001_cut.anim",
"animationActionToStandPath": "resources/w/default_float001_cut.anim",
"animationActionToDeathPath": "resources/w/default_float001_cut.anim",
"animationDeathIdlePath": "resources/w/default_float001_cut.anim",
"positionX": 6.64604,
"positionY": 0.0,
"positionZ": -16.0176,
"walkSpeed": 1.5,
"rotationSpeed": 8.0,
"modelScale": 0.01,
"modelCorrectionRotX": 0.0,
"modelCorrectionRotY": 180.0,
"modelCorrectionRotZ": 0.0,
"hp": 15,
"canAttack": true
}
]
}
-91
View File
@@ -1,91 +0,0 @@
{
"npcs": [
{
"id": "npc_01_default",
"name": "Студент",
"animationIdlePath": "resources/w/jam/man_stand_idle002.anim",
"animationWalkPath": "resources/w/jam/man_walk002.anim",
"meshTextures": {
"Body": "resources/w/jam/male_packed0_diffuse.png",
"Bottoms": "resources/w/jam/male_packed1_diffuse.png",
"Eyelashes": "resources/w/jam/male_packed0_diffuse.png",
"Eyes": "resources/w/jam/male_packed0_diffuse.png",
"Eyewear": "resources/w/jam/male_packed0_diffuse.png",
"Gloves": "resources/w/jam/male_packed1_diffuse.png",
"Hair": "resources/w/jam/male_packed0_diffuse.png",
"Shoes": "resources/w/jam/male_packed1_diffuse.png",
"Tops": "resources/w/jam/male_packed2_diffuse.png"
},
"positionX": 2.95,
"positionY": 0.0,
"positionZ": 16.65,
"facingAngle" : 3.141592,
"walkSpeed": 1.5,
"rotationSpeed": 8.0,
"modelScale": 0.0001,
"modelCorrectionRotX": 0.0,
"modelCorrectionRotY": 180.0,
"modelCorrectionRotZ": 0.0,
"interactionRadius": 2.0,
"gift": {
"id": "guard_token",
"name": "Guard's Token",
"description": "A token from the Guard - sign of respect",
"icon": "resources/w/red.png"
}
},
{
"id": "npc_02_woman",
"name": "Студентка",
"animationIdlePath": "resources/w/jam/woman_idle002.anim",
"animationWalkPath": "resources/w/jam/woman_walk002.anim",
"meshTextures": {
"Body": "resources/w/jam/female_packed0_diffuse.png",
"Bottoms": "resources/w/jam/female_packed3_diffuse.png",
"Eyelashes": "resources/w/jam/female_packed0_diffuse.png",
"Eyes": "resources/w/jam/female_packed0_diffuse.png",
"Hair": "resources/w/jam/female_packed2_diffuse.png",
"Shoes": "resources/w/jam/female_packed1_diffuse.png",
"Tops": "resources/w/jam/female_packed2_diffuse.png"
},
"positionX": 19.5,
"positionY": 0.0,
"positionZ": 32.0,
"facingAngle" : 3.141592,
"walkSpeed": 1.5,
"rotationSpeed": 8.0,
"modelScale": 0.0001,
"modelCorrectionRotX": 0.0,
"modelCorrectionRotY": 180.0,
"modelCorrectionRotZ": 0.0,
"interactionRadius": 2.0
},
{
"id": "npc_03_salesman",
"name": "Мухтар Байке",
"animationIdlePath": "resources/w/jam/salesperson_stand_idle003.anim",
"animationWalkPath": "resources/w/jam/salesperson_walk001.anim",
"meshTextures": {
"Body": "resources/w/jam/Salesperson_packed0_diffuse.png",
"Bottoms": "resources/w/jam/Salesperson_packed2_diffuse.png",
"Eyelashes": "resources/w/jam/Salesperson_packed0_diffuse.png",
"Eyes": "resources/w/jam/Salesperson_packed0_diffuse.png",
"Hats": "resources/w/jam/Salesperson_packed1_diffuse.png",
"Mustashes": "resources/w/jam/Salesperson_packed1_diffuse.png",
"Shoes": "resources/w/jam/Salesperson_packed1_diffuse.png",
"Tops": "resources/w/jam/Salesperson_packed1_diffuse.png"
},
"positionX": -1.94774,
"positionY": 0.0,
"positionZ": 16.2712,
"facingAngle" : 3.141592,
"walkSpeed": 1.5,
"rotationSpeed": 8.0,
"modelScale": 0.001,
"modelCorrectionRotX": 0.0,
"modelCorrectionRotY": 180.0,
"modelCorrectionRotZ": 0.0,
"interactionRadius": 2.0
}
]
}
-99
View File
@@ -1,99 +0,0 @@
{
"root": {
"type": "FrameLayout",
"name": "inventory_root",
"width": "match_parent",
"height": "match_parent",
"children": [
{
"type": "TextButton",
"name": "inventory_button",
"x": 50.0,
"y": 50.0,
"width": 150.0,
"height": 60.0,
"text": "Inventory",
"fontSize": 24,
"fontPath": "resources/fonts/DroidSans.ttf",
"textCentered": true,
"color": [1.0, 1.0, 1.0, 1.0],
"textures": {
"normal": "resources/w/red.png",
"hover": "resources/w/red.png",
"pressed": "resources/w/red.png"
}
},
{
"type": "FrameLayout",
"name": "inventory_items_panel",
"x": 50.0,
"y": 150.0,
"width": 320.0,
"height": 420.0,
"children": [
{
"type": "StaticImage",
"name": "panel_background",
"x": 0.0,
"y": 0.0,
"width": 320.0,
"height": 420.0,
"texture": "resources/w/red.png"
},
{
"type": "TextView",
"name": "inventory_title_text",
"x": 20.0,
"y": 18.0,
"width": 230.0,
"height": 34.0,
"text": "Inventory",
"fontSize": 24,
"fontPath": "resources/fonts/DroidSans.ttf",
"centered": false,
"topAligned": true,
"paddingX": 0.0,
"paddingY": 0.0,
"color": [1.0, 1.0, 1.0, 1.0]
},
{
"type": "TextView",
"name": "inventory_items_text",
"x": 20.0,
"y": 70.0,
"width": 280.0,
"height": 320.0,
"text": "Inventory (Empty)",
"fontSize": 18,
"fontPath": "resources/fonts/DroidSans.ttf",
"centered": false,
"topAligned": true,
"wrap": true,
"paddingX": 0.0,
"paddingY": 0.0,
"maxLines": 14,
"color": [1.0, 1.0, 1.0, 1.0]
},
{
"type": "TextButton",
"name": "close_inventory_button",
"x": 266.0,
"y": 16.0,
"width": 40.0,
"height": 40.0,
"text": "X",
"fontSize": 20,
"fontPath": "resources/fonts/DroidSans.ttf",
"textCentered": true,
"color": [1.0, 1.0, 1.0, 1.0],
"textures": {
"normal": "resources/w/blue.png",
"hover": "resources/w/blue.png",
"pressed": "resources/w/blue.png"
}
}
]
}
]
}
}
-353
View File
@@ -1,353 +0,0 @@
{
"root": {
"type": "FrameLayout",
"name": "quest_journal_root",
"width": "match_parent",
"height": "match_parent",
"children": [
{
"type": "TextButton",
"name": "quest_journal_button",
"x": 220.0,
"y": 50.0,
"width": 170.0,
"height": 60.0,
"text": "Quests",
"fontSize": 24,
"fontPath": "resources/fonts/DroidSans.ttf",
"textCentered": true,
"color": [1.0, 1.0, 1.0, 1.0],
"textures": {
"normal": "resources/w/red.png",
"hover": "resources/w/red.png",
"pressed": "resources/w/red.png"
}
},
{
"type": "FrameLayout",
"name": "quest_journal_panel",
"x": 55.0,
"y": 85.0,
"width": 1170.0,
"height": 590.0,
"children": [
{
"type": "StaticImage",
"name": "quest_panel_background",
"x": 0.0,
"y": 0.0,
"width": 1170.0,
"height": 590.0,
"texture": "resources/black.png"
},
{
"type": "TextView",
"name": "quest_journal_title_text",
"x": 0.0,
"y": 18.0,
"width": 1170.0,
"height": 44.0,
"text": "QUESTS",
"fontSize": 32,
"fontPath": "resources/fonts/DroidSans.ttf",
"centered": true,
"topAligned": true,
"paddingY": 2.0,
"color": [1.0, 1.0, 1.0, 1.0]
},
{
"type": "TextButton",
"name": "quest_close_button",
"x": 1110.0,
"y": 20.0,
"width": 38.0,
"height": 38.0,
"text": "X",
"fontSize": 20,
"fontPath": "resources/fonts/DroidSans.ttf",
"textCentered": true,
"color": [1.0, 1.0, 1.0, 1.0],
"textures": {
"normal": "resources/w/blue.png",
"hover": "resources/w/blue.png",
"pressed": "resources/w/blue.png"
}
},
{
"type": "StaticImage",
"name": "quest_separator_left",
"x": 390.0,
"y": 88.0,
"width": 2.0,
"height": 470.0,
"texture": "resources/w/red.png"
},
{
"type": "StaticImage",
"name": "quest_separator_right",
"x": 770.0,
"y": 88.0,
"width": 2.0,
"height": 470.0,
"texture": "resources/w/red.png"
},
{
"type": "TextView",
"name": "quest_list_header_text",
"x": 35.0,
"y": 90.0,
"width": 330.0,
"height": 32.0,
"text": "ЗАДАНИЯ",
"fontSize": 22,
"fontPath": "resources/fonts/DroidSans.ttf",
"centered": false,
"topAligned": true,
"paddingX": 4.0,
"paddingY": 0.0,
"color": [1.0, 0.88, 0.45, 1.0]
},
{
"type": "TextButton",
"name": "quest_slot_0",
"x": 35.0,
"y": 130.0,
"width": 330.0,
"height": 42.0,
"text": "",
"fontSize": 18,
"fontPath": "resources/fonts/DroidSans.ttf",
"textCentered": false,
"textPaddingX": 12.0,
"color": [1.0, 1.0, 1.0, 1.0],
"textures": { "normal": "resources/transparent.png", "hover": "resources/w/blue.png", "pressed": "resources/w/blue.png" }
},
{
"type": "TextButton",
"name": "quest_slot_1",
"x": 35.0,
"y": 178.0,
"width": 330.0,
"height": 42.0,
"text": "",
"fontSize": 18,
"fontPath": "resources/fonts/DroidSans.ttf",
"textCentered": false,
"textPaddingX": 12.0,
"color": [1.0, 1.0, 1.0, 1.0],
"textures": { "normal": "resources/transparent.png", "hover": "resources/w/blue.png", "pressed": "resources/w/blue.png" }
},
{
"type": "TextButton",
"name": "quest_slot_2",
"x": 35.0,
"y": 226.0,
"width": 330.0,
"height": 42.0,
"text": "",
"fontSize": 18,
"fontPath": "resources/fonts/DroidSans.ttf",
"textCentered": false,
"textPaddingX": 12.0,
"color": [1.0, 1.0, 1.0, 1.0],
"textures": { "normal": "resources/transparent.png", "hover": "resources/w/blue.png", "pressed": "resources/w/blue.png" }
},
{
"type": "TextButton",
"name": "quest_slot_3",
"x": 35.0,
"y": 274.0,
"width": 330.0,
"height": 42.0,
"text": "",
"fontSize": 18,
"fontPath": "resources/fonts/DroidSans.ttf",
"textCentered": false,
"textPaddingX": 12.0,
"color": [1.0, 1.0, 1.0, 1.0],
"textures": { "normal": "resources/transparent.png", "hover": "resources/w/blue.png", "pressed": "resources/w/blue.png" }
},
{
"type": "TextButton",
"name": "quest_slot_4",
"x": 35.0,
"y": 322.0,
"width": 330.0,
"height": 42.0,
"text": "",
"fontSize": 18,
"fontPath": "resources/fonts/DroidSans.ttf",
"textCentered": false,
"textPaddingX": 12.0,
"color": [1.0, 1.0, 1.0, 1.0],
"textures": { "normal": "resources/transparent.png", "hover": "resources/w/blue.png", "pressed": "resources/w/blue.png" }
},
{
"type": "TextButton",
"name": "quest_slot_5",
"x": 35.0,
"y": 370.0,
"width": 330.0,
"height": 42.0,
"text": "",
"fontSize": 18,
"fontPath": "resources/fonts/DroidSans.ttf",
"textCentered": false,
"textPaddingX": 12.0,
"color": [1.0, 1.0, 1.0, 1.0],
"textures": { "normal": "resources/transparent.png", "hover": "resources/w/blue.png", "pressed": "resources/w/blue.png" }
},
{
"type": "TextButton",
"name": "quest_slot_6",
"x": 35.0,
"y": 418.0,
"width": 330.0,
"height": 42.0,
"text": "",
"fontSize": 18,
"fontPath": "resources/fonts/DroidSans.ttf",
"textCentered": false,
"textPaddingX": 12.0,
"color": [1.0, 1.0, 1.0, 1.0],
"textures": { "normal": "resources/transparent.png", "hover": "resources/w/blue.png", "pressed": "resources/w/blue.png" }
},
{
"type": "TextButton",
"name": "quest_slot_7",
"x": 35.0,
"y": 466.0,
"width": 330.0,
"height": 42.0,
"text": "",
"fontSize": 18,
"fontPath": "resources/fonts/DroidSans.ttf",
"textCentered": false,
"textPaddingX": 12.0,
"color": [1.0, 1.0, 1.0, 1.0],
"textures": { "normal": "resources/transparent.png", "hover": "resources/w/blue.png", "pressed": "resources/w/blue.png" }
},
{
"type": "TextButton",
"name": "quest_slot_8",
"x": 35.0,
"y": 514.0,
"width": 330.0,
"height": 42.0,
"text": "",
"fontSize": 18,
"fontPath": "resources/fonts/DroidSans.ttf",
"textCentered": false,
"textPaddingX": 12.0,
"color": [1.0, 1.0, 1.0, 1.0],
"textures": { "normal": "resources/transparent.png", "hover": "resources/w/blue.png", "pressed": "resources/w/blue.png" }
},
{
"type": "TextView",
"name": "quest_middle_title_text",
"x": 415.0,
"y": 94.0,
"width": 330.0,
"height": 72.0,
"text": "Выберите задание",
"fontSize": 22,
"fontPath": "resources/fonts/DroidSans.ttf",
"centered": false,
"topAligned": true,
"wrap": true,
"paddingX": 8.0,
"paddingY": 4.0,
"maxLines": 2,
"color": [1.0, 0.88, 0.45, 1.0]
},
{
"type": "TextView",
"name": "quest_meta_text",
"x": 415.0,
"y": 168.0,
"width": 330.0,
"height": 48.0,
"text": "",
"fontSize": 16,
"fontPath": "resources/fonts/DroidSans.ttf",
"centered": false,
"topAligned": true,
"wrap": true,
"paddingX": 8.0,
"paddingY": 2.0,
"maxLines": 2,
"color": [0.72, 0.72, 0.72, 1.0]
},
{
"type": "TextView",
"name": "quest_objectives_header_text",
"x": 415.0,
"y": 232.0,
"width": 330.0,
"height": 32.0,
"text": "ЦЕЛИ",
"fontSize": 20,
"fontPath": "resources/fonts/DroidSans.ttf",
"centered": false,
"topAligned": true,
"paddingX": 8.0,
"paddingY": 0.0,
"color": [1.0, 0.88, 0.45, 1.0]
},
{
"type": "TextView",
"name": "quest_objectives_text",
"x": 415.0,
"y": 272.0,
"width": 330.0,
"height": 285.0,
"text": "",
"fontSize": 17,
"fontPath": "resources/fonts/DroidSans.ttf",
"centered": false,
"topAligned": true,
"wrap": true,
"paddingX": 8.0,
"paddingY": 4.0,
"maxLines": 10,
"color": [0.88, 0.88, 0.88, 1.0]
},
{
"type": "TextView",
"name": "quest_lore_title_text",
"x": 795.0,
"y": 94.0,
"width": 330.0,
"height": 38.0,
"text": "Описание задания",
"fontSize": 22,
"fontPath": "resources/fonts/DroidSans.ttf",
"centered": false,
"topAligned": true,
"paddingX": 8.0,
"paddingY": 0.0,
"color": [1.0, 0.88, 0.45, 1.0]
},
{
"type": "TextView",
"name": "quest_description_text",
"x": 795.0,
"y": 145.0,
"width": 330.0,
"height": 410.0,
"text": "",
"fontSize": 18,
"fontPath": "resources/fonts/DroidSans.ttf",
"centered": false,
"topAligned": true,
"wrap": true,
"paddingX": 8.0,
"paddingY": 6.0,
"maxLines": 15,
"color": [0.92, 0.92, 0.92, 1.0]
}
]
}
]
}
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,290 +0,0 @@
{
"dialogues": [
{
"id": "test_cutscene_skip_hold_dialogue",
"start": "cutscene_start",
"nodes": [
{
"id": "cutscene_start",
"type": "CutsceneStart",
"cutsceneId": "test_cutscene_skip_hold_01",
"next": "end_1"
},
{
"id": "end_1",
"type": "End"
}
]
},
{
"id": "test_cutscene_images_hardcut_dialogue",
"start": "cutscene_start",
"nodes": [
{
"id": "cutscene_start",
"type": "CutsceneStart",
"cutsceneId": "test_cutscene_images_hardcut_01",
"next": "end_1"
},
{
"id": "end_1",
"type": "End"
}
]
},
{
"id": "test_cutscene_images_crossfade_dialogue",
"start": "cutscene_start",
"nodes": [
{
"id": "cutscene_start",
"type": "CutsceneStart",
"cutsceneId": "test_cutscene_images_crossfade_01",
"next": "end_1"
},
{
"id": "end_1",
"type": "End"
}
]
},
{
"id": "test_cutscene_images_silent_dialogue",
"start": "cutscene_start",
"nodes": [
{
"id": "cutscene_start",
"type": "CutsceneStart",
"cutsceneId": "test_cutscene_images_silent_01",
"next": "end_1"
},
{
"id": "end_1",
"type": "End"
}
]
}
],
"cutscenes": [
{
"id": "test_cutscene_skip_hold_01",
"background": "resources/first_cutscene.png",
"skippable": true,
"durationMs": 12000,
"cameraTrack": [
{
"durationMs": 3000,
"from": { "anchor": "Center", "zoom": 1.0, "rotationDeg": 0.0 },
"to": { "anchor": "TopLeft", "zoom": 1.45, "rotationDeg": 0.0 },
"easing": "EaseInOutSine"
},
{
"durationMs": 3000,
"from": { "anchor": "TopLeft", "zoom": 1.45, "rotationDeg": 0.0 },
"to": { "anchor": "TopRight", "zoom": 1.45, "rotationDeg": 0.0 },
"easing": "EaseInOutSine"
},
{
"durationMs": 3000,
"from": { "anchor": "TopRight", "zoom": 1.45, "rotationDeg": 0.0 },
"to": { "anchor": "BottomRight", "zoom": 1.65, "rotationDeg": 0.0 },
"easing": "EaseInCubic"
},
{
"durationMs": 3000,
"from": { "anchor": "BottomRight", "zoom": 1.65, "rotationDeg": 0.0 },
"to": { "anchor": "BottomLeft", "zoom": 1.45, "rotationDeg": 0.0 },
"easing": "EaseInOutSine"
}
],
"lines": [
{
"speaker": "Narrator",
"portrait": "",
"text": "This cutscene is long enough to test hold-to-skip.",
"durationMs": 2600
},
{
"speaker": "Narrator",
"portrait": "",
"text": "A normal click must not skip it.",
"durationMs": 2600
},
{
"speaker": "Ghost",
"portrait": "resources/ghost_avatar.png",
"text": "Only the skip button with hold should work.",
"durationMs": 2600
}
]
},
{
"id": "test_cutscene_images_hardcut_01",
"background": "resources/first_cutscene.png",
"skippable": true,
"durationMs": 9000,
"cameraTrack": [
{
"durationMs": 4500,
"from": { "anchor": "Center", "zoom": 1.0, "rotationDeg": 0.0 },
"to": { "anchor": "TopLeft", "zoom": 1.35, "rotationDeg": 0.0 },
"easing": "EaseInOutSine"
},
{
"durationMs": 4500,
"from": { "anchor": "TopLeft", "zoom": 1.35, "rotationDeg": 0.0 },
"to": { "anchor": "BottomRight", "zoom": 1.55, "rotationDeg": 0.0 },
"easing": "EaseInOutSine"
}
],
"images": [
{
"path": "resources/first_cutscene.png",
"startMs": 0,
"endMs": 4500,
"fadeInMs": 0,
"fadeOutMs": 0
},
{
"path": "resources/second_cutscene.png",
"startMs": 4500,
"endMs": 9000,
"fadeInMs": 0,
"fadeOutMs": 0
}
],
"lines": [
{
"speaker": "Narrator",
"portrait": "",
"text": "First image should switch sharply to the second one.",
"durationMs": 2800
},
{
"speaker": "Narrator",
"portrait": "",
"text": "No fade should be visible here.",
"durationMs": 2800
}
]
},
{
"id": "test_cutscene_images_crossfade_01",
"background": "resources/first_cutscene.png",
"skippable": true,
"durationMs": 10000,
"cameraTrack": [
{
"durationMs": 2500,
"from": { "anchor": "Center", "zoom": 1.0, "rotationDeg": 0.0 },
"to": { "anchor": "Custom", "centerX": 0.35, "centerY": 0.30, "zoom": 1.45, "rotationDeg": 0.0 },
"easing": "EaseInOutQuad"
},
{
"durationMs": 2500,
"from": { "anchor": "Custom", "centerX": 0.35, "centerY": 0.30, "zoom": 1.45, "rotationDeg": 0.0 },
"to": { "anchor": "Custom", "centerX": 0.70, "centerY": 0.32, "zoom": 1.45, "rotationDeg": 0.0 },
"easing": "EaseOutCubic"
},
{
"durationMs": 2500,
"from": { "anchor": "Custom", "centerX": 0.70, "centerY": 0.32, "zoom": 1.45, "rotationDeg": 0.0 },
"to": { "anchor": "BottomRight", "zoom": 1.70, "rotationDeg": 0.0 },
"easing": "EaseInCubic"
},
{
"durationMs": 2500,
"from": { "anchor": "BottomRight", "zoom": 1.70, "rotationDeg": 0.0 },
"to": { "anchor": "BottomLeft", "zoom": 1.55, "rotationDeg": 0.0 },
"easing": "EaseInOutSine"
}
],
"images": [
{
"path": "resources/first_cutscene.png",
"startMs": 0,
"endMs": 6000,
"fadeInMs": 0,
"fadeOutMs": 0
},
{
"path": "resources/second_cutscene.png",
"startMs": 4500,
"endMs": 10000,
"fadeInMs": 1500,
"fadeOutMs": 0
}
],
"lines": [
{
"speaker": "Narrator",
"portrait": "",
"text": "The second image should fade over the first one.",
"durationMs": 2600
},
{
"speaker": "Ghost",
"portrait": "resources/ghost_avatar.png",
"text": "This test checks overlap and alpha blending.",
"durationMs": 2600
}
]
},
{
"id": "test_cutscene_images_silent_01",
"background": "resources/first_cutscene.png",
"skippable": true,
"durationMs": 11000,
"cameraTrack": [
{
"durationMs": 2500,
"from": { "anchor": "Center", "zoom": 1.0, "rotationDeg": 0.0 },
"to": { "anchor": "TopLeft", "zoom": 1.35, "rotationDeg": 0.0 },
"easing": "EaseInOutSine"
},
{
"durationMs": 3000,
"from": { "anchor": "TopLeft", "zoom": 1.35, "rotationDeg": 0.0 },
"to": { "anchor": "TopRight", "zoom": 1.35, "rotationDeg": 0.0 },
"easing": "EaseInOutSine"
},
{
"durationMs": 3000,
"from": { "anchor": "TopRight", "zoom": 1.35, "rotationDeg": 0.0 },
"to": { "anchor": "BottomRight", "zoom": 1.55, "rotationDeg": 0.0 },
"easing": "EaseOutCubic"
},
{
"durationMs": 2500,
"from": { "anchor": "BottomRight", "zoom": 1.55, "rotationDeg": 0.0 },
"to": { "anchor": "BottomLeft", "zoom": 1.45, "rotationDeg": 0.0 },
"easing": "EaseInOutQuad"
}
],
"images": [
{
"path": "resources/first_cutscene.png",
"startMs": 0,
"endMs": 3500,
"fadeInMs": 0,
"fadeOutMs": 800
},
{
"path": "resources/second_cutscene.png",
"startMs": 3000,
"endMs": 7500,
"fadeInMs": 800,
"fadeOutMs": 1000
},
{
"path": "resources/loading.png",
"startMs": 7000,
"endMs": 11000,
"fadeInMs": 1000,
"fadeOutMs": 0
}
],
"lines": []
}
]
}
Binary file not shown.
Binary file not shown.
-475
View File
@@ -1,475 +0,0 @@
{
"dialogues": [
{
"id": "dialog_student",
"start": "line_1",
"nodes": [
{
"id": "line_1",
"type": "Line",
"speaker": "Студент",
"portrait": "resources/w/avatar_unknown.png",
"text": "В университете завелись призраки, мне страшно ходить на занятия.",
"next": "line_2"
},
{
"id": "line_2",
"type": "Line",
"speaker": "Hero",
"portrait": "resources/w/gg/gg2_s_podsvetkoy5.png",
"text": "Можешь рассказать подробнее?",
"next": "line_3"
},
{
"id": "line_3",
"type": "Line",
"speaker": "Студент",
"portrait": "resources/w/avatar_unknown.png",
"text": "Спроси у Мухтара байке, он все знает.",
"next": "line_4"
},
{
"id": "line_4",
"type": "Line",
"speaker": "Hero",
"portrait": "resources/w/gg/gg2_s_podsvetkoy5.png",
"text": "Хорошо.",
"next": "end_1"
},
{
"id": "end_1",
"type": "End"
}
]
},
{
"id": "dialog_mukhtar",
"start": "line_1",
"nodes": [
{
"id": "line_1",
"type": "Line",
"speaker": "Мухтар байке",
"portrait": "resources/w/avatar_unknown.png",
"text": "Здравствуй, мы давно тебя ждем! Ты поможешь нам избавиться от призраков?",
"next": "line_2"
},
{
"id": "line_2",
"type": "Line",
"speaker": "Hero",
"portrait": "resources/w/gg/gg2_s_podsvetkoy5.png",
"text": "Где их найти?",
"next": "line_3"
},
{
"id": "line_3",
"type": "Line",
"speaker": "Мухтар байке",
"portrait": "resources/w/avatar_unknown.png",
"text": "Заходи в здание универа и поднимайся на второй этаж.",
"next": "line_4"
},
{
"id": "line_4",
"type": "Line",
"speaker": "Мухтар байке",
"portrait": "resources/w/avatar_unknown.png",
"text": "Ты их встретишь прямо там.",
"next": "line_5"
},
{
"id": "line_4",
"type": "Line",
"speaker": "Hero",
"portrait": "resources/w/gg/gg2_s_podsvetkoy5.png",
"text": "Хорошо, я скоро вернусь!",
"next": "end_1"
},
{
"id": "end_1",
"type": "End"
}
]
},
{
"id": "dialog_female_student",
"start": "line_1",
"nodes": [
{
"id": "line_1",
"type": "Line",
"speaker": "Студентка",
"portrait": "resources/w/avatar_unknown.png",
"text": "С этими призраками совсем невозможно ходить на лекции!",
"next": "end_1"
},
{
"id": "end_1",
"type": "End"
}
]
},
{
"id": "test_line_dialogue",
"start": "line_1",
"nodes": [
{
"id": "line_1",
"type": "Line",
"speaker": "Ghost",
"portrait": "resources/ghost_avatar.png",
"text": "Наконец-то ты пришел.",
"next": "line_2"
},
{
"id": "line_2",
"type": "Line",
"speaker": "Hero",
"portrait": "resources/w/gg/gg2_s_podsvetkoy5.png",
"text": "Ты сделан из дыма?",
"next": "line_3"
},
{
"id": "line_3",
"type": "Line",
"speaker": "Ghost",
"portrait": "resources/ghost_avatar.png",
"text": "Ты думаешь, это смешно?",
"next": "line_4"
},
{
"id": "line_4",
"type": "Line",
"speaker": "Hero",
"portrait": "resources/w/gg/gg2_s_podsvetkoy5.png",
"text": "Я думаю что ты пахнешь как выхлоп от Камаза.",
"next": "end_1"
},
{
"id": "end_1",
"type": "End"
}
]
},
{
"id": "ghost_choice_dialogue",
"start": "line_1",
"nodes": [
{
"id": "line_1",
"type": "Line",
"speaker": "Беспокойный Призрак",
"portrait": "resources/ghost_avatar.png",
"text": "Нечасто я вижу смертных, готовых разговаривать со мной.",
"next": "choice_1"
},
{
"id": "choice_1",
"type": "Choice",
"speaker": "Hero",
"portrait": "resources/w/gg/gg2_s_podsvetkoy5.png",
"text": "",
"choices": [
{
"id": "main_1",
"kind": "Main",
"text": "Не мешай студентам учиться!",
"next": "line_goods"
},
{
"id": "optional_1",
"kind": "Optional",
"text": "Почему ты появился здесь?",
"next": "line_who"
}
]
},
{
"id": "line_goods",
"type": "Line",
"speaker": "Беспокойный Призрак",
"portrait": "resources/ghost_avatar.png",
"text": "Это моя месть студентам за то что они призвали меня.",
"next": "end_1"
},
{
"id": "line_who",
"type": "Line",
"speaker": "Беспокойный Призрак",
"portrait": "resources/ghost_avatar.png",
"text": "Группа студентов совершила ритуал и призвала меня сюда. Пока проклятие не спадет, я всегда буду здесь обитать.",
"next": "choice_1"
},
{
"id": "end_1",
"type": "End"
}
]
},
{
"id": "test_condition_dialogue",
"start": "set_flag_1",
"nodes": [
{
"id": "set_flag_1",
"type": "SetFlag",
"effects": [
{ "flag": "met_ghost", "value": 1 }
],
"next": "condition_1"
},
{
"id": "condition_1",
"type": "Condition",
"conditions": [
{ "flag": "met_ghost", "op": "Equals", "value": 1 }
],
"trueNext": "line_true",
"falseNext": "line_false"
},
{
"id": "line_true",
"type": "Line",
"speaker": "Ghost",
"portrait": "resources/ghost_avatar.png",
"text": "Now you know who I am.",
"next": "end_1"
},
{
"id": "line_false",
"type": "Line",
"speaker": "Ghost",
"portrait": "resources/ghost_avatar.png",
"text": "You should not hear this line.",
"next": "end_1"
},
{
"id": "end_1",
"type": "End"
}
]
},
{
"id": "test_cutscene_dialogue",
"start": "cutscene_start",
"nodes": [
{
"id": "cutscene_start",
"type": "CutsceneStart",
"cutsceneId": "test_cutscene_01",
"next": "end_1"
},
{
"id": "end_1",
"type": "End"
}
]
},
{
"id": "test_silent_cutscene_dialogue",
"start": "cutscene_start",
"nodes": [
{
"id": "cutscene_start",
"type": "CutsceneStart",
"cutsceneId": "test_cutscene_silent_01",
"next": "end_1"
},
{
"id": "end_1",
"type": "End"
}
]
},
{
"id": "test_cutscene_pan_dialogue",
"start": "cutscene_start",
"nodes": [
{
"id": "cutscene_start",
"type": "CutsceneStart",
"cutsceneId": "test_cutscene_pan_01",
"next": "end_1"
},
{
"id": "end_1",
"type": "End"
}
]
},
{
"id": "test_cutscene_pan_dialogue_silent",
"start": "cutscene_start",
"nodes": [
{
"id": "cutscene_start",
"type": "CutsceneStart",
"cutsceneId": "test_cutscene_pan_02",
"next": "end_1"
},
{
"id": "end_1",
"type": "End"
}
]
}
],
"cutscenes": [
{
"id": "test_cutscene_01",
"background": "resources/first_cutscene.png",
"durationMs": 6800,
"cameraTrack": [
{
"durationMs": 2400,
"from": { "focusX": 0.50, "focusY": 0.55, "zoom": 1.00, "rotationDeg": 0.0 },
"to": { "focusX": 0.63, "focusY": 0.58, "zoom": 1.16, "rotationDeg": -1.0 },
"easing": "EaseInOutSine"
},
{
"durationMs": 2200,
"from": { "focusX": 0.63, "focusY": 0.58, "zoom": 1.16, "rotationDeg": -1.0 },
"to": { "focusX": 0.74, "focusY": 0.52, "zoom": 1.30, "rotationDeg": -2.4 },
"easing": "EaseInOutCubic"
},
{
"durationMs": 2200,
"from": { "focusX": 0.74, "focusY": 0.52, "zoom": 1.30, "rotationDeg": -2.4 },
"to": { "focusX": 0.58, "focusY": 0.46, "zoom": 1.10, "rotationDeg": -0.6 },
"easing": "EaseOutSine"
}
],
"lines": [
{
"speaker": "Narrator",
"portrait": "resources/hero.png",
"text": "The air in the room turned cold.",
"durationMs": 2200
},
{
"speaker": "Ghost",
"portrait": "resources/ghost_avatar.png",
"text": "Some memories never fade.",
"durationMs": 2600,
"background": "resources/loading.png"
}
]
},
{
"id": "test_cutscene_silent_01",
"background": "resources/first_cutscene.png",
"durationMs": 5200,
"cameraTrack": [
{
"durationMs": 2600,
"from": { "focusX": 0.40, "focusY": 0.54, "zoom": 1.00, "rotationDeg": 0.0 },
"to": { "focusX": 0.58, "focusY": 0.54, "zoom": 1.22, "rotationDeg": 0.8 },
"easing": "EaseInOutSine"
},
{
"durationMs": 2600,
"from": { "focusX": 0.58, "focusY": 0.54, "zoom": 1.22, "rotationDeg": 0.8 },
"to": { "focusX": 0.72, "focusY": 0.48, "zoom": 1.34, "rotationDeg": -0.5 },
"easing": "EaseOutCubic"
}
],
"lines": []
},
{
"id": "test_cutscene_pan_01",
"background": "resources/first_cutscene.png",
"durationMs": 12000,
"cameraTrack": [
{
"durationMs": 1200,
"from": { "anchor": "Center", "zoom": 1.00, "rotationDeg": 0.0 },
"to": { "anchor": "Center", "zoom": 1.00, "rotationDeg": 0.0 },
"easing": "Linear"
},
{
"durationMs": 2500,
"from": { "anchor": "Center", "zoom": 1.00, "rotationDeg": 0.0 },
"to": { "anchor": "TopLeft", "zoom": 1.55, "rotationDeg": 0.0 },
"easing": "EaseInOutSine"
},
{
"durationMs": 2600,
"from": { "anchor": "TopLeft", "zoom": 1.55, "rotationDeg": 0.0 },
"to": { "anchor": "TopRight", "zoom": 1.55, "rotationDeg": 0.0 },
"easing": "EaseInOutSine"
},
{
"durationMs": 1800,
"from": { "anchor": "TopRight", "zoom": 1.55, "rotationDeg": 0.0 },
"to": { "anchor": "BottomRight", "zoom": 1.72, "rotationDeg": 0.0 },
"easing": "EaseInCubic"
},
{
"durationMs": 3900,
"from": { "anchor": "BottomRight", "zoom": 1.72, "rotationDeg": 0.0 },
"to": { "anchor": "BottomLeft", "zoom": 1.55, "rotationDeg": 0.0 },
"easing": "EaseInOutSine"
}
],
"lines": [
{
"speaker": "Narrator",
"portrait": "resources/hero.png",
"text": "The memory begins in silence.",
"durationMs": 2200
},
{
"speaker": "Narrator",
"portrait": "resources/hero.png",
"text": "Something is drawing your eyes across the whole scene.",
"durationMs": 2800
},
{
"speaker": "Ghost",
"portrait": "resources/ghost_avatar.png",
"text": "Do not look away.",
"durationMs": 2400
}
]
},
{
"id": "test_cutscene_pan_02",
"background": "resources/first_cutscene.png",
"durationMs": 12000,
"cameraTrack": [
{
"durationMs": 1200,
"from": { "anchor": "Center", "zoom": 1.00, "rotationDeg": 0.0 },
"to": { "anchor": "Center", "zoom": 1.00, "rotationDeg": 0.0 },
"easing": "Linear"
},
{
"durationMs": 2500,
"from": { "anchor": "Center", "zoom": 1.00, "rotationDeg": 0.0 },
"to": { "anchor": "TopLeft", "zoom": 1.55, "rotationDeg": 0.0 },
"easing": "EaseInOutSine"
},
{
"durationMs": 2600,
"from": { "anchor": "TopLeft", "zoom": 1.55, "rotationDeg": 0.0 },
"to": { "anchor": "TopRight", "zoom": 1.55, "rotationDeg": 0.0 },
"easing": "EaseInOutSine"
},
{
"durationMs": 1800,
"from": { "anchor": "TopRight", "zoom": 1.55, "rotationDeg": 0.0 },
"to": { "anchor": "BottomRight", "zoom": 1.72, "rotationDeg": 0.0 },
"easing": "EaseInCubic"
},
{
"durationMs": 3900,
"from": { "anchor": "BottomRight", "zoom": 1.72, "rotationDeg": 0.0 },
"to": { "anchor": "BottomLeft", "zoom": 1.55, "rotationDeg": 0.0 },
"easing": "EaseInOutSine"
}
],
"lines": []
}
]
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
-90
View File
@@ -1,90 +0,0 @@
{
"quests": [
{
"id": "tutorial_open_journal",
"title": "Журнал заданий",
"category": "Tutorial",
"status": "Completed",
"recommendedLevel": 0,
"description": "Теперь у героя есть журнал заданий. В нём можно просматривать текущие поручения, цели и подробные записи о событиях, которые уже произошли в мире игры.",
"objectives": [
{ "id": "open_journal", "text": "Открыть журнал заданий", "completed": true }
]
},
{
"id": "side_lost_bag",
"title": "Потерянная сумка",
"category": "Side",
"status": "Completed",
"recommendedLevel": 1,
"description": "Путник потерял сумку недалеко от лесной тропы. Внутри оказались вещи, важные для его дальнейшего пути. Возвращение сумки укрепило доверие местных жителей к герою.",
"objectives": [
{ "id": "find_bag", "text": "Найти сумку у лесной тропы", "completed": true },
{ "id": "bring_bag", "text": "Вернуть сумку путнику", "completed": true }
]
},
{
"id": "failed_missing_courier",
"title": "След курьера",
"category": "Side",
"status": "Failed",
"recommendedLevel": 2,
"description": "Курьер исчез до того, как герой смог выйти на его след. Местные говорят, что на дороге остались только следы копыт и обрывок ремня. Возможность узнать, кто забрал посылку, была упущена.",
"objectives": [
{ "id": "ask_innkeeper", "text": "Расспросить трактирщика", "completed": true },
{ "id": "find_courier", "text": "Найти курьера до наступления ночи", "completed": false }
]
},
{
"id": "contract_old_well",
"title": "Старый колодец",
"category": "Contract",
"status": "Active",
"recommendedLevel": 2,
"description": "На окраине деревни стоит старый колодец. Ночью из него слышны голоса, а утром вокруг находят мокрые следы. Местные боятся подходить к нему, но староста считает, что причина происходящего связана с давним проклятием.",
"objectives": [
{ "id": "inspect_well", "text": "Осмотреть старый колодец", "completed": false },
{ "id": "search_tracks", "text": "Найти следы возле каменной ограды", "completed": false },
{ "id": "report_elder", "text": "Вернуться к старосте", "completed": false }
]
},
{
"id": "main_find_ghost",
"title": "Следы призрака",
"category": "Main",
"status": "Active",
"recommendedLevel": 1,
"description": "В заброшенной части деревни появился странный призрак. Местные жители говорят, что он появляется только ночью и исчезает возле старого колодца. Похоже, дух пытается привести кого-то к месту, где много лет назад случилась трагедия.",
"objectives": [
{ "id": "talk_to_ghost", "text": "Поговорить с призраком возле старого дома", "completed": false },
{ "id": "inspect_well", "text": "Осмотреть старый колодец", "completed": false },
{ "id": "return_to_elder", "text": "Вернуться к старосте", "completed": false }
]
},
{
"id": "main_black_letter",
"title": "Письмо с чёрной печатью",
"category": "Main",
"status": "Active",
"recommendedLevel": 3,
"description": "В руки героя попало письмо с чёрной восковой печатью. Имя отправителя стёрто, но на бумаге остался запах дыма и дорогих трав. Письмо упоминает встречу у северных ворот и человека, который знает правду о призраке.",
"objectives": [
{ "id": "read_letter", "text": "Прочитать письмо", "completed": true },
{ "id": "go_north_gate", "text": "Добраться до северных ворот", "completed": false },
{ "id": "find_contact", "text": "Найти человека с серебряным кольцом", "completed": false }
]
},
{
"id": "side_herbalist_help",
"title": "Травы для лекаря",
"category": "Side",
"status": "Available",
"recommendedLevel": 1,
"description": "Лекарю нужны редкие болотные травы, чтобы приготовить настой для больных детей. Он предупредил, что растение раскрывается только на рассвете, а рядом часто появляются дикие звери.",
"objectives": [
{ "id": "collect_herbs", "text": "Собрать болотные травы на рассвете", "completed": false },
{ "id": "bring_herbs", "text": "Отнести травы лекарю", "completed": false }
]
}
]
}
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
@@ -1,11 +0,0 @@
//precision mediump float;
uniform sampler2D Texture;
uniform float uAlpha;
varying vec2 texCoord;
void main()
{
vec4 color = texture2D(Texture, texCoord).rgba;
color.a *= uAlpha;
gl_FragColor = color;
}
@@ -1,11 +0,0 @@
precision mediump float;
uniform sampler2D Texture;
uniform float uAlpha;
varying vec2 texCoord;
void main()
{
vec4 color = texture2D(Texture, texCoord).rgba;
color.a *= uAlpha;
gl_FragColor = color;
}
@@ -1,125 +0,0 @@
//precisionhighp float;
// Фрагментный шейдер:
uniform vec3 uColor;
uniform float uDistanceToPlanetSurface; // Расстояние корабля до поверхности
// Константы затухания, определенные прямо в шейдере
const float DIST_FOG_MAX = 2000.0;
const float DIST_FOG_MIN = 1000.0;
varying vec3 vWorldNormal;
varying vec3 vViewNormal;
varying vec3 vViewPosition;
// Добавь эти uniform-ы
//uniform float uTime;
uniform vec3 uLightDirView;
uniform vec3 uPlayerDirWorld;
// Простая функция псевдослучайного шума
float hash(float n) { return fract(sin(n) * 43758.5453123); }
float noise(vec3 x) {
vec3 p = floor(x);
vec3 f = fract(x);
f = f * f * (3.0 - 2.0 * f);
float n = p.x + p.y * 57.0 + 113.0 * p.z;
return mix(mix(mix(hash(n + 0.0), hash(n + 1.0), f.x),
mix(hash(n + 57.0), hash(n + 58.0), f.x), f.y),
mix(mix(hash(n + 113.0), hash(n + 114.0), f.x),
mix(hash(n + 170.0), hash(n + 171.0), f.x), f.y), f.z);
}
// Fractal Brownian Motion для "кучевости" облаков
float fbm(vec3 p) {
float f = 0.5000 * noise(p); p *= 2.02;
f += 0.2500 * noise(p); p *= 2.03;
f += 0.1250 * noise(p);
return f;
}
void main()
{
//gl_FragColor = vec4(1.0, 0.0, 0.0, 0.5);
vec3 normal = normalize(vViewNormal);
vec3 viewVector = normalize(-vViewPosition);
float NdotV = dot(normal, viewVector);
// Вектор направления от центра планеты к текущему фрагменту атмосферы (Мировой)
vec3 fragmentDir = normalize(vWorldNormal);
// Вектор направления от центра планеты к игроку (нужно передать как uniform)
// Передай его в C++: renderer.RenderUniform3fv("uPlayerDirWorld", playerDirWorld.data());
// --- 1. Плавное отсечение за горизонтом (Horizon Mask) ---
// Считаем косинус угла между игроком и точкой атмосферы
float cosAngle = dot(fragmentDir, uPlayerDirWorld);
// Плавно затухаем от 0.0 (горизонт) до 0.2 (над головой)
//float horizonMask = smoothstep(0.0, 0.4, cosAngle);
float horizonMask = smoothstep(0.93, 1.0, cosAngle);
// --- 2. Плавный переход при прохождении сквозь слой (Transition Mask) ---
// Определяем "высоту" игрока относительно слоя (напр. слой на 1.03 * R)
// uDistanceToPlanetSurface уже вычислен в PlanetData
float layerHeight = 600.0; // Примерная толщина слоя атмосферы (3% от 20000)
// Делаем прозрачным при приближении к границе (dist около layerHeight)
// и снова видимым, когда спустились ниже
float distToLayer = abs(uDistanceToPlanetSurface - layerHeight);
float transitionMask = smoothstep(0.0, 200.0, distToLayer);
// --- 3. Освещение и облака ---
float diff = max(dot(normal, uLightDirView), 0.0);
if (uDistanceToPlanetSurface < layerHeight) {
diff = max(dot(-normal, uLightDirView), 0.0); // Инверсия для взгляда снизу
}
float lightIntensity = diff + 0.05;
vec3 cloudCoord = fragmentDir * 6.0;
//cloudCoord.x += uTime * 0.03;
float n = fbm(cloudCoord);
float cloudMask = smoothstep(0.4, 0.65, n);
// --- 4. Финальный расчет альфы ---
float atmosphereDensity = pow(1.0 - abs(NdotV), 5.0);
float distanceFactor = clamp((uDistanceToPlanetSurface - DIST_FOG_MIN) / (DIST_FOG_MAX - DIST_FOG_MIN), 0.0, 1.0);
// Базовая прозрачность облаков
float cloudAlpha = cloudMask * 0.8;
// Применяем маски:
// В космосе важен distanceFactor, на планете важен horizonMask
float finalCloudAlpha = cloudAlpha * transitionMask;
if (uDistanceToPlanetSurface < layerHeight) {
finalCloudAlpha *= horizonMask; // На планете скрываем то, что под ногами
} else {
finalCloudAlpha *= distanceFactor; // В космосе скрываем по вашей старой логике
if (NdotV <=0.0)
{
//finalCloudAlpha = 0.0;
discard;
}
}
vec3 currentAtmo = mix(vec3(0.01, 0.02, 0.1), uColor, lightIntensity);
vec3 currentCloud = mix(vec3(0.1, 0.1, 0.15), vec3(1.0, 1.0, 1.0), lightIntensity);
vec3 finalRGB = mix(currentAtmo, currentCloud, cloudMask);
//vec3 finalRGB = currentAtmo;
// Для дымки (atmo) оставляем затухание при посадке
float atmoAlpha = atmosphereDensity * distanceFactor;
//float atmoAlpha = distanceFactor;
float finalAtmoAlpha = atmoAlpha;
if (uDistanceToPlanetSurface < layerHeight) {
finalCloudAlpha *= horizonMask;
finalAtmoAlpha *= horizonMask; // Принудительно гасим дымку под горизонтом
}
gl_FragColor = vec4(finalRGB, max(finalAtmoAlpha, finalCloudAlpha));
}
@@ -1,7 +0,0 @@
//precisionmediump float;
varying vec3 color;
void main()
{
gl_FragColor = vec4(color, 1.0);
}
+3 -1
View File
@@ -3,5 +3,7 @@ varying vec3 color;
void main()
{
gl_FragColor = vec4(color, 1.0);
//gl_FragColor = vec4(color, 1.0);
gl_FragColor = vec4(1.0, 1.0, 0.5, 1.0);
}
@@ -1,12 +0,0 @@
//precisionmediump float;
uniform sampler2D Texture;
varying vec2 texCoord;
void main()
{
vec4 color = texture2D(Texture,texCoord).rgba;
//gl_FragColor = vec4(color.rgb*0.1 + vec3(0.9, 0.9, 0.9), 1.0);
gl_FragColor = color;
}
@@ -1,8 +0,0 @@
//precisionmediump float;
uniform samplerCube Texture;
varying vec3 dir;
void main(){
gl_FragColor = textureCube(Texture, normalize(dir));
}
-21
View File
@@ -1,21 +0,0 @@
#version 120
attribute vec3 vPosition;
attribute vec2 vTexCoord;
attribute vec3 vNormal;
varying vec2 texCoord;
varying vec4 fragPosLightSpace;
varying vec3 fragNormal;
uniform mat4 ProjectionModelViewMatrix;
uniform mat4 ModelViewMatrix;
uniform mat4 uLightFromCamera;
void main()
{
gl_Position = ProjectionModelViewMatrix * vec4(vPosition, 1.0);
texCoord = vTexCoord;
fragPosLightSpace = uLightFromCamera * ModelViewMatrix * vec4(vPosition, 1.0);
fragNormal = mat3(ModelViewMatrix) * vNormal;
}
@@ -1,70 +0,0 @@
uniform sampler2D Texture;
uniform sampler2D uShadowMap;
uniform vec3 uLightDir;
varying vec2 texCoord;
varying vec4 fragPosLightSpace;
varying vec3 fragNormal;
float computeShadow(vec4 lightSpacePos, vec3 normal)
{
vec3 projCoords = lightSpacePos.xyz / lightSpacePos.w;
projCoords = projCoords * 0.5 + 0.5;
if (projCoords.x < 0.0 || projCoords.x > 1.0 ||
projCoords.y < 0.0 || projCoords.y > 1.0 ||
projCoords.z > 1.0)
{
return 0.0;
}
float currentDepth = projCoords.z;
// Slope-dependent bias: large for grazing angles, tiny for surfaces facing the light
float bias = 0.0004;
if (dot(normal, normal) > 0.001)
{
float cosTheta = dot(normalize(normal), -normalize(uLightDir));
bias = max(0.002 * (1.0 - cosTheta), 0.0002);
}
// 3x3 PCF (percentage-closer filtering) for softer shadows
float shadow = 0.0;
float texelSize = 1.0 / 2048.0;
for (int x = -1; x <= 1; x++)
{
for (int y = -1; y <= 1; y++)
{
float pcfDepth = texture2D(uShadowMap, projCoords.xy + vec2(float(x), float(y)) * texelSize).r;
shadow += currentDepth - bias > pcfDepth ? 1.0 : 0.0;
}
}
shadow /= 9.0;
return shadow;
}
void main()
{
vec4 color = texture2D(Texture, texCoord);
float ambient = 0.4;
float diffuseStrength = 0.6;
// Compute diffuse term; if normals are missing (zero-length) treat as fully lit
float diffuse = 1.0;
vec3 n = fragNormal;
if (dot(n, n) > 0.001)
{
n = normalize(n);
diffuse = max(dot(n, -normalize(uLightDir)), 0.0);
}
float shadow = computeShadow(fragPosLightSpace, fragNormal);
// Shadow dims only the diffuse contribution; ambient is always present
float lighting = ambient + diffuseStrength * diffuse * (1.0 - shadow);
color.rgb *= lighting;
gl_FragColor = color;
}
@@ -1,68 +0,0 @@
precision mediump float;
uniform sampler2D Texture;
uniform sampler2D uShadowMap;
uniform vec3 uLightDir;
varying vec2 texCoord;
varying vec4 fragPosLightSpace;
varying vec3 fragNormal;
float computeShadow(vec4 lightSpacePos, vec3 normal)
{
vec3 projCoords = lightSpacePos.xyz / lightSpacePos.w;
projCoords = projCoords * 0.5 + 0.5;
if (projCoords.x < 0.0 || projCoords.x > 1.0 ||
projCoords.y < 0.0 || projCoords.y > 1.0 ||
projCoords.z > 1.0)
{
return 0.0;
}
float currentDepth = projCoords.z;
float bias = 0.0004;
if (dot(normal, normal) > 0.001)
{
float cosTheta = dot(normalize(normal), -normalize(uLightDir));
bias = max(0.002 * (1.0 - cosTheta), 0.0002);
}
float shadow = 0.0;
float texelSize = 1.0 / 2048.0;
for (int x = -1; x <= 1; x++)
{
for (int y = -1; y <= 1; y++)
{
float pcfDepth = texture2D(uShadowMap, projCoords.xy + vec2(float(x), float(y)) * texelSize).r;
shadow += currentDepth - bias > pcfDepth ? 1.0 : 0.0;
}
}
shadow /= 9.0;
return shadow;
}
void main()
{
vec4 color = texture2D(Texture, texCoord);
float ambient = 0.4;
float diffuseStrength = 0.6;
float diffuse = 1.0;
vec3 n = fragNormal;
if (dot(n, n) > 0.001)
{
n = normalize(n);
diffuse = max(dot(n, -normalize(uLightDir)), 0.0);
}
float shadow = computeShadow(fragPosLightSpace, fragNormal);
float lighting = ambient + diffuseStrength * diffuse * (1.0 - shadow);
color.rgb *= lighting;
gl_FragColor = color;
}
@@ -1,9 +0,0 @@
//precisionmediump float;
uniform sampler2D Texture;
varying vec2 texCoord;
void main()
{
vec4 color = texture2D(Texture,texCoord).rgba;
gl_FragColor = color;
}
@@ -1,37 +0,0 @@
//precisionmediump float;
uniform samplerCube Texture;
uniform float skyPercent;
uniform float uPlayerLightFactor; // Глобальный фактор дня/ночи для позиции игрока
uniform vec3 uSkyColor;
varying vec3 vViewDir;
void main() {
vec3 viewDir = normalize(vViewDir);
// 1. Получаем звезды
vec4 starsColor = textureCube(Texture, viewDir);
// 2. Цвета атмосферы
vec3 dayColor = uSkyColor;
vec3 nightColor = vec3(0.01, 0.01, 0.04);
// 3. Теперь всё небо окрашивается одинаково в зависимости от положения игрока
// Мы плавно смешиваем ночной и дневной купол
vec3 atmosphericColor = mix(nightColor, dayColor, uPlayerLightFactor);
// 4. Логика видимости звезд
// Звезды видны в космосе (skyPercent=0)
// И в атмосфере, если сейчас ночь (uPlayerLightFactor -> 0)
float starsVisibility = (1.0 - skyPercent) + (skyPercent * (1.0 - uPlayerLightFactor));
// Делаем звезды чуть ярче на ночном небе
starsVisibility = pow(starsVisibility, 1.5);
// 5. Итоговый цвет
// Добавляем слой атмосферы к звездам
vec3 skyLayer = atmosphericColor * skyPercent;
vec3 finalColor = (starsColor.rgb * starsVisibility) + skyLayer;
gl_FragColor = vec4(finalColor, 1.0);
}
-16
View File
@@ -1,16 +0,0 @@
attribute vec3 vPosition;
attribute vec2 vTexCoord;
varying vec2 texCoord;
varying float fogDistance;
uniform mat4 ProjectionModelViewMatrix;
uniform mat4 ModelViewMatrix;
uniform vec3 uPlayerEyePos;
void main()
{
vec4 eyePos = ModelViewMatrix * vec4(vPosition.xyz, 1.0);
fogDistance = length(eyePos.xyz - uPlayerEyePos);
gl_Position = ProjectionModelViewMatrix * vec4(vPosition.xyz, 1.0);
texCoord = vTexCoord;
}
-18
View File
@@ -1,18 +0,0 @@
//precisionmediump float;
uniform sampler2D Texture;
varying vec2 texCoord;
varying float fogDistance;
void main()
{
vec4 color = texture2D(Texture,texCoord).rgba;
if(color.a < 0.1)
discard;
vec3 fogColor = vec3(0.53, 0.81, 0.92);
float fogFactor = clamp((fogDistance - 30.0) / 10.0, 0.0, 1.0);
vec3 finalColor = mix(color.rgb, fogColor, fogFactor);
gl_FragColor = vec4(finalColor, color.a);
}
-23
View File
@@ -1,23 +0,0 @@
attribute vec3 vPosition;
attribute vec2 vTexCoord;
attribute vec3 vNormal;
varying vec2 texCoord;
varying vec4 fragPosLightSpace;
varying vec3 fragNormal;
varying float fogDistance;
uniform mat4 ProjectionModelViewMatrix;
uniform mat4 ModelViewMatrix;
uniform mat4 uLightFromCamera;
uniform vec3 uPlayerEyePos;
void main()
{
vec4 eyePos = ModelViewMatrix * vec4(vPosition, 1.0);
fogDistance = length(eyePos.xyz - uPlayerEyePos);
gl_Position = ProjectionModelViewMatrix * vec4(vPosition, 1.0);
texCoord = vTexCoord;
fragPosLightSpace = uLightFromCamera * eyePos;
fragNormal = mat3(ModelViewMatrix) * vNormal;
}
@@ -1,78 +0,0 @@
uniform sampler2D Texture;
uniform sampler2D uShadowMap;
uniform vec3 uLightDir;
varying vec2 texCoord;
varying vec4 fragPosLightSpace;
varying vec3 fragNormal;
varying float fogDistance;
float computeShadow(vec4 lightSpacePos, vec3 normal)
{
vec3 projCoords = lightSpacePos.xyz / lightSpacePos.w;
projCoords = projCoords * 0.5 + 0.5;
if (projCoords.x < 0.0 || projCoords.x > 1.0 ||
projCoords.y < 0.0 || projCoords.y > 1.0 ||
projCoords.z > 1.0)
{
return 0.0;
}
float currentDepth = projCoords.z;
// Slope-dependent bias: large for grazing angles, tiny for surfaces facing the light
float bias = 0.0004;
if (dot(normal, normal) > 0.001)
{
float cosTheta = dot(normalize(normal), -normalize(uLightDir));
bias = max(0.002 * (1.0 - cosTheta), 0.0002);
}
// 3x3 PCF (percentage-closer filtering) for softer shadows
float shadow = 0.0;
float texelSize = 1.0 / 2048.0;
for (int x = -1; x <= 1; x++)
{
for (int y = -1; y <= 1; y++)
{
float pcfDepth = texture2D(uShadowMap, projCoords.xy + vec2(float(x), float(y)) * texelSize).r;
shadow += currentDepth - bias > pcfDepth ? 1.0 : 0.0;
}
}
shadow /= 9.0;
return shadow;
}
void main()
{
vec4 color = texture2D(Texture, texCoord);
if (color.a < 0.1)
discard;
float ambient = 0.4;
float diffuseStrength = 0.6;
// Compute diffuse term; if normals are missing (zero-length) treat as fully lit
float diffuse = 1.0;
vec3 n = fragNormal;
if (dot(n, n) > 0.001)
{
n = normalize(n);
diffuse = max(dot(n, -normalize(uLightDir)), 0.0);
}
float shadow = computeShadow(fragPosLightSpace, fragNormal);
// Shadow dims only the diffuse contribution; ambient is always present
float lighting = ambient + diffuseStrength * diffuse * (1.0 - shadow);
color.rgb *= lighting;
vec3 fogColor = vec3(0.53, 0.81, 0.92);
float fogFactor = clamp((fogDistance - 30.0) / 10.0, 0.0, 1.0);
color.rgb = mix(color.rgb, fogColor, fogFactor);
gl_FragColor = color;
}
-76
View File
@@ -1,76 +0,0 @@
precision mediump float;
uniform sampler2D Texture;
uniform sampler2D uShadowMap;
uniform vec3 uLightDir;
varying vec2 texCoord;
varying vec4 fragPosLightSpace;
varying vec3 fragNormal;
varying float fogDistance;
float computeShadow(vec4 lightSpacePos, vec3 normal)
{
vec3 projCoords = lightSpacePos.xyz / lightSpacePos.w;
projCoords = projCoords * 0.5 + 0.5;
if (projCoords.x < 0.0 || projCoords.x > 1.0 ||
projCoords.y < 0.0 || projCoords.y > 1.0 ||
projCoords.z > 1.0)
{
return 0.0;
}
float currentDepth = projCoords.z;
float bias = 0.0004;
if (dot(normal, normal) > 0.001)
{
float cosTheta = dot(normalize(normal), -normalize(uLightDir));
bias = max(0.002 * (1.0 - cosTheta), 0.0002);
}
float shadow = 0.0;
float texelSize = 1.0 / 2048.0;
for (int x = -1; x <= 1; x++)
{
for (int y = -1; y <= 1; y++)
{
float pcfDepth = texture2D(uShadowMap, projCoords.xy + vec2(float(x), float(y)) * texelSize).r;
shadow += currentDepth - bias > pcfDepth ? 1.0 : 0.0;
}
}
shadow /= 9.0;
return shadow;
}
void main()
{
vec4 color = texture2D(Texture, texCoord);
if (color.a < 0.1)
discard;
float ambient = 0.4;
float diffuseStrength = 0.6;
float diffuse = 1.0;
vec3 n = fragNormal;
if (dot(n, n) > 0.001)
{
n = normalize(n);
diffuse = max(dot(n, -normalize(uLightDir)), 0.0);
}
float shadow = computeShadow(fragPosLightSpace, fragNormal);
float lighting = ambient + diffuseStrength * diffuse * (1.0 - shadow);
color.rgb *= lighting;
vec3 fogColor = vec3(0.53, 0.81, 0.92);
float fogFactor = clamp((fogDistance - 30.0) / 10.0, 0.0, 1.0);
color.rgb = mix(color.rgb, fogColor, fogFactor);
gl_FragColor = color;
}
-55
View File
@@ -1,55 +0,0 @@
attribute vec3 vPosition;
attribute vec2 vTexCoord;
attribute vec4 aBoneIndices0;
attribute vec2 aBoneIndices1;
attribute vec4 aBoneWeights0;
attribute vec2 aBoneWeights1;
varying vec2 texCoord;
varying float fogDistance;
uniform mat4 ProjectionModelViewMatrix;
uniform mat4 ModelViewMatrix;
uniform mat4 uBoneMatrices[64];
uniform vec3 uPlayerEyePos;
void main()
{
vec4 skinnedPos = vec4(0.0, 0.0, 0.0, 0.0);
vec4 originalPos = vec4(vPosition, 1.0);
float totalWeight = 0.0;
if (aBoneWeights0.x > 0.0) {
skinnedPos += uBoneMatrices[int(aBoneIndices0.x)] * originalPos * aBoneWeights0.x;
totalWeight += aBoneWeights0.x;
}
if (aBoneWeights0.y > 0.0) {
skinnedPos += uBoneMatrices[int(aBoneIndices0.y)] * originalPos * aBoneWeights0.y;
totalWeight += aBoneWeights0.y;
}
if (aBoneWeights0.z > 0.0) {
skinnedPos += uBoneMatrices[int(aBoneIndices0.z)] * originalPos * aBoneWeights0.z;
totalWeight += aBoneWeights0.z;
}
if (aBoneWeights0.w > 0.0) {
skinnedPos += uBoneMatrices[int(aBoneIndices0.w)] * originalPos * aBoneWeights0.w;
totalWeight += aBoneWeights0.w;
}
if (aBoneWeights1.x > 0.0) {
skinnedPos += uBoneMatrices[int(aBoneIndices1.x)] * originalPos * aBoneWeights1.x;
totalWeight += aBoneWeights1.x;
}
if (aBoneWeights1.y > 0.0) {
skinnedPos += uBoneMatrices[int(aBoneIndices1.y)] * originalPos * aBoneWeights1.y;
totalWeight += aBoneWeights1.y;
}
if (totalWeight < 0.001) {
skinnedPos = originalPos;
}
vec4 eyePos = ModelViewMatrix * skinnedPos;
fogDistance = length(eyePos.xyz - uPlayerEyePos);
gl_Position = ProjectionModelViewMatrix * skinnedPos;
texCoord = vTexCoord;
}
@@ -1,69 +0,0 @@
attribute vec3 vPosition;
attribute vec2 vTexCoord;
attribute vec3 vNormal;
attribute vec4 aBoneIndices0;
attribute vec2 aBoneIndices1;
attribute vec4 aBoneWeights0;
attribute vec2 aBoneWeights1;
varying vec2 texCoord;
varying vec4 fragPosLightSpace;
varying vec3 fragNormal;
varying float fogDistance;
uniform mat4 ProjectionModelViewMatrix;
uniform mat4 ModelViewMatrix;
uniform mat4 uLightFromCamera;
uniform mat4 uBoneMatrices[64];
uniform vec3 uPlayerEyePos;
void main()
{
vec4 skinnedPos = vec4(0.0, 0.0, 0.0, 0.0);
vec3 skinnedNormal = vec3(0.0, 0.0, 0.0);
vec4 originalPos = vec4(vPosition, 1.0);
float totalWeight = 0.0;
if (aBoneWeights0.x > 0.0) {
skinnedPos += uBoneMatrices[int(aBoneIndices0.x)] * originalPos * aBoneWeights0.x;
skinnedNormal += mat3(uBoneMatrices[int(aBoneIndices0.x)]) * vNormal * aBoneWeights0.x;
totalWeight += aBoneWeights0.x;
}
if (aBoneWeights0.y > 0.0) {
skinnedPos += uBoneMatrices[int(aBoneIndices0.y)] * originalPos * aBoneWeights0.y;
skinnedNormal += mat3(uBoneMatrices[int(aBoneIndices0.y)]) * vNormal * aBoneWeights0.y;
totalWeight += aBoneWeights0.y;
}
if (aBoneWeights0.z > 0.0) {
skinnedPos += uBoneMatrices[int(aBoneIndices0.z)] * originalPos * aBoneWeights0.z;
skinnedNormal += mat3(uBoneMatrices[int(aBoneIndices0.z)]) * vNormal * aBoneWeights0.z;
totalWeight += aBoneWeights0.z;
}
if (aBoneWeights0.w > 0.0) {
skinnedPos += uBoneMatrices[int(aBoneIndices0.w)] * originalPos * aBoneWeights0.w;
skinnedNormal += mat3(uBoneMatrices[int(aBoneIndices0.w)]) * vNormal * aBoneWeights0.w;
totalWeight += aBoneWeights0.w;
}
if (aBoneWeights1.x > 0.0) {
skinnedPos += uBoneMatrices[int(aBoneIndices1.x)] * originalPos * aBoneWeights1.x;
skinnedNormal += mat3(uBoneMatrices[int(aBoneIndices1.x)]) * vNormal * aBoneWeights1.x;
totalWeight += aBoneWeights1.x;
}
if (aBoneWeights1.y > 0.0) {
skinnedPos += uBoneMatrices[int(aBoneIndices1.y)] * originalPos * aBoneWeights1.y;
skinnedNormal += mat3(uBoneMatrices[int(aBoneIndices1.y)]) * vNormal * aBoneWeights1.y;
totalWeight += aBoneWeights1.y;
}
if (totalWeight < 0.001) {
skinnedPos = originalPos;
skinnedNormal = vNormal;
}
vec4 eyePos = ModelViewMatrix * skinnedPos;
fogDistance = length(eyePos.xyz - uPlayerEyePos);
gl_Position = ProjectionModelViewMatrix * skinnedPos;
texCoord = vTexCoord;
fragPosLightSpace = uLightFromCamera * eyePos;
fragNormal = mat3(ModelViewMatrix) * skinnedNormal;
}
-18
View File
@@ -1,18 +0,0 @@
precision mediump float;
uniform sampler2D Texture;
varying vec2 texCoord;
varying float fogDistance;
void main()
{
vec4 color = texture2D(Texture,texCoord).rgba;
if(color.a < 0.1)
discard;
vec3 fogColor = vec3(0.53, 0.81, 0.92);
float fogFactor = clamp((fogDistance - 30.0) / 10.0, 0.0, 1.0);
vec3 finalColor = mix(color.rgb, fogColor, fogFactor);
gl_FragColor = vec4(finalColor, color.a);
}
@@ -1,14 +0,0 @@
//precisionmediump float;
varying vec2 TexCoord;
varying float vHeight;
uniform sampler2D Texture;
void main()
{
vec4 stoneColor = texture2D(Texture, TexCoord);
gl_FragColor = vec4(stoneColor.rgb, vHeight);
//gl_FragColor = vec4(vHeight, vHeight, vHeight, vHeight);
//gl_FragColor = vec4(1.0, 0.0, 1.0, 1.0);
}
+1 -1
View File
@@ -15,7 +15,7 @@ varying vec3 vWorldNormal;
uniform mat4 ProjectionModelViewMatrix;
uniform mat4 ModelViewMatrix;
uniform vec3 uViewPos;
uniform highp vec3 uViewPos;
void main() {
gl_Position = ProjectionModelViewMatrix * vec4(vPosition, 1.0);
@@ -1,116 +0,0 @@
//precisionhighp float;
varying vec2 TexCoord;
varying vec3 vViewDirTangent;
varying vec3 Color;
varying vec3 worldPosition;
varying vec3 vWorldNormal;
uniform sampler2D Texture;
uniform sampler2D BakedTexture;
uniform float uHeightScale;
uniform float uDistanceToPlanetSurface;
uniform float uCurrentZFar;
uniform vec3 uViewPos;
const vec4 FOG_COLOR = vec4(0.0, 0.5, 1.0, 1.0); // Синий туман
uniform vec3 uLightDirWorld; // Направление ЛУЧЕЙ (1, -1, -1)
uniform float uPlayerLightFactor;
uniform vec3 uPlayerDirWorld;
void main() {
// --- 1. Расчет освещения поверхности ---
// Направление НА источник света
vec3 lightToSource = normalize(-uLightDirWorld);
vec3 fragmentDir = normalize(vWorldNormal);
// Используем vNormal (нормаль в мировом пространстве, так как vPosition тоже в мировом)
// Важно: если vNormal не нормализована в вершинном, делаем это здесь
vec3 normal = normalize(fragmentDir); // На планете-сфере нормаль совпадает с направлением от центра
float diff = max(dot(normal, lightToSource), 0.0);
float ambient = 0.3; // Базовая освещенность ночной стороны
float surfaceLightIntensity = diff + ambient;
// --- 2. Динамический цвет тумана ---
// Синхронизируем туман с атмосферой: днем голубой, ночью темно-синий
vec3 dayFog = vec3(0.0, 0.5, 1.0);
vec3 nightFog = vec3(0.01, 0.01, 0.04);
vec3 dynamicFogColor = mix(nightFog, dayFog, uPlayerLightFactor);
// --- 3. Основная логика текстур (твой код) ---
vec3 viewDir = normalize(vViewDirTangent);
float height = texture2D(Texture, TexCoord).a;
vec2 p = vec2(viewDir.x, -viewDir.y) * (height * uHeightScale);
vec2 finalTexCoord = TexCoord + p;
float realDist = distance(worldPosition, uViewPos);
float textureMixFactor = clamp((2000.0 - realDist) / 500.0, 0.0, 1.0);
vec4 bakedTextureColor = texture2D(BakedTexture, finalTexCoord);
vec4 textureColor = texture2D(Texture, TexCoord);
vec4 textureFlavoredColor = vec4(textureColor.rgb * Color, 1.0);
if (bakedTextureColor.x < 0.01 && bakedTextureColor.y < 0.01 && bakedTextureColor.z < 0.01) {
textureMixFactor = 1.0;
}
vec4 finalColor = textureMixFactor * textureFlavoredColor + (1.0 - textureMixFactor) * bakedTextureColor;
// --- 4. ПРИМЕНЕНИЕ ОСВЕЩЕНИЯ ---
// Умножаем результат текстурирования на освещенность
finalColor.rgb *= surfaceLightIntensity;
// --- 5. Расчет тумана (с использованием динамического цвета) ---
float h = uDistanceToPlanetSurface;
float fogStart, fogEnd;
if (h >= 1000.0) {
gl_FragColor = vec4(finalColor.rgb, 1.0);
}
else
{
if (h > 100.0) {
// Нормализуем высоту от 100 до 1000 (t: 0 -> 1)
float t = (h - 100.0) / 900.0;
// На высоте 100 туман начинается со 100.
// К высоте 1000 он плавно "убегает" к границе 2500, где объект исчезает.
fogStart = mix(1500.0, 15000.0, t);
// Держим ширину зоны тумана в пределах 400-600 единиц
float fogRange = mix(1000.0, 6000.0, t);
fogEnd = fogStart + fogRange;
}
else if (h > 40.0) {
float t = (h - 40.0) / 60.0;
// На высоте 100 туман начинается со 100.
// К высоте 1000 он плавно "убегает" к границе 2500, где объект исчезает.
fogStart = mix(800.0, 1500.0, t);
fogEnd = mix(1000.0, 2500.0, t);
}
else if (h > 20.0) {
float t = (h - 20.0) / 20.0;
fogStart = mix(200.0, 800.0, t);
fogEnd = mix(500.0, 1000.0, t);
}
else {
// Минимумы при h = 0: start 25, end 125
float t = clamp(h / 20.0, 0.0, 1.0);
fogStart = mix(100.0, 200.0, t);
fogEnd = mix(250.0, 500.0, t);
}
float fogRange = max(fogEnd - fogStart, 1.0);
float fogFactor = clamp((realDist - fogStart) / fogRange, 0.0, 1.0);
// Смешиваем с динамическим цветом тумана
vec3 mixedColor = mix(finalColor.rgb, dynamicFogColor, fogFactor);
gl_FragColor = vec4(mixedColor, 1.0);
}
}
+1 -1
View File
@@ -11,7 +11,7 @@ uniform float uHeightScale;
uniform float uDistanceToPlanetSurface;
uniform float uCurrentZFar;
uniform highp vec3 uViewPos;
uniform vec3 uViewPos;
const vec4 FOG_COLOR = vec4(0.0, 0.5, 1.0, 1.0); // Синий туман
+3 -1
View File
@@ -14,7 +14,7 @@ varying vec3 vWorldNormal;
uniform mat4 ProjectionModelViewMatrix;
uniform mat4 ModelViewMatrix;
uniform vec3 uViewPos;
uniform highp vec3 uViewPos;
void main() {
vWorldNormal = vNormal;
@@ -23,6 +23,8 @@ void main() {
vec3 viewDirWorld = normalize(uViewPos - vPosition);
// Строим матрицу перехода из атрибутов
// Так как базис ортонормирован, TBN^-1 == TBN_transpose
vViewDirTangent = vec3(
dot(viewDirWorld, vTangent),
dot(viewDirWorld, vBinormal),
@@ -1,102 +0,0 @@
//precisionhighp float;
// planetStone фрагментный шейдер
varying vec2 TexCoord;
varying vec3 worldPosition;
varying vec3 vWorldNormal;
uniform sampler2D Texture;
uniform float uDistanceToPlanetSurface;
uniform vec3 uViewPos;
uniform vec3 uLightDirWorld;
uniform float uPlayerLightFactor;
void main()
{
// --- 1. Подготовка векторов ---
vec3 normal = normalize(vWorldNormal);
vec3 lightToSource = normalize(-uLightDirWorld);
// Вектор от центра планеты к камню (нормаль самой поверхности планеты под камнем)
// Предполагаем, что центр планеты в (0,0,0)
vec3 surfaceNormal = normalize(worldPosition);
// --- 2. Расчет Shadow Mask ---
// Проверяем, освещена ли точка планеты, на которой стоит камень
float shadowMask = clamp(dot(surfaceNormal, lightToSource) * 5.0, 0.0, 1.0);
// Коэффициент 5.0 делает переход на терминаторе более четким
// --- 3. Освещение камня ---
float diff = max(dot(normal, lightToSource), 0.0);
// Применяем shadowMask к диффузной составляющей
// Если точка на планете в тени, то прямой свет (diff) обнуляется
float ambient = 0.3;
float lightIntensity = (diff * shadowMask);
lightIntensity *= mix(0.2, 1.0, shadowMask);
lightIntensity += ambient;
// --- 4. Остальная логика (цвета и туман) ---
vec3 dayFog = vec3(0.0, 0.5, 1.0);
vec3 nightFog = vec3(0.01, 0.01, 0.04);
vec3 dynamicFogColor = mix(nightFog, dayFog, uPlayerLightFactor);
vec4 textureColor = texture2D(Texture, TexCoord);
vec3 litColor = textureColor.rgb * lightIntensity;
float realDist = distance(worldPosition, uViewPos);
float h = uDistanceToPlanetSurface;
float alphaFactor = clamp((2000.0 - realDist) / 500.0, 0.0, 1.0);
float fogStart, fogEnd;
// --- Логика расчета границ тумана ---
if (h >= 1000.0) {
gl_FragColor = vec4(litColor, alphaFactor);
}
else
{
if (h > 100.0) {
// Нормализуем высоту от 100 до 1000 (t: 0 -> 1)
float t = (h - 100.0) / 900.0;
// На высоте 100 туман начинается со 100.
// К высоте 1000 он плавно "убегает" к границе 2500, где объект исчезает.
fogStart = mix(1500.0, 15000.0, t);
// Держим ширину зоны тумана в пределах 400-600 единиц
float fogRange = mix(1000.0, 6000.0, t);
fogEnd = fogStart + fogRange;
}
else if (h > 40.0) {
float t = (h - 40.0) / 60.0;
// На высоте 100 туман начинается со 100.
// К высоте 1000 он плавно "убегает" к границе 2500, где объект исчезает.
fogStart = mix(800.0, 1500.0, t);
fogEnd = mix(1000.0, 2500.0, t);
}
else if (h > 20.0) {
float t = (h - 20.0) / 20.0;
fogStart = mix(200.0, 800.0, t);
fogEnd = mix(500.0, 1000.0, t);
}
else {
// Минимумы при h = 0: start 25, end 125
float t = clamp(h / 20.0, 0.0, 1.0);
fogStart = mix(100.0, 200.0, t);
fogEnd = mix(250.0, 500.0, t);
}
// Расчет фактора тумана
float fogRange = max(fogEnd - fogStart, 1.0);
float fogFactor = clamp((realDist - fogStart) / fogRange, 0.0, 1.0);
// Смешивание освещенного камня с динамическим туманом
vec3 mixedColor = mix(litColor, dynamicFogColor, fogFactor);
gl_FragColor = vec4(mixedColor, alphaFactor);
}
}
-9
View File
@@ -1,9 +0,0 @@
attribute vec3 vPosition;
attribute vec2 vTexCoord;
uniform mat4 ProjectionModelViewMatrix;
void main()
{
gl_Position = ProjectionModelViewMatrix * vec4(vPosition, 1.0);
}
@@ -1,5 +0,0 @@
void main()
{
// Depth is written automatically by the GPU.
// Nothing to do here.
}
@@ -1,47 +0,0 @@
attribute vec3 vPosition;
attribute vec2 vTexCoord;
attribute vec4 aBoneIndices0;
attribute vec2 aBoneIndices1;
attribute vec4 aBoneWeights0;
attribute vec2 aBoneWeights1;
uniform mat4 ProjectionModelViewMatrix;
uniform mat4 uBoneMatrices[64];
void main()
{
vec4 skinnedPos = vec4(0.0, 0.0, 0.0, 0.0);
vec4 originalPos = vec4(vPosition, 1.0);
float totalWeight = 0.0;
if (aBoneWeights0.x > 0.0) {
skinnedPos += uBoneMatrices[int(aBoneIndices0.x)] * originalPos * aBoneWeights0.x;
totalWeight += aBoneWeights0.x;
}
if (aBoneWeights0.y > 0.0) {
skinnedPos += uBoneMatrices[int(aBoneIndices0.y)] * originalPos * aBoneWeights0.y;
totalWeight += aBoneWeights0.y;
}
if (aBoneWeights0.z > 0.0) {
skinnedPos += uBoneMatrices[int(aBoneIndices0.z)] * originalPos * aBoneWeights0.z;
totalWeight += aBoneWeights0.z;
}
if (aBoneWeights0.w > 0.0) {
skinnedPos += uBoneMatrices[int(aBoneIndices0.w)] * originalPos * aBoneWeights0.w;
totalWeight += aBoneWeights0.w;
}
if (aBoneWeights1.x > 0.0) {
skinnedPos += uBoneMatrices[int(aBoneIndices1.x)] * originalPos * aBoneWeights1.x;
totalWeight += aBoneWeights1.x;
}
if (aBoneWeights1.y > 0.0) {
skinnedPos += uBoneMatrices[int(aBoneIndices1.y)] * originalPos * aBoneWeights1.y;
totalWeight += aBoneWeights1.y;
}
if (totalWeight < 0.001) {
skinnedPos = originalPos;
}
gl_Position = ProjectionModelViewMatrix * skinnedPos;
}
@@ -1,6 +0,0 @@
precision mediump float;
void main()
{
// Depth is written automatically by the GPU.
}
-50
View File
@@ -1,50 +0,0 @@
attribute vec3 vPosition;
attribute vec2 vTexCoord;
attribute vec4 aBoneIndices0;
attribute vec2 aBoneIndices1;
attribute vec4 aBoneWeights0;
attribute vec2 aBoneWeights1;
varying vec2 texCoord;
uniform mat4 ProjectionModelViewMatrix;
uniform mat4 uBoneMatrices[64];
void main()
{
vec4 skinnedPos = vec4(0.0, 0.0, 0.0, 0.0);
vec4 originalPos = vec4(vPosition, 1.0);
float totalWeight = 0.0;
if (aBoneWeights0.x > 0.0) {
skinnedPos += uBoneMatrices[int(aBoneIndices0.x)] * originalPos * aBoneWeights0.x;
totalWeight += aBoneWeights0.x;
}
if (aBoneWeights0.y > 0.0) {
skinnedPos += uBoneMatrices[int(aBoneIndices0.y)] * originalPos * aBoneWeights0.y;
totalWeight += aBoneWeights0.y;
}
if (aBoneWeights0.z > 0.0) {
skinnedPos += uBoneMatrices[int(aBoneIndices0.z)] * originalPos * aBoneWeights0.z;
totalWeight += aBoneWeights0.z;
}
if (aBoneWeights0.w > 0.0) {
skinnedPos += uBoneMatrices[int(aBoneIndices0.w)] * originalPos * aBoneWeights0.w;
totalWeight += aBoneWeights0.w;
}
if (aBoneWeights1.x > 0.0) {
skinnedPos += uBoneMatrices[int(aBoneIndices1.x)] * originalPos * aBoneWeights1.x;
totalWeight += aBoneWeights1.x;
}
if (aBoneWeights1.y > 0.0) {
skinnedPos += uBoneMatrices[int(aBoneIndices1.y)] * originalPos * aBoneWeights1.y;
totalWeight += aBoneWeights1.y;
}
if (totalWeight < 0.001) {
skinnedPos = originalPos;
}
gl_Position = ProjectionModelViewMatrix * skinnedPos;
texCoord = vTexCoord;
}
-67
View File
@@ -1,67 +0,0 @@
#version 120
attribute vec3 vPosition;
attribute vec2 vTexCoord;
attribute vec3 vNormal;
attribute vec4 aBoneIndices0;
attribute vec2 aBoneIndices1;
attribute vec4 aBoneWeights0;
attribute vec2 aBoneWeights1;
varying vec2 texCoord;
varying vec4 fragPosLightSpace;
varying vec3 fragNormal;
uniform mat4 ProjectionModelViewMatrix;
uniform mat4 ModelViewMatrix;
uniform mat4 uLightFromCamera;
uniform mat4 uBoneMatrices[64];
void main()
{
vec4 skinnedPos = vec4(0.0, 0.0, 0.0, 0.0);
vec3 skinnedNormal = vec3(0.0, 0.0, 0.0);
vec4 originalPos = vec4(vPosition, 1.0);
float totalWeight = 0.0;
if (aBoneWeights0.x > 0.0) {
skinnedPos += uBoneMatrices[int(aBoneIndices0.x)] * originalPos * aBoneWeights0.x;
skinnedNormal += mat3(uBoneMatrices[int(aBoneIndices0.x)]) * vNormal * aBoneWeights0.x;
totalWeight += aBoneWeights0.x;
}
if (aBoneWeights0.y > 0.0) {
skinnedPos += uBoneMatrices[int(aBoneIndices0.y)] * originalPos * aBoneWeights0.y;
skinnedNormal += mat3(uBoneMatrices[int(aBoneIndices0.y)]) * vNormal * aBoneWeights0.y;
totalWeight += aBoneWeights0.y;
}
if (aBoneWeights0.z > 0.0) {
skinnedPos += uBoneMatrices[int(aBoneIndices0.z)] * originalPos * aBoneWeights0.z;
skinnedNormal += mat3(uBoneMatrices[int(aBoneIndices0.z)]) * vNormal * aBoneWeights0.z;
totalWeight += aBoneWeights0.z;
}
if (aBoneWeights0.w > 0.0) {
skinnedPos += uBoneMatrices[int(aBoneIndices0.w)] * originalPos * aBoneWeights0.w;
skinnedNormal += mat3(uBoneMatrices[int(aBoneIndices0.w)]) * vNormal * aBoneWeights0.w;
totalWeight += aBoneWeights0.w;
}
if (aBoneWeights1.x > 0.0) {
skinnedPos += uBoneMatrices[int(aBoneIndices1.x)] * originalPos * aBoneWeights1.x;
skinnedNormal += mat3(uBoneMatrices[int(aBoneIndices1.x)]) * vNormal * aBoneWeights1.x;
totalWeight += aBoneWeights1.x;
}
if (aBoneWeights1.y > 0.0) {
skinnedPos += uBoneMatrices[int(aBoneIndices1.y)] * originalPos * aBoneWeights1.y;
skinnedNormal += mat3(uBoneMatrices[int(aBoneIndices1.y)]) * vNormal * aBoneWeights1.y;
totalWeight += aBoneWeights1.y;
}
if (totalWeight < 0.001) {
skinnedPos = originalPos;
skinnedNormal = vNormal;
}
gl_Position = ProjectionModelViewMatrix * skinnedPos;
texCoord = vTexCoord;
fragPosLightSpace = uLightFromCamera * ModelViewMatrix * skinnedPos;
fragNormal = mat3(ModelViewMatrix) * skinnedNormal;
}
File renamed without changes.
-9
View File
@@ -1,9 +0,0 @@
//precisionmediump float;
uniform sampler2D Texture;
varying vec2 texCoord;
void main()
{
vec4 color = texture2D(Texture,texCoord).rgba;
gl_FragColor = color;
}
-11
View File
@@ -1,11 +0,0 @@
attribute vec2 vPosition;
attribute vec2 vTexCoord;
varying vec2 TexCoord;
uniform mat4 uProjection;
void main() {
TexCoord = vTexCoord;
gl_Position = uProjection * vec4(vPosition, 0.0, 1.0);
}
-14
View File
@@ -1,14 +0,0 @@
#version 330 core
in vec2 TexCoord;
out vec4 FragColor;
uniform sampler2D uText;
uniform vec4 uColor;
uniform vec4 uOutlineColor;
uniform float uOutlineWidth = 0.7;
void main() {
float dist = texture(uText, TexCoord).r;
float outline = smoothstep(0.5 - uOutlineWidth, 0.5 + uOutlineWidth, dist);
float text = smoothstep(0.5, 0.5 + 0.1, dist);
float glow = exp(-pow(dist - 0.5, 2.0) / 0.02);
FragColor = vec4(uColor.rgb * (text + glow * 0.6), uColor.a * outline);
}
-19
View File
@@ -1,19 +0,0 @@
precision mediump float;
varying vec2 TexCoord;
uniform sampler2D uText;
uniform vec4 uColor;
//uniform float uOutlineWidth; // Убрано дефолтное значение
void main() {
float dist = texture2D(uText, TexCoord).r;
float uOutlineWidth = 0.7;
float outline = smoothstep(0.5 - uOutlineWidth, 0.5 + uOutlineWidth, dist);
float text = smoothstep(0.5, 0.6, dist); // 0.5 + 0.1 вычислено заранее
float glow = exp(-pow(dist - 0.5, 2.0) / 0.02);
gl_FragColor = vec4(uColor.rgb * (text + glow * 0.6), uColor.a * outline);
}
Binary file not shown.
Binary file not shown.
Loaded 100 of 326 files, more files were not shown because too many files have changed in this diff. Show more