Skip to content

Instantly share code, notes, and snippets.

Created December 25, 2012 16:28
Show Gist options
  • Save anonymous/4374074 to your computer and use it in GitHub Desktop.
Save anonymous/4374074 to your computer and use it in GitHub Desktop.
TC - mmaps beta patch
This file has been truncated, but you can view the full file.
From 44c99034ec1cdfc85a3c79375a18ba30336f6dc0 Mon Sep 17 00:00:00 2001
From: various authors <various@trinitycore.contrib>
Date: Sun, 23 Dec 2012 16:15:40 +0000
Subject: [PATCH] Core: movement maps beta preview release
---
cmake/options.cmake | 2 +-
dep/CMakeLists.txt | 7 +-
dep/PackageList.txt | 4 +
dep/recastnavigation/CMakeLists.txt | 20 +
.../recastnavigation/Detour}/CMakeLists.txt | 23 +-
dep/recastnavigation/Detour/DetourAlloc.cpp | 50 +
dep/recastnavigation/Detour/DetourAlloc.h | 36 +
dep/recastnavigation/Detour/DetourAssert.h | 33 +
dep/recastnavigation/Detour/DetourCommon.cpp | 329 +++
dep/recastnavigation/Detour/DetourCommon.h | 248 ++
dep/recastnavigation/Detour/DetourNavMesh.cpp | 1235 ++++++++++
dep/recastnavigation/Detour/DetourNavMesh.h | 428 ++++
.../Detour/DetourNavMeshBuilder.cpp | 717 ++++++
dep/recastnavigation/Detour/DetourNavMeshBuilder.h | 77 +
dep/recastnavigation/Detour/DetourNavMeshQuery.cpp | 2564 ++++++++++++++++++++
dep/recastnavigation/Detour/DetourNavMeshQuery.h | 407 ++++
dep/recastnavigation/Detour/DetourNode.cpp | 164 ++
dep/recastnavigation/Detour/DetourNode.h | 157 ++
.../Detour/DetourObstacleAvoidance.cpp | 532 ++++
.../Detour/DetourObstacleAvoidance.h | 148 ++
dep/recastnavigation/License.txt | 18 +
dep/recastnavigation/Readme.txt | 120 +
dep/recastnavigation/Recast/CMakeLists.txt | 31 +
dep/recastnavigation/Recast/Recast.cpp | 423 ++++
dep/recastnavigation/Recast/Recast.h | 688 ++++++
dep/recastnavigation/Recast/RecastAlloc.cpp | 67 +
dep/recastnavigation/Recast/RecastAlloc.h | 69 +
dep/recastnavigation/Recast/RecastArea.cpp | 413 ++++
dep/recastnavigation/Recast/RecastAssert.h | 33 +
dep/recastnavigation/Recast/RecastContour.cpp | 804 ++++++
dep/recastnavigation/Recast/RecastFilter.cpp | 179 ++
dep/recastnavigation/Recast/RecastMesh.cpp | 1322 ++++++++++
dep/recastnavigation/Recast/RecastMeshDetail.cpp | 1237 ++++++++++
.../Recast/RecastRasterization.cpp | 360 +++
dep/recastnavigation/Recast/RecastRegion.cpp | 1283 ++++++++++
dep/recastnavigation/TODO.txt | 20 +
sql/updates/world/2012_09_06_00_world_command.sql | 9 +
src/server/CMakeLists.txt | 1 +
src/server/collision/CMakeLists.txt | 2 +
src/server/collision/Management/MMapFactory.cpp | 80 +
src/server/collision/Management/MMapFactory.h | 51 +
src/server/collision/Management/MMapManager.cpp | 298 +++
src/server/collision/Management/MMapManager.h | 84 +
src/server/collision/Management/VMapManager2.h | 2 +
src/server/collision/Maps/MapTree.h | 1 +
src/server/collision/Models/ModelInstance.h | 2 +
src/server/collision/Models/WorldModel.h | 6 +
src/server/game/CMakeLists.txt | 2 +
src/server/game/Entities/Object/Object.cpp | 2 +-
src/server/game/Entities/Unit/Unit.cpp | 14 +-
src/server/game/Entities/Unit/Unit.h | 3 +-
src/server/game/Entities/Vehicle/Vehicle.cpp | 2 +-
src/server/game/Handlers/MovementHandler.cpp | 2 +-
src/server/game/Handlers/PetHandler.cpp | 7 -
src/server/game/Maps/Map.cpp | 35 +-
src/server/game/Maps/Map.h | 3 +
src/server/game/Maps/MapInstanced.cpp | 2 +
src/server/game/Miscellaneous/SharedDefines.h | 30 +
src/server/game/Movement/MotionMaster.cpp | 74 +-
src/server/game/Movement/MotionMaster.h | 6 +-
src/server/game/Movement/MovementGenerator.h | 36 +-
.../ConfusedMovementGenerator.cpp | 303 +--
.../MovementGenerators/ConfusedMovementGenerator.h | 14 +-
.../FleeingMovementGenerator.cpp | 622 ++---
.../MovementGenerators/FleeingMovementGenerator.h | 28 +-
.../MovementGenerators/HomeMovementGenerator.cpp | 48 +-
.../MovementGenerators/HomeMovementGenerator.h | 10 +-
.../MovementGenerators/IdleMovementGenerator.cpp | 54 +-
.../MovementGenerators/IdleMovementGenerator.h | 26 +-
.../MovementGenerators/PointMovementGenerator.cpp | 106 +-
.../MovementGenerators/PointMovementGenerator.h | 29 +-
.../MovementGenerators/RandomMovementGenerator.cpp | 54 +-
.../MovementGenerators/RandomMovementGenerator.h | 12 +-
.../TargetedMovementGenerator.cpp | 672 +++--
.../MovementGenerators/TargetedMovementGenerator.h | 61 +-
.../WaypointMovementGenerator.cpp | 972 +++-----
.../MovementGenerators/WaypointMovementGenerator.h | 32 +-
src/server/game/Movement/PathGenerator.cpp | 803 ++++++
src/server/game/Movement/PathGenerator.h | 135 ++
src/server/game/Movement/Spline/MoveSpline.h | 3 +-
src/server/game/Movement/Spline/MoveSplineInit.cpp | 69 +-
src/server/game/Movement/Spline/MoveSplineInit.h | 23 +-
src/server/game/Movement/Waypoints/Path.h | 24 +-
src/server/game/Scripting/ScriptLoader.cpp | 2 +
src/server/game/Spells/Spell.cpp | 27 +-
src/server/game/Spells/Spell.h | 2 +
src/server/game/Spells/SpellEffects.cpp | 26 +-
src/server/game/World/World.cpp | 14 +-
src/server/game/World/World.h | 2 +-
src/server/scripts/CMakeLists.txt | 2 +
src/server/scripts/Commands/CMakeLists.txt | 1 +
src/server/scripts/Commands/cs_mmaps.cpp | 294 +++
.../Ulduar/Ulduar/boss_algalon_the_observer.cpp | 2 +-
src/server/shared/CMakeLists.txt | 1 +
src/server/shared/Memory.h | 17 +
src/server/worldserver/CMakeLists.txt | 2 +
src/server/worldserver/worldserver.conf.dist | 23 +-
src/tools/CMakeLists.txt | 2 +
src/tools/map_extractor/System.cpp | 42 +-
src/tools/mesh_extractor/ADT.cpp | 53 +
src/tools/mesh_extractor/ADT.h | 29 +
src/tools/mesh_extractor/CMakeLists.txt | 80 +
src/tools/mesh_extractor/Cache.h | 62 +
src/tools/mesh_extractor/Chunk.cpp | 31 +
src/tools/mesh_extractor/Chunk.h | 20 +
src/tools/mesh_extractor/ChunkedData.cpp | 73 +
src/tools/mesh_extractor/ChunkedData.h | 21 +
src/tools/mesh_extractor/Constants.h | 59 +
src/tools/mesh_extractor/ContinentBuilder.cpp | 144 ++
src/tools/mesh_extractor/ContinentBuilder.h | 26 +
src/tools/mesh_extractor/DBC.cpp | 64 +
src/tools/mesh_extractor/DBC.h | 52 +
src/tools/mesh_extractor/DoodadHandler.cpp | 107 +
src/tools/mesh_extractor/DoodadHandler.h | 53 +
src/tools/mesh_extractor/Geometry.cpp | 123 +
src/tools/mesh_extractor/Geometry.h | 23 +
src/tools/mesh_extractor/LiquidHandler.cpp | 102 +
src/tools/mesh_extractor/LiquidHandler.h | 21 +
src/tools/mesh_extractor/MPQ.cpp | 118 +
src/tools/mesh_extractor/MPQ.h | 90 +
src/tools/mesh_extractor/MPQManager.cpp | 109 +
src/tools/mesh_extractor/MPQManager.h | 34 +
src/tools/mesh_extractor/MapChunk.cpp | 73 +
src/tools/mesh_extractor/MapChunk.h | 24 +
src/tools/mesh_extractor/MeshExtractor.cpp | 418 ++++
src/tools/mesh_extractor/Model.cpp | 64 +
src/tools/mesh_extractor/Model.h | 23 +
src/tools/mesh_extractor/ObjectDataHandler.cpp | 21 +
src/tools/mesh_extractor/ObjectDataHandler.h | 15 +
src/tools/mesh_extractor/TileBuilder.cpp | 310 +++
src/tools/mesh_extractor/TileBuilder.h | 30 +
src/tools/mesh_extractor/Utils.cpp | 508 ++++
src/tools/mesh_extractor/Utils.h | 377 +++
src/tools/mesh_extractor/WDT.cpp | 55 +
src/tools/mesh_extractor/WDT.h | 27 +
src/tools/mesh_extractor/WorldModelGroup.cpp | 135 ++
src/tools/mesh_extractor/WorldModelGroup.h | 38 +
src/tools/mesh_extractor/WorldModelHandler.cpp | 198 ++
src/tools/mesh_extractor/WorldModelHandler.h | 47 +
src/tools/mesh_extractor/WorldModelRoot.cpp | 74 +
src/tools/mesh_extractor/WorldModelRoot.h | 26 +
src/tools/mesh_extractor/readme | 6 +
src/tools/mmaps_generator/CMakeLists.txt | 68 +
src/tools/mmaps_generator/Info/readme.txt | 66 +
src/tools/mmaps_generator/IntermediateValues.cpp | 277 +++
src/tools/mmaps_generator/IntermediateValues.h | 53 +
src/tools/mmaps_generator/MapBuilder.cpp | 970 ++++++++
src/tools/mmaps_generator/MapBuilder.h | 154 ++
src/tools/mmaps_generator/PathCommon.h | 128 +
src/tools/mmaps_generator/PathGenerator.cpp | 297 +++
src/tools/mmaps_generator/TerrainBuilder.cpp | 856 +++++++
src/tools/mmaps_generator/TerrainBuilder.h | 138 ++
src/tools/mmaps_generator/VMapExtensions.cpp | 72 +
153 files changed, 24610 insertions(+), 1993 deletions(-)
create mode 100644 dep/recastnavigation/CMakeLists.txt
copy {src/tools => dep/recastnavigation/Detour}/CMakeLists.txt (50%)
create mode 100644 dep/recastnavigation/Detour/DetourAlloc.cpp
create mode 100644 dep/recastnavigation/Detour/DetourAlloc.h
create mode 100644 dep/recastnavigation/Detour/DetourAssert.h
create mode 100644 dep/recastnavigation/Detour/DetourCommon.cpp
create mode 100644 dep/recastnavigation/Detour/DetourCommon.h
create mode 100644 dep/recastnavigation/Detour/DetourNavMesh.cpp
create mode 100644 dep/recastnavigation/Detour/DetourNavMesh.h
create mode 100644 dep/recastnavigation/Detour/DetourNavMeshBuilder.cpp
create mode 100644 dep/recastnavigation/Detour/DetourNavMeshBuilder.h
create mode 100644 dep/recastnavigation/Detour/DetourNavMeshQuery.cpp
create mode 100644 dep/recastnavigation/Detour/DetourNavMeshQuery.h
create mode 100644 dep/recastnavigation/Detour/DetourNode.cpp
create mode 100644 dep/recastnavigation/Detour/DetourNode.h
create mode 100644 dep/recastnavigation/Detour/DetourObstacleAvoidance.cpp
create mode 100644 dep/recastnavigation/Detour/DetourObstacleAvoidance.h
create mode 100644 dep/recastnavigation/License.txt
create mode 100644 dep/recastnavigation/Readme.txt
create mode 100644 dep/recastnavigation/Recast/CMakeLists.txt
create mode 100644 dep/recastnavigation/Recast/Recast.cpp
create mode 100644 dep/recastnavigation/Recast/Recast.h
create mode 100644 dep/recastnavigation/Recast/RecastAlloc.cpp
create mode 100644 dep/recastnavigation/Recast/RecastAlloc.h
create mode 100644 dep/recastnavigation/Recast/RecastArea.cpp
create mode 100644 dep/recastnavigation/Recast/RecastAssert.h
create mode 100644 dep/recastnavigation/Recast/RecastContour.cpp
create mode 100644 dep/recastnavigation/Recast/RecastFilter.cpp
create mode 100644 dep/recastnavigation/Recast/RecastMesh.cpp
create mode 100644 dep/recastnavigation/Recast/RecastMeshDetail.cpp
create mode 100644 dep/recastnavigation/Recast/RecastRasterization.cpp
create mode 100644 dep/recastnavigation/Recast/RecastRegion.cpp
create mode 100644 dep/recastnavigation/TODO.txt
create mode 100644 sql/updates/world/2012_09_06_00_world_command.sql
create mode 100644 src/server/collision/Management/MMapFactory.cpp
create mode 100644 src/server/collision/Management/MMapFactory.h
create mode 100644 src/server/collision/Management/MMapManager.cpp
create mode 100644 src/server/collision/Management/MMapManager.h
rewrite src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.cpp (70%)
rewrite src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.cpp (83%)
rewrite src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp (63%)
rewrite src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp (69%)
create mode 100644 src/server/game/Movement/PathGenerator.cpp
create mode 100644 src/server/game/Movement/PathGenerator.h
create mode 100644 src/server/scripts/Commands/cs_mmaps.cpp
create mode 100644 src/server/shared/Memory.h
create mode 100644 src/tools/mesh_extractor/ADT.cpp
create mode 100644 src/tools/mesh_extractor/ADT.h
create mode 100644 src/tools/mesh_extractor/CMakeLists.txt
create mode 100644 src/tools/mesh_extractor/Cache.h
create mode 100644 src/tools/mesh_extractor/Chunk.cpp
create mode 100644 src/tools/mesh_extractor/Chunk.h
create mode 100644 src/tools/mesh_extractor/ChunkedData.cpp
create mode 100644 src/tools/mesh_extractor/ChunkedData.h
create mode 100644 src/tools/mesh_extractor/Constants.h
create mode 100644 src/tools/mesh_extractor/ContinentBuilder.cpp
create mode 100644 src/tools/mesh_extractor/ContinentBuilder.h
create mode 100644 src/tools/mesh_extractor/DBC.cpp
create mode 100644 src/tools/mesh_extractor/DBC.h
create mode 100644 src/tools/mesh_extractor/DoodadHandler.cpp
create mode 100644 src/tools/mesh_extractor/DoodadHandler.h
create mode 100644 src/tools/mesh_extractor/Geometry.cpp
create mode 100644 src/tools/mesh_extractor/Geometry.h
create mode 100644 src/tools/mesh_extractor/LiquidHandler.cpp
create mode 100644 src/tools/mesh_extractor/LiquidHandler.h
create mode 100644 src/tools/mesh_extractor/MPQ.cpp
create mode 100644 src/tools/mesh_extractor/MPQ.h
create mode 100644 src/tools/mesh_extractor/MPQManager.cpp
create mode 100644 src/tools/mesh_extractor/MPQManager.h
create mode 100644 src/tools/mesh_extractor/MapChunk.cpp
create mode 100644 src/tools/mesh_extractor/MapChunk.h
create mode 100644 src/tools/mesh_extractor/MeshExtractor.cpp
create mode 100644 src/tools/mesh_extractor/Model.cpp
create mode 100644 src/tools/mesh_extractor/Model.h
create mode 100644 src/tools/mesh_extractor/ObjectDataHandler.cpp
create mode 100644 src/tools/mesh_extractor/ObjectDataHandler.h
create mode 100644 src/tools/mesh_extractor/TileBuilder.cpp
create mode 100644 src/tools/mesh_extractor/TileBuilder.h
create mode 100644 src/tools/mesh_extractor/Utils.cpp
create mode 100644 src/tools/mesh_extractor/Utils.h
create mode 100644 src/tools/mesh_extractor/WDT.cpp
create mode 100644 src/tools/mesh_extractor/WDT.h
create mode 100644 src/tools/mesh_extractor/WorldModelGroup.cpp
create mode 100644 src/tools/mesh_extractor/WorldModelGroup.h
create mode 100644 src/tools/mesh_extractor/WorldModelHandler.cpp
create mode 100644 src/tools/mesh_extractor/WorldModelHandler.h
create mode 100644 src/tools/mesh_extractor/WorldModelRoot.cpp
create mode 100644 src/tools/mesh_extractor/WorldModelRoot.h
create mode 100644 src/tools/mesh_extractor/readme
create mode 100644 src/tools/mmaps_generator/CMakeLists.txt
create mode 100644 src/tools/mmaps_generator/Info/readme.txt
create mode 100644 src/tools/mmaps_generator/IntermediateValues.cpp
create mode 100644 src/tools/mmaps_generator/IntermediateValues.h
create mode 100644 src/tools/mmaps_generator/MapBuilder.cpp
create mode 100644 src/tools/mmaps_generator/MapBuilder.h
create mode 100644 src/tools/mmaps_generator/PathCommon.h
create mode 100644 src/tools/mmaps_generator/PathGenerator.cpp
create mode 100644 src/tools/mmaps_generator/TerrainBuilder.cpp
create mode 100644 src/tools/mmaps_generator/TerrainBuilder.h
create mode 100644 src/tools/mmaps_generator/VMapExtensions.cpp
diff --git a/cmake/options.cmake b/cmake/options.cmake
index 11ebe6d..5802ab8 100644
--- a/cmake/options.cmake
+++ b/cmake/options.cmake
@@ -10,7 +10,7 @@
option(SERVERS "Build worldserver and authserver" 1)
option(SCRIPTS "Build core with scripts included" 1)
-option(TOOLS "Build map/vmap extraction/assembler tools" 0)
+option(TOOLS "Build map/vmap/mmap extraction/assembler tools" 0)
option(USE_SCRIPTPCH "Use precompiled headers when compiling scripts" 1)
option(USE_COREPCH "Use precompiled headers when compiling servers" 1)
option(WITH_WARNINGS "Show all warnings during compile" 0)
diff --git a/dep/CMakeLists.txt b/dep/CMakeLists.txt
index d658ba4..f6db7ca 100644
--- a/dep/CMakeLists.txt
+++ b/dep/CMakeLists.txt
@@ -21,11 +21,9 @@ if(CMAKE_SYSTEM_NAME MATCHES "Linux")
endif()
if(CMAKE_SYSTEM_NAME MATCHES "Windows")
- if(SERVERS)
- add_subdirectory(acelite)
- if(USE_MYSQL_SOURCES)
+ add_subdirectory(acelite)
+ if(USE_MYSQL_SOURCES)
add_subdirectory(mysqllite)
- endif()
endif()
if(TOOLS)
add_subdirectory(bzip2)
@@ -34,6 +32,7 @@ if(CMAKE_SYSTEM_NAME MATCHES "Windows")
endif()
add_subdirectory(g3dlite)
+add_subdirectory(recastnavigation)
if(SERVERS)
add_subdirectory(gsoap)
diff --git a/dep/PackageList.txt b/dep/PackageList.txt
index de08e0a..95503ce 100644
--- a/dep/PackageList.txt
+++ b/dep/PackageList.txt
@@ -39,3 +39,7 @@ zlib (A Massively Spiffy Yet Delicately Unobtrusive Compression Library)
gSOAP (a portable development toolkit for C and C++ XML Web services and XML data bindings)
http://gsoap2.sourceforge.net/
Version: 2.8.10
+
+recastnavigation (Recast is state of the art navigation mesh construction toolset for games)
+ http://code.google.com/p/recastnavigation/
+ Version: 1.4
diff --git a/dep/recastnavigation/CMakeLists.txt b/dep/recastnavigation/CMakeLists.txt
new file mode 100644
index 0000000..4a406f7
--- /dev/null
+++ b/dep/recastnavigation/CMakeLists.txt
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2005-2011 MaNGOS project <http://getmangos.com/>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+add_subdirectory(Detour)
+add_subdirectory(Recast)
diff --git a/src/tools/CMakeLists.txt b/dep/recastnavigation/Detour/CMakeLists.txt
similarity index 50%
copy from src/tools/CMakeLists.txt
copy to dep/recastnavigation/Detour/CMakeLists.txt
index a956655..c09e0e3 100644
--- a/src/tools/CMakeLists.txt
+++ b/dep/recastnavigation/Detour/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/>
+# Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
#
# This file is free software; as a special exception the author gives
# unlimited permission to copy and/or distribute it, with or without
@@ -8,6 +8,21 @@
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-add_subdirectory(map_extractor)
-add_subdirectory(vmap4_assembler)
-add_subdirectory(vmap4_extractor)
+set(Detour_STAT_SRCS
+ DetourAlloc.cpp
+ DetourCommon.cpp
+ DetourNavMesh.cpp
+ DetourNavMeshBuilder.cpp
+ DetourNavMeshQuery.cpp
+ DetourNode.cpp
+)
+
+if(WIN32)
+ include_directories(
+ ${CMAKE_SOURCE_DIR}/dep/zlib
+ )
+endif()
+
+add_library(Detour STATIC ${Detour_STAT_SRCS})
+
+target_link_libraries(Detour ${ZLIB_LIBRARIES})
diff --git a/dep/recastnavigation/Detour/DetourAlloc.cpp b/dep/recastnavigation/Detour/DetourAlloc.cpp
new file mode 100644
index 0000000..5f671df
--- /dev/null
+++ b/dep/recastnavigation/Detour/DetourAlloc.cpp
@@ -0,0 +1,50 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#include <stdlib.h>
+#include "DetourAlloc.h"
+
+static void *dtAllocDefault(int size, dtAllocHint)
+{
+ return malloc(size);
+}
+
+static void dtFreeDefault(void *ptr)
+{
+ free(ptr);
+}
+
+static dtAllocFunc* sAllocFunc = dtAllocDefault;
+static dtFreeFunc* sFreeFunc = dtFreeDefault;
+
+void dtAllocSetCustom(dtAllocFunc *allocFunc, dtFreeFunc *freeFunc)
+{
+ sAllocFunc = allocFunc ? allocFunc : dtAllocDefault;
+ sFreeFunc = freeFunc ? freeFunc : dtFreeDefault;
+}
+
+void* dtAlloc(int size, dtAllocHint hint)
+{
+ return sAllocFunc(size, hint);
+}
+
+void dtFree(void* ptr)
+{
+ if (ptr)
+ sFreeFunc(ptr);
+}
diff --git a/dep/recastnavigation/Detour/DetourAlloc.h b/dep/recastnavigation/Detour/DetourAlloc.h
new file mode 100644
index 0000000..8693475
--- /dev/null
+++ b/dep/recastnavigation/Detour/DetourAlloc.h
@@ -0,0 +1,36 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#ifndef DETOURALLOCATOR_H
+#define DETOURALLOCATOR_H
+
+enum dtAllocHint
+{
+ DT_ALLOC_PERM, // Memory persist after a function call.
+ DT_ALLOC_TEMP // Memory used temporarily within a function.
+};
+
+typedef void* (dtAllocFunc)(int size, dtAllocHint hint);
+typedef void (dtFreeFunc)(void* ptr);
+
+void dtAllocSetCustom(dtAllocFunc *allocFunc, dtFreeFunc *freeFunc);
+
+void* dtAlloc(int size, dtAllocHint hint);
+void dtFree(void* ptr);
+
+#endif
diff --git a/dep/recastnavigation/Detour/DetourAssert.h b/dep/recastnavigation/Detour/DetourAssert.h
new file mode 100644
index 0000000..709ebd9
--- /dev/null
+++ b/dep/recastnavigation/Detour/DetourAssert.h
@@ -0,0 +1,33 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#ifndef DETOURASSERT_H
+#define DETOURASSERT_H
+
+// Note: This header file's only purpose is to include define assert.
+// Feel free to change the file and include your own implementation instead.
+
+#ifdef NDEBUG
+// From http://cnicholson.net/2009/02/stupid-c-tricks-adventures-in-assert/
+# define dtAssert(x) do { (void)sizeof(x); } while(__LINE__==-1,false)
+#else
+# include <assert.h>
+# define dtAssert assert
+#endif
+
+#endif // DETOURASSERT_H
diff --git a/dep/recastnavigation/Detour/DetourCommon.cpp b/dep/recastnavigation/Detour/DetourCommon.cpp
new file mode 100644
index 0000000..c0b973e
--- /dev/null
+++ b/dep/recastnavigation/Detour/DetourCommon.cpp
@@ -0,0 +1,329 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#include <math.h>
+#include "DetourCommon.h"
+
+//////////////////////////////////////////////////////////////////////////////////////////
+
+float dtSqrt(float x)
+{
+ return sqrtf(x);
+}
+
+void dtClosestPtPointTriangle(float* closest, const float* p,
+ const float* a, const float* b, const float* c)
+{
+ // Check if P in vertex region outside A
+ float ab[3], ac[3], ap[3];
+ dtVsub(ab, b, a);
+ dtVsub(ac, c, a);
+ dtVsub(ap, p, a);
+ float d1 = dtVdot(ab, ap);
+ float d2 = dtVdot(ac, ap);
+ if (d1 <= 0.0f && d2 <= 0.0f)
+ {
+ // barycentric coordinates (1,0,0)
+ dtVcopy(closest, a);
+ return;
+ }
+
+ // Check if P in vertex region outside B
+ float bp[3];
+ dtVsub(bp, p, b);
+ float d3 = dtVdot(ab, bp);
+ float d4 = dtVdot(ac, bp);
+ if (d3 >= 0.0f && d4 <= d3)
+ {
+ // barycentric coordinates (0,1,0)
+ dtVcopy(closest, b);
+ return;
+ }
+
+ // Check if P in edge region of AB, if so return projection of P onto AB
+ float vc = d1*d4 - d3*d2;
+ if (vc <= 0.0f && d1 >= 0.0f && d3 <= 0.0f)
+ {
+ // barycentric coordinates (1-v,v,0)
+ float v = d1 / (d1 - d3);
+ closest[0] = a[0] + v * ab[0];
+ closest[1] = a[1] + v * ab[1];
+ closest[2] = a[2] + v * ab[2];
+ return;
+ }
+
+ // Check if P in vertex region outside C
+ float cp[3];
+ dtVsub(cp, p, c);
+ float d5 = dtVdot(ab, cp);
+ float d6 = dtVdot(ac, cp);
+ if (d6 >= 0.0f && d5 <= d6)
+ {
+ // barycentric coordinates (0,0,1)
+ dtVcopy(closest, c);
+ return;
+ }
+
+ // Check if P in edge region of AC, if so return projection of P onto AC
+ float vb = d5*d2 - d1*d6;
+ if (vb <= 0.0f && d2 >= 0.0f && d6 <= 0.0f)
+ {
+ // barycentric coordinates (1-w,0,w)
+ float w = d2 / (d2 - d6);
+ closest[0] = a[0] + w * ac[0];
+ closest[1] = a[1] + w * ac[1];
+ closest[2] = a[2] + w * ac[2];
+ return;
+ }
+
+ // Check if P in edge region of BC, if so return projection of P onto BC
+ float va = d3*d6 - d5*d4;
+ if (va <= 0.0f && (d4 - d3) >= 0.0f && (d5 - d6) >= 0.0f)
+ {
+ // barycentric coordinates (0,1-w,w)
+ float w = (d4 - d3) / ((d4 - d3) + (d5 - d6));
+ closest[0] = b[0] + w * (c[0] - b[0]);
+ closest[1] = b[1] + w * (c[1] - b[1]);
+ closest[2] = b[2] + w * (c[2] - b[2]);
+ return;
+ }
+
+ // P inside face region. Compute Q through its barycentric coordinates (u,v,w)
+ float denom = 1.0f / (va + vb + vc);
+ float v = vb * denom;
+ float w = vc * denom;
+ closest[0] = a[0] + ab[0] * v + ac[0] * w;
+ closest[1] = a[1] + ab[1] * v + ac[1] * w;
+ closest[2] = a[2] + ab[2] * v + ac[2] * w;
+}
+
+bool dtIntersectSegmentPoly2D(const float* p0, const float* p1,
+ const float* verts, int nverts,
+ float& tmin, float& tmax,
+ int& segMin, int& segMax)
+{
+ static const float EPS = 0.00000001f;
+
+ tmin = 0;
+ tmax = 1;
+ segMin = -1;
+ segMax = -1;
+
+ float dir[3];
+ dtVsub(dir, p1, p0);
+
+ for (int i = 0, j = nverts-1; i < nverts; j=i++)
+ {
+ float edge[3], diff[3];
+ dtVsub(edge, &verts[i*3], &verts[j*3]);
+ dtVsub(diff, p0, &verts[j*3]);
+ const float n = dtVperp2D(edge, diff);
+ const float d = dtVperp2D(dir, edge);
+ if (fabsf(d) < EPS)
+ {
+ // S is nearly parallel to this edge
+ if (n < 0)
+ return false;
+ else
+ continue;
+ }
+ const float t = n / d;
+ if (d < 0)
+ {
+ // segment S is entering across this edge
+ if (t > tmin)
+ {
+ tmin = t;
+ segMin = j;
+ // S enters after leaving polygon
+ if (tmin > tmax)
+ return false;
+ }
+ }
+ else
+ {
+ // segment S is leaving across this edge
+ if (t < tmax)
+ {
+ tmax = t;
+ segMax = j;
+ // S leaves before entering polygon
+ if (tmax < tmin)
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+float dtDistancePtSegSqr2D(const float* pt, const float* p, const float* q, float& t)
+{
+ float pqx = q[0] - p[0];
+ float pqz = q[2] - p[2];
+ float dx = pt[0] - p[0];
+ float dz = pt[2] - p[2];
+ float d = pqx*pqx + pqz*pqz;
+ t = pqx*dx + pqz*dz;
+ if (d > 0) t /= d;
+ if (t < 0) t = 0;
+ else if (t > 1) t = 1;
+ dx = p[0] + t*pqx - pt[0];
+ dz = p[2] + t*pqz - pt[2];
+ return dx*dx + dz*dz;
+}
+
+void dtCalcPolyCenter(float* tc, const unsigned short* idx, int nidx, const float* verts)
+{
+ tc[0] = 0.0f;
+ tc[1] = 0.0f;
+ tc[2] = 0.0f;
+ for (int j = 0; j < nidx; ++j)
+ {
+ const float* v = &verts[idx[j]*3];
+ tc[0] += v[0];
+ tc[1] += v[1];
+ tc[2] += v[2];
+ }
+ const float s = 1.0f / nidx;
+ tc[0] *= s;
+ tc[1] *= s;
+ tc[2] *= s;
+}
+
+bool dtClosestHeightPointTriangle(const float* p, const float* a, const float* b, const float* c, float& h)
+{
+ float v0[3], v1[3], v2[3];
+ dtVsub(v0, c,a);
+ dtVsub(v1, b,a);
+ dtVsub(v2, p,a);
+
+ const float dot00 = dtVdot2D(v0, v0);
+ const float dot01 = dtVdot2D(v0, v1);
+ const float dot02 = dtVdot2D(v0, v2);
+ const float dot11 = dtVdot2D(v1, v1);
+ const float dot12 = dtVdot2D(v1, v2);
+
+ // Compute barycentric coordinates
+ const float invDenom = 1.0f / (dot00 * dot11 - dot01 * dot01);
+ const float u = (dot11 * dot02 - dot01 * dot12) * invDenom;
+ const float v = (dot00 * dot12 - dot01 * dot02) * invDenom;
+
+ // The (sloppy) epsilon is needed to allow to get height of points which
+ // are interpolated along the edges of the triangles.
+ static const float EPS = 1e-4f;
+
+ // If point lies inside the triangle, return interpolated ycoord.
+ if (u >= -EPS && v >= -EPS && (u+v) <= 1+EPS)
+ {
+ h = a[1] + v0[1]*u + v1[1]*v;
+ return true;
+ }
+
+ return false;
+}
+
+bool dtPointInPolygon(const float* pt, const float* verts, const int nverts)
+{
+ // TODO: Replace pnpoly with triArea2D tests?
+ int i, j;
+ bool c = false;
+ for (i = 0, j = nverts-1; i < nverts; j = i++)
+ {
+ const float* vi = &verts[i*3];
+ const float* vj = &verts[j*3];
+ if (((vi[2] > pt[2]) != (vj[2] > pt[2])) &&
+ (pt[0] < (vj[0]-vi[0]) * (pt[2]-vi[2]) / (vj[2]-vi[2]) + vi[0]) )
+ c = !c;
+ }
+ return c;
+}
+
+bool dtDistancePtPolyEdgesSqr(const float* pt, const float* verts, const int nverts,
+ float* ed, float* et)
+{
+ // TODO: Replace pnpoly with triArea2D tests?
+ int i, j;
+ bool c = false;
+ for (i = 0, j = nverts-1; i < nverts; j = i++)
+ {
+ const float* vi = &verts[i*3];
+ const float* vj = &verts[j*3];
+ if (((vi[2] > pt[2]) != (vj[2] > pt[2])) &&
+ (pt[0] < (vj[0]-vi[0]) * (pt[2]-vi[2]) / (vj[2]-vi[2]) + vi[0]) )
+ c = !c;
+ ed[j] = dtDistancePtSegSqr2D(pt, vj, vi, et[j]);
+ }
+ return c;
+}
+
+static void projectPoly(const float* axis, const float* poly, const int npoly,
+ float& rmin, float& rmax)
+{
+ rmin = rmax = dtVdot2D(axis, &poly[0]);
+ for (int i = 1; i < npoly; ++i)
+ {
+ const float d = dtVdot2D(axis, &poly[i*3]);
+ rmin = dtMin(rmin, d);
+ rmax = dtMax(rmax, d);
+ }
+}
+
+inline bool overlapRange(const float amin, const float amax,
+ const float bmin, const float bmax,
+ const float eps)
+{
+ return ((amin+eps) > bmax || (amax-eps) < bmin) ? false : true;
+}
+
+bool dtOverlapPolyPoly2D(const float* polya, const int npolya,
+ const float* polyb, const int npolyb)
+{
+ const float eps = 1e-4f;
+
+ for (int i = 0, j = npolya-1; i < npolya; j=i++)
+ {
+ const float* va = &polya[j*3];
+ const float* vb = &polya[i*3];
+ const float n[3] = { vb[2]-va[2], 0, -(vb[0]-va[0]) };
+ float amin,amax,bmin,bmax;
+ projectPoly(n, polya, npolya, amin,amax);
+ projectPoly(n, polyb, npolyb, bmin,bmax);
+ if (!overlapRange(amin,amax, bmin,bmax, eps))
+ {
+ // Found separating axis
+ return false;
+ }
+ }
+ for (int i = 0, j = npolyb-1; i < npolyb; j=i++)
+ {
+ const float* va = &polyb[j*3];
+ const float* vb = &polyb[i*3];
+ const float n[3] = { vb[2]-va[2], 0, -(vb[0]-va[0]) };
+ float amin,amax,bmin,bmax;
+ projectPoly(n, polya, npolya, amin,amax);
+ projectPoly(n, polyb, npolyb, bmin,bmax);
+ if (!overlapRange(amin,amax, bmin,bmax, eps))
+ {
+ // Found separating axis
+ return false;
+ }
+ }
+ return true;
+}
+
diff --git a/dep/recastnavigation/Detour/DetourCommon.h b/dep/recastnavigation/Detour/DetourCommon.h
new file mode 100644
index 0000000..3cee3f6
--- /dev/null
+++ b/dep/recastnavigation/Detour/DetourCommon.h
@@ -0,0 +1,248 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#ifndef DETOURCOMMON_H
+#define DETOURCOMMON_H
+
+template<class T> inline void dtSwap(T& a, T& b) { T t = a; a = b; b = t; }
+template<class T> inline T dtMin(T a, T b) { return a < b ? a : b; }
+template<class T> inline T dtMax(T a, T b) { return a > b ? a : b; }
+template<class T> inline T dtAbs(T a) { return a < 0 ? -a : a; }
+template<class T> inline T dtSqr(T a) { return a*a; }
+template<class T> inline T dtClamp(T v, T mn, T mx) { return v < mn ? mn : (v > mx ? mx : v); }
+
+float dtSqrt(float x);
+
+inline void dtVcross(float* dest, const float* v1, const float* v2)
+{
+ dest[0] = v1[1]*v2[2] - v1[2]*v2[1];
+ dest[1] = v1[2]*v2[0] - v1[0]*v2[2];
+ dest[2] = v1[0]*v2[1] - v1[1]*v2[0];
+}
+
+inline float dtVdot(const float* v1, const float* v2)
+{
+ return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2];
+}
+
+inline void dtVmad(float* dest, const float* v1, const float* v2, const float s)
+{
+ dest[0] = v1[0]+v2[0]*s;
+ dest[1] = v1[1]+v2[1]*s;
+ dest[2] = v1[2]+v2[2]*s;
+}
+
+inline void dtVlerp(float* dest, const float* v1, const float* v2, const float t)
+{
+ dest[0] = v1[0]+(v2[0]-v1[0])*t;
+ dest[1] = v1[1]+(v2[1]-v1[1])*t;
+ dest[2] = v1[2]+(v2[2]-v1[2])*t;
+}
+
+inline void dtVadd(float* dest, const float* v1, const float* v2)
+{
+ dest[0] = v1[0]+v2[0];
+ dest[1] = v1[1]+v2[1];
+ dest[2] = v1[2]+v2[2];
+}
+
+inline void dtVsub(float* dest, const float* v1, const float* v2)
+{
+ dest[0] = v1[0]-v2[0];
+ dest[1] = v1[1]-v2[1];
+ dest[2] = v1[2]-v2[2];
+}
+
+inline void dtVscale(float* dest, const float* v, const float t)
+{
+ dest[0] = v[0]*t;
+ dest[1] = v[1]*t;
+ dest[2] = v[2]*t;
+}
+
+inline void dtVmin(float* mn, const float* v)
+{
+ mn[0] = dtMin(mn[0], v[0]);
+ mn[1] = dtMin(mn[1], v[1]);
+ mn[2] = dtMin(mn[2], v[2]);
+}
+
+inline void dtVmax(float* mx, const float* v)
+{
+ mx[0] = dtMax(mx[0], v[0]);
+ mx[1] = dtMax(mx[1], v[1]);
+ mx[2] = dtMax(mx[2], v[2]);
+}
+
+inline void dtVset(float* dest, const float x, const float y, const float z)
+{
+ dest[0] = x; dest[1] = y; dest[2] = z;
+}
+
+inline void dtVcopy(float* dest, const float* a)
+{
+ dest[0] = a[0];
+ dest[1] = a[1];
+ dest[2] = a[2];
+}
+
+inline float dtVlen(const float* v)
+{
+ return dtSqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]);
+}
+
+inline float dtVlenSqr(const float* v)
+{
+ return v[0]*v[0] + v[1]*v[1] + v[2]*v[2];
+}
+
+inline float dtVdist(const float* v1, const float* v2)
+{
+ const float dx = v2[0] - v1[0];
+ const float dy = v2[1] - v1[1];
+ const float dz = v2[2] - v1[2];
+ return dtSqrt(dx*dx + dy*dy + dz*dz);
+}
+
+inline float dtVdistSqr(const float* v1, const float* v2)
+{
+ const float dx = v2[0] - v1[0];
+ const float dy = v2[1] - v1[1];
+ const float dz = v2[2] - v1[2];
+ return dx*dx + dy*dy + dz*dz;
+}
+
+inline float dtVdist2D(const float* v1, const float* v2)
+{
+ const float dx = v2[0] - v1[0];
+ const float dz = v2[2] - v1[2];
+ return dtSqrt(dx*dx + dz*dz);
+}
+
+inline float dtVdist2DSqr(const float* v1, const float* v2)
+{
+ const float dx = v2[0] - v1[0];
+ const float dz = v2[2] - v1[2];
+ return dx*dx + dz*dz;
+}
+
+inline void dtVnormalize(float* v)
+{
+ float d = 1.0f / dtSqrt(dtSqr(v[0]) + dtSqr(v[1]) + dtSqr(v[2]));
+ v[0] *= d;
+ v[1] *= d;
+ v[2] *= d;
+}
+
+inline bool dtVequal(const float* p0, const float* p1)
+{
+ static const float thr = dtSqr(1.0f/16384.0f);
+ const float d = dtVdistSqr(p0, p1);
+ return d < thr;
+}
+
+inline unsigned int dtNextPow2(unsigned int v)
+{
+ v--;
+ v |= v >> 1;
+ v |= v >> 2;
+ v |= v >> 4;
+ v |= v >> 8;
+ v |= v >> 16;
+ v++;
+ return v;
+}
+
+inline unsigned int dtIlog2(unsigned int v)
+{
+ unsigned int r;
+ unsigned int shift;
+ r = (v > 0xffff) << 4; v >>= r;
+ shift = (v > 0xff) << 3; v >>= shift; r |= shift;
+ shift = (v > 0xf) << 2; v >>= shift; r |= shift;
+ shift = (v > 0x3) << 1; v >>= shift; r |= shift;
+ r |= (v >> 1);
+ return r;
+}
+
+inline int dtAlign4(int x) { return (x+3) & ~3; }
+
+inline int dtOppositeTile(int side) { return (side+4) & 0x7; }
+
+inline float dtVdot2D(const float* u, const float* v)
+{
+ return u[0]*v[0] + u[2]*v[2];
+}
+
+inline float dtVperp2D(const float* u, const float* v)
+{
+ return u[2]*v[0] - u[0]*v[2];
+}
+
+inline float dtTriArea2D(const float* a, const float* b, const float* c)
+{
+ const float abx = b[0] - a[0];
+ const float abz = b[2] - a[2];
+ const float acx = c[0] - a[0];
+ const float acz = c[2] - a[2];
+ return acx*abz - abx*acz;
+}
+
+inline bool dtOverlapQuantBounds(const unsigned short amin[3], const unsigned short amax[3],
+ const unsigned short bmin[3], const unsigned short bmax[3])
+{
+ bool overlap = true;
+ overlap = (amin[0] > bmax[0] || amax[0] < bmin[0]) ? false : overlap;
+ overlap = (amin[1] > bmax[1] || amax[1] < bmin[1]) ? false : overlap;
+ overlap = (amin[2] > bmax[2] || amax[2] < bmin[2]) ? false : overlap;
+ return overlap;
+}
+
+inline bool dtOverlapBounds(const float* amin, const float* amax,
+ const float* bmin, const float* bmax)
+{
+ bool overlap = true;
+ overlap = (amin[0] > bmax[0] || amax[0] < bmin[0]) ? false : overlap;
+ overlap = (amin[1] > bmax[1] || amax[1] < bmin[1]) ? false : overlap;
+ overlap = (amin[2] > bmax[2] || amax[2] < bmin[2]) ? false : overlap;
+ return overlap;
+}
+
+void dtClosestPtPointTriangle(float* closest, const float* p,
+ const float* a, const float* b, const float* c);
+
+bool dtClosestHeightPointTriangle(const float* p, const float* a, const float* b, const float* c, float& h);
+
+bool dtIntersectSegmentPoly2D(const float* p0, const float* p1,
+ const float* verts, int nverts,
+ float& tmin, float& tmax,
+ int& segMin, int& segMax);
+
+bool dtPointInPolygon(const float* pt, const float* verts, const int nverts);
+
+bool dtDistancePtPolyEdgesSqr(const float* pt, const float* verts, const int nverts,
+ float* ed, float* et);
+
+float dtDistancePtSegSqr2D(const float* pt, const float* p, const float* q, float& t);
+
+void dtCalcPolyCenter(float* tc, const unsigned short* idx, int nidx, const float* verts);
+
+bool dtOverlapPolyPoly2D(const float* polya, const int npolya,
+ const float* polyb, const int npolyb);
+
+#endif // DETOURCOMMON_H
diff --git a/dep/recastnavigation/Detour/DetourNavMesh.cpp b/dep/recastnavigation/Detour/DetourNavMesh.cpp
new file mode 100644
index 0000000..e139e3f
--- /dev/null
+++ b/dep/recastnavigation/Detour/DetourNavMesh.cpp
@@ -0,0 +1,1235 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#include <math.h>
+#include <float.h>
+#include <string.h>
+#include <stdio.h>
+#include "DetourNavMesh.h"
+#include "DetourNode.h"
+#include "DetourCommon.h"
+#include "DetourAlloc.h"
+#include "DetourAssert.h"
+#include <new>
+
+
+inline bool overlapSlabs(const float* amin, const float* amax,
+ const float* bmin, const float* bmax,
+ const float px, const float py)
+{
+ // Check for horizontal overlap.
+ // The segment is shrunken a little so that slabs which touch
+ // at end points are not connected.
+ const float minx = dtMax(amin[0]+px,bmin[0]+px);
+ const float maxx = dtMin(amax[0]-px,bmax[0]-px);
+ if (minx > maxx)
+ return false;
+
+ // Check vertical overlap.
+ const float ad = (amax[1]-amin[1]) / (amax[0]-amin[0]);
+ const float ak = amin[1] - ad*amin[0];
+ const float bd = (bmax[1]-bmin[1]) / (bmax[0]-bmin[0]);
+ const float bk = bmin[1] - bd*bmin[0];
+ const float aminy = ad*minx + ak;
+ const float amaxy = ad*maxx + ak;
+ const float bminy = bd*minx + bk;
+ const float bmaxy = bd*maxx + bk;
+ const float dmin = bminy - aminy;
+ const float dmax = bmaxy - amaxy;
+
+ // Crossing segments always overlap.
+ if (dmin*dmax < 0)
+ return true;
+
+ // Check for overlap at endpoints.
+ const float thr = dtSqr(py*2);
+ if (dmin*dmin <= thr || dmax*dmax <= thr)
+ return true;
+
+ return false;
+}
+
+static void calcSlabEndPoints(const float* va, const float* vb, float* bmin, float* bmax, const int side)
+{
+ if (side == 0 || side == 4)
+ {
+ if (va[2] < vb[2])
+ {
+ bmin[0] = va[2];
+ bmin[1] = va[1];
+ bmax[0] = vb[2];
+ bmax[1] = vb[1];
+ }
+ else
+ {
+ bmin[0] = vb[2];
+ bmin[1] = vb[1];
+ bmax[0] = va[2];
+ bmax[1] = va[1];
+ }
+ }
+ else if (side == 2 || side == 6)
+ {
+ if (va[0] < vb[0])
+ {
+ bmin[0] = va[0];
+ bmin[1] = va[1];
+ bmax[0] = vb[0];
+ bmax[1] = vb[1];
+ }
+ else
+ {
+ bmin[0] = vb[0];
+ bmin[1] = vb[1];
+ bmax[0] = va[0];
+ bmax[1] = va[1];
+ }
+ }
+}
+
+inline int computeTileHash(int x, int y, const int mask)
+{
+ const unsigned int h1 = 0x8da6b343; // Large multiplicative constants;
+ const unsigned int h2 = 0xd8163841; // here arbitrarily chosen primes
+ unsigned int n = h1 * x + h2 * y;
+ return (int)(n & mask);
+}
+
+inline unsigned int allocLink(dtMeshTile* tile)
+{
+ if (tile->linksFreeList == DT_NULL_LINK)
+ return DT_NULL_LINK;
+ unsigned int link = tile->linksFreeList;
+ tile->linksFreeList = tile->links[link].next;
+ return link;
+}
+
+inline void freeLink(dtMeshTile* tile, unsigned int link)
+{
+ tile->links[link].next = tile->linksFreeList;
+ tile->linksFreeList = link;
+}
+
+
+dtNavMesh* dtAllocNavMesh()
+{
+ void* mem = dtAlloc(sizeof(dtNavMesh), DT_ALLOC_PERM);
+ if (!mem) return 0;
+ return new(mem) dtNavMesh;
+}
+
+void dtFreeNavMesh(dtNavMesh* navmesh)
+{
+ if (!navmesh) return;
+ navmesh->~dtNavMesh();
+ dtFree(navmesh);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+dtNavMesh::dtNavMesh() :
+ m_tileWidth(0),
+ m_tileHeight(0),
+ m_maxTiles(0),
+ m_tileLutSize(0),
+ m_tileLutMask(0),
+ m_posLookup(0),
+ m_nextFree(0),
+ m_tiles(0),
+ m_saltBits(0),
+ m_tileBits(0),
+ m_polyBits(0)
+{
+ m_orig[0] = 0;
+ m_orig[1] = 0;
+ m_orig[2] = 0;
+}
+
+dtNavMesh::~dtNavMesh()
+{
+ for (int i = 0; i < m_maxTiles; ++i)
+ {
+ if (m_tiles[i].flags & DT_TILE_FREE_DATA)
+ {
+ dtFree(m_tiles[i].data);
+ m_tiles[i].data = 0;
+ m_tiles[i].dataSize = 0;
+ }
+ }
+ dtFree(m_posLookup);
+ dtFree(m_tiles);
+}
+
+dtStatus dtNavMesh::init(const dtNavMeshParams* params)
+{
+ memcpy(&m_params, params, sizeof(dtNavMeshParams));
+ dtVcopy(m_orig, params->orig);
+ m_tileWidth = params->tileWidth;
+ m_tileHeight = params->tileHeight;
+
+ // Init tiles
+ m_maxTiles = params->maxTiles;
+ m_tileLutSize = dtNextPow2(params->maxTiles/4);
+ if (!m_tileLutSize) m_tileLutSize = 1;
+ m_tileLutMask = m_tileLutSize-1;
+
+ m_tiles = (dtMeshTile*)dtAlloc(sizeof(dtMeshTile)*m_maxTiles, DT_ALLOC_PERM);
+ if (!m_tiles)
+ return DT_FAILURE;
+ m_posLookup = (dtMeshTile**)dtAlloc(sizeof(dtMeshTile*)*m_tileLutSize, DT_ALLOC_PERM);
+ if (!m_posLookup)
+ return DT_FAILURE;
+ memset(m_tiles, 0, sizeof(dtMeshTile)*m_maxTiles);
+ memset(m_posLookup, 0, sizeof(dtMeshTile*)*m_tileLutSize);
+ m_nextFree = 0;
+ for (int i = m_maxTiles-1; i >= 0; --i)
+ {
+ m_tiles[i].salt = 1;
+ m_tiles[i].next = m_nextFree;
+ m_nextFree = &m_tiles[i];
+ }
+
+ // Init ID generator values.
+ m_tileBits = STATIC_TILE_BITS; //dtIlog2(dtNextPow2((unsigned int)params->maxTiles));
+ m_polyBits = STATIC_POLY_BITS; //dtIlog2(dtNextPow2((unsigned int)params->maxPolys));
+ m_saltBits = STATIC_SALT_BITS; //sizeof(dtPolyRef)*8 - m_tileBits - m_polyBits;
+ //if (m_saltBits < SALT_MIN_BITS)
+ //return DT_FAILURE;
+
+ return DT_SUCCESS;
+}
+
+dtStatus dtNavMesh::init(unsigned char* data, const int dataSize, const int flags)
+{
+ // Make sure the data is in right format.
+ dtMeshHeader* header = (dtMeshHeader*)data;
+ if (header->magic != DT_NAVMESH_MAGIC)
+ return DT_FAILURE;
+ if (header->version != DT_NAVMESH_VERSION)
+ return DT_FAILURE;
+
+ dtNavMeshParams params;
+ dtVcopy(params.orig, header->bmin);
+ params.tileWidth = header->bmax[0] - header->bmin[0];
+ params.tileHeight = header->bmax[2] - header->bmin[2];
+ params.maxTiles = 1;
+ params.maxPolys = header->polyCount;
+
+ dtStatus res = init(&params);
+ if (res != DT_SUCCESS)
+ return res;
+
+ return addTile(data, dataSize, flags, 0, 0);
+}
+
+const dtNavMeshParams* dtNavMesh::getParams() const
+{
+ return &m_params;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+int dtNavMesh::findConnectingPolys(const float* va, const float* vb,
+ const dtMeshTile* tile, int side,
+ dtPolyRef* con, float* conarea, int maxcon) const
+{
+ if (!tile) return 0;
+
+ float amin[2], amax[2];
+ calcSlabEndPoints(va,vb, amin,amax, side);
+
+ // Remove links pointing to 'side' and compact the links array.
+ float bmin[2], bmax[2];
+ unsigned short m = DT_EXT_LINK | (unsigned short)side;
+ int n = 0;
+
+ dtPolyRef base = getPolyRefBase(tile);
+
+ for (int i = 0; i < tile->header->polyCount; ++i)
+ {
+ dtPoly* poly = &tile->polys[i];
+ const int nv = poly->vertCount;
+ for (int j = 0; j < nv; ++j)
+ {
+ // Skip edges which do not point to the right side.
+ if (poly->neis[j] != m) continue;
+ // Check if the segments touch.
+ const float* vc = &tile->verts[poly->verts[j]*3];
+ const float* vd = &tile->verts[poly->verts[(j+1) % nv]*3];
+ calcSlabEndPoints(vc,vd, bmin,bmax, side);
+
+ if (!overlapSlabs(amin,amax, bmin,bmax, 0.01f, tile->header->walkableClimb)) continue;
+
+ // Add return value.
+ if (n < maxcon)
+ {
+ conarea[n*2+0] = dtMax(amin[0], bmin[0]);
+ conarea[n*2+1] = dtMin(amax[0], bmax[0]);
+ con[n] = base | (dtPolyRef)i;
+ n++;
+ }
+ break;
+ }
+ }
+ return n;
+}
+
+void dtNavMesh::unconnectExtLinks(dtMeshTile* tile, int side)
+{
+ if (!tile) return;
+
+ for (int i = 0; i < tile->header->polyCount; ++i)
+ {
+ dtPoly* poly = &tile->polys[i];
+ unsigned int j = poly->firstLink;
+ unsigned int pj = DT_NULL_LINK;
+ while (j != DT_NULL_LINK)
+ {
+ if (tile->links[j].side == side)
+ {
+ // Revove link.
+ unsigned int nj = tile->links[j].next;
+ if (pj == DT_NULL_LINK)
+ poly->firstLink = nj;
+ else
+ tile->links[pj].next = nj;
+ freeLink(tile, j);
+ j = nj;
+ }
+ else
+ {
+ // Advance
+ pj = j;
+ j = tile->links[j].next;
+ }
+ }
+ }
+}
+
+void dtNavMesh::connectExtLinks(dtMeshTile* tile, dtMeshTile* target, int side)
+{
+ if (!tile) return;
+
+ // Connect border links.
+ for (int i = 0; i < tile->header->polyCount; ++i)
+ {
+ dtPoly* poly = &tile->polys[i];
+
+ // Create new links.
+ unsigned short m = DT_EXT_LINK | (unsigned short)side;
+ const int nv = poly->vertCount;
+ for (int j = 0; j < nv; ++j)
+ {
+ // Skip edges which do not point to the right side.
+ if (poly->neis[j] != m) continue;
+
+ // Create new links
+ const float* va = &tile->verts[poly->verts[j]*3];
+ const float* vb = &tile->verts[poly->verts[(j+1) % nv]*3];
+ dtPolyRef nei[4];
+ float neia[4*2];
+ int nnei = findConnectingPolys(va,vb, target, dtOppositeTile(side), nei,neia,4);
+ for (int k = 0; k < nnei; ++k)
+ {
+ unsigned int idx = allocLink(tile);
+ if (idx != DT_NULL_LINK)
+ {
+ dtLink* link = &tile->links[idx];
+ link->ref = nei[k];
+ link->edge = (unsigned char)j;
+ link->side = (unsigned char)side;
+
+ link->next = poly->firstLink;
+ poly->firstLink = idx;
+
+ // Compress portal limits to a byte value.
+ if (side == 0 || side == 4)
+ {
+ float tmin = (neia[k*2+0]-va[2]) / (vb[2]-va[2]);
+ float tmax = (neia[k*2+1]-va[2]) / (vb[2]-va[2]);
+ if (tmin > tmax)
+ dtSwap(tmin,tmax);
+ link->bmin = (unsigned char)(dtClamp(tmin, 0.0f, 1.0f)*255.0f);
+ link->bmax = (unsigned char)(dtClamp(tmax, 0.0f, 1.0f)*255.0f);
+ }
+ else if (side == 2 || side == 6)
+ {
+ float tmin = (neia[k*2+0]-va[0]) / (vb[0]-va[0]);
+ float tmax = (neia[k*2+1]-va[0]) / (vb[0]-va[0]);
+ if (tmin > tmax)
+ dtSwap(tmin,tmax);
+ link->bmin = (unsigned char)(dtClamp(tmin, 0.0f, 1.0f)*255.0f);
+ link->bmax = (unsigned char)(dtClamp(tmax, 0.0f, 1.0f)*255.0f);
+ }
+ }
+ }
+ }
+ }
+}
+
+void dtNavMesh::connectExtOffMeshLinks(dtMeshTile* tile, dtMeshTile* target, int side)
+{
+ if (!tile) return;
+
+ // Connect off-mesh links.
+ // We are interested on links which land from target tile to this tile.
+ const unsigned char oppositeSide = (unsigned char)dtOppositeTile(side);
+
+ for (int i = 0; i < target->header->offMeshConCount; ++i)
+ {
+ dtOffMeshConnection* targetCon = &target->offMeshCons[i];
+ if (targetCon->side != oppositeSide)
+ continue;
+
+ dtPoly* targetPoly = &target->polys[targetCon->poly];
+
+ const float ext[3] = { targetCon->rad, target->header->walkableClimb, targetCon->rad };
+
+ // Find polygon to connect to.
+ const float* p = &targetCon->pos[3];
+ float nearestPt[3];
+ dtPolyRef ref = findNearestPolyInTile(tile, p, ext, nearestPt);
+ if (!ref) continue;
+ // findNearestPoly may return too optimistic results, further check to make sure.
+ if (dtSqr(nearestPt[0]-p[0])+dtSqr(nearestPt[2]-p[2]) > dtSqr(targetCon->rad))
+ continue;
+ // Make sure the location is on current mesh.
+ float* v = &target->verts[targetPoly->verts[1]*3];
+ dtVcopy(v, nearestPt);
+
+ // Link off-mesh connection to target poly.
+ unsigned int idx = allocLink(target);
+ if (idx != DT_NULL_LINK)
+ {
+ dtLink* link = &target->links[idx];
+ link->ref = ref;
+ link->edge = (unsigned char)1;
+ link->side = oppositeSide;
+ link->bmin = link->bmax = 0;
+ // Add to linked list.
+ link->next = targetPoly->firstLink;
+ targetPoly->firstLink = idx;
+ }
+
+ // Link target poly to off-mesh connection.
+ if (targetCon->flags & DT_OFFMESH_CON_BIDIR)
+ {
+ unsigned int idx = allocLink(tile);
+ if (idx != DT_NULL_LINK)
+ {
+ const unsigned short landPolyIdx = (unsigned short)decodePolyIdPoly(ref);
+ dtPoly* landPoly = &tile->polys[landPolyIdx];
+ dtLink* link = &tile->links[idx];
+ link->ref = getPolyRefBase(target) | (dtPolyRef)(targetCon->poly);
+ link->edge = 0xff;
+ link->side = (unsigned char)side;
+ link->bmin = link->bmax = 0;
+ // Add to linked list.
+ link->next = landPoly->firstLink;
+ landPoly->firstLink = idx;
+ }
+ }
+ }
+
+}
+
+void dtNavMesh::connectIntLinks(dtMeshTile* tile)
+{
+ if (!tile) return;
+
+ dtPolyRef base = getPolyRefBase(tile);
+
+ for (int i = 0; i < tile->header->polyCount; ++i)
+ {
+ dtPoly* poly = &tile->polys[i];
+ poly->firstLink = DT_NULL_LINK;
+
+ if (poly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION)
+ continue;
+
+ // Build edge links backwards so that the links will be
+ // in the linked list from lowest index to highest.
+ for (int j = poly->vertCount-1; j >= 0; --j)
+ {
+ // Skip hard and non-internal edges.
+ if (poly->neis[j] == 0 || (poly->neis[j] & DT_EXT_LINK)) continue;
+
+ unsigned int idx = allocLink(tile);
+ if (idx != DT_NULL_LINK)
+ {
+ dtLink* link = &tile->links[idx];
+ link->ref = base | (dtPolyRef)(poly->neis[j]-1);
+ link->edge = (unsigned char)j;
+ link->side = 0xff;
+ link->bmin = link->bmax = 0;
+ // Add to linked list.
+ link->next = poly->firstLink;
+ poly->firstLink = idx;
+ }
+ }
+ }
+}
+
+void dtNavMesh::connectIntOffMeshLinks(dtMeshTile* tile)
+{
+ if (!tile) return;
+
+ dtPolyRef base = getPolyRefBase(tile);
+
+ // Find Off-mesh connection end points.
+ for (int i = 0; i < tile->header->offMeshConCount; ++i)
+ {
+ dtOffMeshConnection* con = &tile->offMeshCons[i];
+ dtPoly* poly = &tile->polys[con->poly];
+
+ const float ext[3] = { con->rad, tile->header->walkableClimb, con->rad };
+
+ for (int j = 0; j < 2; ++j)
+ {
+ unsigned char side = j == 0 ? 0xff : con->side;
+
+ if (side == 0xff)
+ {
+ // Find polygon to connect to.
+ const float* p = &con->pos[j*3];
+ float nearestPt[3];
+ dtPolyRef ref = findNearestPolyInTile(tile, p, ext, nearestPt);
+ if (!ref) continue;
+ // findNearestPoly may return too optimistic results, further check to make sure.
+ if (dtSqr(nearestPt[0]-p[0])+dtSqr(nearestPt[2]-p[2]) > dtSqr(con->rad))
+ continue;
+ // Make sure the location is on current mesh.
+ float* v = &tile->verts[poly->verts[j]*3];
+ dtVcopy(v, nearestPt);
+
+ // Link off-mesh connection to target poly.
+ unsigned int idx = allocLink(tile);
+ if (idx != DT_NULL_LINK)
+ {
+ dtLink* link = &tile->links[idx];
+ link->ref = ref;
+ link->edge = (unsigned char)j;
+ link->side = 0xff;
+ link->bmin = link->bmax = 0;
+ // Add to linked list.
+ link->next = poly->firstLink;
+ poly->firstLink = idx;
+ }
+
+ // Start end-point is always connect back to off-mesh connection,
+ // Destination end-point only if it is bidirectional link.
+ if (j == 0 || (j == 1 && (con->flags & DT_OFFMESH_CON_BIDIR)))
+ {
+ // Link target poly to off-mesh connection.
+ unsigned int idx = allocLink(tile);
+ if (idx != DT_NULL_LINK)
+ {
+ const unsigned short landPolyIdx = (unsigned short)decodePolyIdPoly(ref);
+ dtPoly* landPoly = &tile->polys[landPolyIdx];
+ dtLink* link = &tile->links[idx];
+ link->ref = base | (dtPolyRef)(con->poly);
+ link->edge = 0xff;
+ link->side = 0xff;
+ link->bmin = link->bmax = 0;
+ // Add to linked list.
+ link->next = landPoly->firstLink;
+ landPoly->firstLink = idx;
+ }
+ }
+
+ }
+ }
+ }
+}
+
+dtStatus dtNavMesh::closestPointOnPolyInTile(const dtMeshTile* tile, unsigned int ip,
+ const float* pos, float* closest) const
+{
+ const dtPoly* poly = &tile->polys[ip];
+
+ float closestDistSqr = FLT_MAX;
+ const dtPolyDetail* pd = &tile->detailMeshes[ip];
+
+ for (int j = 0; j < pd->triCount; ++j)
+ {
+ const unsigned char* t = &tile->detailTris[(pd->triBase+j)*4];
+ const float* v[3];
+ for (int k = 0; k < 3; ++k)
+ {
+ if (t[k] < poly->vertCount)
+ v[k] = &tile->verts[poly->verts[t[k]]*3];
+ else
+ v[k] = &tile->detailVerts[(pd->vertBase+(t[k]-poly->vertCount))*3];
+ }
+ float pt[3];
+ dtClosestPtPointTriangle(pt, pos, v[0], v[1], v[2]);
+ float d = dtVdistSqr(pos, pt);
+ if (d < closestDistSqr)
+ {
+ dtVcopy(closest, pt);
+ closestDistSqr = d;
+ }
+ }
+
+ return DT_SUCCESS;
+}
+
+dtPolyRef dtNavMesh::findNearestPolyInTile(const dtMeshTile* tile,
+ const float* center, const float* extents,
+ float* nearestPt) const
+{
+ float bmin[3], bmax[3];
+ dtVsub(bmin, center, extents);
+ dtVadd(bmax, center, extents);
+
+ // Get nearby polygons from proximity grid.
+ dtPolyRef polys[128];
+ int polyCount = queryPolygonsInTile(tile, bmin, bmax, polys, 128);
+
+ // Find nearest polygon amongst the nearby polygons.
+ dtPolyRef nearest = 0;
+ float nearestDistanceSqr = FLT_MAX;
+ for (int i = 0; i < polyCount; ++i)
+ {
+ dtPolyRef ref = polys[i];
+ float closestPtPoly[3];
+ if (closestPointOnPolyInTile(tile, decodePolyIdPoly(ref), center, closestPtPoly) != DT_SUCCESS)
+ continue;
+ float d = dtVdistSqr(center, closestPtPoly);
+ if (d < nearestDistanceSqr)
+ {
+ if (nearestPt)
+ dtVcopy(nearestPt, closestPtPoly);
+ nearestDistanceSqr = d;
+ nearest = ref;
+ }
+ }
+
+ return nearest;
+}
+
+int dtNavMesh::queryPolygonsInTile(const dtMeshTile* tile, const float* qmin, const float* qmax,
+ dtPolyRef* polys, const int maxPolys) const
+{
+ if (tile->bvTree)
+ {
+ const dtBVNode* node = &tile->bvTree[0];
+ const dtBVNode* end = &tile->bvTree[tile->header->bvNodeCount];
+ const float* tbmin = tile->header->bmin;
+ const float* tbmax = tile->header->bmax;
+ const float qfac = tile->header->bvQuantFactor;
+
+ // Calculate quantized box
+ unsigned short bmin[3], bmax[3];
+ // dtClamp query box to world box.
+ float minx = dtClamp(qmin[0], tbmin[0], tbmax[0]) - tbmin[0];
+ float miny = dtClamp(qmin[1], tbmin[1], tbmax[1]) - tbmin[1];
+ float minz = dtClamp(qmin[2], tbmin[2], tbmax[2]) - tbmin[2];
+ float maxx = dtClamp(qmax[0], tbmin[0], tbmax[0]) - tbmin[0];
+ float maxy = dtClamp(qmax[1], tbmin[1], tbmax[1]) - tbmin[1];
+ float maxz = dtClamp(qmax[2], tbmin[2], tbmax[2]) - tbmin[2];
+ // Quantize
+ bmin[0] = (unsigned short)(qfac * minx) & 0xfffe;
+ bmin[1] = (unsigned short)(qfac * miny) & 0xfffe;
+ bmin[2] = (unsigned short)(qfac * minz) & 0xfffe;
+ bmax[0] = (unsigned short)(qfac * maxx + 1) | 1;
+ bmax[1] = (unsigned short)(qfac * maxy + 1) | 1;
+ bmax[2] = (unsigned short)(qfac * maxz + 1) | 1;
+
+ // Traverse tree
+ dtPolyRef base = getPolyRefBase(tile);
+ int n = 0;
+ while (node < end)
+ {
+ const bool overlap = dtOverlapQuantBounds(bmin, bmax, node->bmin, node->bmax);
+ const bool isLeafNode = node->i >= 0;
+
+ if (isLeafNode && overlap)
+ {
+ if (n < maxPolys)
+ polys[n++] = base | (dtPolyRef)node->i;
+ }
+
+ if (overlap || isLeafNode)
+ node++;
+ else
+ {
+ const int escapeIndex = -node->i;
+ node += escapeIndex;
+ }
+ }
+
+ return n;
+ }
+ else
+ {
+ float bmin[3], bmax[3];
+ int n = 0;
+ dtPolyRef base = getPolyRefBase(tile);
+ for (int i = 0; i < tile->header->polyCount; ++i)
+ {
+ // Calc polygon bounds.
+ dtPoly* p = &tile->polys[i];
+ const float* v = &tile->verts[p->verts[0]*3];
+ dtVcopy(bmin, v);
+ dtVcopy(bmax, v);
+ for (int j = 1; j < p->vertCount; ++j)
+ {
+ v = &tile->verts[p->verts[j]*3];
+ dtVmin(bmin, v);
+ dtVmax(bmax, v);
+ }
+ if (dtOverlapBounds(qmin,qmax, bmin,bmax))
+ {
+ if (n < maxPolys)
+ polys[n++] = base | (dtPolyRef)i;
+ }
+ }
+ return n;
+ }
+}
+
+dtStatus dtNavMesh::addTile(unsigned char* data, int dataSize, int flags,
+ dtTileRef lastRef, dtTileRef* result)
+{
+ // Make sure the data is in right format.
+ dtMeshHeader* header = (dtMeshHeader*)data;
+ if (header->magic != DT_NAVMESH_MAGIC)
+ return DT_FAILURE_DATA_MAGIC;
+ if (header->version != DT_NAVMESH_VERSION)
+ return DT_FAILURE_DATA_VERSION;
+
+ // Make sure the location is free.
+ if (getTileAt(header->x, header->y))
+ return DT_FAILURE;
+
+ // Allocate a tile.
+ dtMeshTile* tile = 0;
+ if (!lastRef)
+ {
+ if (m_nextFree)
+ {
+ tile = m_nextFree;
+ m_nextFree = tile->next;
+ tile->next = 0;
+ }
+ }
+ else
+ {
+ // Try to relocate the tile to specific index with same salt.
+ int tileIndex = (int)decodePolyIdTile((dtPolyRef)lastRef);
+ if (tileIndex >= m_maxTiles)
+ return DT_FAILURE_OUT_OF_MEMORY;
+ // Try to find the specific tile id from the free list.
+ dtMeshTile* target = &m_tiles[tileIndex];
+ dtMeshTile* prev = 0;
+ tile = m_nextFree;
+ while (tile && tile != target)
+ {
+ prev = tile;
+ tile = tile->next;
+ }
+ // Could not find the correct location.
+ if (tile != target)
+ return DT_FAILURE_OUT_OF_MEMORY;
+ // Remove from freelist
+ if (!prev)
+ m_nextFree = tile->next;
+ else
+ prev->next = tile->next;
+
+ // Restore salt.
+ tile->salt = decodePolyIdSalt((dtPolyRef)lastRef);
+ }
+
+ // Make sure we could allocate a tile.
+ if (!tile)
+ return DT_FAILURE_OUT_OF_MEMORY;
+
+ // Insert tile into the position lut.
+ int h = computeTileHash(header->x, header->y, m_tileLutMask);
+ tile->next = m_posLookup[h];
+ m_posLookup[h] = tile;
+
+ // Patch header pointers.
+ const int headerSize = dtAlign4(sizeof(dtMeshHeader));
+ const int vertsSize = dtAlign4(sizeof(float)*3*header->vertCount);
+ const int polysSize = dtAlign4(sizeof(dtPoly)*header->polyCount);
+ const int linksSize = dtAlign4(sizeof(dtLink)*(header->maxLinkCount));
+ const int detailMeshesSize = dtAlign4(sizeof(dtPolyDetail)*header->detailMeshCount);
+ const int detailVertsSize = dtAlign4(sizeof(float)*3*header->detailVertCount);
+ const int detailTrisSize = dtAlign4(sizeof(unsigned char)*4*header->detailTriCount);
+ const int bvtreeSize = dtAlign4(sizeof(dtBVNode)*header->bvNodeCount);
+ const int offMeshLinksSize = dtAlign4(sizeof(dtOffMeshConnection)*header->offMeshConCount);
+
+ unsigned char* d = data + headerSize;
+ tile->verts = (float*)d; d += vertsSize;
+ tile->polys = (dtPoly*)d; d += polysSize;
+ tile->links = (dtLink*)d; d += linksSize;
+ tile->detailMeshes = (dtPolyDetail*)d; d += detailMeshesSize;
+ tile->detailVerts = (float*)d; d += detailVertsSize;
+ tile->detailTris = (unsigned char*)d; d += detailTrisSize;
+ tile->bvTree = (dtBVNode*)d; d += bvtreeSize;
+ tile->offMeshCons = (dtOffMeshConnection*)d; d += offMeshLinksSize;
+
+ // Build links freelist
+ tile->linksFreeList = 0;
+ tile->links[header->maxLinkCount-1].next = DT_NULL_LINK;
+ for (int i = 0; i < header->maxLinkCount-1; ++i)
+ tile->links[i].next = i+1;
+
+ // Init tile.
+ tile->header = header;
+ tile->data = data;
+ tile->dataSize = dataSize;
+ tile->flags = flags;
+
+ connectIntLinks(tile);
+ connectIntOffMeshLinks(tile);
+
+ // Create connections connections.
+ for (int i = 0; i < 8; ++i)
+ {
+ dtMeshTile* nei = getNeighbourTileAt(header->x, header->y, i);
+ if (nei)
+ {
+ connectExtLinks(tile, nei, i);
+ connectExtLinks(nei, tile, dtOppositeTile(i));
+ connectExtOffMeshLinks(tile, nei, i);
+ connectExtOffMeshLinks(nei, tile, dtOppositeTile(i));
+ }
+ }
+
+ if (result)
+ *result = getTileRef(tile);
+
+ return DT_SUCCESS;
+}
+
+const dtMeshTile* dtNavMesh::getTileAt(int x, int y) const
+{
+ // Find tile based on hash.
+ int h = computeTileHash(x,y,m_tileLutMask);
+ dtMeshTile* tile = m_posLookup[h];
+ while (tile)
+ {
+ if (tile->header && tile->header->x == x && tile->header->y == y)
+ return tile;
+ tile = tile->next;
+ }
+ return 0;
+}
+
+dtMeshTile* dtNavMesh::getNeighbourTileAt(int x, int y, int side) const
+{
+ switch (side)
+ {
+ case 0: x++; break;
+ case 1: x++; y++; break;
+ case 2: y++; break;
+ case 3: x--; y++; break;
+ case 4: x--; break;
+ case 5: x--; y--; break;
+ case 6: y--; break;
+ case 7: x++; y--; break;
+ };
+
+ // Find tile based on hash.
+ int h = computeTileHash(x,y,m_tileLutMask);
+ dtMeshTile* tile = m_posLookup[h];
+ while (tile)
+ {
+ if (tile->header && tile->header->x == x && tile->header->y == y)
+ return tile;
+ tile = tile->next;
+ }
+ return 0;
+}
+
+dtTileRef dtNavMesh::getTileRefAt(int x, int y) const
+{
+ // Find tile based on hash.
+ int h = computeTileHash(x,y,m_tileLutMask);
+ dtMeshTile* tile = m_posLookup[h];
+ while (tile)
+ {
+ if (tile->header && tile->header->x == x && tile->header->y == y)
+ return getTileRef(tile);
+ tile = tile->next;
+ }
+ return 0;
+}
+
+const dtMeshTile* dtNavMesh::getTileByRef(dtTileRef ref) const
+{
+ if (!ref)
+ return 0;
+ unsigned int tileIndex = decodePolyIdTile((dtPolyRef)ref);
+ unsigned int tileSalt = decodePolyIdSalt((dtPolyRef)ref);
+ if ((int)tileIndex >= m_maxTiles)
+ return 0;
+ const dtMeshTile* tile = &m_tiles[tileIndex];
+ if (tile->salt != tileSalt)
+ return 0;
+ return tile;
+}
+
+int dtNavMesh::getMaxTiles() const
+{
+ return m_maxTiles;
+}
+
+dtMeshTile* dtNavMesh::getTile(int i)
+{
+ return &m_tiles[i];
+}
+
+const dtMeshTile* dtNavMesh::getTile(int i) const
+{
+ return &m_tiles[i];
+}
+
+void dtNavMesh::calcTileLoc(const float* pos, int* tx, int* ty) const
+{
+ *tx = (int)floorf((pos[0]-m_orig[0]) / m_tileWidth);
+ *ty = (int)floorf((pos[2]-m_orig[2]) / m_tileHeight);
+}
+
+dtStatus dtNavMesh::getTileAndPolyByRef(const dtPolyRef ref, const dtMeshTile** tile, const dtPoly** poly) const
+{
+ unsigned int salt, it, ip;
+ decodePolyId(ref, salt, it, ip);
+ if (it >= (unsigned int)m_maxTiles) return DT_FAILURE;
+ if (m_tiles[it].salt != salt || m_tiles[it].header == 0) return DT_FAILURE;
+ if (ip >= (unsigned int)m_tiles[it].header->polyCount) return DT_FAILURE;
+ *tile = &m_tiles[it];
+ *poly = &m_tiles[it].polys[ip];
+ return DT_SUCCESS;
+}
+
+void dtNavMesh::getTileAndPolyByRefUnsafe(const dtPolyRef ref, const dtMeshTile** tile, const dtPoly** poly) const
+{
+ unsigned int salt, it, ip;
+ decodePolyId(ref, salt, it, ip);
+ *tile = &m_tiles[it];
+ *poly = &m_tiles[it].polys[ip];
+}
+
+bool dtNavMesh::isValidPolyRef(dtPolyRef ref) const
+{
+ unsigned int salt, it, ip;
+ decodePolyId(ref, salt, it, ip);
+ if (it >= (unsigned int)m_maxTiles) return false;
+ if (m_tiles[it].salt != salt || m_tiles[it].header == 0) return false;
+ if (ip >= (unsigned int)m_tiles[it].header->polyCount) return false;
+ return true;
+}
+
+dtStatus dtNavMesh::removeTile(dtTileRef ref, unsigned char** data, int* dataSize)
+{
+ if (!ref)
+ return DT_FAILURE;
+ unsigned int tileIndex = decodePolyIdTile((dtPolyRef)ref);
+ unsigned int tileSalt = decodePolyIdSalt((dtPolyRef)ref);
+ if ((int)tileIndex >= m_maxTiles)
+ return DT_FAILURE;
+ dtMeshTile* tile = &m_tiles[tileIndex];
+ if (tile->salt != tileSalt)
+ return DT_FAILURE;
+
+ // Remove tile from hash lookup.
+ int h = computeTileHash(tile->header->x,tile->header->y,m_tileLutMask);
+ dtMeshTile* prev = 0;
+ dtMeshTile* cur = m_posLookup[h];
+ while (cur)
+ {
+ if (cur == tile)
+ {
+ if (prev)
+ prev->next = cur->next;
+ else
+ m_posLookup[h] = cur->next;
+ break;
+ }
+ prev = cur;
+ cur = cur->next;
+ }
+
+ // Remove connections to neighbour tiles.
+ for (int i = 0; i < 8; ++i)
+ {
+ dtMeshTile* nei = getNeighbourTileAt(tile->header->x,tile->header->y,i);
+ if (!nei) continue;
+ unconnectExtLinks(nei, dtOppositeTile(i));
+ }
+
+
+ // Reset tile.
+ if (tile->flags & DT_TILE_FREE_DATA)
+ {
+ // Owns data
+ dtFree(tile->data);
+ tile->data = 0;
+ tile->dataSize = 0;
+ if (data) *data = 0;
+ if (dataSize) *dataSize = 0;
+ }
+ else
+ {
+ if (data) *data = tile->data;
+ if (dataSize) *dataSize = tile->dataSize;
+ }
+
+ tile->header = 0;
+ tile->flags = 0;
+ tile->linksFreeList = 0;
+ tile->polys = 0;
+ tile->verts = 0;
+ tile->links = 0;
+ tile->detailMeshes = 0;
+ tile->detailVerts = 0;
+ tile->detailTris = 0;
+ tile->bvTree = 0;
+ tile->offMeshCons = 0;
+
+ // Update salt, salt should never be zero.
+ tile->salt = (tile->salt+1) & ((1<<m_saltBits)-1);
+ if (tile->salt == 0)
+ tile->salt++;
+
+ // Add to free list.
+ tile->next = m_nextFree;
+ m_nextFree = tile;
+
+ return DT_SUCCESS;
+}
+
+dtTileRef dtNavMesh::getTileRef(const dtMeshTile* tile) const
+{
+ if (!tile) return 0;
+ const unsigned int it = tile - m_tiles;
+ return (dtTileRef)encodePolyId(tile->salt, it, 0);
+}
+
+dtPolyRef dtNavMesh::getPolyRefBase(const dtMeshTile* tile) const
+{
+ if (!tile) return 0;
+ const unsigned int it = tile - m_tiles;
+ return encodePolyId(tile->salt, it, 0);
+}
+
+struct dtTileState
+{
+ int magic; // Magic number, used to identify the data.
+ int version; // Data version number.
+ dtTileRef ref; // Tile ref at the time of storing the data.
+};
+
+struct dtPolyState
+{
+ unsigned short flags; // Flags (see dtPolyFlags).
+ unsigned char area; // Area ID of the polygon.
+};
+
+int dtNavMesh::getTileStateSize(const dtMeshTile* tile) const
+{
+ if (!tile) return 0;
+ const int headerSize = dtAlign4(sizeof(dtTileState));
+ const int polyStateSize = dtAlign4(sizeof(dtPolyState) * tile->header->polyCount);
+ return headerSize + polyStateSize;
+}
+
+dtStatus dtNavMesh::storeTileState(const dtMeshTile* tile, unsigned char* data, const int maxDataSize) const
+{
+ // Make sure there is enough space to store the state.
+ const int sizeReq = getTileStateSize(tile);
+ if (maxDataSize < sizeReq)
+ return DT_FAILURE;
+
+ dtTileState* tileState = (dtTileState*)data; data += dtAlign4(sizeof(dtTileState));
+ dtPolyState* polyStates = (dtPolyState*)data; data += dtAlign4(sizeof(dtPolyState) * tile->header->polyCount);
+
+ // Store tile state.
+ tileState->magic = DT_NAVMESH_STATE_MAGIC;
+ tileState->version = DT_NAVMESH_STATE_VERSION;
+ tileState->ref = getTileRef(tile);
+
+ // Store per poly state.
+ for (int i = 0; i < tile->header->polyCount; ++i)
+ {
+ const dtPoly* p = &tile->polys[i];
+ dtPolyState* s = &polyStates[i];
+ s->flags = p->flags;
+ s->area = p->getArea();
+ }
+
+ return DT_SUCCESS;
+}
+
+dtStatus dtNavMesh::restoreTileState(dtMeshTile* tile, const unsigned char* data, const int maxDataSize)
+{
+ // Make sure there is enough space to store the state.
+ const int sizeReq = getTileStateSize(tile);
+ if (maxDataSize < sizeReq)
+ return DT_FAILURE;
+
+ const dtTileState* tileState = (const dtTileState*)data; data += dtAlign4(sizeof(dtTileState));
+ const dtPolyState* polyStates = (const dtPolyState*)data; data += dtAlign4(sizeof(dtPolyState) * tile->header->polyCount);
+
+ // Check that the restore is possible.
+ if (tileState->magic != DT_NAVMESH_STATE_MAGIC)
+ return DT_FAILURE_DATA_MAGIC;
+ if (tileState->version != DT_NAVMESH_STATE_VERSION)
+ return DT_FAILURE_DATA_VERSION;
+ if (tileState->ref != getTileRef(tile))
+ return DT_FAILURE;
+
+ // Restore per poly state.
+ for (int i = 0; i < tile->header->polyCount; ++i)
+ {
+ dtPoly* p = &tile->polys[i];
+ const dtPolyState* s = &polyStates[i];
+ p->flags = s->flags;
+ p->setArea(s->area);
+ }
+
+ return DT_SUCCESS;
+}
+
+// Returns start and end location of an off-mesh link polygon.
+dtStatus dtNavMesh::getOffMeshConnectionPolyEndPoints(dtPolyRef prevRef, dtPolyRef polyRef, float* startPos, float* endPos) const
+{
+ unsigned int salt, it, ip;
+
+ // Get current polygon
+ decodePolyId(polyRef, salt, it, ip);
+ if (it >= (unsigned int)m_maxTiles) return DT_FAILURE;
+ if (m_tiles[it].salt != salt || m_tiles[it].header == 0) return DT_FAILURE;
+ const dtMeshTile* tile = &m_tiles[it];
+ if (ip >= (unsigned int)tile->header->polyCount) return DT_FAILURE;
+ const dtPoly* poly = &tile->polys[ip];
+
+ // Make sure that the current poly is indeed off-mesh link.
+ if (poly->getType() != DT_POLYTYPE_OFFMESH_CONNECTION)
+ return DT_FAILURE;
+
+ // Figure out which way to hand out the vertices.
+ int idx0 = 0, idx1 = 1;
+
+ // Find link that points to first vertex.
+ for (unsigned int i = poly->firstLink; i != DT_NULL_LINK; i = tile->links[i].next)
+ {
+ if (tile->links[i].edge == 0)
+ {
+ if (tile->links[i].ref != prevRef)
+ {
+ idx0 = 1;
+ idx1 = 0;
+ }
+ break;
+ }
+ }
+
+ dtVcopy(startPos, &tile->verts[poly->verts[idx0]*3]);
+ dtVcopy(endPos, &tile->verts[poly->verts[idx1]*3]);
+
+ return DT_SUCCESS;
+}
+
+
+const dtOffMeshConnection* dtNavMesh::getOffMeshConnectionByRef(dtPolyRef ref) const
+{
+ unsigned int salt, it, ip;
+
+ // Get current polygon
+ decodePolyId(ref, salt, it, ip);
+ if (it >= (unsigned int)m_maxTiles) return 0;
+ if (m_tiles[it].salt != salt || m_tiles[it].header == 0) return 0;
+ const dtMeshTile* tile = &m_tiles[it];
+ if (ip >= (unsigned int)tile->header->polyCount) return 0;
+ const dtPoly* poly = &tile->polys[ip];
+
+ // Make sure that the current poly is indeed off-mesh link.
+ if (poly->getType() != DT_POLYTYPE_OFFMESH_CONNECTION)
+ return 0;
+
+ const unsigned int idx = ip - tile->header->offMeshBase;
+ dtAssert(idx < (unsigned int)tile->header->offMeshConCount);
+ return &tile->offMeshCons[idx];
+}
+
+
+dtStatus dtNavMesh::setPolyFlags(dtPolyRef ref, unsigned short flags)
+{
+ unsigned int salt, it, ip;
+ decodePolyId(ref, salt, it, ip);
+ if (it >= (unsigned int)m_maxTiles) return DT_FAILURE;
+ if (m_tiles[it].salt != salt || m_tiles[it].header == 0) return DT_FAILURE;
+ dtMeshTile* tile = &m_tiles[it];
+ if (ip >= (unsigned int)tile->header->polyCount) return DT_FAILURE;
+ dtPoly* poly = &tile->polys[ip];
+
+ // Change flags.
+ poly->flags = flags;
+
+ return DT_SUCCESS;
+}
+
+dtStatus dtNavMesh::getPolyFlags(dtPolyRef ref, unsigned short* resultFlags) const
+{
+ unsigned int salt, it, ip;
+ decodePolyId(ref, salt, it, ip);
+ if (it >= (unsigned int)m_maxTiles) return DT_FAILURE;
+ if (m_tiles[it].salt != salt || m_tiles[it].header == 0) return DT_FAILURE;
+ const dtMeshTile* tile = &m_tiles[it];
+ if (ip >= (unsigned int)tile->header->polyCount) return DT_FAILURE;
+ const dtPoly* poly = &tile->polys[ip];
+
+ *resultFlags = poly->flags;
+
+ return DT_SUCCESS;
+}
+
+dtStatus dtNavMesh::setPolyArea(dtPolyRef ref, unsigned char area)
+{
+ unsigned int salt, it, ip;
+ decodePolyId(ref, salt, it, ip);
+ if (it >= (unsigned int)m_maxTiles) return DT_FAILURE;
+ if (m_tiles[it].salt != salt || m_tiles[it].header == 0) return DT_FAILURE;
+ dtMeshTile* tile = &m_tiles[it];
+ if (ip >= (unsigned int)tile->header->polyCount) return DT_FAILURE;
+ dtPoly* poly = &tile->polys[ip];
+
+ poly->setArea(area);
+
+ return DT_SUCCESS;
+}
+
+dtStatus dtNavMesh::getPolyArea(dtPolyRef ref, unsigned char* resultArea) const
+{
+ unsigned int salt, it, ip;
+ decodePolyId(ref, salt, it, ip);
+ if (it >= (unsigned int)m_maxTiles) return DT_FAILURE;
+ if (m_tiles[it].salt != salt || m_tiles[it].header == 0) return DT_FAILURE;
+ const dtMeshTile* tile = &m_tiles[it];
+ if (ip >= (unsigned int)tile->header->polyCount) return DT_FAILURE;
+ const dtPoly* poly = &tile->polys[ip];
+
+ *resultArea = poly->getArea();
+
+ return DT_SUCCESS;
+}
+
diff --git a/dep/recastnavigation/Detour/DetourNavMesh.h b/dep/recastnavigation/Detour/DetourNavMesh.h
new file mode 100644
index 0000000..52d2c50
--- /dev/null
+++ b/dep/recastnavigation/Detour/DetourNavMesh.h
@@ -0,0 +1,428 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#ifndef DETOURNAVMESH_H
+#define DETOURNAVMESH_H
+
+#include "DetourAlloc.h"
+
+#ifdef WIN32
+ typedef unsigned __int64 uint64;
+#else
+#include <stdint.h>
+#ifndef uint64_t
+#ifdef __linux__
+#include <linux/types.h>
+#endif
+#endif
+ typedef uint64_t uint64;
+#endif
+
+// Note: If you want to use 64-bit refs, change the types of both dtPolyRef & dtTileRef.
+// It is also recommended to change dtHashRef() to proper 64-bit hash too.
+
+// Reference to navigation polygon.
+typedef uint64 dtPolyRef;
+
+// Reference to navigation mesh tile.
+typedef uint64 dtTileRef;
+
+// Maximum number of vertices per navigation polygon.
+static const int DT_VERTS_PER_POLYGON = 6;
+
+static const int DT_NAVMESH_MAGIC = 'D'<<24 | 'N'<<16 | 'A'<<8 | 'V'; //'DNAV';
+static const int DT_NAVMESH_VERSION = 6;
+
+static const int DT_NAVMESH_STATE_MAGIC = 'D'<<24 | 'N'<<16 | 'M'<<8 | 'S'; //'DNMS';
+static const int DT_NAVMESH_STATE_VERSION = 1;
+
+static const unsigned short DT_EXT_LINK = 0x8000;
+static const unsigned int DT_NULL_LINK = 0xffffffff;
+static const unsigned int DT_OFFMESH_CON_BIDIR = 1;
+
+static const int DT_MAX_AREAS = 64;
+
+static const int STATIC_SALT_BITS = 12;
+static const int STATIC_TILE_BITS = 21;
+static const int STATIC_POLY_BITS = 31;
+// we cannot have over 31 bits for either tile nor poly
+// without changing polyCount to use 64bits too.
+
+// Flags for addTile
+enum dtTileFlags
+{
+ DT_TILE_FREE_DATA = 0x01, // Navmesh owns the tile memory and should free it.
+};
+
+// Flags returned by findStraightPath().
+enum dtStraightPathFlags
+{
+ DT_STRAIGHTPATH_START = 0x01, // The vertex is the start position.
+ DT_STRAIGHTPATH_END = 0x02, // The vertex is the end position.
+ DT_STRAIGHTPATH_OFFMESH_CONNECTION = 0x04, // The vertex is start of an off-mesh link.
+};
+
+// Flags describing polygon properties.
+enum dtPolyTypes
+{
+ DT_POLYTYPE_GROUND = 0, // Regular ground polygons.
+ DT_POLYTYPE_OFFMESH_CONNECTION = 1, // Off-mesh connections.
+};
+
+enum dtStatus
+{
+ DT_FAILURE = 0, // Operation failed.
+ DT_FAILURE_DATA_MAGIC,
+ DT_FAILURE_DATA_VERSION,
+ DT_FAILURE_OUT_OF_MEMORY,
+ DT_SUCCESS, // Operation succeed.
+ DT_IN_PROGRESS, // Operation still in progress.
+};
+
+
+// Structure describing the navigation polygon data.
+struct dtPoly
+{
+ unsigned int firstLink; // Index to first link in linked list.
+ unsigned short verts[DT_VERTS_PER_POLYGON]; // Indices to vertices of the poly.
+ unsigned short neis[DT_VERTS_PER_POLYGON]; // Refs to neighbours of the poly.
+ unsigned short flags; // Flags (see dtPolyFlags).
+ unsigned char vertCount; // Number of vertices.
+ unsigned char areaAndtype; // Bit packed: Area ID of the polygon, and Polygon type, see dtPolyTypes..
+ inline void setArea(unsigned char a) { areaAndtype = (areaAndtype & 0xc0) | (a & 0x3f); }
+ inline void setType(unsigned char t) { areaAndtype = (areaAndtype & 0x3f) | (t << 6); }
+ inline unsigned char getArea() const { return areaAndtype & 0x3f; }
+ inline unsigned char getType() const { return areaAndtype >> 6; }
+};
+
+// Stucture describing polygon detail triangles.
+struct dtPolyDetail
+{
+ unsigned int vertBase; // Offset to detail vertex array.
+ unsigned int triBase; // Offset to detail triangle array.
+ unsigned char vertCount; // Number of vertices in the detail mesh.
+ unsigned char triCount; // Number of triangles.
+};
+
+// Stucture describing a link to another polygon.
+struct dtLink
+{
+ dtPolyRef ref; // Neighbour reference.
+ unsigned int next; // Index to next link.
+ unsigned char edge; // Index to polygon edge which owns this link.
+ unsigned char side; // If boundary link, defines on which side the link is.
+ unsigned char bmin, bmax; // If boundary link, defines the sub edge area.
+};
+
+struct dtBVNode
+{
+ unsigned short bmin[3], bmax[3]; // BVnode bounds
+ int i; // Index to item or if negative, escape index.
+};
+
+struct dtOffMeshConnection
+{
+ float pos[6]; // Both end point locations.
+ float rad; // Link connection radius.
+ unsigned short poly; // Poly Id
+ unsigned char flags; // Link flags
+ unsigned char side; // End point side.
+ unsigned int userId; // User ID to identify this connection.
+};
+
+struct dtMeshHeader
+{
+ int magic; // Magic number, used to identify the data.
+ int version; // Data version number.
+ int x, y; // Location of the time on the grid.
+ unsigned int userId; // User ID of the tile.
+ int polyCount; // Number of polygons in the tile.
+ int vertCount; // Number of vertices in the tile.
+ int maxLinkCount; // Number of allocated links.
+ int detailMeshCount; // Number of detail meshes.
+ int detailVertCount; // Number of detail vertices.
+ int detailTriCount; // Number of detail triangles.
+ int bvNodeCount; // Number of BVtree nodes.
+ int offMeshConCount; // Number of Off-Mesh links.
+ int offMeshBase; // Index to first polygon which is Off-Mesh link.
+ float walkableHeight; // Height of the agent.
+ float walkableRadius; // Radius of the agent
+ float walkableClimb; // Max climb height of the agent.
+ float bmin[3], bmax[3]; // Bounding box of the tile.
+ float bvQuantFactor; // BVtree quantization factor (world to bvnode coords)
+};
+
+struct dtMeshTile
+{
+ unsigned int salt; // Counter describing modifications to the tile.
+
+ unsigned int linksFreeList; // Index to next free link.
+ dtMeshHeader* header; // Pointer to tile header.
+ dtPoly* polys; // Pointer to the polygons (will be updated when tile is added).
+ float* verts; // Pointer to the vertices (will be updated when tile added).
+ dtLink* links; // Pointer to the links (will be updated when tile added).
+ dtPolyDetail* detailMeshes; // Pointer to detail meshes (will be updated when tile added).
+ float* detailVerts; // Pointer to detail vertices (will be updated when tile added).
+ unsigned char* detailTris; // Pointer to detail triangles (will be updated when tile added).
+ dtBVNode* bvTree; // Pointer to BVtree nodes (will be updated when tile added).
+ dtOffMeshConnection* offMeshCons; // Pointer to Off-Mesh links. (will be updated when tile added).
+
+ unsigned char* data; // Pointer to tile data.
+ int dataSize; // Size of the tile data.
+ int flags; // Tile flags, see dtTileFlags.
+ dtMeshTile* next; // Next free tile or, next tile in spatial grid.
+};
+
+struct dtNavMeshParams
+{
+ float orig[3]; // Origin of the nav mesh tile space.
+ float tileWidth, tileHeight; // Width and height of each tile.
+ int maxTiles; // Maximum number of tiles the navmesh can contain.
+ int maxPolys; // Maximum number of polygons each tile can contain.
+};
+
+
+class dtNavMesh
+{
+public:
+ dtNavMesh();
+ ~dtNavMesh();
+
+ // Initializes the nav mesh for tiled use.
+ // Params:
+ // params - (in) navmesh initialization params, see dtNavMeshParams.
+ // Returns: True if succeed, else false.
+ dtStatus init(const dtNavMeshParams* params);
+
+ // Initializes the nav mesh for single tile use.
+ // Params:
+ // data - (in) Data of the new tile mesh.
+ // dataSize - (in) Data size of the new tile mesh.
+ // flags - (in) Tile flags, see dtTileFlags.
+ // Returns: True if succeed, else false.
+ dtStatus init(unsigned char* data, const int dataSize, const int flags);
+
+ // Returns pointer to navmesh initialization params.
+ const dtNavMeshParams* getParams() const;
+
+ // Adds new tile into the navmesh.
+ // The add will fail if the data is in wrong format,
+ // there is not enough tiles left, or if there is a tile already at the location.
+ // Params:
+ // data - (in) Data of the new tile mesh.
+ // dataSize - (in) Data size of the new tile mesh.
+ // flags - (in) Tile flags, see dtTileFlags.
+ // lastRef - (in,optional) Last tile ref, the tile will be restored so that
+ // the reference (as well as poly references) will be the same. Default: 0.
+ // result - (out,optional) tile ref if the tile was succesfully added.
+ dtStatus addTile(unsigned char* data, int dataSize, int flags, dtTileRef lastRef, dtTileRef* result);
+
+ // Removes specified tile.
+ // Params:
+ // ref - (in) Reference to the tile to remove.
+ // data - (out) Data associated with deleted tile.
+ // dataSize - (out) Size of the data associated with deleted tile.
+ dtStatus removeTile(dtTileRef ref, unsigned char** data, int* dataSize);
+
+ // Calculates tile location based in input world position.
+ // Params:
+ // pos - (in) world position of the query.
+ // tx - (out) tile x location.
+ // ty - (out) tile y location.
+ void calcTileLoc(const float* pos, int* tx, int* ty) const;
+
+ // Returns pointer to tile at specified location.
+ // Params:
+ // x,y - (in) Location of the tile to get.
+ // Returns: pointer to tile if tile exists or 0 tile does not exists.
+ const dtMeshTile* getTileAt(int x, int y) const;
+
+ // Returns reference to tile at specified location.
+ // Params:
+ // x,y - (in) Location of the tile to get.
+ // Returns: reference to tile if tile exists or 0 tile does not exists.
+ dtTileRef getTileRefAt(int x, int y) const;
+
+ // Returns tile references of a tile based on tile pointer.
+ dtTileRef getTileRef(const dtMeshTile* tile) const;
+
+ // Returns tile based on references.
+ const dtMeshTile* getTileByRef(dtTileRef ref) const;
+
+ // Returns max number of tiles.
+ int getMaxTiles() const;
+
+ // Returns pointer to tile in the tile array.
+ // Params:
+ // i - (in) Index to the tile to retrieve, max index is getMaxTiles()-1.
+ // Returns: Pointer to specified tile.
+ const dtMeshTile* getTile(int i) const;
+
+ // Returns pointer to tile and polygon pointed by the polygon reference.
+ // Params:
+ // ref - (in) reference to a polygon.
+ // tile - (out) pointer to the tile containing the polygon.
+ // poly - (out) pointer to the polygon.
+ dtStatus getTileAndPolyByRef(const dtPolyRef ref, const dtMeshTile** tile, const dtPoly** poly) const;
+
+ // Returns pointer to tile and polygon pointed by the polygon reference.
+ // Note: this function does not check if 'ref' s valid, and is thus faster. Use only with valid refs!
+ // Params:
+ // ref - (in) reference to a polygon.
+ // tile - (out) pointer to the tile containing the polygon.
+ // poly - (out) pointer to the polygon.
+ void getTileAndPolyByRefUnsafe(const dtPolyRef ref, const dtMeshTile** tile, const dtPoly** poly) const;
+
+ // Returns true if polygon reference points to valid data.
+ bool isValidPolyRef(dtPolyRef ref) const;
+
+ // Returns base poly id for specified tile, polygon refs can be deducted from this.
+ dtPolyRef getPolyRefBase(const dtMeshTile* tile) const;
+
+ // Returns start and end location of an off-mesh link polygon.
+ // Params:
+ // prevRef - (in) ref to the polygon before the link (used to select direction).
+ // polyRef - (in) ref to the off-mesh link polygon.
+ // startPos[3] - (out) start point of the link.
+ // endPos[3] - (out) end point of the link.
+ // Returns: true if link is found.
+ dtStatus getOffMeshConnectionPolyEndPoints(dtPolyRef prevRef, dtPolyRef polyRef, float* startPos, float* endPos) const;
+
+ // Returns pointer to off-mesh connection based on polyref, or null if ref not valid.
+ const dtOffMeshConnection* getOffMeshConnectionByRef(dtPolyRef ref) const;
+
+ // Sets polygon flags.
+ dtStatus setPolyFlags(dtPolyRef ref, unsigned short flags);
+
+ // Return polygon flags.
+ dtStatus getPolyFlags(dtPolyRef ref, unsigned short* resultFlags) const;
+
+ // Set polygon type.
+ dtStatus setPolyArea(dtPolyRef ref, unsigned char area);
+
+ // Return polygon area type.
+ dtStatus getPolyArea(dtPolyRef ref, unsigned char* resultArea) const;
+
+
+ // Returns number of bytes required to store tile state.
+ int getTileStateSize(const dtMeshTile* tile) const;
+
+ // Stores tile state to buffer.
+ dtStatus storeTileState(const dtMeshTile* tile, unsigned char* data, const int maxDataSize) const;
+
+ // Restores tile state.
+ dtStatus restoreTileState(dtMeshTile* tile, const unsigned char* data, const int maxDataSize);
+
+
+ // Encodes a tile id.
+ inline dtPolyRef encodePolyId(unsigned int salt, unsigned int it, unsigned int ip) const
+ {
+ return ((dtPolyRef)salt << (m_polyBits+m_tileBits)) | ((dtPolyRef)it << m_polyBits) | (dtPolyRef)ip;
+ }
+
+ // Decodes a tile id.
+ inline void decodePolyId(dtPolyRef ref, unsigned int& salt, unsigned int& it, unsigned int& ip) const
+ {
+ const dtPolyRef saltMask = ((dtPolyRef)1<<m_saltBits)-1;
+ const dtPolyRef tileMask = ((dtPolyRef)1<<m_tileBits)-1;
+ const dtPolyRef polyMask = ((dtPolyRef)1<<m_polyBits)-1;
+ salt = (unsigned int)((ref >> (m_polyBits+m_tileBits)) & saltMask);
+ it = (unsigned int)((ref >> m_polyBits) & tileMask);
+ ip = (unsigned int)(ref & polyMask);
+ }
+
+ // Decodes a tile salt.
+ inline unsigned int decodePolyIdSalt(dtPolyRef ref) const
+ {
+ const dtPolyRef saltMask = ((dtPolyRef)1<<m_saltBits)-1;
+ return (unsigned int)((ref >> (m_polyBits+m_tileBits)) & saltMask);
+ }
+
+ // Decodes a tile id.
+ inline unsigned int decodePolyIdTile(dtPolyRef ref) const
+ {
+ const dtPolyRef tileMask = ((dtPolyRef)1<<m_tileBits)-1;
+ return (unsigned int)((ref >> m_polyBits) & tileMask);
+ }
+
+ // Decodes a poly id.
+ inline unsigned int decodePolyIdPoly(dtPolyRef ref) const
+ {
+ const dtPolyRef polyMask = ((dtPolyRef)1<<m_polyBits)-1;
+ return (unsigned int)(ref & polyMask);
+ }
+
+private:
+
+ // Returns pointer to tile in the tile array.
+ dtMeshTile* getTile(int i);
+
+ // Returns neighbour tile based on side.
+ dtMeshTile* getNeighbourTileAt(int x, int y, int side) const;
+ // Returns all polygons in neighbour tile based on portal defined by the segment.
+ int findConnectingPolys(const float* va, const float* vb,
+ const dtMeshTile* tile, int side,
+ dtPolyRef* con, float* conarea, int maxcon) const;
+
+ // Builds internal polygons links for a tile.
+ void connectIntLinks(dtMeshTile* tile);
+ // Builds internal polygons links for a tile.
+ void connectIntOffMeshLinks(dtMeshTile* tile);
+
+ // Builds external polygon links for a tile.
+ void connectExtLinks(dtMeshTile* tile, dtMeshTile* target, int side);
+ // Builds external polygon links for a tile.
+ void connectExtOffMeshLinks(dtMeshTile* tile, dtMeshTile* target, int side);
+
+ // Removes external links at specified side.
+ void unconnectExtLinks(dtMeshTile* tile, int side);
+
+
+ // TODO: These methods are duplicates from dtNavMeshQuery, but are needed for off-mesh connection finding.
+
+ // Queries polygons within a tile.
+ int queryPolygonsInTile(const dtMeshTile* tile, const float* qmin, const float* qmax,
+ dtPolyRef* polys, const int maxPolys) const;
+ // Find nearest polygon within a tile.
+ dtPolyRef findNearestPolyInTile(const dtMeshTile* tile, const float* center,
+ const float* extents, float* nearestPt) const;
+ // Returns closest point on polygon.
+ dtStatus closestPointOnPolyInTile(const dtMeshTile* tile, unsigned int ip,
+ const float* pos, float* closest) const;
+
+ dtNavMeshParams m_params; // Current initialization params. TODO: do not store this info twice.
+ float m_orig[3]; // Origin of the tile (0,0)
+ float m_tileWidth, m_tileHeight; // Dimensions of each tile.
+ int m_maxTiles; // Max number of tiles.
+ int m_tileLutSize; // Tile hash lookup size (must be pot).
+ int m_tileLutMask; // Tile hash lookup mask.
+
+ dtMeshTile** m_posLookup; // Tile hash lookup.
+ dtMeshTile* m_nextFree; // Freelist of tiles.
+ dtMeshTile* m_tiles; // List of tiles.
+
+ unsigned int m_saltBits; // Number of salt bits in the tile ID.
+ unsigned int m_tileBits; // Number of tile bits in the tile ID.
+ unsigned int m_polyBits; // Number of poly bits in the tile ID.
+};
+
+// Helper function to allocate navmesh class using Detour allocator.
+dtNavMesh* dtAllocNavMesh();
+void dtFreeNavMesh(dtNavMesh* navmesh);
+
+#endif // DETOURNAVMESH_H
diff --git a/dep/recastnavigation/Detour/DetourNavMeshBuilder.cpp b/dep/recastnavigation/Detour/DetourNavMeshBuilder.cpp
new file mode 100644
index 0000000..f648571
--- /dev/null
+++ b/dep/recastnavigation/Detour/DetourNavMeshBuilder.cpp
@@ -0,0 +1,717 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "DetourNavMesh.h"
+#include "DetourCommon.h"
+#include "DetourNavMeshBuilder.h"
+#include "DetourAlloc.h"
+#include "DetourAssert.h"
+
+static unsigned short MESH_NULL_IDX = 0xffff;
+
+
+struct BVItem
+{
+ unsigned short bmin[3];
+ unsigned short bmax[3];
+ int i;
+};
+
+static int compareItemX(const void* va, const void* vb)
+{
+ const BVItem* a = (const BVItem*)va;
+ const BVItem* b = (const BVItem*)vb;
+ if (a->bmin[0] < b->bmin[0])
+ return -1;
+ if (a->bmin[0] > b->bmin[0])
+ return 1;
+ return 0;
+}
+
+static int compareItemY(const void* va, const void* vb)
+{
+ const BVItem* a = (const BVItem*)va;
+ const BVItem* b = (const BVItem*)vb;
+ if (a->bmin[1] < b->bmin[1])
+ return -1;
+ if (a->bmin[1] > b->bmin[1])
+ return 1;
+ return 0;
+}
+
+static int compareItemZ(const void* va, const void* vb)
+{
+ const BVItem* a = (const BVItem*)va;
+ const BVItem* b = (const BVItem*)vb;
+ if (a->bmin[2] < b->bmin[2])
+ return -1;
+ if (a->bmin[2] > b->bmin[2])
+ return 1;
+ return 0;
+}
+
+static void calcExtends(BVItem* items, const int /*nitems*/, const int imin, const int imax,
+ unsigned short* bmin, unsigned short* bmax)
+{
+ bmin[0] = items[imin].bmin[0];
+ bmin[1] = items[imin].bmin[1];
+ bmin[2] = items[imin].bmin[2];
+
+ bmax[0] = items[imin].bmax[0];
+ bmax[1] = items[imin].bmax[1];
+ bmax[2] = items[imin].bmax[2];
+
+ for (int i = imin+1; i < imax; ++i)
+ {
+ const BVItem& it = items[i];
+ if (it.bmin[0] < bmin[0]) bmin[0] = it.bmin[0];
+ if (it.bmin[1] < bmin[1]) bmin[1] = it.bmin[1];
+ if (it.bmin[2] < bmin[2]) bmin[2] = it.bmin[2];
+
+ if (it.bmax[0] > bmax[0]) bmax[0] = it.bmax[0];
+ if (it.bmax[1] > bmax[1]) bmax[1] = it.bmax[1];
+ if (it.bmax[2] > bmax[2]) bmax[2] = it.bmax[2];
+ }
+}
+
+inline int longestAxis(unsigned short x, unsigned short y, unsigned short z)
+{
+ int axis = 0;
+ unsigned short maxVal = x;
+ if (y > maxVal)
+ {
+ axis = 1;
+ maxVal = y;
+ }
+ if (z > maxVal)
+ {
+ axis = 2;
+ maxVal = z;
+ }
+ return axis;
+}
+
+static void subdivide(BVItem* items, int nitems, int imin, int imax, int& curNode, dtBVNode* nodes)
+{
+ int inum = imax - imin;
+ int icur = curNode;
+
+ dtBVNode& node = nodes[curNode++];
+
+ if (inum == 1)
+ {
+ // Leaf
+ node.bmin[0] = items[imin].bmin[0];
+ node.bmin[1] = items[imin].bmin[1];
+ node.bmin[2] = items[imin].bmin[2];
+
+ node.bmax[0] = items[imin].bmax[0];
+ node.bmax[1] = items[imin].bmax[1];
+ node.bmax[2] = items[imin].bmax[2];
+
+ node.i = items[imin].i;
+ }
+ else
+ {
+ // Split
+ calcExtends(items, nitems, imin, imax, node.bmin, node.bmax);
+
+ int axis = longestAxis(node.bmax[0] - node.bmin[0],
+ node.bmax[1] - node.bmin[1],
+ node.bmax[2] - node.bmin[2]);
+
+ if (axis == 0)
+ {
+ // Sort along x-axis
+ qsort(items+imin, inum, sizeof(BVItem), compareItemX);
+ }
+ else if (axis == 1)
+ {
+ // Sort along y-axis
+ qsort(items+imin, inum, sizeof(BVItem), compareItemY);
+ }
+ else
+ {
+ // Sort along z-axis
+ qsort(items+imin, inum, sizeof(BVItem), compareItemZ);
+ }
+
+ int isplit = imin+inum/2;
+
+ // Left
+ subdivide(items, nitems, imin, isplit, curNode, nodes);
+ // Right
+ subdivide(items, nitems, isplit, imax, curNode, nodes);
+
+ int iescape = curNode - icur;
+ // Negative index means escape.
+ node.i = -iescape;
+ }
+}
+
+static int createBVTree(const unsigned short* verts, const int /*nverts*/,
+ const unsigned short* polys, const int npolys, const int nvp,
+ const float cs, const float ch,
+ const int /*nnodes*/, dtBVNode* nodes)
+{
+ // Build tree
+ BVItem* items = (BVItem*)dtAlloc(sizeof(BVItem)*npolys, DT_ALLOC_TEMP);
+ for (int i = 0; i < npolys; i++)
+ {
+ BVItem& it = items[i];
+ it.i = i;
+ // Calc polygon bounds.
+ const unsigned short* p = &polys[i*nvp*2];
+ it.bmin[0] = it.bmax[0] = verts[p[0]*3+0];
+ it.bmin[1] = it.bmax[1] = verts[p[0]*3+1];
+ it.bmin[2] = it.bmax[2] = verts[p[0]*3+2];
+
+ for (int j = 1; j < nvp; ++j)
+ {
+ if (p[j] == MESH_NULL_IDX) break;
+ unsigned short x = verts[p[j]*3+0];
+ unsigned short y = verts[p[j]*3+1];
+ unsigned short z = verts[p[j]*3+2];
+
+ if (x < it.bmin[0]) it.bmin[0] = x;
+ if (y < it.bmin[1]) it.bmin[1] = y;
+ if (z < it.bmin[2]) it.bmin[2] = z;
+
+ if (x > it.bmax[0]) it.bmax[0] = x;
+ if (y > it.bmax[1]) it.bmax[1] = y;
+ if (z > it.bmax[2]) it.bmax[2] = z;
+ }
+ // Remap y
+ it.bmin[1] = (unsigned short)floorf((float)it.bmin[1]*ch/cs);
+ it.bmax[1] = (unsigned short)ceilf((float)it.bmax[1]*ch/cs);
+ }
+
+ int curNode = 0;
+ subdivide(items, npolys, 0, npolys, curNode, nodes);
+
+ dtFree(items);
+
+ return curNode;
+}
+
+static unsigned char classifyOffMeshPoint(const float* pt, const float* bmin, const float* bmax)
+{
+ static const unsigned char XP = 1<<0;
+ static const unsigned char ZP = 1<<1;
+ static const unsigned char XM = 1<<2;
+ static const unsigned char ZM = 1<<3;
+
+ unsigned char outcode = 0;
+ outcode |= (pt[0] >= bmax[0]) ? XP : 0;
+ outcode |= (pt[2] >= bmax[2]) ? ZP : 0;
+ outcode |= (pt[0] < bmin[0]) ? XM : 0;
+ outcode |= (pt[2] < bmin[2]) ? ZM : 0;
+
+ switch (outcode)
+ {
+ case XP: return 0;
+ case XP|ZP: return 1;
+ case ZP: return 2;
+ case XM|ZP: return 3;
+ case XM: return 4;
+ case XM|ZM: return 5;
+ case ZM: return 6;
+ case XP|ZM: return 7;
+ };
+ return 0xff;
+}
+
+// TODO: Better error handling.
+
+bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData, int* outDataSize)
+{
+ if (params->nvp > DT_VERTS_PER_POLYGON)
+ return false;
+ if (params->vertCount >= 0xffff)
+ return false;
+ if (!params->vertCount || !params->verts)
+ return false;
+ if (!params->polyCount || !params->polys)
+ return false;
+ if (!params->detailMeshes || !params->detailVerts || !params->detailTris)
+ return false;
+
+ const int nvp = params->nvp;
+
+ // Classify off-mesh connection points. We store only the connections
+ // whose start point is inside the tile.
+ unsigned char* offMeshConClass = 0;
+ int storedOffMeshConCount = 0;
+ int offMeshConLinkCount = 0;
+
+ if (params->offMeshConCount > 0)
+ {
+ offMeshConClass = (unsigned char*)dtAlloc(sizeof(unsigned char)*params->offMeshConCount*2, DT_ALLOC_TEMP);
+ if (!offMeshConClass)
+ return false;
+
+ for (int i = 0; i < params->offMeshConCount; ++i)
+ {
+ offMeshConClass[i*2+0] = classifyOffMeshPoint(&params->offMeshConVerts[(i*2+0)*3], params->bmin, params->bmax);
+ offMeshConClass[i*2+1] = classifyOffMeshPoint(&params->offMeshConVerts[(i*2+1)*3], params->bmin, params->bmax);
+
+ // Cound how many links should be allocated for off-mesh connections.
+ if (offMeshConClass[i*2+0] == 0xff)
+ offMeshConLinkCount++;
+ if (offMeshConClass[i*2+1] == 0xff)
+ offMeshConLinkCount++;
+
+ if (offMeshConClass[i*2+0] == 0xff)
+ storedOffMeshConCount++;
+ }
+ }
+
+ // Off-mesh connectionss are stored as polygons, adjust values.
+ const int totPolyCount = params->polyCount + storedOffMeshConCount;
+ const int totVertCount = params->vertCount + storedOffMeshConCount*2;
+
+ // Find portal edges which are at tile borders.
+ int edgeCount = 0;
+ int portalCount = 0;
+ for (int i = 0; i < params->polyCount; ++i)
+ {
+ const unsigned short* p = &params->polys[i*2*nvp];
+ for (int j = 0; j < nvp; ++j)
+ {
+ if (p[j] == MESH_NULL_IDX) break;
+ int nj = j+1;
+ if (nj >= nvp || p[nj] == MESH_NULL_IDX) nj = 0;
+ const unsigned short* va = &params->verts[p[j]*3];
+ const unsigned short* vb = &params->verts[p[nj]*3];
+
+ edgeCount++;
+
+ if (params->tileSize > 0)
+ {
+ if (va[0] == params->tileSize && vb[0] == params->tileSize)
+ portalCount++; // x+
+ else if (va[2] == params->tileSize && vb[2] == params->tileSize)
+ portalCount++; // z+
+ else if (va[0] == 0 && vb[0] == 0)
+ portalCount++; // x-
+ else if (va[2] == 0 && vb[2] == 0)
+ portalCount++; // z-
+ }
+ }
+ }
+
+ const int maxLinkCount = edgeCount + portalCount*2 + offMeshConLinkCount*2;
+
+ // Find unique detail vertices.
+ int uniqueDetailVertCount = 0;
+ for (int i = 0; i < params->polyCount; ++i)
+ {
+ const unsigned short* p = &params->polys[i*nvp*2];
+ int ndv = params->detailMeshes[i*4+1];
+ int nv = 0;
+ for (int j = 0; j < nvp; ++j)
+ {
+ if (p[j] == MESH_NULL_IDX) break;
+ nv++;
+ }
+ ndv -= nv;
+ uniqueDetailVertCount += ndv;
+ }
+
+ // Calculate data size
+ const int headerSize = dtAlign4(sizeof(dtMeshHeader));
+ const int vertsSize = dtAlign4(sizeof(float)*3*totVertCount);
+ const int polysSize = dtAlign4(sizeof(dtPoly)*totPolyCount);
+ const int linksSize = dtAlign4(sizeof(dtLink)*maxLinkCount);
+ const int detailMeshesSize = dtAlign4(sizeof(dtPolyDetail)*params->polyCount);
+ const int detailVertsSize = dtAlign4(sizeof(float)*3*uniqueDetailVertCount);
+ const int detailTrisSize = dtAlign4(sizeof(unsigned char)*4*params->detailTriCount);
+ const int bvTreeSize = dtAlign4(sizeof(dtBVNode)*params->polyCount*2);
+ const int offMeshConsSize = dtAlign4(sizeof(dtOffMeshConnection)*storedOffMeshConCount);
+
+ const int dataSize = headerSize + vertsSize + polysSize + linksSize +
+ detailMeshesSize + detailVertsSize + detailTrisSize +
+ bvTreeSize + offMeshConsSize;
+
+ unsigned char* data = (unsigned char*)dtAlloc(sizeof(unsigned char)*dataSize, DT_ALLOC_PERM);
+ if (!data)
+ {
+ dtFree(offMeshConClass);
+ return false;
+ }
+ memset(data, 0, dataSize);
+
+ unsigned char* d = data;
+ dtMeshHeader* header = (dtMeshHeader*)d; d += headerSize;
+ float* navVerts = (float*)d; d += vertsSize;
+ dtPoly* navPolys = (dtPoly*)d; d += polysSize;
+ d += linksSize;
+ dtPolyDetail* navDMeshes = (dtPolyDetail*)d; d += detailMeshesSize;
+ float* navDVerts = (float*)d; d += detailVertsSize;
+ unsigned char* navDTris = (unsigned char*)d; d += detailTrisSize;
+ dtBVNode* navBvtree = (dtBVNode*)d; d += bvTreeSize;
+ dtOffMeshConnection* offMeshCons = (dtOffMeshConnection*)d; d += offMeshConsSize;
+
+
+ // Store header
+ header->magic = DT_NAVMESH_MAGIC;
+ header->version = DT_NAVMESH_VERSION;
+ header->x = params->tileX;
+ header->y = params->tileY;
+ header->userId = params->userId;
+ header->polyCount = totPolyCount;
+ header->vertCount = totVertCount;
+ header->maxLinkCount = maxLinkCount;
+ dtVcopy(header->bmin, params->bmin);
+ dtVcopy(header->bmax, params->bmax);
+ header->detailMeshCount = params->polyCount;
+ header->detailVertCount = uniqueDetailVertCount;
+ header->detailTriCount = params->detailTriCount;
+ header->bvQuantFactor = 1.0f / params->cs;
+ header->offMeshBase = params->polyCount;
+ header->walkableHeight = params->walkableHeight;
+ header->walkableRadius = params->walkableRadius;
+ header->walkableClimb = params->walkableClimb;
+ header->offMeshConCount = storedOffMeshConCount;
+ header->bvNodeCount = params->polyCount*2;
+
+ const int offMeshVertsBase = params->vertCount;
+ const int offMeshPolyBase = params->polyCount;
+
+ // Store vertices
+ // Mesh vertices
+ for (int i = 0; i < params->vertCount; ++i)
+ {
+ const unsigned short* iv = &params->verts[i*3];
+ float* v = &navVerts[i*3];
+ v[0] = params->bmin[0] + iv[0] * params->cs;
+ v[1] = params->bmin[1] + iv[1] * params->ch;
+ v[2] = params->bmin[2] + iv[2] * params->cs;
+ }
+ // Off-mesh link vertices.
+ int n = 0;
+ for (int i = 0; i < params->offMeshConCount; ++i)
+ {
+ // Only store connections which start from this tile.
+ if (offMeshConClass[i*2+0] == 0xff)
+ {
+ const float* linkv = &params->offMeshConVerts[i*2*3];
+ float* v = &navVerts[(offMeshVertsBase + n*2)*3];
+ dtVcopy(&v[0], &linkv[0]);
+ dtVcopy(&v[3], &linkv[3]);
+ n++;
+ }
+ }
+
+ // Store polygons
+ // Mesh polys
+ const unsigned short* src = params->polys;
+ for (int i = 0; i < params->polyCount; ++i)
+ {
+ dtPoly* p = &navPolys[i];
+ p->vertCount = 0;
+ p->flags = params->polyFlags[i];
+ p->setArea(params->polyAreas[i]);
+ p->setType(DT_POLYTYPE_GROUND);
+ for (int j = 0; j < nvp; ++j)
+ {
+ if (src[j] == MESH_NULL_IDX) break;
+ p->verts[j] = src[j];
+ p->neis[j] = (src[nvp+j]+1) & 0xffff;
+ p->vertCount++;
+ }
+ src += nvp*2;
+ }
+ // Off-mesh connection vertices.
+ n = 0;
+ for (int i = 0; i < params->offMeshConCount; ++i)
+ {
+ // Only store connections which start from this tile.
+ if (offMeshConClass[i*2+0] == 0xff)
+ {
+ dtPoly* p = &navPolys[offMeshPolyBase+n];
+ p->vertCount = 2;
+ p->verts[0] = (unsigned short)(offMeshVertsBase + n*2+0);
+ p->verts[1] = (unsigned short)(offMeshVertsBase + n*2+1);
+ p->flags = params->offMeshConFlags[i];
+ p->setArea(params->offMeshConAreas[i]);
+ p->setType(DT_POLYTYPE_OFFMESH_CONNECTION);
+ n++;
+ }
+ }
+
+ // Store portal edges.
+ if (params->tileSize > 0)
+ {
+ for (int i = 0; i < params->polyCount; ++i)
+ {
+ dtPoly* poly = &navPolys[i];
+ for (int j = 0; j < poly->vertCount; ++j)
+ {
+ int nj = j+1;
+ if (nj >= poly->vertCount) nj = 0;
+
+ const unsigned short* va = &params->verts[poly->verts[j]*3];
+ const unsigned short* vb = &params->verts[poly->verts[nj]*3];
+
+ if (va[0] == params->tileSize && vb[0] == params->tileSize) // x+
+ poly->neis[j] = DT_EXT_LINK | 0;
+ else if (va[2] == params->tileSize && vb[2] == params->tileSize) // z+
+ poly->neis[j] = DT_EXT_LINK | 2;
+ else if (va[0] == 0 && vb[0] == 0) // x-
+ poly->neis[j] = DT_EXT_LINK | 4;
+ else if (va[2] == 0 && vb[2] == 0) // z-
+ poly->neis[j] = DT_EXT_LINK | 6;
+ }
+ }
+ }
+
+ // Store detail meshes and vertices.
+ // The nav polygon vertices are stored as the first vertices on each mesh.
+ // We compress the mesh data by skipping them and using the navmesh coordinates.
+ unsigned short vbase = 0;
+ for (int i = 0; i < params->polyCount; ++i)
+ {
+ dtPolyDetail& dtl = navDMeshes[i];
+ const int vb = (int)params->detailMeshes[i*4+0];
+ const int ndv = (int)params->detailMeshes[i*4+1];
+ const int nv = navPolys[i].vertCount;
+ dtl.vertBase = (unsigned int)vbase;
+ dtl.vertCount = (unsigned char)(ndv-nv);
+ dtl.triBase = (unsigned int)params->detailMeshes[i*4+2];
+ dtl.triCount = (unsigned char)params->detailMeshes[i*4+3];
+ // Copy vertices except the first 'nv' verts which are equal to nav poly verts.
+ if (ndv-nv)
+ {
+ memcpy(&navDVerts[vbase*3], &params->detailVerts[(vb+nv)*3], sizeof(float)*3*(ndv-nv));
+ vbase += (unsigned short)(ndv-nv);
+ }
+ }
+ // Store triangles.
+ memcpy(navDTris, params->detailTris, sizeof(unsigned char)*4*params->detailTriCount);
+
+ // Store and create BVtree.
+ // TODO: take detail mesh into account! use byte per bbox extent?
+ createBVTree(params->verts, params->vertCount, params->polys, params->polyCount,
+ nvp, params->cs, params->ch, params->polyCount*2, navBvtree);
+
+ // Store Off-Mesh connections.
+ n = 0;
+ for (int i = 0; i < params->offMeshConCount; ++i)
+ {
+ // Only store connections which start from this tile.
+ if (offMeshConClass[i*2+0] == 0xff)
+ {
+ dtOffMeshConnection* con = &offMeshCons[n];
+ con->poly = (unsigned short)(offMeshPolyBase + n);
+ // Copy connection end-points.
+ const float* endPts = &params->offMeshConVerts[i*2*3];
+ dtVcopy(&con->pos[0], &endPts[0]);
+ dtVcopy(&con->pos[3], &endPts[3]);
+ con->rad = params->offMeshConRad[i];
+ con->flags = params->offMeshConDir[i] ? DT_OFFMESH_CON_BIDIR : 0;
+ con->side = offMeshConClass[i*2+1];
+ if (params->offMeshConUserID)
+ con->userId = params->offMeshConUserID[i];
+ n++;
+ }
+ }
+
+ dtFree(offMeshConClass);
+
+ *outData = data;
+ *outDataSize = dataSize;
+
+ return true;
+}
+
+inline void swapByte(unsigned char* a, unsigned char* b)
+{
+ unsigned char tmp = *a;
+ *a = *b;
+ *b = tmp;
+}
+
+inline void swapEndian(unsigned short* v)
+{
+ unsigned char* x = (unsigned char*)v;
+ swapByte(x+0, x+1);
+}
+
+inline void swapEndian(short* v)
+{
+ unsigned char* x = (unsigned char*)v;
+ swapByte(x+0, x+1);
+}
+
+inline void swapEndian(unsigned int* v)
+{
+ unsigned char* x = (unsigned char*)v;
+ swapByte(x+0, x+3); swapByte(x+1, x+2);
+}
+
+inline void swapEndian(int* v)
+{
+ unsigned char* x = (unsigned char*)v;
+ swapByte(x+0, x+3); swapByte(x+1, x+2);
+}
+
+inline void swapEndian(float* v)
+{
+ unsigned char* x = (unsigned char*)v;
+ swapByte(x+0, x+3); swapByte(x+1, x+2);
+}
+
+bool dtNavMeshHeaderSwapEndian(unsigned char* data, const int /*dataSize*/)
+{
+ dtMeshHeader* header = (dtMeshHeader*)data;
+
+ int swappedMagic = DT_NAVMESH_MAGIC;
+ int swappedVersion = DT_NAVMESH_VERSION;
+ swapEndian(&swappedMagic);
+ swapEndian(&swappedVersion);
+
+ if ((header->magic != DT_NAVMESH_MAGIC || header->version != DT_NAVMESH_VERSION) &&
+ (header->magic != swappedMagic || header->version != swappedVersion))
+ {
+ return false;
+ }
+
+ swapEndian(&header->magic);
+ swapEndian(&header->version);
+ swapEndian(&header->x);
+ swapEndian(&header->y);
+ swapEndian(&header->userId);
+ swapEndian(&header->polyCount);
+ swapEndian(&header->vertCount);
+ swapEndian(&header->maxLinkCount);
+ swapEndian(&header->detailMeshCount);
+ swapEndian(&header->detailVertCount);
+ swapEndian(&header->detailTriCount);
+ swapEndian(&header->bvNodeCount);
+ swapEndian(&header->offMeshConCount);
+ swapEndian(&header->offMeshBase);
+ swapEndian(&header->walkableHeight);
+ swapEndian(&header->walkableRadius);
+ swapEndian(&header->walkableClimb);
+ swapEndian(&header->bmin[0]);
+ swapEndian(&header->bmin[1]);
+ swapEndian(&header->bmin[2]);
+ swapEndian(&header->bmax[0]);
+ swapEndian(&header->bmax[1]);
+ swapEndian(&header->bmax[2]);
+ swapEndian(&header->bvQuantFactor);
+
+ // Freelist index and pointers are updated when tile is added, no need to swap.
+
+ return true;
+}
+
+bool dtNavMeshDataSwapEndian(unsigned char* data, const int /*dataSize*/)
+{
+ // Make sure the data is in right format.
+ dtMeshHeader* header = (dtMeshHeader*)data;
+ if (header->magic != DT_NAVMESH_MAGIC)
+ return false;
+ if (header->version != DT_NAVMESH_VERSION)
+ return false;
+
+ // Patch header pointers.
+ const int headerSize = dtAlign4(sizeof(dtMeshHeader));
+ const int vertsSize = dtAlign4(sizeof(float)*3*header->vertCount);
+ const int polysSize = dtAlign4(sizeof(dtPoly)*header->polyCount);
+ const int linksSize = dtAlign4(sizeof(dtLink)*(header->maxLinkCount));
+ const int detailMeshesSize = dtAlign4(sizeof(dtPolyDetail)*header->detailMeshCount);
+ const int detailVertsSize = dtAlign4(sizeof(float)*3*header->detailVertCount);
+ const int detailTrisSize = dtAlign4(sizeof(unsigned char)*4*header->detailTriCount);
+ const int bvtreeSize = dtAlign4(sizeof(dtBVNode)*header->bvNodeCount);
+ const int offMeshLinksSize = dtAlign4(sizeof(dtOffMeshConnection)*header->offMeshConCount);
+
+ unsigned char* d = data + headerSize;
+ float* verts = (float*)d; d += vertsSize;
+ dtPoly* polys = (dtPoly*)d; d += polysSize;
+ /*dtLink* links = (dtLink*)d;*/ d += linksSize;
+ dtPolyDetail* detailMeshes = (dtPolyDetail*)d; d += detailMeshesSize;
+ float* detailVerts = (float*)d; d += detailVertsSize;
+ /*unsigned char* detailTris = (unsigned char*)d;*/ d += detailTrisSize;
+ dtBVNode* bvTree = (dtBVNode*)d; d += bvtreeSize;
+ dtOffMeshConnection* offMeshCons = (dtOffMeshConnection*)d; d += offMeshLinksSize;
+
+ // Vertices
+ for (int i = 0; i < header->vertCount*3; ++i)
+ {
+ swapEndian(&verts[i]);
+ }
+
+ // Polys
+ for (int i = 0; i < header->polyCount; ++i)
+ {
+ dtPoly* p = &polys[i];
+ // poly->firstLink is update when tile is added, no need to swap.
+ for (int j = 0; j < DT_VERTS_PER_POLYGON; ++j)
+ {
+ swapEndian(&p->verts[j]);
+ swapEndian(&p->neis[j]);
+ }
+ swapEndian(&p->flags);
+ }
+
+ // Links are rebuild when tile is added, no need to swap.
+
+ // Detail meshes
+ for (int i = 0; i < header->detailMeshCount; ++i)
+ {
+ dtPolyDetail* pd = &detailMeshes[i];
+ swapEndian(&pd->vertBase);
+ swapEndian(&pd->triBase);
+ }
+
+ // Detail verts
+ for (int i = 0; i < header->detailVertCount*3; ++i)
+ {
+ swapEndian(&detailVerts[i]);
+ }
+
+ // BV-tree
+ for (int i = 0; i < header->bvNodeCount; ++i)
+ {
+ dtBVNode* node = &bvTree[i];
+ for (int j = 0; j < 3; ++j)
+ {
+ swapEndian(&node->bmin[j]);
+ swapEndian(&node->bmax[j]);
+ }
+ swapEndian(&node->i);
+ }
+
+ // Off-mesh Connections.
+ for (int i = 0; i < header->offMeshConCount; ++i)
+ {
+ dtOffMeshConnection* con = &offMeshCons[i];
+ for (int j = 0; j < 6; ++j)
+ swapEndian(&con->pos[j]);
+ swapEndian(&con->rad);
+ swapEndian(&con->poly);
+ }
+
+ return true;
+}
diff --git a/dep/recastnavigation/Detour/DetourNavMeshBuilder.h b/dep/recastnavigation/Detour/DetourNavMeshBuilder.h
new file mode 100644
index 0000000..8d8ef2e
--- /dev/null
+++ b/dep/recastnavigation/Detour/DetourNavMeshBuilder.h
@@ -0,0 +1,77 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#ifndef DETOURNAVMESHBUILDER_H
+#define DETOURNAVMESHBUILDER_H
+
+#include "DetourAlloc.h"
+
+
+// The units of the parameters are specified in parenthesis as follows:
+// (vx) voxels, (wu) world units
+struct dtNavMeshCreateParams
+{
+ // Navmesh vertices.
+ const unsigned short* verts; // Array of vertices, each vertex has 3 components. (vx).
+ int vertCount; // Vertex count
+ // Navmesh polygons
+ const unsigned short* polys; // Array of polygons, uses same format as rcPolyMesh.
+ const unsigned short* polyFlags; // Array of flags per polygon.
+ const unsigned char* polyAreas; // Array of area ids per polygon.
+ int polyCount; // Number of polygons
+ int nvp; // Number of verts per polygon.
+ // Navmesh Detail
+ const unsigned int* detailMeshes; // Detail meshes, uses same format as rcPolyMeshDetail.
+ const float* detailVerts; // Detail mesh vertices, uses same format as rcPolyMeshDetail (wu).
+ int detailVertsCount; // Total number of detail vertices
+ const unsigned char* detailTris; // Array of detail tris per detail mesh.
+ int detailTriCount; // Total number of detail triangles.
+ // Off-Mesh Connections.
+ const float* offMeshConVerts; // Off-mesh connection vertices (wu).
+ const float* offMeshConRad; // Off-mesh connection radii (wu).
+ const unsigned short* offMeshConFlags; // Off-mesh connection flags.
+ const unsigned char* offMeshConAreas; // Off-mesh connection area ids.
+ const unsigned char* offMeshConDir; // Off-mesh connection direction flags (1 = bidir, 0 = oneway).
+ const unsigned int* offMeshConUserID; // Off-mesh connection user id (optional).
+ int offMeshConCount; // Number of off-mesh connections
+ // Tile location
+ unsigned int userId; // User ID bound to the tile.
+ int tileX, tileY; // Tile location (tile coords).
+ float bmin[3], bmax[3]; // Tile bounds (wu).
+ // Settings
+ float walkableHeight; // Agent height (wu).
+ float walkableRadius; // Agent radius (wu).
+ float walkableClimb; // Agent max climb (wu).
+ float cs; // Cell size (xz) (wu).
+ float ch; // Cell height (y) (wu).
+ int tileSize; // Tile size (width & height) (vx).
+};
+
+// Build navmesh data from given input data.
+bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData, int* outDataSize);
+
+// Swaps endianess of navmesh header.
+bool dtNavMeshHeaderSwapEndian(unsigned char* data, const int dataSize);
+
+// Swaps endianess of the navmesh data. This function assumes that the header is in correct
+// endianess already. Call dtNavMeshHeaderSwapEndian() first on the data if the data is
+// assumed to be in wrong endianess to start with. If converting from native endianess to foreign,
+// call dtNavMeshHeaderSwapEndian() after the data has been swapped.
+bool dtNavMeshDataSwapEndian(unsigned char* data, const int dataSize);
+
+#endif // DETOURNAVMESHBUILDER_H
diff --git a/dep/recastnavigation/Detour/DetourNavMeshQuery.cpp b/dep/recastnavigation/Detour/DetourNavMeshQuery.cpp
new file mode 100644
index 0000000..6a6eb94
--- /dev/null
+++ b/dep/recastnavigation/Detour/DetourNavMeshQuery.cpp
@@ -0,0 +1,2564 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#include <math.h>
+#include <float.h>
+#include <string.h>
+#include "DetourNavMeshQuery.h"
+#include "DetourNavMesh.h"
+#include "DetourNode.h"
+#include "DetourCommon.h"
+#include "DetourAlloc.h"
+#include "DetourAssert.h"
+#include <new>
+
+
+dtQueryFilter::dtQueryFilter() :
+ m_includeFlags(0xffff),
+ m_excludeFlags(0)
+{
+ for (int i = 0; i < DT_MAX_AREAS; ++i)
+ m_areaCost[i] = 1.0f;
+}
+
+#ifdef DT_VIRTUAL_QUERYFILTER
+bool dtQueryFilter::passFilter(const dtPolyRef /*ref*/,
+ const dtMeshTile* /*tile*/,
+ const dtPoly* poly) const
+{
+ return (poly->flags & m_includeFlags) != 0 && (poly->flags & m_excludeFlags) == 0;
+}
+
+float dtQueryFilter::getCost(const float* pa, const float* pb,
+ const dtPolyRef /*prevRef*/, const dtMeshTile* /*prevTile*/, const dtPoly* /*prevPoly*/,
+ const dtPolyRef /*curRef*/, const dtMeshTile* /*curTile*/, const dtPoly* curPoly,
+ const dtPolyRef /*nextRef*/, const dtMeshTile* /*nextTile*/, const dtPoly* /*nextPoly*/) const
+{
+ return dtVdist(pa, pb) * m_areaCost[curPoly->area];
+}
+#else
+inline bool dtQueryFilter::passFilter(const dtPolyRef /*ref*/,
+ const dtMeshTile* /*tile*/,
+ const dtPoly* poly) const
+{
+ return (poly->flags & m_includeFlags) != 0 && (poly->flags & m_excludeFlags) == 0;
+}
+
+inline float dtQueryFilter::getCost(const float* pa, const float* pb,
+ const dtPolyRef /*prevRef*/, const dtMeshTile* /*prevTile*/, const dtPoly* /*prevPoly*/,
+ const dtPolyRef /*curRef*/, const dtMeshTile* /*curTile*/, const dtPoly* curPoly,
+ const dtPolyRef /*nextRef*/, const dtMeshTile* /*nextTile*/, const dtPoly* /*nextPoly*/) const
+{
+ return dtVdist(pa, pb) * m_areaCost[curPoly->getArea()];
+}
+#endif
+
+static const float H_SCALE = 2.0f; // Search heuristic scale.
+
+
+dtNavMeshQuery* dtAllocNavMeshQuery()
+{
+ void* mem = dtAlloc(sizeof(dtNavMeshQuery), DT_ALLOC_PERM);
+ if (!mem) return 0;
+ return new(mem) dtNavMeshQuery;
+}
+
+void dtFreeNavMeshQuery(dtNavMeshQuery* navmesh)
+{
+ if (!navmesh) return;
+ navmesh->~dtNavMeshQuery();
+ dtFree(navmesh);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+dtNavMeshQuery::dtNavMeshQuery() :
+ m_tinyNodePool(0),
+ m_nodePool(0),
+ m_openList(0)
+{
+ memset(&m_query, 0, sizeof(dtQueryData));
+}
+
+dtNavMeshQuery::~dtNavMeshQuery()
+{
+ if (m_tinyNodePool)
+ m_tinyNodePool->~dtNodePool();
+ if (m_nodePool)
+ m_nodePool->~dtNodePool();
+ if (m_openList)
+ m_openList->~dtNodeQueue();
+ dtFree(m_tinyNodePool);
+ dtFree(m_nodePool);
+ dtFree(m_openList);
+}
+
+dtStatus dtNavMeshQuery::init(const dtNavMesh* nav, const int maxNodes)
+{
+ m_nav = nav;
+
+ if (!m_nodePool || m_nodePool->getMaxNodes() < maxNodes)
+ {
+ if (m_nodePool)
+ {
+ m_nodePool->~dtNodePool();
+ dtFree(m_nodePool);
+ m_nodePool = 0;
+ }
+ m_nodePool = new (dtAlloc(sizeof(dtNodePool), DT_ALLOC_PERM)) dtNodePool(maxNodes, dtNextPow2(maxNodes/4));
+ if (!m_nodePool)
+ return DT_FAILURE_OUT_OF_MEMORY;
+ }
+ else
+ {
+ m_nodePool->clear();
+ }
+
+ if (!m_tinyNodePool)
+ {
+ m_tinyNodePool = new (dtAlloc(sizeof(dtNodePool), DT_ALLOC_PERM)) dtNodePool(64, 32);
+ if (!m_tinyNodePool)
+ return DT_FAILURE_OUT_OF_MEMORY;
+ }
+ else
+ {
+ m_tinyNodePool->clear();
+ }
+
+ // TODO: check the open list size too.
+ if (!m_openList || m_openList->getCapacity() < maxNodes)
+ {
+ if (m_openList)
+ {
+ m_openList->~dtNodeQueue();
+ dtFree(m_openList);
+ m_openList = 0;
+ }
+ m_openList = new (dtAlloc(sizeof(dtNodeQueue), DT_ALLOC_PERM)) dtNodeQueue(maxNodes);
+ if (!m_openList)
+ return DT_FAILURE_OUT_OF_MEMORY;
+ }
+ else
+ {
+ m_openList->clear();
+ }
+
+ return DT_SUCCESS;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+dtStatus dtNavMeshQuery::closestPointOnPoly(dtPolyRef ref, const float* pos, float* closest) const
+{
+ dtAssert(m_nav);
+ const dtMeshTile* tile = 0;
+ const dtPoly* poly = 0;
+ if (m_nav->getTileAndPolyByRef(ref, &tile, &poly) != DT_SUCCESS)
+ return DT_FAILURE;
+ if (!tile) return DT_FAILURE;
+
+ if (poly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION)
+ return DT_FAILURE;
+
+ if (closestPointOnPolyInTile(tile, poly, pos, closest) != DT_SUCCESS)
+ return DT_FAILURE;
+ return DT_SUCCESS;
+}
+
+dtStatus dtNavMeshQuery::closestPointOnPolyInTile(const dtMeshTile* tile, const dtPoly* poly,
+ const float* pos, float* closest) const
+{
+ const unsigned int ip = (unsigned int)(poly - tile->polys);
+ const dtPolyDetail* pd = &tile->detailMeshes[ip];
+
+ // TODO: The commented out version finds 'cylinder distance' instead of 'sphere distance' to the navmesh.
+ // Test and enable.
+/*
+ // Clamp point to be inside the polygon.
+ float verts[DT_VERTS_PER_POLYGON*3];
+ float edged[DT_VERTS_PER_POLYGON];
+ float edget[DT_VERTS_PER_POLYGON];
+ const int nv = poly->vertCount;
+ for (int i = 0; i < nv; ++i)
+ dtVcopy(&verts[i*3], &tile->verts[poly->verts[i]*3]);
+
+ dtVcopy(closest, pos);
+ if (!dtDistancePtPolyEdgesSqr(pos, verts, nv, edged, edget))
+ {
+ // Point is outside the polygon, dtClamp to nearest edge.
+ float dmin = FLT_MAX;
+ int imin = -1;
+ for (int i = 0; i < nv; ++i)
+ {
+ if (edged[i] < dmin)
+ {
+ dmin = edged[i];
+ imin = i;
+ }
+ }
+ const float* va = &verts[imin*3];
+ const float* vb = &verts[((imin+1)%nv)*3];
+ dtVlerp(closest, va, vb, edget[imin]);
+ }
+
+ // Find height at the location.
+ for (int j = 0; j < pd->triCount; ++j)
+ {
+ const unsigned char* t = &tile->detailTris[(pd->triBase+j)*4];
+ const float* v[3];
+ for (int k = 0; k < 3; ++k)
+ {
+ if (t[k] < poly->vertCount)
+ v[k] = &tile->verts[poly->verts[t[k]]*3];
+ else
+ v[k] = &tile->detailVerts[(pd->vertBase+(t[k]-poly->vertCount))*3];
+ }
+ float h;
+ if (dtClosestHeightPointTriangle(pos, v[0], v[1], v[2], h))
+ {
+ closest[1] = h;
+ break;
+ }
+ }
+*/
+ float closestDistSqr = FLT_MAX;
+ for (int j = 0; j < pd->triCount; ++j)
+ {
+ const unsigned char* t = &tile->detailTris[(pd->triBase+j)*4];
+ const float* v[3];
+ for (int k = 0; k < 3; ++k)
+ {
+ if (t[k] < poly->vertCount)
+ v[k] = &tile->verts[poly->verts[t[k]]*3];
+ else
+ v[k] = &tile->detailVerts[(pd->vertBase+(t[k]-poly->vertCount))*3];
+ }
+
+ float pt[3];
+ dtClosestPtPointTriangle(pt, pos, v[0], v[1], v[2]);
+ float d = dtVdistSqr(pos, pt);
+
+ if (d < closestDistSqr)
+ {
+ dtVcopy(closest, pt);
+ closestDistSqr = d;
+ }
+ }
+
+ return DT_SUCCESS;
+}
+
+dtStatus dtNavMeshQuery::closestPointOnPolyBoundary(dtPolyRef ref, const float* pos, float* closest) const
+{
+ dtAssert(m_nav);
+
+ const dtMeshTile* tile = 0;
+ const dtPoly* poly = 0;
+ if (m_nav->getTileAndPolyByRef(ref, &tile, &poly) != DT_SUCCESS)
+ return DT_FAILURE;
+
+ // Collect vertices.
+ float verts[DT_VERTS_PER_POLYGON*3];
+ float edged[DT_VERTS_PER_POLYGON];
+ float edget[DT_VERTS_PER_POLYGON];
+ int nv = 0;
+ for (int i = 0; i < (int)poly->vertCount; ++i)
+ {
+ dtVcopy(&verts[nv*3], &tile->verts[poly->verts[i]*3]);
+ nv++;
+ }
+
+ bool inside = dtDistancePtPolyEdgesSqr(pos, verts, nv, edged, edget);
+ if (inside)
+ {
+ // Point is inside the polygon, return the point.
+ dtVcopy(closest, pos);
+ }
+ else
+ {
+ // Point is outside the polygon, dtClamp to nearest edge.
+ float dmin = FLT_MAX;
+ int imin = -1;
+ for (int i = 0; i < nv; ++i)
+ {
+ if (edged[i] < dmin)
+ {
+ dmin = edged[i];
+ imin = i;
+ }
+ }
+ const float* va = &verts[imin*3];
+ const float* vb = &verts[((imin+1)%nv)*3];
+ dtVlerp(closest, va, vb, edget[imin]);
+ }
+
+ return DT_SUCCESS;
+}
+
+
+dtStatus dtNavMeshQuery::getPolyHeight(dtPolyRef ref, const float* pos, float* height) const
+{
+ dtAssert(m_nav);
+
+ const dtMeshTile* tile = 0;
+ const dtPoly* poly = 0;
+ if (m_nav->getTileAndPolyByRef(ref, &tile, &poly) != DT_SUCCESS)
+ return DT_FAILURE;
+
+ if (poly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION)
+ {
+ const float* v0 = &tile->verts[poly->verts[0]*3];
+ const float* v1 = &tile->verts[poly->verts[1]*3];
+ const float d0 = dtVdist(pos, v0);
+ const float d1 = dtVdist(pos, v1);
+ const float u = d0 / (d0+d1);
+ if (height)
+ *height = v0[1] + (v1[1] - v0[1]) * u;
+ return DT_SUCCESS;
+ }
+ else
+ {
+ const unsigned int ip = (unsigned int)(poly - tile->polys);
+ const dtPolyDetail* pd = &tile->detailMeshes[ip];
+ for (int j = 0; j < pd->triCount; ++j)
+ {
+ const unsigned char* t = &tile->detailTris[(pd->triBase+j)*4];
+ const float* v[3];
+ for (int k = 0; k < 3; ++k)
+ {
+ if (t[k] < poly->vertCount)
+ v[k] = &tile->verts[poly->verts[t[k]]*3];
+ else
+ v[k] = &tile->detailVerts[(pd->vertBase+(t[k]-poly->vertCount))*3];
+ }
+ float h;
+ if (dtClosestHeightPointTriangle(pos, v[0], v[1], v[2], h))
+ {
+ if (height)
+ *height = h;
+ return DT_SUCCESS;
+ }
+ }
+ }
+
+ return DT_FAILURE;
+}
+
+dtStatus dtNavMeshQuery::findNearestPoly(const float* center, const float* extents,
+ const dtQueryFilter* filter,
+ dtPolyRef* nearestRef, float* nearestPt) const
+{
+ dtAssert(m_nav);
+
+ *nearestRef = 0;
+
+ // Get nearby polygons from proximity grid.
+ dtPolyRef polys[128];
+ int polyCount = 0;
+ if (queryPolygons(center, extents, filter, polys, &polyCount, 128) != DT_SUCCESS)
+ return DT_FAILURE;
+
+ // Find nearest polygon amongst the nearby polygons.
+ dtPolyRef nearest = 0;
+ float nearestDistanceSqr = FLT_MAX;
+ for (int i = 0; i < polyCount; ++i)
+ {
+ dtPolyRef ref = polys[i];
+ float closestPtPoly[3];
+ if (closestPointOnPoly(ref, center, closestPtPoly) != DT_SUCCESS)
+ continue;
+ float d = dtVdistSqr(center, closestPtPoly);
+ if (d < nearestDistanceSqr)
+ {
+ if (nearestPt)
+ dtVcopy(nearestPt, closestPtPoly);
+ nearestDistanceSqr = d;
+ nearest = ref;
+ }
+ }
+
+ if (nearestRef)
+ *nearestRef = nearest;
+
+ return DT_SUCCESS;
+}
+
+dtPolyRef dtNavMeshQuery::findNearestPolyInTile(const dtMeshTile* tile, const float* center, const float* extents,
+ const dtQueryFilter* filter, float* nearestPt) const
+{
+ dtAssert(m_nav);
+
+ float bmin[3], bmax[3];
+ dtVsub(bmin, center, extents);
+ dtVadd(bmax, center, extents);
+
+ // Get nearby polygons from proximity grid.
+ dtPolyRef polys[128];
+ int polyCount = queryPolygonsInTile(tile, bmin, bmax, filter, polys, 128);
+
+ // Find nearest polygon amongst the nearby polygons.
+ dtPolyRef nearest = 0;
+ float nearestDistanceSqr = FLT_MAX;
+ for (int i = 0; i < polyCount; ++i)
+ {
+ dtPolyRef ref = polys[i];
+ const dtPoly* poly = &tile->polys[m_nav->decodePolyIdPoly(ref)];
+ float closestPtPoly[3];
+ if (closestPointOnPolyInTile(tile, poly, center, closestPtPoly) != DT_SUCCESS)
+ continue;
+
+ float d = dtVdistSqr(center, closestPtPoly);
+ if (d < nearestDistanceSqr)
+ {
+ if (nearestPt)
+ dtVcopy(nearestPt, closestPtPoly);
+ nearestDistanceSqr = d;
+ nearest = ref;
+ }
+ }
+
+ return nearest;
+}
+
+int dtNavMeshQuery::queryPolygonsInTile(const dtMeshTile* tile, const float* qmin, const float* qmax,
+ const dtQueryFilter* filter,
+ dtPolyRef* polys, const int maxPolys) const
+{
+ dtAssert(m_nav);
+
+ if (tile->bvTree)
+ {
+ const dtBVNode* node = &tile->bvTree[0];
+ const dtBVNode* end = &tile->bvTree[tile->header->bvNodeCount];
+ const float* tbmin = tile->header->bmin;
+ const float* tbmax = tile->header->bmax;
+ const float qfac = tile->header->bvQuantFactor;
+
+ // Calculate quantized box
+ unsigned short bmin[3], bmax[3];
+ // dtClamp query box to world box.
+ float minx = dtClamp(qmin[0], tbmin[0], tbmax[0]) - tbmin[0];
+ float miny = dtClamp(qmin[1], tbmin[1], tbmax[1]) - tbmin[1];
+ float minz = dtClamp(qmin[2], tbmin[2], tbmax[2]) - tbmin[2];
+ float maxx = dtClamp(qmax[0], tbmin[0], tbmax[0]) - tbmin[0];
+ float maxy = dtClamp(qmax[1], tbmin[1], tbmax[1]) - tbmin[1];
+ float maxz = dtClamp(qmax[2], tbmin[2], tbmax[2]) - tbmin[2];
+ // Quantize
+ bmin[0] = (unsigned short)(qfac * minx) & 0xfffe;
+ bmin[1] = (unsigned short)(qfac * miny) & 0xfffe;
+ bmin[2] = (unsigned short)(qfac * minz) & 0xfffe;
+ bmax[0] = (unsigned short)(qfac * maxx + 1) | 1;
+ bmax[1] = (unsigned short)(qfac * maxy + 1) | 1;
+ bmax[2] = (unsigned short)(qfac * maxz + 1) | 1;
+
+ // Traverse tree
+ const dtPolyRef base = m_nav->getPolyRefBase(tile);
+ int n = 0;
+ while (node < end)
+ {
+ const bool overlap = dtOverlapQuantBounds(bmin, bmax, node->bmin, node->bmax);
+ const bool isLeafNode = node->i >= 0;
+
+ if (isLeafNode && overlap)
+ {
+ dtPolyRef ref = base | (dtPolyRef)node->i;
+ if (filter->passFilter(ref, tile, &tile->polys[node->i]))
+ {
+ if (n < maxPolys)
+ polys[n++] = ref;
+ }
+ }
+
+ if (overlap || isLeafNode)
+ node++;
+ else
+ {
+ const int escapeIndex = -node->i;
+ node += escapeIndex;
+ }
+ }
+
+ return n;
+ }
+ else
+ {
+ float bmin[3], bmax[3];
+ int n = 0;
+ const dtPolyRef base = m_nav->getPolyRefBase(tile);
+ for (int i = 0; i < tile->header->polyCount; ++i)
+ {
+ // Calc polygon bounds.
+ dtPoly* p = &tile->polys[i];
+ const float* v = &tile->verts[p->verts[0]*3];
+ dtVcopy(bmin, v);
+ dtVcopy(bmax, v);
+ for (int j = 1; j < p->vertCount; ++j)
+ {
+ v = &tile->verts[p->verts[j]*3];
+ dtVmin(bmin, v);
+ dtVmax(bmax, v);
+ }
+ if (dtOverlapBounds(qmin,qmax, bmin,bmax))
+ {
+ const dtPolyRef ref = base | (dtPolyRef)i;
+ if (filter->passFilter(ref, tile, p))
+ {
+ if (n < maxPolys)
+ polys[n++] = ref;
+ }
+ }
+ }
+ return n;
+ }
+}
+
+dtStatus dtNavMeshQuery::queryPolygons(const float* center, const float* extents,
+ const dtQueryFilter* filter,
+ dtPolyRef* polys, int* polyCount, const int maxPolys) const
+{
+ dtAssert(m_nav);
+
+ float bmin[3], bmax[3];
+ dtVsub(bmin, center, extents);
+ dtVadd(bmax, center, extents);
+
+ // Find tiles the query touches.
+ int minx, miny, maxx, maxy;
+ m_nav->calcTileLoc(bmin, &minx, &miny);
+ m_nav->calcTileLoc(bmax, &maxx, &maxy);
+
+ int n = 0;
+ for (int y = miny; y <= maxy; ++y)
+ {
+ for (int x = minx; x <= maxx; ++x)
+ {
+ const dtMeshTile* tile = m_nav->getTileAt(x,y);
+ if (!tile) continue;
+ n += queryPolygonsInTile(tile, bmin, bmax, filter, polys+n, maxPolys-n);
+ if (n >= maxPolys)
+ {
+ *polyCount = n;
+ return DT_SUCCESS;
+ }
+ }
+ }
+ *polyCount = n;
+
+ return DT_SUCCESS;
+}
+
+dtStatus dtNavMeshQuery::findPath(dtPolyRef startRef, dtPolyRef endRef,
+ const float* startPos, const float* endPos,
+ const dtQueryFilter* filter,
+ dtPolyRef* path, int* pathCount, const int maxPath) const
+{
+ dtAssert(m_nav);
+ dtAssert(m_nodePool);
+ dtAssert(m_openList);
+
+ *pathCount = 0;
+
+ if (!startRef || !endRef)
+ return DT_FAILURE;
+
+ if (!maxPath)
+ return DT_FAILURE;
+
+ // Validate input
+ if (!m_nav->isValidPolyRef(startRef) || !m_nav->isValidPolyRef(endRef))
+ return DT_FAILURE;
+
+ if (startRef == endRef)
+ {
+ path[0] = startRef;
+ *pathCount = 1;
+ return DT_SUCCESS;
+ }
+
+ m_nodePool->clear();
+ m_openList->clear();
+
+ dtNode* startNode = m_nodePool->getNode(startRef);
+ dtVcopy(startNode->pos, startPos);
+ startNode->pidx = 0;
+ startNode->cost = 0;
+ startNode->total = dtVdist(startPos, endPos) * H_SCALE;
+ startNode->id = startRef;
+ startNode->flags = DT_NODE_OPEN;
+ m_openList->push(startNode);
+
+ dtNode* lastBestNode = startNode;
+ float lastBestNodeCost = startNode->total;
+
+ while (!m_openList->empty())
+ {
+ // Remove node from open list and put it in closed list.
+ dtNode* bestNode = m_openList->pop();
+ bestNode->flags &= ~DT_NODE_OPEN;
+ bestNode->flags |= DT_NODE_CLOSED;
+
+ // Reached the goal, stop searching.
+ if (bestNode->id == endRef)
+ {
+ lastBestNode = bestNode;
+ break;
+ }
+
+ // Get current poly and tile.
+ // The API input has been cheked already, skip checking internal data.
+ const dtPolyRef bestRef = bestNode->id;
+ const dtMeshTile* bestTile = 0;
+ const dtPoly* bestPoly = 0;
+ m_nav->getTileAndPolyByRefUnsafe(bestRef, &bestTile, &bestPoly);
+
+ // Get parent poly and tile.
+ dtPolyRef parentRef = 0;
+ const dtMeshTile* parentTile = 0;
+ const dtPoly* parentPoly = 0;
+ if (bestNode->pidx)
+ parentRef = m_nodePool->getNodeAtIdx(bestNode->pidx)->id;
+ if (parentRef)
+ m_nav->getTileAndPolyByRefUnsafe(parentRef, &parentTile, &parentPoly);
+
+ for (unsigned int i = bestPoly->firstLink; i != DT_NULL_LINK; i = bestTile->links[i].next)
+ {
+ dtPolyRef neighbourRef = bestTile->links[i].ref;
+
+ // Skip invalid ids and do not expand back to where we came from.
+ if (!neighbourRef || neighbourRef == parentRef)
+ continue;
+
+ // Get neighbour poly and tile.
+ // The API input has been cheked already, skip checking internal data.
+ const dtMeshTile* neighbourTile = 0;
+ const dtPoly* neighbourPoly = 0;
+ m_nav->getTileAndPolyByRefUnsafe(neighbourRef, &neighbourTile, &neighbourPoly);
+
+ if (!filter->passFilter(neighbourRef, neighbourTile, neighbourPoly))
+ continue;
+
+ dtNode* neighbourNode = m_nodePool->getNode(neighbourRef);
+ if (!neighbourNode)
+ continue;
+
+ // If the node is visited the first time, calculate node position.
+ if (neighbourNode->flags == 0)
+ {
+ getEdgeMidPoint(bestRef, bestPoly, bestTile,
+ neighbourRef, neighbourPoly, neighbourTile,
+ neighbourNode->pos);
+ }
+
+ // Calculate cost and heuristic.
+ float cost = 0;
+ float heuristic = 0;
+
+ // Special case for last node.
+ if (neighbourRef == endRef)
+ {
+ // Cost
+ const float curCost = filter->getCost(bestNode->pos, neighbourNode->pos,
+ parentRef, parentTile, parentPoly,
+ bestRef, bestTile, bestPoly,
+ neighbourRef, neighbourTile, neighbourPoly);
+ const float endCost = filter->getCost(neighbourNode->pos, endPos,
+ bestRef, bestTile, bestPoly,
+ neighbourRef, neighbourTile, neighbourPoly,
+ 0, 0, 0);
+
+ cost = bestNode->cost + curCost + endCost;
+ heuristic = 0;
+ }
+ else
+ {
+ // Cost
+ const float curCost = filter->getCost(bestNode->pos, neighbourNode->pos,
+ parentRef, parentTile, parentPoly,
+ bestRef, bestTile, bestPoly,
+ neighbourRef, neighbourTile, neighbourPoly);
+ cost = bestNode->cost + curCost;
+ heuristic = dtVdist(neighbourNode->pos, endPos)*H_SCALE;
+ }
+
+ const float total = cost + heuristic;
+
+ // The node is already in open list and the new result is worse, skip.
+ if ((neighbourNode->flags & DT_NODE_OPEN) && total >= neighbourNode->total)
+ continue;
+ // The node is already visited and process, and the new result is worse, skip.
+ if ((neighbourNode->flags & DT_NODE_CLOSED) && total >= neighbourNode->total)
+ continue;
+
+ // Add or update the node.
+ neighbourNode->pidx = m_nodePool->getNodeIdx(bestNode);
+ neighbourNode->id = neighbourRef;
+ neighbourNode->flags &= ~DT_NODE_CLOSED;
+ neighbourNode->cost = cost;
+ neighbourNode->total = total;
+
+ if (neighbourNode->flags & DT_NODE_OPEN)
+ {
+ // Already in open, update node location.
+ m_openList->modify(neighbourNode);
+ }
+ else
+ {
+ // Put the node in open list.
+ neighbourNode->flags |= DT_NODE_OPEN;
+ m_openList->push(neighbourNode);
+ }
+
+ // Update nearest node to target so far.
+ if (heuristic < lastBestNodeCost)
+ {
+ lastBestNodeCost = heuristic;
+ lastBestNode = neighbourNode;
+ }
+ }
+ }
+
+ // Reverse the path.
+ dtNode* prev = 0;
+ dtNode* node = lastBestNode;
+ do
+ {
+ dtNode* next = m_nodePool->getNodeAtIdx(node->pidx);
+ node->pidx = m_nodePool->getNodeIdx(prev);
+ prev = node;
+ node = next;
+ }
+ while (node);
+
+ // Store path
+ node = prev;
+ int n = 0;
+ do
+ {
+ path[n++] = node->id;
+ node = m_nodePool->getNodeAtIdx(node->pidx);
+ }
+ while (node && n < maxPath);
+
+ *pathCount = n;
+
+ return DT_SUCCESS;
+}
+
+dtStatus dtNavMeshQuery::initSlicedFindPath(dtPolyRef startRef, dtPolyRef endRef,
+ const float* startPos, const float* endPos,
+ const dtQueryFilter* filter)
+{
+ dtAssert(m_nav);
+ dtAssert(m_nodePool);
+ dtAssert(m_openList);
+
+ // Init path state.
+ memset(&m_query, 0, sizeof(dtQueryData));
+ m_query.status = DT_FAILURE;
+ m_query.startRef = startRef;
+ m_query.endRef = endRef;
+ dtVcopy(m_query.startPos, startPos);
+ dtVcopy(m_query.endPos, endPos);
+ m_query.filter = filter;
+
+ if (!startRef || !endRef)
+ return DT_FAILURE;
+
+ // Validate input
+ if (!m_nav->isValidPolyRef(startRef) || !m_nav->isValidPolyRef(endRef))
+ return DT_FAILURE;
+
+ if (startRef == endRef)
+ {
+ m_query.status = DT_SUCCESS;
+ return DT_SUCCESS;
+ }
+
+ m_nodePool->clear();
+ m_openList->clear();
+
+ dtNode* startNode = m_nodePool->getNode(startRef);
+ dtVcopy(startNode->pos, startPos);
+ startNode->pidx = 0;
+ startNode->cost = 0;
+ startNode->total = dtVdist(startPos, endPos) * H_SCALE;
+ startNode->id = startRef;
+ startNode->flags = DT_NODE_OPEN;
+ m_openList->push(startNode);
+
+ m_query.status = DT_IN_PROGRESS;
+ m_query.lastBestNode = startNode;
+ m_query.lastBestNodeCost = startNode->total;
+
+ return m_query.status;
+}
+
+dtStatus dtNavMeshQuery::updateSlicedFindPath(const int maxIter)
+{
+ if (m_query.status!= DT_IN_PROGRESS)
+ return m_query.status;
+
+ // Make sure the request is still valid.
+ if (!m_nav->isValidPolyRef(m_query.startRef) || !m_nav->isValidPolyRef(m_query.endRef))
+ {
+ m_query.status = DT_FAILURE;
+ return DT_FAILURE;
+ }
+
+ int iter = 0;
+ while (iter < maxIter && !m_openList->empty())
+ {
+ iter++;
+
+ // Remove node from open list and put it in closed list.
+ dtNode* bestNode = m_openList->pop();
+ bestNode->flags &= ~DT_NODE_OPEN;
+ bestNode->flags |= DT_NODE_CLOSED;
+
+ // Reached the goal, stop searching.
+ if (bestNode->id == m_query.endRef)
+ {
+ m_query.lastBestNode = bestNode;
+ m_query.status = DT_SUCCESS;
+ return m_query.status;
+ }
+
+ // Get current poly and tile.
+ // The API input has been cheked already, skip checking internal data.
+ const dtPolyRef bestRef = bestNode->id;
+ const dtMeshTile* bestTile = 0;
+ const dtPoly* bestPoly = 0;
+ if (m_nav->getTileAndPolyByRef(bestRef, &bestTile, &bestPoly) != DT_SUCCESS)
+ {
+ // The polygon has disappeared during the sliced query, fail.
+ m_query.status = DT_FAILURE;
+ return m_query.status;
+ }
+
+ // Get parent poly and tile.
+ dtPolyRef parentRef = 0;
+ const dtMeshTile* parentTile = 0;
+ const dtPoly* parentPoly = 0;
+ if (bestNode->pidx)
+ parentRef = m_nodePool->getNodeAtIdx(bestNode->pidx)->id;
+ if (parentRef)
+ {
+ if (m_nav->getTileAndPolyByRef(parentRef, &parentTile, &parentPoly) != DT_SUCCESS)
+ {
+ // The polygon has disappeared during the sliced query, fail.
+ m_query.status = DT_FAILURE;
+ return m_query.status;
+ }
+ }
+
+ for (unsigned int i = bestPoly->firstLink; i != DT_NULL_LINK; i = bestTile->links[i].next)
+ {
+ dtPolyRef neighbourRef = bestTile->links[i].ref;
+
+ // Skip invalid ids and do not expand back to where we came from.
+ if (!neighbourRef || neighbourRef == parentRef)
+ continue;
+
+ // Get neighbour poly and tile.
+ // The API input has been cheked already, skip checking internal data.
+ const dtMeshTile* neighbourTile = 0;
+ const dtPoly* neighbourPoly = 0;
+ m_nav->getTileAndPolyByRefUnsafe(neighbourRef, &neighbourTile, &neighbourPoly);
+
+ if (!m_query.filter->passFilter(neighbourRef, neighbourTile, neighbourPoly))
+ continue;
+
+ dtNode* neighbourNode = m_nodePool->getNode(neighbourRef);
+ if (!neighbourNode)
+ continue;
+
+ // If the node is visited the first time, calculate node position.
+ if (neighbourNode->flags == 0)
+ {
+ getEdgeMidPoint(bestRef, bestPoly, bestTile,
+ neighbourRef, neighbourPoly, neighbourTile,
+ neighbourNode->pos);
+ }
+
+ // Calculate cost and heuristic.
+ float cost = 0;
+ float heuristic = 0;
+
+ // Special case for last node.
+ if (neighbourRef == m_query.endRef)
+ {
+ // Cost
+ const float curCost = m_query.filter->getCost(bestNode->pos, neighbourNode->pos,
+ parentRef, parentTile, parentPoly,
+ bestRef, bestTile, bestPoly,
+ neighbourRef, neighbourTile, neighbourPoly);
+ const float endCost = m_query.filter->getCost(neighbourNode->pos, m_query.endPos,
+ bestRef, bestTile, bestPoly,
+ neighbourRef, neighbourTile, neighbourPoly,
+ 0, 0, 0);
+
+ cost = bestNode->cost + curCost + endCost;
+ heuristic = 0;
+ }
+ else
+ {
+ // Cost
+ const float curCost = m_query.filter->getCost(bestNode->pos, neighbourNode->pos,
+ parentRef, parentTile, parentPoly,
+ bestRef, bestTile, bestPoly,
+ neighbourRef, neighbourTile, neighbourPoly);
+ cost = bestNode->cost + curCost;
+ heuristic = dtVdist(neighbourNode->pos, m_query.endPos)*H_SCALE;
+ }
+
+ const float total = cost + heuristic;
+
+ // The node is already in open list and the new result is worse, skip.
+ if ((neighbourNode->flags & DT_NODE_OPEN) && total >= neighbourNode->total)
+ continue;
+ // The node is already visited and process, and the new result is worse, skip.
+ if ((neighbourNode->flags & DT_NODE_CLOSED) && total >= neighbourNode->total)
+ continue;
+
+ // Add or update the node.
+ neighbourNode->pidx = m_nodePool->getNodeIdx(bestNode);
+ neighbourNode->id = neighbourRef;
+ neighbourNode->flags &= ~DT_NODE_CLOSED;
+ neighbourNode->cost = cost;
+ neighbourNode->total = total;
+
+ if (neighbourNode->flags & DT_NODE_OPEN)
+ {
+ // Already in open, update node location.
+ m_openList->modify(neighbourNode);
+ }
+ else
+ {
+ // Put the node in open list.
+ neighbourNode->flags |= DT_NODE_OPEN;
+ m_openList->push(neighbourNode);
+ }
+
+ // Update nearest node to target so far.
+ if (heuristic < m_query.lastBestNodeCost)
+ {
+ m_query.lastBestNodeCost = heuristic;
+ m_query.lastBestNode = neighbourNode;
+ }
+ }
+ }
+
+ // Exhausted all nodes, but could not find path.
+ if (m_openList->empty())
+ m_query.status = DT_SUCCESS;
+
+ return m_query.status;
+}
+
+dtStatus dtNavMeshQuery::finalizeSlicedFindPath(dtPolyRef* path, int* pathCount, const int maxPath)
+{
+ *pathCount = 0;
+
+ if (m_query.status != DT_SUCCESS)
+ {
+ // Reset query.
+ memset(&m_query, 0, sizeof(dtQueryData));
+ return DT_FAILURE;
+ }
+
+ int n = 0;
+
+ if (m_query.startRef == m_query.endRef)
+ {
+ // Special case: the search starts and ends at same poly.
+ path[n++] = m_query.startRef;
+ }
+ else
+ {
+ // Reverse the path.
+ dtAssert(m_query.lastBestNode);
+ dtNode* prev = 0;
+ dtNode* node = m_query.lastBestNode;
+ do
+ {
+ dtNode* next = m_nodePool->getNodeAtIdx(node->pidx);
+ node->pidx = m_nodePool->getNodeIdx(prev);
+ prev = node;
+ node = next;
+ }
+ while (node);
+
+ // Store path
+ node = prev;
+ do
+ {
+ path[n++] = node->id;
+ node = m_nodePool->getNodeAtIdx(node->pidx);
+ }
+ while (node && n < maxPath);
+ }
+
+ // Reset query.
+ memset(&m_query, 0, sizeof(dtQueryData));
+
+ *pathCount = n;
+
+ return DT_SUCCESS;
+}
+
+dtStatus dtNavMeshQuery::finalizeSlicedFindPathPartial(const dtPolyRef* existing, const int existingSize,
+ dtPolyRef* path, int* pathCount, const int maxPath)
+{
+ *pathCount = 0;
+
+ if (existingSize == 0)
+ {
+ return DT_FAILURE;
+ }
+
+ if (m_query.status != DT_SUCCESS && m_query.status != DT_IN_PROGRESS)
+ {
+ // Reset query.
+ memset(&m_query, 0, sizeof(dtQueryData));
+ return DT_FAILURE;
+ }
+
+ int n = 0;
+
+ if (m_query.startRef == m_query.endRef)
+ {
+ // Special case: the search starts and ends at same poly.
+ path[n++] = m_query.startRef;
+ }
+ else
+ {
+ // Find furthest existing node that was visited.
+ dtNode* prev = 0;
+ dtNode* node = 0;
+ for (int i = existingSize-1; i >= 0; --i)
+ {
+ node = m_nodePool->findNode(existing[i]);
+ if (node)
+ break;
+ }
+
+ if (!node)
+ {
+ return DT_FAILURE;
+ }
+
+ // Reverse the path.
+ do
+ {
+ dtNode* next = m_nodePool->getNodeAtIdx(node->pidx);
+ node->pidx = m_nodePool->getNodeIdx(prev);
+ prev = node;
+ node = next;
+ }
+ while (node);
+
+ // Store path
+ node = prev;
+ do
+ {
+ path[n++] = node->id;
+ node = m_nodePool->getNodeAtIdx(node->pidx);
+ }
+ while (node && n < maxPath);
+ }
+
+ // Reset query.
+ memset(&m_query, 0, sizeof(dtQueryData));
+
+ *pathCount = n;
+
+ return DT_SUCCESS;
+}
+
+
+dtStatus dtNavMeshQuery::findStraightPath(const float* startPos, const float* endPos,
+ const dtPolyRef* path, const int pathSize,
+ float* straightPath, unsigned char* straightPathFlags, dtPolyRef* straightPathRefs,
+ int* straightPathCount, const int maxStraightPath) const
+{
+ dtAssert(m_nav);
+
+ *straightPathCount = 0;
+
+ if (!maxStraightPath)
+ return DT_FAILURE;
+
+ if (!path[0])
+ return DT_FAILURE;
+
+ int n = 0;
+
+ // TODO: Should this be callers responsibility?
+ float closestStartPos[3];
+ if (closestPointOnPolyBoundary(path[0], startPos, closestStartPos) != DT_SUCCESS)
+ return DT_FAILURE;
+
+ // Add start point.
+ dtVcopy(&straightPath[n*3], closestStartPos);
+ if (straightPathFlags)
+ straightPathFlags[n] = DT_STRAIGHTPATH_START;
+ if (straightPathRefs)
+ straightPathRefs[n] = path[0];
+ n++;
+ if (n >= maxStraightPath)
+ {
+ *straightPathCount = n;
+ return DT_SUCCESS;
+ }
+
+ float closestEndPos[3];
+ if (closestPointOnPolyBoundary(path[pathSize-1], endPos, closestEndPos) != DT_SUCCESS)
+ return DT_FAILURE;
+
+ if (pathSize > 1)
+ {
+ float portalApex[3], portalLeft[3], portalRight[3];
+ dtVcopy(portalApex, closestStartPos);
+ dtVcopy(portalLeft, portalApex);
+ dtVcopy(portalRight, portalApex);
+ int apexIndex = 0;
+ int leftIndex = 0;
+ int rightIndex = 0;
+
+ unsigned char leftPolyType = 0;
+ unsigned char rightPolyType = 0;
+
+ dtPolyRef leftPolyRef = path[0];
+ dtPolyRef rightPolyRef = path[0];
+
+ for (int i = 0; i < pathSize; ++i)
+ {
+ float left[3], right[3];
+ unsigned char fromType, toType;
+
+ if (i+1 < pathSize)
+ {
+ // Next portal.
+ if (getPortalPoints(path[i], path[i+1], left, right, fromType, toType) != DT_SUCCESS)
+ {
+ if (closestPointOnPolyBoundary(path[i], endPos, closestEndPos) != DT_SUCCESS)
+ return DT_FAILURE;
+
+ dtVcopy(&straightPath[n*3], closestEndPos);
+ if (straightPathFlags)
+ straightPathFlags[n] = 0;
+ if (straightPathRefs)
+ straightPathRefs[n] = path[i];
+ n++;
+
+ return DT_SUCCESS;
+ }
+
+ // If starting really close the portal, advance.
+ if (i == 0)
+ {
+ float t;
+ if (dtDistancePtSegSqr2D(portalApex, left, right, t) < dtSqr(0.001f))
+ continue;
+ }
+ }
+ else
+ {
+ // End of the path.
+ dtVcopy(left, closestEndPos);
+ dtVcopy(right, closestEndPos);
+
+ fromType = toType = DT_POLYTYPE_GROUND;
+ }
+
+ // Right vertex.
+ if (dtTriArea2D(portalApex, portalRight, right) <= 0.0f)
+ {
+ if (dtVequal(portalApex, portalRight) || dtTriArea2D(portalApex, portalLeft, right) > 0.0f)
+ {
+ dtVcopy(portalRight, right);
+ rightPolyRef = (i+1 < pathSize) ? path[i+1] : 0;
+ rightPolyType = toType;
+ rightIndex = i;
+ }
+ else
+ {
+ dtVcopy(portalApex, portalLeft);
+ apexIndex = leftIndex;
+
+ unsigned char flags = 0;
+ if (!leftPolyRef)
+ flags = DT_STRAIGHTPATH_END;
+ else if (leftPolyType == DT_POLYTYPE_OFFMESH_CONNECTION)
+ flags = DT_STRAIGHTPATH_OFFMESH_CONNECTION;
+ dtPolyRef ref = leftPolyRef;
+
+ if (!dtVequal(&straightPath[(n-1)*3], portalApex))
+ {
+ // Append new vertex.
+ dtVcopy(&straightPath[n*3], portalApex);
+ if (straightPathFlags)
+ straightPathFlags[n] = flags;
+ if (straightPathRefs)
+ straightPathRefs[n] = ref;
+ n++;
+ // If reached end of path or there is no space to append more vertices, return.
+ if (flags == DT_STRAIGHTPATH_END || n >= maxStraightPath)
+ {
+ *straightPathCount = n;
+ return DT_SUCCESS;
+ }
+ }
+ else
+ {
+ // The vertices are equal, update flags and poly.
+ if (straightPathFlags)
+ straightPathFlags[n-1] = flags;
+ if (straightPathRefs)
+ straightPathRefs[n-1] = ref;
+ }
+
+ dtVcopy(portalLeft, portalApex);
+ dtVcopy(portalRight, portalApex);
+ leftIndex = apexIndex;
+ rightIndex = apexIndex;
+
+ // Restart
+ i = apexIndex;
+
+ continue;
+ }
+ }
+
+ // Left vertex.
+ if (dtTriArea2D(portalApex, portalLeft, left) >= 0.0f)
+ {
+ if (dtVequal(portalApex, portalLeft) || dtTriArea2D(portalApex, portalRight, left) < 0.0f)
+ {
+ dtVcopy(portalLeft, left);
+ leftPolyRef = (i+1 < pathSize) ? path[i+1] : 0;
+ leftPolyType = toType;
+ leftIndex = i;
+ }
+ else
+ {
+ dtVcopy(portalApex, portalRight);
+ apexIndex = rightIndex;
+
+ unsigned char flags = 0;
+ if (!rightPolyRef)
+ flags = DT_STRAIGHTPATH_END;
+ else if (rightPolyType == DT_POLYTYPE_OFFMESH_CONNECTION)
+ flags = DT_STRAIGHTPATH_OFFMESH_CONNECTION;
+ dtPolyRef ref = rightPolyRef;
+
+ if (!dtVequal(&straightPath[(n-1)*3], portalApex))
+ {
+ // Append new vertex.
+ dtVcopy(&straightPath[n*3], portalApex);
+ if (straightPathFlags)
+ straightPathFlags[n] = flags;
+ if (straightPathRefs)
+ straightPathRefs[n] = ref;
+ n++;
+ // If reached end of path or there is no space to append more vertices, return.
+ if (flags == DT_STRAIGHTPATH_END || n >= maxStraightPath)
+ {
+ *straightPathCount = n;
+ return DT_SUCCESS;
+ }
+ }
+ else
+ {
+ // The vertices are equal, update flags and poly.
+ if (straightPathFlags)
+ straightPathFlags[n-1] = flags;
+ if (straightPathRefs)
+ straightPathRefs[n-1] = ref;
+ }
+
+ dtVcopy(portalLeft, portalApex);
+ dtVcopy(portalRight, portalApex);
+ leftIndex = apexIndex;
+ rightIndex = apexIndex;
+
+ // Restart
+ i = apexIndex;
+
+ continue;
+ }
+ }
+ }
+ }
+
+ // If the point already exists, remove it and add reappend the actual end location.
+ if (n > 0 && dtVequal(&straightPath[(n-1)*3], closestEndPos))
+ n--;
+
+ // Add end point.
+ if (n < maxStraightPath)
+ {
+ dtVcopy(&straightPath[n*3], closestEndPos);
+ if (straightPathFlags)
+ straightPathFlags[n] = DT_STRAIGHTPATH_END;
+ if (straightPathRefs)
+ straightPathRefs[n] = 0;
+ n++;
+ }
+
+ *straightPathCount = n;
+ return DT_SUCCESS;
+}
+
+dtStatus dtNavMeshQuery::moveAlongSurface(dtPolyRef startRef, const float* startPos, const float* endPos,
+ const dtQueryFilter* filter,
+ float* resultPos, dtPolyRef* visited, int* visitedCount, const int maxVisitedSize) const
+{
+ dtAssert(m_nav);
+ dtAssert(m_tinyNodePool);
+
+ *visitedCount = 0;
+
+ // Validate input
+ if (!startRef) return DT_FAILURE;
+ if (!m_nav->isValidPolyRef(startRef)) return DT_FAILURE;
+
+ static const int MAX_STACK = 48;
+ dtNode* stack[MAX_STACK];
+ int nstack = 0;
+
+ m_tinyNodePool->clear();
+
+ dtNode* startNode = m_tinyNodePool->getNode(startRef);
+ startNode->pidx = 0;
+ startNode->cost = 0;
+ startNode->total = 0;
+ startNode->id = startRef;
+ startNode->flags = DT_NODE_CLOSED;
+ stack[nstack++] = startNode;
+
+ float bestPos[3];
+ float bestDist = FLT_MAX;
+ dtNode* bestNode = 0;
+ dtVcopy(bestPos, startPos);
+
+ // Search constraints
+ float searchPos[3], searchRadSqr;
+ dtVlerp(searchPos, startPos, endPos, 0.5f);
+ searchRadSqr = dtSqr(dtVdist(startPos, endPos)/2.0f + 0.001f);
+
+ float verts[DT_VERTS_PER_POLYGON*3];
+
+ while (nstack)
+ {
+ // Pop front.
+ dtNode* curNode = stack[0];
+ for (int i = 0; i < nstack-1; ++i)
+ stack[i] = stack[i+1];
+ nstack--;
+
+ // Get poly and tile.
+ // The API input has been cheked already, skip checking internal data.
+ const dtPolyRef curRef = curNode->id;
+ const dtMeshTile* curTile = 0;
+ const dtPoly* curPoly = 0;
+ m_nav->getTileAndPolyByRefUnsafe(curRef, &curTile, &curPoly);
+
+ // Collect vertices.
+ const int nverts = curPoly->vertCount;
+ for (int i = 0; i < nverts; ++i)
+ dtVcopy(&verts[i*3], &curTile->verts[curPoly->verts[i]*3]);
+
+ // If target is inside the poly, stop search.
+ if (dtPointInPolygon(endPos, verts, nverts))
+ {
+ bestNode = curNode;
+ dtVcopy(bestPos, endPos);
+ break;
+ }
+
+ // Find wall edges and find nearest point inside the walls.
+ for (int i = 0, j = (int)curPoly->vertCount-1; i < (int)curPoly->vertCount; j = i++)
+ {
+ // Find links to neighbours.
+ static const int MAX_NEIS = 8;
+ int nneis = 0;
+ dtPolyRef neis[MAX_NEIS];
+
+ if (curPoly->neis[j] & DT_EXT_LINK)
+ {
+ // Tile border.
+ for (unsigned int k = curPoly->firstLink; k != DT_NULL_LINK; k = curTile->links[k].next)
+ {
+ const dtLink* link = &curTile->links[k];
+ if (link->edge == j)
+ {
+ if (link->ref != 0)
+ {
+ const dtMeshTile* neiTile = 0;
+ const dtPoly* neiPoly = 0;
+ m_nav->getTileAndPolyByRefUnsafe(link->ref, &neiTile, &neiPoly);
+ if (filter->passFilter(link->ref, neiTile, neiPoly))
+ {
+ if (nneis < MAX_NEIS)
+ neis[nneis++] = link->ref;
+ }
+ }
+ }
+ }
+ }
+ else if (curPoly->neis[j])
+ {
+ const unsigned int idx = (unsigned int)(curPoly->neis[j]-1);
+ const dtPolyRef ref = m_nav->getPolyRefBase(curTile) | idx;
+ if (filter->passFilter(ref, curTile, &curTile->polys[idx]))
+ {
+ // Internal edge, encode id.
+ neis[nneis++] = ref;
+ }
+ }
+
+ if (!nneis)
+ {
+ // Wall edge, calc distance.
+ const float* vj = &verts[j*3];
+ const float* vi = &verts[i*3];
+ float tseg;
+ const float distSqr = dtDistancePtSegSqr2D(endPos, vj, vi, tseg);
+ if (distSqr < bestDist)
+ {
+ // Update nearest distance.
+ dtVlerp(bestPos, vj,vi, tseg);
+ bestDist = distSqr;
+ bestNode = curNode;
+ }
+ }
+ else
+ {
+ for (int k = 0; k < nneis; ++k)
+ {
+ // Skip if no node can be allocated.
+ dtNode* neighbourNode = m_tinyNodePool->getNode(neis[k]);
+ if (!neighbourNode)
+ continue;
+ // Skip if already visited.
+ if (neighbourNode->flags & DT_NODE_CLOSED)
+ continue;
+
+ // Skip the link if it is too far from search constraint.
+ // TODO: Maybe should use getPortalPoints(), but this one is way faster.
+ const float* vj = &verts[j*3];
+ const float* vi = &verts[i*3];
+ float tseg;
+ float distSqr = dtDistancePtSegSqr2D(searchPos, vj, vi, tseg);
+ if (distSqr > searchRadSqr)
+ continue;
+
+ // Mark as the node as visited and push to queue.
+ if (nstack < MAX_STACK)
+ {
+ neighbourNode->pidx = m_tinyNodePool->getNodeIdx(curNode);
+ neighbourNode->flags |= DT_NODE_CLOSED;
+ stack[nstack++] = neighbourNode;
+ }
+ }
+ }
+ }
+ }
+
+ int n = 0;
+ if (bestNode)
+ {
+ // Reverse the path.
+ dtNode* prev = 0;
+ dtNode* node = bestNode;
+ do
+ {
+ dtNode* next = m_tinyNodePool->getNodeAtIdx(node->pidx);
+ node->pidx = m_tinyNodePool->getNodeIdx(prev);
+ prev = node;
+ node = next;
+ }
+ while (node);
+
+ // Store result
+ node = prev;
+ do
+ {
+ visited[n++] = node->id;
+ node = m_tinyNodePool->getNodeAtIdx(node->pidx);
+ }
+ while (node && n < maxVisitedSize);
+ }
+
+ dtVcopy(resultPos, bestPos);
+
+ *visitedCount = n;
+
+ return DT_SUCCESS;
+}
+
+
+dtStatus dtNavMeshQuery::getPortalPoints(dtPolyRef from, dtPolyRef to, float* left, float* right,
+ unsigned char& fromType, unsigned char& toType) const
+{
+ dtAssert(m_nav);
+
+ const dtMeshTile* fromTile = 0;
+ const dtPoly* fromPoly = 0;
+ if (m_nav->getTileAndPolyByRef(from, &fromTile, &fromPoly) != DT_SUCCESS)
+ return DT_FAILURE;
+ fromType = fromPoly->getType();
+
+ const dtMeshTile* toTile = 0;
+ const dtPoly* toPoly = 0;
+ if (m_nav->getTileAndPolyByRef(to, &toTile, &toPoly) != DT_SUCCESS)
+ return DT_FAILURE;
+ toType = toPoly->getType();
+
+ return getPortalPoints(from, fromPoly, fromTile, to, toPoly, toTile, left, right);
+}
+
+// Returns portal points between two polygons.
+dtStatus dtNavMeshQuery::getPortalPoints(dtPolyRef from, const dtPoly* fromPoly, const dtMeshTile* fromTile,
+ dtPolyRef to, const dtPoly* toPoly, const dtMeshTile* toTile,
+ float* left, float* right) const
+{
+ // Find the link that points to the 'to' polygon.
+ const dtLink* link = 0;
+ for (unsigned int i = fromPoly->firstLink; i != DT_NULL_LINK; i = fromTile->links[i].next)
+ {
+ if (fromTile->links[i].ref == to)
+ {
+ link = &fromTile->links[i];
+ break;
+ }
+ }
+ if (!link)
+ return DT_FAILURE;
+
+ // Handle off-mesh connections.
+ if (fromPoly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION)
+ {
+ // Find link that points to first vertex.
+ for (unsigned int i = fromPoly->firstLink; i != DT_NULL_LINK; i = fromTile->links[i].next)
+ {
+ if (fromTile->links[i].ref == to)
+ {
+ const int v = fromTile->links[i].edge;
+ dtVcopy(left, &fromTile->verts[fromPoly->verts[v]*3]);
+ dtVcopy(right, &fromTile->verts[fromPoly->verts[v]*3]);
+ return DT_SUCCESS;
+ }
+ }
+ return DT_FAILURE;
+ }
+
+ if (toPoly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION)
+ {
+ for (unsigned int i = toPoly->firstLink; i != DT_NULL_LINK; i = toTile->links[i].next)
+ {
+ if (toTile->links[i].ref == from)
+ {
+ const int v = toTile->links[i].edge;
+ dtVcopy(left, &toTile->verts[toPoly->verts[v]*3]);
+ dtVcopy(right, &toTile->verts[toPoly->verts[v]*3]);
+ return DT_SUCCESS;
+ }
+ }
+ return DT_FAILURE;
+ }
+
+ // Find portal vertices.
+ const int v0 = fromPoly->verts[link->edge];
+ const int v1 = fromPoly->verts[(link->edge+1) % (int)fromPoly->vertCount];
+ dtVcopy(left, &fromTile->verts[v0*3]);
+ dtVcopy(right, &fromTile->verts[v1*3]);
+
+ // If the link is at tile boundary, dtClamp the vertices to
+ // the link width.
+ if (link->side != 0xff)
+ {
+ // Unpack portal limits.
+ if (link->bmin != 0 || link->bmax != 255)
+ {
+ const float s = 1.0f/255.0f;
+ const float tmin = link->bmin*s;
+ const float tmax = link->bmax*s;
+ dtVlerp(left, &fromTile->verts[v0*3], &fromTile->verts[v1*3], tmin);
+ dtVlerp(right, &fromTile->verts[v0*3], &fromTile->verts[v1*3], tmax);
+ }
+ }
+
+ return DT_SUCCESS;
+}
+
+// Returns edge mid point between two polygons.
+dtStatus dtNavMeshQuery::getEdgeMidPoint(dtPolyRef from, dtPolyRef to, float* mid) const
+{
+ float left[3], right[3];
+ unsigned char fromType, toType;
+ if (!getPortalPoints(from, to, left,right, fromType, toType)) return DT_FAILURE;
+ mid[0] = (left[0]+right[0])*0.5f;
+ mid[1] = (left[1]+right[1])*0.5f;
+ mid[2] = (left[2]+right[2])*0.5f;
+ return DT_SUCCESS;
+}
+
+dtStatus dtNavMeshQuery::getEdgeMidPoint(dtPolyRef from, const dtPoly* fromPoly, const dtMeshTile* fromTile,
+ dtPolyRef to, const dtPoly* toPoly, const dtMeshTile* toTile,
+ float* mid) const
+{
+ float left[3], right[3];
+ if (getPortalPoints(from, fromPoly, fromTile, to, toPoly, toTile, left, right) != DT_SUCCESS)
+ return DT_FAILURE;
+ mid[0] = (left[0]+right[0])*0.5f;
+ mid[1] = (left[1]+right[1])*0.5f;
+ mid[2] = (left[2]+right[2])*0.5f;
+ return DT_SUCCESS;
+}
+
+dtStatus dtNavMeshQuery::raycast(dtPolyRef startRef, const float* startPos, const float* endPos,
+ const dtQueryFilter* filter,
+ float* t, float* hitNormal, dtPolyRef* path, int* pathCount, const int maxPath) const
+{
+ dtAssert(m_nav);
+
+ *t = 0;
+ if (pathCount)
+ *pathCount = 0;
+
+ // Validate input
+ if (!startRef || !m_nav->isValidPolyRef(startRef))
+ return DT_FAILURE;
+
+ dtPolyRef curRef = startRef;
+ float verts[DT_VERTS_PER_POLYGON*3];
+ int n = 0;
+
+ hitNormal[0] = 0;
+ hitNormal[1] = 0;
+ hitNormal[2] = 0;
+
+ while (curRef)
+ {
+ // Cast ray against current polygon.
+
+ // The API input has been cheked already, skip checking internal data.
+ const dtMeshTile* tile = 0;
+ const dtPoly* poly = 0;
+ m_nav->getTileAndPolyByRefUnsafe(curRef, &tile, &poly);
+
+ // Collect vertices.
+ int nv = 0;
+ for (int i = 0; i < (int)poly->vertCount; ++i)
+ {
+ dtVcopy(&verts[nv*3], &tile->verts[poly->verts[i]*3]);
+ nv++;
+ }
+
+ float tmin, tmax;
+ int segMin, segMax;
+ if (!dtIntersectSegmentPoly2D(startPos, endPos, verts, nv, tmin, tmax, segMin, segMax))
+ {
+ // Could not hit the polygon, keep the old t and report hit.
+ if (pathCount)
+ *pathCount = n;
+ return DT_SUCCESS;
+ }
+ // Keep track of furthest t so far.
+ if (tmax > *t)
+ *t = tmax;
+
+ // Store visited polygons.
+ if (n < maxPath)
+ path[n++] = curRef;
+
+ // Ray end is completely inside the polygon.
+ if (segMax == -1)
+ {
+ *t = FLT_MAX;
+ if (pathCount)
+ *pathCount = n;
+ return DT_SUCCESS;
+ }
+
+ // Follow neighbours.
+ dtPolyRef nextRef = 0;
+
+ for (unsigned int i = poly->firstLink; i != DT_NULL_LINK; i = tile->links[i].next)
+ {
+ const dtLink* link = &tile->links[i];
+
+ // Find link which contains this edge.
+ if ((int)link->edge != segMax)
+ continue;
+
+ // Get pointer to the next polygon.
+ const dtMeshTile* nextTile = 0;
+ const dtPoly* nextPoly = 0;
+ m_nav->getTileAndPolyByRefUnsafe(link->ref, &nextTile, &nextPoly);
+
+ // Skip off-mesh connections.
+ if (nextPoly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION)
+ continue;
+
+ // Skip links based on filter.
+ if (!filter->passFilter(link->ref, nextTile, nextPoly))
+ continue;
+
+ // If the link is internal, just return the ref.
+ if (link->side == 0xff)
+ {
+ nextRef = link->ref;
+ break;
+ }
+
+ // If the link is at tile boundary,
+
+ // Check if the link spans the whole edge, and accept.
+ if (link->bmin == 0 && link->bmax == 255)
+ {
+ nextRef = link->ref;
+ break;
+ }
+
+ // Check for partial edge links.
+ const int v0 = poly->verts[link->edge];
+ const int v1 = poly->verts[(link->edge+1) % poly->vertCount];
+ const float* left = &tile->verts[v0*3];
+ const float* right = &tile->verts[v1*3];
+
+ // Check that the intersection lies inside the link portal.
+ if (link->side == 0 || link->side == 4)
+ {
+ // Calculate link size.
+ const float s = 1.0f/255.0f;
+ float lmin = left[2] + (right[2] - left[2])*(link->bmin*s);
+ float lmax = left[2] + (right[2] - left[2])*(link->bmax*s);
+ if (lmin > lmax) dtSwap(lmin, lmax);
+
+ // Find Z intersection.
+ float z = startPos[2] + (endPos[2]-startPos[2])*tmax;
+ if (z >= lmin && z <= lmax)
+ {
+ nextRef = link->ref;
+ break;
+ }
+ }
+ else if (link->side == 2 || link->side == 6)
+ {
+ // Calculate link size.
+ const float s = 1.0f/255.0f;
+ float lmin = left[0] + (right[0] - left[0])*(link->bmin*s);
+ float lmax = left[0] + (right[0] - left[0])*(link->bmax*s);
+ if (lmin > lmax) dtSwap(lmin, lmax);
+
+ // Find X intersection.
+ float x = startPos[0] + (endPos[0]-startPos[0])*tmax;
+ if (x >= lmin && x <= lmax)
+ {
+ nextRef = link->ref;
+ break;
+ }
+ }
+ }
+
+ if (!nextRef)
+ {
+ // No neighbour, we hit a wall.
+
+ // Calculate hit normal.
+ const int a = segMax;
+ const int b = segMax+1 < nv ? segMax+1 : 0;
+ const float* va = &verts[a*3];
+ const float* vb = &verts[b*3];
+ const float dx = vb[0] - va[0];
+ const float dz = vb[2] - va[2];
+ hitNormal[0] = dz;
+ hitNormal[1] = 0;
+ hitNormal[2] = -dx;
+ dtVnormalize(hitNormal);
+
+ if (pathCount)
+ *pathCount = n;
+ return DT_SUCCESS;
+ }
+
+ // No hit, advance to neighbour polygon.
+ curRef = nextRef;
+ }
+
+ if (pathCount)
+ *pathCount = n;
+
+ return DT_SUCCESS;
+}
+
+dtStatus dtNavMeshQuery::findPolysAroundCircle(dtPolyRef startRef, const float* centerPos, const float radius,
+ const dtQueryFilter* filter,
+ dtPolyRef* resultRef, dtPolyRef* resultParent, float* resultCost,
+ int* resultCount, const int maxResult) const
+{
+ dtAssert(m_nav);
+ dtAssert(m_nodePool);
+ dtAssert(m_openList);
+
+ *resultCount = 0;
+
+ // Validate input
+ if (!startRef) return DT_FAILURE;
+ if (!m_nav->isValidPolyRef(startRef)) return DT_FAILURE;
+
+ m_nodePool->clear();
+ m_openList->clear();
+
+ dtNode* startNode = m_nodePool->getNode(startRef);
+ dtVcopy(startNode->pos, centerPos);
+ startNode->pidx = 0;
+ startNode->cost = 0;
+ startNode->total = 0;
+ startNode->id = startRef;
+ startNode->flags = DT_NODE_OPEN;
+ m_openList->push(startNode);
+
+ int n = 0;
+ if (n < maxResult)
+ {
+ if (resultRef)
+ resultRef[n] = startNode->id;
+ if (resultParent)
+ resultParent[n] = 0;
+ if (resultCost)
+ resultCost[n] = 0;
+ ++n;
+ }
+
+ const float radiusSqr = dtSqr(radius);
+
+ while (!m_openList->empty())
+ {
+ dtNode* bestNode = m_openList->pop();
+ bestNode->flags &= ~DT_NODE_OPEN;
+ bestNode->flags |= DT_NODE_CLOSED;
+
+ // Get poly and tile.
+ // The API input has been cheked already, skip checking internal data.
+ const dtPolyRef bestRef = bestNode->id;
+ const dtMeshTile* bestTile = 0;
+ const dtPoly* bestPoly = 0;
+ m_nav->getTileAndPolyByRefUnsafe(bestRef, &bestTile, &bestPoly);
+
+ // Get parent poly and tile.
+ dtPolyRef parentRef = 0;
+ const dtMeshTile* parentTile = 0;
+ const dtPoly* parentPoly = 0;
+ if (bestNode->pidx)
+ parentRef = m_nodePool->getNodeAtIdx(bestNode->pidx)->id;
+ if (parentRef)
+ m_nav->getTileAndPolyByRefUnsafe(parentRef, &parentTile, &parentPoly);
+
+ for (unsigned int i = bestPoly->firstLink; i != DT_NULL_LINK; i = bestTile->links[i].next)
+ {
+ const dtLink* link = &bestTile->links[i];
+ dtPolyRef neighbourRef = link->ref;
+ // Skip invalid neighbours and do not follow back to parent.
+ if (!neighbourRef || neighbourRef == parentRef)
+ continue;
+
+ // Expand to neighbour
+ const dtMeshTile* neighbourTile = 0;
+ const dtPoly* neighbourPoly = 0;
+ m_nav->getTileAndPolyByRefUnsafe(neighbourRef, &neighbourTile, &neighbourPoly);
+
+ // Do not advance if the polygon is excluded by the filter.
+ if (!filter->passFilter(neighbourRef, neighbourTile, neighbourPoly))
+ continue;
+
+ // Find edge and calc distance to the edge.
+ float va[3], vb[3];
+ if (!getPortalPoints(bestRef, bestPoly, bestTile, neighbourRef, neighbourPoly, neighbourTile, va, vb))
+ continue;
+
+ // If the circle is not touching the next polygon, skip it.
+ float tseg;
+ float distSqr = dtDistancePtSegSqr2D(centerPos, va, vb, tseg);
+ if (distSqr > radiusSqr)
+ continue;
+
+ dtNode* neighbourNode = m_nodePool->getNode(neighbourRef);
+ if (!neighbourNode)
+ continue;
+
+ if (neighbourNode->flags & DT_NODE_CLOSED)
+ continue;
+
+ // Cost
+ if (neighbourNode->flags == 0)
+ dtVlerp(neighbourNode->pos, va, vb, 0.5f);
+
+ const float total = bestNode->total + dtVdist(bestNode->pos, neighbourNode->pos);
+
+ // The node is already in open list and the new result is worse, skip.
+ if ((neighbourNode->flags & DT_NODE_OPEN) && total >= neighbourNode->total)
+ continue;
+
+ neighbourNode->id = neighbourRef;
+ neighbourNode->flags &= ~DT_NODE_CLOSED;
+ neighbourNode->pidx = m_nodePool->getNodeIdx(bestNode);
+ neighbourNode->total = total;
+
+ if (neighbourNode->flags & DT_NODE_OPEN)
+ {
+ m_openList->modify(neighbourNode);
+ }
+ else
+ {
+ if (n < maxResult)
+ {
+ if (resultRef)
+ resultRef[n] = neighbourNode->id;
+ if (resultParent)
+ resultParent[n] = m_nodePool->getNodeAtIdx(neighbourNode->pidx)->id;
+ if (resultCost)
+ resultCost[n] = neighbourNode->total;
+ ++n;
+ }
+ neighbourNode->flags = DT_NODE_OPEN;
+ m_openList->push(neighbourNode);
+ }
+ }
+ }
+
+ *resultCount = n;
+
+ return DT_SUCCESS;
+}
+
+dtStatus dtNavMeshQuery::findPolysAroundShape(dtPolyRef startRef, const float* verts, const int nverts,
+ const dtQueryFilter* filter,
+ dtPolyRef* resultRef, dtPolyRef* resultParent, float* resultCost,
+ int* resultCount, const int maxResult) const
+{
+ dtAssert(m_nav);
+ dtAssert(m_nodePool);
+ dtAssert(m_openList);
+
+ *resultCount = 0;
+
+ // Validate input
+ if (!startRef) return DT_FAILURE;
+ if (!m_nav->isValidPolyRef(startRef)) return DT_FAILURE;
+
+ m_nodePool->clear();
+ m_openList->clear();
+
+ float centerPos[3] = {0,0,0};
+ for (int i = 0; i < nverts; ++i)
+ dtVadd(centerPos,centerPos,&verts[i*3]);
+ dtVscale(centerPos,centerPos,1.0f/nverts);
+
+ dtNode* startNode = m_nodePool->getNode(startRef);
+ dtVcopy(startNode->pos, centerPos);
+ startNode->pidx = 0;
+ startNode->cost = 0;
+ startNode->total = 0;
+ startNode->id = startRef;
+ startNode->flags = DT_NODE_OPEN;
+ m_openList->push(startNode);
+
+ int n = 0;
+ if (n < maxResult)
+ {
+ if (resultRef)
+ resultRef[n] = startNode->id;
+ if (resultParent)
+ resultParent[n] = 0;
+ if (resultCost)
+ resultCost[n] = 0;
+ ++n;
+ }
+
+ while (!m_openList->empty())
+ {
+ dtNode* bestNode = m_openList->pop();
+ bestNode->flags &= ~DT_NODE_OPEN;
+ bestNode->flags |= DT_NODE_CLOSED;
+
+ // Get poly and tile.
+ // The API input has been cheked already, skip checking internal data.
+ const dtPolyRef bestRef = bestNode->id;
+ const dtMeshTile* bestTile = 0;
+ const dtPoly* bestPoly = 0;
+ m_nav->getTileAndPolyByRefUnsafe(bestRef, &bestTile, &bestPoly);
+
+ // Get parent poly and tile.
+ dtPolyRef parentRef = 0;
+ const dtMeshTile* parentTile = 0;
+ const dtPoly* parentPoly = 0;
+ if (bestNode->pidx)
+ parentRef = m_nodePool->getNodeAtIdx(bestNode->pidx)->id;
+ if (parentRef)
+ m_nav->getTileAndPolyByRefUnsafe(parentRef, &parentTile, &parentPoly);
+
+ for (unsigned int i = bestPoly->firstLink; i != DT_NULL_LINK; i = bestTile->links[i].next)
+ {
+ const dtLink* link = &bestTile->links[i];
+ dtPolyRef neighbourRef = link->ref;
+ // Skip invalid neighbours and do not follow back to parent.
+ if (!neighbourRef || neighbourRef == parentRef)
+ continue;
+
+ // Expand to neighbour
+ const dtMeshTile* neighbourTile = 0;
+ const dtPoly* neighbourPoly = 0;
+ m_nav->getTileAndPolyByRefUnsafe(neighbourRef, &neighbourTile, &neighbourPoly);
+
+ // Do not advance if the polygon is excluded by the filter.
+ if (!filter->passFilter(neighbourRef, neighbourTile, neighbourPoly))
+ continue;
+
+ // Find edge and calc distance to the edge.
+ float va[3], vb[3];
+ if (!getPortalPoints(bestRef, bestPoly, bestTile, neighbourRef, neighbourPoly, neighbourTile, va, vb))
+ continue;
+
+ // If the poly is not touching the edge to the next polygon, skip the connection it.
+ float tmin, tmax;
+ int segMin, segMax;
+ if (!dtIntersectSegmentPoly2D(va, vb, verts, nverts, tmin, tmax, segMin, segMax))
+ continue;
+ if (tmin > 1.0f || tmax < 0.0f)
+ continue;
+
+ dtNode* neighbourNode = m_nodePool->getNode(neighbourRef);
+ if (!neighbourNode)
+ continue;
+
+ if (neighbourNode->flags & DT_NODE_CLOSED)
+ continue;
+
+ // Cost
+ if (neighbourNode->flags == 0)
+ dtVlerp(neighbourNode->pos, va, vb, 0.5f);
+
+ const float total = bestNode->total + dtVdist(bestNode->pos, neighbourNode->pos);
+
+ // The node is already in open list and the new result is worse, skip.
+ if ((neighbourNode->flags & DT_NODE_OPEN) && total >= neighbourNode->total)
+ continue;
+
+ neighbourNode->id = neighbourRef;
+ neighbourNode->flags &= ~DT_NODE_CLOSED;
+ neighbourNode->pidx = m_nodePool->getNodeIdx(bestNode);
+ neighbourNode->total = total;
+
+ if (neighbourNode->flags & DT_NODE_OPEN)
+ {
+ m_openList->modify(neighbourNode);
+ }
+ else
+ {
+ if (n < maxResult)
+ {
+ if (resultRef)
+ resultRef[n] = neighbourNode->id;
+ if (resultParent)
+ resultParent[n] = m_nodePool->getNodeAtIdx(neighbourNode->pidx)->id;
+ if (resultCost)
+ resultCost[n] = neighbourNode->total;
+ ++n;
+ }
+ neighbourNode->flags = DT_NODE_OPEN;
+ m_openList->push(neighbourNode);
+ }
+ }
+ }
+
+ *resultCount = n;
+
+ return DT_SUCCESS;
+}
+
+dtStatus dtNavMeshQuery::findLocalNeighbourhood(dtPolyRef startRef, const float* centerPos, const float radius,
+ const dtQueryFilter* filter,
+ dtPolyRef* resultRef, dtPolyRef* resultParent,
+ int* resultCount, const int maxResult) const
+{
+ dtAssert(m_nav);
+ dtAssert(m_tinyNodePool);
+
+ *resultCount = 0;
+
+ // Validate input
+ if (!startRef) return DT_FAILURE;
+ if (!m_nav->isValidPolyRef(startRef)) return DT_FAILURE;
+
+ static const int MAX_STACK = 48;
+ dtNode* stack[MAX_STACK];
+ int nstack = 0;
+
+ m_tinyNodePool->clear();
+
+ dtNode* startNode = m_tinyNodePool->getNode(startRef);
+ startNode->pidx = 0;
+ startNode->id = startRef;
+ startNode->flags = DT_NODE_CLOSED;
+ stack[nstack++] = startNode;
+
+ const float radiusSqr = dtSqr(radius);
+
+ float pa[DT_VERTS_PER_POLYGON*3];
+ float pb[DT_VERTS_PER_POLYGON*3];
+
+ int n = 0;
+ if (n < maxResult)
+ {
+ resultRef[n] = startNode->id;
+ if (resultParent)
+ resultParent[n] = 0;
+ ++n;
+ }
+
+ while (nstack)
+ {
+ // Pop front.
+ dtNode* curNode = stack[0];
+ for (int i = 0; i < nstack-1; ++i)
+ stack[i] = stack[i+1];
+ nstack--;
+
+ // Get poly and tile.
+ // The API input has been cheked already, skip checking internal data.
+ const dtPolyRef curRef = curNode->id;
+ const dtMeshTile* curTile = 0;
+ const dtPoly* curPoly = 0;
+ m_nav->getTileAndPolyByRefUnsafe(curRef, &curTile, &curPoly);
+
+ for (unsigned int i = curPoly->firstLink; i != DT_NULL_LINK; i = curTile->links[i].next)
+ {
+ const dtLink* link = &curTile->links[i];
+ dtPolyRef neighbourRef = link->ref;
+ // Skip invalid neighbours.
+ if (!neighbourRef)
+ continue;
+
+ // Skip if cannot alloca more nodes.
+ dtNode* neighbourNode = m_tinyNodePool->getNode(neighbourRef);
+ if (!neighbourNode)
+ continue;
+ // Skip visited.
+ if (neighbourNode->flags & DT_NODE_CLOSED)
+ continue;
+
+ // Expand to neighbour
+ const dtMeshTile* neighbourTile = 0;
+ const dtPoly* neighbourPoly = 0;
+ m_nav->getTileAndPolyByRefUnsafe(neighbourRef, &neighbourTile, &neighbourPoly);
+
+ // Skip off-mesh connections.
+ if (neighbourPoly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION)
+ continue;
+
+ // Do not advance if the polygon is excluded by the filter.
+ if (!filter->passFilter(neighbourRef, neighbourTile, neighbourPoly))
+ continue;
+
+ // Find edge and calc distance to the edge.
+ float va[3], vb[3];
+ if (!getPortalPoints(curRef, curPoly, curTile, neighbourRef, neighbourPoly, neighbourTile, va, vb))
+ continue;
+
+ // If the circle is not touching the next polygon, skip it.
+ float tseg;
+ float distSqr = dtDistancePtSegSqr2D(centerPos, va, vb, tseg);
+ if (distSqr > radiusSqr)
+ continue;
+
+ // Mark node visited, this is done before the overlap test so that
+ // we will not visit the poly again if the test fails.
+ neighbourNode->flags |= DT_NODE_CLOSED;
+ neighbourNode->pidx = m_tinyNodePool->getNodeIdx(curNode);
+
+ // Check that the polygon does not collide with existing polygons.
+
+ // Collect vertices of the neighbour poly.
+ const int npa = neighbourPoly->vertCount;
+ for (int k = 0; k < npa; ++k)
+ dtVcopy(&pa[k*3], &neighbourTile->verts[neighbourPoly->verts[k]*3]);
+
+ bool overlap = false;
+ for (int j = 0; j < n; ++j)
+ {
+ dtPolyRef pastRef = resultRef[j];
+
+ // Connected polys do not overlap.
+ bool connected = false;
+ for (unsigned int k = curPoly->firstLink; k != DT_NULL_LINK; k = curTile->links[k].next)
+ {
+ if (curTile->links[k].ref == pastRef)
+ {
+ connected = true;
+ break;
+ }
+ }
+ if (connected)
+ continue;
+
+ // Potentially overlapping.
+ const dtMeshTile* pastTile = 0;
+ const dtPoly* pastPoly = 0;
+ m_nav->getTileAndPolyByRefUnsafe(pastRef, &pastTile, &pastPoly);
+
+ // Get vertices and test overlap
+ const int npb = pastPoly->vertCount;
+ for (int k = 0; k < npb; ++k)
+ dtVcopy(&pb[k*3], &pastTile->verts[pastPoly->verts[k]*3]);
+
+ if (dtOverlapPolyPoly2D(pa,npa, pb,npb))
+ {
+ overlap = true;
+ break;
+ }
+ }
+ if (overlap)
+ continue;
+
+ // This poly is fine, store and advance to the poly.
+ if (n < maxResult)
+ {
+ resultRef[n] = neighbourRef;
+ if (resultParent)
+ resultParent[n] = curRef;
+ ++n;
+ }
+
+ if (nstack < MAX_STACK)
+ {
+ stack[nstack++] = neighbourNode;
+ }
+ }
+ }
+
+ *resultCount = n;
+
+ return DT_SUCCESS;
+}
+
+
+struct dtSegInterval
+{
+ short tmin, tmax;
+};
+
+static void insertInterval(dtSegInterval* ints, int& nints, const int maxInts,
+ const short tmin, const short tmax)
+{
+ if (nints+1 > maxInts) return;
+ // Find insertion point.
+ int idx = 0;
+ while (idx < nints)
+ {
+ if (tmax <= ints[idx].tmin)
+ break;
+ idx++;
+ }
+ // Move current results.
+ if (nints-idx)
+ memmove(ints+idx+1, ints+idx, sizeof(dtSegInterval)*(nints-idx));
+ // Store
+ ints[idx].tmin = tmin;
+ ints[idx].tmax = tmax;
+ nints++;
+}
+
+dtStatus dtNavMeshQuery::getPolyWallSegments(dtPolyRef ref, const dtQueryFilter* filter,
+ float* segments, int* segmentCount, const int maxSegments) const
+{
+ dtAssert(m_nav);
+
+ *segmentCount = 0;
+
+ const dtMeshTile* tile = 0;
+ const dtPoly* poly = 0;
+ if (m_nav->getTileAndPolyByRef(ref, &tile, &poly) != DT_SUCCESS)
+ return DT_FAILURE;
+
+ int n = 0;
+ static const int MAX_INTERVAL = 16;
+ dtSegInterval ints[MAX_INTERVAL];
+ int nints;
+
+ for (int i = 0, j = (int)poly->vertCount-1; i < (int)poly->vertCount; j = i++)
+ {
+ // Skip non-solid edges.
+ nints = 0;
+ if (poly->neis[j] & DT_EXT_LINK)
+ {
+ // Tile border.
+ for (unsigned int k = poly->firstLink; k != DT_NULL_LINK; k = tile->links[k].next)
+ {
+ const dtLink* link = &tile->links[k];
+ if (link->edge == j)
+ {
+ if (link->ref != 0)
+ {
+ const dtMeshTile* neiTile = 0;
+ const dtPoly* neiPoly = 0;
+ m_nav->getTileAndPolyByRefUnsafe(link->ref, &neiTile, &neiPoly);
+ if (filter->passFilter(link->ref, neiTile, neiPoly))
+ {
+ insertInterval(ints, nints, MAX_INTERVAL, link->bmin, link->bmax);
+ }
+ }
+ }
+ }
+ }
+ else if (poly->neis[j])
+ {
+ // Internal edge
+ const unsigned int idx = (unsigned int)(poly->neis[j]-1);
+ const dtPolyRef ref = m_nav->getPolyRefBase(tile) | idx;
+ if (filter->passFilter(ref, tile, &tile->polys[idx]))
+ continue;
+ }
+
+ // Add sentinels
+ insertInterval(ints, nints, MAX_INTERVAL, -1, 0);
+ insertInterval(ints, nints, MAX_INTERVAL, 255, 256);
+
+ // Store segment.
+ const float* vj = &tile->verts[poly->verts[j]*3];
+ const float* vi = &tile->verts[poly->verts[i]*3];
+ for (int k = 1; k < nints; ++k)
+ {
+ // Find the space inbetween the opening areas.
+ const int imin = ints[k-1].tmax;
+ const int imax = ints[k].tmin;
+ if (imin == imax) continue;
+ if (imin == 0 && imax == 255)
+ {
+ if (n < maxSegments)
+ {
+ float* seg = &segments[n*6];
+ n++;
+ dtVcopy(seg+0, vj);
+ dtVcopy(seg+3, vi);
+ }
+ }
+ else
+ {
+ const float tmin = imin/255.0f;
+ const float tmax = imax/255.0f;
+ if (n < maxSegments)
+ {
+ float* seg = &segments[n*6];
+ n++;
+ dtVlerp(seg+0, vj,vi, tmin);
+ dtVlerp(seg+3, vj,vi, tmax);
+ }
+ }
+ }
+ }
+
+ *segmentCount = n;
+
+ return DT_SUCCESS;
+}
+
+dtStatus dtNavMeshQuery::findDistanceToWall(dtPolyRef startRef, const float* centerPos, const float maxRadius,
+ const dtQueryFilter* filter,
+ float* hitDist, float* hitPos, float* hitNormal) const
+{
+ dtAssert(m_nav);
+ dtAssert(m_nodePool);
+ dtAssert(m_openList);
+
+ // Validate input
+ if (!startRef) return DT_FAILURE;
+ if (!m_nav->isValidPolyRef(startRef)) return DT_FAILURE;
+
+ m_nodePool->clear();
+ m_openList->clear();
+
+ dtNode* startNode = m_nodePool->getNode(startRef);
+ dtVcopy(startNode->pos, centerPos);
+ startNode->pidx = 0;
+ startNode->cost = 0;
+ startNode->total = 0;
+ startNode->id = startRef;
+ startNode->flags = DT_NODE_OPEN;
+ m_openList->push(startNode);
+
+ float radiusSqr = dtSqr(maxRadius);
+
+ while (!m_openList->empty())
+ {
+ dtNode* bestNode = m_openList->pop();
+ bestNode->flags &= ~DT_NODE_OPEN;
+ bestNode->flags |= DT_NODE_CLOSED;
+
+ // Get poly and tile.
+ // The API input has been cheked already, skip checking internal data.
+ const dtPolyRef bestRef = bestNode->id;
+ const dtMeshTile* bestTile = 0;
+ const dtPoly* bestPoly = 0;
+ m_nav->getTileAndPolyByRefUnsafe(bestRef, &bestTile, &bestPoly);
+
+ // Get parent poly and tile.
+ dtPolyRef parentRef = 0;
+ const dtMeshTile* parentTile = 0;
+ const dtPoly* parentPoly = 0;
+ if (bestNode->pidx)
+ parentRef = m_nodePool->getNodeAtIdx(bestNode->pidx)->id;
+ if (parentRef)
+ m_nav->getTileAndPolyByRefUnsafe(parentRef, &parentTile, &parentPoly);
+
+ // Hit test walls.
+ for (int i = 0, j = (int)bestPoly->vertCount-1; i < (int)bestPoly->vertCount; j = i++)
+ {
+ // Skip non-solid edges.
+ if (bestPoly->neis[j] & DT_EXT_LINK)
+ {
+ // Tile border.
+ bool solid = true;
+ for (unsigned int k = bestPoly->firstLink; k != DT_NULL_LINK; k = bestTile->links[k].next)
+ {
+ const dtLink* link = &bestTile->links[k];
+ if (link->edge == j)
+ {
+ if (link->ref != 0)
+ {
+ const dtMeshTile* neiTile = 0;
+ const dtPoly* neiPoly = 0;
+ m_nav->getTileAndPolyByRefUnsafe(link->ref, &neiTile, &neiPoly);
+ if (filter->passFilter(link->ref, neiTile, neiPoly))
+ solid = false;
+ }
+ break;
+ }
+ }
+ if (!solid) continue;
+ }
+ else if (bestPoly->neis[j])
+ {
+ // Internal edge
+ const unsigned int idx = (unsigned int)(bestPoly->neis[j]-1);
+ const dtPolyRef ref = m_nav->getPolyRefBase(bestTile) | idx;
+ if (filter->passFilter(ref, bestTile, &bestTile->polys[idx]))
+ continue;
+ }
+
+ // Calc distance to the edge.
+ const float* vj = &bestTile->verts[bestPoly->verts[j]*3];
+ const float* vi = &bestTile->verts[bestPoly->verts[i]*3];
+ float tseg;
+ float distSqr = dtDistancePtSegSqr2D(centerPos, vj, vi, tseg);
+
+ // Edge is too far, skip.
+ if (distSqr > radiusSqr)
+ continue;
+
+ // Hit wall, update radius.
+ radiusSqr = distSqr;
+ // Calculate hit pos.
+ hitPos[0] = vj[0] + (vi[0] - vj[0])*tseg;
+ hitPos[1] = vj[1] + (vi[1] - vj[1])*tseg;
+ hitPos[2] = vj[2] + (vi[2] - vj[2])*tseg;
+ }
+
+ for (unsigned int i = bestPoly->firstLink; i != DT_NULL_LINK; i = bestTile->links[i].next)
+ {
+ const dtLink* link = &bestTile->links[i];
+ dtPolyRef neighbourRef = link->ref;
+ // Skip invalid neighbours and do not follow back to parent.
+ if (!neighbourRef || neighbourRef == parentRef)
+ continue;
+
+ // Expand to neighbour.
+ const dtMeshTile* neighbourTile = 0;
+ const dtPoly* neighbourPoly = 0;
+ m_nav->getTileAndPolyByRefUnsafe(neighbourRef, &neighbourTile, &neighbourPoly);
+
+ // Skip off-mesh connections.
+ if (neighbourPoly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION)
+ continue;
+
+ // Calc distance to the edge.
+ const float* va = &bestTile->verts[bestPoly->verts[link->edge]*3];
+ const float* vb = &bestTile->verts[bestPoly->verts[(link->edge+1) % bestPoly->vertCount]*3];
+ float tseg;
+ float distSqr = dtDistancePtSegSqr2D(centerPos, va, vb, tseg);
+
+ // If the circle is not touching the next polygon, skip it.
+ if (distSqr > radiusSqr)
+ continue;
+
+ if (!filter->passFilter(neighbourRef, neighbourTile, neighbourPoly))
+ continue;
+
+ dtNode* neighbourNode = m_nodePool->getNode(neighbourRef);
+ if (!neighbourNode)
+ continue;
+
+ if (neighbourNode->flags & DT_NODE_CLOSED)
+ continue;
+
+ // Cost
+ if (neighbourNode->flags == 0)
+ {
+ getEdgeMidPoint(bestRef, bestPoly, bestTile,
+ neighbourRef, neighbourPoly, neighbourTile, neighbourNode->pos);
+ }
+
+ const float total = bestNode->total + dtVdist(bestNode->pos, neighbourNode->pos);
+
+ // The node is already in open list and the new result is worse, skip.
+ if ((neighbourNode->flags & DT_NODE_OPEN) && total >= neighbourNode->total)
+ continue;
+
+ neighbourNode->id = neighbourRef;
+ neighbourNode->flags &= ~DT_NODE_CLOSED;
+ neighbourNode->pidx = m_nodePool->getNodeIdx(bestNode);
+ neighbourNode->total = total;
+
+ if (neighbourNode->flags & DT_NODE_OPEN)
+ {
+ m_openList->modify(neighbourNode);
+ }
+ else
+ {
+ neighbourNode->flags |= DT_NODE_OPEN;
+ m_openList->push(neighbourNode);
+ }
+ }
+ }
+
+ // Calc hit normal.
+ dtVsub(hitNormal, centerPos, hitPos);
+ dtVnormalize(hitNormal);
+
+ *hitDist = sqrtf(radiusSqr);
+
+ return DT_SUCCESS;
+}
+
+bool dtNavMeshQuery::isInClosedList(dtPolyRef ref) const
+{
+ if (!m_nodePool) return false;
+ const dtNode* node = m_nodePool->findNode(ref);
+ return node && node->flags & DT_NODE_CLOSED;
+}
diff --git a/dep/recastnavigation/Detour/DetourNavMeshQuery.h b/dep/recastnavigation/Detour/DetourNavMeshQuery.h
new file mode 100644
index 0000000..f5046d8
--- /dev/null
+++ b/dep/recastnavigation/Detour/DetourNavMeshQuery.h
@@ -0,0 +1,407 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#ifndef DETOURNAVMESHQUERY_H
+#define DETOURNAVMESHQUERY_H
+
+#include "DetourNavMesh.h"
+
+
+// Define DT_VIRTUAL_QUERYFILTER if you wish to derive a custom filter from dtQueryFilter.
+// On certain platforms indirect or virtual function call is expensive. The default
+// setting is to use non-virtual functions, the actualy implementations of the functions
+// are declared as inline for maximum speed.
+
+//#define DT_VIRTUAL_QUERYFILTER 1
+
+// Class for polygon filtering and cost calculation during query operations.
+// - It is possible to derive a custom query filter from dtQueryFilter by overriding
+// the virtual functions passFilter() and getCost().
+// - Both functions should be as fast as possible. Use cached local copy of data
+// instead of accessing your own objects where possible.
+// - You do not need to adhere to the flags and cost logic provided by the default
+// implementation.
+// - In order for the A* to work properly, the cost should be proportional to
+// the travel distance. Using cost modifier less than 1.0 is likely to lead
+// to problems during pathfinding.
+class dtQueryFilter
+{
+ float m_areaCost[DT_MAX_AREAS]; // Array storing cost per area type, used by default implementation.
+ unsigned short m_includeFlags; // Include poly flags, used by default implementation.
+ unsigned short m_excludeFlags; // Exclude poly flags, used by default implementation.
+
+public:
+ dtQueryFilter();
+
+ // Returns true if the polygon is can visited.
+ // Params:
+ // ref - (in) reference to the polygon test.
+ // tile - (in) pointer to the tile of the polygon test.
+ // poly - (in) pointer to the polygon test.
+#ifdef DT_VIRTUAL_QUERYFILTER
+ virtual bool passFilter(const dtPolyRef ref,
+ const dtMeshTile* tile,
+ const dtPoly* poly) const;
+#else
+ bool passFilter(const dtPolyRef ref,
+ const dtMeshTile* tile,
+ const dtPoly* poly) const;
+#endif
+
+ // Returns cost to travel from 'pa' to 'pb'.'
+ // The segment is fully contained inside 'cur'.
+ // 'pa' lies on the edge between 'prev' and 'cur',
+ // 'pb' lies on the edge between 'cur' and 'next'.
+ // Params:
+ // pa - (in) segment start position.
+ // pb - (in) segment end position.
+ // prevRef, prevTile, prevPoly - (in) data describing the previous polygon, can be null.
+ // curRef, curTile, curPoly - (in) data describing the current polygon.
+ // nextRef, nextTile, nextPoly - (in) data describing the next polygon, can be null.
+#ifdef DT_VIRTUAL_QUERYFILTER
+ virtual float getCost(const float* pa, const float* pb,
+ const dtPolyRef prevRef, const dtMeshTile* prevTile, const dtPoly* prevPoly,
+ const dtPolyRef curRef, const dtMeshTile* curTile, const dtPoly* curPoly,
+ const dtPolyRef nextRef, const dtMeshTile* nextTile, const dtPoly* nextPoly) const;
+#else
+ float getCost(const float* pa, const float* pb,
+ const dtPolyRef prevRef, const dtMeshTile* prevTile, const dtPoly* prevPoly,
+ const dtPolyRef curRef, const dtMeshTile* curTile, const dtPoly* curPoly,
+ const dtPolyRef nextRef, const dtMeshTile* nextTile, const dtPoly* nextPoly) const;
+#endif
+
+ // Getters and setters for the default implementation data.
+ inline float getAreaCost(const int i) const { return m_areaCost[i]; }
+ inline void setAreaCost(const int i, const float cost) { m_areaCost[i] = cost; }
+
+ inline unsigned short getIncludeFlags() const { return m_includeFlags; }
+ inline void setIncludeFlags(const unsigned short flags) { m_includeFlags = flags; }
+
+ inline unsigned short getExcludeFlags() const { return m_excludeFlags; }
+ inline void setExcludeFlags(const unsigned short flags) { m_excludeFlags = flags; }
+};
+
+class dtNavMeshQuery
+{
+public:
+ dtNavMeshQuery();
+ ~dtNavMeshQuery();
+
+ // Initializes the nav mesh query.
+ // Params:
+ // nav - (in) pointer to navigation mesh data.
+ // maxNodes - (in) Maximum number of search nodes to use (max 65536).
+ // Returns: True if succeed, else false.
+ dtStatus init(const dtNavMesh* nav, const int maxNodes);
+
+ // Finds the nearest navigation polygon around the center location.
+ // Params:
+ // center[3] - (in) The center of the search box.
+ // extents[3] - (in) The extents of the search box.
+ // filter - (in) path polygon filter.
+ // nearestRef - (out) Reference to the nearest polygon.
+ // nearestPt[3] - (out, opt) The nearest point on found polygon, null if not needed.
+ // Returns: Reference identifier for the polygon, or 0 if no polygons found.
+ dtStatus findNearestPoly(const float* center, const float* extents,
+ const dtQueryFilter* filter,
+ dtPolyRef* nearestRef, float* nearestPt) const;
+
+ // Returns polygons which overlap the query box.
+ // Params:
+ // center[3] - (in) the center of the search box.
+ // extents[3] - (in) the extents of the search box.
+ // filter - (in) path polygon filter.
+ // polys - (out) array holding the search result.
+ // polyCount - (out) Number of polygons in search result array.
+ // maxPolys - (in) The max number of polygons the polys array can hold.
+ dtStatus queryPolygons(const float* center, const float* extents,
+ const dtQueryFilter* filter,
+ dtPolyRef* polys, int* polyCount, const int maxPolys) const;
+
+ // Finds path from start polygon to end polygon.
+ // If target polygon canno be reached through the navigation graph,
+ // the last node on the array is nearest node to the end polygon.
+ // Start end end positions are needed to calculate more accurate
+ // traversal cost at start end end polygons.
+ // Params:
+ // startRef - (in) ref to path start polygon.
+ // endRef - (in) ref to path end polygon.
+ // startPos[3] - (in) Path start location.
+ // endPos[3] - (in) Path end location.
+ // filter - (in) path polygon filter.
+ // path - (out) array holding the search result.
+ // pathCount - (out) Number of polygons in search result array.
+ // maxPath - (in) The max number of polygons the path array can hold. Must be at least 1.
+ dtStatus findPath(dtPolyRef startRef, dtPolyRef endRef,
+ const float* startPos, const float* endPos,
+ const dtQueryFilter* filter,
+ dtPolyRef* path, int* pathCount, const int maxPath) const;
+
+ // Intializes sliced path find query.
+ // Note 1: calling any other dtNavMeshQuery method before calling findPathEnd()
+ // may results in corrupted data!
+ // Note 2: The pointer to filter is store, and used in subsequent
+ // calls to updateSlicedFindPath().
+ // Params:
+ // startRef - (in) ref to path start polygon.
+ // endRef - (in) ref to path end polygon.
+ // startPos[3] - (in) Path start location.
+ // endPos[3] - (in) Path end location.
+ // filter - (in) path polygon filter.
+ dtStatus initSlicedFindPath(dtPolyRef startRef, dtPolyRef endRef,
+ const float* startPos, const float* endPos,
+ const dtQueryFilter* filter);
+
+ // Updates sliced path find query.
+ // Params:
+ // maxIter - (in) max number of iterations to update.
+ // Returns: Path query state.
+ dtStatus updateSlicedFindPath(const int maxIter);
+
+ // Finalizes sliced path find query and returns found path.
+ // path - (out) array holding the search result.
+ // pathCount - (out) Number of polygons in search result array.
+ // maxPath - (in) The max number of polygons the path array can hold.
+ dtStatus finalizeSlicedFindPath(dtPolyRef* path, int* pathCount, const int maxPath);
+
+ // Finalizes partial sliced path find query and returns path to the furthest
+ // polygon on the existing path that was visited during the search.
+ // existing - (out) Array of polygons in the existing path.
+ // existingSize - (out) Number of polygons in existing path array.
+ // path - (out) array holding the search result.
+ // pathCount - (out) Number of polygons in search result array.
+ // maxPath - (in) The max number of polygons the path array can hold.
+ dtStatus finalizeSlicedFindPathPartial(const dtPolyRef* existing, const int existingSize,
+ dtPolyRef* path, int* pathCount, const int maxPath);
+
+ // Finds a straight path from start to end locations within the corridor
+ // described by the path polygons.
+ // Start and end locations will be clamped on the corridor.
+ // The returned polygon references are point to polygon which was entered when
+ // a path point was added. For the end point, zero will be returned. This allows
+ // to match for example off-mesh link points to their representative polygons.
+ // Params:
+ // startPos[3] - (in) Path start location.
+ // endPo[3] - (in) Path end location.
+ // path - (in) Array of connected polygons describing the corridor.
+ // pathSize - (in) Number of polygons in path array.
+ // straightPath - (out) Points describing the straight path.
+ // straightPathFlags - (out, opt) Flags describing each point type, see dtStraightPathFlags.
+ // straightPathRefs - (out, opt) References to polygons at point locations.
+ // straightPathCount - (out) Number of points in the path.
+ // maxStraightPath - (in) The max number of points the straight path array can hold. Must be at least 1.
+ dtStatus findStraightPath(const float* startPos, const float* endPos,
+ const dtPolyRef* path, const int pathSize,
+ float* straightPath, unsigned char* straightPathFlags, dtPolyRef* straightPathRefs,
+ int* straightPathCount, const int maxStraightPath) const;
+
+ // Moves from startPos to endPos constrained to the navmesh.
+ // If the endPos is reachable, the resultPos will be endPos,
+ // or else the resultPos will be the nearest point in navmesh.
+ // Note: The resulting point is not projected to the ground, use getPolyHeight() to get height.
+ // Note: The algorithm is optimized for small delta movement and small number of polygons.
+ // Params:
+ // startRef - (in) ref to the polygon where startPos lies.
+ // startPos[3] - (in) start position of the mover.
+ // endPos[3] - (in) desired end position of the mover.
+ // filter - (in) path polygon filter.
+ // resultPos[3] - (out) new position of the mover.
+ // visited - (out) array of visited polygons.
+ // visitedCount - (out) Number of entries in the visited array.
+ // maxVisitedSize - (in) max number of polygons in the visited array.
+ dtStatus moveAlongSurface(dtPolyRef startRef, const float* startPos, const float* endPos,
+ const dtQueryFilter* filter,
+ float* resultPos, dtPolyRef* visited, int* visitedCount, const int maxVisitedSize) const;
+
+ // Casts 'walkability' ray along the navmesh surface from startPos towards the endPos.
+ // Params:
+ // startRef - (in) ref to the polygon where the start lies.
+ // startPos[3] - (in) start position of the query.
+ // endPos[3] - (in) end position of the query.
+ // t - (out) hit parameter along the segment, FLT_MAX if no hit.
+ // hitNormal[3] - (out) normal of the nearest hit.
+ // filter - (in) path polygon filter.
+ // path - (out,opt) visited path polygons.
+ // pathCount - (out,opt) Number of polygons visited.
+ // maxPath - (in) max number of polygons in the path array.
+ dtStatus raycast(dtPolyRef startRef, const float* startPos, const float* endPos,
+ const dtQueryFilter* filter,
+ float* t, float* hitNormal, dtPolyRef* path, int* pathCount, const int maxPath) const;
+
+ // Returns distance to nearest wall from the specified location.
+ // Params:
+ // startRef - (in) ref to the polygon where the center lies.
+ // centerPos[3] - (in) center if the query circle.
+ // maxRadius - (in) max search radius.
+ // filter - (in) path polygon filter.
+ // hitDist - (out) distance to nearest wall from the test location.
+ // hitPos[3] - (out) location of the nearest hit.
+ // hitNormal[3] - (out) normal of the nearest hit.
+ dtStatus findDistanceToWall(dtPolyRef startRef, const float* centerPos, const float maxRadius,
+ const dtQueryFilter* filter,
+ float* hitDist, float* hitPos, float* hitNormal) const;
+
+ // Finds polygons found along the navigation graph which touch the specified circle.
+ // Params:
+ // startRef - (in) ref to the polygon where the search starts.
+ // centerPos[3] - (in) center if the query circle.
+ // radius - (in) radius of the query circle.
+ // filter - (in) path polygon filter.
+ // resultRef - (out, opt) refs to the polygons touched by the circle.
+ // resultParent - (out, opt) parent of each result polygon.
+ // resultCost - (out, opt) search cost at each result polygon.
+ // resultCount - (out, opt) Number of results.
+ // maxResult - (int) maximum capacity of search results.
+ dtStatus findPolysAroundCircle(dtPolyRef startRef, const float* centerPos, const float radius,
+ const dtQueryFilter* filter,
+ dtPolyRef* resultRef, dtPolyRef* resultParent, float* resultCost,
+ int* resultCount, const int maxResult) const;
+
+ // Finds polygons found along the navigation graph which touch the convex polygon shape.
+ // Params:
+ // startRef - (in) ref to the polygon where the search starts.
+ // verts[3*n] - (in) vertices describing convex polygon shape (CCW).
+ // nverts - (in) number of vertices in the polygon.
+ // filter - (in) path polygon filter.
+ // resultRef - (out, opt) refs to the polygons touched by the circle.
+ // resultParent - (out, opt) parent of each result polygon.
+ // resultCost - (out, opt) search cost at each result polygon.
+ // resultCount - (out) number of results.
+ // maxResult - (int) maximum capacity of search results.
+ dtStatus findPolysAroundShape(dtPolyRef startRef, const float* verts, const int nverts,
+ const dtQueryFilter* filter,
+ dtPolyRef* resultRef, dtPolyRef* resultParent, float* resultCost,
+ int* resultCount, const int maxResult) const;
+
+ // Finds non-overlapping local neighbourhood around center location.
+ // Note: The algorithm is optimized for small query radius and small number of polygons.
+ // Params:
+ // startRef - (in) ref to the polygon where the search starts.
+ // centerPos[3] - (in) center if the query circle.
+ // radius - (in) radius of the query circle.
+ // filter - (in) path polygon filter.
+ // resultRef - (out) refs to the polygons touched by the circle.
+ // resultParent - (out, opt) parent of each result polygon.
+ // resultCount - (out) number of results.
+ // maxResult - (int) maximum capacity of search results.
+ dtStatus findLocalNeighbourhood(dtPolyRef startRef, const float* centerPos, const float radius,
+ const dtQueryFilter* filter,
+ dtPolyRef* resultRef, dtPolyRef* resultParent,
+ int* resultCount, const int maxResult) const;
+
+ // Returns wall segments of specified polygon.
+ // Params:
+ // ref - (in) ref to the polygon.
+ // filter - (in) path polygon filter.
+ // segments[6*maxSegments] - (out) wall segments (2 endpoints per segment).
+ // segmentCount - (out) number of wall segments.
+ // maxSegments - (in) max number of segments that can be stored in 'segments'.
+ dtStatus getPolyWallSegments(dtPolyRef ref, const dtQueryFilter* filter,
+ float* segments, int* segmentCount, const int maxSegments) const;
+
+ // Returns closest point on navigation polygon.
+ // Uses detail polygons to find the closest point to the navigation polygon surface.
+ // Params:
+ // ref - (in) ref to the polygon.
+ // pos[3] - (in) the point to check.
+ // closest[3] - (out) closest point.
+ // Returns: true if closest point found.
+ dtStatus closestPointOnPoly(dtPolyRef ref, const float* pos, float* closest) const;
+
+ // Returns closest point on navigation polygon boundary.
+ // Uses the navigation polygon boundary to snap the point to poly boundary
+ // if it is outside the polygon. Much faster than closestPointToPoly. Does not affect height.
+ // Params:
+ // ref - (in) ref to the polygon.
+ // pos[3] - (in) the point to check.
+ // closest[3] - (out) closest point.
+ // Returns: true if closest point found.
+ dtStatus closestPointOnPolyBoundary(dtPolyRef ref, const float* pos, float* closest) const;
+
+ // Returns start and end location of an off-mesh link polygon.
+ // Params:
+ // prevRef - (in) ref to the polygon before the link (used to select direction).
+ // polyRef - (in) ref to the off-mesh link polygon.
+ // startPos[3] - (out) start point of the link.
+ // endPos[3] - (out) end point of the link.
+ // Returns: true if link is found.
+ dtStatus getOffMeshConnectionPolyEndPoints(dtPolyRef prevRef, dtPolyRef polyRef, float* startPos, float* endPos) const;
+
+ // Returns height of the polygon at specified location.
+ // Params:
+ // ref - (in) ref to the polygon.
+ // pos[3] - (in) the point where to locate the height.
+ // height - (out) height at the location.
+ // Returns: true if over polygon.
+ dtStatus getPolyHeight(dtPolyRef ref, const float* pos, float* height) const;
+
+ // Returns true if poly reference ins in closed list.
+ bool isInClosedList(dtPolyRef ref) const;
+
+ class dtNodePool* getNodePool() const { return m_nodePool; }
+
+private:
+
+ // Returns neighbour tile based on side.
+ dtMeshTile* getNeighbourTileAt(int x, int y, int side) const;
+
+ // Queries polygons within a tile.
+ int queryPolygonsInTile(const dtMeshTile* tile, const float* qmin, const float* qmax, const dtQueryFilter* filter,
+ dtPolyRef* polys, const int maxPolys) const;
+ // Find nearest polygon within a tile.
+ dtPolyRef findNearestPolyInTile(const dtMeshTile* tile, const float* center, const float* extents,
+ const dtQueryFilter* filter, float* nearestPt) const;
+ // Returns closest point on polygon.
+ dtStatus closestPointOnPolyInTile(const dtMeshTile* tile, const dtPoly* poly, const float* pos, float* closest) const;
+
+ // Returns portal points between two polygons.
+ dtStatus getPortalPoints(dtPolyRef from, dtPolyRef to, float* left, float* right,
+ unsigned char& fromType, unsigned char& toType) const;
+ dtStatus getPortalPoints(dtPolyRef from, const dtPoly* fromPoly, const dtMeshTile* fromTile,
+ dtPolyRef to, const dtPoly* toPoly, const dtMeshTile* toTile,
+ float* left, float* right) const;
+
+ // Returns edge mid point between two polygons.
+ dtStatus getEdgeMidPoint(dtPolyRef from, dtPolyRef to, float* mid) const;
+ dtStatus getEdgeMidPoint(dtPolyRef from, const dtPoly* fromPoly, const dtMeshTile* fromTile,
+ dtPolyRef to, const dtPoly* toPoly, const dtMeshTile* toTile,
+ float* mid) const;
+
+ const dtNavMesh* m_nav; // Pointer to navmesh data.
+
+ struct dtQueryData
+ {
+ dtStatus status;
+ struct dtNode* lastBestNode;
+ float lastBestNodeCost;
+ dtPolyRef startRef, endRef;
+ float startPos[3], endPos[3];
+ const dtQueryFilter* filter;
+ };
+ dtQueryData m_query; // Sliced query state.
+
+ class dtNodePool* m_tinyNodePool; // Pointer to small node pool.
+ class dtNodePool* m_nodePool; // Pointer to node pool.
+ class dtNodeQueue* m_openList; // Pointer to open list queue.
+};
+
+// Helper function to allocate navmesh query class using Detour allocator.
+dtNavMeshQuery* dtAllocNavMeshQuery();
+void dtFreeNavMeshQuery(dtNavMeshQuery* query);
+
+#endif // DETOURNAVMESHQUERY_H
diff --git a/dep/recastnavigation/Detour/DetourNode.cpp b/dep/recastnavigation/Detour/DetourNode.cpp
new file mode 100644
index 0000000..0d1af83
--- /dev/null
+++ b/dep/recastnavigation/Detour/DetourNode.cpp
@@ -0,0 +1,164 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#include "DetourNode.h"
+#include "DetourAlloc.h"
+#include "DetourAssert.h"
+#include "DetourCommon.h"
+#include <string.h>
+
+inline unsigned int dtHashRef(dtPolyRef a)
+{
+ a = (~a) + (a << 18);
+ a = a ^ (a >> 31);
+ a = a * 21;
+ a = a ^ (a >> 11);
+ a = a + (a << 6);
+ a = a ^ (a >> 22);
+ return (unsigned int)a;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+dtNodePool::dtNodePool(int maxNodes, int hashSize) :
+ m_nodes(0),
+ m_first(0),
+ m_next(0),
+ m_maxNodes(maxNodes),
+ m_hashSize(hashSize),
+ m_nodeCount(0)
+{
+ dtAssert(dtNextPow2(m_hashSize) == (unsigned int)m_hashSize);
+ dtAssert(m_maxNodes > 0);
+
+ m_nodes = (dtNode*)dtAlloc(sizeof(dtNode)*m_maxNodes, DT_ALLOC_PERM);
+ m_next = (unsigned short*)dtAlloc(sizeof(unsigned short)*m_maxNodes, DT_ALLOC_PERM);
+ m_first = (unsigned short*)dtAlloc(sizeof(unsigned short)*hashSize, DT_ALLOC_PERM);
+
+ dtAssert(m_nodes);
+ dtAssert(m_next);
+ dtAssert(m_first);
+
+ memset(m_first, 0xff, sizeof(unsigned short)*m_hashSize);
+ memset(m_next, 0xff, sizeof(unsigned short)*m_maxNodes);
+}
+
+dtNodePool::~dtNodePool()
+{
+ dtFree(m_nodes);
+ dtFree(m_next);
+ dtFree(m_first);
+}
+
+void dtNodePool::clear()
+{
+ memset(m_first, 0xff, sizeof(unsigned short)*m_hashSize);
+ m_nodeCount = 0;
+}
+
+dtNode* dtNodePool::findNode(dtPolyRef id)
+{
+ unsigned int bucket = dtHashRef(id) & (m_hashSize-1);
+ unsigned short i = m_first[bucket];
+ while (i != DT_NULL_IDX)
+ {
+ if (m_nodes[i].id == id)
+ return &m_nodes[i];
+ i = m_next[i];
+ }
+ return 0;
+}
+
+dtNode* dtNodePool::getNode(dtPolyRef id)
+{
+ unsigned int bucket = dtHashRef(id) & (m_hashSize-1);
+ unsigned short i = m_first[bucket];
+ dtNode* node = 0;
+ while (i != DT_NULL_IDX)
+ {
+ if (m_nodes[i].id == id)
+ return &m_nodes[i];
+ i = m_next[i];
+ }
+
+ if (m_nodeCount >= m_maxNodes)
+ return 0;
+
+ i = (unsigned short)m_nodeCount;
+ m_nodeCount++;
+
+ // Init node
+ node = &m_nodes[i];
+ node->pidx = 0;
+ node->cost = 0;
+ node->total = 0;
+ node->id = id;
+ node->flags = 0;
+
+ m_next[i] = m_first[bucket];
+ m_first[bucket] = i;
+
+ return node;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////
+dtNodeQueue::dtNodeQueue(int n) :
+ m_heap(0),
+ m_capacity(n),
+ m_size(0)
+{
+ dtAssert(m_capacity > 0);
+
+ m_heap = (dtNode**)dtAlloc(sizeof(dtNode*)*(m_capacity+1), DT_ALLOC_PERM);
+ dtAssert(m_heap);
+}
+
+dtNodeQueue::~dtNodeQueue()
+{
+ dtFree(m_heap);
+}
+
+void dtNodeQueue::bubbleUp(int i, dtNode* node)
+{
+ int parent = (i-1)/2;
+ // note: (index > 0) means there is a parent
+ while ((i > 0) && (m_heap[parent]->total > node->total))
+ {
+ m_heap[i] = m_heap[parent];
+ i = parent;
+ parent = (i-1)/2;
+ }
+ m_heap[i] = node;
+}
+
+void dtNodeQueue::trickleDown(int i, dtNode* node)
+{
+ int child = (i*2)+1;
+ while (child < m_size)
+ {
+ if (((child+1) < m_size) &&
+ (m_heap[child]->total > m_heap[child+1]->total))
+ {
+ child++;
+ }
+ m_heap[i] = m_heap[child];
+ i = child;
+ child = (i*2)+1;
+ }
+ bubbleUp(i, node);
+}
diff --git a/dep/recastnavigation/Detour/DetourNode.h b/dep/recastnavigation/Detour/DetourNode.h
new file mode 100644
index 0000000..e893f78
--- /dev/null
+++ b/dep/recastnavigation/Detour/DetourNode.h
@@ -0,0 +1,157 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#ifndef DETOURNODE_H
+#define DETOURNODE_H
+
+#include "DetourNavMesh.h"
+
+enum dtNodeFlags
+{
+ DT_NODE_OPEN = 0x01,
+ DT_NODE_CLOSED = 0x02,
+};
+
+static const unsigned short DT_NULL_IDX = 0xffff;
+
+struct dtNode
+{
+ float pos[3]; // Position of the node.
+ float cost; // Cost from previous node to current node.
+ float total; // Cost up to the node.
+ unsigned int pidx : 30; // Index to parent node.
+ unsigned int flags : 2; // Node flags 0/open/closed.
+ dtPolyRef id; // Polygon ref the node corresponds to.
+};
+
+class dtNodePool
+{
+public:
+ dtNodePool(int maxNodes, int hashSize);
+ ~dtNodePool();
+ inline void operator=(const dtNodePool&) {}
+ void clear();
+ dtNode* getNode(dtPolyRef id);
+ dtNode* findNode(dtPolyRef id);
+
+ inline unsigned int getNodeIdx(const dtNode* node) const
+ {
+ if (!node) return 0;
+ return (unsigned int)(node - m_nodes)+1;
+ }
+
+ inline dtNode* getNodeAtIdx(unsigned int idx)
+ {
+ if (!idx) return 0;
+ return &m_nodes[idx-1];
+ }
+
+ inline const dtNode* getNodeAtIdx(unsigned int idx) const
+ {
+ if (!idx) return 0;
+ return &m_nodes[idx-1];
+ }
+
+ inline int getMemUsed() const
+ {
+ return sizeof(*this) +
+ sizeof(dtNode)*m_maxNodes +
+ sizeof(unsigned short)*m_maxNodes +
+ sizeof(unsigned short)*m_hashSize;
+ }
+
+ inline int getMaxNodes() const { return m_maxNodes; }
+
+ inline int getHashSize() const { return m_hashSize; }
+ inline unsigned short getFirst(int bucket) const { return m_first[bucket]; }
+ inline unsigned short getNext(int i) const { return m_next[i]; }
+
+private:
+
+ dtNode* m_nodes;
+ unsigned short* m_first;
+ unsigned short* m_next;
+ const int m_maxNodes;
+ const int m_hashSize;
+ int m_nodeCount;
+};
+
+class dtNodeQueue
+{
+public:
+ dtNodeQueue(int n);
+ ~dtNodeQueue();
+ inline void operator=(dtNodeQueue&) {}
+
+ inline void clear()
+ {
+ m_size = 0;
+ }
+
+ inline dtNode* top()
+ {
+ return m_heap[0];
+ }
+
+ inline dtNode* pop()
+ {
+ dtNode* result = m_heap[0];
+ m_size--;
+ trickleDown(0, m_heap[m_size]);
+ return result;
+ }
+
+ inline void push(dtNode* node)
+ {
+ m_size++;
+ bubbleUp(m_size-1, node);
+ }
+
+ inline void modify(dtNode* node)
+ {
+ for (int i = 0; i < m_size; ++i)
+ {
+ if (m_heap[i] == node)
+ {
+ bubbleUp(i, node);
+ return;
+ }
+ }
+ }
+
+ inline bool empty() const { return m_size == 0; }
+
+ inline int getMemUsed() const
+ {
+ return sizeof(*this) +
+ sizeof(dtNode*)*(m_capacity+1);
+ }
+
+ inline int getCapacity() const { return m_capacity; }
+
+private:
+ void bubbleUp(int i, dtNode* node);
+ void trickleDown(int i, dtNode* node);
+
+ dtNode** m_heap;
+ const int m_capacity;
+ int m_size;
+};
+
+
+#endif // DETOURNODE_H
\ No newline at end of file
diff --git a/dep/recastnavigation/Detour/DetourObstacleAvoidance.cpp b/dep/recastnavigation/Detour/DetourObstacleAvoidance.cpp
new file mode 100644
index 0000000..a255c9b
--- /dev/null
+++ b/dep/recastnavigation/Detour/DetourObstacleAvoidance.cpp
@@ -0,0 +1,532 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#include "DetourObstacleAvoidance.h"
+#include "DetourCommon.h"
+#include "DetourAlloc.h"
+#include "DetourAssert.h"
+#include <string.h>
+#include <math.h>
+#include <float.h>
+#include <new>
+
+
+static int sweepCircleCircle(const float* c0, const float r0, const float* v,
+ const float* c1, const float r1,
+ float& tmin, float& tmax)
+{
+ static const float EPS = 0.0001f;
+ float s[3];
+ dtVsub(s,c1,c0);
+ float r = r0+r1;
+ float c = dtVdot2D(s,s) - r*r;
+ float a = dtVdot2D(v,v);
+ if (a < EPS) return 0; // not moving
+
+ // Overlap, calc time to exit.
+ float b = dtVdot2D(v,s);
+ float d = b*b - a*c;
+ if (d < 0.0f) return 0; // no intersection.
+ a = 1.0f / a;
+ const float rd = dtSqrt(d);
+ tmin = (b - rd) * a;
+ tmax = (b + rd) * a;
+ return 1;
+}
+
+static int isectRaySeg(const float* ap, const float* u,
+ const float* bp, const float* bq,
+ float& t)
+{
+ float v[3], w[3];
+ dtVsub(v,bq,bp);
+ dtVsub(w,ap,bp);
+ float d = dtVperp2D(u,v);
+ if (fabsf(d) < 1e-6f) return 0;
+ d = 1.0f/d;
+ t = dtVperp2D(v,w) * d;
+ if (t < 0 || t > 1) return 0;
+ float s = dtVperp2D(u,w) * d;
+ if (s < 0 || s > 1) return 0;
+ return 1;
+}
+
+
+
+dtObstacleAvoidanceDebugData* dtAllocObstacleAvoidanceDebugData()
+{
+ void* mem = dtAlloc(sizeof(dtObstacleAvoidanceDebugData), DT_ALLOC_PERM);
+ if (!mem) return 0;
+ return new(mem) dtObstacleAvoidanceDebugData;
+}
+
+void dtFreeObstacleAvoidanceDebugData(dtObstacleAvoidanceDebugData* ptr)
+{
+ if (!ptr) return;
+ ptr->~dtObstacleAvoidanceDebugData();
+ dtFree(ptr);
+}
+
+
+dtObstacleAvoidanceDebugData::dtObstacleAvoidanceDebugData() :
+ m_nsamples(0),
+ m_maxSamples(0),
+ m_vel(0),
+ m_ssize(0),
+ m_pen(0),
+ m_vpen(0),
+ m_vcpen(0),
+ m_spen(0),
+ m_tpen(0)
+{
+}
+
+dtObstacleAvoidanceDebugData::~dtObstacleAvoidanceDebugData()
+{
+ dtFree(m_vel);
+ dtFree(m_ssize);
+ dtFree(m_pen);
+ dtFree(m_vpen);
+ dtFree(m_vcpen);
+ dtFree(m_spen);
+ dtFree(m_tpen);
+}
+
+bool dtObstacleAvoidanceDebugData::init(const int maxSamples)
+{
+ dtAssert(maxSamples);
+ m_maxSamples = maxSamples;
+
+ m_vel = (float*)dtAlloc(sizeof(float)*3*m_maxSamples, DT_ALLOC_PERM);
+ if (!m_vel)
+ return false;
+ m_pen = (float*)dtAlloc(sizeof(float)*m_maxSamples, DT_ALLOC_PERM);
+ if (!m_pen)
+ return false;
+ m_ssize = (float*)dtAlloc(sizeof(float)*m_maxSamples, DT_ALLOC_PERM);
+ if (!m_ssize)
+ return false;
+ m_vpen = (float*)dtAlloc(sizeof(float)*m_maxSamples, DT_ALLOC_PERM);
+ if (!m_vpen)
+ return false;
+ m_vcpen = (float*)dtAlloc(sizeof(float)*m_maxSamples, DT_ALLOC_PERM);
+ if (!m_vcpen)
+ return false;
+ m_spen = (float*)dtAlloc(sizeof(float)*m_maxSamples, DT_ALLOC_PERM);
+ if (!m_spen)
+ return false;
+ m_tpen = (float*)dtAlloc(sizeof(float)*m_maxSamples, DT_ALLOC_PERM);
+ if (!m_tpen)
+ return false;
+
+ return true;
+}
+
+void dtObstacleAvoidanceDebugData::reset()
+{
+ m_nsamples = 0;
+}
+
+void dtObstacleAvoidanceDebugData::addSample(const float* vel, const float ssize, const float pen,
+ const float vpen, const float vcpen, const float spen, const float tpen)
+{
+ if (m_nsamples >= m_maxSamples)
+ return;
+ dtAssert(m_vel);
+ dtAssert(m_ssize);
+ dtAssert(m_pen);
+ dtAssert(m_vpen);
+ dtAssert(m_vcpen);
+ dtAssert(m_spen);
+ dtAssert(m_tpen);
+ dtVcopy(&m_vel[m_nsamples*3], vel);
+ m_ssize[m_nsamples] = ssize;
+ m_pen[m_nsamples] = pen;
+ m_vpen[m_nsamples] = vpen;
+ m_vcpen[m_nsamples] = vcpen;
+ m_spen[m_nsamples] = spen;
+ m_tpen[m_nsamples] = tpen;
+ m_nsamples++;
+}
+
+static void normalizeArray(float* arr, const int n)
+{
+ // Normalize penaly range.
+ float minPen = FLT_MAX;
+ float maxPen = -FLT_MAX;
+ for (int i = 0; i < n; ++i)
+ {
+ minPen = dtMin(minPen, arr[i]);
+ maxPen = dtMax(maxPen, arr[i]);
+ }
+ const float penRange = maxPen-minPen;
+ const float s = penRange > 0.001f ? (1.0f / penRange) : 1;
+ for (int i = 0; i < n; ++i)
+ arr[i] = dtClamp((arr[i]-minPen)*s, 0.0f, 1.0f);
+}
+
+void dtObstacleAvoidanceDebugData::normalizeSamples()
+{
+ normalizeArray(m_pen, m_nsamples);
+ normalizeArray(m_vpen, m_nsamples);
+ normalizeArray(m_vcpen, m_nsamples);
+ normalizeArray(m_spen, m_nsamples);
+ normalizeArray(m_tpen, m_nsamples);
+}
+
+
+dtObstacleAvoidanceQuery* dtAllocObstacleAvoidanceQuery()
+{
+ void* mem = dtAlloc(sizeof(dtObstacleAvoidanceQuery), DT_ALLOC_PERM);
+ if (!mem) return 0;
+ return new(mem) dtObstacleAvoidanceQuery;
+}
+
+void dtFreeObstacleAvoidanceQuery(dtObstacleAvoidanceQuery* ptr)
+{
+ if (!ptr) return;
+ ptr->~dtObstacleAvoidanceQuery();
+ dtFree(ptr);
+}
+
+
+dtObstacleAvoidanceQuery::dtObstacleAvoidanceQuery() :
+ m_velBias(0.0f),
+ m_weightDesVel(0.0f),
+ m_weightCurVel(0.0f),
+ m_weightSide(0.0f),
+ m_weightToi(0.0f),
+ m_horizTime(0.0f),
+ m_maxCircles(0),
+ m_circles(0),
+ m_ncircles(0),
+ m_maxSegments(0),
+ m_segments(0),
+ m_nsegments(0)
+{
+}
+
+dtObstacleAvoidanceQuery::~dtObstacleAvoidanceQuery()
+{
+ dtFree(m_circles);
+ dtFree(m_segments);
+}
+
+bool dtObstacleAvoidanceQuery::init(const int maxCircles, const int maxSegments)
+{
+ m_maxCircles = maxCircles;
+ m_ncircles = 0;
+ m_circles = (dtObstacleCircle*)dtAlloc(sizeof(dtObstacleCircle)*m_maxCircles, DT_ALLOC_PERM);
+ if (!m_circles)
+ return false;
+ memset(m_circles, 0, sizeof(dtObstacleCircle)*m_maxCircles);
+
+ m_maxSegments = maxSegments;
+ m_nsegments = 0;
+ m_segments = (dtObstacleSegment*)dtAlloc(sizeof(dtObstacleSegment)*m_maxSegments, DT_ALLOC_PERM);
+ if (!m_segments)
+ return false;
+ memset(m_segments, 0, sizeof(dtObstacleSegment)*m_maxSegments);
+
+ return true;
+}
+
+void dtObstacleAvoidanceQuery::reset()
+{
+ m_ncircles = 0;
+ m_nsegments = 0;
+}
+
+void dtObstacleAvoidanceQuery::addCircle(const float* pos, const float rad,
+ const float* vel, const float* dvel)
+{
+ if (m_ncircles >= m_maxCircles)
+ return;
+
+ dtObstacleCircle* cir = &m_circles[m_ncircles++];
+ dtVcopy(cir->p, pos);
+ cir->rad = rad;
+ dtVcopy(cir->vel, vel);
+ dtVcopy(cir->dvel, dvel);
+}
+
+void dtObstacleAvoidanceQuery::addSegment(const float* p, const float* q)
+{
+ if (m_nsegments > m_maxSegments)
+ return;
+
+ dtObstacleSegment* seg = &m_segments[m_nsegments++];
+ dtVcopy(seg->p, p);
+ dtVcopy(seg->q, q);
+}
+
+void dtObstacleAvoidanceQuery::prepare(const float* pos, const float* dvel)
+{
+ // Prepare obstacles
+ for (int i = 0; i < m_ncircles; ++i)
+ {
+ dtObstacleCircle* cir = &m_circles[i];
+
+ // Side
+ const float* pa = pos;
+ const float* pb = cir->p;
+
+ const float orig[3] = {0,0};
+ float dv[3];
+ dtVsub(cir->dp,pb,pa);
+ dtVnormalize(cir->dp);
+ dtVsub(dv, cir->dvel, dvel);
+
+ const float a = dtTriArea2D(orig, cir->dp,dv);
+ if (a < 0.01f)
+ {
+ cir->np[0] = -cir->dp[2];
+ cir->np[2] = cir->dp[0];
+ }
+ else
+ {
+ cir->np[0] = cir->dp[2];
+ cir->np[2] = -cir->dp[0];
+ }
+ }
+
+ for (int i = 0; i < m_nsegments; ++i)
+ {
+ dtObstacleSegment* seg = &m_segments[i];
+
+ // Precalc if the agent is really close to the segment.
+ const float r = 0.01f;
+ float t;
+ seg->touch = dtDistancePtSegSqr2D(pos, seg->p, seg->q, t) < dtSqr(r);
+ }
+}
+
+float dtObstacleAvoidanceQuery::processSample(const float* vcand, const float cs,
+ const float* pos, const float rad,
+ const float vmax, const float* vel, const float* dvel,
+ dtObstacleAvoidanceDebugData* debug)
+{
+ // Find min time of impact and exit amongst all obstacles.
+ float tmin = m_horizTime;
+ float side = 0;
+ int nside = 0;
+
+ for (int i = 0; i < m_ncircles; ++i)
+ {
+ const dtObstacleCircle* cir = &m_circles[i];
+
+ // RVO
+ float vab[3];
+ dtVscale(vab, vcand, 2);
+ dtVsub(vab, vab, vel);
+ dtVsub(vab, vab, cir->vel);
+
+ // Side
+ side += dtClamp(dtMin(dtVdot2D(cir->dp,vab)*0.5f+0.5f, dtVdot2D(cir->np,vab)*2), 0.0f, 1.0f);
+ nside++;
+
+ float htmin = 0, htmax = 0;
+ if (!sweepCircleCircle(pos,rad, vab, cir->p,cir->rad, htmin, htmax))
+ continue;
+
+ // Handle overlapping obstacles.
+ if (htmin < 0.0f && htmax > 0.0f)
+ {
+ // Avoid more when overlapped.
+ htmin = -htmin * 0.5f;
+ }
+
+ if (htmin >= 0.0f)
+ {
+ // The closest obstacle is somewhere ahead of us, keep track of nearest obstacle.
+ if (htmin < tmin)
+ tmin = htmin;
+ }
+ }
+
+ for (int i = 0; i < m_nsegments; ++i)
+ {
+ const dtObstacleSegment* seg = &m_segments[i];
+ float htmin = 0;
+
+ if (seg->touch)
+ {
+ // Special case when the agent is very close to the segment.
+ float sdir[3], snorm[3];
+ dtVsub(sdir, seg->q, seg->p);
+ snorm[0] = -sdir[2];
+ snorm[2] = sdir[0];
+ // If the velocity is pointing towards the segment, no collision.
+ if (dtVdot2D(snorm, vcand) < 0.0f)
+ continue;
+ // Else immediate collision.
+ htmin = 0.0f;
+ }
+ else
+ {
+ if (!isectRaySeg(pos, vcand, seg->p, seg->q, htmin))
+ continue;
+ }
+
+ // Avoid less when facing walls.
+ htmin *= 2.0f;
+
+ // The closest obstacle is somewhere ahead of us, keep track of nearest obstacle.
+ if (htmin < tmin)
+ tmin = htmin;
+ }
+
+ // Normalize side bias, to prevent it dominating too much.
+ if (nside)
+ side /= nside;
+
+ const float ivmax = 1.0f / vmax;
+ const float vpen = m_weightDesVel * (dtVdist2D(vcand, dvel) * ivmax);
+ const float vcpen = m_weightCurVel * (dtVdist2D(vcand, vel) * ivmax);
+ const float spen = m_weightSide * side;
+ const float tpen = m_weightToi * (1.0f/(0.1f+tmin / m_horizTime));
+
+ const float penalty = vpen + vcpen + spen + tpen;
+
+ // Store different penalties for debug viewing
+ if (debug)
+ debug->addSample(vcand, cs, penalty, vpen, vcpen, spen, tpen);
+
+ return penalty;
+}
+
+void dtObstacleAvoidanceQuery::sampleVelocityGrid(const float* pos, const float rad, const float vmax,
+ const float* vel, const float* dvel,
+ float* nvel, const int gsize,
+ dtObstacleAvoidanceDebugData* debug)
+{
+ prepare(pos, dvel);
+
+ dtVset(nvel, 0,0,0);
+
+ if (debug)
+ debug->reset();
+
+ const float cvx = dvel[0] * m_velBias;
+ const float cvz = dvel[2] * m_velBias;
+ const float cs = vmax * 2 * (1 - m_velBias) / (float)(gsize-1);
+ const float half = (gsize-1)*cs*0.5f;
+
+ float minPenalty = FLT_MAX;
+
+ for (int y = 0; y < gsize; ++y)
+ {
+ for (int x = 0; x < gsize; ++x)
+ {
+ float vcand[3];
+ vcand[0] = cvx + x*cs - half;
+ vcand[1] = 0;
+ vcand[2] = cvz + y*cs - half;
+
+ if (dtSqr(vcand[0])+dtSqr(vcand[2]) > dtSqr(vmax+cs/2)) continue;
+
+ const float penalty = processSample(vcand, cs, pos,rad,vmax,vel,dvel, debug);
+ if (penalty < minPenalty)
+ {
+ minPenalty = penalty;
+ dtVcopy(nvel, vcand);
+ }
+ }
+ }
+}
+
+
+static const float DT_PI = 3.14159265f;
+
+void dtObstacleAvoidanceQuery::sampleVelocityAdaptive(const float* pos, const float rad, const float vmax,
+ const float* vel, const float* dvel, float* nvel,
+ const int ndivs, const int nrings, const int depth,
+ dtObstacleAvoidanceDebugData* debug)
+{
+ prepare(pos, dvel);
+
+ dtVset(nvel, 0,0,0);
+
+ if (debug)
+ debug->reset();
+
+ // Build sampling pattern aligned to desired velocity.
+ static const int MAX_PATTERN_DIVS = 32;
+ static const int MAX_PATTERN_RINGS = 4;
+ float pat[(MAX_PATTERN_DIVS*MAX_PATTERN_RINGS+1)*2];
+ int npat = 0;
+
+ const int nd = dtClamp(ndivs, 1, MAX_PATTERN_DIVS);
+ const int nr = dtClamp(nrings, 1, MAX_PATTERN_RINGS);
+ const float da = (1.0f/nd) * DT_PI*2;
+ const float dang = atan2f(dvel[2], dvel[0]);
+
+ // Always add sample at zero
+ pat[npat*2+0] = 0;
+ pat[npat*2+1] = 0;
+ npat++;
+
+ for (int j = 0; j < nr; ++j)
+ {
+ const float rad = (float)(nr-j)/(float)nr;
+ float a = dang + (j&1)*0.5f*da;
+ for (int i = 0; i < nd; ++i)
+ {
+ pat[npat*2+0] = cosf(a)*rad;
+ pat[npat*2+1] = sinf(a)*rad;
+ npat++;
+ a += da;
+ }
+ }
+
+ // Start sampling.
+ float cr = vmax * (1.0f-m_velBias);
+ float res[3];
+ dtVset(res, dvel[0] * m_velBias, 0, dvel[2] * m_velBias);
+
+ for (int k = 0; k < depth; ++k)
+ {
+ float minPenalty = FLT_MAX;
+ float bvel[3];
+ dtVset(bvel, 0,0,0);
+
+ for (int i = 0; i < npat; ++i)
+ {
+ float vcand[3];
+ vcand[0] = res[0] + pat[i*2+0]*cr;
+ vcand[1] = 0;
+ vcand[2] = res[2] + pat[i*2+1]*cr;
+
+ if (dtSqr(vcand[0])+dtSqr(vcand[2]) > dtSqr(vmax+0.001f)) continue;
+
+ const float penalty = processSample(vcand,cr/10, pos,rad,vmax,vel,dvel, debug);
+ if (penalty < minPenalty)
+ {
+ minPenalty = penalty;
+ dtVcopy(bvel, vcand);
+ }
+ }
+
+ dtVcopy(res, bvel);
+
+ cr *= 0.5f;
+ }
+
+ dtVcopy(nvel, res);
+}
+
diff --git a/dep/recastnavigation/Detour/DetourObstacleAvoidance.h b/dep/recastnavigation/Detour/DetourObstacleAvoidance.h
new file mode 100644
index 0000000..4a7187a
--- /dev/null
+++ b/dep/recastnavigation/Detour/DetourObstacleAvoidance.h
@@ -0,0 +1,148 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#ifndef DETOUROBSTACLEAVOIDANCE_H
+#define DETOUROBSTACLEAVOIDANCE_H
+
+struct dtObstacleCircle
+{
+ float p[3]; // Position of the obstacle
+ float vel[3]; // Velocity of the obstacle
+ float dvel[3]; // Velocity of the obstacle
+ float rad; // Radius of the obstacle
+ float dp[3], np[3]; // Use for side selection during sampling.
+};
+
+struct dtObstacleSegment
+{
+ float p[3], q[3]; // End points of the obstacle segment
+ bool touch;
+};
+
+static const int RVO_SAMPLE_RAD = 15;
+static const int MAX_RVO_SAMPLES = (RVO_SAMPLE_RAD*2+1)*(RVO_SAMPLE_RAD*2+1) + 100;
+
+class dtObstacleAvoidanceDebugData
+{
+public:
+ dtObstacleAvoidanceDebugData();
+ ~dtObstacleAvoidanceDebugData();
+
+ bool init(const int maxSamples);
+ void reset();
+ void addSample(const float* vel, const float ssize, const float pen,
+ const float vpen, const float vcpen, const float spen, const float tpen);
+
+ void normalizeSamples();
+
+ inline int getSampleCount() const { return m_nsamples; }
+ inline const float* getSampleVelocity(const int i) const { return &m_vel[i*3]; }
+ inline float getSampleSize(const int i) const { return m_ssize[i]; }
+ inline float getSamplePenalty(const int i) const { return m_pen[i]; }
+ inline float getSampleDesiredVelocityPenalty(const int i) const { return m_vpen[i]; }
+ inline float getSampleCurrentVelocityPenalty(const int i) const { return m_vcpen[i]; }
+ inline float getSamplePreferredSidePenalty(const int i) const { return m_spen[i]; }
+ inline float getSampleCollisionTimePenalty(const int i) const { return m_tpen[i]; }
+
+private:
+ int m_nsamples;
+ int m_maxSamples;
+ float* m_vel;
+ float* m_ssize;
+ float* m_pen;
+ float* m_vpen;
+ float* m_vcpen;
+ float* m_spen;
+ float* m_tpen;
+};
+
+dtObstacleAvoidanceDebugData* dtAllocObstacleAvoidanceDebugData();
+void dtFreeObstacleAvoidanceDebugData(dtObstacleAvoidanceDebugData* ptr);
+
+
+class dtObstacleAvoidanceQuery
+{
+public:
+ dtObstacleAvoidanceQuery();
+ ~dtObstacleAvoidanceQuery();
+
+ bool init(const int maxCircles, const int maxSegments);
+
+ void reset();
+
+ void addCircle(const float* pos, const float rad,
+ const float* vel, const float* dvel);
+
+ void addSegment(const float* p, const float* q);
+
+ inline void setVelocitySelectionBias(float v) { m_velBias = v; }
+ inline void setDesiredVelocityWeight(float w) { m_weightDesVel = w; }
+ inline void setCurrentVelocityWeight(float w) { m_weightCurVel = w; }
+ inline void setPreferredSideWeight(float w) { m_weightSide = w; }
+ inline void setCollisionTimeWeight(float w) { m_weightToi = w; }
+ inline void setTimeHorizon(float t) { m_horizTime = t; }
+
+ void sampleVelocityGrid(const float* pos, const float rad, const float vmax,
+ const float* vel, const float* dvel, float* nvel,
+ const int gsize,
+ dtObstacleAvoidanceDebugData* debug = 0);
+
+ void sampleVelocityAdaptive(const float* pos, const float rad, const float vmax,
+ const float* vel, const float* dvel, float* nvel,
+ const int ndivs, const int nrings, const int depth,
+ dtObstacleAvoidanceDebugData* debug = 0);
+
+ inline int getObstacleCircleCount() const { return m_ncircles; }
+ const dtObstacleCircle* getObstacleCircle(const int i) { return &m_circles[i]; }
+
+ inline int getObstacleSegmentCount() const { return m_nsegments; }
+ const dtObstacleSegment* getObstacleSegment(const int i) { return &m_segments[i]; }
+
+private:
+
+ void prepare(const float* pos, const float* dvel);
+
+ float processSample(const float* vcand, const float cs,
+ const float* pos, const float rad,
+ const float vmax, const float* vel, const float* dvel,
+ dtObstacleAvoidanceDebugData* debug);
+
+ dtObstacleCircle* insertCircle(const float dist);
+ dtObstacleSegment* insertSegment(const float dist);
+
+ float m_velBias;
+ float m_weightDesVel;
+ float m_weightCurVel;
+ float m_weightSide;
+ float m_weightToi;
+ float m_horizTime;
+
+ int m_maxCircles;
+ dtObstacleCircle* m_circles;
+ int m_ncircles;
+
+ int m_maxSegments;
+ dtObstacleSegment* m_segments;
+ int m_nsegments;
+};
+
+dtObstacleAvoidanceQuery* dtAllocObstacleAvoidanceQuery();
+void dtFreeObstacleAvoidanceQuery(dtObstacleAvoidanceQuery* ptr);
+
+
+#endif // DETOUROBSTACLEAVOIDANCE_H
\ No newline at end of file
diff --git a/dep/recastnavigation/License.txt b/dep/recastnavigation/License.txt
new file mode 100644
index 0000000..95f4bfc
--- /dev/null
+++ b/dep/recastnavigation/License.txt
@@ -0,0 +1,18 @@
+Copyright (c) 2009 Mikko Mononen memon@inside.org
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must not
+claim that you wrote the original software. If you use this software
+in a product, an acknowledgment in the product documentation would be
+appreciated but is not required.
+2. Altered source versions must be plainly marked as such, and must not be
+misrepresented as being the original software.
+3. This notice may not be removed or altered from any source distribution.
+
diff --git a/dep/recastnavigation/Readme.txt b/dep/recastnavigation/Readme.txt
new file mode 100644
index 0000000..0c2f7b1
--- /dev/null
+++ b/dep/recastnavigation/Readme.txt
@@ -0,0 +1,120 @@
+
+Recast & Detour Version 1.4
+
+
+Recast
+
+Recast is state of the art navigation mesh construction toolset for games.
+
+ * It is automatic, which means that you can throw any level geometry
+ at it and you will get robust mesh out
+ * It is fast which means swift turnaround times for level designers
+ * It is open source so it comes with full source and you can
+ customize it to your hearts content.
+
+The Recast process starts with constructing a voxel mold from a level geometry
+and then casting a navigation mesh over it. The process consists of three steps,
+building the voxel mold, partitioning the mold into simple regions, peeling off
+the regions as simple polygons.
+
+ 1. The voxel mold is build from the input triangle mesh by rasterizing
+ the triangles into a multi-layer heightfield. Some simple filters are
+ then applied to the mold to prune out locations where the character
+ would not be able to move.
+ 2. The walkable areas described by the mold are divided into simple
+ overlayed 2D regions. The resulting regions have only one non-overlapping
+ contour, which simplifies the final step of the process tremendously.
+ 3. The navigation polygons are peeled off from the regions by first tracing
+ the boundaries and then simplifying them. The resulting polygons are
+ finally converted to convex polygons which makes them perfect for
+ pathfinding and spatial reasoning about the level.
+
+The toolset code is located in the Recast folder and demo application using the Recast
+toolset is located in the RecastDemo folder.
+
+The project files with this distribution can be compiled with Microsoft Visual C++ 2008
+(you can download it for free) and XCode 3.1.
+
+
+Detour
+
+Recast is accompanied with Detour, path-finding and spatial reasoning toolkit. You can use any navigation mesh with Detour, but of course the data generated with Recast fits perfectly.
+
+Detour offers simple static navigation mesh which is suitable for many simple cases, as well as tiled navigation mesh which allows you to plug in and out pieces of the mesh. The tiled mesh allows to create systems where you stream new navigation data in and out as the player progresses the level, or you may regenerate tiles as the world changes.
+
+
+Latest code available at http://code.google.com/p/recastnavigation/
+
+
+--
+
+Release Notes
+
+----------------
+* Recast 1.4
+ Released August 24th, 2009
+
+- Added detail height mesh generation (RecastDetailMesh.cpp) for single,
+ tiled statmeshes as well as tilemesh.
+- Added feature to contour tracing which detects extra vertices along
+ tile edges which should be removed later.
+- Changed the tiled stat mesh preprocess, so that it first generated
+ polymeshes per tile and finally combines them.
+- Fixed bug in the GUI code where invisible buttons could be pressed.
+
+----------------
+* Recast 1.31
+ Released July 24th, 2009
+
+- Better cost and heuristic functions.
+- Fixed tile navmesh raycast on tile borders.
+
+----------------
+* Recast 1.3
+ Released July 14th, 2009
+
+- Added dtTileNavMesh which allows to dynamically add and remove navmesh pieces at runtime.
+- Renamed stat navmesh types to dtStat* (i.e. dtPoly is now dtStatPoly).
+- Moved common code used by tile and stat navmesh to DetourNode.h/cpp and DetourCommon.h/cpp.
+- Refactores the demo code.
+
+----------------
+* Recast 1.2
+ Released June 17th, 2009
+
+- Added tiled mesh generation. The tiled generation allows to generate navigation for
+ much larger worlds, it removes some of the artifacts that comes from distance fields
+ in open areas, and allows later streaming and dynamic runtime generation
+- Improved and added some debug draw modes
+- API change: The helper function rcBuildNavMesh does not exists anymore,
+ had to change few internal things to cope with the tiled processing,
+ similar API functionality will be added later once the tiled process matures
+- The demo is getting way too complicated, need to split demos
+- Fixed several filtering functions so that the mesh is tighter to the geometry,
+ sometimes there could be up error up to tow voxel units close to walls,
+ now it should be just one.
+
+----------------
+* Recast 1.1
+ Released April 11th, 2009
+
+This is the first release of Detour.
+
+----------------
+* Recast 1.0
+ Released March 29th, 2009
+
+This is the first release of Recast.
+
+The process is not always as robust as I would wish. The watershed phase sometimes swallows tiny islands
+which are close to edges. These droppings are handled in rcBuildContours, but the code is not
+particularly robust either.
+
+Another non-robust case is when portal contours (contours shared between two regions) are always
+assumed to be straight. That can lead to overlapping contours specially when the level has
+large open areas.
+
+
+
+Mikko Mononen
+memon@inside.org
diff --git a/dep/recastnavigation/Recast/CMakeLists.txt b/dep/recastnavigation/Recast/CMakeLists.txt
new file mode 100644
index 0000000..7ced79c
--- /dev/null
+++ b/dep/recastnavigation/Recast/CMakeLists.txt
@@ -0,0 +1,31 @@
+# Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
+#
+# This file is free software; as a special exception the author gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+set(Recast_STAT_SRCS
+ Recast.cpp
+ RecastAlloc.cpp
+ RecastArea.cpp
+ RecastContour.cpp
+ RecastFilter.cpp
+ RecastMesh.cpp
+ RecastMeshDetail.cpp
+ RecastRasterization.cpp
+ RecastRegion.cpp
+)
+
+if(WIN32)
+ include_directories(
+ ${CMAKE_SOURCE_DIR}/dep/zlib
+ )
+endif()
+
+add_library(Recast STATIC ${Recast_STAT_SRCS})
+
+target_link_libraries(Recast ${ZLIB_LIBRARIES})
\ No newline at end of file
diff --git a/dep/recastnavigation/Recast/Recast.cpp b/dep/recastnavigation/Recast/Recast.cpp
new file mode 100644
index 0000000..d051418
--- /dev/null
+++ b/dep/recastnavigation/Recast/Recast.cpp
@@ -0,0 +1,423 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#include <float.h>
+#define _USE_MATH_DEFINES
+#include <math.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include "Recast.h"
+#include "RecastAlloc.h"
+#include "RecastAssert.h"
+
+float rcSqrt(float x)
+{
+ return sqrtf(x);
+}
+
+
+void rcContext::log(const rcLogCategory category, const char* format, ...)
+{
+ if (!m_logEnabled)
+ return;
+ static const int MSG_SIZE = 512;
+ char msg[MSG_SIZE];
+ va_list ap;
+ va_start(ap, format);
+ int len = vsnprintf(msg, MSG_SIZE, format, ap);
+ if (len >= MSG_SIZE)
+ {
+ len = MSG_SIZE-1;
+ msg[MSG_SIZE-1] = '\0';
+ }
+ va_end(ap);
+ doLog(category, msg, len);
+}
+
+rcHeightfield* rcAllocHeightfield()
+{
+ rcHeightfield* hf = (rcHeightfield*)rcAlloc(sizeof(rcHeightfield), RC_ALLOC_PERM);
+ memset(hf, 0, sizeof(rcHeightfield));
+ return hf;
+}
+
+void rcFreeHeightField(rcHeightfield* hf)
+{
+ if (!hf) return;
+ // Delete span array.
+ rcFree(hf->spans);
+ // Delete span pools.
+ while (hf->pools)
+ {
+ rcSpanPool* next = hf->pools->next;
+ rcFree(hf->pools);
+ hf->pools = next;
+ }
+ rcFree(hf);
+}
+
+rcCompactHeightfield* rcAllocCompactHeightfield()
+{
+ rcCompactHeightfield* chf = (rcCompactHeightfield*)rcAlloc(sizeof(rcCompactHeightfield), RC_ALLOC_PERM);
+ memset(chf, 0, sizeof(rcCompactHeightfield));
+ return chf;
+}
+
+void rcFreeCompactHeightfield(rcCompactHeightfield* chf)
+{
+ if (!chf) return;
+ rcFree(chf->cells);
+ rcFree(chf->spans);
+ rcFree(chf->dist);
+ rcFree(chf->areas);
+ rcFree(chf);
+}
+
+rcContourSet* rcAllocContourSet()
+{
+ rcContourSet* cset = (rcContourSet*)rcAlloc(sizeof(rcContourSet), RC_ALLOC_PERM);
+ memset(cset, 0, sizeof(rcContourSet));
+ return cset;
+}
+
+void rcFreeContourSet(rcContourSet* cset)
+{
+ if (!cset) return;
+ for (int i = 0; i < cset->nconts; ++i)
+ {
+ rcFree(cset->conts[i].verts);
+ rcFree(cset->conts[i].rverts);
+ }
+ rcFree(cset->conts);
+ rcFree(cset);
+}
+
+rcPolyMesh* rcAllocPolyMesh()
+{
+ rcPolyMesh* pmesh = (rcPolyMesh*)rcAlloc(sizeof(rcPolyMesh), RC_ALLOC_PERM);
+ memset(pmesh, 0, sizeof(rcPolyMesh));
+ return pmesh;
+}
+
+void rcFreePolyMesh(rcPolyMesh* pmesh)
+{
+ if (!pmesh) return;
+ rcFree(pmesh->verts);
+ rcFree(pmesh->polys);
+ rcFree(pmesh->regs);
+ rcFree(pmesh->flags);
+ rcFree(pmesh->areas);
+ rcFree(pmesh);
+}
+
+rcPolyMeshDetail* rcAllocPolyMeshDetail()
+{
+ rcPolyMeshDetail* dmesh = (rcPolyMeshDetail*)rcAlloc(sizeof(rcPolyMeshDetail), RC_ALLOC_PERM);
+ memset(dmesh, 0, sizeof(rcPolyMeshDetail));
+ return dmesh;
+}
+
+void rcFreePolyMeshDetail(rcPolyMeshDetail* dmesh)
+{
+ if (!dmesh) return;
+ rcFree(dmesh->meshes);
+ rcFree(dmesh->verts);
+ rcFree(dmesh->tris);
+ rcFree(dmesh);
+}
+
+
+void rcCalcBounds(const float* verts, int nv, float* bmin, float* bmax)
+{
+ // Calculate bounding box.
+ rcVcopy(bmin, verts);
+ rcVcopy(bmax, verts);
+ for (int i = 1; i < nv; ++i)
+ {
+ const float* v = &verts[i*3];
+ rcVmin(bmin, v);
+ rcVmax(bmax, v);
+ }
+}
+
+void rcCalcGridSize(const float* bmin, const float* bmax, float cs, int* w, int* h)
+{
+ *w = (int)((bmax[0] - bmin[0])/cs+0.5f);
+ *h = (int)((bmax[2] - bmin[2])/cs+0.5f);
+}
+
+bool rcCreateHeightfield(rcContext* /*ctx*/, rcHeightfield& hf, int width, int height,
+ const float* bmin, const float* bmax,
+ float cs, float ch)
+{
+ // TODO: VC complains about unref formal variable, figure out a way to handle this better.
+// rcAssert(ctx);
+
+ hf.width = width;
+ hf.height = height;
+ rcVcopy(hf.bmin, bmin);
+ rcVcopy(hf.bmax, bmax);
+ hf.cs = cs;
+ hf.ch = ch;
+ hf.spans = (rcSpan**)rcAlloc(sizeof(rcSpan*)*hf.width*hf.height, RC_ALLOC_PERM);
+ if (!hf.spans)
+ return false;
+ memset(hf.spans, 0, sizeof(rcSpan*)*hf.width*hf.height);
+ return true;
+}
+
+static void calcTriNormal(const float* v0, const float* v1, const float* v2, float* norm)
+{
+ float e0[3], e1[3];
+ rcVsub(e0, v1, v0);
+ rcVsub(e1, v2, v0);
+ rcVcross(norm, e0, e1);
+ rcVnormalize(norm);
+}
+
+void rcMarkWalkableTriangles(rcContext* /*ctx*/, const float walkableSlopeAngle,
+ const float* verts, int /*nv*/,
+ const int* tris, int nt,
+ unsigned char* areas)
+{
+ // TODO: VC complains about unref formal variable, figure out a way to handle this better.
+// rcAssert(ctx);
+
+ const float walkableThr = cosf(walkableSlopeAngle/180.0f*RC_PI);
+
+ float norm[3];
+
+ for (int i = 0; i < nt; ++i)
+ {
+ const int* tri = &tris[i*3];
+ calcTriNormal(&verts[tri[0]*3], &verts[tri[1]*3], &verts[tri[2]*3], norm);
+ // Check if the face is walkable.
+ if (norm[1] > walkableThr)
+ areas[i] = RC_WALKABLE_AREA;
+ }
+}
+
+void rcClearUnwalkableTriangles(rcContext* /*ctx*/, const float walkableSlopeAngle,
+ const float* verts, int /*nv*/,
+ const int* tris, int nt,
+ unsigned char* areas)
+{
+ // TODO: VC complains about unref formal variable, figure out a way to handle this better.
+// rcAssert(ctx);
+
+ const float walkableThr = cosf(walkableSlopeAngle/180.0f*RC_PI);
+
+ float norm[3];
+
+ for (int i = 0; i < nt; ++i)
+ {
+ const int* tri = &tris[i*3];
+ calcTriNormal(&verts[tri[0]*3], &verts[tri[1]*3], &verts[tri[2]*3], norm);
+ // Check if the face is walkable.
+ if (norm[1] <= walkableThr)
+ areas[i] = RC_NULL_AREA;
+ }
+}
+
+int rcGetHeightFieldSpanCount(rcContext* /*ctx*/, rcHeightfield& hf)
+{
+ // TODO: VC complains about unref formal variable, figure out a way to handle this better.
+// rcAssert(ctx);
+
+ const int w = hf.width;
+ const int h = hf.height;
+ int spanCount = 0;
+ for (int y = 0; y < h; ++y)
+ {
+ for (int x = 0; x < w; ++x)
+ {
+ for (rcSpan* s = hf.spans[x + y*w]; s; s = s->next)
+ {
+ if (s->area != RC_NULL_AREA)
+ spanCount++;
+ }
+ }
+ }
+ return spanCount;
+}
+
+bool rcBuildCompactHeightfield(rcContext* ctx, const int walkableHeight, const int walkableClimb,
+ rcHeightfield& hf, rcCompactHeightfield& chf)
+{
+ rcAssert(ctx);
+
+ ctx->startTimer(RC_TIMER_BUILD_COMPACTHEIGHTFIELD);
+
+ const int w = hf.width;
+ const int h = hf.height;
+ const int spanCount = rcGetHeightFieldSpanCount(ctx, hf);
+
+ // Fill in header.
+ chf.width = w;
+ chf.height = h;
+ chf.spanCount = spanCount;
+ chf.walkableHeight = walkableHeight;
+ chf.walkableClimb = walkableClimb;
+ chf.maxRegions = 0;
+ rcVcopy(chf.bmin, hf.bmin);
+ rcVcopy(chf.bmax, hf.bmax);
+ chf.bmax[1] += walkableHeight*hf.ch;
+ chf.cs = hf.cs;
+ chf.ch = hf.ch;
+ chf.cells = (rcCompactCell*)rcAlloc(sizeof(rcCompactCell)*w*h, RC_ALLOC_PERM);
+ if (!chf.cells)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Out of memory 'chf.cells' (%d)", w*h);
+ return false;
+ }
+ memset(chf.cells, 0, sizeof(rcCompactCell)*w*h);
+ chf.spans = (rcCompactSpan*)rcAlloc(sizeof(rcCompactSpan)*spanCount, RC_ALLOC_PERM);
+ if (!chf.spans)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Out of memory 'chf.spans' (%d)", spanCount);
+ return false;
+ }
+ memset(chf.spans, 0, sizeof(rcCompactSpan)*spanCount);
+ chf.areas = (unsigned char*)rcAlloc(sizeof(unsigned char)*spanCount, RC_ALLOC_PERM);
+ if (!chf.areas)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Out of memory 'chf.areas' (%d)", spanCount);
+ return false;
+ }
+ memset(chf.areas, RC_NULL_AREA, sizeof(unsigned char)*spanCount);
+
+ const int MAX_HEIGHT = 0xffff;
+
+ // Fill in cells and spans.
+ int idx = 0;
+ for (int y = 0; y < h; ++y)
+ {
+ for (int x = 0; x < w; ++x)
+ {
+ const rcSpan* s = hf.spans[x + y*w];
+ // If there are no spans at this cell, just leave the data to index=0, count=0.
+ if (!s) continue;
+ rcCompactCell& c = chf.cells[x+y*w];
+ c.index = idx;
+ c.count = 0;
+ while (s)
+ {
+ if (s->area != RC_NULL_AREA)
+ {
+ const int bot = (int)s->smax;
+ const int top = s->next ? (int)s->next->smin : MAX_HEIGHT;
+ chf.spans[idx].y = (unsigned short)rcClamp(bot, 0, 0xffff);
+ chf.spans[idx].h = (unsigned char)rcClamp(top - bot, 0, 0xff);
+ chf.areas[idx] = s->area;
+ idx++;
+ c.count++;
+ }
+ s = s->next;
+ }
+ }
+ }
+
+ // Find neighbour connections.
+ const int MAX_LAYERS = RC_NOT_CONNECTED-1;
+ int tooHighNeighbour = 0;
+ for (int y = 0; y < h; ++y)
+ {
+ for (int x = 0; x < w; ++x)
+ {
+ const rcCompactCell& c = chf.cells[x+y*w];
+ for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
+ {
+ rcCompactSpan& s = chf.spans[i];
+
+ for (int dir = 0; dir < 4; ++dir)
+ {
+ rcSetCon(s, dir, RC_NOT_CONNECTED);
+ const int nx = x + rcGetDirOffsetX(dir);
+ const int ny = y + rcGetDirOffsetY(dir);
+ // First check that the neighbour cell is in bounds.
+ if (nx < 0 || ny < 0 || nx >= w || ny >= h)
+ continue;
+
+ // Iterate over all neighbour spans and check if any of the is
+ // accessible from current cell.
+ const rcCompactCell& nc = chf.cells[nx+ny*w];
+ for (int k = (int)nc.index, nk = (int)(nc.index+nc.count); k < nk; ++k)
+ {
+ const rcCompactSpan& ns = chf.spans[k];
+ const int bot = rcMax(s.y, ns.y);
+ const int top = rcMin(s.y+s.h, ns.y+ns.h);
+
+ // Check that the gap between the spans is walkable,
+ // and that the climb height between the gaps is not too high.
+ if ((top - bot) >= walkableHeight && rcAbs((int)ns.y - (int)s.y) <= walkableClimb)
+ {
+ // Mark direction as walkable.
+ const int idx = k - (int)nc.index;
+ if (idx < 0 || idx > MAX_LAYERS)
+ {
+ tooHighNeighbour = rcMax(tooHighNeighbour, idx);
+ continue;
+ }
+ rcSetCon(s, dir, idx);
+ break;
+ }
+ }
+
+ }
+ }
+ }
+ }
+
+ if (tooHighNeighbour > MAX_LAYERS)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Heightfield has too many layers %d (max: %d)",
+ tooHighNeighbour, MAX_LAYERS);
+ }
+
+ ctx->stopTimer(RC_TIMER_BUILD_COMPACTHEIGHTFIELD);
+
+ return true;
+}
+
+/*
+static int getHeightfieldMemoryUsage(const rcHeightfield& hf)
+{
+ int size = 0;
+ size += sizeof(hf);
+ size += hf.width * hf.height * sizeof(rcSpan*);
+
+ rcSpanPool* pool = hf.pools;
+ while (pool)
+ {
+ size += (sizeof(rcSpanPool) - sizeof(rcSpan)) + sizeof(rcSpan)*RC_SPANS_PER_POOL;
+ pool = pool->next;
+ }
+ return size;
+}
+
+static int getCompactHeightFieldMemoryusage(const rcCompactHeightfield& chf)
+{
+ int size = 0;
+ size += sizeof(rcCompactHeightfield);
+ size += sizeof(rcCompactSpan) * chf.spanCount;
+ size += sizeof(rcCompactCell) * chf.width * chf.height;
+ return size;
+}
+*/
\ No newline at end of file
diff --git a/dep/recastnavigation/Recast/Recast.h b/dep/recastnavigation/Recast/Recast.h
new file mode 100644
index 0000000..0e5f074
--- /dev/null
+++ b/dep/recastnavigation/Recast/Recast.h
@@ -0,0 +1,688 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#ifndef RECAST_H
+#define RECAST_H
+
+// Some math headers don't have PI defined.
+static const float RC_PI = 3.14159265f;
+
+enum rcLogCategory
+{
+ RC_LOG_PROGRESS = 1,
+ RC_LOG_WARNING,
+ RC_LOG_ERROR,
+};
+
+enum rcTimerLabel
+{
+ RC_TIMER_TOTAL,
+ RC_TIMER_TEMP,
+ RC_TIMER_RASTERIZE_TRIANGLES,
+ RC_TIMER_BUILD_COMPACTHEIGHTFIELD,
+ RC_TIMER_BUILD_CONTOURS,
+ RC_TIMER_BUILD_CONTOURS_TRACE,
+ RC_TIMER_BUILD_CONTOURS_SIMPLIFY,
+ RC_TIMER_FILTER_BORDER,
+ RC_TIMER_FILTER_WALKABLE,
+ RC_TIMER_MEDIAN_AREA,
+ RC_TIMER_FILTER_LOW_OBSTACLES,
+ RC_TIMER_BUILD_POLYMESH,
+ RC_TIMER_MERGE_POLYMESH,
+ RC_TIMER_ERODE_AREA,
+ RC_TIMER_MARK_BOX_AREA,
+ RC_TIMER_MARK_CONVEXPOLY_AREA,
+ RC_TIMER_BUILD_DISTANCEFIELD,
+ RC_TIMER_BUILD_DISTANCEFIELD_DIST,
+ RC_TIMER_BUILD_DISTANCEFIELD_BLUR,
+ RC_TIMER_BUILD_REGIONS,
+ RC_TIMER_BUILD_REGIONS_WATERSHED,
+ RC_TIMER_BUILD_REGIONS_EXPAND,
+ RC_TIMER_BUILD_REGIONS_FLOOD,
+ RC_TIMER_BUILD_REGIONS_FILTER,
+ RC_TIMER_BUILD_POLYMESHDETAIL,
+ RC_TIMER_MERGE_POLYMESHDETAIL,
+ RC_MAX_TIMERS
+};
+
+// Build context provides several optional utilities needed for the build process,
+// such as timing, logging, and build time collecting.
+class rcContext
+{
+public:
+ inline rcContext(bool state = true) : m_logEnabled(state), m_timerEnabled(state) {}
+ virtual ~rcContext() {}
+
+ // Enables or disables logging.
+ inline void enableLog(bool state) { m_logEnabled = state; }
+ // Resets log.
+ inline void resetLog() { if (m_logEnabled) doResetLog(); }
+ // Logs a message.
+ void log(const rcLogCategory category, const char* format, ...);
+
+ // Enables or disables timer.
+ inline void enableTimer(bool state) { m_timerEnabled = state; }
+ // Resets all timers.
+ inline void resetTimers() { if (m_timerEnabled) doResetTimers(); }
+ // Starts timer, used for performance timing.
+ inline void startTimer(const rcTimerLabel label) { if (m_timerEnabled) doStartTimer(label); }
+ // Stops timer, used for performance timing.
+ inline void stopTimer(const rcTimerLabel label) { if (m_timerEnabled) doStopTimer(label); }
+ // Returns time accumulated between timer start/stop.
+ inline int getAccumulatedTime(const rcTimerLabel label) const { return m_timerEnabled ? doGetAccumulatedTime(label) : -1; }
+
+protected:
+ // Virtual functions to override for custom implementations.
+ virtual void doResetLog() {}
+ virtual void doLog(const rcLogCategory /*category*/, const char* /*msg*/, const int /*len*/) {}
+ virtual void doResetTimers() {}
+ virtual void doStartTimer(const rcTimerLabel /*label*/) {}
+ virtual void doStopTimer(const rcTimerLabel /*label*/) {}
+ virtual int doGetAccumulatedTime(const rcTimerLabel /*label*/) const { return -1; }
+
+ bool m_logEnabled;
+ bool m_timerEnabled;
+};
+
+
+// The units of the parameters are specified in parenthesis as follows:
+// (vx) voxels, (wu) world units
+struct rcConfig
+{
+ int width, height; // Dimensions of the rasterized heightfield (vx)
+ int tileSize; // Width and Height of a tile (vx)
+ int borderSize; // Non-navigable Border around the heightfield (vx)
+ float cs, ch; // Grid cell size and height (wu)
+ float bmin[3], bmax[3]; // Grid bounds (wu)
+ float walkableSlopeAngle; // Maximum walkable slope angle in degrees.
+ int walkableHeight; // Minimum height where the agent can still walk (vx)
+ int walkableClimb; // Maximum height between grid cells the agent can climb (vx)
+ int walkableRadius; // Radius of the agent in cells (vx)
+ int maxEdgeLen; // Maximum contour edge length (vx)
+ float maxSimplificationError; // Maximum distance error from contour to cells (vx)
+ int minRegionArea; // Regions whose area is smaller than this threshold will be removed. (vx)
+ int mergeRegionArea; // Regions whose area is smaller than this threshold will be merged (vx)
+ int maxVertsPerPoly; // Max number of vertices per polygon
+ float detailSampleDist; // Detail mesh sample spacing.
+ float detailSampleMaxError; // Detail mesh simplification max sample error.
+};
+
+// Define number of bits in the above structure for smin/smax.
+// The max height is used for clamping rasterized values.
+static const int RC_SPAN_HEIGHT_BITS = 16;
+static const int RC_SPAN_MAX_HEIGHT = (1<<RC_SPAN_HEIGHT_BITS)-1;
+
+// Heightfield span.
+struct rcSpan
+{
+ unsigned int smin : 16; // Span min height.
+ unsigned int smax : 16; // Span max height.
+ unsigned char area; // Span area type.
+ rcSpan* next; // Next span in column.
+};
+
+// Number of spans allocated per pool.
+static const int RC_SPANS_PER_POOL = 2048;
+
+// Memory pool used for quick span allocation.
+struct rcSpanPool
+{
+ rcSpanPool* next; // Pointer to next pool.
+ rcSpan items[RC_SPANS_PER_POOL]; // Array of spans.
+};
+
+// Dynamic span-heightfield.
+struct rcHeightfield
+{
+ int width, height; // Dimension of the heightfield.
+ float bmin[3], bmax[3]; // Bounding box of the heightfield
+ float cs, ch; // Cell size and height.
+ rcSpan** spans; // Heightfield of spans (width*height).
+ rcSpanPool* pools; // Linked list of span pools.
+ rcSpan* freelist; // Pointer to next free span.
+};
+
+rcHeightfield* rcAllocHeightfield();
+void rcFreeHeightField(rcHeightfield* hf);
+
+
+struct rcCompactCell
+{
+ unsigned int index : 24; // Index to first span in column.
+ unsigned int count : 8; // Number of spans in this column.
+};
+
+struct rcCompactSpan
+{
+ unsigned short y; // Bottom coordinate of the span.
+ unsigned short reg;
+ unsigned int con : 24; // Connections to neighbour cells.
+ unsigned int h : 8; // Height of the span.
+};
+
+// Compact static heightfield.
+struct rcCompactHeightfield
+{
+ int width, height; // Width and height of the heightfield.
+ int spanCount; // Number of spans in the heightfield.
+ int walkableHeight, walkableClimb; // Agent properties.
+ unsigned short maxDistance; // Maximum distance value stored in heightfield.
+ unsigned short maxRegions; // Maximum Region Id stored in heightfield.
+ float bmin[3], bmax[3]; // Bounding box of the heightfield.
+ float cs, ch; // Cell size and height.
+ rcCompactCell* cells; // Pointer to width*height cells.
+ rcCompactSpan* spans; // Pointer to spans.
+ unsigned short* dist; // Pointer to per span distance to border.
+ unsigned char* areas; // Pointer to per span area ID.
+};
+
+rcCompactHeightfield* rcAllocCompactHeightfield();
+void rcFreeCompactHeightfield(rcCompactHeightfield* chf);
+
+
+struct rcContour
+{
+ int* verts; // Vertex coordinates, each vertex contains 4 components.
+ int nverts; // Number of vertices.
+ int* rverts; // Raw vertex coordinates, each vertex contains 4 components.
+ int nrverts; // Number of raw vertices.
+ unsigned short reg; // Region ID of the contour.
+ unsigned char area; // Area ID of the contour.
+};
+
+struct rcContourSet
+{
+ rcContour* conts; // Pointer to all contours.
+ int nconts; // Number of contours.
+ float bmin[3], bmax[3]; // Bounding box of the heightfield.
+ float cs, ch; // Cell size and height.
+};
+
+rcContourSet* rcAllocContourSet();
+void rcFreeContourSet(rcContourSet* cset);
+
+
+// Polymesh store a connected mesh of polygons.
+// The polygons are store in an array where each polygons takes
+// 'nvp*2' elements. The first 'nvp' elements are indices to vertices
+// and the second 'nvp' elements are indices to neighbour polygons.
+// If a polygon has less than 'bvp' vertices, the remaining indices
+// are set to RC_MESH_NULL_IDX. If an polygon edge does not have a neighbour
+// the neighbour index is set to RC_MESH_NULL_IDX.
+// Vertices can be transformed into world space as follows:
+// x = bmin[0] + verts[i*3+0]*cs;
+// y = bmin[1] + verts[i*3+1]*ch;
+// z = bmin[2] + verts[i*3+2]*cs;
+struct rcPolyMesh
+{
+ unsigned short* verts; // Vertices of the mesh, 3 elements per vertex.
+ unsigned short* polys; // Polygons of the mesh, nvp*2 elements per polygon.
+ unsigned short* regs; // Region ID of the polygons.
+ unsigned short* flags; // Per polygon flags.
+ unsigned char* areas; // Area ID of polygons.
+ int nverts; // Number of vertices.
+ int npolys; // Number of polygons.
+ int maxpolys; // Number of allocated polygons.
+ int nvp; // Max number of vertices per polygon.
+ float bmin[3], bmax[3]; // Bounding box of the mesh.
+ float cs, ch; // Cell size and height.
+};
+
+rcPolyMesh* rcAllocPolyMesh();
+void rcFreePolyMesh(rcPolyMesh* pmesh);
+
+
+// Detail mesh generated from a rcPolyMesh.
+// Each submesh represents a polygon in the polymesh and they are stored in
+// exactly same order. Each submesh is described as 4 values:
+// base vertex, vertex count, base triangle, triangle count. That is,
+// const unsigned char* t = &dmesh.tris[(tbase+i)*3]; and
+// const float* v = &dmesh.verts[(vbase+t[j])*3];
+// If the input polygon has 'n' vertices, those vertices are first in the
+// submesh vertex list. This allows to compres the mesh by not storing the
+// first vertices and using the polymesh vertices instead.
+// Max number of vertices per submesh is 127 and
+// max number of triangles per submesh is 255.
+
+struct rcPolyMeshDetail
+{
+ unsigned int* meshes; // Pointer to all mesh data.
+ float* verts; // Pointer to all vertex data.
+ unsigned char* tris; // Pointer to all triangle data.
+ int nmeshes; // Number of meshes.
+ int nverts; // Number of total vertices.
+ int ntris; // Number of triangles.
+};
+
+rcPolyMeshDetail* rcAllocPolyMeshDetail();
+void rcFreePolyMeshDetail(rcPolyMeshDetail* dmesh);
+
+
+// If heightfield region ID has the following bit set, the region is on border area
+// and excluded from many calculations.
+static const unsigned short RC_BORDER_REG = 0x8000;
+
+// If contour region ID has the following bit set, the vertex will be later
+// removed in order to match the segments and vertices at tile boundaries.
+static const int RC_BORDER_VERTEX = 0x10000;
+
+static const int RC_AREA_BORDER = 0x20000;
+
+enum rcBuildContoursFlags
+{
+ RC_CONTOUR_TESS_WALL_EDGES = 0x01, // Tessellate wall edges
+ RC_CONTOUR_TESS_AREA_EDGES = 0x02, // Tessellate edges between areas.
+};
+
+// Mask used with contours to extract region id.
+static const int RC_CONTOUR_REG_MASK = 0xffff;
+
+// Null index which is used with meshes to mark unset or invalid indices.
+static const unsigned short RC_MESH_NULL_IDX = 0xffff;
+
+// Area ID that is considered empty.
+static const unsigned char RC_NULL_AREA = 0;
+
+// Area ID that is considered generally walkable.
+static const unsigned char RC_WALKABLE_AREA = 63;
+
+// Value returned by rcGetCon() if the direction is not connected.
+static const int RC_NOT_CONNECTED = 0x3f;
+
+// Compact span neighbour helpers.
+inline void rcSetCon(rcCompactSpan& s, int dir, int i)
+{
+ const unsigned int shift = (unsigned int)dir*6;
+ unsigned int con = s.con;
+ s.con = (con & ~(0x3f << shift)) | (((unsigned int)i & 0x3f) << shift);
+}
+
+inline int rcGetCon(const rcCompactSpan& s, int dir)
+{
+ const unsigned int shift = (unsigned int)dir*6;
+ return (s.con >> shift) & 0x3f;
+}
+
+inline int rcGetDirOffsetX(int dir)
+{
+ const int offset[4] = { -1, 0, 1, 0, };
+ return offset[dir&0x03];
+}
+
+inline int rcGetDirOffsetY(int dir)
+{
+ const int offset[4] = { 0, 1, 0, -1 };
+ return offset[dir&0x03];
+}
+
+// Common helper functions
+template<class T> inline void rcSwap(T& a, T& b) { T t = a; a = b; b = t; }
+template<class T> inline T rcMin(T a, T b) { return a < b ? a : b; }
+template<class T> inline T rcMax(T a, T b) { return a > b ? a : b; }
+template<class T> inline T rcAbs(T a) { return a < 0 ? -a : a; }
+template<class T> inline T rcSqr(T a) { return a*a; }
+template<class T> inline T rcClamp(T v, T mn, T mx) { return v < mn ? mn : (v > mx ? mx : v); }
+float rcSqrt(float x);
+
+// Common vector helper functions.
+inline void rcVcross(float* dest, const float* v1, const float* v2)
+{
+ dest[0] = v1[1]*v2[2] - v1[2]*v2[1];
+ dest[1] = v1[2]*v2[0] - v1[0]*v2[2];
+ dest[2] = v1[0]*v2[1] - v1[1]*v2[0];
+}
+
+inline float rcVdot(const float* v1, const float* v2)
+{
+ return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2];
+}
+
+inline void rcVmad(float* dest, const float* v1, const float* v2, const float s)
+{
+ dest[0] = v1[0]+v2[0]*s;
+ dest[1] = v1[1]+v2[1]*s;
+ dest[2] = v1[2]+v2[2]*s;
+}
+
+inline void rcVadd(float* dest, const float* v1, const float* v2)
+{
+ dest[0] = v1[0]+v2[0];
+ dest[1] = v1[1]+v2[1];
+ dest[2] = v1[2]+v2[2];
+}
+
+inline void rcVsub(float* dest, const float* v1, const float* v2)
+{
+ dest[0] = v1[0]-v2[0];
+ dest[1] = v1[1]-v2[1];
+ dest[2] = v1[2]-v2[2];
+}
+
+inline void rcVmin(float* mn, const float* v)
+{
+ mn[0] = rcMin(mn[0], v[0]);
+ mn[1] = rcMin(mn[1], v[1]);
+ mn[2] = rcMin(mn[2], v[2]);
+}
+
+inline void rcVmax(float* mx, const float* v)
+{
+ mx[0] = rcMax(mx[0], v[0]);
+ mx[1] = rcMax(mx[1], v[1]);
+ mx[2] = rcMax(mx[2], v[2]);
+}
+
+inline void rcVcopy(float* dest, const float* v)
+{
+ dest[0] = v[0];
+ dest[1] = v[1];
+ dest[2] = v[2];
+}
+
+inline float rcVdist(const float* v1, const float* v2)
+{
+ float dx = v2[0] - v1[0];
+ float dy = v2[1] - v1[1];
+ float dz = v2[2] - v1[2];
+ return rcSqrt(dx*dx + dy*dy + dz*dz);
+}
+
+inline float rcVdistSqr(const float* v1, const float* v2)
+{
+ float dx = v2[0] - v1[0];
+ float dy = v2[1] - v1[1];
+ float dz = v2[2] - v1[2];
+ return dx*dx + dy*dy + dz*dz;
+}
+
+inline void rcVnormalize(float* v)
+{
+ float d = 1.0f / rcSqrt(rcSqr(v[0]) + rcSqr(v[1]) + rcSqr(v[2]));
+ v[0] *= d;
+ v[1] *= d;
+ v[2] *= d;
+}
+
+inline bool rcVequal(const float* p0, const float* p1)
+{
+ static const float thr = rcSqr(1.0f/16384.0f);
+ const float d = rcVdistSqr(p0, p1);
+ return d < thr;
+}
+
+// Calculated bounding box of array of vertices.
+// Params:
+// verts - (in) array of vertices
+// nv - (in) vertex count
+// bmin, bmax - (out) bounding box
+void rcCalcBounds(const float* verts, int nv, float* bmin, float* bmax);
+
+// Calculates grid size based on bounding box and grid cell size.
+// Params:
+// bmin, bmax - (in) bounding box
+// cs - (in) grid cell size
+// w - (out) grid width
+// h - (out) grid height
+void rcCalcGridSize(const float* bmin, const float* bmax, float cs, int* w, int* h);
+
+// Creates and initializes new heightfield.
+// Params:
+// hf - (in/out) heightfield to initialize.
+// width - (in) width of the heightfield.
+// height - (in) height of the heightfield.
+// bmin, bmax - (in) bounding box of the heightfield
+// cs - (in) grid cell size
+// ch - (in) grid cell height
+bool rcCreateHeightfield(rcContext* ctx, rcHeightfield& hf, int width, int height,
+ const float* bmin, const float* bmax,
+ float cs, float ch);
+
+// Sets the RC_WALKABLE_AREA for every triangle whose slope is below
+// the maximum walkable slope angle.
+// Params:
+// walkableSlopeAngle - (in) maximum slope angle in degrees.
+// verts - (in) array of vertices
+// nv - (in) vertex count
+// tris - (in) array of triangle vertex indices
+// nt - (in) triangle count
+// areas - (out) array of triangle area types
+void rcMarkWalkableTriangles(rcContext* ctx, const float walkableSlopeAngle, const float* verts, int nv,
+ const int* tris, int nt, unsigned char* areas);
+
+// Sets the RC_NULL_AREA for every triangle whose slope is steeper than
+// the maximum walkable slope angle.
+// Params:
+// walkableSlopeAngle - (in) maximum slope angle in degrees.
+// verts - (in) array of vertices
+// nv - (in) vertex count
+// tris - (in) array of triangle vertex indices
+// nt - (in) triangle count
+// areas - (out) array of triangle are types
+void rcClearUnwalkableTriangles(rcContext* ctx, const float walkableSlopeAngle, const float* verts, int nv,
+ const int* tris, int nt, unsigned char* areas);
+
+// Adds span to heightfield.
+// The span addition can set to favor flags. If the span is merged to
+// another span and the new smax is within 'flagMergeThr' units away
+// from the existing span the span flags are merged and stored.
+// Params:
+// solid - (in) heightfield where the spans is added to
+// x,y - (in) location on the heightfield where the span is added
+// smin,smax - (in) spans min/max height
+// flags - (in) span flags (zero or WALKABLE)
+// flagMergeThr - (in) merge threshold.
+void rcAddSpan(rcContext* ctx, rcHeightfield& solid, const int x, const int y,
+ const unsigned short smin, const unsigned short smax,
+ const unsigned short area, const int flagMergeThr);
+
+// Rasterizes a triangle into heightfield spans.
+// Params:
+// v0,v1,v2 - (in) the vertices of the triangle.
+// area - (in) area type of the triangle.
+// solid - (in) heightfield where the triangle is rasterized
+// flagMergeThr - (in) distance in voxel where walkable flag is favored over non-walkable.
+void rcRasterizeTriangle(rcContext* ctx, const float* v0, const float* v1, const float* v2,
+ const unsigned char area, rcHeightfield& solid,
+ const int flagMergeThr = 1);
+
+// Rasterizes indexed triangle mesh into heightfield spans.
+// Params:
+// verts - (in) array of vertices
+// nv - (in) vertex count
+// tris - (in) array of triangle vertex indices
+// area - (in) array of triangle area types.
+// nt - (in) triangle count
+// solid - (in) heightfield where the triangles are rasterized
+// flagMergeThr - (in) distance in voxel where walkable flag is favored over non-walkable.
+void rcRasterizeTriangles(rcContext* ctx, const float* verts, const int nv,
+ const int* tris, const unsigned char* areas, const int nt,
+ rcHeightfield& solid, const int flagMergeThr = 1);
+
+// Rasterizes indexed triangle mesh into heightfield spans.
+// Params:
+// verts - (in) array of vertices
+// nv - (in) vertex count
+// tris - (in) array of triangle vertex indices
+// area - (in) array of triangle area types.
+// nt - (in) triangle count
+// solid - (in) heightfield where the triangles are rasterized
+// flagMergeThr - (in) distance in voxel where walkable flag is favored over non-walkable.
+void rcRasterizeTriangles(rcContext* ctx, const float* verts, const int nv,
+ const unsigned short* tris, const unsigned char* areas, const int nt,
+ rcHeightfield& solid, const int flagMergeThr = 1);
+
+// Rasterizes the triangles into heightfield spans.
+// Params:
+// verts - (in) array of vertices
+// area - (in) array of triangle area types.
+// nt - (in) triangle count
+// solid - (in) heightfield where the triangles are rasterized
+void rcRasterizeTriangles(rcContext* ctx, const float* verts, const unsigned char* areas, const int nt,
+ rcHeightfield& solid, const int flagMergeThr = 1);
+
+// Marks non-walkable low obstacles as walkable if they are closer than walkableClimb
+// from a walkable surface. Applying this filter allows to step over low hanging
+// low obstacles.
+// Params:
+// walkableHeight - (in) minimum height where the agent can still walk
+// solid - (in/out) heightfield describing the solid space
+// TODO: Missuses ledge flag, must be called before rcFilterLedgeSpans!
+void rcFilterLowHangingWalkableObstacles(rcContext* ctx, const int walkableClimb, rcHeightfield& solid);
+
+// Removes WALKABLE flag from all spans that are at ledges. This filtering
+// removes possible overestimation of the conservative voxelization so that
+// the resulting mesh will not have regions hanging in air over ledges.
+// Params:
+// walkableHeight - (in) minimum height where the agent can still walk
+// walkableClimb - (in) maximum height between grid cells the agent can climb
+// solid - (in/out) heightfield describing the solid space
+void rcFilterLedgeSpans(rcContext* ctx, const int walkableHeight,
+ const int walkableClimb, rcHeightfield& solid);
+
+// Removes WALKABLE flag from all spans which have smaller than
+// 'walkableHeight' clearance above them.
+// Params:
+// walkableHeight - (in) minimum height where the agent can still walk
+// solid - (in/out) heightfield describing the solid space
+void rcFilterWalkableLowHeightSpans(rcContext* ctx, int walkableHeight, rcHeightfield& solid);
+
+// Returns number of spans contained in a heightfield.
+// Params:
+// hf - (in) heightfield to be compacted
+// Returns number of spans.
+int rcGetHeightFieldSpanCount(rcContext* ctx, rcHeightfield& hf);
+
+// Builds compact representation of the heightfield.
+// Params:
+// walkableHeight - (in) minimum height where the agent can still walk
+// walkableClimb - (in) maximum height between grid cells the agent can climb
+// flags - (in) require flags for a cell to be included in the compact heightfield.
+// hf - (in) heightfield to be compacted
+// chf - (out) compact heightfield representing the open space.
+// Returns false if operation ran out of memory.
+bool rcBuildCompactHeightfield(rcContext* ctx, const int walkableHeight, const int walkableClimb,
+ rcHeightfield& hf, rcCompactHeightfield& chf);
+
+// Erodes walkable area.
+// Params:
+// radius - (in) radius of erosion (max 255).
+// chf - (in/out) compact heightfield to erode.
+// Returns false if operation ran out of memory.
+bool rcErodeWalkableArea(rcContext* ctx, int radius, rcCompactHeightfield& chf);
+
+// Applies median filter to walkable area types, removing noise.
+// Params:
+// chf - (in/out) compact heightfield to erode.
+// Returns false if operation ran out of memory.
+bool rcMedianFilterWalkableArea(rcContext* ctx, rcCompactHeightfield& chf);
+
+// Marks the area of the convex polygon into the area type of the compact heightfield.
+// Params:
+// bmin/bmax - (in) bounds of the axis aligned box.
+// areaId - (in) area ID to mark.
+// chf - (in/out) compact heightfield to mark.
+void rcMarkBoxArea(rcContext* ctx, const float* bmin, const float* bmax, unsigned char areaId,
+ rcCompactHeightfield& chf);
+
+// Marks the area of the convex polygon into the area type of the compact heightfield.
+// Params:
+// verts - (in) vertices of the convex polygon.
+// nverts - (in) number of vertices in the polygon.
+// hmin/hmax - (in) min and max height of the polygon.
+// areaId - (in) area ID to mark.
+// chf - (in/out) compact heightfield to mark.
+void rcMarkConvexPolyArea(rcContext* ctx, const float* verts, const int nverts,
+ const float hmin, const float hmax, unsigned char areaId,
+ rcCompactHeightfield& chf);
+
+// Builds distance field and stores it into the combat heightfield.
+// Params:
+// chf - (in/out) compact heightfield representing the open space.
+// Returns false if operation ran out of memory.
+bool rcBuildDistanceField(rcContext* ctx, rcCompactHeightfield& chf);
+
+// Divides the walkable heighfied into simple regions using watershed partitioning.
+// Each region has only one contour and no overlaps.
+// The regions are stored in the compact heightfield 'reg' field.
+// The process sometimes creates small regions. If the area of a regions is
+// smaller than 'mergeRegionArea' then the region will be merged with a neighbour
+// region if possible. If multiple regions form an area which is smaller than
+// 'minRegionArea' all the regions belonging to that area will be removed.
+// Here area means the count of spans in an area.
+// Params:
+// chf - (in/out) compact heightfield representing the open space.
+// minRegionArea - (in) the smallest allowed region area.
+// maxMergeRegionArea - (in) the largest allowed region area which can be merged.
+// Returns false if operation ran out of memory.
+bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf,
+ const int borderSize, const int minRegionArea, const int mergeRegionArea);
+
+// Divides the walkable heighfied into simple regions using simple monotone partitioning.
+// Each region has only one contour and no overlaps.
+// The regions are stored in the compact heightfield 'reg' field.
+// The process sometimes creates small regions. If the area of a regions is
+// smaller than 'mergeRegionArea' then the region will be merged with a neighbour
+// region if possible. If multiple regions form an area which is smaller than
+// 'minRegionArea' all the regions belonging to that area will be removed.
+// Here area means the count of spans in an area.
+// Params:
+// chf - (in/out) compact heightfield representing the open space.
+// minRegionArea - (in) the smallest allowed regions size.
+// maxMergeRegionArea - (in) the largest allowed regions size which can be merged.
+// Returns false if operation ran out of memory.
+bool rcBuildRegionsMonotone(rcContext* ctx, rcCompactHeightfield& chf,
+ const int borderSize, const int minRegionArea, const int mergeRegionArea);
+
+// Builds simplified contours from the regions outlines.
+// Params:
+// chf - (in) compact heightfield which has regions set.
+// maxError - (in) maximum allowed distance between simplified contour and cells.
+// maxEdgeLen - (in) maximum allowed contour edge length in cells.
+// cset - (out) Resulting contour set.
+// flags - (in) build flags, see rcBuildContoursFlags.
+// Returns false if operation ran out of memory.
+bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf,
+ const float maxError, const int maxEdgeLen,
+ rcContourSet& cset, const int flags = RC_CONTOUR_TESS_WALL_EDGES);
+
+// Builds connected convex polygon mesh from contour polygons.
+// Params:
+// cset - (in) contour set.
+// nvp - (in) maximum number of vertices per polygon.
+// mesh - (out) poly mesh.
+// Returns false if operation ran out of memory.
+bool rcBuildPolyMesh(rcContext* ctx, rcContourSet& cset, int nvp, rcPolyMesh& mesh);
+
+bool rcMergePolyMeshes(rcContext* ctx, rcPolyMesh** meshes, const int nmeshes, rcPolyMesh& mesh);
+
+// Builds detail triangle mesh for each polygon in the poly mesh.
+// Params:
+// mesh - (in) poly mesh to detail.
+// chf - (in) compact height field, used to query height for new vertices.
+// sampleDist - (in) spacing between height samples used to generate more detail into mesh.
+// sampleMaxError - (in) maximum allowed distance between simplified detail mesh and height sample.
+// pmdtl - (out) detail mesh.
+// Returns false if operation ran out of memory.
+bool rcBuildPolyMeshDetail(rcContext* ctx, const rcPolyMesh& mesh, const rcCompactHeightfield& chf,
+ const float sampleDist, const float sampleMaxError,
+ rcPolyMeshDetail& dmesh);
+
+bool rcMergePolyMeshDetails(rcContext* ctx, rcPolyMeshDetail** meshes, const int nmeshes, rcPolyMeshDetail& mesh);
+
+
+#endif // RECAST_H
diff --git a/dep/recastnavigation/Recast/RecastAlloc.cpp b/dep/recastnavigation/Recast/RecastAlloc.cpp
new file mode 100644
index 0000000..2c7396a
--- /dev/null
+++ b/dep/recastnavigation/Recast/RecastAlloc.cpp
@@ -0,0 +1,67 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#include <stdlib.h>
+#include <string.h>
+#include "RecastAlloc.h"
+
+static void *rcAllocDefault(int size, rcAllocHint)
+{
+ return malloc(size);
+}
+
+static void rcFreeDefault(void *ptr)
+{
+ free(ptr);
+}
+
+static rcAllocFunc* sRecastAllocFunc = rcAllocDefault;
+static rcFreeFunc* sRecastFreeFunc = rcFreeDefault;
+
+void rcAllocSetCustom(rcAllocFunc *allocFunc, rcFreeFunc *freeFunc)
+{
+ sRecastAllocFunc = allocFunc ? allocFunc : rcAllocDefault;
+ sRecastFreeFunc = freeFunc ? freeFunc : rcFreeDefault;
+}
+
+void* rcAlloc(int size, rcAllocHint hint)
+{
+ return sRecastAllocFunc(size, hint);
+}
+
+void rcFree(void* ptr)
+{
+ if (ptr)
+ sRecastFreeFunc(ptr);
+}
+
+
+void rcIntArray::resize(int n)
+{
+ if (n > m_cap)
+ {
+ if (!m_cap) m_cap = n;
+ while (m_cap < n) m_cap *= 2;
+ int* newData = (int*)rcAlloc(m_cap*sizeof(int), RC_ALLOC_TEMP);
+ if (m_size && newData) memcpy(newData, m_data, m_size*sizeof(int));
+ rcFree(m_data);
+ m_data = newData;
+ }
+ m_size = n;
+}
+
diff --git a/dep/recastnavigation/Recast/RecastAlloc.h b/dep/recastnavigation/Recast/RecastAlloc.h
new file mode 100644
index 0000000..9a31637
--- /dev/null
+++ b/dep/recastnavigation/Recast/RecastAlloc.h
@@ -0,0 +1,69 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#ifndef RECASTALLOC_H
+#define RECASTALLOC_H
+
+enum rcAllocHint
+{
+ RC_ALLOC_PERM, // Memory persist after a function call.
+ RC_ALLOC_TEMP // Memory used temporarily within a function.
+};
+
+typedef void* (rcAllocFunc)(int size, rcAllocHint hint);
+typedef void (rcFreeFunc)(void* ptr);
+
+void rcAllocSetCustom(rcAllocFunc *allocFunc, rcFreeFunc *freeFunc);
+
+void* rcAlloc(int size, rcAllocHint hint);
+void rcFree(void* ptr);
+
+
+
+// Simple dynamic array ints.
+class rcIntArray
+{
+ int* m_data;
+ int m_size, m_cap;
+ inline rcIntArray(const rcIntArray&);
+ inline rcIntArray& operator=(const rcIntArray&);
+public:
+ inline rcIntArray() : m_data(0), m_size(0), m_cap(0) {}
+ inline rcIntArray(int n) : m_data(0), m_size(0), m_cap(0) { resize(n); }
+ inline ~rcIntArray() { rcFree(m_data); }
+ void resize(int n);
+ inline void push(int item) { resize(m_size+1); m_data[m_size-1] = item; }
+ inline int pop() { if (m_size > 0) m_size--; return m_data[m_size]; }
+ inline const int& operator[](int i) const { return m_data[i]; }
+ inline int& operator[](int i) { return m_data[i]; }
+ inline int size() const { return m_size; }
+};
+
+// Simple internal helper class to delete array in scope
+template<class T> class rcScopedDelete
+{
+ T* ptr;
+ inline T* operator=(T* p);
+public:
+ inline rcScopedDelete() : ptr(0) {}
+ inline rcScopedDelete(T* p) : ptr(p) {}
+ inline ~rcScopedDelete() { rcFree(ptr); }
+ inline operator T*() { return ptr; }
+};
+
+#endif
diff --git a/dep/recastnavigation/Recast/RecastArea.cpp b/dep/recastnavigation/Recast/RecastArea.cpp
new file mode 100644
index 0000000..e89caee
--- /dev/null
+++ b/dep/recastnavigation/Recast/RecastArea.cpp
@@ -0,0 +1,413 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#include <float.h>
+#define _USE_MATH_DEFINES
+#include <math.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "Recast.h"
+#include "RecastAlloc.h"
+#include "RecastAssert.h"
+
+
+bool rcErodeWalkableArea(rcContext* ctx, int radius, rcCompactHeightfield& chf)
+{
+ rcAssert(ctx);
+
+ const int w = chf.width;
+ const int h = chf.height;
+
+ ctx->startTimer(RC_TIMER_ERODE_AREA);
+
+ unsigned char* dist = (unsigned char*)rcAlloc(sizeof(unsigned char)*chf.spanCount, RC_ALLOC_TEMP);
+ if (!dist)
+ {
+ ctx->log(RC_LOG_ERROR, "erodeWalkableArea: Out of memory 'dist' (%d).", chf.spanCount);
+ return false;
+ }
+
+ // Init distance.
+ memset(dist, 0xff, sizeof(unsigned char)*chf.spanCount);
+
+ // Mark boundary cells.
+ for (int y = 0; y < h; ++y)
+ {
+ for (int x = 0; x < w; ++x)
+ {
+ const rcCompactCell& c = chf.cells[x+y*w];
+ for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
+ {
+ if (chf.areas[i] != RC_NULL_AREA)
+ {
+ const rcCompactSpan& s = chf.spans[i];
+ int nc = 0;
+ for (int dir = 0; dir < 4; ++dir)
+ {
+ if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
+ nc++;
+ }
+ // At least one missing neighbour.
+ if (nc != 4)
+ dist[i] = 0;
+ }
+ }
+ }
+ }
+
+ unsigned char nd;
+
+ // Pass 1
+ for (int y = 0; y < h; ++y)
+ {
+ for (int x = 0; x < w; ++x)
+ {
+ const rcCompactCell& c = chf.cells[x+y*w];
+ for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
+ {
+ const rcCompactSpan& s = chf.spans[i];
+
+ if (rcGetCon(s, 0) != RC_NOT_CONNECTED)
+ {
+ // (-1,0)
+ const int ax = x + rcGetDirOffsetX(0);
+ const int ay = y + rcGetDirOffsetY(0);
+ const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 0);
+ const rcCompactSpan& as = chf.spans[ai];
+ nd = (unsigned char)rcMin((int)dist[ai]+2, 255);
+ if (nd < dist[i])
+ dist[i] = nd;
+
+ // (-1,-1)
+ if (rcGetCon(as, 3) != RC_NOT_CONNECTED)
+ {
+ const int aax = ax + rcGetDirOffsetX(3);
+ const int aay = ay + rcGetDirOffsetY(3);
+ const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 3);
+ nd = (unsigned char)rcMin((int)dist[aai]+3, 255);
+ if (nd < dist[i])
+ dist[i] = nd;
+ }
+ }
+ if (rcGetCon(s, 3) != RC_NOT_CONNECTED)
+ {
+ // (0,-1)
+ const int ax = x + rcGetDirOffsetX(3);
+ const int ay = y + rcGetDirOffsetY(3);
+ const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 3);
+ const rcCompactSpan& as = chf.spans[ai];
+ nd = (unsigned char)rcMin((int)dist[ai]+2, 255);
+ if (nd < dist[i])
+ dist[i] = nd;
+
+ // (1,-1)
+ if (rcGetCon(as, 2) != RC_NOT_CONNECTED)
+ {
+ const int aax = ax + rcGetDirOffsetX(2);
+ const int aay = ay + rcGetDirOffsetY(2);
+ const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 2);
+ nd = (unsigned char)rcMin((int)dist[aai]+3, 255);
+ if (nd < dist[i])
+ dist[i] = nd;
+ }
+ }
+ }
+ }
+ }
+
+ // Pass 2
+ for (int y = h-1; y >= 0; --y)
+ {
+ for (int x = w-1; x >= 0; --x)
+ {
+ const rcCompactCell& c = chf.cells[x+y*w];
+ for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
+ {
+ const rcCompactSpan& s = chf.spans[i];
+
+ if (rcGetCon(s, 2) != RC_NOT_CONNECTED)
+ {
+ // (1,0)
+ const int ax = x + rcGetDirOffsetX(2);
+ const int ay = y + rcGetDirOffsetY(2);
+ const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 2);
+ const rcCompactSpan& as = chf.spans[ai];
+ nd = (unsigned char)rcMin((int)dist[ai]+2, 255);
+ if (nd < dist[i])
+ dist[i] = nd;
+
+ // (1,1)
+ if (rcGetCon(as, 1) != RC_NOT_CONNECTED)
+ {
+ const int aax = ax + rcGetDirOffsetX(1);
+ const int aay = ay + rcGetDirOffsetY(1);
+ const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 1);
+ nd = (unsigned char)rcMin((int)dist[aai]+3, 255);
+ if (nd < dist[i])
+ dist[i] = nd;
+ }
+ }
+ if (rcGetCon(s, 1) != RC_NOT_CONNECTED)
+ {
+ // (0,1)
+ const int ax = x + rcGetDirOffsetX(1);
+ const int ay = y + rcGetDirOffsetY(1);
+ const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 1);
+ const rcCompactSpan& as = chf.spans[ai];
+ nd = (unsigned char)rcMin((int)dist[ai]+2, 255);
+ if (nd < dist[i])
+ dist[i] = nd;
+
+ // (-1,1)
+ if (rcGetCon(as, 0) != RC_NOT_CONNECTED)
+ {
+ const int aax = ax + rcGetDirOffsetX(0);
+ const int aay = ay + rcGetDirOffsetY(0);
+ const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 0);
+ nd = (unsigned char)rcMin((int)dist[aai]+3, 255);
+ if (nd < dist[i])
+ dist[i] = nd;
+ }
+ }
+ }
+ }
+ }
+
+ const unsigned char thr = (unsigned char)(radius*2);
+ for (int i = 0; i < chf.spanCount; ++i)
+ if (dist[i] < thr)
+ chf.areas[i] = RC_NULL_AREA;
+
+ rcFree(dist);
+
+ ctx->stopTimer(RC_TIMER_ERODE_AREA);
+
+ return true;
+}
+
+static void insertSort(unsigned char* a, const int n)
+{
+ int i, j;
+ for (i = 1; i < n; i++)
+ {
+ const unsigned char value = a[i];
+ for (j = i - 1; j >= 0 && a[j] > value; j--)
+ a[j+1] = a[j];
+ a[j+1] = value;
+ }
+}
+
+
+bool rcMedianFilterWalkableArea(rcContext* ctx, rcCompactHeightfield& chf)
+{
+ rcAssert(ctx);
+
+ const int w = chf.width;
+ const int h = chf.height;
+
+ ctx->startTimer(RC_TIMER_MEDIAN_AREA);
+
+ unsigned char* areas = (unsigned char*)rcAlloc(sizeof(unsigned char)*chf.spanCount, RC_ALLOC_TEMP);
+ if (!areas)
+ {
+ ctx->log(RC_LOG_ERROR, "medianFilterWalkableArea: Out of memory 'areas' (%d).", chf.spanCount);
+ return false;
+ }
+
+ // Init distance.
+ memset(areas, 0xff, sizeof(unsigned char)*chf.spanCount);
+
+ for (int y = 0; y < h; ++y)
+ {
+ for (int x = 0; x < w; ++x)
+ {
+ const rcCompactCell& c = chf.cells[x+y*w];
+ for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
+ {
+ const rcCompactSpan& s = chf.spans[i];
+ if (chf.areas[i] == RC_NULL_AREA)
+ {
+ areas[i] = chf.areas[i];
+ continue;
+ }
+
+ unsigned char nei[9];
+ for (int j = 0; j < 9; ++j)
+ nei[j] = chf.areas[i];
+
+ for (int dir = 0; dir < 4; ++dir)
+ {
+ if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
+ {
+ const int ax = x + rcGetDirOffsetX(dir);
+ const int ay = y + rcGetDirOffsetY(dir);
+ const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir);
+ if (chf.areas[ai] != RC_NULL_AREA)
+ nei[dir*2+0] = chf.areas[ai];
+
+ const rcCompactSpan& as = chf.spans[ai];
+ const int dir2 = (dir+1) & 0x3;
+ if (rcGetCon(as, dir2) != RC_NOT_CONNECTED)
+ {
+ const int ax2 = ax + rcGetDirOffsetX(dir2);
+ const int ay2 = ay + rcGetDirOffsetY(dir2);
+ const int ai2 = (int)chf.cells[ax2+ay2*w].index + rcGetCon(as, dir2);
+ if (chf.areas[ai2] != RC_NULL_AREA)
+ nei[dir*2+1] = chf.areas[ai2];
+ }
+ }
+ }
+ insertSort(nei, 9);
+ areas[i] = nei[4];
+ }
+ }
+ }
+
+ memcpy(chf.areas, areas, sizeof(unsigned char)*chf.spanCount);
+
+ rcFree(areas);
+
+ ctx->stopTimer(RC_TIMER_MEDIAN_AREA);
+
+ return true;
+}
+
+void rcMarkBoxArea(rcContext* ctx, const float* bmin, const float* bmax, unsigned char areaId,
+ rcCompactHeightfield& chf)
+{
+ rcAssert(ctx);
+
+ ctx->startTimer(RC_TIMER_MARK_BOX_AREA);
+
+ int minx = (int)((bmin[0]-chf.bmin[0])/chf.cs);
+ int miny = (int)((bmin[1]-chf.bmin[1])/chf.ch);
+ int minz = (int)((bmin[2]-chf.bmin[2])/chf.cs);
+ int maxx = (int)((bmax[0]-chf.bmin[0])/chf.cs);
+ int maxy = (int)((bmax[1]-chf.bmin[1])/chf.ch);
+ int maxz = (int)((bmax[2]-chf.bmin[2])/chf.cs);
+
+ if (maxx < 0) return;
+ if (minx >= chf.width) return;
+ if (maxz < 0) return;
+ if (minz >= chf.height) return;
+
+ if (minx < 0) minx = 0;
+ if (maxx >= chf.width) maxx = chf.width-1;
+ if (minz < 0) minz = 0;
+ if (maxz >= chf.height) maxz = chf.height-1;
+
+ for (int z = minz; z <= maxz; ++z)
+ {
+ for (int x = minx; x <= maxx; ++x)
+ {
+ const rcCompactCell& c = chf.cells[x+z*chf.width];
+ for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
+ {
+ rcCompactSpan& s = chf.spans[i];
+ if ((int)s.y >= miny && (int)s.y <= maxy)
+ {
+ chf.areas[i] = areaId;
+ }
+ }
+ }
+ }
+
+ ctx->stopTimer(RC_TIMER_MARK_BOX_AREA);
+
+}
+
+
+static int pointInPoly(int nvert, const float* verts, const float* p)
+{
+ int i, j, c = 0;
+ for (i = 0, j = nvert-1; i < nvert; j = i++)
+ {
+ const float* vi = &verts[i*3];
+ const float* vj = &verts[j*3];
+ if (((vi[2] > p[2]) != (vj[2] > p[2])) &&
+ (p[0] < (vj[0]-vi[0]) * (p[2]-vi[2]) / (vj[2]-vi[2]) + vi[0]) )
+ c = !c;
+ }
+ return c;
+}
+
+void rcMarkConvexPolyArea(rcContext* ctx, const float* verts, const int nverts,
+ const float hmin, const float hmax, unsigned char areaId,
+ rcCompactHeightfield& chf)
+{
+ rcAssert(ctx);
+
+ ctx->startTimer(RC_TIMER_MARK_CONVEXPOLY_AREA);
+
+ float bmin[3], bmax[3];
+ rcVcopy(bmin, verts);
+ rcVcopy(bmax, verts);
+ for (int i = 1; i < nverts; ++i)
+ {
+ rcVmin(bmin, &verts[i*3]);
+ rcVmax(bmax, &verts[i*3]);
+ }
+ bmin[1] = hmin;
+ bmax[1] = hmax;
+
+ int minx = (int)((bmin[0]-chf.bmin[0])/chf.cs);
+ int miny = (int)((bmin[1]-chf.bmin[1])/chf.ch);
+ int minz = (int)((bmin[2]-chf.bmin[2])/chf.cs);
+ int maxx = (int)((bmax[0]-chf.bmin[0])/chf.cs);
+ int maxy = (int)((bmax[1]-chf.bmin[1])/chf.ch);
+ int maxz = (int)((bmax[2]-chf.bmin[2])/chf.cs);
+
+ if (maxx < 0) return;
+ if (minx >= chf.width) return;
+ if (maxz < 0) return;
+ if (minz >= chf.height) return;
+
+ if (minx < 0) minx = 0;
+ if (maxx >= chf.width) maxx = chf.width-1;
+ if (minz < 0) minz = 0;
+ if (maxz >= chf.height) maxz = chf.height-1;
+
+
+ // TODO: Optimize.
+ for (int z = minz; z <= maxz; ++z)
+ {
+ for (int x = minx; x <= maxx; ++x)
+ {
+ const rcCompactCell& c = chf.cells[x+z*chf.width];
+ for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
+ {
+ rcCompactSpan& s = chf.spans[i];
+ if ((int)s.y >= miny && (int)s.y <= maxy)
+ {
+ float p[3];
+ p[0] = chf.bmin[0] + (x+0.5f)*chf.cs;
+ p[1] = 0;
+ p[2] = chf.bmin[2] + (z+0.5f)*chf.cs;
+
+ if (pointInPoly(nverts, verts, p))
+ {
+ chf.areas[i] = areaId;
+ }
+ }
+ }
+ }
+ }
+
+ ctx->stopTimer(RC_TIMER_MARK_CONVEXPOLY_AREA);
+}
diff --git a/dep/recastnavigation/Recast/RecastAssert.h b/dep/recastnavigation/Recast/RecastAssert.h
new file mode 100644
index 0000000..b58b8fc
--- /dev/null
+++ b/dep/recastnavigation/Recast/RecastAssert.h
@@ -0,0 +1,33 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#ifndef RECASTASSERT_H
+#define RECASTASSERT_H
+
+// Note: This header file's only purpose is to include define assert.
+// Feel free to change the file and include your own implementation instead.
+
+#ifdef NDEBUG
+// From http://cnicholson.net/2009/02/stupid-c-tricks-adventures-in-assert/
+# define rcAssert(x) do { (void)sizeof(x); } while(__LINE__==-1,false)
+#else
+# include <assert.h>
+# define rcAssert assert
+#endif
+
+#endif // RECASTASSERT_H
diff --git a/dep/recastnavigation/Recast/RecastContour.cpp b/dep/recastnavigation/Recast/RecastContour.cpp
new file mode 100644
index 0000000..1906b6e
--- /dev/null
+++ b/dep/recastnavigation/Recast/RecastContour.cpp
@@ -0,0 +1,804 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#define _USE_MATH_DEFINES
+#include <math.h>
+#include <string.h>
+#include <stdio.h>
+#include "Recast.h"
+#include "RecastAlloc.h"
+#include "RecastAssert.h"
+
+
+static int getCornerHeight(int x, int y, int i, int dir,
+ const rcCompactHeightfield& chf,
+ bool& isBorderVertex)
+{
+ const rcCompactSpan& s = chf.spans[i];
+ int ch = (int)s.y;
+ int dirp = (dir+1) & 0x3;
+
+ unsigned int regs[4] = {0,0,0,0};
+
+ // Combine region and area codes in order to prevent
+ // border vertices which are in between two areas to be removed.
+ regs[0] = chf.spans[i].reg | (chf.areas[i] << 16);
+
+ if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
+ {
+ const int ax = x + rcGetDirOffsetX(dir);
+ const int ay = y + rcGetDirOffsetY(dir);
+ const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(s, dir);
+ const rcCompactSpan& as = chf.spans[ai];
+ ch = rcMax(ch, (int)as.y);
+ regs[1] = chf.spans[ai].reg | (chf.areas[ai] << 16);
+ if (rcGetCon(as, dirp) != RC_NOT_CONNECTED)
+ {
+ const int ax2 = ax + rcGetDirOffsetX(dirp);
+ const int ay2 = ay + rcGetDirOffsetY(dirp);
+ const int ai2 = (int)chf.cells[ax2+ay2*chf.width].index + rcGetCon(as, dirp);
+ const rcCompactSpan& as2 = chf.spans[ai2];
+ ch = rcMax(ch, (int)as2.y);
+ regs[2] = chf.spans[ai2].reg | (chf.areas[ai2] << 16);
+ }
+ }
+ if (rcGetCon(s, dirp) != RC_NOT_CONNECTED)
+ {
+ const int ax = x + rcGetDirOffsetX(dirp);
+ const int ay = y + rcGetDirOffsetY(dirp);
+ const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(s, dirp);
+ const rcCompactSpan& as = chf.spans[ai];
+ ch = rcMax(ch, (int)as.y);
+ regs[3] = chf.spans[ai].reg | (chf.areas[ai] << 16);
+ if (rcGetCon(as, dir) != RC_NOT_CONNECTED)
+ {
+ const int ax2 = ax + rcGetDirOffsetX(dir);
+ const int ay2 = ay + rcGetDirOffsetY(dir);
+ const int ai2 = (int)chf.cells[ax2+ay2*chf.width].index + rcGetCon(as, dir);
+ const rcCompactSpan& as2 = chf.spans[ai2];
+ ch = rcMax(ch, (int)as2.y);
+ regs[2] = chf.spans[ai2].reg | (chf.areas[ai2] << 16);
+ }
+ }
+
+ // Check if the vertex is special edge vertex, these vertices will be removed later.
+ for (int j = 0; j < 4; ++j)
+ {
+ const int a = j;
+ const int b = (j+1) & 0x3;
+ const int c = (j+2) & 0x3;
+ const int d = (j+3) & 0x3;
+
+ // The vertex is a border vertex there are two same exterior cells in a row,
+ // followed by two interior cells and none of the regions are out of bounds.
+ const bool twoSameExts = (regs[a] & regs[b] & RC_BORDER_REG) != 0 && regs[a] == regs[b];
+ const bool twoInts = ((regs[c] | regs[d]) & RC_BORDER_REG) == 0;
+ const bool intsSameArea = (regs[c]>>16) == (regs[d]>>16);
+ const bool noZeros = regs[a] != 0 && regs[b] != 0 && regs[c] != 0 && regs[d] != 0;
+ if (twoSameExts && twoInts && intsSameArea && noZeros)
+ {
+ isBorderVertex = true;
+ break;
+ }
+ }
+
+ return ch;
+}
+
+static void walkContour(int x, int y, int i,
+ rcCompactHeightfield& chf,
+ unsigned char* flags, rcIntArray& points)
+{
+ // Choose the first non-connected edge
+ unsigned char dir = 0;
+ while ((flags[i] & (1 << dir)) == 0)
+ dir++;
+
+ unsigned char startDir = dir;
+ int starti = i;
+
+ const unsigned char area = chf.areas[i];
+
+ int iter = 0;
+ while (++iter < 40000)
+ {
+ if (flags[i] & (1 << dir))
+ {
+ // Choose the edge corner
+ bool isBorderVertex = false;
+ bool isAreaBorder = false;
+ int px = x;
+ int py = getCornerHeight(x, y, i, dir, chf, isBorderVertex);
+ int pz = y;
+ switch(dir)
+ {
+ case 0: pz++; break;
+ case 1: px++; pz++; break;
+ case 2: px++; break;
+ }
+ int r = 0;
+ const rcCompactSpan& s = chf.spans[i];
+ if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
+ {
+ const int ax = x + rcGetDirOffsetX(dir);
+ const int ay = y + rcGetDirOffsetY(dir);
+ const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(s, dir);
+ r = (int)chf.spans[ai].reg;
+ if (area != chf.areas[ai])
+ isAreaBorder = true;
+ }
+ if (isBorderVertex)
+ r |= RC_BORDER_VERTEX;
+ if (isAreaBorder)
+ r |= RC_AREA_BORDER;
+ points.push(px);
+ points.push(py);
+ points.push(pz);
+ points.push(r);
+
+ flags[i] &= ~(1 << dir); // Remove visited edges
+ dir = (dir+1) & 0x3; // Rotate CW
+ }
+ else
+ {
+ int ni = -1;
+ const int nx = x + rcGetDirOffsetX(dir);
+ const int ny = y + rcGetDirOffsetY(dir);
+ const rcCompactSpan& s = chf.spans[i];
+ if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
+ {
+ const rcCompactCell& nc = chf.cells[nx+ny*chf.width];
+ ni = (int)nc.index + rcGetCon(s, dir);
+ }
+ if (ni == -1)
+ {
+ // Should not happen.
+ return;
+ }
+ x = nx;
+ y = ny;
+ i = ni;
+ dir = (dir+3) & 0x3; // Rotate CCW
+ }
+
+ if (starti == i && startDir == dir)
+ {
+ break;
+ }
+ }
+}
+
+static float distancePtSeg(const int x, const int z,
+ const int px, const int pz,
+ const int qx, const int qz)
+{
+/* float pqx = (float)(qx - px);
+ float pqy = (float)(qy - py);
+ float pqz = (float)(qz - pz);
+ float dx = (float)(x - px);
+ float dy = (float)(y - py);
+ float dz = (float)(z - pz);
+ float d = pqx*pqx + pqy*pqy + pqz*pqz;
+ float t = pqx*dx + pqy*dy + pqz*dz;
+ if (d > 0)
+ t /= d;
+ if (t < 0)
+ t = 0;
+ else if (t > 1)
+ t = 1;
+
+ dx = px + t*pqx - x;
+ dy = py + t*pqy - y;
+ dz = pz + t*pqz - z;
+
+ return dx*dx + dy*dy + dz*dz;*/
+
+ float pqx = (float)(qx - px);
+ float pqz = (float)(qz - pz);
+ float dx = (float)(x - px);
+ float dz = (float)(z - pz);
+ float d = pqx*pqx + pqz*pqz;
+ float t = pqx*dx + pqz*dz;
+ if (d > 0)
+ t /= d;
+ if (t < 0)
+ t = 0;
+ else if (t > 1)
+ t = 1;
+
+ dx = px + t*pqx - x;
+ dz = pz + t*pqz - z;
+
+ return dx*dx + dz*dz;
+}
+
+static void simplifyContour(rcIntArray& points, rcIntArray& simplified,
+ const float maxError, const int maxEdgeLen, const int buildFlags)
+{
+ // Add initial points.
+ bool hasConnections = false;
+ for (int i = 0; i < points.size(); i += 4)
+ {
+ if ((points[i+3] & RC_CONTOUR_REG_MASK) != 0)
+ {
+ hasConnections = true;
+ break;
+ }
+ }
+
+ if (hasConnections)
+ {
+ // The contour has some portals to other regions.
+ // Add a new point to every location where the region changes.
+ for (int i = 0, ni = points.size()/4; i < ni; ++i)
+ {
+ int ii = (i+1) % ni;
+ const bool differentRegs = (points[i*4+3] & RC_CONTOUR_REG_MASK) != (points[ii*4+3] & RC_CONTOUR_REG_MASK);
+ const bool areaBorders = (points[i*4+3] & RC_AREA_BORDER) != (points[ii*4+3] & RC_AREA_BORDER);
+ if (differentRegs || areaBorders)
+ {
+ simplified.push(points[i*4+0]);
+ simplified.push(points[i*4+1]);
+ simplified.push(points[i*4+2]);
+ simplified.push(i);
+ }
+ }
+ }
+
+ if (simplified.size() == 0)
+ {
+ // If there is no connections at all,
+ // create some initial points for the simplification process.
+ // Find lower-left and upper-right vertices of the contour.
+ int llx = points[0];
+ int lly = points[1];
+ int llz = points[2];
+ int lli = 0;
+ int urx = points[0];
+ int ury = points[1];
+ int urz = points[2];
+ int uri = 0;
+ for (int i = 0; i < points.size(); i += 4)
+ {
+ int x = points[i+0];
+ int y = points[i+1];
+ int z = points[i+2];
+ if (x < llx || (x == llx && z < llz))
+ {
+ llx = x;
+ lly = y;
+ llz = z;
+ lli = i/4;
+ }
+ if (x > urx || (x == urx && z > urz))
+ {
+ urx = x;
+ ury = y;
+ urz = z;
+ uri = i/4;
+ }
+ }
+ simplified.push(llx);
+ simplified.push(lly);
+ simplified.push(llz);
+ simplified.push(lli);
+
+ simplified.push(urx);
+ simplified.push(ury);
+ simplified.push(urz);
+ simplified.push(uri);
+ }
+
+ // Add points until all raw points are within
+ // error tolerance to the simplified shape.
+ const int pn = points.size()/4;
+ for (int i = 0; i < simplified.size()/4; )
+ {
+ int ii = (i+1) % (simplified.size()/4);
+
+ const int ax = simplified[i*4+0];
+ const int az = simplified[i*4+2];
+ const int ai = simplified[i*4+3];
+
+ const int bx = simplified[ii*4+0];
+ const int bz = simplified[ii*4+2];
+ const int bi = simplified[ii*4+3];
+
+ // Find maximum deviation from the segment.
+ float maxd = 0;
+ int maxi = -1;
+ int ci, cinc, endi;
+
+ // Traverse the segment in lexilogical order so that the
+ // max deviation is calculated similarly when traversing
+ // opposite segments.
+ if (bx > ax || (bx == ax && bz > az))
+ {
+ cinc = 1;
+ ci = (ai+cinc) % pn;
+ endi = bi;
+ }
+ else
+ {
+ cinc = pn-1;
+ ci = (bi+cinc) % pn;
+ endi = ai;
+ }
+
+ // Tessellate only outer edges oredges between areas.
+ if ((points[ci*4+3] & RC_CONTOUR_REG_MASK) == 0 ||
+ (points[ci*4+3] & RC_AREA_BORDER))
+ {
+ while (ci != endi)
+ {
+ float d = distancePtSeg(points[ci*4+0], points[ci*4+2], ax, az, bx, bz);
+ if (d > maxd)
+ {
+ maxd = d;
+ maxi = ci;
+ }
+ ci = (ci+cinc) % pn;
+ }
+ }
+
+
+ // If the max deviation is larger than accepted error,
+ // add new point, else continue to next segment.
+ if (maxi != -1 && maxd > (maxError*maxError))
+ {
+ // Add space for the new point.
+ simplified.resize(simplified.size()+4);
+ const int n = simplified.size()/4;
+ for (int j = n-1; j > i; --j)
+ {
+ simplified[j*4+0] = simplified[(j-1)*4+0];
+ simplified[j*4+1] = simplified[(j-1)*4+1];
+ simplified[j*4+2] = simplified[(j-1)*4+2];
+ simplified[j*4+3] = simplified[(j-1)*4+3];
+ }
+ // Add the point.
+ simplified[(i+1)*4+0] = points[maxi*4+0];
+ simplified[(i+1)*4+1] = points[maxi*4+1];
+ simplified[(i+1)*4+2] = points[maxi*4+2];
+ simplified[(i+1)*4+3] = maxi;
+ }
+ else
+ {
+ ++i;
+ }
+ }
+
+ // Split too long edges.
+ if (maxEdgeLen > 0 && (buildFlags & (RC_CONTOUR_TESS_WALL_EDGES|RC_CONTOUR_TESS_AREA_EDGES)) != 0)
+ {
+ for (int i = 0; i < simplified.size()/4; )
+ {
+ const int ii = (i+1) % (simplified.size()/4);
+
+ const int ax = simplified[i*4+0];
+ const int az = simplified[i*4+2];
+ const int ai = simplified[i*4+3];
+
+ const int bx = simplified[ii*4+0];
+ const int bz = simplified[ii*4+2];
+ const int bi = simplified[ii*4+3];
+
+ // Find maximum deviation from the segment.
+ int maxi = -1;
+ int ci = (ai+1) % pn;
+
+ // Tessellate only outer edges or edges between areas.
+ bool tess = false;
+ // Wall edges.
+ if ((buildFlags & RC_CONTOUR_TESS_WALL_EDGES) && (points[ci*4+3] & RC_CONTOUR_REG_MASK) == 0)
+ tess = true;
+ // Edges between areas.
+ if ((buildFlags & RC_CONTOUR_TESS_AREA_EDGES) && (points[ci*4+3] & RC_AREA_BORDER))
+ tess = true;
+
+ if (tess)
+ {
+ int dx = bx - ax;
+ int dz = bz - az;
+ if (dx*dx + dz*dz > maxEdgeLen*maxEdgeLen)
+ {
+ // Round based on the segments in lexilogical order so that the
+ // max tesselation is consistent regardles in which direction
+ // segments are traversed.
+ if (bx > ax || (bx == ax && bz > az))
+ {
+ const int n = bi < ai ? (bi+pn - ai) : (bi - ai);
+ maxi = (ai + n/2) % pn;
+ }
+ else
+ {
+ const int n = bi < ai ? (bi+pn - ai) : (bi - ai);
+ maxi = (ai + (n+1)/2) % pn;
+ }
+ }
+ }
+
+ // If the max deviation is larger than accepted error,
+ // add new point, else continue to next segment.
+ if (maxi != -1)
+ {
+ // Add space for the new point.
+ simplified.resize(simplified.size()+4);
+ const int n = simplified.size()/4;
+ for (int j = n-1; j > i; --j)
+ {
+ simplified[j*4+0] = simplified[(j-1)*4+0];
+ simplified[j*4+1] = simplified[(j-1)*4+1];
+ simplified[j*4+2] = simplified[(j-1)*4+2];
+ simplified[j*4+3] = simplified[(j-1)*4+3];
+ }
+ // Add the point.
+ simplified[(i+1)*4+0] = points[maxi*4+0];
+ simplified[(i+1)*4+1] = points[maxi*4+1];
+ simplified[(i+1)*4+2] = points[maxi*4+2];
+ simplified[(i+1)*4+3] = maxi;
+ }
+ else
+ {
+ ++i;
+ }
+ }
+ }
+
+ for (int i = 0; i < simplified.size()/4; ++i)
+ {
+ // The edge vertex flag is take from the current raw point,
+ // and the neighbour region is take from the next raw point.
+ const int ai = (simplified[i*4+3]+1) % pn;
+ const int bi = simplified[i*4+3];
+ simplified[i*4+3] = (points[ai*4+3] & RC_CONTOUR_REG_MASK) | (points[bi*4+3] & RC_BORDER_VERTEX);
+ }
+
+}
+
+static void removeDegenerateSegments(rcIntArray& simplified)
+{
+ // Remove adjacent vertices which are equal on xz-plane,
+ // or else the triangulator will get confused.
+ for (int i = 0; i < simplified.size()/4; ++i)
+ {
+ int ni = i+1;
+ if (ni >= (simplified.size()/4))
+ ni = 0;
+
+ if (simplified[i*4+0] == simplified[ni*4+0] &&
+ simplified[i*4+2] == simplified[ni*4+2])
+ {
+ // Degenerate segment, remove.
+ for (int j = i; j < simplified.size()/4-1; ++j)
+ {
+ simplified[j*4+0] = simplified[(j+1)*4+0];
+ simplified[j*4+1] = simplified[(j+1)*4+1];
+ simplified[j*4+2] = simplified[(j+1)*4+2];
+ simplified[j*4+3] = simplified[(j+1)*4+3];
+ }
+ simplified.resize(simplified.size()-4);
+ }
+ }
+}
+
+static int calcAreaOfPolygon2D(const int* verts, const int nverts)
+{
+ int area = 0;
+ for (int i = 0, j = nverts-1; i < nverts; j=i++)
+ {
+ const int* vi = &verts[i*4];
+ const int* vj = &verts[j*4];
+ area += vi[0] * vj[2] - vj[0] * vi[2];
+ }
+ return (area+1) / 2;
+}
+
+inline bool ileft(const int* a, const int* b, const int* c)
+{
+ return (b[0] - a[0]) * (c[2] - a[2]) - (c[0] - a[0]) * (b[2] - a[2]) <= 0;
+}
+
+static void getClosestIndices(const int* vertsa, const int nvertsa,
+ const int* vertsb, const int nvertsb,
+ int& ia, int& ib)
+{
+ int closestDist = 0xfffffff;
+ ia = -1, ib = -1;
+ for (int i = 0; i < nvertsa; ++i)
+ {
+ const int in = (i+1) % nvertsa;
+ const int ip = (i+nvertsa-1) % nvertsa;
+ const int* va = &vertsa[i*4];
+ const int* van = &vertsa[in*4];
+ const int* vap = &vertsa[ip*4];
+
+ for (int j = 0; j < nvertsb; ++j)
+ {
+ const int* vb = &vertsb[j*4];
+ // vb must be "infront" of va.
+ if (ileft(vap,va,vb) && ileft(va,van,vb))
+ {
+ const int dx = vb[0] - va[0];
+ const int dz = vb[2] - va[2];
+ const int d = dx*dx + dz*dz;
+ if (d < closestDist)
+ {
+ ia = i;
+ ib = j;
+ closestDist = d;
+ }
+ }
+ }
+ }
+}
+
+static bool mergeContours(rcContour& ca, rcContour& cb, int ia, int ib)
+{
+ const int maxVerts = ca.nverts + cb.nverts + 2;
+ int* verts = (int*)rcAlloc(sizeof(int)*maxVerts*4, RC_ALLOC_PERM);
+ if (!verts)
+ return false;
+
+ int nv = 0;
+
+ // Copy contour A.
+ for (int i = 0; i <= ca.nverts; ++i)
+ {
+ int* dst = &verts[nv*4];
+ const int* src = &ca.verts[((ia+i)%ca.nverts)*4];
+ dst[0] = src[0];
+ dst[1] = src[1];
+ dst[2] = src[2];
+ dst[3] = src[3];
+ nv++;
+ }
+
+ // Copy contour B
+ for (int i = 0; i <= cb.nverts; ++i)
+ {
+ int* dst = &verts[nv*4];
+ const int* src = &cb.verts[((ib+i)%cb.nverts)*4];
+ dst[0] = src[0];
+ dst[1] = src[1];
+ dst[2] = src[2];
+ dst[3] = src[3];
+ nv++;
+ }
+
+ rcFree(ca.verts);
+ ca.verts = verts;
+ ca.nverts = nv;
+
+ rcFree(cb.verts);
+ cb.verts = 0;
+ cb.nverts = 0;
+
+ return true;
+}
+
+bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf,
+ const float maxError, const int maxEdgeLen,
+ rcContourSet& cset, const int buildFlags)
+{
+ rcAssert(ctx);
+
+ const int w = chf.width;
+ const int h = chf.height;
+
+ ctx->startTimer(RC_TIMER_BUILD_CONTOURS);
+
+ rcVcopy(cset.bmin, chf.bmin);
+ rcVcopy(cset.bmax, chf.bmax);
+ cset.cs = chf.cs;
+ cset.ch = chf.ch;
+
+ int maxContours = rcMax((int)chf.maxRegions, 8);
+ cset.conts = (rcContour*)rcAlloc(sizeof(rcContour)*maxContours, RC_ALLOC_PERM);
+ if (!cset.conts)
+ return false;
+ cset.nconts = 0;
+
+ rcScopedDelete<unsigned char> flags = (unsigned char*)rcAlloc(sizeof(unsigned char)*chf.spanCount, RC_ALLOC_TEMP);
+ if (!flags)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'flags' (%d).", chf.spanCount);
+ return false;
+ }
+
+ ctx->startTimer(RC_TIMER_BUILD_CONTOURS_TRACE);
+
+ // Mark boundaries.
+ for (int y = 0; y < h; ++y)
+ {
+ for (int x = 0; x < w; ++x)
+ {
+ const rcCompactCell& c = chf.cells[x+y*w];
+ for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
+ {
+ unsigned char res = 0;
+ const rcCompactSpan& s = chf.spans[i];
+ if (!chf.spans[i].reg || (chf.spans[i].reg & RC_BORDER_REG))
+ {
+ flags[i] = 0;
+ continue;
+ }
+ for (int dir = 0; dir < 4; ++dir)
+ {
+ unsigned short r = 0;
+ if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
+ {
+ const int ax = x + rcGetDirOffsetX(dir);
+ const int ay = y + rcGetDirOffsetY(dir);
+ const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir);
+ r = chf.spans[ai].reg;
+ }
+ if (r == chf.spans[i].reg)
+ res |= (1 << dir);
+ }
+ flags[i] = res ^ 0xf; // Inverse, mark non connected edges.
+ }
+ }
+ }
+
+ ctx->stopTimer(RC_TIMER_BUILD_CONTOURS_TRACE);
+
+ ctx->startTimer(RC_TIMER_BUILD_CONTOURS_SIMPLIFY);
+
+ rcIntArray verts(256);
+ rcIntArray simplified(64);
+
+ for (int y = 0; y < h; ++y)
+ {
+ for (int x = 0; x < w; ++x)
+ {
+ const rcCompactCell& c = chf.cells[x+y*w];
+ for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
+ {
+ if (flags[i] == 0 || flags[i] == 0xf)
+ {
+ flags[i] = 0;
+ continue;
+ }
+ const unsigned short reg = chf.spans[i].reg;
+ if (!reg || (reg & RC_BORDER_REG))
+ continue;
+ const unsigned char area = chf.areas[i];
+
+ verts.resize(0);
+ simplified.resize(0);
+ walkContour(x, y, i, chf, flags, verts);
+ simplifyContour(verts, simplified, maxError, maxEdgeLen, buildFlags);
+ removeDegenerateSegments(simplified);
+
+ // Store region->contour remap info.
+ // Create contour.
+ if (simplified.size()/4 >= 3)
+ {
+ if (cset.nconts >= maxContours)
+ {
+ // Allocate more contours.
+ // This can happen when there are tiny holes in the heightfield.
+ const int oldMax = maxContours;
+ maxContours *= 2;
+ rcContour* newConts = (rcContour*)rcAlloc(sizeof(rcContour)*maxContours, RC_ALLOC_PERM);
+ for (int j = 0; j < cset.nconts; ++j)
+ {
+ newConts[j] = cset.conts[j];
+ // Reset source pointers to prevent data deletion.
+ cset.conts[j].verts = 0;
+ cset.conts[j].rverts = 0;
+ }
+ rcFree(cset.conts);
+ cset.conts = newConts;
+
+ ctx->log(RC_LOG_WARNING, "rcBuildContours: Expanding max contours from %d to %d.", oldMax, maxContours);
+ }
+
+ rcContour* cont = &cset.conts[cset.nconts++];
+
+ cont->nverts = simplified.size()/4;
+ cont->verts = (int*)rcAlloc(sizeof(int)*cont->nverts*4, RC_ALLOC_PERM);
+ if (!cont->verts)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'verts' (%d).", cont->nverts);
+ return false;
+ }
+ memcpy(cont->verts, &simplified[0], sizeof(int)*cont->nverts*4);
+
+ cont->nrverts = verts.size()/4;
+ cont->rverts = (int*)rcAlloc(sizeof(int)*cont->nrverts*4, RC_ALLOC_PERM);
+ if (!cont->rverts)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'rverts' (%d).", cont->nrverts);
+ return false;
+ }
+ memcpy(cont->rverts, &verts[0], sizeof(int)*cont->nrverts*4);
+
+/* cont->cx = cont->cy = cont->cz = 0;
+ for (int i = 0; i < cont->nverts; ++i)
+ {
+ cont->cx += cont->verts[i*4+0];
+ cont->cy += cont->verts[i*4+1];
+ cont->cz += cont->verts[i*4+2];
+ }
+ cont->cx /= cont->nverts;
+ cont->cy /= cont->nverts;
+ cont->cz /= cont->nverts;*/
+
+ cont->reg = reg;
+ cont->area = area;
+ }
+ }
+ }
+ }
+
+ // Check and merge droppings.
+ // Sometimes the previous algorithms can fail and create several contours
+ // per area. This pass will try to merge the holes into the main region.
+ for (int i = 0; i < cset.nconts; ++i)
+ {
+ rcContour& cont = cset.conts[i];
+ // Check if the contour is would backwards.
+ if (calcAreaOfPolygon2D(cont.verts, cont.nverts) < 0)
+ {
+ // Find another contour which has the same region ID.
+ int mergeIdx = -1;
+ for (int j = 0; j < cset.nconts; ++j)
+ {
+ if (i == j) continue;
+ if (cset.conts[j].nverts && cset.conts[j].reg == cont.reg)
+ {
+ // Make sure the polygon is correctly oriented.
+ if (calcAreaOfPolygon2D(cset.conts[j].verts, cset.conts[j].nverts))
+ {
+ mergeIdx = j;
+ break;
+ }
+ }
+ }
+ if (mergeIdx == -1)
+ {
+ ctx->log(RC_LOG_WARNING, "rcBuildContours: Could not find merge target for bad contour %d.", i);
+ }
+ else
+ {
+ rcContour& mcont = cset.conts[mergeIdx];
+ // Merge by closest points.
+ int ia = 0, ib = 0;
+ getClosestIndices(mcont.verts, mcont.nverts, cont.verts, cont.nverts, ia, ib);
+ if (ia == -1 || ib == -1)
+ {
+ ctx->log(RC_LOG_WARNING, "rcBuildContours: Failed to find merge points for %d and %d.", i, mergeIdx);
+ continue;
+ }
+ if (!mergeContours(mcont, cont, ia, ib))
+ {
+ ctx->log(RC_LOG_WARNING, "rcBuildContours: Failed to merge contours %d and %d.", i, mergeIdx);
+ continue;
+ }
+ }
+ }
+ }
+
+ ctx->stopTimer(RC_TIMER_BUILD_CONTOURS_SIMPLIFY);
+
+ ctx->stopTimer(RC_TIMER_BUILD_CONTOURS);
+
+ return true;
+}
diff --git a/dep/recastnavigation/Recast/RecastFilter.cpp b/dep/recastnavigation/Recast/RecastFilter.cpp
new file mode 100644
index 0000000..d01808a
--- /dev/null
+++ b/dep/recastnavigation/Recast/RecastFilter.cpp
@@ -0,0 +1,179 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#define _USE_MATH_DEFINES
+#include <math.h>
+#include <stdio.h>
+#include "Recast.h"
+#include "RecastAssert.h"
+
+
+void rcFilterLowHangingWalkableObstacles(rcContext* ctx, const int walkableClimb, rcHeightfield& solid)
+{
+ rcAssert(ctx);
+
+ ctx->startTimer(RC_TIMER_FILTER_LOW_OBSTACLES);
+
+ const int w = solid.width;
+ const int h = solid.height;
+
+ for (int y = 0; y < h; ++y)
+ {
+ for (int x = 0; x < w; ++x)
+ {
+ rcSpan* ps = 0;
+ bool previousWalkable = false;
+
+ for (rcSpan* s = solid.spans[x + y*w]; s; ps = s, s = s->next)
+ {
+ const bool walkable = s->area != RC_NULL_AREA;
+ // If current span is not walkable, but there is walkable
+ // span just below it, mark the span above it walkable too.
+ if (!walkable && previousWalkable)
+ {
+ if (rcAbs((int)s->smax - (int)ps->smax) <= walkableClimb)
+ s->area = RC_NULL_AREA;
+ }
+ // Copy walkable flag so that it cannot propagate
+ // past multiple non-walkable objects.
+ previousWalkable = walkable;
+ }
+ }
+ }
+
+ ctx->stopTimer(RC_TIMER_FILTER_LOW_OBSTACLES);
+}
+
+void rcFilterLedgeSpans(rcContext* ctx, const int walkableHeight, const int walkableClimb,
+ rcHeightfield& solid)
+{
+ rcAssert(ctx);
+
+ ctx->startTimer(RC_TIMER_FILTER_BORDER);
+
+ const int w = solid.width;
+ const int h = solid.height;
+ const int MAX_HEIGHT = 0xffff;
+
+ // Mark border spans.
+ for (int y = 0; y < h; ++y)
+ {
+ for (int x = 0; x < w; ++x)
+ {
+ for (rcSpan* s = solid.spans[x + y*w]; s; s = s->next)
+ {
+ // Skip non walkable spans.
+ if (s->area == RC_NULL_AREA)
+ continue;
+
+ const int bot = (int)(s->smax);
+ const int top = s->next ? (int)(s->next->smin) : MAX_HEIGHT;
+
+ // Find neighbours minimum height.
+ int minh = MAX_HEIGHT;
+
+ // Min and max height of accessible neighbours.
+ int asmin = s->smax;
+ int asmax = s->smax;
+
+ for (int dir = 0; dir < 4; ++dir)
+ {
+ int dx = x + rcGetDirOffsetX(dir);
+ int dy = y + rcGetDirOffsetY(dir);
+ // Skip neighbours which are out of bounds.
+ if (dx < 0 || dy < 0 || dx >= w || dy >= h)
+ {
+ minh = rcMin(minh, -walkableClimb - bot);
+ continue;
+ }
+
+ // From minus infinity to the first span.
+ rcSpan* ns = solid.spans[dx + dy*w];
+ int nbot = -walkableClimb;
+ int ntop = ns ? (int)ns->smin : MAX_HEIGHT;
+ // Skip neightbour if the gap between the spans is too small.
+ if (rcMin(top,ntop) - rcMax(bot,nbot) > walkableHeight)
+ minh = rcMin(minh, nbot - bot);
+
+ // Rest of the spans.
+ for (ns = solid.spans[dx + dy*w]; ns; ns = ns->next)
+ {
+ nbot = (int)ns->smax;
+ ntop = ns->next ? (int)ns->next->smin : MAX_HEIGHT;
+ // Skip neightbour if the gap between the spans is too small.
+ if (rcMin(top,ntop) - rcMax(bot,nbot) > walkableHeight)
+ {
+ minh = rcMin(minh, nbot - bot);
+
+ // Find min/max accessible neighbour height.
+ if (rcAbs(nbot - bot) <= walkableClimb)
+ {
+ if (nbot < asmin) asmin = nbot;
+ if (nbot > asmax) asmax = nbot;
+ }
+
+ }
+ }
+ }
+
+ // The current span is close to a ledge if the drop to any
+ // neighbour span is less than the walkableClimb.
+ if (minh < -walkableClimb)
+ s->area = RC_NULL_AREA;
+
+ // If the difference between all neighbours is too large,
+ // we are at steep slope, mark the span as ledge.
+ if ((asmax - asmin) > walkableClimb)
+ {
+ s->area = RC_NULL_AREA;
+ }
+ }
+ }
+ }
+
+ ctx->stopTimer(RC_TIMER_FILTER_BORDER);
+}
+
+void rcFilterWalkableLowHeightSpans(rcContext* ctx, int walkableHeight, rcHeightfield& solid)
+{
+ rcAssert(ctx);
+
+ ctx->startTimer(RC_TIMER_FILTER_WALKABLE);
+
+ const int w = solid.width;
+ const int h = solid.height;
+ const int MAX_HEIGHT = 0xffff;
+
+ // Remove walkable flag from spans which do not have enough
+ // space above them for the agent to stand there.
+ for (int y = 0; y < h; ++y)
+ {
+ for (int x = 0; x < w; ++x)
+ {
+ for (rcSpan* s = solid.spans[x + y*w]; s; s = s->next)
+ {
+ const int bot = (int)(s->smax);
+ const int top = s->next ? (int)(s->next->smin) : MAX_HEIGHT;
+ if ((top - bot) <= walkableHeight)
+ s->area = RC_NULL_AREA;
+ }
+ }
+ }
+
+ ctx->stopTimer(RC_TIMER_FILTER_WALKABLE);
+}
diff --git a/dep/recastnavigation/Recast/RecastMesh.cpp b/dep/recastnavigation/Recast/RecastMesh.cpp
new file mode 100644
index 0000000..4b33c10
--- /dev/null
+++ b/dep/recastnavigation/Recast/RecastMesh.cpp
@@ -0,0 +1,1322 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#define _USE_MATH_DEFINES
+#include <math.h>
+#include <string.h>
+#include <stdio.h>
+#include "Recast.h"
+#include "RecastAlloc.h"
+#include "RecastAssert.h"
+
+struct rcEdge
+{
+ unsigned short vert[2];
+ unsigned short polyEdge[2];
+ unsigned short poly[2];
+};
+
+static bool buildMeshAdjacency(unsigned short* polys, const int npolys,
+ const int nverts, const int vertsPerPoly)
+{
+ // Based on code by Eric Lengyel from:
+ // http://www.terathon.com/code/edges.php
+
+ int maxEdgeCount = npolys*vertsPerPoly;
+ unsigned short* firstEdge = (unsigned short*)rcAlloc(sizeof(unsigned short)*(nverts + maxEdgeCount), RC_ALLOC_TEMP);
+ if (!firstEdge)
+ return false;
+ unsigned short* nextEdge = firstEdge + nverts;
+ int edgeCount = 0;
+
+ rcEdge* edges = (rcEdge*)rcAlloc(sizeof(rcEdge)*maxEdgeCount, RC_ALLOC_TEMP);
+ if (!edges)
+ {
+ rcFree(firstEdge);
+ return false;
+ }
+
+ for (int i = 0; i < nverts; i++)
+ firstEdge[i] = RC_MESH_NULL_IDX;
+
+ for (int i = 0; i < npolys; ++i)
+ {
+ unsigned short* t = &polys[i*vertsPerPoly*2];
+ for (int j = 0; j < vertsPerPoly; ++j)
+ {
+ unsigned short v0 = t[j];
+ unsigned short v1 = (j+1 >= vertsPerPoly || t[j+1] == RC_MESH_NULL_IDX) ? t[0] : t[j+1];
+ if (v0 < v1)
+ {
+ rcEdge& edge = edges[edgeCount];
+ edge.vert[0] = v0;
+ edge.vert[1] = v1;
+ edge.poly[0] = (unsigned short)i;
+ edge.polyEdge[0] = (unsigned short)j;
+ edge.poly[1] = (unsigned short)i;
+ edge.polyEdge[1] = 0;
+ // Insert edge
+ nextEdge[edgeCount] = firstEdge[v0];
+ firstEdge[v0] = (unsigned short)edgeCount;
+ edgeCount++;
+ }
+ }
+ }
+
+ for (int i = 0; i < npolys; ++i)
+ {
+ unsigned short* t = &polys[i*vertsPerPoly*2];
+ for (int j = 0; j < vertsPerPoly; ++j)
+ {
+ unsigned short v0 = t[j];
+ unsigned short v1 = (j+1 >= vertsPerPoly || t[j+1] == RC_MESH_NULL_IDX) ? t[0] : t[j+1];
+ if (v0 > v1)
+ {
+ for (unsigned short e = firstEdge[v1]; e != RC_MESH_NULL_IDX; e = nextEdge[e])
+ {
+ rcEdge& edge = edges[e];
+ if (edge.vert[1] == v0 && edge.poly[0] == edge.poly[1])
+ {
+ edge.poly[1] = (unsigned short)i;
+ edge.polyEdge[1] = (unsigned short)j;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // Store adjacency
+ for (int i = 0; i < edgeCount; ++i)
+ {
+ const rcEdge& e = edges[i];
+ if (e.poly[0] != e.poly[1])
+ {
+ unsigned short* p0 = &polys[e.poly[0]*vertsPerPoly*2];
+ unsigned short* p1 = &polys[e.poly[1]*vertsPerPoly*2];
+ p0[vertsPerPoly + e.polyEdge[0]] = e.poly[1];
+ p1[vertsPerPoly + e.polyEdge[1]] = e.poly[0];
+ }
+ }
+
+ rcFree(firstEdge);
+ rcFree(edges);
+
+ return true;
+}
+
+
+static const int VERTEX_BUCKET_COUNT = (1<<12);
+
+inline int computeVertexHash(int x, int y, int z)
+{
+ const unsigned int h1 = 0x8da6b343; // Large multiplicative constants;
+ const unsigned int h2 = 0xd8163841; // here arbitrarily chosen primes
+ const unsigned int h3 = 0xcb1ab31f;
+ unsigned int n = h1 * x + h2 * y + h3 * z;
+ return (int)(n & (VERTEX_BUCKET_COUNT-1));
+}
+
+static unsigned short addVertex(unsigned short x, unsigned short y, unsigned short z,
+ unsigned short* verts, int* firstVert, int* nextVert, int& nv)
+{
+ int bucket = computeVertexHash(x, 0, z);
+ int i = firstVert[bucket];
+
+ while (i != -1)
+ {
+ const unsigned short* v = &verts[i*3];
+ if (v[0] == x && (rcAbs(v[1] - y) <= 2) && v[2] == z)
+ return (unsigned short)i;
+ i = nextVert[i]; // next
+ }
+
+ // Could not find, create new.
+ i = nv; nv++;
+ unsigned short* v = &verts[i*3];
+ v[0] = x;
+ v[1] = y;
+ v[2] = z;
+ nextVert[i] = firstVert[bucket];
+ firstVert[bucket] = i;
+
+ return (unsigned short)i;
+}
+
+inline int prev(int i, int n) { return i-1 >= 0 ? i-1 : n-1; }
+inline int next(int i, int n) { return i+1 < n ? i+1 : 0; }
+
+inline int area2(const int* a, const int* b, const int* c)
+{
+ return (b[0] - a[0]) * (c[2] - a[2]) - (c[0] - a[0]) * (b[2] - a[2]);
+}
+
+// Exclusive or: true iff exactly one argument is true.
+// The arguments are negated to ensure that they are 0/1
+// values. Then the bitwise Xor operator may apply.
+// (This idea is due to Michael Baldwin.)
+inline bool xorb(bool x, bool y)
+{
+ return !x ^ !y;
+}
+
+// Returns true iff c is strictly to the left of the directed
+// line through a to b.
+inline bool left(const int* a, const int* b, const int* c)
+{
+ return area2(a, b, c) < 0;
+}
+
+inline bool leftOn(const int* a, const int* b, const int* c)
+{
+ return area2(a, b, c) <= 0;
+}
+
+inline bool collinear(const int* a, const int* b, const int* c)
+{
+ return area2(a, b, c) == 0;
+}
+
+// Returns true iff ab properly intersects cd: they share
+// a point interior to both segments. The properness of the
+// intersection is ensured by using strict leftness.
+bool intersectProp(const int* a, const int* b, const int* c, const int* d)
+{
+ // Eliminate improper cases.
+ if (collinear(a,b,c) || collinear(a,b,d) ||
+ collinear(c,d,a) || collinear(c,d,b))
+ return false;
+
+ return xorb(left(a,b,c), left(a,b,d)) && xorb(left(c,d,a), left(c,d,b));
+}
+
+// Returns T iff (a,b,c) are collinear and point c lies
+// on the closed segement ab.
+static bool between(const int* a, const int* b, const int* c)
+{
+ if (!collinear(a, b, c))
+ return false;
+ // If ab not vertical, check betweenness on x; else on y.
+ if (a[0] != b[0])
+ return ((a[0] <= c[0]) && (c[0] <= b[0])) || ((a[0] >= c[0]) && (c[0] >= b[0]));
+ else
+ return ((a[2] <= c[2]) && (c[2] <= b[2])) || ((a[2] >= c[2]) && (c[2] >= b[2]));
+}
+
+// Returns true iff segments ab and cd intersect, properly or improperly.
+static bool intersect(const int* a, const int* b, const int* c, const int* d)
+{
+ if (intersectProp(a, b, c, d))
+ return true;
+ else if (between(a, b, c) || between(a, b, d) ||
+ between(c, d, a) || between(c, d, b))
+ return true;
+ else
+ return false;
+}
+
+static bool vequal(const int* a, const int* b)
+{
+ return a[0] == b[0] && a[2] == b[2];
+}
+
+// Returns T iff (v_i, v_j) is a proper internal *or* external
+// diagonal of P, *ignoring edges incident to v_i and v_j*.
+static bool diagonalie(int i, int j, int n, const int* verts, int* indices)
+{
+ const int* d0 = &verts[(indices[i] & 0x0fffffff) * 4];
+ const int* d1 = &verts[(indices[j] & 0x0fffffff) * 4];
+
+ // For each edge (k,k+1) of P
+ for (int k = 0; k < n; k++)
+ {
+ int k1 = next(k, n);
+ // Skip edges incident to i or j
+ if (!((k == i) || (k1 == i) || (k == j) || (k1 == j)))
+ {
+ const int* p0 = &verts[(indices[k] & 0x0fffffff) * 4];
+ const int* p1 = &verts[(indices[k1] & 0x0fffffff) * 4];
+
+ if (vequal(d0, p0) || vequal(d1, p0) || vequal(d0, p1) || vequal(d1, p1))
+ continue;
+
+ if (intersect(d0, d1, p0, p1))
+ return false;
+ }
+ }
+ return true;
+}
+
+// Returns true iff the diagonal (i,j) is strictly internal to the
+// polygon P in the neighborhood of the i endpoint.
+static bool inCone(int i, int j, int n, const int* verts, int* indices)
+{
+ const int* pi = &verts[(indices[i] & 0x0fffffff) * 4];
+ const int* pj = &verts[(indices[j] & 0x0fffffff) * 4];
+ const int* pi1 = &verts[(indices[next(i, n)] & 0x0fffffff) * 4];
+ const int* pin1 = &verts[(indices[prev(i, n)] & 0x0fffffff) * 4];
+
+ // If P[i] is a convex vertex [ i+1 left or on (i-1,i) ].
+ if (leftOn(pin1, pi, pi1))
+ return left(pi, pj, pin1) && left(pj, pi, pi1);
+ // Assume (i-1,i,i+1) not collinear.
+ // else P[i] is reflex.
+ return !(leftOn(pi, pj, pi1) && leftOn(pj, pi, pin1));
+}
+
+// Returns T iff (v_i, v_j) is a proper internal
+// diagonal of P.
+static bool diagonal(int i, int j, int n, const int* verts, int* indices)
+{
+ return inCone(i, j, n, verts, indices) && diagonalie(i, j, n, verts, indices);
+}
+
+static int triangulate(int n, const int* verts, int* indices, int* tris)
+{
+ int ntris = 0;
+ int* dst = tris;
+
+ // The last bit of the index is used to indicate if the vertex can be removed.
+ for (int i = 0; i < n; i++)
+ {
+ int i1 = next(i, n);
+ int i2 = next(i1, n);
+ if (diagonal(i, i2, n, verts, indices))
+ indices[i1] |= 0x80000000;
+ }
+
+ while (n > 3)
+ {
+ int minLen = -1;
+ int mini = -1;
+ for (int i = 0; i < n; i++)
+ {
+ int i1 = next(i, n);
+ if (indices[i1] & 0x80000000)
+ {
+ const int* p0 = &verts[(indices[i] & 0x0fffffff) * 4];
+ const int* p2 = &verts[(indices[next(i1, n)] & 0x0fffffff) * 4];
+
+ int dx = p2[0] - p0[0];
+ int dy = p2[2] - p0[2];
+ int len = dx*dx + dy*dy;
+
+ if (minLen < 0 || len < minLen)
+ {
+ minLen = len;
+ mini = i;
+ }
+ }
+ }
+
+ if (mini == -1)
+ {
+ // Should not happen.
+/* printf("mini == -1 ntris=%d n=%d\n", ntris, n);
+ for (int i = 0; i < n; i++)
+ {
+ printf("%d ", indices[i] & 0x0fffffff);
+ }
+ printf("\n");*/
+ return -ntris;
+ }
+
+ int i = mini;
+ int i1 = next(i, n);
+ int i2 = next(i1, n);
+
+ *dst++ = indices[i] & 0x0fffffff;
+ *dst++ = indices[i1] & 0x0fffffff;
+ *dst++ = indices[i2] & 0x0fffffff;
+ ntris++;
+
+ // Removes P[i1] by copying P[i+1]...P[n-1] left one index.
+ n--;
+ for (int k = i1; k < n; k++)
+ indices[k] = indices[k+1];
+
+ if (i1 >= n) i1 = 0;
+ i = prev(i1,n);
+ // Update diagonal flags.
+ if (diagonal(prev(i, n), i1, n, verts, indices))
+ indices[i] |= 0x80000000;
+ else
+ indices[i] &= 0x0fffffff;
+
+ if (diagonal(i, next(i1, n), n, verts, indices))
+ indices[i1] |= 0x80000000;
+ else
+ indices[i1] &= 0x0fffffff;
+ }
+
+ // Append the remaining triangle.
+ *dst++ = indices[0] & 0x0fffffff;
+ *dst++ = indices[1] & 0x0fffffff;
+ *dst++ = indices[2] & 0x0fffffff;
+ ntris++;
+
+ return ntris;
+}
+
+static int countPolyVerts(const unsigned short* p, const int nvp)
+{
+ for (int i = 0; i < nvp; ++i)
+ if (p[i] == RC_MESH_NULL_IDX)
+ return i;
+ return nvp;
+}
+
+inline bool uleft(const unsigned short* a, const unsigned short* b, const unsigned short* c)
+{
+ return ((int)b[0] - (int)a[0]) * ((int)c[2] - (int)a[2]) -
+ ((int)c[0] - (int)a[0]) * ((int)b[2] - (int)a[2]) < 0;
+}
+
+static int getPolyMergeValue(unsigned short* pa, unsigned short* pb,
+ const unsigned short* verts, int& ea, int& eb,
+ const int nvp)
+{
+ const int na = countPolyVerts(pa, nvp);
+ const int nb = countPolyVerts(pb, nvp);
+
+ // If the merged polygon would be too big, do not merge.
+ if (na+nb-2 > nvp)
+ return -1;
+
+ // Check if the polygons share an edge.
+ ea = -1;
+ eb = -1;
+
+ for (int i = 0; i < na; ++i)
+ {
+ unsigned short va0 = pa[i];
+ unsigned short va1 = pa[(i+1) % na];
+ if (va0 > va1)
+ rcSwap(va0, va1);
+ for (int j = 0; j < nb; ++j)
+ {
+ unsigned short vb0 = pb[j];
+ unsigned short vb1 = pb[(j+1) % nb];
+ if (vb0 > vb1)
+ rcSwap(vb0, vb1);
+ if (va0 == vb0 && va1 == vb1)
+ {
+ ea = i;
+ eb = j;
+ break;
+ }
+ }
+ }
+
+ // No common edge, cannot merge.
+ if (ea == -1 || eb == -1)
+ return -1;
+
+ // Check to see if the merged polygon would be convex.
+ unsigned short va, vb, vc;
+
+ va = pa[(ea+na-1) % na];
+ vb = pa[ea];
+ vc = pb[(eb+2) % nb];
+ if (!uleft(&verts[va*3], &verts[vb*3], &verts[vc*3]))
+ return -1;
+
+ va = pb[(eb+nb-1) % nb];
+ vb = pb[eb];
+ vc = pa[(ea+2) % na];
+ if (!uleft(&verts[va*3], &verts[vb*3], &verts[vc*3]))
+ return -1;
+
+ va = pa[ea];
+ vb = pa[(ea+1)%na];
+
+ int dx = (int)verts[va*3+0] - (int)verts[vb*3+0];
+ int dy = (int)verts[va*3+2] - (int)verts[vb*3+2];
+
+ return dx*dx + dy*dy;
+}
+
+static void mergePolys(unsigned short* pa, unsigned short* pb, int ea, int eb,
+ unsigned short* tmp, const int nvp)
+{
+ const int na = countPolyVerts(pa, nvp);
+ const int nb = countPolyVerts(pb, nvp);
+
+ // Merge polygons.
+ memset(tmp, 0xff, sizeof(unsigned short)*nvp);
+ int n = 0;
+ // Add pa
+ for (int i = 0; i < na-1; ++i)
+ tmp[n++] = pa[(ea+1+i) % na];
+ // Add pb
+ for (int i = 0; i < nb-1; ++i)
+ tmp[n++] = pb[(eb+1+i) % nb];
+
+ memcpy(pa, tmp, sizeof(unsigned short)*nvp);
+}
+
+static void pushFront(int v, int* arr, int& an)
+{
+ an++;
+ for (int i = an-1; i > 0; --i) arr[i] = arr[i-1];
+ arr[0] = v;
+}
+
+static void pushBack(int v, int* arr, int& an)
+{
+ arr[an] = v;
+ an++;
+}
+
+static bool canRemoveVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned short rem)
+{
+ const int nvp = mesh.nvp;
+
+ // Count number of polygons to remove.
+ int numRemovedVerts = 0;
+ int numTouchedVerts = 0;
+ int numRemainingEdges = 0;
+ for (int i = 0; i < mesh.npolys; ++i)
+ {
+ unsigned short* p = &mesh.polys[i*nvp*2];
+ const int nv = countPolyVerts(p, nvp);
+ int numRemoved = 0;
+ int numVerts = 0;
+ for (int j = 0; j < nv; ++j)
+ {
+ if (p[j] == rem)
+ {
+ numTouchedVerts++;
+ numRemoved++;
+ }
+ numVerts++;
+ }
+ if (numRemoved)
+ {
+ numRemovedVerts += numRemoved;
+ numRemainingEdges += numVerts-(numRemoved+1);
+ }
+ }
+
+ // There would be too few edges remaining to create a polygon.
+ // This can happen for example when a tip of a triangle is marked
+ // as deletion, but there are no other polys that share the vertex.
+ // In this case, the vertex should not be removed.
+ if (numRemainingEdges <= 2)
+ return false;
+
+ // Find edges which share the removed vertex.
+ const int maxEdges = numTouchedVerts*2;
+ int nedges = 0;
+ rcScopedDelete<int> edges = (int*)rcAlloc(sizeof(int)*maxEdges*3, RC_ALLOC_TEMP);
+ if (!edges)
+ {
+ ctx->log(RC_LOG_WARNING, "canRemoveVertex: Out of memory 'edges' (%d).", maxEdges*3);
+ return false;
+ }
+
+ for (int i = 0; i < mesh.npolys; ++i)
+ {
+ unsigned short* p = &mesh.polys[i*nvp*2];
+ const int nv = countPolyVerts(p, nvp);
+
+ // Collect edges which touches the removed vertex.
+ for (int j = 0, k = nv-1; j < nv; k = j++)
+ {
+ if (p[j] == rem || p[k] == rem)
+ {
+ // Arrange edge so that a=rem.
+ int a = p[j], b = p[k];
+ if (b == rem)
+ rcSwap(a,b);
+
+ // Check if the edge exists
+ bool exists = false;
+ for (int k = 0; k < nedges; ++k)
+ {
+ int* e = &edges[k*3];
+ if (e[1] == b)
+ {
+ // Exists, increment vertex share count.
+ e[2]++;
+ exists = true;
+ }
+ }
+ // Add new edge.
+ if (!exists)
+ {
+ int* e = &edges[nedges*3];
+ e[0] = a;
+ e[1] = b;
+ e[2] = 1;
+ nedges++;
+ }
+ }
+ }
+ }
+
+ // There should be no more than 2 open edges.
+ // This catches the case that two non-adjacent polygons
+ // share the removed vertex. In that case, do not remove the vertex.
+ int numOpenEdges = 0;
+ for (int i = 0; i < nedges; ++i)
+ {
+ if (edges[i*3+2] < 2)
+ numOpenEdges++;
+ }
+ if (numOpenEdges > 2)
+ return false;
+
+ return true;
+}
+
+static bool removeVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned short rem, const int maxTris)
+{
+ const int nvp = mesh.nvp;
+
+ // Count number of polygons to remove.
+ int numRemovedVerts = 0;
+ for (int i = 0; i < mesh.npolys; ++i)
+ {
+ unsigned short* p = &mesh.polys[i*nvp*2];
+ const int nv = countPolyVerts(p, nvp);
+ for (int j = 0; j < nv; ++j)
+ {
+ if (p[j] == rem)
+ numRemovedVerts++;
+ }
+ }
+
+ int nedges = 0;
+ rcScopedDelete<int> edges = (int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp*4, RC_ALLOC_TEMP);
+ if (!edges)
+ {
+ ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'edges' (%d).", numRemovedVerts*nvp*4);
+ return false;
+ }
+
+ int nhole = 0;
+ rcScopedDelete<int> hole = (int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp, RC_ALLOC_TEMP);
+ if (!hole)
+ {
+ ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'hole' (%d).", numRemovedVerts*nvp);
+ return false;
+ }
+
+ int nhreg = 0;
+ rcScopedDelete<int> hreg = (int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp, RC_ALLOC_TEMP);
+ if (!hreg)
+ {
+ ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'hreg' (%d).", numRemovedVerts*nvp);
+ return false;
+ }
+
+ int nharea = 0;
+ rcScopedDelete<int> harea = (int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp, RC_ALLOC_TEMP);
+ if (!harea)
+ {
+ ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'harea' (%d).", numRemovedVerts*nvp);
+ return false;
+ }
+
+ for (int i = 0; i < mesh.npolys; ++i)
+ {
+ unsigned short* p = &mesh.polys[i*nvp*2];
+ const int nv = countPolyVerts(p, nvp);
+ bool hasRem = false;
+ for (int j = 0; j < nv; ++j)
+ if (p[j] == rem) hasRem = true;
+ if (hasRem)
+ {
+ // Collect edges which does not touch the removed vertex.
+ for (int j = 0, k = nv-1; j < nv; k = j++)
+ {
+ if (p[j] != rem && p[k] != rem)
+ {
+ int* e = &edges[nedges*4];
+ e[0] = p[k];
+ e[1] = p[j];
+ e[2] = mesh.regs[i];
+ e[3] = mesh.areas[i];
+ nedges++;
+ }
+ }
+ // Remove the polygon.
+ unsigned short* p2 = &mesh.polys[(mesh.npolys-1)*nvp*2];
+ memcpy(p,p2,sizeof(unsigned short)*nvp);
+ memset(p+nvp,0xff,sizeof(unsigned short)*nvp);
+ mesh.regs[i] = mesh.regs[mesh.npolys-1];
+ mesh.areas[i] = mesh.areas[mesh.npolys-1];
+ mesh.npolys--;
+ --i;
+ }
+ }
+
+ // Remove vertex.
+ for (int i = (int)rem; i < mesh.nverts; ++i)
+ {
+ mesh.verts[i*3+0] = mesh.verts[(i+1)*3+0];
+ mesh.verts[i*3+1] = mesh.verts[(i+1)*3+1];
+ mesh.verts[i*3+2] = mesh.verts[(i+1)*3+2];
+ }
+ mesh.nverts--;
+
+ // Adjust indices to match the removed vertex layout.
+ for (int i = 0; i < mesh.npolys; ++i)
+ {
+ unsigned short* p = &mesh.polys[i*nvp*2];
+ const int nv = countPolyVerts(p, nvp);
+ for (int j = 0; j < nv; ++j)
+ if (p[j] > rem) p[j]--;
+ }
+ for (int i = 0; i < nedges; ++i)
+ {
+ if (edges[i*4+0] > rem) edges[i*4+0]--;
+ if (edges[i*4+1] > rem) edges[i*4+1]--;
+ }
+
+ if (nedges == 0)
+ return true;
+
+ // Start with one vertex, keep appending connected
+ // segments to the start and end of the hole.
+ pushBack(edges[0], hole, nhole);
+ pushBack(edges[2], hreg, nhreg);
+ pushBack(edges[3], harea, nharea);
+
+ while (nedges)
+ {
+ bool match = false;
+
+ for (int i = 0; i < nedges; ++i)
+ {
+ const int ea = edges[i*4+0];
+ const int eb = edges[i*4+1];
+ const int r = edges[i*4+2];
+ const int a = edges[i*4+3];
+ bool add = false;
+ if (hole[0] == eb)
+ {
+ // The segment matches the beginning of the hole boundary.
+ pushFront(ea, hole, nhole);
+ pushFront(r, hreg, nhreg);
+ pushFront(a, harea, nharea);
+ add = true;
+ }
+ else if (hole[nhole-1] == ea)
+ {
+ // The segment matches the end of the hole boundary.
+ pushBack(eb, hole, nhole);
+ pushBack(r, hreg, nhreg);
+ pushBack(a, harea, nharea);
+ add = true;
+ }
+ if (add)
+ {
+ // The edge segment was added, remove it.
+ edges[i*4+0] = edges[(nedges-1)*4+0];
+ edges[i*4+1] = edges[(nedges-1)*4+1];
+ edges[i*4+2] = edges[(nedges-1)*4+2];
+ edges[i*4+3] = edges[(nedges-1)*4+3];
+ --nedges;
+ match = true;
+ --i;
+ }
+ }
+
+ if (!match)
+ break;
+ }
+
+ rcScopedDelete<int> tris = (int*)rcAlloc(sizeof(int)*nhole*3, RC_ALLOC_TEMP);
+ if (!tris)
+ {
+ ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'tris' (%d).", nhole*3);
+ return false;
+ }
+
+ rcScopedDelete<int> tverts = (int*)rcAlloc(sizeof(int)*nhole*4, RC_ALLOC_TEMP);
+ if (!tverts)
+ {
+ ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'tverts' (%d).", nhole*4);
+ return false;
+ }
+
+ rcScopedDelete<int> thole = (int*)rcAlloc(sizeof(int)*nhole, RC_ALLOC_TEMP);
+ if (!tverts)
+ {
+ ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'thole' (%d).", nhole);
+ return false;
+ }
+
+ // Generate temp vertex array for triangulation.
+ for (int i = 0; i < nhole; ++i)
+ {
+ const int pi = hole[i];
+ tverts[i*4+0] = mesh.verts[pi*3+0];
+ tverts[i*4+1] = mesh.verts[pi*3+1];
+ tverts[i*4+2] = mesh.verts[pi*3+2];
+ tverts[i*4+3] = 0;
+ thole[i] = i;
+ }
+
+ // Triangulate the hole.
+ int ntris = triangulate(nhole, &tverts[0], &thole[0], tris);
+ if (ntris < 0)
+ {
+ ntris = -ntris;
+ ctx->log(RC_LOG_WARNING, "removeVertex: triangulate() returned bad results.");
+ }
+
+ // Merge the hole triangles back to polygons.
+ rcScopedDelete<unsigned short> polys = (unsigned short*)rcAlloc(sizeof(unsigned short)*(ntris+1)*nvp, RC_ALLOC_TEMP);
+ if (!polys)
+ {
+ ctx->log(RC_LOG_ERROR, "removeVertex: Out of memory 'polys' (%d).", (ntris+1)*nvp);
+ return false;
+ }
+ rcScopedDelete<unsigned short> pregs = (unsigned short*)rcAlloc(sizeof(unsigned short)*ntris, RC_ALLOC_TEMP);
+ if (!pregs)
+ {
+ ctx->log(RC_LOG_ERROR, "removeVertex: Out of memory 'pregs' (%d).", ntris);
+ return false;
+ }
+ rcScopedDelete<unsigned char> pareas = (unsigned char*)rcAlloc(sizeof(unsigned char)*ntris, RC_ALLOC_TEMP);
+ if (!pregs)
+ {
+ ctx->log(RC_LOG_ERROR, "removeVertex: Out of memory 'pareas' (%d).", ntris);
+ return false;
+ }
+
+ unsigned short* tmpPoly = &polys[ntris*nvp];
+
+ // Build initial polygons.
+ int npolys = 0;
+ memset(polys, 0xff, ntris*nvp*sizeof(unsigned short));
+ for (int j = 0; j < ntris; ++j)
+ {
+ int* t = &tris[j*3];
+ if (t[0] != t[1] && t[0] != t[2] && t[1] != t[2])
+ {
+ polys[npolys*nvp+0] = (unsigned short)hole[t[0]];
+ polys[npolys*nvp+1] = (unsigned short)hole[t[1]];
+ polys[npolys*nvp+2] = (unsigned short)hole[t[2]];
+ pregs[npolys] = (unsigned short)hreg[t[0]];
+ pareas[npolys] = (unsigned char)harea[t[0]];
+ npolys++;
+ }
+ }
+ if (!npolys)
+ return true;
+
+ // Merge polygons.
+ if (nvp > 3)
+ {
+ for (;;)
+ {
+ // Find best polygons to merge.
+ int bestMergeVal = 0;
+ int bestPa = 0, bestPb = 0, bestEa = 0, bestEb = 0;
+
+ for (int j = 0; j < npolys-1; ++j)
+ {
+ unsigned short* pj = &polys[j*nvp];
+ for (int k = j+1; k < npolys; ++k)
+ {
+ unsigned short* pk = &polys[k*nvp];
+ int ea, eb;
+ int v = getPolyMergeValue(pj, pk, mesh.verts, ea, eb, nvp);
+ if (v > bestMergeVal)
+ {
+ bestMergeVal = v;
+ bestPa = j;
+ bestPb = k;
+ bestEa = ea;
+ bestEb = eb;
+ }
+ }
+ }
+
+ if (bestMergeVal > 0)
+ {
+ // Found best, merge.
+ unsigned short* pa = &polys[bestPa*nvp];
+ unsigned short* pb = &polys[bestPb*nvp];
+ mergePolys(pa, pb, bestEa, bestEb, tmpPoly, nvp);
+ memcpy(pb, &polys[(npolys-1)*nvp], sizeof(unsigned short)*nvp);
+ pregs[bestPb] = pregs[npolys-1];
+ pareas[bestPb] = pareas[npolys-1];
+ npolys--;
+ }
+ else
+ {
+ // Could not merge any polygons, stop.
+ break;
+ }
+ }
+ }
+
+ // Store polygons.
+ for (int i = 0; i < npolys; ++i)
+ {
+ if (mesh.npolys >= maxTris) break;
+ unsigned short* p = &mesh.polys[mesh.npolys*nvp*2];
+ memset(p,0xff,sizeof(unsigned short)*nvp*2);
+ for (int j = 0; j < nvp; ++j)
+ p[j] = polys[i*nvp+j];
+ mesh.regs[mesh.npolys] = pregs[i];
+ mesh.areas[mesh.npolys] = pareas[i];
+ mesh.npolys++;
+ if (mesh.npolys > maxTris)
+ {
+ ctx->log(RC_LOG_ERROR, "removeVertex: Too many polygons %d (max:%d).", mesh.npolys, maxTris);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+bool rcBuildPolyMesh(rcContext* ctx, rcContourSet& cset, int nvp, rcPolyMesh& mesh)
+{
+ rcAssert(ctx);
+
+ ctx->startTimer(RC_TIMER_BUILD_POLYMESH);
+
+ rcVcopy(mesh.bmin, cset.bmin);
+ rcVcopy(mesh.bmax, cset.bmax);
+ mesh.cs = cset.cs;
+ mesh.ch = cset.ch;
+
+ int maxVertices = 0;
+ int maxTris = 0;
+ int maxVertsPerCont = 0;
+ for (int i = 0; i < cset.nconts; ++i)
+ {
+ // Skip null contours.
+ if (cset.conts[i].nverts < 3) continue;
+ maxVertices += cset.conts[i].nverts;
+ maxTris += cset.conts[i].nverts - 2;
+ maxVertsPerCont = rcMax(maxVertsPerCont, cset.conts[i].nverts);
+ }
+
+ if (maxVertices >= 0xfffe)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Too many vertices %d.", maxVertices);
+ return false;
+ }
+
+ rcScopedDelete<unsigned char> vflags = (unsigned char*)rcAlloc(sizeof(unsigned char)*maxVertices, RC_ALLOC_TEMP);
+ if (!vflags)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.verts' (%d).", maxVertices);
+ return false;
+ }
+ memset(vflags, 0, maxVertices);
+
+ mesh.verts = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxVertices*3, RC_ALLOC_PERM);
+ if (!mesh.verts)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.verts' (%d).", maxVertices);
+ return false;
+ }
+ mesh.polys = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxTris*nvp*2*2, RC_ALLOC_PERM);
+ if (!mesh.polys)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.polys' (%d).", maxTris*nvp*2);
+ return false;
+ }
+ mesh.regs = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxTris, RC_ALLOC_PERM);
+ if (!mesh.regs)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.regs' (%d).", maxTris);
+ return false;
+ }
+ mesh.areas = (unsigned char*)rcAlloc(sizeof(unsigned char)*maxTris, RC_ALLOC_PERM);
+ if (!mesh.areas)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.areas' (%d).", maxTris);
+ return false;
+ }
+
+ mesh.nverts = 0;
+ mesh.npolys = 0;
+ mesh.nvp = nvp;
+ mesh.maxpolys = maxTris;
+
+ memset(mesh.verts, 0, sizeof(unsigned short)*maxVertices*3);
+ memset(mesh.polys, 0xff, sizeof(unsigned short)*maxTris*nvp*2);
+ memset(mesh.regs, 0, sizeof(unsigned short)*maxTris);
+ memset(mesh.areas, 0, sizeof(unsigned char)*maxTris);
+
+ rcScopedDelete<int> nextVert = (int*)rcAlloc(sizeof(int)*maxVertices, RC_ALLOC_TEMP);
+ if (!nextVert)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'nextVert' (%d).", maxVertices);
+ return false;
+ }
+ memset(nextVert, 0, sizeof(int)*maxVertices);
+
+ rcScopedDelete<int> firstVert = (int*)rcAlloc(sizeof(int)*VERTEX_BUCKET_COUNT, RC_ALLOC_TEMP);
+ if (!firstVert)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'firstVert' (%d).", VERTEX_BUCKET_COUNT);
+ return false;
+ }
+ for (int i = 0; i < VERTEX_BUCKET_COUNT; ++i)
+ firstVert[i] = -1;
+
+ rcScopedDelete<int> indices = (int*)rcAlloc(sizeof(int)*maxVertsPerCont, RC_ALLOC_TEMP);
+ if (!indices)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'indices' (%d).", maxVertsPerCont);
+ return false;
+ }
+ rcScopedDelete<int> tris = (int*)rcAlloc(sizeof(int)*maxVertsPerCont*3, RC_ALLOC_TEMP);
+ if (!tris)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'tris' (%d).", maxVertsPerCont*3);
+ return false;
+ }
+ rcScopedDelete<unsigned short> polys = (unsigned short*)rcAlloc(sizeof(unsigned short)*(maxVertsPerCont+1)*nvp, RC_ALLOC_TEMP);
+ if (!polys)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'polys' (%d).", maxVertsPerCont*nvp);
+ return false;
+ }
+ unsigned short* tmpPoly = &polys[maxVertsPerCont*nvp];
+
+ for (int i = 0; i < cset.nconts; ++i)
+ {
+ rcContour& cont = cset.conts[i];
+
+ // Skip null contours.
+ if (cont.nverts < 3)
+ continue;
+
+ // Triangulate contour
+ for (int j = 0; j < cont.nverts; ++j)
+ indices[j] = j;
+
+ int ntris = triangulate(cont.nverts, cont.verts, &indices[0], &tris[0]);
+ if (ntris <= 0)
+ {
+ // Bad triangulation, should not happen.
+/* printf("\tconst float bmin[3] = {%ff,%ff,%ff};\n", cset.bmin[0], cset.bmin[1], cset.bmin[2]);
+ printf("\tconst float cs = %ff;\n", cset.cs);
+ printf("\tconst float ch = %ff;\n", cset.ch);
+ printf("\tconst int verts[] = {\n");
+ for (int k = 0; k < cont.nverts; ++k)
+ {
+ const int* v = &cont.verts[k*4];
+ printf("\t\t%d,%d,%d,%d,\n", v[0], v[1], v[2], v[3]);
+ }
+ printf("\t};\n\tconst int nverts = sizeof(verts)/(sizeof(int)*4);\n");*/
+ ctx->log(RC_LOG_WARNING, "rcBuildPolyMesh: Bad triangulation Contour %d.", i);
+ ntris = -ntris;
+ }
+
+ // Add and merge vertices.
+ for (int j = 0; j < cont.nverts; ++j)
+ {
+ const int* v = &cont.verts[j*4];
+ indices[j] = addVertex((unsigned short)v[0], (unsigned short)v[1], (unsigned short)v[2],
+ mesh.verts, firstVert, nextVert, mesh.nverts);
+ if (v[3] & RC_BORDER_VERTEX)
+ {
+ // This vertex should be removed.
+ vflags[indices[j]] = 1;
+ }
+ }
+
+ // Build initial polygons.
+ int npolys = 0;
+ memset(polys, 0xff, maxVertsPerCont*nvp*sizeof(unsigned short));
+ for (int j = 0; j < ntris; ++j)
+ {
+ int* t = &tris[j*3];
+ if (t[0] != t[1] && t[0] != t[2] && t[1] != t[2])
+ {
+ polys[npolys*nvp+0] = (unsigned short)indices[t[0]];
+ polys[npolys*nvp+1] = (unsigned short)indices[t[1]];
+ polys[npolys*nvp+2] = (unsigned short)indices[t[2]];
+ npolys++;
+ }
+ }
+ if (!npolys)
+ continue;
+
+ // Merge polygons.
+ if (nvp > 3)
+ {
+ for(;;)
+ {
+ // Find best polygons to merge.
+ int bestMergeVal = 0;
+ int bestPa = 0, bestPb = 0, bestEa = 0, bestEb = 0;
+
+ for (int j = 0; j < npolys-1; ++j)
+ {
+ unsigned short* pj = &polys[j*nvp];
+ for (int k = j+1; k < npolys; ++k)
+ {
+ unsigned short* pk = &polys[k*nvp];
+ int ea, eb;
+ int v = getPolyMergeValue(pj, pk, mesh.verts, ea, eb, nvp);
+ if (v > bestMergeVal)
+ {
+ bestMergeVal = v;
+ bestPa = j;
+ bestPb = k;
+ bestEa = ea;
+ bestEb = eb;
+ }
+ }
+ }
+
+ if (bestMergeVal > 0)
+ {
+ // Found best, merge.
+ unsigned short* pa = &polys[bestPa*nvp];
+ unsigned short* pb = &polys[bestPb*nvp];
+ mergePolys(pa, pb, bestEa, bestEb, tmpPoly, nvp);
+ memcpy(pb, &polys[(npolys-1)*nvp], sizeof(unsigned short)*nvp);
+ npolys--;
+ }
+ else
+ {
+ // Could not merge any polygons, stop.
+ break;
+ }
+ }
+ }
+
+ // Store polygons.
+ for (int j = 0; j < npolys; ++j)
+ {
+ unsigned short* p = &mesh.polys[mesh.npolys*nvp*2];
+ unsigned short* q = &polys[j*nvp];
+ for (int k = 0; k < nvp; ++k)
+ p[k] = q[k];
+ mesh.regs[mesh.npolys] = cont.reg;
+ mesh.areas[mesh.npolys] = cont.area;
+ mesh.npolys++;
+ if (mesh.npolys > maxTris)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Too many polygons %d (max:%d).", mesh.npolys, maxTris);
+ return false;
+ }
+ }
+ }
+
+
+ // Remove edge vertices.
+ for (int i = 0; i < mesh.nverts; ++i)
+ {
+ if (vflags[i])
+ {
+ if (!canRemoveVertex(ctx, mesh, (unsigned short)i))
+ continue;
+ if (!removeVertex(ctx, mesh, (unsigned short)i, maxTris))
+ {
+ // Failed to remove vertex
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Failed to remove edge vertex %d.", i);
+ return false;
+ }
+ // Remove vertex
+ // Note: mesh.nverts is already decremented inside removeVertex()!
+ for (int j = i; j < mesh.nverts; ++j)
+ vflags[j] = vflags[j+1];
+ --i;
+ }
+ }
+
+ // Calculate adjacency.
+ if (!buildMeshAdjacency(mesh.polys, mesh.npolys, mesh.nverts, nvp))
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Adjacency failed.");
+ return false;
+ }
+
+ // Just allocate the mesh flags array. The user is resposible to fill it.
+ mesh.flags = (unsigned short*)rcAlloc(sizeof(unsigned short)*mesh.npolys, RC_ALLOC_PERM);
+ if (!mesh.flags)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.flags' (%d).", mesh.npolys);
+ return false;
+ }
+ memset(mesh.flags, 0, sizeof(unsigned short) * mesh.npolys);
+
+ if (mesh.nverts > 0xffff)
+ {
+ ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: The resulting mesh has too many vertices %d (max %d). Data can be corrupted.", mesh.nverts, 0xffff);
+ }
+ if (mesh.npolys > 0xffff)
+ {
+ ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: The resulting mesh has too many polygons %d (max %d). Data can be corrupted.", mesh.npolys, 0xffff);
+ }
+
+ ctx->stopTimer(RC_TIMER_BUILD_POLYMESH);
+
+ return true;
+}
+
+bool rcMergePolyMeshes(rcContext* ctx, rcPolyMesh** meshes, const int nmeshes, rcPolyMesh& mesh)
+{
+ rcAssert(ctx);
+
+ if (!nmeshes || !meshes)
+ return true;
+
+ ctx->startTimer(RC_TIMER_MERGE_POLYMESH);
+
+ mesh.nvp = meshes[0]->nvp;
+ mesh.cs = meshes[0]->cs;
+ mesh.ch = meshes[0]->ch;
+ rcVcopy(mesh.bmin, meshes[0]->bmin);
+ rcVcopy(mesh.bmax, meshes[0]->bmax);
+
+ int maxVerts = 0;
+ int maxPolys = 0;
+ int maxVertsPerMesh = 0;
+ for (int i = 0; i < nmeshes; ++i)
+ {
+ rcVmin(mesh.bmin, meshes[i]->bmin);
+ rcVmax(mesh.bmax, meshes[i]->bmax);
+ maxVertsPerMesh = rcMax(maxVertsPerMesh, meshes[i]->nverts);
+ maxVerts += meshes[i]->nverts;
+ maxPolys += meshes[i]->npolys;
+ }
+
+ mesh.nverts = 0;
+ mesh.verts = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxVerts*3, RC_ALLOC_PERM);
+ if (!mesh.verts)
+ {
+ ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'mesh.verts' (%d).", maxVerts*3);
+ return false;
+ }
+
+ mesh.npolys = 0;
+ mesh.polys = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxPolys*2*mesh.nvp, RC_ALLOC_PERM);
+ if (!mesh.polys)
+ {
+ ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'mesh.polys' (%d).", maxPolys*2*mesh.nvp);
+ return false;
+ }
+ memset(mesh.polys, 0xff, sizeof(unsigned short)*maxPolys*2*mesh.nvp);
+
+ mesh.regs = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxPolys, RC_ALLOC_PERM);
+ if (!mesh.regs)
+ {
+ ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'mesh.regs' (%d).", maxPolys);
+ return false;
+ }
+ memset(mesh.regs, 0, sizeof(unsigned short)*maxPolys);
+
+ mesh.areas = (unsigned char*)rcAlloc(sizeof(unsigned char)*maxPolys, RC_ALLOC_PERM);
+ if (!mesh.areas)
+ {
+ ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'mesh.areas' (%d).", maxPolys);
+ return false;
+ }
+ memset(mesh.areas, 0, sizeof(unsigned char)*maxPolys);
+
+ mesh.flags = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxPolys, RC_ALLOC_PERM);
+ if (!mesh.flags)
+ {
+ ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'mesh.flags' (%d).", maxPolys);
+ return false;
+ }
+ memset(mesh.flags, 0, sizeof(unsigned short)*maxPolys);
+
+ rcScopedDelete<int> nextVert = (int*)rcAlloc(sizeof(int)*maxVerts, RC_ALLOC_TEMP);
+ if (!nextVert)
+ {
+ ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'nextVert' (%d).", maxVerts);
+ return false;
+ }
+ memset(nextVert, 0, sizeof(int)*maxVerts);
+
+ rcScopedDelete<int> firstVert = (int*)rcAlloc(sizeof(int)*VERTEX_BUCKET_COUNT, RC_ALLOC_TEMP);
+ if (!firstVert)
+ {
+ ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'firstVert' (%d).", VERTEX_BUCKET_COUNT);
+ return false;
+ }
+ for (int i = 0; i < VERTEX_BUCKET_COUNT; ++i)
+ firstVert[i] = -1;
+
+ rcScopedDelete<unsigned short> vremap = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxVertsPerMesh, RC_ALLOC_PERM);
+ if (!vremap)
+ {
+ ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'vremap' (%d).", maxVertsPerMesh);
+ return false;
+ }
+ memset(nextVert, 0, sizeof(int)*maxVerts);
+
+ for (int i = 0; i < nmeshes; ++i)
+ {
+ const rcPolyMesh* pmesh = meshes[i];
+
+ const unsigned short ox = (unsigned short)floorf((pmesh->bmin[0]-mesh.bmin[0])/mesh.cs+0.5f);
+ const unsigned short oz = (unsigned short)floorf((pmesh->bmin[2]-mesh.bmin[2])/mesh.cs+0.5f);
+
+ for (int j = 0; j < pmesh->nverts; ++j)
+ {
+ unsigned short* v = &pmesh->verts[j*3];
+ vremap[j] = addVertex(v[0]+ox, v[1], v[2]+oz,
+ mesh.verts, firstVert, nextVert, mesh.nverts);
+ }
+
+ for (int j = 0; j < pmesh->npolys; ++j)
+ {
+ unsigned short* tgt = &mesh.polys[mesh.npolys*2*mesh.nvp];
+ unsigned short* src = &pmesh->polys[j*2*mesh.nvp];
+ mesh.regs[mesh.npolys] = pmesh->regs[j];
+ mesh.areas[mesh.npolys] = pmesh->areas[j];
+ mesh.flags[mesh.npolys] = pmesh->flags[j];
+ mesh.npolys++;
+ for (int k = 0; k < mesh.nvp; ++k)
+ {
+ if (src[k] == RC_MESH_NULL_IDX) break;
+ tgt[k] = vremap[src[k]];
+ }
+ }
+ }
+
+ // Calculate adjacency.
+ if (!buildMeshAdjacency(mesh.polys, mesh.npolys, mesh.nverts, mesh.nvp))
+ {
+ ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Adjacency failed.");
+ return false;
+ }
+
+ if (mesh.nverts > 0xffff)
+ {
+ ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: The resulting mesh has too many vertices %d (max %d). Data can be corrupted.", mesh.nverts, 0xffff);
+ }
+ if (mesh.npolys > 0xffff)
+ {
+ ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: The resulting mesh has too many polygons %d (max %d). Data can be corrupted.", mesh.npolys, 0xffff);
+ }
+
+ ctx->stopTimer(RC_TIMER_MERGE_POLYMESH);
+
+ return true;
+}
diff --git a/dep/recastnavigation/Recast/RecastMeshDetail.cpp b/dep/recastnavigation/Recast/RecastMeshDetail.cpp
new file mode 100644
index 0000000..ffb4b58
--- /dev/null
+++ b/dep/recastnavigation/Recast/RecastMeshDetail.cpp
@@ -0,0 +1,1237 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#include <float.h>
+#define _USE_MATH_DEFINES
+#include <math.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "Recast.h"
+#include "RecastAlloc.h"
+#include "RecastAssert.h"
+
+
+static const unsigned RC_UNSET_HEIGHT = 0xffff;
+
+struct rcHeightPatch
+{
+ inline rcHeightPatch() : data(0), xmin(0), ymin(0), width(0), height(0) {}
+ inline ~rcHeightPatch() { rcFree(data); }
+ unsigned short* data;
+ int xmin, ymin, width, height;
+};
+
+
+inline float vdot2(const float* a, const float* b)
+{
+ return a[0]*b[0] + a[2]*b[2];
+}
+
+inline float vdistSq2(const float* p, const float* q)
+{
+ const float dx = q[0] - p[0];
+ const float dy = q[2] - p[2];
+ return dx*dx + dy*dy;
+}
+
+inline float vdist2(const float* p, const float* q)
+{
+ return sqrtf(vdistSq2(p,q));
+}
+
+inline float vcross2(const float* p1, const float* p2, const float* p3)
+{
+ const float u1 = p2[0] - p1[0];
+ const float v1 = p2[2] - p1[2];
+ const float u2 = p3[0] - p1[0];
+ const float v2 = p3[2] - p1[2];
+ return u1 * v2 - v1 * u2;
+}
+
+static bool circumCircle(const float* p1, const float* p2, const float* p3,
+ float* c, float& r)
+{
+ static const float EPS = 1e-6f;
+
+ const float cp = vcross2(p1, p2, p3);
+ if (fabsf(cp) > EPS)
+ {
+ const float p1Sq = vdot2(p1,p1);
+ const float p2Sq = vdot2(p2,p2);
+ const float p3Sq = vdot2(p3,p3);
+ c[0] = (p1Sq*(p2[2]-p3[2]) + p2Sq*(p3[2]-p1[2]) + p3Sq*(p1[2]-p2[2])) / (2*cp);
+ c[2] = (p1Sq*(p3[0]-p2[0]) + p2Sq*(p1[0]-p3[0]) + p3Sq*(p2[0]-p1[0])) / (2*cp);
+ r = vdist2(c, p1);
+ return true;
+ }
+
+ c[0] = p1[0];
+ c[2] = p1[2];
+ r = 0;
+ return false;
+}
+
+static float distPtTri(const float* p, const float* a, const float* b, const float* c)
+{
+ float v0[3], v1[3], v2[3];
+ rcVsub(v0, c,a);
+ rcVsub(v1, b,a);
+ rcVsub(v2, p,a);
+
+ const float dot00 = vdot2(v0, v0);
+ const float dot01 = vdot2(v0, v1);
+ const float dot02 = vdot2(v0, v2);
+ const float dot11 = vdot2(v1, v1);
+ const float dot12 = vdot2(v1, v2);
+
+ // Compute barycentric coordinates
+ const float invDenom = 1.0f / (dot00 * dot11 - dot01 * dot01);
+ const float u = (dot11 * dot02 - dot01 * dot12) * invDenom;
+ float v = (dot00 * dot12 - dot01 * dot02) * invDenom;
+
+ // If point lies inside the triangle, return interpolated y-coord.
+ static const float EPS = 1e-4f;
+ if (u >= -EPS && v >= -EPS && (u+v) <= 1+EPS)
+ {
+ const float y = a[1] + v0[1]*u + v1[1]*v;
+ return fabsf(y-p[1]);
+ }
+ return FLT_MAX;
+}
+
+static float distancePtSeg(const float* pt, const float* p, const float* q)
+{
+ float pqx = q[0] - p[0];
+ float pqy = q[1] - p[1];
+ float pqz = q[2] - p[2];
+ float dx = pt[0] - p[0];
+ float dy = pt[1] - p[1];
+ float dz = pt[2] - p[2];
+ float d = pqx*pqx + pqy*pqy + pqz*pqz;
+ float t = pqx*dx + pqy*dy + pqz*dz;
+ if (d > 0)
+ t /= d;
+ if (t < 0)
+ t = 0;
+ else if (t > 1)
+ t = 1;
+
+ dx = p[0] + t*pqx - pt[0];
+ dy = p[1] + t*pqy - pt[1];
+ dz = p[2] + t*pqz - pt[2];
+
+ return dx*dx + dy*dy + dz*dz;
+}
+
+static float distancePtSeg2d(const float* pt, const float* p, const float* q)
+{
+ float pqx = q[0] - p[0];
+ float pqz = q[2] - p[2];
+ float dx = pt[0] - p[0];
+ float dz = pt[2] - p[2];
+ float d = pqx*pqx + pqz*pqz;
+ float t = pqx*dx + pqz*dz;
+ if (d > 0)
+ t /= d;
+ if (t < 0)
+ t = 0;
+ else if (t > 1)
+ t = 1;
+
+ dx = p[0] + t*pqx - pt[0];
+ dz = p[2] + t*pqz - pt[2];
+
+ return dx*dx + dz*dz;
+}
+
+static float distToTriMesh(const float* p, const float* verts, const int /*nverts*/, const int* tris, const int ntris)
+{
+ float dmin = FLT_MAX;
+ for (int i = 0; i < ntris; ++i)
+ {
+ const float* va = &verts[tris[i*4+0]*3];
+ const float* vb = &verts[tris[i*4+1]*3];
+ const float* vc = &verts[tris[i*4+2]*3];
+ float d = distPtTri(p, va,vb,vc);
+ if (d < dmin)
+ dmin = d;
+ }
+ if (dmin == FLT_MAX) return -1;
+ return dmin;
+}
+
+static float distToPoly(int nvert, const float* verts, const float* p)
+{
+
+ float dmin = FLT_MAX;
+ int i, j, c = 0;
+ for (i = 0, j = nvert-1; i < nvert; j = i++)
+ {
+ const float* vi = &verts[i*3];
+ const float* vj = &verts[j*3];
+ if (((vi[2] > p[2]) != (vj[2] > p[2])) &&
+ (p[0] < (vj[0]-vi[0]) * (p[2]-vi[2]) / (vj[2]-vi[2]) + vi[0]) )
+ c = !c;
+ dmin = rcMin(dmin, distancePtSeg2d(p, vj, vi));
+ }
+ return c ? -dmin : dmin;
+}
+
+
+static unsigned short getHeight(const float fx, const float fy, const float fz,
+ const float /*cs*/, const float ics, const float ch,
+ const rcHeightPatch& hp)
+{
+ int ix = (int)floorf(fx*ics + 0.01f);
+ int iz = (int)floorf(fz*ics + 0.01f);
+ ix = rcClamp(ix-hp.xmin, 0, hp.width);
+ iz = rcClamp(iz-hp.ymin, 0, hp.height);
+ unsigned short h = hp.data[ix+iz*hp.width];
+ if (h == RC_UNSET_HEIGHT)
+ {
+ // Special case when data might be bad.
+ // Find nearest neighbour pixel which has valid height.
+ const int off[8*2] = { -1,0, -1,-1, 0,-1, 1,-1, 1,0, 1,1, 0,1, -1,1};
+ float dmin = FLT_MAX;
+ for (int i = 0; i < 8; ++i)
+ {
+ const int nx = ix+off[i*2+0];
+ const int nz = iz+off[i*2+1];
+ if (nx < 0 || nz < 0 || nx >= hp.width || nz >= hp.height) continue;
+ const unsigned short nh = hp.data[nx+nz*hp.width];
+ if (nh == RC_UNSET_HEIGHT) continue;
+
+ const float d = fabsf(nh*ch - fy);
+ if (d < dmin)
+ {
+ h = nh;
+ dmin = d;
+ }
+
+/* const float dx = (nx+0.5f)*cs - fx;
+ const float dz = (nz+0.5f)*cs - fz;
+ const float d = dx*dx+dz*dz;
+ if (d < dmin)
+ {
+ h = nh;
+ dmin = d;
+ } */
+ }
+ }
+ return h;
+}
+
+
+enum EdgeValues
+{
+ UNDEF = -1,
+ HULL = -2,
+};
+
+static int findEdge(const int* edges, int nedges, int s, int t)
+{
+ for (int i = 0; i < nedges; i++)
+ {
+ const int* e = &edges[i*4];
+ if ((e[0] == s && e[1] == t) || (e[0] == t && e[1] == s))
+ return i;
+ }
+ return UNDEF;
+}
+
+static int addEdge(rcContext* ctx, int* edges, int& nedges, const int maxEdges, int s, int t, int l, int r)
+{
+ if (nedges >= maxEdges)
+ {
+ ctx->log(RC_LOG_ERROR, "addEdge: Too many edges (%d/%d).", nedges, maxEdges);
+ return UNDEF;
+ }
+
+ // Add edge if not already in the triangulation.
+ int e = findEdge(edges, nedges, s, t);
+ if (e == UNDEF)
+ {
+ int* e = &edges[nedges*4];
+ e[0] = s;
+ e[1] = t;
+ e[2] = l;
+ e[3] = r;
+ return nedges++;
+ }
+ else
+ {
+ return UNDEF;
+ }
+}
+
+static void updateLeftFace(int* e, int s, int t, int f)
+{
+ if (e[0] == s && e[1] == t && e[2] == UNDEF)
+ e[2] = f;
+ else if (e[1] == s && e[0] == t && e[3] == UNDEF)
+ e[3] = f;
+}
+
+static int overlapSegSeg2d(const float* a, const float* b, const float* c, const float* d)
+{
+ const float a1 = vcross2(a, b, d);
+ const float a2 = vcross2(a, b, c);
+ if (a1*a2 < 0.0f)
+ {
+ float a3 = vcross2(c, d, a);
+ float a4 = a3 + a2 - a1;
+ if (a3 * a4 < 0.0f)
+ return 1;
+ }
+ return 0;
+}
+
+static bool overlapEdges(const float* pts, const int* edges, int nedges, int s1, int t1)
+{
+ for (int i = 0; i < nedges; ++i)
+ {
+ const int s0 = edges[i*4+0];
+ const int t0 = edges[i*4+1];
+ // Same or connected edges do not overlap.
+ if (s0 == s1 || s0 == t1 || t0 == s1 || t0 == t1)
+ continue;
+ if (overlapSegSeg2d(&pts[s0*3],&pts[t0*3], &pts[s1*3],&pts[t1*3]))
+ return true;
+ }
+ return false;
+}
+
+static void completeFacet(rcContext* ctx, const float* pts, int npts, int* edges, int& nedges, const int maxEdges, int& nfaces, int e)
+{
+ static const float EPS = 1e-5f;
+
+ int* edge = &edges[e*4];
+
+ // Cache s and t.
+ int s,t;
+ if (edge[2] == UNDEF)
+ {
+ s = edge[0];
+ t = edge[1];
+ }
+ else if (edge[3] == UNDEF)
+ {
+ s = edge[1];
+ t = edge[0];
+ }
+ else
+ {
+ // Edge already completed.
+ return;
+ }
+
+ // Find best point on left of edge.
+ int pt = npts;
+ float c[3] = {0,0,0};
+ float r = -1;
+ for (int u = 0; u < npts; ++u)
+ {
+ if (u == s || u == t) continue;
+ if (vcross2(&pts[s*3], &pts[t*3], &pts[u*3]) > EPS)
+ {
+ if (r < 0)
+ {
+ // The circle is not updated yet, do it now.
+ pt = u;
+ circumCircle(&pts[s*3], &pts[t*3], &pts[u*3], c, r);
+ continue;
+ }
+ const float d = vdist2(c, &pts[u*3]);
+ const float tol = 0.001f;
+ if (d > r*(1+tol))
+ {
+ // Outside current circumcircle, skip.
+ continue;
+ }
+ else if (d < r*(1-tol))
+ {
+ // Inside safe circumcircle, update circle.
+ pt = u;
+ circumCircle(&pts[s*3], &pts[t*3], &pts[u*3], c, r);
+ }
+ else
+ {
+ // Inside epsilon circum circle, do extra tests to make sure the edge is valid.
+ // s-u and t-u cannot overlap with s-pt nor t-pt if they exists.
+ if (overlapEdges(pts, edges, nedges, s,u))
+ continue;
+ if (overlapEdges(pts, edges, nedges, t,u))
+ continue;
+ // Edge is valid.
+ pt = u;
+ circumCircle(&pts[s*3], &pts[t*3], &pts[u*3], c, r);
+ }
+ }
+ }
+
+ // Add new triangle or update edge info if s-t is on hull.
+ if (pt < npts)
+ {
+ // Update face information of edge being completed.
+ updateLeftFace(&edges[e*4], s, t, nfaces);
+
+ // Add new edge or update face info of old edge.
+ e = findEdge(edges, nedges, pt, s);
+ if (e == UNDEF)
+ addEdge(ctx, edges, nedges, maxEdges, pt, s, nfaces, UNDEF);
+ else
+ updateLeftFace(&edges[e*4], pt, s, nfaces);
+
+ // Add new edge or update face info of old edge.
+ e = findEdge(edges, nedges, t, pt);
+ if (e == UNDEF)
+ addEdge(ctx, edges, nedges, maxEdges, t, pt, nfaces, UNDEF);
+ else
+ updateLeftFace(&edges[e*4], t, pt, nfaces);
+
+ nfaces++;
+ }
+ else
+ {
+ updateLeftFace(&edges[e*4], s, t, HULL);
+ }
+}
+
+static void delaunayHull(rcContext* ctx, const int npts, const float* pts,
+ const int nhull, const int* hull,
+ rcIntArray& tris, rcIntArray& edges)
+{
+ int nfaces = 0;
+ int nedges = 0;
+ const int maxEdges = npts*10;
+ edges.resize(maxEdges*4);
+
+ for (int i = 0, j = nhull-1; i < nhull; j=i++)
+ addEdge(ctx, &edges[0], nedges, maxEdges, hull[j],hull[i], HULL, UNDEF);
+
+ int currentEdge = 0;
+ while (currentEdge < nedges)
+ {
+ if (edges[currentEdge*4+2] == UNDEF)
+ completeFacet(ctx, pts, npts, &edges[0], nedges, maxEdges, nfaces, currentEdge);
+ if (edges[currentEdge*4+3] == UNDEF)
+ completeFacet(ctx, pts, npts, &edges[0], nedges, maxEdges, nfaces, currentEdge);
+ currentEdge++;
+ }
+
+ // Create tris
+ tris.resize(nfaces*4);
+ for (int i = 0; i < nfaces*4; ++i)
+ tris[i] = -1;
+
+ for (int i = 0; i < nedges; ++i)
+ {
+ const int* e = &edges[i*4];
+ if (e[3] >= 0)
+ {
+ // Left face
+ int* t = &tris[e[3]*4];
+ if (t[0] == -1)
+ {
+ t[0] = e[0];
+ t[1] = e[1];
+ }
+ else if (t[0] == e[1])
+ t[2] = e[0];
+ else if (t[1] == e[0])
+ t[2] = e[1];
+ }
+ if (e[2] >= 0)
+ {
+ // Right
+ int* t = &tris[e[2]*4];
+ if (t[0] == -1)
+ {
+ t[0] = e[1];
+ t[1] = e[0];
+ }
+ else if (t[0] == e[0])
+ t[2] = e[1];
+ else if (t[1] == e[1])
+ t[2] = e[0];
+ }
+ }
+
+ for (int i = 0; i < tris.size()/4; ++i)
+ {
+ int* t = &tris[i*4];
+ if (t[0] == -1 || t[1] == -1 || t[2] == -1)
+ {
+ ctx->log(RC_LOG_WARNING, "delaunayHull: Removing dangling face %d [%d,%d,%d].", i, t[0],t[1],t[2]);
+ t[0] = tris[tris.size()-4];
+ t[1] = tris[tris.size()-3];
+ t[2] = tris[tris.size()-2];
+ t[3] = tris[tris.size()-1];
+ tris.resize(tris.size()-4);
+ --i;
+ }
+ }
+}
+
+
+inline float getJitterX(const int i)
+{
+ return (((i * 0x8da6b343) & 0xffff) / 65535.0f * 2.0f) - 1.0f;
+}
+
+inline float getJitterY(const int i)
+{
+ return (((i * 0xd8163841) & 0xffff) / 65535.0f * 2.0f) - 1.0f;
+}
+
+static bool buildPolyDetail(rcContext* ctx, const float* in, const int nin,
+ const float sampleDist, const float sampleMaxError,
+ const rcCompactHeightfield& chf, const rcHeightPatch& hp,
+ float* verts, int& nverts, rcIntArray& tris,
+ rcIntArray& edges, rcIntArray& samples)
+{
+ static const int MAX_VERTS = 127;
+ static const int MAX_TRIS = 255; // Max tris for delaunay is 2n-2-k (n=num verts, k=num hull verts).
+ static const int MAX_VERTS_PER_EDGE = 32;
+ float edge[(MAX_VERTS_PER_EDGE+1)*3];
+ int hull[MAX_VERTS];
+ int nhull = 0;
+
+ nverts = 0;
+
+ for (int i = 0; i < nin; ++i)
+ rcVcopy(&verts[i*3], &in[i*3]);
+ nverts = nin;
+
+ const float cs = chf.cs;
+ const float ics = 1.0f/cs;
+
+ // Tessellate outlines.
+ // This is done in separate pass in order to ensure
+ // seamless height values across the ply boundaries.
+ if (sampleDist > 0)
+ {
+ for (int i = 0, j = nin-1; i < nin; j=i++)
+ {
+ const float* vj = &in[j*3];
+ const float* vi = &in[i*3];
+ bool swapped = false;
+ // Make sure the segments are always handled in same order
+ // using lexological sort or else there will be seams.
+ if (fabsf(vj[0]-vi[0]) < 1e-6f)
+ {
+ if (vj[2] > vi[2])
+ {
+ rcSwap(vj,vi);
+ swapped = true;
+ }
+ }
+ else
+ {
+ if (vj[0] > vi[0])
+ {
+ rcSwap(vj,vi);
+ swapped = true;
+ }
+ }
+ // Create samples along the edge.
+ float dx = vi[0] - vj[0];
+ float dy = vi[1] - vj[1];
+ float dz = vi[2] - vj[2];
+ float d = sqrtf(dx*dx + dz*dz);
+ int nn = 1 + (int)floorf(d/sampleDist);
+ if (nn >= MAX_VERTS_PER_EDGE) nn = MAX_VERTS_PER_EDGE-1;
+ if (nverts+nn >= MAX_VERTS)
+ nn = MAX_VERTS-1-nverts;
+
+ for (int k = 0; k <= nn; ++k)
+ {
+ float u = (float)k/(float)nn;
+ float* pos = &edge[k*3];
+ pos[0] = vj[0] + dx*u;
+ pos[1] = vj[1] + dy*u;
+ pos[2] = vj[2] + dz*u;
+ pos[1] = getHeight(pos[0],pos[1],pos[2], cs, ics, chf.ch, hp)*chf.ch;
+ }
+ // Simplify samples.
+ int idx[MAX_VERTS_PER_EDGE] = {0,nn};
+ int nidx = 2;
+ for (int k = 0; k < nidx-1; )
+ {
+ const int a = idx[k];
+ const int b = idx[k+1];
+ const float* va = &edge[a*3];
+ const float* vb = &edge[b*3];
+ // Find maximum deviation along the segment.
+ float maxd = 0;
+ int maxi = -1;
+ for (int m = a+1; m < b; ++m)
+ {
+ float d = distancePtSeg(&edge[m*3],va,vb);
+ if (d > maxd)
+ {
+ maxd = d;
+ maxi = m;
+ }
+ }
+ // If the max deviation is larger than accepted error,
+ // add new point, else continue to next segment.
+ if (maxi != -1 && maxd > rcSqr(sampleMaxError))
+ {
+ for (int m = nidx; m > k; --m)
+ idx[m] = idx[m-1];
+ idx[k+1] = maxi;
+ nidx++;
+ }
+ else
+ {
+ ++k;
+ }
+ }
+
+ hull[nhull++] = j;
+ // Add new vertices.
+ if (swapped)
+ {
+ for (int k = nidx-2; k > 0; --k)
+ {
+ rcVcopy(&verts[nverts*3], &edge[idx[k]*3]);
+ hull[nhull++] = nverts;
+ nverts++;
+ }
+ }
+ else
+ {
+ for (int k = 1; k < nidx-1; ++k)
+ {
+ rcVcopy(&verts[nverts*3], &edge[idx[k]*3]);
+ hull[nhull++] = nverts;
+ nverts++;
+ }
+ }
+ }
+ }
+
+
+ // Tessellate the base mesh.
+ edges.resize(0);
+ tris.resize(0);
+
+ delaunayHull(ctx, nverts, verts, nhull, hull, tris, edges);
+
+ if (tris.size() == 0)
+ {
+ // Could not triangulate the poly, make sure there is some valid data there.
+ ctx->log(RC_LOG_WARNING, "buildPolyDetail: Could not triangulate polygon, adding default data.");
+ for (int i = 2; i < nverts; ++i)
+ {
+ tris.push(0);
+ tris.push(i-1);
+ tris.push(i);
+ tris.push(0);
+ }
+ return true;
+ }
+
+ if (sampleDist > 0)
+ {
+ // Create sample locations in a grid.
+ float bmin[3], bmax[3];
+ rcVcopy(bmin, in);
+ rcVcopy(bmax, in);
+ for (int i = 1; i < nin; ++i)
+ {
+ rcVmin(bmin, &in[i*3]);
+ rcVmax(bmax, &in[i*3]);
+ }
+ int x0 = (int)floorf(bmin[0]/sampleDist);
+ int x1 = (int)ceilf(bmax[0]/sampleDist);
+ int z0 = (int)floorf(bmin[2]/sampleDist);
+ int z1 = (int)ceilf(bmax[2]/sampleDist);
+ samples.resize(0);
+ for (int z = z0; z < z1; ++z)
+ {
+ for (int x = x0; x < x1; ++x)
+ {
+ float pt[3];
+ pt[0] = x*sampleDist;
+ pt[1] = (bmax[1]+bmin[1])*0.5f;
+ pt[2] = z*sampleDist;
+ // Make sure the samples are not too close to the edges.
+ if (distToPoly(nin,in,pt) > -sampleDist/2) continue;
+ samples.push(x);
+ samples.push(getHeight(pt[0], pt[1], pt[2], cs, ics, chf.ch, hp));
+ samples.push(z);
+ samples.push(0); // Not added
+ }
+ }
+
+ // Add the samples starting from the one that has the most
+ // error. The procedure stops when all samples are added
+ // or when the max error is within treshold.
+ const int nsamples = samples.size()/4;
+ for (int iter = 0; iter < nsamples; ++iter)
+ {
+ if (nverts >= MAX_VERTS)
+ break;
+
+ // Find sample with most error.
+ float bestpt[3] = {0,0,0};
+ float bestd = 0;
+ int besti = -1;
+ for (int i = 0; i < nsamples; ++i)
+ {
+ const int* s = &samples[i*4];
+ if (s[3]) continue; // skip added.
+ float pt[3];
+ // The sample location is jittered to get rid of some bad triangulations
+ // which are cause by symmetrical data from the grid structure.
+ pt[0] = s[0]*sampleDist + getJitterX(i)*cs*0.1f;
+ pt[1] = s[1]*chf.ch;
+ pt[2] = s[2]*sampleDist + getJitterY(i)*cs*0.1f;
+ float d = distToTriMesh(pt, verts, nverts, &tris[0], tris.size()/4);
+ if (d < 0) continue; // did not hit the mesh.
+ if (d > bestd)
+ {
+ bestd = d;
+ besti = i;
+ rcVcopy(bestpt,pt);
+ }
+ }
+ // If the max error is within accepted threshold, stop tesselating.
+ if (bestd <= sampleMaxError || besti == -1)
+ break;
+ // Mark sample as added.
+ samples[besti*4+3] = 1;
+ // Add the new sample point.
+ rcVcopy(&verts[nverts*3],bestpt);
+ nverts++;
+
+ // Create new triangulation.
+ // TODO: Incremental add instead of full rebuild.
+ edges.resize(0);
+ tris.resize(0);
+ delaunayHull(ctx, nverts, verts, nhull, hull, tris, edges);
+ }
+ }
+
+ const int ntris = tris.size()/4;
+ if (ntris > MAX_TRIS)
+ {
+ tris.resize(MAX_TRIS*4);
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Shrinking triangle count from %d to max %d.", ntris, MAX_TRIS);
+ }
+
+ return true;
+}
+
+static void getHeightData(const rcCompactHeightfield& chf,
+ const unsigned short* poly, const int npoly,
+ const unsigned short* verts,
+ rcHeightPatch& hp, rcIntArray& stack)
+{
+ // Floodfill the heightfield to get 2D height data,
+ // starting at vertex locations as seeds.
+
+ memset(hp.data, 0, sizeof(unsigned short)*hp.width*hp.height);
+
+ stack.resize(0);
+
+ static const int offset[9*2] =
+ {
+ 0,0, -1,-1, 0,-1, 1,-1, 1,0, 1,1, 0,1, -1,1, -1,0,
+ };
+
+ // Use poly vertices as seed points for the flood fill.
+ for (int j = 0; j < npoly; ++j)
+ {
+ int cx = 0, cz = 0, ci =-1;
+ int dmin = RC_UNSET_HEIGHT;
+ for (int k = 0; k < 9; ++k)
+ {
+ const int ax = (int)verts[poly[j]*3+0] + offset[k*2+0];
+ const int ay = (int)verts[poly[j]*3+1];
+ const int az = (int)verts[poly[j]*3+2] + offset[k*2+1];
+ if (ax < hp.xmin || ax >= hp.xmin+hp.width ||
+ az < hp.ymin || az >= hp.ymin+hp.height)
+ continue;
+
+ const rcCompactCell& c = chf.cells[ax+az*chf.width];
+ for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
+ {
+ const rcCompactSpan& s = chf.spans[i];
+ int d = rcAbs(ay - (int)s.y);
+ if (d < dmin)
+ {
+ cx = ax;
+ cz = az;
+ ci = i;
+ dmin = d;
+ }
+ }
+ }
+ if (ci != -1)
+ {
+ stack.push(cx);
+ stack.push(cz);
+ stack.push(ci);
+ }
+ }
+
+ // Find center of the polygon using flood fill.
+ int pcx = 0, pcz = 0;
+ for (int j = 0; j < npoly; ++j)
+ {
+ pcx += (int)verts[poly[j]*3+0];
+ pcz += (int)verts[poly[j]*3+2];
+ }
+ pcx /= npoly;
+ pcz /= npoly;
+
+ for (int i = 0; i < stack.size(); i += 3)
+ {
+ int cx = stack[i+0];
+ int cy = stack[i+1];
+ int idx = cx-hp.xmin+(cy-hp.ymin)*hp.width;
+ hp.data[idx] = 1;
+ }
+
+ while (stack.size() > 0)
+ {
+ int ci = stack.pop();
+ int cy = stack.pop();
+ int cx = stack.pop();
+
+ // Check if close to center of the polygon.
+ if (rcAbs(cx-pcx) <= 1 && rcAbs(cy-pcz) <= 1)
+ {
+ stack.resize(0);
+ stack.push(cx);
+ stack.push(cy);
+ stack.push(ci);
+ break;
+ }
+
+ const rcCompactSpan& cs = chf.spans[ci];
+
+ for (int dir = 0; dir < 4; ++dir)
+ {
+ if (rcGetCon(cs, dir) == RC_NOT_CONNECTED) continue;
+
+ const int ax = cx + rcGetDirOffsetX(dir);
+ const int ay = cy + rcGetDirOffsetY(dir);
+
+ if (ax < hp.xmin || ax >= (hp.xmin+hp.width) ||
+ ay < hp.ymin || ay >= (hp.ymin+hp.height))
+ continue;
+
+ if (hp.data[ax-hp.xmin+(ay-hp.ymin)*hp.width] != 0)
+ continue;
+
+ const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(cs, dir);
+
+ int idx = ax-hp.xmin+(ay-hp.ymin)*hp.width;
+ hp.data[idx] = 1;
+
+ stack.push(ax);
+ stack.push(ay);
+ stack.push(ai);
+ }
+ }
+
+ memset(hp.data, 0xff, sizeof(unsigned short)*hp.width*hp.height);
+
+ // Mark start locations.
+ for (int i = 0; i < stack.size(); i += 3)
+ {
+ int cx = stack[i+0];
+ int cy = stack[i+1];
+ int ci = stack[i+2];
+ int idx = cx-hp.xmin+(cy-hp.ymin)*hp.width;
+ const rcCompactSpan& cs = chf.spans[ci];
+ hp.data[idx] = cs.y;
+ }
+
+ static const int RETRACT_SIZE = 256;
+ int head = 0;
+
+ while (head*3 < stack.size())
+ {
+ int cx = stack[head*3+0];
+ int cy = stack[head*3+1];
+ int ci = stack[head*3+2];
+ head++;
+ if (head >= RETRACT_SIZE)
+ {
+ head = 0;
+ if (stack.size() > RETRACT_SIZE*3)
+ memmove(&stack[0], &stack[RETRACT_SIZE*3], sizeof(int)*(stack.size()-RETRACT_SIZE*3));
+ stack.resize(stack.size()-RETRACT_SIZE*3);
+ }
+
+ const rcCompactSpan& cs = chf.spans[ci];
+ for (int dir = 0; dir < 4; ++dir)
+ {
+ if (rcGetCon(cs, dir) == RC_NOT_CONNECTED) continue;
+
+ const int ax = cx + rcGetDirOffsetX(dir);
+ const int ay = cy + rcGetDirOffsetY(dir);
+
+ if (ax < hp.xmin || ax >= (hp.xmin+hp.width) ||
+ ay < hp.ymin || ay >= (hp.ymin+hp.height))
+ continue;
+
+ if (hp.data[ax-hp.xmin+(ay-hp.ymin)*hp.width] != RC_UNSET_HEIGHT)
+ continue;
+
+ const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(cs, dir);
+
+ const rcCompactSpan& as = chf.spans[ai];
+ int idx = ax-hp.xmin+(ay-hp.ymin)*hp.width;
+ hp.data[idx] = as.y;
+
+ stack.push(ax);
+ stack.push(ay);
+ stack.push(ai);
+ }
+ }
+
+}
+
+static unsigned char getEdgeFlags(const float* va, const float* vb,
+ const float* vpoly, const int npoly)
+{
+ // Return true if edge (va,vb) is part of the polygon.
+ static const float thrSqr = rcSqr(0.001f);
+ for (int i = 0, j = npoly-1; i < npoly; j=i++)
+ {
+ if (distancePtSeg2d(va, &vpoly[j*3], &vpoly[i*3]) < thrSqr &&
+ distancePtSeg2d(vb, &vpoly[j*3], &vpoly[i*3]) < thrSqr)
+ return 1;
+ }
+ return 0;
+}
+
+static unsigned char getTriFlags(const float* va, const float* vb, const float* vc,
+ const float* vpoly, const int npoly)
+{
+ unsigned char flags = 0;
+ flags |= getEdgeFlags(va,vb,vpoly,npoly) << 0;
+ flags |= getEdgeFlags(vb,vc,vpoly,npoly) << 2;
+ flags |= getEdgeFlags(vc,va,vpoly,npoly) << 4;
+ return flags;
+}
+
+
+
+bool rcBuildPolyMeshDetail(rcContext* ctx, const rcPolyMesh& mesh, const rcCompactHeightfield& chf,
+ const float sampleDist, const float sampleMaxError,
+ rcPolyMeshDetail& dmesh)
+{
+ rcAssert(ctx);
+
+ ctx->startTimer(RC_TIMER_BUILD_POLYMESHDETAIL);
+
+ if (mesh.nverts == 0 || mesh.npolys == 0)
+ return true;
+
+ const int nvp = mesh.nvp;
+ const float cs = mesh.cs;
+ const float ch = mesh.ch;
+ const float* orig = mesh.bmin;
+
+ rcIntArray edges(64);
+ rcIntArray tris(512);
+ rcIntArray stack(512);
+ rcIntArray samples(512);
+ float verts[256*3];
+ rcHeightPatch hp;
+ int nPolyVerts = 0;
+ int maxhw = 0, maxhh = 0;
+
+ rcScopedDelete<int> bounds = (int*)rcAlloc(sizeof(int)*mesh.npolys*4, RC_ALLOC_TEMP);
+ if (!bounds)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'bounds' (%d).", mesh.npolys*4);
+ return false;
+ }
+ rcScopedDelete<float> poly = (float*)rcAlloc(sizeof(float)*nvp*3, RC_ALLOC_TEMP);
+ if (!poly)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'poly' (%d).", nvp*3);
+ return false;
+ }
+
+ // Find max size for a polygon area.
+ for (int i = 0; i < mesh.npolys; ++i)
+ {
+ const unsigned short* p = &mesh.polys[i*nvp*2];
+ int& xmin = bounds[i*4+0];
+ int& xmax = bounds[i*4+1];
+ int& ymin = bounds[i*4+2];
+ int& ymax = bounds[i*4+3];
+ xmin = chf.width;
+ xmax = 0;
+ ymin = chf.height;
+ ymax = 0;
+ for (int j = 0; j < nvp; ++j)
+ {
+ if(p[j] == RC_MESH_NULL_IDX) break;
+ const unsigned short* v = &mesh.verts[p[j]*3];
+ xmin = rcMin(xmin, (int)v[0]);
+ xmax = rcMax(xmax, (int)v[0]);
+ ymin = rcMin(ymin, (int)v[2]);
+ ymax = rcMax(ymax, (int)v[2]);
+ nPolyVerts++;
+ }
+ xmin = rcMax(0,xmin-1);
+ xmax = rcMin(chf.width,xmax+1);
+ ymin = rcMax(0,ymin-1);
+ ymax = rcMin(chf.height,ymax+1);
+ if (xmin >= xmax || ymin >= ymax) continue;
+ maxhw = rcMax(maxhw, xmax-xmin);
+ maxhh = rcMax(maxhh, ymax-ymin);
+ }
+
+ hp.data = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxhw*maxhh, RC_ALLOC_TEMP);
+ if (!hp.data)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'hp.data' (%d).", maxhw*maxhh);
+ return false;
+ }
+
+ dmesh.nmeshes = mesh.npolys;
+ dmesh.nverts = 0;
+ dmesh.ntris = 0;
+ dmesh.meshes = (unsigned int*)rcAlloc(sizeof(unsigned int)*dmesh.nmeshes*4, RC_ALLOC_PERM);
+ if (!dmesh.meshes)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.meshes' (%d).", dmesh.nmeshes*4);
+ return false;
+ }
+
+ int vcap = nPolyVerts+nPolyVerts/2;
+ int tcap = vcap*2;
+
+ dmesh.nverts = 0;
+ dmesh.verts = (float*)rcAlloc(sizeof(float)*vcap*3, RC_ALLOC_PERM);
+ if (!dmesh.verts)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.verts' (%d).", vcap*3);
+ return false;
+ }
+ dmesh.ntris = 0;
+ dmesh.tris = (unsigned char*)rcAlloc(sizeof(unsigned char*)*tcap*4, RC_ALLOC_PERM);
+ if (!dmesh.tris)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.tris' (%d).", tcap*4);
+ return false;
+ }
+
+ for (int i = 0; i < mesh.npolys; ++i)
+ {
+ const unsigned short* p = &mesh.polys[i*nvp*2];
+
+ // Store polygon vertices for processing.
+ int npoly = 0;
+ for (int j = 0; j < nvp; ++j)
+ {
+ if(p[j] == RC_MESH_NULL_IDX) break;
+ const unsigned short* v = &mesh.verts[p[j]*3];
+ poly[j*3+0] = v[0]*cs;
+ poly[j*3+1] = v[1]*ch;
+ poly[j*3+2] = v[2]*cs;
+ npoly++;
+ }
+
+ // Get the height data from the area of the polygon.
+ hp.xmin = bounds[i*4+0];
+ hp.ymin = bounds[i*4+2];
+ hp.width = bounds[i*4+1]-bounds[i*4+0];
+ hp.height = bounds[i*4+3]-bounds[i*4+2];
+ getHeightData(chf, p, npoly, mesh.verts, hp, stack);
+
+ // Build detail mesh.
+ int nverts = 0;
+ if (!buildPolyDetail(ctx, poly, npoly,
+ sampleDist, sampleMaxError,
+ chf, hp, verts, nverts, tris,
+ edges, samples))
+ {
+ return false;
+ }
+
+ // Move detail verts to world space.
+ for (int j = 0; j < nverts; ++j)
+ {
+ verts[j*3+0] += orig[0];
+ verts[j*3+1] += orig[1] + chf.ch; // Is this offset necessary?
+ verts[j*3+2] += orig[2];
+ }
+ // Offset poly too, will be used to flag checking.
+ for (int j = 0; j < npoly; ++j)
+ {
+ poly[j*3+0] += orig[0];
+ poly[j*3+1] += orig[1];
+ poly[j*3+2] += orig[2];
+ }
+
+ // Store detail submesh.
+ const int ntris = tris.size()/4;
+
+ dmesh.meshes[i*4+0] = (unsigned int)dmesh.nverts;
+ dmesh.meshes[i*4+1] = (unsigned int)nverts;
+ dmesh.meshes[i*4+2] = (unsigned int)dmesh.ntris;
+ dmesh.meshes[i*4+3] = (unsigned int)ntris;
+
+ // Store vertices, allocate more memory if necessary.
+ if (dmesh.nverts+nverts > vcap)
+ {
+ while (dmesh.nverts+nverts > vcap)
+ vcap += 256;
+
+ float* newv = (float*)rcAlloc(sizeof(float)*vcap*3, RC_ALLOC_PERM);
+ if (!newv)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'newv' (%d).", vcap*3);
+ return false;
+ }
+ if (dmesh.nverts)
+ memcpy(newv, dmesh.verts, sizeof(float)*3*dmesh.nverts);
+ rcFree(dmesh.verts);
+ dmesh.verts = newv;
+ }
+ for (int j = 0; j < nverts; ++j)
+ {
+ dmesh.verts[dmesh.nverts*3+0] = verts[j*3+0];
+ dmesh.verts[dmesh.nverts*3+1] = verts[j*3+1];
+ dmesh.verts[dmesh.nverts*3+2] = verts[j*3+2];
+ dmesh.nverts++;
+ }
+
+ // Store triangles, allocate more memory if necessary.
+ if (dmesh.ntris+ntris > tcap)
+ {
+ while (dmesh.ntris+ntris > tcap)
+ tcap += 256;
+ unsigned char* newt = (unsigned char*)rcAlloc(sizeof(unsigned char)*tcap*4, RC_ALLOC_PERM);
+ if (!newt)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'newt' (%d).", tcap*4);
+ return false;
+ }
+ if (dmesh.ntris)
+ memcpy(newt, dmesh.tris, sizeof(unsigned char)*4*dmesh.ntris);
+ rcFree(dmesh.tris);
+ dmesh.tris = newt;
+ }
+ for (int j = 0; j < ntris; ++j)
+ {
+ const int* t = &tris[j*4];
+ dmesh.tris[dmesh.ntris*4+0] = (unsigned char)t[0];
+ dmesh.tris[dmesh.ntris*4+1] = (unsigned char)t[1];
+ dmesh.tris[dmesh.ntris*4+2] = (unsigned char)t[2];
+ dmesh.tris[dmesh.ntris*4+3] = getTriFlags(&verts[t[0]*3], &verts[t[1]*3], &verts[t[2]*3], poly, npoly);
+ dmesh.ntris++;
+ }
+ }
+
+ ctx->stopTimer(RC_TIMER_BUILD_POLYMESHDETAIL);
+
+ return true;
+}
+
+bool rcMergePolyMeshDetails(rcContext* ctx, rcPolyMeshDetail** meshes, const int nmeshes, rcPolyMeshDetail& mesh)
+{
+ rcAssert(ctx);
+
+ ctx->startTimer(RC_TIMER_MERGE_POLYMESHDETAIL);
+
+ int maxVerts = 0;
+ int maxTris = 0;
+ int maxMeshes = 0;
+
+ for (int i = 0; i < nmeshes; ++i)
+ {
+ if (!meshes[i]) continue;
+ maxVerts += meshes[i]->nverts;
+ maxTris += meshes[i]->ntris;
+ maxMeshes += meshes[i]->nmeshes;
+ }
+
+ mesh.nmeshes = 0;
+ mesh.meshes = (unsigned int*)rcAlloc(sizeof(unsigned int)*maxMeshes*4, RC_ALLOC_PERM);
+ if (!mesh.meshes)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'pmdtl.meshes' (%d).", maxMeshes*4);
+ return false;
+ }
+
+ mesh.ntris = 0;
+ mesh.tris = (unsigned char*)rcAlloc(sizeof(unsigned char)*maxTris*4, RC_ALLOC_PERM);
+ if (!mesh.tris)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.tris' (%d).", maxTris*4);
+ return false;
+ }
+
+ mesh.nverts = 0;
+ mesh.verts = (float*)rcAlloc(sizeof(float)*maxVerts*3, RC_ALLOC_PERM);
+ if (!mesh.verts)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.verts' (%d).", maxVerts*3);
+ return false;
+ }
+
+ // Merge datas.
+ for (int i = 0; i < nmeshes; ++i)
+ {
+ rcPolyMeshDetail* dm = meshes[i];
+ if (!dm) continue;
+ for (int j = 0; j < dm->nmeshes; ++j)
+ {
+ unsigned int* dst = &mesh.meshes[mesh.nmeshes*4];
+ unsigned int* src = &dm->meshes[j*4];
+ dst[0] = (unsigned int)mesh.nverts+src[0];
+ dst[1] = src[1];
+ dst[2] = (unsigned int)mesh.ntris+src[2];
+ dst[3] = src[3];
+ mesh.nmeshes++;
+ }
+
+ for (int k = 0; k < dm->nverts; ++k)
+ {
+ rcVcopy(&mesh.verts[mesh.nverts*3], &dm->verts[k*3]);
+ mesh.nverts++;
+ }
+ for (int k = 0; k < dm->ntris; ++k)
+ {
+ mesh.tris[mesh.ntris*4+0] = dm->tris[k*4+0];
+ mesh.tris[mesh.ntris*4+1] = dm->tris[k*4+1];
+ mesh.tris[mesh.ntris*4+2] = dm->tris[k*4+2];
+ mesh.tris[mesh.ntris*4+3] = dm->tris[k*4+3];
+ mesh.ntris++;
+ }
+ }
+
+ ctx->stopTimer(RC_TIMER_MERGE_POLYMESHDETAIL);
+
+ return true;
+}
+
diff --git a/dep/recastnavigation/Recast/RecastRasterization.cpp b/dep/recastnavigation/Recast/RecastRasterization.cpp
new file mode 100644
index 0000000..71adfb6
--- /dev/null
+++ b/dep/recastnavigation/Recast/RecastRasterization.cpp
@@ -0,0 +1,360 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#define _USE_MATH_DEFINES
+#include <math.h>
+#include <stdio.h>
+#include "Recast.h"
+#include "RecastAlloc.h"
+#include "RecastAssert.h"
+
+inline bool overlapBounds(const float* amin, const float* amax, const float* bmin, const float* bmax)
+{
+ bool overlap = true;
+ overlap = (amin[0] > bmax[0] || amax[0] < bmin[0]) ? false : overlap;
+ overlap = (amin[1] > bmax[1] || amax[1] < bmin[1]) ? false : overlap;
+ overlap = (amin[2] > bmax[2] || amax[2] < bmin[2]) ? false : overlap;
+ return overlap;
+}
+
+inline bool overlapInterval(unsigned short amin, unsigned short amax,
+ unsigned short bmin, unsigned short bmax)
+{
+ if (amax < bmin) return false;
+ if (amin > bmax) return false;
+ return true;
+}
+
+
+static rcSpan* allocSpan(rcHeightfield& hf)
+{
+ // If running out of memory, allocate new page and update the freelist.
+ if (!hf.freelist || !hf.freelist->next)
+ {
+ // Create new page.
+ // Allocate memory for the new pool.
+ rcSpanPool* pool = (rcSpanPool*)rcAlloc(sizeof(rcSpanPool), RC_ALLOC_PERM);
+ if (!pool) return 0;
+ pool->next = 0;
+ // Add the pool into the list of pools.
+ pool->next = hf.pools;
+ hf.pools = pool;
+ // Add new items to the free list.
+ rcSpan* freelist = hf.freelist;
+ rcSpan* head = &pool->items[0];
+ rcSpan* it = &pool->items[RC_SPANS_PER_POOL];
+ do
+ {
+ --it;
+ it->next = freelist;
+ freelist = it;
+ }
+ while (it != head);
+ hf.freelist = it;
+ }
+
+ // Pop item from in front of the free list.
+ rcSpan* it = hf.freelist;
+ hf.freelist = hf.freelist->next;
+ return it;
+}
+
+static void freeSpan(rcHeightfield& hf, rcSpan* ptr)
+{
+ if (!ptr) return;
+ // Add the node in front of the free list.
+ ptr->next = hf.freelist;
+ hf.freelist = ptr;
+}
+
+static void addSpan(rcHeightfield& hf, const int x, const int y,
+ const unsigned short smin, const unsigned short smax,
+ const unsigned char area, const int flagMergeThr)
+{
+
+ int idx = x + y*hf.width;
+
+ rcSpan* s = allocSpan(hf);
+ s->smin = smin;
+ s->smax = smax;
+ s->area = area;
+ s->next = 0;
+
+ // Empty cell, add he first span.
+ if (!hf.spans[idx])
+ {
+ hf.spans[idx] = s;
+ return;
+ }
+ rcSpan* prev = 0;
+ rcSpan* cur = hf.spans[idx];
+
+ // Insert and merge spans.
+ while (cur)
+ {
+ if (cur->smin > s->smax)
+ {
+ // Current span is further than the new span, break.
+ break;
+ }
+ else if (cur->smax < s->smin)
+ {
+ // Current span is before the new span advance.
+ prev = cur;
+ cur = cur->next;
+ }
+ else
+ {
+ // Merge spans.
+ if (cur->smin < s->smin)
+ s->smin = cur->smin;
+ if (cur->smax > s->smax)
+ s->smax = cur->smax;
+
+ // Merge flags.
+ if (rcAbs((int)s->smax - (int)cur->smax) <= flagMergeThr)
+ s->area = rcMax(s->area, cur->area);
+
+ // Remove current span.
+ rcSpan* next = cur->next;
+ freeSpan(hf, cur);
+ if (prev)
+ prev->next = next;
+ else
+ hf.spans[idx] = next;
+ cur = next;
+ }
+ }
+
+ // Insert new span.
+ if (prev)
+ {
+ s->next = prev->next;
+ prev->next = s;
+ }
+ else
+ {
+ s->next = hf.spans[idx];
+ hf.spans[idx] = s;
+ }
+}
+
+void rcAddSpan(rcContext* /*ctx*/, rcHeightfield& hf, const int x, const int y,
+ const unsigned short smin, const unsigned short smax,
+ const unsigned char area, const int flagMergeThr)
+{
+// rcAssert(ctx);
+ addSpan(hf, x,y, smin, smax, area, flagMergeThr);
+}
+
+static int clipPoly(const float* in, int n, float* out, float pnx, float pnz, float pd)
+{
+ float d[12];
+ for (int i = 0; i < n; ++i)
+ d[i] = pnx*in[i*3+0] + pnz*in[i*3+2] + pd;
+
+ int m = 0;
+ for (int i = 0, j = n-1; i < n; j=i, ++i)
+ {
+ bool ina = d[j] >= 0;
+ bool inb = d[i] >= 0;
+ if (ina != inb)
+ {
+ float s = d[j] / (d[j] - d[i]);
+ out[m*3+0] = in[j*3+0] + (in[i*3+0] - in[j*3+0])*s;
+ out[m*3+1] = in[j*3+1] + (in[i*3+1] - in[j*3+1])*s;
+ out[m*3+2] = in[j*3+2] + (in[i*3+2] - in[j*3+2])*s;
+ m++;
+ }
+ if (inb)
+ {
+ out[m*3+0] = in[i*3+0];
+ out[m*3+1] = in[i*3+1];
+ out[m*3+2] = in[i*3+2];
+ m++;
+ }
+ }
+ return m;
+}
+
+static void rasterizeTri(const float* v0, const float* v1, const float* v2,
+ const unsigned char area, rcHeightfield& hf,
+ const float* bmin, const float* bmax,
+ const float cs, const float ics, const float ich,
+ const int flagMergeThr)
+{
+ const int w = hf.width;
+ const int h = hf.height;
+ float tmin[3], tmax[3];
+ const float by = bmax[1] - bmin[1];
+
+ // Calculate the bounding box of the triangle.
+ rcVcopy(tmin, v0);
+ rcVcopy(tmax, v0);
+ rcVmin(tmin, v1);
+ rcVmin(tmin, v2);
+ rcVmax(tmax, v1);
+ rcVmax(tmax, v2);
+
+ // If the triangle does not touch the bbox of the heightfield, skip the triagle.
+ if (!overlapBounds(bmin, bmax, tmin, tmax))
+ return;
+
+ // Calculate the footpring of the triangle on the grid.
+ int x0 = (int)((tmin[0] - bmin[0])*ics);
+ int y0 = (int)((tmin[2] - bmin[2])*ics);
+ int x1 = (int)((tmax[0] - bmin[0])*ics);
+ int y1 = (int)((tmax[2] - bmin[2])*ics);
+ x0 = rcClamp(x0, 0, w-1);
+ y0 = rcClamp(y0, 0, h-1);
+ x1 = rcClamp(x1, 0, w-1);
+ y1 = rcClamp(y1, 0, h-1);
+
+ // Clip the triangle into all grid cells it touches.
+ float in[7*3], out[7*3], inrow[7*3];
+
+ for (int y = y0; y <= y1; ++y)
+ {
+ // Clip polygon to row.
+ rcVcopy(&in[0], v0);
+ rcVcopy(&in[1*3], v1);
+ rcVcopy(&in[2*3], v2);
+ int nvrow = 3;
+ const float cz = bmin[2] + y*cs;
+ nvrow = clipPoly(in, nvrow, out, 0, 1, -cz);
+ if (nvrow < 3) continue;
+ nvrow = clipPoly(out, nvrow, inrow, 0, -1, cz+cs);
+ if (nvrow < 3) continue;
+
+ for (int x = x0; x <= x1; ++x)
+ {
+ // Clip polygon to column.
+ int nv = nvrow;
+ const float cx = bmin[0] + x*cs;
+ nv = clipPoly(inrow, nv, out, 1, 0, -cx);
+ if (nv < 3) continue;
+ nv = clipPoly(out, nv, in, -1, 0, cx+cs);
+ if (nv < 3) continue;
+
+ // Calculate min and max of the span.
+ float smin = in[1], smax = in[1];
+ for (int i = 1; i < nv; ++i)
+ {
+ smin = rcMin(smin, in[i*3+1]);
+ smax = rcMax(smax, in[i*3+1]);
+ }
+ smin -= bmin[1];
+ smax -= bmin[1];
+ // Skip the span if it is outside the heightfield bbox
+ if (smax < 0.0f) continue;
+ if (smin > by) continue;
+ // Clamp the span to the heightfield bbox.
+ if (smin < 0.0f) smin = 0;
+ if (smax > by) smax = by;
+
+ // Snap the span to the heightfield height grid.
+ unsigned short ismin = (unsigned short)rcClamp((int)floorf(smin * ich), 0, RC_SPAN_MAX_HEIGHT);
+ unsigned short ismax = (unsigned short)rcClamp((int)ceilf(smax * ich), (int)ismin+1, RC_SPAN_MAX_HEIGHT);
+
+ addSpan(hf, x, y, ismin, ismax, area, flagMergeThr);
+ }
+ }
+}
+
+void rcRasterizeTriangle(rcContext* ctx, const float* v0, const float* v1, const float* v2,
+ const unsigned char area, rcHeightfield& solid,
+ const int flagMergeThr)
+{
+ rcAssert(ctx);
+
+ ctx->startTimer(RC_TIMER_RASTERIZE_TRIANGLES);
+
+ const float ics = 1.0f/solid.cs;
+ const float ich = 1.0f/solid.ch;
+ rasterizeTri(v0, v1, v2, area, solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr);
+
+ ctx->stopTimer(RC_TIMER_RASTERIZE_TRIANGLES);
+}
+
+void rcRasterizeTriangles(rcContext* ctx, const float* verts, const int /*nv*/,
+ const int* tris, const unsigned char* areas, const int nt,
+ rcHeightfield& solid, const int flagMergeThr)
+{
+ rcAssert(ctx);
+
+ ctx->startTimer(RC_TIMER_RASTERIZE_TRIANGLES);
+
+ const float ics = 1.0f/solid.cs;
+ const float ich = 1.0f/solid.ch;
+ // Rasterize triangles.
+ for (int i = 0; i < nt; ++i)
+ {
+ const float* v0 = &verts[tris[i*3+0]*3];
+ const float* v1 = &verts[tris[i*3+1]*3];
+ const float* v2 = &verts[tris[i*3+2]*3];
+ // Rasterize.
+ rasterizeTri(v0, v1, v2, areas[i], solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr);
+ }
+
+ ctx->stopTimer(RC_TIMER_RASTERIZE_TRIANGLES);
+}
+
+void rcRasterizeTriangles(rcContext* ctx, const float* verts, const int /*nv*/,
+ const unsigned short* tris, const unsigned char* areas, const int nt,
+ rcHeightfield& solid, const int flagMergeThr)
+{
+ rcAssert(ctx);
+
+ ctx->startTimer(RC_TIMER_RASTERIZE_TRIANGLES);
+
+ const float ics = 1.0f/solid.cs;
+ const float ich = 1.0f/solid.ch;
+ // Rasterize triangles.
+ for (int i = 0; i < nt; ++i)
+ {
+ const float* v0 = &verts[tris[i*3+0]*3];
+ const float* v1 = &verts[tris[i*3+1]*3];
+ const float* v2 = &verts[tris[i*3+2]*3];
+ // Rasterize.
+ rasterizeTri(v0, v1, v2, areas[i], solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr);
+ }
+
+ ctx->stopTimer(RC_TIMER_RASTERIZE_TRIANGLES);
+}
+
+void rcRasterizeTriangles(rcContext* ctx, const float* verts, const unsigned char* areas, const int nt,
+ rcHeightfield& solid, const int flagMergeThr)
+{
+ rcAssert(ctx);
+
+ ctx->startTimer(RC_TIMER_RASTERIZE_TRIANGLES);
+
+ const float ics = 1.0f/solid.cs;
+ const float ich = 1.0f/solid.ch;
+ // Rasterize triangles.
+ for (int i = 0; i < nt; ++i)
+ {
+ const float* v0 = &verts[(i*3+0)*3];
+ const float* v1 = &verts[(i*3+1)*3];
+ const float* v2 = &verts[(i*3+2)*3];
+ // Rasterize.
+ rasterizeTri(v0, v1, v2, areas[i], solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr);
+ }
+
+ ctx->stopTimer(RC_TIMER_RASTERIZE_TRIANGLES);
+}
diff --git a/dep/recastnavigation/Recast/RecastRegion.cpp b/dep/recastnavigation/Recast/RecastRegion.cpp
new file mode 100644
index 0000000..6ad9fa5
--- /dev/null
+++ b/dep/recastnavigation/Recast/RecastRegion.cpp
@@ -0,0 +1,1283 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#include <float.h>
+#define _USE_MATH_DEFINES
+#include <math.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "Recast.h"
+#include "RecastAlloc.h"
+#include "RecastAssert.h"
+#include <new>
+
+
+static void calculateDistanceField(rcCompactHeightfield& chf, unsigned short* src, unsigned short& maxDist)
+{
+ const int w = chf.width;
+ const int h = chf.height;
+
+ // Init distance and points.
+ for (int i = 0; i < chf.spanCount; ++i)
+ src[i] = 0xffff;
+
+ // Mark boundary cells.
+ for (int y = 0; y < h; ++y)
+ {
+ for (int x = 0; x < w; ++x)
+ {
+ const rcCompactCell& c = chf.cells[x+y*w];
+ for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
+ {
+ const rcCompactSpan& s = chf.spans[i];
+ const unsigned char area = chf.areas[i];
+
+ int nc = 0;
+ for (int dir = 0; dir < 4; ++dir)
+ {
+ if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
+ {
+ const int ax = x + rcGetDirOffsetX(dir);
+ const int ay = y + rcGetDirOffsetY(dir);
+ const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir);
+ if (area == chf.areas[ai])
+ nc++;
+ }
+ }
+ if (nc != 4)
+ src[i] = 0;
+ }
+ }
+ }
+
+
+ // Pass 1
+ for (int y = 0; y < h; ++y)
+ {
+ for (int x = 0; x < w; ++x)
+ {
+ const rcCompactCell& c = chf.cells[x+y*w];
+ for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
+ {
+ const rcCompactSpan& s = chf.spans[i];
+
+ if (rcGetCon(s, 0) != RC_NOT_CONNECTED)
+ {
+ // (-1,0)
+ const int ax = x + rcGetDirOffsetX(0);
+ const int ay = y + rcGetDirOffsetY(0);
+ const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 0);
+ const rcCompactSpan& as = chf.spans[ai];
+ if (src[ai]+2 < src[i])
+ src[i] = src[ai]+2;
+
+ // (-1,-1)
+ if (rcGetCon(as, 3) != RC_NOT_CONNECTED)
+ {
+ const int aax = ax + rcGetDirOffsetX(3);
+ const int aay = ay + rcGetDirOffsetY(3);
+ const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 3);
+ if (src[aai]+3 < src[i])
+ src[i] = src[aai]+3;
+ }
+ }
+ if (rcGetCon(s, 3) != RC_NOT_CONNECTED)
+ {
+ // (0,-1)
+ const int ax = x + rcGetDirOffsetX(3);
+ const int ay = y + rcGetDirOffsetY(3);
+ const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 3);
+ const rcCompactSpan& as = chf.spans[ai];
+ if (src[ai]+2 < src[i])
+ src[i] = src[ai]+2;
+
+ // (1,-1)
+ if (rcGetCon(as, 2) != RC_NOT_CONNECTED)
+ {
+ const int aax = ax + rcGetDirOffsetX(2);
+ const int aay = ay + rcGetDirOffsetY(2);
+ const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 2);
+ if (src[aai]+3 < src[i])
+ src[i] = src[aai]+3;
+ }
+ }
+ }
+ }
+ }
+
+ // Pass 2
+ for (int y = h-1; y >= 0; --y)
+ {
+ for (int x = w-1; x >= 0; --x)
+ {
+ const rcCompactCell& c = chf.cells[x+y*w];
+ for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
+ {
+ const rcCompactSpan& s = chf.spans[i];
+
+ if (rcGetCon(s, 2) != RC_NOT_CONNECTED)
+ {
+ // (1,0)
+ const int ax = x + rcGetDirOffsetX(2);
+ const int ay = y + rcGetDirOffsetY(2);
+ const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 2);
+ const rcCompactSpan& as = chf.spans[ai];
+ if (src[ai]+2 < src[i])
+ src[i] = src[ai]+2;
+
+ // (1,1)
+ if (rcGetCon(as, 1) != RC_NOT_CONNECTED)
+ {
+ const int aax = ax + rcGetDirOffsetX(1);
+ const int aay = ay + rcGetDirOffsetY(1);
+ const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 1);
+ if (src[aai]+3 < src[i])
+ src[i] = src[aai]+3;
+ }
+ }
+ if (rcGetCon(s, 1) != RC_NOT_CONNECTED)
+ {
+ // (0,1)
+ const int ax = x + rcGetDirOffsetX(1);
+ const int ay = y + rcGetDirOffsetY(1);
+ const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 1);
+ const rcCompactSpan& as = chf.spans[ai];
+ if (src[ai]+2 < src[i])
+ src[i] = src[ai]+2;
+
+ // (-1,1)
+ if (rcGetCon(as, 0) != RC_NOT_CONNECTED)
+ {
+ const int aax = ax + rcGetDirOffsetX(0);
+ const int aay = ay + rcGetDirOffsetY(0);
+ const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 0);
+ if (src[aai]+3 < src[i])
+ src[i] = src[aai]+3;
+ }
+ }
+ }
+ }
+ }
+
+ maxDist = 0;
+ for (int i = 0; i < chf.spanCount; ++i)
+ maxDist = rcMax(src[i], maxDist);
+
+}
+
+static unsigned short* boxBlur(rcCompactHeightfield& chf, int thr,
+ unsigned short* src, unsigned short* dst)
+{
+ const int w = chf.width;
+ const int h = chf.height;
+
+ thr *= 2;
+
+ for (int y = 0; y < h; ++y)
+ {
+ for (int x = 0; x < w; ++x)
+ {
+ const rcCompactCell& c = chf.cells[x+y*w];
+ for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
+ {
+ const rcCompactSpan& s = chf.spans[i];
+ const unsigned short cd = src[i];
+ if (cd <= thr)
+ {
+ dst[i] = cd;
+ continue;
+ }
+
+ int d = (int)cd;
+ for (int dir = 0; dir < 4; ++dir)
+ {
+ if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
+ {
+ const int ax = x + rcGetDirOffsetX(dir);
+ const int ay = y + rcGetDirOffsetY(dir);
+ const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir);
+ d += (int)src[ai];
+
+ const rcCompactSpan& as = chf.spans[ai];
+ const int dir2 = (dir+1) & 0x3;
+ if (rcGetCon(as, dir2) != RC_NOT_CONNECTED)
+ {
+ const int ax2 = ax + rcGetDirOffsetX(dir2);
+ const int ay2 = ay + rcGetDirOffsetY(dir2);
+ const int ai2 = (int)chf.cells[ax2+ay2*w].index + rcGetCon(as, dir2);
+ d += (int)src[ai2];
+ }
+ else
+ {
+ d += cd;
+ }
+ }
+ else
+ {
+ d += cd*2;
+ }
+ }
+ dst[i] = (unsigned short)((d+5)/9);
+ }
+ }
+ }
+ return dst;
+}
+
+
+static bool floodRegion(int x, int y, int i,
+ unsigned short level, unsigned short r,
+ rcCompactHeightfield& chf,
+ unsigned short* srcReg, unsigned short* srcDist,
+ rcIntArray& stack)
+{
+ const int w = chf.width;
+
+ const unsigned char area = chf.areas[i];
+
+ // Flood fill mark region.
+ stack.resize(0);
+ stack.push((int)x);
+ stack.push((int)y);
+ stack.push((int)i);
+ srcReg[i] = r;
+ srcDist[i] = 0;
+
+ unsigned short lev = level >= 2 ? level-2 : 0;
+ int count = 0;
+
+ while (stack.size() > 0)
+ {
+ int ci = stack.pop();
+ int cy = stack.pop();
+ int cx = stack.pop();
+
+ const rcCompactSpan& cs = chf.spans[ci];
+
+ // Check if any of the neighbours already have a valid region set.
+ unsigned short ar = 0;
+ for (int dir = 0; dir < 4; ++dir)
+ {
+ // 8 connected
+ if (rcGetCon(cs, dir) != RC_NOT_CONNECTED)
+ {
+ const int ax = cx + rcGetDirOffsetX(dir);
+ const int ay = cy + rcGetDirOffsetY(dir);
+ const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(cs, dir);
+ if (chf.areas[ai] != area)
+ continue;
+ unsigned short nr = srcReg[ai];
+ if (nr != 0 && nr != r)
+ ar = nr;
+
+ const rcCompactSpan& as = chf.spans[ai];
+
+ const int dir2 = (dir+1) & 0x3;
+ if (rcGetCon(as, dir2) != RC_NOT_CONNECTED)
+ {
+ const int ax2 = ax + rcGetDirOffsetX(dir2);
+ const int ay2 = ay + rcGetDirOffsetY(dir2);
+ const int ai2 = (int)chf.cells[ax2+ay2*w].index + rcGetCon(as, dir2);
+ if (chf.areas[ai2] != area)
+ continue;
+ unsigned short nr = srcReg[ai2];
+ if (nr != 0 && nr != r)
+ ar = nr;
+ }
+ }
+ }
+ if (ar != 0)
+ {
+ srcReg[ci] = 0;
+ continue;
+ }
+ count++;
+
+ // Expand neighbours.
+ for (int dir = 0; dir < 4; ++dir)
+ {
+ if (rcGetCon(cs, dir) != RC_NOT_CONNECTED)
+ {
+ const int ax = cx + rcGetDirOffsetX(dir);
+ const int ay = cy + rcGetDirOffsetY(dir);
+ const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(cs, dir);
+ if (chf.areas[ai] != area)
+ continue;
+ if (chf.dist[ai] >= lev)
+ {
+ if (srcReg[ai] == 0)
+ {
+ srcReg[ai] = r;
+ srcDist[ai] = 0;
+ stack.push(ax);
+ stack.push(ay);
+ stack.push(ai);
+ }
+ }
+ }
+ }
+ }
+
+ return count > 0;
+}
+
+static unsigned short* expandRegions(int maxIter, unsigned short level,
+ rcCompactHeightfield& chf,
+ unsigned short* srcReg, unsigned short* srcDist,
+ unsigned short* dstReg, unsigned short* dstDist,
+ rcIntArray& stack)
+{
+ const int w = chf.width;
+ const int h = chf.height;
+
+ // Find cells revealed by the raised level.
+ stack.resize(0);
+ for (int y = 0; y < h; ++y)
+ {
+ for (int x = 0; x < w; ++x)
+ {
+ const rcCompactCell& c = chf.cells[x+y*w];
+ for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
+ {
+ if (chf.dist[i] >= level && srcReg[i] == 0 && chf.areas[i] != RC_NULL_AREA)
+ {
+ stack.push(x);
+ stack.push(y);
+ stack.push(i);
+ }
+ }
+ }
+ }
+
+ int iter = 0;
+ while (stack.size() > 0)
+ {
+ int failed = 0;
+
+ memcpy(dstReg, srcReg, sizeof(unsigned short)*chf.spanCount);
+ memcpy(dstDist, srcDist, sizeof(unsigned short)*chf.spanCount);
+
+ for (int j = 0; j < stack.size(); j += 3)
+ {
+ int x = stack[j+0];
+ int y = stack[j+1];
+ int i = stack[j+2];
+ if (i < 0)
+ {
+ failed++;
+ continue;
+ }
+
+ unsigned short r = srcReg[i];
+ unsigned short d2 = 0xffff;
+ const unsigned char area = chf.areas[i];
+ const rcCompactSpan& s = chf.spans[i];
+ for (int dir = 0; dir < 4; ++dir)
+ {
+ if (rcGetCon(s, dir) == RC_NOT_CONNECTED) continue;
+ const int ax = x + rcGetDirOffsetX(dir);
+ const int ay = y + rcGetDirOffsetY(dir);
+ const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir);
+ if (chf.areas[ai] != area) continue;
+ if (srcReg[ai] > 0 && (srcReg[ai] & RC_BORDER_REG) == 0)
+ {
+ if ((int)srcDist[ai]+2 < (int)d2)
+ {
+ r = srcReg[ai];
+ d2 = srcDist[ai]+2;
+ }
+ }
+ }
+ if (r)
+ {
+ stack[j+2] = -1; // mark as used
+ dstReg[i] = r;
+ dstDist[i] = d2;
+ }
+ else
+ {
+ failed++;
+ }
+ }
+
+ // rcSwap source and dest.
+ rcSwap(srcReg, dstReg);
+ rcSwap(srcDist, dstDist);
+
+ if (failed*3 == stack.size())
+ break;
+
+ if (level > 0)
+ {
+ ++iter;
+ if (iter >= maxIter)
+ break;
+ }
+ }
+
+ return srcReg;
+}
+
+
+struct rcRegion
+{
+ inline rcRegion(unsigned short i) :
+ spanCount(0),
+ id(i),
+ areaType(0),
+ remap(false),
+ visited(false)
+ {}
+
+ int spanCount; // Number of spans belonging to this region
+ unsigned short id; // ID of the region
+ unsigned char areaType; // Are type.
+ bool remap;
+ bool visited;
+ rcIntArray connections;
+ rcIntArray floors;
+};
+
+static void removeAdjacentNeighbours(rcRegion& reg)
+{
+ // Remove adjacent duplicates.
+ for (int i = 0; i < reg.connections.size() && reg.connections.size() > 1; )
+ {
+ int ni = (i+1) % reg.connections.size();
+ if (reg.connections[i] == reg.connections[ni])
+ {
+ // Remove duplicate
+ for (int j = i; j < reg.connections.size()-1; ++j)
+ reg.connections[j] = reg.connections[j+1];
+ reg.connections.pop();
+ }
+ else
+ ++i;
+ }
+}
+
+static void replaceNeighbour(rcRegion& reg, unsigned short oldId, unsigned short newId)
+{
+ bool neiChanged = false;
+ for (int i = 0; i < reg.connections.size(); ++i)
+ {
+ if (reg.connections[i] == oldId)
+ {
+ reg.connections[i] = newId;
+ neiChanged = true;
+ }
+ }
+ for (int i = 0; i < reg.floors.size(); ++i)
+ {
+ if (reg.floors[i] == oldId)
+ reg.floors[i] = newId;
+ }
+ if (neiChanged)
+ removeAdjacentNeighbours(reg);
+}
+
+static bool canMergeWithRegion(const rcRegion& rega, const rcRegion& regb)
+{
+ if (rega.areaType != regb.areaType)
+ return false;
+ int n = 0;
+ for (int i = 0; i < rega.connections.size(); ++i)
+ {
+ if (rega.connections[i] == regb.id)
+ n++;
+ }
+ if (n > 1)
+ return false;
+ for (int i = 0; i < rega.floors.size(); ++i)
+ {
+ if (rega.floors[i] == regb.id)
+ return false;
+ }
+ return true;
+}
+
+static void addUniqueFloorRegion(rcRegion& reg, int n)
+{
+ for (int i = 0; i < reg.floors.size(); ++i)
+ if (reg.floors[i] == n)
+ return;
+ reg.floors.push(n);
+}
+
+static bool mergeRegions(rcRegion& rega, rcRegion& regb)
+{
+ unsigned short aid = rega.id;
+ unsigned short bid = regb.id;
+
+ // Duplicate current neighbourhood.
+ rcIntArray acon;
+ acon.resize(rega.connections.size());
+ for (int i = 0; i < rega.connections.size(); ++i)
+ acon[i] = rega.connections[i];
+ rcIntArray& bcon = regb.connections;
+
+ // Find insertion point on A.
+ int insa = -1;
+ for (int i = 0; i < acon.size(); ++i)
+ {
+ if (acon[i] == bid)
+ {
+ insa = i;
+ break;
+ }
+ }
+ if (insa == -1)
+ return false;
+
+ // Find insertion point on B.
+ int insb = -1;
+ for (int i = 0; i < bcon.size(); ++i)
+ {
+ if (bcon[i] == aid)
+ {
+ insb = i;
+ break;
+ }
+ }
+ if (insb == -1)
+ return false;
+
+ // Merge neighbours.
+ rega.connections.resize(0);
+ for (int i = 0, ni = acon.size(); i < ni-1; ++i)
+ rega.connections.push(acon[(insa+1+i) % ni]);
+
+ for (int i = 0, ni = bcon.size(); i < ni-1; ++i)
+ rega.connections.push(bcon[(insb+1+i) % ni]);
+
+ removeAdjacentNeighbours(rega);
+
+ for (int j = 0; j < regb.floors.size(); ++j)
+ addUniqueFloorRegion(rega, regb.floors[j]);
+ rega.spanCount += regb.spanCount;
+ regb.spanCount = 0;
+ regb.connections.resize(0);
+
+ return true;
+}
+
+static bool isRegionConnectedToBorder(const rcRegion& reg)
+{
+ // Region is connected to border if
+ // one of the neighbours is null id.
+ for (int i = 0; i < reg.connections.size(); ++i)
+ {
+ if (reg.connections[i] == 0)
+ return true;
+ }
+ return false;
+}
+
+static bool isSolidEdge(rcCompactHeightfield& chf, unsigned short* srcReg,
+ int x, int y, int i, int dir)
+{
+ const rcCompactSpan& s = chf.spans[i];
+ unsigned short r = 0;
+ if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
+ {
+ const int ax = x + rcGetDirOffsetX(dir);
+ const int ay = y + rcGetDirOffsetY(dir);
+ const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(s, dir);
+ r = srcReg[ai];
+ }
+ if (r == srcReg[i])
+ return false;
+ return true;
+}
+
+static void walkContour(int x, int y, int i, int dir,
+ rcCompactHeightfield& chf,
+ unsigned short* srcReg,
+ rcIntArray& cont)
+{
+ int startDir = dir;
+ int starti = i;
+
+ const rcCompactSpan& ss = chf.spans[i];
+ unsigned short curReg = 0;
+ if (rcGetCon(ss, dir) != RC_NOT_CONNECTED)
+ {
+ const int ax = x + rcGetDirOffsetX(dir);
+ const int ay = y + rcGetDirOffsetY(dir);
+ const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(ss, dir);
+ curReg = srcReg[ai];
+ }
+ cont.push(curReg);
+
+ int iter = 0;
+ while (++iter < 40000)
+ {
+ const rcCompactSpan& s = chf.spans[i];
+
+ if (isSolidEdge(chf, srcReg, x, y, i, dir))
+ {
+ // Choose the edge corner
+ unsigned short r = 0;
+ if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
+ {
+ const int ax = x + rcGetDirOffsetX(dir);
+ const int ay = y + rcGetDirOffsetY(dir);
+ const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(s, dir);
+ r = srcReg[ai];
+ }
+ if (r != curReg)
+ {
+ curReg = r;
+ cont.push(curReg);
+ }
+
+ dir = (dir+1) & 0x3; // Rotate CW
+ }
+ else
+ {
+ int ni = -1;
+ const int nx = x + rcGetDirOffsetX(dir);
+ const int ny = y + rcGetDirOffsetY(dir);
+ if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
+ {
+ const rcCompactCell& nc = chf.cells[nx+ny*chf.width];
+ ni = (int)nc.index + rcGetCon(s, dir);
+ }
+ if (ni == -1)
+ {
+ // Should not happen.
+ return;
+ }
+ x = nx;
+ y = ny;
+ i = ni;
+ dir = (dir+3) & 0x3; // Rotate CCW
+ }
+
+ if (starti == i && startDir == dir)
+ {
+ break;
+ }
+ }
+
+ // Remove adjacent duplicates.
+ if (cont.size() > 1)
+ {
+ for (int i = 0; i < cont.size(); )
+ {
+ int ni = (i+1) % cont.size();
+ if (cont[i] == cont[ni])
+ {
+ for (int j = i; j < cont.size()-1; ++j)
+ cont[j] = cont[j+1];
+ cont.pop();
+ }
+ else
+ ++i;
+ }
+ }
+}
+
+static bool filterSmallRegions(rcContext* ctx, int minRegionArea, int mergeRegionSize,
+ unsigned short& maxRegionId,
+ rcCompactHeightfield& chf,
+ unsigned short* srcReg)
+{
+ const int w = chf.width;
+ const int h = chf.height;
+
+ const int nreg = maxRegionId+1;
+ rcRegion* regions = (rcRegion*)rcAlloc(sizeof(rcRegion)*nreg, RC_ALLOC_TEMP);
+ if (!regions)
+ {
+ ctx->log(RC_LOG_ERROR, "filterSmallRegions: Out of memory 'regions' (%d).", nreg);
+ return false;
+ }
+
+ // Construct regions
+ for (int i = 0; i < nreg; ++i)
+ new(&regions[i]) rcRegion((unsigned short)i);
+
+ // Find edge of a region and find connections around the contour.
+ for (int y = 0; y < h; ++y)
+ {
+ for (int x = 0; x < w; ++x)
+ {
+ const rcCompactCell& c = chf.cells[x+y*w];
+ for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
+ {
+ unsigned short r = srcReg[i];
+ if (r == 0 || r >= nreg)
+ continue;
+
+ rcRegion& reg = regions[r];
+ reg.spanCount++;
+
+
+ // Update floors.
+ for (int j = (int)c.index; j < ni; ++j)
+ {
+ if (i == j) continue;
+ unsigned short floorId = srcReg[j];
+ if (floorId == 0 || floorId >= nreg)
+ continue;
+ addUniqueFloorRegion(reg, floorId);
+ }
+
+ // Have found contour
+ if (reg.connections.size() > 0)
+ continue;
+
+ reg.areaType = chf.areas[i];
+
+ // Check if this cell is next to a border.
+ int ndir = -1;
+ for (int dir = 0; dir < 4; ++dir)
+ {
+ if (isSolidEdge(chf, srcReg, x, y, i, dir))
+ {
+ ndir = dir;
+ break;
+ }
+ }
+
+ if (ndir != -1)
+ {
+ // The cell is at border.
+ // Walk around the contour to find all the neighbours.
+ walkContour(x, y, i, ndir, chf, srcReg, reg.connections);
+ }
+ }
+ }
+ }
+
+ // Remove too small regions.
+ rcIntArray stack(32);
+ rcIntArray trace(32);
+ for (int i = 0; i < nreg; ++i)
+ {
+ rcRegion& reg = regions[i];
+ if (reg.id == 0 || (reg.id & RC_BORDER_REG))
+ continue;
+ if (reg.spanCount == 0)
+ continue;
+ if (reg.visited)
+ continue;
+
+ // Count the total size of all the connected regions.
+ // Also keep track of the regions connects to a tile border.
+ bool connectsToBorder = false;
+ int spanCount = 0;
+ stack.resize(0);
+ trace.resize(0);
+
+ reg.visited = true;
+ stack.push(i);
+
+ while (stack.size())
+ {
+ // Pop
+ int ri = stack.pop();
+
+ rcRegion& creg = regions[ri];
+
+ spanCount += creg.spanCount;
+ trace.push(ri);
+
+ for (int j = 0; j < creg.connections.size(); ++j)
+ {
+ if (creg.connections[j] & RC_BORDER_REG)
+ {
+ connectsToBorder = true;
+ continue;
+ }
+ rcRegion& nreg = regions[creg.connections[j]];
+ if (nreg.visited)
+ continue;
+ if (nreg.id == 0 || (nreg.id & RC_BORDER_REG))
+ continue;
+ // Visit
+ stack.push(nreg.id);
+ nreg.visited = true;
+ }
+ }
+
+ // If the accumulated regions size is too small, remove it.
+ // Do not remove areas which connect to tile borders
+ // as their size cannot be estimated correctly and removing them
+ // can potentially remove necessary areas.
+ if (spanCount < minRegionArea && !connectsToBorder)
+ {
+ // Kill all visited regions.
+ for (int j = 0; j < trace.size(); ++j)
+ {
+ regions[trace[j]].spanCount = 0;
+ regions[trace[j]].id = 0;
+ }
+ }
+ }
+
+ // Merge too small regions to neighbour regions.
+ int mergeCount = 0 ;
+ do
+ {
+ mergeCount = 0;
+ for (int i = 0; i < nreg; ++i)
+ {
+ rcRegion& reg = regions[i];
+ if (reg.id == 0 || (reg.id & RC_BORDER_REG))
+ continue;
+ if (reg.spanCount == 0)
+ continue;
+
+ // Check to see if the region should be merged.
+ if (reg.spanCount > mergeRegionSize && isRegionConnectedToBorder(reg))
+ continue;
+
+ // Small region with more than 1 connection.
+ // Or region which is not connected to a border at all.
+ // Find smallest neighbour region that connects to this one.
+ int smallest = 0xfffffff;
+ unsigned short mergeId = reg.id;
+ for (int j = 0; j < reg.connections.size(); ++j)
+ {
+ if (reg.connections[j] & RC_BORDER_REG) continue;
+ rcRegion& mreg = regions[reg.connections[j]];
+ if (mreg.id == 0 || (mreg.id & RC_BORDER_REG)) continue;
+ if (mreg.spanCount < smallest &&
+ canMergeWithRegion(reg, mreg) &&
+ canMergeWithRegion(mreg, reg))
+ {
+ smallest = mreg.spanCount;
+ mergeId = mreg.id;
+ }
+ }
+ // Found new id.
+ if (mergeId != reg.id)
+ {
+ unsigned short oldId = reg.id;
+ rcRegion& target = regions[mergeId];
+
+ // Merge neighbours.
+ if (mergeRegions(target, reg))
+ {
+ // Fixup regions pointing to current region.
+ for (int j = 0; j < nreg; ++j)
+ {
+ if (regions[j].id == 0 || (regions[j].id & RC_BORDER_REG)) continue;
+ // If another region was already merged into current region
+ // change the nid of the previous region too.
+ if (regions[j].id == oldId)
+ regions[j].id = mergeId;
+ // Replace the current region with the new one if the
+ // current regions is neighbour.
+ replaceNeighbour(regions[j], oldId, mergeId);
+ }
+ mergeCount++;
+ }
+ }
+ }
+ }
+ while (mergeCount > 0);
+
+ // Compress region Ids.
+ for (int i = 0; i < nreg; ++i)
+ {
+ regions[i].remap = false;
+ if (regions[i].id == 0) continue; // Skip nil regions.
+ if (regions[i].id & RC_BORDER_REG) continue; // Skip external regions.
+ regions[i].remap = true;
+ }
+
+ unsigned short regIdGen = 0;
+ for (int i = 0; i < nreg; ++i)
+ {
+ if (!regions[i].remap)
+ continue;
+ unsigned short oldId = regions[i].id;
+ unsigned short newId = ++regIdGen;
+ for (int j = i; j < nreg; ++j)
+ {
+ if (regions[j].id == oldId)
+ {
+ regions[j].id = newId;
+ regions[j].remap = false;
+ }
+ }
+ }
+ maxRegionId = regIdGen;
+
+ // Remap regions.
+ for (int i = 0; i < chf.spanCount; ++i)
+ {
+ if ((srcReg[i] & RC_BORDER_REG) == 0)
+ srcReg[i] = regions[srcReg[i]].id;
+ }
+
+ for (int i = 0; i < nreg; ++i)
+ regions[i].~rcRegion();
+ rcFree(regions);
+
+ return true;
+}
+
+
+bool rcBuildDistanceField(rcContext* ctx, rcCompactHeightfield& chf)
+{
+ rcAssert(ctx);
+
+ ctx->startTimer(RC_TIMER_BUILD_DISTANCEFIELD);
+
+ if (chf.dist)
+ {
+ rcFree(chf.dist);
+ chf.dist = 0;
+ }
+
+ unsigned short* src = (unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount, RC_ALLOC_TEMP);
+ if (!src)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildDistanceField: Out of memory 'src' (%d).", chf.spanCount);
+ return false;
+ }
+ unsigned short* dst = (unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount, RC_ALLOC_TEMP);
+ if (!dst)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildDistanceField: Out of memory 'dst' (%d).", chf.spanCount);
+ rcFree(src);
+ return false;
+ }
+
+ unsigned short maxDist = 0;
+
+ ctx->startTimer(RC_TIMER_BUILD_DISTANCEFIELD_DIST);
+
+ calculateDistanceField(chf, src, maxDist);
+ chf.maxDistance = maxDist;
+
+ ctx->stopTimer(RC_TIMER_BUILD_DISTANCEFIELD_DIST);
+
+ ctx->startTimer(RC_TIMER_BUILD_DISTANCEFIELD_BLUR);
+
+ // Blur
+ if (boxBlur(chf, 1, src, dst) != src)
+ rcSwap(src, dst);
+
+ // Store distance.
+ chf.dist = src;
+
+ ctx->stopTimer(RC_TIMER_BUILD_DISTANCEFIELD_BLUR);
+
+ ctx->stopTimer(RC_TIMER_BUILD_DISTANCEFIELD);
+
+ rcFree(dst);
+
+ return true;
+}
+
+static void paintRectRegion(int minx, int maxx, int miny, int maxy, unsigned short regId,
+ rcCompactHeightfield& chf, unsigned short* srcReg)
+{
+ const int w = chf.width;
+ for (int y = miny; y < maxy; ++y)
+ {
+ for (int x = minx; x < maxx; ++x)
+ {
+ const rcCompactCell& c = chf.cells[x+y*w];
+ for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
+ {
+ if (chf.areas[i] != RC_NULL_AREA)
+ srcReg[i] = regId;
+ }
+ }
+ }
+}
+
+
+static const unsigned short RC_NULL_NEI = 0xffff;
+
+struct rcSweepSpan
+{
+ unsigned short rid; // row id
+ unsigned short id; // region id
+ unsigned short ns; // number samples
+ unsigned short nei; // neighbour id
+};
+
+bool rcBuildRegionsMonotone(rcContext* ctx, rcCompactHeightfield& chf,
+ const int borderSize, const int minRegionArea, const int mergeRegionArea)
+{
+ rcAssert(ctx);
+
+ ctx->startTimer(RC_TIMER_BUILD_REGIONS);
+
+ const int w = chf.width;
+ const int h = chf.height;
+ unsigned short id = 1;
+
+ rcScopedDelete<unsigned short> srcReg = (unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount, RC_ALLOC_TEMP);
+ if (!srcReg)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildRegionsMonotone: Out of memory 'src' (%d).", chf.spanCount);
+ return false;
+ }
+ memset(srcReg,0,sizeof(unsigned short)*chf.spanCount);
+
+ const int nsweeps = rcMax(chf.width,chf.height);
+ rcScopedDelete<rcSweepSpan> sweeps = (rcSweepSpan*)rcAlloc(sizeof(rcSweepSpan)*nsweeps, RC_ALLOC_TEMP);
+ if (!sweeps)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildRegionsMonotone: Out of memory 'sweeps' (%d).", nsweeps);
+ return false;
+ }
+
+
+ // Mark border regions.
+ if (borderSize > 0)
+ {
+ // Make sure border will not overflow.
+ const int bw = rcMin(w, borderSize);
+ const int bh = rcMin(h, borderSize);
+ // Paint regions
+ paintRectRegion(0, bw, 0, h, id|RC_BORDER_REG, chf, srcReg); id++;
+ paintRectRegion(w-bw, w, 0, h, id|RC_BORDER_REG, chf, srcReg); id++;
+ paintRectRegion(0, w, 0, bh, id|RC_BORDER_REG, chf, srcReg); id++;
+ paintRectRegion(0, w, h-bh, h, id|RC_BORDER_REG, chf, srcReg); id++;
+ }
+
+ rcIntArray prev(256);
+
+ // Sweep one line at a time.
+ for (int y = borderSize; y < h-borderSize; ++y)
+ {
+ // Collect spans from this row.
+ prev.resize(id+1);
+ memset(&prev[0],0,sizeof(int)*id);
+ unsigned short rid = 1;
+
+ for (int x = borderSize; x < w-borderSize; ++x)
+ {
+ const rcCompactCell& c = chf.cells[x+y*w];
+
+ for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
+ {
+ const rcCompactSpan& s = chf.spans[i];
+ if (chf.areas[i] == RC_NULL_AREA) continue;
+
+ // -x
+ unsigned short previd = 0;
+ if (rcGetCon(s, 0) != RC_NOT_CONNECTED)
+ {
+ const int ax = x + rcGetDirOffsetX(0);
+ const int ay = y + rcGetDirOffsetY(0);
+ const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 0);
+ if ((srcReg[ai] & RC_BORDER_REG) == 0 && chf.areas[i] == chf.areas[ai])
+ previd = srcReg[ai];
+ }
+
+ if (!previd)
+ {
+ previd = rid++;
+ sweeps[previd].rid = previd;
+ sweeps[previd].ns = 0;
+ sweeps[previd].nei = 0;
+ }
+
+ // -y
+ if (rcGetCon(s,3) != RC_NOT_CONNECTED)
+ {
+ const int ax = x + rcGetDirOffsetX(3);
+ const int ay = y + rcGetDirOffsetY(3);
+ const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 3);
+ if (srcReg[ai] && (srcReg[ai] & RC_BORDER_REG) == 0 && chf.areas[i] == chf.areas[ai])
+ {
+ unsigned short nr = srcReg[ai];
+ if (!sweeps[previd].nei || sweeps[previd].nei == nr)
+ {
+ sweeps[previd].nei = nr;
+ sweeps[previd].ns++;
+ prev[nr]++;
+ }
+ else
+ {
+ sweeps[previd].nei = RC_NULL_NEI;
+ }
+ }
+ }
+
+ srcReg[i] = previd;
+ }
+ }
+
+ // Create unique ID.
+ for (int i = 1; i < rid; ++i)
+ {
+ if (sweeps[i].nei != RC_NULL_NEI && sweeps[i].nei != 0 &&
+ prev[sweeps[i].nei] == (int)sweeps[i].ns)
+ {
+ sweeps[i].id = sweeps[i].nei;
+ }
+ else
+ {
+ sweeps[i].id = id++;
+ }
+ }
+
+ // Remap IDs
+ for (int x = borderSize; x < w-borderSize; ++x)
+ {
+ const rcCompactCell& c = chf.cells[x+y*w];
+
+ for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
+ {
+ if (srcReg[i] > 0 && srcReg[i] < rid)
+ srcReg[i] = sweeps[srcReg[i]].id;
+ }
+ }
+ }
+
+ ctx->startTimer(RC_TIMER_BUILD_REGIONS_FILTER);
+
+ // Filter out small regions.
+ chf.maxRegions = id;
+ if (!filterSmallRegions(ctx, minRegionArea, mergeRegionArea, chf.maxRegions, chf, srcReg))
+ return false;
+
+ ctx->stopTimer(RC_TIMER_BUILD_REGIONS_FILTER);
+
+ // Store the result out.
+ for (int i = 0; i < chf.spanCount; ++i)
+ chf.spans[i].reg = srcReg[i];
+
+ ctx->stopTimer(RC_TIMER_BUILD_REGIONS);
+
+ return true;
+}
+
+bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf,
+ const int borderSize, const int minRegionArea, const int mergeRegionArea)
+{
+ rcAssert(ctx);
+
+ ctx->startTimer(RC_TIMER_BUILD_REGIONS);
+
+ const int w = chf.width;
+ const int h = chf.height;
+
+ rcScopedDelete<unsigned short> buf = (unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount*4, RC_ALLOC_TEMP);
+ if (!buf)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildRegions: Out of memory 'tmp' (%d).", chf.spanCount*4);
+ return false;
+ }
+
+ ctx->startTimer(RC_TIMER_BUILD_REGIONS_WATERSHED);
+
+ rcIntArray stack(1024);
+ rcIntArray visited(1024);
+
+ unsigned short* srcReg = buf;
+ unsigned short* srcDist = buf+chf.spanCount;
+ unsigned short* dstReg = buf+chf.spanCount*2;
+ unsigned short* dstDist = buf+chf.spanCount*3;
+
+ memset(srcReg, 0, sizeof(unsigned short)*chf.spanCount);
+ memset(srcDist, 0, sizeof(unsigned short)*chf.spanCount);
+
+ unsigned short regionId = 1;
+ unsigned short level = (chf.maxDistance+1) & ~1;
+
+ // TODO: Figure better formula, expandIters defines how much the
+ // watershed "overflows" and simplifies the regions. Tying it to
+ // agent radius was usually good indication how greedy it could be.
+// const int expandIters = 4 + walkableRadius * 2;
+ const int expandIters = 8;
+
+ // Mark border regions.
+ paintRectRegion(0, borderSize, 0, h, regionId|RC_BORDER_REG, chf, srcReg); regionId++;
+ paintRectRegion(w-borderSize, w, 0, h, regionId|RC_BORDER_REG, chf, srcReg); regionId++;
+ paintRectRegion(0, w, 0, borderSize, regionId|RC_BORDER_REG, chf, srcReg); regionId++;
+ paintRectRegion(0, w, h-borderSize, h, regionId|RC_BORDER_REG, chf, srcReg); regionId++;
+
+ while (level > 0)
+ {
+ level = level >= 2 ? level-2 : 0;
+
+ ctx->startTimer(RC_TIMER_BUILD_REGIONS_EXPAND);
+
+ // Expand current regions until no empty connected cells found.
+ if (expandRegions(expandIters, level, chf, srcReg, srcDist, dstReg, dstDist, stack) != srcReg)
+ {
+ rcSwap(srcReg, dstReg);
+ rcSwap(srcDist, dstDist);
+ }
+
+ ctx->stopTimer(RC_TIMER_BUILD_REGIONS_EXPAND);
+
+ ctx->startTimer(RC_TIMER_BUILD_REGIONS_FLOOD);
+
+ // Mark new regions with IDs.
+ for (int y = 0; y < h; ++y)
+ {
+ for (int x = 0; x < w; ++x)
+ {
+ const rcCompactCell& c = chf.cells[x+y*w];
+ for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
+ {
+ if (chf.dist[i] < level || srcReg[i] != 0 || chf.areas[i] == RC_NULL_AREA)
+ continue;
+
+ if (floodRegion(x, y, i, level, regionId, chf, srcReg, srcDist, stack))
+ regionId++;
+ }
+ }
+ }
+
+ ctx->stopTimer(RC_TIMER_BUILD_REGIONS_FLOOD);
+
+ }
+
+ // Expand current regions until no empty connected cells found.
+ if (expandRegions(expandIters*8, 0, chf, srcReg, srcDist, dstReg, dstDist, stack) != srcReg)
+ {
+ rcSwap(srcReg, dstReg);
+ rcSwap(srcDist, dstDist);
+ }
+
+ ctx->stopTimer(RC_TIMER_BUILD_REGIONS_WATERSHED);
+
+ ctx->startTimer(RC_TIMER_BUILD_REGIONS_FILTER);
+
+ // Filter out small regions.
+ chf.maxRegions = regionId;
+ if (!filterSmallRegions(ctx, minRegionArea, mergeRegionArea, chf.maxRegions, chf, srcReg))
+ return false;
+
+ ctx->stopTimer(RC_TIMER_BUILD_REGIONS_FILTER);
+
+ // Write the result out.
+ for (int i = 0; i < chf.spanCount; ++i)
+ chf.spans[i].reg = srcReg[i];
+
+ ctx->stopTimer(RC_TIMER_BUILD_REGIONS);
+
+ return true;
+}
+
+
diff --git a/dep/recastnavigation/TODO.txt b/dep/recastnavigation/TODO.txt
new file mode 100644
index 0000000..b911c0e
--- /dev/null
+++ b/dep/recastnavigation/TODO.txt
@@ -0,0 +1,20 @@
+TODO/Roadmap
+
+Summer/Autumn 2009
+
+- Off mesh links (jump links)
+- Area annotations
+- Embed extra data per polygon
+- Height conforming navmesh
+
+
+Autumn/Winter 2009/2010
+
+- Detour path following
+- More dynamic example with tile navmesh
+- Faster small tile process
+
+
+More info at http://digestingduck.blogspot.com/2009/07/recast-and-detour-roadmap.html
+
+-
diff --git a/sql/updates/world/2012_09_06_00_world_command.sql b/sql/updates/world/2012_09_06_00_world_command.sql
new file mode 100644
index 0000000..e60c164
--- /dev/null
+++ b/sql/updates/world/2012_09_06_00_world_command.sql
@@ -0,0 +1,9 @@
+DELETE FROM `command` WHERE `name`='mmap' OR `name` LIKE 'mmap%';
+INSERT INTO `command` (`name`, `security`, `help`) VALUES
+('mmap', 3, 'Syntax: Syntax: .mmaps $subcommand Type .mmaps to see the list of possible subcommands or .help mmaps $subcommand to see info on subcommands'),
+('mmap path', 3, 'Syntax: .mmap path to calculate and show a path to current select unit'),
+('mmap loc', 3, 'Syntax: .mmap loc to print on which tile one is'),
+('mmap loadedtiles', 3, 'Syntax: .mmap loadedtiles to show which tiles are currently loaded'),
+('mmap stats', 3, 'Syntax: .mmap stats to show information about current state of mmaps'),
+('mmap testarea', 3, 'Syntax: .mmap testarea to calculate paths for all nearby npcs to player');
+
diff --git a/src/server/CMakeLists.txt b/src/server/CMakeLists.txt
index 2b6c553..e038f36 100644
--- a/src/server/CMakeLists.txt
+++ b/src/server/CMakeLists.txt
@@ -30,5 +30,6 @@ if( SERVERS )
else()
if( TOOLS )
add_subdirectory(collision)
+ add_subdirectory(shared)
endif()
endif()
diff --git a/src/server/collision/CMakeLists.txt b/src/server/collision/CMakeLists.txt
index 3182b20..a960575 100644
--- a/src/server/collision/CMakeLists.txt
+++ b/src/server/collision/CMakeLists.txt
@@ -33,7 +33,9 @@ set(collision_STAT_SRCS
include_directories(
${CMAKE_BINARY_DIR}
${CMAKE_SOURCE_DIR}/dep/g3dlite/include
+ ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Detour
${CMAKE_SOURCE_DIR}/src/server/shared
+ ${CMAKE_SOURCE_DIR}/src/server/shared/Configuration
${CMAKE_SOURCE_DIR}/src/server/shared/Debugging
${CMAKE_SOURCE_DIR}/src/server/shared/Database
${CMAKE_SOURCE_DIR}/src/server/shared/Debugging
diff --git a/src/server/collision/Management/MMapFactory.cpp b/src/server/collision/Management/MMapFactory.cpp
new file mode 100644
index 0000000..f4b2f3d
--- /dev/null
+++ b/src/server/collision/Management/MMapFactory.cpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2008-2011 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "MMapFactory.h"
+#include "World.h"
+#include "Config.h"
+#include <set>
+
+namespace MMAP
+{
+ // ######################## MMapFactory ########################
+ // our global singleton copy
+ MMapManager *g_MMapManager = NULL;
+
+ // stores list of mapids which do not use pathfinding
+ std::set<uint32>* g_mmapDisabledIds = NULL;
+
+ MMapManager* MMapFactory::createOrGetMMapManager()
+ {
+ if (g_MMapManager == NULL)
+ g_MMapManager = new MMapManager();
+
+ return g_MMapManager;
+ }
+
+ void MMapFactory::preventPathfindingOnMaps(const char* ignoreMapIds)
+ {
+ if (!g_mmapDisabledIds)
+ g_mmapDisabledIds = new std::set<uint32>();
+
+ uint32 strLenght = strlen(ignoreMapIds)+1;
+ char* mapList = new char[strLenght];
+ memcpy(mapList, ignoreMapIds, sizeof(char)*strLenght);
+
+ char* idstr = strtok(mapList, ",");
+ while (idstr)
+ {
+ g_mmapDisabledIds->insert(uint32(atoi(idstr)));
+ idstr = strtok(NULL, ",");
+ }
+
+ delete[] mapList;
+ }
+
+ bool MMapFactory::IsPathfindingEnabled(uint32 mapId)
+ {
+ return sWorld->getBoolConfig(CONFIG_ENABLE_MMAPS)
+ && g_mmapDisabledIds->find(mapId) == g_mmapDisabledIds->end();
+ }
+
+ void MMapFactory::clear()
+ {
+ if (g_mmapDisabledIds)
+ {
+ delete g_mmapDisabledIds;
+ g_mmapDisabledIds = NULL;
+ }
+
+ if (g_MMapManager)
+ {
+ delete g_MMapManager;
+ g_MMapManager = NULL;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/server/collision/Management/MMapFactory.h b/src/server/collision/Management/MMapFactory.h
new file mode 100644
index 0000000..ab04733
--- /dev/null
+++ b/src/server/collision/Management/MMapFactory.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2008-2011 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _MMAP_FACTORY_H
+#define _MMAP_FACTORY_H
+
+#include "MMapManager.h"
+#include "UnorderedMap.h"
+#include "DetourAlloc.h"
+#include "DetourNavMesh.h"
+#include "DetourNavMeshQuery.h"
+
+namespace MMAP
+{
+ enum MMAP_LOAD_RESULT
+ {
+ MMAP_LOAD_RESULT_ERROR,
+ MMAP_LOAD_RESULT_OK,
+ MMAP_LOAD_RESULT_IGNORED,
+ };
+
+ // static class
+ // holds all mmap global data
+ // access point to MMapManager singleton
+ class MMapFactory
+ {
+ public:
+ static MMapManager* createOrGetMMapManager();
+ static void clear();
+ static void preventPathfindingOnMaps(const char* ignoreMapIds);
+ static bool IsPathfindingEnabled(uint32 mapId);
+ };
+}
+
+#endif
+
diff --git a/src/server/collision/Management/MMapManager.cpp b/src/server/collision/Management/MMapManager.cpp
new file mode 100644
index 0000000..c7dea35
--- /dev/null
+++ b/src/server/collision/Management/MMapManager.cpp
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2008-2011 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "MMapManager.h"
+#include "Log.h"
+#include "World.h"
+
+namespace MMAP
+{
+ // ######################## MMapManager ########################
+ MMapManager::~MMapManager()
+ {
+ for (MMapDataSet::iterator i = loadedMMaps.begin(); i != loadedMMaps.end(); ++i)
+ delete i->second;
+
+ // by now we should not have maps loaded
+ // if we had, tiles in MMapData->mmapLoadedTiles, their actual data is lost!
+ }
+
+ bool MMapManager::loadMapData(uint32 mapId)
+ {
+ // we already have this map loaded?
+ if (loadedMMaps.find(mapId) != loadedMMaps.end())
+ return true;
+
+ // load and init dtNavMesh - read parameters from file
+ uint32 pathLen = sWorld->GetDataPath().length() + strlen("mmaps/%03i.mmap")+1;
+ char *fileName = new char[pathLen];
+ snprintf(fileName, pathLen, (sWorld->GetDataPath()+"mmaps/%03i.mmap").c_str(), mapId);
+
+ FILE* file = fopen(fileName, "rb");
+ if (!file)
+ {
+ sLog->outDebug(LOG_FILTER_MAPS, "MMAP:loadMapData: Error: Could not open mmap file '%s'", fileName);
+ delete [] fileName;
+ return false;
+ }
+
+ dtNavMeshParams params;
+ fread(&params, sizeof(dtNavMeshParams), 1, file);
+ fclose(file);
+
+ dtNavMesh* mesh = dtAllocNavMesh();
+ ASSERT(mesh);
+ if (DT_SUCCESS != mesh->init(&params))
+ {
+ dtFreeNavMesh(mesh);
+ sLog->outError(LOG_FILTER_MAPS, "MMAP:loadMapData: Failed to initialize dtNavMesh for mmap %03u from file %s", mapId, fileName);
+ delete [] fileName;
+ return false;
+ }
+
+ delete [] fileName;
+
+ sLog->outInfo(LOG_FILTER_MAPS, "MMAP:loadMapData: Loaded %03i.mmap", mapId);
+
+ // store inside our map list
+ MMapData* mmap_data = new MMapData(mesh);
+ mmap_data->mmapLoadedTiles.clear();
+
+ loadedMMaps.insert(std::pair<uint32, MMapData*>(mapId, mmap_data));
+ return true;
+ }
+
+ uint32 MMapManager::packTileID(int32 x, int32 y)
+ {
+ return uint32(x << 16 | y);
+ }
+
+ bool MMapManager::loadMap(const std::string& basePath, uint32 mapId, int32 x, int32 y)
+ {
+ // make sure the mmap is loaded and ready to load tiles
+ if (!loadMapData(mapId))
+ return false;
+
+ // get this mmap data
+ MMapData* mmap = loadedMMaps[mapId];
+ ASSERT(mmap->navMesh);
+
+ // check if we already have this tile loaded
+ uint32 packedGridPos = packTileID(x, y);
+ if (mmap->mmapLoadedTiles.find(packedGridPos) != mmap->mmapLoadedTiles.end())
+ return false;
+
+ // load this tile :: mmaps/MMMXXYY.mmtile
+ uint32 pathLen = sWorld->GetDataPath().length() + strlen("mmaps/%03i%02i%02i.mmtile")+1;
+ char *fileName = new char[pathLen];
+
+ snprintf(fileName, pathLen, (sWorld->GetDataPath()+"mmaps/%03i%02i%02i.mmtile").c_str(), mapId, x, y);
+
+ FILE *file = fopen(fileName, "rb");
+ if (!file)
+ {
+ sLog->outDebug(LOG_FILTER_MAPS, "MMAP:loadMap: Could not open mmtile file '%s'", fileName);
+ delete [] fileName;
+ return false;
+ }
+ delete [] fileName;
+
+ // read header
+ MmapTileHeader fileHeader;
+ fread(&fileHeader, sizeof(MmapTileHeader), 1, file);
+
+ if (fileHeader.mmapMagic != MMAP_MAGIC)
+ {
+ sLog->outError(LOG_FILTER_MAPS, "MMAP:loadMap: Bad header in mmap %03u%02i%02i.mmtile", mapId, x, y);
+ return false;
+ }
+
+ if (fileHeader.mmapVersion != MMAP_VERSION)
+ {
+ sLog->outError(LOG_FILTER_MAPS, "MMAP:loadMap: %03u%02i%02i.mmtile was built with generator v%i, expected v%i",
+ mapId, x, y, fileHeader.mmapVersion, MMAP_VERSION);
+ return false;
+ }
+
+ unsigned char* data = (unsigned char*)dtAlloc(fileHeader.size, DT_ALLOC_PERM);
+ ASSERT(data);
+
+ size_t result = fread(data, fileHeader.size, 1, file);
+ if (!result)
+ {
+ sLog->outError(LOG_FILTER_MAPS, "MMAP:loadMap: Bad header or data in mmap %03u%02i%02i.mmtile", mapId, x, y);
+ fclose(file);
+ return false;
+ }
+
+ fclose(file);
+
+ dtMeshHeader* header = (dtMeshHeader*)data;
+ dtTileRef tileRef = 0;
+
+ // memory allocated for data is now managed by detour, and will be deallocated when the tile is removed
+ if (DT_SUCCESS == mmap->navMesh->addTile(data, fileHeader.size, DT_TILE_FREE_DATA, 0, &tileRef))
+ {
+ mmap->mmapLoadedTiles.insert(std::pair<uint32, dtTileRef>(packedGridPos, tileRef));
+ ++loadedTiles;
+ sLog->outInfo(LOG_FILTER_MAPS, "MMAP:loadMap: Loaded mmtile %03i[%02i,%02i] into %03i[%02i,%02i]", mapId, x, y, mapId, header->x, header->y);
+ return true;
+ }
+ else
+ {
+ sLog->outError(LOG_FILTER_MAPS, "MMAP:loadMap: Could not load %03u%02i%02i.mmtile into navmesh", mapId, x, y);
+ dtFree(data);
+ return false;
+ }
+
+ return false;
+ }
+
+ bool MMapManager::unloadMap(uint32 mapId, int32 x, int32 y)
+ {
+ // check if we have this map loaded
+ if (loadedMMaps.find(mapId) == loadedMMaps.end())
+ {
+ // file may not exist, therefore not loaded
+ sLog->outDebug(LOG_FILTER_MAPS, "MMAP:unloadMap: Asked to unload not loaded navmesh map. %03u%02i%02i.mmtile", mapId, x, y);
+ return false;
+ }
+
+ MMapData* mmap = loadedMMaps[mapId];
+
+ // check if we have this tile loaded
+ uint32 packedGridPos = packTileID(x, y);
+ if (mmap->mmapLoadedTiles.find(packedGridPos) == mmap->mmapLoadedTiles.end())
+ {
+ // file may not exist, therefore not loaded
+ sLog->outDebug(LOG_FILTER_MAPS, "MMAP:unloadMap: Asked to unload not loaded navmesh tile. %03u%02i%02i.mmtile", mapId, x, y);
+ return false;
+ }
+
+ dtTileRef tileRef = mmap->mmapLoadedTiles[packedGridPos];
+
+ // unload, and mark as non loaded
+ if (DT_SUCCESS != mmap->navMesh->removeTile(tileRef, NULL, NULL))
+ {
+ // this is technically a memory leak
+ // if the grid is later reloaded, dtNavMesh::addTile will return error but no extra memory is used
+ // we cannot recover from this error - assert out
+ sLog->outError(LOG_FILTER_MAPS, "MMAP:unloadMap: Could not unload %03u%02i%02i.mmtile from navmesh", mapId, x, y);
+ ASSERT(false);
+ }
+ else
+ {
+ mmap->mmapLoadedTiles.erase(packedGridPos);
+ --loadedTiles;
+ sLog->outInfo(LOG_FILTER_MAPS, "MMAP:unloadMap: Unloaded mmtile %03i[%02i,%02i] from %03i", mapId, x, y, mapId);
+ return true;
+ }
+
+ return false;
+ }
+
+ bool MMapManager::unloadMap(uint32 mapId)
+ {
+ if (loadedMMaps.find(mapId) == loadedMMaps.end())
+ {
+ // file may not exist, therefore not loaded
+ sLog->outDebug(LOG_FILTER_MAPS, "MMAP:unloadMap: Asked to unload not loaded navmesh map %03u", mapId);
+ return false;
+ }
+
+ // unload all tiles from given map
+ MMapData* mmap = loadedMMaps[mapId];
+ for (MMapTileSet::iterator i = mmap->mmapLoadedTiles.begin(); i != mmap->mmapLoadedTiles.end(); ++i)
+ {
+ uint32 x = (i->first >> 16);
+ uint32 y = (i->first & 0x0000FFFF);
+ if (DT_SUCCESS != mmap->navMesh->removeTile(i->second, NULL, NULL))
+ sLog->outError(LOG_FILTER_MAPS, "MMAP:unloadMap: Could not unload %03u%02i%02i.mmtile from navmesh", mapId, x, y);
+ else
+ {
+ --loadedTiles;
+ sLog->outInfo(LOG_FILTER_MAPS, "MMAP:unloadMap: Unloaded mmtile %03i[%02i,%02i] from %03i", mapId, x, y, mapId);
+ }
+ }
+
+ delete mmap;
+ loadedMMaps.erase(mapId);
+ sLog->outInfo(LOG_FILTER_MAPS, "MMAP:unloadMap: Unloaded %03i.mmap", mapId);
+
+ return true;
+ }
+
+ bool MMapManager::unloadMapInstance(uint32 mapId, uint32 instanceId)
+ {
+ // check if we have this map loaded
+ if (loadedMMaps.find(mapId) == loadedMMaps.end())
+ {
+ // file may not exist, therefore not loaded
+ sLog->outDebug(LOG_FILTER_MAPS, "MMAP:unloadMapInstance: Asked to unload not loaded navmesh map %03u", mapId);
+ return false;
+ }
+
+ MMapData* mmap = loadedMMaps[mapId];
+ if (mmap->navMeshQueries.find(instanceId) == mmap->navMeshQueries.end())
+ {
+ sLog->outDebug(LOG_FILTER_MAPS, "MMAP:unloadMapInstance: Asked to unload not loaded dtNavMeshQuery mapId %03u instanceId %u", mapId, instanceId);
+ return false;
+ }
+
+ dtNavMeshQuery* query = mmap->navMeshQueries[instanceId];
+
+ dtFreeNavMeshQuery(query);
+ mmap->navMeshQueries.erase(instanceId);
+ sLog->outInfo(LOG_FILTER_MAPS, "MMAP:unloadMapInstance: Unloaded mapId %03u instanceId %u", mapId, instanceId);
+
+ return true;
+ }
+
+ dtNavMesh const* MMapManager::GetNavMesh(uint32 mapId)
+ {
+ if (loadedMMaps.find(mapId) == loadedMMaps.end())
+ return NULL;
+
+ return loadedMMaps[mapId]->navMesh;
+ }
+
+ dtNavMeshQuery const* MMapManager::GetNavMeshQuery(uint32 mapId, uint32 instanceId)
+ {
+ if (loadedMMaps.find(mapId) == loadedMMaps.end())
+ return NULL;
+
+ MMapData* mmap = loadedMMaps[mapId];
+ if (mmap->navMeshQueries.find(instanceId) == mmap->navMeshQueries.end())
+ {
+ // allocate mesh query
+ dtNavMeshQuery* query = dtAllocNavMeshQuery();
+ ASSERT(query);
+ if (DT_SUCCESS != query->init(mmap->navMesh, 1024))
+ {
+ dtFreeNavMeshQuery(query);
+ sLog->outError(LOG_FILTER_MAPS, "MMAP:GetNavMeshQuery: Failed to initialize dtNavMeshQuery for mapId %03u instanceId %u", mapId, instanceId);
+ return NULL;
+ }
+
+ sLog->outInfo(LOG_FILTER_MAPS, "MMAP:GetNavMeshQuery: created dtNavMeshQuery for mapId %03u instanceId %u", mapId, instanceId);
+ mmap->navMeshQueries.insert(std::pair<uint32, dtNavMeshQuery*>(instanceId, query));
+ }
+
+ return mmap->navMeshQueries[instanceId];
+ }
+}
diff --git a/src/server/collision/Management/MMapManager.h b/src/server/collision/Management/MMapManager.h
new file mode 100644
index 0000000..23b7d5f
--- /dev/null
+++ b/src/server/collision/Management/MMapManager.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2008-2011 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _MMAP_MANAGER_H
+#define _MMAP_MANAGER_H
+
+#include "UnorderedMap.h"
+#include "DetourAlloc.h"
+#include "DetourNavMesh.h"
+#include "DetourNavMeshQuery.h"
+
+// move map related classes
+namespace MMAP
+{
+ typedef UNORDERED_MAP<uint32, dtTileRef> MMapTileSet;
+ typedef UNORDERED_MAP<uint32, dtNavMeshQuery*> NavMeshQuerySet;
+
+ // dummy struct to hold map's mmap data
+ struct MMapData
+ {
+ MMapData(dtNavMesh* mesh) : navMesh(mesh) {}
+ ~MMapData()
+ {
+ for (NavMeshQuerySet::iterator i = navMeshQueries.begin(); i != navMeshQueries.end(); ++i)
+ dtFreeNavMeshQuery(i->second);
+
+ if (navMesh)
+ dtFreeNavMesh(navMesh);
+ }
+
+ dtNavMesh* navMesh;
+
+ // we have to use single dtNavMeshQuery for every instance, since those are not thread safe
+ NavMeshQuerySet navMeshQueries; // instanceId to query
+ MMapTileSet mmapLoadedTiles; // maps [map grid coords] to [dtTile]
+ };
+
+
+ typedef UNORDERED_MAP<uint32, MMapData*> MMapDataSet;
+
+ // singleton class
+ // holds all all access to mmap loading unloading and meshes
+ class MMapManager
+ {
+ public:
+ MMapManager() : loadedTiles(0) {}
+ ~MMapManager();
+
+ bool loadMap(const std::string& basePath, uint32 mapId, int32 x, int32 y);
+ bool unloadMap(uint32 mapId, int32 x, int32 y);
+ bool unloadMap(uint32 mapId);
+ bool unloadMapInstance(uint32 mapId, uint32 instanceId);
+
+ // the returned [dtNavMeshQuery const*] is NOT threadsafe
+ dtNavMeshQuery const* GetNavMeshQuery(uint32 mapId, uint32 instanceId);
+ dtNavMesh const* GetNavMesh(uint32 mapId);
+
+ uint32 getLoadedTilesCount() const { return loadedTiles; }
+ uint32 getLoadedMapsCount() const { return loadedMMaps.size(); }
+ private:
+ bool loadMapData(uint32 mapId);
+ uint32 packTileID(int32 x, int32 y);
+
+ MMapDataSet loadedMMaps;
+ uint32 loadedTiles;
+ };
+}
+
+#endif
\ No newline at end of file
diff --git a/src/server/collision/Management/VMapManager2.h b/src/server/collision/Management/VMapManager2.h
index 1fba108..c87c42f 100644
--- a/src/server/collision/Management/VMapManager2.h
+++ b/src/server/collision/Management/VMapManager2.h
@@ -112,6 +112,8 @@ namespace VMAP
return getMapFileName(mapId);
}
virtual bool existsMap(const char* basePath, unsigned int mapId, int x, int y);
+ public:
+ void getInstanceMapTree(InstanceTreeMap &instanceMapTree);
};
}
diff --git a/src/server/collision/Maps/MapTree.h b/src/server/collision/Maps/MapTree.h
index 1732209..f0df713 100644
--- a/src/server/collision/Maps/MapTree.h
+++ b/src/server/collision/Maps/MapTree.h
@@ -80,6 +80,7 @@ namespace VMAP
void UnloadMapTile(uint32 tileX, uint32 tileY, VMapManager2* vm);
bool isTiled() const { return iIsTiled; }
uint32 numLoadedTiles() const { return iLoadedTiles.size(); }
+ void getModelInstances(ModelInstance* &models, uint32 &count);
};
struct AreaInfo
diff --git a/src/server/collision/Models/ModelInstance.h b/src/server/collision/Models/ModelInstance.h
index b0189ad..92591a9 100644
--- a/src/server/collision/Models/ModelInstance.h
+++ b/src/server/collision/Models/ModelInstance.h
@@ -74,6 +74,8 @@ namespace VMAP
G3D::Matrix3 iInvRot;
float iInvScale;
WorldModel* iModel;
+ public:
+ WorldModel* const getWorldModel();
};
} // namespace VMAP
diff --git a/src/server/collision/Models/WorldModel.h b/src/server/collision/Models/WorldModel.h
index ebf828e..98be349 100644
--- a/src/server/collision/Models/WorldModel.h
+++ b/src/server/collision/Models/WorldModel.h
@@ -66,6 +66,8 @@ namespace VMAP
uint32 iType; //!< liquid type
float *iHeight; //!< (tilesX + 1)*(tilesY + 1) height values
uint8 *iFlags; //!< info if liquid tile is used
+ public:
+ void getPosInfo(uint32 &tilesX, uint32 &tilesY, Vector3 &corner) const;
};
/*! holding additional info for WMO group files */
@@ -98,6 +100,8 @@ namespace VMAP
std::vector<MeshTriangle> triangles;
BIH meshTree;
WmoLiquid* iLiquid;
+ public:
+ void getMeshData(std::vector<Vector3> &vertices, std::vector<MeshTriangle> &triangles, WmoLiquid* &liquid);
};
/*! Holds a model (converted M2 or WMO) in its original coordinate space */
class WorldModel
@@ -117,6 +121,8 @@ namespace VMAP
uint32 RootWMOID;
std::vector<GroupModel> groupModels;
BIH groupTree;
+ public:
+ void getGroupModels(std::vector<GroupModel> &groupModels);
};
} // namespace VMAP
diff --git a/src/server/game/CMakeLists.txt b/src/server/game/CMakeLists.txt
index cf71bb1..15eb61b 100644
--- a/src/server/game/CMakeLists.txt
+++ b/src/server/game/CMakeLists.txt
@@ -102,6 +102,8 @@ set(game_STAT_SRCS
include_directories(
${CMAKE_BINARY_DIR}
+ ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Detour
+ ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Recast
${CMAKE_SOURCE_DIR}/dep/g3dlite/include
${CMAKE_SOURCE_DIR}/dep/SFMT
${CMAKE_SOURCE_DIR}/dep/zlib
diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp
index d96cf58..6f16618 100644
--- a/src/server/game/Entities/Object/Object.cpp
+++ b/src/server/game/Entities/Object/Object.cpp
@@ -2672,7 +2672,7 @@ void WorldObject::MovePosition(Position &pos, float dist, float angle)
desty = pos.m_positionY + dist * std::sin(angle);
// Prevent invalid coordinates here, position is unchanged
- if (!Trinity::IsValidMapCoord(destx, desty))
+ if (!Trinity::IsValidMapCoord(destx, desty, pos.m_positionZ))
{
sLog->outFatal(LOG_FILTER_GENERAL, "WorldObject::MovePosition invalid coordinates X: %f and Y: %f were passed!", destx, desty);
return;
diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp
index d44dc6f..dee977d 100644
--- a/src/server/game/Entities/Unit/Unit.cpp
+++ b/src/server/game/Entities/Unit/Unit.cpp
@@ -385,10 +385,10 @@ bool Unit::haveOffhandWeapon() const
return m_canDualWield;
}
-void Unit::MonsterMoveWithSpeed(float x, float y, float z, float speed)
+void Unit::MonsterMoveWithSpeed(float x, float y, float z, float speed, bool generatePath, bool forceDestination)
{
- Movement::MoveSplineInit init(*this);
- init.MoveTo(x,y,z);
+ Movement::MoveSplineInit init(this);
+ init.MoveTo(x, y, z, generatePath, forceDestination);
init.SetVelocity(speed);
init.Launch();
}
@@ -14804,7 +14804,7 @@ void Unit::StopMoving()
if (!IsInWorld())
return;
- Movement::MoveSplineInit init(*this);
+ Movement::MoveSplineInit init(this);
init.MoveTo(GetPositionX(), GetPositionY(), GetPositionZMinusOffset());
init.SetFacing(GetOrientation());
init.Launch();
@@ -17231,8 +17231,8 @@ void Unit::_ExitVehicle(Position const* exitPosition)
SendMessageToSet(&data, false);
}
- Movement::MoveSplineInit init(*this);
- init.MoveTo(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ());
+ Movement::MoveSplineInit init(this);
+ init.MoveTo(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), false);
init.SetFacing(GetOrientation());
init.SetTransportExit();
init.Launch();
@@ -17665,7 +17665,7 @@ void Unit::SetInFront(Unit const* target)
void Unit::SetFacingTo(float ori)
{
- Movement::MoveSplineInit init(*this);
+ Movement::MoveSplineInit init(this);
init.MoveTo(GetPositionX(), GetPositionY(), GetPositionZMinusOffset());
init.SetFacing(ori);
init.Launch();
diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h
index e46fbb9..5dbf800 100644
--- a/src/server/game/Entities/Unit/Unit.h
+++ b/src/server/game/Entities/Unit/Unit.h
@@ -493,6 +493,7 @@ enum UnitState
UNIT_STATE_FLEEING_MOVE = 0x02000000,
UNIT_STATE_CHASE_MOVE = 0x04000000,
UNIT_STATE_FOLLOW_MOVE = 0x08000000,
+ UNIT_STATE_IGNORE_PATHFINDING = 0x10000000, // do not use pathfinding in any MovementGenerator
UNIT_STATE_UNATTACKABLE = (UNIT_STATE_IN_FLIGHT | UNIT_STATE_ONVEHICLE),
// for real move using movegen check and stop (except unstoppable flight)
UNIT_STATE_MOVING = UNIT_STATE_ROAMING_MOVE | UNIT_STATE_CONFUSED_MOVE | UNIT_STATE_FLEEING_MOVE | UNIT_STATE_CHASE_MOVE | UNIT_STATE_FOLLOW_MOVE ,
@@ -1603,7 +1604,7 @@ class Unit : public WorldObject
void JumpTo(float speedXY, float speedZ, bool forward = true);
void JumpTo(WorldObject* obj, float speedZ);
- void MonsterMoveWithSpeed(float x, float y, float z, float speed);
+ void MonsterMoveWithSpeed(float x, float y, float z, float speed, bool generatePath = false, bool forceDestination = false);
//void SetFacing(float ori, WorldObject* obj = NULL);
//void SendMonsterMove(float NewPosX, float NewPosY, float NewPosZ, uint8 type, uint32 MovementFlags, uint32 Time, Player* player = NULL);
void SendMovementFlagUpdate(bool self = false);
diff --git a/src/server/game/Entities/Vehicle/Vehicle.cpp b/src/server/game/Entities/Vehicle/Vehicle.cpp
index c81f9af..502ec03 100644
--- a/src/server/game/Entities/Vehicle/Vehicle.cpp
+++ b/src/server/game/Entities/Vehicle/Vehicle.cpp
@@ -366,7 +366,7 @@ bool Vehicle::AddPassenger(Unit* unit, int8 seatId)
unit->SendClearTarget(); // SMSG_BREAK_TARGET
unit->SetControlled(true, UNIT_STATE_ROOT); // SMSG_FORCE_ROOT - In some cases we send SMSG_SPLINE_MOVE_ROOT here (for creatures)
// also adds MOVEMENTFLAG_ROOT
- Movement::MoveSplineInit init(*unit);
+ Movement::MoveSplineInit init(unit);
init.DisableTransportPathTransformations();
init.MoveTo(veSeat->m_attachmentOffsetX, veSeat->m_attachmentOffsetY, veSeat->m_attachmentOffsetZ);
init.SetFacing(0.0f);
diff --git a/src/server/game/Handlers/MovementHandler.cpp b/src/server/game/Handlers/MovementHandler.cpp
index 8c5594a..9ad7077 100644
--- a/src/server/game/Handlers/MovementHandler.cpp
+++ b/src/server/game/Handlers/MovementHandler.cpp
@@ -126,7 +126,7 @@ void WorldSession::HandleMoveWorldportAckOpcode()
{
// short preparations to continue flight
FlightPathMovementGenerator* flight = (FlightPathMovementGenerator*)(GetPlayer()->GetMotionMaster()->top());
- flight->Initialize(*GetPlayer());
+ flight->Initialize(GetPlayer());
return;
}
diff --git a/src/server/game/Handlers/PetHandler.cpp b/src/server/game/Handlers/PetHandler.cpp
index 5e43462..6bcbcbb 100644
--- a/src/server/game/Handlers/PetHandler.cpp
+++ b/src/server/game/Handlers/PetHandler.cpp
@@ -198,13 +198,6 @@ void WorldSession::HandlePetActionHelper(Unit* pet, uint64 guid1, uint32 spellid
if (!owner->IsValidAttackTarget(TargetUnit))
return;
- // Not let attack through obstructions
- if (sWorld->getBoolConfig(CONFIG_PET_LOS))
- {
- if (!pet->IsWithinLOSInMap(TargetUnit))
- return;
- }
-
pet->ClearUnitState(UNIT_STATE_FOLLOW);
// This is true if pet has no target or has target but targets differs.
if (pet->getVictim() != TargetUnit || (pet->getVictim() == TargetUnit && !pet->GetCharmInfo()->IsCommandAttack()))
diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp
index 2c8da44..3ef1689 100644
--- a/src/server/game/Maps/Map.cpp
+++ b/src/server/game/Maps/Map.cpp
@@ -18,6 +18,7 @@
#include "Map.h"
#include "Battleground.h"
+#include "MMapFactory.h"
#include "CellImpl.h"
#include "DynamicTree.h"
#include "GridNotifiers.h"
@@ -43,7 +44,7 @@ union u_map_magic
};
u_map_magic MapMagic = { {'M','A','P','S'} };
-u_map_magic MapVersionMagic = { {'v','1','.','2'} };
+u_map_magic MapVersionMagic = { {'v','1','.','3'} };
u_map_magic MapAreaMagic = { {'A','R','E','A'} };
u_map_magic MapHeightMagic = { {'M','H','G','T'} };
u_map_magic MapLiquidMagic = { {'M','L','I','Q'} };
@@ -71,6 +72,8 @@ Map::~Map()
if (!m_scriptSchedule.empty())
sScriptMgr->DecreaseScheduledScriptCount(m_scriptSchedule.size());
+
+ MMAP::MMapFactory::createOrGetMMapManager()->unloadMapInstance(GetId(), i_InstanceId);
}
bool Map::ExistMap(uint32 mapid, int gx, int gy)
@@ -119,6 +122,16 @@ bool Map::ExistVMap(uint32 mapid, int gx, int gy)
return true;
}
+void Map::LoadMMap(int gx, int gy)
+{
+ bool mmapLoadResult = MMAP::MMapFactory::createOrGetMMapManager()->loadMap((sWorld->GetDataPath() + "mmaps").c_str(), GetId(), gx, gy);
+
+ if (mmapLoadResult)
+ sLog->outInfo(LOG_FILTER_MAPS, "MMAP loaded name:%s, id:%d, x:%d, y:%d (mmap rep.: x:%d, y:%d)", GetMapName(), GetId(), gx, gy, gx, gy);
+ else
+ sLog->outInfo(LOG_FILTER_MAPS, "Could not load MMAP name:%s, id:%d, x:%d, y:%d (mmap rep.: x:%d, y:%d)", GetMapName(), GetId(), gx, gy, gx, gy);
+}
+
void Map::LoadVMap(int gx, int gy)
{
// x and y are swapped !!
@@ -167,18 +180,16 @@ void Map::LoadMap(int gx, int gy, bool reload)
}
// map file name
- char *tmp=NULL;
- int len = sWorld->GetDataPath().length()+strlen("maps/%03u%02u%02u.map")+1;
+ char* tmp = NULL;
+ int len = sWorld->GetDataPath().length() + strlen("maps/%03u%02u%02u.map") + 1;
tmp = new char[len];
- snprintf(tmp, len, (char *)(sWorld->GetDataPath()+"maps/%03u%02u%02u.map").c_str(), GetId(), gx, gy);
+ snprintf(tmp, len, (char *)(sWorld->GetDataPath() + "maps/%03u%02u%02u.map").c_str(), GetId(), gx, gy);
sLog->outInfo(LOG_FILTER_MAPS, "Loading map %s", tmp);
// loading data
GridMaps[gx][gy] = new GridMap();
if (!GridMaps[gx][gy]->loadData(tmp))
- {
sLog->outError(LOG_FILTER_MAPS, "Error loading map file: \n %s\n", tmp);
- }
- delete [] tmp;
+ delete[] tmp;
sScriptMgr->OnLoadGridMap(this, GridMaps[gx][gy], gx, gy);
}
@@ -186,8 +197,12 @@ void Map::LoadMap(int gx, int gy, bool reload)
void Map::LoadMapAndVMap(int gx, int gy)
{
LoadMap(gx, gy);
+ // Only load the data for the base map
if (i_InstanceId == 0)
- LoadVMap(gx, gy); // Only load the data for the base map
+ {
+ LoadVMap(gx, gy);
+ LoadMMap(gx, gy);
+ }
}
void Map::InitStateMachine()
@@ -998,8 +1013,8 @@ bool Map::UnloadGrid(NGridType& ngrid, bool unloadAll)
GridMaps[gx][gy]->unloadData();
delete GridMaps[gx][gy];
}
- // x and y are swapped
VMAP::VMapFactory::createOrGetVMapManager()->unloadMap(GetId(), gx, gy);
+ MMAP::MMapFactory::createOrGetMMapManager()->unloadMap(GetId(), gx, gy);
}
else
((MapInstanced*)m_parentMap)->RemoveGridMapReference(GridCoord(gx, gy));
@@ -1071,7 +1086,7 @@ GridMap::~GridMap()
unloadData();
}
-bool GridMap::loadData(char *filename)
+bool GridMap::loadData(char* filename)
{
// Unload old data if exist
unloadData();
diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h
index a2ce227..4bf2544 100644
--- a/src/server/game/Maps/Map.h
+++ b/src/server/game/Maps/Map.h
@@ -76,6 +76,8 @@ struct map_fileheader
uint32 heightMapSize;
uint32 liquidMapOffset;
uint32 liquidMapSize;
+ uint32 holesOffset;
+ uint32 holesSize;
};
#define MAP_AREA_NO_AREA 0x0001
@@ -479,6 +481,7 @@ class Map : public GridRefManager<NGridType>
void LoadMapAndVMap(int gx, int gy);
void LoadVMap(int gx, int gy);
void LoadMap(int gx, int gy, bool reload = false);
+ void LoadMMap(int gx, int gy);
GridMap* GetGrid(float x, float y);
void SetTimer(uint32 t) { i_gridExpiry = t < MIN_GRID_DELAY ? MIN_GRID_DELAY : t; }
diff --git a/src/server/game/Maps/MapInstanced.cpp b/src/server/game/Maps/MapInstanced.cpp
index 9ca8155..f45105c 100644
--- a/src/server/game/Maps/MapInstanced.cpp
+++ b/src/server/game/Maps/MapInstanced.cpp
@@ -21,6 +21,7 @@
#include "MapManager.h"
#include "Battleground.h"
#include "VMapFactory.h"
+#include "MMapFactory.h"
#include "InstanceSaveMgr.h"
#include "World.h"
#include "Group.h"
@@ -261,6 +262,7 @@ bool MapInstanced::DestroyInstance(InstancedMaps::iterator &itr)
if (m_InstancedMaps.size() <= 1 && sWorld->getBoolConfig(CONFIG_GRID_UNLOAD))
{
VMAP::VMapFactory::createOrGetVMapManager()->unloadMap(itr->second->GetId());
+ MMAP::MMapFactory::createOrGetMMapManager()->unloadMap(itr->second->GetId());
// in that case, unload grids of the base map, too
// so in the next map creation, (EnsureGridCreated actually) VMaps will be reloaded
Map::UnloadAll();
diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h
index e3cf975..283025c 100644
--- a/src/server/game/Miscellaneous/SharedDefines.h
+++ b/src/server/game/Miscellaneous/SharedDefines.h
@@ -19,6 +19,7 @@
#ifndef TRINITY_SHAREDDEFINES_H
#define TRINITY_SHAREDDEFINES_H
+#include "DetourNavMesh.h"
#include "Define.h"
#include <cassert>
@@ -3532,4 +3533,33 @@ enum PartyResult
ERR_PARTY_LFG_TELEPORT_IN_COMBAT = 30
};
+#define MMAP_MAGIC 0x4d4d4150 // 'MMAP'
+#define MMAP_VERSION 3
+
+struct MmapTileHeader
+{
+ uint32 mmapMagic;
+ uint32 dtVersion;
+ uint32 mmapVersion;
+ uint32 size;
+ bool usesLiquids : 1;
+
+ MmapTileHeader() : mmapMagic(MMAP_MAGIC), dtVersion(DT_NAVMESH_VERSION),
+ mmapVersion(MMAP_VERSION), size(0), usesLiquids(true) {}
+};
+
+enum NavTerrain
+{
+ NAV_EMPTY = 0x00,
+ NAV_GROUND = 0x01,
+ NAV_MAGMA = 0x02,
+ NAV_SLIME = 0x04,
+ NAV_WATER = 0x08,
+ NAV_UNUSED1 = 0x10,
+ NAV_UNUSED2 = 0x20,
+ NAV_UNUSED3 = 0x40,
+ NAV_UNUSED4 = 0x80
+ // we only have 8 bits
+};
+
#endif
diff --git a/src/server/game/Movement/MotionMaster.cpp b/src/server/game/Movement/MotionMaster.cpp
index 4f45044..600165f 100644
--- a/src/server/game/Movement/MotionMaster.cpp
+++ b/src/server/game/Movement/MotionMaster.cpp
@@ -87,7 +87,7 @@ void MotionMaster::UpdateMotion(uint32 diff)
ASSERT(!empty());
_cleanFlag |= MMCF_UPDATE;
- if (!top()->Update(*_owner, diff))
+ if (!top()->Update(_owner, diff))
{
_cleanFlag &= ~MMCF_UPDATE;
MovementExpired();
@@ -111,7 +111,7 @@ void MotionMaster::UpdateMotion(uint32 diff)
else if (needInitTop())
InitTop();
else if (_cleanFlag & MMCF_RESET)
- top()->Reset(*_owner);
+ top()->Reset(_owner);
_cleanFlag &= ~MMCF_RESET;
}
@@ -132,7 +132,7 @@ void MotionMaster::DirectClean(bool reset)
if (needInitTop())
InitTop();
else if (reset)
- top()->Reset(*_owner);
+ top()->Reset(_owner);
}
void MotionMaster::DelayedClean()
@@ -163,7 +163,7 @@ void MotionMaster::DirectExpire(bool reset)
else if (needInitTop())
InitTop();
else if (reset)
- top()->Reset(*_owner);
+ top()->Reset(_owner);
}
void MotionMaster::DelayedExpire()
@@ -199,19 +199,19 @@ void MotionMaster::MoveTargetedHome()
{
Clear(false);
- if (_owner->GetTypeId()==TYPEID_UNIT && !((Creature*)_owner)->GetCharmerOrOwnerGUID())
+ if (_owner->GetTypeId() == TYPEID_UNIT && !_owner->ToCreature()->GetCharmerOrOwnerGUID())
{
sLog->outDebug(LOG_FILTER_GENERAL, "Creature (Entry: %u GUID: %u) targeted home", _owner->GetEntry(), _owner->GetGUIDLow());
Mutate(new HomeMovementGenerator<Creature>(), MOTION_SLOT_ACTIVE);
}
- else if (_owner->GetTypeId()==TYPEID_UNIT && ((Creature*)_owner)->GetCharmerOrOwnerGUID())
+ else if (_owner->GetTypeId() == TYPEID_UNIT && _owner->ToCreature()->GetCharmerOrOwnerGUID())
{
sLog->outDebug(LOG_FILTER_GENERAL, "Pet or controlled creature (Entry: %u GUID: %u) targeting home", _owner->GetEntry(), _owner->GetGUIDLow());
- Unit *target = ((Creature*)_owner)->GetCharmerOrOwner();
+ Unit* target = _owner->ToCreature()->GetCharmerOrOwner();
if (target)
{
sLog->outDebug(LOG_FILTER_GENERAL, "Following %s (GUID: %u)", target->GetTypeId() == TYPEID_PLAYER ? "player" : "creature", target->GetTypeId() == TYPEID_PLAYER ? target->GetGUIDLow() : ((Creature*)target)->GetDBTableGUIDLow());
- Mutate(new FollowMovementGenerator<Creature>(*target,PET_FOLLOW_DIST,PET_FOLLOW_ANGLE), MOTION_SLOT_ACTIVE);
+ Mutate(new FollowMovementGenerator<Creature>(target, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE), MOTION_SLOT_ACTIVE);
}
}
else
@@ -248,7 +248,7 @@ void MotionMaster::MoveChase(Unit* target, float dist, float angle)
_owner->GetGUIDLow(),
target->GetTypeId() == TYPEID_PLAYER ? "player" : "creature",
target->GetTypeId() == TYPEID_PLAYER ? target->GetGUIDLow() : target->ToCreature()->GetDBTableGUIDLow());
- Mutate(new ChaseMovementGenerator<Player>(*target,dist,angle), MOTION_SLOT_ACTIVE);
+ Mutate(new ChaseMovementGenerator<Player>(target, dist, angle), MOTION_SLOT_ACTIVE);
}
else
{
@@ -256,7 +256,7 @@ void MotionMaster::MoveChase(Unit* target, float dist, float angle)
_owner->GetEntry(), _owner->GetGUIDLow(),
target->GetTypeId() == TYPEID_PLAYER ? "player" : "creature",
target->GetTypeId() == TYPEID_PLAYER ? target->GetGUIDLow() : target->ToCreature()->GetDBTableGUIDLow());
- Mutate(new ChaseMovementGenerator<Creature>(*target,dist,angle), MOTION_SLOT_ACTIVE);
+ Mutate(new ChaseMovementGenerator<Creature>(target, dist, angle), MOTION_SLOT_ACTIVE);
}
}
@@ -272,7 +272,7 @@ void MotionMaster::MoveFollow(Unit* target, float dist, float angle, MovementSlo
sLog->outDebug(LOG_FILTER_GENERAL, "Player (GUID: %u) follow to %s (GUID: %u)", _owner->GetGUIDLow(),
target->GetTypeId() == TYPEID_PLAYER ? "player" : "creature",
target->GetTypeId() == TYPEID_PLAYER ? target->GetGUIDLow() : target->ToCreature()->GetDBTableGUIDLow());
- Mutate(new FollowMovementGenerator<Player>(*target,dist,angle), slot);
+ Mutate(new FollowMovementGenerator<Player>(target, dist, angle), slot);
}
else
{
@@ -280,22 +280,22 @@ void MotionMaster::MoveFollow(Unit* target, float dist, float angle, MovementSlo
_owner->GetEntry(), _owner->GetGUIDLow(),
target->GetTypeId() == TYPEID_PLAYER ? "player" : "creature",
target->GetTypeId() == TYPEID_PLAYER ? target->GetGUIDLow() : target->ToCreature()->GetDBTableGUIDLow());
- Mutate(new FollowMovementGenerator<Creature>(*target,dist,angle), slot);
+ Mutate(new FollowMovementGenerator<Creature>(target, dist, angle), slot);
}
}
-void MotionMaster::MovePoint(uint32 id, float x, float y, float z)
+void MotionMaster::MovePoint(uint32 id, float x, float y, float z, bool generatePath)
{
if (_owner->GetTypeId() == TYPEID_PLAYER)
{
sLog->outDebug(LOG_FILTER_GENERAL, "Player (GUID: %u) targeted point (Id: %u X: %f Y: %f Z: %f)", _owner->GetGUIDLow(), id, x, y, z);
- Mutate(new PointMovementGenerator<Player>(id, x, y, z), MOTION_SLOT_ACTIVE);
+ Mutate(new PointMovementGenerator<Player>(id, x, y, z, generatePath), MOTION_SLOT_ACTIVE);
}
else
{
sLog->outDebug(LOG_FILTER_GENERAL, "Creature (Entry: %u GUID: %u) targeted point (ID: %u X: %f Y: %f Z: %f)",
_owner->GetEntry(), _owner->GetGUIDLow(), id, x, y, z);
- Mutate(new PointMovementGenerator<Creature>(id, x, y, z), MOTION_SLOT_ACTIVE);
+ Mutate(new PointMovementGenerator<Creature>(id, x, y, z, generatePath), MOTION_SLOT_ACTIVE);
}
}
@@ -306,7 +306,7 @@ void MotionMaster::MoveLand(uint32 id, Position const& pos)
sLog->outDebug(LOG_FILTER_GENERAL, "Creature (Entry: %u) landing point (ID: %u X: %f Y: %f Z: %f)", _owner->GetEntry(), id, x, y, z);
- Movement::MoveSplineInit init(*_owner);
+ Movement::MoveSplineInit init(_owner);
init.MoveTo(x,y,z);
init.SetAnimation(Movement::ToGround);
init.Launch();
@@ -320,7 +320,7 @@ void MotionMaster::MoveTakeoff(uint32 id, Position const& pos)
sLog->outDebug(LOG_FILTER_GENERAL, "Creature (Entry: %u) landing point (ID: %u X: %f Y: %f Z: %f)", _owner->GetEntry(), id, x, y, z);
- Movement::MoveSplineInit init(*_owner);
+ Movement::MoveSplineInit init(_owner);
init.MoveTo(x,y,z);
init.SetAnimation(Movement::ToFly);
init.Launch();
@@ -340,8 +340,8 @@ void MotionMaster::MoveKnockbackFrom(float srcX, float srcY, float speedXY, floa
_owner->GetNearPoint(_owner, x, y, z, _owner->GetObjectSize(), dist, _owner->GetAngle(srcX, srcY) + M_PI);
- Movement::MoveSplineInit init(*_owner);
- init.MoveTo(x,y,z);
+ Movement::MoveSplineInit init(_owner);
+ init.MoveTo(x, y, z);
init.SetParabolic(max_height,0);
init.SetOrientationFixed(true);
init.SetVelocity(speedXY);
@@ -370,8 +370,8 @@ void MotionMaster::MoveJump(float x, float y, float z, float speedXY, float spee
float moveTimeHalf = speedZ / Movement::gravity;
float max_height = -Movement::computeFallElevation(moveTimeHalf,false,-speedZ);
- Movement::MoveSplineInit init(*_owner);
- init.MoveTo(x,y,z);
+ Movement::MoveSplineInit init(_owner);
+ init.MoveTo(x, y, z, false);
init.SetParabolic(max_height,0);
init.SetVelocity(speedXY);
init.Launch();
@@ -399,14 +399,14 @@ void MotionMaster::MoveFall(uint32 id /*=0*/)
_owner->m_movementInfo.SetFallTime(0);
}
- Movement::MoveSplineInit init(*_owner);
- init.MoveTo(_owner->GetPositionX(), _owner->GetPositionY(), tz);
+ Movement::MoveSplineInit init(_owner);
+ init.MoveTo(_owner->GetPositionX(), _owner->GetPositionY(), tz, false);
init.SetFall();
init.Launch();
Mutate(new EffectMovementGenerator(id), MOTION_SLOT_CONTROLLED);
}
-void MotionMaster::MoveCharge(float x, float y, float z, float speed, uint32 id)
+void MotionMaster::MoveCharge(float x, float y, float z, float speed, uint32 id, bool generatePath)
{
if (Impl[MOTION_SLOT_CONTROLLED] && Impl[MOTION_SLOT_CONTROLLED]->GetMovementGeneratorType() != DISTRACT_MOTION_TYPE)
return;
@@ -414,16 +414,28 @@ void MotionMaster::MoveCharge(float x, float y, float z, float speed, uint32 id)
if (_owner->GetTypeId() == TYPEID_PLAYER)
{
sLog->outDebug(LOG_FILTER_GENERAL, "Player (GUID: %u) charge point (X: %f Y: %f Z: %f)", _owner->GetGUIDLow(), x, y, z);
- Mutate(new PointMovementGenerator<Player>(id, x, y, z, speed), MOTION_SLOT_CONTROLLED);
+ Mutate(new PointMovementGenerator<Player>(id, x, y, z, generatePath, speed), MOTION_SLOT_CONTROLLED);
}
else
{
sLog->outDebug(LOG_FILTER_GENERAL, "Creature (Entry: %u GUID: %u) charge point (X: %f Y: %f Z: %f)",
_owner->GetEntry(), _owner->GetGUIDLow(), x, y, z);
- Mutate(new PointMovementGenerator<Creature>(id, x, y, z, speed), MOTION_SLOT_CONTROLLED);
+ Mutate(new PointMovementGenerator<Creature>(id, x, y, z, generatePath, speed), MOTION_SLOT_CONTROLLED);
}
}
+void MotionMaster::MoveCharge(PathGenerator path, float speed, uint32 id)
+{
+ Vector3 dest = path.GetActualEndPosition();
+
+ MoveCharge(dest.x, dest.y, dest.z);
+
+ Movement::MoveSplineInit init(_owner);
+ init.MovebyPath(path.GetPath());
+ init.SetVelocity(speed);
+ init.Launch();
+}
+
void MotionMaster::MoveSeekAssistance(float x, float y, float z)
{
if (_owner->GetTypeId() == TYPEID_PLAYER)
@@ -546,7 +558,7 @@ void MotionMaster::Mutate(MovementGenerator *m, MovementSlot slot)
else
{
_needInit[slot] = false;
- m->Initialize(*_owner);
+ m->Initialize(_owner);
}
}
@@ -614,7 +626,7 @@ MovementGeneratorType MotionMaster::GetMotionSlotType(int slot) const
void MotionMaster::InitTop()
{
- top()->Initialize(*_owner);
+ top()->Initialize(_owner);
_needInit[_top] = false;
}
@@ -622,7 +634,7 @@ void MotionMaster::DirectDelete(_Ty curr)
{
if (isStatic(curr))
return;
- curr->Finalize(*_owner);
+ curr->Finalize(_owner);
delete curr;
}
@@ -639,9 +651,9 @@ void MotionMaster::DelayedDelete(_Ty curr)
bool MotionMaster::GetDestination(float &x, float &y, float &z)
{
if (_owner->movespline->Finalized())
- return false;
+ return false;
- const G3D::Vector3& dest = _owner->movespline->FinalDestination();
+ G3D::Vector3 const& dest = _owner->movespline->FinalDestination();
x = dest.x;
y = dest.y;
z = dest.z;
diff --git a/src/server/game/Movement/MotionMaster.h b/src/server/game/Movement/MotionMaster.h
index 7037d13..b0676e0 100644
--- a/src/server/game/Movement/MotionMaster.h
+++ b/src/server/game/Movement/MotionMaster.h
@@ -26,6 +26,7 @@
class MovementGenerator;
class Unit;
+class PathGenerator;
// Creature Entry ID used for waypoints show, visible only for GMs
#define VISUAL_WAYPOINT 1
@@ -154,13 +155,14 @@ class MotionMaster //: private std::stack<MovementGenerator *>
void MoveFleeing(Unit* enemy, uint32 time = 0);
void MovePoint(uint32 id, const Position &pos)
{ MovePoint(id, pos.m_positionX, pos.m_positionY, pos.m_positionZ); }
- void MovePoint(uint32 id, float x, float y, float z);
+ void MovePoint(uint32 id, float x, float y, float z, bool generatePath = true);
// These two movement types should only be used with creatures having landing/takeoff animations
void MoveLand(uint32 id, Position const& pos);
void MoveTakeoff(uint32 id, Position const& pos);
- void MoveCharge(float x, float y, float z, float speed = SPEED_CHARGE, uint32 id = EVENT_CHARGE);
+ void MoveCharge(float x, float y, float z, float speed = SPEED_CHARGE, uint32 id = EVENT_CHARGE, bool generatePath = false);
+ void MoveCharge(PathGenerator path, float speed = SPEED_CHARGE, uint32 id = EVENT_CHARGE);
void MoveKnockbackFrom(float srcX, float srcY, float speedXY, float speedZ);
void MoveJumpTo(float angle, float speedXY, float speedZ);
void MoveJump(Position const& pos, float speedXY, float speedZ, uint32 id = EVENT_JUMP)
diff --git a/src/server/game/Movement/MovementGenerator.h b/src/server/game/Movement/MovementGenerator.h
index e586d7e..43d0316 100644
--- a/src/server/game/Movement/MovementGenerator.h
+++ b/src/server/game/Movement/MovementGenerator.h
@@ -33,42 +33,54 @@ class MovementGenerator
public:
virtual ~MovementGenerator();
- virtual void Initialize(Unit &) = 0;
- virtual void Finalize(Unit &) = 0;
+ virtual void Initialize(Unit*) = 0;
+ virtual void Finalize(Unit*) = 0;
- virtual void Reset(Unit &) = 0;
+ virtual void Reset(Unit*) = 0;
- virtual bool Update(Unit &, uint32 time_diff) = 0;
+ virtual bool Update(Unit*, uint32 time_diff) = 0;
virtual MovementGeneratorType GetMovementGeneratorType() = 0;
virtual void unitSpeedChanged() { }
+
+ // used by Evade code for select point to evade with expected restart default movement
+ virtual bool GetResetPosition(Unit*, float& /*x*/, float& /*y*/, float& /*z*/) { return false; }
};
template<class T, class D>
class MovementGeneratorMedium : public MovementGenerator
{
public:
- void Initialize(Unit &u)
+ void Initialize(Unit* u)
{
//u->AssertIsType<T>();
- (static_cast<D*>(this))->DoInitialize(*((T*)&u));
+ (static_cast<D*>(this))->Initialize(static_cast<T*>(u));
}
- void Finalize(Unit &u)
+
+ void Finalize(Unit* u)
{
//u->AssertIsType<T>();
- (static_cast<D*>(this))->DoFinalize(*((T*)&u));
+ (static_cast<D*>(this))->Finalize(static_cast<T*>(u));
}
- void Reset(Unit &u)
+
+ void Reset(Unit* u)
{
//u->AssertIsType<T>();
- (static_cast<D*>(this))->DoReset(*((T*)&u));
+ (static_cast<D*>(this))->Reset(static_cast<T*>(u));
}
- bool Update(Unit &u, uint32 time_diff)
+
+ bool Update(Unit* u, uint32 time_diff)
{
//u->AssertIsType<T>();
- return (static_cast<D*>(this))->DoUpdate(*((T*)&u), time_diff);
+ return (static_cast<D*>(this))->Update(static_cast<T*>(u), time_diff);
}
+ public:
+ // will not link if not overridden in the generators
+ void Initialize(T* u);
+ void Finalize(T* u);
+ void Reset(T* u);
+ bool Update(T* u, const uint32& time_diff);
};
struct SelectableMovement : public FactoryHolder<MovementGenerator, MovementGeneratorType>
diff --git a/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.cpp
dissimilarity index 70%
index f93e94f..9ccb13c 100755
--- a/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.cpp
+++ b/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.cpp
@@ -1,173 +1,130 @@
-/*
- * Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/>
- * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "Creature.h"
-#include "MapManager.h"
-#include "ConfusedMovementGenerator.h"
-#include "VMapFactory.h"
-#include "MoveSplineInit.h"
-#include "MoveSpline.h"
-#include "Player.h"
-
-#ifdef MAP_BASED_RAND_GEN
-#define rand_norm() unit.rand_norm()
-#define urand(a, b) unit.urand(a, b)
-#endif
-
-template<class T>
-void ConfusedMovementGenerator<T>::DoInitialize(T &unit)
-{
- unit.StopMoving();
- float const wander_distance = 4;
- float x = unit.GetPositionX();
- float y = unit.GetPositionY();
- float z = unit.GetPositionZ();
-
- Map const* map = unit.GetBaseMap();
-
- i_nextMove = 1;
-
- bool is_water_ok, is_land_ok;
- _InitSpecific(unit, is_water_ok, is_land_ok);
-
- for (uint8 idx = 0; idx < MAX_CONF_WAYPOINTS + 1; ++idx)
- {
- float wanderX = x + (wander_distance * (float)rand_norm() - wander_distance/2);
- float wanderY = y + (wander_distance * (float)rand_norm() - wander_distance/2);
-
- // prevent invalid coordinates generation
- Trinity::NormalizeMapCoord(wanderX);
- Trinity::NormalizeMapCoord(wanderY);
-
- if (unit.IsWithinLOS(wanderX, wanderY, z))
- {
- bool is_water = map->IsInWater(wanderX, wanderY, z);
-
- if ((is_water && !is_water_ok) || (!is_water && !is_land_ok))
- {
- //! Cannot use coordinates outside our InhabitType. Use the current or previous position.
- wanderX = idx > 0 ? i_waypoints[idx-1][0] : x;
- wanderY = idx > 0 ? i_waypoints[idx-1][1] : y;
- }
- }
- else
- {
- //! Trying to access path outside line of sight. Skip this by using the current or previous position.
- wanderX = idx > 0 ? i_waypoints[idx-1][0] : x;
- wanderY = idx > 0 ? i_waypoints[idx-1][1] : y;
- }
-
- unit.UpdateAllowedPositionZ(wanderX, wanderY, z);
-
- //! Positions are fine - apply them to this waypoint
- i_waypoints[idx][0] = wanderX;
- i_waypoints[idx][1] = wanderY;
- i_waypoints[idx][2] = z;
- }
-
- unit.StopMoving();
- unit.SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_CONFUSED);
- unit.AddUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_CONFUSED_MOVE);
-}
-
-template<>
-void ConfusedMovementGenerator<Creature>::_InitSpecific(Creature &creature, bool &is_water_ok, bool &is_land_ok)
-{
- is_water_ok = creature.canSwim();
- is_land_ok = creature.canWalk();
-}
-
-template<>
-void ConfusedMovementGenerator<Player>::_InitSpecific(Player &, bool &is_water_ok, bool &is_land_ok)
-{
- is_water_ok = true;
- is_land_ok = true;
-}
-
-template<class T>
-void ConfusedMovementGenerator<T>::DoReset(T &unit)
-{
- i_nextMove = 1;
- i_nextMoveTime.Reset(0);
- unit.StopMoving();
- unit.AddUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_CONFUSED_MOVE);
-}
-
-template<class T>
-bool ConfusedMovementGenerator<T>::DoUpdate(T &unit, uint32 diff)
-{
- if (unit.HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED | UNIT_STATE_DISTRACTED))
- return true;
-
- if (i_nextMoveTime.Passed())
- {
- // currently moving, update location
- unit.AddUnitState(UNIT_STATE_CONFUSED_MOVE);
-
- if (unit.movespline->Finalized())
- {
- i_nextMove = urand(1, MAX_CONF_WAYPOINTS);
- i_nextMoveTime.Reset(urand(500, 1200)); // Guessed
- }
- }
- else
- {
- // waiting for next move
- i_nextMoveTime.Update(diff);
- if (i_nextMoveTime.Passed())
- {
- // start moving
- unit.AddUnitState(UNIT_STATE_CONFUSED_MOVE);
-
- ASSERT(i_nextMove <= MAX_CONF_WAYPOINTS);
- float x = i_waypoints[i_nextMove][0];
- float y = i_waypoints[i_nextMove][1];
- float z = i_waypoints[i_nextMove][2];
- Movement::MoveSplineInit init(unit);
- init.MoveTo(x, y, z);
- init.SetWalk(true);
- init.Launch();
- }
- }
-
- return true;
-}
-
-template<>
-void ConfusedMovementGenerator<Player>::DoFinalize(Player &unit)
-{
- unit.RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_CONFUSED);
- unit.ClearUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_CONFUSED_MOVE);
-}
-
-template<>
-void ConfusedMovementGenerator<Creature>::DoFinalize(Creature &unit)
-{
- unit.RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_CONFUSED);
- unit.ClearUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_CONFUSED_MOVE);
- if (unit.getVictim())
- unit.SetTarget(unit.getVictim()->GetGUID());
-}
-
-template void ConfusedMovementGenerator<Player>::DoInitialize(Player &player);
-template void ConfusedMovementGenerator<Creature>::DoInitialize(Creature &creature);
-template void ConfusedMovementGenerator<Player>::DoReset(Player &player);
-template void ConfusedMovementGenerator<Creature>::DoReset(Creature &creature);
-template bool ConfusedMovementGenerator<Player>::DoUpdate(Player &player, uint32 diff);
-template bool ConfusedMovementGenerator<Creature>::DoUpdate(Creature &creature, uint32 diff);
-
+/*
+ * Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "Creature.h"
+#include "MapManager.h"
+#include "ConfusedMovementGenerator.h"
+#include "PathGenerator.h"
+#include "VMapFactory.h"
+#include "MoveSplineInit.h"
+#include "MoveSpline.h"
+#include "Player.h"
+
+#ifdef MAP_BASED_RAND_GEN
+#define rand_norm() unit.rand_norm()
+#define urand(a, b) unit.urand(a, b)
+#endif
+
+template<class T>
+void ConfusedMovementGenerator<T>::Initialize(T* unit)
+{
+ unit->AddUnitState(UNIT_STATE_CONFUSED);
+ unit->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_CONFUSED);
+ unit->GetPosition(i_x, i_y, i_z);
+
+ if (!unit->isAlive() || unit->IsStopped())
+ return;
+
+ unit->StopMoving();
+ unit->AddUnitState(UNIT_STATE_CONFUSED_MOVE);
+}
+
+template<class T>
+void ConfusedMovementGenerator<T>::Reset(T* unit)
+{
+ i_nextMoveTime.Reset(0);
+
+ if (!unit->isAlive() || unit->IsStopped())
+ return;
+
+ unit->StopMoving();
+ unit->AddUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_CONFUSED_MOVE);
+}
+
+template<class T>
+bool ConfusedMovementGenerator<T>::Update(T* unit, uint32 diff)
+{
+ if (unit->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED | UNIT_STATE_DISTRACTED))
+ return true;
+
+ if (i_nextMoveTime.Passed())
+ {
+ // currently moving, update location
+ unit->AddUnitState(UNIT_STATE_CONFUSED_MOVE);
+
+ if (unit->movespline->Finalized())
+ i_nextMoveTime.Reset(urand(800, 1500));
+ }
+ else
+ {
+ // waiting for next move
+ i_nextMoveTime.Update(diff);
+ if (i_nextMoveTime.Passed())
+ {
+ // start moving
+ unit->AddUnitState(UNIT_STATE_CONFUSED_MOVE);
+
+ float dest = 4.0f * (float)rand_norm() - 2.0f;
+
+ Position pos;
+ pos.Relocate(i_x, i_y, i_z);
+ unit->MovePositionToFirstCollision(pos, dest, 0.0f);
+
+ PathGenerator path(unit);
+ path.SetPathLengthLimit(30.0f);
+ bool result = path.CalculatePath(pos.m_positionX, pos.m_positionY, pos.m_positionZ);
+ if (!result || (path.GetPathType() & PATHFIND_NOPATH))
+ {
+ i_nextMoveTime.Reset(100);
+ return true;
+ }
+
+ Movement::MoveSplineInit init(unit);
+ init.MovebyPath(path.GetPath());
+ init.SetWalk(true);
+ init.Launch();
+ }
+ }
+
+ return true;
+}
+
+template<>
+void ConfusedMovementGenerator<Player>::Finalize(Player* unit)
+{
+ unit->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_CONFUSED);
+ unit->ClearUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_CONFUSED_MOVE);
+ unit->StopMoving();
+}
+
+template<>
+void ConfusedMovementGenerator<Creature>::Finalize(Creature* unit)
+{
+ unit->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_CONFUSED);
+ unit->ClearUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_CONFUSED_MOVE);
+ if (unit->getVictim())
+ unit->SetTarget(unit->getVictim()->GetGUID());
+}
+
+template void ConfusedMovementGenerator<Player>::Initialize(Player*);
+template void ConfusedMovementGenerator<Creature>::Initialize(Creature*);
+template void ConfusedMovementGenerator<Player>::Reset(Player*);
+template void ConfusedMovementGenerator<Creature>::Reset(Creature*);
+template bool ConfusedMovementGenerator<Player>::Update(Player*, uint32 diff);
+template bool ConfusedMovementGenerator<Creature>::Update(Creature*, uint32 diff);
+
diff --git a/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.h b/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.h
index 34ce1e0..22d9bcc 100755
--- a/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.h
+++ b/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.h
@@ -22,25 +22,21 @@
#include "MovementGenerator.h"
#include "Timer.h"
-#define MAX_CONF_WAYPOINTS 24 //! Allows a twelve second confusion if i_nextMove always is the absolute minimum timer.
-
template<class T>
class ConfusedMovementGenerator : public MovementGeneratorMedium< T, ConfusedMovementGenerator<T> >
{
public:
explicit ConfusedMovementGenerator() : i_nextMoveTime(0) {}
- void DoInitialize(T &);
- void DoFinalize(T &);
- void DoReset(T &);
- bool DoUpdate(T &, uint32);
+ void Initialize(T*);
+ void Finalize(T*);
+ void Reset(T*);
+ bool Update(T*, uint32);
MovementGeneratorType GetMovementGeneratorType() { return CONFUSED_MOTION_TYPE; }
private:
- void _InitSpecific(T &, bool &, bool &);
TimeTracker i_nextMoveTime;
- float i_waypoints[MAX_CONF_WAYPOINTS+1][3];
- uint32 i_nextMove;
+ float i_x, i_y, i_z;
};
#endif
diff --git a/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.cpp
dissimilarity index 83%
index b070e21..7f9c7fa 100755
--- a/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.cpp
+++ b/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.cpp
@@ -1,421 +1,201 @@
-/*
- * Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/>
- * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "Creature.h"
-#include "CreatureAI.h"
-#include "MapManager.h"
-#include "FleeingMovementGenerator.h"
-#include "ObjectAccessor.h"
-#include "MoveSplineInit.h"
-#include "MoveSpline.h"
-#include "Player.h"
-
-#define MIN_QUIET_DISTANCE 28.0f
-#define MAX_QUIET_DISTANCE 43.0f
-
-template<class T>
-void FleeingMovementGenerator<T>::_setTargetLocation(T &owner)
-{
- if (!&owner)
- return;
-
- if (owner.HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED))
- return;
-
- if (!_setMoveData(owner))
- return;
-
- float x, y, z;
- if (!_getPoint(owner, x, y, z))
- return;
-
- owner.AddUnitState(UNIT_STATE_FLEEING_MOVE);
-
- Movement::MoveSplineInit init(owner);
- init.MoveTo(x,y,z);
- init.SetWalk(false);
- init.Launch();
-}
-
-template<class T>
-bool FleeingMovementGenerator<T>::_getPoint(T &owner, float &x, float &y, float &z)
-{
- if (!&owner)
- return false;
-
- x = owner.GetPositionX();
- y = owner.GetPositionY();
- z = owner.GetPositionZ();
-
- float temp_x, temp_y, angle;
- const Map* _map = owner.GetBaseMap();
- // primitive path-finding
- for (uint8 i = 0; i < 18; ++i)
- {
- if (i_only_forward && i > 2)
- break;
-
- float distance = 5.0f;
-
- switch (i)
- {
- case 0:
- angle = i_cur_angle;
- break;
- case 1:
- angle = i_cur_angle;
- distance /= 2;
- break;
- case 2:
- angle = i_cur_angle;
- distance /= 4;
- break;
- case 3:
- angle = i_cur_angle + static_cast<float>(M_PI/4);
- break;
- case 4:
- angle = i_cur_angle - static_cast<float>(M_PI/4);
- break;
- case 5:
- angle = i_cur_angle + static_cast<float>(M_PI/4);
- distance /= 2;
- break;
- case 6:
- angle = i_cur_angle - static_cast<float>(M_PI/4);
- distance /= 2;
- break;
- case 7:
- angle = i_cur_angle + static_cast<float>(M_PI/2);
- break;
- case 8:
- angle = i_cur_angle - static_cast<float>(M_PI/2);
- break;
- case 9:
- angle = i_cur_angle + static_cast<float>(M_PI/2);
- distance /= 2;
- break;
- case 10:
- angle = i_cur_angle - static_cast<float>(M_PI/2);
- distance /= 2;
- break;
- case 11:
- angle = i_cur_angle + static_cast<float>(M_PI/4);
- distance /= 4;
- break;
- case 12:
- angle = i_cur_angle - static_cast<float>(M_PI/4);
- distance /= 4;
- break;
- case 13:
- angle = i_cur_angle + static_cast<float>(M_PI/2);
- distance /= 4;
- break;
- case 14:
- angle = i_cur_angle - static_cast<float>(M_PI/2);
- distance /= 4;
- break;
- case 15:
- angle = i_cur_angle + static_cast<float>(3*M_PI/4);
- distance /= 2;
- break;
- case 16:
- angle = i_cur_angle - static_cast<float>(3*M_PI/4);
- distance /= 2;
- break;
- case 17:
- angle = i_cur_angle + static_cast<float>(M_PI);
- distance /= 2;
- break;
- default:
- angle = 0.0f;
- distance = 0.0f;
- break;
- }
-
- temp_x = x + distance * std::cos(angle);
- temp_y = y + distance * std::sin(angle);
- Trinity::NormalizeMapCoord(temp_x);
- Trinity::NormalizeMapCoord(temp_y);
- if (owner.IsWithinLOS(temp_x, temp_y, z))
- {
- bool is_water_now = _map->IsInWater(x,y,z);
-
- if (is_water_now && _map->IsInWater(temp_x,temp_y,z))
- {
- x = temp_x;
- y = temp_y;
- return true;
- }
- float new_z = _map->GetHeight(owner.GetPhaseMask(), temp_x, temp_y, z, true);
-
- if (new_z <= INVALID_HEIGHT)
- continue;
-
- bool is_water_next = _map->IsInWater(temp_x, temp_y, new_z);
-
- if ((is_water_now && !is_water_next && !is_land_ok) || (!is_water_now && is_water_next && !is_water_ok))
- continue;
-
- if (!(new_z - z) || distance / fabs(new_z - z) > 1.0f)
- {
- float new_z_left = _map->GetHeight(owner.GetPhaseMask(), temp_x + 1.0f* std::cos(angle+static_cast<float>(M_PI/2)),temp_y + 1.0f* std::sin(angle+static_cast<float>(M_PI/2)),z,true);
- float new_z_right = _map->GetHeight(owner.GetPhaseMask(), temp_x + 1.0f* std::cos(angle-static_cast<float>(M_PI/2)),temp_y + 1.0f* std::sin(angle-static_cast<float>(M_PI/2)),z,true);
- if (fabs(new_z_left - new_z) < 1.2f && fabs(new_z_right - new_z) < 1.2f)
- {
- x = temp_x;
- y = temp_y;
- z = new_z;
- return true;
- }
- }
- }
- }
- i_to_distance_from_caster = 0.0f;
- i_nextCheckTime.Reset(urand(500,1000));
- return false;
-}
-
-template<class T>
-bool FleeingMovementGenerator<T>::_setMoveData(T &owner)
-{
- float cur_dist_xyz = owner.GetDistance(i_caster_x, i_caster_y, i_caster_z);
-
- if (i_to_distance_from_caster > 0.0f)
- {
- if ((i_last_distance_from_caster > i_to_distance_from_caster && cur_dist_xyz < i_to_distance_from_caster) ||
- // if we reach lower distance
- (i_last_distance_from_caster > i_to_distance_from_caster && cur_dist_xyz > i_last_distance_from_caster) ||
- // if we can't be close
- (i_last_distance_from_caster < i_to_distance_from_caster && cur_dist_xyz > i_to_distance_from_caster) ||
- // if we reach bigger distance
- (cur_dist_xyz > MAX_QUIET_DISTANCE) || // if we are too far
- (i_last_distance_from_caster > MIN_QUIET_DISTANCE && cur_dist_xyz < MIN_QUIET_DISTANCE))
- // if we leave 'quiet zone'
- {
- // we are very far or too close, stopping
- i_to_distance_from_caster = 0.0f;
- i_nextCheckTime.Reset(urand(500,1000));
- return false;
- }
- else
- {
- // now we are running, continue
- i_last_distance_from_caster = cur_dist_xyz;
- return true;
- }
- }
-
- float cur_dist;
- float angle_to_caster;
-
- if (Unit* fright = ObjectAccessor::GetUnit(owner, i_frightGUID))
- {
- cur_dist = fright->GetDistance(&owner);
- if (cur_dist < cur_dist_xyz)
- {
- i_caster_x = fright->GetPositionX();
- i_caster_y = fright->GetPositionY();
- i_caster_z = fright->GetPositionZ();
- angle_to_caster = fright->GetAngle(&owner);
- }
- else
- {
- cur_dist = cur_dist_xyz;
- angle_to_caster = owner.GetAngle(i_caster_x, i_caster_y) + static_cast<float>(M_PI);
- }
- }
- else
- {
- cur_dist = cur_dist_xyz;
- angle_to_caster = owner.GetAngle(i_caster_x, i_caster_y) + static_cast<float>(M_PI);
- }
-
- // if we too close may use 'path-finding' else just stop
- i_only_forward = cur_dist >= MIN_QUIET_DISTANCE/3;
-
- //get angle and 'distance from caster' to run
- float angle;
-
- if (i_cur_angle == 0.0f && i_last_distance_from_caster == 0.0f) //just started, first time
- {
- angle = (float)rand_norm()*(1.0f - cur_dist/MIN_QUIET_DISTANCE) * static_cast<float>(M_PI/3) + (float)rand_norm()*static_cast<float>(M_PI*2/3);
- i_to_distance_from_caster = MIN_QUIET_DISTANCE;
- i_only_forward = true;
- }
- else if (cur_dist < MIN_QUIET_DISTANCE)
- {
- angle = static_cast<float>(M_PI/6) + (float)rand_norm()*static_cast<float>(M_PI*2/3);
- i_to_distance_from_caster = cur_dist*2/3 + (float)rand_norm()*(MIN_QUIET_DISTANCE - cur_dist*2/3);
- }
- else if (cur_dist > MAX_QUIET_DISTANCE)
- {
- angle = (float)rand_norm()*static_cast<float>(M_PI/3) + static_cast<float>(M_PI*2/3);
- i_to_distance_from_caster = MIN_QUIET_DISTANCE + 2.5f + (float)rand_norm()*(MAX_QUIET_DISTANCE - MIN_QUIET_DISTANCE - 2.5f);
- }
- else
- {
- angle = (float)rand_norm()*static_cast<float>(M_PI);
- i_to_distance_from_caster = MIN_QUIET_DISTANCE + 2.5f + (float)rand_norm()*(MAX_QUIET_DISTANCE - MIN_QUIET_DISTANCE - 2.5f);
- }
-
- int8 sign = (float)rand_norm() > 0.5f ? 1 : -1;
- i_cur_angle = sign*angle + angle_to_caster;
-
- // current distance
- i_last_distance_from_caster = cur_dist;
-
- return true;
-}
-
-template<class T>
-void FleeingMovementGenerator<T>::DoInitialize(T &owner)
-{
- if (!&owner)
- return;
-
- owner.SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING);
- owner.AddUnitState(UNIT_STATE_FLEEING|UNIT_STATE_FLEEING_MOVE);
-
- _Init(owner);
-
- if (Unit *fright = ObjectAccessor::GetUnit(owner, i_frightGUID))
- {
- i_caster_x = fright->GetPositionX();
- i_caster_y = fright->GetPositionY();
- i_caster_z = fright->GetPositionZ();
- }
- else
- {
- i_caster_x = owner.GetPositionX();
- i_caster_y = owner.GetPositionY();
- i_caster_z = owner.GetPositionZ();
- }
-
- i_only_forward = true;
- i_cur_angle = 0.0f;
- i_last_distance_from_caster = 0.0f;
- i_to_distance_from_caster = 0.0f;
- _setTargetLocation(owner);
-}
-
-template<>
-void FleeingMovementGenerator<Creature>::_Init(Creature &owner)
-{
- if (!&owner)
- return;
-
- //owner.SetTargetGuid(ObjectGuid());
- is_water_ok = owner.canSwim();
- is_land_ok = owner.canWalk();
-}
-
-template<>
-void FleeingMovementGenerator<Player>::_Init(Player &)
-{
- is_water_ok = true;
- is_land_ok = true;
-}
-
-template<>
-void FleeingMovementGenerator<Player>::DoFinalize(Player &owner)
-{
- owner.RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING);
- owner.ClearUnitState(UNIT_STATE_FLEEING|UNIT_STATE_FLEEING_MOVE);
- owner.StopMoving();
-}
-
-template<>
-void FleeingMovementGenerator<Creature>::DoFinalize(Creature &owner)
-{
- owner.RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING);
- owner.ClearUnitState(UNIT_STATE_FLEEING|UNIT_STATE_FLEEING_MOVE);
- if (owner.getVictim())
- owner.SetTarget(owner.getVictim()->GetGUID());
-}
-
-template<class T>
-void FleeingMovementGenerator<T>::DoReset(T &owner)
-{
- DoInitialize(owner);
-}
-
-template<class T>
-bool FleeingMovementGenerator<T>::DoUpdate(T &owner, uint32 time_diff)
-{
- if (!&owner || !owner.isAlive())
- return false;
- if (owner.HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED))
- {
- owner.ClearUnitState(UNIT_STATE_FLEEING_MOVE);
- return true;
- }
-
- i_nextCheckTime.Update(time_diff);
- if (i_nextCheckTime.Passed() && owner.movespline->Finalized())
- _setTargetLocation(owner);
-
- return true;
-}
-
-template void FleeingMovementGenerator<Player>::DoInitialize(Player &);
-template void FleeingMovementGenerator<Creature>::DoInitialize(Creature &);
-template bool FleeingMovementGenerator<Player>::_setMoveData(Player &);
-template bool FleeingMovementGenerator<Creature>::_setMoveData(Creature &);
-template bool FleeingMovementGenerator<Player>::_getPoint(Player &, float &, float &, float &);
-template bool FleeingMovementGenerator<Creature>::_getPoint(Creature &, float &, float &, float &);
-template void FleeingMovementGenerator<Player>::_setTargetLocation(Player &);
-template void FleeingMovementGenerator<Creature>::_setTargetLocation(Creature &);
-template void FleeingMovementGenerator<Player>::DoReset(Player &);
-template void FleeingMovementGenerator<Creature>::DoReset(Creature &);
-template bool FleeingMovementGenerator<Player>::DoUpdate(Player &, uint32);
-template bool FleeingMovementGenerator<Creature>::DoUpdate(Creature &, uint32);
-
-void TimedFleeingMovementGenerator::Finalize(Unit &owner)
-{
- owner.RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING);
- owner.ClearUnitState(UNIT_STATE_FLEEING|UNIT_STATE_FLEEING_MOVE);
- if (Unit* victim = owner.getVictim())
- {
- if (owner.isAlive())
- {
- owner.AttackStop();
- owner.ToCreature()->AI()->AttackStart(victim);
- }
- }
-}
-
-bool TimedFleeingMovementGenerator::Update(Unit & owner, uint32 time_diff)
-{
- if (!owner.isAlive())
- return false;
-
- if (owner.HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED))
- {
- owner.ClearUnitState(UNIT_STATE_FLEEING_MOVE);
- return true;
- }
-
- i_totalFleeTime.Update(time_diff);
- if (i_totalFleeTime.Passed())
- return false;
-
- // This calls grant-parent Update method hiden by FleeingMovementGenerator::Update(Creature &, uint32) version
- // This is done instead of casting Unit& to Creature& and call parent method, then we can use Unit directly
- return MovementGeneratorMedium< Creature, FleeingMovementGenerator<Creature> >::Update(owner, time_diff);
-}
-
+/*
+ * Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "Creature.h"
+#include "CreatureAI.h"
+#include "MapManager.h"
+#include "FleeingMovementGenerator.h"
+#include "PathGenerator.h"
+#include "ObjectAccessor.h"
+#include "MoveSplineInit.h"
+#include "MoveSpline.h"
+#include "Player.h"
+
+#define MIN_QUIET_DISTANCE 28.0f
+#define MAX_QUIET_DISTANCE 43.0f
+
+template<class T>
+void FleeingMovementGenerator<T>::_setTargetLocation(T* owner)
+{
+ if (!owner)
+ return;
+
+ if (owner->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED))
+ return;
+
+ owner->AddUnitState(UNIT_STATE_FLEEING_MOVE);
+
+ float x, y, z;
+ _getPoint(owner, x, y, z);
+
+ PathGenerator path(owner);
+ path.SetPathLengthLimit(30.0f);
+ bool result = path.CalculatePath(x, y, z);
+ if (!result || (path.GetPathType() & PATHFIND_NOPATH))
+ {
+ i_nextCheckTime.Reset(100);
+ return;
+ }
+
+ Movement::MoveSplineInit init(owner);
+ init.MovebyPath(path.GetPath());
+ init.SetWalk(false);
+ int32 traveltime = init.Launch();
+ i_nextCheckTime.Reset(traveltime + urand(800, 1500));
+}
+
+template<class T>
+void FleeingMovementGenerator<T>::_getPoint(T* owner, float &x, float &y, float &z)
+{
+ float dist_from_caster, angle_to_caster;
+ if (Unit* fright = ObjectAccessor::GetUnit(*owner, i_frightGUID))
+ {
+ dist_from_caster = fright->GetDistance(owner);
+ if (dist_from_caster > 0.2f)
+ angle_to_caster = fright->GetAngle(owner);
+ else
+ angle_to_caster = frand(0, 2 * static_cast<float>(M_PI));
+ }
+ else
+ {
+ dist_from_caster = 0.0f;
+ angle_to_caster = frand(0, 2 * static_cast<float>(M_PI));
+ }
+
+ float dist, angle;
+ if (dist_from_caster < MIN_QUIET_DISTANCE)
+ {
+ dist = frand(0.4f, 1.3f)*(MIN_QUIET_DISTANCE - dist_from_caster);
+ angle = angle_to_caster + frand(-static_cast<float>(M_PI)/8, static_cast<float>(M_PI)/8);
+ }
+ else if (dist_from_caster > MAX_QUIET_DISTANCE)
+ {
+ dist = frand(0.4f, 1.0f)*(MAX_QUIET_DISTANCE - MIN_QUIET_DISTANCE);
+ angle = -angle_to_caster + frand(-static_cast<float>(M_PI)/4, static_cast<float>(M_PI)/4);
+ }
+ else // we are inside quiet range
+ {
+ dist = frand(0.6f, 1.2f)*(MAX_QUIET_DISTANCE - MIN_QUIET_DISTANCE);
+ angle = frand(0, 2*static_cast<float>(M_PI));
+ }
+
+ Position pos;
+ owner->GetFirstCollisionPosition(pos, dist, angle);
+ x = pos.m_positionX;
+ y = pos.m_positionY;
+ z = pos.m_positionZ;
+}
+
+template<class T>
+void FleeingMovementGenerator<T>::Initialize(T* owner)
+{
+ if (!owner)
+ return;
+
+ owner->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING);
+ owner->AddUnitState(UNIT_STATE_FLEEING | UNIT_STATE_FLEEING_MOVE);
+ _setTargetLocation(owner);
+}
+
+template<>
+void FleeingMovementGenerator<Player>::Finalize(Player* owner)
+{
+ owner->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING);
+ owner->ClearUnitState(UNIT_STATE_FLEEING | UNIT_STATE_FLEEING_MOVE);
+ owner->StopMoving();
+}
+
+template<>
+void FleeingMovementGenerator<Creature>::Finalize(Creature* owner)
+{
+ owner->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING);
+ owner->ClearUnitState(UNIT_STATE_FLEEING|UNIT_STATE_FLEEING_MOVE);
+ if (owner->getVictim())
+ owner->SetTarget(owner->getVictim()->GetGUID());
+}
+
+template<class T>
+void FleeingMovementGenerator<T>::Reset(T* owner)
+{
+ Initialize(owner);
+}
+
+template<class T>
+bool FleeingMovementGenerator<T>::Update(T* owner, uint32 time_diff)
+{
+ if (!owner || !owner->isAlive())
+ return false;
+
+ if (owner->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED))
+ {
+ owner->ClearUnitState(UNIT_STATE_FLEEING_MOVE);
+ return true;
+ }
+
+ i_nextCheckTime.Update(time_diff);
+ if (i_nextCheckTime.Passed() && owner->movespline->Finalized())
+ _setTargetLocation(owner);
+
+ return true;
+}
+
+template void FleeingMovementGenerator<Player>::Initialize(Player*);
+template void FleeingMovementGenerator<Creature>::Initialize(Creature*);
+template void FleeingMovementGenerator<Player>::_getPoint(Player*, float&, float&, float&);
+template void FleeingMovementGenerator<Creature>::_getPoint(Creature*, float&, float&, float&);
+template void FleeingMovementGenerator<Player>::_setTargetLocation(Player*);
+template void FleeingMovementGenerator<Creature>::_setTargetLocation(Creature*);
+template void FleeingMovementGenerator<Player>::Reset(Player*);
+template void FleeingMovementGenerator<Creature>::Reset(Creature*);
+template bool FleeingMovementGenerator<Player>::Update(Player*, uint32);
+template bool FleeingMovementGenerator<Creature>::Update(Creature*, uint32);
+
+void TimedFleeingMovementGenerator::Finalize(Unit* owner)
+{
+ owner->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING);
+ owner->ClearUnitState(UNIT_STATE_FLEEING|UNIT_STATE_FLEEING_MOVE);
+ if (Unit* victim = owner->getVictim())
+ {
+ if (owner->isAlive())
+ {
+ owner->AttackStop();
+ owner->ToCreature()->AI()->AttackStart(victim);
+ }
+ }
+}
+
+bool TimedFleeingMovementGenerator::Update(Unit* owner, uint32 time_diff)
+{
+ if (!owner->isAlive())
+ return false;
+
+ if (owner->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED))
+ {
+ owner->ClearUnitState(UNIT_STATE_FLEEING_MOVE);
+ return true;
+ }
+
+ i_totalFleeTime.Update(time_diff);
+ if (i_totalFleeTime.Passed())
+ return false;
+
+ // This calls grant-parent Update method hiden by FleeingMovementGenerator::Update(Creature &, uint32) version
+ // This is done instead of casting Unit& to Creature& and call parent method, then we can use Unit directly
+ return MovementGeneratorMedium< Creature, FleeingMovementGenerator<Creature> >::Update(owner, time_diff);
+}
+
diff --git a/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.h b/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.h
index 5acebb5..05c3916 100755
--- a/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.h
+++ b/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.h
@@ -27,29 +27,17 @@ class FleeingMovementGenerator : public MovementGeneratorMedium< T, FleeingMovem
public:
FleeingMovementGenerator(uint64 fright) : i_frightGUID(fright), i_nextCheckTime(0) {}
- void DoInitialize(T &);
- void DoFinalize(T &);
- void DoReset(T &);
- bool DoUpdate(T &, uint32);
+ void Initialize(T*);
+ void Finalize(T*);
+ void Reset(T*);
+ bool Update(T*, uint32);
MovementGeneratorType GetMovementGeneratorType() { return FLEEING_MOTION_TYPE; }
private:
- void _setTargetLocation(T &owner);
- bool _getPoint(T &owner, float &x, float &y, float &z);
- bool _setMoveData(T &owner);
- void _Init(T &);
+ void _setTargetLocation(T*);
+ void _getPoint(T*, float &x, float &y, float &z);
- bool is_water_ok :1;
- bool is_land_ok :1;
- bool i_only_forward:1;
-
- float i_caster_x;
- float i_caster_y;
- float i_caster_z;
- float i_last_distance_from_caster;
- float i_to_distance_from_caster;
- float i_cur_angle;
uint64 i_frightGUID;
TimeTracker i_nextCheckTime;
};
@@ -62,8 +50,8 @@ class TimedFleeingMovementGenerator : public FleeingMovementGenerator<Creature>
i_totalFleeTime(time) {}
MovementGeneratorType GetMovementGeneratorType() { return TIMED_FLEEING_MOTION_TYPE; }
- bool Update(Unit &, uint32);
- void Finalize(Unit &);
+ bool Update(Unit*, uint32);
+ void Finalize(Unit*);
private:
TimeTracker i_totalFleeTime;
diff --git a/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp
index 1e9ea31..78c1f80 100644
--- a/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp
+++ b/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp
@@ -23,49 +23,49 @@
#include "MoveSplineInit.h"
#include "MoveSpline.h"
-void HomeMovementGenerator<Creature>::DoInitialize(Creature & owner)
+void HomeMovementGenerator<Creature>::Initialize(Creature* owner)
{
_setTargetLocation(owner);
}
-void HomeMovementGenerator<Creature>::DoReset(Creature &)
+void HomeMovementGenerator<Creature>::Finalize(Creature* owner)
{
+ if (arrived)
+ {
+ owner->ClearUnitState(UNIT_STATE_EVADE);
+ owner->SetWalk(true);
+ owner->LoadCreaturesAddon(true);
+ owner->AI()->JustReachedHome();
+ }
}
-void HomeMovementGenerator<Creature>::_setTargetLocation(Creature & owner)
+void HomeMovementGenerator<Creature>::Reset(Creature*)
{
- if (owner.HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED | UNIT_STATE_DISTRACTED))
+}
+
+void HomeMovementGenerator<Creature>::_setTargetLocation(Creature* owner)
+{
+ if (owner->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED | UNIT_STATE_DISTRACTED))
return;
Movement::MoveSplineInit init(owner);
float x, y, z, o;
// at apply we can select more nice return points base at current movegen
- //if (owner.GetMotionMaster()->empty() || !owner.GetMotionMaster()->top()->GetResetPosition(owner,x,y,z))
- //{
- owner.GetHomePosition(x, y, z, o);
- init.SetFacing(o);
- //}
- init.MoveTo(x,y,z);
+ if (owner->GetMotionMaster()->empty() || !owner->GetMotionMaster()->top()->GetResetPosition(owner, x, y, z))
+ {
+ owner->GetHomePosition(x, y, z, o);
+ init.SetFacing(o);
+ }
+ init.MoveTo(x, y, z);
init.SetWalk(false);
init.Launch();
arrived = false;
- owner.ClearUnitState(UNIT_STATE_ALL_STATE & ~UNIT_STATE_EVADE);
+ owner->ClearUnitState(UNIT_STATE_ALL_STATE & ~UNIT_STATE_EVADE);
}
-bool HomeMovementGenerator<Creature>::DoUpdate(Creature &owner, const uint32 /*time_diff*/)
+bool HomeMovementGenerator<Creature>::Update(Creature* owner, const uint32 /*time_diff*/)
{
- arrived = owner.movespline->Finalized();
+ arrived = owner->movespline->Finalized();
return !arrived;
}
-
-void HomeMovementGenerator<Creature>::DoFinalize(Creature& owner)
-{
- if (arrived)
- {
- owner.ClearUnitState(UNIT_STATE_EVADE);
- owner.SetWalk(true);
- owner.LoadCreaturesAddon(true);
- owner.AI()->JustReachedHome();
- }
-}
diff --git a/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.h b/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.h
index a3d53e2..dfbea60 100644
--- a/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.h
+++ b/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.h
@@ -34,14 +34,14 @@ class HomeMovementGenerator<Creature> : public MovementGeneratorMedium< Creature
HomeMovementGenerator() : arrived(false) {}
~HomeMovementGenerator() {}
- void DoInitialize(Creature &);
- void DoFinalize(Creature &);
- void DoReset(Creature &);
- bool DoUpdate(Creature &, const uint32);
+ void Initialize(Creature*);
+ void Finalize(Creature*);
+ void Reset(Creature*);
+ bool Update(Creature*, const uint32);
MovementGeneratorType GetMovementGeneratorType() { return HOME_MOTION_TYPE; }
private:
- void _setTargetLocation(Creature &);
+ void _setTargetLocation(Creature*);
bool arrived;
};
#endif
diff --git a/src/server/game/Movement/MovementGenerators/IdleMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/IdleMovementGenerator.cpp
index e237f22..b33cddc 100755
--- a/src/server/game/Movement/MovementGenerators/IdleMovementGenerator.cpp
+++ b/src/server/game/Movement/MovementGenerators/IdleMovementGenerator.cpp
@@ -24,33 +24,33 @@ IdleMovementGenerator si_idleMovement;
// StopMoving is needed to make unit stop if its last movement generator expires
// But it should not be sent otherwise there are many redundent packets
-void IdleMovementGenerator::Initialize(Unit &owner)
+void IdleMovementGenerator::Initialize(Unit* owner)
{
Reset(owner);
}
-void IdleMovementGenerator::Reset(Unit& owner)
+void IdleMovementGenerator::Reset(Unit* owner)
{
- if (!owner.IsStopped())
- owner.StopMoving();
+ if (!owner->IsStopped())
+ owner->StopMoving();
}
-void RotateMovementGenerator::Initialize(Unit& owner)
+void RotateMovementGenerator::Initialize(Unit* owner)
{
- if (!owner.IsStopped())
- owner.StopMoving();
+ if (!owner->IsStopped())
+ owner->StopMoving();
- if (owner.getVictim())
- owner.SetInFront(owner.getVictim());
+ if (owner->getVictim())
+ owner->SetInFront(owner->getVictim());
- owner.AddUnitState(UNIT_STATE_ROTATING);
+ owner->AddUnitState(UNIT_STATE_ROTATING);
- owner.AttackStop();
+ owner->AttackStop();
}
-bool RotateMovementGenerator::Update(Unit& owner, uint32 diff)
+bool RotateMovementGenerator::Update(Unit* owner, uint32 diff)
{
- float angle = owner.GetOrientation();
+ float angle = owner->GetOrientation();
if (m_direction == ROTATE_DIRECTION_LEFT)
{
angle += (float)diff * static_cast<float>(M_PI * 2) / m_maxDuration;
@@ -61,8 +61,8 @@ bool RotateMovementGenerator::Update(Unit& owner, uint32 diff)
angle -= (float)diff * static_cast<float>(M_PI * 2) / m_maxDuration;
while (angle < 0) angle += static_cast<float>(M_PI * 2);
}
- owner.SetOrientation(angle);
- owner.SendMovementFlagUpdate(); // this is a hack. we do not have anything correct to send in the beginning
+ owner->SetOrientation(angle);
+ owner->SendMovementFlagUpdate(); // this is a hack. we do not have anything correct to send in the beginning
if (m_duration > diff)
m_duration -= diff;
@@ -72,24 +72,24 @@ bool RotateMovementGenerator::Update(Unit& owner, uint32 diff)
return true;
}
-void RotateMovementGenerator::Finalize(Unit &unit)
+void RotateMovementGenerator::Finalize(Unit* unit)
{
- unit.ClearUnitState(UNIT_STATE_ROTATING);
- if (unit.GetTypeId() == TYPEID_UNIT)
- unit.ToCreature()->AI()->MovementInform(ROTATE_MOTION_TYPE, 0);
+ unit->ClearUnitState(UNIT_STATE_ROTATING);
+ if (unit->GetTypeId() == TYPEID_UNIT)
+ unit->ToCreature()->AI()->MovementInform(ROTATE_MOTION_TYPE, 0);
}
-void DistractMovementGenerator::Initialize(Unit& owner)
+void DistractMovementGenerator::Initialize(Unit* owner)
{
- owner.AddUnitState(UNIT_STATE_DISTRACTED);
+ owner->AddUnitState(UNIT_STATE_DISTRACTED);
}
-void DistractMovementGenerator::Finalize(Unit& owner)
+void DistractMovementGenerator::Finalize(Unit* owner)
{
- owner.ClearUnitState(UNIT_STATE_DISTRACTED);
+ owner->ClearUnitState(UNIT_STATE_DISTRACTED);
}
-bool DistractMovementGenerator::Update(Unit& /*owner*/, uint32 time_diff)
+bool DistractMovementGenerator::Update(Unit* /*owner*/, uint32 time_diff)
{
if (time_diff > m_timer)
return false;
@@ -98,9 +98,9 @@ bool DistractMovementGenerator::Update(Unit& /*owner*/, uint32 time_diff)
return true;
}
-void AssistanceDistractMovementGenerator::Finalize(Unit &unit)
+void AssistanceDistractMovementGenerator::Finalize(Unit* unit)
{
- unit.ClearUnitState(UNIT_STATE_DISTRACTED);
- unit.ToCreature()->SetReactState(REACT_AGGRESSIVE);
+ unit->ClearUnitState(UNIT_STATE_DISTRACTED);
+ unit->ToCreature()->SetReactState(REACT_AGGRESSIVE);
}
diff --git a/src/server/game/Movement/MovementGenerators/IdleMovementGenerator.h b/src/server/game/Movement/MovementGenerators/IdleMovementGenerator.h
index ad28cc1..4a2a5e3 100644
--- a/src/server/game/Movement/MovementGenerators/IdleMovementGenerator.h
+++ b/src/server/game/Movement/MovementGenerators/IdleMovementGenerator.h
@@ -25,10 +25,10 @@ class IdleMovementGenerator : public MovementGenerator
{
public:
- void Initialize(Unit &);
- void Finalize(Unit &) { }
- void Reset(Unit &);
- bool Update(Unit &, uint32) { return true; }
+ void Initialize(Unit*);
+ void Finalize(Unit*) { }
+ void Reset(Unit*);
+ bool Update(Unit*, uint32) { return true; }
MovementGeneratorType GetMovementGeneratorType() { return IDLE_MOTION_TYPE; }
};
@@ -39,10 +39,10 @@ class RotateMovementGenerator : public MovementGenerator
public:
explicit RotateMovementGenerator(uint32 time, RotateDirection direction) : m_duration(time), m_maxDuration(time), m_direction(direction) {}
- void Initialize(Unit& owner);
- void Finalize(Unit& owner);
- void Reset(Unit& owner) { Initialize(owner); }
- bool Update(Unit& owner, uint32 time_diff);
+ void Initialize(Unit*);
+ void Finalize(Unit*);
+ void Reset(Unit* owner) { Initialize(owner); }
+ bool Update(Unit*, uint32);
MovementGeneratorType GetMovementGeneratorType() { return ROTATE_MOTION_TYPE; }
private:
@@ -55,10 +55,10 @@ class DistractMovementGenerator : public MovementGenerator
public:
explicit DistractMovementGenerator(uint32 timer) : m_timer(timer) {}
- void Initialize(Unit& owner);
- void Finalize(Unit& owner);
- void Reset(Unit& owner) { Initialize(owner); }
- bool Update(Unit& owner, uint32 time_diff);
+ void Initialize(Unit*);
+ void Finalize(Unit*);
+ void Reset(Unit* owner) { Initialize(owner); }
+ bool Update(Unit*, uint32);
MovementGeneratorType GetMovementGeneratorType() { return DISTRACT_MOTION_TYPE; }
private:
@@ -72,7 +72,7 @@ class AssistanceDistractMovementGenerator : public DistractMovementGenerator
DistractMovementGenerator(timer) {}
MovementGeneratorType GetMovementGeneratorType() { return ASSISTANCE_DISTRACT_MOTION_TYPE; }
- void Finalize(Unit& unit);
+ void Finalize(Unit*);
};
#endif
diff --git a/src/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp
index 9bc3e09..5d70b1a 100644
--- a/src/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp
+++ b/src/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp
@@ -27,111 +27,115 @@
//----- Point Movement Generator
template<class T>
-void PointMovementGenerator<T>::DoInitialize(T &unit)
+void PointMovementGenerator<T>::Initialize(T* unit)
{
- if (!unit.IsStopped())
- unit.StopMoving();
+ if (!unit->IsStopped())
+ unit->StopMoving();
+
+ unit->AddUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
+
+ if (id == EVENT_CHARGE)
+ return;
- unit.AddUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
- i_recalculateSpeed = false;
Movement::MoveSplineInit init(unit);
- init.MoveTo(i_x, i_y, i_z);
+ init.MoveTo(i_x, i_y, i_z, m_generatePath);
if (speed > 0.0f)
init.SetVelocity(speed);
init.Launch();
}
template<class T>
-bool PointMovementGenerator<T>::DoUpdate(T &unit, uint32 /*diff*/)
+bool PointMovementGenerator<T>::Update(T* unit, uint32 /*diff*/)
{
- if (!&unit)
+ if (!unit)
return false;
- if (unit.HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED))
+ if (unit->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED))
{
- unit.ClearUnitState(UNIT_STATE_ROAMING_MOVE);
+ unit->ClearUnitState(UNIT_STATE_ROAMING_MOVE);
return true;
}
- unit.AddUnitState(UNIT_STATE_ROAMING_MOVE);
+ unit->AddUnitState(UNIT_STATE_ROAMING_MOVE);
- if (i_recalculateSpeed && !unit.movespline->Finalized())
+ if (id != EVENT_CHARGE && i_recalculateSpeed && !unit->movespline->Finalized())
{
i_recalculateSpeed = false;
Movement::MoveSplineInit init(unit);
- init.MoveTo(i_x, i_y, i_z);
+ init.MoveTo(i_x, i_y, i_z, m_generatePath);
if (speed > 0.0f) // Default value for point motion type is 0.0, if 0.0 spline will use GetSpeed on unit
init.SetVelocity(speed);
init.Launch();
}
- return !unit.movespline->Finalized();
+ return !unit->movespline->Finalized();
}
template<class T>
-void PointMovementGenerator<T>::DoFinalize(T &unit)
+void PointMovementGenerator<T>::Finalize(T* unit)
{
- unit.ClearUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
+ if (unit->HasUnitState(UNIT_STATE_CHARGING))
+ unit->ClearUnitState(UNIT_STATE_ROAMING | UNIT_STATE_ROAMING_MOVE);
- if (unit.movespline->Finalized())
+ if (unit->movespline->Finalized())
MovementInform(unit);
}
template<class T>
-void PointMovementGenerator<T>::DoReset(T &unit)
+void PointMovementGenerator<T>::Reset(T* unit)
{
- if (!unit.IsStopped())
- unit.StopMoving();
+ if (!unit->IsStopped())
+ unit->StopMoving();
- unit.AddUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
+ unit->AddUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
}
template<class T>
-void PointMovementGenerator<T>::MovementInform(T & /*unit*/)
+void PointMovementGenerator<T>::MovementInform(T* /*unit*/)
{
}
-template <> void PointMovementGenerator<Creature>::MovementInform(Creature &unit)
+template <> void PointMovementGenerator<Creature>::MovementInform(Creature* unit)
{
- if (unit.AI())
- unit.AI()->MovementInform(POINT_MOTION_TYPE, id);
+ if (unit->AI())
+ unit->AI()->MovementInform(POINT_MOTION_TYPE, id);
}
-template void PointMovementGenerator<Player>::DoInitialize(Player&);
-template void PointMovementGenerator<Creature>::DoInitialize(Creature&);
-template void PointMovementGenerator<Player>::DoFinalize(Player&);
-template void PointMovementGenerator<Creature>::DoFinalize(Creature&);
-template void PointMovementGenerator<Player>::DoReset(Player&);
-template void PointMovementGenerator<Creature>::DoReset(Creature&);
-template bool PointMovementGenerator<Player>::DoUpdate(Player &, uint32);
-template bool PointMovementGenerator<Creature>::DoUpdate(Creature&, uint32);
+template void PointMovementGenerator<Player>::Initialize(Player*);
+template void PointMovementGenerator<Creature>::Initialize(Creature*);
+template void PointMovementGenerator<Player>::Finalize(Player*);
+template void PointMovementGenerator<Creature>::Finalize(Creature*);
+template void PointMovementGenerator<Player>::Reset(Player*);
+template void PointMovementGenerator<Creature>::Reset(Creature*);
+template bool PointMovementGenerator<Player>::Update(Player*, uint32);
+template bool PointMovementGenerator<Creature>::Update(Creature*, uint32);
-void AssistanceMovementGenerator::Finalize(Unit &unit)
+void AssistanceMovementGenerator::Finalize(Unit* unit)
{
- unit.ToCreature()->SetNoCallAssistance(false);
- unit.ToCreature()->CallAssistance();
- if (unit.isAlive())
- unit.GetMotionMaster()->MoveSeekAssistanceDistract(sWorld->getIntConfig(CONFIG_CREATURE_FAMILY_ASSISTANCE_DELAY));
+ unit->ToCreature()->SetNoCallAssistance(false);
+ unit->ToCreature()->CallAssistance();
+ if (unit->isAlive())
+ unit->GetMotionMaster()->MoveSeekAssistanceDistract(sWorld->getIntConfig(CONFIG_CREATURE_FAMILY_ASSISTANCE_DELAY));
}
-bool EffectMovementGenerator::Update(Unit &unit, uint32)
+bool EffectMovementGenerator::Update(Unit* unit, uint32)
{
- return !unit.movespline->Finalized();
+ return !unit->movespline->Finalized();
}
-void EffectMovementGenerator::Finalize(Unit &unit)
+void EffectMovementGenerator::Finalize(Unit* unit)
{
- if (unit.GetTypeId() != TYPEID_UNIT)
+ if (unit->GetTypeId() != TYPEID_UNIT)
return;
- if (((Creature&)unit).AI())
- ((Creature&)unit).AI()->MovementInform(EFFECT_MOTION_TYPE, m_Id);
+ if (unit->ToCreature()->AI())
+ unit->ToCreature()->AI()->MovementInform(EFFECT_MOTION_TYPE, m_Id);
// Need restore previous movement since we have no proper states system
- //if (unit.isAlive() && !unit.HasUnitState(UNIT_STATE_CONFUSED|UNIT_STATE_FLEEING))
- //{
- // if (Unit * victim = unit.getVictim())
- // unit.GetMotionMaster()->MoveChase(victim);
- // else
- // unit.GetMotionMaster()->Initialize();
- //}
+ if (unit->isAlive() && !unit->HasUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_FLEEING))
+ {
+ if (Unit* victim = unit->getVictim())
+ unit->GetMotionMaster()->MoveChase(victim);
+ else
+ unit->GetMotionMaster()->Initialize();
+ }
}
diff --git a/src/server/game/Movement/MovementGenerators/PointMovementGenerator.h b/src/server/game/Movement/MovementGenerators/PointMovementGenerator.h
index 93516e5..c26ff94 100644
--- a/src/server/game/Movement/MovementGenerators/PointMovementGenerator.h
+++ b/src/server/game/Movement/MovementGenerators/PointMovementGenerator.h
@@ -26,25 +26,26 @@ template<class T>
class PointMovementGenerator : public MovementGeneratorMedium< T, PointMovementGenerator<T> >
{
public:
- PointMovementGenerator(uint32 _id, float _x, float _y, float _z, float _speed = 0.0f) : id(_id),
- i_x(_x), i_y(_y), i_z(_z), speed(_speed) {}
+ PointMovementGenerator(uint32 _id, float _x, float _y, float _z, bool _generatePath, float _speed = 0.0f) : id(_id),
+ i_x(_x), i_y(_y), i_z(_z), m_generatePath(_generatePath), speed(_speed), i_recalculateSpeed(false) {}
- void DoInitialize(T &);
- void DoFinalize(T &);
- void DoReset(T &);
- bool DoUpdate(T &, uint32);
+ void Initialize(T*);
+ void Finalize(T*);
+ void Reset(T*);
+ bool Update(T*, uint32);
- void MovementInform(T &);
+ void MovementInform(T*);
void unitSpeedChanged() { i_recalculateSpeed = true; }
MovementGeneratorType GetMovementGeneratorType() { return POINT_MOTION_TYPE; }
- bool GetDestination(float& x, float& y, float& z) const { x=i_x; y=i_y; z=i_z; return true; }
+ void GetDestination(float& x, float& y, float& z) const { x = i_x; y = i_y; z = i_z; }
private:
uint32 id;
float i_x, i_y, i_z;
float speed;
+ bool m_generatePath;
bool i_recalculateSpeed;
};
@@ -52,10 +53,10 @@ class AssistanceMovementGenerator : public PointMovementGenerator<Creature>
{
public:
AssistanceMovementGenerator(float _x, float _y, float _z) :
- PointMovementGenerator<Creature>(0, _x, _y, _z) {}
+ PointMovementGenerator<Creature>(0, _x, _y, _z, true) {}
MovementGeneratorType GetMovementGeneratorType() { return ASSISTANCE_MOTION_TYPE; }
- void Finalize(Unit &);
+ void Finalize(Unit*);
};
// Does almost nothing - just doesn't allows previous movegen interrupt current effect.
@@ -63,10 +64,10 @@ class EffectMovementGenerator : public MovementGenerator
{
public:
explicit EffectMovementGenerator(uint32 Id) : m_Id(Id) {}
- void Initialize(Unit &) {}
- void Finalize(Unit &unit);
- void Reset(Unit &) {}
- bool Update(Unit &u, uint32);
+ void Initialize(Unit*) {}
+ void Finalize(Unit*);
+ void Reset(Unit*) {}
+ bool Update(Unit*, uint32);
MovementGeneratorType GetMovementGeneratorType() { return EFFECT_MOTION_TYPE; }
private:
uint32 m_Id;
diff --git a/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp
index 4d74ba2..a9d3c40 100644
--- a/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp
+++ b/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp
@@ -33,16 +33,16 @@
#endif
template<>
-void RandomMovementGenerator<Creature>::_setRandomLocation(Creature& creature)
+void RandomMovementGenerator<Creature>::_setRandomLocation(Creature* creature)
{
float respX, respY, respZ, respO, destX, destY, destZ, travelDistZ;
- creature.GetHomePosition(respX, respY, respZ, respO);
- Map const* map = creature.GetBaseMap();
+ creature->GetHomePosition(respX, respY, respZ, respO);
+ Map const* map = creature->GetBaseMap();
// For 2D/3D system selection
//bool is_land_ok = creature.CanWalk(); // not used?
//bool is_water_ok = creature.CanSwim(); // not used?
- bool is_air_ok = creature.CanFly();
+ bool is_air_ok = creature->CanFly();
const float angle = float(rand_norm()) * static_cast<float>(M_PI*2.0f);
const float range = float(rand_norm()) * wander_distance;
@@ -77,17 +77,17 @@ void RandomMovementGenerator<Creature>::_setRandomLocation(Creature& creature)
// The fastest way to get an accurate result 90% of the time.
// Better result can be obtained like 99% accuracy with a ray light, but the cost is too high and the code is too long.
- destZ = map->GetHeight(creature.GetPhaseMask(), destX, destY, respZ+travelDistZ-2.0f, false);
+ destZ = map->GetHeight(creature->GetPhaseMask(), destX, destY, respZ+travelDistZ-2.0f, false);
if (fabs(destZ - respZ) > travelDistZ) // Map check
{
// Vmap Horizontal or above
- destZ = map->GetHeight(creature.GetPhaseMask(), destX, destY, respZ - 2.0f, true);
+ destZ = map->GetHeight(creature->GetPhaseMask(), destX, destY, respZ - 2.0f, true);
if (fabs(destZ - respZ) > travelDistZ)
{
// Vmap Higher
- destZ = map->GetHeight(creature.GetPhaseMask(), destX, destY, respZ+travelDistZ-2.0f, true);
+ destZ = map->GetHeight(creature->GetPhaseMask(), destX, destY, respZ+travelDistZ-2.0f, true);
// let's forget this bad coords where a z cannot be find and retry at next tick
if (fabs(destZ - respZ) > travelDistZ)
@@ -101,7 +101,7 @@ void RandomMovementGenerator<Creature>::_setRandomLocation(Creature& creature)
else
i_nextMoveTime.Reset(urand(500, 10000));
- creature.AddUnitState(UNIT_STATE_ROAMING_MOVE);
+ creature->AddUnitState(UNIT_STATE_ROAMING_MOVE);
Movement::MoveSplineInit init(creature);
init.MoveTo(destX, destY, destZ);
@@ -109,47 +109,47 @@ void RandomMovementGenerator<Creature>::_setRandomLocation(Creature& creature)
init.Launch();
//Call for creature group update
- if (creature.GetFormation() && creature.GetFormation()->getLeader() == &creature)
- creature.GetFormation()->LeaderMoveTo(destX, destY, destZ);
+ if (creature->GetFormation() && creature->GetFormation()->getLeader() == creature)
+ creature->GetFormation()->LeaderMoveTo(destX, destY, destZ);
}
template<>
-void RandomMovementGenerator<Creature>::DoInitialize(Creature &creature)
+void RandomMovementGenerator<Creature>::Initialize(Creature* creature)
{
- if (!creature.isAlive())
+ if (!creature->isAlive())
return;
if (!wander_distance)
- wander_distance = creature.GetRespawnRadius();
+ wander_distance = creature->GetRespawnRadius();
- creature.AddUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
+ creature->AddUnitState(UNIT_STATE_ROAMING | UNIT_STATE_ROAMING_MOVE);
_setRandomLocation(creature);
}
template<>
-void RandomMovementGenerator<Creature>::DoReset(Creature &creature)
+void RandomMovementGenerator<Creature>::Reset(Creature* creature)
{
- DoInitialize(creature);
+ Initialize(creature);
}
template<>
-void RandomMovementGenerator<Creature>::DoFinalize(Creature &creature)
+void RandomMovementGenerator<Creature>::Finalize(Creature* creature)
{
- creature.ClearUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
- creature.SetWalk(false);
+ creature->ClearUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
+ creature->SetWalk(false);
}
template<>
-bool RandomMovementGenerator<Creature>::DoUpdate(Creature &creature, const uint32 diff)
+bool RandomMovementGenerator<Creature>::Update(Creature* creature, const uint32 diff)
{
- if (creature.HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED | UNIT_STATE_DISTRACTED))
+ if (creature->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED | UNIT_STATE_DISTRACTED))
{
i_nextMoveTime.Reset(0); // Expire the timer
- creature.ClearUnitState(UNIT_STATE_ROAMING_MOVE);
+ creature->ClearUnitState(UNIT_STATE_ROAMING_MOVE);
return true;
}
- if (creature.movespline->Finalized())
+ if (creature->movespline->Finalized())
{
i_nextMoveTime.Update(diff);
if (i_nextMoveTime.Passed())
@@ -159,14 +159,14 @@ bool RandomMovementGenerator<Creature>::DoUpdate(Creature &creature, const uint3
}
template<>
-bool RandomMovementGenerator<Creature>::GetResetPosition(Creature &creature, float& x, float& y, float& z)
+bool RandomMovementGenerator<Creature>::GetResetPosition(Creature* creature, float& x, float& y, float& z)
{
float radius;
- creature.GetRespawnPosition(x, y, z, NULL, &radius);
+ creature->GetRespawnPosition(x, y, z, NULL, &radius);
// use current if in range
- if (creature.IsWithinDist2d(x,y,radius))
- creature.GetPosition(x,y,z);
+ if (creature->IsWithinDist2d(x,y,radius))
+ creature->GetPosition(x,y,z);
return true;
}
diff --git a/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.h b/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.h
index 449b268..ca370de 100644
--- a/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.h
+++ b/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.h
@@ -27,12 +27,12 @@ class RandomMovementGenerator : public MovementGeneratorMedium< T, RandomMovemen
public:
RandomMovementGenerator(float spawn_dist = 0.0f) : i_nextMoveTime(0), wander_distance(spawn_dist) {}
- void _setRandomLocation(T &);
- void DoInitialize(T &);
- void DoFinalize(T &);
- void DoReset(T &);
- bool DoUpdate(T &, const uint32);
- bool GetResetPosition(T&, float& x, float& y, float& z);
+ void _setRandomLocation(T*);
+ void Initialize(T*);
+ void Finalize(T*);
+ void Reset(T*);
+ bool Update(T*, const uint32);
+ bool GetResetPosition(T*, float& x, float& y, float& z);
MovementGeneratorType GetMovementGeneratorType() { return RANDOM_MOTION_TYPE; }
private:
TimeTrackerSmall i_nextMoveTime;
diff --git a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp
dissimilarity index 63%
index 8712b2c..88eaccc 100755
--- a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp
+++ b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp
@@ -1,347 +1,325 @@
-/*
- * Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/>
- * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "ByteBuffer.h"
-#include "TargetedMovementGenerator.h"
-#include "Errors.h"
-#include "Creature.h"
-#include "CreatureAI.h"
-#include "World.h"
-#include "MoveSplineInit.h"
-#include "MoveSpline.h"
-#include "Player.h"
-
-#include <cmath>
-
-template<class T, typename D>
-void TargetedMovementGeneratorMedium<T,D>::_setTargetLocation(T &owner)
-{
- if (!i_target.isValid() || !i_target->IsInWorld())
- return;
-
- if (owner.HasUnitState(UNIT_STATE_NOT_MOVE))
- return;
-
- float x, y, z;
- //! Following block of code deleted by MrSmite in issue 4891
- //! Code kept for learning and diagnostical purposes
-//
-// if (i_offset && i_target->IsWithinDistInMap(&owner,2*i_offset))
-// {
-// if (!owner.movespline->Finalized())
-// return;
-//
-// owner.GetPosition(x, y, z);
-// }
-// else
- if (!i_offset)
- {
- if (i_target->IsWithinMeleeRange(&owner))
- return;
-
- // to nearest random contact position
- i_target->GetRandomContactPoint(&owner, x, y, z, 0, MELEE_RANGE - 0.5f);
- }
- else
- {
- float dist;
- float size;
-
- // Pets need special handling.
- // We need to subtract GetObjectSize() because it gets added back further down the chain
- // and that makes pets too far away. Subtracting it allows pets to properly
- // be (GetCombatReach() + i_offset) away.
- // Only applies when i_target is pet's owner otherwise pets and mobs end up
- // doing a "dance" while fighting
- if (owner.isPet() && i_target->GetTypeId() == TYPEID_PLAYER)
- {
- dist = i_target->GetCombatReach();
- size = i_target->GetCombatReach() - i_target->GetObjectSize();
- }
- else
- {
- dist = i_offset + 1.0f;
- size = owner.GetObjectSize();
- }
-
- if (i_target->IsWithinDistInMap(&owner, dist))
- return;
-
- // to at i_offset distance from target and i_angle from target facing
- i_target->GetClosePoint(x, y, z, size, i_offset, i_angle);
- }
-
- /*
- We MUST not check the distance difference and avoid setting the new location for smaller distances.
- By that we risk having far too many GetContactPoint() calls freezing the whole system.
- In TargetedMovementGenerator<T>::Update() we check the distance to the target and at
- some range we calculate a new position. The calculation takes some processor cycles due to vmaps.
- If the distance to the target it too large to ignore,
- but the distance to the new contact point is short enough to be ignored,
- we will calculate a new contact point each update loop, but will never move to it.
- The system will freeze.
- ralf
-
- //We don't update Mob Movement, if the difference between New destination and last destination is < BothObjectSize
- float bothObjectSize = i_target->GetObjectBoundingRadius() + owner.GetObjectBoundingRadius() + CONTACT_DISTANCE;
- if ( i_destinationHolder.HasDestination() && i_destinationHolder.GetDestinationDiff(x,y,z) < bothObjectSize )
- return;
- */
-
-
- D::_addUnitStateMove(owner);
- i_targetReached = false;
- i_recalculateTravel = false;
-
- Movement::MoveSplineInit init(owner);
- init.MoveTo(x,y,z);
- init.SetWalk(((D*)this)->EnableWalking());
- init.Launch();
-}
-
-template<>
-void TargetedMovementGeneratorMedium<Player,ChaseMovementGenerator<Player> >::UpdateFinalDistance(float /*fDistance*/)
-{
- // nothing to do for Player
-}
-
-template<>
-void TargetedMovementGeneratorMedium<Player,FollowMovementGenerator<Player> >::UpdateFinalDistance(float /*fDistance*/)
-{
- // nothing to do for Player
-}
-
-template<>
-void TargetedMovementGeneratorMedium<Creature,ChaseMovementGenerator<Creature> >::UpdateFinalDistance(float fDistance)
-{
- i_offset = fDistance;
- i_recalculateTravel = true;
-}
-
-template<>
-void TargetedMovementGeneratorMedium<Creature,FollowMovementGenerator<Creature> >::UpdateFinalDistance(float fDistance)
-{
- i_offset = fDistance;
- i_recalculateTravel = true;
-}
-
-template<class T, typename D>
-bool TargetedMovementGeneratorMedium<T,D>::DoUpdate(T &owner, uint32 time_diff)
-{
- if (!i_target.isValid() || !i_target->IsInWorld())
- return false;
-
- if (!owner.isAlive())
- return true;
-
- if (owner.HasUnitState(UNIT_STATE_NOT_MOVE))
- {
- D::_clearUnitStateMove(owner);
- return true;
- }
-
- // prevent movement while casting spells with cast time or channel time
- if (owner.HasUnitState(UNIT_STATE_CASTING))
- {
- if (!owner.IsStopped())
- owner.StopMoving();
- return true;
- }
-
- // prevent crash after creature killed pet
- if (static_cast<D*>(this)->_lostTarget(owner))
- {
- D::_clearUnitStateMove(owner);
- return true;
- }
-
- i_recheckDistance.Update(time_diff);
- if (i_recheckDistance.Passed())
- {
- i_recheckDistance.Reset(50);
- //More distance let have better performance, less distance let have more sensitive reaction at target move.
- float allowed_dist = i_target->GetObjectSize() + owner.GetObjectSize() + MELEE_RANGE - 0.5f;
- float dist = (owner.movespline->FinalDestination() - G3D::Vector3(i_target->GetPositionX(),i_target->GetPositionY(),i_target->GetPositionZ())).squaredLength();
- if (dist >= allowed_dist * allowed_dist)
- _setTargetLocation(owner);
- }
-
- if (owner.movespline->Finalized())
- {
- static_cast<D*>(this)->MovementInform(owner);
- if (i_angle == 0.f && !owner.HasInArc(0.01f, i_target.getTarget()))
- owner.SetInFront(i_target.getTarget());
-
- if (!i_targetReached)
- {
- i_targetReached = true;
- static_cast<D*>(this)->_reachTarget(owner);
- }
- }
- else
- {
- if (i_recalculateTravel)
- _setTargetLocation(owner);
- }
- return true;
-}
-
-//-----------------------------------------------//
-template<class T>
-void ChaseMovementGenerator<T>::_reachTarget(T &owner)
-{
- if (owner.IsWithinMeleeRange(this->i_target.getTarget()))
- owner.Attack(this->i_target.getTarget(),true);
-}
-
-template<>
-void ChaseMovementGenerator<Player>::DoInitialize(Player &owner)
-{
- owner.AddUnitState(UNIT_STATE_CHASE|UNIT_STATE_CHASE_MOVE);
- _setTargetLocation(owner);
-}
-
-template<>
-void ChaseMovementGenerator<Creature>::DoInitialize(Creature &owner)
-{
- owner.SetWalk(false);
- owner.AddUnitState(UNIT_STATE_CHASE|UNIT_STATE_CHASE_MOVE);
- _setTargetLocation(owner);
-}
-
-template<class T>
-void ChaseMovementGenerator<T>::DoFinalize(T &owner)
-{
- owner.ClearUnitState(UNIT_STATE_CHASE|UNIT_STATE_CHASE_MOVE);
-}
-
-template<class T>
-void ChaseMovementGenerator<T>::DoReset(T &owner)
-{
- DoInitialize(owner);
-}
-
-template<class T>
-void ChaseMovementGenerator<T>::MovementInform(T & /*unit*/)
-{
-}
-
-template<>
-void ChaseMovementGenerator<Creature>::MovementInform(Creature &unit)
-{
- // Pass back the GUIDLow of the target. If it is pet's owner then PetAI will handle
- if (unit.AI())
- unit.AI()->MovementInform(CHASE_MOTION_TYPE, i_target.getTarget()->GetGUIDLow());
-}
-
-//-----------------------------------------------//
-template<>
-bool FollowMovementGenerator<Creature>::EnableWalking() const
-{
- return i_target.isValid() && i_target->IsWalking();
-}
-
-template<>
-bool FollowMovementGenerator<Player>::EnableWalking() const
-{
- return false;
-}
-
-template<>
-void FollowMovementGenerator<Player>::_updateSpeed(Player &/*u*/)
-{
- // nothing to do for Player
-}
-
-template<>
-void FollowMovementGenerator<Creature>::_updateSpeed(Creature &u)
-{
- // pet only sync speed with owner
- if (!((Creature&)u).isPet() || !i_target.isValid() || i_target->GetGUID() != u.GetOwnerGUID())
- return;
-
- u.UpdateSpeed(MOVE_RUN,true);
- u.UpdateSpeed(MOVE_WALK,true);
- u.UpdateSpeed(MOVE_SWIM,true);
-}
-
-template<>
-void FollowMovementGenerator<Player>::DoInitialize(Player &owner)
-{
- owner.AddUnitState(UNIT_STATE_FOLLOW|UNIT_STATE_FOLLOW_MOVE);
- _updateSpeed(owner);
- _setTargetLocation(owner);
-}
-
-template<>
-void FollowMovementGenerator<Creature>::DoInitialize(Creature &owner)
-{
- owner.AddUnitState(UNIT_STATE_FOLLOW|UNIT_STATE_FOLLOW_MOVE);
- _updateSpeed(owner);
- _setTargetLocation(owner);
-}
-
-template<class T>
-void FollowMovementGenerator<T>::DoFinalize(T &owner)
-{
- owner.ClearUnitState(UNIT_STATE_FOLLOW|UNIT_STATE_FOLLOW_MOVE);
- _updateSpeed(owner);
-}
-
-template<class T>
-void FollowMovementGenerator<T>::DoReset(T &owner)
-{
- DoInitialize(owner);
-}
-
-template<class T>
-void FollowMovementGenerator<T>::MovementInform(T & /*unit*/)
-{
-}
-
-template<>
-void FollowMovementGenerator<Creature>::MovementInform(Creature &unit)
-{
- // Pass back the GUIDLow of the target. If it is pet's owner then PetAI will handle
- if (unit.AI())
- unit.AI()->MovementInform(FOLLOW_MOTION_TYPE, i_target.getTarget()->GetGUIDLow());
-}
-
-//-----------------------------------------------//
-template void TargetedMovementGeneratorMedium<Player,ChaseMovementGenerator<Player> >::_setTargetLocation(Player &);
-template void TargetedMovementGeneratorMedium<Player,FollowMovementGenerator<Player> >::_setTargetLocation(Player &);
-template void TargetedMovementGeneratorMedium<Creature,ChaseMovementGenerator<Creature> >::_setTargetLocation(Creature &);
-template void TargetedMovementGeneratorMedium<Creature,FollowMovementGenerator<Creature> >::_setTargetLocation(Creature &);
-template bool TargetedMovementGeneratorMedium<Player,ChaseMovementGenerator<Player> >::DoUpdate(Player &, uint32);
-template bool TargetedMovementGeneratorMedium<Player,FollowMovementGenerator<Player> >::DoUpdate(Player &, uint32);
-template bool TargetedMovementGeneratorMedium<Creature,ChaseMovementGenerator<Creature> >::DoUpdate(Creature &, uint32);
-template bool TargetedMovementGeneratorMedium<Creature,FollowMovementGenerator<Creature> >::DoUpdate(Creature &, uint32);
-
-template void ChaseMovementGenerator<Player>::_reachTarget(Player &);
-template void ChaseMovementGenerator<Creature>::_reachTarget(Creature &);
-template void ChaseMovementGenerator<Player>::DoFinalize(Player &);
-template void ChaseMovementGenerator<Creature>::DoFinalize(Creature &);
-template void ChaseMovementGenerator<Player>::DoReset(Player &);
-template void ChaseMovementGenerator<Creature>::DoReset(Creature &);
-template void ChaseMovementGenerator<Player>::MovementInform(Player &unit);
-
-template void FollowMovementGenerator<Player>::DoFinalize(Player &);
-template void FollowMovementGenerator<Creature>::DoFinalize(Creature &);
-template void FollowMovementGenerator<Player>::DoReset(Player &);
-template void FollowMovementGenerator<Creature>::DoReset(Creature &);
-template void FollowMovementGenerator<Player>::MovementInform(Player &unit);
+/*
+ * Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "ByteBuffer.h"
+#include "TargetedMovementGenerator.h"
+#include "Errors.h"
+#include "Creature.h"
+#include "CreatureAI.h"
+#include "World.h"
+#include "MoveSplineInit.h"
+#include "MoveSpline.h"
+#include "Player.h"
+
+template<class T, typename D>
+void TargetedMovementGeneratorMedium<T,D>::_setTargetLocation(T* owner, bool updateDestination)
+{
+ if (!i_target.isValid() || !i_target->IsInWorld())
+ return;
+
+ if (owner->HasUnitState(UNIT_STATE_NOT_MOVE))
+ return;
+
+ if (owner->GetTypeId() == TYPEID_UNIT && !i_target->isInAccessiblePlaceFor(owner->ToCreature()))
+ return;
+
+ float x, y, z;
+
+ if (updateDestination || !i_path)
+ {
+ if (!i_offset)
+ {
+ // to nearest contact position
+ i_target->GetContactPoint(owner, x, y, z);
+ }
+ else
+ {
+ float dist;
+ float size;
+
+ // Pets need special handling.
+ // We need to subtract GetObjectSize() because it gets added back further down the chain
+ // and that makes pets too far away. Subtracting it allows pets to properly
+ // be (GetCombatReach() + i_offset) away.
+ // Only applies when i_target is pet's owner otherwise pets and mobs end up
+ // doing a "dance" while fighting
+ if (owner->isPet() && i_target->GetTypeId() == TYPEID_PLAYER)
+ {
+ dist = i_target->GetCombatReach();
+ size = i_target->GetCombatReach() - i_target->GetObjectSize();
+ }
+ else
+ {
+ dist = i_offset + 1.0f;
+ size = owner->GetObjectSize();
+ }
+
+ if (i_target->IsWithinDistInMap(owner, dist))
+ return;
+
+ // to at i_offset distance from target and i_angle from target facing
+ i_target->GetClosePoint(x, y, z, size, i_offset, i_angle);
+ }
+ }
+ else
+ {
+ // the destination has not changed, we just need to refresh the path (usually speed change)
+ G3D::Vector3 end = i_path->GetEndPosition();
+ x = end.x;
+ y = end.y;
+ z = end.z;
+ }
+
+ if (!i_path)
+ i_path = new PathGenerator(owner);
+
+ // allow pets to use shortcut if no path found when following their master
+ bool forceDest = (owner->GetTypeId() == TYPEID_UNIT && owner->ToCreature()->isPet()
+ && owner->HasUnitState(UNIT_STATE_FOLLOW));
+
+ bool result = i_path->CalculatePath(x, y, z, forceDest);
+ if (!result || (i_path->GetPathType() & PATHFIND_NOPATH))
+ {
+ // Cant reach target
+ i_recalculateTravel = true;
+ return;
+ }
+
+ D::_addUnitStateMove(owner);
+ i_targetReached = false;
+ i_recalculateTravel = false;
+ owner->AddUnitState(UNIT_STATE_CHASE);
+
+ Movement::MoveSplineInit init(owner);
+ init.MovebyPath(i_path->GetPath());
+ if (forceDest && updateDestination)
+ init.SetFacing(i_target.getTarget()->GetOrientation());
+ else
+ init.SetFacing(i_target.getTarget());
+ init.SetWalk(((D*)this)->EnableWalking());
+ init.Launch();
+}
+
+template<class T, typename D>
+bool TargetedMovementGeneratorMedium<T,D>::Update(T* owner, uint32 time_diff)
+{
+ if (!i_target.isValid() || !i_target->IsInWorld())
+ return false;
+
+ if (!owner || !owner->isAlive())
+ return false;
+
+ if (owner->HasUnitState(UNIT_STATE_NOT_MOVE))
+ {
+ D::_clearUnitStateMove(owner);
+ return true;
+ }
+
+ // prevent movement while casting spells with cast time or channel time
+ if (owner->HasUnitState(UNIT_STATE_CASTING))
+ {
+ if (!owner->IsStopped())
+ owner->StopMoving();
+ return true;
+ }
+
+ // prevent crash after creature killed pet
+ if (static_cast<D*>(this)->_lostTarget(owner))
+ {
+ D::_clearUnitStateMove(owner);
+ return true;
+ }
+
+ bool targetMoved = false;
+ i_recheckDistance.Update(time_diff);
+ if (i_recheckDistance.Passed())
+ {
+ i_recheckDistance.Reset(100);
+ //More distance let have better performance, less distance let have more sensitive reaction at target move.
+ float allowed_dist = owner->GetCombatReach() + sWorld->getRate(RATE_TARGET_POS_RECALCULATION_RANGE);
+ G3D::Vector3 dest = owner->movespline->FinalDestination();
+
+ if (owner->GetTypeId() == TYPEID_UNIT && owner->ToCreature()->CanFly())
+ targetMoved = !i_target->IsWithinDist3d(dest.x, dest.y, dest.z, allowed_dist);
+ else
+ targetMoved = !i_target->IsWithinDist2d(dest.x, dest.y, allowed_dist);
+ }
+
+ if (i_recalculateTravel || targetMoved)
+ _setTargetLocation(owner, targetMoved);
+
+ if (owner->movespline->Finalized())
+ {
+ static_cast<D*>(this)->MovementInform(owner);
+ if (i_angle == 0.f && !owner->HasInArc(0.01f, i_target.getTarget()))
+ owner->SetInFront(i_target.getTarget());
+
+ if (!i_targetReached)
+ {
+ i_targetReached = true;
+ static_cast<D*>(this)->_reachTarget(owner);
+ }
+ }
+
+ return true;
+}
+
+//-----------------------------------------------//
+template<class T>
+void ChaseMovementGenerator<T>::_reachTarget(T* owner)
+{
+ if (owner->IsWithinMeleeRange(this->i_target.getTarget()))
+ owner->Attack(this->i_target.getTarget(),true);
+}
+
+template<>
+void ChaseMovementGenerator<Player>::Initialize(Player* owner)
+{
+ owner->AddUnitState(UNIT_STATE_CHASE | UNIT_STATE_CHASE_MOVE);
+ _setTargetLocation(owner, true);
+}
+
+template<>
+void ChaseMovementGenerator<Creature>::Initialize(Creature* owner)
+{
+ owner->SetWalk(false);
+ owner->AddUnitState(UNIT_STATE_CHASE | UNIT_STATE_CHASE_MOVE);
+ _setTargetLocation(owner, true);
+}
+
+template<class T>
+void ChaseMovementGenerator<T>::Finalize(T* owner)
+{
+ owner->ClearUnitState(UNIT_STATE_CHASE | UNIT_STATE_CHASE_MOVE);
+}
+
+template<class T>
+void ChaseMovementGenerator<T>::Reset(T* owner)
+{
+ Initialize(owner);
+}
+
+template<class T>
+void ChaseMovementGenerator<T>::MovementInform(T* /*unit*/)
+{
+}
+
+template<>
+void ChaseMovementGenerator<Creature>::MovementInform(Creature* unit)
+{
+ // Pass back the GUIDLow of the target. If it is pet's owner then PetAI will handle
+ if (unit->AI())
+ unit->AI()->MovementInform(CHASE_MOTION_TYPE, i_target.getTarget()->GetGUIDLow());
+}
+
+//-----------------------------------------------//
+template<>
+bool FollowMovementGenerator<Creature>::EnableWalking() const
+{
+ return i_target.isValid() && i_target->IsWalking();
+}
+
+template<>
+bool FollowMovementGenerator<Player>::EnableWalking() const
+{
+ return false;
+}
+
+template<>
+void FollowMovementGenerator<Player>::_updateSpeed(Player* /*u*/)
+{
+ // nothing to do for Player
+}
+
+template<>
+void FollowMovementGenerator<Creature>::_updateSpeed(Creature* u)
+{
+ // pet only sync speed with owner
+ if (!u->isPet() || !i_target.isValid() || i_target->GetGUID() != u->GetOwnerGUID())
+ return;
+
+ u->UpdateSpeed(MOVE_RUN,true);
+ u->UpdateSpeed(MOVE_WALK,true);
+ u->UpdateSpeed(MOVE_SWIM,true);
+}
+
+template<>
+void FollowMovementGenerator<Player>::Initialize(Player* owner)
+{
+ owner->AddUnitState(UNIT_STATE_FOLLOW | UNIT_STATE_FOLLOW_MOVE);
+ _updateSpeed(owner);
+ _setTargetLocation(owner, true);
+}
+
+template<>
+void FollowMovementGenerator<Creature>::Initialize(Creature* owner)
+{
+ owner->AddUnitState(UNIT_STATE_FOLLOW | UNIT_STATE_FOLLOW_MOVE);
+ _updateSpeed(owner);
+ _setTargetLocation(owner, true);
+}
+
+template<class T>
+void FollowMovementGenerator<T>::Finalize(T* owner)
+{
+ owner->ClearUnitState(UNIT_STATE_FOLLOW | UNIT_STATE_FOLLOW_MOVE);
+ _updateSpeed(owner);
+}
+
+template<class T>
+void FollowMovementGenerator<T>::Reset(T* owner)
+{
+ Initialize(owner);
+}
+
+template<class T>
+void FollowMovementGenerator<T>::MovementInform(T* /*unit*/)
+{
+}
+
+template<>
+void FollowMovementGenerator<Creature>::MovementInform(Creature* unit)
+{
+ // Pass back the GUIDLow of the target. If it is pet's owner then PetAI will handle
+ if (unit->AI())
+ unit->AI()->MovementInform(FOLLOW_MOTION_TYPE, i_target.getTarget()->GetGUIDLow());
+}
+
+//-----------------------------------------------//
+template void TargetedMovementGeneratorMedium<Player,ChaseMovementGenerator<Player> >::_setTargetLocation(Player*, bool);
+template void TargetedMovementGeneratorMedium<Player,FollowMovementGenerator<Player> >::_setTargetLocation(Player*, bool);
+template void TargetedMovementGeneratorMedium<Creature,ChaseMovementGenerator<Creature> >::_setTargetLocation(Creature*, bool);
+template void TargetedMovementGeneratorMedium<Creature,FollowMovementGenerator<Creature> >::_setTargetLocation(Creature*, bool);
+template bool TargetedMovementGeneratorMedium<Player,ChaseMovementGenerator<Player> >::Update(Player*, uint32);
+template bool TargetedMovementGeneratorMedium<Player,FollowMovementGenerator<Player> >::Update(Player*, uint32);
+template bool TargetedMovementGeneratorMedium<Creature,ChaseMovementGenerator<Creature> >::Update(Creature*, uint32);
+template bool TargetedMovementGeneratorMedium<Creature,FollowMovementGenerator<Creature> >::Update(Creature*, uint32);
+
+template void ChaseMovementGenerator<Player>::_reachTarget(Player*);
+template void ChaseMovementGenerator<Creature>::_reachTarget(Creature*);
+template void ChaseMovementGenerator<Player>::Finalize(Player*);
+template void ChaseMovementGenerator<Creature>::Finalize(Creature*);
+template void ChaseMovementGenerator<Player>::Reset(Player*);
+template void ChaseMovementGenerator<Creature>::Reset(Creature*);
+template void ChaseMovementGenerator<Player>::MovementInform(Player*);
+
+template void FollowMovementGenerator<Player>::Finalize(Player*);
+template void FollowMovementGenerator<Creature>::Finalize(Creature*);
+template void FollowMovementGenerator<Player>::Reset(Player*);
+template void FollowMovementGenerator<Creature>::Reset(Creature*);
+template void FollowMovementGenerator<Player>::MovementInform(Player*);
diff --git a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.h b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.h
index 67cd877..987baa9 100755
--- a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.h
+++ b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.h
@@ -23,11 +23,12 @@
#include "FollowerReference.h"
#include "Timer.h"
#include "Unit.h"
+#include "PathGenerator.h"
class TargetedMovementGeneratorBase
{
public:
- TargetedMovementGeneratorBase(Unit &target) { i_target.link(&target, this); }
+ TargetedMovementGeneratorBase(Unit* target) { i_target.link(target, this); }
void stopFollowing() { }
protected:
FollowerReference i_target;
@@ -37,79 +38,79 @@ template<class T, typename D>
class TargetedMovementGeneratorMedium : public MovementGeneratorMedium< T, D >, public TargetedMovementGeneratorBase
{
protected:
- TargetedMovementGeneratorMedium(Unit &target, float offset, float angle) :
- TargetedMovementGeneratorBase(target), i_recheckDistance(0),
+ TargetedMovementGeneratorMedium(Unit* target, float offset, float angle) :
+ TargetedMovementGeneratorBase(target), i_recheckDistance(0), i_path(NULL),
i_offset(offset), i_angle(angle),
i_recalculateTravel(false), i_targetReached(false)
{
}
- ~TargetedMovementGeneratorMedium() {}
+ ~TargetedMovementGeneratorMedium() { delete i_path; }
public:
- bool DoUpdate(T &, uint32);
+ bool Update(T*, uint32);
Unit* GetTarget() const { return i_target.getTarget(); }
- void unitSpeedChanged() { i_recalculateTravel=true; }
- void UpdateFinalDistance(float fDistance);
-
+ void unitSpeedChanged() { i_recalculateTravel = true; }
+ bool IsReachable() const { return (i_path) ? (i_path->GetPathType() & PATHFIND_NORMAL) : true; }
protected:
- void _setTargetLocation(T &);
+ void _setTargetLocation(T* owner, bool updateDestination);
TimeTrackerSmall i_recheckDistance;
float i_offset;
float i_angle;
bool i_recalculateTravel : 1;
bool i_targetReached : 1;
+ PathGenerator* i_path;
};
template<class T>
class ChaseMovementGenerator : public TargetedMovementGeneratorMedium<T, ChaseMovementGenerator<T> >
{
public:
- ChaseMovementGenerator(Unit &target)
+ ChaseMovementGenerator(Unit* target)
: TargetedMovementGeneratorMedium<T, ChaseMovementGenerator<T> >(target) {}
- ChaseMovementGenerator(Unit &target, float offset, float angle)
+ ChaseMovementGenerator(Unit* target, float offset, float angle)
: TargetedMovementGeneratorMedium<T, ChaseMovementGenerator<T> >(target, offset, angle) {}
~ChaseMovementGenerator() {}
MovementGeneratorType GetMovementGeneratorType() { return CHASE_MOTION_TYPE; }
- void DoInitialize(T &);
- void DoFinalize(T &);
- void DoReset(T &);
- void MovementInform(T &);
+ void Initialize(T*);
+ void Finalize(T*);
+ void Reset(T*);
+ void MovementInform(T*);
- static void _clearUnitStateMove(T &u) { u.ClearUnitState(UNIT_STATE_CHASE_MOVE); }
- static void _addUnitStateMove(T &u) { u.AddUnitState(UNIT_STATE_CHASE_MOVE); }
+ static void _clearUnitStateMove(T* u) { u->ClearUnitState(UNIT_STATE_CHASE_MOVE); }
+ static void _addUnitStateMove(T* u) { u->AddUnitState(UNIT_STATE_CHASE_MOVE); }
bool EnableWalking() const { return false;}
- bool _lostTarget(T &u) const { return u.getVictim() != this->GetTarget(); }
- void _reachTarget(T &);
+ bool _lostTarget(T* u) const { return u->getVictim() != this->GetTarget(); }
+ void _reachTarget(T*);
};
template<class T>
class FollowMovementGenerator : public TargetedMovementGeneratorMedium<T, FollowMovementGenerator<T> >
{
public:
- FollowMovementGenerator(Unit &target)
+ FollowMovementGenerator(Unit* target)
: TargetedMovementGeneratorMedium<T, FollowMovementGenerator<T> >(target){}
- FollowMovementGenerator(Unit &target, float offset, float angle)
+ FollowMovementGenerator(Unit* target, float offset, float angle)
: TargetedMovementGeneratorMedium<T, FollowMovementGenerator<T> >(target, offset, angle) {}
~FollowMovementGenerator() {}
MovementGeneratorType GetMovementGeneratorType() { return FOLLOW_MOTION_TYPE; }
- void DoInitialize(T &);
- void DoFinalize(T &);
- void DoReset(T &);
- void MovementInform(T &);
+ void Initialize(T*);
+ void Finalize(T*);
+ void Reset(T*);
+ void MovementInform(T*);
- static void _clearUnitStateMove(T &u) { u.ClearUnitState(UNIT_STATE_FOLLOW_MOVE); }
- static void _addUnitStateMove(T &u) { u.AddUnitState(UNIT_STATE_FOLLOW_MOVE); }
+ static void _clearUnitStateMove(T* u) { u->ClearUnitState(UNIT_STATE_FOLLOW_MOVE); }
+ static void _addUnitStateMove(T* u) { u->AddUnitState(UNIT_STATE_FOLLOW_MOVE); }
bool EnableWalking() const;
- bool _lostTarget(T &) const { return false; }
- void _reachTarget(T &) {}
+ bool _lostTarget(T*) const { return false; }
+ void _reachTarget(T*) {}
private:
- void _updateSpeed(T &u);
+ void _updateSpeed(T* u);
};
#endif
diff --git a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp
dissimilarity index 69%
index e9c9cf6..d3b63ed 100644
--- a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp
+++ b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp
@@ -1,650 +1,322 @@
-/*
- * Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/>
- * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-//Basic headers
-#include "WaypointMovementGenerator.h"
-//Extended headers
-#include "ObjectMgr.h"
-#include "World.h"
-//Flightmaster grid preloading
-#include "MapManager.h"
-//Creature-specific headers
-#include "Creature.h"
-#include "CreatureAI.h"
-#include "CreatureGroups.h"
-//Player-specific
-#include "Player.h"
-#include "MoveSplineInit.h"
-#include "MoveSpline.h"
-
-void WaypointMovementGenerator<Creature>::LoadPath(Creature &creature)
-{
- if (!path_id)
- path_id = creature.GetWaypointPath();
-
- i_path = sWaypointMgr->GetPath(path_id);
-
- if (!i_path)
- {
- // No movement found for entry
- sLog->outError(LOG_FILTER_SQL, "WaypointMovementGenerator::LoadPath: creature %s (Entry: %u GUID: %u) doesn't have waypoint path id: %u", creature.GetName().c_str(), creature.GetEntry(), creature.GetGUIDLow(), path_id);
- return;
- }
-
- StartMoveNow(creature);
-}
-
-void WaypointMovementGenerator<Creature>::DoInitialize(Creature &creature)
-{
- LoadPath(creature);
- creature.AddUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
-}
-
-void WaypointMovementGenerator<Creature>::DoFinalize(Creature &creature)
-{
- creature.ClearUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
- creature.SetWalk(false);
-}
-
-void WaypointMovementGenerator<Creature>::DoReset(Creature &creature)
-{
- creature.AddUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
- StartMoveNow(creature);
-}
-
-void WaypointMovementGenerator<Creature>::OnArrived(Creature& creature)
-{
- if (!i_path || i_path->empty())
- return;
- if (m_isArrivalDone)
- return;
-
- creature.ClearUnitState(UNIT_STATE_ROAMING_MOVE);
- m_isArrivalDone = true;
-
- if (i_path->at(i_currentNode)->event_id && urand(0, 99) < i_path->at(i_currentNode)->event_chance)
- {
- sLog->outDebug(LOG_FILTER_MAPSCRIPTS, "Creature movement start script %u at point %u for "UI64FMTD".", i_path->at(i_currentNode)->event_id, i_currentNode, creature.GetGUID());
- creature.GetMap()->ScriptsStart(sWaypointScripts, i_path->at(i_currentNode)->event_id, &creature, NULL);
- }
-
- // Inform script
- MovementInform(creature);
- creature.UpdateWaypointID(i_currentNode);
- Stop(i_path->at(i_currentNode)->delay);
-}
-
-bool WaypointMovementGenerator<Creature>::StartMove(Creature &creature)
-{
- if (!i_path || i_path->empty())
- return false;
- if (Stopped())
- return true;
-
- if (m_isArrivalDone)
- {
- if ((i_currentNode == i_path->size() - 1) && !repeating) // If that's our last waypoint
- {
- creature.SetHomePosition(i_path->at(i_currentNode)->x, i_path->at(i_currentNode)->y, i_path->at(i_currentNode)->z, creature.GetOrientation());
- creature.GetMotionMaster()->Initialize();
- return false;
- }
-
- i_currentNode = (i_currentNode+1) % i_path->size();
- }
-
- WaypointData const* node = i_path->at(i_currentNode);
-
- m_isArrivalDone = false;
-
- creature.AddUnitState(UNIT_STATE_ROAMING_MOVE);
-
- Movement::MoveSplineInit init(creature);
- init.MoveTo(node->x, node->y, node->z);
-
- //! Accepts angles such as 0.00001 and -0.00001, 0 must be ignored, default value in waypoint table
- if (node->orientation && node->delay)
- init.SetFacing(node->orientation);
-
- init.SetWalk(!node->run);
- init.Launch();
-
- //Call for creature group update
- if (creature.GetFormation() && creature.GetFormation()->getLeader() == &creature)
- creature.GetFormation()->LeaderMoveTo(node->x, node->y, node->z);
-
- return true;
-}
-
-bool WaypointMovementGenerator<Creature>::DoUpdate(Creature &creature, uint32 diff)
-{
- // Waypoint movement can be switched on/off
- // This is quite handy for escort quests and other stuff
- if (creature.HasUnitState(UNIT_STATE_NOT_MOVE))
- {
- creature.ClearUnitState(UNIT_STATE_ROAMING_MOVE);
- return true;
- }
- // prevent a crash at empty waypoint path.
- if (!i_path || i_path->empty())
- return false;
-
- if (Stopped())
- {
- if (CanMove(diff))
- return StartMove(creature);
- }
- else
- {
- if (creature.IsStopped())
- Stop(STOP_TIME_FOR_PLAYER);
- else if (creature.movespline->Finalized())
- {
- OnArrived(creature);
- return StartMove(creature);
- }
- }
- return true;
- }
-
-void WaypointMovementGenerator<Creature>::MovementInform(Creature &creature)
-{
- if (creature.AI())
- creature.AI()->MovementInform(WAYPOINT_MOTION_TYPE, i_currentNode);
-}
-
-bool WaypointMovementGenerator<Creature>::GetResetPosition(Creature&, float& x, float& y, float& z)
-{
- // prevent a crash at empty waypoint path.
- if (!i_path || i_path->empty())
- return false;
-
- const WaypointData* node = i_path->at(i_currentNode);
- x = node->x; y = node->y; z = node->z;
- return true;
-}
-
-
-//----------------------------------------------------//
-
-uint32 FlightPathMovementGenerator::GetPathAtMapEnd() const
-{
- if (i_currentNode >= i_path->size())
- return i_path->size();
-
- uint32 curMapId = (*i_path)[i_currentNode].mapid;
- for (uint32 i = i_currentNode; i < i_path->size(); ++i)
- {
- if ((*i_path)[i].mapid != curMapId)
- return i;
- }
-
- return i_path->size();
-}
-
-void FlightPathMovementGenerator::DoInitialize(Player &player)
-{
- DoReset(player);
- InitEndGridInfo();
-}
-
-void FlightPathMovementGenerator::DoFinalize(Player& player)
-{
- // remove flag to prevent send object build movement packets for flight state and crash (movement generator already not at top of stack)
- player.ClearUnitState(UNIT_STATE_IN_FLIGHT);
-
- player.Dismount();
- player.RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_TAXI_FLIGHT);
-
- if (player.m_taxi.empty())
- {
- player.getHostileRefManager().setOnlineOfflineState(true);
- // update z position to ground and orientation for landing point
- // this prevent cheating with landing point at lags
- // when client side flight end early in comparison server side
- player.StopMoving();
- }
-}
-
-#define PLAYER_FLIGHT_SPEED 32.0f
-
-void FlightPathMovementGenerator::DoReset(Player & player)
-{
- player.getHostileRefManager().setOnlineOfflineState(false);
- player.AddUnitState(UNIT_STATE_IN_FLIGHT);
- player.SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_TAXI_FLIGHT);
-
- Movement::MoveSplineInit init(player);
- uint32 end = GetPathAtMapEnd();
- for (uint32 i = GetCurrentNode(); i != end; ++i)
- {
- G3D::Vector3 vertice((*i_path)[i].x, (*i_path)[i].y, (*i_path)[i].z);
- init.Path().push_back(vertice);
- }
- init.SetFirstPointId(GetCurrentNode());
- init.SetFly();
- init.SetVelocity(PLAYER_FLIGHT_SPEED);
- init.Launch();
-}
-
-bool FlightPathMovementGenerator::DoUpdate(Player &player, uint32 /*diff*/)
-{
- uint32 pointId = (uint32)player.movespline->currentPathIdx();
- if (pointId > i_currentNode)
- {
- bool departureEvent = true;
- do
- {
- DoEventIfAny(player, (*i_path)[i_currentNode], departureEvent);
- if (pointId == i_currentNode)
- break;
- if (i_currentNode == _preloadTargetNode)
- PreloadEndGrid();
- i_currentNode += (uint32)departureEvent;
- departureEvent = !departureEvent;
- }
- while (true);
- }
-
- return i_currentNode < (i_path->size()-1);
-}
-
-void FlightPathMovementGenerator::SetCurrentNodeAfterTeleport()
-{
- if (i_path->empty())
- return;
-
- uint32 map0 = (*i_path)[0].mapid;
- for (size_t i = 1; i < i_path->size(); ++i)
- {
- if ((*i_path)[i].mapid != map0)
- {
- i_currentNode = i;
- return;
- }
- }
-}
-
-void FlightPathMovementGenerator::DoEventIfAny(Player& player, TaxiPathNodeEntry const& node, bool departure)
-{
- if (uint32 eventid = departure ? node.departureEventID : node.arrivalEventID)
- {
- sLog->outDebug(LOG_FILTER_MAPSCRIPTS, "Taxi %s event %u of node %u of path %u for player %s", departure ? "departure" : "arrival", eventid, node.index, node.path, player.GetName().c_str());
- player.GetMap()->ScriptsStart(sEventScripts, eventid, &player, &player);
- }
-}
-
-bool FlightPathMovementGenerator::GetResetPosition(Player&, float& x, float& y, float& z)
-{
- const TaxiPathNodeEntry& node = (*i_path)[i_currentNode];
- x = node.x; y = node.y; z = node.z;
- return true;
-}
-
-void FlightPathMovementGenerator::InitEndGridInfo()
-{
- /*! Storage to preload flightmaster grid at end of flight. For multi-stop flights, this will
- be reinitialized for each flightmaster at the end of each spline (or stop) in the flight. */
- uint32 nodeCount = (*i_path).size(); //! Number of nodes in path.
- _endMapId = (*i_path)[nodeCount - 1].mapid; //! MapId of last node
- _preloadTargetNode = nodeCount - 3;
- _endGridX = (*i_path)[nodeCount - 1].x;
- _endGridY = (*i_path)[nodeCount - 1].y;
-}
-
-void FlightPathMovementGenerator::PreloadEndGrid()
-{
- // used to preload the final grid where the flightmaster is
- Map* endMap = sMapMgr->FindBaseNonInstanceMap(_endMapId);
-
- // Load the grid
- if (endMap)
- {
- sLog->outInfo(LOG_FILTER_GENERAL, "Preloading rid (%f, %f) for map %u at node index %u/%u", _endGridX, _endGridY, _endMapId, _preloadTargetNode, (uint32)(i_path->size()-1));
- endMap->LoadGrid(_endGridX, _endGridY);
- }
- else
- sLog->outInfo(LOG_FILTER_GENERAL, "Unable to determine map to preload flightmaster grid");
-}
-
-
-//
-// Unique1's ASTAR Pathfinding Code... For future use & reference...
-//
-
-#ifdef __PATHFINDING__
-
-int GetFCost(int to, int num, int parentNum, float *gcost); // Below...
-
-int ShortenASTARRoute(short int *pathlist, int number)
-{ // Wrote this to make the routes a little smarter (shorter)... No point looping back to the same places... Unique1
- short int temppathlist[MAX_PATHLIST_NODES];
- int count = 0;
- // int count2 = 0;
- int temp, temp2;
- int link;
- int upto = 0;
-
- for (temp = number; temp >= 0; temp--)
- {
- qboolean shortened = qfalse;
-
- for (temp2 = 0; temp2 < temp; temp2++)
- {
- for (link = 0; link < nodes[pathlist[temp]].enodenum; link++)
- {
- if (nodes[pathlist[temp]].links[link].flags & PATH_BLOCKED)
- continue;
-
- //if ((bot->client->ps.eFlags & EF_TANK) && nodes[bot->current_node].links[link].flags & PATH_NOTANKS) //if this path is blocked, skip it
- // continue;
-
- //if (nodes[nodes[pathlist[temp]].links[link].targetNode].origin[2] > nodes[pathlist[temp]].origin[2] + 32)
- // continue;
-
- if (nodes[pathlist[temp]].links[link].targetNode == pathlist[temp2])
- { // Found a shorter route...
- //if (OrgVisible(nodes[pathlist[temp2]].origin, nodes[pathlist[temp]].origin, -1))
- {
- temppathlist[count] = pathlist[temp2];
- temp = temp2;
- ++count;
- shortened = qtrue;
- }
- }
- }
- }
-
- if (!shortened)
- {
- temppathlist[count] = pathlist[temp];
- ++count;
- }
- }
-
- upto = count;
-
- for (temp = 0; temp < count; temp++)
- {
- pathlist[temp] = temppathlist[upto];
- --upto;
- }
-
- G_Printf("ShortenASTARRoute: Path size reduced from %i to %i nodes...n", number, count);
- return count;
-}
-
-/*
-===========================================================================
-CreatePathAStar
-This function uses the A* pathfinding algorithm to determine the
-shortest path between any two nodes.
-It's fairly complex, so I'm not really going to explain it much.
-Look up A* and binary heaps for more info.
-pathlist stores the ideal path between the nodes, in reverse order,
-and the return value is the number of nodes in that path
-===========================================================================
-*/
-int CreatePathAStar(gentity_t *bot, int from, int to, short int *pathlist)
-{
- //all the data we have to hold...since we can't do dynamic allocation, has to be MAX_NODES
- //we can probably lower this later - eg, the open list should never have more than at most a few dozen items on it
- short int openlist[MAX_NODES+1]; //add 1 because it's a binary heap, and they don't use 0 - 1 is the first used index
- float gcost[MAX_NODES];
- int fcost[MAX_NODES];
- char list[MAX_NODES]; //0 is neither, 1 is open, 2 is closed - char because it's the smallest data type
- short int parent[MAX_NODES];
-
- short int numOpen = 0;
- short int atNode, temp, newnode=-1;
- qboolean found = qfalse;
- int count = -1;
- float gc;
- int i, u, v, m;
- vec3_t vec;
-
- //clear out all the arrays
- memset(openlist, 0, sizeof(short int)*(MAX_NODES+1));
- memset(fcost, 0, sizeof(int)*MAX_NODES);
- memset(list, 0, sizeof(char)*MAX_NODES);
- memset(parent, 0, sizeof(short int)*MAX_NODES);
- memset(gcost, -1, sizeof(float)*MAX_NODES);
-
- //make sure we have valid data before calculating everything
- if ((from == NODE_INVALID) || (to == NODE_INVALID) || (from >= MAX_NODES) || (to >= MAX_NODES) || (from == to))
- return -1;
-
- openlist[1] = from; //add the starting node to the open list
- ++numOpen;
- gcost[from] = 0; //its f and g costs are obviously 0
- fcost[from] = 0;
-
- while (1)
- {
- if (numOpen != 0) //if there are still items in the open list
- {
- //pop the top item off of the list
- atNode = openlist[1];
- list[atNode] = 2; //put the node on the closed list so we don't check it again
- --numOpen;
-
- openlist[1] = openlist[numOpen+1]; //move the last item in the list to the top position
- v = 1;
-
- //this while loop reorders the list so that the new lowest fcost is at the top again
- while (1)
- {
- u = v;
- if ((2*u+1) < numOpen) //if both children exist
- {
- if (fcost[openlist[u]] >= fcost[openlist[2*u]])
- v = 2*u;
- if (fcost[openlist[v]] >= fcost[openlist[2*u+1]])
- v = 2*u+1;
- }
- else
- {
- if ((2*u) < numOpen) //if only one child exists
- {
- if (fcost[openlist[u]] >= fcost[openlist[2*u]])
- v = 2*u;
- }
- }
-
- if (u != v) //if they're out of order, swap this item with its parent
- {
- temp = openlist[u];
- openlist[u] = openlist[v];
- openlist[v] = temp;
- }
- else
- break;
- }
-
- for (i = 0; i < nodes[atNode].enodenum; ++i) //loop through all the links for this node
- {
- newnode = nodes[atNode].links[i].targetNode;
-
- //if this path is blocked, skip it
- if (nodes[atNode].links[i].flags & PATH_BLOCKED)
- continue;
- //if this path is blocked, skip it
- if (bot->client && (bot->client->ps.eFlags & EF_TANK) && nodes[atNode].links[i].flags & PATH_NOTANKS)
- continue;
- //skip any unreachable nodes
- if (bot->client && (nodes[newnode].type & NODE_ALLY_UNREACHABLE) && (bot->client->sess.sessionTeam == TEAM_ALLIES))
- continue;
- if (bot->client && (nodes[newnode].type & NODE_AXIS_UNREACHABLE) && (bot->client->sess.sessionTeam == TEAM_AXIS))
- continue;
-
- if (list[newnode] == 2) //if this node is on the closed list, skip it
- continue;
-
- if (list[newnode] != 1) //if this node is not already on the open list
- {
- openlist[++numOpen] = newnode; //add the new node to the open list
- list[newnode] = 1;
- parent[newnode] = atNode; //record the node's parent
-
- if (newnode == to) //if we've found the goal, don't keep computing paths!
- break; //this will break the 'for' and go all the way to 'if (list[to] == 1)'
-
- //store it's f cost value
- fcost[newnode] = GetFCost(to, newnode, parent[newnode], gcost);
-
- //this loop re-orders the heap so that the lowest fcost is at the top
- m = numOpen;
- while (m != 1) //while this item isn't at the top of the heap already
- {
- //if it has a lower fcost than its parent
- if (fcost[openlist[m]] <= fcost[openlist[m/2]])
- {
- temp = openlist[m/2];
- openlist[m/2] = openlist[m];
- openlist[m] = temp; //swap them
- m /= 2;
- }
- else
- break;
- }
- }
- else //if this node is already on the open list
- {
- gc = gcost[atNode];
- VectorSubtract(nodes[newnode].origin, nodes[atNode].origin, vec);
- gc += VectorLength(vec); //calculate what the gcost would be if we reached this node along the current path
-
- if (gc < gcost[newnode]) //if the new gcost is less (ie, this path is shorter than what we had before)
- {
- parent[newnode] = atNode; //set the new parent for this node
- gcost[newnode] = gc; //and the new g cost
-
- for (i = 1; i < numOpen; ++i) //loop through all the items on the open list
- {
- if (openlist[i] == newnode) //find this node in the list
- {
- //calculate the new fcost and store it
- fcost[newnode] = GetFCost(to, newnode, parent[newnode], gcost);
-
- //reorder the list again, with the lowest fcost item on top
- m = i;
- while (m != 1)
- {
- //if the item has a lower fcost than it's parent
- if (fcost[openlist[m]] < fcost[openlist[m/2]])
- {
- temp = openlist[m/2];
- openlist[m/2] = openlist[m];
- openlist[m] = temp; //swap them
- m /= 2;
- }
- else
- break;
- }
- break; //exit the 'for' loop because we already changed this node
- } //if
- } //for
- } //if (gc < gcost[newnode])
- } //if (list[newnode] != 1) --> else
- } //for (loop through links)
- } //if (numOpen != 0)
- else
- {
- found = qfalse; //there is no path between these nodes
- break;
- }
-
- if (list[to] == 1) //if the destination node is on the open list, we're done
- {
- found = qtrue;
- break;
- }
- } //while (1)
-
- if (found == qtrue) //if we found a path
- {
- //G_Printf("%s - path found!n", bot->client->pers.netname);
- count = 0;
-
- temp = to; //start at the end point
- while (temp != from) //travel along the path (backwards) until we reach the starting point
- {
- pathlist[count++] = temp; //add the node to the pathlist and increment the count
- temp = parent[temp]; //move to the parent of this node to continue the path
- }
-
- pathlist[count++] = from; //add the beginning node to the end of the pathlist
-
- #ifdef __BOT_SHORTEN_ROUTING__
- count = ShortenASTARRoute(pathlist, count); // This isn't working... Dunno why.. Unique1
- #endif //__BOT_SHORTEN_ROUTING__
- }
- else
- {
- //G_Printf("^1*** ^4BOT DEBUG^5: (CreatePathAStar) There is no route between node ^7%i^5 and node ^7%i^5.n", from, to);
- count = CreateDumbRoute(from, to, pathlist);
-
- if (count > 0)
- {
- #ifdef __BOT_SHORTEN_ROUTING__
- count = ShortenASTARRoute(pathlist, count); // This isn't working... Dunno why.. Unique1
- #endif //__BOT_SHORTEN_ROUTING__
- return count;
- }
- }
-
- return count; //return the number of nodes in the path, -1 if not found
-}
-
-/*
-===========================================================================
-GetFCost
-Utility function used by A* pathfinding to calculate the
-cost to move between nodes towards a goal. Using the A*
-algorithm F = G + H, G here is the distance along the node
-paths the bot must travel, and H is the straight-line distance
-to the goal node.
-Returned as an int because more precision is unnecessary and it
-will slightly speed up heap access
-===========================================================================
-*/
-int GetFCost(int to, int num, int parentNum, float *gcost)
-{
- float gc = 0;
- float hc = 0;
- vec3_t v;
-
- if (gcost[num] == -1)
- {
- if (parentNum != -1)
- {
- gc = gcost[parentNum];
- VectorSubtract(nodes[num].origin, nodes[parentNum].origin, v);
- gc += VectorLength(v);
- }
- gcost[num] = gc;
- }
- else
- gc = gcost[num];
-
- VectorSubtract(nodes[to].origin, nodes[num].origin, v);
- hc = VectorLength(v);
-
- return (int)(gc + hc);
-}
-#endif //__PATHFINDING__
-
+/*
+ * Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+//Basic headers
+#include "WaypointMovementGenerator.h"
+//Extended headers
+#include "ObjectMgr.h"
+#include "World.h"
+//Flightmaster grid preloading
+#include "MapManager.h"
+//Creature-specific headers
+#include "Creature.h"
+#include "CreatureAI.h"
+#include "CreatureGroups.h"
+//Player-specific
+#include "Player.h"
+#include "MoveSplineInit.h"
+#include "MoveSpline.h"
+
+void WaypointMovementGenerator<Creature>::LoadPath(Creature* creature)
+{
+ if (!path_id)
+ path_id = creature->GetWaypointPath();
+
+ i_path = sWaypointMgr->GetPath(path_id);
+
+ if (!i_path)
+ {
+ // No movement found for entry
+ sLog->outError(LOG_FILTER_SQL, "WaypointMovementGenerator::LoadPath: creature %s (Entry: %u GUID: %u) doesn't have waypoint path id: %u", creature->GetName().c_str(), creature->GetEntry(), creature->GetGUIDLow(), path_id);
+ return;
+ }
+
+ StartMoveNow(creature);
+}
+
+void WaypointMovementGenerator<Creature>::Initialize(Creature* creature)
+{
+ LoadPath(creature);
+ creature->AddUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
+}
+
+void WaypointMovementGenerator<Creature>::Finalize(Creature* creature)
+{
+ creature->ClearUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
+ creature->SetWalk(false);
+}
+
+void WaypointMovementGenerator<Creature>::Reset(Creature* creature)
+{
+ creature->AddUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
+ StartMoveNow(creature);
+}
+
+void WaypointMovementGenerator<Creature>::OnArrived(Creature* creature)
+{
+ if (!i_path || i_path->empty())
+ return;
+ if (m_isArrivalDone)
+ return;
+
+ creature->ClearUnitState(UNIT_STATE_ROAMING_MOVE);
+ m_isArrivalDone = true;
+
+ if (i_path->at(i_currentNode)->event_id && urand(0, 99) < i_path->at(i_currentNode)->event_chance)
+ {
+ sLog->outDebug(LOG_FILTER_MAPSCRIPTS, "Creature movement start script %u at point %u for "UI64FMTD".", i_path->at(i_currentNode)->event_id, i_currentNode, creature->GetGUID());
+ creature->GetMap()->ScriptsStart(sWaypointScripts, i_path->at(i_currentNode)->event_id, creature, NULL);
+ }
+
+ // Inform script
+ MovementInform(creature);
+ creature->UpdateWaypointID(i_currentNode);
+ Stop(i_path->at(i_currentNode)->delay);
+}
+
+bool WaypointMovementGenerator<Creature>::StartMove(Creature* creature)
+{
+ if (!i_path || i_path->empty())
+ return false;
+ if (Stopped())
+ return true;
+
+ if (m_isArrivalDone)
+ {
+ if ((i_currentNode == i_path->size() - 1) && !repeating) // If that's our last waypoint
+ {
+ creature->SetHomePosition(i_path->at(i_currentNode)->x, i_path->at(i_currentNode)->y, i_path->at(i_currentNode)->z, creature->GetOrientation());
+ creature->GetMotionMaster()->Initialize();
+ return false;
+ }
+
+ i_currentNode = (i_currentNode+1) % i_path->size();
+ }
+
+ WaypointData const* node = i_path->at(i_currentNode);
+
+ m_isArrivalDone = false;
+
+ creature->AddUnitState(UNIT_STATE_ROAMING_MOVE);
+
+ Movement::MoveSplineInit init(creature);
+ init.MoveTo(node->x, node->y, node->z);
+
+ //! Accepts angles such as 0.00001 and -0.00001, 0 must be ignored, default value in waypoint table
+ if (node->orientation && node->delay)
+ init.SetFacing(node->orientation);
+
+ init.SetWalk(!node->run);
+ init.Launch();
+
+ //Call for creature group update
+ if (creature->GetFormation() && creature->GetFormation()->getLeader() == creature)
+ creature->GetFormation()->LeaderMoveTo(node->x, node->y, node->z);
+
+ return true;
+}
+
+bool WaypointMovementGenerator<Creature>::Update(Creature* creature, uint32 diff)
+{
+ // Waypoint movement can be switched on/off
+ // This is quite handy for escort quests and other stuff
+ if (creature->HasUnitState(UNIT_STATE_NOT_MOVE))
+ {
+ creature->ClearUnitState(UNIT_STATE_ROAMING_MOVE);
+ return true;
+ }
+ // prevent a crash at empty waypoint path.
+ if (!i_path || i_path->empty())
+ return false;
+
+ if (Stopped())
+ {
+ if (CanMove(diff))
+ return StartMove(creature);
+ }
+ else
+ {
+ if (creature->IsStopped())
+ Stop(STOP_TIME_FOR_PLAYER);
+ else if (creature->movespline->Finalized())
+ {
+ OnArrived(creature);
+ return StartMove(creature);
+ }
+ }
+ return true;
+ }
+
+void WaypointMovementGenerator<Creature>::MovementInform(Creature* creature)
+{
+ if (creature->AI())
+ creature->AI()->MovementInform(WAYPOINT_MOTION_TYPE, i_currentNode);
+}
+
+bool WaypointMovementGenerator<Creature>::GetResetPosition(Creature*, float& x, float& y, float& z)
+{
+ // prevent a crash at empty waypoint path.
+ if (!i_path || i_path->empty())
+ return false;
+
+ const WaypointData* node = i_path->at(i_currentNode);
+ x = node->x; y = node->y; z = node->z;
+ return true;
+}
+
+
+//----------------------------------------------------//
+
+uint32 FlightPathMovementGenerator::GetPathAtMapEnd() const
+{
+ if (i_currentNode >= i_path->size())
+ return i_path->size();
+
+ uint32 curMapId = (*i_path)[i_currentNode].mapid;
+ for (uint32 i = i_currentNode; i < i_path->size(); ++i)
+ {
+ if ((*i_path)[i].mapid != curMapId)
+ return i;
+ }
+
+ return i_path->size();
+}
+
+void FlightPathMovementGenerator::Initialize(Player* player)
+{
+ Reset(player);
+ InitEndGridInfo();
+}
+
+void FlightPathMovementGenerator::Finalize(Player* player)
+{
+ // remove flag to prevent send object build movement packets for flight state and crash (movement generator already not at top of stack)
+ player->ClearUnitState(UNIT_STATE_IN_FLIGHT);
+
+ player->Dismount();
+ player->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_TAXI_FLIGHT);
+
+ if (player->m_taxi.empty())
+ {
+ player->getHostileRefManager().setOnlineOfflineState(true);
+ // update z position to ground and orientation for landing point
+ // this prevent cheating with landing point at lags
+ // when client side flight end early in comparison server side
+ player->StopMoving();
+ }
+}
+
+#define PLAYER_FLIGHT_SPEED 32.0f
+
+void FlightPathMovementGenerator::Reset(Player* player)
+{
+ player->getHostileRefManager().setOnlineOfflineState(false);
+ player->AddUnitState(UNIT_STATE_IN_FLIGHT);
+ player->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_TAXI_FLIGHT);
+
+ Movement::MoveSplineInit init(player);
+ uint32 end = GetPathAtMapEnd();
+ for (uint32 i = GetCurrentNode(); i != end; ++i)
+ {
+ G3D::Vector3 vertice((*i_path)[i].x, (*i_path)[i].y, (*i_path)[i].z);
+ init.Path().push_back(vertice);
+ }
+ init.SetFirstPointId(GetCurrentNode());
+ init.SetFly();
+ init.SetVelocity(PLAYER_FLIGHT_SPEED);
+ init.Launch();
+}
+
+bool FlightPathMovementGenerator::Update(Player* player, uint32 /*diff*/)
+{
+ uint32 pointId = (uint32)player->movespline->currentPathIdx();
+ if (pointId > i_currentNode)
+ {
+ bool departureEvent = true;
+ do
+ {
+ DoEventIfAny(player, (*i_path)[i_currentNode], departureEvent);
+ if (pointId == i_currentNode)
+ break;
+ if (i_currentNode == _preloadTargetNode)
+ PreloadEndGrid();
+ i_currentNode += (uint32)departureEvent;
+ departureEvent = !departureEvent;
+ }
+ while (true);
+ }
+
+ return i_currentNode < (i_path->size()-1);
+}
+
+void FlightPathMovementGenerator::SetCurrentNodeAfterTeleport()
+{
+ if (i_path->empty())
+ return;
+
+ uint32 map0 = (*i_path)[0].mapid;
+ for (size_t i = 1; i < i_path->size(); ++i)
+ {
+ if ((*i_path)[i].mapid != map0)
+ {
+ i_currentNode = i;
+ return;
+ }
+ }
+}
+
+void FlightPathMovementGenerator::DoEventIfAny(Player* player, TaxiPathNodeEntry const& node, bool departure)
+{
+ if (uint32 eventid = departure ? node.departureEventID : node.arrivalEventID)
+ {
+ sLog->outDebug(LOG_FILTER_MAPSCRIPTS, "Taxi %s event %u of node %u of path %u for player %s", departure ? "departure" : "arrival", eventid, node.index, node.path, player->GetName().c_str());
+ player->GetMap()->ScriptsStart(sEventScripts, eventid, player, player);
+ }
+}
+
+bool FlightPathMovementGenerator::GetResetPosition(Player*, float& x, float& y, float& z)
+{
+ const TaxiPathNodeEntry& node = (*i_path)[i_currentNode];
+ x = node.x; y = node.y; z = node.z;
+ return true;
+}
+
+void FlightPathMovementGenerator::InitEndGridInfo()
+{
+ /*! Storage to preload flightmaster grid at end of flight. For multi-stop flights, this will
+ be reinitialized for each flightmaster at the end of each spline (or stop) in the flight. */
+ uint32 nodeCount = (*i_path).size(); //! Number of nodes in path.
+ _endMapId = (*i_path)[nodeCount - 1].mapid; //! MapId of last node
+ _preloadTargetNode = nodeCount - 3;
+ _endGridX = (*i_path)[nodeCount - 1].x;
+ _endGridY = (*i_path)[nodeCount - 1].y;
+}
+
+void FlightPathMovementGenerator::PreloadEndGrid()
+{
+ // used to preload the final grid where the flightmaster is
+ Map* endMap = sMapMgr->FindBaseNonInstanceMap(_endMapId);
+
+ // Load the grid
+ if (endMap)
+ {
+ sLog->outInfo(LOG_FILTER_GENERAL, "Preloading rid (%f, %f) for map %u at node index %u/%u", _endGridX, _endGridY, _endMapId, _preloadTargetNode, (uint32)(i_path->size()-1));
+ endMap->LoadGrid(_endGridX, _endGridY);
+ }
+ else
+ sLog->outInfo(LOG_FILTER_GENERAL, "Unable to determine map to preload flightmaster grid");
+}
diff --git a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h
index 0bd7507..bf3f6f4 100644
--- a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h
+++ b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h
@@ -65,19 +65,19 @@ class WaypointMovementGenerator<Creature> : public MovementGeneratorMedium< Crea
WaypointMovementGenerator(uint32 _path_id = 0, bool _repeating = true)
: i_nextMoveTime(0), m_isArrivalDone(false), path_id(_path_id), repeating(_repeating) {}
~WaypointMovementGenerator() { i_path = NULL; }
- void DoInitialize(Creature &);
- void DoFinalize(Creature &);
- void DoReset(Creature &);
- bool DoUpdate(Creature &, uint32 diff);
+ void Initialize(Creature*);
+ void Finalize(Creature*);
+ void Reset(Creature*);
+ bool Update(Creature*, uint32 diff);
- void MovementInform(Creature &);
+ void MovementInform(Creature*);
MovementGeneratorType GetMovementGeneratorType() { return WAYPOINT_MOTION_TYPE; }
// now path movement implmementation
- void LoadPath(Creature &c);
+ void LoadPath(Creature*);
- bool GetResetPosition(Creature&, float& x, float& y, float& z);
+ bool GetResetPosition(Creature*, float& x, float& y, float& z);
private:
@@ -91,10 +91,10 @@ class WaypointMovementGenerator<Creature> : public MovementGeneratorMedium< Crea
return i_nextMoveTime.Passed();
}
- void OnArrived(Creature&);
- bool StartMove(Creature&);
+ void OnArrived(Creature*);
+ bool StartMove(Creature*);
- void StartMoveNow(Creature& creature)
+ void StartMoveNow(Creature* creature)
{
i_nextMoveTime.Reset(0);
StartMove(creature);
@@ -118,10 +118,10 @@ class FlightPathMovementGenerator : public MovementGeneratorMedium< Player, Flig
i_path = &pathnodes;
i_currentNode = startNode;
}
- void DoInitialize(Player &);
- void DoReset(Player &);
- void DoFinalize(Player &);
- bool DoUpdate(Player &, uint32);
+ void Initialize(Player*);
+ void Reset(Player*);
+ void Finalize(Player*);
+ bool Update(Player*, uint32);
MovementGeneratorType GetMovementGeneratorType() { return FLIGHT_MOTION_TYPE; }
TaxiPathNodeList const& GetPath() { return *i_path; }
@@ -129,9 +129,9 @@ class FlightPathMovementGenerator : public MovementGeneratorMedium< Player, Flig
bool HasArrived() const { return (i_currentNode >= i_path->size()); }
void SetCurrentNodeAfterTeleport();
void SkipCurrentNode() { ++i_currentNode; }
- void DoEventIfAny(Player& player, TaxiPathNodeEntry const& node, bool departure);
+ void DoEventIfAny(Player* player, TaxiPathNodeEntry const& node, bool departure);
- bool GetResetPosition(Player&, float& x, float& y, float& z);
+ bool GetResetPosition(Player*, float& x, float& y, float& z);
void InitEndGridInfo();
void PreloadEndGrid();
diff --git a/src/server/game/Movement/PathGenerator.cpp b/src/server/game/Movement/PathGenerator.cpp
new file mode 100644
index 0000000..71617f6
--- /dev/null
+++ b/src/server/game/Movement/PathGenerator.cpp
@@ -0,0 +1,803 @@
+/*
+ * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "PathGenerator.h"
+#include "Map.h"
+#include "Creature.h"
+#include "MMapFactory.h"
+#include "MMapManager.h"
+#include "Log.h"
+
+#include "DetourCommon.h"
+#include "DetourNavMeshQuery.h"
+
+////////////////// PathGenerator //////////////////
+PathGenerator::PathGenerator(const Unit* owner) :
+ _polyLength(0), _type(PATHFIND_BLANK),
+ _useStraightPath(false), _forceDestination(false), _pointPathLimit(MAX_POINT_PATH_LENGTH),
+ _sourceUnit(owner), _navMesh(NULL), _navMeshQuery(NULL), _endPosition(Vector3::zero())
+{
+ sLog->outDebug(LOG_FILTER_MAPS, "++ PathGenerator::PathGenerator for %u \n", _sourceUnit->GetGUIDLow());
+
+ uint32 mapId = _sourceUnit->GetMapId();
+ if (MMAP::MMapFactory::IsPathfindingEnabled(mapId))
+ {
+ MMAP::MMapManager* mmap = MMAP::MMapFactory::createOrGetMMapManager();
+ _navMesh = mmap->GetNavMesh(mapId);
+ _navMeshQuery = mmap->GetNavMeshQuery(mapId, _sourceUnit->GetInstanceId());
+ }
+
+ CreateFilter();
+}
+
+PathGenerator::~PathGenerator()
+{
+ sLog->outDebug(LOG_FILTER_MAPS, "++ PathGenerator::~PathGenerator() for %u \n", _sourceUnit->GetGUIDLow());
+}
+
+bool PathGenerator::CalculatePath(float destX, float destY, float destZ, bool forceDest)
+{
+ float x, y, z;
+ _sourceUnit->GetPosition(x, y, z);
+
+ if (!Trinity::IsValidMapCoord(destX, destY, destZ) || !Trinity::IsValidMapCoord(x, y, z))
+ return false;
+
+ Vector3 dest(destX, destY, destZ);
+ SetEndPosition(dest);
+
+ Vector3 start(x, y, z);
+ SetStartPosition(start);
+
+ _forceDestination = forceDest;
+
+ sLog->outDebug(LOG_FILTER_MAPS, "++ PathGenerator::CalculatePath() for %u \n", _sourceUnit->GetGUIDLow());
+
+ // make sure navMesh works - we can run on map w/o mmap
+ // check if the start and end point have a .mmtile loaded (can we pass via not loaded tile on the way?)
+ if (!_navMesh || !_navMeshQuery || _sourceUnit->HasUnitState(UNIT_STATE_IGNORE_PATHFINDING) ||
+ !HaveTile(start) || !HaveTile(dest))
+ {
+ BuildShortcut();
+ _type = PathType(PATHFIND_NORMAL | PATHFIND_NOT_USING_PATH);
+ return true;
+ }
+
+ UpdateFilter();
+
+ BuildPolyPath(start, dest);
+ return true;
+}
+
+dtPolyRef PathGenerator::GetPathPolyByPosition(dtPolyRef const* polyPath, uint32 polyPathSize, float const* point, float* distance) const
+{
+ if (!polyPath || !polyPathSize)
+ return INVALID_POLYREF;
+
+ dtPolyRef nearestPoly = INVALID_POLYREF;
+ float minDist2d = FLT_MAX;
+ float minDist3d = 0.0f;
+
+ for (uint32 i = 0; i < polyPathSize; ++i)
+ {
+ float closestPoint[VERTEX_SIZE];
+ if (DT_SUCCESS != _navMeshQuery->closestPointOnPoly(polyPath[i], point, closestPoint))
+ continue;
+
+ float d = dtVdist2DSqr(point, closestPoint);
+ if (d < minDist2d)
+ {
+ minDist2d = d;
+ nearestPoly = polyPath[i];
+ minDist3d = dtVdistSqr(point, closestPoint);
+ }
+
+ if (minDist2d < 1.0f) // shortcut out - close enough for us
+ break;
+ }
+
+ if (distance)
+ *distance = dtSqrt(minDist3d);
+
+ return (minDist2d < 3.0f) ? nearestPoly : INVALID_POLYREF;
+}
+
+dtPolyRef PathGenerator::GetPolyByLocation(float const* point, float* distance) const
+{
+ // first we check the current path
+ // if the current path doesn't contain the current poly,
+ // we need to use the expensive navMesh.findNearestPoly
+ dtPolyRef polyRef = GetPathPolyByPosition(_pathPolyRefs, _polyLength, point, distance);
+ if (polyRef != INVALID_POLYREF)
+ return polyRef;
+
+ // we don't have it in our old path
+ // try to get it by findNearestPoly()
+ // first try with low search box
+ float extents[VERTEX_SIZE] = {3.0f, 5.0f, 3.0f}; // bounds of poly search area
+ float closestPoint[VERTEX_SIZE] = {0.0f, 0.0f, 0.0f};
+ dtStatus result = _navMeshQuery->findNearestPoly(point, extents, &_filter, &polyRef, closestPoint);
+ if (DT_SUCCESS == result && polyRef != INVALID_POLYREF)
+ {
+ *distance = dtVdist(closestPoint, point);
+ return polyRef;
+ }
+
+ // still nothing ..
+ // try with bigger search box
+ extents[1] = 200.0f;
+ result = _navMeshQuery->findNearestPoly(point, extents, &_filter, &polyRef, closestPoint);
+ if (DT_SUCCESS == result && polyRef != INVALID_POLYREF)
+ {
+ *distance = dtVdist(closestPoint, point);
+ return polyRef;
+ }
+
+ return INVALID_POLYREF;
+}
+
+void PathGenerator::BuildPolyPath(Vector3 const& startPos, Vector3 const& endPos)
+{
+ // *** getting start/end poly logic ***
+
+ float distToStartPoly, distToEndPoly;
+ float startPoint[VERTEX_SIZE] = {startPos.y, startPos.z, startPos.x};
+ float endPoint[VERTEX_SIZE] = {endPos.y, endPos.z, endPos.x};
+
+ dtPolyRef startPoly = GetPolyByLocation(startPoint, &distToStartPoly);
+ dtPolyRef endPoly = GetPolyByLocation(endPoint, &distToEndPoly);
+
+ // we have a hole in our mesh
+ // make shortcut path and mark it as NOPATH ( with flying and swimming exception )
+ // its up to caller how he will use this info
+ if (startPoly == INVALID_POLYREF || endPoly == INVALID_POLYREF)
+ {
+ sLog->outDebug(LOG_FILTER_MAPS, "++ BuildPolyPath :: (startPoly == 0 || endPoly == 0)\n");
+ BuildShortcut();
+ bool path = _sourceUnit->GetTypeId() == TYPEID_UNIT && _sourceUnit->ToCreature()->CanFly();
+
+ bool waterPath = _sourceUnit->GetTypeId() == TYPEID_UNIT && _sourceUnit->ToCreature()->canSwim();
+ if (waterPath)
+ {
+ // Check both start and end points, if they're both in water, then we can *safely* let the creature move
+ for (uint32 i = 0; i < _pathPoints.size(); ++i)
+ {
+ ZLiquidStatus status = _sourceUnit->GetBaseMap()->getLiquidStatus(_pathPoints[i].x, _pathPoints[i].y, _pathPoints[i].z, MAP_ALL_LIQUIDS, NULL);
+ // One of the points is not in the water, cancel movement.
+ if (status == LIQUID_MAP_NO_WATER)
+ {
+ waterPath = false;
+ break;
+ }
+ }
+ }
+
+ _type = (path || waterPath) ? PathType(PATHFIND_NORMAL | PATHFIND_NOT_USING_PATH) : PATHFIND_NOPATH;
+ return;
+ }
+
+ // we may need a better number here
+ bool farFromPoly = (distToStartPoly > 7.0f || distToEndPoly > 7.0f);
+ if (farFromPoly)
+ {
+ sLog->outDebug(LOG_FILTER_MAPS, "++ BuildPolyPath :: farFromPoly distToStartPoly=%.3f distToEndPoly=%.3f\n", distToStartPoly, distToEndPoly);
+
+ bool buildShotrcut = false;
+ if (_sourceUnit->GetTypeId() == TYPEID_UNIT)
+ {
+ Creature* owner = (Creature*)_sourceUnit;
+
+ Vector3 p = (distToStartPoly > 7.0f) ? startPos : endPos;
+ if (_sourceUnit->GetBaseMap()->IsUnderWater(p.x, p.y, p.z))
+ {
+ sLog->outDebug(LOG_FILTER_MAPS, "++ BuildPolyPath :: underWater case\n");
+ if (owner->canSwim())
+ buildShotrcut = true;
+ }
+ else
+ {
+ sLog->outDebug(LOG_FILTER_MAPS, "++ BuildPolyPath :: flying case\n");
+ if (owner->CanFly())
+ buildShotrcut = true;
+ }
+ }
+
+ if (buildShotrcut)
+ {
+ BuildShortcut();
+ _type = PathType(PATHFIND_NORMAL | PATHFIND_NOT_USING_PATH);
+ return;
+ }
+ else
+ {
+ float closestPoint[VERTEX_SIZE];
+ // we may want to use closestPointOnPolyBoundary instead
+ if (DT_SUCCESS == _navMeshQuery->closestPointOnPoly(endPoly, endPoint, closestPoint))
+ {
+ dtVcopy(endPoint, closestPoint);
+ SetActualEndPosition(Vector3(endPoint[2], endPoint[0], endPoint[1]));
+ }
+
+ _type = PATHFIND_INCOMPLETE;
+ }
+ }
+
+ // *** poly path generating logic ***
+
+ // start and end are on same polygon
+ // just need to move in straight line
+ if (startPoly == endPoly)
+ {
+ sLog->outDebug(LOG_FILTER_MAPS, "++ BuildPolyPath :: (startPoly == endPoly)\n");
+
+ BuildShortcut();
+
+ _pathPolyRefs[0] = startPoly;
+ _polyLength = 1;
+
+ _type = farFromPoly ? PATHFIND_INCOMPLETE : PATHFIND_NORMAL;
+ sLog->outDebug(LOG_FILTER_MAPS, "++ BuildPolyPath :: path type %d\n", _type);
+ return;
+ }
+
+ // look for startPoly/endPoly in current path
+ // TODO: we can merge it with getPathPolyByPosition() loop
+ bool startPolyFound = false;
+ bool endPolyFound = false;
+ uint32 pathStartIndex = 0;
+ uint32 pathEndIndex = 0;
+
+ if (_polyLength)
+ {
+ for (; pathStartIndex < _polyLength; ++pathStartIndex)
+ {
+ // here to carch few bugs
+ ASSERT(_pathPolyRefs[pathStartIndex] != INVALID_POLYREF);
+
+ if (_pathPolyRefs[pathStartIndex] == startPoly)
+ {
+ startPolyFound = true;
+ break;
+ }
+ }
+
+ for (pathEndIndex = _polyLength-1; pathEndIndex > pathStartIndex; --pathEndIndex)
+ if (_pathPolyRefs[pathEndIndex] == endPoly)
+ {
+ endPolyFound = true;
+ break;
+ }
+ }
+
+ if (startPolyFound && endPolyFound)
+ {
+ sLog->outDebug(LOG_FILTER_MAPS, "++ BuildPolyPath :: (startPolyFound && endPolyFound)\n");
+
+ // we moved along the path and the target did not move out of our old poly-path
+ // our path is a simple subpath case, we have all the data we need
+ // just "cut" it out
+
+ _polyLength = pathEndIndex - pathStartIndex + 1;
+ memmove(_pathPolyRefs, _pathPolyRefs + pathStartIndex, _polyLength * sizeof(dtPolyRef));
+ }
+ else if (startPolyFound && !endPolyFound)
+ {
+ sLog->outDebug(LOG_FILTER_MAPS, "++ BuildPolyPath :: (startPolyFound && !endPolyFound)\n");
+
+ // we are moving on the old path but target moved out
+ // so we have atleast part of poly-path ready
+
+ _polyLength -= pathStartIndex;
+
+ // try to adjust the suffix of the path instead of recalculating entire length
+ // at given interval the target cannot get too far from its last location
+ // thus we have less poly to cover
+ // sub-path of optimal path is optimal
+
+ // take ~80% of the original length
+ // TODO : play with the values here
+ uint32 prefixPolyLength = uint32(_polyLength * 0.8f + 0.5f);
+ memmove(_pathPolyRefs, _pathPolyRefs+pathStartIndex, prefixPolyLength * sizeof(dtPolyRef));
+
+ dtPolyRef suffixStartPoly = _pathPolyRefs[prefixPolyLength-1];
+
+ // we need any point on our suffix start poly to generate poly-path, so we need last poly in prefix data
+ float suffixEndPoint[VERTEX_SIZE];
+ if (DT_SUCCESS != _navMeshQuery->closestPointOnPoly(suffixStartPoly, endPoint, suffixEndPoint))
+ {
+ // we can hit offmesh connection as last poly - closestPointOnPoly() don't like that
+ // try to recover by using prev polyref
+ --prefixPolyLength;
+ suffixStartPoly = _pathPolyRefs[prefixPolyLength-1];
+ if (DT_SUCCESS != _navMeshQuery->closestPointOnPoly(suffixStartPoly, endPoint, suffixEndPoint))
+ {
+ // suffixStartPoly is still invalid, error state
+ BuildShortcut();
+ _type = PATHFIND_NOPATH;
+ return;
+ }
+ }
+
+ // generate suffix
+ uint32 suffixPolyLength = 0;
+ dtStatus dtResult = _navMeshQuery->findPath(
+ suffixStartPoly, // start polygon
+ endPoly, // end polygon
+ suffixEndPoint, // start position
+ endPoint, // end position
+ &_filter, // polygon search filter
+ _pathPolyRefs + prefixPolyLength - 1, // [out] path
+ (int*)&suffixPolyLength,
+ MAX_PATH_LENGTH-prefixPolyLength); // max number of polygons in output path
+
+ if (!suffixPolyLength || dtResult != DT_SUCCESS)
+ {
+ // this is probably an error state, but we'll leave it
+ // and hopefully recover on the next Update
+ // we still need to copy our preffix
+ sLog->outError(LOG_FILTER_MAPS, "%u's Path Build failed: 0 length path", _sourceUnit->GetGUIDLow());
+ }
+
+ sLog->outDebug(LOG_FILTER_MAPS, "++ m_polyLength=%u prefixPolyLength=%u suffixPolyLength=%u \n", _polyLength, prefixPolyLength, suffixPolyLength);
+
+ // new path = prefix + suffix - overlap
+ _polyLength = prefixPolyLength + suffixPolyLength - 1;
+ }
+ else
+ {
+ sLog->outDebug(LOG_FILTER_MAPS, "++ BuildPolyPath :: (!startPolyFound && !endPolyFound)\n");
+
+ // either we have no path at all -> first run
+ // or something went really wrong -> we aren't moving along the path to the target
+ // just generate new path
+
+ // free and invalidate old path data
+ Clear();
+
+ dtStatus dtResult = _navMeshQuery->findPath(
+ startPoly, // start polygon
+ endPoly, // end polygon
+ startPoint, // start position
+ endPoint, // end position
+ &_filter, // polygon search filter
+ _pathPolyRefs, // [out] path
+ (int*)&_polyLength,
+ MAX_PATH_LENGTH); // max number of polygons in output path
+
+ if (!_polyLength || dtResult != DT_SUCCESS)
+ {
+ // only happens if we passed bad data to findPath(), or navmesh is messed up
+ sLog->outError(LOG_FILTER_MAPS, "%u's Path Build failed: 0 length path", _sourceUnit->GetGUIDLow());
+ BuildShortcut();
+ _type = PATHFIND_NOPATH;
+ return;
+ }
+ }
+
+ // by now we know what type of path we can get
+ if (_pathPolyRefs[_polyLength - 1] == endPoly && !(_type & PATHFIND_INCOMPLETE))
+ _type = PATHFIND_NORMAL;
+ else
+ _type = PATHFIND_INCOMPLETE;
+
+ // generate the point-path out of our up-to-date poly-path
+ BuildPointPath(startPoint, endPoint);
+}
+
+void PathGenerator::BuildPointPath(const float *startPoint, const float *endPoint)
+{
+ float pathPoints[MAX_POINT_PATH_LENGTH*VERTEX_SIZE];
+ uint32 pointCount = 0;
+ dtStatus dtResult = DT_FAILURE;
+ if (_useStraightPath)
+ {
+ dtResult = _navMeshQuery->findStraightPath(
+ startPoint, // start position
+ endPoint, // end position
+ _pathPolyRefs, // current path
+ _polyLength, // lenth of current path
+ pathPoints, // [out] path corner points
+ NULL, // [out] flags
+ NULL, // [out] shortened path
+ (int*)&pointCount,
+ _pointPathLimit); // maximum number of points/polygons to use
+ }
+ else
+ {
+ dtResult = FindSmoothPath(
+ startPoint, // start position
+ endPoint, // end position
+ _pathPolyRefs, // current path
+ _polyLength, // length of current path
+ pathPoints, // [out] path corner points
+ (int*)&pointCount,
+ _pointPathLimit); // maximum number of points
+ }
+
+ if (pointCount < 2 || dtResult != DT_SUCCESS)
+ {
+ // only happens if pass bad data to findStraightPath or navmesh is broken
+ // single point paths can be generated here
+ // TODO : check the exact cases
+ sLog->outDebug(LOG_FILTER_MAPS, "++ PathGenerator::BuildPointPath FAILED! path sized %d returned\n", pointCount);
+ BuildShortcut();
+ _type = PATHFIND_NOPATH;
+ return;
+ }
+ else if (pointCount == _pointPathLimit)
+ {
+ sLog->outDebug(LOG_FILTER_MAPS, "++ PathGenerator::BuildPointPath FAILED! path sized %d returned, lower than limit set to %d\n", pointCount, _pointPathLimit);
+ BuildShortcut();
+ _type = PATHFIND_SHORT;
+ return;
+ }
+
+ _pathPoints.resize(pointCount);
+ for (uint32 i = 0; i < pointCount; ++i)
+ _pathPoints[i] = Vector3(pathPoints[i*VERTEX_SIZE+2], pathPoints[i*VERTEX_SIZE], pathPoints[i*VERTEX_SIZE+1]);
+
+ NormalizePath();
+
+ // first point is always our current location - we need the next one
+ SetActualEndPosition(_pathPoints[pointCount-1]);
+
+ // force the given destination, if needed
+ if (_forceDestination &&
+ (!(_type & PATHFIND_NORMAL) || !InRange(GetEndPosition(), GetActualEndPosition(), 1.0f, 1.0f)))
+ {
+ // we may want to keep partial subpath
+ if (Dist3DSqr(GetActualEndPosition(), GetEndPosition()) < 0.3f * Dist3DSqr(GetStartPosition(), GetEndPosition()))
+ {
+ SetActualEndPosition(GetEndPosition());
+ _pathPoints[_pathPoints.size()-1] = GetEndPosition();
+ }
+ else
+ {
+ SetActualEndPosition(GetEndPosition());
+ BuildShortcut();
+ }
+
+ _type = PathType(PATHFIND_NORMAL | PATHFIND_NOT_USING_PATH);
+ }
+
+ sLog->outDebug(LOG_FILTER_MAPS, "++ PathGenerator::BuildPointPath path type %d size %d poly-size %d\n", _type, pointCount, _polyLength);
+}
+
+void PathGenerator::NormalizePath()
+{
+ for (uint32 i = 0; i < _pathPoints.size(); ++i)
+ _sourceUnit->UpdateAllowedPositionZ(_pathPoints[i].x, _pathPoints[i].y, _pathPoints[i].z);
+}
+
+void PathGenerator::BuildShortcut()
+{
+ sLog->outDebug(LOG_FILTER_MAPS, "++ BuildShortcut :: making shortcut\n");
+
+ Clear();
+
+ // make two point path, our curr pos is the start, and dest is the end
+ _pathPoints.resize(2);
+
+ // set start and a default next position
+ _pathPoints[0] = GetStartPosition();
+ _pathPoints[1] = GetActualEndPosition();
+
+ NormalizePath();
+
+ _type = PATHFIND_SHORTCUT;
+}
+
+void PathGenerator::CreateFilter()
+{
+ uint16 includeFlags = 0;
+ uint16 excludeFlags = 0;
+
+ if (_sourceUnit->GetTypeId() == TYPEID_UNIT)
+ {
+ Creature* creature = (Creature*)_sourceUnit;
+ if (creature->canWalk())
+ includeFlags |= NAV_GROUND; // walk
+
+ // creatures don't take environmental damage
+ if (creature->canSwim())
+ includeFlags |= (NAV_WATER | NAV_MAGMA | NAV_SLIME); // swim
+ }
+ else // assume Player
+ {
+ // perfect support not possible, just stay 'safe'
+ includeFlags |= (NAV_GROUND | NAV_WATER | NAV_MAGMA | NAV_SLIME);
+ }
+
+ _filter.setIncludeFlags(includeFlags);
+ _filter.setExcludeFlags(excludeFlags);
+
+ UpdateFilter();
+}
+
+void PathGenerator::UpdateFilter()
+{
+ // allow creatures to cheat and use different movement types if they are moved
+ // forcefully into terrain they can't normally move in
+ if (_sourceUnit->IsInWater() || _sourceUnit->IsUnderWater())
+ {
+ uint16 includedFlags = _filter.getIncludeFlags();
+ includedFlags |= GetNavTerrain(_sourceUnit->GetPositionX(),
+ _sourceUnit->GetPositionY(),
+ _sourceUnit->GetPositionZ());
+
+ _filter.setIncludeFlags(includedFlags);
+ }
+}
+
+NavTerrain PathGenerator::GetNavTerrain(float x, float y, float z)
+{
+ LiquidData data;
+ _sourceUnit->GetBaseMap()->getLiquidStatus(x, y, z, MAP_ALL_LIQUIDS, &data);
+
+ switch (data.type_flags)
+ {
+ case MAP_LIQUID_TYPE_WATER:
+ case MAP_LIQUID_TYPE_OCEAN:
+ return NAV_WATER;
+ case MAP_LIQUID_TYPE_MAGMA:
+ return NAV_MAGMA;
+ case MAP_LIQUID_TYPE_SLIME:
+ return NAV_SLIME;
+ default:
+ return NAV_GROUND;
+ }
+}
+
+bool PathGenerator::HaveTile(const Vector3& p) const
+{
+ int tx = -1, ty = -1;
+ float point[VERTEX_SIZE] = {p.y, p.z, p.x};
+
+ _navMesh->calcTileLoc(point, &tx, &ty);
+
+ /// Workaround
+ /// For some reason, often the tx and ty variables wont get a valid value
+ /// Use this check to prevent getting negative tile coords and crashing on getTileAt
+ if (tx < 0 || ty < 0)
+ return false;
+
+ return (_navMesh->getTileAt(tx, ty) != NULL);
+}
+
+uint32 PathGenerator::FixupCorridor(dtPolyRef* path, uint32 npath, uint32 maxPath, dtPolyRef const* visited, uint32 nvisited)
+{
+ int32 furthestPath = -1;
+ int32 furthestVisited = -1;
+
+ // Find furthest common polygon.
+ for (int32 i = npath-1; i >= 0; --i)
+ {
+ bool found = false;
+ for (int32 j = nvisited-1; j >= 0; --j)
+ {
+ if (path[i] == visited[j])
+ {
+ furthestPath = i;
+ furthestVisited = j;
+ found = true;
+ }
+ }
+ if (found)
+ break;
+ }
+
+ // If no intersection found just return current path.
+ if (furthestPath == -1 || furthestVisited == -1)
+ return npath;
+
+ // Concatenate paths.
+
+ // Adjust beginning of the buffer to include the visited.
+ uint32 req = nvisited - furthestVisited;
+ uint32 orig = uint32(furthestPath + 1) < npath ? furthestPath + 1 : npath;
+ uint32 size = npath > orig ? npath - orig : 0;
+ if (req + size > maxPath)
+ size = maxPath-req;
+
+ if (size)
+ memmove(path + req, path + orig, size * sizeof(dtPolyRef));
+
+ // Store visited
+ for (uint32 i = 0; i < req; ++i)
+ path[i] = visited[(nvisited - 1) - i];
+
+ return req+size;
+}
+
+bool PathGenerator::GetSteerTarget(float const* startPos, float const* endPos,
+ float minTargetDist, dtPolyRef const* path, uint32 pathSize,
+ float* steerPos, unsigned char& steerPosFlag, dtPolyRef& steerPosRef)
+{
+ // Find steer target.
+ static const uint32 MAX_STEER_POINTS = 3;
+ float steerPath[MAX_STEER_POINTS*VERTEX_SIZE];
+ unsigned char steerPathFlags[MAX_STEER_POINTS];
+ dtPolyRef steerPathPolys[MAX_STEER_POINTS];
+ uint32 nsteerPath = 0;
+ dtStatus dtResult = _navMeshQuery->findStraightPath(startPos, endPos, path, pathSize,
+ steerPath, steerPathFlags, steerPathPolys, (int*)&nsteerPath, MAX_STEER_POINTS);
+ if (!nsteerPath || DT_SUCCESS != dtResult)
+ return false;
+
+ // Find vertex far enough to steer to.
+ uint32 ns = 0;
+ while (ns < nsteerPath)
+ {
+ // Stop at Off-Mesh link or when point is further than slop away.
+ if ((steerPathFlags[ns] & DT_STRAIGHTPATH_OFFMESH_CONNECTION) ||
+ !InRangeYZX(&steerPath[ns*VERTEX_SIZE], startPos, minTargetDist, 1000.0f))
+ break;
+ ns++;
+ }
+ // Failed to find good point to steer to.
+ if (ns >= nsteerPath)
+ return false;
+
+ dtVcopy(steerPos, &steerPath[ns*VERTEX_SIZE]);
+ steerPos[1] = startPos[1]; // keep Z value
+ steerPosFlag = steerPathFlags[ns];
+ steerPosRef = steerPathPolys[ns];
+
+ return true;
+}
+
+dtStatus PathGenerator::FindSmoothPath(float const* startPos, float const* endPos,
+ dtPolyRef const* polyPath, uint32 polyPathSize,
+ float* smoothPath, int* smoothPathSize, uint32 maxSmoothPathSize)
+{
+ *smoothPathSize = 0;
+ uint32 nsmoothPath = 0;
+
+ dtPolyRef polys[MAX_PATH_LENGTH];
+ memcpy(polys, polyPath, sizeof(dtPolyRef)*polyPathSize);
+ uint32 npolys = polyPathSize;
+
+ float iterPos[VERTEX_SIZE], targetPos[VERTEX_SIZE];
+ if (DT_SUCCESS != _navMeshQuery->closestPointOnPolyBoundary(polys[0], startPos, iterPos))
+ return DT_FAILURE;
+
+ if (DT_SUCCESS != _navMeshQuery->closestPointOnPolyBoundary(polys[npolys-1], endPos, targetPos))
+ return DT_FAILURE;
+
+ dtVcopy(&smoothPath[nsmoothPath*VERTEX_SIZE], iterPos);
+ nsmoothPath++;
+
+ // Move towards target a small advancement at a time until target reached or
+ // when ran out of memory to store the path.
+ while (npolys && nsmoothPath < maxSmoothPathSize)
+ {
+ // Find location to steer towards.
+ float steerPos[VERTEX_SIZE];
+ unsigned char steerPosFlag;
+ dtPolyRef steerPosRef = INVALID_POLYREF;
+
+ if (!GetSteerTarget(iterPos, targetPos, SMOOTH_PATH_SLOP, polys, npolys, steerPos, steerPosFlag, steerPosRef))
+ break;
+
+ bool endOfPath = (steerPosFlag & DT_STRAIGHTPATH_END);
+ bool offMeshConnection = (steerPosFlag & DT_STRAIGHTPATH_OFFMESH_CONNECTION);
+
+ // Find movement delta.
+ float delta[VERTEX_SIZE];
+ dtVsub(delta, steerPos, iterPos);
+ float len = dtSqrt(dtVdot(delta,delta));
+ // If the steer target is end of path or off-mesh link, do not move past the location.
+ if ((endOfPath || offMeshConnection) && len < SMOOTH_PATH_STEP_SIZE)
+ len = 1.0f;
+ else
+ len = SMOOTH_PATH_STEP_SIZE / len;
+
+ float moveTgt[VERTEX_SIZE];
+ dtVmad(moveTgt, iterPos, delta, len);
+
+ // Move
+ float result[VERTEX_SIZE];
+ const static uint32 MAX_VISIT_POLY = 16;
+ dtPolyRef visited[MAX_VISIT_POLY];
+
+ uint32 nvisited = 0;
+ _navMeshQuery->moveAlongSurface(polys[0], iterPos, moveTgt, &_filter, result, visited, (int*)&nvisited, MAX_VISIT_POLY);
+ npolys = FixupCorridor(polys, npolys, MAX_PATH_LENGTH, visited, nvisited);
+
+ _navMeshQuery->getPolyHeight(polys[0], result, &result[1]);
+ result[1] += 0.5f;
+ dtVcopy(iterPos, result);
+
+ // Handle end of path and off-mesh links when close enough.
+ if (endOfPath && InRangeYZX(iterPos, steerPos, SMOOTH_PATH_SLOP, 1.0f))
+ {
+ // Reached end of path.
+ dtVcopy(iterPos, targetPos);
+ if (nsmoothPath < maxSmoothPathSize)
+ {
+ dtVcopy(&smoothPath[nsmoothPath*VERTEX_SIZE], iterPos);
+ nsmoothPath++;
+ }
+ break;
+ }
+ else if (offMeshConnection && InRangeYZX(iterPos, steerPos, SMOOTH_PATH_SLOP, 1.0f))
+ {
+ // Advance the path up to and over the off-mesh connection.
+ dtPolyRef prevRef = INVALID_POLYREF;
+ dtPolyRef polyRef = polys[0];
+ uint32 npos = 0;
+ while (npos < npolys && polyRef != steerPosRef)
+ {
+ prevRef = polyRef;
+ polyRef = polys[npos];
+ npos++;
+ }
+
+ for (uint32 i = npos; i < npolys; ++i)
+ polys[i-npos] = polys[i];
+
+ npolys -= npos;
+
+ // Handle the connection.
+ float startPos[VERTEX_SIZE], endPos[VERTEX_SIZE];
+ if (DT_SUCCESS == _navMesh->getOffMeshConnectionPolyEndPoints(prevRef, polyRef, startPos, endPos))
+ {
+ if (nsmoothPath < maxSmoothPathSize)
+ {
+ dtVcopy(&smoothPath[nsmoothPath*VERTEX_SIZE], startPos);
+ nsmoothPath++;
+ }
+ // Move position at the other side of the off-mesh link.
+ dtVcopy(iterPos, endPos);
+ _navMeshQuery->getPolyHeight(polys[0], iterPos, &iterPos[1]);
+ iterPos[1] += 0.5f;
+ }
+ }
+
+ // Store results.
+ if (nsmoothPath < maxSmoothPathSize)
+ {
+ dtVcopy(&smoothPath[nsmoothPath*VERTEX_SIZE], iterPos);
+ nsmoothPath++;
+ }
+ }
+
+ *smoothPathSize = nsmoothPath;
+
+ // this is most likely a loop
+ return nsmoothPath < MAX_POINT_PATH_LENGTH ? DT_SUCCESS : DT_FAILURE;
+}
+
+bool PathGenerator::InRangeYZX(const float* v1, const float* v2, float r, float h) const
+{
+ const float dx = v2[0] - v1[0];
+ const float dy = v2[1] - v1[1]; // elevation
+ const float dz = v2[2] - v1[2];
+ return (dx * dx + dz * dz) < r * r && fabsf(dy) < h;
+}
+
+bool PathGenerator::InRange(Vector3 const& p1, Vector3 const& p2, float r, float h) const
+{
+ Vector3 d = p1 - p2;
+ return (d.x * d.x + d.y * d.y) < r * r && fabsf(d.z) < h;
+}
+
+float PathGenerator::Dist3DSqr(Vector3 const& p1, Vector3 const& p2) const
+{
+ return (p1 - p2).squaredLength();
+}
diff --git a/src/server/game/Movement/PathGenerator.h b/src/server/game/Movement/PathGenerator.h
new file mode 100644
index 0000000..a20f900
--- /dev/null
+++ b/src/server/game/Movement/PathGenerator.h
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _PATH_GENERATOR_H
+#define _PATH_GENERATOR_H
+
+#include "SharedDefines.h"
+#include "DetourNavMesh.h"
+#include "DetourNavMeshQuery.h"
+#include "MoveSplineInitArgs.h"
+
+using Movement::Vector3;
+using Movement::PointsArray;
+
+class Unit;
+
+// 74*4.0f=296y number_of_points*interval = max_path_len
+// this is way more than actual evade range
+// I think we can safely cut those down even more
+#define MAX_PATH_LENGTH 74
+#define MAX_POINT_PATH_LENGTH 74
+
+#define SMOOTH_PATH_STEP_SIZE 4.0f
+#define SMOOTH_PATH_SLOP 0.3f
+
+#define VERTEX_SIZE 3
+#define INVALID_POLYREF 0
+
+enum PathType
+{
+ PATHFIND_BLANK = 0x00, // path not built yet
+ PATHFIND_NORMAL = 0x01, // normal path
+ PATHFIND_SHORTCUT = 0x02, // travel through obstacles, terrain, air, etc (old behavior)
+ PATHFIND_INCOMPLETE = 0x04, // we have partial path to follow - getting closer to target
+ PATHFIND_NOPATH = 0x08, // no valid path at all or error in generating one
+ PATHFIND_NOT_USING_PATH = 0x10, // used when we are either flying/swiming or on map w/o mmaps
+ PATHFIND_SHORT = 0x20, // path is longer or equal to its limited path length
+};
+
+class PathGenerator
+{
+ public:
+ PathGenerator(Unit const* owner);
+ ~PathGenerator();
+
+ // Calculate the path from owner to given destination
+ // return: true if new path was calculated, false otherwise (no change needed)
+ bool CalculatePath(float destX, float destY, float destZ, bool forceDest = false);
+
+ // option setters - use optional
+ void SetUseStraightPath(bool useStraightPath) { _useStraightPath = useStraightPath; };
+ void SetPathLengthLimit(float distance) { _pointPathLimit = std::min<uint32>(uint32(distance/SMOOTH_PATH_STEP_SIZE), MAX_POINT_PATH_LENGTH); };
+
+ // result getters
+ Vector3 const& GetStartPosition() const { return _startPosition; }
+ Vector3 const& GetEndPosition() const { return _endPosition; }
+ Vector3 const& GetActualEndPosition() const { return _actualEndPosition; }
+
+ PointsArray& GetPath() { return _pathPoints; }
+ PathType GetPathType() const { return _type; }
+
+ private:
+
+ dtPolyRef _pathPolyRefs[MAX_PATH_LENGTH]; // array of detour polygon references
+ uint32 _polyLength; // number of polygons in the path
+
+ PointsArray _pathPoints; // our actual (x,y,z) path to the target
+ PathType _type; // tells what kind of path this is
+
+ bool _useStraightPath; // type of path will be generated
+ bool _forceDestination; // when set, we will always arrive at given point
+ uint32 _pointPathLimit; // limit point path size; min(this, MAX_POINT_PATH_LENGTH)
+
+ Vector3 _startPosition; // {x, y, z} of current location
+ Vector3 _endPosition; // {x, y, z} of the destination
+ Vector3 _actualEndPosition;// {x, y, z} of the closest possible point to given destination
+
+ Unit const* const _sourceUnit; // the unit that is moving
+ dtNavMesh const* _navMesh; // the nav mesh
+ dtNavMeshQuery const* _navMeshQuery; // the nav mesh query used to find the path
+
+ dtQueryFilter _filter; // use single filter for all movements, update it when needed
+
+ void SetStartPosition(Vector3 Point) { _startPosition = Point; }
+ void SetEndPosition(Vector3 Point) { _actualEndPosition = Point; _endPosition = Point; }
+ void SetActualEndPosition(Vector3 Point) { _actualEndPosition = Point; }
+ void NormalizePath();
+
+ void Clear()
+ {
+ _polyLength = 0;
+ _pathPoints.clear();
+ }
+
+ bool InRange(Vector3 const& p1, Vector3 const& p2, float r, float h) const;
+ float Dist3DSqr(Vector3 const& p1, Vector3 const& p2) const;
+ bool InRangeYZX(float const* v1, float const* v2, float r, float h) const;
+
+ dtPolyRef GetPathPolyByPosition(dtPolyRef const* polyPath, uint32 polyPathSize, float const* Point, float* Distance = NULL) const;
+ dtPolyRef GetPolyByLocation(float const* Point, float* Distance) const;
+ bool HaveTile(Vector3 const& p) const;
+
+ void BuildPolyPath(Vector3 const& startPos, Vector3 const& endPos);
+ void BuildPointPath(float const* startPoint, float const* endPoint);
+ void BuildShortcut();
+
+ NavTerrain GetNavTerrain(float x, float y, float z);
+ void CreateFilter();
+ void UpdateFilter();
+
+ // smooth path aux functions
+ uint32 FixupCorridor(dtPolyRef* path, uint32 npath, uint32 maxPath, dtPolyRef const* visited, uint32 nvisited);
+ bool GetSteerTarget(float const* startPos, float const* endPos, float minTargetDist, dtPolyRef const* path, uint32 pathSize, float* steerPos,
+ unsigned char& steerPosFlag, dtPolyRef& steerPosRef);
+ dtStatus FindSmoothPath(float const* startPos, float const* endPos,
+ dtPolyRef const* polyPath, uint32 polyPathSize,
+ float* smoothPath, int* smoothPathSize, uint32 smoothPathMaxSize);
+};
+
+#endif
diff --git a/src/server/game/Movement/Spline/MoveSpline.h b/src/server/game/Movement/Spline/MoveSpline.h
index 46611e5..938466c 100644
--- a/src/server/game/Movement/Spline/MoveSpline.h
+++ b/src/server/game/Movement/Spline/MoveSpline.h
@@ -78,18 +78,17 @@ namespace Movement
UpdateResult _updateState(int32& ms_time_diff);
int32 next_timestamp() const { return spline.length(point_Idx+1); }
int32 segment_time_elapsed() const { return next_timestamp()-time_passed; }
- int32 Duration() const { return spline.length(); }
int32 timeElapsed() const { return Duration() - time_passed; }
int32 timePassed() const { return time_passed; }
public:
+ int32 Duration() const { return spline.length(); }
const MySpline& _Spline() const { return spline; }
int32 _currentSplineIdx() const { return point_Idx; }
void _Finalize();
void _Interrupt() { splineflags.done = true;}
public:
-
void Initialize(const MoveSplineInitArgs&);
bool Initialized() const { return !spline.empty(); }
diff --git a/src/server/game/Movement/Spline/MoveSplineInit.cpp b/src/server/game/Movement/Spline/MoveSplineInit.cpp
index df0b3e3..e2a18d2 100644
--- a/src/server/game/Movement/Spline/MoveSplineInit.cpp
+++ b/src/server/game/Movement/Spline/MoveSplineInit.cpp
@@ -58,38 +58,38 @@ namespace Movement
return MOVE_RUN;
}
- void MoveSplineInit::Launch()
+ int32 MoveSplineInit::Launch()
{
- MoveSpline& move_spline = *unit.movespline;
+ MoveSpline& move_spline = *unit->movespline;
bool transport = false;
- Location real_position(unit.GetPositionX(), unit.GetPositionY(), unit.GetPositionZMinusOffset(), unit.GetOrientation());
+ Location real_position(unit->GetPositionX(), unit->GetPositionY(), unit->GetPositionZMinusOffset(), unit->GetOrientation());
// Elevators also use MOVEMENTFLAG_ONTRANSPORT but we do not keep track of their position changes
- if (unit.HasUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT) && unit.GetTransGUID())
+ if (unit->HasUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT) && unit->GetTransGUID())
{
transport = true;
- real_position.x = unit.GetTransOffsetX();
- real_position.y = unit.GetTransOffsetY();
- real_position.z = unit.GetTransOffsetZ();
- real_position.orientation = unit.GetTransOffsetO();
+ real_position.x = unit->GetTransOffsetX();
+ real_position.y = unit->GetTransOffsetY();
+ real_position.z = unit->GetTransOffsetZ();
+ real_position.orientation = unit->GetTransOffsetO();
}
// there is a big chance that current position is unknown if current state is not finalized, need compute it
- // this also allows calculate spline position and update map position in much greater intervals
+ // this also allows CalculatePath spline position and update map position in much greater intervals
// Don't compute for transport movement if the unit is in a motion between two transports
if (!move_spline.Finalized() && move_spline.onTransport == transport)
real_position = move_spline.ComputePosition();
// should i do the things that user should do? - no.
if (args.path.empty())
- return;
+ return 0;
// corrent first vertex
args.path[0] = real_position;
args.initialOrientation = real_position.orientation;
move_spline.onTransport = transport;
- uint32 moveFlags = unit.m_movementInfo.GetMovementFlags();
+ uint32 moveFlags = unit->m_movementInfo.GetMovementFlags();
if (args.flags.walkmode)
moveFlags |= MOVEMENTFLAG_WALKING;
else
@@ -98,39 +98,41 @@ namespace Movement
moveFlags |= (MOVEMENTFLAG_SPLINE_ENABLED|MOVEMENTFLAG_FORWARD);
if (!args.HasVelocity)
- args.velocity = unit.GetSpeed(SelectSpeedType(moveFlags));
+ args.velocity = unit->GetSpeed(SelectSpeedType(moveFlags));
- if (!args.Validate(&unit))
- return;
+ if (!args.Validate(unit))
+ return 0;
if (moveFlags & MOVEMENTFLAG_ROOT)
moveFlags &= ~MOVEMENTFLAG_MASK_MOVING;
- unit.m_movementInfo.SetMovementFlags((MovementFlags)moveFlags);
+ unit->m_movementInfo.SetMovementFlags((MovementFlags)moveFlags);
move_spline.Initialize(args);
WorldPacket data(!transport ? SMSG_MONSTER_MOVE : SMSG_MONSTER_MOVE_TRANSPORT, 64);
- data.append(unit.GetPackGUID());
+ data.append(unit->GetPackGUID());
if (transport)
{
- data.appendPackGUID(unit.GetTransGUID());
- data << int8(unit.GetTransSeat());
+ data.appendPackGUID(unit->GetTransGUID());
+ data << int8(unit->GetTransSeat());
}
PacketBuilder::WriteMonsterMove(move_spline, data);
- unit.SendMessageToSet(&data,true);
+ unit->SendMessageToSet(&data,true);
+
+ return move_spline.Duration();
}
- MoveSplineInit::MoveSplineInit(Unit& m) : unit(m)
+ MoveSplineInit::MoveSplineInit(Unit* m) : unit(m)
{
// Elevators also use MOVEMENTFLAG_ONTRANSPORT but we do not keep track of their position changes
- args.TransformForTransport = unit.HasUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT) && unit.GetTransGUID();
+ args.TransformForTransport = unit->HasUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT) && unit->GetTransGUID();
// mix existing state into new
- args.flags.walkmode = unit.m_movementInfo.HasMovementFlag(MOVEMENTFLAG_WALKING);
- args.flags.flying = unit.m_movementInfo.HasMovementFlag((MovementFlags)(MOVEMENTFLAG_CAN_FLY|MOVEMENTFLAG_DISABLE_GRAVITY));
+ args.flags.walkmode = unit->m_movementInfo.HasMovementFlag(MOVEMENTFLAG_WALKING);
+ args.flags.flying = unit->m_movementInfo.HasMovementFlag((MovementFlags)(MOVEMENTFLAG_CAN_FLY | MOVEMENTFLAG_DISABLE_GRAVITY));
}
- void MoveSplineInit::SetFacing(const Unit * target)
+ void MoveSplineInit::SetFacing(const Unit* target)
{
args.flags.EnableFacingTarget();
args.facing.target = target->GetGUID();
@@ -140,9 +142,9 @@ namespace Movement
{
if (args.TransformForTransport)
{
- if (Unit* vehicle = unit.GetVehicleBase())
+ if (Unit* vehicle = unit->GetVehicleBase())
angle -= vehicle->GetOrientation();
- else if (Transport* transport = unit.GetTransport())
+ else if (Transport* transport = unit->GetTransport())
angle -= transport->GetOrientation();
}
@@ -150,8 +152,19 @@ namespace Movement
args.flags.EnableFacingAngle();
}
- void MoveSplineInit::MoveTo(Vector3 const& dest)
+ void MoveSplineInit::MoveTo(const Vector3& dest, bool generatePath, bool forceDestination)
{
+ if (generatePath)
+ {
+ PathGenerator path(unit);
+ bool result = path.CalculatePath(dest.x, dest.y, dest.z, forceDestination);
+ if (result && path.GetPathType() & ~PATHFIND_NOPATH)
+ {
+ MovebyPath(path.GetPath());
+ return;
+ }
+ }
+
args.path_Idx_offset = 0;
args.path.resize(2);
TransportPathTransform transform(unit, args.TransformForTransport);
@@ -163,7 +176,7 @@ namespace Movement
if (_transformForTransport)
{
float unused = 0.0f;
- if (TransportBase* transport = _owner.GetDirectTransport())
+ if (TransportBase* transport = _owner->GetDirectTransport())
transport->CalculatePassengerOffset(input.x, input.y, input.z, unused);
}
diff --git a/src/server/game/Movement/Spline/MoveSplineInit.h b/src/server/game/Movement/Spline/MoveSplineInit.h
index cae9f13..4972f6d 100644
--- a/src/server/game/Movement/Spline/MoveSplineInit.h
+++ b/src/server/game/Movement/Spline/MoveSplineInit.h
@@ -20,6 +20,7 @@
#define TRINITYSERVER_MOVESPLINEINIT_H
#include "MoveSplineInitArgs.h"
+#include "PathGenerator.h"
class Unit;
@@ -37,12 +38,12 @@ namespace Movement
class TransportPathTransform
{
public:
- TransportPathTransform(Unit& owner, bool transformForTransport)
+ TransportPathTransform(Unit* owner, bool transformForTransport)
: _owner(owner), _transformForTransport(transformForTransport) { }
Vector3 operator()(Vector3 input);
private:
- Unit& _owner;
+ Unit* _owner;
bool _transformForTransport;
};
@@ -52,11 +53,11 @@ namespace Movement
{
public:
- explicit MoveSplineInit(Unit& m);
+ explicit MoveSplineInit(Unit* m);
/* Final pass of initialization that launches spline movement.
*/
- void Launch();
+ int32 Launch();
/* Adds movement by parabolic trajectory
* @param amplitude - the maximum height of parabola, value could be negative and positive
@@ -75,7 +76,7 @@ namespace Movement
*/
void SetFacing(float angle);
void SetFacing(Vector3 const& point);
- void SetFacing(const Unit * target);
+ void SetFacing(const Unit* target);
/* Initializes movement by path
* @param path - array of points, shouldn't be empty
@@ -83,10 +84,10 @@ namespace Movement
*/
void MovebyPath(const PointsArray& path, int32 pointId = 0);
- /* Initializes simple A to B mition, A is current unit's position, B is destination
+ /* Initializes simple A to B motion, A is current unit's position, B is destination
*/
- void MoveTo(const Vector3& destination);
- void MoveTo(float x, float y, float z);
+ void MoveTo(const Vector3& destination, bool generatePath = true, bool forceDestination = false);
+ void MoveTo(float x, float y, float z, bool generatePath = true, bool forceDestination = false);
/* Sets Id of fisrt point of the path. When N-th path point will be done ILisener will notify that pointId + N done
* Needed for waypoint movement where path splitten into parts
@@ -137,7 +138,7 @@ namespace Movement
protected:
MoveSplineInitArgs args;
- Unit& unit;
+ Unit* unit;
};
inline void MoveSplineInit::SetFly() { args.flags.EnableFlying(); }
@@ -158,10 +159,10 @@ namespace Movement
std::transform(controls.begin(), controls.end(), args.path.begin(), TransportPathTransform(unit, args.TransformForTransport));
}
- inline void MoveSplineInit::MoveTo(float x, float y, float z)
+ inline void MoveSplineInit::MoveTo(float x, float y, float z, bool generatePath, bool forceDestination)
{
Vector3 v(x, y, z);
- MoveTo(v);
+ MoveTo(v, generatePath, forceDestination);
}
inline void MoveSplineInit::SetParabolic(float amplitude, float time_shift)
diff --git a/src/server/game/Movement/Waypoints/Path.h b/src/server/game/Movement/Waypoints/Path.h
index b6ddaa9..0389585 100644
--- a/src/server/game/Movement/Waypoints/Path.h
+++ b/src/server/game/Movement/Waypoints/Path.h
@@ -20,10 +20,12 @@
#define TRINITYCORE_PATH_H
#include "Common.h"
-#include <vector>
+#include <deque>
-struct SimplePathNode
+struct PathNode
{
+ PathNode(): x(0.0f), y(0.0f), z(0.0f) { }
+ PathNode(float _x, float _y, float _z): x(_x), y(_y), z(_z) { }
float x, y, z;
};
template<typename PathElem, typename PathNode = PathElem>
@@ -36,6 +38,20 @@ class Path
void resize(unsigned int sz) { i_nodes.resize(sz); }
void clear() { i_nodes.clear(); }
void erase(uint32 idx) { i_nodes.erase(i_nodes.begin()+idx); }
+ void crop(unsigned int start, unsigned int end)
+ {
+ while(start && !i_nodes.empty())
+ {
+ i_nodes.pop_front();
+ --start;
+ }
+
+ while(end && !i_nodes.empty())
+ {
+ i_nodes.pop_back();
+ --end;
+ }
+ }
float GetTotalLength(uint32 start, uint32 end) const
{
@@ -76,10 +92,10 @@ class Path
void set(size_t idx, PathElem elem) { i_nodes[idx] = elem; }
protected:
- std::vector<PathElem> i_nodes;
+ std::deque<PathElem> i_nodes;
};
-typedef Path<SimplePathNode> SimplePath;
+typedef Path<PathNode> SimplePath;
#endif
diff --git a/src/server/game/Scripting/ScriptLoader.cpp b/src/server/game/Scripting/ScriptLoader.cpp
index 08b1495..a41ebc0 100644
--- a/src/server/game/Scripting/ScriptLoader.cpp
+++ b/src/server/game/Scripting/ScriptLoader.cpp
@@ -76,6 +76,7 @@ void AddSC_tele_commandscript();
void AddSC_ticket_commandscript();
void AddSC_titles_commandscript();
void AddSC_wp_commandscript();
+void AddSC_mmaps_commandscript();
#ifdef SCRIPTS
//world
@@ -700,6 +701,7 @@ void AddCommandScripts()
AddSC_ticket_commandscript();
AddSC_titles_commandscript();
AddSC_wp_commandscript();
+ AddSC_mmaps_commandscript();
}
void AddWorldScripts()
diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp
index bb51d9d..aa0104d 100644
--- a/src/server/game/Spells/Spell.cpp
+++ b/src/server/game/Spells/Spell.cpp
@@ -490,7 +490,7 @@ SpellValue::SpellValue(SpellInfo const* proto)
Spell::Spell(Unit* caster, SpellInfo const* info, TriggerCastFlags triggerFlags, uint64 originalCasterGUID, bool skipCheck) :
m_spellInfo(sSpellMgr->GetSpellForDifficultyFromSpell(info, caster)),
m_caster((info->AttributesEx6 & SPELL_ATTR6_CAST_BY_CHARMER && caster->GetCharmerOrOwner()) ? caster->GetCharmerOrOwner() : caster)
-, m_spellValue(new SpellValue(m_spellInfo))
+, m_spellValue(new SpellValue(m_spellInfo)), m_preGeneratedPath(PathGenerator(m_caster))
{
m_customError = SPELL_CUSTOM_ERROR_NONE;
m_skipCheck = skipCheck;
@@ -610,6 +610,7 @@ Spell::~Spell()
if (m_caster && m_caster->GetTypeId() == TYPEID_PLAYER)
ASSERT(m_caster->ToPlayer()->m_spellModTakingSpell != this);
+
delete m_spellValue;
CheckEffectExecuteData();
@@ -5142,12 +5143,30 @@ SpellCastResult Spell::CheckCast(bool strict)
if (strict && m_caster->IsScriptOverriden(m_spellInfo, 6953))
m_caster->RemoveMovementImpairingAuras();
}
+
if (m_caster->HasUnitState(UNIT_STATE_ROOT))
return SPELL_FAILED_ROOTED;
+
+ Unit* target = m_targets.GetUnitTarget();
+
+ if (!target)
+ return SPELL_FAILED_DONT_REPORT;
+
if (m_caster->GetTypeId() == TYPEID_PLAYER)
- if (Unit* target = m_targets.GetUnitTarget())
- if (!target->isAlive())
- return SPELL_FAILED_BAD_TARGETS;
+ if (!target->isAlive())
+ return SPELL_FAILED_BAD_TARGETS;
+
+ Position pos;
+ target->GetContactPoint(m_caster, pos.m_positionX, pos.m_positionY, pos.m_positionZ);
+ target->GetFirstCollisionPosition(pos, CONTACT_DISTANCE, target->GetRelativeAngle(m_caster));
+
+ m_preGeneratedPath.SetPathLengthLimit(m_spellInfo->GetMaxRange(true) * 1.5f);
+ bool result = m_preGeneratedPath.CalculatePath(pos.m_positionX, pos.m_positionY, pos.m_positionZ + target->GetObjectSize());
+ if (m_preGeneratedPath.GetPathType() & PATHFIND_SHORT)
+ return SPELL_FAILED_OUT_OF_RANGE;
+ else if (!result)
+ return SPELL_FAILED_NOPATH;
+
break;
}
case SPELL_EFFECT_SKINNING:
diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h
index a892d6c..c43ee40 100644
--- a/src/server/game/Spells/Spell.h
+++ b/src/server/game/Spells/Spell.h
@@ -23,6 +23,7 @@
#include "SharedDefines.h"
#include "ObjectMgr.h"
#include "SpellInfo.h"
+#include "PathGenerator.h"
class Unit;
class Player;
@@ -665,6 +666,7 @@ class Spell
bool m_skipCheck;
uint8 m_auraScaleMask;
+ PathGenerator m_preGeneratedPath;
ByteBuffer * m_effectExecuteData[MAX_SPELL_EFFECTS];
diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp
index a642d40..8bf1647 100644
--- a/src/server/game/Spells/SpellEffects.cpp
+++ b/src/server/game/Spells/SpellEffects.cpp
@@ -63,6 +63,7 @@
#include "GameObjectAI.h"
#include "AccountMgr.h"
#include "InstanceScript.h"
+#include "PathGenerator.h"
#include "ReputationMgr.h"
pEffect SpellEffects[TOTAL_SPELL_EFFECTS]=
@@ -5097,25 +5098,24 @@ void Spell::EffectSkinning(SpellEffIndex /*effIndex*/)
void Spell::EffectCharge(SpellEffIndex /*effIndex*/)
{
+ if (!unitTarget)
+ return;
+
if (effectHandleMode == SPELL_EFFECT_HANDLE_LAUNCH_TARGET)
{
- if (!unitTarget)
- return;
-
- float angle = unitTarget->GetRelativeAngle(m_caster);
- Position pos;
-
- unitTarget->GetContactPoint(m_caster, pos.m_positionX, pos.m_positionY, pos.m_positionZ);
- unitTarget->GetFirstCollisionPosition(pos, unitTarget->GetObjectSize(), angle);
-
- m_caster->GetMotionMaster()->MoveCharge(pos.m_positionX, pos.m_positionY, pos.m_positionZ + unitTarget->GetObjectSize());
+ if (m_preGeneratedPath.GetPathType() & PATHFIND_NOPATH)
+ {
+ Position pos;
+ unitTarget->GetContactPoint(m_caster, pos.m_positionX, pos.m_positionY, pos.m_positionZ);
+ unitTarget->GetFirstCollisionPosition(pos, unitTarget->GetObjectSize(), unitTarget->GetRelativeAngle(m_caster));
+ m_caster->GetMotionMaster()->MoveCharge(pos.m_positionX, pos.m_positionY, pos.m_positionZ);
+ }
+ else
+ m_caster->GetMotionMaster()->MoveCharge(m_preGeneratedPath);
}
if (effectHandleMode == SPELL_EFFECT_HANDLE_HIT_TARGET)
{
- if (!unitTarget)
- return;
-
// not all charge effects used in negative spells
if (!m_spellInfo->IsPositive() && m_caster->GetTypeId() == TYPEID_PLAYER)
m_caster->Attack(unitTarget, true);
diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp
index 7824a72..67693f2 100644
--- a/src/server/game/World/World.cpp
+++ b/src/server/game/World/World.cpp
@@ -21,6 +21,7 @@
*/
#include "Common.h"
+#include "Memory.h"
#include "DatabaseEnv.h"
#include "Config.h"
#include "SystemConfig.h"
@@ -54,6 +55,7 @@
#include "TemporarySummon.h"
#include "WaypointMovementGenerator.h"
#include "VMapFactory.h"
+#include "MMapFactory.h"
#include "GameEventMgr.h"
#include "PoolMgr.h"
#include "GridNotifiersImpl.h"
@@ -135,6 +137,7 @@ World::~World()
delete command;
VMAP::VMapFactory::clear();
+ MMAP::MMapFactory::clear();
//TODO free addSessQueue
}
@@ -1129,11 +1132,14 @@ void World::LoadConfigSettings(bool reload)
sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Using DataDir %s", m_dataPath.c_str());
}
+ m_bool_configs[CONFIG_ENABLE_MMAPS] = ConfigMgr::GetBoolDefault("mmap.enablePathFinding", true);
+ sLog->outInfo(LOG_FILTER_SERVER_LOADING, "WORLD: MMap data directory is: %smmaps", m_dataPath.c_str());
+ MMAP::MMapFactory::preventPathfindingOnMaps(ConfigMgr::GetStringDefault("mmap.ignoreMapIds", "").c_str());
+
m_bool_configs[CONFIG_VMAP_INDOOR_CHECK] = ConfigMgr::GetBoolDefault("vmap.enableIndoorCheck", 0);
bool enableIndoor = ConfigMgr::GetBoolDefault("vmap.enableIndoorCheck", true);
bool enableLOS = ConfigMgr::GetBoolDefault("vmap.enableLOS", true);
bool enableHeight = ConfigMgr::GetBoolDefault("vmap.enableHeight", true);
- bool enablePetLOS = ConfigMgr::GetBoolDefault("vmap.petLOS", true);
std::string ignoreSpellIds = ConfigMgr::GetStringDefault("vmap.ignoreSpellIds", "");
if (!enableHeight)
@@ -1142,11 +1148,10 @@ void World::LoadConfigSettings(bool reload)
VMAP::VMapFactory::createOrGetVMapManager()->setEnableLineOfSightCalc(enableLOS);
VMAP::VMapFactory::createOrGetVMapManager()->setEnableHeightCalc(enableHeight);
VMAP::VMapFactory::preventSpellsFromBeingTestedForLoS(ignoreSpellIds.c_str());
- sLog->outInfo(LOG_FILTER_SERVER_LOADING, "VMap support included. LineOfSight:%i, getHeight:%i, indoorCheck:%i PetLOS:%i", enableLOS, enableHeight, enableIndoor, enablePetLOS);
+ sLog->outInfo(LOG_FILTER_SERVER_LOADING, "VMap support included. LineOfSight:%i, getHeight:%i, indoorCheck:%i", enableLOS, enableHeight, enableIndoor);
sLog->outInfo(LOG_FILTER_SERVER_LOADING, "VMap data directory is: %svmaps", m_dataPath.c_str());
m_int_configs[CONFIG_MAX_WHO] = ConfigMgr::GetIntDefault("MaxWhoListReturns", 49);
- m_bool_configs[CONFIG_PET_LOS] = ConfigMgr::GetBoolDefault("vmap.petLOS", true);
m_bool_configs[CONFIG_START_ALL_SPELLS] = ConfigMgr::GetBoolDefault("PlayerStart.AllSpells", false);
if (m_bool_configs[CONFIG_START_ALL_SPELLS])
sLog->outWarn(LOG_FILTER_SERVER_LOADING, "PlayerStart.AllSpells enabled - may not function as intended!");
@@ -1238,6 +1243,9 @@ void World::SetInitialWorldSettings()
///- Initialize the random number generator
srand((unsigned int)time(NULL));
+ ///- Initialize detour memory management
+ dtAllocSetCustom(dtCustomAlloc, dtCustomFree);
+
///- Initialize config settings
LoadConfigSettings();
diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h
index c541945..afc2690 100644
--- a/src/server/game/World/World.h
+++ b/src/server/game/World/World.h
@@ -138,7 +138,6 @@ enum WorldBoolConfigs
CONFIG_ARENA_LOG_EXTENDED_INFO,
CONFIG_OFFHAND_CHECK_AT_SPELL_UNLEARN,
CONFIG_VMAP_INDOOR_CHECK,
- CONFIG_PET_LOS,
CONFIG_START_ALL_SPELLS,
CONFIG_START_ALL_EXPLORED,
CONFIG_START_ALL_REP,
@@ -164,6 +163,7 @@ enum WorldBoolConfigs
CONFIG_QUEST_IGNORE_AUTO_ACCEPT,
CONFIG_QUEST_IGNORE_AUTO_COMPLETE,
CONFIG_WARDEN_ENABLED,
+ CONFIG_ENABLE_MMAPS,
CONFIG_WINTERGRASP_ENABLE,
BOOL_CONFIG_VALUE_COUNT
};
diff --git a/src/server/scripts/CMakeLists.txt b/src/server/scripts/CMakeLists.txt
index 8e636ee..b41c379 100644
--- a/src/server/scripts/CMakeLists.txt
+++ b/src/server/scripts/CMakeLists.txt
@@ -45,6 +45,8 @@ message("")
include_directories(
${CMAKE_BINARY_DIR}
+ ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Detour
+ ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Recast
${CMAKE_SOURCE_DIR}/dep/g3dlite/include
${CMAKE_SOURCE_DIR}/dep/SFMT
${CMAKE_SOURCE_DIR}/dep/zlib
diff --git a/src/server/scripts/Commands/CMakeLists.txt b/src/server/scripts/Commands/CMakeLists.txt
index a65324a..e7e2e43 100644
--- a/src/server/scripts/Commands/CMakeLists.txt
+++ b/src/server/scripts/Commands/CMakeLists.txt
@@ -42,6 +42,7 @@ set(scripts_STAT_SRCS
Commands/cs_server.cpp
Commands/cs_titles.cpp
Commands/cs_wp.cpp
+ Commands/cs_mmaps.cpp
# Commands/cs_pdump.cpp
# Commands/cs_channel.cpp
# Commands/cs_pet.cpp
diff --git a/src/server/scripts/Commands/cs_mmaps.cpp b/src/server/scripts/Commands/cs_mmaps.cpp
new file mode 100644
index 0000000..c17c01a
--- /dev/null
+++ b/src/server/scripts/Commands/cs_mmaps.cpp
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+* @file cs_mmaps.cpp
+* @brief .mmap related commands
+*
+* This file contains the CommandScripts for all
+* mmap sub-commands
+*/
+
+#include "ScriptMgr.h"
+#include "Chat.h"
+#include "ObjectMgr.h"
+#include "Player.h"
+#include "PointMovementGenerator.h"
+#include "PathGenerator.h"
+#include "MMapFactory.h"
+#include "Map.h"
+#include "TargetedMovementGenerator.h"
+#include "GridNotifiers.h"
+#include "GridNotifiersImpl.h"
+#include "CellImpl.h"
+
+class mmaps_commandscript : public CommandScript
+{
+public:
+ mmaps_commandscript() : CommandScript("mmaps_commandscript") { }
+
+ ChatCommand* GetCommands() const
+ {
+ static ChatCommand mmapCommandTable[] =
+ {
+ { "path", SEC_ADMINISTRATOR, false, &HandleMmapPathCommand, "", NULL },
+ { "loc", SEC_ADMINISTRATOR, false, &HandleMmapLocCommand, "", NULL },
+ { "loadedtiles", SEC_ADMINISTRATOR, false, &HandleMmapLoadedTilesCommand, "", NULL },
+ { "stats", SEC_ADMINISTRATOR, false, &HandleMmapStatsCommand, "", NULL },
+ { "testarea", SEC_ADMINISTRATOR, false, &HandleMmapTestArea, "", NULL },
+ { NULL, 0, false, NULL, "", NULL }
+ };
+
+ static ChatCommand commandTable[] =
+ {
+ { "mmap", SEC_ADMINISTRATOR, true, NULL, "", mmapCommandTable },
+ { NULL, 0, false, NULL, "", NULL }
+ };
+ return commandTable;
+ }
+
+ static bool HandleMmapPathCommand(ChatHandler* handler, char const* args)
+ {
+ if (!MMAP::MMapFactory::createOrGetMMapManager()->GetNavMesh(handler->GetSession()->GetPlayer()->GetMapId()))
+ {
+ handler->PSendSysMessage("NavMesh not loaded for current map.");
+ return true;
+ }
+
+ handler->PSendSysMessage("mmap path:");
+
+ // units
+ Player* player = handler->GetSession()->GetPlayer();
+ Unit* target = handler->getSelectedUnit();
+ if (!player || !target)
+ {
+ handler->PSendSysMessage("Invalid target/source selection.");
+ return true;
+ }
+
+ char* para = strtok((char*)args, " ");
+
+ bool useStraightPath = false;
+ if (para && strcmp(para, "true") == 0)
+ useStraightPath = true;
+
+ // unit locations
+ float x, y, z;
+ player->GetPosition(x, y, z);
+
+ // path
+ PathGenerator path(target);
+ path.SetUseStraightPath(useStraightPath);
+ bool result = path.CalculatePath(x, y, z);
+
+ PointsArray pointPath = path.GetPath();
+ handler->PSendSysMessage("%s's path to %s:", target->GetName().c_str(), player->GetName().c_str());
+ handler->PSendSysMessage("Building: %s", useStraightPath ? "StraightPath" : "SmoothPath");
+ handler->PSendSysMessage("Result: %s - Length: %i - Type: %u", (result ? "true" : "false"), pointPath.size(), path.GetPathType());
+
+ Vector3 start = path.GetStartPosition();
+ Vector3 end = path.GetEndPosition();
+ Vector3 actualEnd = path.GetActualEndPosition();
+
+ handler->PSendSysMessage("StartPosition (%.3f, %.3f, %.3f)", start.x, start.y, start.z);
+ handler->PSendSysMessage("EndPosition (%.3f, %.3f, %.3f)", end.x, end.y, end.z);
+ handler->PSendSysMessage("ActualEndPosition (%.3f, %.3f, %.3f)", actualEnd.x, actualEnd.y, actualEnd.z);
+
+ if (!player->isGameMaster())
+ handler->PSendSysMessage("Enable GM mode to see the path points.");
+
+ for (uint32 i = 0; i < pointPath.size(); ++i)
+ player->SummonCreature(VISUAL_WAYPOINT, pointPath[i].x, pointPath[i].y, pointPath[i].z, 0, TEMPSUMMON_TIMED_DESPAWN, 9000);
+
+ return true;
+ }
+
+ static bool HandleMmapLocCommand(ChatHandler* handler, const char* args)
+ {
+ handler->PSendSysMessage("mmap tileloc:");
+
+ // grid tile location
+ Player* player = handler->GetSession()->GetPlayer();
+
+ int32 gx = 32 - player->GetPositionX() / SIZE_OF_GRIDS;
+ int32 gy = 32 - player->GetPositionY() / SIZE_OF_GRIDS;
+
+ handler->PSendSysMessage("%03u%02i%02i.mmtile", player->GetMapId(), gy, gx);
+ handler->PSendSysMessage("gridloc [%i,%i]", gx, gy);
+
+ // calculate navmesh tile location
+ dtNavMesh const* navmesh = MMAP::MMapFactory::createOrGetMMapManager()->GetNavMesh(handler->GetSession()->GetPlayer()->GetMapId());
+ dtNavMeshQuery const* navmeshquery = MMAP::MMapFactory::createOrGetMMapManager()->GetNavMeshQuery(handler->GetSession()->GetPlayer()->GetMapId(), player->GetInstanceId());
+ if (!navmesh || !navmeshquery)
+ {
+ handler->PSendSysMessage("NavMesh not loaded for current map.");
+ return true;
+ }
+
+ float const* min = navmesh->getParams()->orig;
+ float x, y, z;
+ player->GetPosition(x, y, z);
+ float location[VERTEX_SIZE] = {y, z, x};
+ float extents[VERTEX_SIZE] = {3.0f, 5.0f, 3.0f};
+
+ int32 tilex = int32((y - min[0]) / SIZE_OF_GRIDS);
+ int32 tiley = int32((x - min[2]) / SIZE_OF_GRIDS);
+
+ handler->PSendSysMessage("Calc [%02i,%02i]", tilex, tiley);
+
+ // navmesh poly -> navmesh tile location
+ dtQueryFilter filter = dtQueryFilter();
+ dtPolyRef polyRef = INVALID_POLYREF;
+ navmeshquery->findNearestPoly(location, extents, &filter, &polyRef, NULL);
+
+ if (polyRef == INVALID_POLYREF)
+ handler->PSendSysMessage("Dt [??,??] (invalid poly, probably no tile loaded)");
+ else
+ {
+ dtMeshTile const* tile;
+ dtPoly const* poly;
+ navmesh->getTileAndPolyByRef(polyRef, &tile, &poly);
+ if (tile)
+ handler->PSendSysMessage("Dt [%02i,%02i]", tile->header->x, tile->header->y);
+ else
+ handler->PSendSysMessage("Dt [??,??] (no tile loaded)");
+ }
+
+ return true;
+ }
+
+ static bool HandleMmapLoadedTilesCommand(ChatHandler* handler, const char* args)
+ {
+ uint32 mapid = handler->GetSession()->GetPlayer()->GetMapId();
+ dtNavMesh const* navmesh = MMAP::MMapFactory::createOrGetMMapManager()->GetNavMesh(mapid);
+ dtNavMeshQuery const* navmeshquery = MMAP::MMapFactory::createOrGetMMapManager()->GetNavMeshQuery(mapid, handler->GetSession()->GetPlayer()->GetInstanceId());
+ if (!navmesh || !navmeshquery)
+ {
+ handler->PSendSysMessage("NavMesh not loaded for current map.");
+ return true;
+ }
+
+ handler->PSendSysMessage("mmap loadedtiles:");
+
+ for (int32 i = 0; i < navmesh->getMaxTiles(); ++i)
+ {
+ dtMeshTile const* tile = navmesh->getTile(i);
+ if (!tile || !tile->header)
+ continue;
+
+ handler->PSendSysMessage("[%02i,%02i]", tile->header->x, tile->header->y);
+ }
+
+ return true;
+ }
+
+ static bool HandleMmapStatsCommand(ChatHandler* handler, const char* args)
+ {
+ uint32 mapId = handler->GetSession()->GetPlayer()->GetMapId();
+ handler->PSendSysMessage("mmap stats:");
+ handler->PSendSysMessage(" global mmap pathfinding is %sabled", MMAP::MMapFactory::IsPathfindingEnabled(mapId) ? "en" : "dis");
+
+ MMAP::MMapManager* manager = MMAP::MMapFactory::createOrGetMMapManager();
+ handler->PSendSysMessage(" %u maps loaded with %u tiles overall", manager->getLoadedMapsCount(), manager->getLoadedTilesCount());
+
+ dtNavMesh const* navmesh = manager->GetNavMesh(handler->GetSession()->GetPlayer()->GetMapId());
+ if (!navmesh)
+ {
+ handler->PSendSysMessage("NavMesh not loaded for current map.");
+ return true;
+ }
+
+ uint32 tileCount = 0;
+ uint32 nodeCount = 0;
+ uint32 polyCount = 0;
+ uint32 vertCount = 0;
+ uint32 triCount = 0;
+ uint32 triVertCount = 0;
+ uint32 dataSize = 0;
+ for (int32 i = 0; i < navmesh->getMaxTiles(); ++i)
+ {
+ dtMeshTile const* tile = navmesh->getTile(i);
+ if (!tile || !tile->header)
+ continue;
+
+ tileCount++;
+ nodeCount += tile->header->bvNodeCount;
+ polyCount += tile->header->polyCount;
+ vertCount += tile->header->vertCount;
+ triCount += tile->header->detailTriCount;
+ triVertCount += tile->header->detailVertCount;
+ dataSize += tile->dataSize;
+ }
+
+ handler->PSendSysMessage("Navmesh stats:");
+ handler->PSendSysMessage(" %u tiles loaded", tileCount);
+ handler->PSendSysMessage(" %u BVTree nodes", nodeCount);
+ handler->PSendSysMessage(" %u polygons (%u vertices)", polyCount, vertCount);
+ handler->PSendSysMessage(" %u triangles (%u vertices)", triCount, triVertCount);
+ handler->PSendSysMessage(" %.2f MB of data (not including pointers)", ((float)dataSize / sizeof(unsigned char)) / 1048576);
+
+ return true;
+ }
+
+ static bool HandleMmapTestArea(ChatHandler* handler, const char* args)
+ {
+ float radius = 40.0f;
+ WorldObject* object = handler->GetSession()->GetPlayer();
+
+ CellCoord pair(Trinity::ComputeCellCoord(object->GetPositionX(), object->GetPositionY()) );
+ Cell cell(pair);
+ cell.SetNoCreate();
+
+ std::list<Creature*> creatureList;
+
+ Trinity::AnyUnitInObjectRangeCheck go_check(object, radius);
+ Trinity::CreatureListSearcher<Trinity::AnyUnitInObjectRangeCheck> go_search(object, creatureList, go_check);
+ TypeContainerVisitor<Trinity::CreatureListSearcher<Trinity::AnyUnitInObjectRangeCheck>, GridTypeMapContainer> go_visit(go_search);
+
+ // Get Creatures
+ cell.Visit(pair, go_visit, *(object->GetMap()), *object, radius);
+
+ if (!creatureList.empty())
+ {
+ handler->PSendSysMessage("Found %i Creatures.", creatureList.size());
+
+ uint32 paths = 0;
+ uint32 uStartTime = getMSTime();
+
+ float gx, gy, gz;
+ object->GetPosition(gx, gy, gz);
+ for (std::list<Creature*>::iterator itr = creatureList.begin(); itr != creatureList.end(); ++itr)
+ {
+ PathGenerator path(*itr);
+ path.CalculatePath(gx, gy, gz);
+ ++paths;
+ }
+
+ uint32 uPathLoadTime = getMSTimeDiff(uStartTime, getMSTime());
+ handler->PSendSysMessage("Generated %i paths in %i ms", paths, uPathLoadTime);
+ }
+ else
+ handler->PSendSysMessage("No creatures in %f yard range.", radius);
+
+ return true;
+ }
+};
+
+void AddSC_mmaps_commandscript()
+{
+ new mmaps_commandscript();
+}
diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp
index e52f1ef..010b4f4 100644
--- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp
+++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp
@@ -343,7 +343,7 @@ class boss_algalon_the_observer : public CreatureScript
DoCast(me, SPELL_RIDE_THE_LIGHTNING, true);
me->GetMotionMaster()->MovePoint(POINT_ALGALON_LAND, AlgalonLandPos);
me->SetHomePosition(AlgalonLandPos);
- Movement::MoveSplineInit init(*me);
+ Movement::MoveSplineInit init(me);
init.MoveTo(AlgalonLandPos.GetPositionX(), AlgalonLandPos.GetPositionY(), AlgalonLandPos.GetPositionZ());
init.SetOrientationFixed(true);
init.Launch();
diff --git a/src/server/shared/CMakeLists.txt b/src/server/shared/CMakeLists.txt
index e9f414d..7143c8b 100644
--- a/src/server/shared/CMakeLists.txt
+++ b/src/server/shared/CMakeLists.txt
@@ -50,6 +50,7 @@ set(shared_STAT_SRCS
include_directories(
${CMAKE_BINARY_DIR}
+ ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Detour
${CMAKE_SOURCE_DIR}/dep/SFMT
${CMAKE_SOURCE_DIR}/dep/sockets/include
${CMAKE_SOURCE_DIR}/dep/utf8cpp
diff --git a/src/server/shared/Memory.h b/src/server/shared/Memory.h
new file mode 100644
index 0000000..ac697f7
--- /dev/null
+++ b/src/server/shared/Memory.h
@@ -0,0 +1,17 @@
+
+
+#ifndef _MEMORY_H
+#define _MEMORY_H
+
+// memory management
+inline void* dtCustomAlloc(int size, dtAllocHint /*hint*/)
+{
+ return (void*)new unsigned char[size];
+}
+
+inline void dtCustomFree(void* ptr)
+{
+ delete [] (unsigned char*)ptr;
+}
+
+#endif
\ No newline at end of file
diff --git a/src/server/worldserver/CMakeLists.txt b/src/server/worldserver/CMakeLists.txt
index 556edb5..d7c9a41 100644
--- a/src/server/worldserver/CMakeLists.txt
+++ b/src/server/worldserver/CMakeLists.txt
@@ -39,6 +39,7 @@ endif()
include_directories(
${CMAKE_BINARY_DIR}
${CMAKE_SOURCE_DIR}/dep/g3dlite/include
+ ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Detour
${CMAKE_SOURCE_DIR}/dep/gsoap
${CMAKE_SOURCE_DIR}/dep/sockets/include
${CMAKE_SOURCE_DIR}/dep/SFMT
@@ -163,6 +164,7 @@ target_link_libraries(worldserver
collision
g3dlib
gsoap
+ Detour
${JEMALLOC_LIBRARY}
${READLINE_LIBRARY}
${TERMCAP_LIBRARY}
diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist
index fafc136..426d4ab 100644
--- a/src/server/worldserver/worldserver.conf.dist
+++ b/src/server/worldserver/worldserver.conf.dist
@@ -269,6 +269,21 @@ PlayerSave.Stats.MinLevel = 0
PlayerSave.Stats.SaveOnlyOnLogout = 1
#
+# mmap.enablePathFinding
+# Description: Enable/Disable pathfinding using mmaps
+# Default: 1 - (Enabled)
+# 0 - (Disabled)
+
+mmap.enablePathFinding = 1
+
+#
+# mmap.ignoreMapIds
+# Disable mmap pathfinding on the listed maps.
+# List of map ids with delimiter ','
+
+mmap.ignoreMapIds = ""
+
+#
# vmap.enableLOS
# vmap.enableHeight
# Description: VMmap support for line of sight and height calculation.
@@ -289,14 +304,6 @@ vmap.enableHeight = 1
vmap.ignoreSpellIds = "7720"
#
-# vmap.petLOS
-# Description: Check line of sight for pets, to avoid them attacking through walls.
-# Default: 1 - (Enabled, each pet attack will be checked for line of sight)
-# 0 - (Disabled, somewhat less CPU usage)
-
-vmap.petLOS = 1
-
-#
# vmap.enableIndoorCheck
# Description: VMap based indoor check to remove outdoor-only auras (mounts etc.).
# Default: 1 - (Enabled)
diff --git a/src/tools/CMakeLists.txt b/src/tools/CMakeLists.txt
index a956655..bbff6f8 100644
--- a/src/tools/CMakeLists.txt
+++ b/src/tools/CMakeLists.txt
@@ -11,3 +11,5 @@
add_subdirectory(map_extractor)
add_subdirectory(vmap4_assembler)
add_subdirectory(vmap4_extractor)
+add_subdirectory(mmaps_generator)
+add_subdirectory(mesh_extractor)
diff --git a/src/tools/map_extractor/System.cpp b/src/tools/map_extractor/System.cpp
index d034271..2e11793 100644
--- a/src/tools/map_extractor/System.cpp
+++ b/src/tools/map_extractor/System.cpp
@@ -277,7 +277,7 @@ void ReadLiquidTypeTableDBC()
// Map file format data
static char const* MAP_MAGIC = "MAPS";
-static char const* MAP_VERSION_MAGIC = "v1.2";
+static char const* MAP_VERSION_MAGIC = "v1.3";
static char const* MAP_AREA_MAGIC = "AREA";
static char const* MAP_HEIGHT_MAGIC = "MHGT";
static char const* MAP_LIQUID_MAGIC = "MLIQ";
@@ -293,6 +293,8 @@ struct map_fileheader
uint32 heightMapSize;
uint32 liquidMapOffset;
uint32 liquidMapSize;
+ uint32 holesOffset;
+ uint32 holesSize;
};
#define MAP_AREA_NO_AREA 0x0001
@@ -827,9 +829,38 @@ bool ConvertADT(char *filename, char *filename2, int /*cell_y*/, int /*cell_x*/,
map.liquidMapSize += sizeof(float)*liquidHeader.width*liquidHeader.height;
}
+ // map hole info
+ uint16 holes[ADT_CELLS_PER_GRID][ADT_CELLS_PER_GRID];
+
+ if (map.liquidMapOffset)
+ map.holesOffset = map.liquidMapOffset + map.liquidMapSize;
+ else
+ map.holesOffset = map.heightMapOffset + map.heightMapSize;
+
+ memset(holes, 0, sizeof(holes));
+ bool hasHoles = false;
+
+ for (int i = 0; i < ADT_CELLS_PER_GRID; ++i)
+ {
+ for (int j = 0; j < ADT_CELLS_PER_GRID; ++j)
+ {
+ adt_MCNK * cell = cells->getMCNK(i,j);
+ if (!cell)
+ continue;
+ holes[i][j] = cell->holes;
+ if (!hasHoles && cell->holes != 0)
+ hasHoles = true;
+ }
+ }
+
+ if (hasHoles)
+ map.holesSize = sizeof(holes);
+ else
+ map.holesSize = 0;
+
// Ok all data prepared - store it
- FILE *output=fopen(filename2, "wb");
- if(!output)
+ FILE* output = fopen(filename2, "wb");
+ if (!output)
{
printf("Can't create the output file '%s'\n", filename2);
return false;
@@ -876,6 +907,11 @@ bool ConvertADT(char *filename, char *filename2, int /*cell_y*/, int /*cell_x*/,
fwrite(&liquid_height[y+liquidHeader.offsetY][liquidHeader.offsetX], sizeof(float), liquidHeader.width, output);
}
}
+
+ // store hole data
+ if (hasHoles)
+ fwrite(holes, map.holesSize, 1, output);
+
fclose(output);
return true;
diff --git a/src/tools/mesh_extractor/ADT.cpp b/src/tools/mesh_extractor/ADT.cpp
new file mode 100644
index 0000000..8d7dce9
--- /dev/null
+++ b/src/tools/mesh_extractor/ADT.cpp
@@ -0,0 +1,53 @@
+#include "ADT.h"
+#include "DoodadHandler.h"
+#include "LiquidHandler.h"
+#include "WorldModelHandler.h"
+
+ADT::ADT( std::string file ) : ObjectData(NULL), Data(NULL), _DoodadHandler(NULL), _WorldModelHandler(NULL), _LiquidHandler(NULL), HasObjectData(false)
+{
+ Data = new ChunkedData(file);
+ ObjectData = new ChunkedData(Utils::Replace(file, ".adt", "_obj0.adt"));
+ if (ObjectData->Stream)
+ HasObjectData = true;
+ else
+ ObjectData = NULL;
+}
+
+ADT::~ADT()
+{
+ delete ObjectData;
+ delete Data;
+
+ for (std::vector<MapChunk*>::iterator itr = MapChunks.begin(); itr != MapChunks.end(); ++itr)
+ delete *itr;
+
+ MapChunks.clear();
+ delete _DoodadHandler;
+ delete _WorldModelHandler;
+ delete _LiquidHandler;
+}
+
+void ADT::Read()
+{
+ Header.Read(Data->GetChunkByName("MHDR")->GetStream());
+ MapChunks.reserve(16 * 16);
+ int mapChunkIndex = 0;
+
+ for (std::vector<Chunk*>::iterator itr = Data->Chunks.begin(); itr != Data->Chunks.end(); ++itr)
+ if ((*itr)->Name == "MCNK")
+ MapChunks.push_back(new MapChunk(this, *itr));
+
+ _LiquidHandler = new LiquidHandler(this);
+
+ // do this separate from map chunk initialization to access liquid data
+ for (std::vector<MapChunk*>::iterator itr = MapChunks.begin(); itr != MapChunks.end(); ++itr)
+ (*itr)->GenerateTriangles();
+
+ _DoodadHandler = new DoodadHandler(this);
+ for (std::vector<MapChunk*>::iterator itr = MapChunks.begin(); itr != MapChunks.end(); ++itr)
+ _DoodadHandler->ProcessMapChunk(*itr);
+
+ _WorldModelHandler = new WorldModelHandler(this);
+ for (std::vector<MapChunk*>::iterator itr = MapChunks.begin(); itr != MapChunks.end(); ++itr)
+ _WorldModelHandler->ProcessMapChunk(*itr);
+}
diff --git a/src/tools/mesh_extractor/ADT.h b/src/tools/mesh_extractor/ADT.h
new file mode 100644
index 0000000..133596e
--- /dev/null
+++ b/src/tools/mesh_extractor/ADT.h
@@ -0,0 +1,29 @@
+#ifndef ADT_H
+#define ADT_H
+#include "ChunkedData.h"
+#include "MapChunk.h"
+
+class DoodadHandler;
+class WorldModelHandler;
+class LiquidHandler;
+
+class ADT
+{
+public:
+ ADT(std::string file);
+ ~ADT();
+
+ void Read();
+
+ ChunkedData* ObjectData;
+ ChunkedData* Data;
+ std::vector<MapChunk*> MapChunks;
+ MHDR Header;
+ // Can we dispose of this?
+ bool HasObjectData;
+
+ DoodadHandler* _DoodadHandler;
+ WorldModelHandler* _WorldModelHandler;
+ LiquidHandler* _LiquidHandler;
+};
+#endif
\ No newline at end of file
diff --git a/src/tools/mesh_extractor/CMakeLists.txt b/src/tools/mesh_extractor/CMakeLists.txt
new file mode 100644
index 0000000..8ef48a9
--- /dev/null
+++ b/src/tools/mesh_extractor/CMakeLists.txt
@@ -0,0 +1,80 @@
+# Copyright (C) 2005-2009 MaNGOS project <http://getmangos.com/>
+# Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/>
+#
+# This file is free software; as a special exception the author gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+file(GLOB_RECURSE sources *.cpp *.h)
+
+if( UNIX )
+ include_directories (
+ ${CMAKE_BINARY_DIR}
+ ${CMAKE_SOURCE_DIR}/src/server/shared
+ ${CMAKE_SOURCE_DIR}/src/server/shared/Database
+ ${CMAKE_SOURCE_DIR}/src/server/shared/Database/Implementation
+ ${CMAKE_SOURCE_DIR}/src/server/shared/Threading
+ ${CMAKE_SOURCE_DIR}/src/server/shared/Logging
+ ${CMAKE_SOURCE_DIR}/src/server/shared/Utilities
+ ${CMAKE_SOURCE_DIR}/src/server/shared/Dynamic
+ ${CMAKE_SOURCE_DIR}/src/server/shared/Dynamic/LinkedReference
+ ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Recast
+ ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Detour
+ ${CMAKE_SOURCE_DIR}/dep/libmpq
+ ${CMAKE_SOURCE_DIR}/dep/g3dlite/include
+ ${MYSQL_INCLUDE_DIR}
+ ${OPENSSL_INCLUDE_DIR}
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ )
+elseif( WIN32 )
+ include_directories (
+ ${CMAKE_BINARY_DIR}
+ ${CMAKE_SOURCE_DIR}/src/server/shared
+ ${CMAKE_SOURCE_DIR}/src/server/shared/Database
+ ${CMAKE_SOURCE_DIR}/src/server/shared/Database/Implementation
+ ${CMAKE_SOURCE_DIR}/src/server/shared/Threading
+ ${CMAKE_SOURCE_DIR}/src/server/shared/Logging
+ ${CMAKE_SOURCE_DIR}/src/server/shared/Utilities
+ ${CMAKE_SOURCE_DIR}/src/server/shared/Dynamic
+ ${CMAKE_SOURCE_DIR}/src/server/shared/Dynamic/LinkedReference
+ ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Recast
+ ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Detour
+ ${CMAKE_SOURCE_DIR}/dep/libmpq
+ ${CMAKE_SOURCE_DIR}/dep/libmpq/win
+ ${CMAKE_SOURCE_DIR}/dep/g3dlite/include
+ ${ACE_INCLUDE_DIR}
+ ${MYSQL_INCLUDE_DIR}
+ ${OPENSSL_INCLUDE_DIR}
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ )
+endif()
+
+add_executable(MeshExtractor
+ ${sources}
+)
+
+target_link_libraries(MeshExtractor
+ shared
+ g3dlib
+ mpq
+ Recast
+ Detour
+ ${MYSQL_LIBRARY}
+ ${OPENSSL_LIBRARIES}
+ ${OPENSSL_EXTRA_LIBRARIES}
+ ${BZIP2_LIBRARIES}
+ ${ZLIB_LIBRARIES}
+ ${ACE_LIBRARY}
+)
+
+add_dependencies(MeshExtractor mpq Recast Detour)
+
+if( UNIX )
+ install(TARGETS MeshExtractor DESTINATION bin)
+elseif( WIN32 )
+ install(TARGETS MeshExtractor DESTINATION "${CMAKE_INSTALL_PREFIX}")
+endif()
\ No newline at end of file
diff --git a/src/tools/mesh_extractor/Cache.h b/src/tools/mesh_extractor/Cache.h
new file mode 100644
index 0000000..90e2c13
--- /dev/null
+++ b/src/tools/mesh_extractor/Cache.h
@@ -0,0 +1,62 @@
+#ifndef CACHE_H
+#define CACHE_H
+#include <string>
+#include <map>
+#include "Common.h"
+#include "ace/Synch.h"
+
+class WorldModelRoot;
+class Model;
+
+template<class K, class T>
+class GenericCache
+{
+public:
+ GenericCache() {}
+
+ static const int32 FlushLimit = 1000;
+
+ void Insert(K key, T* val)
+ {
+ ACE_GUARD(ACE_Thread_Mutex, g, mutex);
+
+ if (_items.size() > FlushLimit)
+ Clear();
+ _items[key] = val;
+ }
+
+ T* Get(K key)
+ {
+ ACE_GUARD_RETURN(ACE_Thread_Mutex, g, mutex, NULL);
+ typename std::map<K, T*>::iterator itr = _items.find(key);
+ if (itr != _items.end())
+ return itr->second;
+ return NULL;
+ }
+
+ void Clear()
+ {
+ for (typename std::map<K, T*>::iterator itr = _items.begin(); itr != _items.end(); ++itr)
+ delete itr->second;
+ _items.clear();
+ }
+private:
+ std::map<K, T*> _items;
+ ACE_Thread_Mutex mutex;
+};
+
+class CacheClass
+{
+public:
+ CacheClass() {}
+ GenericCache<std::string, Model> ModelCache;
+ GenericCache<std::string, WorldModelRoot> WorldModelCache;
+
+ void Clear()
+ {
+
+ }
+};
+
+extern CacheClass* Cache;
+#endif
\ No newline at end of file
diff --git a/src/tools/mesh_extractor/Chunk.cpp b/src/tools/mesh_extractor/Chunk.cpp
new file mode 100644
index 0000000..2f521f1
--- /dev/null
+++ b/src/tools/mesh_extractor/Chunk.cpp
@@ -0,0 +1,31 @@
+#include "Chunk.h"
+#include "Utils.h"
+
+int32 Chunk::FindSubChunkOffset(std::string name)
+{
+ // Reverse the name
+ name = std::string(name.rbegin(), name.rend());
+ if (name.size() != 4)
+ return -1;
+
+ FILE* stream = GetStream();
+ uint32 matched = 0;
+ while (uint32(ftell(stream)) < Utils::Size(stream))
+ {
+ char b;
+ fread(&b, sizeof(char), 1, stream);
+ if (b == name[matched])
+ ++matched;
+ else
+ matched = 0;
+ if (matched == 4)
+ return ftell(stream) - 4;
+ }
+ return -1;
+}
+
+FILE* Chunk::GetStream()
+{
+ fseek(Stream, Offset, SEEK_SET);
+ return Stream;
+}
diff --git a/src/tools/mesh_extractor/Chunk.h b/src/tools/mesh_extractor/Chunk.h
new file mode 100644
index 0000000..2eea36f
--- /dev/null
+++ b/src/tools/mesh_extractor/Chunk.h
@@ -0,0 +1,20 @@
+#ifndef CHUNK_H
+#define CHUNK_H
+#include "Common.h"
+#include <string>
+class ChunkedData;
+
+class Chunk
+{
+public:
+ Chunk(const char* name, uint32 length, uint32 offset, FILE* stream) : Name(name), Length(length), Offset(offset), Stream(stream) {}
+
+ int32 FindSubChunkOffset(std::string name);
+ FILE* GetStream();
+ std::string Name;
+ uint32 Length;
+ uint32 Offset;
+ FILE* Stream;
+};
+
+#endif
\ No newline at end of file
diff --git a/src/tools/mesh_extractor/ChunkedData.cpp b/src/tools/mesh_extractor/ChunkedData.cpp
new file mode 100644
index 0000000..4d609d8
--- /dev/null
+++ b/src/tools/mesh_extractor/ChunkedData.cpp
@@ -0,0 +1,73 @@
+#include "ChunkedData.h"
+#include "MPQManager.h"
+#include "Utils.h"
+
+#include <string>
+
+ChunkedData::ChunkedData( FILE* stream, uint32 maxLength, uint32 chunksHint /*= 300*/ ) :
+Stream(stream)
+{
+ if (!Stream)
+ return;
+ Load(maxLength, chunksHint);
+}
+
+ChunkedData::ChunkedData( std::string file, uint32 chunksHint /*= 300*/ )
+{
+ Stream = MPQHandler->GetFile(file);
+ if (!Stream)
+ return;
+ Load(0, chunksHint);
+}
+
+void ChunkedData::Load( uint32 maxLength, uint32 chunksHint )
+{
+ if (!maxLength)
+ maxLength = Utils::Size(Stream);
+ Chunks.reserve(chunksHint);
+ uint32 baseOffset = ftell(Stream);
+ uint32 calcOffset = 0;
+ while ((calcOffset + baseOffset) < Utils::Size(Stream) && (calcOffset < maxLength))
+ {
+ char nameBytes[5];
+ uint32 read = fread(&nameBytes, sizeof(char), 4, Stream);
+ nameBytes[read] = '\0';
+ std::string name = std::string(nameBytes);
+ // Utils::Reverse(nameBytes);
+ name = std::string(name.rbegin(), name.rend());
+ uint32 length;
+ fread(&length, sizeof(uint32), 1, Stream);
+ calcOffset += 8;
+ Chunks.push_back(new Chunk(name.c_str(), length, calcOffset + baseOffset, Stream));
+ calcOffset += length;
+ // save an extra seek at the end
+ if ((calcOffset + baseOffset) < Utils::Size(Stream) && calcOffset < maxLength)
+ fseek(Stream, length, SEEK_CUR);
+ }
+}
+
+int ChunkedData::GetFirstIndex( std::string name )
+{
+ for (uint32 i = 0; i < Chunks.size(); ++i)
+ if (Chunks[i]->Name == name)
+ return i;
+ return -1;
+}
+
+Chunk* ChunkedData::GetChunkByName( std::string name )
+{
+ for (uint32 i = 0; i < Chunks.size(); ++i)
+ if (Chunks[i]->Name == name)
+ return Chunks[i];
+ return NULL;
+}
+
+ChunkedData::~ChunkedData()
+{
+ for (std::vector<Chunk*>::iterator itr = Chunks.begin(); itr != Chunks.end(); ++itr)
+ delete *itr;
+
+ Chunks.clear();
+ if (Stream)
+ fclose(Stream);
+}
diff --git a/src/tools/mesh_extractor/ChunkedData.h b/src/tools/mesh_extractor/ChunkedData.h
new file mode 100644
index 0000000..e23648c
--- /dev/null
+++ b/src/tools/mesh_extractor/ChunkedData.h
@@ -0,0 +1,21 @@
+#ifndef CHNK_H
+#define CHNK_H
+
+#include <vector>
+#include "Chunk.h"
+
+class ChunkedData
+{
+public:
+ ChunkedData(FILE* stream, uint32 maxLength, uint32 chunksHint = 300);
+ ChunkedData(std::string file, uint32 chunksHint = 300);
+ ~ChunkedData();
+
+ int GetFirstIndex(std::string name);
+ Chunk* GetChunkByName(std::string name);
+
+ void Load(uint32 maxLength, uint32 chunksHint);
+ std::vector<Chunk*> Chunks;
+ FILE* Stream;
+};
+#endif
\ No newline at end of file
diff --git a/src/tools/mesh_extractor/Constants.h b/src/tools/mesh_extractor/Constants.h
new file mode 100644
index 0000000..54fa073
--- /dev/null
+++ b/src/tools/mesh_extractor/Constants.h
@@ -0,0 +1,59 @@
+#ifndef CONSTANTS_H
+#define CONSTANTS_H
+
+#include "Common.h"
+
+class Constants
+{
+public:
+ enum TriangleType
+ {
+ TRIANGLE_TYPE_UNKNOWN,
+ TRIANGLE_TYPE_TERRAIN,
+ TRIANGLE_TYPE_WATER,
+ TRIANGLE_TYPE_DOODAD,
+ TRIANGLE_TYPE_WMO
+ };
+
+ enum PolyArea
+ {
+ POLY_AREA_TERRAIN = 1,
+ POLY_AREA_WATER = 2,
+ POLY_AREA_ROAD = 3,
+ POLY_AREA_DANGER = 4,
+ };
+
+ enum PolyFlag
+ {
+ POLY_FLAG_WALK = 1,
+ POLY_FLAG_SWIM = 2,
+ POLY_FLAG_FLIGHTMASTER = 4
+ };
+
+ enum ExtractFlags
+ {
+ EXTRACT_FLAG_DBC = 0x01,
+ EXTRACT_FLAG_MAPS = 0x02,
+ EXTRACT_FLAG_VMAPS = 0x04,
+ EXTRACT_FLAG_GOB_MODELS = 0x08,
+ EXTRACT_FLAG_MMAPS = 0x10,
+ EXTRACT_FLAG_TEST = 0x20,
+ EXTRACT_FLAG_ALLOWED = EXTRACT_FLAG_DBC | EXTRACT_FLAG_MAPS | EXTRACT_FLAG_VMAPS | EXTRACT_FLAG_GOB_MODELS | EXTRACT_FLAG_MMAPS | EXTRACT_FLAG_TEST
+ };
+
+ static const float TileSize;
+ static const float MaxXY;
+ static const float ChunkSize;
+ static const float UnitSize;
+ static const float Origin[];
+ static const float PI;
+ static const float MaxStandableHeight;
+ static bool ToWoWCoords;
+ static const char* VMAPMagic;
+ static const float BaseUnitDim;
+ static const int VertexPerMap;
+ static const int VertexPerTile;
+ static const int TilesPerMap;
+};
+
+#endif
\ No newline at end of file
diff --git a/src/tools/mesh_extractor/ContinentBuilder.cpp b/src/tools/mesh_extractor/ContinentBuilder.cpp
new file mode 100644
index 0000000..013c0ff
--- /dev/null
+++ b/src/tools/mesh_extractor/ContinentBuilder.cpp
@@ -0,0 +1,144 @@
+#include "ContinentBuilder.h"
+#include "TileBuilder.h"
+#include "WDT.h"
+#include "Utils.h"
+#include "DetourNavMesh.h"
+#include "Cache.h"
+#include "ace/Task.h"
+#include "Recast.h"
+
+class BuilderThread : public ACE_Task_Base
+{
+private:
+ int X, Y, MapId;
+ std::string Continent;
+ bool debug;
+ dtNavMeshParams Params;
+ ContinentBuilder* cBuilder;
+public:
+ BuilderThread(ContinentBuilder* _cBuilder, bool deb, dtNavMeshParams& params) : Free(true), debug(deb), Params(params), cBuilder(_cBuilder) {}
+ void SetData(int x, int y, int map, std::string cont) { X = x; Y = y; MapId = map; Continent = cont; }
+
+ int svc()
+ {
+ Free = false;
+ printf("[%02i,%02i] Building tile\n", X, Y);
+ TileBuilder builder(cBuilder, Continent, X, Y, MapId);
+ char buff[100];
+ sprintf(buff, "mmaps/%03u%02u%02u.mmtile", MapId, Y, X);
+ FILE* f = fopen(buff, "r");
+ if (f) // Check if file already exists.
+ {
+ printf("[%02i,%02i] Tile skipped, file already exists\n", X, Y);
+ fclose(f);
+ Free = true;
+ return 0;
+ }
+ uint8* nav = builder.Build(debug, Params);
+ if (nav)
+ {
+ f = fopen(buff, "wb");
+ if (!f)
+ {
+ printf("Could not create file %s. Check that you have write permissions to the destination folder and try again\n", buff);
+ return 0;
+ }
+ MmapTileHeader header;
+ header.size = builder.DataSize;
+ fwrite(&header, sizeof(MmapTileHeader), 1, f);
+ fwrite(nav, sizeof(unsigned char), builder.DataSize, f);
+ fclose(f);
+ }
+ dtFree(nav);
+ printf("[%02u,%02u] Tile Built!\n", X, Y);
+ Free = true;
+ return 0;
+ }
+
+ bool Free;
+};
+
+void ContinentBuilder::getTileBounds(uint32 tileX, uint32 tileY, float* verts, int vertCount, float* bmin, float* bmax)
+{
+ // this is for elevation
+ if (verts && vertCount)
+ rcCalcBounds(verts, vertCount, bmin, bmax);
+ else
+ {
+ bmin[1] = FLT_MIN;
+ bmax[1] = FLT_MAX;
+ }
+
+ // this is for width and depth
+ bmax[0] = (32 - int(tileX)) * Constants::TileSize;
+ bmax[2] = (32 - int(tileY)) * Constants::TileSize;
+ bmin[0] = bmax[0] - Constants::TileSize;
+ bmin[2] = bmax[2] - Constants::TileSize;
+}
+
+void ContinentBuilder::CalculateTileBounds()
+{
+ for (std::vector<TilePos>::iterator itr = TileMap->TileTable.begin(); itr != TileMap->TileTable.end(); ++itr)
+ {
+ tileXMax = std::max(itr->X, tileXMax);
+ tileXMin = std::min(itr->X, tileXMin);
+
+ tileYMax = std::max(itr->Y, tileYMax);
+ tileYMin = std::min(itr->Y, tileYMin);
+ }
+ getTileBounds(tileXMax, tileYMax, NULL, 0, bmin, bmax);
+}
+
+void ContinentBuilder::Build(bool debug)
+{
+ char buff[50];
+ sprintf(buff, "mmaps/%03u.mmap", MapId);
+ FILE* mmap = fopen(buff, "wb");
+ if (!mmap)
+ {
+ printf("Could not create file %s. Check that you have write permissions to the destination folder and try again\n", buff);
+ return;
+ }
+
+ CalculateTileBounds();
+
+ dtNavMeshParams params;
+ params.maxPolys = 1 << STATIC_POLY_BITS;
+ params.maxTiles = TileMap->TileTable.size();
+ rcVcopy(params.orig, bmin);
+ params.tileHeight = Constants::TileSize;
+ params.tileWidth = Constants::TileSize;
+ fwrite(&params, sizeof(dtNavMeshParams), 1, mmap);
+ fclose(mmap);
+ std::vector<BuilderThread*> Threads;
+ for (uint32 i = 0; i < NumberOfThreads; ++i)
+ Threads.push_back(new BuilderThread(this, debug, params));
+ printf("Map %s ( %i ) has %i tiles. Building them with %i threads\n", Continent.c_str(), MapId, TileMap->TileTable.size(), NumberOfThreads);
+ for (std::vector<TilePos>::iterator itr = TileMap->TileTable.begin(); itr != TileMap->TileTable.end(); ++itr)
+ {
+ bool next = false;
+ while (!next)
+ {
+ for (std::vector<BuilderThread*>::iterator _th = Threads.begin(); _th != Threads.end(); ++_th)
+ {
+ if ((*_th)->Free)
+ {
+ (*_th)->SetData(itr->X, itr->Y, MapId, Continent);
+ (*_th)->activate();
+ next = true;
+ break;
+ }
+ }
+ // Wait for 20 seconds
+ ACE_OS::sleep(ACE_Time_Value (0, 20000));
+ }
+ }
+ Cache->Clear();
+
+ // Free memory
+ for (std::vector<BuilderThread*>::iterator _th = Threads.begin(); _th != Threads.end(); ++_th)
+ {
+ (*_th)->wait();
+ delete *_th;
+ }
+}
diff --git a/src/tools/mesh_extractor/ContinentBuilder.h b/src/tools/mesh_extractor/ContinentBuilder.h
new file mode 100644
index 0000000..4c16fa0
--- /dev/null
+++ b/src/tools/mesh_extractor/ContinentBuilder.h
@@ -0,0 +1,26 @@
+#ifndef CONT_BUILDER_H
+#define CONT_BUILDER_H
+#include <string>
+#include "WDT.h"
+#include "Common.h"
+
+class ContinentBuilder
+{
+public:
+ ContinentBuilder(std::string continent, uint32 mapId, WDT* wdt, uint32 tn) : MapId(mapId), Continent(continent), TileMap(wdt), NumberOfThreads(tn), tileXMin(64), tileYMin(64), tileXMax(0), tileYMax(0) {}
+ void Build(bool debug);
+ void getTileBounds(uint32 tileX, uint32 tileY, float* verts, int vertCount, float* bmin, float* bmax);
+ void CalculateTileBounds();
+ float bmin[3];
+ float bmax[3];
+private:
+ std::string Continent;
+ WDT* TileMap;
+ uint32 MapId;
+ uint32 NumberOfThreads;
+ int tileXMin;
+ int tileYMin;
+ int tileXMax;
+ int tileYMax;
+};
+#endif
\ No newline at end of file
diff --git a/src/tools/mesh_extractor/DBC.cpp b/src/tools/mesh_extractor/DBC.cpp
new file mode 100644
index 0000000..c50e020
--- /dev/null
+++ b/src/tools/mesh_extractor/DBC.cpp
@@ -0,0 +1,64 @@
+#include <cstdio>
+#include "DBC.h"
+#include "Common.h"
+
+DBC::DBC( FILE* stream ) : StringBlock(NULL), IsFaulty(true), StringBlockSize(0)
+{
+ char magic[5];
+ fread(&magic, sizeof(char), 4, stream);
+ magic[4] = '\0';
+ fread(&RecordCount, sizeof(uint32), 1, stream);
+ Records.reserve(RecordCount);
+ fread(&Fields, sizeof(uint32), 1, stream);
+ fread(&RecordSize, sizeof(uint32), 1, stream);
+ fread(&StringBlockSize, sizeof(uint32), 1, stream);
+
+ for (int i = 0; i < RecordCount; i++)
+ {
+ Record* rec = new Record(this);
+ Records.push_back(rec);
+ int size = 0;
+ for (int f = 0; f < Fields; f++)
+ {
+ if (size + 4 > RecordSize)
+ {
+ IsFaulty = true;
+ break;
+ }
+ uint32 tmp;
+ fread(&tmp, sizeof(uint32), 1, stream);
+ rec->Values.push_back(tmp);
+ size += 4;
+ }
+ }
+ StringBlock = new uint8[StringBlockSize];
+ fread(StringBlock, sizeof(uint8), StringBlockSize, stream);
+}
+
+std::string DBC::GetStringByOffset( int offset )
+{
+ int len = 0;
+ for (uint32 i = offset; i < StringBlockSize; i++)
+ {
+ if (!StringBlock[i])
+ {
+ len = (i - offset);
+ break;
+ }
+ }
+ char* d = new char[len+1];
+ strcpy(d, (const char*)(StringBlock + offset));
+ d[len] = '\0';
+ std::string val = std::string(d);
+ delete d;
+ return val;
+}
+
+Record* DBC::GetRecordById( int id )
+{
+ // we assume Id is index 0
+ for (std::vector<Record*>::iterator itr = Records.begin(); itr != Records.end(); ++itr)
+ if ((*itr)->Values[0] == id)
+ return *itr;
+ return NULL;
+}
diff --git a/src/tools/mesh_extractor/DBC.h b/src/tools/mesh_extractor/DBC.h
new file mode 100644
index 0000000..0c857bd
--- /dev/null
+++ b/src/tools/mesh_extractor/DBC.h
@@ -0,0 +1,52 @@
+#ifndef DBC_H
+#define DBC_H
+#include <vector>
+#include "Common.h"
+
+class Record;
+
+class DBC
+{
+public:
+ DBC(FILE* stream);
+
+ std::string GetStringByOffset(int offset);
+
+ Record* GetRecordById(int id);
+
+ std::string Name;
+ std::vector<Record*> Records;
+ int RecordCount;
+ int Fields;
+ int RecordSize;
+ uint8* StringBlock;
+ uint32 StringBlockSize;
+ bool IsFaulty;
+};
+
+class Record
+{
+public:
+ Record(DBC* dbc) : Source(dbc) {}
+
+ DBC* Source;
+ std::vector<int> Values;
+
+ int operator[](int index)
+ {
+ return Values[index];
+ }
+
+ template <typename T>
+ T GetValue(int index)
+ {
+ return *(T*)(&Values[index]);
+ }
+
+ std::string GetString(int index)
+ {
+ return Source->GetStringByOffset(Values[index]);
+ }
+};
+
+#endif
\ No newline at end of file
diff --git a/src/tools/mesh_extractor/DoodadHandler.cpp b/src/tools/mesh_extractor/DoodadHandler.cpp
new file mode 100644
index 0000000..4e8028b
--- /dev/null
+++ b/src/tools/mesh_extractor/DoodadHandler.cpp
@@ -0,0 +1,107 @@
+#include "DoodadHandler.h"
+#include "Chunk.h"
+#include "Cache.h"
+#include "Model.h"
+#include "G3D/Matrix4.h"
+
+DoodadHandler::DoodadHandler( ADT* adt ) : ObjectDataHandler(adt), _definitions(NULL), _paths(NULL)
+{
+ if (!adt->HasObjectData)
+ return;
+ Chunk* mddf = adt->ObjectData->GetChunkByName("MDDF");
+ if (mddf)
+ ReadDoodadDefinitions(mddf);
+
+ Chunk* mmid = adt->ObjectData->GetChunkByName("MMID");
+ Chunk* mmdx = adt->ObjectData->GetChunkByName("MMDX");
+ if (mmid && mmdx)
+ ReadDoodadPaths(mmid, mmdx);
+}
+
+void DoodadHandler::ProcessInternal( ChunkedData* subChunks )
+{
+ if (!IsSane())
+ return;
+ Chunk* doodadReferencesChunk = subChunks->GetChunkByName("MCRD");
+ if (!doodadReferencesChunk)
+ return;
+ FILE* stream = doodadReferencesChunk->GetStream();
+ uint32 refCount = doodadReferencesChunk->Length / 4;
+ for (uint32 i = 0; i < refCount; i++)
+ {
+ int32 index;
+ fread(&index, sizeof(int32), 1, stream);
+ if (index < 0 || uint32(index) >= _definitions->size())
+ continue;
+ DoodadDefinition doodad = (*_definitions)[index];
+ if (_drawn.find(doodad.UniqueId) != _drawn.end())
+ continue;
+ _drawn.insert(doodad.UniqueId);
+ if (doodad.MmidIndex >= _paths->size())
+ continue;
+
+ std::string path = (*_paths)[doodad.MmidIndex];
+ Model* model = Cache->ModelCache.Get(path);
+ if (!model)
+ {
+ model = new Model(path);
+ Cache->ModelCache.Insert(path, model);
+ }
+ if (!model->IsCollidable)
+ continue;
+
+ Vertices.reserve(refCount * model->Vertices.size() * 0.2);
+ Triangles.reserve(refCount * model->Triangles.size() * 0.2);
+
+ InsertModelGeometry(doodad, model);
+ }
+}
+
+void DoodadHandler::ReadDoodadDefinitions( Chunk* chunk )
+{
+ int32 count = chunk->Length / 36;
+ _definitions = new std::vector<DoodadDefinition>;
+ _definitions->reserve(count);
+ FILE* stream = chunk->GetStream();
+ for (int i = 0; i < count; i++)
+ {
+ DoodadDefinition def;
+ def.Read(stream);
+ _definitions->push_back(def);
+ }
+}
+
+void DoodadHandler::ReadDoodadPaths( Chunk* id, Chunk* data )
+{
+ int paths = id->Length / 4;
+ _paths = new std::vector<std::string>();
+ _paths->reserve(paths);
+ for (int i = 0; i < paths; i++)
+ {
+ FILE* idStream = id->GetStream();
+ fseek(idStream, i * 4, SEEK_CUR);
+ uint32 offset;
+ fread(&offset, sizeof(uint32), 1, idStream);
+ FILE* dataStream = data->GetStream();
+ fseek(dataStream, offset + data->Offset, SEEK_SET);
+ _paths->push_back(Utils::ReadString(dataStream));
+ }
+}
+
+void DoodadHandler::InsertModelGeometry(const DoodadDefinition& def, Model* model)
+{
+ G3D::Matrix4 transformation = Utils::GetTransformation(def);
+ uint32 vertOffset = Vertices.size();
+
+ for (std::vector<Vector3>::iterator itr = model->Vertices.begin(); itr != model->Vertices.end(); ++itr)
+ Vertices.push_back(Utils::VectorTransform(*itr, transformation));
+
+ for (std::vector<Triangle<uint16> >::iterator itr = model->Triangles.begin(); itr != model->Triangles.end(); ++itr)
+ Triangles.push_back(Triangle<uint32>(Constants::TRIANGLE_TYPE_DOODAD, itr->V0 + vertOffset, itr->V1 + vertOffset, itr->V2 + vertOffset));
+}
+
+DoodadHandler::~DoodadHandler()
+{
+ delete _definitions;
+ delete _paths;
+}
diff --git a/src/tools/mesh_extractor/DoodadHandler.h b/src/tools/mesh_extractor/DoodadHandler.h
new file mode 100644
index 0000000..a212b03
--- /dev/null
+++ b/src/tools/mesh_extractor/DoodadHandler.h
@@ -0,0 +1,53 @@
+#ifndef DOOADHNDL_H
+#define DOOADHNDL_H
+#include "ObjectDataHandler.h"
+#include "Utils.h"
+#include "Chunk.h"
+#include "Model.h"
+#include <set>
+#include <vector>
+
+class DoodadDefinition : public IDefinition
+{
+public:
+ uint32 MmidIndex;
+ uint32 UniqueId;
+ uint16 DecimalScale;
+ uint16 Flags;
+
+ virtual float Scale() const { return DecimalScale / 1024.0f; }
+
+ void Read(FILE* stream)
+ {
+ fread(&MmidIndex, sizeof(uint32), 1, stream);
+ fread(&UniqueId, sizeof(uint32), 1, stream);
+ Position = Vector3::Read(stream);
+ Rotation = Vector3::Read(stream);
+ fread(&DecimalScale, sizeof(uint16), 1, stream);
+ fread(&Flags, sizeof(uint16), 1, stream);
+ }
+};
+
+class DoodadHandler : public ObjectDataHandler
+{
+public:
+ DoodadHandler(ADT* adt);
+ ~DoodadHandler();
+
+ std::vector<Vector3> Vertices;
+ std::vector<Triangle<uint32> > Triangles;
+ bool IsSane() { return _definitions && _paths; }
+
+
+protected:
+ void ProcessInternal(ChunkedData* chunk);
+
+private:
+ void ReadDoodadDefinitions(Chunk* chunk);
+ void ReadDoodadPaths(Chunk* id, Chunk* data);
+ void InsertModelGeometry(const DoodadDefinition& def, Model* model);
+ std::set<uint32> _drawn;
+ std::vector<DoodadDefinition>* _definitions;
+ std::vector<std::string>* _paths;
+};
+#endif
\ No newline at end of file
diff --git a/src/tools/mesh_extractor/Geometry.cpp b/src/tools/mesh_extractor/Geometry.cpp
new file mode 100644
index 0000000..2fc470e
--- /dev/null
+++ b/src/tools/mesh_extractor/Geometry.cpp
@@ -0,0 +1,123 @@
+#include "Geometry.h"
+#include "Constants.h"
+#include "ADT.h"
+#include "WorldModelHandler.h"
+#include "DoodadHandler.h"
+
+Geometry::Geometry() : Transform(false)
+{
+ Vertices.reserve(10000);
+ Triangles.reserve(10000);
+}
+
+void Geometry::CalculateBoundingBox( float*& min, float*& max )
+{
+ min = new float[3];
+ max = new float[3];
+
+ for (std::vector<Vector3>::iterator itr = Vertices.begin(); itr != Vertices.end(); ++itr)
+ {
+ if (itr->x > max[0])
+ max[0] = itr->x;
+ if (itr->x < min[0])
+ min[0] = itr->x;
+
+ if (itr->y > max[1])
+ max[1] = itr->y;
+ if (itr->y < min[1])
+ min[1] = itr->y;
+
+ if (itr->z > max[2])
+ max[2] = itr->z;
+ if (itr->z < min[2])
+ min[2] = itr->z;
+ }
+}
+
+void Geometry::CalculateMinMaxHeight( float& min, float& max )
+{
+ min = 0.0f;
+ max = 0.0f;
+
+ for (std::vector<Vector3>::iterator itr = Vertices.begin(); itr != Vertices.end(); ++itr)
+ {
+ if (Transform)
+ {
+ if (itr->y < min)
+ min = itr->y;
+ if (itr->y > max)
+ max = itr->y;
+ }
+ else
+ {
+ if (itr->z < min)
+ min = itr->z;
+ if (itr->z > max)
+ max = itr->z;
+ }
+ }
+}
+
+void Geometry::AddData( std::vector<Vector3>& verts, std::vector<Triangle<uint32> >& tris )
+{
+ uint32 vertOffset = Vertices.size();
+ for (std::vector<Vector3>::iterator itr = verts.begin(); itr != verts.end(); ++itr)
+ Vertices.push_back(Transform ? Utils::ToRecast(*itr) : *itr);
+
+ for (std::vector<Triangle<uint32> >::iterator itr = tris.begin(); itr != tris.end(); ++itr)
+ Triangles.push_back(Triangle<uint32>(itr->Type, itr->V0 + vertOffset, itr->V1 + vertOffset, itr->V2 + vertOffset));
+}
+
+void Geometry::GetRawData( float*& verts, int*& tris, uint8*& areas )
+{
+ verts = new float[Vertices.size() * 3];
+ for (uint32 i = 0; i < Vertices.size(); ++i)
+ {
+ Vector3& vert = Vertices[i];
+ verts[(i * 3) + 0] = vert.x;
+ verts[(i * 3) + 1] = vert.y;
+ verts[(i * 3) + 2] = vert.z;
+ }
+
+ tris = new int[Triangles.size() * 3];
+ for (uint32 i = 0; i < Triangles.size(); ++i)
+ {
+ Triangle<uint32>& tri = Triangles[i];
+ tris[(i * 3) + 0] = (int)tri.V0;
+ tris[(i * 3) + 1] = (int)tri.V1;
+ tris[(i * 3) + 2] = (int)tri.V2;
+ }
+
+ areas = new uint8[Triangles.size()];
+ for (uint32 i = 0; i < Triangles.size(); i++)
+ {
+ switch (Triangles[i].Type)
+ {
+ case Constants::TRIANGLE_TYPE_WATER:
+ areas[i] = Constants::POLY_AREA_WATER;
+ break;
+ default:
+ areas[i] = Constants::POLY_AREA_TERRAIN;
+ break;
+ }
+ }
+}
+
+void Geometry::AddAdt( ADT* adt )
+{
+ for (std::vector<MapChunk*>::iterator itr = adt->MapChunks.begin(); itr != adt->MapChunks.end(); ++itr)
+ {
+ std::vector<Triangle<uint32> > tmp;
+ tmp.reserve((*itr)->Triangles.size());
+ for (std::vector<Triangle<uint8> >::iterator itr2 = (*itr)->Triangles.begin(); itr2 != (*itr)->Triangles.end(); ++itr2)
+ tmp.push_back(Triangle<uint32>(itr2->Type, itr2->V0, itr2->V1, itr2->V2));
+ AddData((*itr)->Vertices, tmp);
+ }
+
+ if (!adt->_DoodadHandler->Triangles.empty())
+ AddData(adt->_DoodadHandler->Vertices, adt->_DoodadHandler->Triangles);
+
+ if (!adt->_WorldModelHandler->Triangles.empty())
+ AddData(adt->_WorldModelHandler->Vertices, adt->_WorldModelHandler->Triangles);
+}
+
diff --git a/src/tools/mesh_extractor/Geometry.h b/src/tools/mesh_extractor/Geometry.h
new file mode 100644
index 0000000..e445234
--- /dev/null
+++ b/src/tools/mesh_extractor/Geometry.h
@@ -0,0 +1,23 @@
+#ifndef GEOMETRY_H
+#define GEOMETRY_H
+#include <vector>
+
+#include "Utils.h"
+
+class ADT;
+class Geometry
+{
+public:
+ Geometry();
+
+ void CalculateBoundingBox(float*& min, float*& max);
+ void CalculateMinMaxHeight(float& min, float& max);
+ void AddData(std::vector<Vector3>& verts, std::vector<Triangle<uint32> >& tris);
+ void AddAdt(ADT* adt);
+ void GetRawData(float*& verts, int*& tris, uint8*& areas);
+
+ std::vector<Vector3> Vertices;
+ std::vector<Triangle<uint32> > Triangles;
+ bool Transform;
+};
+#endif
\ No newline at end of file
diff --git a/src/tools/mesh_extractor/LiquidHandler.cpp b/src/tools/mesh_extractor/LiquidHandler.cpp
new file mode 100644
index 0000000..7844130
--- /dev/null
+++ b/src/tools/mesh_extractor/LiquidHandler.cpp
@@ -0,0 +1,102 @@
+#include "LiquidHandler.h"
+#include "Utils.h"
+
+LiquidHandler::LiquidHandler( ADT* adt ) : Source(adt)
+{
+ HandleNewLiquid();
+}
+
+void LiquidHandler::HandleNewLiquid()
+{
+ Chunk* chunk = Source->Data->GetChunkByName("MH2O");
+ if (!chunk)
+ return;
+
+ Vertices.reserve(1000);
+ Triangles.reserve(1000);
+
+ FILE* stream = chunk->GetStream();
+ H2OHeader header[256];
+ MCNKData.reserve(256);
+ for (int i = 0; i < 256; i++)
+ header[i] = H2OHeader::Read(stream);
+
+ for (int i = 0; i < 256; i++)
+ {
+ H2OHeader h = header[i];
+ if (h.LayerCount == 0)
+ {
+ // Need to fill in missing data with dummies.
+ MCNKData.push_back(MCNKLiquidData(NULL, H2ORenderMask()));
+ continue;
+ }
+ fseek(stream, chunk->Offset + h.OffsetInformation, SEEK_SET);
+ H2OInformation information = H2OInformation::Read(stream);
+
+ float** heights = new float*[9];
+ for (int j = 0; j < 9; ++i)
+ {
+ heights[j] = new float[9];
+ memset(heights[j], 0, sizeof(float) * 9);
+ }
+
+ H2ORenderMask renderMask;
+ if (information.LiquidType != 2)
+ {
+ fseek(stream, chunk->Offset + h.OffsetRender, SEEK_SET);
+ renderMask = H2ORenderMask::Read(stream);
+ if ((Utils::IsAllZero(renderMask.Mask, 8) || (information.Width == 8 && information.Height == 8)) && information.OffsetMask2)
+ {
+ fseek(stream, chunk->Offset + information.OffsetMask2, SEEK_SET);
+ uint32 size = ceil(information.Width * information.Height / 8.0f);
+ uint8* altMask = new uint8[size];
+ fread(altMask, sizeof(uint8), size, stream);
+
+ for (uint32 mi = 0; mi < size; mi++)
+ renderMask.Mask[mi + information.OffsetY] |= altMask[mi];
+ delete[] altMask;
+ }
+ fseek(stream, chunk->Offset + information.OffsetHeightmap, SEEK_SET);
+
+ for (int y = information.OffsetY; y < (information.OffsetY + information.Height); y++)
+ for (int x = information.OffsetX; x < (information.OffsetX + information.Width); x++)
+ fread(&heights[x][y], sizeof(float), 1, stream);
+ }
+ else
+ {
+ // Fill with ocean data
+ for (uint32 i = 0; i < 8; ++i)
+ renderMask.Mask[i] = 0xFF;
+
+ for (uint32 y = 0; y < 9; ++y)
+ for (uint32 x = 0; x < 9; ++x)
+ heights[x][y] = information.HeightLevel1;
+ }
+
+ MCNKData.push_back(MCNKLiquidData(heights, renderMask));
+
+ for (int y = information.OffsetY; y < (information.OffsetY + information.Height); y++)
+ {
+ for (int x = information.OffsetX; x < (information.OffsetX + information.Width); x++)
+ {
+ if (!renderMask.ShouldRender(x, y))
+ continue;
+
+ MapChunk* mapChunk = Source->MapChunks[i];
+ Vector3 location = mapChunk->Header.Position;
+ location.y = location.y - (x * Constants::UnitSize);
+ location.x = location.x - (y * Constants::UnitSize);
+ location.z = heights[x][y];
+
+ uint32 vertOffset = Vertices.size();
+ Vertices.push_back(location);
+ Vertices.push_back(Vector3(location.x - Constants::UnitSize, location.y, location.z));
+ Vertices.push_back(Vector3(location.x, location.y - Constants::UnitSize, location.z));
+ Vertices.push_back(Vector3(location.x - Constants::UnitSize, location.y - Constants::UnitSize, location.z));
+
+ Triangles.push_back(Triangle<uint32>(Constants::TRIANGLE_TYPE_WATER, vertOffset, vertOffset+2, vertOffset + 1));
+ Triangles.push_back(Triangle<uint32>(Constants::TRIANGLE_TYPE_WATER, vertOffset + 2, vertOffset + 3, vertOffset + 1));
+ }
+ }
+ }
+}
diff --git a/src/tools/mesh_extractor/LiquidHandler.h b/src/tools/mesh_extractor/LiquidHandler.h
new file mode 100644
index 0000000..6e8d008
--- /dev/null
+++ b/src/tools/mesh_extractor/LiquidHandler.h
@@ -0,0 +1,21 @@
+#ifndef LIQUID_H
+#define LIQUID_H
+#include "ADT.h"
+#include "Utils.h"
+#include "Common.h"
+
+#include <vector>
+
+class LiquidHandler
+{
+public:
+ LiquidHandler(ADT* adt);
+
+ ADT* Source;
+ std::vector<Vector3> Vertices;
+ std::vector<Triangle<uint32> > Triangles;
+ std::vector<MCNKLiquidData> MCNKData;
+private:
+ void HandleNewLiquid();
+};
+#endif
\ No newline at end of file
diff --git a/src/tools/mesh_extractor/MPQ.cpp b/src/tools/mesh_extractor/MPQ.cpp
new file mode 100644
index 0000000..b7be181
--- /dev/null
+++ b/src/tools/mesh_extractor/MPQ.cpp
@@ -0,0 +1,118 @@
+#include "MPQ.h"
+#include "MPQManager.h"
+#include <deque>
+#include <cstdio>
+
+MPQArchive::MPQArchive(const char* filename)
+{
+ int result = libmpq__archive_open(&mpq_a, filename, -1);
+ printf("Opening %s\n", filename);
+ if (result)
+ {
+ switch (result)
+ {
+ case LIBMPQ_ERROR_OPEN :
+ printf("Error opening archive '%s': Does file really exist?\n", filename);
+ break;
+ case LIBMPQ_ERROR_FORMAT : /* bad file format */
+ printf("Error opening archive '%s': Bad file format\n", filename);
+ break;
+ case LIBMPQ_ERROR_SEEK : /* seeking in file failed */
+ printf("Error opening archive '%s': Seeking in file failed\n", filename);
+ break;
+ case LIBMPQ_ERROR_READ : /* Read error in archive */
+ printf("Error opening archive '%s': Read error in archive\n", filename);
+ break;
+ case LIBMPQ_ERROR_MALLOC : /* maybe not enough memory? :) */
+ printf("Error opening archive '%s': Maybe not enough memory\n", filename);
+ break;
+ default:
+ printf("Error opening archive '%s': Unknown error\n", filename);
+ break;
+ }
+ }
+ GetFileListTo(Files);
+}
+
+void MPQArchive::close()
+{
+ libmpq__archive_close(mpq_a);
+}
+
+MPQFile::MPQFile(const char* filename):
+eof(false), buffer(0), pointer(0), size(0)
+{
+ for (std::deque<MPQArchive*>::iterator i = MPQHandler->Archives.begin(); i != MPQHandler->Archives.end();++i)
+ {
+ mpq_archive* mpq_a = (*i)->mpq_a;
+
+ uint32_t filenum;
+ if(libmpq__file_number(mpq_a, filename, &filenum))
+ continue;
+ libmpq__off_t transferred;
+ libmpq__file_unpacked_size(mpq_a, filenum, &size);
+
+ // HACK: in patch.mpq some files don't want to open and give 1 for filesize
+ if (size<=1) {
+ // printf("warning: file %s has size %d; cannot Read.\n", filename, size);
+ eof = true;
+ buffer = 0;
+ return;
+ }
+ buffer = new char[size];
+
+ //libmpq_file_getdata
+ libmpq__file_read(mpq_a, filenum, (unsigned char*)buffer, size, &transferred);
+ /*libmpq_file_getdata(&mpq_a, hash, fileno, (unsigned char*)buffer);*/
+ return;
+
+ }
+ eof = true;
+ buffer = 0;
+}
+
+size_t MPQFile::Read(void* dest, size_t bytes)
+{
+ if (eof)
+ return 0;
+
+ size_t rpos = pointer + bytes;
+ if (rpos > size) {
+ bytes = size - pointer;
+ eof = true;
+ }
+
+ memcpy(dest, &(buffer[pointer]), bytes);
+
+ pointer = rpos;
+
+ return bytes;
+}
+
+void MPQFile::seek(int offset)
+{
+ pointer = offset;
+ eof = (pointer >= size);
+}
+
+void MPQFile::seekRelative(int offset)
+{
+ pointer += offset;
+ eof = (pointer >= size);
+}
+
+void MPQFile::close()
+{
+ if (buffer)
+ delete[] buffer;
+ buffer = 0;
+ eof = true;
+}
+
+FILE* MPQFile::GetFileStream()
+{
+ FILE* file = tmpfile();
+ fwrite(buffer, sizeof(char), size, file);
+ fseek(file, 0, SEEK_SET);
+ return file;
+}
diff --git a/src/tools/mesh_extractor/MPQ.h b/src/tools/mesh_extractor/MPQ.h
new file mode 100644
index 0000000..15fce45
--- /dev/null
+++ b/src/tools/mesh_extractor/MPQ.h
@@ -0,0 +1,90 @@
+#ifndef MPQ_H
+#define MPQ_H
+
+#include "libmpq/mpq.h"
+#include "Common.h"
+#include <string.h>
+#include <ctype.h>
+#include <vector>
+#include <iostream>
+#include <deque>
+
+using namespace std;
+
+class MPQArchive
+{
+
+public:
+ mpq_archive_s *mpq_a;
+
+ vector<string> Files;
+
+ MPQArchive(const char* filename);
+ void close();
+
+ void GetFileListTo(vector<string>& filelist) {
+ uint32_t filenum;
+ if(libmpq__file_number(mpq_a, "(listfile)", &filenum)) return;
+ libmpq__off_t size, transferred;
+ libmpq__file_unpacked_size(mpq_a, filenum, &size);
+
+ char *buffer = new char[size];
+
+ libmpq__file_read(mpq_a, filenum, (unsigned char*)buffer, size, &transferred);
+
+ char seps[] = "\n";
+ char* token;
+
+ token = strtok( buffer, seps );
+ uint32 counter = 0;
+ while ((token != NULL) && (counter < size)) {
+ //cout << token << endl;
+ token[strlen(token) - 1] = 0;
+ string s = token;
+ filelist.push_back(s);
+ counter += strlen(token) + 2;
+ token = strtok(NULL, seps);
+ }
+
+ delete[] buffer;
+ }
+};
+
+class MPQFile
+{
+ //MPQHANDLE handle;
+ bool eof;
+ char *buffer;
+ libmpq__off_t pointer,size;
+
+ // disable copying
+ MPQFile(const MPQFile& /*f*/) {}
+ void operator=(const MPQFile& /*f*/) {}
+
+public:
+ MPQFile(const char* filename); // filenames are not case sensitive
+ ~MPQFile() { close(); }
+ size_t Read(void* dest, size_t bytes);
+ FILE* GetFileStream();
+ size_t getSize() { return size; }
+ size_t getPos() { return pointer; }
+ char* getBuffer() { return buffer; }
+ char* getPointer() { return buffer + pointer; }
+ bool isEof() { return eof; }
+ void seek(int offset);
+ void seekRelative(int offset);
+ void close();
+};
+
+inline void flipcc(char *fcc)
+{
+ char t;
+ t=fcc[0];
+ fcc[0]=fcc[3];
+ fcc[3]=t;
+ t=fcc[1];
+ fcc[1]=fcc[2];
+ fcc[2]=t;
+}
+
+#endif
diff --git a/src/tools/mesh_extractor/MPQManager.cpp b/src/tools/mesh_extractor/MPQManager.cpp
new file mode 100644
index 0000000..90491df
--- /dev/null
+++ b/src/tools/mesh_extractor/MPQManager.cpp
@@ -0,0 +1,109 @@
+#include "MPQManager.h"
+#include "MPQ.h"
+#include "DBC.h"
+#include "Utils.h"
+
+#include "ace/Synch.h"
+
+char* MPQManager::Files[] = {
+ "common.MPQ",
+ "common-2.MPQ",
+ "expansion.MPQ",
+ "lichking.MPQ",
+ "patch.MPQ",
+ "patch-2.MPQ",
+ "patch-3.MPQ"
+};
+
+char* MPQManager::Languages[] = { "enGB", "enUS", "deDE", "esES", "frFR", "koKR", "zhCN", "zhTW", "enCN", "enTW", "esMX", "ruRU" };
+
+void MPQManager::Initialize()
+{
+ InitializeDBC();
+ uint32 size = sizeof(Files) / sizeof(char*);
+ for (uint32 i = 0; i < size; ++i)
+ {
+ MPQArchive* arc = new MPQArchive(std::string("Data/" + std::string(Files[i])).c_str());
+ Archives.push_front(arc);
+ printf("Opened %s\n", Files[i]);
+ }
+}
+
+void MPQManager::InitializeDBC()
+{
+ BaseLocale = -1;
+ std::string fileName;
+ uint32 size = sizeof(Languages) / sizeof(char*);
+ MPQArchive* _baseLocale = NULL;
+ for (uint32 i = 0; i < size; ++i)
+ {
+ std::string _fileName = "Data/" + std::string(Languages[i]) + "/locale-" + std::string(Languages[i]) + ".MPQ";
+ FILE* file = fopen(_fileName.c_str(), "rb");
+ if (file)
+ {
+ if (BaseLocale == -1)
+ {
+ BaseLocale = i;
+ _baseLocale = new MPQArchive(_fileName.c_str());
+ fileName = _fileName;
+ LocaleFiles[i] = _baseLocale;
+ }
+ else
+ LocaleFiles[i] = new MPQArchive(_fileName.c_str());
+
+ AvailableLocales.insert(i);
+ printf("Detected locale: %s\n", Languages[i]);
+ }
+ }
+ Archives.push_front(_baseLocale);
+ if (BaseLocale == -1)
+ {
+ printf("No locale data detected\n");
+ ASSERT(false);
+ }
+ else
+ printf("Using default locale: %s\n", Languages[BaseLocale]);
+}
+
+FILE* MPQManager::GetFile( std::string path )
+{
+ ACE_GUARD_RETURN(ACE_Thread_Mutex, g, mutex, NULL);
+ MPQFile file(path.c_str());
+ if (file.isEof())
+ return NULL;
+ return file.GetFileStream();
+}
+
+DBC* MPQManager::GetDBC( std::string name )
+{
+ std::string path = "DBFilesClient\\" + name + ".dbc";
+ return new DBC(GetFile(path));
+}
+
+FILE* MPQManager::GetFileFrom( std::string path, MPQArchive* file )
+{
+ ACE_GUARD_RETURN(ACE_Thread_Mutex, g, mutex, NULL);
+ mpq_archive* mpq_a = file->mpq_a;
+
+ uint32_t filenum;
+ if(libmpq__file_number(mpq_a, path.c_str(), &filenum))
+ return NULL;
+
+ libmpq__off_t transferred;
+ libmpq__off_t size = 0;
+ libmpq__file_unpacked_size(mpq_a, filenum, &size);
+
+ // HACK: in patch.mpq some files don't want to open and give 1 for filesize
+ if (size <= 1)
+ return NULL;
+
+ uint8* buffer = new uint8[size];
+
+ //libmpq_file_getdata
+ libmpq__file_read(mpq_a, filenum, (unsigned char*)buffer, size, &transferred);
+
+ // Pack the return into a FILE stream
+ FILE* ret = tmpfile();
+ fwrite(buffer, sizeof(uint8), size, ret);
+ return ret;
+}
diff --git a/src/tools/mesh_extractor/MPQManager.h b/src/tools/mesh_extractor/MPQManager.h
new file mode 100644
index 0000000..c23d717
--- /dev/null
+++ b/src/tools/mesh_extractor/MPQManager.h
@@ -0,0 +1,34 @@
+#ifndef MPQ_MANAGER_H
+#define MPQ_MANAGER_H
+
+#include "MPQ.h"
+#include "ace/Synch.h"
+
+class DBC;
+class MPQManager
+{
+public:
+ MPQManager() {}
+ ~MPQManager() {}
+
+ void Initialize();
+ FILE* GetFile(std::string path);
+ FILE* GetFileFrom(std::string path, MPQArchive* file);
+ DBC* GetDBC(std::string name);
+ std::vector<std::string> GetAllFiles(std::string extension);
+
+ std::deque<MPQArchive*> Archives;
+ int32 BaseLocale;
+ std::set<uint32> AvailableLocales;
+ std::map<uint32, MPQArchive*> LocaleFiles;
+
+ static char* Files[];
+ static char* Languages[];
+protected:
+ void InitializeDBC();
+private:
+ ACE_Thread_Mutex mutex;
+};
+
+extern MPQManager* MPQHandler;
+#endif
\ No newline at end of file
diff --git a/src/tools/mesh_extractor/MapChunk.cpp b/src/tools/mesh_extractor/MapChunk.cpp
new file mode 100644
index 0000000..67f4061
--- /dev/null
+++ b/src/tools/mesh_extractor/MapChunk.cpp
@@ -0,0 +1,73 @@
+#include "MapChunk.h"
+#include "ADT.h"
+#include "LiquidHandler.h"
+
+MapChunk::MapChunk( ADT* _adt, Chunk* chunk ) : Adt(_adt), Source(chunk)
+{
+ FILE* stream = chunk->GetStream();
+ Header.Read(stream);
+ fseek(stream, chunk->Offset, SEEK_SET);
+ Index = Header.IndexX + Header.IndexY * 16;
+ GenerateVertices(stream);
+}
+
+void MapChunk::GenerateTriangles()
+{
+ Triangles.reserve(256);
+ for (int y = 0; y < 8; y++)
+ {
+ for (int x = 0; x < 8; x++)
+ {
+ if (HasHole(Header.Holes, x / 2, y / 2))
+ continue;
+
+ uint32 topLeft = (17 * y) + x;
+ uint32 topRight = (17 * y) + x + 1;
+ uint32 bottomLeft = (17 * (y + 1)) + x;
+ uint32 bottomRight = (17 * (y + 1)) + x + 1;
+ uint32 center = (17 * y) + 9 + x;
+
+ Constants::TriangleType triangleType = Constants::TRIANGLE_TYPE_TERRAIN;
+ if (Adt->_LiquidHandler && !Adt->_LiquidHandler->MCNKData.empty())
+ {
+ MCNKLiquidData& data = Adt->_LiquidHandler->MCNKData[Index];
+ float maxHeight = std::max(
+ std::max(
+ std::max(std::max(Vertices[topLeft].z, Vertices[topRight].z), Vertices[bottomLeft].z),
+ Vertices[bottomRight].z), Vertices[center].z);
+ if (data.IsWater(x, y, maxHeight))
+ triangleType = Constants::TRIANGLE_TYPE_WATER;
+ }
+
+ Triangles.push_back(Triangle<uint8>(triangleType, topRight, topLeft, center));
+ Triangles.push_back(Triangle<uint8>(triangleType, topLeft, bottomLeft, center));
+ Triangles.push_back(Triangle<uint8>(triangleType, bottomLeft, bottomRight, center));
+ Triangles.push_back(Triangle<uint8>(triangleType, bottomRight, topRight, center));
+ }
+ }
+}
+
+void MapChunk::GenerateVertices( FILE* stream )
+{
+ fseek(stream, Header.OffsetMCVT, SEEK_CUR);
+ Vertices.reserve(125);
+
+ for (int j = 0; j < 17; j++)
+ {
+ int values = j % 2 ? 8 : 9;
+ for (int i = 0; i < values; i++)
+ {
+ float tmp;
+ fread(&tmp, sizeof(float), 1, stream);
+ Vector3 vert(Header.Position.x - (j * (Constants::UnitSize * 0.5f)), Header.Position.y - (i * Constants::UnitSize), Header.Position.z + tmp);
+ if (values == 8)
+ vert.y -= Constants::UnitSize * 0.5f;
+ Vertices.push_back(vert);
+ }
+ }
+}
+
+bool MapChunk::HasHole( uint32 map, int x, int y )
+{
+ return (map & 0x0000FFFF) & ((1 << x) << (y << 2));
+}
diff --git a/src/tools/mesh_extractor/MapChunk.h b/src/tools/mesh_extractor/MapChunk.h
new file mode 100644
index 0000000..e7d835a
--- /dev/null
+++ b/src/tools/mesh_extractor/MapChunk.h
@@ -0,0 +1,24 @@
+#ifndef MAPCHUNK_H
+#define MAPCHUNK_H
+#include "Chunk.h"
+#include "Utils.h"
+#include "Constants.h"
+#include <vector>
+class ADT;
+
+class MapChunk
+{
+public:
+ MapChunk(ADT* _adt, Chunk* chunk);
+
+ void GenerateTriangles();
+ void GenerateVertices(FILE* stream);
+ static bool HasHole(uint32 map, int x, int y);
+ ADT* Adt;
+ Chunk* Source;
+ MapChunkHeader Header;
+ std::vector<Vector3> Vertices;
+ std::vector<Triangle<uint8> > Triangles;
+ int32 Index;
+};
+#endif
\ No newline at end of file
diff --git a/src/tools/mesh_extractor/MeshExtractor.cpp b/src/tools/mesh_extractor/MeshExtractor.cpp
new file mode 100644
index 0000000..7379ba8
--- /dev/null
+++ b/src/tools/mesh_extractor/MeshExtractor.cpp
@@ -0,0 +1,418 @@
+#include "MPQManager.h"
+#include "WDT.h"
+#include "ContinentBuilder.h"
+#include "Cache.h"
+#include "DBC.h"
+#include "Constants.h"
+#include "Model.h"
+
+#include "Recast.h"
+#include "DetourNavMesh.h"
+#include "DetourNavMeshQuery.h"
+
+#include <set>
+
+#include "Common.h"
+#include "LoginDatabase.h"
+#include "Util.h"
+LoginDatabaseWorkerPool LoginDatabase;
+
+MPQManager* MPQHandler;
+CacheClass* Cache;
+
+void ExtractMMaps(std::set<uint32>& mapIds, uint32 threads, bool debug)
+{
+ DBC* dbc = MPQHandler->GetDBC("Map");
+ for (std::vector<Record*>::iterator itr = dbc->Records.begin(); itr != dbc->Records.end(); ++itr)
+ {
+ uint32 mapId = (*itr)->Values[0];
+
+ // Skip this map if a list of specific maps was provided and this one is not contained in it.
+ if (!mapIds.empty() && mapIds.find(mapId) == mapIds.end())
+ continue;
+
+ std::string name = (*itr)->GetString(1);
+ WDT wdt("World\\maps\\" + name + "\\" + name + ".wdt");
+ if (!wdt.IsValid || wdt.IsGlobalModel)
+ continue;
+ printf("Building %s MapId %u\n", name.c_str(), mapId);
+ ContinentBuilder builder(name, mapId, &wdt, threads);
+ builder.Build(debug);
+ }
+}
+
+void ExtractDBCs()
+{
+ printf("Extracting DBCs\n");
+ // Create the filesystem structure
+ std::string baseDBCPath = "dbc/";
+ Utils::CreateDir(baseDBCPath);
+
+ // Populate list of DBC files
+ std::set<std::string> DBCFiles;
+ for (std::vector<std::string>::iterator itr = MPQHandler->LocaleFiles[MPQHandler->BaseLocale]->Files.begin(); itr != MPQHandler->LocaleFiles[MPQHandler->BaseLocale]->Files.end(); ++itr)
+ if (itr->rfind(".dbc") == itr->length() - strlen(".dbc"))
+ DBCFiles.insert(*itr);
+
+ // Iterate over all available locales
+ for (std::set<uint32>::iterator itr = MPQHandler->AvailableLocales.begin(); itr != MPQHandler->AvailableLocales.end(); ++itr)
+ {
+ printf("Extracting DBCs for locale %s\n", MPQManager::Languages[*itr]);
+ std::string path = baseDBCPath;
+ if (*itr != MPQHandler->BaseLocale)
+ {
+ path += std::string(MPQManager::Languages[*itr]) + "/";
+ Utils::CreateDir(path);
+ }
+
+ std::string component = "component.wow-" + std::string(MPQManager::Languages[*itr]) + ".txt";
+ // Extract the component file
+ Utils::SaveToDisk(MPQHandler->GetFile(component), path + component);
+ // Extract the DBC files for the given locale
+ for (std::set<std::string>::iterator itr2 = DBCFiles.begin(); itr2 != DBCFiles.end(); ++itr2)
+ Utils::SaveToDisk(MPQHandler->GetFileFrom(*itr2, MPQHandler->LocaleFiles[*itr]), path + (itr2->c_str() + strlen("DBFilesClient\\")));
+ }
+ printf("DBC extraction finished!\n");
+}
+
+void ExtractGameobjectModels()
+{
+ Constants::ToWoWCoords = true;
+ printf("Extracting GameObject models\n");
+
+ std::string baseBuildingsPath = "Buildings/";
+ std::string baseVmapsPath = "vmaps/";
+ Utils::CreateDir(baseVmapsPath);
+ Utils::CreateDir(baseBuildingsPath);
+
+ FILE* modelList = fopen((baseVmapsPath + "GameObjectModels.list").c_str(), "wb");
+ if (!modelList)
+ {
+ printf("Could not create file vmaps/GameObjectModels.list, please make sure that you have the write permissions in the folder\n");
+ return;
+ }
+
+ DBC* dbc = MPQHandler->GetDBC("GameObjectDisplayInfo");
+ for (std::vector<Record*>::iterator itr = dbc->Records.begin(); itr != dbc->Records.end(); ++itr)
+ {
+ std::string path = (*itr)->GetString(1);
+ std::string fileName = Utils::GetPlainName(path.c_str());
+ std::string extension = Utils::GetExtension(fileName);
+ // Convert the extension to lowercase
+ std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower);
+ if (extension == "mdx" || extension == "m2")
+ {
+ fileName = Utils::FixModelPath(fileName);
+ Model model(path);
+
+ if (model.IsBad)
+ continue;
+
+ FILE* output = fopen((baseBuildingsPath + fileName).c_str(), "wb");
+ if (!output)
+ {
+ printf("Could not create file %s, please check that you have write permissions\n", (baseBuildingsPath + fileName).c_str());
+ continue;
+ }
+ // Placeholder for 0 values
+ int Nop[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+ fwrite(Constants::VMAPMagic, 8, 1, output);
+ uint32 numVerts = model.Header.CountBoundingVertices;
+ fwrite(&numVerts, sizeof(uint32), 1, output);
+ uint32 numGroups = 1;
+ fwrite(&numGroups, sizeof(uint32), 1, output);
+ fwrite(Nop, 4 * 3 , 1, output); // rootwmoid, flags, groupid
+ fwrite(Nop, sizeof(float), 3 * 2, output);//bbox, only needed for WMO currently
+ fwrite(Nop, 4, 1, output);// liquidflags
+ fwrite("GRP ", 4, 1, output);
+
+ uint32 branches = 1;
+ uint32 wsize = sizeof(branches) + sizeof(uint32) * branches;
+ fwrite(&wsize, sizeof(uint32), 1, output);
+ fwrite(&branches, sizeof(branches), 1, output);
+ uint32 numTris = model.Header.CountBoundingTriangles;
+ fwrite(&numTris, sizeof(uint32), 1, output);
+ fwrite("INDX", 4, 1, output);
+ wsize = sizeof(uint32) + sizeof(unsigned short) * numTris;
+ fwrite(&wsize, sizeof(int), 1, output);
+ fwrite(&numTris, sizeof(uint32), 1, output);
+ uint16* indices = new uint16[numTris];
+
+ if (numTris > 0)
+ {
+ uint32 i = 0;
+ for (std::vector<Triangle<uint16> >::iterator itr2 = model.Triangles.begin(); itr2 != model.Triangles.end(); ++itr2, ++i)
+ {
+ indices[i * 3 + 0] = itr2->V0;
+ indices[i * 3 + 1] = itr2->V1;
+ indices[i * 3 + 2] = itr2->V2;
+ }
+ fwrite(indices, sizeof(uint16), numTris, output);
+ }
+
+
+ fwrite("VERT", 4, 1, output);
+ wsize = sizeof(int) + sizeof(float) * 3 * numVerts;
+ fwrite(&wsize, sizeof(int), 1, output);
+ fwrite(&numVerts, sizeof(int), 1, output);
+ float* vertices = new float[numVerts*3];
+
+ if (numVerts > 0)
+ {
+ uint32 i = 0;
+ for (std::vector<Vector3>::iterator itr2 = model.Vertices.begin(); itr2 != model.Vertices.end(); ++itr2, ++i)
+ {
+ vertices[i * 3 + 0] = itr2->x;
+ vertices[i * 3 + 1] = itr2->y;
+ vertices[i * 3 + 2] = itr2->z;
+ }
+
+ fwrite(vertices, sizeof(float), numVerts * 3, output);
+ }
+
+ fclose(output);
+ delete[] indices;
+ delete[] vertices;
+
+ uint32 displayId = (*itr)->Values[0];
+ uint32 pathLength = fileName.size();
+ fwrite(&displayId, sizeof(uint32), 1, modelList);
+ fwrite(&pathLength, sizeof(uint32), 1, modelList);
+ fwrite(fileName.c_str(), sizeof(char), pathLength, modelList);
+ }
+ else if (extension == "wmo")
+ {
+ WorldModelRoot model(path);
+
+ FILE* output = fopen((baseBuildingsPath + fileName).c_str(), "wb");
+ if (!output)
+ {
+ printf("Could not create file %s, please check that you have write permissions\n", (baseBuildingsPath + fileName).c_str());
+ continue;
+ }
+
+ fwrite(Constants::VMAPMagic, 1, 8, output);
+ uint32 numVertices = 0;
+ fwrite(&numVertices, sizeof(uint32), 1, output); // will be filled later
+ fwrite(&model.Header.CountGroups, sizeof(uint32), 1, output);
+ fwrite(&model.Header.WmoId, sizeof(uint32), 1, output);
+
+ uint32 i = 0;
+ for (std::vector<WorldModelGroup>::iterator itr2 = model.Groups.begin(); itr2 != model.Groups.end(); ++itr2)
+ {
+ fwrite(&itr2->Header.Flags, sizeof(uint32), 1, output);
+ fwrite(&itr2->Header.WmoId, sizeof(uint32), 1, output);
+ fwrite(&itr2->Header.BoundingBox[0], sizeof(uint32), 1, output);
+ fwrite(&itr2->Header.BoundingBox[1], sizeof(uint32), 1, output);
+ uint32 LiquidFlags = itr2->HasLiquidData ? 1 : 0;
+ fwrite(&LiquidFlags, sizeof(uint32), 1, output);
+
+ fwrite("GRP ", sizeof(char), 4, output);
+ uint32 k = 0;
+ uint32 mobaBatch = itr2->MOBALength / 12;
+ uint32* MobaEx = new uint32[mobaBatch*4];
+
+ for(uint32 i = 8; i < itr2->MOBALength; i += 12)
+ MobaEx[k++] = itr2->MOBA[i];
+
+ int mobaSizeGrp = mobaBatch * 4 + 4;
+ fwrite(&mobaSizeGrp, 4, 1, output);
+ fwrite(&mobaBatch, 4, 1, output);
+ fwrite(MobaEx, 4, k, output);
+ delete[] MobaEx;
+
+ // Note: still not finished
+ }
+
+ fclose(output);
+ }
+ }
+
+ fclose(modelList);
+ printf("GameObject models extraction finished!");
+ Constants::ToWoWCoords = false;
+}
+
+bool HandleArgs(int argc, char** argv, uint32& threads, std::set<uint32>& mapList, bool& debugOutput, uint32& extractFlags)
+{
+ char* param = NULL;
+ extractFlags = 0;
+
+ for (int i = 1; i < argc; ++i)
+ {
+ if (strcmp(argv[i], "--threads") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+
+ threads = atoi(param);
+ printf("Using %i threads\n", threads);
+ }
+ else if (strcmp(argv[i], "--maps") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+ std::string maps = std::string(param);
+ Tokenizer tokens(maps, ',');
+
+ for (Tokenizer::const_iterator itr = tokens.begin(); itr != tokens.end(); ++itr)
+ mapList.insert(atoi(*itr));
+
+ printf("Extracting only provided list of maps (%u).\n", mapList.size());
+ }
+ else if (strcmp(argv[i], "--debug") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+ debugOutput = atoi(param);
+ if (debugOutput)
+ printf("Output will contain debug information (.obj files)\n");
+ }
+ else if (strcmp(argv[i], "--extract") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+
+ extractFlags = atoi(param);
+
+ if (!(extractFlags & Constants::EXTRACT_FLAG_ALLOWED)) // Tried to use an invalid flag
+ return false;
+
+ printf("Detected flags: \n");
+ printf("* Extract DBCs: %s\n", (extractFlags & Constants::EXTRACT_FLAG_DBC) ? "Yes" : "No");
+ printf("* Extract Maps: %s\n", (extractFlags & Constants::EXTRACT_FLAG_MAPS) ? "Yes" : "No");
+ printf("* Extract VMaps: %s\n", (extractFlags & Constants::EXTRACT_FLAG_VMAPS) ? "Yes" : "No");
+ printf("* Extract GameObject Models: %s\n", (extractFlags & Constants::EXTRACT_FLAG_GOB_MODELS) ? "Yes" : "No");
+ printf("* Extract MMaps: %s\n", (extractFlags & Constants::EXTRACT_FLAG_MMAPS) ? "Yes" : "No");
+ }
+ }
+ return true;
+}
+
+void PrintUsage()
+{
+ printf("MeshExtractor help.\n");
+ printf("* Use \"--threads <number>\" to specify <number> threads, default to 4 (Only available when extracting MMaps)\n");
+ printf("* Use \"--maps a,b,c,d,e\" to extract only the maps specified (Do not use spaces)\n");
+ printf("* Use \"--debug 1\" to generate debug information of the tiles (Only available when extracting MMaps)\n");
+ printf("* Use \"--extract X\" to extract the data specified by the flag X (Note: You can combine the flags with the bitwise OR operator |). Available flags are: \n");
+ {
+ printf("- %u to extract DBCs\n", Constants::EXTRACT_FLAG_DBC);
+ printf("- %u to extract Maps (Not yet implemented)\n", Constants::EXTRACT_FLAG_MAPS);
+ printf("- %u to extract VMaps (Not yet implemented)\n", Constants::EXTRACT_FLAG_VMAPS);
+ printf("- %u to extract GameObject models (Not yet finished, you need to run VMapAssembler on the extracted files)\n", Constants::EXTRACT_FLAG_GOB_MODELS);
+ printf("- %u to extract MMaps (Not yet finished)\n", Constants::EXTRACT_FLAG_MMAPS);
+ }
+}
+
+void LoadTile(dtNavMesh*& navMesh, const char* tile)
+{
+ FILE* f = fopen(tile, "rb");
+ MmapTileHeader header;
+ fread(&header, sizeof(MmapTileHeader), 1, f);
+ uint8* nav = new uint8[header.size];
+ fread(nav, header.size, 1, f);
+
+ dtStatus res = navMesh->addTile(nav, header.size, DT_TILE_FREE_DATA, 0, NULL);
+
+ fclose(f);
+}
+
+int main(int argc, char* argv[])
+{
+ system("pause");
+ uint32 threads = 4, extractFlags = 0;
+ std::set<uint32> mapIds;
+ bool debug = false;
+
+ if (!HandleArgs(argc, argv, threads, mapIds, debug, extractFlags))
+ {
+ PrintUsage();
+ return -1;
+ }
+
+ Cache = new CacheClass();
+ MPQHandler = new MPQManager();
+ MPQHandler->Initialize();
+
+ if (extractFlags & Constants::EXTRACT_FLAG_DBC)
+ ExtractDBCs();
+
+ if (extractFlags & Constants::EXTRACT_FLAG_MMAPS)
+ ExtractMMaps(mapIds, threads, debug);
+
+ if (extractFlags & Constants::EXTRACT_FLAG_GOB_MODELS)
+ ExtractGameobjectModels();
+
+ if (extractFlags & Constants::EXTRACT_FLAG_TEST)
+ {
+ float start[] = { 0.0f, 0.0f, 0.0f };
+ float end[] = { 0.0f, 0.0f, 0.0f };
+
+ //
+ float m_spos[3];
+ m_spos[0] = -1.0f * start[1];
+ m_spos[1] = start[2];
+ m_spos[2] = -1.0f * start[0];
+
+ //
+ float m_epos[3];
+ m_epos[0] = -1.0f * end[1];
+ m_epos[1] = end[2];
+ m_epos[2] = -1.0f * end[0];
+
+ //
+ dtQueryFilter m_filter;
+ m_filter.setIncludeFlags(0xffff) ;
+ m_filter.setExcludeFlags(0);
+
+ //
+ float m_polyPickExt[3];
+ m_polyPickExt[0] = 2;
+ m_polyPickExt[1] = 4;
+ m_polyPickExt[2] = 2;
+
+ //
+ dtPolyRef m_startRef;
+ dtPolyRef m_endRef;
+
+ FILE* mmap = fopen(".mmap", "rb");
+ dtNavMeshParams params;
+ fread(&params, sizeof(dtNavMeshParams), 1, mmap);
+ fclose(mmap);
+
+ dtNavMesh* navMesh = new dtNavMesh();
+ dtNavMeshQuery* navMeshQuery = new dtNavMeshQuery();
+
+ navMesh->init(&params);
+ LoadTile(navMesh, ".mmtile");
+ LoadTile(navMesh, ".mmtile");
+ LoadTile(navMesh, ".mmtile");
+ LoadTile(navMesh, ".mmtile");
+ LoadTile(navMesh, ".mmtile");
+ LoadTile(navMesh, ".mmtile");
+
+ navMeshQuery->init(navMesh, 2048);
+
+ float nearestPt[3];
+
+ dtStatus status = navMeshQuery->findNearestPoly(m_spos, m_polyPickExt, &m_filter, &m_startRef, nearestPt);
+ status = navMeshQuery->findNearestPoly(m_epos, m_polyPickExt, &m_filter, &m_endRef, nearestPt);
+
+ //
+ if ( !m_startRef || !m_endRef )
+ {
+ std::cerr << "Could not find any nearby poly's (" << m_startRef << "," << m_endRef << ")" << std::endl;
+ return 0;
+ }
+
+ printf("Found!");
+ }
+
+ return 0;
+}
\ No newline at end of file
diff --git a/src/tools/mesh_extractor/Model.cpp b/src/tools/mesh_extractor/Model.cpp
new file mode 100644
index 0000000..10fde0e
--- /dev/null
+++ b/src/tools/mesh_extractor/Model.cpp
@@ -0,0 +1,64 @@
+#include "Model.h"
+#include "MPQManager.h"
+#include "Utils.h"
+
+Model::Model( std::string path ) : IsCollidable(false), IsBad(false)
+{
+ Stream = MPQHandler->GetFile(Utils::FixModelPath(path));
+ if (!Stream)
+ {
+ IsBad = true;
+ return;
+ }
+ Header.Read(Stream);
+ if (Header.OffsetBoundingNormals > 0 && Header.OffsetBoundingVertices > 0 &&
+ Header.OffsetBoundingTriangles > 0 && Header.BoundingRadius > 0.0f)
+ {
+ IsCollidable = true;
+ ReadVertices(Stream);
+ ReadBoundingNormals(Stream);
+ ReadBoundingTriangles(Stream);
+ }
+}
+
+Model::~Model()
+{
+ if (Stream)
+ fclose(Stream);
+}
+
+void Model::ReadVertices( FILE* stream )
+{
+ fseek(stream, Header.OffsetBoundingVertices, SEEK_SET);
+ Vertices.reserve(Header.CountBoundingVertices);
+ for (uint32 i = 0; i < Header.CountBoundingVertices; ++i)
+ {
+ Vertices.push_back(Vector3::Read(stream));
+ if (Constants::ToWoWCoords)
+ Vertices[i] = Utils::ToWoWCoords(Vertices[i]);
+ }
+}
+
+void Model::ReadBoundingTriangles( FILE* stream )
+{
+ fseek(stream, Header.OffsetBoundingTriangles, SEEK_SET);
+ Triangles.reserve(Header.CountBoundingTriangles / 3);
+ for (uint32 i = 0; i < Header.CountBoundingTriangles / 3; i++)
+ {
+ Triangle<uint16> tri;
+ tri.Type = Constants::TRIANGLE_TYPE_DOODAD;
+ fread(&tri.V0, sizeof(uint16), 1, stream);
+ fread(&tri.V1, sizeof(uint16), 1, stream);
+ fread(&tri.V2, sizeof(uint16), 1, stream);
+ Triangles.push_back(tri);
+ }
+}
+
+void Model::ReadBoundingNormals( FILE* stream )
+{
+ fseek(stream, Header.OffsetBoundingNormals, SEEK_SET);
+ Normals.reserve(Header.CountBoundingNormals);
+ for (uint32 i = 0; i < Header.CountBoundingNormals; i++)
+ Normals.push_back(Vector3::Read(stream));
+}
+
diff --git a/src/tools/mesh_extractor/Model.h b/src/tools/mesh_extractor/Model.h
new file mode 100644
index 0000000..ea9331e
--- /dev/null
+++ b/src/tools/mesh_extractor/Model.h
@@ -0,0 +1,23 @@
+#ifndef MODEL_H
+#define MODEL_H
+#include <vector>
+#include "Utils.h"
+
+class Model
+{
+public:
+ Model(std::string path);
+ ~Model();
+
+ void ReadVertices(FILE* stream);
+ void ReadBoundingTriangles(FILE* stream);
+ void ReadBoundingNormals(FILE* stream);
+ ModelHeader Header;
+ std::vector<Vector3> Vertices;
+ std::vector<Vector3> Normals;
+ std::vector<Triangle<uint16> > Triangles;
+ bool IsCollidable;
+ FILE* Stream;
+ bool IsBad;
+};
+#endif
\ No newline at end of file
diff --git a/src/tools/mesh_extractor/ObjectDataHandler.cpp b/src/tools/mesh_extractor/ObjectDataHandler.cpp
new file mode 100644
index 0000000..789efc6
--- /dev/null
+++ b/src/tools/mesh_extractor/ObjectDataHandler.cpp
@@ -0,0 +1,21 @@
+#include "ObjectDataHandler.h"
+#include "Chunk.h"
+#include "ADT.h"
+#include "ChunkedData.h"
+
+void ObjectDataHandler::ProcessMapChunk( MapChunk* chunk )
+{
+ if (!Source->HasObjectData)
+ return;
+ // fuck it blizzard, why is this crap necessary?
+ int32 firstIndex = Source->ObjectData->GetFirstIndex("MCNK");
+ if (firstIndex == -1)
+ return;
+ if (uint32(firstIndex + chunk->Index) > Source->ObjectData->Chunks.size())
+ return;
+ Chunk* ourChunk = Source->ObjectData->Chunks[firstIndex + chunk->Index];
+ if (ourChunk->Length == 0)
+ return;
+ ChunkedData* subChunks = new ChunkedData(ourChunk->GetStream(), ourChunk->Length, 2);
+ ProcessInternal(subChunks);
+}
diff --git a/src/tools/mesh_extractor/ObjectDataHandler.h b/src/tools/mesh_extractor/ObjectDataHandler.h
new file mode 100644
index 0000000..75b4e45
--- /dev/null
+++ b/src/tools/mesh_extractor/ObjectDataHandler.h
@@ -0,0 +1,15 @@
+#ifndef ODATA_HNDL_H
+#define ODATA_HNDL_H
+#include "ADT.h"
+#include "MapChunk.h"
+
+class ObjectDataHandler
+{
+public:
+ ObjectDataHandler(ADT* _adt) : Source(_adt) {}
+
+ void ProcessMapChunk(MapChunk* chunk);
+ virtual void ProcessInternal(ChunkedData* data) = 0;
+ ADT* Source;
+};
+#endif
\ No newline at end of file
diff --git a/src/tools/mesh_extractor/TileBuilder.cpp b/src/tools/mesh_extractor/TileBuilder.cpp
new file mode 100644
index 0000000..2a02bc5
--- /dev/null
+++ b/src/tools/mesh_extractor/TileBuilder.cpp
@@ -0,0 +1,310 @@
+#include "ContinentBuilder.h"
+#include "TileBuilder.h"
+#include "Geometry.h"
+#include "Constants.h"
+#include "Utils.h"
+#include "Cache.h"
+#include "ADT.h"
+#include "WDT.h"
+#include "Recast.h"
+#include "RecastAlloc.h"
+#include "DetourNavMeshBuilder.h"
+
+#include "ace/Synch.h"
+
+TileBuilder::TileBuilder(ContinentBuilder* _cBuilder, std::string world, int x, int y, uint32 mapId) : _Geometry(NULL), World(world), X(x), Y(y), MapId(mapId), DataSize(0), cBuilder(_cBuilder)
+{
+ /*
+ Test, non-working values
+ // Cell Size = TileSize / TileVoxelSize
+ // 1800 = TileVoxelSize
+ Config.cs = Constants::TileSize / 1800;
+ // Cell Height
+ Config.ch = 0.4f;
+ // Min Region Area = 20^2
+ Config.minRegionArea = 20*20;
+ // Merge Region Area = 40^2
+ Config.mergeRegionArea = 40*40;
+ Config.tileSize = Constants::TileSize / 4;
+ Config.walkableSlopeAngle = 50.0f;
+ Config.detailSampleDist = 3.0f;
+ Config.detailSampleMaxError = 1.25f;
+ Config.walkableClimb = floorf(1.0f / Config.ch);
+ Config.walkableHeight = ceilf(1.652778f / Config.ch);
+ Config.walkableRadius = ceilf(0.2951389f / Config.cs);
+ Config.maxEdgeLen = Config.walkableRadius * 8;
+ Config.borderSize = Config.walkableRadius + 4;
+ Config.width = 1800 + Config.borderSize * 2;
+ Config.height = 1800 + Config.borderSize * 2;
+ Config.maxVertsPerPoly = 6;
+ Config.maxSimplificationError = 1.3f;
+ */
+
+ // All are in UNIT metrics!
+ memset(&Config, 0, sizeof(rcConfig));
+
+ Config.maxVertsPerPoly = DT_VERTS_PER_POLYGON;
+ Config.cs = Constants::BaseUnitDim;
+ Config.ch = Constants::BaseUnitDim;
+ Config.walkableSlopeAngle = 60.0f;
+ Config.tileSize = Constants::VertexPerTile;
+ Config.walkableRadius = 1;
+ Config.borderSize = Config.walkableRadius + 3;
+ Config.maxEdgeLen = Constants::VertexPerTile + 1; //anything bigger than tileSize
+ Config.walkableHeight = 3;
+ Config.walkableClimb = 2; // keep less than walkableHeight
+ Config.minRegionArea = rcSqr(60);
+ Config.mergeRegionArea = rcSqr(50);
+ Config.maxSimplificationError = 2.0f; // eliminates most jagged edges (tinny polygons)
+ Config.detailSampleDist = Config.cs * 64;
+ Config.detailSampleMaxError = Config.ch * 2;
+
+ Context = new rcContext;
+}
+
+void TileBuilder::CalculateTileBounds( float*& bmin, float*& bmax, dtNavMeshParams& navMeshParams )
+{
+ bmin = new float[3];
+ bmax = new float[3];
+ bmin[0] = Constants::Origin[0] /*navMeshParams.orig[0]*/ + (Constants::TileSize * X);
+ bmin[2] = Constants::Origin[2] /*navMeshParams.orig[2]*/ + (Constants::TileSize * Y);
+ bmax[0] = Constants::Origin[0] /*navMeshParams.orig[0]*/ + (Constants::TileSize * (X + 1));
+ bmax[2] = Constants::Origin[2] /*navMeshParams.orig[2]*/ + (Constants::TileSize * (Y + 1));
+}
+
+uint8* TileBuilder::Build(bool dbg, dtNavMeshParams& navMeshParams)
+{
+ _Geometry = new Geometry();
+ _Geometry->Transform = true;
+ ADT* adt = new ADT(Utils::GetAdtPath(World, X, Y));
+ adt->Read();
+ _Geometry->AddAdt(adt);
+ delete adt;
+
+ if (_Geometry->Vertices.empty() && _Geometry->Triangles.empty())
+ return NULL;
+
+ // again, we load everything - wasteful but who cares
+ for (int ty = Y - 2; ty <= Y + 2; ty++)
+ {
+ for (int tx = X - 2; tx <= X + 2; tx++)
+ {
+ // don't load main tile again
+ if (tx == X && ty == Y)
+ continue;
+
+ ADT* _adt = new ADT(Utils::GetAdtPath(World, tx, ty));
+ // If this condition is met, it means that this wdt does not contain the ADT
+ if (!_adt->Data->Stream)
+ {
+ delete _adt;
+ continue;
+ }
+ _adt->Read();
+ _Geometry->AddAdt(_adt);
+ delete _adt;
+ }
+ }
+
+ if (dbg)
+ {
+ char buff[100];
+ sprintf(buff, "mmaps/%s_%02u%02u.obj", World.c_str(), Y, X);
+ FILE* debug = fopen(buff, "wb");
+ for (uint32 i = 0; i < _Geometry->Vertices.size(); ++i)
+ fprintf(debug, "v %f %f %f\n", _Geometry->Vertices[i].x, _Geometry->Vertices[i].y, _Geometry->Vertices[i].z);
+ for (uint32 i = 0; i < _Geometry->Triangles.size(); ++i)
+ fprintf(debug, "f %i %i %i\n", _Geometry->Triangles[i].V0 + 1, _Geometry->Triangles[i].V1 + 1, _Geometry->Triangles[i].V2 + 1);
+ fclose(debug);
+ }
+
+ uint32 numVerts = _Geometry->Vertices.size();
+ uint32 numTris = _Geometry->Triangles.size();
+ float* vertices;
+ int* triangles;
+ uint8* areas;
+ _Geometry->GetRawData(vertices, triangles, areas);
+ _Geometry->Vertices.clear();
+ _Geometry->Triangles.clear();
+
+
+ rcVcopy(Config.bmin, cBuilder->bmin);
+ rcVcopy(Config.bmax, cBuilder->bmax);
+
+ // this sets the dimensions of the heightfield - should maybe happen before border padding
+ rcCalcGridSize(Config.bmin, Config.bmax, Config.cs, &Config.width, &Config.height);
+
+ // Initialize per tile config.
+ rcConfig tileCfg = Config;
+ tileCfg.width = Config.tileSize + Config.borderSize * 2;
+ tileCfg.height = Config.tileSize + Config.borderSize * 2;
+
+ // merge per tile poly and detail meshes
+ rcPolyMesh** pmmerge = new rcPolyMesh*[Constants::TilesPerMap * Constants::TilesPerMap];
+ rcPolyMeshDetail** dmmerge = new rcPolyMeshDetail*[Constants::TilesPerMap * Constants::TilesPerMap];
+
+ int nmerge = 0;
+ for (int y = 0; y < Constants::TilesPerMap; ++y)
+ {
+ for (int x = 0; x < Constants::TilesPerMap; ++x)
+ {
+ // Calculate the per tile bounding box.
+ tileCfg.bmin[0] = Config.bmin[0] + float(x * Config.tileSize - Config.borderSize) * Config.cs;
+ tileCfg.bmin[2] = Config.bmin[2] + float(y * Config.tileSize - Config.borderSize) * Config.cs;
+ tileCfg.bmax[0] = Config.bmin[0] + float((x + 1) * Config.tileSize + Config.borderSize) * Config.cs;
+ tileCfg.bmax[2] = Config.bmin[2] + float((y + 1) * Config.tileSize + Config.borderSize) * Config.cs;
+
+
+ rcHeightfield* hf = rcAllocHeightfield();
+ rcCreateHeightfield(Context, *hf, tileCfg.width, tileCfg.height, tileCfg.bmin, tileCfg.bmax, tileCfg.cs, tileCfg.ch);
+ rcClearUnwalkableTriangles(Context, tileCfg.walkableSlopeAngle, vertices, numVerts, triangles, numTris, areas);
+ rcRasterizeTriangles(Context, vertices, numVerts, triangles, areas, numTris, *hf, Config.walkableClimb);
+
+ // Once all geometry is rasterized, we do initial pass of filtering to
+ // remove unwanted overhangs caused by the conservative rasterization
+ // as well as filter spans where the character cannot possibly stand.
+ rcFilterLowHangingWalkableObstacles(Context, Config.walkableClimb, *hf);
+ rcFilterLedgeSpans(Context, tileCfg.walkableHeight, tileCfg.walkableClimb, *hf);
+ rcFilterWalkableLowHeightSpans(Context, tileCfg.walkableHeight, *hf);
+
+ // Compact the heightfield so that it is faster to handle from now on.
+ // This will result in more cache coherent data as well as the neighbours
+ // between walkable cells will be calculated.
+ rcCompactHeightfield* chf = rcAllocCompactHeightfield();
+ rcBuildCompactHeightfield(Context, tileCfg.walkableHeight, tileCfg.walkableClimb, *hf, *chf);
+
+ rcFreeHeightField(hf);
+
+ // Erode the walkable area by agent radius.
+ rcErodeWalkableArea(Context, Config.walkableRadius, *chf);
+ // Prepare for region partitioning, by calculating distance field along the walkable surface.
+ rcBuildDistanceField(Context, *chf);
+ // Partition the walkable surface into simple regions without holes.
+ rcBuildRegions(Context, *chf, tileCfg.borderSize, tileCfg.minRegionArea, tileCfg.mergeRegionArea);
+
+ // Create contours.
+ rcContourSet* cset = rcAllocContourSet();
+ rcBuildContours(Context, *chf, tileCfg.maxSimplificationError, tileCfg.maxEdgeLen, *cset);
+
+ // Build polygon navmesh from the contours.
+ rcPolyMesh* pmesh = rcAllocPolyMesh();
+ rcBuildPolyMesh(Context, *cset, tileCfg.maxVertsPerPoly, *pmesh);
+
+ // Build detail mesh.
+ rcPolyMeshDetail* dmesh = rcAllocPolyMeshDetail();
+ rcBuildPolyMeshDetail(Context, *pmesh, *chf, tileCfg.detailSampleDist, tileCfg.detailSampleMaxError, *dmesh);
+
+ // Free memory
+ rcFreeCompactHeightfield(chf);
+ rcFreeContourSet(cset);
+
+ pmmerge[nmerge] = pmesh;
+ dmmerge[nmerge] = dmesh;
+ ++nmerge;
+ }
+ }
+
+ rcPolyMesh* pmesh = rcAllocPolyMesh();
+ rcMergePolyMeshes(Context, pmmerge, nmerge, *pmesh);
+
+ rcPolyMeshDetail* dmesh = rcAllocPolyMeshDetail();
+ rcMergePolyMeshDetails(Context, dmmerge, nmerge, *dmesh);
+
+ delete[] pmmerge;
+ delete[] dmmerge;
+
+ printf("[%02i,%02i] Meshes merged!\n", X, Y);
+
+ // Remove padding from the polymesh data. (Remove this odditity)
+ for (int i = 0; i < pmesh->nverts; ++i)
+ {
+ unsigned short* v = &pmesh->verts[i * 3];
+ v[0] -= (unsigned short)Config.borderSize;
+ v[2] -= (unsigned short)Config.borderSize;
+ }
+
+ // Set flags according to area types (e.g. Swim for Water)
+ for (int i = 0; i < pmesh->npolys; i++)
+ {
+ if (pmesh->areas[i] == Constants::POLY_AREA_ROAD || pmesh->areas[i] == Constants::POLY_AREA_TERRAIN)
+ pmesh->flags[i] = Constants::POLY_FLAG_WALK;
+ else if (pmesh->areas[i] == Constants::POLY_AREA_WATER)
+ pmesh->flags[i] = Constants::POLY_FLAG_SWIM;
+ }
+
+ dtNavMeshCreateParams params;
+ memset(&params, 0, sizeof(params));
+ // PolyMesh data
+ params.verts = pmesh->verts;
+ params.vertCount = pmesh->nverts;
+ params.polys = pmesh->polys;
+ params.polyAreas = pmesh->areas;
+ params.polyFlags = pmesh->flags;
+ params.polyCount = pmesh->npolys;
+ params.nvp = pmesh->nvp;
+ // PolyMeshDetail data
+ params.detailMeshes = dmesh->meshes;
+ params.detailVerts = dmesh->verts;
+ params.detailVertsCount = dmesh->nverts;
+ params.detailTris = dmesh->tris;
+ params.detailTriCount = dmesh->ntris;
+ rcVcopy(params.bmin, pmesh->bmin);
+ rcVcopy(params.bmax, pmesh->bmax);
+ // General settings
+ params.ch = Config.ch;
+ params.cs = Config.cs;
+ params.walkableClimb = Constants::BaseUnitDim * Config.walkableClimb;
+ params.walkableHeight = Constants::BaseUnitDim * Config.walkableHeight;
+ params.walkableRadius = Constants::BaseUnitDim * Config.walkableRadius;
+ params.tileX = (((cBuilder->bmin[0] + cBuilder->bmax[0]) / 2) - navMeshParams.orig[0]) / Constants::TileSize;
+ params.tileY = (((cBuilder->bmin[2] + cBuilder->bmax[2]) / 2) - navMeshParams.orig[2]) / Constants::TileSize;
+
+ rcVcopy(params.bmin, cBuilder->bmin);
+ rcVcopy(params.bmax, cBuilder->bmax);
+
+ // Offmesh-connection settings
+ params.offMeshConCount = 0; // none for now
+
+ params.tileSize = Constants::VertexPerMap;
+
+ if (!params.polyCount || !params.polys || Constants::TilesPerMap * Constants::TilesPerMap == params.polyCount)
+ {
+ // we have flat tiles with no actual geometry - don't build those, its useless
+ // keep in mind that we do output those into debug info
+ // drop tiles with only exact count - some tiles may have geometry while having less tiles
+ printf("[%02i,%02i] No polygons to build on tile, skipping.\n", X, Y);
+ rcFreePolyMesh(pmesh);
+ rcFreePolyMeshDetail(dmesh);
+ delete areas;
+ delete triangles;
+ delete vertices;
+ return NULL;
+ }
+
+ int navDataSize;
+ uint8* navData;
+ printf("[%02i,%02i] Creating the navmesh with %i vertices, %i polys, %i triangles!\n", X, Y, pmesh->nverts, pmesh->npolys, dmesh->ntris);
+ bool result = dtCreateNavMeshData(&params, &navData, &navDataSize);
+
+ // Free some memory
+ rcFreePolyMesh(pmesh);
+ rcFreePolyMeshDetail(dmesh);
+ delete areas;
+ delete triangles;
+ delete vertices;
+
+ if (result)
+ {
+ printf("[%02i,%02i] NavMesh created, size %i!\n", X, Y, navDataSize);
+ DataSize = navDataSize;
+ return navData;
+ }
+
+ return NULL;
+}
+
+TileBuilder::~TileBuilder()
+{
+ delete Context;
+ delete _Geometry;
+}
diff --git a/src/tools/mesh_extractor/TileBuilder.h b/src/tools/mesh_extractor/TileBuilder.h
new file mode 100644
index 0000000..40c96f6
--- /dev/null
+++ b/src/tools/mesh_extractor/TileBuilder.h
@@ -0,0 +1,30 @@
+#ifndef TILE_BUILD_H
+#define TILE_BUILD_H
+#include <string>
+#include "Recast.h"
+
+#include "Geometry.h"
+
+class ContinentBuilder;
+class WDT;
+
+class TileBuilder
+{
+public:
+ TileBuilder(ContinentBuilder* _cBuilder, std::string world, int x, int y, uint32 mapId);
+ ~TileBuilder();
+
+ void CalculateTileBounds(float*& bmin, float*& bmax, dtNavMeshParams& navMeshParams);
+ uint8* Build(bool dbg, dtNavMeshParams& navMeshParams);
+
+ std::string World;
+ int X;
+ int Y;
+ int MapId;
+ rcConfig Config;
+ rcContext* Context;
+ Geometry* _Geometry;
+ uint32 DataSize;
+ ContinentBuilder* cBuilder;
+};
+#endif
\ No newline at end of file
diff --git a/src/tools/mesh_extractor/Utils.cpp b/src/tools/mesh_extractor/Utils.cpp
new file mode 100644
index 0000000..3639cb8
--- /dev/null
+++ b/src/tools/mesh_extractor/Utils.cpp
@@ -0,0 +1,508 @@
+#include "Utils.h"
+#include "WorldModelHandler.h"
+#include "Constants.h"
+#include <cstring>
+#include "G3D/Matrix4.h"
+#include "G3D/Quat.h"
+
+#ifdef _WIN32
+ #include "direct.h"
+#else
+ #include <sys/stat.h>
+ #include <unistd.h>
+#endif
+
+const float Constants::TileSize = 533.0f + (1/3.0f);
+const float Constants::MaxXY = 32.0f * Constants::TileSize;
+const float Constants::ChunkSize = Constants::TileSize / 16.0f;
+const float Constants::UnitSize = Constants::ChunkSize / 8.0f;
+const float Constants::Origin[] = { -Constants::MaxXY, 0.0f, -Constants::MaxXY };
+const float Constants::PI = 3.1415926f;
+const float Constants::MaxStandableHeight = 1.5f;
+const char* Constants::VMAPMagic = "VMAP041";
+bool Constants::ToWoWCoords = false;
+const float Constants::BaseUnitDim = 0.533333f;
+const int Constants::VertexPerMap = (Constants::TileSize / Constants::BaseUnitDim) + 0.5f;
+const int Constants::VertexPerTile = 40;
+const int Constants::TilesPerMap = Constants::VertexPerMap / Constants::VertexPerTile;
+
+void Utils::CreateDir( const std::string& Path )
+{
+#ifdef _WIN32
+ _mkdir( Path.c_str());
+#else
+ mkdir( Path.c_str(), 0777 );
+#endif
+}
+
+void Utils::Reverse(char word[])
+{
+ int len = strlen(word);
+ for (int i = 0;i < len / 2; i++)
+ {
+ word[i] ^= word[len-i-1];
+ word[len-i-1] ^= word[i];
+ word[i] ^= word[len-i-1];
+ }
+}
+
+std::string Utils::ReadString( FILE* file )
+{
+ std::string ret;
+ int i = 0;
+ while (true)
+ {
+ char b;
+ fread(&b, sizeof(char), 1, file);
+ if (b == 0)
+ break;
+ ret[i++] = b;
+ }
+ return ret;
+}
+
+uint32 Utils::Size( FILE* file )
+{
+ // store the old position
+ uint32 offset = ftell(file);
+ // Get file size
+ fseek(file, 0, SEEK_END);
+ uint32 size = ftell(file);
+ // reset back to the old position
+ fseek(file, offset, SEEK_SET);
+ return size;
+}
+
+Vector3 Utils::ToRecast( Vector3 val )
+{
+ return Vector3(-val.y, val.z, -val.x);
+}
+
+std::string Utils::GetAdtPath( std::string world, int x, int y )
+{
+ return "World\\Maps\\" + world + "\\" + world + "_" + Utils::ToString(x) + "_" + Utils::ToString(y) + ".adt";
+}
+
+std::string Utils::FixModelPath( std::string path )
+{
+ return Utils::GetPathBase(path) + ".M2";
+}
+
+G3D::Matrix4 Utils::RotationX(float angle)
+{
+ float _cos = cos(angle);
+ float _sin = sin(angle);
+ G3D::Matrix4 ret = G3D::Matrix4::identity();
+ ret[2][2] = _cos;
+ ret[2][3] = _sin;
+ ret[3][2] = -_sin;
+ ret[3][3] = _cos;
+ return ret;
+}
+
+G3D::Matrix4 Utils::GetTransformation(IDefinition def)
+{
+ G3D::Matrix4 translation;
+ if (def.Position.x == 0.0f && def.Position.y == 0.0f && def.Position.z == 0.0f)
+ translation = G3D::Matrix4::identity();
+ else
+ translation = G3D::Matrix4::translation(-(def.Position.z - Constants::MaxXY),
+ -(def.Position.x - Constants::MaxXY), def.Position.y);
+
+ G3D::Matrix4 rotation = RotationX(ToRadians(def.Rotation.z)) * RotationY(ToRadians(def.Rotation.x)) * RotationZ(ToRadians(def.Rotation.y + 180));
+ if (def.Scale() < 1.0f || def.Scale() > 1.0f)
+ return G3D::Matrix4::scale(def.Scale()) * rotation * translation;
+ return rotation * translation;
+}
+
+G3D::Matrix4 Utils::RotationY( float angle )
+{
+ float _cos = cos(angle);
+ float _sin = sin(angle);
+ G3D::Matrix4 ret = G3D::Matrix4::identity();
+ ret[1][1] = _cos;
+ ret[1][3] = -_sin;
+ ret[3][1] = _sin;
+ ret[3][3] = _cos;
+ return ret;
+}
+
+G3D::Matrix4 Utils::RotationZ( float angle )
+{
+ float _cos = cos(angle);
+ float _sin = sin(angle);
+ G3D::Matrix4 ret = G3D::Matrix4::identity();
+ ret[1][1] = _cos;
+ ret[1][2] = _sin;
+ ret[2][1] = -_sin;
+ ret[2][2] = _cos;
+ return ret;
+}
+
+float Utils::ToRadians( float degrees )
+{
+ return Constants::PI * degrees / 180.0f;
+}
+
+Vector3 Utils::VectorTransform( Vector3 vec, G3D::Matrix4 matrix )
+{
+ Vector3 ret;
+ ret.x = vec.x * matrix[1][1] + vec.y * matrix[2][1] + vec.z * matrix[3][1] + matrix[4][1];
+ ret.y = vec.x * matrix[1][2] + vec.y * matrix[2][2] + vec.z * matrix[3][2] + matrix[4][2];
+ ret.z = vec.x * matrix[1][3] + vec.y * matrix[2][3] + vec.z * matrix[3][3] + matrix[4][3];
+ return ret;
+}
+
+std::string Utils::GetPathBase( std::string path )
+{
+ int lastIndex = path.find_last_of(".");
+ if (lastIndex != std::string::npos)
+ return path.substr(0, lastIndex);
+ return path;
+}
+
+Vector3 Vector3::Read( FILE* file )
+{
+ Vector3 ret;
+ fread(&ret, sizeof(Vector3), 1, file);
+ return ret;
+}
+
+Vector3 Utils::GetLiquidVert(G3D::Matrix4 transformation, Vector3 basePosition, float height, int x, int y)
+{
+ if (Utils::Distance(height, 0.0f) > 0.5f)
+ basePosition.z = 0.0f;
+ return Utils::VectorTransform(basePosition + Vector3(basePosition.x * Constants::UnitSize, basePosition.y * Constants::UnitSize, height), transformation);
+}
+
+float Utils::Distance( float x, float y )
+{
+ return sqrt(x*x + y*y);
+}
+
+std::string Utils::Replace( std::string str, const std::string& oldStr, const std::string& newStr )
+{
+ size_t pos = 0;
+ while((pos = str.find(oldStr, pos)) != std::string::npos)
+ {
+ str.replace(pos, oldStr.length(), newStr);
+ pos += newStr.length();
+ }
+ return str;
+}
+
+G3D::Matrix4 Utils::GetWmoDoodadTransformation( DoodadInstance inst, WorldModelDefinition root )
+{
+ G3D::Matrix4 rootTransformation = Utils::GetTransformation(root);
+ G3D::Matrix4 translation = G3D::Matrix4::translation(inst.Position.x, inst.Position.y, inst.Position.z);
+ G3D::Matrix4 scale = G3D::Matrix4::scale(inst.Scale);
+ G3D::Matrix4 rotation = Utils::RotationY(Constants::PI);
+ G3D::Quat quat(-inst.QuatY, inst.QuatZ, -inst.QuatX, inst.QuatW);
+ G3D::Matrix4 quatRotation = quat.toRotationMatrix();
+
+ return scale * rotation * quatRotation ** translation * rootTransformation;
+}
+
+void Utils::SaveToDisk( FILE* stream, std::string path )
+{
+ FILE* disk = fopen(path.c_str(), "wb");
+ if (!disk)
+ {
+ printf("Could not save file %s to disk, please verify that you have write permissions on that directory\n", path.c_str());
+ return;
+ }
+
+ uint32 size = Utils::Size(stream);
+ uint8* data = new uint8[size];
+ // Read the data to an array
+ fread(data, 1, size, stream);
+ // And write it in the file
+ fwrite(data, 1, size, disk);
+
+ // Close the filestream
+ fclose(disk);
+ // Free the used memory
+ delete data;
+}
+
+Vector3 Utils::ToWoWCoords( Vector3 vec )
+{
+ return Vector3(vec.x, -vec.z, vec.y);
+}
+
+std::string Utils::GetExtension( std::string path )
+{
+ std::string::size_type idx = path.rfind('.');
+ std::string extension = "";
+
+ if(idx != std::string::npos)
+ extension = path.substr(idx+1);
+ return extension;
+}
+
+void MapChunkHeader::Read(FILE* stream)
+{
+ fread(&Flags, sizeof(uint32), 1, stream);
+ fread(&IndexX, sizeof(uint32), 1, stream);
+ fread(&IndexY, sizeof(uint32), 1, stream);
+ fread(&Layers, sizeof(uint32), 1, stream);
+ fread(&DoodadRefs, sizeof(uint32), 1, stream);
+ fread(&OffsetMCVT, sizeof(uint32), 1, stream);
+ fread(&OffsetMCNR, sizeof(uint32), 1, stream);
+ fread(&OffsetMCLY, sizeof(uint32), 1, stream);
+ fread(&OffsetMCRF, sizeof(uint32), 1, stream);
+ fread(&OffsetMCAL, sizeof(uint32), 1, stream);
+ fread(&SizeMCAL, sizeof(uint32), 1, stream);
+ fread(&OffsetMCSH, sizeof(uint32), 1, stream);
+ fread(&SizeMCSH, sizeof(uint32), 1, stream);
+ fread(&AreaId, sizeof(uint32), 1, stream);
+ fread(&MapObjectRefs, sizeof(uint32), 1, stream);
+ fread(&Holes, sizeof(uint32), 1, stream);
+ LowQualityTextureMap = new uint32[4];
+ fread(LowQualityTextureMap, sizeof(uint32), 4, stream);
+ fread(&PredTex, sizeof(uint32), 1, stream);
+ fread(&NumberEffectDoodad, sizeof(uint32), 1, stream);
+ fread(&OffsetMCSE, sizeof(uint32), 1, stream);
+ fread(&SoundEmitters, sizeof(uint32), 1, stream);
+ fread(&OffsetMCLQ, sizeof(uint32), 1, stream);
+ fread(&SizeMCLQ, sizeof(uint32), 1, stream);
+ Position = Vector3::Read(stream);
+ fread(&OffsetMCCV, sizeof(uint32), 1, stream);
+}
+
+void MHDR::Read(FILE* stream)
+{
+ fread(&Flags, sizeof(uint32), 1, stream);
+ fread(&OffsetMCIN, sizeof(uint32), 1, stream);
+ fread(&OffsetMTEX, sizeof(uint32), 1, stream);
+ fread(&OffsetMMDX, sizeof(uint32), 1, stream);
+ fread(&OffsetMMID, sizeof(uint32), 1, stream);
+ fread(&OffsetMWMO, sizeof(uint32), 1, stream);
+ fread(&OffsetMWID, sizeof(uint32), 1, stream);
+ fread(&OffsetMDDF, sizeof(uint32), 1, stream);
+ fread(&OffsetMODF, sizeof(uint32), 1, stream);
+ fread(&OffsetMFBO, sizeof(uint32), 1, stream);
+ fread(&OffsetMH2O, sizeof(uint32), 1, stream);
+ fread(&OffsetMTFX, sizeof(uint32), 1, stream);
+}
+
+void ModelHeader::Read(FILE* stream)
+{
+ fread(&Magic, sizeof(char), 4, stream);
+ Magic[4] = '\0'; // null-terminate it.
+ fread(&Version, sizeof(uint32), 1, stream);
+ fread(&LengthModelName, sizeof(uint32), 1, stream);
+ fread(&OffsetName, sizeof(uint32), 1, stream);
+ fread(&ModelFlags, sizeof(uint32), 1, stream);
+ fread(&CountGlobalSequences, sizeof(uint32), 1, stream);
+ fread(&OffsetGlobalSequences, sizeof(uint32), 1, stream);
+ fread(&CountAnimations, sizeof(uint32), 1, stream);
+ fread(&OffsetAnimations, sizeof(uint32), 1, stream);
+ fread(&CountAnimationLookup, sizeof(uint32), 1, stream);
+ fread(&OffsetAnimationLookup, sizeof(uint32), 1, stream);
+ fread(&CountBones, sizeof(uint32), 1, stream);
+ fread(&OffsetBones, sizeof(uint32), 1, stream);
+ fread(&CountKeyBoneLookup, sizeof(uint32), 1, stream);
+ fread(&OffsetKeyBoneLookup, sizeof(uint32), 1, stream);
+ fread(&CountVertices, sizeof(uint32), 1, stream);
+ fread(&OffsetVertices, sizeof(uint32), 1, stream);
+ fread(&CountViews, sizeof(uint32), 1, stream);
+ fread(&CountColors, sizeof(uint32), 1, stream);
+ fread(&OffsetColors, sizeof(uint32), 1, stream);
+ fread(&CountTextures, sizeof(uint32), 1, stream);
+ fread(&OffsetTextures, sizeof(uint32), 1, stream);
+ fread(&CountTransparency, sizeof(uint32), 1, stream);
+ fread(&OffsetTransparency, sizeof(uint32), 1, stream);
+ fread(&CountUvAnimation, sizeof(uint32), 1, stream);
+ fread(&OffsetUvAnimation, sizeof(uint32), 1, stream);
+ fread(&CountTexReplace, sizeof(uint32), 1, stream);
+ fread(&OffsetTexReplace, sizeof(uint32), 1, stream);
+ fread(&CountRenderFlags, sizeof(uint32), 1, stream);
+ fread(&OffsetRenderFlags, sizeof(uint32), 1, stream);
+ fread(&CountBoneLookup, sizeof(uint32), 1, stream);
+ fread(&OffsetBoneLookup, sizeof(uint32), 1, stream);
+ fread(&CountTexLookup, sizeof(uint32), 1, stream);
+ fread(&OffsetTexLookup, sizeof(uint32), 1, stream);
+ fread(&CountTexUnits, sizeof(uint32), 1, stream);
+ fread(&OffsetTexUnits, sizeof(uint32), 1, stream);
+ fread(&CountTransLookup, sizeof(uint32), 1, stream);
+ fread(&OffsetTransLookup, sizeof(uint32), 1, stream);
+ fread(&CountUvAnimLookup, sizeof(uint32), 1, stream);
+ fread(&OffsetUvAnimLookup, sizeof(uint32), 1, stream);
+ VertexBox[0] = Vector3::Read(stream);
+ VertexBox[1] = Vector3::Read(stream);
+ fread(&VertexRadius, sizeof(float), 1, stream);
+ BoundingBox[0] = Vector3::Read(stream);
+ BoundingBox[1] = Vector3::Read(stream);
+ fread(&BoundingRadius, sizeof(float), 1, stream);
+ fread(&CountBoundingTriangles, sizeof(uint32), 1, stream);
+ fread(&OffsetBoundingTriangles, sizeof(uint32), 1, stream);
+ fread(&CountBoundingVertices, sizeof(uint32), 1, stream);
+ fread(&OffsetBoundingVertices, sizeof(uint32), 1, stream);
+ fread(&CountBoundingNormals, sizeof(uint32), 1, stream);
+ fread(&OffsetBoundingNormals, sizeof(uint32), 1, stream);
+}
+
+WorldModelHeader WorldModelHeader::Read(FILE* stream)
+{
+ WorldModelHeader ret;
+ fread(&ret.CountMaterials, sizeof(uint32), 1, stream);
+ fread(&ret.CountGroups, sizeof(uint32), 1, stream);
+ fread(&ret.CountPortals, sizeof(uint32), 1, stream);
+ fread(&ret.CountLights, sizeof(uint32), 1, stream);
+ fread(&ret.CountModels, sizeof(uint32), 1, stream);
+ fread(&ret.CountDoodads, sizeof(uint32), 1, stream);
+ fread(&ret.CountSets, sizeof(uint32), 1, stream);
+ fread(&ret.AmbientColorUnk, sizeof(uint32), 1, stream);
+ fread(&ret.WmoId, sizeof(uint32), 1, stream);
+ ret.BoundingBox[0] = Vector3::Read(stream);
+ ret.BoundingBox[1] = Vector3::Read(stream);
+ fread(&ret.LiquidTypeRelated, sizeof(uint32), 1, stream);
+ return ret;
+}
+
+DoodadInstance DoodadInstance::Read(FILE* stream)
+{
+ DoodadInstance ret;
+ fread(&ret.FileOffset, sizeof(uint32), 1, stream);
+ ret.Position = Vector3::Read(stream);
+ fread(&ret.QuatW, sizeof(float), 1, stream);
+ fread(&ret.QuatX, sizeof(float), 1, stream);
+ fread(&ret.QuatY, sizeof(float), 1, stream);
+ fread(&ret.QuatZ, sizeof(float), 1, stream);
+ fread(&ret.Scale, sizeof(float), 1, stream);
+ fread(&ret.LightColor, sizeof(uint32), 1, stream);
+ return ret;
+}
+
+DoodadSet DoodadSet::Read(FILE* stream)
+{
+ DoodadSet ret;
+ char name[21];
+ fread(&name, sizeof(char), 20, stream);
+ name[20] = '\0';
+ ret.Name = name;
+ fread(&ret.FirstInstanceIndex, sizeof(uint32), 1, stream);
+ fread(&ret.CountInstances, sizeof(uint32), 1, stream);
+ fread(&ret.UnknownZero, sizeof(uint32), 1, stream);
+ return ret;
+}
+
+LiquidHeader LiquidHeader::Read(FILE* stream)
+{
+ LiquidHeader ret;
+ fread(&ret.CountXVertices, sizeof(uint32), 1, stream);
+ fread(&ret.CountYVertices, sizeof(uint32), 1, stream);
+ fread(&ret.Width, sizeof(uint32), 1, stream);
+ fread(&ret.Height, sizeof(uint32), 1, stream);
+ ret.BaseLocation = Vector3::Read(stream);
+ fread(&ret.MaterialId, sizeof(uint16), 1, stream);
+ return ret;
+}
+
+LiquidData LiquidData::Read(FILE* stream, LiquidHeader& header)
+{
+ LiquidData ret;
+ ret.HeightMap = new float*[header.CountXVertices];
+ for (uint32 i = 0; i < header.CountXVertices; ++i)
+ ret.HeightMap[i] = new float[header.CountYVertices];
+
+ ret.RenderFlags = new uint8*[header.Width];
+ for (uint32 i = 0; i < header.Width; ++i)
+ ret.RenderFlags[i] = new uint8[header.Height];
+
+ for (uint32 y = 0; y < header.CountYVertices; y++)
+ {
+ for (uint32 x = 0; x < header.CountXVertices; x++)
+ {
+ uint32 discard;
+ fread(&discard, sizeof(uint32), 1, stream);
+ float tmp;
+ fread(&tmp, sizeof(float), 1, stream);
+ ret.HeightMap[x][y] = tmp;
+ }
+ }
+
+ for (uint32 y = 0; y < header.Height; y++)
+ {
+ for (uint32 x = 0; x < header.Width; x++)
+ {
+ uint8 tmp;
+ fread(&tmp, sizeof(uint8), 1, stream);
+ ret.RenderFlags[x][y] = tmp;
+ }
+ }
+
+ return ret;
+}
+
+H2ORenderMask H2ORenderMask::Read(FILE* stream)
+{
+ H2ORenderMask ret;
+ fread(&ret.Mask, sizeof(uint8), 8, stream);
+ return ret;
+}
+
+bool MCNKLiquidData::IsWater(int x, int y, float height)
+{
+ if (!Heights)
+ return false;
+ if (!Mask.ShouldRender(x, y))
+ return false;
+ float diff = Heights[x][y] - height;
+ if (diff > Constants::MaxStandableHeight)
+ return true;
+ return false;
+}
+
+H2OHeader H2OHeader::Read(FILE* stream)
+{
+ H2OHeader ret;
+ fread(&ret.OffsetInformation, sizeof(uint32), 1, stream);
+ fread(&ret.LayerCount, sizeof(uint32), 1, stream);
+ fread(&ret.OffsetRender, sizeof(uint32), 1, stream);
+ return ret;
+}
+
+H2OInformation H2OInformation::Read(FILE* stream)
+{
+ H2OInformation ret;
+ fread(&ret.LiquidType, sizeof(uint16), 1, stream);
+ fread(&ret.Flags, sizeof(uint16), 1, stream);
+ fread(&ret.HeightLevel1, sizeof(float), 1, stream);
+ fread(&ret.HeightLevel2, sizeof(float), 1, stream);
+ fread(&ret.OffsetX, sizeof(uint8), 1, stream);
+ fread(&ret.OffsetY, sizeof(uint8), 1, stream);
+ fread(&ret.Width, sizeof(uint8), 1, stream);
+ fread(&ret.Height, sizeof(uint8), 1, stream);
+ fread(&ret.OffsetMask2, sizeof(uint32), 1, stream);
+ fread(&ret.OffsetHeightmap, sizeof(uint32), 1, stream);
+ return ret;
+}
+
+char* Utils::GetPlainName(const char* FileName)
+{
+ char* temp;
+
+ if((temp = (char*)strrchr(FileName, '\\')) != NULL)
+ FileName = temp + 1;
+ return (char*)FileName;
+}
+
+WMOGroupHeader WMOGroupHeader::Read( FILE* stream )
+{
+ WMOGroupHeader ret;
+ fread(&ret.OffsetGroupName, sizeof(uint32), 1, stream);
+ fread(&ret.OffsetDescriptiveName, sizeof(uint32), 1, stream);
+ fread(&ret.Flags, sizeof(uint32), 1, stream);
+ ret.BoundingBox[0] = Vector3::Read(stream);
+ ret.BoundingBox[1] = Vector3::Read(stream);
+ fread(&ret.OffsetPortals, sizeof(uint32), 1, stream);
+ fread(&ret.CountPortals, sizeof(uint32), 1, stream);
+ fread(&ret.CountBatches, sizeof(uint16), 4, stream);
+ fread(&ret.Fogs, sizeof(uint8), 4, stream);
+ fread(&ret.LiquidTypeRelated, sizeof(uint32), 1, stream);
+ fread(&ret.WmoId, sizeof(uint32), 1, stream);
+
+ return ret;
+}
diff --git a/src/tools/mesh_extractor/Utils.h b/src/tools/mesh_extractor/Utils.h
new file mode 100644
index 0000000..c5dd04f
--- /dev/null
+++ b/src/tools/mesh_extractor/Utils.h
@@ -0,0 +1,377 @@
+#ifndef UTILS_H
+#define UTILS_H
+#include <cstdio>
+#include <string>
+#include <sstream>
+
+#include "G3D/Matrix4.h"
+#include "DetourNavMesh.h"
+
+#include "Common.h"
+#include "Constants.h"
+
+struct WorldModelDefinition;
+class DoodadInstance;
+
+struct Vector3
+{
+ Vector3() {}
+ Vector3(float X, float Y, float Z) : x(X), y(Y), z(Z) {}
+ float x;
+ float y;
+ float z;
+
+ Vector3 operator +(Vector3 const& other)
+ {
+ return Vector3(x + other.x, y + other.y, z + other.z);
+ }
+
+ static Vector3 Read(FILE* file);
+};
+
+struct TilePos
+{
+ TilePos(int x, int y) : X(x), Y(y) {}
+ int X;
+ int Y;
+};
+
+template<typename T>
+struct Triangle
+{
+ Triangle() {}
+ Triangle(Constants::TriangleType type, T v0, T v1, T v2) : Type(type), V0(v0), V1(v1), V2(v2) {}
+ T V0;
+ T V1;
+ T V2;
+ Constants::TriangleType Type;
+};
+
+class MapChunkHeader
+{
+public:
+ MapChunkHeader() {}
+ uint32 Flags;
+ uint32 IndexX;
+ uint32 IndexY;
+ uint32 Layers;
+ uint32 DoodadRefs;
+ uint32 OffsetMCVT;
+ uint32 OffsetMCNR;
+ uint32 OffsetMCLY;
+ uint32 OffsetMCRF;
+ uint32 OffsetMCAL;
+ uint32 SizeMCAL;
+ uint32 OffsetMCSH;
+ uint32 SizeMCSH;
+ uint32 AreaId;
+ uint32 MapObjectRefs;
+ uint32 Holes;
+ uint32* LowQualityTextureMap;
+ uint32 PredTex;
+ uint32 NumberEffectDoodad;
+ uint32 OffsetMCSE;
+ uint32 SoundEmitters;
+ uint32 OffsetMCLQ;
+ uint32 SizeMCLQ;
+ Vector3 Position;
+ uint32 OffsetMCCV;
+
+ void Read(FILE* stream);
+};
+
+class MHDR
+{
+public:
+ MHDR() {}
+ uint32 Flags;
+ uint32 OffsetMCIN;
+ uint32 OffsetMTEX;
+ uint32 OffsetMMDX;
+ uint32 OffsetMMID;
+ uint32 OffsetMWMO;
+ uint32 OffsetMWID;
+ uint32 OffsetMDDF;
+ uint32 OffsetMODF;
+ uint32 OffsetMFBO;
+ uint32 OffsetMH2O;
+ uint32 OffsetMTFX;
+
+ void Read(FILE* stream);
+};
+
+class ModelHeader
+{
+public:
+ char Magic[5];
+ uint32 Version;
+ uint32 LengthModelName;
+ uint32 OffsetName;
+ uint32 ModelFlags;
+ uint32 CountGlobalSequences;
+ uint32 OffsetGlobalSequences;
+ uint32 CountAnimations;
+ uint32 OffsetAnimations;
+ uint32 CountAnimationLookup;
+ uint32 OffsetAnimationLookup;
+ uint32 CountBones;
+ uint32 OffsetBones;
+ uint32 CountKeyBoneLookup;
+ uint32 OffsetKeyBoneLookup;
+ uint32 CountVertices;
+ uint32 OffsetVertices;
+ uint32 CountViews;
+ uint32 CountColors;
+ uint32 OffsetColors;
+ uint32 CountTextures;
+ uint32 OffsetTextures;
+ uint32 CountTransparency;
+ uint32 OffsetTransparency;
+ uint32 CountUvAnimation;
+ uint32 OffsetUvAnimation;
+ uint32 CountTexReplace;
+ uint32 OffsetTexReplace;
+ uint32 CountRenderFlags;
+ uint32 OffsetRenderFlags;
+ uint32 CountBoneLookup;
+ uint32 OffsetBoneLookup;
+ uint32 CountTexLookup;
+ uint32 OffsetTexLookup;
+ uint32 CountTexUnits;
+ uint32 OffsetTexUnits;
+ uint32 CountTransLookup;
+ uint32 OffsetTransLookup;
+ uint32 CountUvAnimLookup;
+ uint32 OffsetUvAnimLookup;
+ Vector3 VertexBox[2];
+ float VertexRadius;
+ Vector3 BoundingBox[2];
+ float BoundingRadius;
+ uint32 CountBoundingTriangles;
+ uint32 OffsetBoundingTriangles;
+ uint32 CountBoundingVertices;
+ uint32 OffsetBoundingVertices;
+ uint32 CountBoundingNormals;
+ uint32 OffsetBoundingNormals;
+
+ void Read(FILE* stream);
+};
+
+class WorldModelHeader
+{
+public:
+ WorldModelHeader() {}
+ uint32 CountMaterials;
+ uint32 CountGroups;
+ uint32 CountPortals;
+ uint32 CountLights;
+ uint32 CountModels;
+ uint32 CountDoodads;
+ uint32 CountSets;
+ uint32 AmbientColorUnk;
+ uint32 WmoId;
+ Vector3 BoundingBox[2];
+ uint32 LiquidTypeRelated;
+
+ static WorldModelHeader Read(FILE* stream);
+};
+
+class DoodadInstance
+{
+public:
+ DoodadInstance() {}
+ uint32 FileOffset;
+ std::string File;
+ Vector3 Position;
+ float QuatW;
+ float QuatX;
+ float QuatY;
+ float QuatZ;
+ float Scale;
+ uint32 LightColor;
+
+ static DoodadInstance Read(FILE* stream);
+};
+
+class DoodadSet
+{
+public:
+ DoodadSet() {}
+ std::string Name;
+ uint32 FirstInstanceIndex;
+ uint32 CountInstances;
+ uint32 UnknownZero;
+
+ static DoodadSet Read(FILE* stream);
+};
+
+class LiquidHeader
+{
+public:
+ LiquidHeader() {}
+ uint32 CountXVertices;
+ uint32 CountYVertices;
+ uint32 Width;
+ uint32 Height;
+ Vector3 BaseLocation;
+ uint16 MaterialId;
+
+ static LiquidHeader Read(FILE* stream);
+};
+
+class LiquidData
+{
+public:
+ LiquidData() {}
+ float** HeightMap;
+ uint8** RenderFlags;
+
+ bool ShouldRender(int x, int y)
+ {
+ return RenderFlags[x][y] != 0x0F;
+ }
+
+ static LiquidData Read(FILE* stream, LiquidHeader& header);
+};
+
+class H2ORenderMask
+{
+public:
+ H2ORenderMask() {}
+ uint8 Mask[8];
+
+ bool ShouldRender(int x, int y)
+ {
+ return (Mask[y] >> x & 1) != 0;
+ }
+
+ static H2ORenderMask Read(FILE* stream);
+};
+
+class MCNKLiquidData
+{
+public:
+ MCNKLiquidData() {}
+ MCNKLiquidData(float** heights, H2ORenderMask mask) : Heights(heights), Mask(mask) {}
+
+ float** Heights;
+ H2ORenderMask Mask;
+
+ bool IsWater(int x, int y, float height);
+};
+
+class H2OHeader
+{
+public:
+ H2OHeader() {}
+ uint32 OffsetInformation;
+ uint32 LayerCount;
+ uint32 OffsetRender;
+
+ static H2OHeader Read(FILE* stream);
+};
+
+class H2OInformation
+{
+public:
+ H2OInformation() {}
+ uint16 LiquidType;
+ uint16 Flags;
+ float HeightLevel1;
+ float HeightLevel2;
+ uint8 OffsetX;
+ uint8 OffsetY;
+ uint8 Width;
+ uint8 Height;
+ uint32 OffsetMask2;
+ uint32 OffsetHeightmap;
+
+ static H2OInformation Read(FILE* stream);
+};
+
+class WMOGroupHeader
+{
+public:
+ WMOGroupHeader() {}
+
+ uint32 OffsetGroupName;
+ uint32 OffsetDescriptiveName;
+ uint32 Flags;
+ Vector3 BoundingBox[2];
+ uint32 OffsetPortals;
+ uint32 CountPortals;
+ uint16 CountBatches[4];
+ uint8 Fogs[4];
+ uint32 LiquidTypeRelated;
+ uint32 WmoId;
+
+ static WMOGroupHeader Read(FILE* stream);
+};
+
+// Dummy class to act as an interface.
+class IDefinition
+{
+public:
+ Vector3 Position;
+ Vector3 Rotation;
+ virtual float Scale() const { return 1.0f; };
+};
+
+#define MMAP_MAGIC 0x4d4d4150 // 'MMAP'
+#define MMAP_VERSION 3
+
+struct MmapTileHeader
+{
+ uint32 mmapMagic;
+ uint32 dtVersion;
+ uint32 mmapVersion;
+ uint32 size;
+ bool usesLiquids;
+
+ MmapTileHeader() : mmapMagic(MMAP_MAGIC), dtVersion(DT_NAVMESH_VERSION),
+ mmapVersion(MMAP_VERSION), size(0), usesLiquids(true) {}
+};
+
+class Utils
+{
+public:
+ static void Reverse(char word[]);
+ static std::string ReadString(FILE* file);
+ static uint32 Size(FILE* file);
+ static Vector3 ToRecast( Vector3 val );
+ static std::string GetAdtPath(std::string world, int x, int y);
+ static std::string FixModelPath(std::string path);
+ static G3D::Matrix4 GetTransformation(IDefinition def);
+ /// They say its better to declare template functions in the header files.
+ template <typename T>
+ static std::string ToString(T val)
+ {
+ std::stringstream ss;
+ ss << val;
+ return ss.str();
+ }
+ static G3D::Matrix4 RotationX(float angle);
+ static G3D::Matrix4 RotationY(float angle);
+ static G3D::Matrix4 RotationZ(float angle);
+ static float ToRadians(float degrees);
+ static Vector3 VectorTransform(Vector3 vec, G3D::Matrix4 matrix);
+ static std::string GetPathBase(std::string path);
+ static Vector3 GetLiquidVert(G3D::Matrix4 transformation, Vector3 basePosition, float height, int x, int y);
+ static float Distance(float x, float y);
+ template<typename T>
+ static bool IsAllZero(T* arr, uint32 size)
+ {
+ for (uint32 i = 0; i < size; ++i)
+ if (arr[i])
+ return false;
+ return true;
+ }
+ static std::string Replace( std::string str, const std::string& oldStr, const std::string& newStr );
+ static G3D::Matrix4 GetWmoDoodadTransformation( DoodadInstance inst, WorldModelDefinition root );
+ static void CreateDir( const std::string& Path );
+ static void SaveToDisk(FILE* stream, std::string path);
+ static Vector3 ToWoWCoords( Vector3 vec );
+ static std::string GetExtension( std::string path );
+ static char* GetPlainName(const char* FileName);
+};
+#endif
\ No newline at end of file
diff --git a/src/tools/mesh_extractor/WDT.cpp b/src/tools/mesh_extractor/WDT.cpp
new file mode 100644
index 0000000..f0c3091
--- /dev/null
+++ b/src/tools/mesh_extractor/WDT.cpp
@@ -0,0 +1,55 @@
+#include "WDT.h"
+#include "Chunk.h"
+#include "ChunkedData.h"
+#include "Utils.h"
+#include "WorldModelHandler.h"
+
+WDT::WDT(std::string file) : IsValid(false), IsGlobalModel(false)
+{
+ Data = new ChunkedData(file, 2);
+ ReadTileTable();
+ ReadGlobalModel();
+}
+
+void WDT::ReadGlobalModel()
+{
+ Chunk* fileChunk = Data->GetChunkByName("MWMO");
+ Chunk* defChunk = Data->GetChunkByName("MODF");
+ if (!fileChunk || !defChunk)
+ return;
+
+ IsGlobalModel = true;
+ ModelDefinition = WorldModelDefinition::Read(defChunk->GetStream());
+ ModelFile = Utils::ReadString(fileChunk->GetStream());
+}
+
+void WDT::ReadTileTable()
+{
+ Chunk* chunk = Data->GetChunkByName("MAIN");
+ if (!chunk)
+ return;
+ IsValid = true;
+ FILE* stream = chunk->GetStream();
+ for (int y = 0; y < 64; ++y)
+ {
+ for (int x = 0; x < 64; ++x)
+ {
+ const uint32 hasTileFlag = 0x1;
+ uint32 flags;
+ uint32 discard;
+ fread(&flags, sizeof(uint32), 1, stream);
+ fread(&discard, sizeof(uint32), 1, stream);
+ if (flags & hasTileFlag)
+ TileTable.push_back(TilePos(x, y));
+
+ }
+ }
+}
+
+bool WDT::HasTile( int x, int y )
+{
+ for (std::vector<TilePos>::iterator itr = TileTable.begin(); itr != TileTable.end(); ++itr)
+ if (itr->X == x && itr->Y == y)
+ return true;
+ return false;
+}
diff --git a/src/tools/mesh_extractor/WDT.h b/src/tools/mesh_extractor/WDT.h
new file mode 100644
index 0000000..a12aa65
--- /dev/null
+++ b/src/tools/mesh_extractor/WDT.h
@@ -0,0 +1,27 @@
+#ifndef WDT_H
+#define WDT_H
+#include <string>
+#include <vector>
+
+#include "ChunkedData.h"
+#include "WorldModelHandler.h"
+#include "Utils.h"
+
+class WDT
+{
+public:
+ WDT(std::string file);
+
+ ChunkedData* Data;
+ std::vector<TilePos> TileTable;
+ bool IsGlobalModel;
+ bool IsValid;
+ std::string ModelFile;
+ WorldModelDefinition ModelDefinition;
+ bool HasTile(int x, int y);
+private:
+ void ReadGlobalModel();
+ void ReadTileTable();
+};
+
+#endif
\ No newline at end of file
diff --git a/src/tools/mesh_extractor/WorldModelGroup.cpp b/src/tools/mesh_extractor/WorldModelGroup.cpp
new file mode 100644
index 0000000..35a5ba6
--- /dev/null
+++ b/src/tools/mesh_extractor/WorldModelGroup.cpp
@@ -0,0 +1,135 @@
+#include "WorldModelGroup.h"
+#include "ChunkedData.h"
+#include "Chunk.h"
+#include "Utils.h"
+
+WorldModelGroup::WorldModelGroup( std::string path, int groupIndex ) : GroupIndex(groupIndex), IsBad(false), MOBA(NULL)
+{
+ Data = new ChunkedData(path);
+ if (!Data->Stream)
+ {
+ IsBad = true;
+ return;
+ }
+ Chunk* mainChunk = Data->GetChunkByName("MOGP");
+ int32 firstSub = mainChunk->FindSubChunkOffset("MOPY");
+ if (firstSub == -1)
+ return;
+
+ Name = Utils::GetPlainName(path.c_str());
+
+ FILE* stream = mainChunk->GetStream();
+ fseek(stream, firstSub, SEEK_SET);
+ SubData = new ChunkedData(stream, mainChunk->Length - firstSub);
+
+ ReadHeader();
+ ReadMaterials();
+ ReadTriangles();
+ ReadVertices();
+ ReadNormals();
+ ReadLiquid();
+ ReadBatches();
+}
+
+void WorldModelGroup::ReadNormals()
+{
+ Chunk* chunk = SubData->GetChunkByName("MONR");
+ if (!chunk)
+ return;
+
+ uint32 normalCount = chunk->Length / 12;
+ ASSERT(normalCount == Vertices.size() && "normalCount is different than the Vertices count");
+ Normals.reserve(normalCount);
+ FILE* stream = chunk->GetStream();
+ for (uint32 i = 0; i < normalCount; i++)
+ Normals.push_back(Vector3::Read(stream));
+}
+
+void WorldModelGroup::ReadLiquid()
+{
+ Chunk* chunk = SubData->GetChunkByName("MLIQ");
+ if (!chunk)
+ return;
+
+ HasLiquidData = true;
+ FILE* stream = chunk->GetStream();
+ LiquidDataHeader = LiquidHeader::Read(stream);
+ LiquidDataGeometry = LiquidData::Read(stream, LiquidDataHeader);
+}
+
+void WorldModelGroup::ReadVertices()
+{
+ Chunk* chunk = SubData->GetChunkByName("MOVT");
+ if (!chunk)
+ return;
+
+ uint32 verticeCount = chunk->Length / 12;
+ Vertices.reserve(verticeCount);
+ FILE* stream = chunk->GetStream();
+ for (uint32 i = 0; i < verticeCount; i++)
+ Vertices.push_back(Vector3::Read(stream));
+}
+
+void WorldModelGroup::ReadTriangles()
+{
+ Chunk* chunk = SubData->GetChunkByName("MOVI");
+ if (!chunk)
+ return;
+
+ uint32 triangleCount = chunk->Length / 6;
+ ASSERT(triangleCount == TriangleFlags.size() && "triangleCount != TriangleFlags.size()");
+ FILE* stream = chunk->GetStream();
+ Triangles.reserve(triangleCount);
+ for (uint32 i = 0; i < triangleCount; i++)
+ {
+ uint16 v0;
+ uint16 v1;
+ uint16 v2;
+ fread(&v0, sizeof(uint16), 1, stream);
+ fread(&v1, sizeof(uint16), 1, stream);
+ fread(&v2, sizeof(uint16), 1, stream);
+ Triangles.push_back(Triangle<uint16>(Constants::TRIANGLE_TYPE_WMO, v0, v1, v2));
+ }
+}
+
+void WorldModelGroup::ReadMaterials()
+{
+ Chunk* chunk = SubData->GetChunkByName("MOPY");
+ if (!chunk)
+ return;
+
+ FILE* stream = chunk->GetStream();
+ uint32 triangleCount = chunk->Length / 2;
+ TriangleFlags.reserve(triangleCount);
+ TriangleMaterials.reserve(triangleCount);
+ for (uint32 i = 0; i < triangleCount; i++)
+ {
+ uint8 tmp;
+ fread(&tmp, sizeof(uint8), 1, stream);
+ TriangleFlags.push_back(tmp);
+ // Read again for material.
+ fread(&tmp, sizeof(uint8), 1, stream);
+ TriangleMaterials.push_back(tmp);
+ }
+}
+
+void WorldModelGroup::ReadHeader()
+{
+ Chunk* chunk = Data->GetChunkByName("MOGP");
+ if (!chunk)
+ return;
+
+ FILE* stream = chunk->GetStream();
+ Header = WMOGroupHeader::Read(stream);
+}
+
+void WorldModelGroup::ReadBatches()
+{
+ Chunk* chunk = Data->GetChunkByName("MOBA");
+ if (!chunk)
+ return;
+
+ MOBALength = chunk->Length / 2;
+ MOBA = new uint16[MOBALength];
+ fread(MOBA, sizeof(uint16), MOBALength, chunk->GetStream());
+}
diff --git a/src/tools/mesh_extractor/WorldModelGroup.h b/src/tools/mesh_extractor/WorldModelGroup.h
new file mode 100644
index 0000000..e4fe34c
--- /dev/null
+++ b/src/tools/mesh_extractor/WorldModelGroup.h
@@ -0,0 +1,38 @@
+#ifndef WMOGROUP_H
+#define WMOGROUP_H
+#include "ChunkedData.h"
+#include "Utils.h"
+
+class WorldModelGroup
+{
+public:
+ WorldModelGroup(std::string path, int groupIndex);
+ ChunkedData* Data;
+ ChunkedData* SubData;
+ int GroupIndex;
+ std::string Name;
+ WMOGroupHeader Header;
+
+ std::vector<uint8> TriangleFlags;
+ std::vector<uint8> TriangleMaterials;
+ std::vector<Triangle<uint16> > Triangles;
+ std::vector<Vector3> Vertices;
+ std::vector<Vector3> Normals;
+ // @ToDo: Research.
+ uint16* MOBA;
+ uint32 MOBALength;
+
+ bool HasLiquidData;
+ bool IsBad;
+ LiquidHeader LiquidDataHeader;
+ LiquidData LiquidDataGeometry;
+private:
+ void ReadNormals();
+ void ReadLiquid();
+ void ReadVertices();
+ void ReadTriangles();
+ void ReadMaterials();
+ void ReadHeader();
+ void ReadBatches();
+};
+#endif
\ No newline at end of file
diff --git a/src/tools/mesh_extractor/WorldModelHandler.cpp b/src/tools/mesh_extractor/WorldModelHandler.cpp
new file mode 100644
index 0000000..7b60b9b
--- /dev/null
+++ b/src/tools/mesh_extractor/WorldModelHandler.cpp
@@ -0,0 +1,198 @@
+#include "WorldModelHandler.h"
+#include "WorldModelRoot.h"
+#include "Chunk.h"
+#include "Cache.h"
+#include "Model.h"
+#include "Common.h"
+#include "G3D/Matrix4.h"
+#include <cstdio>
+
+WorldModelDefinition WorldModelDefinition::Read( FILE* file )
+{
+ WorldModelDefinition ret;
+ fread(&ret.MwidIndex, sizeof(uint32), 1, file);
+ fread(&ret.UniqueId, sizeof(uint32), 1, file);
+ ret.Position = Vector3::Read(file);
+ ret.Rotation = Vector3::Read(file);
+ ret.UpperExtents = Vector3::Read(file);
+ ret.LowerExtents = Vector3::Read(file);
+ fread(&ret.Flags, sizeof(uint16), 1, file);
+ fread(&ret.DoodadSet, sizeof(uint16), 1, file);
+ uint32 discard;
+ fread(&discard, sizeof(uint32), 1, file);
+ return ret;
+}
+
+
+WorldModelHandler::WorldModelHandler( ADT* adt ) : ObjectDataHandler(adt), _definitions(NULL), _paths(NULL)
+{
+ if (!adt->HasObjectData)
+ return;
+ ReadModelPaths();
+ ReadDefinitions();
+}
+
+void WorldModelHandler::ProcessInternal( ChunkedData* subChunks )
+{
+ if (!IsSane())
+ return;
+ Chunk* wmoReferencesChunk = subChunks->GetChunkByName("MCRW");
+ if (!wmoReferencesChunk)
+ return;
+ FILE* stream = wmoReferencesChunk->GetStream();
+ uint32 refCount = wmoReferencesChunk->Length / 4;
+ for (uint32 i = 0; i < refCount; i++)
+ {
+ int32 index;
+ fread(&index, sizeof(int32), 1, stream);
+
+ if (index < 0 || uint32(index) >= _definitions->size())
+ continue;
+
+ WorldModelDefinition wmo = (*_definitions)[index];
+
+ if (_drawn.find(wmo.UniqueId) != _drawn.end())
+ continue;
+ _drawn.insert(wmo.UniqueId);
+
+ if (wmo.MwidIndex >= _paths->size())
+ continue;
+
+ std::string path = (*_paths)[wmo.MwidIndex];
+ WorldModelRoot* model = Cache->WorldModelCache.Get(path);
+ if (!model)
+ {
+ model = new WorldModelRoot(path);
+ Cache->WorldModelCache.Insert(path, model);
+ }
+
+ Vertices.reserve(1000);
+ Triangles.reserve(1000);
+
+ InsertModelGeometry(Vertices, Triangles, wmo, model);
+ }
+}
+
+void WorldModelHandler::InsertModelGeometry( std::vector<Vector3>& verts, std::vector<Triangle<uint32> >& tris, WorldModelDefinition& def, WorldModelRoot* root )
+{
+ G3D::Matrix4 transformation = Utils::GetTransformation(def);
+ for (std::vector<WorldModelGroup>::iterator group = root->Groups.begin(); group != root->Groups.end(); ++group)
+ {
+ uint32 vertOffset = verts.size();
+ for (std::vector<Vector3>::iterator itr2 = group->Vertices.begin(); itr2 != group->Vertices.end(); ++itr2)
+ verts.push_back(Utils::VectorTransform(*itr2, transformation));
+
+ for (uint32 i = 0; i < group->Triangles.size(); ++i)
+ {
+ // only include collidable tris
+ if ((group->TriangleFlags[i] & 0x04) != 0 && group->TriangleMaterials[i] != 0xFF)
+ continue;
+ Triangle<uint16> tri = group->Triangles[i];
+ tris.push_back(Triangle<uint32>(Constants::TRIANGLE_TYPE_WMO, tri.V0 + vertOffset, tri.V1 + vertOffset, tri.V2 + vertOffset));
+ }
+ }
+
+ if (def.DoodadSet < root->DoodadSets.size())
+ {
+ DoodadSet set = root->DoodadSets[def.DoodadSet];
+ std::vector<DoodadInstance> instances;
+ instances.reserve(set.CountInstances);
+ for (uint32 i = set.FirstInstanceIndex; i < (set.CountInstances + set.FirstInstanceIndex); i++)
+ {
+ if (i >= root->DoodadInstances.size())
+ break;
+ instances.push_back(root->DoodadInstances[i]);
+ }
+
+ for (std::vector<DoodadInstance>::iterator instance = instances.begin(); instance != instances.end(); ++instance)
+ {
+ Model* model = Cache->ModelCache.Get(instance->File);
+ if (!model)
+ {
+ model = new Model(instance->File);
+ Cache->ModelCache.Insert(instance->File, model);
+ }
+
+ if (!model->IsCollidable)
+ continue;
+ G3D::Matrix4 doodadTransformation = Utils::GetWmoDoodadTransformation(*instance, def);
+ int vertOffset = verts.size();
+ for (std::vector<Vector3>::iterator itr2 = model->Vertices.begin(); itr2 != model->Vertices.end(); ++itr2)
+ verts.push_back(Utils::VectorTransform(*itr2, doodadTransformation));
+ for (std::vector<Triangle<uint16> >::iterator itr2 = model->Triangles.begin(); itr2 != model->Triangles.end(); ++itr2)
+ tris.push_back(Triangle<uint32>(Constants::TRIANGLE_TYPE_WMO, itr2->V0 + vertOffset, itr2->V1 + vertOffset, itr2->V2 + vertOffset));
+ }
+
+ for (std::vector<WorldModelGroup>::iterator group = root->Groups.begin(); group != root->Groups.end(); ++group)
+ {
+ if (!group->HasLiquidData)
+ continue;
+
+ for (uint32 y = 0; y < group->LiquidDataHeader.Height; y++)
+ {
+ for (uint32 x = 0; x < group->LiquidDataHeader.Width; x++)
+ {
+ if (!group->LiquidDataGeometry.ShouldRender(x, y))
+ continue;
+
+ uint32 vertOffset = verts.size();
+ verts.push_back(Utils::GetLiquidVert(transformation, group->LiquidDataHeader.BaseLocation,
+ group->LiquidDataGeometry.HeightMap[x][y], x, y));
+ verts.push_back(Utils::GetLiquidVert(transformation, group->LiquidDataHeader.BaseLocation,
+ group->LiquidDataGeometry.HeightMap[x + 1][y], x + 1, y));
+ verts.push_back(Utils::GetLiquidVert(transformation, group->LiquidDataHeader.BaseLocation,
+ group->LiquidDataGeometry.HeightMap[x][y + 1], x, y + 1));
+ verts.push_back(Utils::GetLiquidVert(transformation, group->LiquidDataHeader.BaseLocation,
+ group->LiquidDataGeometry.HeightMap[x + 1][y + 1], x + 1, y + 1));
+
+ tris.push_back(Triangle<uint32>(Constants::TRIANGLE_TYPE_WATER, vertOffset, vertOffset + 2, vertOffset + 1));
+ tris.push_back(Triangle<uint32>(Constants::TRIANGLE_TYPE_WATER, vertOffset + 2, vertOffset + 3, vertOffset + 1));
+
+ }
+ }
+ }
+ }
+}
+
+void WorldModelHandler::ReadDefinitions()
+{
+ Chunk* chunk = Source->ObjectData->GetChunkByName("MODF");
+ if (!chunk)
+ return;
+
+ const int32 definitionSize = 64;
+ uint32 definitionCount = chunk->Length / definitionSize;
+ _definitions = new std::vector<WorldModelDefinition>;
+ _definitions->reserve(definitionCount);
+ FILE* stream = chunk->GetStream();
+ for (uint32 i = 0; i < definitionCount; i++)
+ _definitions->push_back(WorldModelDefinition::Read(stream));
+}
+
+void WorldModelHandler::ReadModelPaths()
+{
+ Chunk* mwid = Source->ObjectData->GetChunkByName("MWID");
+ Chunk* mwmo = Source->ObjectData->GetChunkByName("MWMO");
+ if (!mwid || !mwmo)
+ return;
+
+ uint32 paths = mwid->Length / 4;
+ _paths = new std::vector<std::string>;
+ _paths->reserve(paths);
+ for (uint32 i = 0; i < paths; i++)
+ {
+ FILE* stream = mwid->GetStream();
+ fseek(stream, i * 4, SEEK_CUR);
+ uint32 offset;
+ fread(&offset, sizeof(uint32), 1, stream);
+ FILE* dataStream = mwmo->GetStream();
+ fseek(dataStream, offset + mwmo->Offset, SEEK_SET);
+ _paths->push_back(Utils::ReadString(dataStream));
+ }
+}
+
+WorldModelHandler::~WorldModelHandler()
+{
+ delete _definitions;
+ delete _paths;
+}
diff --git a/src/tools/mesh_extractor/WorldModelHandler.h b/src/tools/mesh_extractor/WorldModelHandler.h
new file mode 100644
index 0000000..cccedfa
--- /dev/null
+++ b/src/tools/mesh_extractor/WorldModelHandler.h
@@ -0,0 +1,47 @@
+#ifndef WMODEL_HNDL_H
+#define WMODEL_HNDL_H
+#include "Common.h"
+#include "Utils.h"
+#include "WorldModelRoot.h"
+#include "ObjectDataHandler.h"
+
+#include <set>
+#include <vector>
+
+class ADT;
+
+struct WorldModelDefinition : public IDefinition
+{
+public:
+ WorldModelDefinition() {}
+
+ uint32 MwidIndex;
+ uint32 UniqueId;
+ Vector3 UpperExtents;
+ Vector3 LowerExtents;
+ uint16 Flags;
+ uint16 DoodadSet;
+
+ static WorldModelDefinition Read(FILE* file);
+};
+
+class WorldModelHandler : public ObjectDataHandler
+{
+public:
+ WorldModelHandler(ADT* adt);
+ ~WorldModelHandler();
+
+ std::vector<Vector3> Vertices;
+ std::vector<Triangle<uint32> > Triangles;
+ bool IsSane() { return _definitions && _paths; }
+ void InsertModelGeometry(std::vector<Vector3>& verts, std::vector<Triangle<uint32> >& tris, WorldModelDefinition& def, WorldModelRoot* root);
+protected:
+ void ProcessInternal(ChunkedData* data);
+private:
+ void ReadDefinitions();
+ void ReadModelPaths();
+ std::set<uint32> _drawn;
+ std::vector<WorldModelDefinition>* _definitions;
+ std::vector<std::string>* _paths;
+};
+#endif
\ No newline at end of file
diff --git a/src/tools/mesh_extractor/WorldModelRoot.cpp b/src/tools/mesh_extractor/WorldModelRoot.cpp
new file mode 100644
index 0000000..c34a77e
--- /dev/null
+++ b/src/tools/mesh_extractor/WorldModelRoot.cpp
@@ -0,0 +1,74 @@
+#include "WorldModelRoot.h"
+#include "ChunkedData.h"
+#include "Utils.h"
+
+WorldModelRoot::WorldModelRoot( std::string path )
+{
+ Data = new ChunkedData(path);
+ Path = path;
+ ReadHeader();
+ ReadGroups();
+ ReadDoodadInstances();
+ ReadDoodadSets();
+}
+
+void WorldModelRoot::ReadGroups()
+{
+ std::string pathBase = Utils::GetPathBase(Path);
+ Groups.reserve(Header.CountGroups);
+ for (uint32 i = 0; i < Header.CountGroups; i++)
+ {
+ char name[200];
+ sprintf(name, "%s_%03u.wmo", pathBase.c_str(), i);
+ WorldModelGroup group(name, i);
+ if (!group.IsBad)
+ Groups.push_back(group);
+ }
+}
+
+void WorldModelRoot::ReadDoodadSets()
+{
+ Chunk* chunk = Data->GetChunkByName("MODS");
+ if (!chunk)
+ return;
+
+ FILE* stream = chunk->GetStream();
+ ASSERT(chunk->Length / 32 == Header.CountSets && "chunk.Length / 32 == Header.CountSets");
+ DoodadSets.reserve(Header.CountSets);
+ for (uint32 i = 0; i < Header.CountSets; i++)
+ DoodadSets.push_back(DoodadSet::Read(stream));
+}
+
+void WorldModelRoot::ReadDoodadInstances()
+{
+ Chunk* chunk = Data->GetChunkByName("MODD");
+ Chunk* nameChunk = Data->GetChunkByName("MODN");
+ if (!chunk || !nameChunk)
+ return;
+
+ const uint32 instanceSize = 40;
+ uint32 countInstances = chunk->Length / instanceSize;
+ DoodadInstances.reserve(countInstances);
+ for (uint32 i = 0; i < countInstances; i++)
+ {
+ FILE* stream = chunk->GetStream();
+ fseek(stream, instanceSize * i, SEEK_CUR);
+ DoodadInstance instance = DoodadInstance::Read(stream);
+ FILE* nameStream = nameChunk->GetStream();
+ if (instance.FileOffset >= nameChunk->Length)
+ continue;
+ fseek(nameStream, instance.FileOffset, SEEK_CUR);
+ instance.File = Utils::ReadString(nameStream);
+ DoodadInstances.push_back(instance);
+ }
+}
+
+void WorldModelRoot::ReadHeader()
+{
+ Chunk* chunk = Data->GetChunkByName("MOHD");
+ if (!chunk)
+ return;
+
+ FILE* stream = chunk->GetStream();
+ Header = WorldModelHeader::Read(stream);
+}
diff --git a/src/tools/mesh_extractor/WorldModelRoot.h b/src/tools/mesh_extractor/WorldModelRoot.h
new file mode 100644
index 0000000..c06ff3d
--- /dev/null
+++ b/src/tools/mesh_extractor/WorldModelRoot.h
@@ -0,0 +1,26 @@
+#ifndef WMOROOT_H
+#define WMOROOT_H
+#include <string>
+#include <vector>
+
+#include "ChunkedData.h"
+#include "Utils.h"
+#include "WorldModelGroup.h"
+
+class WorldModelRoot
+{
+public:
+ WorldModelRoot(std::string path);
+ std::string Path;
+ ChunkedData* Data;
+ WorldModelHeader Header;
+ std::vector<DoodadInstance> DoodadInstances;
+ std::vector<DoodadSet> DoodadSets;
+ std::vector<WorldModelGroup> Groups;
+private:
+ void ReadGroups();
+ void ReadDoodadSets();
+ void ReadDoodadInstances();
+ void ReadHeader();
+};
+#endif
\ No newline at end of file
diff --git a/src/tools/mesh_extractor/readme b/src/tools/mesh_extractor/readme
new file mode 100644
index 0000000..85cd7cf
--- /dev/null
+++ b/src/tools/mesh_extractor/readme
@@ -0,0 +1,6 @@
+Experimental mesh extractor.
+Original work in C# by stschake
+Thanks to:
+Subv
+~
+For helping in the porting to C++
\ No newline at end of file
diff --git a/src/tools/mmaps_generator/CMakeLists.txt b/src/tools/mmaps_generator/CMakeLists.txt
new file mode 100644
index 0000000..46bf9d0
--- /dev/null
+++ b/src/tools/mmaps_generator/CMakeLists.txt
@@ -0,0 +1,68 @@
+# Copyright (C) 2008-2011 Trinity <http://www.trinitycore.org/>
+#
+# This file is free software; as a special exception the author gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+file(GLOB_RECURSE sources *.cpp *.h)
+
+# definitions
+add_definitions(-DNO_CORE_FUNCS)
+add_definitions(-DDEBUG)
+add_definitions(-DNO_vsnprintf)
+
+include_directories(
+ ${CMAKE_BINARY_DIR}
+ ${ACE_INCLUDE_DIR}
+ ${MYSQL_INCLUDE_DIR}
+ ${CMAKE_SOURCE_DIR}/dep/libmpq
+ ${CMAKE_SOURCE_DIR}/dep/zlib
+ ${CMAKE_SOURCE_DIR}/dep/bzip2
+ ${CMAKE_SOURCE_DIR}/dep/acelite
+ ${CMAKE_SOURCE_DIR}/dep/g3dlite/include
+ ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Recast
+ ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Detour
+ ${CMAKE_SOURCE_DIR}/src/server/shared
+ ${CMAKE_SOURCE_DIR}/src/server/shared/Database
+ ${CMAKE_SOURCE_DIR}/src/server/shared/Database/Implementation
+ ${CMAKE_SOURCE_DIR}/src/server/shared/Threading
+ ${CMAKE_SOURCE_DIR}/src/server/shared/Logging
+ ${CMAKE_SOURCE_DIR}/src/server/shared/Utilities
+ ${CMAKE_SOURCE_DIR}/src/server/shared/Dynamic
+ ${CMAKE_SOURCE_DIR}/src/server/shared/Dynamic/LinkedReference
+ ${CMAKE_SOURCE_DIR}/src/server/game/Maps
+ ${CMAKE_SOURCE_DIR}/src/server/game/DataStores
+ ${CMAKE_SOURCE_DIR}/src/server/game/Movement/Waypoints
+ ${CMAKE_SOURCE_DIR}/src/server/game/Grids
+ ${CMAKE_SOURCE_DIR}/src/server/game/Grids/Cells
+ ${CMAKE_SOURCE_DIR}/src/server/game/Miscellaneous
+ ${CMAKE_SOURCE_DIR}/src/server/game/Conditions
+ ${CMAKE_SOURCE_DIR}/src/server/collision
+ ${CMAKE_SOURCE_DIR}/src/server/collision/Management
+ ${CMAKE_SOURCE_DIR}/src/server/collision/Maps
+ ${CMAKE_SOURCE_DIR}/src/server/collision/Models
+)
+
+add_executable(mmaps_generator ${sources})
+
+target_link_libraries(mmaps_generator
+ ${MYSQL_LIBRARY}
+ ${ACE_LIBRARY}
+ ${BZIP2_LIBRARIES}
+ ${ZLIB_LIBRARIES}
+ Recast
+ Detour
+ collision
+ g3dlib
+ shared
+)
+
+if( UNIX )
+ install(TARGETS mmaps_generator DESTINATION bin)
+elseif( WIN32 )
+ install(TARGETS mmaps_generator DESTINATION "${CMAKE_INSTALL_PREFIX}")
+endif()
diff --git a/src/tools/mmaps_generator/Info/readme.txt b/src/tools/mmaps_generator/Info/readme.txt
new file mode 100644
index 0000000..8d7c4f9
--- /dev/null
+++ b/src/tools/mmaps_generator/Info/readme.txt
@@ -0,0 +1,66 @@
+Generator command line args
+
+--offMeshInput [file.*] Path to file containing off mesh connections data.
+ Format must be: (see offmesh_example.txt)
+ "map_id tile_x,tile_y (start_x start_y start_z) (end_x end_y end_z) size //optional comments"
+ Single mesh connection per line.
+
+--silent Make us script friendly. Do not wait for user input
+ on error or completion.
+
+--bigBaseUnit [true|false] Generate tile/map using bigger basic unit.
+ Use this option only if you have unexpected gaps.
+
+ false: use normal metrics (default)
+
+--maxAngle [#] Max walkable inclination angle
+
+ float between 45 and 90 degrees (default 60)
+
+--skipLiquid liquid data for maps
+
+ false: include liquid data (default)
+
+--skipContinents [true|false] continents are maps 0 (Eastern Kingdoms),
+ 1 (Kalimdor), 530 (Outlands), 571 (Northrend)
+
+ false: build continents (default)
+
+--skipJunkMaps [true|false] junk maps include some unused
+ maps, transport maps, and some other
+
+ true: skip junk maps (default)
+
+--skipBattlegrounds [true|false] does not include PVP arenas
+
+ false: skip battlegrounds (default)
+
+--debugOutput [true|false] create debugging files for use with RecastDemo
+ if you are only creating mmaps for use with MaNGOS,
+ you don't want debugging files
+
+ false: don't create debugging files (default)
+
+--tile [#,#] Build the specified tile
+ seperate number with a comma ','
+ must specify a map number (see below)
+ if this option is not used, all tiles are built
+
+ [#] Build only the map specified by #
+ this command will build the map regardless of --skip* option settings
+ if you do not specify a map number, builds all maps that pass the filters specified by --skip* options
+
+
+examples:
+
+movement_extractor
+builds maps using the default settings (see above for defaults)
+
+movement_extractor --skipContinents true
+builds the default maps, except continents
+
+movement_extractor 0
+builds all tiles of map 0
+
+movement_extractor 0 --tile 34,46
+builds only tile 34,46 of map 0 (this is the southern face of blackrock mountain)
\ No newline at end of file
diff --git a/src/tools/mmaps_generator/IntermediateValues.cpp b/src/tools/mmaps_generator/IntermediateValues.cpp
new file mode 100644
index 0000000..b473d64
--- /dev/null
+++ b/src/tools/mmaps_generator/IntermediateValues.cpp
@@ -0,0 +1,277 @@
+/*
+ * Copyright (C) 2008-2011 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "IntermediateValues.h"
+
+namespace MMAP
+{
+ IntermediateValues::~IntermediateValues()
+ {
+ rcFreeCompactHeightfield(compactHeightfield);
+ rcFreeHeightField(heightfield);
+ rcFreeContourSet(contours);
+ rcFreePolyMesh(polyMesh);
+ rcFreePolyMeshDetail(polyMeshDetail);
+ }
+
+ void IntermediateValues::writeIV(uint32 mapID, uint32 tileX, uint32 tileY)
+ {
+ char fileName[255];
+ char tileString[25];
+ sprintf(tileString, "[%02u,%02u]: ", tileX, tileY);
+
+ printf("%sWriting debug output... \r", tileString);
+
+ string name("meshes/%03u%02i%02i.");
+
+#define DEBUG_WRITE(fileExtension,data) \
+ do { \
+ sprintf(fileName, (name + fileExtension).c_str(), mapID, tileY, tileX); \
+ FILE* file = fopen(fileName, "wb"); \
+ if (!file) \
+ { \
+ char message[1024]; \
+ sprintf(message, "%sFailed to open %s for writing!\n", tileString, fileName); \
+ perror(message); \
+ } \
+ else \
+ debugWrite(file, data); \
+ if (file) fclose(file); \
+ printf("%sWriting debug output... \r", tileString); \
+ } while (false)
+
+ if (heightfield)
+ DEBUG_WRITE("hf", heightfield);
+ if (compactHeightfield)
+ DEBUG_WRITE("chf", compactHeightfield);
+ if (contours)
+ DEBUG_WRITE("cs", contours);
+ if (polyMesh)
+ DEBUG_WRITE("pmesh", polyMesh);
+ if (polyMeshDetail)
+ DEBUG_WRITE("dmesh", polyMeshDetail);
+
+#undef DEBUG_WRITE
+ }
+
+ void IntermediateValues::debugWrite(FILE* file, const rcHeightfield* mesh)
+ {
+ if (!file || !mesh)
+ return;
+
+ fwrite(&(mesh->cs), sizeof(float), 1, file);
+ fwrite(&(mesh->ch), sizeof(float), 1, file);
+ fwrite(&(mesh->width), sizeof(int), 1, file);
+ fwrite(&(mesh->height), sizeof(int), 1, file);
+ fwrite(mesh->bmin, sizeof(float), 3, file);
+ fwrite(mesh->bmax, sizeof(float), 3, file);
+
+ for (int y = 0; y < mesh->height; ++y)
+ for (int x = 0; x < mesh->width; ++x)
+ {
+ rcSpan* span = mesh->spans[x+y*mesh->width];
+
+ // first, count the number of spans
+ int spanCount = 0;
+ while (span)
+ {
+ spanCount++;
+ span = span->next;
+ }
+
+ // write the span count
+ fwrite(&spanCount, sizeof(int), 1, file);
+
+ // write the spans
+ span = mesh->spans[x+y*mesh->width];
+ while (span)
+ {
+ fwrite(span, sizeof(rcSpan), 1, file);
+ span = span->next;
+ }
+ }
+ }
+
+ void IntermediateValues::debugWrite(FILE* file, const rcCompactHeightfield* chf)
+ {
+ if (!file | !chf)
+ return;
+
+ fwrite(&(chf->width), sizeof(chf->width), 1, file);
+ fwrite(&(chf->height), sizeof(chf->height), 1, file);
+ fwrite(&(chf->spanCount), sizeof(chf->spanCount), 1, file);
+
+ fwrite(&(chf->walkableHeight), sizeof(chf->walkableHeight), 1, file);
+ fwrite(&(chf->walkableClimb), sizeof(chf->walkableClimb), 1, file);
+
+ fwrite(&(chf->maxDistance), sizeof(chf->maxDistance), 1, file);
+ fwrite(&(chf->maxRegions), sizeof(chf->maxRegions), 1, file);
+
+ fwrite(chf->bmin, sizeof(chf->bmin), 1, file);
+ fwrite(chf->bmax, sizeof(chf->bmax), 1, file);
+
+ fwrite(&(chf->cs), sizeof(chf->cs), 1, file);
+ fwrite(&(chf->ch), sizeof(chf->ch), 1, file);
+
+ int tmp = 0;
+ if (chf->cells) tmp |= 1;
+ if (chf->spans) tmp |= 2;
+ if (chf->dist) tmp |= 4;
+ if (chf->areas) tmp |= 8;
+
+ fwrite(&tmp, sizeof(tmp), 1, file);
+
+ if (chf->cells)
+ fwrite(chf->cells, sizeof(rcCompactCell), chf->width*chf->height, file);
+ if (chf->spans)
+ fwrite(chf->spans, sizeof(rcCompactSpan), chf->spanCount, file);
+ if (chf->dist)
+ fwrite(chf->dist, sizeof(unsigned short), chf->spanCount, file);
+ if (chf->areas)
+ fwrite(chf->areas, sizeof(unsigned char), chf->spanCount, file);
+ }
+
+ void IntermediateValues::debugWrite(FILE* file, const rcContourSet* cs)
+ {
+ if (!file || !cs)
+ return;
+
+ fwrite(&(cs->cs), sizeof(float), 1, file);
+ fwrite(&(cs->ch), sizeof(float), 1, file);
+ fwrite(cs->bmin, sizeof(float), 3, file);
+ fwrite(cs->bmax, sizeof(float), 3, file);
+ fwrite(&(cs->nconts), sizeof(int), 1, file);
+ for (int i = 0; i < cs->nconts; ++i)
+ {
+ fwrite(&cs->conts[i].area, sizeof(unsigned char), 1, file);
+ fwrite(&cs->conts[i].reg, sizeof(unsigned short), 1, file);
+ fwrite(&cs->conts[i].nverts, sizeof(int), 1, file);
+ fwrite(cs->conts[i].verts, sizeof(int), cs->conts[i].nverts*4, file);
+ fwrite(&cs->conts[i].nrverts, sizeof(int), 1, file);
+ fwrite(cs->conts[i].rverts, sizeof(int), cs->conts[i].nrverts*4, file);
+ }
+ }
+
+ void IntermediateValues::debugWrite(FILE* file, const rcPolyMesh* mesh)
+ {
+ if (!file || !mesh)
+ return;
+
+ fwrite(&(mesh->cs), sizeof(float), 1, file);
+ fwrite(&(mesh->ch), sizeof(float), 1, file);
+ fwrite(&(mesh->nvp), sizeof(int), 1, file);
+ fwrite(mesh->bmin, sizeof(float), 3, file);
+ fwrite(mesh->bmax, sizeof(float), 3, file);
+ fwrite(&(mesh->nverts), sizeof(int), 1, file);
+ fwrite(mesh->verts, sizeof(unsigned short), mesh->nverts*3, file);
+ fwrite(&(mesh->npolys), sizeof(int), 1, file);
+ fwrite(mesh->polys, sizeof(unsigned short), mesh->npolys*mesh->nvp*2, file);
+ fwrite(mesh->flags, sizeof(unsigned short), mesh->npolys, file);
+ fwrite(mesh->areas, sizeof(unsigned char), mesh->npolys, file);
+ fwrite(mesh->regs, sizeof(unsigned short), mesh->npolys, file);
+ }
+
+ void IntermediateValues::debugWrite(FILE* file, const rcPolyMeshDetail* mesh)
+ {
+ if (!file || !mesh)
+ return;
+
+ fwrite(&(mesh->nverts), sizeof(int), 1, file);
+ fwrite(mesh->verts, sizeof(float), mesh->nverts*3, file);
+ fwrite(&(mesh->ntris), sizeof(int), 1, file);
+ fwrite(mesh->tris, sizeof(char), mesh->ntris*4, file);
+ fwrite(&(mesh->nmeshes), sizeof(int), 1, file);
+ fwrite(mesh->meshes, sizeof(int), mesh->nmeshes*4, file);
+ }
+
+ void IntermediateValues::generateObjFile(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData)
+ {
+ char objFileName[255];
+ sprintf(objFileName, "meshes/map%03u%02u%02u.obj", mapID, tileY, tileX);
+
+ FILE* objFile = fopen(objFileName, "wb");
+ if (!objFile)
+ {
+ char message[1024];
+ sprintf(message, "Failed to open %s for writing!\n", objFileName);
+ perror(message);
+ return;
+ }
+
+ G3D::Array<float> allVerts;
+ G3D::Array<int> allTris;
+
+ allTris.append(meshData.liquidTris);
+ allVerts.append(meshData.liquidVerts);
+ TerrainBuilder::copyIndices(meshData.solidTris, allTris, allVerts.size() / 3);
+ allVerts.append(meshData.solidVerts);
+
+ float* verts = allVerts.getCArray();
+ int vertCount = allVerts.size() / 3;
+ int* tris = allTris.getCArray();
+ int triCount = allTris.size() / 3;
+
+ for (int i = 0; i < allVerts.size() / 3; i++)
+ fprintf(objFile, "v %f %f %f\n", verts[i*3], verts[i*3 + 1], verts[i*3 + 2]);
+
+ for (int i = 0; i < allTris.size() / 3; i++)
+ fprintf(objFile, "f %i %i %i\n", tris[i*3] + 1, tris[i*3 + 1] + 1, tris[i*3 + 2] + 1);
+
+ fclose(objFile);
+
+
+ char tileString[25];
+ sprintf(tileString, "[%02u,%02u]: ", tileY, tileX);
+ printf("%sWriting debug output... \r", tileString);
+
+ sprintf(objFileName, "meshes/%03u.map", mapID);
+
+ objFile = fopen(objFileName, "wb");
+ if (!objFile)
+ {
+ char message[1024];
+ sprintf(message, "Failed to open %s for writing!\n", objFileName);
+ perror(message);
+ return;
+ }
+
+ char b = '\0';
+ fwrite(&b, sizeof(char), 1, objFile);
+ fclose(objFile);
+
+ sprintf(objFileName, "meshes/%03u%02u%02u.mesh", mapID, tileY, tileX);
+ objFile = fopen(objFileName, "wb");
+ if (!objFile)
+ {
+ char message[1024];
+ sprintf(message, "Failed to open %s for writing!\n", objFileName);
+ perror(message);
+ return;
+ }
+
+ fwrite(&vertCount, sizeof(int), 1, objFile);
+ fwrite(verts, sizeof(float), vertCount*3, objFile);
+ fflush(objFile);
+
+ fwrite(&triCount, sizeof(int), 1, objFile);
+ fwrite(tris, sizeof(int), triCount*3, objFile);
+ fflush(objFile);
+
+ fclose(objFile);
+ }
+}
diff --git a/src/tools/mmaps_generator/IntermediateValues.h b/src/tools/mmaps_generator/IntermediateValues.h
new file mode 100644
index 0000000..a267a0f
--- /dev/null
+++ b/src/tools/mmaps_generator/IntermediateValues.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2008-2011 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _INTERMEDIATE_VALUES_H
+#define _INTERMEDIATE_VALUES_H
+
+#include "PathCommon.h"
+#include "TerrainBuilder.h"
+#include "Recast.h"
+#include "DetourNavMesh.h"
+
+namespace MMAP
+{
+ // this class gathers all debug info holding and output
+ struct IntermediateValues
+ {
+ rcHeightfield* heightfield;
+ rcCompactHeightfield* compactHeightfield;
+ rcContourSet* contours;
+ rcPolyMesh* polyMesh;
+ rcPolyMeshDetail* polyMeshDetail;
+
+ IntermediateValues() : compactHeightfield(NULL), heightfield(NULL),
+ contours(NULL), polyMesh(NULL), polyMeshDetail(NULL) {}
+ ~IntermediateValues();
+
+ void writeIV(uint32 mapID, uint32 tileX, uint32 tileY);
+
+ void debugWrite(FILE* file, const rcHeightfield* mesh);
+ void debugWrite(FILE* file, const rcCompactHeightfield* chf);
+ void debugWrite(FILE* file, const rcContourSet* cs);
+ void debugWrite(FILE* file, const rcPolyMesh* mesh);
+ void debugWrite(FILE* file, const rcPolyMeshDetail* mesh);
+
+ void generateObjFile(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData);
+ };
+}
+#endif
diff --git a/src/tools/mmaps_generator/MapBuilder.cpp b/src/tools/mmaps_generator/MapBuilder.cpp
new file mode 100644
index 0000000..754bced
--- /dev/null
+++ b/src/tools/mmaps_generator/MapBuilder.cpp
@@ -0,0 +1,970 @@
+/*
+ * Copyright (C) 2008-2011 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "PathCommon.h"
+#include "MapBuilder.h"
+
+#include "MapTree.h"
+#include "ModelInstance.h"
+#include "LoginDatabase.h"
+
+#include "DetourNavMeshBuilder.h"
+#include "DetourNavMesh.h"
+#include "DetourCommon.h"
+
+// These make the linker happy.
+LoginDatabaseWorkerPool LoginDatabase;
+uint32 GetLiquidFlags(uint32 liquidType)
+{
+ return 0;
+}
+
+#include "DisableMgr.h"
+namespace DisableMgr
+{
+ bool IsDisabledFor(DisableType type, uint32 entry, Unit const* unit, uint8 flags)
+ {
+ return 0;
+ }
+}
+
+using namespace VMAP;
+
+namespace MMAP
+{
+ MapBuilder::MapBuilder(float maxWalkableAngle, bool skipLiquid,
+ bool skipContinents, bool skipJunkMaps, bool skipBattlegrounds,
+ bool debugOutput, bool bigBaseUnit, const char* offMeshFilePath) :
+ m_terrainBuilder(NULL),
+ m_debugOutput (debugOutput),
+ m_skipContinents (skipContinents),
+ m_skipJunkMaps (skipJunkMaps),
+ m_skipBattlegrounds (skipBattlegrounds),
+ m_maxWalkableAngle (maxWalkableAngle),
+ m_bigBaseUnit (bigBaseUnit),
+ m_rcContext (NULL),
+ m_offMeshFilePath (offMeshFilePath)
+ {
+ m_terrainBuilder = new TerrainBuilder(skipLiquid);
+
+ m_rcContext = new rcContext(false);
+
+ discoverTiles();
+ }
+
+ /**************************************************************************/
+ MapBuilder::~MapBuilder()
+ {
+ for (TileList::iterator it = m_tiles.begin(); it != m_tiles.end(); ++it)
+ {
+ (*it).second->clear();
+ delete (*it).second;
+ }
+
+ delete m_terrainBuilder;
+ delete m_rcContext;
+ }
+
+ /**************************************************************************/
+ void MapBuilder::discoverTiles()
+ {
+ vector<string> files;
+ uint32 mapID, tileX, tileY, tileID, count = 0;
+ char filter[12];
+
+ printf("Discovering maps... ");
+ getDirContents(files, "maps");
+ for (uint32 i = 0; i < files.size(); ++i)
+ {
+ mapID = uint32(atoi(files[i].substr(0,3).c_str()));
+ if (m_tiles.find(mapID) == m_tiles.end())
+ {
+ m_tiles.insert(pair<uint32,set<uint32>*>(mapID, new set<uint32>));
+ count++;
+ }
+ }
+
+ files.clear();
+ getDirContents(files, "vmaps", "*.vmtree");
+ for (uint32 i = 0; i < files.size(); ++i)
+ {
+ mapID = uint32(atoi(files[i].substr(0,3).c_str()));
+ m_tiles.insert(pair<uint32,set<uint32>*>(mapID, new set<uint32>));
+ count++;
+ }
+ printf("found %u.\n", count);
+
+ count = 0;
+ printf("Discovering tiles... ");
+ for (TileList::iterator itr = m_tiles.begin(); itr != m_tiles.end(); ++itr)
+ {
+ set<uint32>* tiles = (*itr).second;
+ mapID = (*itr).first;
+
+ sprintf(filter, "%03u*.vmtile", mapID);
+ files.clear();
+ getDirContents(files, "vmaps", filter);
+ for (uint32 i = 0; i < files.size(); ++i)
+ {
+ tileX = uint32(atoi(files[i].substr(7,2).c_str()));
+ tileY = uint32(atoi(files[i].substr(4,2).c_str()));
+ tileID = StaticMapTree::packTileID(tileY, tileX);
+
+ tiles->insert(tileID);
+ count++;
+ }
+
+ sprintf(filter, "%03u*", mapID);
+ files.clear();
+ getDirContents(files, "maps", filter);
+ for (uint32 i = 0; i < files.size(); ++i)
+ {
+ tileY = uint32(atoi(files[i].substr(3,2).c_str()));
+ tileX = uint32(atoi(files[i].substr(5,2).c_str()));
+ tileID = StaticMapTree::packTileID(tileX, tileY);
+
+ if (tiles->insert(tileID).second)
+ count++;
+ }
+ }
+ printf("found %u.\n\n", count);
+ }
+
+ /**************************************************************************/
+ set<uint32>* MapBuilder::getTileList(uint32 mapID)
+ {
+ TileList::iterator itr = m_tiles.find(mapID);
+ if (itr != m_tiles.end())
+ return (*itr).second;
+
+ set<uint32>* tiles = new set<uint32>();
+ m_tiles.insert(pair<uint32, set<uint32>*>(mapID, tiles));
+ return tiles;
+ }
+
+ /**************************************************************************/
+ void MapBuilder::buildAllMaps(int threads)
+ {
+ std::vector<BuilderThread*> _threads;
+
+ for (int i = 0; i < threads; ++i)
+ _threads.push_back(new BuilderThread(this));
+
+ for (TileList::iterator it = m_tiles.begin(); it != m_tiles.end(); ++it)
+ {
+ uint32 mapID = it->first;
+ if (!shouldSkipMap(mapID))
+ {
+ if (threads > 1)
+ {
+ bool next = false;
+ while (!next)
+ {
+ for (std::vector<BuilderThread*>::iterator _th = _threads.begin(); _th != _threads.end(); ++_th)
+ {
+ if ((*_th)->Free)
+ {
+ (*_th)->SetMapId(mapID);
+ (*_th)->activate();
+ next = true;
+ break;
+ }
+ }
+ // Wait for 20 seconds
+ ACE_OS::sleep(ACE_Time_Value (0, 20000));
+ }
+ }
+ else
+ buildMap(mapID);
+ }
+ }
+
+ // Free memory
+ for (std::vector<BuilderThread*>::iterator _th = _threads.begin(); _th != _threads.end(); ++_th)
+ {
+ (*_th)->wait();
+ delete *_th;
+ }
+ }
+
+ /**************************************************************************/
+ void MapBuilder::getGridBounds(uint32 mapID, uint32 &minX, uint32 &minY, uint32 &maxX, uint32 &maxY)
+ {
+ maxX = INT_MAX;
+ maxY = INT_MAX;
+ minX = INT_MIN;
+ minY = INT_MIN;
+
+ float bmin[3], bmax[3], lmin[3], lmax[3];
+ MeshData meshData;
+
+ // make sure we process maps which don't have tiles
+ // initialize the static tree, which loads WDT models
+ if (!m_terrainBuilder->loadVMap(mapID, 64, 64, meshData))
+ return;
+
+ // get the coord bounds of the model data
+ if (meshData.solidVerts.size() + meshData.liquidVerts.size() == 0)
+ return;
+
+ // get the coord bounds of the model data
+ if (meshData.solidVerts.size() && meshData.liquidVerts.size())
+ {
+ rcCalcBounds(meshData.solidVerts.getCArray(), meshData.solidVerts.size() / 3, bmin, bmax);
+ rcCalcBounds(meshData.liquidVerts.getCArray(), meshData.liquidVerts.size() / 3, lmin, lmax);
+ rcVmin(bmin, lmin);
+ rcVmax(bmax, lmax);
+ }
+ else if (meshData.solidVerts.size())
+ rcCalcBounds(meshData.solidVerts.getCArray(), meshData.solidVerts.size() / 3, bmin, bmax);
+ else
+ rcCalcBounds(meshData.liquidVerts.getCArray(), meshData.liquidVerts.size() / 3, lmin, lmax);
+
+ // convert coord bounds to grid bounds
+ maxX = 32 - bmin[0] / GRID_SIZE;
+ maxY = 32 - bmin[2] / GRID_SIZE;
+ minX = 32 - bmax[0] / GRID_SIZE;
+ minY = 32 - bmax[2] / GRID_SIZE;
+ }
+
+ void MapBuilder::buildMeshFromFile(char* name)
+ {
+ FILE* file = fopen(name, "rb");
+ if (!file)
+ return;
+
+ printf("Building mesh from file\n");
+ int tileX, tileY, mapId;
+ fread(&mapId, sizeof(int), 1, file);
+ fread(&tileX, sizeof(int), 1, file);
+ fread(&tileY, sizeof(int), 1, file);
+
+ dtNavMesh* navMesh = NULL;
+ buildNavMesh(mapId, navMesh);
+ if (!navMesh)
+ {
+ printf("Failed creating navmesh! \n");
+ fclose(file);
+ return;
+ }
+
+
+ int verticesCount, indicesCount;
+ fread(&verticesCount, sizeof(int), 1, file);
+ fread(&indicesCount, sizeof(int), 1, file);
+
+ float* verts = new float[verticesCount];
+ int* inds = new int[indicesCount];
+
+ fread(verts, sizeof(float), verticesCount, file);
+ fread(inds, sizeof(int), indicesCount, file);
+
+ MeshData data;
+
+ for (int i = 0; i < verticesCount; ++i)
+ data.solidVerts.append(verts[i]);
+
+ for (int i = 0; i < indicesCount; ++i)
+ data.solidTris.append(inds[i]);
+
+ TerrainBuilder::cleanVertices(data.solidVerts, data.solidTris);
+ // get bounds of current tile
+ float bmin[3], bmax[3];
+ getTileBounds(tileX, tileY, data.solidVerts.getCArray(), data.solidVerts.size() / 3, bmin, bmax);
+
+ // build navmesh tile
+ buildMoveMapTile(mapId, tileX, tileY, data, bmin, bmax, navMesh);
+ fclose(file);
+ }
+
+ /**************************************************************************/
+ void MapBuilder::buildSingleTile(uint32 mapID, uint32 tileX, uint32 tileY)
+ {
+ dtNavMesh* navMesh = NULL;
+ buildNavMesh(mapID, navMesh);
+ if (!navMesh)
+ {
+ printf("Failed creating navmesh! \n");
+ return;
+ }
+
+ buildTile(mapID, tileX, tileY, navMesh);
+ dtFreeNavMesh(navMesh);
+ }
+
+ /**************************************************************************/
+ void MapBuilder::buildMap(uint32 mapID)
+ {
+ printf("[Thread %i] Building map %03u:\n", ACE_Thread::self(), mapID);
+
+ set<uint32>* tiles = getTileList(mapID);
+
+ // make sure we process maps which don't have tiles
+ if (!tiles->size())
+ {
+ // convert coord bounds to grid bounds
+ uint32 minX, minY, maxX, maxY;
+ getGridBounds(mapID, minX, minY, maxX, maxY);
+
+ // add all tiles within bounds to tile list.
+ for (uint32 i = minX; i <= maxX; ++i)
+ for (uint32 j = minY; j <= maxY; ++j)
+ tiles->insert(StaticMapTree::packTileID(i, j));
+ }
+
+ if (!tiles->empty())
+ {
+ // build navMesh
+ dtNavMesh* navMesh = NULL;
+ buildNavMesh(mapID, navMesh);
+ if (!navMesh)
+ {
+ printf("[Map %i] Failed creating navmesh!\n", mapID);
+ return;
+ }
+
+ // now start building mmtiles for each tile
+ printf("[Map %i] We have %u tiles. \n", mapID, (unsigned int)tiles->size());
+ for (set<uint32>::iterator it = tiles->begin(); it != tiles->end(); ++it)
+ {
+ uint32 tileX, tileY;
+
+ // unpack tile coords
+ StaticMapTree::unpackTileID((*it), tileX, tileY);
+
+ if (shouldSkipTile(mapID, tileX, tileY))
+ continue;
+
+ buildTile(mapID, tileX, tileY, navMesh);
+ }
+
+ dtFreeNavMesh(navMesh);
+ }
+
+ printf("[Map %i] Complete!\n", mapID);
+ }
+
+ /**************************************************************************/
+ void MapBuilder::buildTile(uint32 mapID, uint32 tileX, uint32 tileY, dtNavMesh* navMesh)
+ {
+ printf("[Map %i] Building tile [%02u,%02u]\n", mapID, tileX, tileY);
+
+ MeshData meshData;
+
+ // get heightmap data
+ m_terrainBuilder->loadMap(mapID, tileX, tileY, meshData);
+
+ // get model data
+ m_terrainBuilder->loadVMap(mapID, tileY, tileX, meshData);
+
+ // if there is no data, give up now
+ if (!meshData.solidVerts.size() && !meshData.liquidVerts.size())
+ return;
+
+ // remove unused vertices
+ TerrainBuilder::cleanVertices(meshData.solidVerts, meshData.solidTris);
+ TerrainBuilder::cleanVertices(meshData.liquidVerts, meshData.liquidTris);
+
+ // gather all mesh data for final data check, and bounds calculation
+ G3D::Array<float> allVerts;
+ allVerts.append(meshData.liquidVerts);
+ allVerts.append(meshData.solidVerts);
+
+ if (!allVerts.size())
+ return;
+
+ // get bounds of current tile
+ float bmin[3], bmax[3];
+ getTileBounds(tileX, tileY, allVerts.getCArray(), allVerts.size() / 3, bmin, bmax);
+
+ m_terrainBuilder->loadOffMeshConnections(mapID, tileX, tileY, meshData, m_offMeshFilePath);
+
+ // build navmesh tile
+ buildMoveMapTile(mapID, tileX, tileY, meshData, bmin, bmax, navMesh);
+ }
+
+ /**************************************************************************/
+ void MapBuilder::buildNavMesh(uint32 mapID, dtNavMesh* &navMesh)
+ {
+ set<uint32>* tiles = getTileList(mapID);
+
+ // old code for non-statically assigned bitmask sizes:
+ ///*** calculate number of bits needed to store tiles & polys ***/
+ //int tileBits = dtIlog2(dtNextPow2(tiles->size()));
+ //if (tileBits < 1) tileBits = 1; // need at least one bit!
+ //int polyBits = sizeof(dtPolyRef)*8 - SALT_MIN_BITS - tileBits;
+
+ int tileBits = STATIC_TILE_BITS;
+ int polyBits = STATIC_POLY_BITS;
+
+ int maxTiles = tiles->size();
+ int maxPolysPerTile = 1 << polyBits;
+
+ /*** calculate bounds of map ***/
+
+ uint32 tileXMin = 64, tileYMin = 64, tileXMax = 0, tileYMax = 0, tileX, tileY;
+ for (set<uint32>::iterator it = tiles->begin(); it != tiles->end(); ++it)
+ {
+ StaticMapTree::unpackTileID((*it), tileX, tileY);
+
+ if (tileX > tileXMax)
+ tileXMax = tileX;
+ else if (tileX < tileXMin)
+ tileXMin = tileX;
+
+ if (tileY > tileYMax)
+ tileYMax = tileY;
+ else if (tileY < tileYMin)
+ tileYMin = tileY;
+ }
+
+ // use Max because '32 - tileX' is negative for values over 32
+ float bmin[3], bmax[3];
+ getTileBounds(tileXMax, tileYMax, NULL, 0, bmin, bmax);
+
+ /*** now create the navmesh ***/
+
+ // navmesh creation params
+ dtNavMeshParams navMeshParams;
+ memset(&navMeshParams, 0, sizeof(dtNavMeshParams));
+ navMeshParams.tileWidth = GRID_SIZE;
+ navMeshParams.tileHeight = GRID_SIZE;
+ rcVcopy(navMeshParams.orig, bmin);
+ navMeshParams.maxTiles = maxTiles;
+ navMeshParams.maxPolys = maxPolysPerTile;
+
+ navMesh = dtAllocNavMesh();
+ printf("[Map %i] Creating navMesh...\n", mapID);
+ if (!navMesh->init(&navMeshParams))
+ {
+ printf("[Map %i] Failed creating navmesh! \n", mapID);
+ return;
+ }
+
+ char fileName[25];
+ sprintf(fileName, "mmaps/%03u.mmap", mapID);
+
+ FILE* file = fopen(fileName, "wb");
+ if (!file)
+ {
+ dtFreeNavMesh(navMesh);
+ char message[1024];
+ sprintf(message, "[Map %i] Failed to open %s for writing!\n", mapID, fileName);
+ perror(message);
+ return;
+ }
+
+ // now that we know navMesh params are valid, we can write them to file
+ fwrite(&navMeshParams, sizeof(dtNavMeshParams), 1, file);
+ fclose(file);
+ }
+
+ /**************************************************************************/
+ void MapBuilder::buildMoveMapTile(uint32 mapID, uint32 tileX, uint32 tileY,
+ MeshData &meshData, float bmin[3], float bmax[3],
+ dtNavMesh* navMesh)
+ {
+ // console output
+ char tileString[20];
+ sprintf(tileString, "[Map %03i] [%02i,%02i]: ", mapID, tileX, tileY);
+ printf("%s Building movemap tiles...\n", tileString);
+
+ IntermediateValues iv;
+
+ float* tVerts = meshData.solidVerts.getCArray();
+ int tVertCount = meshData.solidVerts.size() / 3;
+ int* tTris = meshData.solidTris.getCArray();
+ int tTriCount = meshData.solidTris.size() / 3;
+
+ float* lVerts = meshData.liquidVerts.getCArray();
+ int lVertCount = meshData.liquidVerts.size() / 3;
+ int* lTris = meshData.liquidTris.getCArray();
+ int lTriCount = meshData.liquidTris.size() / 3;
+ uint8* lTriFlags = meshData.liquidType.getCArray();
+
+ // these are WORLD UNIT based metrics
+ // this are basic unit dimentions
+ // value have to divide GRID_SIZE(533.33333f) ( aka: 0.5333, 0.2666, 0.3333, 0.1333, etc )
+ const static float BASE_UNIT_DIM = m_bigBaseUnit ? 0.533333f : 0.266666f;
+
+ // All are in UNIT metrics!
+ const static int VERTEX_PER_MAP = int(GRID_SIZE/BASE_UNIT_DIM + 0.5f);
+ const static int VERTEX_PER_TILE = m_bigBaseUnit ? 40 : 80; // must divide VERTEX_PER_MAP
+ const static int TILES_PER_MAP = VERTEX_PER_MAP/VERTEX_PER_TILE;
+
+ rcConfig config;
+ memset(&config, 0, sizeof(rcConfig));
+
+ rcVcopy(config.bmin, bmin);
+ rcVcopy(config.bmax, bmax);
+
+ config.maxVertsPerPoly = DT_VERTS_PER_POLYGON;
+ config.cs = BASE_UNIT_DIM;
+ config.ch = BASE_UNIT_DIM;
+ config.walkableSlopeAngle = m_maxWalkableAngle;
+ config.tileSize = VERTEX_PER_TILE;
+ config.walkableRadius = m_bigBaseUnit ? 1 : 2;
+ config.borderSize = config.walkableRadius + 3;
+ config.maxEdgeLen = VERTEX_PER_TILE + 1; //anything bigger than tileSize
+ config.walkableHeight = m_bigBaseUnit ? 3 : 6;
+ config.walkableClimb = m_bigBaseUnit ? 2 : 4; // keep less than walkableHeight
+ config.minRegionArea = rcSqr(60);
+ config.mergeRegionArea = rcSqr(50);
+ config.maxSimplificationError = 2.0f; // eliminates most jagged edges (tinny polygons)
+ config.detailSampleDist = config.cs * 64;
+ config.detailSampleMaxError = config.ch * 2;
+
+ // this sets the dimensions of the heightfield - should maybe happen before border padding
+ rcCalcGridSize(config.bmin, config.bmax, config.cs, &config.width, &config.height);
+
+ // allocate subregions : tiles
+ Tile* tiles = new Tile[TILES_PER_MAP * TILES_PER_MAP];
+
+ // Initialize per tile config.
+ rcConfig tileCfg = config;
+ tileCfg.width = config.tileSize + config.borderSize*2;
+ tileCfg.height = config.tileSize + config.borderSize*2;
+
+ // merge per tile poly and detail meshes
+ rcPolyMesh** pmmerge = new rcPolyMesh*[TILES_PER_MAP * TILES_PER_MAP];
+ if (!pmmerge)
+ {
+ printf("%s alloc pmmerge FIALED!\n", tileString);
+ return;
+ }
+
+ rcPolyMeshDetail** dmmerge = new rcPolyMeshDetail*[TILES_PER_MAP * TILES_PER_MAP];
+ if (!dmmerge)
+ {
+ printf("%s alloc dmmerge FIALED!\n", tileString);
+ return;
+ }
+
+ int nmerge = 0;
+ // build all tiles
+ for (int y = 0; y < TILES_PER_MAP; ++y)
+ {
+ for (int x = 0; x < TILES_PER_MAP; ++x)
+ {
+ Tile& tile = tiles[x + y * TILES_PER_MAP];
+
+ // Calculate the per tile bounding box.
+ tileCfg.bmin[0] = config.bmin[0] + float(x*config.tileSize - config.borderSize)*config.cs;
+ tileCfg.bmin[2] = config.bmin[2] + float(y*config.tileSize - config.borderSize)*config.cs;
+ tileCfg.bmax[0] = config.bmin[0] + float((x+1)*config.tileSize + config.borderSize)*config.cs;
+ tileCfg.bmax[2] = config.bmin[2] + float((y+1)*config.tileSize + config.borderSize)*config.cs;
+
+ // build heightfield
+ tile.solid = rcAllocHeightfield();
+ if (!tile.solid || !rcCreateHeightfield(m_rcContext, *tile.solid, tileCfg.width, tileCfg.height, tileCfg.bmin, tileCfg.bmax, tileCfg.cs, tileCfg.ch))
+ {
+ printf("%s Failed building heightfield! \n", tileString);
+ continue;
+ }
+
+ // mark all walkable tiles, both liquids and solids
+ unsigned char* triFlags = new unsigned char[tTriCount];
+ memset(triFlags, NAV_GROUND, tTriCount*sizeof(unsigned char));
+ rcClearUnwalkableTriangles(m_rcContext, tileCfg.walkableSlopeAngle, tVerts, tVertCount, tTris, tTriCount, triFlags);
+ rcRasterizeTriangles(m_rcContext, tVerts, tVertCount, tTris, triFlags, tTriCount, *tile.solid, config.walkableClimb);
+ delete[] triFlags;
+
+ rcFilterLowHangingWalkableObstacles(m_rcContext, config.walkableClimb, *tile.solid);
+ rcFilterLedgeSpans(m_rcContext, tileCfg.walkableHeight, tileCfg.walkableClimb, *tile.solid);
+ rcFilterWalkableLowHeightSpans(m_rcContext, tileCfg.walkableHeight, *tile.solid);
+
+ rcRasterizeTriangles(m_rcContext, lVerts, lVertCount, lTris, lTriFlags, lTriCount, *tile.solid, config.walkableClimb);
+
+ // compact heightfield spans
+ tile.chf = rcAllocCompactHeightfield();
+ if (!tile.chf || !rcBuildCompactHeightfield(m_rcContext, tileCfg.walkableHeight, tileCfg.walkableClimb, *tile.solid, *tile.chf))
+ {
+ printf("%s Failed compacting heightfield! \n", tileString);
+ continue;
+ }
+
+ // build polymesh intermediates
+ if (!rcErodeWalkableArea(m_rcContext, config.walkableRadius, *tile.chf))
+ {
+ printf("%s Failed eroding area! \n", tileString);
+ continue;
+ }
+
+ if (!rcBuildDistanceField(m_rcContext, *tile.chf))
+ {
+ printf("%s Failed building distance field! \n", tileString);
+ continue;
+ }
+
+ if (!rcBuildRegions(m_rcContext, *tile.chf, tileCfg.borderSize, tileCfg.minRegionArea, tileCfg.mergeRegionArea))
+ {
+ printf("%s Failed building regions! \n", tileString);
+ continue;
+ }
+
+ tile.cset = rcAllocContourSet();
+ if (!tile.cset || !rcBuildContours(m_rcContext, *tile.chf, tileCfg.maxSimplificationError, tileCfg.maxEdgeLen, *tile.cset))
+ {
+ printf("%s Failed building contours! \n", tileString);
+ continue;
+ }
+
+ // build polymesh
+ tile.pmesh = rcAllocPolyMesh();
+ if (!tile.pmesh || !rcBuildPolyMesh(m_rcContext, *tile.cset, tileCfg.maxVertsPerPoly, *tile.pmesh))
+ {
+ printf("%s Failed building polymesh! \n", tileString);
+ continue;
+ }
+
+ tile.dmesh = rcAllocPolyMeshDetail();
+ if (!tile.dmesh || !rcBuildPolyMeshDetail(m_rcContext, *tile.pmesh, *tile.chf, tileCfg.detailSampleDist, tileCfg.detailSampleMaxError, *tile.dmesh))
+ {
+ printf("%s Failed building polymesh detail! \n", tileString);
+ continue;
+ }
+
+ // free those up
+ // we may want to keep them in the future for debug
+ // but right now, we don't have the code to merge them
+ rcFreeHeightField(tile.solid);
+ tile.solid = NULL;
+ rcFreeCompactHeightfield(tile.chf);
+ tile.chf = NULL;
+ rcFreeContourSet(tile.cset);
+ tile.cset = NULL;
+
+ if (tile.pmesh)
+ {
+ pmmerge[nmerge] = tile.pmesh;
+ dmmerge[nmerge] = tile.dmesh;
+ nmerge++;
+ }
+ }
+ }
+
+ iv.polyMesh = rcAllocPolyMesh();
+ if (!iv.polyMesh)
+ {
+ printf("%s alloc iv.polyMesh FIALED!\n", tileString);
+ return;
+ }
+ rcMergePolyMeshes(m_rcContext, pmmerge, nmerge, *iv.polyMesh);
+
+ iv.polyMeshDetail = rcAllocPolyMeshDetail();
+ if (!iv.polyMeshDetail)
+ {
+ printf("%s alloc m_dmesh FIALED!\n", tileString);
+ return;
+ }
+ rcMergePolyMeshDetails(m_rcContext, dmmerge, nmerge, *iv.polyMeshDetail);
+
+ // free things up
+ delete[] pmmerge;
+ delete[] dmmerge;
+
+ delete[] tiles;
+
+ // remove padding for extraction
+ for (int i = 0; i < iv.polyMesh->nverts; ++i)
+ {
+ unsigned short* v = &iv.polyMesh->verts[i*3];
+ v[0] -= (unsigned short)config.borderSize;
+ v[2] -= (unsigned short)config.borderSize;
+ }
+
+ // set polygons as walkable
+ // TODO: special flags for DYNAMIC polygons, ie surfaces that can be turned on and off
+ for (int i = 0; i < iv.polyMesh->npolys; ++i)
+ if (iv.polyMesh->areas[i] & RC_WALKABLE_AREA)
+ iv.polyMesh->flags[i] = iv.polyMesh->areas[i];
+
+ // setup mesh parameters
+ dtNavMeshCreateParams params;
+ memset(&params, 0, sizeof(params));
+ params.verts = iv.polyMesh->verts;
+ params.vertCount = iv.polyMesh->nverts;
+ params.polys = iv.polyMesh->polys;
+ params.polyAreas = iv.polyMesh->areas;
+ params.polyFlags = iv.polyMesh->flags;
+ params.polyCount = iv.polyMesh->npolys;
+ params.nvp = iv.polyMesh->nvp;
+ params.detailMeshes = iv.polyMeshDetail->meshes;
+ params.detailVerts = iv.polyMeshDetail->verts;
+ params.detailVertsCount = iv.polyMeshDetail->nverts;
+ params.detailTris = iv.polyMeshDetail->tris;
+ params.detailTriCount = iv.polyMeshDetail->ntris;
+
+ params.offMeshConVerts = meshData.offMeshConnections.getCArray();
+ params.offMeshConCount = meshData.offMeshConnections.size()/6;
+ params.offMeshConRad = meshData.offMeshConnectionRads.getCArray();
+ params.offMeshConDir = meshData.offMeshConnectionDirs.getCArray();
+ params.offMeshConAreas = meshData.offMeshConnectionsAreas.getCArray();
+ params.offMeshConFlags = meshData.offMeshConnectionsFlags.getCArray();
+
+ params.walkableHeight = BASE_UNIT_DIM*config.walkableHeight; // agent height
+ params.walkableRadius = BASE_UNIT_DIM*config.walkableRadius; // agent radius
+ params.walkableClimb = BASE_UNIT_DIM*config.walkableClimb; // keep less that walkableHeight (aka agent height)!
+ params.tileX = (((bmin[0] + bmax[0]) / 2) - navMesh->getParams()->orig[0]) / GRID_SIZE;
+ params.tileY = (((bmin[2] + bmax[2]) / 2) - navMesh->getParams()->orig[2]) / GRID_SIZE;
+ rcVcopy(params.bmin, bmin);
+ rcVcopy(params.bmax, bmax);
+ params.cs = config.cs;
+ params.ch = config.ch;
+ params.tileSize = VERTEX_PER_MAP;
+
+ // will hold final navmesh
+ unsigned char* navData = NULL;
+ int navDataSize = 0;
+
+ do
+ {
+ // these values are checked within dtCreateNavMeshData - handle them here
+ // so we have a clear error message
+ if (params.nvp > DT_VERTS_PER_POLYGON)
+ {
+ printf("%s Invalid verts-per-polygon value! \n", tileString);
+ continue;
+ }
+ if (params.vertCount >= 0xffff)
+ {
+ printf("%s Too many vertices! \n", tileString);
+ continue;
+ }
+ if (!params.vertCount || !params.verts)
+ {
+ // occurs mostly when adjacent tiles have models
+ // loaded but those models don't span into this tile
+
+ // message is an annoyance
+ //printf("%sNo vertices to build tile! \n", tileString);
+ continue;
+ }
+ if (!params.polyCount || !params.polys ||
+ TILES_PER_MAP*TILES_PER_MAP == params.polyCount)
+ {
+ // we have flat tiles with no actual geometry - don't build those, its useless
+ // keep in mind that we do output those into debug info
+ // drop tiles with only exact count - some tiles may have geometry while having less tiles
+ printf("%s No polygons to build on tile! \n", tileString);
+ continue;
+ }
+ if (!params.detailMeshes || !params.detailVerts || !params.detailTris)
+ {
+ printf("%s No detail mesh to build tile! \n", tileString);
+ continue;
+ }
+
+ printf("%s Building navmesh tile...\n", tileString);
+ if (!dtCreateNavMeshData(&params, &navData, &navDataSize))
+ {
+ printf("%s Failed building navmesh tile! \n", tileString);
+ continue;
+ }
+
+ dtTileRef tileRef = 0;
+ printf("%s Adding tile to navmesh...\n", tileString);
+ // DT_TILE_FREE_DATA tells detour to unallocate memory when the tile
+ // is removed via removeTile()
+ dtStatus dtResult = navMesh->addTile(navData, navDataSize, DT_TILE_FREE_DATA, 0, &tileRef);
+ if (!tileRef || dtResult != DT_SUCCESS)
+ {
+ printf("%s Failed adding tile to navmesh! \n", tileString);
+ continue;
+ }
+
+ // file output
+ char fileName[255];
+ sprintf(fileName, "mmaps/%03u%02i%02i.mmtile", mapID, tileY, tileX);
+ FILE* file = fopen(fileName, "wb");
+ if (!file)
+ {
+ char message[1024];
+ sprintf(message, "[Map %i] Failed to open %s for writing!\n", mapID, fileName);
+ perror(message);
+ navMesh->removeTile(tileRef, NULL, NULL);
+ continue;
+ }
+
+ printf("%s Writing to file...\n", tileString);
+
+ // write header
+ MmapTileHeader header;
+ header.usesLiquids = m_terrainBuilder->usesLiquids();
+ header.size = uint32(navDataSize);
+ fwrite(&header, sizeof(MmapTileHeader), 1, file);
+
+ // write data
+ fwrite(navData, sizeof(unsigned char), navDataSize, file);
+ fclose(file);
+
+ // now that tile is written to disk, we can unload it
+ navMesh->removeTile(tileRef, NULL, NULL);
+ }
+ while (0);
+
+ if (m_debugOutput)
+ {
+ // restore padding so that the debug visualization is correct
+ for (int i = 0; i < iv.polyMesh->nverts; ++i)
+ {
+ unsigned short* v = &iv.polyMesh->verts[i*3];
+ v[0] += (unsigned short)config.borderSize;
+ v[2] += (unsigned short)config.borderSize;
+ }
+
+ iv.generateObjFile(mapID, tileX, tileY, meshData);
+ iv.writeIV(mapID, tileX, tileY);
+ }
+ }
+
+ /**************************************************************************/
+ void MapBuilder::getTileBounds(uint32 tileX, uint32 tileY, float* verts, int vertCount, float* bmin, float* bmax)
+ {
+ // this is for elevation
+ if (verts && vertCount)
+ rcCalcBounds(verts, vertCount, bmin, bmax);
+ else
+ {
+ bmin[1] = FLT_MIN;
+ bmax[1] = FLT_MAX;
+ }
+
+ // this is for width and depth
+ bmax[0] = (32 - int(tileX)) * GRID_SIZE;
+ bmax[2] = (32 - int(tileY)) * GRID_SIZE;
+ bmin[0] = bmax[0] - GRID_SIZE;
+ bmin[2] = bmax[2] - GRID_SIZE;
+ }
+
+ /**************************************************************************/
+ bool MapBuilder::shouldSkipMap(uint32 mapID)
+ {
+ if (m_skipContinents)
+ switch (mapID)
+ {
+ case 0:
+ case 1:
+ case 530:
+ case 571:
+ return true;
+ default:
+ break;
+ }
+
+ if (m_skipJunkMaps)
+ switch (mapID)
+ {
+ case 13: // test.wdt
+ case 25: // ScottTest.wdt
+ case 29: // Test.wdt
+ case 42: // Colin.wdt
+ case 169: // EmeraldDream.wdt (unused, and very large)
+ case 451: // development.wdt
+ case 573: // ExteriorTest.wdt
+ case 597: // CraigTest.wdt
+ case 605: // development_nonweighted.wdt
+ case 606: // QA_DVD.wdt
+ return true;
+ default:
+ if (isTransportMap(mapID))
+ return true;
+ break;
+ }
+
+ if (m_skipBattlegrounds)
+ switch (mapID)
+ {
+ case 30: // AV
+ case 37: // ?
+ case 489: // WSG
+ case 529: // AB
+ case 566: // EotS
+ case 607: // SotA
+ case 628: // IoC
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+ }
+
+ /**************************************************************************/
+ bool MapBuilder::isTransportMap(uint32 mapID)
+ {
+ switch (mapID)
+ {
+ // transport maps
+ case 582:
+ case 584:
+ case 586:
+ case 587:
+ case 588:
+ case 589:
+ case 590:
+ case 591:
+ case 592:
+ case 593:
+ case 594:
+ case 596:
+ case 610:
+ case 612:
+ case 613:
+ case 614:
+ case 620:
+ case 621:
+ case 622:
+ case 623:
+ case 641:
+ case 642:
+ case 647:
+ case 672:
+ case 673:
+ case 712:
+ case 713:
+ case 718:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /**************************************************************************/
+ bool MapBuilder::shouldSkipTile(uint32 mapID, uint32 tileX, uint32 tileY)
+ {
+ char fileName[255];
+ sprintf(fileName, "mmaps/%03u%02i%02i.mmtile", mapID, tileY, tileX);
+ FILE* file = fopen(fileName, "rb");
+ if (!file)
+ return false;
+
+ MmapTileHeader header;
+ fread(&header, sizeof(MmapTileHeader), 1, file);
+ fclose(file);
+
+ if (header.mmapMagic != MMAP_MAGIC || header.dtVersion != DT_NAVMESH_VERSION)
+ return false;
+
+ if (header.mmapVersion != MMAP_VERSION)
+ return false;
+
+ return true;
+ }
+
+}
diff --git a/src/tools/mmaps_generator/MapBuilder.h b/src/tools/mmaps_generator/MapBuilder.h
new file mode 100644
index 0000000..61a71ff
--- /dev/null
+++ b/src/tools/mmaps_generator/MapBuilder.h
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2008-2011 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _MAP_BUILDER_H
+#define _MAP_BUILDER_H
+
+#include <vector>
+#include <set>
+#include <map>
+
+#include "TerrainBuilder.h"
+#include "IntermediateValues.h"
+
+#include "IVMapManager.h"
+#include "WorldModel.h"
+
+#include "Recast.h"
+#include "DetourNavMesh.h"
+
+#include "ace/Task.h"
+
+using namespace std;
+using namespace VMAP;
+
+// G3D namespace typedefs conflicts with ACE typedefs
+
+namespace MMAP
+{
+ typedef map<uint32,set<uint32>*> TileList;
+ struct Tile
+ {
+ Tile() : chf(NULL), solid(NULL), cset(NULL), pmesh(NULL), dmesh(NULL) {}
+ ~Tile()
+ {
+ rcFreeCompactHeightfield(chf);
+ rcFreeContourSet(cset);
+ rcFreeHeightField(solid);
+ rcFreePolyMesh(pmesh);
+ rcFreePolyMeshDetail(dmesh);
+ }
+ rcCompactHeightfield* chf;
+ rcHeightfield* solid;
+ rcContourSet* cset;
+ rcPolyMesh* pmesh;
+ rcPolyMeshDetail* dmesh;
+ };
+
+ class MapBuilder
+ {
+ public:
+ MapBuilder(float maxWalkableAngle = 60.f,
+ bool skipLiquid = false,
+ bool skipContinents = false,
+ bool skipJunkMaps = true,
+ bool skipBattlegrounds = false,
+ bool debugOutput = false,
+ bool bigBaseUnit = false,
+ const char* offMeshFilePath = NULL);
+
+ ~MapBuilder();
+
+ // builds all mmap tiles for the specified map id (ignores skip settings)
+ void buildMap(uint32 mapID);
+ void buildMeshFromFile(char* name);
+
+ // builds an mmap tile for the specified map and its mesh
+ void buildSingleTile(uint32 mapID, uint32 tileX, uint32 tileY);
+
+ // builds list of maps, then builds all of mmap tiles (based on the skip settings)
+ void buildAllMaps(int threads);
+
+ private:
+ // detect maps and tiles
+ void discoverTiles();
+ set<uint32>* getTileList(uint32 mapID);
+
+ void buildNavMesh(uint32 mapID, dtNavMesh* &navMesh);
+
+ void buildTile(uint32 mapID, uint32 tileX, uint32 tileY, dtNavMesh* navMesh);
+
+ // move map building
+ void buildMoveMapTile(uint32 mapID,
+ uint32 tileX,
+ uint32 tileY,
+ MeshData &meshData,
+ float bmin[3],
+ float bmax[3],
+ dtNavMesh* navMesh);
+
+ void getTileBounds(uint32 tileX, uint32 tileY,
+ float* verts, int vertCount,
+ float* bmin, float* bmax);
+ void getGridBounds(uint32 mapID, uint32 &minX, uint32 &minY, uint32 &maxX, uint32 &maxY);
+
+ bool shouldSkipMap(uint32 mapID);
+ bool isTransportMap(uint32 mapID);
+ bool shouldSkipTile(uint32 mapID, uint32 tileX, uint32 tileY);
+
+ TerrainBuilder* m_terrainBuilder;
+ TileList m_tiles;
+
+ bool m_debugOutput;
+
+ const char* m_offMeshFilePath;
+ bool m_skipContinents;
+ bool m_skipJunkMaps;
+ bool m_skipBattlegrounds;
+
+ float m_maxWalkableAngle;
+ bool m_bigBaseUnit;
+
+ // build performance - not really used for now
+ rcContext* m_rcContext;
+ };
+
+ class BuilderThread : public ACE_Task<ACE_MT_SYNCH>
+ {
+ private:
+ MapBuilder* _builder;
+ uint32 _mapId;
+ public:
+ BuilderThread(MapBuilder* builder) : _builder(builder), Free(true) {}
+
+ void SetMapId(uint32 mapId) { _mapId = mapId; }
+
+ int svc()
+ {
+ Free = false;
+ if (_builder)
+ _builder->buildMap(_mapId);
+ Free = true;
+ return 0;
+ }
+
+ bool Free;
+ };
+}
+
+#endif
diff --git a/src/tools/mmaps_generator/PathCommon.h b/src/tools/mmaps_generator/PathCommon.h
new file mode 100644
index 0000000..fd02ec0
--- /dev/null
+++ b/src/tools/mmaps_generator/PathCommon.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2008-2011 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _MMAP_COMMON_H
+#define _MMAP_COMMON_H
+
+#include <string>
+#include <vector>
+
+#include "Define.h"
+
+#ifndef _WIN32
+ #include <stddef.h>
+ #include <dirent.h>
+#endif
+
+#ifdef __linux__
+ #include <errno.h>
+#endif
+
+using namespace std;
+
+namespace MMAP
+{
+ inline bool matchWildcardFilter(const char* filter, const char* str)
+ {
+ if (!filter || !str)
+ return false;
+
+ // end on null character
+ while (*filter && *str)
+ {
+ if (*filter == '*')
+ {
+ if (*++filter == '\0') // wildcard at end of filter means all remaing chars match
+ return true;
+
+ while (true)
+ {
+ if (*filter == *str)
+ break;
+ if (*str == '\0')
+ return false; // reached end of string without matching next filter character
+ str++;
+ }
+ }
+ else if (*filter != *str)
+ return false; // mismatch
+
+ filter++;
+ str++;
+ }
+
+ return ((*filter == '\0' || (*filter == '*' && *++filter == '\0')) && *str == '\0');
+ }
+
+ enum ListFilesResult
+ {
+ LISTFILE_DIRECTORY_NOT_FOUND = 0,
+ LISTFILE_OK = 1
+ };
+
+ inline ListFilesResult getDirContents(vector<string> &fileList, string dirpath = ".", string filter = "*", bool includeSubDirs = false)
+ {
+ #ifdef WIN32
+ HANDLE hFind;
+ WIN32_FIND_DATA findFileInfo;
+ string directory;
+
+ directory = dirpath + "/" + filter;
+
+ hFind = FindFirstFile(directory.c_str(), &findFileInfo);
+
+ if (hFind == INVALID_HANDLE_VALUE)
+ return LISTFILE_DIRECTORY_NOT_FOUND;
+ do
+ {
+ if (includeSubDirs || (findFileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
+ fileList.push_back(string(findFileInfo.cFileName));
+ }
+ while (FindNextFile(hFind, &findFileInfo));
+
+ FindClose(hFind);
+
+ #else
+ const char *p = dirpath.c_str();
+ DIR * dirp = opendir(p);
+ struct dirent * dp;
+ dirp = opendir(p);
+
+ while (dirp)
+ {
+ errno = 0;
+ if ((dp = readdir(dirp)) != NULL)
+ {
+ if (matchWildcardFilter(filter.c_str(), dp->d_name))
+ fileList.push_back(string(dp->d_name));
+ }
+ else
+ break;
+ }
+
+ if (dirp)
+ closedir(dirp);
+ else
+ return LISTFILE_DIRECTORY_NOT_FOUND;
+ #endif
+
+ return LISTFILE_OK;
+ }
+}
+
+#endif
diff --git a/src/tools/mmaps_generator/PathGenerator.cpp b/src/tools/mmaps_generator/PathGenerator.cpp
new file mode 100644
index 0000000..9707cb4
--- /dev/null
+++ b/src/tools/mmaps_generator/PathGenerator.cpp
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "PathCommon.h"
+#include "MapBuilder.h"
+#include "Timer.h"
+
+using namespace MMAP;
+
+bool checkDirectories(bool debugOutput)
+{
+ vector<string> dirFiles;
+
+ if (getDirContents(dirFiles, "maps") == LISTFILE_DIRECTORY_NOT_FOUND || dirFiles.empty())
+ {
+ printf("'maps' directory is empty or does not exist\n");
+ return false;
+ }
+
+ dirFiles.clear();
+ if (getDirContents(dirFiles, "vmaps", "*.vmtree") == LISTFILE_DIRECTORY_NOT_FOUND || dirFiles.empty())
+ {
+ printf("'vmaps' directory is empty or does not exist\n");
+ return false;
+ }
+
+ dirFiles.clear();
+ if (getDirContents(dirFiles, "mmaps") == LISTFILE_DIRECTORY_NOT_FOUND)
+ {
+ printf("'mmaps' directory does not exist\n");
+ return false;
+ }
+
+ dirFiles.clear();
+ if (debugOutput)
+ {
+ if (getDirContents(dirFiles, "meshes") == LISTFILE_DIRECTORY_NOT_FOUND)
+ {
+ printf("'meshes' directory does not exist (no place to put debugOutput files)\n");
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool handleArgs(int argc, char** argv,
+ int &mapnum,
+ int &tileX,
+ int &tileY,
+ float &maxAngle,
+ bool &skipLiquid,
+ bool &skipContinents,
+ bool &skipJunkMaps,
+ bool &skipBattlegrounds,
+ bool &debugOutput,
+ bool &silent,
+ bool &bigBaseUnit,
+ char* &offMeshInputPath,
+ char* &file,
+ int& threads)
+{
+ char* param = NULL;
+ for (int i = 1; i < argc; ++i)
+ {
+ if (strcmp(argv[i], "--maxAngle") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+
+ float maxangle = atof(param);
+ if (maxangle <= 90.f && maxangle >= 45.f)
+ maxAngle = maxangle;
+ else
+ printf("invalid option for '--maxAngle', using default\n");
+ }
+ else if (strcmp(argv[i], "--threads") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+ threads = atoi(param);
+ printf("Using %i threads to extract mmaps\n", threads);
+ }
+ else if (strcmp(argv[i], "--file") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+ file = param;
+ }
+ else if (strcmp(argv[i], "--tile") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+
+ char* stileX = strtok(param, ",");
+ char* stileY = strtok(NULL, ",");
+ int tilex = atoi(stileX);
+ int tiley = atoi(stileY);
+
+ if ((tilex > 0 && tilex < 64) || (tilex == 0 && strcmp(stileX, "0") == 0))
+ tileX = tilex;
+ if ((tiley > 0 && tiley < 64) || (tiley == 0 && strcmp(stileY, "0") == 0))
+ tileY = tiley;
+
+ if (tileX < 0 || tileY < 0)
+ {
+ printf("invalid tile coords.\n");
+ return false;
+ }
+ }
+ else if (strcmp(argv[i], "--skipLiquid") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+
+ if (strcmp(param, "true") == 0)
+ skipLiquid = true;
+ else if (strcmp(param, "false") == 0)
+ skipLiquid = false;
+ else
+ printf("invalid option for '--skipLiquid', using default\n");
+ }
+ else if (strcmp(argv[i], "--skipContinents") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+
+ if (strcmp(param, "true") == 0)
+ skipContinents = true;
+ else if (strcmp(param, "false") == 0)
+ skipContinents = false;
+ else
+ printf("invalid option for '--skipContinents', using default\n");
+ }
+ else if (strcmp(argv[i], "--skipJunkMaps") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+
+ if (strcmp(param, "true") == 0)
+ skipJunkMaps = true;
+ else if (strcmp(param, "false") == 0)
+ skipJunkMaps = false;
+ else
+ printf("invalid option for '--skipJunkMaps', using default\n");
+ }
+ else if (strcmp(argv[i], "--skipBattlegrounds") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+
+ if (strcmp(param, "true") == 0)
+ skipBattlegrounds = true;
+ else if (strcmp(param, "false") == 0)
+ skipBattlegrounds = false;
+ else
+ printf("invalid option for '--skipBattlegrounds', using default\n");
+ }
+ else if (strcmp(argv[i], "--debugOutput") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+
+ if (strcmp(param, "true") == 0)
+ debugOutput = true;
+ else if (strcmp(param, "false") == 0)
+ debugOutput = false;
+ else
+ printf("invalid option for '--debugOutput', using default true\n");
+ }
+ else if (strcmp(argv[i], "--silent") == 0)
+ {
+ silent = true;
+ }
+ else if (strcmp(argv[i], "--bigBaseUnit") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+
+ if (strcmp(param, "true") == 0)
+ bigBaseUnit = true;
+ else if (strcmp(param, "false") == 0)
+ bigBaseUnit = false;
+ else
+ printf("invalid option for '--bigBaseUnit', using default false\n");
+ }
+ else if (strcmp(argv[i], "--offMeshInput") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+
+ offMeshInputPath = param;
+ }
+ else
+ {
+ int map = atoi(argv[i]);
+ if (map > 0 || (map == 0 && (strcmp(argv[i], "0") == 0)))
+ mapnum = map;
+ else
+ {
+ printf("invalid map id\n");
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+int finish(const char* message, int returnValue)
+{
+ printf("%s", message);
+ getchar();
+ return returnValue;
+}
+
+int main(int argc, char** argv)
+{
+ int threads = 3, mapnum = -1;
+ float maxAngle = 60.0f;
+ int tileX = -1, tileY = -1;
+ bool skipLiquid = false,
+ skipContinents = false,
+ skipJunkMaps = true,
+ skipBattlegrounds = false,
+ debugOutput = false,
+ silent = false,
+ bigBaseUnit = false;
+ char* offMeshInputPath = NULL;
+ char* file = NULL;
+
+ bool validParam = handleArgs(argc, argv, mapnum,
+ tileX, tileY, maxAngle,
+ skipLiquid, skipContinents, skipJunkMaps, skipBattlegrounds,
+ debugOutput, silent, bigBaseUnit, offMeshInputPath, file, threads);
+
+ if (!validParam)
+ return silent ? -1 : finish("You have specified invalid parameters", -1);
+
+ if (mapnum == -1 && debugOutput)
+ {
+ if (silent)
+ return -2;
+
+ printf("You have specifed debug output, but didn't specify a map to generate.\n");
+ printf("This will generate debug output for ALL maps.\n");
+ printf("Are you sure you want to continue? (y/n) ");
+ if (getchar() != 'y')
+ return 0;
+ }
+
+ if (!checkDirectories(debugOutput))
+ return silent ? -3 : finish("Press any key to close...", -3);
+
+ MapBuilder builder(maxAngle, skipLiquid, skipContinents, skipJunkMaps,
+ skipBattlegrounds, debugOutput, bigBaseUnit, offMeshInputPath);
+
+ uint32 start = getMSTime();
+ if (file)
+ builder.buildMeshFromFile(file);
+ else if (tileX > -1 && tileY > -1 && mapnum >= 0)
+ builder.buildSingleTile(mapnum, tileX, tileY);
+ else if (mapnum >= 0)
+ builder.buildMap(uint32(mapnum));
+ else
+ builder.buildAllMaps(threads);
+
+ if (!silent)
+ printf("Finished. MMAPS were built in %u ms!\n", GetMSTimeDiffToNow(start));
+ return 1;
+}
diff --git a/src/tools/mmaps_generator/TerrainBuilder.cpp b/src/tools/mmaps_generator/TerrainBuilder.cpp
new file mode 100644
index 0000000..0159219
--- /dev/null
+++ b/src/tools/mmaps_generator/TerrainBuilder.cpp
@@ -0,0 +1,856 @@
+/*
+ * Copyright (C) 2008-2011 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "TerrainBuilder.h"
+
+#include "PathCommon.h"
+#include "MapBuilder.h"
+
+#include "VMapManager2.h"
+#include "MapTree.h"
+#include "ModelInstance.h"
+
+namespace MMAP
+{
+ TerrainBuilder::TerrainBuilder(bool skipLiquid) : m_skipLiquid (skipLiquid){ }
+ TerrainBuilder::~TerrainBuilder() { }
+
+ /**************************************************************************/
+ void TerrainBuilder::getLoopVars(Spot portion, int &loopStart, int &loopEnd, int &loopInc)
+ {
+ switch (portion)
+ {
+ case ENTIRE:
+ loopStart = 0;
+ loopEnd = V8_SIZE_SQ;
+ loopInc = 1;
+ break;
+ case TOP:
+ loopStart = 0;
+ loopEnd = V8_SIZE;
+ loopInc = 1;
+ break;
+ case LEFT:
+ loopStart = 0;
+ loopEnd = V8_SIZE_SQ - V8_SIZE + 1;
+ loopInc = V8_SIZE;
+ break;
+ case RIGHT:
+ loopStart = V8_SIZE - 1;
+ loopEnd = V8_SIZE_SQ;
+ loopInc = V8_SIZE;
+ break;
+ case BOTTOM:
+ loopStart = V8_SIZE_SQ - V8_SIZE;
+ loopEnd = V8_SIZE_SQ;
+ loopInc = 1;
+ break;
+ }
+ }
+
+ /**************************************************************************/
+ void TerrainBuilder::loadMap(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData)
+ {
+ if (loadMap(mapID, tileX, tileY, meshData, ENTIRE))
+ {
+ loadMap(mapID, tileX+1, tileY, meshData, LEFT);
+ loadMap(mapID, tileX-1, tileY, meshData, RIGHT);
+ loadMap(mapID, tileX, tileY+1, meshData, TOP);
+ loadMap(mapID, tileX, tileY-1, meshData, BOTTOM);
+ }
+ }
+
+ /**************************************************************************/
+ bool TerrainBuilder::loadMap(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData, Spot portion)
+ {
+ char mapFileName[255];
+ sprintf(mapFileName, "maps/%03u%02u%02u.map", mapID, tileY, tileX);
+
+ FILE* mapFile = fopen(mapFileName, "rb");
+ if (!mapFile)
+ return false;
+
+ map_fileheader fheader;
+ fread(&fheader, sizeof(map_fileheader), 1, mapFile);
+
+ if (fheader.versionMagic != *((uint32 const*)(MAP_VERSION_MAGIC)))
+ {
+ fclose(mapFile);
+ printf("%s is the wrong version, please extract new .map files\n", mapFileName);
+ return false;
+ }
+
+ map_heightHeader hheader;
+ fseek(mapFile, fheader.heightMapOffset, SEEK_SET);
+ fread(&hheader, sizeof(map_heightHeader), 1, mapFile);
+
+ bool haveTerrain = !(hheader.flags & MAP_HEIGHT_NO_HEIGHT);
+ bool haveLiquid = fheader.liquidMapOffset && !m_skipLiquid;
+
+ // no data in this map file
+ if (!haveTerrain && !haveLiquid)
+ {
+ fclose(mapFile);
+ return false;
+ }
+
+ // data used later
+ uint16 holes[16][16];
+ memset(holes, 0, sizeof(holes));
+ uint8 liquid_type[16][16];
+ memset(liquid_type, 0, sizeof(liquid_type));
+ G3D::Array<int> ltriangles;
+ G3D::Array<int> ttriangles;
+
+ // terrain data
+ if (haveTerrain)
+ {
+ int i;
+ float heightMultiplier;
+ float V9[V9_SIZE_SQ], V8[V8_SIZE_SQ];
+
+ if (hheader.flags & MAP_HEIGHT_AS_INT8)
+ {
+ uint8 v9[V9_SIZE_SQ];
+ uint8 v8[V8_SIZE_SQ];
+ fread(v9, sizeof(uint8), V9_SIZE_SQ, mapFile);
+ fread(v8, sizeof(uint8), V8_SIZE_SQ, mapFile);
+ heightMultiplier = (hheader.gridMaxHeight - hheader.gridHeight) / 255;
+
+ for (i = 0; i < V9_SIZE_SQ; ++i)
+ V9[i] = (float)v9[i]*heightMultiplier + hheader.gridHeight;
+
+ for (i = 0; i < V8_SIZE_SQ; ++i)
+ V8[i] = (float)v8[i]*heightMultiplier + hheader.gridHeight;
+ }
+ else if (hheader.flags & MAP_HEIGHT_AS_INT16)
+ {
+ uint16 v9[V9_SIZE_SQ];
+ uint16 v8[V8_SIZE_SQ];
+ fread(v9, sizeof(uint16), V9_SIZE_SQ, mapFile);
+ fread(v8, sizeof(uint16), V8_SIZE_SQ, mapFile);
+ heightMultiplier = (hheader.gridMaxHeight - hheader.gridHeight) / 65535;
+
+ for (i = 0; i < V9_SIZE_SQ; ++i)
+ V9[i] = (float)v9[i]*heightMultiplier + hheader.gridHeight;
+
+ for (i = 0; i < V8_SIZE_SQ; ++i)
+ V8[i] = (float)v8[i]*heightMultiplier + hheader.gridHeight;
+ }
+ else
+ {
+ fread (V9, sizeof(float), V9_SIZE_SQ, mapFile);
+ fread(V8, sizeof(float), V8_SIZE_SQ, mapFile);
+ }
+
+ // hole data
+ if (fheader.holesSize != 0)
+ {
+ memset(holes, 0, fheader.holesSize);
+ fseek(mapFile, fheader.holesOffset, SEEK_SET);
+ fread(holes, fheader.holesSize, 1, mapFile);
+ }
+
+ int count = meshData.solidVerts.size() / 3;
+ float xoffset = (float(tileX)-32)*GRID_SIZE;
+ float yoffset = (float(tileY)-32)*GRID_SIZE;
+
+ float coord[3];
+
+ for (i = 0; i < V9_SIZE_SQ; ++i)
+ {
+ getHeightCoord(i, GRID_V9, xoffset, yoffset, coord, V9);
+ meshData.solidVerts.append(coord[0]);
+ meshData.solidVerts.append(coord[2]);
+ meshData.solidVerts.append(coord[1]);
+ }
+
+ for (i = 0; i < V8_SIZE_SQ; ++i)
+ {
+ getHeightCoord(i, GRID_V8, xoffset, yoffset, coord, V8);
+ meshData.solidVerts.append(coord[0]);
+ meshData.solidVerts.append(coord[2]);
+ meshData.solidVerts.append(coord[1]);
+ }
+
+ int j, indices[3], loopStart, loopEnd, loopInc;
+ getLoopVars(portion, loopStart, loopEnd, loopInc);
+ for (i = loopStart; i < loopEnd; i+=loopInc)
+ for (j
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment