diff --git a/blender scripts/generate_tree001.py b/blender scripts/generate_tree001.py
new file mode 100644
index 0000000..5f34c80
--- /dev/null
+++ b/blender scripts/generate_tree001.py
@@ -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()
+
diff --git a/config/ui.json b/config/ui.json
deleted file mode 100644
index 974d34a..0000000
--- a/config/ui.json
+++ /dev/null
@@ -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"
- }
- }
- ]
- }
-}
\ No newline at end of file
diff --git a/proj-web/CMakeLists.txt b/proj-web/CMakeLists.txt
index e1fd3a7..2d5b599 100644
--- a/proj-web/CMakeLists.txt
+++ b/proj-web/CMakeLists.txt
@@ -8,7 +8,7 @@ if(NOT CMAKE_MAKE_PROGRAM AND WIN32)
endif()
endif()
-project(space-game001 LANGUAGES C CXX)
+project(bishkek-witcher LANGUAGES C CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
@@ -83,49 +83,49 @@ set(SOURCES
../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/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.cpp
../src/MenuManager.h
../src/MenuManager.cpp
-# ../src/Space.h
-# ../src/Space.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
)
-add_executable(space-game001 ${SOURCES})
+add_executable(bishkek-witcher ${SOURCES})
# Настройка путей к инклудам (используем скачанные исходники)
-target_include_directories(space-game001 PRIVATE
+target_include_directories(bishkek-witcher PRIVATE
../src
../thirdparty/eigen-5.0.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)
-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
@@ -155,7 +155,7 @@ set(EMSCRIPTEN_FLAGS
"-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.
# resources.zip is downloaded asynchronously at runtime and served as a separate file.
@@ -171,11 +171,11 @@ set(EMSCRIPTEN_LINK_FLAGS
)
# Применяем настройки линковки
-target_link_options(space-game001 PRIVATE ${EMSCRIPTEN_LINK_FLAGS})
+target_link_options(bishkek-witcher PRIVATE ${EMSCRIPTEN_LINK_FLAGS})
# Для совместимости со старыми версиями CMake, если target_link_options недостаточно
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}"
SUFFIX ".html"
)
@@ -193,33 +193,33 @@ add_custom_command(
)
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")
# 1. Устанавливаем основной HTML файл
-install(TARGETS space-game001
+install(TARGETS bishkek-witcher
RUNTIME DESTINATION .
)
# 2. Устанавливаем сопутствующие файлы (JS, WASM и сгенерированный архив данных)
install(FILES
- "${CMAKE_CURRENT_BINARY_DIR}/space-game001.js"
- "${CMAKE_CURRENT_BINARY_DIR}/space-game001.wasm"
- "${CMAKE_CURRENT_BINARY_DIR}/space-game001.data"
+ "${CMAKE_CURRENT_BINARY_DIR}/bishkek-witcher.js"
+ "${CMAKE_CURRENT_BINARY_DIR}/bishkek-witcher.wasm"
+ "${CMAKE_CURRENT_BINARY_DIR}/bishkek-witcher.data"
DESTINATION .
)
-install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/space-game001plain.html"
+install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/index.html"
DESTINATION .
)
# resources.zip is served separately and downloaded asynchronously at runtime
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 .
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
COMMENT "Automatically deploying to public directory..."
diff --git a/proj-web/index.html b/proj-web/index.html
new file mode 100644
index 0000000..2f35ddb
--- /dev/null
+++ b/proj-web/index.html
@@ -0,0 +1,82 @@
+
+
+
+
+
+ Bishkek Witcher
+
+
+
+
+ Downloading...
+
+
+
+
+
+
diff --git a/proj-windows/CMakeLists.txt b/proj-windows/CMakeLists.txt
index 0ec0df8..5bcf89b 100644
--- a/proj-windows/CMakeLists.txt
+++ b/proj-windows/CMakeLists.txt
@@ -46,8 +46,6 @@ add_executable(space-game001
../src/render/ShadowMap.h
../src/UiManager.cpp
../src/UiManager.h
-# ../src/Projectile.h
-# ../src/Projectile.cpp
../src/render/TextRenderer.h
../src/render/TextRenderer.cpp
../src/MenuManager.h
diff --git a/resources/dialogue/sample_dialogues.json b/resources/dialogue/sample_dialogues.json
index b232fa4..afb586e 100644
--- a/resources/dialogue/sample_dialogues.json
+++ b/resources/dialogue/sample_dialogues.json
@@ -59,7 +59,7 @@
"type": "Choice",
"speaker": "Hero",
"portrait": "resources/hero.png",
- "text": "Choose your answer.",
+ "text": "",
"choices": [
{
"id": "main_1",
diff --git a/src/BoneAnimatedModel.cpp b/src/BoneAnimatedModel.cpp
index 099e6d3..8d7ccc6 100644
--- a/src/BoneAnimatedModel.cpp
+++ b/src/BoneAnimatedModel.cpp
@@ -8,6 +8,10 @@
namespace ZL
{
+#ifdef EMSCRIPTEN
+ using std::min;
+ using std::max;
+#endif
int getIndexByValue(const std::string& name, const std::vector& words)
{
@@ -909,29 +913,51 @@ namespace ZL
void GpuSkinningShaderData::RenderVBO(Renderer& renderer)
{
+ CheckGlError(__FILE__, __LINE__);
// Bind position and texcoord VBOs
glBindBuffer(GL_ARRAY_BUFFER, bindPoseMutable.positionVBO->getBuffer());
renderer.VertexAttribPointer3fv("vPosition", 0, NULL);
+ CheckGlError(__FILE__, __LINE__);
if (bindPoseMutable.texCoordVBO) {
glBindBuffer(GL_ARRAY_BUFFER, bindPoseMutable.texCoordVBO->getBuffer());
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
glBindBuffer(GL_ARRAY_BUFFER, boneIndices0VBO->getBuffer());
renderer.VertexAttribPointer4fv("aBoneIndices0", 0, NULL);
+ CheckGlError(__FILE__, __LINE__);
+
glBindBuffer(GL_ARRAY_BUFFER, boneIndices1VBO->getBuffer());
renderer.VertexAttribPointer2fv("aBoneIndices1", 0, NULL);
+ CheckGlError(__FILE__, __LINE__);
+
// Bind bone weight VBOs
glBindBuffer(GL_ARRAY_BUFFER, boneWeights0VBO->getBuffer());
renderer.VertexAttribPointer4fv("aBoneWeights0", 0, NULL);
+ CheckGlError(__FILE__, __LINE__);
+
glBindBuffer(GL_ARRAY_BUFFER, boneWeights1VBO->getBuffer());
renderer.VertexAttribPointer2fv("aBoneWeights1", 0, NULL);
+ CheckGlError(__FILE__, __LINE__);
+
glDrawArrays(GL_TRIANGLES, 0, static_cast(bindPoseMutable.data.PositionData.size()));
}
diff --git a/src/Character.cpp b/src/Character.cpp
index 3360992..6e3e2dc 100644
--- a/src/Character.cpp
+++ b/src/Character.cpp
@@ -417,12 +417,18 @@ void Character::drawShadowDepthCpu(Renderer& renderer) {
}
void Character::drawShadowDepthGpuSkinning(Renderer& renderer) {
+
+ CheckGlError(__FILE__, __LINE__);
AnimationState drawState = resolveActiveState();
auto it = animations.find(drawState);
if (it == animations.end()) return;
+ CheckGlError(__FILE__, __LINE__);
+
if (!prepareGpuSkinning()) return;
+ CheckGlError(__FILE__, __LINE__);
+
static const std::string shadowSkinningShader = "shadow_depth_skinning";
static const std::string boneMatricesUniform = "uBoneMatrices[0]";
@@ -435,14 +441,19 @@ void Character::drawShadowDepthGpuSkinning(Renderer& renderer) {
renderer.ScaleMatrix(modelScale);
renderer.RotateMatrix(modelCorrectionRotation.toRotationMatrix());
+ CheckGlError(__FILE__, __LINE__);
renderer.RenderUniformMatrix4fvArray(boneMatricesUniform,
static_cast(it->second.gpuSkinningShaderData.skinningMatrices.size()), false,
it->second.gpuSkinningShaderData.skinningMatrices[0].data());
+ CheckGlError(__FILE__, __LINE__);
it->second.gpuSkinningShaderData.RenderVBO(renderer);
+ CheckGlError(__FILE__, __LINE__);
renderer.PopMatrix();
renderer.shaderManager.PopShader();
+
+ CheckGlError(__FILE__, __LINE__);
}
// ==================== Main pass with shadows ====================
@@ -498,8 +509,12 @@ void Character::drawGpuSkinningWithShadow(Renderer& renderer, const Eigen::Matri
auto it = animations.find(drawState);
if (it == animations.end() || !texture) return;
+ CheckGlError(__FILE__, __LINE__);
+
if (!prepareGpuSkinning()) return;
+ CheckGlError(__FILE__, __LINE__);
+
static const std::string skinningShadowShader = "skinning_shadow";
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.RenderUniform3fv("uLightDir", lightDirCamera.data());
+ CheckGlError(__FILE__, __LINE__);
+
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, shadowMapTex);
glActiveTexture(GL_TEXTURE0);
+ CheckGlError(__FILE__, __LINE__);
+
renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5,
static_cast(Environment::width) / static_cast(Environment::height),
Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
@@ -523,17 +542,24 @@ void Character::drawGpuSkinningWithShadow(Renderer& renderer, const Eigen::Matri
renderer.ScaleMatrix(modelScale);
renderer.RotateMatrix(modelCorrectionRotation.toRotationMatrix());
+ CheckGlError(__FILE__, __LINE__);
renderer.RenderUniformMatrix4fvArray(boneMatricesUniform,
static_cast(it->second.gpuSkinningShaderData.skinningMatrices.size()), false,
it->second.gpuSkinningShaderData.skinningMatrices[0].data());
+ CheckGlError(__FILE__, __LINE__);
glBindTexture(GL_TEXTURE_2D, texture->getTexID());
+ CheckGlError(__FILE__, __LINE__);
it->second.gpuSkinningShaderData.RenderVBO(renderer);
+ CheckGlError(__FILE__, __LINE__);
+
renderer.PopMatrix();
renderer.PopProjectionMatrix();
renderer.shaderManager.PopShader();
+
+ CheckGlError(__FILE__, __LINE__);
}
} // namespace ZL
diff --git a/src/Game.cpp b/src/Game.cpp
index c2cf608..6eeacff 100644
--- a/src/Game.cpp
+++ b/src/Game.cpp
@@ -72,7 +72,7 @@ namespace ZL
Environment::computeProjectionDimensions();
ZL::BindOpenGlFunctions();
- ZL::CheckGlError();
+ ZL::CheckGlError(__FILE__, __LINE__);
renderer.InitOpenGL();
#ifdef EMSCRIPTEN
@@ -236,7 +236,7 @@ namespace ZL
glDepthMask(GL_TRUE);
glEnable(GL_DEPTH_TEST);
- CheckGlError();
+ CheckGlError(__FILE__, __LINE__);
}
void Game::drawScene() {
@@ -249,11 +249,15 @@ namespace ZL
if (currentLocation)
{
if (currentLocation->shadowMap) {
+ CheckGlError(__FILE__, __LINE__);
currentLocation->drawShadowDepthPass();
+ CheckGlError(__FILE__, __LINE__);
currentLocation->drawGameWithShadows();
+ CheckGlError(__FILE__, __LINE__);
}
else {
currentLocation->drawGame();
+ CheckGlError(__FILE__, __LINE__);
}
}
else
@@ -262,7 +266,7 @@ namespace ZL
}
drawUI();
}
- CheckGlError();
+ CheckGlError(__FILE__, __LINE__);
}
void Game::drawLoading()
@@ -289,7 +293,7 @@ namespace ZL
renderer.PopMatrix();
renderer.PopProjectionMatrix();
renderer.shaderManager.PopShader();
- CheckGlError();
+ CheckGlError(__FILE__, __LINE__);
}
@@ -329,7 +333,7 @@ namespace ZL
}
void Game::render() {
- ZL::CheckGlError();
+ ZL::CheckGlError(__FILE__, __LINE__);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
diff --git a/src/Location.cpp b/src/Location.cpp
index a873de9..71e6884 100644
--- a/src/Location.cpp
+++ b/src/Location.cpp
@@ -9,7 +9,7 @@
#include
#include
#include
-
+#include
#include "GameConstants.h"
@@ -449,19 +449,23 @@ namespace ZL
renderer.RenderUniform3fv("uLightDir", lightDirCamera.data());
#endif
+ CheckGlError(__FILE__, __LINE__);
glBindTexture(GL_TEXTURE_2D, roomTexture->getTexID());
renderer.DrawVertexRenderStruct(roomMesh);
+ CheckGlError(__FILE__, __LINE__);
for (auto& [name, gameObj] : gameObjects) {
glBindTexture(GL_TEXTURE_2D, gameObj.texture->getTexID());
renderer.DrawVertexRenderStruct(gameObj.mesh);
}
+ CheckGlError(__FILE__, __LINE__);
for (auto& intObj : interactiveObjects) {
if (intObj.isActive) {
intObj.draw(renderer);
}
}
+ CheckGlError(__FILE__, __LINE__);
#ifdef DEBUG_LIGHT
// In debug-light mode characters use the plain shaders (draw normally
@@ -470,9 +474,14 @@ namespace ZL
for (auto& npc : npcs) npc->draw(renderer);
#else
// Characters use their own shadow-aware shaders
+ CheckGlError(__FILE__, __LINE__);
+
if (player) player->drawWithShadow(renderer, lightFromCamera, shadowMap->getDepthTexture(), lightDirCamera);
+ CheckGlError(__FILE__, __LINE__);
+
for (auto& npc : npcs) npc->drawWithShadow(renderer, lightFromCamera, shadowMap->getDepthTexture(), lightDirCamera);
#endif
+ CheckGlError(__FILE__, __LINE__);
renderer.PopMatrix();
renderer.PopProjectionMatrix();
diff --git a/src/Projectile.cpp b/src/Projectile.cpp
deleted file mode 100644
index ee2eaba..0000000
--- a/src/Projectile.cpp
+++ /dev/null
@@ -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 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
\ No newline at end of file
diff --git a/src/Projectile.h b/src/Projectile.h
deleted file mode 100644
index 2f3b4cc..0000000
--- a/src/Projectile.h
+++ /dev/null
@@ -1,38 +0,0 @@
-#pragma once
-
-#include "render/Renderer.h"
-#include "render/TextureManager.h"
-#include
-#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 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;
-
- void rebuildMesh(Renderer& renderer);
- };
-
-} // namespace ZL
\ No newline at end of file
diff --git a/src/dialogue/DialogueDatabase.cpp b/src/dialogue/DialogueDatabase.cpp
index 93ada15..bc5db6d 100644
--- a/src/dialogue/DialogueDatabase.cpp
+++ b/src/dialogue/DialogueDatabase.cpp
@@ -3,6 +3,11 @@
#include "utils/Utils.h"
#include
+namespace ZL
+{
+ extern const char* CONST_ZIP_FILE;
+}
+
namespace ZL::Dialogue {
NodeType DialogueDatabase::parseNodeType(const std::string& value) {
@@ -202,10 +207,23 @@ bool DialogueDatabase::loadFromFile(const std::string& path) {
dialogues.clear();
cutscenes.clear();
- const std::string raw = ZL::readTextFile(path);
- if (raw.empty()) {
- std::cerr << "[dialogue] Failed to read file: " << path << "\n";
- return false;
+ std::string raw;
+ try {
+ if (strlen(CONST_ZIP_FILE) == 0) {
+ 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;
diff --git a/src/items/GameObjectLoader.cpp b/src/items/GameObjectLoader.cpp
index fb5a9c9..c7cf2a9 100644
--- a/src/items/GameObjectLoader.cpp
+++ b/src/items/GameObjectLoader.cpp
@@ -14,10 +14,40 @@ namespace ZL {
std::vector GameObjectLoader::loadFromJson(const std::string& jsonPath, const std::string& zipPath)
{
std::vector 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;
+ 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 {
- std::ifstream file(jsonPath);
+ /*std::ifstream file(jsonPath);
if (!file.is_open()) {
throw std::runtime_error("Could not open file: " + jsonPath);
}
@@ -26,7 +56,7 @@ namespace ZL {
throw std::runtime_error("JSON file is empty: " + jsonPath);
}
- file >> j;
+ file >> j;*/
if (!j.contains("objects") || !j["objects"].is_array()) {
std::cerr << "Warning: 'objects' array not found in " << jsonPath << std::endl;
@@ -255,10 +285,40 @@ namespace ZL {
std::vector GameObjectLoader::loadNpcsFromJson(const std::string& jsonPath, const std::string& zipPath)
{
std::vector 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;
+ 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 {
- std::ifstream file(jsonPath);
+ /*std::ifstream file(jsonPath);
if (!file.is_open()) {
throw std::runtime_error("Could not open file: " + jsonPath);
}
@@ -267,7 +327,7 @@ namespace ZL {
throw std::runtime_error("JSON file is empty: " + jsonPath);
}
- file >> j;
+ file >> j;*/
if (!j.contains("npcs") || !j["npcs"].is_array()) {
std::cerr << "Warning: 'npcs' array not found in " << jsonPath << std::endl;
diff --git a/src/render/OpenGlExtensions.cpp b/src/render/OpenGlExtensions.cpp
index f57e1c5..294d25c 100644
--- a/src/render/OpenGlExtensions.cpp
+++ b/src/render/OpenGlExtensions.cpp
@@ -329,6 +329,16 @@ namespace ZL {
size_t error = glGetError();
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");
}
}
diff --git a/src/render/OpenGlExtensions.h b/src/render/OpenGlExtensions.h
index a80ddcb..af73844 100644
--- a/src/render/OpenGlExtensions.h
+++ b/src/render/OpenGlExtensions.h
@@ -161,4 +161,6 @@ namespace ZL {
bool BindOpenGlFunctions();
void CheckGlError();
+
+ void CheckGlError(const char* file, int line);
}
\ No newline at end of file
diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp
index 83e2ab1..aebce47 100644
--- a/src/render/Renderer.cpp
+++ b/src/render/Renderer.cpp
@@ -536,7 +536,7 @@ namespace ZL {
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDepthFunc(GL_LEQUAL);
- CheckGlError();
+ CheckGlError(__FILE__, __LINE__);
}
void Renderer::PushProjectionMatrix(float width, float height, float zNear, float zFar)
diff --git a/src/render/ShadowMap.cpp b/src/render/ShadowMap.cpp
index f689ed5..55e3503 100644
--- a/src/render/ShadowMap.cpp
+++ b/src/render/ShadowMap.cpp
@@ -56,6 +56,22 @@ namespace ZL {
{
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);
glBindTexture(GL_TEXTURE_2D, depthTexture);
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_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-
+#endif
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
GL_TEXTURE_2D, depthTexture, 0);
// No color buffer for this FBO — depth only.
+#ifdef EMSCRIPTEN
+ GLenum drawBuffers[] = { GL_NONE };
+ glDrawBuffers(1, drawBuffers);
+#else
glDrawBuffer(GL_NONE);
glReadBuffer(GL_NONE);
-
- if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
- std::cerr << "Error: Shadow map framebuffer is not complete!" << std::endl;
+#endif
+
+ GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+ 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);
diff --git a/src/render/TextRenderer.cpp b/src/render/TextRenderer.cpp
index 5cbb122..ad1e915 100644
--- a/src/render/TextRenderer.cpp
+++ b/src/render/TextRenderer.cpp
@@ -54,13 +54,13 @@ bool TextRenderer::init(Renderer& renderer, const std::string& ttfPath, int pixe
zipfilename
);
#endif
- ZL::CheckGlError();
+ ZL::CheckGlError(__FILE__, __LINE__);
if (!loadGlyphs(ttfPath, pixelSize, zipfilename)) return false;
- ZL::CheckGlError();
+ ZL::CheckGlError(__FILE__, __LINE__);
textMesh.data.PositionData.resize(6, Eigen::Vector3f(0, 0, 0));
textMesh.RefreshVBO();
- ZL::CheckGlError();
+ ZL::CheckGlError(__FILE__, __LINE__);
return true;
}
diff --git a/src/render/TextureManager.cpp b/src/render/TextureManager.cpp
index e5373bf..e348e7b 100644
--- a/src/render/TextureManager.cpp
+++ b/src/render/TextureManager.cpp
@@ -54,7 +54,7 @@ namespace ZL
glTexImage2D(GL_TEXTURE_2D, 0, internalFormat,
static_cast(width), static_cast(height),
0, externalFormat, GL_UNSIGNED_BYTE, texData.data.data());
- CheckGlError();
+ CheckGlError(__FILE__, __LINE__);
// 3. Фильтрация и Мип-мапы
// ВНИМАНИЕ: Для шрифтов (NPOT) в WebGL glGenerateMipmap работать НЕ будет!
@@ -67,7 +67,7 @@ namespace ZL
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- CheckGlError();
+ CheckGlError(__FILE__, __LINE__);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
}
@@ -91,7 +91,7 @@ namespace ZL
glBindTexture(GL_TEXTURE_CUBE_MAP, texID);
- CheckGlError();
+ CheckGlError(__FILE__, __LINE__);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
@@ -106,7 +106,7 @@ namespace ZL
#endif
#endif
- CheckGlError();
+ CheckGlError(__FILE__, __LINE__);
for (int i = 0; i < 6; ++i)
{
@@ -136,7 +136,7 @@ namespace ZL
GL_UNSIGNED_BYTE,
texDataArray[i].data.data()
);
- CheckGlError();
+ CheckGlError(__FILE__, __LINE__);
}
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);