Compare commits
5 Commits
3d3c3b2a36
...
fe01a5d1b2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fe01a5d1b2 | ||
|
|
b6e0422d71 | ||
|
|
3778a603f2 | ||
|
|
e310822462 | ||
|
|
b5bcd3a812 |
3
.gitattributes
vendored
3
.gitattributes
vendored
@ -2,3 +2,6 @@
|
|||||||
*.png filter=lfs diff=lfs merge=lfs -text
|
*.png filter=lfs diff=lfs merge=lfs -text
|
||||||
*.jpg filter=lfs diff=lfs merge=lfs -text
|
*.jpg filter=lfs diff=lfs merge=lfs -text
|
||||||
*.anim 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
audio/background.wav
(Stored with Git LFS)
Normal file
BIN
audio/background.wav
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
audio/lullaby-music-vol20-186394--online-audio-convert.com.ogg
(Stored with Git LFS)
Normal file
BIN
audio/lullaby-music-vol20-186394--online-audio-convert.com.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
111
blender scripts/generate_tree001.py
Normal file
111
blender scripts/generate_tree001.py
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
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()
|
||||||
|
|
||||||
@ -53,3 +53,6 @@ check_and_download("https://github.com/lua/lua/archive/refs/tags/v5.4.8.zip" "lu
|
|||||||
|
|
||||||
# 10) sol2 (header-only C++ bindings for Lua)
|
# 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")
|
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")
|
||||||
|
|||||||
@ -608,3 +608,121 @@ if(NOT TARGET sol2_external_lib)
|
|||||||
target_include_directories(sol2_external_lib INTERFACE "${SOL2_SRC_DIR}/include")
|
target_include_directories(sol2_external_lib INTERFACE "${SOL2_SRC_DIR}/include")
|
||||||
target_link_libraries(sol2_external_lib INTERFACE lua_static)
|
target_link_libraries(sol2_external_lib INTERFACE lua_static)
|
||||||
endif()
|
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"
|
||||||
|
)
|
||||||
156
config/ui.json
156
config/ui.json
@ -1,156 +0,0 @@
|
|||||||
{
|
|
||||||
"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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -8,7 +8,7 @@ if(NOT CMAKE_MAKE_PROGRAM AND WIN32)
|
|||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
project(space-game001 LANGUAGES C CXX)
|
project(bishkek-witcher LANGUAGES C CXX)
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
@ -83,49 +83,49 @@ set(SOURCES
|
|||||||
../src/utils/Utils.h
|
../src/utils/Utils.h
|
||||||
../src/SparkEmitter.cpp
|
../src/SparkEmitter.cpp
|
||||||
../src/SparkEmitter.h
|
../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.cpp
|
||||||
../src/utils/Perlin.h
|
../src/utils/Perlin.h
|
||||||
../src/utils/TaskManager.cpp
|
../src/utils/TaskManager.cpp
|
||||||
../src/utils/TaskManager.h
|
../src/utils/TaskManager.h
|
||||||
# ../src/planet/StoneObject.cpp
|
|
||||||
# ../src/planet/StoneObject.h
|
|
||||||
../src/render/FrameBuffer.cpp
|
../src/render/FrameBuffer.cpp
|
||||||
../src/render/FrameBuffer.h
|
../src/render/FrameBuffer.h
|
||||||
|
../src/render/ShadowMap.cpp
|
||||||
|
../src/render/ShadowMap.h
|
||||||
../src/UiManager.cpp
|
../src/UiManager.cpp
|
||||||
../src/UiManager.h
|
../src/UiManager.h
|
||||||
../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/WebSocketClientBase.h
|
|
||||||
# ../src/network/WebSocketClientBase.cpp
|
|
||||||
# ../src/network/WebSocketClientEmscripten.h
|
|
||||||
# ../src/network/WebSocketClientEmscripten.cpp
|
|
||||||
../src/render/TextRenderer.h
|
../src/render/TextRenderer.h
|
||||||
../src/render/TextRenderer.cpp
|
../src/render/TextRenderer.cpp
|
||||||
../src/MenuManager.h
|
../src/MenuManager.h
|
||||||
../src/MenuManager.cpp
|
../src/MenuManager.cpp
|
||||||
# ../src/Space.h
|
../src/Location.h
|
||||||
# ../src/Space.cpp
|
../src/Location.cpp
|
||||||
../src/GameConstants.h
|
../src/GameConstants.h
|
||||||
../src/GameConstants.cpp
|
../src/GameConstants.cpp
|
||||||
../src/ScriptEngine.h
|
../src/ScriptEngine.h
|
||||||
../src/ScriptEngine.cpp
|
../src/ScriptEngine.cpp
|
||||||
../src/navigation/PathFinder.h
|
../src/navigation/PathFinder.h
|
||||||
../src/navigation/PathFinder.cpp
|
../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
|
||||||
)
|
)
|
||||||
|
|
||||||
add_executable(space-game001 ${SOURCES})
|
add_executable(bishkek-witcher ${SOURCES})
|
||||||
|
|
||||||
# Настройка путей к инклудам (используем скачанные исходники)
|
# Настройка путей к инклудам (используем скачанные исходники)
|
||||||
target_include_directories(space-game001 PRIVATE
|
target_include_directories(bishkek-witcher PRIVATE
|
||||||
../src
|
../src
|
||||||
../thirdparty/eigen-5.0.0
|
../thirdparty/eigen-5.0.0
|
||||||
../thirdparty/boost_1_90_0
|
../thirdparty/boost_1_90_0
|
||||||
@ -140,7 +140,7 @@ set(ENABLE_COMMONCRYPTO OFF CACHE BOOL "" FORCE)
|
|||||||
|
|
||||||
add_subdirectory("../thirdparty/libzip-1.11.4" libzip-build)
|
add_subdirectory("../thirdparty/libzip-1.11.4" libzip-build)
|
||||||
|
|
||||||
target_link_libraries(space-game001 PRIVATE zip z lua_static websocket.js)
|
target_link_libraries(bishkek-witcher PRIVATE zip z lua_static websocket.js)
|
||||||
|
|
||||||
# Эмскриптен-флаги
|
# Эмскриптен-флаги
|
||||||
set(EMSCRIPTEN_FLAGS
|
set(EMSCRIPTEN_FLAGS
|
||||||
@ -149,13 +149,15 @@ set(EMSCRIPTEN_FLAGS
|
|||||||
"-sUSE_LIBPNG=1"
|
"-sUSE_LIBPNG=1"
|
||||||
"-sUSE_ZLIB=1"
|
"-sUSE_ZLIB=1"
|
||||||
"-sUSE_SDL_TTF=2"
|
"-sUSE_SDL_TTF=2"
|
||||||
|
"-sUSE_SDL_MIXER=2"
|
||||||
|
"-sSDL2_MIXER_FORMATS=[ogg,mp3]"
|
||||||
#"-pthread"
|
#"-pthread"
|
||||||
#"-sUSE_PTHREADS=1"
|
#"-sUSE_PTHREADS=1"
|
||||||
"-fexceptions"
|
"-fexceptions"
|
||||||
"-DNETWORK"
|
"-DNETWORK"
|
||||||
)
|
)
|
||||||
|
|
||||||
target_compile_options(space-game001 PRIVATE ${EMSCRIPTEN_FLAGS} "-O2")
|
target_compile_options(bishkek-witcher PRIVATE ${EMSCRIPTEN_FLAGS} "-O2")
|
||||||
|
|
||||||
# Only loading.png and the shaders used before resources.zip is ready are preloaded.
|
# 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.
|
# resources.zip is downloaded asynchronously at runtime and served as a separate file.
|
||||||
@ -168,14 +170,15 @@ set(EMSCRIPTEN_LINK_FLAGS
|
|||||||
"--preload-file ${CMAKE_CURRENT_SOURCE_DIR}/../resources/loading.png@resources/loading.png"
|
"--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/shaders@resources/shaders"
|
||||||
"--preload-file ${CMAKE_CURRENT_SOURCE_DIR}/../resources/start.lua@resources/start.lua"
|
"--preload-file ${CMAKE_CURRENT_SOURCE_DIR}/../resources/start.lua@resources/start.lua"
|
||||||
|
"--preload-file ${CMAKE_CURRENT_SOURCE_DIR}/../audio@audio"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Применяем настройки линковки
|
# Применяем настройки линковки
|
||||||
target_link_options(space-game001 PRIVATE ${EMSCRIPTEN_LINK_FLAGS})
|
target_link_options(bishkek-witcher PRIVATE ${EMSCRIPTEN_LINK_FLAGS})
|
||||||
|
|
||||||
# Для совместимости со старыми версиями CMake, если target_link_options недостаточно
|
# Для совместимости со старыми версиями CMake, если target_link_options недостаточно
|
||||||
string(REPLACE ";" " " EMSCRIPTEN_LINK_FLAGS_STR "${EMSCRIPTEN_LINK_FLAGS}")
|
string(REPLACE ";" " " EMSCRIPTEN_LINK_FLAGS_STR "${EMSCRIPTEN_LINK_FLAGS}")
|
||||||
set_target_properties(space-game001 PROPERTIES
|
set_target_properties(bishkek-witcher PROPERTIES
|
||||||
LINK_FLAGS "${EMSCRIPTEN_LINK_FLAGS_STR}"
|
LINK_FLAGS "${EMSCRIPTEN_LINK_FLAGS_STR}"
|
||||||
SUFFIX ".html"
|
SUFFIX ".html"
|
||||||
)
|
)
|
||||||
@ -193,33 +196,33 @@ add_custom_command(
|
|||||||
)
|
)
|
||||||
|
|
||||||
add_custom_target(pack_resources DEPENDS "${RESOURCES_ZIP}")
|
add_custom_target(pack_resources DEPENDS "${RESOURCES_ZIP}")
|
||||||
add_dependencies(space-game001 pack_resources)
|
add_dependencies(bishkek-witcher pack_resources)
|
||||||
|
|
||||||
|
|
||||||
# Определяем путь к директории установки (относительно папки билда)
|
# Определяем путь к директории установки (относительно папки билда)
|
||||||
set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/public")
|
set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/public")
|
||||||
|
|
||||||
# 1. Устанавливаем основной HTML файл
|
# 1. Устанавливаем основной HTML файл
|
||||||
install(TARGETS space-game001
|
install(TARGETS bishkek-witcher
|
||||||
RUNTIME DESTINATION .
|
RUNTIME DESTINATION .
|
||||||
)
|
)
|
||||||
|
|
||||||
# 2. Устанавливаем сопутствующие файлы (JS, WASM и сгенерированный архив данных)
|
# 2. Устанавливаем сопутствующие файлы (JS, WASM и сгенерированный архив данных)
|
||||||
install(FILES
|
install(FILES
|
||||||
"${CMAKE_CURRENT_BINARY_DIR}/space-game001.js"
|
"${CMAKE_CURRENT_BINARY_DIR}/bishkek-witcher.js"
|
||||||
"${CMAKE_CURRENT_BINARY_DIR}/space-game001.wasm"
|
"${CMAKE_CURRENT_BINARY_DIR}/bishkek-witcher.wasm"
|
||||||
"${CMAKE_CURRENT_BINARY_DIR}/space-game001.data"
|
"${CMAKE_CURRENT_BINARY_DIR}/bishkek-witcher.data"
|
||||||
DESTINATION .
|
DESTINATION .
|
||||||
)
|
)
|
||||||
|
|
||||||
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/space-game001plain.html"
|
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/index.html"
|
||||||
DESTINATION .
|
DESTINATION .
|
||||||
)
|
)
|
||||||
|
|
||||||
# resources.zip is served separately and downloaded asynchronously at runtime
|
# resources.zip is served separately and downloaded asynchronously at runtime
|
||||||
install(FILES "${RESOURCES_ZIP}" DESTINATION .)
|
install(FILES "${RESOURCES_ZIP}" DESTINATION .)
|
||||||
|
|
||||||
add_custom_command(TARGET space-game001 POST_BUILD
|
add_custom_command(TARGET bishkek-witcher POST_BUILD
|
||||||
COMMAND ${CMAKE_COMMAND} --install .
|
COMMAND ${CMAKE_COMMAND} --install .
|
||||||
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
|
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
|
||||||
COMMENT "Automatically deploying to public directory..."
|
COMMENT "Automatically deploying to public directory..."
|
||||||
|
|||||||
82
proj-web/index.html
Normal file
82
proj-web/index.html
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
<!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>
|
||||||
@ -46,8 +46,6 @@ add_executable(space-game001
|
|||||||
../src/render/ShadowMap.h
|
../src/render/ShadowMap.h
|
||||||
../src/UiManager.cpp
|
../src/UiManager.cpp
|
||||||
../src/UiManager.h
|
../src/UiManager.h
|
||||||
# ../src/Projectile.h
|
|
||||||
# ../src/Projectile.cpp
|
|
||||||
../src/render/TextRenderer.h
|
../src/render/TextRenderer.h
|
||||||
../src/render/TextRenderer.cpp
|
../src/render/TextRenderer.cpp
|
||||||
../src/MenuManager.h
|
../src/MenuManager.h
|
||||||
@ -114,6 +112,7 @@ target_link_libraries(space-game001 PRIVATE
|
|||||||
eigen_external_lib
|
eigen_external_lib
|
||||||
boost_external_lib
|
boost_external_lib
|
||||||
sol2_external_lib
|
sol2_external_lib
|
||||||
|
SDL2_mixer_external_lib
|
||||||
)
|
)
|
||||||
|
|
||||||
# Линкуем OpenGL (Windows)
|
# Линкуем OpenGL (Windows)
|
||||||
@ -138,6 +137,7 @@ if (WIN32)
|
|||||||
|
|
||||||
set(SDL2TTF_DLL_SRC "$<IF:$<CONFIG:Debug>,${SDL2TTF_BASE_DIR}-Debug/bin/SDL2_ttfd.dll,${SDL2TTF_BASE_DIR}-Release/bin/SDL2_ttf.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
|
add_custom_command(TARGET space-game001 POST_BUILD
|
||||||
COMMAND ${CMAKE_COMMAND} -E echo "Copying DLLs to output folder..."
|
COMMAND ${CMAKE_COMMAND} -E echo "Copying DLLs to output folder..."
|
||||||
@ -159,6 +159,10 @@ if (WIN32)
|
|||||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||||
"${SDL2TTF_DLL_SRC}"
|
"${SDL2TTF_DLL_SRC}"
|
||||||
"$<TARGET_FILE_DIR:space-game001>"
|
"$<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()
|
endif()
|
||||||
|
|
||||||
@ -169,6 +173,7 @@ endif()
|
|||||||
# Какие папки с ресурсами нужно копировать
|
# Какие папки с ресурсами нужно копировать
|
||||||
set(RUNTIME_RESOURCE_DIRS
|
set(RUNTIME_RESOURCE_DIRS
|
||||||
"resources"
|
"resources"
|
||||||
|
"audio"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Копируем ресурсы и шейдеры в папку exe и в корень build/
|
# Копируем ресурсы и шейдеры в папку exe и в корень build/
|
||||||
|
|||||||
@ -59,7 +59,7 @@
|
|||||||
"type": "Choice",
|
"type": "Choice",
|
||||||
"speaker": "Hero",
|
"speaker": "Hero",
|
||||||
"portrait": "resources/hero.png",
|
"portrait": "resources/hero.png",
|
||||||
"text": "Choose your answer.",
|
"text": "",
|
||||||
"choices": [
|
"choices": [
|
||||||
{
|
{
|
||||||
"id": "main_1",
|
"id": "main_1",
|
||||||
|
|||||||
@ -1,93 +1,207 @@
|
|||||||
#ifdef AUDIO
|
|
||||||
|
|
||||||
#include "AudioPlayerAsync.h"
|
#include "AudioPlayerAsync.h"
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#ifdef __EMSCRIPTEN__
|
||||||
|
AudioPlayerAsync::AudioPlayerAsync() {}
|
||||||
|
#else
|
||||||
AudioPlayerAsync::AudioPlayerAsync() : worker(&AudioPlayerAsync::workerThread, this) {}
|
AudioPlayerAsync::AudioPlayerAsync() : worker(&AudioPlayerAsync::workerThread, this) {}
|
||||||
|
#endif
|
||||||
|
|
||||||
AudioPlayerAsync::~AudioPlayerAsync() {
|
AudioPlayerAsync::~AudioPlayerAsync() {
|
||||||
|
#ifndef __EMSCRIPTEN__
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lock(mtx);
|
std::unique_lock<std::mutex> lock(mtx);
|
||||||
stop = true;
|
stop = true;
|
||||||
cv.notify_all();
|
cv.notify_all();
|
||||||
}
|
}
|
||||||
|
if (worker.joinable())
|
||||||
worker.join();
|
worker.join();
|
||||||
|
#endif
|
||||||
|
shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioPlayerAsync::stopAsync() {
|
bool AudioPlayerAsync::init() {
|
||||||
|
if (initialized) return true;
|
||||||
|
|
||||||
|
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
|
||||||
|
std::cerr << "SDL_InitSubSystem(SDL_INIT_AUDIO) failed: " << SDL_GetError() << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 2048) < 0) {
|
||||||
|
std::cerr << "Mix_OpenAudio failed: " << Mix_GetError() << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Mix_AllocateChannels(16);
|
||||||
|
|
||||||
|
initialized = true;
|
||||||
|
std::cout << "AudioPlayerAsync initialized with SDL2_mixer" << std::endl;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioPlayerAsync::shutdown() {
|
||||||
|
if (!initialized) return;
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(soundCacheMutex);
|
||||||
|
for (auto& pair : soundCache) {
|
||||||
|
Mix_FreeChunk(pair.second);
|
||||||
|
}
|
||||||
|
soundCache.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
Mix_CloseAudio();
|
||||||
|
SDL_QuitSubSystem(SDL_INIT_AUDIO);
|
||||||
|
initialized = false;
|
||||||
|
std::cout << "AudioPlayerAsync shutdown" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioPlayerAsync::playSoundAsync(const std::string& filePath, int loops, int channel) {
|
||||||
|
if (!initialized) {
|
||||||
|
std::cerr << "AudioPlayerAsync not initialized" << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto task = [this, filePath, loops, channel]() {
|
||||||
|
Mix_Chunk* sound = nullptr;
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> cacheLock(soundCacheMutex);
|
||||||
|
auto it = soundCache.find(filePath);
|
||||||
|
if (it != soundCache.end()) {
|
||||||
|
sound = it->second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sound) {
|
||||||
|
sound = Mix_LoadWAV(filePath.c_str());
|
||||||
|
if (!sound) {
|
||||||
|
std::cerr << "Failed to load sound " << filePath << ": " << Mix_GetError() << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::lock_guard<std::mutex> cacheLock(soundCacheMutex);
|
||||||
|
soundCache[filePath] = sound;
|
||||||
|
}
|
||||||
|
|
||||||
|
int result = Mix_PlayChannel(channel, sound, loops);
|
||||||
|
if (result == -1) {
|
||||||
|
std::cerr << "Mix_PlayChannel failed: " << Mix_GetError() << std::endl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef __EMSCRIPTEN__
|
||||||
|
task();
|
||||||
|
#else
|
||||||
std::unique_lock<std::mutex> lock(mtx);
|
std::unique_lock<std::mutex> lock(mtx);
|
||||||
taskQueue.push([this]() {
|
taskQueue.push(std::move(task));
|
||||||
//audioPlayerMutex.lock();
|
cv.notify_one();
|
||||||
audioPlayer->stop();
|
#endif
|
||||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
}
|
||||||
//audioPlayerMutex.unlock();
|
|
||||||
|
void AudioPlayerAsync::playMusicAsync(const std::string& filePath, int loops) {
|
||||||
|
if (!initialized) return;
|
||||||
|
|
||||||
|
auto task = [filePath, loops]() {
|
||||||
|
Mix_Music* music = Mix_LoadMUS(filePath.c_str());
|
||||||
|
if (!music) {
|
||||||
|
std::cerr << "Failed to load music " << filePath << ": " << Mix_GetError() << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (Mix_PlayMusic(music, loops) == -1) {
|
||||||
|
std::cerr << "Mix_PlayMusic failed: " << Mix_GetError() << std::endl;
|
||||||
|
Mix_FreeMusic(music);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef __EMSCRIPTEN__
|
||||||
|
task();
|
||||||
|
#else
|
||||||
|
std::unique_lock<std::mutex> lock(mtx);
|
||||||
|
taskQueue.push(std::move(task));
|
||||||
|
cv.notify_one();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioPlayerAsync::stopMusicAsync() {
|
||||||
|
if (!initialized) return;
|
||||||
|
#ifdef __EMSCRIPTEN__
|
||||||
|
Mix_HaltMusic();
|
||||||
|
#else
|
||||||
|
std::unique_lock<std::mutex> lock(mtx);
|
||||||
|
taskQueue.push([]() {
|
||||||
|
Mix_HaltMusic();
|
||||||
});
|
});
|
||||||
cv.notify_one();
|
cv.notify_one();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioPlayerAsync::resetAsync() {
|
void AudioPlayerAsync::pauseMusicAsync() {
|
||||||
|
if (!initialized) return;
|
||||||
|
#ifdef __EMSCRIPTEN__
|
||||||
|
Mix_PauseMusic();
|
||||||
|
#else
|
||||||
std::unique_lock<std::mutex> lock(mtx);
|
std::unique_lock<std::mutex> lock(mtx);
|
||||||
taskQueue.push([this]() {
|
taskQueue.push([]() {
|
||||||
//audioPlayerMutex.lock();
|
Mix_PauseMusic();
|
||||||
audioPlayer.reset();
|
|
||||||
audioPlayer = std::make_unique<AudioPlayer>();
|
|
||||||
|
|
||||||
//audioPlayerMutex.unlock();
|
|
||||||
});
|
});
|
||||||
cv.notify_one();
|
cv.notify_one();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioPlayerAsync::playSoundAsync(std::string soundName) {
|
void AudioPlayerAsync::resumeMusicAsync() {
|
||||||
|
if (!initialized) return;
|
||||||
soundNameMutex.lock();
|
#ifdef __EMSCRIPTEN__
|
||||||
latestSoundName = soundName;
|
Mix_ResumeMusic();
|
||||||
soundNameMutex.unlock();
|
#else
|
||||||
|
|
||||||
std::unique_lock<std::mutex> lock(mtx);
|
std::unique_lock<std::mutex> lock(mtx);
|
||||||
taskQueue.push([this]() {
|
taskQueue.push([]() {
|
||||||
//audioPlayerMutex.lock();
|
Mix_ResumeMusic();
|
||||||
if (audioPlayer) {
|
|
||||||
audioPlayer->playSound(latestSoundName);
|
|
||||||
}
|
|
||||||
//audioPlayerMutex.unlock();
|
|
||||||
});
|
});
|
||||||
cv.notify_one();
|
cv.notify_one();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioPlayerAsync::playMusicAsync(std::string musicName) {
|
void AudioPlayerAsync::setMusicVolume(int volume) {
|
||||||
|
if (!initialized) return;
|
||||||
musicNameMutex.lock();
|
volume = std::max(0, std::min(128, volume));
|
||||||
latestMusicName = musicName;
|
#ifdef __EMSCRIPTEN__
|
||||||
musicNameMutex.unlock();
|
Mix_VolumeMusic(volume);
|
||||||
|
#else
|
||||||
std::unique_lock<std::mutex> lock(mtx);
|
std::unique_lock<std::mutex> lock(mtx);
|
||||||
taskQueue.push([this]() {
|
taskQueue.push([volume]() {
|
||||||
//audioPlayerMutex.lock();
|
Mix_VolumeMusic(volume);
|
||||||
if (audioPlayer) {
|
|
||||||
audioPlayer->playMusic(latestMusicName);
|
|
||||||
}
|
|
||||||
//audioPlayerMutex.unlock();
|
|
||||||
});
|
});
|
||||||
cv.notify_one();
|
cv.notify_one();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AudioPlayerAsync::setSoundVolume(int volume) {
|
||||||
|
if (!initialized) return;
|
||||||
|
volume = std::max(0, std::min(128, volume));
|
||||||
|
#ifdef __EMSCRIPTEN__
|
||||||
|
Mix_Volume(-1, volume);
|
||||||
|
#else
|
||||||
|
std::unique_lock<std::mutex> lock(mtx);
|
||||||
|
taskQueue.push([volume]() {
|
||||||
|
Mix_Volume(-1, volume); // все каналы
|
||||||
|
});
|
||||||
|
cv.notify_one();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef __EMSCRIPTEN__
|
||||||
void AudioPlayerAsync::workerThread() {
|
void AudioPlayerAsync::workerThread() {
|
||||||
while (true) {
|
while (true) {
|
||||||
std::function<void()> task;
|
std::function<void()> task;
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lock(mtx);
|
std::unique_lock<std::mutex> lock(mtx);
|
||||||
cv.wait(lock, [this]() { return !taskQueue.empty() || stop; });
|
cv.wait(lock, [this] { return !taskQueue.empty() || stop; });
|
||||||
|
if (stop && taskQueue.empty())
|
||||||
if (stop && taskQueue.empty()) {
|
|
||||||
break;
|
break;
|
||||||
}
|
task = std::move(taskQueue.front());
|
||||||
|
|
||||||
task = taskQueue.front();
|
|
||||||
taskQueue.pop();
|
taskQueue.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
task();
|
task();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@ -1,53 +1,50 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#ifdef AUDIO
|
#include <SDL2/SDL_mixer.h>
|
||||||
|
#include <string>
|
||||||
#include <iostream>
|
#include <unordered_map>
|
||||||
#include <thread>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <condition_variable>
|
#include <SDL2/SDL.h>
|
||||||
#include <queue>
|
|
||||||
#include <functional>
|
|
||||||
#include "cmakeaudioplayer/include/AudioPlayer.hpp"
|
|
||||||
|
|
||||||
|
#ifndef __EMSCRIPTEN__
|
||||||
|
#include <queue>
|
||||||
|
#include <thread>
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <functional>
|
||||||
|
#endif
|
||||||
|
|
||||||
class AudioPlayerAsync {
|
class AudioPlayerAsync {
|
||||||
public:
|
public:
|
||||||
AudioPlayerAsync();
|
AudioPlayerAsync();
|
||||||
~AudioPlayerAsync();
|
~AudioPlayerAsync();
|
||||||
|
|
||||||
void resetAsync();
|
bool init();
|
||||||
|
void shutdown();
|
||||||
|
|
||||||
void playSoundAsync(std::string soundName);
|
void playSoundAsync(const std::string& filePath, int loops = 0, int channel = -1);
|
||||||
|
void playMusicAsync(const std::string& filePath, int loops = -1);
|
||||||
|
void stopMusicAsync();
|
||||||
|
void pauseMusicAsync();
|
||||||
|
void resumeMusicAsync();
|
||||||
|
void setMusicVolume(int volume); // 0..128
|
||||||
|
void setSoundVolume(int volume); // 0..128
|
||||||
|
|
||||||
void playMusicAsync(std::string musicName);
|
void exit() { stop = true; }
|
||||||
|
|
||||||
void stopAsync();
|
|
||||||
|
|
||||||
void exit()
|
|
||||||
{
|
|
||||||
stop = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::thread worker;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<AudioPlayer> audioPlayer;
|
#ifndef __EMSCRIPTEN__
|
||||||
//std::mutex audioPlayerMutex;
|
void workerThread();
|
||||||
|
|
||||||
std::mutex soundNameMutex;
|
|
||||||
std::mutex musicNameMutex;
|
|
||||||
|
|
||||||
std::string latestSoundName;
|
|
||||||
std::string latestMusicName;
|
|
||||||
|
|
||||||
|
std::thread worker;
|
||||||
std::mutex mtx;
|
std::mutex mtx;
|
||||||
std::condition_variable cv;
|
std::condition_variable cv;
|
||||||
std::queue<std::function<void()>> taskQueue;
|
std::queue<std::function<void()>> taskQueue;
|
||||||
|
#endif
|
||||||
bool stop = false;
|
bool stop = false;
|
||||||
|
|
||||||
void workerThread();
|
std::unordered_map<std::string, Mix_Chunk*> soundCache;
|
||||||
|
std::mutex soundCacheMutex;
|
||||||
|
|
||||||
|
bool initialized = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
|
||||||
@ -8,6 +8,10 @@
|
|||||||
|
|
||||||
namespace ZL
|
namespace ZL
|
||||||
{
|
{
|
||||||
|
#ifdef EMSCRIPTEN
|
||||||
|
using std::min;
|
||||||
|
using std::max;
|
||||||
|
#endif
|
||||||
|
|
||||||
int getIndexByValue(const std::string& name, const std::vector<std::string>& words)
|
int getIndexByValue(const std::string& name, const std::vector<std::string>& words)
|
||||||
{
|
{
|
||||||
@ -909,29 +913,51 @@ namespace ZL
|
|||||||
|
|
||||||
void GpuSkinningShaderData::RenderVBO(Renderer& renderer)
|
void GpuSkinningShaderData::RenderVBO(Renderer& renderer)
|
||||||
{
|
{
|
||||||
|
CheckGlError(__FILE__, __LINE__);
|
||||||
// Bind position and texcoord VBOs
|
// Bind position and texcoord VBOs
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, bindPoseMutable.positionVBO->getBuffer());
|
glBindBuffer(GL_ARRAY_BUFFER, bindPoseMutable.positionVBO->getBuffer());
|
||||||
renderer.VertexAttribPointer3fv("vPosition", 0, NULL);
|
renderer.VertexAttribPointer3fv("vPosition", 0, NULL);
|
||||||
|
|
||||||
|
CheckGlError(__FILE__, __LINE__);
|
||||||
if (bindPoseMutable.texCoordVBO) {
|
if (bindPoseMutable.texCoordVBO) {
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, bindPoseMutable.texCoordVBO->getBuffer());
|
glBindBuffer(GL_ARRAY_BUFFER, bindPoseMutable.texCoordVBO->getBuffer());
|
||||||
renderer.VertexAttribPointer2fv("vTexCoord", 0, NULL);
|
renderer.VertexAttribPointer2fv("vTexCoord", 0, NULL);
|
||||||
|
CheckGlError(__FILE__, __LINE__);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CheckGlError(__FILE__, __LINE__);
|
||||||
|
if (bindPoseMutable.normalVBO) {
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, bindPoseMutable.normalVBO->getBuffer());
|
||||||
|
renderer.VertexAttribPointer3fv("vNormal", 0, NULL);
|
||||||
|
CheckGlError(__FILE__, __LINE__);
|
||||||
|
} else {
|
||||||
|
renderer.DisableVertexAttribArray("vNormal");
|
||||||
|
CheckGlError(__FILE__, __LINE__);
|
||||||
|
}
|
||||||
|
|
||||||
|
CheckGlError(__FILE__, __LINE__);
|
||||||
// Bind bone index VBOs
|
// Bind bone index VBOs
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, boneIndices0VBO->getBuffer());
|
glBindBuffer(GL_ARRAY_BUFFER, boneIndices0VBO->getBuffer());
|
||||||
renderer.VertexAttribPointer4fv("aBoneIndices0", 0, NULL);
|
renderer.VertexAttribPointer4fv("aBoneIndices0", 0, NULL);
|
||||||
|
|
||||||
|
CheckGlError(__FILE__, __LINE__);
|
||||||
|
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, boneIndices1VBO->getBuffer());
|
glBindBuffer(GL_ARRAY_BUFFER, boneIndices1VBO->getBuffer());
|
||||||
renderer.VertexAttribPointer2fv("aBoneIndices1", 0, NULL);
|
renderer.VertexAttribPointer2fv("aBoneIndices1", 0, NULL);
|
||||||
|
|
||||||
|
CheckGlError(__FILE__, __LINE__);
|
||||||
|
|
||||||
// Bind bone weight VBOs
|
// Bind bone weight VBOs
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, boneWeights0VBO->getBuffer());
|
glBindBuffer(GL_ARRAY_BUFFER, boneWeights0VBO->getBuffer());
|
||||||
renderer.VertexAttribPointer4fv("aBoneWeights0", 0, NULL);
|
renderer.VertexAttribPointer4fv("aBoneWeights0", 0, NULL);
|
||||||
|
|
||||||
|
CheckGlError(__FILE__, __LINE__);
|
||||||
|
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, boneWeights1VBO->getBuffer());
|
glBindBuffer(GL_ARRAY_BUFFER, boneWeights1VBO->getBuffer());
|
||||||
renderer.VertexAttribPointer2fv("aBoneWeights1", 0, NULL);
|
renderer.VertexAttribPointer2fv("aBoneWeights1", 0, NULL);
|
||||||
|
|
||||||
|
CheckGlError(__FILE__, __LINE__);
|
||||||
|
|
||||||
glDrawArrays(GL_TRIANGLES, 0, static_cast<GLsizei>(bindPoseMutable.data.PositionData.size()));
|
glDrawArrays(GL_TRIANGLES, 0, static_cast<GLsizei>(bindPoseMutable.data.PositionData.size()));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -417,12 +417,18 @@ void Character::drawShadowDepthCpu(Renderer& renderer) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Character::drawShadowDepthGpuSkinning(Renderer& renderer) {
|
void Character::drawShadowDepthGpuSkinning(Renderer& renderer) {
|
||||||
|
|
||||||
|
CheckGlError(__FILE__, __LINE__);
|
||||||
AnimationState drawState = resolveActiveState();
|
AnimationState drawState = resolveActiveState();
|
||||||
auto it = animations.find(drawState);
|
auto it = animations.find(drawState);
|
||||||
if (it == animations.end()) return;
|
if (it == animations.end()) return;
|
||||||
|
|
||||||
|
CheckGlError(__FILE__, __LINE__);
|
||||||
|
|
||||||
if (!prepareGpuSkinning()) return;
|
if (!prepareGpuSkinning()) return;
|
||||||
|
|
||||||
|
CheckGlError(__FILE__, __LINE__);
|
||||||
|
|
||||||
static const std::string shadowSkinningShader = "shadow_depth_skinning";
|
static const std::string shadowSkinningShader = "shadow_depth_skinning";
|
||||||
static const std::string boneMatricesUniform = "uBoneMatrices[0]";
|
static const std::string boneMatricesUniform = "uBoneMatrices[0]";
|
||||||
|
|
||||||
@ -435,14 +441,19 @@ void Character::drawShadowDepthGpuSkinning(Renderer& renderer) {
|
|||||||
renderer.ScaleMatrix(modelScale);
|
renderer.ScaleMatrix(modelScale);
|
||||||
renderer.RotateMatrix(modelCorrectionRotation.toRotationMatrix());
|
renderer.RotateMatrix(modelCorrectionRotation.toRotationMatrix());
|
||||||
|
|
||||||
|
CheckGlError(__FILE__, __LINE__);
|
||||||
renderer.RenderUniformMatrix4fvArray(boneMatricesUniform,
|
renderer.RenderUniformMatrix4fvArray(boneMatricesUniform,
|
||||||
static_cast<int>(it->second.gpuSkinningShaderData.skinningMatrices.size()), false,
|
static_cast<int>(it->second.gpuSkinningShaderData.skinningMatrices.size()), false,
|
||||||
it->second.gpuSkinningShaderData.skinningMatrices[0].data());
|
it->second.gpuSkinningShaderData.skinningMatrices[0].data());
|
||||||
|
|
||||||
|
CheckGlError(__FILE__, __LINE__);
|
||||||
it->second.gpuSkinningShaderData.RenderVBO(renderer);
|
it->second.gpuSkinningShaderData.RenderVBO(renderer);
|
||||||
|
|
||||||
|
CheckGlError(__FILE__, __LINE__);
|
||||||
renderer.PopMatrix();
|
renderer.PopMatrix();
|
||||||
renderer.shaderManager.PopShader();
|
renderer.shaderManager.PopShader();
|
||||||
|
|
||||||
|
CheckGlError(__FILE__, __LINE__);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== Main pass with shadows ====================
|
// ==================== Main pass with shadows ====================
|
||||||
@ -498,8 +509,12 @@ void Character::drawGpuSkinningWithShadow(Renderer& renderer, const Eigen::Matri
|
|||||||
auto it = animations.find(drawState);
|
auto it = animations.find(drawState);
|
||||||
if (it == animations.end() || !texture) return;
|
if (it == animations.end() || !texture) return;
|
||||||
|
|
||||||
|
CheckGlError(__FILE__, __LINE__);
|
||||||
|
|
||||||
if (!prepareGpuSkinning()) return;
|
if (!prepareGpuSkinning()) return;
|
||||||
|
|
||||||
|
CheckGlError(__FILE__, __LINE__);
|
||||||
|
|
||||||
static const std::string skinningShadowShader = "skinning_shadow";
|
static const std::string skinningShadowShader = "skinning_shadow";
|
||||||
static const std::string boneMatricesUniform = "uBoneMatrices[0]";
|
static const std::string boneMatricesUniform = "uBoneMatrices[0]";
|
||||||
|
|
||||||
@ -509,10 +524,14 @@ void Character::drawGpuSkinningWithShadow(Renderer& renderer, const Eigen::Matri
|
|||||||
renderer.RenderUniformMatrix4fv("uLightFromCamera", false, lightFromCamera.data());
|
renderer.RenderUniformMatrix4fv("uLightFromCamera", false, lightFromCamera.data());
|
||||||
renderer.RenderUniform3fv("uLightDir", lightDirCamera.data());
|
renderer.RenderUniform3fv("uLightDir", lightDirCamera.data());
|
||||||
|
|
||||||
|
CheckGlError(__FILE__, __LINE__);
|
||||||
|
|
||||||
glActiveTexture(GL_TEXTURE1);
|
glActiveTexture(GL_TEXTURE1);
|
||||||
glBindTexture(GL_TEXTURE_2D, shadowMapTex);
|
glBindTexture(GL_TEXTURE_2D, shadowMapTex);
|
||||||
glActiveTexture(GL_TEXTURE0);
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
|
||||||
|
CheckGlError(__FILE__, __LINE__);
|
||||||
|
|
||||||
renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5,
|
renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5,
|
||||||
static_cast<float>(Environment::width) / static_cast<float>(Environment::height),
|
static_cast<float>(Environment::width) / static_cast<float>(Environment::height),
|
||||||
Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
|
Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
|
||||||
@ -523,17 +542,24 @@ void Character::drawGpuSkinningWithShadow(Renderer& renderer, const Eigen::Matri
|
|||||||
renderer.ScaleMatrix(modelScale);
|
renderer.ScaleMatrix(modelScale);
|
||||||
renderer.RotateMatrix(modelCorrectionRotation.toRotationMatrix());
|
renderer.RotateMatrix(modelCorrectionRotation.toRotationMatrix());
|
||||||
|
|
||||||
|
CheckGlError(__FILE__, __LINE__);
|
||||||
renderer.RenderUniformMatrix4fvArray(boneMatricesUniform,
|
renderer.RenderUniformMatrix4fvArray(boneMatricesUniform,
|
||||||
static_cast<int>(it->second.gpuSkinningShaderData.skinningMatrices.size()), false,
|
static_cast<int>(it->second.gpuSkinningShaderData.skinningMatrices.size()), false,
|
||||||
it->second.gpuSkinningShaderData.skinningMatrices[0].data());
|
it->second.gpuSkinningShaderData.skinningMatrices[0].data());
|
||||||
|
|
||||||
|
CheckGlError(__FILE__, __LINE__);
|
||||||
glBindTexture(GL_TEXTURE_2D, texture->getTexID());
|
glBindTexture(GL_TEXTURE_2D, texture->getTexID());
|
||||||
|
|
||||||
|
CheckGlError(__FILE__, __LINE__);
|
||||||
it->second.gpuSkinningShaderData.RenderVBO(renderer);
|
it->second.gpuSkinningShaderData.RenderVBO(renderer);
|
||||||
|
|
||||||
|
CheckGlError(__FILE__, __LINE__);
|
||||||
|
|
||||||
renderer.PopMatrix();
|
renderer.PopMatrix();
|
||||||
renderer.PopProjectionMatrix();
|
renderer.PopProjectionMatrix();
|
||||||
renderer.shaderManager.PopShader();
|
renderer.shaderManager.PopShader();
|
||||||
|
|
||||||
|
CheckGlError(__FILE__, __LINE__);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace ZL
|
} // namespace ZL
|
||||||
|
|||||||
41
src/Game.cpp
41
src/Game.cpp
@ -55,6 +55,7 @@ namespace ZL
|
|||||||
: newTickCount(0)
|
: newTickCount(0)
|
||||||
, lastTickCount(0)
|
, lastTickCount(0)
|
||||||
, menuManager(renderer)
|
, menuManager(renderer)
|
||||||
|
, audioPlayer(std::make_unique<AudioPlayerAsync>())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,7 +73,7 @@ namespace ZL
|
|||||||
Environment::computeProjectionDimensions();
|
Environment::computeProjectionDimensions();
|
||||||
|
|
||||||
ZL::BindOpenGlFunctions();
|
ZL::BindOpenGlFunctions();
|
||||||
ZL::CheckGlError();
|
ZL::CheckGlError(__FILE__, __LINE__);
|
||||||
renderer.InitOpenGL();
|
renderer.InitOpenGL();
|
||||||
|
|
||||||
#ifdef EMSCRIPTEN
|
#ifdef EMSCRIPTEN
|
||||||
@ -211,6 +212,15 @@ namespace ZL
|
|||||||
|
|
||||||
loadingCompleted = true;
|
loadingCompleted = true;
|
||||||
|
|
||||||
|
if (audioPlayer->init()) {
|
||||||
|
audioPlayer->setMusicVolume(100);
|
||||||
|
audioPlayer->setSoundVolume(80);
|
||||||
|
std::cout << "Audio initialized successfully" << std::endl;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
std::cout << "Audio initialization failed" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Game::drawUI()
|
void Game::drawUI()
|
||||||
@ -236,7 +246,7 @@ namespace ZL
|
|||||||
glDepthMask(GL_TRUE);
|
glDepthMask(GL_TRUE);
|
||||||
glEnable(GL_DEPTH_TEST);
|
glEnable(GL_DEPTH_TEST);
|
||||||
|
|
||||||
CheckGlError();
|
CheckGlError(__FILE__, __LINE__);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Game::drawScene() {
|
void Game::drawScene() {
|
||||||
@ -249,11 +259,15 @@ namespace ZL
|
|||||||
if (currentLocation)
|
if (currentLocation)
|
||||||
{
|
{
|
||||||
if (currentLocation->shadowMap) {
|
if (currentLocation->shadowMap) {
|
||||||
|
CheckGlError(__FILE__, __LINE__);
|
||||||
currentLocation->drawShadowDepthPass();
|
currentLocation->drawShadowDepthPass();
|
||||||
|
CheckGlError(__FILE__, __LINE__);
|
||||||
currentLocation->drawGameWithShadows();
|
currentLocation->drawGameWithShadows();
|
||||||
|
CheckGlError(__FILE__, __LINE__);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
currentLocation->drawGame();
|
currentLocation->drawGame();
|
||||||
|
CheckGlError(__FILE__, __LINE__);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -262,7 +276,7 @@ namespace ZL
|
|||||||
}
|
}
|
||||||
drawUI();
|
drawUI();
|
||||||
}
|
}
|
||||||
CheckGlError();
|
CheckGlError(__FILE__, __LINE__);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Game::drawLoading()
|
void Game::drawLoading()
|
||||||
@ -289,7 +303,7 @@ namespace ZL
|
|||||||
renderer.PopMatrix();
|
renderer.PopMatrix();
|
||||||
renderer.PopProjectionMatrix();
|
renderer.PopProjectionMatrix();
|
||||||
renderer.shaderManager.PopShader();
|
renderer.shaderManager.PopShader();
|
||||||
CheckGlError();
|
CheckGlError(__FILE__, __LINE__);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -329,7 +343,7 @@ namespace ZL
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Game::render() {
|
void Game::render() {
|
||||||
ZL::CheckGlError();
|
ZL::CheckGlError(__FILE__, __LINE__);
|
||||||
|
|
||||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||||
@ -470,16 +484,17 @@ namespace ZL
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (event.type == SDL_KEYDOWN) {
|
|
||||||
if (currentLocation && currentLocation->dialogueSystem.handleKeyDown(event.key.keysym.sym))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event.type == SDL_KEYDOWN && event.key.repeat == 0) {
|
if (event.type == SDL_KEYDOWN && event.key.repeat == 0) {
|
||||||
switch (event.key.keysym.sym) {
|
switch (event.key.keysym.sym) {
|
||||||
|
case SDLK_1:
|
||||||
|
if (audioPlayer) audioPlayer->playSoundAsync("audio/background.wav");
|
||||||
|
break;
|
||||||
|
case SDLK_2:
|
||||||
|
if (audioPlayer) audioPlayer->playMusicAsync("audio/lullaby-music-vol20-186394--online-audio-convert.com.ogg");
|
||||||
|
break;
|
||||||
|
case SDLK_3:
|
||||||
|
if (audioPlayer) audioPlayer->stopMusicAsync();
|
||||||
|
break;
|
||||||
case SDLK_f:
|
case SDLK_f:
|
||||||
currentLocation->dialogueSystem.startDialogue("test_choice_dialogue");
|
currentLocation->dialogueSystem.startDialogue("test_choice_dialogue");
|
||||||
break;
|
break;
|
||||||
|
|||||||
@ -20,6 +20,7 @@
|
|||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
#include "Location.h"
|
#include "Location.h"
|
||||||
|
#include "AudioPlayerAsync.h"
|
||||||
|
|
||||||
namespace ZL {
|
namespace ZL {
|
||||||
|
|
||||||
@ -56,6 +57,7 @@ namespace ZL {
|
|||||||
bool rightMouseDown = false;
|
bool rightMouseDown = false;
|
||||||
int lastMouseX = 0;
|
int lastMouseX = 0;
|
||||||
int lastMouseY = 0;
|
int lastMouseY = 0;
|
||||||
|
std::unique_ptr<AudioPlayerAsync> audioPlayer;
|
||||||
|
|
||||||
int64_t getSyncTimeMs();
|
int64_t getSyncTimeMs();
|
||||||
void processTickCount();
|
void processTickCount();
|
||||||
|
|||||||
@ -9,7 +9,7 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <cfloat>
|
||||||
#include "GameConstants.h"
|
#include "GameConstants.h"
|
||||||
|
|
||||||
|
|
||||||
@ -449,19 +449,23 @@ namespace ZL
|
|||||||
renderer.RenderUniform3fv("uLightDir", lightDirCamera.data());
|
renderer.RenderUniform3fv("uLightDir", lightDirCamera.data());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
CheckGlError(__FILE__, __LINE__);
|
||||||
glBindTexture(GL_TEXTURE_2D, roomTexture->getTexID());
|
glBindTexture(GL_TEXTURE_2D, roomTexture->getTexID());
|
||||||
renderer.DrawVertexRenderStruct(roomMesh);
|
renderer.DrawVertexRenderStruct(roomMesh);
|
||||||
|
|
||||||
|
CheckGlError(__FILE__, __LINE__);
|
||||||
for (auto& [name, gameObj] : gameObjects) {
|
for (auto& [name, gameObj] : gameObjects) {
|
||||||
glBindTexture(GL_TEXTURE_2D, gameObj.texture->getTexID());
|
glBindTexture(GL_TEXTURE_2D, gameObj.texture->getTexID());
|
||||||
renderer.DrawVertexRenderStruct(gameObj.mesh);
|
renderer.DrawVertexRenderStruct(gameObj.mesh);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CheckGlError(__FILE__, __LINE__);
|
||||||
for (auto& intObj : interactiveObjects) {
|
for (auto& intObj : interactiveObjects) {
|
||||||
if (intObj.isActive) {
|
if (intObj.isActive) {
|
||||||
intObj.draw(renderer);
|
intObj.draw(renderer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
CheckGlError(__FILE__, __LINE__);
|
||||||
|
|
||||||
#ifdef DEBUG_LIGHT
|
#ifdef DEBUG_LIGHT
|
||||||
// In debug-light mode characters use the plain shaders (draw normally
|
// In debug-light mode characters use the plain shaders (draw normally
|
||||||
@ -470,9 +474,14 @@ namespace ZL
|
|||||||
for (auto& npc : npcs) npc->draw(renderer);
|
for (auto& npc : npcs) npc->draw(renderer);
|
||||||
#else
|
#else
|
||||||
// Characters use their own shadow-aware shaders
|
// Characters use their own shadow-aware shaders
|
||||||
|
CheckGlError(__FILE__, __LINE__);
|
||||||
|
|
||||||
if (player) player->drawWithShadow(renderer, lightFromCamera, shadowMap->getDepthTexture(), lightDirCamera);
|
if (player) player->drawWithShadow(renderer, lightFromCamera, shadowMap->getDepthTexture(), lightDirCamera);
|
||||||
|
CheckGlError(__FILE__, __LINE__);
|
||||||
|
|
||||||
for (auto& npc : npcs) npc->drawWithShadow(renderer, lightFromCamera, shadowMap->getDepthTexture(), lightDirCamera);
|
for (auto& npc : npcs) npc->drawWithShadow(renderer, lightFromCamera, shadowMap->getDepthTexture(), lightDirCamera);
|
||||||
#endif
|
#endif
|
||||||
|
CheckGlError(__FILE__, __LINE__);
|
||||||
|
|
||||||
renderer.PopMatrix();
|
renderer.PopMatrix();
|
||||||
renderer.PopProjectionMatrix();
|
renderer.PopProjectionMatrix();
|
||||||
|
|||||||
@ -1,71 +0,0 @@
|
|||||||
#include "Projectile.h"
|
|
||||||
|
|
||||||
namespace ZL {
|
|
||||||
|
|
||||||
Projectile::Projectile()
|
|
||||||
: pos({ 0,0,0 })
|
|
||||||
, vel({ 0,0,0 })
|
|
||||||
, life(0.0f)
|
|
||||||
, maxLife(0.0f)
|
|
||||||
, active(false)
|
|
||||||
, size(0.5f)
|
|
||||||
, texture(nullptr)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void Projectile::init(const Vector3f& startPos, const Vector3f& startVel, float lifeMs, float s, std::shared_ptr<Texture> tex, Renderer& renderer) {
|
|
||||||
pos = startPos;
|
|
||||||
vel = startVel;
|
|
||||||
life = 0.0f;
|
|
||||||
maxLife = lifeMs;
|
|
||||||
size = s;
|
|
||||||
texture = tex;
|
|
||||||
active = true;
|
|
||||||
|
|
||||||
rebuildMesh(renderer);
|
|
||||||
mesh.RefreshVBO();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Projectile::update(float deltaMs, Renderer& renderer) {
|
|
||||||
if (!active) return;
|
|
||||||
|
|
||||||
pos = pos + vel * (deltaMs / 1000.0f);
|
|
||||||
life += deltaMs;
|
|
||||||
if (life >= maxLife) {
|
|
||||||
active = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
rebuildMesh(renderer);
|
|
||||||
mesh.RefreshVBO();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Projectile::rebuildMesh(Renderer&) {
|
|
||||||
float half = 10 * size * 0.5f;
|
|
||||||
|
|
||||||
mesh.data.PositionData.clear();
|
|
||||||
mesh.data.TexCoordData.clear();
|
|
||||||
|
|
||||||
mesh.data.PositionData.push_back({ pos(0) - half, pos(1) - half, pos(2) });
|
|
||||||
mesh.data.PositionData.push_back({ pos(0) - half, pos(1) + half, pos(2) });
|
|
||||||
mesh.data.PositionData.push_back({ pos(0) + half, pos(1) + half, pos(2) });
|
|
||||||
|
|
||||||
mesh.data.PositionData.push_back({ pos(0) - half, pos(1) - half, pos(2) });
|
|
||||||
mesh.data.PositionData.push_back({ pos(0) + half, pos(1) + half, pos(2) });
|
|
||||||
mesh.data.PositionData.push_back({ pos(0) + half, pos(1) - half, pos(2) });
|
|
||||||
|
|
||||||
mesh.data.TexCoordData.push_back({ 0.0f, 0.0f });
|
|
||||||
mesh.data.TexCoordData.push_back({ 0.0f, 1.0f });
|
|
||||||
mesh.data.TexCoordData.push_back({ 1.0f, 1.0f });
|
|
||||||
mesh.data.TexCoordData.push_back({ 0.0f, 0.0f });
|
|
||||||
mesh.data.TexCoordData.push_back({ 1.0f, 1.0f });
|
|
||||||
mesh.data.TexCoordData.push_back({ 1.0f, 0.0f });
|
|
||||||
}
|
|
||||||
|
|
||||||
void Projectile::draw(Renderer& renderer) const {
|
|
||||||
if (!active || !texture) return;
|
|
||||||
glBindTexture(GL_TEXTURE_2D, texture->getTexID());
|
|
||||||
renderer.DrawVertexRenderStruct(mesh);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace ZL
|
|
||||||
@ -1,38 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "render/Renderer.h"
|
|
||||||
#include "render/TextureManager.h"
|
|
||||||
#include <memory>
|
|
||||||
#include "SparkEmitter.h"
|
|
||||||
|
|
||||||
namespace ZL {
|
|
||||||
|
|
||||||
class Projectile {
|
|
||||||
public:
|
|
||||||
Projectile();
|
|
||||||
~Projectile() = default;
|
|
||||||
|
|
||||||
void init(const Vector3f& startPos, const Vector3f& startVel, float lifeMs, float size, std::shared_ptr<Texture> tex, Renderer& renderer);
|
|
||||||
void update(float deltaMs, Renderer& renderer);
|
|
||||||
void draw(Renderer& renderer) const;
|
|
||||||
|
|
||||||
bool isActive() const { return active; }
|
|
||||||
|
|
||||||
Vector3f getPosition() const { return pos; }
|
|
||||||
void deactivate() { active = false; }
|
|
||||||
|
|
||||||
SparkEmitter projectileEmitter;
|
|
||||||
private:
|
|
||||||
Vector3f pos;
|
|
||||||
Vector3f vel;
|
|
||||||
float life;
|
|
||||||
float maxLife;
|
|
||||||
bool active;
|
|
||||||
float size;
|
|
||||||
VertexRenderStruct mesh;
|
|
||||||
std::shared_ptr<Texture> texture;
|
|
||||||
|
|
||||||
void rebuildMesh(Renderer& renderer);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace ZL
|
|
||||||
@ -3,6 +3,11 @@
|
|||||||
#include "utils/Utils.h"
|
#include "utils/Utils.h"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace ZL
|
||||||
|
{
|
||||||
|
extern const char* CONST_ZIP_FILE;
|
||||||
|
}
|
||||||
|
|
||||||
namespace ZL::Dialogue {
|
namespace ZL::Dialogue {
|
||||||
|
|
||||||
NodeType DialogueDatabase::parseNodeType(const std::string& value) {
|
NodeType DialogueDatabase::parseNodeType(const std::string& value) {
|
||||||
@ -202,10 +207,23 @@ bool DialogueDatabase::loadFromFile(const std::string& path) {
|
|||||||
dialogues.clear();
|
dialogues.clear();
|
||||||
cutscenes.clear();
|
cutscenes.clear();
|
||||||
|
|
||||||
const std::string raw = ZL::readTextFile(path);
|
std::string raw;
|
||||||
if (raw.empty()) {
|
try {
|
||||||
std::cerr << "[dialogue] Failed to read file: " << path << "\n";
|
if (strlen(CONST_ZIP_FILE) == 0) {
|
||||||
return false;
|
raw = readTextFile(path);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
auto buf = readFileFromZIP(path, CONST_ZIP_FILE);
|
||||||
|
if (buf.empty()) {
|
||||||
|
std::cerr << "UiManager: failed to read " << path << " from zip " << CONST_ZIP_FILE << std::endl;
|
||||||
|
throw std::runtime_error("Failed to load UI file: " + path);
|
||||||
|
}
|
||||||
|
raw.assign(buf.begin(), buf.end());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const std::exception& e) {
|
||||||
|
std::cerr << "UiManager: failed to open " << path << " : " << e.what() << std::endl;
|
||||||
|
throw std::runtime_error("Failed to load UI file: " + path);
|
||||||
}
|
}
|
||||||
|
|
||||||
json root;
|
json root;
|
||||||
|
|||||||
@ -14,10 +14,40 @@ namespace ZL {
|
|||||||
std::vector<GameObjectData> GameObjectLoader::loadFromJson(const std::string& jsonPath, const std::string& zipPath)
|
std::vector<GameObjectData> GameObjectLoader::loadFromJson(const std::string& jsonPath, const std::string& zipPath)
|
||||||
{
|
{
|
||||||
std::vector<GameObjectData> objects;
|
std::vector<GameObjectData> objects;
|
||||||
|
|
||||||
|
std::string content;
|
||||||
|
try {
|
||||||
|
if (zipPath.empty()) {
|
||||||
|
content = readTextFile(jsonPath);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
auto buf = readFileFromZIP(jsonPath, zipPath);
|
||||||
|
if (buf.empty()) {
|
||||||
|
std::cerr << "UiManager: failed to read " << jsonPath << " from zip " << zipPath << std::endl;
|
||||||
|
throw std::runtime_error("Failed to load UI file: " + jsonPath);
|
||||||
|
}
|
||||||
|
content.assign(buf.begin(), buf.end());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const std::exception& e) {
|
||||||
|
std::cerr << "UiManager: failed to open " << jsonPath << " : " << e.what() << std::endl;
|
||||||
|
throw std::runtime_error("Failed to load UI file: " + jsonPath);
|
||||||
|
}
|
||||||
|
|
||||||
json j;
|
json j;
|
||||||
|
try {
|
||||||
|
j = json::parse(content);
|
||||||
|
}
|
||||||
|
catch (const std::exception& e) {
|
||||||
|
std::cerr << "UiManager: json parse error: " << e.what() << std::endl;
|
||||||
|
throw std::runtime_error("Failed to load UI file: " + jsonPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//json j;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
std::ifstream file(jsonPath);
|
/*std::ifstream file(jsonPath);
|
||||||
if (!file.is_open()) {
|
if (!file.is_open()) {
|
||||||
throw std::runtime_error("Could not open file: " + jsonPath);
|
throw std::runtime_error("Could not open file: " + jsonPath);
|
||||||
}
|
}
|
||||||
@ -26,7 +56,7 @@ namespace ZL {
|
|||||||
throw std::runtime_error("JSON file is empty: " + jsonPath);
|
throw std::runtime_error("JSON file is empty: " + jsonPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
file >> j;
|
file >> j;*/
|
||||||
|
|
||||||
if (!j.contains("objects") || !j["objects"].is_array()) {
|
if (!j.contains("objects") || !j["objects"].is_array()) {
|
||||||
std::cerr << "Warning: 'objects' array not found in " << jsonPath << std::endl;
|
std::cerr << "Warning: 'objects' array not found in " << jsonPath << std::endl;
|
||||||
@ -255,10 +285,40 @@ namespace ZL {
|
|||||||
std::vector<NpcData> GameObjectLoader::loadNpcsFromJson(const std::string& jsonPath, const std::string& zipPath)
|
std::vector<NpcData> GameObjectLoader::loadNpcsFromJson(const std::string& jsonPath, const std::string& zipPath)
|
||||||
{
|
{
|
||||||
std::vector<NpcData> npcs;
|
std::vector<NpcData> npcs;
|
||||||
|
|
||||||
|
|
||||||
|
std::string content;
|
||||||
|
try {
|
||||||
|
if (zipPath.empty()) {
|
||||||
|
content = readTextFile(jsonPath);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
auto buf = readFileFromZIP(jsonPath, zipPath);
|
||||||
|
if (buf.empty()) {
|
||||||
|
std::cerr << "UiManager: failed to read " << jsonPath << " from zip " << zipPath << std::endl;
|
||||||
|
throw std::runtime_error("Failed to load UI file: " + jsonPath);
|
||||||
|
}
|
||||||
|
content.assign(buf.begin(), buf.end());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const std::exception& e) {
|
||||||
|
std::cerr << "UiManager: failed to open " << jsonPath << " : " << e.what() << std::endl;
|
||||||
|
throw std::runtime_error("Failed to load UI file: " + jsonPath);
|
||||||
|
}
|
||||||
|
|
||||||
json j;
|
json j;
|
||||||
|
try {
|
||||||
|
j = json::parse(content);
|
||||||
|
}
|
||||||
|
catch (const std::exception& e) {
|
||||||
|
std::cerr << "UiManager: json parse error: " << e.what() << std::endl;
|
||||||
|
throw std::runtime_error("Failed to load UI file: " + jsonPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
//json j;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
std::ifstream file(jsonPath);
|
/*std::ifstream file(jsonPath);
|
||||||
if (!file.is_open()) {
|
if (!file.is_open()) {
|
||||||
throw std::runtime_error("Could not open file: " + jsonPath);
|
throw std::runtime_error("Could not open file: " + jsonPath);
|
||||||
}
|
}
|
||||||
@ -267,7 +327,7 @@ namespace ZL {
|
|||||||
throw std::runtime_error("JSON file is empty: " + jsonPath);
|
throw std::runtime_error("JSON file is empty: " + jsonPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
file >> j;
|
file >> j;*/
|
||||||
|
|
||||||
if (!j.contains("npcs") || !j["npcs"].is_array()) {
|
if (!j.contains("npcs") || !j["npcs"].is_array()) {
|
||||||
std::cerr << "Warning: 'npcs' array not found in " << jsonPath << std::endl;
|
std::cerr << "Warning: 'npcs' array not found in " << jsonPath << std::endl;
|
||||||
|
|||||||
@ -12,7 +12,7 @@ namespace ZL {
|
|||||||
|
|
||||||
void InteractiveObject::draw(Renderer& renderer) const {
|
void InteractiveObject::draw(Renderer& renderer) const {
|
||||||
if (!isActive || !texture) return;
|
if (!isActive || !texture) return;
|
||||||
|
/*
|
||||||
std::cout << "[DRAW] InteractiveObject::draw() called" << std::endl;
|
std::cout << "[DRAW] InteractiveObject::draw() called" << std::endl;
|
||||||
std::cout << "[DRAW] Object: " << name << std::endl;
|
std::cout << "[DRAW] Object: " << name << std::endl;
|
||||||
std::cout << "[DRAW] Position: (" << position.x() << ", " << position.y() << ", " << position.z() << ")" << std::endl;
|
std::cout << "[DRAW] Position: (" << position.x() << ", " << position.y() << ", " << position.z() << ")" << std::endl;
|
||||||
@ -22,7 +22,7 @@ namespace ZL {
|
|||||||
std::cout << "[DRAW] First vertex: (" << mesh.data.PositionData[0].x() << ", "
|
std::cout << "[DRAW] First vertex: (" << mesh.data.PositionData[0].x() << ", "
|
||||||
<< mesh.data.PositionData[0].y() << ", " << mesh.data.PositionData[0].z() << ")" << std::endl;
|
<< mesh.data.PositionData[0].y() << ", " << mesh.data.PositionData[0].z() << ")" << std::endl;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
// Apply position transformation
|
// Apply position transformation
|
||||||
renderer.PushMatrix();
|
renderer.PushMatrix();
|
||||||
renderer.TranslateMatrix(position);
|
renderer.TranslateMatrix(position);
|
||||||
|
|||||||
@ -329,6 +329,16 @@ namespace ZL {
|
|||||||
size_t error = glGetError();
|
size_t error = glGetError();
|
||||||
if (error != GL_NO_ERROR)
|
if (error != GL_NO_ERROR)
|
||||||
{
|
{
|
||||||
|
std::cout << "OpenGL error: " << error << std::endl;
|
||||||
|
throw std::runtime_error("Gl error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void CheckGlError(const char* file, int line)
|
||||||
|
{
|
||||||
|
size_t error = glGetError();
|
||||||
|
if (error != GL_NO_ERROR)
|
||||||
|
{
|
||||||
|
std::cout << "OpenGL error: " << error << " happened in file: " << file << " at line: " << line << std::endl;
|
||||||
throw std::runtime_error("Gl error");
|
throw std::runtime_error("Gl error");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -161,4 +161,6 @@ namespace ZL {
|
|||||||
bool BindOpenGlFunctions();
|
bool BindOpenGlFunctions();
|
||||||
|
|
||||||
void CheckGlError();
|
void CheckGlError();
|
||||||
|
|
||||||
|
void CheckGlError(const char* file, int line);
|
||||||
}
|
}
|
||||||
@ -536,7 +536,7 @@ namespace ZL {
|
|||||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
glDepthFunc(GL_LEQUAL);
|
glDepthFunc(GL_LEQUAL);
|
||||||
|
|
||||||
CheckGlError();
|
CheckGlError(__FILE__, __LINE__);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Renderer::PushProjectionMatrix(float width, float height, float zNear, float zFar)
|
void Renderer::PushProjectionMatrix(float width, float height, float zNear, float zFar)
|
||||||
|
|||||||
@ -56,6 +56,22 @@ namespace ZL {
|
|||||||
{
|
{
|
||||||
glGenFramebuffers(1, &fbo);
|
glGenFramebuffers(1, &fbo);
|
||||||
|
|
||||||
|
#ifdef EMSCRIPTEN
|
||||||
|
glGenTextures(1, &depthTexture);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, depthTexture);
|
||||||
|
|
||||||
|
// Используем GL_DEPTH_COMPONENT24 для WebGL 2
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24,
|
||||||
|
resolution, resolution, 0,
|
||||||
|
GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL);
|
||||||
|
|
||||||
|
// ВАЖНО: Используем GL_NEAREST, так как GL_LINEAR часто ломает FBO в WebGL
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||||
|
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||||
|
#else
|
||||||
glGenTextures(1, &depthTexture);
|
glGenTextures(1, &depthTexture);
|
||||||
glBindTexture(GL_TEXTURE_2D, depthTexture);
|
glBindTexture(GL_TEXTURE_2D, depthTexture);
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT,
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT,
|
||||||
@ -65,17 +81,24 @@ namespace ZL {
|
|||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||||
|
#endif
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
||||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
|
||||||
GL_TEXTURE_2D, depthTexture, 0);
|
GL_TEXTURE_2D, depthTexture, 0);
|
||||||
|
|
||||||
// No color buffer for this FBO — depth only.
|
// No color buffer for this FBO — depth only.
|
||||||
|
#ifdef EMSCRIPTEN
|
||||||
|
GLenum drawBuffers[] = { GL_NONE };
|
||||||
|
glDrawBuffers(1, drawBuffers);
|
||||||
|
#else
|
||||||
glDrawBuffer(GL_NONE);
|
glDrawBuffer(GL_NONE);
|
||||||
glReadBuffer(GL_NONE);
|
glReadBuffer(GL_NONE);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
|
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||||
std::cerr << "Error: Shadow map framebuffer is not complete!" << std::endl;
|
if (status != GL_FRAMEBUFFER_COMPLETE) {
|
||||||
|
std::cerr << "Error: Shadow map framebuffer is not complete! Status: 0x"
|
||||||
|
<< std::hex << status << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
|
|||||||
@ -54,13 +54,13 @@ bool TextRenderer::init(Renderer& renderer, const std::string& ttfPath, int pixe
|
|||||||
zipfilename
|
zipfilename
|
||||||
);
|
);
|
||||||
#endif
|
#endif
|
||||||
ZL::CheckGlError();
|
ZL::CheckGlError(__FILE__, __LINE__);
|
||||||
if (!loadGlyphs(ttfPath, pixelSize, zipfilename)) return false;
|
if (!loadGlyphs(ttfPath, pixelSize, zipfilename)) return false;
|
||||||
ZL::CheckGlError();
|
ZL::CheckGlError(__FILE__, __LINE__);
|
||||||
|
|
||||||
textMesh.data.PositionData.resize(6, Eigen::Vector3f(0, 0, 0));
|
textMesh.data.PositionData.resize(6, Eigen::Vector3f(0, 0, 0));
|
||||||
textMesh.RefreshVBO();
|
textMesh.RefreshVBO();
|
||||||
ZL::CheckGlError();
|
ZL::CheckGlError(__FILE__, __LINE__);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -54,7 +54,7 @@ namespace ZL
|
|||||||
glTexImage2D(GL_TEXTURE_2D, 0, internalFormat,
|
glTexImage2D(GL_TEXTURE_2D, 0, internalFormat,
|
||||||
static_cast<GLsizei>(width), static_cast<GLsizei>(height),
|
static_cast<GLsizei>(width), static_cast<GLsizei>(height),
|
||||||
0, externalFormat, GL_UNSIGNED_BYTE, texData.data.data());
|
0, externalFormat, GL_UNSIGNED_BYTE, texData.data.data());
|
||||||
CheckGlError();
|
CheckGlError(__FILE__, __LINE__);
|
||||||
|
|
||||||
// 3. Фильтрация и Мип-мапы
|
// 3. Фильтрация и Мип-мапы
|
||||||
// ВНИМАНИЕ: Для шрифтов (NPOT) в WebGL glGenerateMipmap работать НЕ будет!
|
// ВНИМАНИЕ: Для шрифтов (NPOT) в WebGL glGenerateMipmap работать НЕ будет!
|
||||||
@ -67,7 +67,7 @@ namespace ZL
|
|||||||
}
|
}
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
|
|
||||||
CheckGlError();
|
CheckGlError(__FILE__, __LINE__);
|
||||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,7 +91,7 @@ namespace ZL
|
|||||||
|
|
||||||
glBindTexture(GL_TEXTURE_CUBE_MAP, texID);
|
glBindTexture(GL_TEXTURE_CUBE_MAP, texID);
|
||||||
|
|
||||||
CheckGlError();
|
CheckGlError(__FILE__, __LINE__);
|
||||||
|
|
||||||
|
|
||||||
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
@ -106,7 +106,7 @@ namespace ZL
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
CheckGlError();
|
CheckGlError(__FILE__, __LINE__);
|
||||||
|
|
||||||
for (int i = 0; i < 6; ++i)
|
for (int i = 0; i < 6; ++i)
|
||||||
{
|
{
|
||||||
@ -136,7 +136,7 @@ namespace ZL
|
|||||||
GL_UNSIGNED_BYTE,
|
GL_UNSIGNED_BYTE,
|
||||||
texDataArray[i].data.data()
|
texDataArray[i].data.data()
|
||||||
);
|
);
|
||||||
CheckGlError();
|
CheckGlError(__FILE__, __LINE__);
|
||||||
}
|
}
|
||||||
|
|
||||||
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
|
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user