Compare commits
7 Commits
4de8f78eca
...
e04fcbb9bd
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e04fcbb9bd | ||
|
|
519d780b3c | ||
|
|
16d250a51d | ||
|
|
5231b50aab | ||
| 8b0a18a6cf | |||
|
|
b98631a8c0 | ||
| cebbb8bb5d |
BIN
audio/background.wav
(Stored with Git LFS)
BIN
audio/background.wav
(Stored with Git LFS)
Binary file not shown.
BIN
audio/lullaby-music-vol20-186394--online-audio-convert.com.ogg
(Stored with Git LFS)
BIN
audio/lullaby-music-vol20-186394--online-audio-convert.com.ogg
(Stored with Git LFS)
Binary file not shown.
@ -52,6 +52,7 @@ add_library(main SHARED
|
||||
../../../../src/planet/PlanetObject.cpp
|
||||
../../../../src/planet/StoneObject.cpp
|
||||
../../../../src/render/FrameBuffer.cpp
|
||||
../../../../src/render/ShadowMap.cpp
|
||||
../../../../src/render/Renderer.cpp
|
||||
../../../../src/render/ShaderManager.cpp
|
||||
../../../../src/render/TextureManager.cpp
|
||||
|
||||
@ -175,6 +175,7 @@ set(EMSCRIPTEN_LINK_FLAGS
|
||||
"--preload-file ${CMAKE_CURRENT_SOURCE_DIR}/../resources/loading.png@resources/loading.png"
|
||||
"--preload-file ${CMAKE_CURRENT_SOURCE_DIR}/../resources/shaders@resources/shaders"
|
||||
"--preload-file ${CMAKE_CURRENT_SOURCE_DIR}/../resources/start.lua@resources/start.lua"
|
||||
"--preload-file ${CMAKE_CURRENT_SOURCE_DIR}/../resources/start2.lua@resources/start2.lua"
|
||||
"--preload-file ${CMAKE_CURRENT_SOURCE_DIR}/../audio@audio"
|
||||
)
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
project(space-game001 LANGUAGES C CXX)
|
||||
project(witcher001 LANGUAGES C CXX)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
@ -12,9 +12,9 @@ option(FULLSCREEN "Launch the game in fullscreen mode" OFF)
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/../cmake/ThirdParty.cmake)
|
||||
|
||||
# ===========================================
|
||||
# Основной проект space-game001
|
||||
# Основной проект witcher001
|
||||
# ===========================================
|
||||
add_executable(space-game001
|
||||
add_executable(witcher001
|
||||
../src/main.cpp
|
||||
../src/Game.cpp
|
||||
../src/Game.h
|
||||
@ -32,8 +32,6 @@ add_executable(space-game001
|
||||
../src/TextModel.h
|
||||
../src/AudioPlayerAsync.cpp
|
||||
../src/AudioPlayerAsync.h
|
||||
../src/BoneAnimatedModel.cpp
|
||||
../src/BoneAnimatedModel.h
|
||||
../src/BoneAnimatedModelNew.cpp
|
||||
../src/BoneAnimatedModelNew.h
|
||||
../src/render/OpenGlExtensions.cpp
|
||||
@ -42,8 +40,6 @@ add_executable(space-game001
|
||||
../src/utils/Utils.h
|
||||
../src/SparkEmitter.cpp
|
||||
../src/SparkEmitter.h
|
||||
../src/utils/Perlin.cpp
|
||||
../src/utils/Perlin.h
|
||||
../src/utils/TaskManager.cpp
|
||||
../src/utils/TaskManager.h
|
||||
../src/render/FrameBuffer.cpp
|
||||
@ -85,21 +81,21 @@ add_executable(space-game001
|
||||
)
|
||||
|
||||
# Установка проекта по умолчанию для Visual Studio
|
||||
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT space-game001)
|
||||
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT witcher001)
|
||||
|
||||
# include-пути проекта
|
||||
target_include_directories(space-game001 PRIVATE
|
||||
target_include_directories(witcher001 PRIVATE
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/../src"
|
||||
)
|
||||
|
||||
set_target_properties(space-game001 PROPERTIES
|
||||
OUTPUT_NAME "space-game001"
|
||||
set_target_properties(witcher001 PROPERTIES
|
||||
OUTPUT_NAME "witcher001"
|
||||
)
|
||||
|
||||
# Определения препроцессора:
|
||||
# PNG_ENABLED – включает код PNG в TextureManager
|
||||
# SDL_MAIN_HANDLED – отключает переопределение main -> SDL_main
|
||||
target_compile_definitions(space-game001 PRIVATE
|
||||
target_compile_definitions(witcher001 PRIVATE
|
||||
WIN32_LEAN_AND_MEAN
|
||||
PNG_ENABLED
|
||||
SDL_MAIN_HANDLED
|
||||
@ -108,14 +104,14 @@ target_compile_definitions(space-game001 PRIVATE
|
||||
)
|
||||
|
||||
if(FULLSCREEN)
|
||||
target_compile_definitions(space-game001 PRIVATE FULLSCREEN)
|
||||
target_compile_definitions(witcher001 PRIVATE FULLSCREEN)
|
||||
endif()
|
||||
|
||||
# Линкуем с SDL2main, если он вообще установлен
|
||||
target_link_libraries(space-game001 PRIVATE SDL2main_external_lib)
|
||||
target_link_libraries(witcher001 PRIVATE SDL2main_external_lib)
|
||||
|
||||
# Линкуем сторонние библиотеки
|
||||
target_link_libraries(space-game001 PRIVATE
|
||||
target_link_libraries(witcher001 PRIVATE
|
||||
SDL2_external_lib
|
||||
libpng_external_lib
|
||||
zlib_external_lib
|
||||
@ -130,7 +126,7 @@ target_link_libraries(space-game001 PRIVATE
|
||||
|
||||
# Линкуем OpenGL (Windows)
|
||||
if(WIN32)
|
||||
target_link_libraries(space-game001 PRIVATE opengl32)
|
||||
target_link_libraries(witcher001 PRIVATE opengl32)
|
||||
endif()
|
||||
|
||||
# ===========================================
|
||||
@ -140,19 +136,19 @@ if (WIN32)
|
||||
|
||||
# SDL2: в Debug - SDL2d.dll, в Release - SDL2.dll
|
||||
set(SDL2_DLL_SRC "$<IF:$<CONFIG:Debug>,${SDL2_INSTALL_DIR}/bin/SDL2d.dll,${SDL2_INSTALL_DIR}/bin/SDL2.dll>")
|
||||
set(SDL2_DLL_DST "$<IF:$<CONFIG:Debug>,$<TARGET_FILE_DIR:space-game001>/SDL2d.dll,$<TARGET_FILE_DIR:space-game001>/SDL2.dll>")
|
||||
set(SDL2_DLL_DST "$<IF:$<CONFIG:Debug>,$<TARGET_FILE_DIR:witcher001>/SDL2d.dll,$<TARGET_FILE_DIR:witcher001>/SDL2.dll>")
|
||||
|
||||
|
||||
set(LIBZIP_DLL_SRC "$<IF:$<CONFIG:Debug>,${LIBZIP_BASE_DIR}-Debug/bin/zip.dll,${LIBZIP_BASE_DIR}-Release/bin/zip.dll>")
|
||||
|
||||
set(ZLIB_DLL_SRC "$<IF:$<CONFIG:Debug>,${ZLIB_INSTALL_DIR}/bin/zd.dll,${ZLIB_INSTALL_DIR}/bin/z.dll>")
|
||||
set(ZLIB_DLL_DST "$<IF:$<CONFIG:Debug>,$<TARGET_FILE_DIR:space-game001>/zd.dll,$<TARGET_FILE_DIR:space-game001>/z.dll>")
|
||||
set(ZLIB_DLL_DST "$<IF:$<CONFIG:Debug>,$<TARGET_FILE_DIR:witcher001>/zd.dll,$<TARGET_FILE_DIR:witcher001>/z.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 witcher001 POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E echo "Copying DLLs to output folder..."
|
||||
|
||||
# Копируем SDL2 (целевое имя всегда SDL2.dll)
|
||||
@ -163,7 +159,7 @@ if (WIN32)
|
||||
# Копируем LIBZIP (целевое имя всегда zip.dll)
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
"${LIBZIP_DLL_SRC}"
|
||||
"$<TARGET_FILE_DIR:space-game001>/zip.dll"
|
||||
"$<TARGET_FILE_DIR:witcher001>/zip.dll"
|
||||
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
"${ZLIB_DLL_SRC}"
|
||||
@ -171,11 +167,11 @@ if (WIN32)
|
||||
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
"${SDL2TTF_DLL_SRC}"
|
||||
"$<TARGET_FILE_DIR:space-game001>"
|
||||
"$<TARGET_FILE_DIR:witcher001>"
|
||||
|
||||
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"
|
||||
"$<TARGET_FILE_DIR:witcher001>/SDL2_mixer$<$<CONFIG:Debug>:d>.dll"
|
||||
)
|
||||
endif()
|
||||
|
||||
@ -191,12 +187,12 @@ set(RUNTIME_RESOURCE_DIRS
|
||||
|
||||
# Копируем ресурсы и шейдеры в папку exe и в корень build/
|
||||
foreach(resdir IN LISTS RUNTIME_RESOURCE_DIRS)
|
||||
add_custom_command(TARGET space-game001 POST_BUILD
|
||||
add_custom_command(TARGET witcher001 POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E echo "Copying ${resdir} to runtime folders..."
|
||||
# 1) туда, где лежит exe (build/Debug, build/Release и т.п.)
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
"${CMAKE_SOURCE_DIR}/../${resdir}"
|
||||
"$<TARGET_FILE_DIR:space-game001>/${resdir}"
|
||||
"$<TARGET_FILE_DIR:witcher001>/${resdir}"
|
||||
# 2) в корень build, если захочешь запускать из этой папки
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
"${CMAKE_SOURCE_DIR}/../${resdir}"
|
||||
|
||||
@ -3,175 +3,472 @@
|
||||
"agentRadius": 0.45,
|
||||
"floorY": 0.0,
|
||||
"objectPadding": 0.25,
|
||||
"boundaryPadding": 0.35,
|
||||
"areas": [
|
||||
{
|
||||
"name": "main_corridor",
|
||||
"available": true,
|
||||
"polygon": [
|
||||
[-2.2, 0.8],
|
||||
[2.2, 0.8],
|
||||
[2.2, -40.8],
|
||||
[-2.2, -40.8]
|
||||
[
|
||||
-2.2,
|
||||
0.8
|
||||
],
|
||||
[
|
||||
2.2,
|
||||
0.8
|
||||
],
|
||||
[
|
||||
2.2,
|
||||
-40.8
|
||||
],
|
||||
[
|
||||
-2.2,
|
||||
-40.8
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "left_door_05",
|
||||
"available": true,
|
||||
"polygon": [
|
||||
[-3.4, -4.2],
|
||||
[-2.0, -4.2],
|
||||
[-2.0, -5.8],
|
||||
[-3.4, -5.8]
|
||||
[
|
||||
-3.4,
|
||||
-4.2
|
||||
],
|
||||
[
|
||||
-2.0,
|
||||
-4.2
|
||||
],
|
||||
[
|
||||
-2.0,
|
||||
-5.8
|
||||
],
|
||||
[
|
||||
-3.4,
|
||||
-5.8
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "right_door_05",
|
||||
"available": true,
|
||||
"polygon": [
|
||||
[2.0, -4.2],
|
||||
[3.4, -4.2],
|
||||
[3.4, -5.8],
|
||||
[2.0, -5.8]
|
||||
[
|
||||
2.0,
|
||||
-4.2
|
||||
],
|
||||
[
|
||||
3.4,
|
||||
-4.2
|
||||
],
|
||||
[
|
||||
3.4,
|
||||
-5.8
|
||||
],
|
||||
[
|
||||
2.0,
|
||||
-5.8
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "left_room_05",
|
||||
"available": true,
|
||||
"polygon": [
|
||||
[-8.8, -1.0],
|
||||
[-3.0, -1.0],
|
||||
[-3.0, -9.0],
|
||||
[-8.8, -9.0]
|
||||
[
|
||||
-8.8,
|
||||
-1.0
|
||||
],
|
||||
[
|
||||
-3.0,
|
||||
-1.0
|
||||
],
|
||||
[
|
||||
-3.0,
|
||||
-9.0
|
||||
],
|
||||
[
|
||||
-8.8,
|
||||
-9.0
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "right_room_05",
|
||||
"available": true,
|
||||
"polygon": [
|
||||
[3.0, -1.0],
|
||||
[8.8, -1.0],
|
||||
[8.8, -9.0],
|
||||
[3.0, -9.0]
|
||||
[
|
||||
3.0,
|
||||
-1.0
|
||||
],
|
||||
[
|
||||
8.8,
|
||||
-1.0
|
||||
],
|
||||
[
|
||||
8.8,
|
||||
-9.0
|
||||
],
|
||||
[
|
||||
3.0,
|
||||
-9.0
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "left_door_15",
|
||||
"available": true,
|
||||
"polygon": [
|
||||
[-3.4, -14.2],
|
||||
[-2.0, -14.2],
|
||||
[-2.0, -15.8],
|
||||
[-3.4, -15.8]
|
||||
[
|
||||
-3.4,
|
||||
-14.2
|
||||
],
|
||||
[
|
||||
-2.0,
|
||||
-14.2
|
||||
],
|
||||
[
|
||||
-2.0,
|
||||
-15.8
|
||||
],
|
||||
[
|
||||
-3.4,
|
||||
-15.8
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "right_door_15",
|
||||
"available": true,
|
||||
"polygon": [
|
||||
[2.0, -14.2],
|
||||
[3.4, -14.2],
|
||||
[3.4, -15.8],
|
||||
[2.0, -15.8]
|
||||
[
|
||||
2.0,
|
||||
-14.2
|
||||
],
|
||||
[
|
||||
3.4,
|
||||
-14.2
|
||||
],
|
||||
[
|
||||
3.4,
|
||||
-15.8
|
||||
],
|
||||
[
|
||||
2.0,
|
||||
-15.8
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "left_room_15",
|
||||
"available": true,
|
||||
"polygon": [
|
||||
[-8.8, -11.0],
|
||||
[-3.0, -11.0],
|
||||
[-3.0, -19.0],
|
||||
[-8.8, -19.0]
|
||||
[
|
||||
-8.8,
|
||||
-11.0
|
||||
],
|
||||
[
|
||||
-3.0,
|
||||
-11.0
|
||||
],
|
||||
[
|
||||
-3.0,
|
||||
-19.0
|
||||
],
|
||||
[
|
||||
-8.8,
|
||||
-19.0
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "right_room_15",
|
||||
"available": true,
|
||||
"polygon": [
|
||||
[3.0, -11.0],
|
||||
[8.8, -11.0],
|
||||
[8.8, -19.0],
|
||||
[3.0, -19.0]
|
||||
[
|
||||
3.0,
|
||||
-11.0
|
||||
],
|
||||
[
|
||||
8.8,
|
||||
-11.0
|
||||
],
|
||||
[
|
||||
8.8,
|
||||
-19.0
|
||||
],
|
||||
[
|
||||
3.0,
|
||||
-19.0
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "left_door_25",
|
||||
"available": true,
|
||||
"polygon": [
|
||||
[-3.4, -24.2],
|
||||
[-2.0, -24.2],
|
||||
[-2.0, -25.8],
|
||||
[-3.4, -25.8]
|
||||
[
|
||||
-3.4,
|
||||
-24.2
|
||||
],
|
||||
[
|
||||
-2.0,
|
||||
-24.2
|
||||
],
|
||||
[
|
||||
-2.0,
|
||||
-25.8
|
||||
],
|
||||
[
|
||||
-3.4,
|
||||
-25.8
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "right_door_25",
|
||||
"available": true,
|
||||
"polygon": [
|
||||
[2.0, -24.2],
|
||||
[3.4, -24.2],
|
||||
[3.4, -25.8],
|
||||
[2.0, -25.8]
|
||||
[
|
||||
2.0,
|
||||
-24.2
|
||||
],
|
||||
[
|
||||
3.4,
|
||||
-24.2
|
||||
],
|
||||
[
|
||||
3.4,
|
||||
-25.8
|
||||
],
|
||||
[
|
||||
2.0,
|
||||
-25.8
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "left_room_25",
|
||||
"available": true,
|
||||
"polygon": [
|
||||
[-8.8, -21.0],
|
||||
[-3.0, -21.0],
|
||||
[-3.0, -29.0],
|
||||
[-8.8, -29.0]
|
||||
[
|
||||
-8.8,
|
||||
-21.0
|
||||
],
|
||||
[
|
||||
-3.0,
|
||||
-21.0
|
||||
],
|
||||
[
|
||||
-3.0,
|
||||
-29.0
|
||||
],
|
||||
[
|
||||
-8.8,
|
||||
-29.0
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "right_room_25",
|
||||
"available": true,
|
||||
"polygon": [
|
||||
[3.0, -21.0],
|
||||
[8.8, -21.0],
|
||||
[8.8, -29.0],
|
||||
[3.0, -29.0]
|
||||
[
|
||||
3.0,
|
||||
-21.0
|
||||
],
|
||||
[
|
||||
8.8,
|
||||
-21.0
|
||||
],
|
||||
[
|
||||
8.8,
|
||||
-29.0
|
||||
],
|
||||
[
|
||||
3.0,
|
||||
-29.0
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "left_door_35",
|
||||
"available": true,
|
||||
"polygon": [
|
||||
[-3.4, -34.2],
|
||||
[-2.0, -34.2],
|
||||
[-2.0, -35.8],
|
||||
[-3.4, -35.8]
|
||||
[
|
||||
-3.4,
|
||||
-34.2
|
||||
],
|
||||
[
|
||||
-2.0,
|
||||
-34.2
|
||||
],
|
||||
[
|
||||
-2.0,
|
||||
-35.8
|
||||
],
|
||||
[
|
||||
-3.4,
|
||||
-35.8
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "right_door_35",
|
||||
"available": true,
|
||||
"polygon": [
|
||||
[2.0, -34.2],
|
||||
[3.4, -34.2],
|
||||
[3.4, -35.8],
|
||||
[2.0, -35.8]
|
||||
[
|
||||
2.0,
|
||||
-34.2
|
||||
],
|
||||
[
|
||||
3.4,
|
||||
-34.2
|
||||
],
|
||||
[
|
||||
3.4,
|
||||
-35.8
|
||||
],
|
||||
[
|
||||
2.0,
|
||||
-35.8
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "left_room_35",
|
||||
"available": true,
|
||||
"polygon": [
|
||||
[-8.8, -31.0],
|
||||
[-3.0, -31.0],
|
||||
[-3.0, -39.0],
|
||||
[-8.8, -39.0]
|
||||
[
|
||||
-8.8,
|
||||
-31.0
|
||||
],
|
||||
[
|
||||
-3.0,
|
||||
-31.0
|
||||
],
|
||||
[
|
||||
-3.0,
|
||||
-39.0
|
||||
],
|
||||
[
|
||||
-8.8,
|
||||
-39.0
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "right_room_35",
|
||||
"available": true,
|
||||
"polygon": [
|
||||
[3.0, -31.0],
|
||||
[8.8, -31.0],
|
||||
[8.8, -39.0],
|
||||
[3.0, -39.0]
|
||||
[
|
||||
3.0,
|
||||
-31.0
|
||||
],
|
||||
[
|
||||
8.8,
|
||||
-31.0
|
||||
],
|
||||
[
|
||||
8.8,
|
||||
-39.0
|
||||
],
|
||||
[
|
||||
3.0,
|
||||
-39.0
|
||||
]
|
||||
]
|
||||
}
|
||||
],
|
||||
"obstacles": [
|
||||
{
|
||||
"name": "firebox",
|
||||
"polygon": [
|
||||
[
|
||||
-2.49468,
|
||||
-32.87888
|
||||
],
|
||||
[
|
||||
-2.05776,
|
||||
-32.87888
|
||||
],
|
||||
[
|
||||
-2.05776,
|
||||
-31.51618
|
||||
],
|
||||
[
|
||||
-2.49468,
|
||||
-31.51618
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "bench",
|
||||
"polygon": [
|
||||
[
|
||||
-2.54485,
|
||||
-9.31999
|
||||
],
|
||||
[
|
||||
-2.51786,
|
||||
-9.3557
|
||||
],
|
||||
[
|
||||
-2.48369,
|
||||
-9.37177
|
||||
],
|
||||
[
|
||||
-2.45574,
|
||||
-9.38357
|
||||
],
|
||||
[
|
||||
-2.17907,
|
||||
-9.39566
|
||||
],
|
||||
[
|
||||
-1.80829,
|
||||
-9.40749
|
||||
],
|
||||
[
|
||||
-1.7521,
|
||||
-9.36413
|
||||
],
|
||||
[
|
||||
-1.66462,
|
||||
-9.28728
|
||||
],
|
||||
[
|
||||
-1.65801,
|
||||
-9.14512
|
||||
],
|
||||
[
|
||||
-1.65114,
|
||||
-7.22169
|
||||
],
|
||||
[
|
||||
-1.65535,
|
||||
-6.51944
|
||||
],
|
||||
[
|
||||
-1.72626,
|
||||
-6.42306
|
||||
],
|
||||
[
|
||||
-1.76837,
|
||||
-6.39498
|
||||
],
|
||||
[
|
||||
-2.47166,
|
||||
-6.39746
|
||||
],
|
||||
[
|
||||
-2.5309,
|
||||
-6.44135
|
||||
],
|
||||
[
|
||||
-2.53691,
|
||||
-7.18464
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@ -8,10 +8,668 @@
|
||||
"name": "main_corridor",
|
||||
"available": true,
|
||||
"polygon": [
|
||||
[-200, 200],
|
||||
[200, 200],
|
||||
[200, -200],
|
||||
[-200, -200]
|
||||
[
|
||||
-200,
|
||||
200
|
||||
],
|
||||
[
|
||||
200,
|
||||
200
|
||||
],
|
||||
[
|
||||
200,
|
||||
-200
|
||||
],
|
||||
[
|
||||
-200,
|
||||
-200
|
||||
]
|
||||
]
|
||||
}
|
||||
],
|
||||
"obstacles": [
|
||||
{
|
||||
"name": "door",
|
||||
"polygon": [
|
||||
[
|
||||
-5.2,
|
||||
7.08001
|
||||
],
|
||||
[
|
||||
-5.19904,
|
||||
7.07025
|
||||
],
|
||||
[
|
||||
-5.19619,
|
||||
7.06087
|
||||
],
|
||||
[
|
||||
-5.19157,
|
||||
7.05223
|
||||
],
|
||||
[
|
||||
-5.18536,
|
||||
7.04465
|
||||
],
|
||||
[
|
||||
-5.17778,
|
||||
7.03843
|
||||
],
|
||||
[
|
||||
-5.16913,
|
||||
7.03381
|
||||
],
|
||||
[
|
||||
-5.15975,
|
||||
7.03097
|
||||
],
|
||||
[
|
||||
-5.15,
|
||||
7.03001
|
||||
],
|
||||
[
|
||||
-3.75,
|
||||
7.13001
|
||||
],
|
||||
[
|
||||
-3.625,
|
||||
7.43001
|
||||
],
|
||||
[
|
||||
-3.625,
|
||||
8.83001
|
||||
],
|
||||
[
|
||||
-3.75,
|
||||
9.13001
|
||||
],
|
||||
[
|
||||
-5.15,
|
||||
9.23001
|
||||
],
|
||||
[
|
||||
-5.15975,
|
||||
9.22905
|
||||
],
|
||||
[
|
||||
-5.16913,
|
||||
9.2262
|
||||
],
|
||||
[
|
||||
-5.17778,
|
||||
9.22158
|
||||
],
|
||||
[
|
||||
-5.18536,
|
||||
9.21536
|
||||
],
|
||||
[
|
||||
-5.19157,
|
||||
9.20778
|
||||
],
|
||||
[
|
||||
-5.19619,
|
||||
9.19914
|
||||
],
|
||||
[
|
||||
-5.19904,
|
||||
9.18976
|
||||
],
|
||||
[
|
||||
-5.2,
|
||||
9.18001
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "inai",
|
||||
"polygon": [
|
||||
[
|
||||
-3.75,
|
||||
-15.0
|
||||
],
|
||||
[
|
||||
3.75,
|
||||
-15.0
|
||||
],
|
||||
[
|
||||
3.75,
|
||||
15.0
|
||||
],
|
||||
[
|
||||
-3.75,
|
||||
15.0
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tree001",
|
||||
"polygon": [
|
||||
[
|
||||
10.45,
|
||||
12.0
|
||||
],
|
||||
[
|
||||
10.38971,
|
||||
12.225
|
||||
],
|
||||
[
|
||||
10.225,
|
||||
12.38971
|
||||
],
|
||||
[
|
||||
10.0,
|
||||
12.45
|
||||
],
|
||||
[
|
||||
9.775,
|
||||
12.38971
|
||||
],
|
||||
[
|
||||
9.61029,
|
||||
12.225
|
||||
],
|
||||
[
|
||||
9.55,
|
||||
12.0
|
||||
],
|
||||
[
|
||||
9.61029,
|
||||
11.775
|
||||
],
|
||||
[
|
||||
9.775,
|
||||
11.61029
|
||||
],
|
||||
[
|
||||
10.0,
|
||||
11.55
|
||||
],
|
||||
[
|
||||
10.225,
|
||||
11.61029
|
||||
],
|
||||
[
|
||||
10.38971,
|
||||
11.775
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tree002",
|
||||
"polygon": [
|
||||
[
|
||||
-11.55,
|
||||
19.0
|
||||
],
|
||||
[
|
||||
-11.61029,
|
||||
19.225
|
||||
],
|
||||
[
|
||||
-11.775,
|
||||
19.38971
|
||||
],
|
||||
[
|
||||
-12.0,
|
||||
19.45
|
||||
],
|
||||
[
|
||||
-12.225,
|
||||
19.38971
|
||||
],
|
||||
[
|
||||
-12.38971,
|
||||
19.225
|
||||
],
|
||||
[
|
||||
-12.45,
|
||||
19.0
|
||||
],
|
||||
[
|
||||
-12.38971,
|
||||
18.775
|
||||
],
|
||||
[
|
||||
-12.225,
|
||||
18.61029
|
||||
],
|
||||
[
|
||||
-12.0,
|
||||
18.55
|
||||
],
|
||||
[
|
||||
-11.775,
|
||||
18.61029
|
||||
],
|
||||
[
|
||||
-11.61029,
|
||||
18.775
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tree003",
|
||||
"polygon": [
|
||||
[
|
||||
-11.55,
|
||||
8.0
|
||||
],
|
||||
[
|
||||
-11.61029,
|
||||
8.225
|
||||
],
|
||||
[
|
||||
-11.775,
|
||||
8.38971
|
||||
],
|
||||
[
|
||||
-12.0,
|
||||
8.45
|
||||
],
|
||||
[
|
||||
-12.225,
|
||||
8.38971
|
||||
],
|
||||
[
|
||||
-12.38971,
|
||||
8.225
|
||||
],
|
||||
[
|
||||
-12.45,
|
||||
8.0
|
||||
],
|
||||
[
|
||||
-12.38971,
|
||||
7.775
|
||||
],
|
||||
[
|
||||
-12.225,
|
||||
7.61029
|
||||
],
|
||||
[
|
||||
-12.0,
|
||||
7.55
|
||||
],
|
||||
[
|
||||
-11.775,
|
||||
7.61029
|
||||
],
|
||||
[
|
||||
-11.61029,
|
||||
7.775
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tree004",
|
||||
"polygon": [
|
||||
[
|
||||
-11.55,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
-11.61029,
|
||||
0.225
|
||||
],
|
||||
[
|
||||
-11.775,
|
||||
0.38971
|
||||
],
|
||||
[
|
||||
-12.0,
|
||||
0.45
|
||||
],
|
||||
[
|
||||
-12.225,
|
||||
0.38971
|
||||
],
|
||||
[
|
||||
-12.38971,
|
||||
0.225
|
||||
],
|
||||
[
|
||||
-12.45,
|
||||
0.0
|
||||
],
|
||||
[
|
||||
-12.38971,
|
||||
-0.225
|
||||
],
|
||||
[
|
||||
-12.225,
|
||||
-0.38971
|
||||
],
|
||||
[
|
||||
-12.0,
|
||||
-0.45
|
||||
],
|
||||
[
|
||||
-11.775,
|
||||
-0.38971
|
||||
],
|
||||
[
|
||||
-11.61029,
|
||||
-0.225
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tree005",
|
||||
"polygon": [
|
||||
[
|
||||
-11.55,
|
||||
-8.0
|
||||
],
|
||||
[
|
||||
-11.61029,
|
||||
-7.775
|
||||
],
|
||||
[
|
||||
-11.775,
|
||||
-7.61029
|
||||
],
|
||||
[
|
||||
-12.0,
|
||||
-7.55
|
||||
],
|
||||
[
|
||||
-12.225,
|
||||
-7.61029
|
||||
],
|
||||
[
|
||||
-12.38971,
|
||||
-7.775
|
||||
],
|
||||
[
|
||||
-12.45,
|
||||
-8.0
|
||||
],
|
||||
[
|
||||
-12.38971,
|
||||
-8.225
|
||||
],
|
||||
[
|
||||
-12.225,
|
||||
-8.38971
|
||||
],
|
||||
[
|
||||
-12.0,
|
||||
-8.45
|
||||
],
|
||||
[
|
||||
-11.775,
|
||||
-8.38971
|
||||
],
|
||||
[
|
||||
-11.61029,
|
||||
-8.225
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tree006",
|
||||
"polygon": [
|
||||
[
|
||||
8.94915,
|
||||
-2.59884
|
||||
],
|
||||
[
|
||||
8.88886,
|
||||
-2.37384
|
||||
],
|
||||
[
|
||||
8.72415,
|
||||
-2.20913
|
||||
],
|
||||
[
|
||||
8.49915,
|
||||
-2.14884
|
||||
],
|
||||
[
|
||||
8.27415,
|
||||
-2.20913
|
||||
],
|
||||
[
|
||||
8.10944,
|
||||
-2.37384
|
||||
],
|
||||
[
|
||||
8.04915,
|
||||
-2.59884
|
||||
],
|
||||
[
|
||||
8.10944,
|
||||
-2.82384
|
||||
],
|
||||
[
|
||||
8.27415,
|
||||
-2.98855
|
||||
],
|
||||
[
|
||||
8.49915,
|
||||
-3.04884
|
||||
],
|
||||
[
|
||||
8.72415,
|
||||
-2.98855
|
||||
],
|
||||
[
|
||||
8.88886,
|
||||
-2.82384
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tree007",
|
||||
"polygon": [
|
||||
[
|
||||
15.0436,
|
||||
5.3401
|
||||
],
|
||||
[
|
||||
14.98331,
|
||||
5.5651
|
||||
],
|
||||
[
|
||||
14.8186,
|
||||
5.72981
|
||||
],
|
||||
[
|
||||
14.5936,
|
||||
5.7901
|
||||
],
|
||||
[
|
||||
14.3686,
|
||||
5.72981
|
||||
],
|
||||
[
|
||||
14.20389,
|
||||
5.5651
|
||||
],
|
||||
[
|
||||
14.1436,
|
||||
5.3401
|
||||
],
|
||||
[
|
||||
14.20389,
|
||||
5.1151
|
||||
],
|
||||
[
|
||||
14.3686,
|
||||
4.95039
|
||||
],
|
||||
[
|
||||
14.5936,
|
||||
4.8901
|
||||
],
|
||||
[
|
||||
14.8186,
|
||||
4.95039
|
||||
],
|
||||
[
|
||||
14.98331,
|
||||
5.1151
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tree008",
|
||||
"polygon": [
|
||||
[
|
||||
24.3795,
|
||||
9.00583
|
||||
],
|
||||
[
|
||||
24.31921,
|
||||
9.23083
|
||||
],
|
||||
[
|
||||
24.1545,
|
||||
9.39554
|
||||
],
|
||||
[
|
||||
23.9295,
|
||||
9.45583
|
||||
],
|
||||
[
|
||||
23.7045,
|
||||
9.39554
|
||||
],
|
||||
[
|
||||
23.53979,
|
||||
9.23083
|
||||
],
|
||||
[
|
||||
23.4795,
|
||||
9.00583
|
||||
],
|
||||
[
|
||||
23.53979,
|
||||
8.78083
|
||||
],
|
||||
[
|
||||
23.7045,
|
||||
8.61612
|
||||
],
|
||||
[
|
||||
23.9295,
|
||||
8.55583
|
||||
],
|
||||
[
|
||||
24.1545,
|
||||
8.61612
|
||||
],
|
||||
[
|
||||
24.31921,
|
||||
8.78083
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tree009",
|
||||
"polygon": [
|
||||
[
|
||||
30.2628,
|
||||
-1.45278
|
||||
],
|
||||
[
|
||||
30.20251,
|
||||
-1.22778
|
||||
],
|
||||
[
|
||||
30.0378,
|
||||
-1.06307
|
||||
],
|
||||
[
|
||||
29.8128,
|
||||
-1.00278
|
||||
],
|
||||
[
|
||||
29.5878,
|
||||
-1.06307
|
||||
],
|
||||
[
|
||||
29.42309,
|
||||
-1.22778
|
||||
],
|
||||
[
|
||||
29.3628,
|
||||
-1.45278
|
||||
],
|
||||
[
|
||||
29.42309,
|
||||
-1.67778
|
||||
],
|
||||
[
|
||||
29.5878,
|
||||
-1.84249
|
||||
],
|
||||
[
|
||||
29.8128,
|
||||
-1.90278
|
||||
],
|
||||
[
|
||||
30.0378,
|
||||
-1.84249
|
||||
],
|
||||
[
|
||||
30.20251,
|
||||
-1.67778
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tree010",
|
||||
"polygon": [
|
||||
[
|
||||
33.6271,
|
||||
14.609
|
||||
],
|
||||
[
|
||||
33.56681,
|
||||
14.834
|
||||
],
|
||||
[
|
||||
33.4021,
|
||||
14.99871
|
||||
],
|
||||
[
|
||||
33.1771,
|
||||
15.059
|
||||
],
|
||||
[
|
||||
32.9521,
|
||||
14.99871
|
||||
],
|
||||
[
|
||||
32.78739,
|
||||
14.834
|
||||
],
|
||||
[
|
||||
32.7271,
|
||||
14.609
|
||||
],
|
||||
[
|
||||
32.78739,
|
||||
14.384
|
||||
],
|
||||
[
|
||||
32.9521,
|
||||
14.21929
|
||||
],
|
||||
[
|
||||
33.1771,
|
||||
14.159
|
||||
],
|
||||
[
|
||||
33.4021,
|
||||
14.21929
|
||||
],
|
||||
[
|
||||
33.56681,
|
||||
14.384
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
BIN
resources/w/Zombie_BaseColor.png
(Stored with Git LFS)
BIN
resources/w/Zombie_BaseColor.png
(Stored with Git LFS)
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
BIN
resources/w/exterior/Segmented_Plane — копия.png
(Stored with Git LFS)
BIN
resources/w/exterior/Segmented_Plane — копия.png
(Stored with Git LFS)
Binary file not shown.
BIN
resources/w/exterior/Segmented_Plane.png
(Stored with Git LFS)
BIN
resources/w/exterior/Segmented_Plane.png
(Stored with Git LFS)
Binary file not shown.
@ -1,37 +0,0 @@
|
||||
===Vertices (Split by UV/Normal): 16
|
||||
V 0: Pos(-33.333332, -100.0, 0.0) Norm(0.0, 0.0, 1.0) UV(0.333333, 0.0)
|
||||
V 1: Pos(-100.0, -33.333332, 0.0) Norm(0.0, 0.0, 1.0) UV(0.0, 0.333333)
|
||||
V 2: Pos(-100.0, -100.0, 0.0) Norm(0.0, 0.0, 1.0) UV(0.0, 0.0)
|
||||
V 3: Pos(33.333336, -100.0, 0.0) Norm(0.0, 0.0, 1.0) UV(0.666667, 0.0)
|
||||
V 4: Pos(-33.333332, -33.333332, 0.0) Norm(0.0, 0.0, 1.0) UV(0.333333, 0.333333)
|
||||
V 5: Pos(100.0, -100.0, 0.0) Norm(0.0, 0.0, 1.0) UV(1.0, 0.0)
|
||||
V 6: Pos(33.333336, -33.333332, 0.0) Norm(0.0, 0.0, 1.0) UV(0.666667, 0.333333)
|
||||
V 7: Pos(-100.0, 33.333336, 0.0) Norm(0.0, 0.0, 1.0) UV(0.0, 0.666667)
|
||||
V 8: Pos(-33.333332, 33.333336, 0.0) Norm(0.0, 0.0, 1.0) UV(0.333333, 0.666667)
|
||||
V 9: Pos(100.0, -33.333332, 0.0) Norm(0.0, 0.0, 1.0) UV(1.0, 0.333333)
|
||||
V 10: Pos(33.333336, 33.333336, 0.0) Norm(0.0, 0.0, 1.0) UV(0.666667, 0.666667)
|
||||
V 11: Pos(-100.0, 100.0, 0.0) Norm(0.0, 0.0, 1.0) UV(0.0, 1.0)
|
||||
V 12: Pos(-33.333332, 100.0, 0.0) Norm(0.0, 0.0, 1.0) UV(0.333333, 1.0)
|
||||
V 13: Pos(100.0, 33.333336, 0.0) Norm(0.0, 0.0, 1.0) UV(1.0, 0.666667)
|
||||
V 14: Pos(33.333336, 100.0, 0.0) Norm(0.0, 0.0, 1.0) UV(0.666667, 1.0)
|
||||
V 15: Pos(100.0, 100.0, 0.0) Norm(0.0, 0.0, 1.0) UV(1.0, 1.0)
|
||||
|
||||
===Triangles (Indices): 18
|
||||
Tri: 0 1 2
|
||||
Tri: 3 4 0
|
||||
Tri: 5 6 3
|
||||
Tri: 4 7 1
|
||||
Tri: 6 8 4
|
||||
Tri: 9 10 6
|
||||
Tri: 8 11 7
|
||||
Tri: 10 12 8
|
||||
Tri: 13 14 10
|
||||
Tri: 0 4 1
|
||||
Tri: 3 6 4
|
||||
Tri: 5 9 6
|
||||
Tri: 4 8 7
|
||||
Tri: 6 10 8
|
||||
Tri: 9 13 10
|
||||
Tri: 8 12 11
|
||||
Tri: 10 14 12
|
||||
Tri: 13 15 14
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,64 +0,0 @@
|
||||
===Vertices (Split by UV/Normal): 41
|
||||
V 0: Pos(0.02, 0.15, 0.0) Norm(0.577338, 0.577356, -0.577357) UV(0.77503, 0.750033)
|
||||
V 1: Pos(-0.02, -0.15, 0.0) Norm(-0.577338, -0.577356, -0.577357) UV(0.808365, 0.500022)
|
||||
V 2: Pos(-0.02, 0.15, 0.0) Norm(-0.577347, 0.577357, -0.577347) UV(0.808365, 0.750033)
|
||||
V 3: Pos(0.02, -0.15, 0.6) Norm(0.577338, -0.577356, 0.577357) UV(0.866701, 0.496276)
|
||||
V 4: Pos(-0.02, 0.15, 0.6) Norm(-0.577338, 0.577356, 0.577357) UV(0.833366, 0.746287)
|
||||
V 5: Pos(-0.02, -0.15, 0.6) Norm(-0.577347, -0.577357, 0.577347) UV(0.833366, 0.496276)
|
||||
V 6: Pos(0.02, -0.15, 0.0) Norm(0.577347, -0.577357, -0.577347) UV(0.833366, 0.496276)
|
||||
V 7: Pos(-0.02, -0.15, 0.6) Norm(-0.577347, -0.577357, 0.577347) UV(0.866701, 0.0)
|
||||
V 8: Pos(-0.02, -0.15, 0.0) Norm(-0.577338, -0.577356, -0.577357) UV(0.866701, 0.496276)
|
||||
V 9: Pos(0.02, 0.15, 0.0) Norm(0.577338, 0.577356, -0.577357) UV(0.500022, 0.5)
|
||||
V 10: Pos(0.02, -0.15, 0.6) Norm(0.577338, -0.577356, 0.577357) UV(0.750033, 0.0)
|
||||
V 11: Pos(0.02, -0.15, 0.0) Norm(0.577347, -0.577357, -0.577347) UV(0.750033, 0.5)
|
||||
V 12: Pos(-0.02, 0.15, 0.0) Norm(-0.577347, 0.577357, -0.577347) UV(0.77503, 0.500022)
|
||||
V 13: Pos(0.02, 0.15, 0.6) Norm(0.577347, 0.577357, 0.577347) UV(0.808365, 0.0)
|
||||
V 14: Pos(0.02, 0.15, 0.0) Norm(0.577338, 0.577356, -0.577357) UV(0.808365, 0.500022)
|
||||
V 15: Pos(-0.02, -0.15, 0.0) Norm(-0.577338, -0.577356, -0.577357) UV(0.500022, 1.0)
|
||||
V 16: Pos(-0.02, 0.15, 0.6) Norm(-0.577338, 0.577356, 0.577357) UV(0.750033, 0.5)
|
||||
V 17: Pos(-0.02, 0.15, 0.0) Norm(-0.577347, 0.577357, -0.577347) UV(0.750033, 1.0)
|
||||
V 18: Pos(0.015, 0.15, 0.6) Norm(0.583227, 0.576373, -0.572398) UV(0.8667, 0.250011)
|
||||
V 19: Pos(-0.015, -0.15, 0.6) Norm(-0.570792, -0.648582, -0.503526) UV(0.891702, 0.0)
|
||||
V 20: Pos(-0.015, 0.15, 0.6) Norm(-0.583229, 0.576375, -0.572394) UV(0.891702, 0.250011)
|
||||
V 21: Pos(0.0, 0.05, 1.4) Norm(0.0, -0.417006, 0.908904) UV(0.166674, 0.0)
|
||||
V 22: Pos(-0.015, 0.15, 0.6) Norm(-0.583229, 0.576375, -0.572394) UV(0.250011, 0.666784)
|
||||
V 23: Pos(-0.015, -0.15, 0.6) Norm(-0.570792, -0.648582, -0.503526) UV(0.0, 0.666784)
|
||||
V 24: Pos(0.015, 0.15, 0.6) Norm(0.583227, 0.576373, -0.572398) UV(0.250011, 0.666784)
|
||||
V 25: Pos(0.0, 0.05, 1.4) Norm(0.0, -0.417006, 0.908904) UV(0.333348, 0.0)
|
||||
V 26: Pos(0.015, -0.15, 0.6) Norm(0.570794, -0.648585, -0.50352) UV(0.500022, 0.666784)
|
||||
V 27: Pos(-0.015, -0.15, 0.6) Norm(-0.570792, -0.648582, -0.503526) UV(0.77503, 0.68218)
|
||||
V 28: Pos(0.015, -0.15, 0.6) Norm(0.570794, -0.648585, -0.50352) UV(0.750033, 0.681722)
|
||||
V 29: Pos(0.0, 0.05, 1.4) Norm(0.0, -0.417006, 0.908904) UV(0.77503, 0.0)
|
||||
V 30: Pos(-0.015, 0.15, 0.6) Norm(-0.583229, 0.576375, -0.572394) UV(0.808365, 0.666696)
|
||||
V 31: Pos(0.0, 0.15, 1.4) Norm(0.0, 0.536928, 0.843628) UV(0.820865, 0.0)
|
||||
V 32: Pos(0.015, 0.15, 0.6) Norm(0.583227, 0.576373, -0.572398) UV(0.833366, 0.666696)
|
||||
V 33: Pos(0.02, -0.15, 0.0) Norm(0.577347, -0.577357, -0.577347) UV(0.77503, 0.500022)
|
||||
V 34: Pos(0.02, 0.15, 0.6) Norm(0.577347, 0.577357, 0.577347) UV(0.866701, 0.746287)
|
||||
V 35: Pos(0.02, -0.15, 0.6) Norm(0.577338, -0.577356, 0.577357) UV(0.833366, 0.0)
|
||||
V 36: Pos(0.02, 0.15, 0.6) Norm(0.577347, 0.577357, 0.577347) UV(0.500022, 0.0)
|
||||
V 37: Pos(-0.02, 0.15, 0.6) Norm(-0.577338, 0.577356, 0.577357) UV(0.77503, 0.0)
|
||||
V 38: Pos(-0.02, -0.15, 0.6) Norm(-0.577347, -0.577357, 0.577347) UV(0.500022, 0.5)
|
||||
V 39: Pos(0.015, -0.15, 0.6) Norm(0.570794, -0.648585, -0.50352) UV(0.8667, 0.0)
|
||||
V 40: Pos(0.0, 0.15, 1.4) Norm(0.0, 0.536928, 0.843628) UV(0.250011, 0.0)
|
||||
|
||||
===Triangles (Indices): 20
|
||||
Tri: 0 1 2
|
||||
Tri: 3 4 5
|
||||
Tri: 6 7 8
|
||||
Tri: 9 10 11
|
||||
Tri: 12 13 14
|
||||
Tri: 15 16 17
|
||||
Tri: 18 19 20
|
||||
Tri: 21 22 23
|
||||
Tri: 24 25 26
|
||||
Tri: 27 28 29
|
||||
Tri: 30 31 32
|
||||
Tri: 0 33 1
|
||||
Tri: 3 34 4
|
||||
Tri: 6 35 7
|
||||
Tri: 9 36 10
|
||||
Tri: 12 37 13
|
||||
Tri: 15 38 16
|
||||
Tri: 18 39 19
|
||||
Tri: 21 40 22
|
||||
Tri: 24 40 25
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
BIN
resources/w/zombie002.anim
(Stored with Git LFS)
BIN
resources/w/zombie002.anim
(Stored with Git LFS)
Binary file not shown.
464463
resources/w/zombie002.txt
464463
resources/w/zombie002.txt
File diff suppressed because it is too large
Load Diff
BIN
resources/w/zombie_idle001.anim
(Stored with Git LFS)
BIN
resources/w/zombie_idle001.anim
(Stored with Git LFS)
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,103 +0,0 @@
|
||||
#pragma once
|
||||
#include "render/Renderer.h"
|
||||
#include <unordered_map>
|
||||
|
||||
|
||||
namespace ZL
|
||||
{
|
||||
constexpr int MAX_BONE_COUNT = 6;
|
||||
constexpr int MAX_GPU_BONES = 64;
|
||||
struct Bone
|
||||
{
|
||||
Vector3f boneStartWorld;
|
||||
float boneLength;
|
||||
Matrix4f boneMatrixWorld;
|
||||
|
||||
int parent;
|
||||
std::vector<int> children;
|
||||
};
|
||||
|
||||
struct BoneWeight
|
||||
{
|
||||
int boneIndex = -1;
|
||||
float weight = 0;
|
||||
};
|
||||
|
||||
struct AnimationKeyFrame
|
||||
{
|
||||
int frame;
|
||||
std::vector<Bone> bones;
|
||||
};
|
||||
|
||||
struct Animation
|
||||
{
|
||||
std::vector<AnimationKeyFrame> keyFrames;
|
||||
};
|
||||
|
||||
struct GpuBoneData {
|
||||
std::vector<Vector4f> boneIndices0; // bone indices 0-3 per vertex (as float)
|
||||
std::vector<Vector2f> boneIndices1; // bone indices 4-5 per vertex
|
||||
std::vector<Vector4f> boneWeights0; // bone weights 0-3 per vertex
|
||||
std::vector<Vector2f> boneWeights1; // bone weights 4-5 per vertex
|
||||
bool prepared = false;
|
||||
void PrepareGpuSkinningData(const std::vector<std::array<BoneWeight, MAX_BONE_COUNT>>& verticesBoneWeight);
|
||||
};
|
||||
|
||||
struct BoneSystem
|
||||
{
|
||||
VertexDataStruct mesh;
|
||||
VertexDataStruct startMesh;
|
||||
std::vector<std::array<BoneWeight, MAX_BONE_COUNT>> verticesBoneWeight;
|
||||
|
||||
Matrix4f armatureMatrix;
|
||||
|
||||
std::vector<Bone> startBones;
|
||||
std::vector<Bone> currentBones;
|
||||
std::vector<std::string> boneNames;
|
||||
|
||||
std::vector<Animation> animations;
|
||||
int startingFrame = 0;
|
||||
|
||||
void LoadFromFile(const std::string& fileName, const std::string& ZIPFileName = "");
|
||||
void LoadFromBinaryFile(const std::string& fileName, const std::string& ZIPFileName = "");
|
||||
|
||||
void Interpolate(int frame);
|
||||
|
||||
int findBoneIndex(const std::string& name) const;
|
||||
|
||||
// GPU skinning: compute skinning matrices without modifying the mesh
|
||||
//void ComputeSkinningMatrices(int frame, std::vector<Matrix4f>& outMatrices) const;
|
||||
};
|
||||
|
||||
|
||||
struct GpuSkinningShaderData {
|
||||
GpuBoneData gpuBoneData;
|
||||
|
||||
// GPU skinning data (lazily initialized)
|
||||
VertexRenderStruct bindPoseMutable;
|
||||
std::shared_ptr<VBOHolder> boneIndices0VBO;
|
||||
std::shared_ptr<VBOHolder> boneIndices1VBO;
|
||||
std::shared_ptr<VBOHolder> boneWeights0VBO;
|
||||
std::shared_ptr<VBOHolder> boneWeights1VBO;
|
||||
bool gpuSkinningPrepared = false;
|
||||
std::vector<Eigen::Matrix4f> skinningMatrices;
|
||||
|
||||
void prepareGpuSkinningVBOs(BoneSystem& model);
|
||||
void RenderVBO(Renderer& renderer);
|
||||
void ComputeSkinningMatrices(const std::vector<Bone>& startBones, const std::vector<AnimationKeyFrame>& keyFrames, int frame);
|
||||
};
|
||||
|
||||
struct BoneAnimationData {
|
||||
BoneSystem model;
|
||||
float currentFrame = 0.f;
|
||||
int lastFrame = -1;
|
||||
int totalFrames = 1;
|
||||
|
||||
GpuSkinningShaderData gpuSkinningShaderData;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
};
|
||||
@ -13,7 +13,21 @@ namespace ZL
|
||||
using std::max;
|
||||
#endif
|
||||
|
||||
extern int getIndexByValue(const std::string& name, const std::vector<std::string>& words);
|
||||
int getIndexByValue(const std::string& name, const std::vector<std::string>& words)
|
||||
{
|
||||
for (int i = 0; i < words.size(); i++)
|
||||
{
|
||||
if (words[i] == name)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "Bone name not found: " << name << std::endl;
|
||||
throw std::runtime_error("Bone name not found: " + name);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static std::string trimRight(const std::string& s)
|
||||
{
|
||||
@ -23,6 +37,42 @@ namespace ZL
|
||||
return s.substr(0, end);
|
||||
}
|
||||
|
||||
|
||||
void GpuBoneData::PrepareGpuSkinningData(const std::vector<std::array<BoneWeight, MAX_BONE_COUNT>>& verticesBoneWeight)
|
||||
{
|
||||
size_t vertexCount = verticesBoneWeight.size();
|
||||
boneIndices0.resize(vertexCount);
|
||||
boneIndices1.resize(vertexCount);
|
||||
boneWeights0.resize(vertexCount);
|
||||
boneWeights1.resize(vertexCount);
|
||||
|
||||
for (size_t i = 0; i < vertexCount; i++)
|
||||
{
|
||||
boneIndices0[i] = Vector4f(
|
||||
static_cast<float>(max(0, verticesBoneWeight[i][0].boneIndex)),
|
||||
static_cast<float>(max(0, verticesBoneWeight[i][1].boneIndex)),
|
||||
static_cast<float>(max(0, verticesBoneWeight[i][2].boneIndex)),
|
||||
static_cast<float>(max(0, verticesBoneWeight[i][3].boneIndex))
|
||||
);
|
||||
boneIndices1[i] = Vector2f(
|
||||
static_cast<float>(max(0, verticesBoneWeight[i][4].boneIndex)),
|
||||
static_cast<float>(max(0, verticesBoneWeight[i][5].boneIndex))
|
||||
);
|
||||
boneWeights0[i] = Vector4f(
|
||||
verticesBoneWeight[i][0].weight,
|
||||
verticesBoneWeight[i][1].weight,
|
||||
verticesBoneWeight[i][2].weight,
|
||||
verticesBoneWeight[i][3].weight
|
||||
);
|
||||
boneWeights1[i] = Vector2f(
|
||||
verticesBoneWeight[i][4].weight,
|
||||
verticesBoneWeight[i][5].weight
|
||||
);
|
||||
}
|
||||
|
||||
prepared = true;
|
||||
}
|
||||
|
||||
void BoneSystemNew::LoadFromFile(const std::string& fileName, const std::string& ZIPFileName)
|
||||
{
|
||||
std::ifstream filestream;
|
||||
|
||||
@ -1,9 +1,48 @@
|
||||
#pragma once
|
||||
#include "BoneAnimatedModel.h"
|
||||
#include "render/Renderer.h"
|
||||
#include <unordered_map>
|
||||
|
||||
|
||||
namespace ZL
|
||||
{
|
||||
constexpr int MAX_BONE_COUNT = 6;
|
||||
constexpr int MAX_GPU_BONES = 64;
|
||||
struct Bone
|
||||
{
|
||||
Eigen::Vector3f boneStartWorld;
|
||||
float boneLength;
|
||||
Eigen::Matrix4f boneMatrixWorld;
|
||||
|
||||
int parent;
|
||||
std::vector<int> children;
|
||||
};
|
||||
|
||||
struct BoneWeight
|
||||
{
|
||||
int boneIndex = -1;
|
||||
float weight = 0;
|
||||
};
|
||||
|
||||
struct AnimationKeyFrame
|
||||
{
|
||||
int frame;
|
||||
std::vector<Bone> bones;
|
||||
};
|
||||
|
||||
struct Animation
|
||||
{
|
||||
std::vector<AnimationKeyFrame> keyFrames;
|
||||
};
|
||||
|
||||
struct GpuBoneData {
|
||||
std::vector<Vector4f> boneIndices0; // bone indices 0-3 per vertex (as float)
|
||||
std::vector<Vector2f> boneIndices1; // bone indices 4-5 per vertex
|
||||
std::vector<Vector4f> boneWeights0; // bone weights 0-3 per vertex
|
||||
std::vector<Vector2f> boneWeights1; // bone weights 4-5 per vertex
|
||||
bool prepared = false;
|
||||
void PrepareGpuSkinningData(const std::vector<std::array<BoneWeight, MAX_BONE_COUNT>>& verticesBoneWeight);
|
||||
};
|
||||
|
||||
struct MeshBoneData
|
||||
{
|
||||
VertexDataStruct mesh;
|
||||
|
||||
@ -78,6 +78,43 @@ void Character::setPathPlanner(PathPlanner planner) {
|
||||
pathPlanner = std::move(planner);
|
||||
}
|
||||
|
||||
bool Character::isMoving() const
|
||||
{
|
||||
Eigen::Vector3f toTarget = walkTarget - position;
|
||||
toTarget.y() = 0.f;
|
||||
return !pathWaypoints.empty() || toTarget.norm() > WALK_THRESHOLD;
|
||||
}
|
||||
|
||||
Eigen::Vector3f Character::getCurrentNavigationTarget() const
|
||||
{
|
||||
if (!pathWaypoints.empty() && currentWaypointIndex < pathWaypoints.size()) {
|
||||
return pathWaypoints[currentWaypointIndex];
|
||||
}
|
||||
return walkTarget;
|
||||
}
|
||||
|
||||
void Character::forceReplan()
|
||||
{
|
||||
if (!pathPlanner) {
|
||||
return;
|
||||
}
|
||||
|
||||
const Eigen::Vector3f normalizedTarget(requestedWalkTarget.x(), 0.f, requestedWalkTarget.z());
|
||||
pathWaypoints = pathPlanner(position, normalizedTarget);
|
||||
currentWaypointIndex = 0;
|
||||
|
||||
if (!pathWaypoints.empty()) {
|
||||
for (Eigen::Vector3f& waypoint : pathWaypoints) {
|
||||
waypoint.y() = 0.f;
|
||||
}
|
||||
walkTarget = pathWaypoints.back();
|
||||
return;
|
||||
}
|
||||
|
||||
walkTarget = Eigen::Vector3f(position.x(), 0.f, position.z());
|
||||
onArrivedCallback = nullptr;
|
||||
}
|
||||
|
||||
void Character::setTexture(std::shared_ptr<ZL::Texture> texture)
|
||||
{
|
||||
for (auto& animEntry : animations) {
|
||||
@ -90,15 +127,7 @@ void Character::setTexture(std::shared_ptr<ZL::Texture> texture)
|
||||
void Character::setTexture(const std::string& meshName, std::shared_ptr<Texture> tex) {
|
||||
meshTextures[meshName] = std::move(tex);
|
||||
}
|
||||
/*
|
||||
void Character::setTexture(std::shared_ptr<Texture> tex) {
|
||||
for (auto& animEntry : animations) {
|
||||
for (const auto& name : animEntry.second.model.meshNamesOrdered) {
|
||||
meshTextures[name] = tex;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
AnimationState Character::resolveActiveState() const {
|
||||
|
||||
if (animations.count(currentState)) return currentState;
|
||||
@ -137,14 +166,6 @@ void Character::update(int64_t deltaMs) {
|
||||
deltaMs = static_cast<int64_t>(static_cast<float>(deltaMs) * speedMultiplier);
|
||||
}
|
||||
|
||||
|
||||
//weaponInitialRotation = Eigen::AngleAxisf(x/180.0, Eigen::Vector3f::UnitZ()).toRotationMatrix();
|
||||
//weaponInitialRotation = Eigen::AngleAxisf(-M_PI*0.5, Eigen::Vector3f::UnitZ()).toRotationMatrix();
|
||||
//weaponInitialPosition = Eigen::Vector3f(0, 0.09, 0.016);
|
||||
/*if (deltaMs > 200)
|
||||
{
|
||||
deltaMs = 200;
|
||||
}*/
|
||||
Eigen::Vector3f activeTarget;
|
||||
Eigen::Vector3f lookTarget;
|
||||
|
||||
@ -336,14 +357,8 @@ void Character::update(int64_t deltaMs) {
|
||||
resetAnim = false;
|
||||
anim.currentFrame = 0;
|
||||
}
|
||||
//19
|
||||
int prevFrame = anim.currentFrame;
|
||||
anim.currentFrame += static_cast<float>(deltaMs) / 24.f;
|
||||
/*
|
||||
if (npcId == "ghost_01x")
|
||||
{
|
||||
std::cout << "Current animation frame: " << anim.currentFrame << " / " << anim.totalFrames << " -- " << anim.lastFrame << std::endl;
|
||||
}*/
|
||||
|
||||
if (static_cast<int>(anim.currentFrame) >= 20 && currentState == AnimationState::STAND_TO_ACTION)
|
||||
{
|
||||
@ -399,25 +414,21 @@ void Character::update(int64_t deltaMs) {
|
||||
if (currentState == AnimationState::STAND_TO_ACTION)
|
||||
{
|
||||
currentState = AnimationState::ACTION_IDLE;
|
||||
//resetAnim = true;
|
||||
}
|
||||
if (currentState == AnimationState::ACTION_TO_STAND)
|
||||
{
|
||||
currentState = AnimationState::STAND;
|
||||
//resetAnim = true;
|
||||
}
|
||||
|
||||
if (currentState == AnimationState::ACTION_ATTACK)
|
||||
{
|
||||
currentState = AnimationState::ACTION_IDLE;
|
||||
//resetAnim = true;
|
||||
attack = 0;
|
||||
}
|
||||
|
||||
if (currentState == AnimationState::ACTION_TO_DEATH)
|
||||
{
|
||||
currentState = AnimationState::DEATH_IDLE;
|
||||
//resetAnim = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -53,11 +53,21 @@ public:
|
||||
void drawShadowDepth(Renderer& renderer);
|
||||
void drawWithShadow(Renderer& renderer, const Eigen::Matrix4f& lightFromCamera, GLuint shadowMapTex, const Eigen::Vector3f& lightDirCamera);
|
||||
|
||||
// Character-to-character collision (XZ-plane). Used by Location to keep
|
||||
// player/NPCs from walking through each other.
|
||||
float collisionRadius = 0.45f;
|
||||
|
||||
// attackDirection is a world-space horizontal vector pointing from the
|
||||
// attacker toward this character — i.e. the direction the hit pushes
|
||||
// them. A zero vector disables directional sparks (isotropic burst).
|
||||
void applyDamage(float damageAmount, const Eigen::Vector3f& attackDirection = Eigen::Vector3f::Zero());
|
||||
// Movement/path introspection used for dynamic replanning.
|
||||
bool isMoving() const;
|
||||
const Eigen::Vector3f& getRequestedWalkTarget() const { return requestedWalkTarget; }
|
||||
Eigen::Vector3f getCurrentNavigationTarget() const;
|
||||
void forceReplan();
|
||||
|
||||
|
||||
// attackDirection is a world-space horizontal vector pointing from the
|
||||
// attacker toward this character — i.e. the direction the hit pushes
|
||||
// them. A zero vector disables directional sparks (isotropic burst).
|
||||
void applyDamage(float damageAmount, const Eigen::Vector3f& attackDirection = Eigen::Vector3f::Zero());
|
||||
|
||||
// Configures the per-character hit-spark emitter with the shared spark
|
||||
// texture. Safe to call once per character after construction.
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
#include "Game.h"
|
||||
#include "AnimatedModel.h"
|
||||
#include "BoneAnimatedModel.h"
|
||||
#include "utils/Utils.h"
|
||||
#include "render/OpenGlExtensions.h"
|
||||
#include <iostream>
|
||||
@ -662,16 +661,16 @@ namespace ZL
|
||||
if (event.type == SDL_KEYDOWN && event.key.repeat == 0) {
|
||||
switch (event.key.keysym.sym) {
|
||||
case SDLK_1:
|
||||
if (audioPlayer) audioPlayer->playSoundAsync("audio/background.wav");
|
||||
//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");
|
||||
//if (audioPlayer) audioPlayer->playMusicAsync("audio/lullaby-music-vol20-186394--online-audio-convert.com.ogg");
|
||||
break;
|
||||
case SDLK_3:
|
||||
if (audioPlayer) audioPlayer->stopMusicAsync();
|
||||
//if (audioPlayer) audioPlayer->stopMusicAsync();
|
||||
break;
|
||||
case SDLK_f:
|
||||
currentLocation->dialogueSystem.startDialogue("test_choice_dialogue");
|
||||
//currentLocation->dialogueSystem.startDialogue("test_choice_dialogue");
|
||||
break;
|
||||
|
||||
case SDLK_e:
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
#pragma once
|
||||
#include "Character.h"
|
||||
#include "BoneAnimatedModel.h"
|
||||
#include "render/Renderer.h"
|
||||
#include "Environment.h"
|
||||
#include "render/TextureManager.h"
|
||||
@ -107,9 +106,6 @@ namespace ZL {
|
||||
void updatePinchZoom();
|
||||
void endPinch();
|
||||
int countNonUiPointers() const;
|
||||
void handleDown(int64_t fingerId, int mx, int my);
|
||||
void handleUp(int64_t fingerId, int mx, int my);
|
||||
void handleMotion(int64_t fingerId, int mx, int my);
|
||||
|
||||
void setupQuestJournalUi();
|
||||
void toggleQuestJournal();
|
||||
|
||||
225
src/Location.cpp
225
src/Location.cpp
@ -23,6 +23,23 @@ namespace ZL
|
||||
// on_npc_interact callback fires and the conversation begins.
|
||||
static constexpr float NPC_TALK_DISTANCE = 1.25f;
|
||||
|
||||
static float distancePointToSegmentXZ(const Eigen::Vector3f& p,
|
||||
const Eigen::Vector3f& a,
|
||||
const Eigen::Vector3f& b)
|
||||
{
|
||||
const Eigen::Vector2f p2(p.x(), p.z());
|
||||
const Eigen::Vector2f a2(a.x(), a.z());
|
||||
const Eigen::Vector2f b2(b.x(), b.z());
|
||||
const Eigen::Vector2f ab = b2 - a2;
|
||||
const float abLenSq = ab.squaredNorm();
|
||||
if (abLenSq <= 1e-8f) {
|
||||
return (p2 - a2).norm();
|
||||
}
|
||||
const float t = std::clamp((p2 - a2).dot(ab) / abLenSq, 0.0f, 1.0f);
|
||||
const Eigen::Vector2f closest = a2 + ab * t;
|
||||
return (p2 - closest).norm();
|
||||
}
|
||||
|
||||
Location::Location(Renderer& iRenderer, Inventory& iInventory)
|
||||
: renderer(iRenderer)
|
||||
, inventory(iInventory)
|
||||
@ -181,38 +198,51 @@ namespace ZL
|
||||
|
||||
void Location::setupNavigation(const std::string& navigationJsonPath)
|
||||
{
|
||||
std::vector<PathFinder::ObstacleMesh> obstacles;
|
||||
obstacles.reserve(gameObjects.size() + interactiveObjects.size());
|
||||
|
||||
for (const auto& item : gameObjects) {
|
||||
const LoadedGameObject& gameObj = item.second;
|
||||
obstacles.push_back({ &gameObj.mesh.data, Eigen::Vector3f::Zero() });
|
||||
}
|
||||
|
||||
for (const InteractiveObject& intObj : interactiveObjects) {
|
||||
if (!intObj.isActive) {
|
||||
continue;
|
||||
}
|
||||
obstacles.push_back({ &intObj.mesh.data, intObj.position });
|
||||
}
|
||||
|
||||
navigation.build(obstacles, navigationJsonPath, CONST_ZIP_FILE);
|
||||
// Static navigation blockers are defined in the navigation JSON as polygons.
|
||||
// NPC + player are handled as dynamic obstacles, so they are intentionally NOT in JSON.
|
||||
navigation.build({}, navigationJsonPath, CONST_ZIP_FILE);
|
||||
|
||||
#ifdef SHOW_PATH
|
||||
buildDebugNavMeshes();
|
||||
#endif
|
||||
|
||||
auto planner = [this](const Eigen::Vector3f& start, const Eigen::Vector3f& end) {
|
||||
return navigation.findPath(start, end);
|
||||
static constexpr float kDynamicObstacleInfluenceDist = 6.0f;
|
||||
|
||||
auto makePlanner = [this](const Character* self) {
|
||||
return [this, self](const Eigen::Vector3f& start, const Eigen::Vector3f& end) {
|
||||
std::vector<PathFinder::DynamicObstacle> dynamicObstacles;
|
||||
dynamicObstacles.reserve(npcs.size() + 1);
|
||||
|
||||
const auto addCharacter = [&](const Character* other) {
|
||||
if (!other || other == self) return;
|
||||
if (other->hp <= 0.f) return;
|
||||
|
||||
if (distancePointToSegmentXZ(other->position, start, end) > kDynamicObstacleInfluenceDist) {
|
||||
return;
|
||||
}
|
||||
|
||||
PathFinder::DynamicObstacle obs;
|
||||
obs.position = Eigen::Vector3f(other->position.x(), navigation.getFloorY(), other->position.z());
|
||||
obs.radius = (std::max)(0.0f, other->collisionRadius);
|
||||
dynamicObstacles.push_back(obs);
|
||||
};
|
||||
|
||||
addCharacter(player.get());
|
||||
for (const auto& npc : npcs) {
|
||||
addCharacter(npc.get());
|
||||
}
|
||||
|
||||
return navigation.findPath(start, end, dynamicObstacles);
|
||||
};
|
||||
};
|
||||
|
||||
if (player) {
|
||||
player->setPathPlanner(planner);
|
||||
player->setPathPlanner(makePlanner(player.get()));
|
||||
}
|
||||
|
||||
for (auto& npc : npcs) {
|
||||
if (npc) {
|
||||
npc->setPathPlanner(planner);
|
||||
npc->setPathPlanner(makePlanner(npc.get()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -641,6 +671,156 @@ namespace ZL
|
||||
return navigation.setAreaAvailable(areaName, available);
|
||||
}
|
||||
|
||||
void Location::resolveCharacterCollisions()
|
||||
{
|
||||
std::vector<Character*> characters;
|
||||
characters.reserve(npcs.size() + 1);
|
||||
|
||||
if (player) {
|
||||
characters.push_back(player.get());
|
||||
}
|
||||
for (auto& npc : npcs) {
|
||||
if (npc) {
|
||||
characters.push_back(npc.get());
|
||||
}
|
||||
}
|
||||
|
||||
if (characters.size() < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
static constexpr int kIterations = 3;
|
||||
static constexpr float kMinSeparationEps = 1e-4f;
|
||||
|
||||
for (int iter = 0; iter < kIterations; ++iter) {
|
||||
for (size_t i = 0; i < characters.size(); ++i) {
|
||||
for (size_t j = i + 1; j < characters.size(); ++j) {
|
||||
Character* a = characters[i];
|
||||
Character* b = characters[j];
|
||||
if (!a || !b) continue;
|
||||
if (a->hp <= 0.f || b->hp <= 0.f) continue;
|
||||
|
||||
const float minDist = a->collisionRadius + b->collisionRadius;
|
||||
if (minDist <= 0.f) continue;
|
||||
|
||||
const Eigen::Vector2f pa(a->position.x(), a->position.z());
|
||||
const Eigen::Vector2f pb(b->position.x(), b->position.z());
|
||||
const Eigen::Vector2f delta = pb - pa;
|
||||
const float dist = delta.norm();
|
||||
|
||||
if (dist >= minDist) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Eigen::Vector2f normal(1.f, 0.f);
|
||||
if (dist > kMinSeparationEps) {
|
||||
normal = delta / dist;
|
||||
}
|
||||
|
||||
const float penetration = (minDist - dist);
|
||||
const float push = penetration * 0.5f;
|
||||
|
||||
Eigen::Vector3f newA = a->position;
|
||||
Eigen::Vector3f newB = b->position;
|
||||
newA.x() -= normal.x() * push;
|
||||
newA.z() -= normal.y() * push;
|
||||
newA.y() = 0.f;
|
||||
newB.x() += normal.x() * push;
|
||||
newB.z() += normal.y() * push;
|
||||
newB.y() = 0.f;
|
||||
|
||||
if (navigation.isReady()) {
|
||||
const bool aOk = navigation.isWalkable(newA);
|
||||
const bool bOk = navigation.isWalkable(newB);
|
||||
|
||||
if (aOk && bOk) {
|
||||
a->position = newA;
|
||||
b->position = newB;
|
||||
}
|
||||
else if (aOk && !bOk) {
|
||||
a->position = newA;
|
||||
}
|
||||
else if (!aOk && bOk) {
|
||||
b->position = newB;
|
||||
}
|
||||
}
|
||||
else {
|
||||
a->position = newA;
|
||||
b->position = newB;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Location::updateDynamicReplans(int64_t deltaMs)
|
||||
{
|
||||
static constexpr float kMovedEps = 0.05f;
|
||||
static constexpr float kReplanTriggerDist = 1.1f;
|
||||
static constexpr int64_t kReplanCooldownMs = 300;
|
||||
|
||||
for (auto it = replanCooldownRemainingMs.begin(); it != replanCooldownRemainingMs.end();) {
|
||||
it->second -= deltaMs;
|
||||
if (it->second <= 0) {
|
||||
it = replanCooldownRemainingMs.erase(it);
|
||||
}
|
||||
else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Character*> characters;
|
||||
characters.reserve(npcs.size() + 1);
|
||||
if (player) characters.push_back(player.get());
|
||||
for (auto& npc : npcs) if (npc) characters.push_back(npc.get());
|
||||
|
||||
std::vector<Character*> movers;
|
||||
movers.reserve(characters.size());
|
||||
for (Character* c : characters) {
|
||||
if (!c) continue;
|
||||
auto it = lastCharacterPositions.find(c);
|
||||
if (it == lastCharacterPositions.end()) {
|
||||
lastCharacterPositions[c] = c->position;
|
||||
continue;
|
||||
}
|
||||
|
||||
const Eigen::Vector3f prev = it->second;
|
||||
const float moved = (c->position - prev).norm();
|
||||
it->second = c->position;
|
||||
|
||||
if (moved > kMovedEps) {
|
||||
movers.push_back(c);
|
||||
}
|
||||
}
|
||||
|
||||
if (movers.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (Character* mover : movers) {
|
||||
if (!mover) continue;
|
||||
|
||||
for (Character* walker : characters) {
|
||||
if (!walker || walker == mover) continue;
|
||||
if (walker->hp <= 0.f) continue;
|
||||
if (!walker->isMoving()) continue;
|
||||
|
||||
if (replanCooldownRemainingMs.find(walker) != replanCooldownRemainingMs.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const Eigen::Vector3f nextTarget = walker->getCurrentNavigationTarget();
|
||||
const float distToSegment = distancePointToSegmentXZ(mover->position, walker->position, nextTarget);
|
||||
if (distToSegment > kReplanTriggerDist) {
|
||||
continue;
|
||||
}
|
||||
|
||||
walker->forceReplan();
|
||||
replanCooldownRemainingMs[walker] = kReplanCooldownMs;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Location::update(int64_t delta)
|
||||
{
|
||||
if (player) {
|
||||
@ -652,6 +832,9 @@ namespace ZL
|
||||
npc->update(delta);
|
||||
}
|
||||
|
||||
resolveCharacterCollisions();
|
||||
updateDynamicReplans(delta);
|
||||
|
||||
// Check if player reached target interactive object
|
||||
if (targetInteractiveObject && player) {
|
||||
float distToObject = (player->position - targetInteractiveObject->position).norm();
|
||||
@ -888,4 +1071,4 @@ namespace ZL
|
||||
}
|
||||
|
||||
|
||||
} // namespace ZL
|
||||
} // namespace ZL
|
||||
|
||||
@ -13,6 +13,8 @@
|
||||
#include "dialogue/DialogueSystem.h"
|
||||
#include "SparkEmitter.h"
|
||||
#include <functional>
|
||||
#include <cstdint>
|
||||
#include <unordered_map>
|
||||
namespace ZL
|
||||
{
|
||||
|
||||
@ -112,6 +114,13 @@ namespace ZL
|
||||
protected:
|
||||
Renderer& renderer;
|
||||
Inventory& inventory;
|
||||
|
||||
private:
|
||||
void resolveCharacterCollisions();
|
||||
void updateDynamicReplans(int64_t deltaMs);
|
||||
|
||||
std::unordered_map<Character*, Eigen::Vector3f> lastCharacterPositions;
|
||||
std::unordered_map<Character*, int64_t> replanCooldownRemainingMs;
|
||||
};
|
||||
|
||||
} // namespace ZL
|
||||
} // namespace ZL
|
||||
|
||||
@ -10,219 +10,6 @@
|
||||
namespace ZL
|
||||
{
|
||||
|
||||
|
||||
VertexDataStruct LoadFromTextFile(const std::string& fileName, const std::string& ZIPFileName)
|
||||
{
|
||||
VertexDataStruct result;
|
||||
std::ifstream filestream;
|
||||
std::istringstream zipStream;
|
||||
|
||||
if (!ZIPFileName.empty())
|
||||
{
|
||||
std::vector<char> fileData = readFileFromZIP(fileName, ZIPFileName);
|
||||
std::string fileContents(fileData.begin(), fileData.end());
|
||||
zipStream.str(fileContents);
|
||||
}
|
||||
else
|
||||
{
|
||||
filestream.open(fileName);
|
||||
}
|
||||
|
||||
// Создаем ссылку f на нужный поток – после этого код ниже остается без изменений
|
||||
std::istream& f = (!ZIPFileName.empty()) ? static_cast<std::istream&>(zipStream) : static_cast<std::istream&>(filestream);
|
||||
|
||||
|
||||
//Skip first 5 lines
|
||||
std::string tempLine;
|
||||
|
||||
std::getline(f, tempLine);
|
||||
|
||||
static const std::regex pattern_count(R"(\d+)");
|
||||
static const std::regex pattern_float(R"([-]?\d+\.\d+)");
|
||||
static const std::regex pattern_int(R"([-]?\d+)");
|
||||
|
||||
|
||||
std::smatch match;
|
||||
|
||||
int numberVertices;
|
||||
|
||||
if (std::regex_search(tempLine, match, pattern_count)) {
|
||||
std::string number_str = match.str();
|
||||
numberVertices = std::stoi(number_str);
|
||||
}
|
||||
else {
|
||||
std::cout << "No number found in the input string: " << tempLine << std::endl;
|
||||
throw std::runtime_error("No number found in the input string.");
|
||||
}
|
||||
|
||||
std::vector<Vector3f> vertices;
|
||||
|
||||
vertices.resize(numberVertices);
|
||||
for (int i = 0; i < numberVertices; i++)
|
||||
{
|
||||
std::getline(f, tempLine);
|
||||
|
||||
std::vector<float> floatValues;
|
||||
|
||||
auto b = tempLine.cbegin();
|
||||
auto e = tempLine.cend();
|
||||
while (std::regex_search(b, e, match, pattern_float)) {
|
||||
floatValues.push_back(std::stof(match.str()));
|
||||
b = match.suffix().first;
|
||||
}
|
||||
|
||||
vertices[i] = Vector3f{ floatValues[0], floatValues[1], floatValues[2] };
|
||||
}
|
||||
|
||||
std::cout << "UV Coordinates" << std::endl;
|
||||
|
||||
std::getline(f, tempLine); //===UV Coordinates:
|
||||
|
||||
std::getline(f, tempLine); //triangle count
|
||||
int numberTriangles;
|
||||
|
||||
if (std::regex_search(tempLine, match, pattern_count)) {
|
||||
std::string number_str = match.str();
|
||||
numberTriangles = std::stoi(number_str);
|
||||
}
|
||||
else {
|
||||
std::cout << "-No number found in the input string: " << tempLine << std::endl;
|
||||
throw std::runtime_error("-No number found in the input string.");
|
||||
}
|
||||
|
||||
|
||||
// Now process UVs
|
||||
std::vector<std::array<Vector2f, 3>> uvCoords;
|
||||
|
||||
uvCoords.resize(numberTriangles);
|
||||
|
||||
for (int i = 0; i < numberTriangles; i++)
|
||||
{
|
||||
std::getline(f, tempLine); //Face 0
|
||||
|
||||
int uvCount;
|
||||
std::getline(f, tempLine);
|
||||
if (std::regex_search(tempLine, match, pattern_count)) {
|
||||
std::string number_str = match.str();
|
||||
uvCount = std::stoi(number_str);
|
||||
}
|
||||
else {
|
||||
std::cout << "2 No number found in the input string: " << tempLine << std::endl;
|
||||
throw std::runtime_error("2 No number found in the input string.");
|
||||
}
|
||||
|
||||
if (uvCount != 3)
|
||||
{
|
||||
std::cout << "UV count is not 3 for triangle " << i << ": " << uvCount << std::endl;
|
||||
throw std::runtime_error("more than 3 uvs");
|
||||
}
|
||||
|
||||
std::vector<float> floatValues;
|
||||
|
||||
for (int j = 0; j < 3; j++)
|
||||
{
|
||||
std::getline(f, tempLine); //UV <Vector (-0.3661, -1.1665)>
|
||||
|
||||
auto b = tempLine.cbegin();
|
||||
auto e = tempLine.cend();
|
||||
floatValues.clear();
|
||||
while (std::regex_search(b, e, match, pattern_float)) {
|
||||
floatValues.push_back(std::stof(match.str()));
|
||||
b = match.suffix().first;
|
||||
}
|
||||
|
||||
if (floatValues.size() != 2)
|
||||
{
|
||||
std::cout << "UV count is not 2: " << j << " " << floatValues.size() << std::endl;
|
||||
throw std::runtime_error("more than 2 uvs---");
|
||||
}
|
||||
|
||||
uvCoords[i][j] = Vector2f{ floatValues[0],floatValues[1] };
|
||||
}
|
||||
}
|
||||
|
||||
//std::cout << "Normals go" << std::endl;
|
||||
|
||||
std::getline(f, tempLine); //===Normals:
|
||||
|
||||
|
||||
std::vector<Vector3f> normals;
|
||||
|
||||
normals.resize(numberVertices);
|
||||
for (int i = 0; i < numberVertices; i++)
|
||||
{
|
||||
std::getline(f, tempLine);
|
||||
|
||||
std::vector<float> floatValues;
|
||||
|
||||
auto b = tempLine.cbegin();
|
||||
auto e = tempLine.cend();
|
||||
while (std::regex_search(b, e, match, pattern_float)) {
|
||||
floatValues.push_back(std::stof(match.str()));
|
||||
b = match.suffix().first;
|
||||
}
|
||||
|
||||
normals[i] = Vector3f{ floatValues[0], floatValues[1], floatValues[2] };
|
||||
}
|
||||
|
||||
//std::cout << "Triangles go:" << std::endl;
|
||||
|
||||
std::getline(f, tempLine); //===Triangles: 3974
|
||||
|
||||
|
||||
std::vector<std::array<int, 3>> triangles;
|
||||
|
||||
triangles.resize(numberTriangles);
|
||||
for (int i = 0; i < numberTriangles; i++)
|
||||
{
|
||||
std::getline(f, tempLine);
|
||||
|
||||
std::vector<int> intValues;
|
||||
|
||||
auto b = tempLine.cbegin();
|
||||
auto e = tempLine.cend();
|
||||
while (std::regex_search(b, e, match, pattern_int)) {
|
||||
intValues.push_back(std::stoi(match.str()));
|
||||
b = match.suffix().first;
|
||||
}
|
||||
|
||||
triangles[i] = { intValues[0], intValues[1], intValues[2] };
|
||||
}
|
||||
|
||||
|
||||
std::cout << "Process vertices" << std::endl;
|
||||
|
||||
|
||||
|
||||
// Now let's process vertices
|
||||
|
||||
for (int i = 0; i < numberTriangles; i++)
|
||||
{
|
||||
|
||||
result.PositionData.push_back(vertices[triangles[i][0]]);
|
||||
result.PositionData.push_back(vertices[triangles[i][1]]);
|
||||
result.PositionData.push_back(vertices[triangles[i][2]]);
|
||||
|
||||
result.TexCoordData.push_back(uvCoords[i][0]);
|
||||
result.TexCoordData.push_back(uvCoords[i][1]);
|
||||
result.TexCoordData.push_back(uvCoords[i][2]);
|
||||
|
||||
}
|
||||
|
||||
//Swap from Blender format to OpenGL format
|
||||
for (int i = 0; i < result.PositionData.size(); i++)
|
||||
{
|
||||
Vector3f tempVec = result.PositionData[i];
|
||||
result.PositionData[i](0) = tempVec(1);
|
||||
result.PositionData[i](1) = tempVec(2);
|
||||
result.PositionData[i](2) = tempVec(0);
|
||||
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
VertexDataStruct LoadFromTextFile02(const std::string& fileName, const std::string& ZIPFileName)
|
||||
{
|
||||
VertexDataStruct result;
|
||||
|
||||
@ -6,6 +6,5 @@
|
||||
|
||||
namespace ZL
|
||||
{
|
||||
VertexDataStruct LoadFromTextFile(const std::string& fileName, const std::string& ZIPFileName = "");
|
||||
VertexDataStruct LoadFromTextFile02(const std::string& fileName, const std::string& ZIPFileName = "");
|
||||
}
|
||||
@ -205,11 +205,7 @@ void DialogueOverlay::drawDialogue(Renderer& renderer, const PresentationModel&
|
||||
nameRenderer->drawText(model.speaker, nameX, nameY, 1.0f, false, { 1.0f, 0.88f, 0.45f, 1.0f });
|
||||
}
|
||||
|
||||
// const std::string wrappedBody = wrapText(model.visibleText, 90);
|
||||
// bodyRenderer->drawText(wrappedBody, bodyX, bodyY, 1.0f, false, { 1.0f, 1.0f, 1.0f, 1.0f });
|
||||
// const std::string wrappedBody = wrapText(model.visibleText, 56);
|
||||
// bodyRenderer->drawText(wrappedBody, bodyX, bodyY, 1.0f, false, { 1.0f, 1.0f, 1.0f, 1.0f });
|
||||
|
||||
|
||||
const float bodyTextScale = 1.0f;
|
||||
const float bodyMaxWidthPx = textboxRect.w - 48.0f;
|
||||
|
||||
|
||||
@ -7,14 +7,9 @@
|
||||
#include "external/nlohmann/json.hpp"
|
||||
#include "TextModel.h"
|
||||
#include "InteractiveObject.h"
|
||||
//#include "Character.h"
|
||||
//#include "render/TextureManager.h"
|
||||
|
||||
namespace ZL {
|
||||
|
||||
//struct Texture;
|
||||
//struct VertexRenderStruct;
|
||||
//class Renderer;
|
||||
|
||||
class Character;
|
||||
|
||||
struct GameObjectData {
|
||||
|
||||
@ -38,6 +38,52 @@ namespace {
|
||||
|
||||
return polygon;
|
||||
}
|
||||
|
||||
float distancePointToSegment2D(const Eigen::Vector2f& p,
|
||||
const Eigen::Vector2f& a,
|
||||
const Eigen::Vector2f& b)
|
||||
{
|
||||
const Eigen::Vector2f ab = b - a;
|
||||
const float abLenSq = ab.squaredNorm();
|
||||
if (abLenSq <= 1e-8f) {
|
||||
return (p - a).norm();
|
||||
}
|
||||
float t = (p - a).dot(ab) / abLenSq;
|
||||
t = (std::max)(0.0f, (std::min)(1.0f, t));
|
||||
const Eigen::Vector2f proj = a + t * ab;
|
||||
return (p - proj).norm();
|
||||
}
|
||||
|
||||
Eigen::Vector2f closestPointOnSegment2D(const Eigen::Vector2f& p,
|
||||
const Eigen::Vector2f& a,
|
||||
const Eigen::Vector2f& b)
|
||||
{
|
||||
const Eigen::Vector2f ab = b - a;
|
||||
const float abLenSq = ab.squaredNorm();
|
||||
if (abLenSq <= 1e-8f) {
|
||||
return a;
|
||||
}
|
||||
|
||||
float t = (p - a).dot(ab) / abLenSq;
|
||||
t = (std::max)(0.0f, (std::min)(1.0f, t));
|
||||
return a + t * ab;
|
||||
}
|
||||
|
||||
float distancePointToPolygonEdges(const Eigen::Vector2f& p,
|
||||
const std::vector<Eigen::Vector2f>& polygon)
|
||||
{
|
||||
if (polygon.size() < 2) {
|
||||
return (std::numeric_limits<float>::max)();
|
||||
}
|
||||
|
||||
float best = (std::numeric_limits<float>::max)();
|
||||
for (size_t i = 0; i < polygon.size(); ++i) {
|
||||
const Eigen::Vector2f& a = polygon[i];
|
||||
const Eigen::Vector2f& b = polygon[(i + 1) % polygon.size()];
|
||||
best = (std::min)(best, distancePointToSegment2D(p, a, b));
|
||||
}
|
||||
return best;
|
||||
}
|
||||
}
|
||||
|
||||
void PathFinder::build(const std::vector<ObstacleMesh>& obstacleMeshes,
|
||||
@ -74,8 +120,8 @@ std::vector<Eigen::Vector3f> PathFinder::findPath(const Eigen::Vector3f& start,
|
||||
|
||||
Cell startCell;
|
||||
Cell endCell;
|
||||
if (!findNearestWalkableCell(start, startCell) ||
|
||||
!findNearestWalkableCell(end, endCell)) {
|
||||
if (!findNearestWalkableCell(start, startCell, walkable) ||
|
||||
!findNearestWalkableCell(end, endCell, walkable)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
@ -121,7 +167,7 @@ std::vector<Eigen::Vector3f> PathFinder::findPath(const Eigen::Vector3f& start,
|
||||
|
||||
for (const auto& offset : offsets) {
|
||||
Cell next{ currentCell.x + offset[0], currentCell.z + offset[1] };
|
||||
if (!isCellWalkable(next)) {
|
||||
if (!isCellWalkable(next, walkable)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -129,7 +175,7 @@ std::vector<Eigen::Vector3f> PathFinder::findPath(const Eigen::Vector3f& start,
|
||||
if (diagonal) {
|
||||
Cell horizontal{ currentCell.x + offset[0], currentCell.z };
|
||||
Cell vertical{ currentCell.x, currentCell.z + offset[1] };
|
||||
if (!isCellWalkable(horizontal) || !isCellWalkable(vertical)) {
|
||||
if (!isCellWalkable(horizontal, walkable) || !isCellWalkable(vertical, walkable)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@ -160,7 +206,161 @@ std::vector<Eigen::Vector3f> PathFinder::findPath(const Eigen::Vector3f& start,
|
||||
}
|
||||
}
|
||||
std::reverse(cells.begin(), cells.end());
|
||||
cells = smoothCells(cells);
|
||||
cells = smoothCells(cells, walkable);
|
||||
|
||||
std::vector<Eigen::Vector3f> path;
|
||||
path.reserve(cells.size());
|
||||
for (const Cell& cell : cells) {
|
||||
path.push_back(cellCenter(cell));
|
||||
}
|
||||
|
||||
if (!path.empty() && (path.front() - Eigen::Vector3f(start.x(), floorY, start.z())).norm() < cellSize * 0.75f) {
|
||||
path.erase(path.begin());
|
||||
}
|
||||
|
||||
if (!path.empty()) {
|
||||
Cell requestedEndCell;
|
||||
if (worldToCell(end, requestedEndCell) &&
|
||||
requestedEndCell.x == endCell.x &&
|
||||
requestedEndCell.z == endCell.z) {
|
||||
path.back() = Eigen::Vector3f(end.x(), floorY, end.z());
|
||||
}
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
std::vector<Eigen::Vector3f> PathFinder::findPath(const Eigen::Vector3f& start,
|
||||
const Eigen::Vector3f& end,
|
||||
const std::vector<DynamicObstacle>& dynamicObstacles) const
|
||||
{
|
||||
if (!ready || walkable.empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<unsigned char> walkableGrid = walkable;
|
||||
if (!dynamicObstacles.empty()) {
|
||||
for (const DynamicObstacle& obstacle : dynamicObstacles) {
|
||||
const float radius = obstacle.radius + agentRadius;
|
||||
if (radius <= 0.0f) continue;
|
||||
|
||||
const float minWorldX = obstacle.position.x() - radius;
|
||||
const float maxWorldX = obstacle.position.x() + radius;
|
||||
const float minWorldZ = obstacle.position.z() - radius;
|
||||
const float maxWorldZ = obstacle.position.z() + radius;
|
||||
|
||||
const int minCellX = (std::max)(0, static_cast<int>(std::floor((minWorldX - minX) / cellSize)));
|
||||
const int maxCellX = (std::min)(gridWidth - 1, static_cast<int>(std::floor((maxWorldX - minX) / cellSize)));
|
||||
const int minCellZ = (std::max)(0, static_cast<int>(std::floor((minWorldZ - minZ) / cellSize)));
|
||||
const int maxCellZ = (std::min)(gridDepth - 1, static_cast<int>(std::floor((maxWorldZ - minZ) / cellSize)));
|
||||
|
||||
const float radiusSq = radius * radius;
|
||||
for (int z = minCellZ; z <= maxCellZ; ++z) {
|
||||
for (int x = minCellX; x <= maxCellX; ++x) {
|
||||
const Cell cell{ x, z };
|
||||
const Eigen::Vector3f center = cellCenter(cell);
|
||||
const float dx = center.x() - obstacle.position.x();
|
||||
const float dz = center.z() - obstacle.position.z();
|
||||
if (dx * dx + dz * dz <= radiusSq) {
|
||||
const int idx = indexOf(cell);
|
||||
if (idx >= 0 && idx < static_cast<int>(walkableGrid.size())) {
|
||||
walkableGrid[static_cast<size_t>(idx)] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Cell startCell;
|
||||
Cell endCell;
|
||||
if (!findNearestWalkableCell(start, startCell, walkableGrid) ||
|
||||
!findNearestWalkableCell(end, endCell, walkableGrid)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (startCell.x == endCell.x && startCell.z == endCell.z) {
|
||||
return { cellCenter(endCell) };
|
||||
}
|
||||
|
||||
struct QueueNode {
|
||||
int index = 0;
|
||||
float priority = 0.0f;
|
||||
|
||||
bool operator<(const QueueNode& other) const
|
||||
{
|
||||
return priority > other.priority;
|
||||
}
|
||||
};
|
||||
|
||||
const int cellCount = gridWidth * gridDepth;
|
||||
std::vector<float> cost(static_cast<size_t>(cellCount), INF_COST);
|
||||
std::vector<int> cameFrom(static_cast<size_t>(cellCount), -1);
|
||||
std::priority_queue<QueueNode> open;
|
||||
|
||||
const int startIndex = indexOf(startCell);
|
||||
const int endIndex = indexOf(endCell);
|
||||
|
||||
cost[static_cast<size_t>(startIndex)] = 0.0f;
|
||||
open.push({ startIndex, 0.0f });
|
||||
|
||||
static const int offsets[8][2] = {
|
||||
{ 1, 0 }, { -1, 0 }, { 0, 1 }, { 0, -1 },
|
||||
{ 1, 1 }, { 1, -1 }, { -1, 1 }, { -1, -1 }
|
||||
};
|
||||
|
||||
while (!open.empty()) {
|
||||
const QueueNode current = open.top();
|
||||
open.pop();
|
||||
|
||||
if (current.index == endIndex) {
|
||||
break;
|
||||
}
|
||||
|
||||
const Cell currentCell{ current.index % gridWidth, current.index / gridWidth };
|
||||
|
||||
for (const auto& offset : offsets) {
|
||||
Cell next{ currentCell.x + offset[0], currentCell.z + offset[1] };
|
||||
if (!isCellWalkable(next, walkableGrid)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const bool diagonal = offset[0] != 0 && offset[1] != 0;
|
||||
if (diagonal) {
|
||||
Cell horizontal{ currentCell.x + offset[0], currentCell.z };
|
||||
Cell vertical{ currentCell.x, currentCell.z + offset[1] };
|
||||
if (!isCellWalkable(horizontal, walkableGrid) || !isCellWalkable(vertical, walkableGrid)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
const int nextIndex = indexOf(next);
|
||||
const float stepCost = diagonal ? 1.41421356f : 1.0f;
|
||||
const float newCost = cost[static_cast<size_t>(current.index)] + stepCost;
|
||||
if (newCost >= cost[static_cast<size_t>(nextIndex)]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
cost[static_cast<size_t>(nextIndex)] = newCost;
|
||||
cameFrom[static_cast<size_t>(nextIndex)] = current.index;
|
||||
const float priority = newCost + distanceCells(next, endCell);
|
||||
open.push({ nextIndex, priority });
|
||||
}
|
||||
}
|
||||
|
||||
if (cameFrom[static_cast<size_t>(endIndex)] == -1) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<Cell> cells;
|
||||
for (int current = endIndex; current != -1; current = cameFrom[static_cast<size_t>(current)]) {
|
||||
cells.push_back({ current % gridWidth, current / gridWidth });
|
||||
if (current == startIndex) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
std::reverse(cells.begin(), cells.end());
|
||||
cells = smoothCells(cells, walkableGrid);
|
||||
|
||||
std::vector<Eigen::Vector3f> path;
|
||||
path.reserve(cells.size());
|
||||
@ -206,7 +406,7 @@ bool PathFinder::setAreaAvailable(const std::string& areaName, bool available)
|
||||
bool PathFinder::isWalkable(const Eigen::Vector3f& point) const
|
||||
{
|
||||
Cell cell;
|
||||
return worldToCell(point, cell) && isCellWalkable(cell);
|
||||
return worldToCell(point, cell) && isCellWalkable(cell, walkable);
|
||||
}
|
||||
|
||||
void PathFinder::loadConfig(const std::string& configPath, const std::string& zipPath)
|
||||
@ -215,7 +415,9 @@ void PathFinder::loadConfig(const std::string& configPath, const std::string& zi
|
||||
agentRadius = 0.45f;
|
||||
floorY = 0.0f;
|
||||
objectPadding = 0.25f;
|
||||
boundaryPadding = 0.0f;
|
||||
areas.clear();
|
||||
obstaclePolygons.clear();
|
||||
|
||||
try {
|
||||
std::string content;
|
||||
@ -236,6 +438,7 @@ void PathFinder::loadConfig(const std::string& configPath, const std::string& zi
|
||||
agentRadius = root.value("agentRadius", agentRadius);
|
||||
floorY = root.value("floorY", floorY);
|
||||
objectPadding = root.value("objectPadding", objectPadding);
|
||||
boundaryPadding = root.value("boundaryPadding", boundaryPadding);
|
||||
|
||||
if (!root.contains("areas") || !root["areas"].is_array()) {
|
||||
std::cerr << "[nav] Navigation config has no 'areas' array: " << configPath << "\n";
|
||||
@ -259,6 +462,18 @@ void PathFinder::loadConfig(const std::string& configPath, const std::string& zi
|
||||
|
||||
areas.push_back(area);
|
||||
}
|
||||
|
||||
if (root.contains("obstacles") && root["obstacles"].is_array()) {
|
||||
for (const auto& item : root["obstacles"]) {
|
||||
ObstaclePolygon obstacle;
|
||||
obstacle.name = item.value("name", "");
|
||||
obstacle.polygon = readPolygon(item);
|
||||
if (obstacle.polygon.size() < 3) {
|
||||
continue;
|
||||
}
|
||||
obstaclePolygons.push_back(std::move(obstacle));
|
||||
}
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "[nav] Failed to load config '" << configPath << "': " << e.what() << "\n";
|
||||
}
|
||||
@ -266,6 +481,7 @@ void PathFinder::loadConfig(const std::string& configPath, const std::string& zi
|
||||
cellSize = (std::max)(cellSize, 0.1f);
|
||||
agentRadius = (std::max)(agentRadius, 0.0f);
|
||||
objectPadding = (std::max)(objectPadding, 0.0f);
|
||||
boundaryPadding = (std::max)(boundaryPadding, 0.0f);
|
||||
}
|
||||
|
||||
void PathFinder::resetGridBounds()
|
||||
@ -304,6 +520,12 @@ void PathFinder::resetGridBounds()
|
||||
}
|
||||
}
|
||||
|
||||
for (const ObstaclePolygon& obstacle : obstaclePolygons) {
|
||||
for (const Eigen::Vector2f& point : obstacle.polygon) {
|
||||
includePoint(point.x(), point.y());
|
||||
}
|
||||
}
|
||||
|
||||
const float padding = cellSize * 2.0f + agentRadius + objectPadding;
|
||||
minX -= padding;
|
||||
minZ -= padding;
|
||||
@ -319,20 +541,75 @@ void PathFinder::rebuildWalkableGrid()
|
||||
walkable.assign(static_cast<size_t>(gridWidth * gridDepth), 0);
|
||||
markAvailableAreasWalkable();
|
||||
markObstacleMeshesBlocked();
|
||||
markObstaclePolygonsBlocked();
|
||||
}
|
||||
|
||||
void PathFinder::markAvailableAreasWalkable()
|
||||
{
|
||||
const auto insideAvailableArea = [this](const Eigen::Vector2f& point) {
|
||||
for (const NavigationArea& area : areas) {
|
||||
if (!area.available) {
|
||||
continue;
|
||||
}
|
||||
if (pointInPolygon(point.x(), point.y(), area.polygon)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const auto externalBoundaryDistance = [this, &insideAvailableArea](const Eigen::Vector2f& point) {
|
||||
if (boundaryPadding <= 0.0f) {
|
||||
return (std::numeric_limits<float>::max)();
|
||||
}
|
||||
|
||||
const float sampleOffset = (std::max)(cellSize * 0.25f, 0.05f);
|
||||
float best = (std::numeric_limits<float>::max)();
|
||||
|
||||
for (const NavigationArea& area : areas) {
|
||||
if (!area.available || area.polygon.size() < 2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < area.polygon.size(); ++i) {
|
||||
const Eigen::Vector2f& a = area.polygon[i];
|
||||
const Eigen::Vector2f& b = area.polygon[(i + 1) % area.polygon.size()];
|
||||
const Eigen::Vector2f edge = b - a;
|
||||
if (edge.squaredNorm() <= 1e-8f) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const Eigen::Vector2f closest = closestPointOnSegment2D(point, a, b);
|
||||
Eigen::Vector2f normal(-edge.y(), edge.x());
|
||||
normal.normalize();
|
||||
|
||||
const bool sideAInside = insideAvailableArea(closest + normal * sampleOffset);
|
||||
const bool sideBInside = insideAvailableArea(closest - normal * sampleOffset);
|
||||
if (sideAInside && sideBInside) {
|
||||
continue;
|
||||
}
|
||||
|
||||
best = (std::min)(best, distancePointToSegment2D(point, a, b));
|
||||
}
|
||||
}
|
||||
|
||||
return best;
|
||||
};
|
||||
|
||||
for (int z = 0; z < gridDepth; ++z) {
|
||||
for (int x = 0; x < gridWidth; ++x) {
|
||||
const Cell cell{ x, z };
|
||||
const Eigen::Vector3f center = cellCenter(cell);
|
||||
const Eigen::Vector2f point(center.x(), center.z());
|
||||
|
||||
for (const NavigationArea& area : areas) {
|
||||
if (!area.available) {
|
||||
continue;
|
||||
}
|
||||
if (pointInPolygon(center.x(), center.z(), area.polygon)) {
|
||||
if (boundaryPadding > 0.0f && externalBoundaryDistance(point) < boundaryPadding) {
|
||||
continue;
|
||||
}
|
||||
walkable[static_cast<size_t>(indexOf(cell))] = 1;
|
||||
break;
|
||||
}
|
||||
@ -341,8 +618,51 @@ void PathFinder::markAvailableAreasWalkable()
|
||||
}
|
||||
}
|
||||
|
||||
void PathFinder::markObstaclePolygonsBlocked()
|
||||
{
|
||||
if (obstaclePolygons.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const float padding = agentRadius + objectPadding;
|
||||
|
||||
for (int z = 0; z < gridDepth; ++z) {
|
||||
for (int x = 0; x < gridWidth; ++x) {
|
||||
const Cell cell{ x, z };
|
||||
const int idx = indexOf(cell);
|
||||
if (idx < 0 || idx >= static_cast<int>(walkable.size())) {
|
||||
continue;
|
||||
}
|
||||
if (!walkable[static_cast<size_t>(idx)]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const Eigen::Vector3f center3 = cellCenter(cell);
|
||||
const Eigen::Vector2f center(center3.x(), center3.z());
|
||||
|
||||
for (const ObstaclePolygon& obstacle : obstaclePolygons) {
|
||||
if (pointInPolygon(center.x(), center.y(), obstacle.polygon)) {
|
||||
walkable[static_cast<size_t>(idx)] = 0;
|
||||
break;
|
||||
}
|
||||
if (padding > 0.0f &&
|
||||
distancePointToPolygonEdges(center, obstacle.polygon) <= padding) {
|
||||
walkable[static_cast<size_t>(idx)] = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PathFinder::markObstacleMeshesBlocked()
|
||||
{
|
||||
if (!obstaclePolygons.empty()) {
|
||||
// Prefer JSON-defined obstacle polygons over mesh-derived blockers.
|
||||
// Mesh blockers tend to over-block (e.g. tree crowns) and are harder to author/tune.
|
||||
return;
|
||||
}
|
||||
|
||||
const float padding = agentRadius + objectPadding;
|
||||
|
||||
for (const ObstacleMesh& obstacle : obstacles) {
|
||||
@ -406,7 +726,16 @@ bool PathFinder::isInsideGrid(const Cell& cell) const
|
||||
|
||||
bool PathFinder::isCellWalkable(const Cell& cell) const
|
||||
{
|
||||
return isInsideGrid(cell) && walkable[static_cast<size_t>(indexOf(cell))] != 0;
|
||||
return isCellWalkable(cell, walkable);
|
||||
}
|
||||
|
||||
bool PathFinder::isCellWalkable(const Cell& cell, const std::vector<unsigned char>& walkableGrid) const
|
||||
{
|
||||
const int idx = indexOf(cell);
|
||||
return isInsideGrid(cell) &&
|
||||
idx >= 0 &&
|
||||
idx < static_cast<int>(walkableGrid.size()) &&
|
||||
walkableGrid[static_cast<size_t>(idx)] != 0;
|
||||
}
|
||||
|
||||
int PathFinder::indexOf(const Cell& cell) const
|
||||
@ -415,6 +744,11 @@ int PathFinder::indexOf(const Cell& cell) const
|
||||
}
|
||||
|
||||
bool PathFinder::findNearestWalkableCell(const Eigen::Vector3f& point, Cell& out) const
|
||||
{
|
||||
return findNearestWalkableCell(point, out, walkable);
|
||||
}
|
||||
|
||||
bool PathFinder::findNearestWalkableCell(const Eigen::Vector3f& point, Cell& out, const std::vector<unsigned char>& walkableGrid) const
|
||||
{
|
||||
Cell origin;
|
||||
if (!worldToCell(point, origin)) {
|
||||
@ -424,7 +758,7 @@ bool PathFinder::findNearestWalkableCell(const Eigen::Vector3f& point, Cell& out
|
||||
origin.z = (std::min)((std::max)(z, 0), gridDepth - 1);
|
||||
}
|
||||
|
||||
if (isCellWalkable(origin)) {
|
||||
if (isCellWalkable(origin, walkableGrid)) {
|
||||
out = origin;
|
||||
return true;
|
||||
}
|
||||
@ -438,7 +772,7 @@ bool PathFinder::findNearestWalkableCell(const Eigen::Vector3f& point, Cell& out
|
||||
}
|
||||
|
||||
Cell candidate{ origin.x + dx, origin.z + dz };
|
||||
if (isCellWalkable(candidate)) {
|
||||
if (isCellWalkable(candidate, walkableGrid)) {
|
||||
out = candidate;
|
||||
return true;
|
||||
}
|
||||
@ -450,6 +784,11 @@ bool PathFinder::findNearestWalkableCell(const Eigen::Vector3f& point, Cell& out
|
||||
}
|
||||
|
||||
bool PathFinder::hasLineOfSight(const Cell& from, const Cell& to) const
|
||||
{
|
||||
return hasLineOfSight(from, to, walkable);
|
||||
}
|
||||
|
||||
bool PathFinder::hasLineOfSight(const Cell& from, const Cell& to, const std::vector<unsigned char>& walkableGrid) const
|
||||
{
|
||||
const Eigen::Vector3f a = cellCenter(from);
|
||||
const Eigen::Vector3f b = cellCenter(to);
|
||||
@ -460,7 +799,7 @@ bool PathFinder::hasLineOfSight(const Cell& from, const Cell& to) const
|
||||
const float t = static_cast<float>(i) / static_cast<float>(steps);
|
||||
const Eigen::Vector3f p = a * (1.0f - t) + b * t;
|
||||
Cell cell;
|
||||
if (!worldToCell(p, cell) || !isCellWalkable(cell)) {
|
||||
if (!worldToCell(p, cell) || !isCellWalkable(cell, walkableGrid)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -469,6 +808,11 @@ bool PathFinder::hasLineOfSight(const Cell& from, const Cell& to) const
|
||||
}
|
||||
|
||||
std::vector<PathFinder::Cell> PathFinder::smoothCells(const std::vector<Cell>& cells) const
|
||||
{
|
||||
return smoothCells(cells, walkable);
|
||||
}
|
||||
|
||||
std::vector<PathFinder::Cell> PathFinder::smoothCells(const std::vector<Cell>& cells, const std::vector<unsigned char>& walkableGrid) const
|
||||
{
|
||||
if (cells.size() <= 2) {
|
||||
return cells;
|
||||
@ -480,7 +824,7 @@ std::vector<PathFinder::Cell> PathFinder::smoothCells(const std::vector<Cell>& c
|
||||
|
||||
while (anchor < cells.size() - 1) {
|
||||
size_t next = cells.size() - 1;
|
||||
while (next > anchor + 1 && !hasLineOfSight(cells[anchor], cells[next])) {
|
||||
while (next > anchor + 1 && !hasLineOfSight(cells[anchor], cells[next], walkableGrid)) {
|
||||
--next;
|
||||
}
|
||||
|
||||
|
||||
@ -14,6 +14,16 @@ public:
|
||||
Eigen::Vector3f offset = Eigen::Vector3f::Zero();
|
||||
};
|
||||
|
||||
struct ObstaclePolygon {
|
||||
std::string name;
|
||||
std::vector<Eigen::Vector2f> polygon;
|
||||
};
|
||||
|
||||
struct DynamicObstacle {
|
||||
Eigen::Vector3f position = Eigen::Vector3f::Zero();
|
||||
float radius = 0.0f;
|
||||
};
|
||||
|
||||
struct Cell {
|
||||
int x = 0;
|
||||
int z = 0;
|
||||
@ -32,6 +42,10 @@ public:
|
||||
std::vector<Eigen::Vector3f> findPath(const Eigen::Vector3f& start,
|
||||
const Eigen::Vector3f& end) const;
|
||||
|
||||
std::vector<Eigen::Vector3f> findPath(const Eigen::Vector3f& start,
|
||||
const Eigen::Vector3f& end,
|
||||
const std::vector<DynamicObstacle>& dynamicObstacles) const;
|
||||
|
||||
bool setAreaAvailable(const std::string& areaName, bool available);
|
||||
bool isReady() const { return ready; }
|
||||
bool isWalkable(const Eigen::Vector3f& point) const;
|
||||
@ -44,6 +58,7 @@ private:
|
||||
float agentRadius = 0.45f;
|
||||
float floorY = 0.0f;
|
||||
float objectPadding = 0.25f;
|
||||
float boundaryPadding = 0.0f;
|
||||
|
||||
float minX = 0.0f;
|
||||
float minZ = 0.0f;
|
||||
@ -54,6 +69,7 @@ private:
|
||||
std::string loadedConfigPath;
|
||||
std::string loadedZipPath;
|
||||
std::vector<ObstacleMesh> obstacles;
|
||||
std::vector<ObstaclePolygon> obstaclePolygons;
|
||||
std::vector<unsigned char> walkable;
|
||||
std::vector<NavigationArea> areas;
|
||||
|
||||
@ -62,6 +78,7 @@ private:
|
||||
void rebuildWalkableGrid();
|
||||
void markAvailableAreasWalkable();
|
||||
void markObstacleMeshesBlocked();
|
||||
void markObstaclePolygonsBlocked();
|
||||
|
||||
bool worldToCell(const Eigen::Vector3f& point, Cell& out) const;
|
||||
Eigen::Vector3f cellCenter(const Cell& cell) const;
|
||||
@ -74,6 +91,11 @@ private:
|
||||
std::vector<Cell> smoothCells(const std::vector<Cell>& cells) const;
|
||||
|
||||
static bool pointInPolygon(float x, float z, const std::vector<Eigen::Vector2f>& polygon);
|
||||
|
||||
bool isCellWalkable(const Cell& cell, const std::vector<unsigned char>& walkableGrid) const;
|
||||
bool findNearestWalkableCell(const Eigen::Vector3f& point, Cell& out, const std::vector<unsigned char>& walkableGrid) const;
|
||||
bool hasLineOfSight(const Cell& from, const Cell& to, const std::vector<unsigned char>& walkableGrid) const;
|
||||
std::vector<Cell> smoothCells(const std::vector<Cell>& cells, const std::vector<unsigned char>& walkableGrid) const;
|
||||
};
|
||||
|
||||
} // namespace ZL
|
||||
|
||||
@ -9,10 +9,9 @@ namespace ZL {
|
||||
GLuint fbo = 0;
|
||||
GLuint textureID = 0;
|
||||
int width, height;
|
||||
bool useMipmaps; // <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD>-<2D><><EFBFBD><EFBFBD><EFBFBD>
|
||||
bool useMipmaps;
|
||||
|
||||
public:
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> false
|
||||
FrameBuffer(int w, int h, bool useMipmaps = false);
|
||||
~FrameBuffer();
|
||||
|
||||
@ -22,7 +21,6 @@ namespace ZL {
|
||||
void Bind();
|
||||
void Unbind();
|
||||
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD>-<2D><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
void GenerateMipmaps();
|
||||
|
||||
GLuint getTextureID() const { return textureID; }
|
||||
|
||||
@ -14,8 +14,6 @@ namespace ZL {
|
||||
GLuint shaderProgram;
|
||||
|
||||
std::unordered_map<std::string, GLuint> uniformList;
|
||||
|
||||
//std::unordered_map<std::string, std::pair<bool, size_t>> UniformList;
|
||||
std::map<std::string, GLuint> attribList;
|
||||
|
||||
|
||||
|
||||
@ -298,34 +298,6 @@ bool TextRenderer::loadGlyphs(const std::string& ttfPath, int pixelSize, const s
|
||||
rowHeight = max(rowHeight, gb.height);
|
||||
}
|
||||
|
||||
// // Проходим по стандартным ASCII символам
|
||||
// for (unsigned char c = 32; c < 128; ++c) {
|
||||
//
|
||||
// FT_Load_Char(face, c, FT_LOAD_RENDER);
|
||||
|
||||
// TextureDataStruct glyphData;
|
||||
// glyphData.width = face->glyph->bitmap.width;
|
||||
// glyphData.height = face->glyph->bitmap.rows;
|
||||
// glyphData.format = TextureDataStruct::R8;
|
||||
// glyphData.mipmap = TextureDataStruct::NONE;
|
||||
|
||||
// // Копируем буфер FreeType в вектор данных
|
||||
// size_t dataSize = glyphData.width * glyphData.height;
|
||||
// glyphData.data.assign(face->glyph->bitmap.buffer, face->glyph->bitmap.buffer + dataSize);
|
||||
|
||||
// // Теперь создание текстуры — это одна строка!
|
||||
// auto tex = std::make_shared<Texture>(glyphData);
|
||||
|
||||
//GlyphInfo g;
|
||||
// g.texture = tex;
|
||||
// g.size = Eigen::Vector2f((float)face->glyph->bitmap.width, (float)face->glyph->bitmap.rows);
|
||||
// g.bearing = Eigen::Vector2f((float)face->glyph->bitmap_left, (float)face->glyph->bitmap_top);
|
||||
// // Advance во FreeType измеряется в 1/64 пикселя
|
||||
// g.advance = (unsigned int)face->glyph->advance.x;
|
||||
|
||||
// glyphs.emplace((char)c, g);
|
||||
// }
|
||||
|
||||
// Создаём Texture из atlasData (R8)
|
||||
TextureDataStruct atlasTex;
|
||||
atlasTex.width = atlasWidth;
|
||||
@ -466,12 +438,9 @@ void TextRenderer::drawText(const std::string& text, float x, float y, float sca
|
||||
// Примечание: для текста лучше использовать GL_DYNAMIC_DRAW,
|
||||
// но RefreshVBO сейчас жестко зашит на GL_STATIC_DRAW.
|
||||
// Для UI это обычно не критично, если строк не тысячи.
|
||||
// textMesh.AssignFrom(textData);
|
||||
|
||||
// 4. Рендеринг
|
||||
r->shaderManager.PushShader(shaderName);
|
||||
//r->PushMatrix();
|
||||
//r->LoadIdentity();
|
||||
|
||||
// Матрица проекции — используем виртуальные проекционные размеры,
|
||||
// чтобы координаты текста были независимы от физического разрешения экрана.
|
||||
@ -498,24 +467,7 @@ void TextRenderer::drawText(const std::string& text, float x, float y, float sca
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
//for (size_t i = 0; i < text.length(); ++i) {
|
||||
// auto it = glyphs.find(text[i]);
|
||||
// if (it == glyphs.end()) continue;
|
||||
|
||||
// glBindTexture(GL_TEXTURE_2D, it->second.texture->getTexID());
|
||||
|
||||
// // Отрисовываем по 6 вершин за раз
|
||||
// // Нам нужно вручную биндить VBO, так как DrawVertexRenderStruct рисует всё сразу
|
||||
// glBindBuffer(GL_ARRAY_BUFFER, textMesh.positionVBO->getBuffer());
|
||||
// r->VertexAttribPointer3fv("vPosition", 0, (const char*)(i * 6 * sizeof(Vector3f)));
|
||||
|
||||
// glBindBuffer(GL_ARRAY_BUFFER, textMesh.texCoordVBO->getBuffer());
|
||||
// r->VertexAttribPointer2fv("vTexCoord", 0, (const char*)(i * 6 * sizeof(Vector2f)));
|
||||
|
||||
// glDrawArrays(GL_TRIANGLES, 0, 6);
|
||||
//}
|
||||
r->DrawVertexRenderStruct(cached.mesh);
|
||||
//r->PopMatrix();
|
||||
|
||||
glDisable(GL_BLEND);
|
||||
|
||||
|
||||
@ -12,7 +12,6 @@
|
||||
namespace ZL {
|
||||
|
||||
struct GlyphInfo {
|
||||
// std::shared_ptr<Texture> texture; // Texture for glyph
|
||||
Eigen::Vector2f uv; // u,v координата левого верхнего угла в атласе (0..1)
|
||||
Eigen::Vector2f uvSize; // ширина/высота в UV (0..1)
|
||||
|
||||
@ -42,10 +41,6 @@ private:
|
||||
|
||||
std::unordered_map<uint32_t, GlyphInfo> glyphs;
|
||||
|
||||
// OpenGL objects for a dynamic quad
|
||||
//unsigned int vao = 0;
|
||||
//unsigned int vbo = 0;
|
||||
|
||||
// единый атлас для всех глифов
|
||||
std::shared_ptr<Texture> atlasTexture;
|
||||
size_t atlasWidth = 0;
|
||||
|
||||
@ -1,73 +0,0 @@
|
||||
#include "Perlin.h"
|
||||
#include <cmath>
|
||||
#include <numeric>
|
||||
#include <random>
|
||||
#include <algorithm>
|
||||
|
||||
namespace ZL {
|
||||
|
||||
PerlinNoise::PerlinNoise() {
|
||||
p.resize(256);
|
||||
std::iota(p.begin(), p.end(), 0);
|
||||
// Перемешиваем для случайности (можно задать seed)
|
||||
std::default_random_engine engine(77777);
|
||||
std::shuffle(p.begin(), p.end(), engine);
|
||||
p.insert(p.end(), p.begin(), p.end()); // Дублируем для переполнения
|
||||
}
|
||||
|
||||
PerlinNoise::PerlinNoise(uint64_t seed) {
|
||||
p.resize(256);
|
||||
std::iota(p.begin(), p.end(), 0);
|
||||
// Перемешиваем для случайности (используем переданный seed)
|
||||
std::default_random_engine engine(static_cast<unsigned int>(seed));
|
||||
std::shuffle(p.begin(), p.end(), engine);
|
||||
p.insert(p.end(), p.begin(), p.end()); // Дублируем для переполнения
|
||||
}
|
||||
|
||||
float PerlinNoise::fade(float t) { return t * t * t * (t * (t * 6 - 15) + 10); }
|
||||
|
||||
float PerlinNoise::lerp(float t, float a, float b) { return a + t * (b - a); }
|
||||
|
||||
float PerlinNoise::grad(int hash, float x, float y, float z) {
|
||||
int h = hash & 15;
|
||||
float u = h < 8 ? x : y;
|
||||
float v = h < 4 ? y : (h == 12 || h == 14 ? x : z);
|
||||
return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v);
|
||||
}
|
||||
|
||||
float PerlinNoise::noise(float x, float y, float z) {
|
||||
int X = (int)floor(x) & 255;
|
||||
int Y = (int)floor(y) & 255;
|
||||
int Z = (int)floor(z) & 255;
|
||||
|
||||
x -= floor(x);
|
||||
y -= floor(y);
|
||||
z -= floor(z);
|
||||
|
||||
float u = fade(x);
|
||||
float v = fade(y);
|
||||
float w = fade(z);
|
||||
|
||||
int A = p[X] + Y, AA = p[A] + Z, AB = p[A + 1] + Z;
|
||||
int B = p[X + 1] + Y, BA = p[B] + Z, BB = p[B + 1] + Z;
|
||||
|
||||
return lerp(w, lerp(v, lerp(u, grad(p[AA], x, y, z), grad(p[BA], x - 1, y, z)),
|
||||
lerp(u, grad(p[AB], x, y - 1, z), grad(p[BB], x - 1, y - 1, z))),
|
||||
lerp(v, lerp(u, grad(p[AA + 1], x, y, z - 1), grad(p[BA + 1], x - 1, y, z - 1)),
|
||||
lerp(u, grad(p[AB + 1], x, y - 1, z - 1), grad(p[BB + 1], x - 1, y - 1, z - 1))));
|
||||
}
|
||||
|
||||
float PerlinNoise::getSurfaceHeight(Eigen::Vector3f pos, float noiseCoeff) {
|
||||
// Частота шума (чем больше, тем больше "холмов")
|
||||
float frequency = 7.0f;
|
||||
|
||||
// Получаем значение шума (обычно от -1 до 1)
|
||||
float noiseValue = noise(pos(0) * frequency, pos(1) * frequency, pos(2) * frequency);
|
||||
|
||||
// Масштабируем: хотим отклонение от 1.0 до 1.1 (примерно)
|
||||
float height = 1.0f + (noiseValue * noiseCoeff);
|
||||
|
||||
return height;
|
||||
}
|
||||
|
||||
} // namespace ZL
|
||||
@ -1,24 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
#include <Eigen/Dense>
|
||||
|
||||
namespace ZL {
|
||||
|
||||
class PerlinNoise {
|
||||
std::vector<int> p;
|
||||
public:
|
||||
PerlinNoise();
|
||||
PerlinNoise(uint64_t seed);
|
||||
|
||||
float fade(float t);
|
||||
float lerp(float t, float a, float b);
|
||||
float grad(int hash, float x, float y, float z);
|
||||
|
||||
float noise(float x, float y, float z);
|
||||
|
||||
float getSurfaceHeight(Eigen::Vector3f pos, float noiseCoeff);
|
||||
};
|
||||
|
||||
} // namespace ZL
|
||||
Loading…
Reference in New Issue
Block a user