mirror of
https://github.com/EQEmu/Server.git
synced 2026-06-19 04:28:22 +00:00
Recast navigation
This commit is contained in:
@@ -0,0 +1 @@
|
||||
Recast.app
|
||||
Binary file not shown.
@@ -0,0 +1,15 @@
|
||||
s Solo Mesh
|
||||
f movement.obj
|
||||
pf -100.539185 -1.000000 54.028996 62.582016 15.757828 52.842243 0x3 0x0
|
||||
pf -100.539185 -1.000000 54.028996 -1.259964 -1.000000 50.116970 0x3 0x0
|
||||
pf -100.539185 -1.000000 54.028996 1.598934 -1.000000 23.528656 0x3 0x0
|
||||
pf -100.539185 -1.000000 54.028996 3.652847 -1.000000 -5.022881 0x3 0x0
|
||||
pf -100.539185 -1.000000 54.028996 -39.182816 8.999985 -24.697731 0x3 0x0
|
||||
pf -100.539185 -1.000000 54.028996 -66.847992 -1.000000 -28.908646 0x3 0x0
|
||||
pf -100.539185 -1.000000 54.028996 -90.966019 -1.000000 -3.219864 0x3 0x0
|
||||
pf -43.394421 -1.000000 13.312424 -90.966019 -1.000000 -3.219864 0x3 0x0
|
||||
pf -43.394421 -1.000000 13.312424 -36.447182 3.999992 -25.008087 0x3 0x0
|
||||
pf -43.394421 -1.000000 13.312424 26.394167 15.757812 -13.491264 0x3 0x0
|
||||
pf -43.394421 -1.000000 13.312424 -4.140746 6.944923 4.888435 0x3 0x0
|
||||
pf -43.394421 -1.000000 13.312424 -73.532791 -1.062469 23.137051 0x3 0x0
|
||||
pf -43.394421 -1.000000 13.312424 -72.902054 7.996834 15.076473 0x3 0x0
|
||||
@@ -0,0 +1,23 @@
|
||||
s Solo Mesh
|
||||
f nav_test.obj
|
||||
pf 18.138550 -2.370003 -21.319118 -19.206181 -2.369133 24.802742 0x3 0x0
|
||||
pf 18.252758 -2.368240 -7.000238 -19.206181 -2.369133 24.802742 0x3 0x0
|
||||
pf 18.252758 -2.368240 -7.000238 -22.759071 -2.369453 2.003946 0x3 0x0
|
||||
pf 18.252758 -2.368240 -7.000238 -24.483898 -2.369728 -6.778278 0x3 0x0
|
||||
pf 18.252758 -2.368240 -7.000238 -24.068850 -2.370285 -18.879251 0x3 0x0
|
||||
pf 18.252758 -2.368240 -7.000238 12.124170 -2.369637 -21.222471 0x3 0x0
|
||||
pf 10.830146 -2.366791 19.002508 12.124170 -2.369637 -21.222471 0x3 0x0
|
||||
pf 10.830146 -2.366791 19.002508 -7.146484 -2.368736 -16.031403 0x3 0x0
|
||||
pf 10.830146 -2.366791 19.002508 -21.615391 -2.368706 -3.264029 0x3 0x0
|
||||
pf 10.830146 -2.366791 19.002508 -22.651268 -2.369354 1.053217 0x3 0x0
|
||||
pf 10.830146 -2.366791 19.002508 19.181122 -2.368134 3.011776 0x3 0x0
|
||||
pf 10.830146 -2.366791 19.002508 19.041592 -2.368713 -7.404587 0x3 0x0
|
||||
pf 6.054083 -2.365402 3.330421 19.041592 -2.368713 -7.404587 0x3 0x0
|
||||
pf 6.054083 -2.365402 3.330421 21.846087 -2.368568 17.918859 0x3 0x0
|
||||
pf 6.054083 -2.365402 3.330421 0.967449 -2.368439 25.767756 0x3 0x0
|
||||
pf 6.054083 -2.365402 3.330421 -17.518076 -2.368477 26.569633 0x3 0x0
|
||||
pf 6.054083 -2.365402 3.330421 -22.141787 -2.369209 2.440046 0x3 0x0
|
||||
pf 6.054083 -2.365402 3.330421 -23.296972 -2.369797 -17.411043 0x3 0x0
|
||||
pf 6.054083 -2.365402 3.330421 -1.564062 -2.369926 -20.452827 0x3 0x0
|
||||
pf 6.054083 -2.365402 3.330421 16.905643 -2.370193 -21.811655 0x3 0x0
|
||||
pf 6.054083 -2.365402 3.330421 19.289761 -2.368813 -6.954918 0x3 0x0
|
||||
@@ -0,0 +1,6 @@
|
||||
s Tile Mesh
|
||||
f nav_test.obj
|
||||
rc 45.133884 -0.533207 -3.775568 47.078232 7.797605 14.293253 0xffef 0x0
|
||||
rc 52.979847 -2.778793 -2.914886 50.628868 -2.350212 13.917850 0xffef 0x0
|
||||
rc 45.209217 2.024442 1.838851 46.888412 7.797606 15.772338 0xffef 0x0
|
||||
rc 45.388317 -0.562073 -3.673226 46.651001 7.797606 15.513507 0xffef 0x0
|
||||
@@ -0,0 +1,50 @@
|
||||
file(GLOB SOURCES Source/*.cpp Contrib/fastlz/fastlz.c)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
|
||||
|
||||
if(NOT SDL2_ROOT_DIR)
|
||||
set(SDL2_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Contrib/SDL")
|
||||
endif()
|
||||
|
||||
find_package(OpenGL REQUIRED)
|
||||
find_package(SDL2 REQUIRED)
|
||||
|
||||
include_directories(SYSTEM ${OPENGL_INCLUDE_DIR})
|
||||
include_directories(SYSTEM Contrib/fastlz)
|
||||
include_directories(SYSTEM Contrib)
|
||||
include_directories(../DebugUtils/Include)
|
||||
include_directories(../Detour/Include)
|
||||
include_directories(../DetourCrowd/Include)
|
||||
include_directories(../DetourTileCache/Include)
|
||||
include_directories(../Recast/Include)
|
||||
include_directories(Include)
|
||||
|
||||
if (WIN32)
|
||||
add_executable(RecastDemo WIN32 ${SOURCES})
|
||||
elseif(APPLE)
|
||||
add_executable(RecastDemo MACOSX_BUNDLE ${SOURCES})
|
||||
else()
|
||||
add_executable(RecastDemo ${SOURCES})
|
||||
endif()
|
||||
|
||||
file(COPY Bin/Meshes DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
|
||||
file(COPY Bin/TestCases DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
|
||||
file(COPY Bin/DroidSans.ttf DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
if (WIN32)
|
||||
file(COPY "${SDL2_RUNTIME_LIBRARY}" DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
|
||||
endif()
|
||||
|
||||
add_dependencies(RecastDemo DebugUtils Detour DetourCrowd DetourTileCache Recast)
|
||||
target_link_libraries(RecastDemo ${OPENGL_LIBRARIES} SDL2::SDL2main DebugUtils Detour DetourCrowd DetourTileCache Recast)
|
||||
|
||||
install(TARGETS RecastDemo
|
||||
RUNTIME DESTINATION bin
|
||||
BUNDLE DESTINATION bin)
|
||||
install(DIRECTORY Bin/Meshes DESTINATION bin)
|
||||
install(DIRECTORY Bin/TestCases DESTINATION bin)
|
||||
install(FILES Bin/DroidSans.ttf DESTINATION bin)
|
||||
|
||||
if (WIN32)
|
||||
install(FILES "${SDL2_RUNTIME_LIBRARY}" DESTINATION bin)
|
||||
endif()
|
||||
@@ -0,0 +1,75 @@
|
||||
FastLZ - lightning-fast lossless compression library
|
||||
|
||||
Author: Ariya Hidayat
|
||||
Official website: http://www.fastlz.org
|
||||
|
||||
FastLZ is distributed using the MIT license, see file LICENSE
|
||||
for details.
|
||||
|
||||
FastLZ consists of two files: fastlz.h and fastlz.c. Just add these
|
||||
files to your project in order to use FastLZ. For information on
|
||||
compression and decompression routines, see fastlz.h.
|
||||
|
||||
A simple file compressor called 6pack is included as an example
|
||||
on how to use FastLZ. The corresponding decompressor is 6unpack.
|
||||
|
||||
To compile using GCC:
|
||||
|
||||
gcc -o 6pack 6pack.c fastlz.c
|
||||
gcc -o 6unpack 6unpack.c fastlz.c
|
||||
|
||||
To compile using MinGW:
|
||||
|
||||
mingw32-gcc -o 6pack 6pack.c fastlz.c
|
||||
mingw32-gcc -o 6unpack 6unpack.c fastlz.c
|
||||
|
||||
To compile using Microsoft Visual C++:
|
||||
|
||||
cl 6pack.c fastlz.c
|
||||
cl 6unpack.c fastlz.c
|
||||
|
||||
To compile using Borland C++:
|
||||
|
||||
bcc32 6pack.c fastlz.c
|
||||
bcc32 6unpack.c fastlz.c
|
||||
|
||||
To compile using OpenWatcom C/C++:
|
||||
|
||||
cl386 6pack.c fastlz.c
|
||||
cl386 6unpack.c fastlz.c
|
||||
|
||||
To compile using Intel C++ compiler for Windows:
|
||||
|
||||
icl 6pack.c fastlz.c
|
||||
icl 6unpack.c fastlz.c
|
||||
|
||||
To compile using Intel C++ compiler for Linux:
|
||||
|
||||
icc -o 6pack 6pack.c fastlz.c
|
||||
icc -o 6unpack 6unpack.c fastlz.c
|
||||
|
||||
To compile 6pack using LCC-Win32:
|
||||
|
||||
lc 6pack.c fastlz.c
|
||||
lc 6unpack.c fastlz.c
|
||||
|
||||
To compile 6pack using Pelles C:
|
||||
|
||||
pocc 6pack.c
|
||||
pocc 6unpack.c
|
||||
pocc fastlz.c
|
||||
polink 6pack.obj fastlz.obj
|
||||
polink 6unpack.obj fastlz.obj
|
||||
|
||||
For speed optimization, always use proper compile flags for optimization options.
|
||||
Typical compiler flags are given below:
|
||||
|
||||
* GCC (pre 4.2): -march=pentium -O3 -fomit-frame-pointer -mtune=pentium
|
||||
* GCC 4.2 or later: -march=pentium -O3 -fomit-frame-pointer -mtune=generic
|
||||
* Digital Mars C/C++: -o+all -5
|
||||
* Intel C++ (Windows): /O3 /Qipo
|
||||
* Intel C++ (Linux): -O2 -march=pentium -mtune=pentium
|
||||
* Borland C++: -O2 -5
|
||||
* LCC-Win32: -O
|
||||
* Pelles C: /O2
|
||||
|
||||
@@ -0,0 +1,556 @@
|
||||
/*
|
||||
FastLZ - lightning-fast lossless compression library
|
||||
|
||||
Copyright (C) 2007 Ariya Hidayat (ariya@kde.org)
|
||||
Copyright (C) 2006 Ariya Hidayat (ariya@kde.org)
|
||||
Copyright (C) 2005 Ariya Hidayat (ariya@kde.org)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#if !defined(FASTLZ__COMPRESSOR) && !defined(FASTLZ_DECOMPRESSOR)
|
||||
|
||||
/*
|
||||
* Always check for bound when decompressing.
|
||||
* Generally it is best to leave it defined.
|
||||
*/
|
||||
#define FASTLZ_SAFE
|
||||
|
||||
/*
|
||||
* Give hints to the compiler for branch prediction optimization.
|
||||
*/
|
||||
#if defined(__GNUC__) && (__GNUC__ > 2)
|
||||
#define FASTLZ_EXPECT_CONDITIONAL(c) (__builtin_expect((c), 1))
|
||||
#define FASTLZ_UNEXPECT_CONDITIONAL(c) (__builtin_expect((c), 0))
|
||||
#else
|
||||
#define FASTLZ_EXPECT_CONDITIONAL(c) (c)
|
||||
#define FASTLZ_UNEXPECT_CONDITIONAL(c) (c)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Use inlined functions for supported systems.
|
||||
*/
|
||||
#if defined(__GNUC__) || defined(__DMC__) || defined(__POCC__) || defined(__WATCOMC__) || defined(__SUNPRO_C)
|
||||
#define FASTLZ_INLINE inline
|
||||
#elif defined(__BORLANDC__) || defined(_MSC_VER) || defined(__LCC__)
|
||||
#define FASTLZ_INLINE __inline
|
||||
#else
|
||||
#define FASTLZ_INLINE
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Prevent accessing more than 8-bit at once, except on x86 architectures.
|
||||
*/
|
||||
#if !defined(FASTLZ_STRICT_ALIGN)
|
||||
#define FASTLZ_STRICT_ALIGN
|
||||
#if defined(__i386__) || defined(__386) /* GNU C, Sun Studio */
|
||||
#undef FASTLZ_STRICT_ALIGN
|
||||
#elif defined(__i486__) || defined(__i586__) || defined(__i686__) /* GNU C */
|
||||
#undef FASTLZ_STRICT_ALIGN
|
||||
#elif defined(_M_IX86) /* Intel, MSVC */
|
||||
#undef FASTLZ_STRICT_ALIGN
|
||||
#elif defined(__386)
|
||||
#undef FASTLZ_STRICT_ALIGN
|
||||
#elif defined(_X86_) /* MinGW */
|
||||
#undef FASTLZ_STRICT_ALIGN
|
||||
#elif defined(__I86__) /* Digital Mars */
|
||||
#undef FASTLZ_STRICT_ALIGN
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* FIXME: use preprocessor magic to set this on different platforms!
|
||||
*/
|
||||
typedef unsigned char flzuint8;
|
||||
typedef unsigned short flzuint16;
|
||||
typedef unsigned int flzuint32;
|
||||
|
||||
/* Disable "conversion from A to B, possible loss of data" warning when using MSVC */
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(disable: 4244)
|
||||
#endif
|
||||
|
||||
/* prototypes */
|
||||
int fastlz_compress(const void* input, int length, void* output);
|
||||
int fastlz_compress_level(int level, const void* input, int length, void* output);
|
||||
int fastlz_decompress(const void* input, int length, void* output, int maxout);
|
||||
|
||||
#define MAX_COPY 32
|
||||
#define MAX_LEN 264 /* 256 + 8 */
|
||||
#define MAX_DISTANCE 8192
|
||||
|
||||
#if !defined(FASTLZ_STRICT_ALIGN)
|
||||
#define FASTLZ_READU16(p) *((const flzuint16*)(p))
|
||||
#else
|
||||
#define FASTLZ_READU16(p) ((p)[0] | (p)[1]<<8)
|
||||
#endif
|
||||
|
||||
#define HASH_LOG 13
|
||||
#define HASH_SIZE (1<< HASH_LOG)
|
||||
#define HASH_MASK (HASH_SIZE-1)
|
||||
#define HASH_FUNCTION(v,p) { v = FASTLZ_READU16(p); v ^= FASTLZ_READU16(p+1)^(v>>(16-HASH_LOG));v &= HASH_MASK; }
|
||||
|
||||
#undef FASTLZ_LEVEL
|
||||
#define FASTLZ_LEVEL 1
|
||||
|
||||
#undef FASTLZ_COMPRESSOR
|
||||
#undef FASTLZ_DECOMPRESSOR
|
||||
#define FASTLZ_COMPRESSOR fastlz1_compress
|
||||
#define FASTLZ_DECOMPRESSOR fastlz1_decompress
|
||||
static FASTLZ_INLINE int FASTLZ_COMPRESSOR(const void* input, int length, void* output);
|
||||
static FASTLZ_INLINE int FASTLZ_DECOMPRESSOR(const void* input, int length, void* output, int maxout);
|
||||
#include "fastlz.c"
|
||||
|
||||
#undef FASTLZ_LEVEL
|
||||
#define FASTLZ_LEVEL 2
|
||||
|
||||
#undef MAX_DISTANCE
|
||||
#define MAX_DISTANCE 8191
|
||||
#define MAX_FARDISTANCE (65535+MAX_DISTANCE-1)
|
||||
|
||||
#undef FASTLZ_COMPRESSOR
|
||||
#undef FASTLZ_DECOMPRESSOR
|
||||
#define FASTLZ_COMPRESSOR fastlz2_compress
|
||||
#define FASTLZ_DECOMPRESSOR fastlz2_decompress
|
||||
static FASTLZ_INLINE int FASTLZ_COMPRESSOR(const void* input, int length, void* output);
|
||||
static FASTLZ_INLINE int FASTLZ_DECOMPRESSOR(const void* input, int length, void* output, int maxout);
|
||||
#include "fastlz.c"
|
||||
|
||||
int fastlz_compress(const void* input, int length, void* output)
|
||||
{
|
||||
/* for short block, choose fastlz1 */
|
||||
if(length < 65536)
|
||||
return fastlz1_compress(input, length, output);
|
||||
|
||||
/* else... */
|
||||
return fastlz2_compress(input, length, output);
|
||||
}
|
||||
|
||||
int fastlz_decompress(const void* input, int length, void* output, int maxout)
|
||||
{
|
||||
/* magic identifier for compression level */
|
||||
int level = ((*(const flzuint8*)input) >> 5) + 1;
|
||||
|
||||
if(level == 1)
|
||||
return fastlz1_decompress(input, length, output, maxout);
|
||||
if(level == 2)
|
||||
return fastlz2_decompress(input, length, output, maxout);
|
||||
|
||||
/* unknown level, trigger error */
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fastlz_compress_level(int level, const void* input, int length, void* output)
|
||||
{
|
||||
if(level == 1)
|
||||
return fastlz1_compress(input, length, output);
|
||||
if(level == 2)
|
||||
return fastlz2_compress(input, length, output);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else /* !defined(FASTLZ_COMPRESSOR) && !defined(FASTLZ_DECOMPRESSOR) */
|
||||
|
||||
static FASTLZ_INLINE int FASTLZ_COMPRESSOR(const void* input, int length, void* output)
|
||||
{
|
||||
const flzuint8* ip = (const flzuint8*) input;
|
||||
const flzuint8* ip_bound = ip + length - 2;
|
||||
const flzuint8* ip_limit = ip + length - 12;
|
||||
flzuint8* op = (flzuint8*) output;
|
||||
|
||||
const flzuint8* htab[HASH_SIZE];
|
||||
const flzuint8** hslot;
|
||||
flzuint32 hval;
|
||||
|
||||
flzuint32 copy;
|
||||
|
||||
/* sanity check */
|
||||
if(FASTLZ_UNEXPECT_CONDITIONAL(length < 4))
|
||||
{
|
||||
if(length)
|
||||
{
|
||||
/* create literal copy only */
|
||||
*op++ = length-1;
|
||||
ip_bound++;
|
||||
while(ip <= ip_bound)
|
||||
*op++ = *ip++;
|
||||
return length+1;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* initializes hash table */
|
||||
for (hslot = htab; hslot < htab + HASH_SIZE; hslot++)
|
||||
*hslot = ip;
|
||||
|
||||
/* we start with literal copy */
|
||||
copy = 2;
|
||||
*op++ = MAX_COPY-1;
|
||||
*op++ = *ip++;
|
||||
*op++ = *ip++;
|
||||
|
||||
/* main loop */
|
||||
while(FASTLZ_EXPECT_CONDITIONAL(ip < ip_limit))
|
||||
{
|
||||
const flzuint8* ref;
|
||||
flzuint32 distance;
|
||||
|
||||
/* minimum match length */
|
||||
flzuint32 len = 3;
|
||||
|
||||
/* comparison starting-point */
|
||||
const flzuint8* anchor = ip;
|
||||
|
||||
/* check for a run */
|
||||
#if FASTLZ_LEVEL==2
|
||||
if(ip[0] == ip[-1] && FASTLZ_READU16(ip-1)==FASTLZ_READU16(ip+1))
|
||||
{
|
||||
distance = 1;
|
||||
ip += 3;
|
||||
ref = anchor - 1 + 3;
|
||||
goto match;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* find potential match */
|
||||
HASH_FUNCTION(hval,ip);
|
||||
hslot = htab + hval;
|
||||
ref = htab[hval];
|
||||
|
||||
/* calculate distance to the match */
|
||||
distance = anchor - ref;
|
||||
|
||||
/* update hash table */
|
||||
*hslot = anchor;
|
||||
|
||||
/* is this a match? check the first 3 bytes */
|
||||
if(distance==0 ||
|
||||
#if FASTLZ_LEVEL==1
|
||||
(distance >= MAX_DISTANCE) ||
|
||||
#else
|
||||
(distance >= MAX_FARDISTANCE) ||
|
||||
#endif
|
||||
*ref++ != *ip++ || *ref++!=*ip++ || *ref++!=*ip++)
|
||||
goto literal;
|
||||
|
||||
#if FASTLZ_LEVEL==2
|
||||
/* far, needs at least 5-byte match */
|
||||
if(distance >= MAX_DISTANCE)
|
||||
{
|
||||
if(*ip++ != *ref++ || *ip++!= *ref++)
|
||||
goto literal;
|
||||
len += 2;
|
||||
}
|
||||
|
||||
match:
|
||||
#endif
|
||||
|
||||
/* last matched byte */
|
||||
ip = anchor + len;
|
||||
|
||||
/* distance is biased */
|
||||
distance--;
|
||||
|
||||
if(!distance)
|
||||
{
|
||||
/* zero distance means a run */
|
||||
flzuint8 x = ip[-1];
|
||||
while(ip < ip_bound)
|
||||
if(*ref++ != x) break; else ip++;
|
||||
}
|
||||
else
|
||||
for(;;)
|
||||
{
|
||||
/* safe because the outer check against ip limit */
|
||||
if(*ref++ != *ip++) break;
|
||||
if(*ref++ != *ip++) break;
|
||||
if(*ref++ != *ip++) break;
|
||||
if(*ref++ != *ip++) break;
|
||||
if(*ref++ != *ip++) break;
|
||||
if(*ref++ != *ip++) break;
|
||||
if(*ref++ != *ip++) break;
|
||||
if(*ref++ != *ip++) break;
|
||||
while(ip < ip_bound)
|
||||
if(*ref++ != *ip++) break;
|
||||
break;
|
||||
}
|
||||
|
||||
/* if we have copied something, adjust the copy count */
|
||||
if(copy)
|
||||
/* copy is biased, '0' means 1 byte copy */
|
||||
*(op-copy-1) = copy-1;
|
||||
else
|
||||
/* back, to overwrite the copy count */
|
||||
op--;
|
||||
|
||||
/* reset literal counter */
|
||||
copy = 0;
|
||||
|
||||
/* length is biased, '1' means a match of 3 bytes */
|
||||
ip -= 3;
|
||||
len = ip - anchor;
|
||||
|
||||
/* encode the match */
|
||||
#if FASTLZ_LEVEL==2
|
||||
if(distance < MAX_DISTANCE)
|
||||
{
|
||||
if(len < 7)
|
||||
{
|
||||
*op++ = (len << 5) + (distance >> 8);
|
||||
*op++ = (distance & 255);
|
||||
}
|
||||
else
|
||||
{
|
||||
*op++ = (7 << 5) + (distance >> 8);
|
||||
for(len-=7; len >= 255; len-= 255)
|
||||
*op++ = 255;
|
||||
*op++ = len;
|
||||
*op++ = (distance & 255);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* far away, but not yet in the another galaxy... */
|
||||
if(len < 7)
|
||||
{
|
||||
distance -= MAX_DISTANCE;
|
||||
*op++ = (len << 5) + 31;
|
||||
*op++ = 255;
|
||||
*op++ = distance >> 8;
|
||||
*op++ = distance & 255;
|
||||
}
|
||||
else
|
||||
{
|
||||
distance -= MAX_DISTANCE;
|
||||
*op++ = (7 << 5) + 31;
|
||||
for(len-=7; len >= 255; len-= 255)
|
||||
*op++ = 255;
|
||||
*op++ = len;
|
||||
*op++ = 255;
|
||||
*op++ = distance >> 8;
|
||||
*op++ = distance & 255;
|
||||
}
|
||||
}
|
||||
#else
|
||||
|
||||
if(FASTLZ_UNEXPECT_CONDITIONAL(len > MAX_LEN-2))
|
||||
while(len > MAX_LEN-2)
|
||||
{
|
||||
*op++ = (7 << 5) + (distance >> 8);
|
||||
*op++ = MAX_LEN - 2 - 7 -2;
|
||||
*op++ = (distance & 255);
|
||||
len -= MAX_LEN-2;
|
||||
}
|
||||
|
||||
if(len < 7)
|
||||
{
|
||||
*op++ = (len << 5) + (distance >> 8);
|
||||
*op++ = (distance & 255);
|
||||
}
|
||||
else
|
||||
{
|
||||
*op++ = (7 << 5) + (distance >> 8);
|
||||
*op++ = len - 7;
|
||||
*op++ = (distance & 255);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* update the hash at match boundary */
|
||||
HASH_FUNCTION(hval,ip);
|
||||
htab[hval] = ip++;
|
||||
HASH_FUNCTION(hval,ip);
|
||||
htab[hval] = ip++;
|
||||
|
||||
/* assuming literal copy */
|
||||
*op++ = MAX_COPY-1;
|
||||
|
||||
continue;
|
||||
|
||||
literal:
|
||||
*op++ = *anchor++;
|
||||
ip = anchor;
|
||||
copy++;
|
||||
if(FASTLZ_UNEXPECT_CONDITIONAL(copy == MAX_COPY))
|
||||
{
|
||||
copy = 0;
|
||||
*op++ = MAX_COPY-1;
|
||||
}
|
||||
}
|
||||
|
||||
/* left-over as literal copy */
|
||||
ip_bound++;
|
||||
while(ip <= ip_bound)
|
||||
{
|
||||
*op++ = *ip++;
|
||||
copy++;
|
||||
if(copy == MAX_COPY)
|
||||
{
|
||||
copy = 0;
|
||||
*op++ = MAX_COPY-1;
|
||||
}
|
||||
}
|
||||
|
||||
/* if we have copied something, adjust the copy length */
|
||||
if(copy)
|
||||
*(op-copy-1) = copy-1;
|
||||
else
|
||||
op--;
|
||||
|
||||
#if FASTLZ_LEVEL==2
|
||||
/* marker for fastlz2 */
|
||||
*(flzuint8*)output |= (1 << 5);
|
||||
#endif
|
||||
|
||||
return op - (flzuint8*)output;
|
||||
}
|
||||
|
||||
static FASTLZ_INLINE int FASTLZ_DECOMPRESSOR(const void* input, int length, void* output, int maxout)
|
||||
{
|
||||
const flzuint8* ip = (const flzuint8*) input;
|
||||
const flzuint8* ip_limit = ip + length;
|
||||
flzuint8* op = (flzuint8*) output;
|
||||
flzuint8* op_limit = op + maxout;
|
||||
flzuint32 ctrl = (*ip++) & 31;
|
||||
int loop = 1;
|
||||
|
||||
do
|
||||
{
|
||||
const flzuint8* ref = op;
|
||||
flzuint32 len = ctrl >> 5;
|
||||
flzuint32 ofs = (ctrl & 31) << 8;
|
||||
|
||||
if(ctrl >= 32)
|
||||
{
|
||||
#if FASTLZ_LEVEL==2
|
||||
flzuint8 code;
|
||||
#endif
|
||||
len--;
|
||||
ref -= ofs;
|
||||
if (len == 7-1)
|
||||
#if FASTLZ_LEVEL==1
|
||||
len += *ip++;
|
||||
ref -= *ip++;
|
||||
#else
|
||||
do
|
||||
{
|
||||
code = *ip++;
|
||||
len += code;
|
||||
} while (code==255);
|
||||
code = *ip++;
|
||||
ref -= code;
|
||||
|
||||
/* match from 16-bit distance */
|
||||
if(FASTLZ_UNEXPECT_CONDITIONAL(code==255))
|
||||
if(FASTLZ_EXPECT_CONDITIONAL(ofs==(31 << 8)))
|
||||
{
|
||||
ofs = (*ip++) << 8;
|
||||
ofs += *ip++;
|
||||
ref = op - ofs - MAX_DISTANCE;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef FASTLZ_SAFE
|
||||
if (FASTLZ_UNEXPECT_CONDITIONAL(op + len + 3 > op_limit))
|
||||
return 0;
|
||||
|
||||
if (FASTLZ_UNEXPECT_CONDITIONAL(ref-1 < (flzuint8 *)output))
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
if(FASTLZ_EXPECT_CONDITIONAL(ip < ip_limit))
|
||||
ctrl = *ip++;
|
||||
else
|
||||
loop = 0;
|
||||
|
||||
if(ref == op)
|
||||
{
|
||||
/* optimize copy for a run */
|
||||
flzuint8 b = ref[-1];
|
||||
*op++ = b;
|
||||
*op++ = b;
|
||||
*op++ = b;
|
||||
for(; len; --len)
|
||||
*op++ = b;
|
||||
}
|
||||
else
|
||||
{
|
||||
#if !defined(FASTLZ_STRICT_ALIGN)
|
||||
const flzuint16* p;
|
||||
flzuint16* q;
|
||||
#endif
|
||||
/* copy from reference */
|
||||
ref--;
|
||||
*op++ = *ref++;
|
||||
*op++ = *ref++;
|
||||
*op++ = *ref++;
|
||||
|
||||
#if !defined(FASTLZ_STRICT_ALIGN)
|
||||
/* copy a byte, so that now it's word aligned */
|
||||
if(len & 1)
|
||||
{
|
||||
*op++ = *ref++;
|
||||
len--;
|
||||
}
|
||||
|
||||
/* copy 16-bit at once */
|
||||
q = (flzuint16*) op;
|
||||
op += len;
|
||||
p = (const flzuint16*) ref;
|
||||
for(len>>=1; len > 4; len-=4)
|
||||
{
|
||||
*q++ = *p++;
|
||||
*q++ = *p++;
|
||||
*q++ = *p++;
|
||||
*q++ = *p++;
|
||||
}
|
||||
for(; len; --len)
|
||||
*q++ = *p++;
|
||||
#else
|
||||
for(; len; --len)
|
||||
*op++ = *ref++;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ctrl++;
|
||||
#ifdef FASTLZ_SAFE
|
||||
if (FASTLZ_UNEXPECT_CONDITIONAL(op + ctrl > op_limit))
|
||||
return 0;
|
||||
if (FASTLZ_UNEXPECT_CONDITIONAL(ip + ctrl > ip_limit))
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
*op++ = *ip++;
|
||||
for(--ctrl; ctrl; ctrl--)
|
||||
*op++ = *ip++;
|
||||
|
||||
loop = FASTLZ_EXPECT_CONDITIONAL(ip < ip_limit);
|
||||
if(loop)
|
||||
ctrl = *ip++;
|
||||
}
|
||||
}
|
||||
while(FASTLZ_EXPECT_CONDITIONAL(loop));
|
||||
|
||||
return op - (flzuint8*)output;
|
||||
}
|
||||
|
||||
#endif /* !defined(FASTLZ_COMPRESSOR) && !defined(FASTLZ_DECOMPRESSOR) */
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
FastLZ - lightning-fast lossless compression library
|
||||
|
||||
Copyright (C) 2007 Ariya Hidayat (ariya@kde.org)
|
||||
Copyright (C) 2006 Ariya Hidayat (ariya@kde.org)
|
||||
Copyright (C) 2005 Ariya Hidayat (ariya@kde.org)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef FASTLZ_H
|
||||
#define FASTLZ_H
|
||||
|
||||
#define FASTLZ_VERSION 0x000100
|
||||
|
||||
#define FASTLZ_VERSION_MAJOR 0
|
||||
#define FASTLZ_VERSION_MINOR 0
|
||||
#define FASTLZ_VERSION_REVISION 0
|
||||
|
||||
#define FASTLZ_VERSION_STRING "0.1.0"
|
||||
|
||||
#if defined (__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
Compress a block of data in the input buffer and returns the size of
|
||||
compressed block. The size of input buffer is specified by length. The
|
||||
minimum input buffer size is 16.
|
||||
|
||||
The output buffer must be at least 5% larger than the input buffer
|
||||
and can not be smaller than 66 bytes.
|
||||
|
||||
If the input is not compressible, the return value might be larger than
|
||||
length (input buffer size).
|
||||
|
||||
The input buffer and the output buffer can not overlap.
|
||||
*/
|
||||
|
||||
int fastlz_compress(const void* input, int length, void* output);
|
||||
|
||||
/**
|
||||
Decompress a block of compressed data and returns the size of the
|
||||
decompressed block. If error occurs, e.g. the compressed data is
|
||||
corrupted or the output buffer is not large enough, then 0 (zero)
|
||||
will be returned instead.
|
||||
|
||||
The input buffer and the output buffer can not overlap.
|
||||
|
||||
Decompression is memory safe and guaranteed not to write the output buffer
|
||||
more than what is specified in maxout.
|
||||
*/
|
||||
|
||||
int fastlz_decompress(const void* input, int length, void* output, int maxout);
|
||||
|
||||
/**
|
||||
Compress a block of data in the input buffer and returns the size of
|
||||
compressed block. The size of input buffer is specified by length. The
|
||||
minimum input buffer size is 16.
|
||||
|
||||
The output buffer must be at least 5% larger than the input buffer
|
||||
and can not be smaller than 66 bytes.
|
||||
|
||||
If the input is not compressible, the return value might be larger than
|
||||
length (input buffer size).
|
||||
|
||||
The input buffer and the output buffer can not overlap.
|
||||
|
||||
Compression level can be specified in parameter level. At the moment,
|
||||
only level 1 and level 2 are supported.
|
||||
Level 1 is the fastest compression and generally useful for short data.
|
||||
Level 2 is slightly slower but it gives better compression ratio.
|
||||
|
||||
Note that the compressed data, regardless of the level, can always be
|
||||
decompressed using the function fastlz_decompress above.
|
||||
*/
|
||||
|
||||
int fastlz_compress_level(int level, const void* input, int length, void* output);
|
||||
|
||||
#if defined (__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* FASTLZ_H */
|
||||
@@ -0,0 +1,6 @@
|
||||
|
||||
Windows
|
||||
Download SDL Developer Libraries from http://www.libsdl.org and unzip them to SDL/
|
||||
|
||||
OSX
|
||||
Download and install OSX SDL Developer Libraries from http://www.libsdl.org
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,59 @@
|
||||
//
|
||||
// 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 CHUNKYTRIMESH_H
|
||||
#define CHUNKYTRIMESH_H
|
||||
|
||||
struct rcChunkyTriMeshNode
|
||||
{
|
||||
float bmin[2];
|
||||
float bmax[2];
|
||||
int i;
|
||||
int n;
|
||||
};
|
||||
|
||||
struct rcChunkyTriMesh
|
||||
{
|
||||
inline rcChunkyTriMesh() : nodes(0), nnodes(0), tris(0), ntris(0), maxTrisPerChunk(0) {};
|
||||
inline ~rcChunkyTriMesh() { delete [] nodes; delete [] tris; }
|
||||
|
||||
rcChunkyTriMeshNode* nodes;
|
||||
int nnodes;
|
||||
int* tris;
|
||||
int ntris;
|
||||
int maxTrisPerChunk;
|
||||
|
||||
private:
|
||||
// Explicitly disabled copy constructor and copy assignment operator.
|
||||
rcChunkyTriMesh(const rcChunkyTriMesh&);
|
||||
rcChunkyTriMesh& operator=(const rcChunkyTriMesh&);
|
||||
};
|
||||
|
||||
/// Creates partitioned triangle mesh (AABB tree),
|
||||
/// where each node contains at max trisPerChunk triangles.
|
||||
bool rcCreateChunkyTriMesh(const float* verts, const int* tris, int ntris,
|
||||
int trisPerChunk, rcChunkyTriMesh* cm);
|
||||
|
||||
/// Returns the chunk indices which overlap the input rectable.
|
||||
int rcGetChunksOverlappingRect(const rcChunkyTriMesh* cm, float bmin[2], float bmax[2], int* ids, const int maxIds);
|
||||
|
||||
/// Returns the chunk indices which overlap the input segment.
|
||||
int rcGetChunksOverlappingSegment(const rcChunkyTriMesh* cm, float p[2], float q[2], int* ids, const int maxIds);
|
||||
|
||||
|
||||
#endif // CHUNKYTRIMESH_H
|
||||
@@ -0,0 +1,55 @@
|
||||
//
|
||||
// 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 CONVEXVOLUMETOOL_H
|
||||
#define CONVEXVOLUMETOOL_H
|
||||
|
||||
#include "Sample.h"
|
||||
|
||||
// Tool to create convex volumess for InputGeom
|
||||
|
||||
class ConvexVolumeTool : public SampleTool
|
||||
{
|
||||
Sample* m_sample;
|
||||
int m_areaType;
|
||||
float m_polyOffset;
|
||||
float m_boxHeight;
|
||||
float m_boxDescent;
|
||||
|
||||
static const int MAX_PTS = 12;
|
||||
float m_pts[MAX_PTS*3];
|
||||
int m_npts;
|
||||
int m_hull[MAX_PTS];
|
||||
int m_nhull;
|
||||
|
||||
public:
|
||||
ConvexVolumeTool();
|
||||
|
||||
virtual int type() { return TOOL_CONVEX_VOLUME; }
|
||||
virtual void init(Sample* sample);
|
||||
virtual void reset();
|
||||
virtual void handleMenu();
|
||||
virtual void handleClick(const float* s, const float* p, bool shift);
|
||||
virtual void handleToggle();
|
||||
virtual void handleStep();
|
||||
virtual void handleUpdate(const float dt);
|
||||
virtual void handleRender();
|
||||
virtual void handleRenderOverlay(double* proj, double* model, int* view);
|
||||
};
|
||||
|
||||
#endif // CONVEXVOLUMETOOL_H
|
||||
@@ -0,0 +1,144 @@
|
||||
//
|
||||
// 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 CROWDTOOL_H
|
||||
#define CROWDTOOL_H
|
||||
|
||||
#include "Sample.h"
|
||||
#include "DetourNavMesh.h"
|
||||
#include "DetourObstacleAvoidance.h"
|
||||
#include "ValueHistory.h"
|
||||
#include "DetourCrowd.h"
|
||||
|
||||
// Tool to create crowds.
|
||||
|
||||
struct CrowdToolParams
|
||||
{
|
||||
bool m_expandSelectedDebugDraw;
|
||||
bool m_showCorners;
|
||||
bool m_showCollisionSegments;
|
||||
bool m_showPath;
|
||||
bool m_showVO;
|
||||
bool m_showOpt;
|
||||
bool m_showNeis;
|
||||
|
||||
bool m_expandDebugDraw;
|
||||
bool m_showLabels;
|
||||
bool m_showGrid;
|
||||
bool m_showNodes;
|
||||
bool m_showPerfGraph;
|
||||
bool m_showDetailAll;
|
||||
|
||||
bool m_expandOptions;
|
||||
bool m_anticipateTurns;
|
||||
bool m_optimizeVis;
|
||||
bool m_optimizeTopo;
|
||||
bool m_obstacleAvoidance;
|
||||
float m_obstacleAvoidanceType;
|
||||
bool m_separation;
|
||||
float m_separationWeight;
|
||||
};
|
||||
|
||||
class CrowdToolState : public SampleToolState
|
||||
{
|
||||
Sample* m_sample;
|
||||
dtNavMesh* m_nav;
|
||||
dtCrowd* m_crowd;
|
||||
|
||||
float m_targetPos[3];
|
||||
dtPolyRef m_targetRef;
|
||||
|
||||
dtCrowdAgentDebugInfo m_agentDebug;
|
||||
dtObstacleAvoidanceDebugData* m_vod;
|
||||
|
||||
static const int AGENT_MAX_TRAIL = 64;
|
||||
static const int MAX_AGENTS = 128;
|
||||
struct AgentTrail
|
||||
{
|
||||
float trail[AGENT_MAX_TRAIL*3];
|
||||
int htrail;
|
||||
};
|
||||
AgentTrail m_trails[MAX_AGENTS];
|
||||
|
||||
ValueHistory m_crowdTotalTime;
|
||||
ValueHistory m_crowdSampleCount;
|
||||
|
||||
CrowdToolParams m_toolParams;
|
||||
|
||||
bool m_run;
|
||||
|
||||
public:
|
||||
CrowdToolState();
|
||||
virtual ~CrowdToolState();
|
||||
|
||||
virtual void init(class Sample* sample);
|
||||
virtual void reset();
|
||||
virtual void handleRender();
|
||||
virtual void handleRenderOverlay(double* proj, double* model, int* view);
|
||||
virtual void handleUpdate(const float dt);
|
||||
|
||||
inline bool isRunning() const { return m_run; }
|
||||
inline void setRunning(const bool s) { m_run = s; }
|
||||
|
||||
void addAgent(const float* pos);
|
||||
void removeAgent(const int idx);
|
||||
void hilightAgent(const int idx);
|
||||
void updateAgentParams();
|
||||
int hitTestAgents(const float* s, const float* p);
|
||||
void setMoveTarget(const float* p, bool adjust);
|
||||
void updateTick(const float dt);
|
||||
|
||||
inline CrowdToolParams* getToolParams() { return &m_toolParams; }
|
||||
|
||||
private:
|
||||
// Explicitly disabled copy constructor and copy assignment operator.
|
||||
CrowdToolState(const CrowdToolState&);
|
||||
CrowdToolState& operator=(const CrowdToolState&);
|
||||
};
|
||||
|
||||
|
||||
class CrowdTool : public SampleTool
|
||||
{
|
||||
Sample* m_sample;
|
||||
CrowdToolState* m_state;
|
||||
|
||||
enum ToolMode
|
||||
{
|
||||
TOOLMODE_CREATE,
|
||||
TOOLMODE_MOVE_TARGET,
|
||||
TOOLMODE_SELECT,
|
||||
TOOLMODE_TOGGLE_POLYS,
|
||||
};
|
||||
ToolMode m_mode;
|
||||
|
||||
public:
|
||||
CrowdTool();
|
||||
|
||||
virtual int type() { return TOOL_CROWD; }
|
||||
virtual void init(Sample* sample);
|
||||
virtual void reset();
|
||||
virtual void handleMenu();
|
||||
virtual void handleClick(const float* s, const float* p, bool shift);
|
||||
virtual void handleToggle();
|
||||
virtual void handleStep();
|
||||
virtual void handleUpdate(const float dt);
|
||||
virtual void handleRender();
|
||||
virtual void handleRenderOverlay(double* proj, double* model, int* view);
|
||||
};
|
||||
|
||||
#endif // CROWDTOOL_H
|
||||
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// 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 FILELIST_H
|
||||
#define FILELIST_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
void scanDirectoryAppend(const std::string& path, const std::string& ext, std::vector<std::string>& fileList);
|
||||
void scanDirectory(const std::string& path, const std::string& ext, std::vector<std::string>& fileList);
|
||||
|
||||
#endif // FILELIST_H
|
||||
@@ -0,0 +1,150 @@
|
||||
//
|
||||
// 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 INPUTGEOM_H
|
||||
#define INPUTGEOM_H
|
||||
|
||||
#include "ChunkyTriMesh.h"
|
||||
#include "MeshLoaderObj.h"
|
||||
|
||||
static const int MAX_CONVEXVOL_PTS = 12;
|
||||
struct ConvexVolume
|
||||
{
|
||||
float verts[MAX_CONVEXVOL_PTS*3];
|
||||
float hmin, hmax;
|
||||
int nverts;
|
||||
int area;
|
||||
};
|
||||
|
||||
struct BuildSettings
|
||||
{
|
||||
// Cell size in world units
|
||||
float cellSize;
|
||||
// Cell height in world units
|
||||
float cellHeight;
|
||||
// Agent height in world units
|
||||
float agentHeight;
|
||||
// Agent radius in world units
|
||||
float agentRadius;
|
||||
// Agent max climb in world units
|
||||
float agentMaxClimb;
|
||||
// Agent max slope in degrees
|
||||
float agentMaxSlope;
|
||||
// Region minimum size in voxels.
|
||||
// regionMinSize = sqrt(regionMinArea)
|
||||
float regionMinSize;
|
||||
// Region merge size in voxels.
|
||||
// regionMergeSize = sqrt(regionMergeArea)
|
||||
float regionMergeSize;
|
||||
// Edge max length in world units
|
||||
float edgeMaxLen;
|
||||
// Edge max error in voxels
|
||||
float edgeMaxError;
|
||||
float vertsPerPoly;
|
||||
// Detail sample distance in voxels
|
||||
float detailSampleDist;
|
||||
// Detail sample max error in voxel heights.
|
||||
float detailSampleMaxError;
|
||||
// Partition type, see SamplePartitionType
|
||||
int partitionType;
|
||||
// Bounds of the area to mesh
|
||||
float navMeshBMin[3];
|
||||
float navMeshBMax[3];
|
||||
// Size of the tiles in voxels
|
||||
float tileSize;
|
||||
};
|
||||
|
||||
class InputGeom
|
||||
{
|
||||
rcChunkyTriMesh* m_chunkyMesh;
|
||||
rcMeshLoaderObj* m_mesh;
|
||||
float m_meshBMin[3], m_meshBMax[3];
|
||||
BuildSettings m_buildSettings;
|
||||
bool m_hasBuildSettings;
|
||||
|
||||
/// @name Off-Mesh connections.
|
||||
///@{
|
||||
static const int MAX_OFFMESH_CONNECTIONS = 256;
|
||||
float m_offMeshConVerts[MAX_OFFMESH_CONNECTIONS*3*2];
|
||||
float m_offMeshConRads[MAX_OFFMESH_CONNECTIONS];
|
||||
unsigned char m_offMeshConDirs[MAX_OFFMESH_CONNECTIONS];
|
||||
unsigned char m_offMeshConAreas[MAX_OFFMESH_CONNECTIONS];
|
||||
unsigned short m_offMeshConFlags[MAX_OFFMESH_CONNECTIONS];
|
||||
unsigned int m_offMeshConId[MAX_OFFMESH_CONNECTIONS];
|
||||
int m_offMeshConCount;
|
||||
///@}
|
||||
|
||||
/// @name Convex Volumes.
|
||||
///@{
|
||||
static const int MAX_VOLUMES = 256;
|
||||
ConvexVolume m_volumes[MAX_VOLUMES];
|
||||
int m_volumeCount;
|
||||
///@}
|
||||
|
||||
bool loadMesh(class rcContext* ctx, const std::string& filepath);
|
||||
bool loadGeomSet(class rcContext* ctx, const std::string& filepath);
|
||||
public:
|
||||
InputGeom();
|
||||
~InputGeom();
|
||||
|
||||
|
||||
bool load(class rcContext* ctx, const std::string& filepath);
|
||||
bool saveGeomSet(const BuildSettings* settings);
|
||||
|
||||
/// Method to return static mesh data.
|
||||
const rcMeshLoaderObj* getMesh() const { return m_mesh; }
|
||||
const float* getMeshBoundsMin() const { return m_meshBMin; }
|
||||
const float* getMeshBoundsMax() const { return m_meshBMax; }
|
||||
const float* getNavMeshBoundsMin() const { return m_hasBuildSettings ? m_buildSettings.navMeshBMin : m_meshBMin; }
|
||||
const float* getNavMeshBoundsMax() const { return m_hasBuildSettings ? m_buildSettings.navMeshBMax : m_meshBMax; }
|
||||
const rcChunkyTriMesh* getChunkyMesh() const { return m_chunkyMesh; }
|
||||
const BuildSettings* getBuildSettings() const { return m_hasBuildSettings ? &m_buildSettings : 0; }
|
||||
bool raycastMesh(float* src, float* dst, float& tmin);
|
||||
|
||||
/// @name Off-Mesh connections.
|
||||
///@{
|
||||
int getOffMeshConnectionCount() const { return m_offMeshConCount; }
|
||||
const float* getOffMeshConnectionVerts() const { return m_offMeshConVerts; }
|
||||
const float* getOffMeshConnectionRads() const { return m_offMeshConRads; }
|
||||
const unsigned char* getOffMeshConnectionDirs() const { return m_offMeshConDirs; }
|
||||
const unsigned char* getOffMeshConnectionAreas() const { return m_offMeshConAreas; }
|
||||
const unsigned short* getOffMeshConnectionFlags() const { return m_offMeshConFlags; }
|
||||
const unsigned int* getOffMeshConnectionId() const { return m_offMeshConId; }
|
||||
void addOffMeshConnection(const float* spos, const float* epos, const float rad,
|
||||
unsigned char bidir, unsigned char area, unsigned short flags);
|
||||
void deleteOffMeshConnection(int i);
|
||||
void drawOffMeshConnections(struct duDebugDraw* dd, bool hilight = false);
|
||||
///@}
|
||||
|
||||
/// @name Box Volumes.
|
||||
///@{
|
||||
int getConvexVolumeCount() const { return m_volumeCount; }
|
||||
const ConvexVolume* getConvexVolumes() const { return m_volumes; }
|
||||
void addConvexVolume(const float* verts, const int nverts,
|
||||
const float minh, const float maxh, unsigned char area);
|
||||
void deleteConvexVolume(int i);
|
||||
void drawConvexVolumes(struct duDebugDraw* dd, bool hilight = false);
|
||||
///@}
|
||||
|
||||
private:
|
||||
// Explicitly disabled copy constructor and copy assignment operator.
|
||||
InputGeom(const InputGeom&);
|
||||
InputGeom& operator=(const InputGeom&);
|
||||
};
|
||||
|
||||
#endif // INPUTGEOM_H
|
||||
@@ -0,0 +1,56 @@
|
||||
//
|
||||
// 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 MESHLOADER_OBJ
|
||||
#define MESHLOADER_OBJ
|
||||
|
||||
#include <string>
|
||||
|
||||
class rcMeshLoaderObj
|
||||
{
|
||||
public:
|
||||
rcMeshLoaderObj();
|
||||
~rcMeshLoaderObj();
|
||||
|
||||
bool load(const std::string& fileName);
|
||||
|
||||
const float* getVerts() const { return m_verts; }
|
||||
const float* getNormals() const { return m_normals; }
|
||||
const int* getTris() const { return m_tris; }
|
||||
int getVertCount() const { return m_vertCount; }
|
||||
int getTriCount() const { return m_triCount; }
|
||||
const std::string& getFileName() const { return m_filename; }
|
||||
|
||||
private:
|
||||
// Explicitly disabled copy constructor and copy assignment operator.
|
||||
rcMeshLoaderObj(const rcMeshLoaderObj&);
|
||||
rcMeshLoaderObj& operator=(const rcMeshLoaderObj&);
|
||||
|
||||
void addVertex(float x, float y, float z, int& cap);
|
||||
void addTriangle(int a, int b, int c, int& cap);
|
||||
|
||||
std::string m_filename;
|
||||
float m_scale;
|
||||
float* m_verts;
|
||||
int* m_tris;
|
||||
float* m_normals;
|
||||
int m_vertCount;
|
||||
int m_triCount;
|
||||
};
|
||||
|
||||
#endif // MESHLOADER_OBJ
|
||||
@@ -0,0 +1,56 @@
|
||||
//
|
||||
// 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 NAVMESHPRUNETOOL_H
|
||||
#define NAVMESHPRUNETOOL_H
|
||||
|
||||
#include "Sample.h"
|
||||
|
||||
// Prune navmesh to accessible locations from a point.
|
||||
|
||||
class NavMeshPruneTool : public SampleTool
|
||||
{
|
||||
Sample* m_sample;
|
||||
|
||||
class NavmeshFlags* m_flags;
|
||||
|
||||
float m_hitPos[3];
|
||||
bool m_hitPosSet;
|
||||
|
||||
public:
|
||||
NavMeshPruneTool();
|
||||
virtual ~NavMeshPruneTool();
|
||||
|
||||
virtual int type() { return TOOL_NAVMESH_PRUNE; }
|
||||
virtual void init(Sample* sample);
|
||||
virtual void reset();
|
||||
virtual void handleMenu();
|
||||
virtual void handleClick(const float* s, const float* p, bool shift);
|
||||
virtual void handleToggle();
|
||||
virtual void handleStep();
|
||||
virtual void handleUpdate(const float dt);
|
||||
virtual void handleRender();
|
||||
virtual void handleRenderOverlay(double* proj, double* model, int* view);
|
||||
|
||||
private:
|
||||
// Explicitly disabled copy constructor and copy assignment operator.
|
||||
NavMeshPruneTool(const NavMeshPruneTool&);
|
||||
NavMeshPruneTool& operator=(const NavMeshPruneTool&);
|
||||
};
|
||||
|
||||
#endif // NAVMESHPRUNETOOL_H
|
||||
@@ -0,0 +1,113 @@
|
||||
//
|
||||
// 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 NAVMESHTESTERTOOL_H
|
||||
#define NAVMESHTESTERTOOL_H
|
||||
|
||||
#include "Sample.h"
|
||||
#include "DetourNavMesh.h"
|
||||
#include "DetourNavMeshQuery.h"
|
||||
|
||||
class NavMeshTesterTool : public SampleTool
|
||||
{
|
||||
Sample* m_sample;
|
||||
|
||||
dtNavMesh* m_navMesh;
|
||||
dtNavMeshQuery* m_navQuery;
|
||||
|
||||
dtQueryFilter m_filter;
|
||||
|
||||
dtStatus m_pathFindStatus;
|
||||
|
||||
enum ToolMode
|
||||
{
|
||||
TOOLMODE_PATHFIND_FOLLOW,
|
||||
TOOLMODE_PATHFIND_STRAIGHT,
|
||||
TOOLMODE_PATHFIND_SLICED,
|
||||
TOOLMODE_RAYCAST,
|
||||
TOOLMODE_DISTANCE_TO_WALL,
|
||||
TOOLMODE_FIND_POLYS_IN_CIRCLE,
|
||||
TOOLMODE_FIND_POLYS_IN_SHAPE,
|
||||
TOOLMODE_FIND_LOCAL_NEIGHBOURHOOD,
|
||||
};
|
||||
|
||||
ToolMode m_toolMode;
|
||||
|
||||
int m_straightPathOptions;
|
||||
|
||||
static const int MAX_POLYS = 256;
|
||||
static const int MAX_SMOOTH = 2048;
|
||||
|
||||
dtPolyRef m_startRef;
|
||||
dtPolyRef m_endRef;
|
||||
dtPolyRef m_polys[MAX_POLYS];
|
||||
dtPolyRef m_parent[MAX_POLYS];
|
||||
int m_npolys;
|
||||
float m_straightPath[MAX_POLYS*3];
|
||||
unsigned char m_straightPathFlags[MAX_POLYS];
|
||||
dtPolyRef m_straightPathPolys[MAX_POLYS];
|
||||
int m_nstraightPath;
|
||||
float m_polyPickExt[3];
|
||||
float m_smoothPath[MAX_SMOOTH*3];
|
||||
int m_nsmoothPath;
|
||||
float m_queryPoly[4*3];
|
||||
|
||||
static const int MAX_RAND_POINTS = 64;
|
||||
float m_randPoints[MAX_RAND_POINTS*3];
|
||||
int m_nrandPoints;
|
||||
bool m_randPointsInCircle;
|
||||
|
||||
float m_spos[3];
|
||||
float m_epos[3];
|
||||
float m_hitPos[3];
|
||||
float m_hitNormal[3];
|
||||
bool m_hitResult;
|
||||
float m_distanceToWall;
|
||||
float m_neighbourhoodRadius;
|
||||
float m_randomRadius;
|
||||
bool m_sposSet;
|
||||
bool m_eposSet;
|
||||
|
||||
int m_pathIterNum;
|
||||
dtPolyRef m_pathIterPolys[MAX_POLYS];
|
||||
int m_pathIterPolyCount;
|
||||
float m_prevIterPos[3], m_iterPos[3], m_steerPos[3], m_targetPos[3];
|
||||
|
||||
static const int MAX_STEER_POINTS = 10;
|
||||
float m_steerPoints[MAX_STEER_POINTS*3];
|
||||
int m_steerPointCount;
|
||||
|
||||
public:
|
||||
NavMeshTesterTool();
|
||||
|
||||
virtual int type() { return TOOL_NAVMESH_TESTER; }
|
||||
virtual void init(Sample* sample);
|
||||
virtual void reset();
|
||||
virtual void handleMenu();
|
||||
virtual void handleClick(const float* s, const float* p, bool shift);
|
||||
virtual void handleToggle();
|
||||
virtual void handleStep();
|
||||
virtual void handleUpdate(const float dt);
|
||||
virtual void handleRender();
|
||||
virtual void handleRenderOverlay(double* proj, double* model, int* view);
|
||||
|
||||
void recalc();
|
||||
void drawAgent(const float* pos, float r, float h, float c, const unsigned int col);
|
||||
};
|
||||
|
||||
#endif // NAVMESHTESTERTOOL_H
|
||||
@@ -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.
|
||||
//
|
||||
|
||||
#ifndef OFFMESHCONNECTIONTOOL_H
|
||||
#define OFFMESHCONNECTIONTOOL_H
|
||||
|
||||
#include "Sample.h"
|
||||
|
||||
// Tool to create off-mesh connection for InputGeom
|
||||
|
||||
class OffMeshConnectionTool : public SampleTool
|
||||
{
|
||||
Sample* m_sample;
|
||||
float m_hitPos[3];
|
||||
bool m_hitPosSet;
|
||||
bool m_bidir;
|
||||
unsigned char m_oldFlags;
|
||||
|
||||
public:
|
||||
OffMeshConnectionTool();
|
||||
~OffMeshConnectionTool();
|
||||
|
||||
virtual int type() { return TOOL_OFFMESH_CONNECTION; }
|
||||
virtual void init(Sample* sample);
|
||||
virtual void reset();
|
||||
virtual void handleMenu();
|
||||
virtual void handleClick(const float* s, const float* p, bool shift);
|
||||
virtual void handleToggle();
|
||||
virtual void handleStep();
|
||||
virtual void handleUpdate(const float dt);
|
||||
virtual void handleRender();
|
||||
virtual void handleRenderOverlay(double* proj, double* model, int* view);
|
||||
};
|
||||
|
||||
#endif // OFFMESHCONNECTIONTOOL_H
|
||||
@@ -0,0 +1,32 @@
|
||||
//
|
||||
// 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 PERFTIMER_H
|
||||
#define PERFTIMER_H
|
||||
|
||||
#ifdef __GNUC__
|
||||
#include <stdint.h>
|
||||
typedef int64_t TimeVal;
|
||||
#else
|
||||
typedef __int64 TimeVal;
|
||||
#endif
|
||||
|
||||
TimeVal getPerfTime();
|
||||
int getPerfTimeUsec(const TimeVal duration);
|
||||
|
||||
#endif // PERFTIMER_H
|
||||
@@ -0,0 +1,190 @@
|
||||
//
|
||||
// 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 RECASTSAMPLE_H
|
||||
#define RECASTSAMPLE_H
|
||||
|
||||
#include "Recast.h"
|
||||
#include "SampleInterfaces.h"
|
||||
|
||||
|
||||
/// Tool types.
|
||||
enum SampleToolType
|
||||
{
|
||||
TOOL_NONE = 0,
|
||||
TOOL_TILE_EDIT,
|
||||
TOOL_TILE_HIGHLIGHT,
|
||||
TOOL_TEMP_OBSTACLE,
|
||||
TOOL_NAVMESH_TESTER,
|
||||
TOOL_NAVMESH_PRUNE,
|
||||
TOOL_OFFMESH_CONNECTION,
|
||||
TOOL_CONVEX_VOLUME,
|
||||
TOOL_CROWD,
|
||||
MAX_TOOLS
|
||||
};
|
||||
|
||||
/// These are just sample areas to use consistent values across the samples.
|
||||
/// The use should specify these base on his needs.
|
||||
enum SamplePolyAreas
|
||||
{
|
||||
SAMPLE_POLYAREA_GROUND,
|
||||
SAMPLE_POLYAREA_WATER,
|
||||
SAMPLE_POLYAREA_ROAD,
|
||||
SAMPLE_POLYAREA_DOOR,
|
||||
SAMPLE_POLYAREA_GRASS,
|
||||
SAMPLE_POLYAREA_JUMP,
|
||||
};
|
||||
enum SamplePolyFlags
|
||||
{
|
||||
SAMPLE_POLYFLAGS_WALK = 0x01, // Ability to walk (ground, grass, road)
|
||||
SAMPLE_POLYFLAGS_SWIM = 0x02, // Ability to swim (water).
|
||||
SAMPLE_POLYFLAGS_DOOR = 0x04, // Ability to move through doors.
|
||||
SAMPLE_POLYFLAGS_JUMP = 0x08, // Ability to jump.
|
||||
SAMPLE_POLYFLAGS_DISABLED = 0x10, // Disabled polygon
|
||||
SAMPLE_POLYFLAGS_ALL = 0xffff // All abilities.
|
||||
};
|
||||
|
||||
class SampleDebugDraw : public DebugDrawGL
|
||||
{
|
||||
public:
|
||||
virtual unsigned int areaToCol(unsigned int area);
|
||||
};
|
||||
|
||||
enum SamplePartitionType
|
||||
{
|
||||
SAMPLE_PARTITION_WATERSHED,
|
||||
SAMPLE_PARTITION_MONOTONE,
|
||||
SAMPLE_PARTITION_LAYERS,
|
||||
};
|
||||
|
||||
struct SampleTool
|
||||
{
|
||||
virtual ~SampleTool() {}
|
||||
virtual int type() = 0;
|
||||
virtual void init(class Sample* sample) = 0;
|
||||
virtual void reset() = 0;
|
||||
virtual void handleMenu() = 0;
|
||||
virtual void handleClick(const float* s, const float* p, bool shift) = 0;
|
||||
virtual void handleRender() = 0;
|
||||
virtual void handleRenderOverlay(double* proj, double* model, int* view) = 0;
|
||||
virtual void handleToggle() = 0;
|
||||
virtual void handleStep() = 0;
|
||||
virtual void handleUpdate(const float dt) = 0;
|
||||
};
|
||||
|
||||
struct SampleToolState {
|
||||
virtual ~SampleToolState() {}
|
||||
virtual void init(class Sample* sample) = 0;
|
||||
virtual void reset() = 0;
|
||||
virtual void handleRender() = 0;
|
||||
virtual void handleRenderOverlay(double* proj, double* model, int* view) = 0;
|
||||
virtual void handleUpdate(const float dt) = 0;
|
||||
};
|
||||
|
||||
class Sample
|
||||
{
|
||||
protected:
|
||||
class InputGeom* m_geom;
|
||||
class dtNavMesh* m_navMesh;
|
||||
class dtNavMeshQuery* m_navQuery;
|
||||
class dtCrowd* m_crowd;
|
||||
|
||||
unsigned char m_navMeshDrawFlags;
|
||||
|
||||
float m_cellSize;
|
||||
float m_cellHeight;
|
||||
float m_agentHeight;
|
||||
float m_agentRadius;
|
||||
float m_agentMaxClimb;
|
||||
float m_agentMaxSlope;
|
||||
float m_regionMinSize;
|
||||
float m_regionMergeSize;
|
||||
float m_edgeMaxLen;
|
||||
float m_edgeMaxError;
|
||||
float m_vertsPerPoly;
|
||||
float m_detailSampleDist;
|
||||
float m_detailSampleMaxError;
|
||||
int m_partitionType;
|
||||
|
||||
bool m_filterLowHangingObstacles;
|
||||
bool m_filterLedgeSpans;
|
||||
bool m_filterWalkableLowHeightSpans;
|
||||
|
||||
SampleTool* m_tool;
|
||||
SampleToolState* m_toolStates[MAX_TOOLS];
|
||||
|
||||
BuildContext* m_ctx;
|
||||
|
||||
SampleDebugDraw m_dd;
|
||||
|
||||
dtNavMesh* loadAll(const char* path);
|
||||
void saveAll(const char* path, const dtNavMesh* mesh);
|
||||
|
||||
public:
|
||||
Sample();
|
||||
virtual ~Sample();
|
||||
|
||||
void setContext(BuildContext* ctx) { m_ctx = ctx; }
|
||||
|
||||
void setTool(SampleTool* tool);
|
||||
SampleToolState* getToolState(int type) { return m_toolStates[type]; }
|
||||
void setToolState(int type, SampleToolState* s) { m_toolStates[type] = s; }
|
||||
|
||||
SampleDebugDraw& getDebugDraw() { return m_dd; }
|
||||
|
||||
virtual void handleSettings();
|
||||
virtual void handleTools();
|
||||
virtual void handleDebugMode();
|
||||
virtual void handleClick(const float* s, const float* p, bool shift);
|
||||
virtual void handleToggle();
|
||||
virtual void handleStep();
|
||||
virtual void handleRender();
|
||||
virtual void handleRenderOverlay(double* proj, double* model, int* view);
|
||||
virtual void handleMeshChanged(class InputGeom* geom);
|
||||
virtual bool handleBuild();
|
||||
virtual void handleUpdate(const float dt);
|
||||
virtual void collectSettings(struct BuildSettings& settings);
|
||||
|
||||
virtual class InputGeom* getInputGeom() { return m_geom; }
|
||||
virtual class dtNavMesh* getNavMesh() { return m_navMesh; }
|
||||
virtual class dtNavMeshQuery* getNavMeshQuery() { return m_navQuery; }
|
||||
virtual class dtCrowd* getCrowd() { return m_crowd; }
|
||||
virtual float getAgentRadius() { return m_agentRadius; }
|
||||
virtual float getAgentHeight() { return m_agentHeight; }
|
||||
virtual float getAgentClimb() { return m_agentMaxClimb; }
|
||||
|
||||
unsigned char getNavMeshDrawFlags() const { return m_navMeshDrawFlags; }
|
||||
void setNavMeshDrawFlags(unsigned char flags) { m_navMeshDrawFlags = flags; }
|
||||
|
||||
void updateToolStates(const float dt);
|
||||
void initToolStates(Sample* sample);
|
||||
void resetToolStates();
|
||||
void renderToolStates();
|
||||
void renderOverlayToolStates(double* proj, double* model, int* view);
|
||||
|
||||
void resetCommonSettings();
|
||||
void handleCommonSettings();
|
||||
|
||||
private:
|
||||
// Explicitly disabled copy constructor and copy assignment operator.
|
||||
Sample(const Sample&);
|
||||
Sample& operator=(const Sample&);
|
||||
};
|
||||
|
||||
|
||||
#endif // RECASTSAMPLE_H
|
||||
@@ -0,0 +1,99 @@
|
||||
//
|
||||
// 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 SAMPLEINTERFACES_H
|
||||
#define SAMPLEINTERFACES_H
|
||||
|
||||
#include "DebugDraw.h"
|
||||
#include "Recast.h"
|
||||
#include "RecastDump.h"
|
||||
#include "PerfTimer.h"
|
||||
|
||||
// These are example implementations of various interfaces used in Recast and Detour.
|
||||
|
||||
/// Recast build context.
|
||||
class BuildContext : public rcContext
|
||||
{
|
||||
TimeVal m_startTime[RC_MAX_TIMERS];
|
||||
TimeVal m_accTime[RC_MAX_TIMERS];
|
||||
|
||||
static const int MAX_MESSAGES = 1000;
|
||||
const char* m_messages[MAX_MESSAGES];
|
||||
int m_messageCount;
|
||||
static const int TEXT_POOL_SIZE = 8000;
|
||||
char m_textPool[TEXT_POOL_SIZE];
|
||||
int m_textPoolSize;
|
||||
|
||||
public:
|
||||
BuildContext();
|
||||
|
||||
/// Dumps the log to stdout.
|
||||
void dumpLog(const char* format, ...);
|
||||
/// Returns number of log messages.
|
||||
int getLogCount() const;
|
||||
/// Returns log message text.
|
||||
const char* getLogText(const int i) const;
|
||||
|
||||
protected:
|
||||
/// Virtual functions 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;
|
||||
///@}
|
||||
};
|
||||
|
||||
/// OpenGL debug draw implementation.
|
||||
class DebugDrawGL : public duDebugDraw
|
||||
{
|
||||
public:
|
||||
virtual void depthMask(bool state);
|
||||
virtual void texture(bool state);
|
||||
virtual void begin(duDebugDrawPrimitives prim, float size = 1.0f);
|
||||
virtual void vertex(const float* pos, unsigned int color);
|
||||
virtual void vertex(const float x, const float y, const float z, unsigned int color);
|
||||
virtual void vertex(const float* pos, unsigned int color, const float* uv);
|
||||
virtual void vertex(const float x, const float y, const float z, unsigned int color, const float u, const float v);
|
||||
virtual void end();
|
||||
};
|
||||
|
||||
/// stdio file implementation.
|
||||
class FileIO : public duFileIO
|
||||
{
|
||||
FILE* m_fp;
|
||||
int m_mode;
|
||||
public:
|
||||
FileIO();
|
||||
virtual ~FileIO();
|
||||
bool openForWrite(const char* path);
|
||||
bool openForRead(const char* path);
|
||||
virtual bool isWriting() const;
|
||||
virtual bool isReading() const;
|
||||
virtual bool write(const void* ptr, const size_t size);
|
||||
virtual bool read(void* ptr, const size_t size);
|
||||
private:
|
||||
// Explicitly disabled copy constructor and copy assignment operator.
|
||||
FileIO(const FileIO&);
|
||||
FileIO& operator=(const FileIO&);
|
||||
};
|
||||
|
||||
#endif // SAMPLEINTERFACES_H
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
//
|
||||
// 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 RECASTSAMPLEDEBUG_H
|
||||
#define RECASTSAMPLEDEBUG_H
|
||||
|
||||
#include "Sample.h"
|
||||
#include "DetourNavMesh.h"
|
||||
#include "Recast.h"
|
||||
|
||||
/// Sample used for random debugging.
|
||||
class Sample_Debug : public Sample
|
||||
{
|
||||
protected:
|
||||
rcCompactHeightfield* m_chf;
|
||||
rcContourSet* m_cset;
|
||||
rcPolyMesh* m_pmesh;
|
||||
|
||||
float m_halfExtents[3];
|
||||
float m_center[3];
|
||||
float m_bmin[3], m_bmax[3];
|
||||
dtPolyRef m_ref;
|
||||
|
||||
public:
|
||||
Sample_Debug();
|
||||
virtual ~Sample_Debug();
|
||||
|
||||
virtual void handleSettings();
|
||||
virtual void handleTools();
|
||||
virtual void handleDebugMode();
|
||||
virtual void handleClick(const float* s, const float* p, bool shift);
|
||||
virtual void handleToggle();
|
||||
virtual void handleRender();
|
||||
virtual void handleRenderOverlay(double* proj, double* model, int* view);
|
||||
virtual void handleMeshChanged(class InputGeom* geom);
|
||||
virtual bool handleBuild();
|
||||
|
||||
virtual const float* getBoundsMin();
|
||||
virtual const float* getBoundsMax();
|
||||
|
||||
private:
|
||||
// Explicitly disabled copy constructor and copy assignment operator.
|
||||
Sample_Debug(const Sample_Debug&);
|
||||
Sample_Debug& operator=(const Sample_Debug&);
|
||||
};
|
||||
|
||||
|
||||
#endif // RECASTSAMPLE_H
|
||||
@@ -0,0 +1,86 @@
|
||||
//
|
||||
// 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 RECASTSAMPLESOLOMESH_H
|
||||
#define RECASTSAMPLESOLOMESH_H
|
||||
|
||||
#include "Sample.h"
|
||||
#include "DetourNavMesh.h"
|
||||
#include "Recast.h"
|
||||
|
||||
class Sample_SoloMesh : public Sample
|
||||
{
|
||||
protected:
|
||||
bool m_keepInterResults;
|
||||
float m_totalBuildTimeMs;
|
||||
|
||||
unsigned char* m_triareas;
|
||||
rcHeightfield* m_solid;
|
||||
rcCompactHeightfield* m_chf;
|
||||
rcContourSet* m_cset;
|
||||
rcPolyMesh* m_pmesh;
|
||||
rcConfig m_cfg;
|
||||
rcPolyMeshDetail* m_dmesh;
|
||||
|
||||
enum DrawMode
|
||||
{
|
||||
DRAWMODE_NAVMESH,
|
||||
DRAWMODE_NAVMESH_TRANS,
|
||||
DRAWMODE_NAVMESH_BVTREE,
|
||||
DRAWMODE_NAVMESH_NODES,
|
||||
DRAWMODE_NAVMESH_INVIS,
|
||||
DRAWMODE_MESH,
|
||||
DRAWMODE_VOXELS,
|
||||
DRAWMODE_VOXELS_WALKABLE,
|
||||
DRAWMODE_COMPACT,
|
||||
DRAWMODE_COMPACT_DISTANCE,
|
||||
DRAWMODE_COMPACT_REGIONS,
|
||||
DRAWMODE_REGION_CONNECTIONS,
|
||||
DRAWMODE_RAW_CONTOURS,
|
||||
DRAWMODE_BOTH_CONTOURS,
|
||||
DRAWMODE_CONTOURS,
|
||||
DRAWMODE_POLYMESH,
|
||||
DRAWMODE_POLYMESH_DETAIL,
|
||||
MAX_DRAWMODE
|
||||
};
|
||||
|
||||
DrawMode m_drawMode;
|
||||
|
||||
void cleanup();
|
||||
|
||||
public:
|
||||
Sample_SoloMesh();
|
||||
virtual ~Sample_SoloMesh();
|
||||
|
||||
virtual void handleSettings();
|
||||
virtual void handleTools();
|
||||
virtual void handleDebugMode();
|
||||
|
||||
virtual void handleRender();
|
||||
virtual void handleRenderOverlay(double* proj, double* model, int* view);
|
||||
virtual void handleMeshChanged(class InputGeom* geom);
|
||||
virtual bool handleBuild();
|
||||
|
||||
private:
|
||||
// Explicitly disabled copy constructor and copy assignment operator.
|
||||
Sample_SoloMesh(const Sample_SoloMesh&);
|
||||
Sample_SoloMesh& operator=(const Sample_SoloMesh&);
|
||||
};
|
||||
|
||||
|
||||
#endif // RECASTSAMPLESOLOMESHSIMPLE_H
|
||||
@@ -0,0 +1,98 @@
|
||||
//
|
||||
// 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 RECASTSAMPLETEMPOBSTACLE_H
|
||||
#define RECASTSAMPLETEMPOBSTACLE_H
|
||||
|
||||
#include "Sample.h"
|
||||
#include "DetourNavMesh.h"
|
||||
#include "Recast.h"
|
||||
#include "ChunkyTriMesh.h"
|
||||
|
||||
|
||||
class Sample_TempObstacles : public Sample
|
||||
{
|
||||
protected:
|
||||
bool m_keepInterResults;
|
||||
|
||||
struct LinearAllocator* m_talloc;
|
||||
struct FastLZCompressor* m_tcomp;
|
||||
struct MeshProcess* m_tmproc;
|
||||
|
||||
class dtTileCache* m_tileCache;
|
||||
|
||||
float m_cacheBuildTimeMs;
|
||||
int m_cacheCompressedSize;
|
||||
int m_cacheRawSize;
|
||||
int m_cacheLayerCount;
|
||||
unsigned int m_cacheBuildMemUsage;
|
||||
|
||||
enum DrawMode
|
||||
{
|
||||
DRAWMODE_NAVMESH,
|
||||
DRAWMODE_NAVMESH_TRANS,
|
||||
DRAWMODE_NAVMESH_BVTREE,
|
||||
DRAWMODE_NAVMESH_NODES,
|
||||
DRAWMODE_NAVMESH_PORTALS,
|
||||
DRAWMODE_NAVMESH_INVIS,
|
||||
DRAWMODE_MESH,
|
||||
DRAWMODE_CACHE_BOUNDS,
|
||||
MAX_DRAWMODE
|
||||
};
|
||||
|
||||
DrawMode m_drawMode;
|
||||
|
||||
int m_maxTiles;
|
||||
int m_maxPolysPerTile;
|
||||
float m_tileSize;
|
||||
|
||||
public:
|
||||
Sample_TempObstacles();
|
||||
virtual ~Sample_TempObstacles();
|
||||
|
||||
virtual void handleSettings();
|
||||
virtual void handleTools();
|
||||
virtual void handleDebugMode();
|
||||
virtual void handleRender();
|
||||
virtual void handleRenderOverlay(double* proj, double* model, int* view);
|
||||
virtual void handleMeshChanged(class InputGeom* geom);
|
||||
virtual bool handleBuild();
|
||||
virtual void handleUpdate(const float dt);
|
||||
|
||||
void getTilePos(const float* pos, int& tx, int& ty);
|
||||
|
||||
void renderCachedTile(const int tx, const int ty, const int type);
|
||||
void renderCachedTileOverlay(const int tx, const int ty, double* proj, double* model, int* view);
|
||||
|
||||
void addTempObstacle(const float* pos);
|
||||
void removeTempObstacle(const float* sp, const float* sq);
|
||||
void clearAllTempObstacles();
|
||||
|
||||
void saveAll(const char* path);
|
||||
void loadAll(const char* path);
|
||||
|
||||
private:
|
||||
// Explicitly disabled copy constructor and copy assignment operator.
|
||||
Sample_TempObstacles(const Sample_TempObstacles&);
|
||||
Sample_TempObstacles& operator=(const Sample_TempObstacles&);
|
||||
|
||||
int rasterizeTileLayers(const int tx, const int ty, const rcConfig& cfg, struct TileCacheData* tiles, const int maxTiles);
|
||||
};
|
||||
|
||||
|
||||
#endif // RECASTSAMPLETEMPOBSTACLE_H
|
||||
@@ -0,0 +1,112 @@
|
||||
//
|
||||
// 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 RECASTSAMPLETILEMESH_H
|
||||
#define RECASTSAMPLETILEMESH_H
|
||||
|
||||
#include "Sample.h"
|
||||
#include "DetourNavMesh.h"
|
||||
#include "Recast.h"
|
||||
#include "ChunkyTriMesh.h"
|
||||
|
||||
class Sample_TileMesh : public Sample
|
||||
{
|
||||
protected:
|
||||
bool m_keepInterResults;
|
||||
bool m_buildAll;
|
||||
float m_totalBuildTimeMs;
|
||||
|
||||
unsigned char* m_triareas;
|
||||
rcHeightfield* m_solid;
|
||||
rcCompactHeightfield* m_chf;
|
||||
rcContourSet* m_cset;
|
||||
rcPolyMesh* m_pmesh;
|
||||
rcPolyMeshDetail* m_dmesh;
|
||||
rcConfig m_cfg;
|
||||
|
||||
enum DrawMode
|
||||
{
|
||||
DRAWMODE_NAVMESH,
|
||||
DRAWMODE_NAVMESH_TRANS,
|
||||
DRAWMODE_NAVMESH_BVTREE,
|
||||
DRAWMODE_NAVMESH_NODES,
|
||||
DRAWMODE_NAVMESH_PORTALS,
|
||||
DRAWMODE_NAVMESH_INVIS,
|
||||
DRAWMODE_MESH,
|
||||
DRAWMODE_VOXELS,
|
||||
DRAWMODE_VOXELS_WALKABLE,
|
||||
DRAWMODE_COMPACT,
|
||||
DRAWMODE_COMPACT_DISTANCE,
|
||||
DRAWMODE_COMPACT_REGIONS,
|
||||
DRAWMODE_REGION_CONNECTIONS,
|
||||
DRAWMODE_RAW_CONTOURS,
|
||||
DRAWMODE_BOTH_CONTOURS,
|
||||
DRAWMODE_CONTOURS,
|
||||
DRAWMODE_POLYMESH,
|
||||
DRAWMODE_POLYMESH_DETAIL,
|
||||
MAX_DRAWMODE
|
||||
};
|
||||
|
||||
DrawMode m_drawMode;
|
||||
|
||||
int m_maxTiles;
|
||||
int m_maxPolysPerTile;
|
||||
float m_tileSize;
|
||||
|
||||
unsigned int m_tileCol;
|
||||
float m_lastBuiltTileBmin[3];
|
||||
float m_lastBuiltTileBmax[3];
|
||||
float m_tileBuildTime;
|
||||
float m_tileMemUsage;
|
||||
int m_tileTriCount;
|
||||
|
||||
unsigned char* buildTileMesh(const int tx, const int ty, const float* bmin, const float* bmax, int& dataSize);
|
||||
|
||||
void cleanup();
|
||||
|
||||
void saveAll(const char* path, const dtNavMesh* mesh);
|
||||
dtNavMesh* loadAll(const char* path);
|
||||
|
||||
public:
|
||||
Sample_TileMesh();
|
||||
virtual ~Sample_TileMesh();
|
||||
|
||||
virtual void handleSettings();
|
||||
virtual void handleTools();
|
||||
virtual void handleDebugMode();
|
||||
virtual void handleRender();
|
||||
virtual void handleRenderOverlay(double* proj, double* model, int* view);
|
||||
virtual void handleMeshChanged(class InputGeom* geom);
|
||||
virtual bool handleBuild();
|
||||
virtual void collectSettings(struct BuildSettings& settings);
|
||||
|
||||
void getTilePos(const float* pos, int& tx, int& ty);
|
||||
|
||||
void buildTile(const float* pos);
|
||||
void removeTile(const float* pos);
|
||||
void buildAllTiles();
|
||||
void removeAllTiles();
|
||||
|
||||
private:
|
||||
// Explicitly disabled copy constructor and copy assignment operator.
|
||||
Sample_TileMesh(const Sample_TileMesh&);
|
||||
Sample_TileMesh& operator=(const Sample_TileMesh&);
|
||||
};
|
||||
|
||||
|
||||
#endif // RECASTSAMPLETILEMESH_H
|
||||
@@ -0,0 +1,110 @@
|
||||
//
|
||||
// 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 TESTCASE_H
|
||||
#define TESTCASE_H
|
||||
|
||||
#include <string>
|
||||
#include "DetourNavMesh.h"
|
||||
|
||||
class TestCase
|
||||
{
|
||||
enum TestType
|
||||
{
|
||||
TEST_PATHFIND,
|
||||
TEST_RAYCAST,
|
||||
};
|
||||
|
||||
struct Test
|
||||
{
|
||||
Test() :
|
||||
type(),
|
||||
radius(0),
|
||||
includeFlags(0),
|
||||
excludeFlags(0),
|
||||
expand(false),
|
||||
straight(0),
|
||||
nstraight(0),
|
||||
polys(0),
|
||||
npolys(0),
|
||||
findNearestPolyTime(0),
|
||||
findPathTime(0),
|
||||
findStraightPathTime(0),
|
||||
next(0)
|
||||
{
|
||||
}
|
||||
|
||||
~Test()
|
||||
{
|
||||
delete [] straight;
|
||||
delete [] polys;
|
||||
}
|
||||
|
||||
TestType type;
|
||||
float spos[3];
|
||||
float epos[3];
|
||||
float nspos[3];
|
||||
float nepos[3];
|
||||
float radius;
|
||||
unsigned short includeFlags;
|
||||
unsigned short excludeFlags;
|
||||
bool expand;
|
||||
|
||||
float* straight;
|
||||
int nstraight;
|
||||
dtPolyRef* polys;
|
||||
int npolys;
|
||||
|
||||
int findNearestPolyTime;
|
||||
int findPathTime;
|
||||
int findStraightPathTime;
|
||||
|
||||
Test* next;
|
||||
private:
|
||||
// Explicitly disabled copy constructor and copy assignment operator.
|
||||
Test(const Test&);
|
||||
Test& operator=(const Test&);
|
||||
};
|
||||
|
||||
std::string m_sampleName;
|
||||
std::string m_geomFileName;
|
||||
Test* m_tests;
|
||||
|
||||
void resetTimes();
|
||||
|
||||
public:
|
||||
TestCase();
|
||||
~TestCase();
|
||||
|
||||
bool load(const std::string& filePath);
|
||||
|
||||
const std::string& getSampleName() const { return m_sampleName; }
|
||||
const std::string& getGeomFileName() const { return m_geomFileName; }
|
||||
|
||||
void doTests(class dtNavMesh* navmesh, class dtNavMeshQuery* navquery);
|
||||
|
||||
void handleRender();
|
||||
bool handleRenderOverlay(double* proj, double* model, int* view);
|
||||
|
||||
private:
|
||||
// Explicitly disabled copy constructor and copy assignment operator.
|
||||
TestCase(const TestCase&);
|
||||
TestCase& operator=(const TestCase&);
|
||||
};
|
||||
|
||||
#endif // TESTCASE_H
|
||||
@@ -0,0 +1,50 @@
|
||||
#ifndef VALUEHISTORY_H
|
||||
#define VALUEHISTORY_H
|
||||
|
||||
class ValueHistory
|
||||
{
|
||||
static const int MAX_HISTORY = 256;
|
||||
float m_samples[MAX_HISTORY];
|
||||
int m_hsamples;
|
||||
public:
|
||||
ValueHistory();
|
||||
|
||||
inline void addSample(const float val)
|
||||
{
|
||||
m_hsamples = (m_hsamples+MAX_HISTORY-1) % MAX_HISTORY;
|
||||
m_samples[m_hsamples] = val;
|
||||
}
|
||||
|
||||
inline int getSampleCount() const
|
||||
{
|
||||
return MAX_HISTORY;
|
||||
}
|
||||
|
||||
inline float getSample(const int i) const
|
||||
{
|
||||
return m_samples[(m_hsamples+i) % MAX_HISTORY];
|
||||
}
|
||||
|
||||
float getSampleMin() const;
|
||||
float getSampleMax() const;
|
||||
float getAverage() const;
|
||||
};
|
||||
|
||||
struct GraphParams
|
||||
{
|
||||
void setRect(int ix, int iy, int iw, int ih, int ipad);
|
||||
void setValueRange(float ivmin, float ivmax, int indiv, const char* iunits);
|
||||
|
||||
int x, y, w, h, pad;
|
||||
float vmin, vmax;
|
||||
int ndiv;
|
||||
char units[16];
|
||||
};
|
||||
|
||||
void drawGraphBackground(const GraphParams* p);
|
||||
|
||||
void drawGraph(const GraphParams* p, const ValueHistory* graph,
|
||||
int idx, const char* label, const unsigned int col);
|
||||
|
||||
|
||||
#endif // VALUEHISTORY_H
|
||||
@@ -0,0 +1,108 @@
|
||||
//
|
||||
// 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 IMGUI_H
|
||||
#define IMGUI_H
|
||||
|
||||
enum imguiMouseButton
|
||||
{
|
||||
IMGUI_MBUT_LEFT = 0x01,
|
||||
IMGUI_MBUT_RIGHT = 0x02,
|
||||
};
|
||||
|
||||
enum imguiTextAlign
|
||||
{
|
||||
IMGUI_ALIGN_LEFT,
|
||||
IMGUI_ALIGN_CENTER,
|
||||
IMGUI_ALIGN_RIGHT,
|
||||
};
|
||||
|
||||
inline unsigned int imguiRGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a=255)
|
||||
{
|
||||
return (r) | (g << 8) | (b << 16) | (a << 24);
|
||||
}
|
||||
|
||||
void imguiBeginFrame(int mx, int my, unsigned char mbut, int scroll);
|
||||
void imguiEndFrame();
|
||||
|
||||
bool imguiBeginScrollArea(const char* name, int x, int y, int w, int h, int* scroll);
|
||||
void imguiEndScrollArea();
|
||||
|
||||
void imguiIndent();
|
||||
void imguiUnindent();
|
||||
void imguiSeparator();
|
||||
void imguiSeparatorLine();
|
||||
|
||||
bool imguiButton(const char* text, bool enabled = true);
|
||||
bool imguiItem(const char* text, bool enabled = true);
|
||||
bool imguiCheck(const char* text, bool checked, bool enabled = true);
|
||||
bool imguiCollapse(const char* text, const char* subtext, bool checked, bool enabled = true);
|
||||
void imguiLabel(const char* text);
|
||||
void imguiValue(const char* text);
|
||||
bool imguiSlider(const char* text, float* val, float vmin, float vmax, float vinc, bool enabled = true);
|
||||
|
||||
void imguiDrawText(int x, int y, int align, const char* text, unsigned int color);
|
||||
void imguiDrawLine(float x0, float y0, float x1, float y1, float r, unsigned int color);
|
||||
void imguiDrawRoundedRect(float x, float y, float w, float h, float r, unsigned int color);
|
||||
void imguiDrawRect(float x, float y, float w, float h, unsigned int color);
|
||||
|
||||
// Pull render interface.
|
||||
enum imguiGfxCmdType
|
||||
{
|
||||
IMGUI_GFXCMD_RECT,
|
||||
IMGUI_GFXCMD_TRIANGLE,
|
||||
IMGUI_GFXCMD_LINE,
|
||||
IMGUI_GFXCMD_TEXT,
|
||||
IMGUI_GFXCMD_SCISSOR,
|
||||
};
|
||||
|
||||
struct imguiGfxRect
|
||||
{
|
||||
short x,y,w,h,r;
|
||||
};
|
||||
|
||||
struct imguiGfxText
|
||||
{
|
||||
short x,y,align;
|
||||
const char* text;
|
||||
};
|
||||
|
||||
struct imguiGfxLine
|
||||
{
|
||||
short x0,y0,x1,y1,r;
|
||||
};
|
||||
|
||||
struct imguiGfxCmd
|
||||
{
|
||||
char type;
|
||||
char flags;
|
||||
char pad[2];
|
||||
unsigned int col;
|
||||
union
|
||||
{
|
||||
imguiGfxLine line;
|
||||
imguiGfxRect rect;
|
||||
imguiGfxText text;
|
||||
};
|
||||
};
|
||||
|
||||
const imguiGfxCmd* imguiGetRenderQueue();
|
||||
int imguiGetRenderQueueSize();
|
||||
|
||||
|
||||
#endif // IMGUI_H
|
||||
@@ -0,0 +1,26 @@
|
||||
//
|
||||
// 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 IMGUI_RENDER_GL_H
|
||||
#define IMGUI_RENDER_GL_H
|
||||
|
||||
bool imguiRenderGLInit(const char* fontpath);
|
||||
void imguiRenderGLDestroy();
|
||||
void imguiRenderGLDraw();
|
||||
|
||||
#endif // IMGUI_RENDER_GL_H
|
||||
@@ -0,0 +1,315 @@
|
||||
//
|
||||
// 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 "ChunkyTriMesh.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
|
||||
struct BoundsItem
|
||||
{
|
||||
float bmin[2];
|
||||
float bmax[2];
|
||||
int i;
|
||||
};
|
||||
|
||||
static int compareItemX(const void* va, const void* vb)
|
||||
{
|
||||
const BoundsItem* a = (const BoundsItem*)va;
|
||||
const BoundsItem* b = (const BoundsItem*)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 BoundsItem* a = (const BoundsItem*)va;
|
||||
const BoundsItem* b = (const BoundsItem*)vb;
|
||||
if (a->bmin[1] < b->bmin[1])
|
||||
return -1;
|
||||
if (a->bmin[1] > b->bmin[1])
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void calcExtends(const BoundsItem* items, const int /*nitems*/,
|
||||
const int imin, const int imax,
|
||||
float* bmin, float* bmax)
|
||||
{
|
||||
bmin[0] = items[imin].bmin[0];
|
||||
bmin[1] = items[imin].bmin[1];
|
||||
|
||||
bmax[0] = items[imin].bmax[0];
|
||||
bmax[1] = items[imin].bmax[1];
|
||||
|
||||
for (int i = imin+1; i < imax; ++i)
|
||||
{
|
||||
const BoundsItem& 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.bmax[0] > bmax[0]) bmax[0] = it.bmax[0];
|
||||
if (it.bmax[1] > bmax[1]) bmax[1] = it.bmax[1];
|
||||
}
|
||||
}
|
||||
|
||||
inline int longestAxis(float x, float y)
|
||||
{
|
||||
return y > x ? 1 : 0;
|
||||
}
|
||||
|
||||
static void subdivide(BoundsItem* items, int nitems, int imin, int imax, int trisPerChunk,
|
||||
int& curNode, rcChunkyTriMeshNode* nodes, const int maxNodes,
|
||||
int& curTri, int* outTris, const int* inTris)
|
||||
{
|
||||
int inum = imax - imin;
|
||||
int icur = curNode;
|
||||
|
||||
if (curNode > maxNodes)
|
||||
return;
|
||||
|
||||
rcChunkyTriMeshNode& node = nodes[curNode++];
|
||||
|
||||
if (inum <= trisPerChunk)
|
||||
{
|
||||
// Leaf
|
||||
calcExtends(items, nitems, imin, imax, node.bmin, node.bmax);
|
||||
|
||||
// Copy triangles.
|
||||
node.i = curTri;
|
||||
node.n = inum;
|
||||
|
||||
for (int i = imin; i < imax; ++i)
|
||||
{
|
||||
const int* src = &inTris[items[i].i*3];
|
||||
int* dst = &outTris[curTri*3];
|
||||
curTri++;
|
||||
dst[0] = src[0];
|
||||
dst[1] = src[1];
|
||||
dst[2] = src[2];
|
||||
}
|
||||
}
|
||||
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]);
|
||||
|
||||
if (axis == 0)
|
||||
{
|
||||
// Sort along x-axis
|
||||
qsort(items+imin, static_cast<size_t>(inum), sizeof(BoundsItem), compareItemX);
|
||||
}
|
||||
else if (axis == 1)
|
||||
{
|
||||
// Sort along y-axis
|
||||
qsort(items+imin, static_cast<size_t>(inum), sizeof(BoundsItem), compareItemY);
|
||||
}
|
||||
|
||||
int isplit = imin+inum/2;
|
||||
|
||||
// Left
|
||||
subdivide(items, nitems, imin, isplit, trisPerChunk, curNode, nodes, maxNodes, curTri, outTris, inTris);
|
||||
// Right
|
||||
subdivide(items, nitems, isplit, imax, trisPerChunk, curNode, nodes, maxNodes, curTri, outTris, inTris);
|
||||
|
||||
int iescape = curNode - icur;
|
||||
// Negative index means escape.
|
||||
node.i = -iescape;
|
||||
}
|
||||
}
|
||||
|
||||
bool rcCreateChunkyTriMesh(const float* verts, const int* tris, int ntris,
|
||||
int trisPerChunk, rcChunkyTriMesh* cm)
|
||||
{
|
||||
int nchunks = (ntris + trisPerChunk-1) / trisPerChunk;
|
||||
|
||||
cm->nodes = new rcChunkyTriMeshNode[nchunks*4];
|
||||
if (!cm->nodes)
|
||||
return false;
|
||||
|
||||
cm->tris = new int[ntris*3];
|
||||
if (!cm->tris)
|
||||
return false;
|
||||
|
||||
cm->ntris = ntris;
|
||||
|
||||
// Build tree
|
||||
BoundsItem* items = new BoundsItem[ntris];
|
||||
if (!items)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < ntris; i++)
|
||||
{
|
||||
const int* t = &tris[i*3];
|
||||
BoundsItem& it = items[i];
|
||||
it.i = i;
|
||||
// Calc triangle XZ bounds.
|
||||
it.bmin[0] = it.bmax[0] = verts[t[0]*3+0];
|
||||
it.bmin[1] = it.bmax[1] = verts[t[0]*3+2];
|
||||
for (int j = 1; j < 3; ++j)
|
||||
{
|
||||
const float* v = &verts[t[j]*3];
|
||||
if (v[0] < it.bmin[0]) it.bmin[0] = v[0];
|
||||
if (v[2] < it.bmin[1]) it.bmin[1] = v[2];
|
||||
|
||||
if (v[0] > it.bmax[0]) it.bmax[0] = v[0];
|
||||
if (v[2] > it.bmax[1]) it.bmax[1] = v[2];
|
||||
}
|
||||
}
|
||||
|
||||
int curTri = 0;
|
||||
int curNode = 0;
|
||||
subdivide(items, ntris, 0, ntris, trisPerChunk, curNode, cm->nodes, nchunks*4, curTri, cm->tris, tris);
|
||||
|
||||
delete [] items;
|
||||
|
||||
cm->nnodes = curNode;
|
||||
|
||||
// Calc max tris per node.
|
||||
cm->maxTrisPerChunk = 0;
|
||||
for (int i = 0; i < cm->nnodes; ++i)
|
||||
{
|
||||
rcChunkyTriMeshNode& node = cm->nodes[i];
|
||||
const bool isLeaf = node.i >= 0;
|
||||
if (!isLeaf) continue;
|
||||
if (node.n > cm->maxTrisPerChunk)
|
||||
cm->maxTrisPerChunk = node.n;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
inline bool checkOverlapRect(const float amin[2], const float amax[2],
|
||||
const float bmin[2], const float bmax[2])
|
||||
{
|
||||
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;
|
||||
return overlap;
|
||||
}
|
||||
|
||||
int rcGetChunksOverlappingRect(const rcChunkyTriMesh* cm,
|
||||
float bmin[2], float bmax[2],
|
||||
int* ids, const int maxIds)
|
||||
{
|
||||
// Traverse tree
|
||||
int i = 0;
|
||||
int n = 0;
|
||||
while (i < cm->nnodes)
|
||||
{
|
||||
const rcChunkyTriMeshNode* node = &cm->nodes[i];
|
||||
const bool overlap = checkOverlapRect(bmin, bmax, node->bmin, node->bmax);
|
||||
const bool isLeafNode = node->i >= 0;
|
||||
|
||||
if (isLeafNode && overlap)
|
||||
{
|
||||
if (n < maxIds)
|
||||
{
|
||||
ids[n] = i;
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
||||
if (overlap || isLeafNode)
|
||||
i++;
|
||||
else
|
||||
{
|
||||
const int escapeIndex = -node->i;
|
||||
i += escapeIndex;
|
||||
}
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static bool checkOverlapSegment(const float p[2], const float q[2],
|
||||
const float bmin[2], const float bmax[2])
|
||||
{
|
||||
static const float EPSILON = 1e-6f;
|
||||
|
||||
float tmin = 0;
|
||||
float tmax = 1;
|
||||
float d[2];
|
||||
d[0] = q[0] - p[0];
|
||||
d[1] = q[1] - p[1];
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
if (fabsf(d[i]) < EPSILON)
|
||||
{
|
||||
// Ray is parallel to slab. No hit if origin not within slab
|
||||
if (p[i] < bmin[i] || p[i] > bmax[i])
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Compute intersection t value of ray with near and far plane of slab
|
||||
float ood = 1.0f / d[i];
|
||||
float t1 = (bmin[i] - p[i]) * ood;
|
||||
float t2 = (bmax[i] - p[i]) * ood;
|
||||
if (t1 > t2) { float tmp = t1; t1 = t2; t2 = tmp; }
|
||||
if (t1 > tmin) tmin = t1;
|
||||
if (t2 < tmax) tmax = t2;
|
||||
if (tmin > tmax) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int rcGetChunksOverlappingSegment(const rcChunkyTriMesh* cm,
|
||||
float p[2], float q[2],
|
||||
int* ids, const int maxIds)
|
||||
{
|
||||
// Traverse tree
|
||||
int i = 0;
|
||||
int n = 0;
|
||||
while (i < cm->nnodes)
|
||||
{
|
||||
const rcChunkyTriMeshNode* node = &cm->nodes[i];
|
||||
const bool overlap = checkOverlapSegment(p, q, node->bmin, node->bmax);
|
||||
const bool isLeafNode = node->i >= 0;
|
||||
|
||||
if (isLeafNode && overlap)
|
||||
{
|
||||
if (n < maxIds)
|
||||
{
|
||||
ids[n] = i;
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
||||
if (overlap || isLeafNode)
|
||||
i++;
|
||||
else
|
||||
{
|
||||
const int escapeIndex = -node->i;
|
||||
i += escapeIndex;
|
||||
}
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
@@ -0,0 +1,297 @@
|
||||
//
|
||||
// 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 <string.h>
|
||||
#include <float.h>
|
||||
#include "SDL.h"
|
||||
#include "SDL_opengl.h"
|
||||
#include "imgui.h"
|
||||
#include "ConvexVolumeTool.h"
|
||||
#include "InputGeom.h"
|
||||
#include "Sample.h"
|
||||
#include "Recast.h"
|
||||
#include "RecastDebugDraw.h"
|
||||
#include "DetourDebugDraw.h"
|
||||
|
||||
#ifdef WIN32
|
||||
# define snprintf _snprintf
|
||||
#endif
|
||||
|
||||
// Quick and dirty convex hull.
|
||||
|
||||
// Returns true if 'c' is left of line 'a'-'b'.
|
||||
inline bool left(const float* a, const float* b, const float* c)
|
||||
{
|
||||
const float u1 = b[0] - a[0];
|
||||
const float v1 = b[2] - a[2];
|
||||
const float u2 = c[0] - a[0];
|
||||
const float v2 = c[2] - a[2];
|
||||
return u1 * v2 - v1 * u2 < 0;
|
||||
}
|
||||
|
||||
// Returns true if 'a' is more lower-left than 'b'.
|
||||
inline bool cmppt(const float* a, const float* b)
|
||||
{
|
||||
if (a[0] < b[0]) return true;
|
||||
if (a[0] > b[0]) return false;
|
||||
if (a[2] < b[2]) return true;
|
||||
if (a[2] > b[2]) return false;
|
||||
return false;
|
||||
}
|
||||
// Calculates convex hull on xz-plane of points on 'pts',
|
||||
// stores the indices of the resulting hull in 'out' and
|
||||
// returns number of points on hull.
|
||||
static int convexhull(const float* pts, int npts, int* out)
|
||||
{
|
||||
// Find lower-leftmost point.
|
||||
int hull = 0;
|
||||
for (int i = 1; i < npts; ++i)
|
||||
if (cmppt(&pts[i*3], &pts[hull*3]))
|
||||
hull = i;
|
||||
// Gift wrap hull.
|
||||
int endpt = 0;
|
||||
int i = 0;
|
||||
do
|
||||
{
|
||||
out[i++] = hull;
|
||||
endpt = 0;
|
||||
for (int j = 1; j < npts; ++j)
|
||||
if (hull == endpt || left(&pts[hull*3], &pts[endpt*3], &pts[j*3]))
|
||||
endpt = j;
|
||||
hull = endpt;
|
||||
}
|
||||
while (endpt != out[0]);
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
ConvexVolumeTool::ConvexVolumeTool() :
|
||||
m_sample(0),
|
||||
m_areaType(SAMPLE_POLYAREA_GRASS),
|
||||
m_polyOffset(0.0f),
|
||||
m_boxHeight(6.0f),
|
||||
m_boxDescent(1.0f),
|
||||
m_npts(0),
|
||||
m_nhull(0)
|
||||
{
|
||||
}
|
||||
|
||||
void ConvexVolumeTool::init(Sample* sample)
|
||||
{
|
||||
m_sample = sample;
|
||||
}
|
||||
|
||||
void ConvexVolumeTool::reset()
|
||||
{
|
||||
m_npts = 0;
|
||||
m_nhull = 0;
|
||||
}
|
||||
|
||||
void ConvexVolumeTool::handleMenu()
|
||||
{
|
||||
imguiSlider("Shape Height", &m_boxHeight, 0.1f, 20.0f, 0.1f);
|
||||
imguiSlider("Shape Descent", &m_boxDescent, 0.1f, 20.0f, 0.1f);
|
||||
imguiSlider("Poly Offset", &m_polyOffset, 0.0f, 10.0f, 0.1f);
|
||||
|
||||
imguiSeparator();
|
||||
|
||||
imguiLabel("Area Type");
|
||||
imguiIndent();
|
||||
if (imguiCheck("Ground", m_areaType == SAMPLE_POLYAREA_GROUND))
|
||||
m_areaType = SAMPLE_POLYAREA_GROUND;
|
||||
if (imguiCheck("Water", m_areaType == SAMPLE_POLYAREA_WATER))
|
||||
m_areaType = SAMPLE_POLYAREA_WATER;
|
||||
if (imguiCheck("Road", m_areaType == SAMPLE_POLYAREA_ROAD))
|
||||
m_areaType = SAMPLE_POLYAREA_ROAD;
|
||||
if (imguiCheck("Door", m_areaType == SAMPLE_POLYAREA_DOOR))
|
||||
m_areaType = SAMPLE_POLYAREA_DOOR;
|
||||
if (imguiCheck("Grass", m_areaType == SAMPLE_POLYAREA_GRASS))
|
||||
m_areaType = SAMPLE_POLYAREA_GRASS;
|
||||
if (imguiCheck("Jump", m_areaType == SAMPLE_POLYAREA_JUMP))
|
||||
m_areaType = SAMPLE_POLYAREA_JUMP;
|
||||
imguiUnindent();
|
||||
|
||||
imguiSeparator();
|
||||
|
||||
if (imguiButton("Clear Shape"))
|
||||
{
|
||||
m_npts = 0;
|
||||
m_nhull = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ConvexVolumeTool::handleClick(const float* /*s*/, const float* p, bool shift)
|
||||
{
|
||||
if (!m_sample) return;
|
||||
InputGeom* geom = m_sample->getInputGeom();
|
||||
if (!geom) return;
|
||||
|
||||
if (shift)
|
||||
{
|
||||
// Delete
|
||||
int nearestIndex = -1;
|
||||
const ConvexVolume* vols = geom->getConvexVolumes();
|
||||
for (int i = 0; i < geom->getConvexVolumeCount(); ++i)
|
||||
{
|
||||
if (pointInPoly(vols[i].nverts, vols[i].verts, p) &&
|
||||
p[1] >= vols[i].hmin && p[1] <= vols[i].hmax)
|
||||
{
|
||||
nearestIndex = i;
|
||||
}
|
||||
}
|
||||
// If end point close enough, delete it.
|
||||
if (nearestIndex != -1)
|
||||
{
|
||||
geom->deleteConvexVolume(nearestIndex);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create
|
||||
|
||||
// If clicked on that last pt, create the shape.
|
||||
if (m_npts && rcVdistSqr(p, &m_pts[(m_npts-1)*3]) < rcSqr(0.2f))
|
||||
{
|
||||
if (m_nhull > 2)
|
||||
{
|
||||
// Create shape.
|
||||
float verts[MAX_PTS*3];
|
||||
for (int i = 0; i < m_nhull; ++i)
|
||||
rcVcopy(&verts[i*3], &m_pts[m_hull[i]*3]);
|
||||
|
||||
float minh = FLT_MAX, maxh = 0;
|
||||
for (int i = 0; i < m_nhull; ++i)
|
||||
minh = rcMin(minh, verts[i*3+1]);
|
||||
minh -= m_boxDescent;
|
||||
maxh = minh + m_boxHeight;
|
||||
|
||||
if (m_polyOffset > 0.01f)
|
||||
{
|
||||
float offset[MAX_PTS*2*3];
|
||||
int noffset = rcOffsetPoly(verts, m_nhull, m_polyOffset, offset, MAX_PTS*2);
|
||||
if (noffset > 0)
|
||||
geom->addConvexVolume(offset, noffset, minh, maxh, (unsigned char)m_areaType);
|
||||
}
|
||||
else
|
||||
{
|
||||
geom->addConvexVolume(verts, m_nhull, minh, maxh, (unsigned char)m_areaType);
|
||||
}
|
||||
}
|
||||
|
||||
m_npts = 0;
|
||||
m_nhull = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add new point
|
||||
if (m_npts < MAX_PTS)
|
||||
{
|
||||
rcVcopy(&m_pts[m_npts*3], p);
|
||||
m_npts++;
|
||||
// Update hull.
|
||||
if (m_npts > 1)
|
||||
m_nhull = convexhull(m_pts, m_npts, m_hull);
|
||||
else
|
||||
m_nhull = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ConvexVolumeTool::handleToggle()
|
||||
{
|
||||
}
|
||||
|
||||
void ConvexVolumeTool::handleStep()
|
||||
{
|
||||
}
|
||||
|
||||
void ConvexVolumeTool::handleUpdate(const float /*dt*/)
|
||||
{
|
||||
}
|
||||
|
||||
void ConvexVolumeTool::handleRender()
|
||||
{
|
||||
duDebugDraw& dd = m_sample->getDebugDraw();
|
||||
|
||||
// Find height extent of the shape.
|
||||
float minh = FLT_MAX, maxh = 0;
|
||||
for (int i = 0; i < m_npts; ++i)
|
||||
minh = rcMin(minh, m_pts[i*3+1]);
|
||||
minh -= m_boxDescent;
|
||||
maxh = minh + m_boxHeight;
|
||||
|
||||
dd.begin(DU_DRAW_POINTS, 4.0f);
|
||||
for (int i = 0; i < m_npts; ++i)
|
||||
{
|
||||
unsigned int col = duRGBA(255,255,255,255);
|
||||
if (i == m_npts-1)
|
||||
col = duRGBA(240,32,16,255);
|
||||
dd.vertex(m_pts[i*3+0],m_pts[i*3+1]+0.1f,m_pts[i*3+2], col);
|
||||
}
|
||||
dd.end();
|
||||
|
||||
dd.begin(DU_DRAW_LINES, 2.0f);
|
||||
for (int i = 0, j = m_nhull-1; i < m_nhull; j = i++)
|
||||
{
|
||||
const float* vi = &m_pts[m_hull[j]*3];
|
||||
const float* vj = &m_pts[m_hull[i]*3];
|
||||
dd.vertex(vj[0],minh,vj[2], duRGBA(255,255,255,64));
|
||||
dd.vertex(vi[0],minh,vi[2], duRGBA(255,255,255,64));
|
||||
dd.vertex(vj[0],maxh,vj[2], duRGBA(255,255,255,64));
|
||||
dd.vertex(vi[0],maxh,vi[2], duRGBA(255,255,255,64));
|
||||
dd.vertex(vj[0],minh,vj[2], duRGBA(255,255,255,64));
|
||||
dd.vertex(vj[0],maxh,vj[2], duRGBA(255,255,255,64));
|
||||
}
|
||||
dd.end();
|
||||
}
|
||||
|
||||
void ConvexVolumeTool::handleRenderOverlay(double* /*proj*/, double* /*model*/, int* view)
|
||||
{
|
||||
// Tool help
|
||||
const int h = view[3];
|
||||
if (!m_npts)
|
||||
{
|
||||
imguiDrawText(280, h-40, IMGUI_ALIGN_LEFT, "LMB: Create new shape. SHIFT+LMB: Delete existing shape (click inside a shape).", imguiRGBA(255,255,255,192));
|
||||
}
|
||||
else
|
||||
{
|
||||
imguiDrawText(280, h-40, IMGUI_ALIGN_LEFT, "Click LMB to add new points. Click on the red point to finish the shape.", imguiRGBA(255,255,255,192));
|
||||
imguiDrawText(280, h-60, IMGUI_ALIGN_LEFT, "The shape will be convex hull of all added points.", imguiRGBA(255,255,255,192));
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,78 @@
|
||||
//
|
||||
// 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 "Filelist.h"
|
||||
|
||||
#include <algorithm>
|
||||
#ifdef WIN32
|
||||
# include <io.h>
|
||||
#else
|
||||
# include <dirent.h>
|
||||
# include <cstring>
|
||||
#endif
|
||||
|
||||
using std::vector;
|
||||
using std::string;
|
||||
|
||||
void scanDirectoryAppend(const string& path, const string& ext, vector<string>& filelist)
|
||||
{
|
||||
#ifdef WIN32
|
||||
string pathWithExt = path + "/*" + ext;
|
||||
|
||||
_finddata_t dir;
|
||||
intptr_t fh = _findfirst(pathWithExt.c_str(), &dir);
|
||||
if (fh == -1L)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
filelist.push_back(dir.name);
|
||||
}
|
||||
while (_findnext(fh, &dir) == 0);
|
||||
_findclose(fh);
|
||||
#else
|
||||
dirent* current = 0;
|
||||
DIR* dp = opendir(path.c_str());
|
||||
if (!dp)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int extLen = strlen(ext.c_str());
|
||||
while ((current = readdir(dp)) != 0)
|
||||
{
|
||||
int len = strlen(current->d_name);
|
||||
if (len > extLen && strncmp(current->d_name + len - extLen, ext.c_str(), extLen) == 0)
|
||||
{
|
||||
filelist.push_back(current->d_name);
|
||||
}
|
||||
}
|
||||
closedir(dp);
|
||||
#endif
|
||||
|
||||
// Sort the list of files alphabetically.
|
||||
std::sort(filelist.begin(), filelist.end());
|
||||
}
|
||||
|
||||
void scanDirectory(const string& path, const string& ext, vector<string>& filelist)
|
||||
{
|
||||
filelist.clear();
|
||||
scanDirectoryAppend(path, ext, filelist);
|
||||
}
|
||||
@@ -0,0 +1,617 @@
|
||||
//
|
||||
// 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 <ctype.h>
|
||||
#include <string.h>
|
||||
#include <algorithm>
|
||||
#include "Recast.h"
|
||||
#include "InputGeom.h"
|
||||
#include "ChunkyTriMesh.h"
|
||||
#include "MeshLoaderObj.h"
|
||||
#include "DebugDraw.h"
|
||||
#include "RecastDebugDraw.h"
|
||||
#include "DetourNavMesh.h"
|
||||
#include "Sample.h"
|
||||
|
||||
static bool intersectSegmentTriangle(const float* sp, const float* sq,
|
||||
const float* a, const float* b, const float* c,
|
||||
float &t)
|
||||
{
|
||||
float v, w;
|
||||
float ab[3], ac[3], qp[3], ap[3], norm[3], e[3];
|
||||
rcVsub(ab, b, a);
|
||||
rcVsub(ac, c, a);
|
||||
rcVsub(qp, sp, sq);
|
||||
|
||||
// Compute triangle normal. Can be precalculated or cached if
|
||||
// intersecting multiple segments against the same triangle
|
||||
rcVcross(norm, ab, ac);
|
||||
|
||||
// Compute denominator d. If d <= 0, segment is parallel to or points
|
||||
// away from triangle, so exit early
|
||||
float d = rcVdot(qp, norm);
|
||||
if (d <= 0.0f) return false;
|
||||
|
||||
// Compute intersection t value of pq with plane of triangle. A ray
|
||||
// intersects iff 0 <= t. Segment intersects iff 0 <= t <= 1. Delay
|
||||
// dividing by d until intersection has been found to pierce triangle
|
||||
rcVsub(ap, sp, a);
|
||||
t = rcVdot(ap, norm);
|
||||
if (t < 0.0f) return false;
|
||||
if (t > d) return false; // For segment; exclude this code line for a ray test
|
||||
|
||||
// Compute barycentric coordinate components and test if within bounds
|
||||
rcVcross(e, qp, ap);
|
||||
v = rcVdot(ac, e);
|
||||
if (v < 0.0f || v > d) return false;
|
||||
w = -rcVdot(ab, e);
|
||||
if (w < 0.0f || v + w > d) return false;
|
||||
|
||||
// Segment/ray intersects triangle. Perform delayed division
|
||||
t /= d;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static char* parseRow(char* buf, char* bufEnd, char* row, int len)
|
||||
{
|
||||
bool start = true;
|
||||
bool done = false;
|
||||
int n = 0;
|
||||
while (!done && buf < bufEnd)
|
||||
{
|
||||
char c = *buf;
|
||||
buf++;
|
||||
// multirow
|
||||
switch (c)
|
||||
{
|
||||
case '\n':
|
||||
if (start) break;
|
||||
done = true;
|
||||
break;
|
||||
case '\r':
|
||||
break;
|
||||
case '\t':
|
||||
case ' ':
|
||||
if (start) break;
|
||||
// else falls through
|
||||
default:
|
||||
start = false;
|
||||
row[n++] = c;
|
||||
if (n >= len-1)
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
row[n] = '\0';
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
||||
|
||||
InputGeom::InputGeom() :
|
||||
m_chunkyMesh(0),
|
||||
m_mesh(0),
|
||||
m_hasBuildSettings(false),
|
||||
m_offMeshConCount(0),
|
||||
m_volumeCount(0)
|
||||
{
|
||||
}
|
||||
|
||||
InputGeom::~InputGeom()
|
||||
{
|
||||
delete m_chunkyMesh;
|
||||
delete m_mesh;
|
||||
}
|
||||
|
||||
bool InputGeom::loadMesh(rcContext* ctx, const std::string& filepath)
|
||||
{
|
||||
if (m_mesh)
|
||||
{
|
||||
delete m_chunkyMesh;
|
||||
m_chunkyMesh = 0;
|
||||
delete m_mesh;
|
||||
m_mesh = 0;
|
||||
}
|
||||
m_offMeshConCount = 0;
|
||||
m_volumeCount = 0;
|
||||
|
||||
m_mesh = new rcMeshLoaderObj;
|
||||
if (!m_mesh)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "loadMesh: Out of memory 'm_mesh'.");
|
||||
return false;
|
||||
}
|
||||
if (!m_mesh->load(filepath))
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "buildTiledNavigation: Could not load '%s'", filepath.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
rcCalcBounds(m_mesh->getVerts(), m_mesh->getVertCount(), m_meshBMin, m_meshBMax);
|
||||
|
||||
m_chunkyMesh = new rcChunkyTriMesh;
|
||||
if (!m_chunkyMesh)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "buildTiledNavigation: Out of memory 'm_chunkyMesh'.");
|
||||
return false;
|
||||
}
|
||||
if (!rcCreateChunkyTriMesh(m_mesh->getVerts(), m_mesh->getTris(), m_mesh->getTriCount(), 256, m_chunkyMesh))
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "buildTiledNavigation: Failed to build chunky mesh.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InputGeom::loadGeomSet(rcContext* ctx, const std::string& filepath)
|
||||
{
|
||||
char* buf = 0;
|
||||
FILE* fp = fopen(filepath.c_str(), "rb");
|
||||
if (!fp)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (fseek(fp, 0, SEEK_END) != 0)
|
||||
{
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
|
||||
long bufSize = ftell(fp);
|
||||
if (bufSize < 0)
|
||||
{
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
if (fseek(fp, 0, SEEK_SET) != 0)
|
||||
{
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
buf = new char[bufSize];
|
||||
if (!buf)
|
||||
{
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
size_t readLen = fread(buf, bufSize, 1, fp);
|
||||
fclose(fp);
|
||||
if (readLen != 1)
|
||||
{
|
||||
delete[] buf;
|
||||
return false;
|
||||
}
|
||||
|
||||
m_offMeshConCount = 0;
|
||||
m_volumeCount = 0;
|
||||
delete m_mesh;
|
||||
m_mesh = 0;
|
||||
|
||||
char* src = buf;
|
||||
char* srcEnd = buf + bufSize;
|
||||
char row[512];
|
||||
while (src < srcEnd)
|
||||
{
|
||||
// Parse one row
|
||||
row[0] = '\0';
|
||||
src = parseRow(src, srcEnd, row, sizeof(row)/sizeof(char));
|
||||
if (row[0] == 'f')
|
||||
{
|
||||
// File name.
|
||||
const char* name = row+1;
|
||||
// Skip white spaces
|
||||
while (*name && isspace(*name))
|
||||
name++;
|
||||
if (*name)
|
||||
{
|
||||
if (!loadMesh(ctx, name))
|
||||
{
|
||||
delete [] buf;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (row[0] == 'c')
|
||||
{
|
||||
// Off-mesh connection
|
||||
if (m_offMeshConCount < MAX_OFFMESH_CONNECTIONS)
|
||||
{
|
||||
float* v = &m_offMeshConVerts[m_offMeshConCount*3*2];
|
||||
int bidir, area = 0, flags = 0;
|
||||
float rad;
|
||||
sscanf(row+1, "%f %f %f %f %f %f %f %d %d %d",
|
||||
&v[0], &v[1], &v[2], &v[3], &v[4], &v[5], &rad, &bidir, &area, &flags);
|
||||
m_offMeshConRads[m_offMeshConCount] = rad;
|
||||
m_offMeshConDirs[m_offMeshConCount] = (unsigned char)bidir;
|
||||
m_offMeshConAreas[m_offMeshConCount] = (unsigned char)area;
|
||||
m_offMeshConFlags[m_offMeshConCount] = (unsigned short)flags;
|
||||
m_offMeshConCount++;
|
||||
}
|
||||
}
|
||||
else if (row[0] == 'v')
|
||||
{
|
||||
// Convex volumes
|
||||
if (m_volumeCount < MAX_VOLUMES)
|
||||
{
|
||||
ConvexVolume* vol = &m_volumes[m_volumeCount++];
|
||||
sscanf(row+1, "%d %d %f %f", &vol->nverts, &vol->area, &vol->hmin, &vol->hmax);
|
||||
for (int i = 0; i < vol->nverts; ++i)
|
||||
{
|
||||
row[0] = '\0';
|
||||
src = parseRow(src, srcEnd, row, sizeof(row)/sizeof(char));
|
||||
sscanf(row, "%f %f %f", &vol->verts[i*3+0], &vol->verts[i*3+1], &vol->verts[i*3+2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (row[0] == 's')
|
||||
{
|
||||
// Settings
|
||||
m_hasBuildSettings = true;
|
||||
sscanf(row + 1, "%f %f %f %f %f %f %f %f %f %f %f %f %f %d %f %f %f %f %f %f %f",
|
||||
&m_buildSettings.cellSize,
|
||||
&m_buildSettings.cellHeight,
|
||||
&m_buildSettings.agentHeight,
|
||||
&m_buildSettings.agentRadius,
|
||||
&m_buildSettings.agentMaxClimb,
|
||||
&m_buildSettings.agentMaxSlope,
|
||||
&m_buildSettings.regionMinSize,
|
||||
&m_buildSettings.regionMergeSize,
|
||||
&m_buildSettings.edgeMaxLen,
|
||||
&m_buildSettings.edgeMaxError,
|
||||
&m_buildSettings.vertsPerPoly,
|
||||
&m_buildSettings.detailSampleDist,
|
||||
&m_buildSettings.detailSampleMaxError,
|
||||
&m_buildSettings.partitionType,
|
||||
&m_buildSettings.navMeshBMin[0],
|
||||
&m_buildSettings.navMeshBMin[1],
|
||||
&m_buildSettings.navMeshBMin[2],
|
||||
&m_buildSettings.navMeshBMax[0],
|
||||
&m_buildSettings.navMeshBMax[1],
|
||||
&m_buildSettings.navMeshBMax[2],
|
||||
&m_buildSettings.tileSize);
|
||||
}
|
||||
}
|
||||
|
||||
delete [] buf;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InputGeom::load(rcContext* ctx, const std::string& filepath)
|
||||
{
|
||||
size_t extensionPos = filepath.find_last_of('.');
|
||||
if (extensionPos == std::string::npos)
|
||||
return false;
|
||||
|
||||
std::string extension = filepath.substr(extensionPos);
|
||||
std::transform(extension.begin(), extension.end(), extension.begin(), tolower);
|
||||
|
||||
if (extension == ".gset")
|
||||
return loadGeomSet(ctx, filepath);
|
||||
if (extension == ".obj")
|
||||
return loadMesh(ctx, filepath);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool InputGeom::saveGeomSet(const BuildSettings* settings)
|
||||
{
|
||||
if (!m_mesh) return false;
|
||||
|
||||
// Change extension
|
||||
std::string filepath = m_mesh->getFileName();
|
||||
size_t extPos = filepath.find_last_of('.');
|
||||
if (extPos != std::string::npos)
|
||||
filepath = filepath.substr(0, extPos);
|
||||
|
||||
filepath += ".gset";
|
||||
|
||||
FILE* fp = fopen(filepath.c_str(), "w");
|
||||
if (!fp) return false;
|
||||
|
||||
// Store mesh filename.
|
||||
fprintf(fp, "f %s\n", m_mesh->getFileName().c_str());
|
||||
|
||||
// Store settings if any
|
||||
if (settings)
|
||||
{
|
||||
fprintf(fp,
|
||||
"s %f %f %f %f %f %f %f %f %f %f %f %f %f %d %f %f %f %f %f %f %f\n",
|
||||
settings->cellSize,
|
||||
settings->cellHeight,
|
||||
settings->agentHeight,
|
||||
settings->agentRadius,
|
||||
settings->agentMaxClimb,
|
||||
settings->agentMaxSlope,
|
||||
settings->regionMinSize,
|
||||
settings->regionMergeSize,
|
||||
settings->edgeMaxLen,
|
||||
settings->edgeMaxError,
|
||||
settings->vertsPerPoly,
|
||||
settings->detailSampleDist,
|
||||
settings->detailSampleMaxError,
|
||||
settings->partitionType,
|
||||
settings->navMeshBMin[0],
|
||||
settings->navMeshBMin[1],
|
||||
settings->navMeshBMin[2],
|
||||
settings->navMeshBMax[0],
|
||||
settings->navMeshBMax[1],
|
||||
settings->navMeshBMax[2],
|
||||
settings->tileSize);
|
||||
}
|
||||
|
||||
// Store off-mesh links.
|
||||
for (int i = 0; i < m_offMeshConCount; ++i)
|
||||
{
|
||||
const float* v = &m_offMeshConVerts[i*3*2];
|
||||
const float rad = m_offMeshConRads[i];
|
||||
const int bidir = m_offMeshConDirs[i];
|
||||
const int area = m_offMeshConAreas[i];
|
||||
const int flags = m_offMeshConFlags[i];
|
||||
fprintf(fp, "c %f %f %f %f %f %f %f %d %d %d\n",
|
||||
v[0], v[1], v[2], v[3], v[4], v[5], rad, bidir, area, flags);
|
||||
}
|
||||
|
||||
// Convex volumes
|
||||
for (int i = 0; i < m_volumeCount; ++i)
|
||||
{
|
||||
ConvexVolume* vol = &m_volumes[i];
|
||||
fprintf(fp, "v %d %d %f %f\n", vol->nverts, vol->area, vol->hmin, vol->hmax);
|
||||
for (int j = 0; j < vol->nverts; ++j)
|
||||
fprintf(fp, "%f %f %f\n", vol->verts[j*3+0], vol->verts[j*3+1], vol->verts[j*3+2]);
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool isectSegAABB(const float* sp, const float* sq,
|
||||
const float* amin, const float* amax,
|
||||
float& tmin, float& tmax)
|
||||
{
|
||||
static const float EPS = 1e-6f;
|
||||
|
||||
float d[3];
|
||||
d[0] = sq[0] - sp[0];
|
||||
d[1] = sq[1] - sp[1];
|
||||
d[2] = sq[2] - sp[2];
|
||||
tmin = 0.0;
|
||||
tmax = 1.0f;
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
if (fabsf(d[i]) < EPS)
|
||||
{
|
||||
if (sp[i] < amin[i] || sp[i] > amax[i])
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
const float ood = 1.0f / d[i];
|
||||
float t1 = (amin[i] - sp[i]) * ood;
|
||||
float t2 = (amax[i] - sp[i]) * ood;
|
||||
if (t1 > t2) { float tmp = t1; t1 = t2; t2 = tmp; }
|
||||
if (t1 > tmin) tmin = t1;
|
||||
if (t2 < tmax) tmax = t2;
|
||||
if (tmin > tmax) return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool InputGeom::raycastMesh(float* src, float* dst, float& tmin)
|
||||
{
|
||||
float dir[3];
|
||||
rcVsub(dir, dst, src);
|
||||
|
||||
// Prune hit ray.
|
||||
float btmin, btmax;
|
||||
if (!isectSegAABB(src, dst, m_meshBMin, m_meshBMax, btmin, btmax))
|
||||
return false;
|
||||
float p[2], q[2];
|
||||
p[0] = src[0] + (dst[0]-src[0])*btmin;
|
||||
p[1] = src[2] + (dst[2]-src[2])*btmin;
|
||||
q[0] = src[0] + (dst[0]-src[0])*btmax;
|
||||
q[1] = src[2] + (dst[2]-src[2])*btmax;
|
||||
|
||||
int cid[512];
|
||||
const int ncid = rcGetChunksOverlappingSegment(m_chunkyMesh, p, q, cid, 512);
|
||||
if (!ncid)
|
||||
return false;
|
||||
|
||||
tmin = 1.0f;
|
||||
bool hit = false;
|
||||
const float* verts = m_mesh->getVerts();
|
||||
|
||||
for (int i = 0; i < ncid; ++i)
|
||||
{
|
||||
const rcChunkyTriMeshNode& node = m_chunkyMesh->nodes[cid[i]];
|
||||
const int* tris = &m_chunkyMesh->tris[node.i*3];
|
||||
const int ntris = node.n;
|
||||
|
||||
for (int j = 0; j < ntris*3; j += 3)
|
||||
{
|
||||
float t = 1;
|
||||
if (intersectSegmentTriangle(src, dst,
|
||||
&verts[tris[j]*3],
|
||||
&verts[tris[j+1]*3],
|
||||
&verts[tris[j+2]*3], t))
|
||||
{
|
||||
if (t < tmin)
|
||||
tmin = t;
|
||||
hit = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return hit;
|
||||
}
|
||||
|
||||
void InputGeom::addOffMeshConnection(const float* spos, const float* epos, const float rad,
|
||||
unsigned char bidir, unsigned char area, unsigned short flags)
|
||||
{
|
||||
if (m_offMeshConCount >= MAX_OFFMESH_CONNECTIONS) return;
|
||||
float* v = &m_offMeshConVerts[m_offMeshConCount*3*2];
|
||||
m_offMeshConRads[m_offMeshConCount] = rad;
|
||||
m_offMeshConDirs[m_offMeshConCount] = bidir;
|
||||
m_offMeshConAreas[m_offMeshConCount] = area;
|
||||
m_offMeshConFlags[m_offMeshConCount] = flags;
|
||||
m_offMeshConId[m_offMeshConCount] = 1000 + m_offMeshConCount;
|
||||
rcVcopy(&v[0], spos);
|
||||
rcVcopy(&v[3], epos);
|
||||
m_offMeshConCount++;
|
||||
}
|
||||
|
||||
void InputGeom::deleteOffMeshConnection(int i)
|
||||
{
|
||||
m_offMeshConCount--;
|
||||
float* src = &m_offMeshConVerts[m_offMeshConCount*3*2];
|
||||
float* dst = &m_offMeshConVerts[i*3*2];
|
||||
rcVcopy(&dst[0], &src[0]);
|
||||
rcVcopy(&dst[3], &src[3]);
|
||||
m_offMeshConRads[i] = m_offMeshConRads[m_offMeshConCount];
|
||||
m_offMeshConDirs[i] = m_offMeshConDirs[m_offMeshConCount];
|
||||
m_offMeshConAreas[i] = m_offMeshConAreas[m_offMeshConCount];
|
||||
m_offMeshConFlags[i] = m_offMeshConFlags[m_offMeshConCount];
|
||||
}
|
||||
|
||||
void InputGeom::drawOffMeshConnections(duDebugDraw* dd, bool hilight)
|
||||
{
|
||||
unsigned int conColor = duRGBA(192,0,128,192);
|
||||
unsigned int baseColor = duRGBA(0,0,0,64);
|
||||
dd->depthMask(false);
|
||||
|
||||
dd->begin(DU_DRAW_LINES, 2.0f);
|
||||
for (int i = 0; i < m_offMeshConCount; ++i)
|
||||
{
|
||||
float* v = &m_offMeshConVerts[i*3*2];
|
||||
|
||||
dd->vertex(v[0],v[1],v[2], baseColor);
|
||||
dd->vertex(v[0],v[1]+0.2f,v[2], baseColor);
|
||||
|
||||
dd->vertex(v[3],v[4],v[5], baseColor);
|
||||
dd->vertex(v[3],v[4]+0.2f,v[5], baseColor);
|
||||
|
||||
duAppendCircle(dd, v[0],v[1]+0.1f,v[2], m_offMeshConRads[i], baseColor);
|
||||
duAppendCircle(dd, v[3],v[4]+0.1f,v[5], m_offMeshConRads[i], baseColor);
|
||||
|
||||
if (hilight)
|
||||
{
|
||||
duAppendArc(dd, v[0],v[1],v[2], v[3],v[4],v[5], 0.25f,
|
||||
(m_offMeshConDirs[i]&1) ? 0.6f : 0.0f, 0.6f, conColor);
|
||||
}
|
||||
}
|
||||
dd->end();
|
||||
|
||||
dd->depthMask(true);
|
||||
}
|
||||
|
||||
void InputGeom::addConvexVolume(const float* verts, const int nverts,
|
||||
const float minh, const float maxh, unsigned char area)
|
||||
{
|
||||
if (m_volumeCount >= MAX_VOLUMES) return;
|
||||
ConvexVolume* vol = &m_volumes[m_volumeCount++];
|
||||
memset(vol, 0, sizeof(ConvexVolume));
|
||||
memcpy(vol->verts, verts, sizeof(float)*3*nverts);
|
||||
vol->hmin = minh;
|
||||
vol->hmax = maxh;
|
||||
vol->nverts = nverts;
|
||||
vol->area = area;
|
||||
}
|
||||
|
||||
void InputGeom::deleteConvexVolume(int i)
|
||||
{
|
||||
m_volumeCount--;
|
||||
m_volumes[i] = m_volumes[m_volumeCount];
|
||||
}
|
||||
|
||||
void InputGeom::drawConvexVolumes(struct duDebugDraw* dd, bool /*hilight*/)
|
||||
{
|
||||
dd->depthMask(false);
|
||||
|
||||
dd->begin(DU_DRAW_TRIS);
|
||||
|
||||
for (int i = 0; i < m_volumeCount; ++i)
|
||||
{
|
||||
const ConvexVolume* vol = &m_volumes[i];
|
||||
unsigned int col = duTransCol(dd->areaToCol(vol->area), 32);
|
||||
for (int j = 0, k = vol->nverts-1; j < vol->nverts; k = j++)
|
||||
{
|
||||
const float* va = &vol->verts[k*3];
|
||||
const float* vb = &vol->verts[j*3];
|
||||
|
||||
dd->vertex(vol->verts[0],vol->hmax,vol->verts[2], col);
|
||||
dd->vertex(vb[0],vol->hmax,vb[2], col);
|
||||
dd->vertex(va[0],vol->hmax,va[2], col);
|
||||
|
||||
dd->vertex(va[0],vol->hmin,va[2], duDarkenCol(col));
|
||||
dd->vertex(va[0],vol->hmax,va[2], col);
|
||||
dd->vertex(vb[0],vol->hmax,vb[2], col);
|
||||
|
||||
dd->vertex(va[0],vol->hmin,va[2], duDarkenCol(col));
|
||||
dd->vertex(vb[0],vol->hmax,vb[2], col);
|
||||
dd->vertex(vb[0],vol->hmin,vb[2], duDarkenCol(col));
|
||||
}
|
||||
}
|
||||
|
||||
dd->end();
|
||||
|
||||
dd->begin(DU_DRAW_LINES, 2.0f);
|
||||
for (int i = 0; i < m_volumeCount; ++i)
|
||||
{
|
||||
const ConvexVolume* vol = &m_volumes[i];
|
||||
unsigned int col = duTransCol(dd->areaToCol(vol->area), 220);
|
||||
for (int j = 0, k = vol->nverts-1; j < vol->nverts; k = j++)
|
||||
{
|
||||
const float* va = &vol->verts[k*3];
|
||||
const float* vb = &vol->verts[j*3];
|
||||
dd->vertex(va[0],vol->hmin,va[2], duDarkenCol(col));
|
||||
dd->vertex(vb[0],vol->hmin,vb[2], duDarkenCol(col));
|
||||
dd->vertex(va[0],vol->hmax,va[2], col);
|
||||
dd->vertex(vb[0],vol->hmax,vb[2], col);
|
||||
dd->vertex(va[0],vol->hmin,va[2], duDarkenCol(col));
|
||||
dd->vertex(va[0],vol->hmax,va[2], col);
|
||||
}
|
||||
}
|
||||
dd->end();
|
||||
|
||||
dd->begin(DU_DRAW_POINTS, 3.0f);
|
||||
for (int i = 0; i < m_volumeCount; ++i)
|
||||
{
|
||||
const ConvexVolume* vol = &m_volumes[i];
|
||||
unsigned int col = duDarkenCol(duTransCol(dd->areaToCol(vol->area), 220));
|
||||
for (int j = 0; j < vol->nverts; ++j)
|
||||
{
|
||||
dd->vertex(vol->verts[j*3+0],vol->verts[j*3+1]+0.1f,vol->verts[j*3+2], col);
|
||||
dd->vertex(vol->verts[j*3+0],vol->hmin,vol->verts[j*3+2], col);
|
||||
dd->vertex(vol->verts[j*3+0],vol->hmax,vol->verts[j*3+2], col);
|
||||
}
|
||||
}
|
||||
dd->end();
|
||||
|
||||
|
||||
dd->depthMask(true);
|
||||
}
|
||||
@@ -0,0 +1,245 @@
|
||||
//
|
||||
// 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 "MeshLoaderObj.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <cstring>
|
||||
#define _USE_MATH_DEFINES
|
||||
#include <math.h>
|
||||
|
||||
rcMeshLoaderObj::rcMeshLoaderObj() :
|
||||
m_scale(1.0f),
|
||||
m_verts(0),
|
||||
m_tris(0),
|
||||
m_normals(0),
|
||||
m_vertCount(0),
|
||||
m_triCount(0)
|
||||
{
|
||||
}
|
||||
|
||||
rcMeshLoaderObj::~rcMeshLoaderObj()
|
||||
{
|
||||
delete [] m_verts;
|
||||
delete [] m_normals;
|
||||
delete [] m_tris;
|
||||
}
|
||||
|
||||
void rcMeshLoaderObj::addVertex(float x, float y, float z, int& cap)
|
||||
{
|
||||
if (m_vertCount+1 > cap)
|
||||
{
|
||||
cap = !cap ? 8 : cap*2;
|
||||
float* nv = new float[cap*3];
|
||||
if (m_vertCount)
|
||||
memcpy(nv, m_verts, m_vertCount*3*sizeof(float));
|
||||
delete [] m_verts;
|
||||
m_verts = nv;
|
||||
}
|
||||
float* dst = &m_verts[m_vertCount*3];
|
||||
*dst++ = x*m_scale;
|
||||
*dst++ = y*m_scale;
|
||||
*dst++ = z*m_scale;
|
||||
m_vertCount++;
|
||||
}
|
||||
|
||||
void rcMeshLoaderObj::addTriangle(int a, int b, int c, int& cap)
|
||||
{
|
||||
if (m_triCount+1 > cap)
|
||||
{
|
||||
cap = !cap ? 8 : cap*2;
|
||||
int* nv = new int[cap*3];
|
||||
if (m_triCount)
|
||||
memcpy(nv, m_tris, m_triCount*3*sizeof(int));
|
||||
delete [] m_tris;
|
||||
m_tris = nv;
|
||||
}
|
||||
int* dst = &m_tris[m_triCount*3];
|
||||
*dst++ = a;
|
||||
*dst++ = b;
|
||||
*dst++ = c;
|
||||
m_triCount++;
|
||||
}
|
||||
|
||||
static char* parseRow(char* buf, char* bufEnd, char* row, int len)
|
||||
{
|
||||
bool start = true;
|
||||
bool done = false;
|
||||
int n = 0;
|
||||
while (!done && buf < bufEnd)
|
||||
{
|
||||
char c = *buf;
|
||||
buf++;
|
||||
// multirow
|
||||
switch (c)
|
||||
{
|
||||
case '\\':
|
||||
break;
|
||||
case '\n':
|
||||
if (start) break;
|
||||
done = true;
|
||||
break;
|
||||
case '\r':
|
||||
break;
|
||||
case '\t':
|
||||
case ' ':
|
||||
if (start) break;
|
||||
// else falls through
|
||||
default:
|
||||
start = false;
|
||||
row[n++] = c;
|
||||
if (n >= len-1)
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
row[n] = '\0';
|
||||
return buf;
|
||||
}
|
||||
|
||||
static int parseFace(char* row, int* data, int n, int vcnt)
|
||||
{
|
||||
int j = 0;
|
||||
while (*row != '\0')
|
||||
{
|
||||
// Skip initial white space
|
||||
while (*row != '\0' && (*row == ' ' || *row == '\t'))
|
||||
row++;
|
||||
char* s = row;
|
||||
// Find vertex delimiter and terminated the string there for conversion.
|
||||
while (*row != '\0' && *row != ' ' && *row != '\t')
|
||||
{
|
||||
if (*row == '/') *row = '\0';
|
||||
row++;
|
||||
}
|
||||
if (*s == '\0')
|
||||
continue;
|
||||
int vi = atoi(s);
|
||||
data[j++] = vi < 0 ? vi+vcnt : vi-1;
|
||||
if (j >= n) return j;
|
||||
}
|
||||
return j;
|
||||
}
|
||||
|
||||
bool rcMeshLoaderObj::load(const std::string& filename)
|
||||
{
|
||||
char* buf = 0;
|
||||
FILE* fp = fopen(filename.c_str(), "rb");
|
||||
if (!fp)
|
||||
return false;
|
||||
if (fseek(fp, 0, SEEK_END) != 0)
|
||||
{
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
long bufSize = ftell(fp);
|
||||
if (bufSize < 0)
|
||||
{
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
if (fseek(fp, 0, SEEK_SET) != 0)
|
||||
{
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
buf = new char[bufSize];
|
||||
if (!buf)
|
||||
{
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
size_t readLen = fread(buf, bufSize, 1, fp);
|
||||
fclose(fp);
|
||||
|
||||
if (readLen != 1)
|
||||
{
|
||||
delete[] buf;
|
||||
return false;
|
||||
}
|
||||
|
||||
char* src = buf;
|
||||
char* srcEnd = buf + bufSize;
|
||||
char row[512];
|
||||
int face[32];
|
||||
float x,y,z;
|
||||
int nv;
|
||||
int vcap = 0;
|
||||
int tcap = 0;
|
||||
|
||||
while (src < srcEnd)
|
||||
{
|
||||
// Parse one row
|
||||
row[0] = '\0';
|
||||
src = parseRow(src, srcEnd, row, sizeof(row)/sizeof(char));
|
||||
// Skip comments
|
||||
if (row[0] == '#') continue;
|
||||
if (row[0] == 'v' && row[1] != 'n' && row[1] != 't')
|
||||
{
|
||||
// Vertex pos
|
||||
sscanf(row+1, "%f %f %f", &x, &y, &z);
|
||||
addVertex(x, y, z, vcap);
|
||||
}
|
||||
if (row[0] == 'f')
|
||||
{
|
||||
// Faces
|
||||
nv = parseFace(row+1, face, 32, m_vertCount);
|
||||
for (int i = 2; i < nv; ++i)
|
||||
{
|
||||
const int a = face[0];
|
||||
const int b = face[i-1];
|
||||
const int c = face[i];
|
||||
if (a < 0 || a >= m_vertCount || b < 0 || b >= m_vertCount || c < 0 || c >= m_vertCount)
|
||||
continue;
|
||||
addTriangle(a, b, c, tcap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete [] buf;
|
||||
|
||||
// Calculate normals.
|
||||
m_normals = new float[m_triCount*3];
|
||||
for (int i = 0; i < m_triCount*3; i += 3)
|
||||
{
|
||||
const float* v0 = &m_verts[m_tris[i]*3];
|
||||
const float* v1 = &m_verts[m_tris[i+1]*3];
|
||||
const float* v2 = &m_verts[m_tris[i+2]*3];
|
||||
float e0[3], e1[3];
|
||||
for (int j = 0; j < 3; ++j)
|
||||
{
|
||||
e0[j] = v1[j] - v0[j];
|
||||
e1[j] = v2[j] - v0[j];
|
||||
}
|
||||
float* n = &m_normals[i];
|
||||
n[0] = e0[1]*e1[2] - e0[2]*e1[1];
|
||||
n[1] = e0[2]*e1[0] - e0[0]*e1[2];
|
||||
n[2] = e0[0]*e1[1] - e0[1]*e1[0];
|
||||
float d = sqrtf(n[0]*n[0] + n[1]*n[1] + n[2]*n[2]);
|
||||
if (d > 0)
|
||||
{
|
||||
d = 1.0f/d;
|
||||
n[0] *= d;
|
||||
n[1] *= d;
|
||||
n[2] *= d;
|
||||
}
|
||||
}
|
||||
|
||||
m_filename = filename;
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,323 @@
|
||||
//
|
||||
// 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 <string.h>
|
||||
#include <float.h>
|
||||
#include <vector>
|
||||
#include "SDL.h"
|
||||
#include "SDL_opengl.h"
|
||||
#include "imgui.h"
|
||||
#include "NavMeshPruneTool.h"
|
||||
#include "InputGeom.h"
|
||||
#include "Sample.h"
|
||||
#include "DetourNavMesh.h"
|
||||
#include "DetourCommon.h"
|
||||
#include "DetourAssert.h"
|
||||
#include "DetourDebugDraw.h"
|
||||
|
||||
#ifdef WIN32
|
||||
# define snprintf _snprintf
|
||||
#endif
|
||||
|
||||
class NavmeshFlags
|
||||
{
|
||||
struct TileFlags
|
||||
{
|
||||
inline void purge() { dtFree(flags); }
|
||||
unsigned char* flags;
|
||||
int nflags;
|
||||
dtPolyRef base;
|
||||
};
|
||||
|
||||
const dtNavMesh* m_nav;
|
||||
TileFlags* m_tiles;
|
||||
int m_ntiles;
|
||||
|
||||
public:
|
||||
NavmeshFlags() :
|
||||
m_nav(0), m_tiles(0), m_ntiles(0)
|
||||
{
|
||||
}
|
||||
|
||||
~NavmeshFlags()
|
||||
{
|
||||
for (int i = 0; i < m_ntiles; ++i)
|
||||
m_tiles[i].purge();
|
||||
dtFree(m_tiles);
|
||||
}
|
||||
|
||||
bool init(const dtNavMesh* nav)
|
||||
{
|
||||
m_ntiles = nav->getMaxTiles();
|
||||
if (!m_ntiles)
|
||||
return true;
|
||||
m_tiles = (TileFlags*)dtAlloc(sizeof(TileFlags)*m_ntiles, DT_ALLOC_TEMP);
|
||||
if (!m_tiles)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
memset(m_tiles, 0, sizeof(TileFlags)*m_ntiles);
|
||||
|
||||
// Alloc flags for each tile.
|
||||
for (int i = 0; i < nav->getMaxTiles(); ++i)
|
||||
{
|
||||
const dtMeshTile* tile = nav->getTile(i);
|
||||
if (!tile->header) continue;
|
||||
TileFlags* tf = &m_tiles[i];
|
||||
tf->nflags = tile->header->polyCount;
|
||||
tf->base = nav->getPolyRefBase(tile);
|
||||
if (tf->nflags)
|
||||
{
|
||||
tf->flags = (unsigned char*)dtAlloc(tf->nflags, DT_ALLOC_TEMP);
|
||||
if (!tf->flags)
|
||||
return false;
|
||||
memset(tf->flags, 0, tf->nflags);
|
||||
}
|
||||
}
|
||||
|
||||
m_nav = nav;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
inline void clearAllFlags()
|
||||
{
|
||||
for (int i = 0; i < m_ntiles; ++i)
|
||||
{
|
||||
TileFlags* tf = &m_tiles[i];
|
||||
if (tf->nflags)
|
||||
memset(tf->flags, 0, tf->nflags);
|
||||
}
|
||||
}
|
||||
|
||||
inline unsigned char getFlags(dtPolyRef ref)
|
||||
{
|
||||
dtAssert(m_nav);
|
||||
dtAssert(m_ntiles);
|
||||
// Assume the ref is valid, no bounds checks.
|
||||
unsigned int salt, it, ip;
|
||||
m_nav->decodePolyId(ref, salt, it, ip);
|
||||
return m_tiles[it].flags[ip];
|
||||
}
|
||||
|
||||
inline void setFlags(dtPolyRef ref, unsigned char flags)
|
||||
{
|
||||
dtAssert(m_nav);
|
||||
dtAssert(m_ntiles);
|
||||
// Assume the ref is valid, no bounds checks.
|
||||
unsigned int salt, it, ip;
|
||||
m_nav->decodePolyId(ref, salt, it, ip);
|
||||
m_tiles[it].flags[ip] = flags;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
static void floodNavmesh(dtNavMesh* nav, NavmeshFlags* flags, dtPolyRef start, unsigned char flag)
|
||||
{
|
||||
// If already visited, skip.
|
||||
if (flags->getFlags(start))
|
||||
return;
|
||||
|
||||
flags->setFlags(start, flag);
|
||||
|
||||
std::vector<dtPolyRef> openList;
|
||||
openList.push_back(start);
|
||||
|
||||
while (openList.size())
|
||||
{
|
||||
const dtPolyRef ref = openList.back();
|
||||
openList.pop_back();
|
||||
|
||||
// Get current poly and tile.
|
||||
// The API input has been cheked already, skip checking internal data.
|
||||
const dtMeshTile* tile = 0;
|
||||
const dtPoly* poly = 0;
|
||||
nav->getTileAndPolyByRefUnsafe(ref, &tile, &poly);
|
||||
|
||||
// Visit linked polygons.
|
||||
for (unsigned int i = poly->firstLink; i != DT_NULL_LINK; i = tile->links[i].next)
|
||||
{
|
||||
const dtPolyRef neiRef = tile->links[i].ref;
|
||||
// Skip invalid and already visited.
|
||||
if (!neiRef || flags->getFlags(neiRef))
|
||||
continue;
|
||||
// Mark as visited
|
||||
flags->setFlags(neiRef, flag);
|
||||
// Visit neighbours
|
||||
openList.push_back(neiRef);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void disableUnvisitedPolys(dtNavMesh* nav, NavmeshFlags* flags)
|
||||
{
|
||||
for (int i = 0; i < nav->getMaxTiles(); ++i)
|
||||
{
|
||||
const dtMeshTile* tile = ((const dtNavMesh*)nav)->getTile(i);
|
||||
if (!tile->header) continue;
|
||||
const dtPolyRef base = nav->getPolyRefBase(tile);
|
||||
for (int j = 0; j < tile->header->polyCount; ++j)
|
||||
{
|
||||
const dtPolyRef ref = base | (unsigned int)j;
|
||||
if (!flags->getFlags(ref))
|
||||
{
|
||||
unsigned short f = 0;
|
||||
nav->getPolyFlags(ref, &f);
|
||||
nav->setPolyFlags(ref, f | SAMPLE_POLYFLAGS_DISABLED);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NavMeshPruneTool::NavMeshPruneTool() :
|
||||
m_sample(0),
|
||||
m_flags(0),
|
||||
m_hitPosSet(false)
|
||||
{
|
||||
}
|
||||
|
||||
NavMeshPruneTool::~NavMeshPruneTool()
|
||||
{
|
||||
delete m_flags;
|
||||
}
|
||||
|
||||
void NavMeshPruneTool::init(Sample* sample)
|
||||
{
|
||||
m_sample = sample;
|
||||
}
|
||||
|
||||
void NavMeshPruneTool::reset()
|
||||
{
|
||||
m_hitPosSet = false;
|
||||
delete m_flags;
|
||||
m_flags = 0;
|
||||
}
|
||||
|
||||
void NavMeshPruneTool::handleMenu()
|
||||
{
|
||||
dtNavMesh* nav = m_sample->getNavMesh();
|
||||
if (!nav) return;
|
||||
if (!m_flags) return;
|
||||
|
||||
if (imguiButton("Clear Selection"))
|
||||
{
|
||||
m_flags->clearAllFlags();
|
||||
}
|
||||
|
||||
if (imguiButton("Prune Unselected"))
|
||||
{
|
||||
disableUnvisitedPolys(nav, m_flags);
|
||||
delete m_flags;
|
||||
m_flags = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void NavMeshPruneTool::handleClick(const float* s, const float* p, bool shift)
|
||||
{
|
||||
rcIgnoreUnused(s);
|
||||
rcIgnoreUnused(shift);
|
||||
|
||||
if (!m_sample) return;
|
||||
InputGeom* geom = m_sample->getInputGeom();
|
||||
if (!geom) return;
|
||||
dtNavMesh* nav = m_sample->getNavMesh();
|
||||
if (!nav) return;
|
||||
dtNavMeshQuery* query = m_sample->getNavMeshQuery();
|
||||
if (!query) return;
|
||||
|
||||
dtVcopy(m_hitPos, p);
|
||||
m_hitPosSet = true;
|
||||
|
||||
if (!m_flags)
|
||||
{
|
||||
m_flags = new NavmeshFlags;
|
||||
m_flags->init(nav);
|
||||
}
|
||||
|
||||
const float halfExtents[3] = { 2, 4, 2 };
|
||||
dtQueryFilter filter;
|
||||
dtPolyRef ref = 0;
|
||||
query->findNearestPoly(p, halfExtents, &filter, &ref, 0);
|
||||
|
||||
floodNavmesh(nav, m_flags, ref, 1);
|
||||
}
|
||||
|
||||
void NavMeshPruneTool::handleToggle()
|
||||
{
|
||||
}
|
||||
|
||||
void NavMeshPruneTool::handleStep()
|
||||
{
|
||||
}
|
||||
|
||||
void NavMeshPruneTool::handleUpdate(const float /*dt*/)
|
||||
{
|
||||
}
|
||||
|
||||
void NavMeshPruneTool::handleRender()
|
||||
{
|
||||
duDebugDraw& dd = m_sample->getDebugDraw();
|
||||
|
||||
if (m_hitPosSet)
|
||||
{
|
||||
const float s = m_sample->getAgentRadius();
|
||||
const unsigned int col = duRGBA(255,255,255,255);
|
||||
dd.begin(DU_DRAW_LINES);
|
||||
dd.vertex(m_hitPos[0]-s,m_hitPos[1],m_hitPos[2], col);
|
||||
dd.vertex(m_hitPos[0]+s,m_hitPos[1],m_hitPos[2], col);
|
||||
dd.vertex(m_hitPos[0],m_hitPos[1]-s,m_hitPos[2], col);
|
||||
dd.vertex(m_hitPos[0],m_hitPos[1]+s,m_hitPos[2], col);
|
||||
dd.vertex(m_hitPos[0],m_hitPos[1],m_hitPos[2]-s, col);
|
||||
dd.vertex(m_hitPos[0],m_hitPos[1],m_hitPos[2]+s, col);
|
||||
dd.end();
|
||||
}
|
||||
|
||||
const dtNavMesh* nav = m_sample->getNavMesh();
|
||||
if (m_flags && nav)
|
||||
{
|
||||
for (int i = 0; i < nav->getMaxTiles(); ++i)
|
||||
{
|
||||
const dtMeshTile* tile = nav->getTile(i);
|
||||
if (!tile->header) continue;
|
||||
const dtPolyRef base = nav->getPolyRefBase(tile);
|
||||
for (int j = 0; j < tile->header->polyCount; ++j)
|
||||
{
|
||||
const dtPolyRef ref = base | (unsigned int)j;
|
||||
if (m_flags->getFlags(ref))
|
||||
{
|
||||
duDebugDrawNavMeshPoly(&dd, *nav, ref, duRGBA(255,255,255,128));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void NavMeshPruneTool::handleRenderOverlay(double* proj, double* model, int* view)
|
||||
{
|
||||
rcIgnoreUnused(model);
|
||||
rcIgnoreUnused(proj);
|
||||
|
||||
// Tool help
|
||||
const int h = view[3];
|
||||
|
||||
imguiDrawText(280, h-40, IMGUI_ALIGN_LEFT, "LMB: Click fill area.", imguiRGBA(255,255,255,192));
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,177 @@
|
||||
//
|
||||
// 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 <string.h>
|
||||
#include <float.h>
|
||||
#include "SDL.h"
|
||||
#include "SDL_opengl.h"
|
||||
#ifdef __APPLE__
|
||||
# include <OpenGL/glu.h>
|
||||
#else
|
||||
# include <GL/glu.h>
|
||||
#endif
|
||||
#include "imgui.h"
|
||||
#include "OffMeshConnectionTool.h"
|
||||
#include "InputGeom.h"
|
||||
#include "Sample.h"
|
||||
#include "Recast.h"
|
||||
#include "RecastDebugDraw.h"
|
||||
#include "DetourDebugDraw.h"
|
||||
|
||||
#ifdef WIN32
|
||||
# define snprintf _snprintf
|
||||
#endif
|
||||
|
||||
OffMeshConnectionTool::OffMeshConnectionTool() :
|
||||
m_sample(0),
|
||||
m_hitPosSet(0),
|
||||
m_bidir(true),
|
||||
m_oldFlags(0)
|
||||
{
|
||||
}
|
||||
|
||||
OffMeshConnectionTool::~OffMeshConnectionTool()
|
||||
{
|
||||
if (m_sample)
|
||||
{
|
||||
m_sample->setNavMeshDrawFlags(m_oldFlags);
|
||||
}
|
||||
}
|
||||
|
||||
void OffMeshConnectionTool::init(Sample* sample)
|
||||
{
|
||||
if (m_sample != sample)
|
||||
{
|
||||
m_sample = sample;
|
||||
m_oldFlags = m_sample->getNavMeshDrawFlags();
|
||||
m_sample->setNavMeshDrawFlags(m_oldFlags & ~DU_DRAWNAVMESH_OFFMESHCONS);
|
||||
}
|
||||
}
|
||||
|
||||
void OffMeshConnectionTool::reset()
|
||||
{
|
||||
m_hitPosSet = false;
|
||||
}
|
||||
|
||||
void OffMeshConnectionTool::handleMenu()
|
||||
{
|
||||
if (imguiCheck("One Way", !m_bidir))
|
||||
m_bidir = false;
|
||||
if (imguiCheck("Bidirectional", m_bidir))
|
||||
m_bidir = true;
|
||||
}
|
||||
|
||||
void OffMeshConnectionTool::handleClick(const float* /*s*/, const float* p, bool shift)
|
||||
{
|
||||
if (!m_sample) return;
|
||||
InputGeom* geom = m_sample->getInputGeom();
|
||||
if (!geom) return;
|
||||
|
||||
if (shift)
|
||||
{
|
||||
// Delete
|
||||
// Find nearest link end-point
|
||||
float nearestDist = FLT_MAX;
|
||||
int nearestIndex = -1;
|
||||
const float* verts = geom->getOffMeshConnectionVerts();
|
||||
for (int i = 0; i < geom->getOffMeshConnectionCount()*2; ++i)
|
||||
{
|
||||
const float* v = &verts[i*3];
|
||||
float d = rcVdistSqr(p, v);
|
||||
if (d < nearestDist)
|
||||
{
|
||||
nearestDist = d;
|
||||
nearestIndex = i/2; // Each link has two vertices.
|
||||
}
|
||||
}
|
||||
// If end point close enough, delete it.
|
||||
if (nearestIndex != -1 &&
|
||||
sqrtf(nearestDist) < m_sample->getAgentRadius())
|
||||
{
|
||||
geom->deleteOffMeshConnection(nearestIndex);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create
|
||||
if (!m_hitPosSet)
|
||||
{
|
||||
rcVcopy(m_hitPos, p);
|
||||
m_hitPosSet = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
const unsigned char area = SAMPLE_POLYAREA_JUMP;
|
||||
const unsigned short flags = SAMPLE_POLYFLAGS_JUMP;
|
||||
geom->addOffMeshConnection(m_hitPos, p, m_sample->getAgentRadius(), m_bidir ? 1 : 0, area, flags);
|
||||
m_hitPosSet = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void OffMeshConnectionTool::handleToggle()
|
||||
{
|
||||
}
|
||||
|
||||
void OffMeshConnectionTool::handleStep()
|
||||
{
|
||||
}
|
||||
|
||||
void OffMeshConnectionTool::handleUpdate(const float /*dt*/)
|
||||
{
|
||||
}
|
||||
|
||||
void OffMeshConnectionTool::handleRender()
|
||||
{
|
||||
duDebugDraw& dd = m_sample->getDebugDraw();
|
||||
const float s = m_sample->getAgentRadius();
|
||||
|
||||
if (m_hitPosSet)
|
||||
duDebugDrawCross(&dd, m_hitPos[0],m_hitPos[1]+0.1f,m_hitPos[2], s, duRGBA(0,0,0,128), 2.0f);
|
||||
|
||||
InputGeom* geom = m_sample->getInputGeom();
|
||||
if (geom)
|
||||
geom->drawOffMeshConnections(&dd, true);
|
||||
}
|
||||
|
||||
void OffMeshConnectionTool::handleRenderOverlay(double* proj, double* model, int* view)
|
||||
{
|
||||
GLdouble x, y, z;
|
||||
|
||||
// Draw start and end point labels
|
||||
if (m_hitPosSet && gluProject((GLdouble)m_hitPos[0], (GLdouble)m_hitPos[1], (GLdouble)m_hitPos[2],
|
||||
model, proj, view, &x, &y, &z))
|
||||
{
|
||||
imguiDrawText((int)x, (int)(y-25), IMGUI_ALIGN_CENTER, "Start", imguiRGBA(0,0,0,220));
|
||||
}
|
||||
|
||||
// Tool help
|
||||
const int h = view[3];
|
||||
if (!m_hitPosSet)
|
||||
{
|
||||
imguiDrawText(280, h-40, IMGUI_ALIGN_LEFT, "LMB: Create new connection. SHIFT+LMB: Delete existing connection, click close to start or end point.", imguiRGBA(255,255,255,192));
|
||||
}
|
||||
else
|
||||
{
|
||||
imguiDrawText(280, h-40, IMGUI_ALIGN_LEFT, "LMB: Set connection end point and finish.", imguiRGBA(255,255,255,192));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
//
|
||||
// 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 "PerfTimer.h"
|
||||
|
||||
#if defined(WIN32)
|
||||
|
||||
// Win32
|
||||
#include <windows.h>
|
||||
|
||||
TimeVal getPerfTime()
|
||||
{
|
||||
__int64 count;
|
||||
QueryPerformanceCounter((LARGE_INTEGER*)&count);
|
||||
return count;
|
||||
}
|
||||
|
||||
int getPerfTimeUsec(const TimeVal duration)
|
||||
{
|
||||
static __int64 freq = 0;
|
||||
if (freq == 0)
|
||||
QueryPerformanceFrequency((LARGE_INTEGER*)&freq);
|
||||
return (int)(duration*1000000 / freq);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
// Linux, BSD, OSX
|
||||
|
||||
#include <sys/time.h>
|
||||
|
||||
TimeVal getPerfTime()
|
||||
{
|
||||
timeval now;
|
||||
gettimeofday(&now, 0);
|
||||
return (TimeVal)now.tv_sec*1000000L + (TimeVal)now.tv_usec;
|
||||
}
|
||||
|
||||
int getPerfTimeUsec(const TimeVal duration)
|
||||
{
|
||||
return (int)duration;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,449 @@
|
||||
//
|
||||
// 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 "Sample.h"
|
||||
#include "InputGeom.h"
|
||||
#include "Recast.h"
|
||||
#include "RecastDebugDraw.h"
|
||||
#include "DetourDebugDraw.h"
|
||||
#include "DetourNavMesh.h"
|
||||
#include "DetourNavMeshQuery.h"
|
||||
#include "DetourCrowd.h"
|
||||
#include "imgui.h"
|
||||
#include "SDL.h"
|
||||
#include "SDL_opengl.h"
|
||||
|
||||
#ifdef WIN32
|
||||
# define snprintf _snprintf
|
||||
#endif
|
||||
|
||||
unsigned int SampleDebugDraw::areaToCol(unsigned int area)
|
||||
{
|
||||
switch(area)
|
||||
{
|
||||
// Ground (0) : light blue
|
||||
case SAMPLE_POLYAREA_GROUND: return duRGBA(0, 192, 255, 255);
|
||||
// Water : blue
|
||||
case SAMPLE_POLYAREA_WATER: return duRGBA(0, 0, 255, 255);
|
||||
// Road : brown
|
||||
case SAMPLE_POLYAREA_ROAD: return duRGBA(50, 20, 12, 255);
|
||||
// Door : cyan
|
||||
case SAMPLE_POLYAREA_DOOR: return duRGBA(0, 255, 255, 255);
|
||||
// Grass : green
|
||||
case SAMPLE_POLYAREA_GRASS: return duRGBA(0, 255, 0, 255);
|
||||
// Jump : yellow
|
||||
case SAMPLE_POLYAREA_JUMP: return duRGBA(255, 255, 0, 255);
|
||||
// Unexpected : red
|
||||
default: return duRGBA(255, 0, 0, 255);
|
||||
}
|
||||
}
|
||||
|
||||
Sample::Sample() :
|
||||
m_geom(0),
|
||||
m_navMesh(0),
|
||||
m_navQuery(0),
|
||||
m_crowd(0),
|
||||
m_navMeshDrawFlags(DU_DRAWNAVMESH_OFFMESHCONS|DU_DRAWNAVMESH_CLOSEDLIST),
|
||||
m_filterLowHangingObstacles(true),
|
||||
m_filterLedgeSpans(true),
|
||||
m_filterWalkableLowHeightSpans(true),
|
||||
m_tool(0),
|
||||
m_ctx(0)
|
||||
{
|
||||
resetCommonSettings();
|
||||
m_navQuery = dtAllocNavMeshQuery();
|
||||
m_crowd = dtAllocCrowd();
|
||||
|
||||
for (int i = 0; i < MAX_TOOLS; i++)
|
||||
m_toolStates[i] = 0;
|
||||
}
|
||||
|
||||
Sample::~Sample()
|
||||
{
|
||||
dtFreeNavMeshQuery(m_navQuery);
|
||||
dtFreeNavMesh(m_navMesh);
|
||||
dtFreeCrowd(m_crowd);
|
||||
delete m_tool;
|
||||
for (int i = 0; i < MAX_TOOLS; i++)
|
||||
delete m_toolStates[i];
|
||||
}
|
||||
|
||||
void Sample::setTool(SampleTool* tool)
|
||||
{
|
||||
delete m_tool;
|
||||
m_tool = tool;
|
||||
if (tool)
|
||||
m_tool->init(this);
|
||||
}
|
||||
|
||||
void Sample::handleSettings()
|
||||
{
|
||||
}
|
||||
|
||||
void Sample::handleTools()
|
||||
{
|
||||
}
|
||||
|
||||
void Sample::handleDebugMode()
|
||||
{
|
||||
}
|
||||
|
||||
void Sample::handleRender()
|
||||
{
|
||||
if (!m_geom)
|
||||
return;
|
||||
|
||||
// Draw mesh
|
||||
duDebugDrawTriMesh(&m_dd, m_geom->getMesh()->getVerts(), m_geom->getMesh()->getVertCount(),
|
||||
m_geom->getMesh()->getTris(), m_geom->getMesh()->getNormals(), m_geom->getMesh()->getTriCount(), 0, 1.0f);
|
||||
// Draw bounds
|
||||
const float* bmin = m_geom->getMeshBoundsMin();
|
||||
const float* bmax = m_geom->getMeshBoundsMax();
|
||||
duDebugDrawBoxWire(&m_dd, bmin[0],bmin[1],bmin[2], bmax[0],bmax[1],bmax[2], duRGBA(255,255,255,128), 1.0f);
|
||||
}
|
||||
|
||||
void Sample::handleRenderOverlay(double* /*proj*/, double* /*model*/, int* /*view*/)
|
||||
{
|
||||
}
|
||||
|
||||
void Sample::handleMeshChanged(InputGeom* geom)
|
||||
{
|
||||
m_geom = geom;
|
||||
|
||||
const BuildSettings* buildSettings = geom->getBuildSettings();
|
||||
if (buildSettings)
|
||||
{
|
||||
m_cellSize = buildSettings->cellSize;
|
||||
m_cellHeight = buildSettings->cellHeight;
|
||||
m_agentHeight = buildSettings->agentHeight;
|
||||
m_agentRadius = buildSettings->agentRadius;
|
||||
m_agentMaxClimb = buildSettings->agentMaxClimb;
|
||||
m_agentMaxSlope = buildSettings->agentMaxSlope;
|
||||
m_regionMinSize = buildSettings->regionMinSize;
|
||||
m_regionMergeSize = buildSettings->regionMergeSize;
|
||||
m_edgeMaxLen = buildSettings->edgeMaxLen;
|
||||
m_edgeMaxError = buildSettings->edgeMaxError;
|
||||
m_vertsPerPoly = buildSettings->vertsPerPoly;
|
||||
m_detailSampleDist = buildSettings->detailSampleDist;
|
||||
m_detailSampleMaxError = buildSettings->detailSampleMaxError;
|
||||
m_partitionType = buildSettings->partitionType;
|
||||
}
|
||||
}
|
||||
|
||||
void Sample::collectSettings(BuildSettings& settings)
|
||||
{
|
||||
settings.cellSize = m_cellSize;
|
||||
settings.cellHeight = m_cellHeight;
|
||||
settings.agentHeight = m_agentHeight;
|
||||
settings.agentRadius = m_agentRadius;
|
||||
settings.agentMaxClimb = m_agentMaxClimb;
|
||||
settings.agentMaxSlope = m_agentMaxSlope;
|
||||
settings.regionMinSize = m_regionMinSize;
|
||||
settings.regionMergeSize = m_regionMergeSize;
|
||||
settings.edgeMaxLen = m_edgeMaxLen;
|
||||
settings.edgeMaxError = m_edgeMaxError;
|
||||
settings.vertsPerPoly = m_vertsPerPoly;
|
||||
settings.detailSampleDist = m_detailSampleDist;
|
||||
settings.detailSampleMaxError = m_detailSampleMaxError;
|
||||
settings.partitionType = m_partitionType;
|
||||
}
|
||||
|
||||
|
||||
void Sample::resetCommonSettings()
|
||||
{
|
||||
m_cellSize = 0.3f;
|
||||
m_cellHeight = 0.2f;
|
||||
m_agentHeight = 2.0f;
|
||||
m_agentRadius = 0.6f;
|
||||
m_agentMaxClimb = 0.9f;
|
||||
m_agentMaxSlope = 45.0f;
|
||||
m_regionMinSize = 8;
|
||||
m_regionMergeSize = 20;
|
||||
m_edgeMaxLen = 12.0f;
|
||||
m_edgeMaxError = 1.3f;
|
||||
m_vertsPerPoly = 6.0f;
|
||||
m_detailSampleDist = 6.0f;
|
||||
m_detailSampleMaxError = 1.0f;
|
||||
m_partitionType = SAMPLE_PARTITION_WATERSHED;
|
||||
}
|
||||
|
||||
void Sample::handleCommonSettings()
|
||||
{
|
||||
imguiLabel("Rasterization");
|
||||
imguiSlider("Cell Size", &m_cellSize, 0.1f, 1.0f, 0.01f);
|
||||
imguiSlider("Cell Height", &m_cellHeight, 0.1f, 1.0f, 0.01f);
|
||||
|
||||
if (m_geom)
|
||||
{
|
||||
const float* bmin = m_geom->getNavMeshBoundsMin();
|
||||
const float* bmax = m_geom->getNavMeshBoundsMax();
|
||||
int gw = 0, gh = 0;
|
||||
rcCalcGridSize(bmin, bmax, m_cellSize, &gw, &gh);
|
||||
char text[64];
|
||||
snprintf(text, 64, "Voxels %d x %d", gw, gh);
|
||||
imguiValue(text);
|
||||
}
|
||||
|
||||
imguiSeparator();
|
||||
imguiLabel("Agent");
|
||||
imguiSlider("Height", &m_agentHeight, 0.1f, 5.0f, 0.1f);
|
||||
imguiSlider("Radius", &m_agentRadius, 0.0f, 5.0f, 0.1f);
|
||||
imguiSlider("Max Climb", &m_agentMaxClimb, 0.1f, 5.0f, 0.1f);
|
||||
imguiSlider("Max Slope", &m_agentMaxSlope, 0.0f, 90.0f, 1.0f);
|
||||
|
||||
imguiSeparator();
|
||||
imguiLabel("Region");
|
||||
imguiSlider("Min Region Size", &m_regionMinSize, 0.0f, 150.0f, 1.0f);
|
||||
imguiSlider("Merged Region Size", &m_regionMergeSize, 0.0f, 150.0f, 1.0f);
|
||||
|
||||
imguiSeparator();
|
||||
imguiLabel("Partitioning");
|
||||
if (imguiCheck("Watershed", m_partitionType == SAMPLE_PARTITION_WATERSHED))
|
||||
m_partitionType = SAMPLE_PARTITION_WATERSHED;
|
||||
if (imguiCheck("Monotone", m_partitionType == SAMPLE_PARTITION_MONOTONE))
|
||||
m_partitionType = SAMPLE_PARTITION_MONOTONE;
|
||||
if (imguiCheck("Layers", m_partitionType == SAMPLE_PARTITION_LAYERS))
|
||||
m_partitionType = SAMPLE_PARTITION_LAYERS;
|
||||
|
||||
imguiSeparator();
|
||||
imguiLabel("Filtering");
|
||||
if (imguiCheck("Low Hanging Obstacles", m_filterLowHangingObstacles))
|
||||
m_filterLowHangingObstacles = !m_filterLowHangingObstacles;
|
||||
if (imguiCheck("Ledge Spans", m_filterLedgeSpans))
|
||||
m_filterLedgeSpans= !m_filterLedgeSpans;
|
||||
if (imguiCheck("Walkable Low Height Spans", m_filterWalkableLowHeightSpans))
|
||||
m_filterWalkableLowHeightSpans = !m_filterWalkableLowHeightSpans;
|
||||
|
||||
imguiSeparator();
|
||||
imguiLabel("Polygonization");
|
||||
imguiSlider("Max Edge Length", &m_edgeMaxLen, 0.0f, 50.0f, 1.0f);
|
||||
imguiSlider("Max Edge Error", &m_edgeMaxError, 0.1f, 3.0f, 0.1f);
|
||||
imguiSlider("Verts Per Poly", &m_vertsPerPoly, 3.0f, 12.0f, 1.0f);
|
||||
|
||||
imguiSeparator();
|
||||
imguiLabel("Detail Mesh");
|
||||
imguiSlider("Sample Distance", &m_detailSampleDist, 0.0f, 16.0f, 1.0f);
|
||||
imguiSlider("Max Sample Error", &m_detailSampleMaxError, 0.0f, 16.0f, 1.0f);
|
||||
|
||||
imguiSeparator();
|
||||
}
|
||||
|
||||
void Sample::handleClick(const float* s, const float* p, bool shift)
|
||||
{
|
||||
if (m_tool)
|
||||
m_tool->handleClick(s, p, shift);
|
||||
}
|
||||
|
||||
void Sample::handleToggle()
|
||||
{
|
||||
if (m_tool)
|
||||
m_tool->handleToggle();
|
||||
}
|
||||
|
||||
void Sample::handleStep()
|
||||
{
|
||||
if (m_tool)
|
||||
m_tool->handleStep();
|
||||
}
|
||||
|
||||
bool Sample::handleBuild()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void Sample::handleUpdate(const float dt)
|
||||
{
|
||||
if (m_tool)
|
||||
m_tool->handleUpdate(dt);
|
||||
updateToolStates(dt);
|
||||
}
|
||||
|
||||
|
||||
void Sample::updateToolStates(const float dt)
|
||||
{
|
||||
for (int i = 0; i < MAX_TOOLS; i++)
|
||||
{
|
||||
if (m_toolStates[i])
|
||||
m_toolStates[i]->handleUpdate(dt);
|
||||
}
|
||||
}
|
||||
|
||||
void Sample::initToolStates(Sample* sample)
|
||||
{
|
||||
for (int i = 0; i < MAX_TOOLS; i++)
|
||||
{
|
||||
if (m_toolStates[i])
|
||||
m_toolStates[i]->init(sample);
|
||||
}
|
||||
}
|
||||
|
||||
void Sample::resetToolStates()
|
||||
{
|
||||
for (int i = 0; i < MAX_TOOLS; i++)
|
||||
{
|
||||
if (m_toolStates[i])
|
||||
m_toolStates[i]->reset();
|
||||
}
|
||||
}
|
||||
|
||||
void Sample::renderToolStates()
|
||||
{
|
||||
for (int i = 0; i < MAX_TOOLS; i++)
|
||||
{
|
||||
if (m_toolStates[i])
|
||||
m_toolStates[i]->handleRender();
|
||||
}
|
||||
}
|
||||
|
||||
void Sample::renderOverlayToolStates(double* proj, double* model, int* view)
|
||||
{
|
||||
for (int i = 0; i < MAX_TOOLS; i++)
|
||||
{
|
||||
if (m_toolStates[i])
|
||||
m_toolStates[i]->handleRenderOverlay(proj, model, view);
|
||||
}
|
||||
}
|
||||
|
||||
static const int NAVMESHSET_MAGIC = 'M'<<24 | 'S'<<16 | 'E'<<8 | 'T'; //'MSET';
|
||||
static const int NAVMESHSET_VERSION = 1;
|
||||
|
||||
struct NavMeshSetHeader
|
||||
{
|
||||
int magic;
|
||||
int version;
|
||||
int numTiles;
|
||||
dtNavMeshParams params;
|
||||
};
|
||||
|
||||
struct NavMeshTileHeader
|
||||
{
|
||||
dtTileRef tileRef;
|
||||
int dataSize;
|
||||
};
|
||||
|
||||
dtNavMesh* Sample::loadAll(const char* path)
|
||||
{
|
||||
FILE* fp = fopen(path, "rb");
|
||||
if (!fp) return 0;
|
||||
|
||||
// Read header.
|
||||
NavMeshSetHeader header;
|
||||
size_t readLen = fread(&header, sizeof(NavMeshSetHeader), 1, fp);
|
||||
if (readLen != 1)
|
||||
{
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
if (header.magic != NAVMESHSET_MAGIC)
|
||||
{
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
if (header.version != NAVMESHSET_VERSION)
|
||||
{
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
dtNavMesh* mesh = dtAllocNavMesh();
|
||||
if (!mesh)
|
||||
{
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
dtStatus status = mesh->init(&header.params);
|
||||
if (dtStatusFailed(status))
|
||||
{
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Read tiles.
|
||||
for (int i = 0; i < header.numTiles; ++i)
|
||||
{
|
||||
NavMeshTileHeader tileHeader;
|
||||
readLen = fread(&tileHeader, sizeof(tileHeader), 1, fp);
|
||||
if (readLen != 1)
|
||||
{
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!tileHeader.tileRef || !tileHeader.dataSize)
|
||||
break;
|
||||
|
||||
unsigned char* data = (unsigned char*)dtAlloc(tileHeader.dataSize, DT_ALLOC_PERM);
|
||||
if (!data) break;
|
||||
memset(data, 0, tileHeader.dataSize);
|
||||
readLen = fread(data, tileHeader.dataSize, 1, fp);
|
||||
if (readLen != 1)
|
||||
{
|
||||
dtFree(data);
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
mesh->addTile(data, tileHeader.dataSize, DT_TILE_FREE_DATA, tileHeader.tileRef, 0);
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
void Sample::saveAll(const char* path, const dtNavMesh* mesh)
|
||||
{
|
||||
if (!mesh) return;
|
||||
|
||||
FILE* fp = fopen(path, "wb");
|
||||
if (!fp)
|
||||
return;
|
||||
|
||||
// Store header.
|
||||
NavMeshSetHeader header;
|
||||
header.magic = NAVMESHSET_MAGIC;
|
||||
header.version = NAVMESHSET_VERSION;
|
||||
header.numTiles = 0;
|
||||
for (int i = 0; i < mesh->getMaxTiles(); ++i)
|
||||
{
|
||||
const dtMeshTile* tile = mesh->getTile(i);
|
||||
if (!tile || !tile->header || !tile->dataSize) continue;
|
||||
header.numTiles++;
|
||||
}
|
||||
memcpy(&header.params, mesh->getParams(), sizeof(dtNavMeshParams));
|
||||
fwrite(&header, sizeof(NavMeshSetHeader), 1, fp);
|
||||
|
||||
// Store tiles.
|
||||
for (int i = 0; i < mesh->getMaxTiles(); ++i)
|
||||
{
|
||||
const dtMeshTile* tile = mesh->getTile(i);
|
||||
if (!tile || !tile->header || !tile->dataSize) continue;
|
||||
|
||||
NavMeshTileHeader tileHeader;
|
||||
tileHeader.tileRef = mesh->getTileRef(tile);
|
||||
tileHeader.dataSize = tile->dataSize;
|
||||
fwrite(&tileHeader, sizeof(tileHeader), 1, fp);
|
||||
|
||||
fwrite(tile->data, tile->dataSize, 1, fp);
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
}
|
||||
@@ -0,0 +1,317 @@
|
||||
#define _USE_MATH_DEFINES
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include "SampleInterfaces.h"
|
||||
#include "Recast.h"
|
||||
#include "RecastDebugDraw.h"
|
||||
#include "DetourDebugDraw.h"
|
||||
#include "PerfTimer.h"
|
||||
#include "SDL.h"
|
||||
#include "SDL_opengl.h"
|
||||
|
||||
#ifdef WIN32
|
||||
# define snprintf _snprintf
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
BuildContext::BuildContext() :
|
||||
m_messageCount(0),
|
||||
m_textPoolSize(0)
|
||||
{
|
||||
memset(m_messages, 0, sizeof(char*) * MAX_MESSAGES);
|
||||
|
||||
resetTimers();
|
||||
}
|
||||
|
||||
// Virtual functions for custom implementations.
|
||||
void BuildContext::doResetLog()
|
||||
{
|
||||
m_messageCount = 0;
|
||||
m_textPoolSize = 0;
|
||||
}
|
||||
|
||||
void BuildContext::doLog(const rcLogCategory category, const char* msg, const int len)
|
||||
{
|
||||
if (!len) return;
|
||||
if (m_messageCount >= MAX_MESSAGES)
|
||||
return;
|
||||
char* dst = &m_textPool[m_textPoolSize];
|
||||
int n = TEXT_POOL_SIZE - m_textPoolSize;
|
||||
if (n < 2)
|
||||
return;
|
||||
char* cat = dst;
|
||||
char* text = dst+1;
|
||||
const int maxtext = n-1;
|
||||
// Store category
|
||||
*cat = (char)category;
|
||||
// Store message
|
||||
const int count = rcMin(len+1, maxtext);
|
||||
memcpy(text, msg, count);
|
||||
text[count-1] = '\0';
|
||||
m_textPoolSize += 1 + count;
|
||||
m_messages[m_messageCount++] = dst;
|
||||
}
|
||||
|
||||
void BuildContext::doResetTimers()
|
||||
{
|
||||
for (int i = 0; i < RC_MAX_TIMERS; ++i)
|
||||
m_accTime[i] = -1;
|
||||
}
|
||||
|
||||
void BuildContext::doStartTimer(const rcTimerLabel label)
|
||||
{
|
||||
m_startTime[label] = getPerfTime();
|
||||
}
|
||||
|
||||
void BuildContext::doStopTimer(const rcTimerLabel label)
|
||||
{
|
||||
const TimeVal endTime = getPerfTime();
|
||||
const TimeVal deltaTime = endTime - m_startTime[label];
|
||||
if (m_accTime[label] == -1)
|
||||
m_accTime[label] = deltaTime;
|
||||
else
|
||||
m_accTime[label] += deltaTime;
|
||||
}
|
||||
|
||||
int BuildContext::doGetAccumulatedTime(const rcTimerLabel label) const
|
||||
{
|
||||
return getPerfTimeUsec(m_accTime[label]);
|
||||
}
|
||||
|
||||
void BuildContext::dumpLog(const char* format, ...)
|
||||
{
|
||||
// Print header.
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
vprintf(format, ap);
|
||||
va_end(ap);
|
||||
printf("\n");
|
||||
|
||||
// Print messages
|
||||
const int TAB_STOPS[4] = { 28, 36, 44, 52 };
|
||||
for (int i = 0; i < m_messageCount; ++i)
|
||||
{
|
||||
const char* msg = m_messages[i]+1;
|
||||
int n = 0;
|
||||
while (*msg)
|
||||
{
|
||||
if (*msg == '\t')
|
||||
{
|
||||
int count = 1;
|
||||
for (int j = 0; j < 4; ++j)
|
||||
{
|
||||
if (n < TAB_STOPS[j])
|
||||
{
|
||||
count = TAB_STOPS[j] - n;
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (--count)
|
||||
{
|
||||
putchar(' ');
|
||||
n++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
putchar(*msg);
|
||||
n++;
|
||||
}
|
||||
msg++;
|
||||
}
|
||||
putchar('\n');
|
||||
}
|
||||
}
|
||||
|
||||
int BuildContext::getLogCount() const
|
||||
{
|
||||
return m_messageCount;
|
||||
}
|
||||
|
||||
const char* BuildContext::getLogText(const int i) const
|
||||
{
|
||||
return m_messages[i]+1;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class GLCheckerTexture
|
||||
{
|
||||
unsigned int m_texId;
|
||||
public:
|
||||
GLCheckerTexture() : m_texId(0)
|
||||
{
|
||||
}
|
||||
|
||||
~GLCheckerTexture()
|
||||
{
|
||||
if (m_texId != 0)
|
||||
glDeleteTextures(1, &m_texId);
|
||||
}
|
||||
void bind()
|
||||
{
|
||||
if (m_texId == 0)
|
||||
{
|
||||
// Create checker pattern.
|
||||
const unsigned int col0 = duRGBA(215,215,215,255);
|
||||
const unsigned int col1 = duRGBA(255,255,255,255);
|
||||
static const int TSIZE = 64;
|
||||
unsigned int data[TSIZE*TSIZE];
|
||||
|
||||
glGenTextures(1, &m_texId);
|
||||
glBindTexture(GL_TEXTURE_2D, m_texId);
|
||||
|
||||
int level = 0;
|
||||
int size = TSIZE;
|
||||
while (size > 0)
|
||||
{
|
||||
for (int y = 0; y < size; ++y)
|
||||
for (int x = 0; x < size; ++x)
|
||||
data[x+y*size] = (x==0 || y==0) ? col0 : col1;
|
||||
glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, size,size, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
|
||||
size /= 2;
|
||||
level++;
|
||||
}
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
}
|
||||
else
|
||||
{
|
||||
glBindTexture(GL_TEXTURE_2D, m_texId);
|
||||
}
|
||||
}
|
||||
};
|
||||
GLCheckerTexture g_tex;
|
||||
|
||||
|
||||
void DebugDrawGL::depthMask(bool state)
|
||||
{
|
||||
glDepthMask(state ? GL_TRUE : GL_FALSE);
|
||||
}
|
||||
|
||||
void DebugDrawGL::texture(bool state)
|
||||
{
|
||||
if (state)
|
||||
{
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
g_tex.bind();
|
||||
}
|
||||
else
|
||||
{
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
}
|
||||
}
|
||||
|
||||
void DebugDrawGL::begin(duDebugDrawPrimitives prim, float size)
|
||||
{
|
||||
switch (prim)
|
||||
{
|
||||
case DU_DRAW_POINTS:
|
||||
glPointSize(size);
|
||||
glBegin(GL_POINTS);
|
||||
break;
|
||||
case DU_DRAW_LINES:
|
||||
glLineWidth(size);
|
||||
glBegin(GL_LINES);
|
||||
break;
|
||||
case DU_DRAW_TRIS:
|
||||
glBegin(GL_TRIANGLES);
|
||||
break;
|
||||
case DU_DRAW_QUADS:
|
||||
glBegin(GL_QUADS);
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
void DebugDrawGL::vertex(const float* pos, unsigned int color)
|
||||
{
|
||||
glColor4ubv((GLubyte*)&color);
|
||||
glVertex3fv(pos);
|
||||
}
|
||||
|
||||
void DebugDrawGL::vertex(const float x, const float y, const float z, unsigned int color)
|
||||
{
|
||||
glColor4ubv((GLubyte*)&color);
|
||||
glVertex3f(x,y,z);
|
||||
}
|
||||
|
||||
void DebugDrawGL::vertex(const float* pos, unsigned int color, const float* uv)
|
||||
{
|
||||
glColor4ubv((GLubyte*)&color);
|
||||
glTexCoord2fv(uv);
|
||||
glVertex3fv(pos);
|
||||
}
|
||||
|
||||
void DebugDrawGL::vertex(const float x, const float y, const float z, unsigned int color, const float u, const float v)
|
||||
{
|
||||
glColor4ubv((GLubyte*)&color);
|
||||
glTexCoord2f(u,v);
|
||||
glVertex3f(x,y,z);
|
||||
}
|
||||
|
||||
void DebugDrawGL::end()
|
||||
{
|
||||
glEnd();
|
||||
glLineWidth(1.0f);
|
||||
glPointSize(1.0f);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
FileIO::FileIO() :
|
||||
m_fp(0),
|
||||
m_mode(-1)
|
||||
{
|
||||
}
|
||||
|
||||
FileIO::~FileIO()
|
||||
{
|
||||
if (m_fp) fclose(m_fp);
|
||||
}
|
||||
|
||||
bool FileIO::openForWrite(const char* path)
|
||||
{
|
||||
if (m_fp) return false;
|
||||
m_fp = fopen(path, "wb");
|
||||
if (!m_fp) return false;
|
||||
m_mode = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FileIO::openForRead(const char* path)
|
||||
{
|
||||
if (m_fp) return false;
|
||||
m_fp = fopen(path, "rb");
|
||||
if (!m_fp) return false;
|
||||
m_mode = 2;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FileIO::isWriting() const
|
||||
{
|
||||
return m_mode == 1;
|
||||
}
|
||||
|
||||
bool FileIO::isReading() const
|
||||
{
|
||||
return m_mode == 2;
|
||||
}
|
||||
|
||||
bool FileIO::write(const void* ptr, const size_t size)
|
||||
{
|
||||
if (!m_fp || m_mode != 1) return false;
|
||||
fwrite(ptr, size, 1, m_fp);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FileIO::read(void* ptr, const size_t size)
|
||||
{
|
||||
if (!m_fp || m_mode != 2) return false;
|
||||
size_t readLen = fread(ptr, size, 1, m_fp);
|
||||
return readLen == 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,387 @@
|
||||
//
|
||||
// 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 "Sample_Debug.h"
|
||||
#include "InputGeom.h"
|
||||
#include "Recast.h"
|
||||
#include "DetourNavMesh.h"
|
||||
#include "RecastDebugDraw.h"
|
||||
#include "DetourDebugDraw.h"
|
||||
#include "RecastDump.h"
|
||||
#include "imgui.h"
|
||||
#include "SDL.h"
|
||||
#include "SDL_opengl.h"
|
||||
|
||||
#ifdef WIN32
|
||||
# define snprintf _snprintf
|
||||
#endif
|
||||
|
||||
/*
|
||||
static int loadBin(const char* path, unsigned char** data)
|
||||
{
|
||||
FILE* fp = fopen(path, "rb");
|
||||
if (!fp) return 0;
|
||||
fseek(fp, 0, SEEK_END);
|
||||
int size = ftell(fp);
|
||||
fseek(fp, 0, SEEK_SET);
|
||||
*data = new unsigned char[size];
|
||||
fread(*data, size, 1, fp);
|
||||
fclose(fp);
|
||||
return size;
|
||||
}
|
||||
*/
|
||||
|
||||
Sample_Debug::Sample_Debug() :
|
||||
m_chf(0),
|
||||
m_cset(0),
|
||||
m_pmesh(0)
|
||||
{
|
||||
resetCommonSettings();
|
||||
|
||||
// Test
|
||||
/* m_chf = rcAllocCompactHeightfield();
|
||||
FileIO io;
|
||||
if (!io.openForRead("test.chf"))
|
||||
{
|
||||
delete m_chf;
|
||||
m_chf = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!duReadCompactHeightfield(*m_chf, &io))
|
||||
{
|
||||
delete m_chf;
|
||||
m_chf = 0;
|
||||
}
|
||||
}*/
|
||||
|
||||
/* if (m_chf)
|
||||
{
|
||||
unsigned short ymin = 0xffff;
|
||||
unsigned short ymax = 0;
|
||||
for (int i = 0; i < m_chf->spanCount; ++i)
|
||||
{
|
||||
const rcCompactSpan& s = m_chf->spans[i];
|
||||
if (s.y < ymin) ymin = s.y;
|
||||
if (s.y > ymax) ymax = s.y;
|
||||
}
|
||||
printf("ymin=%d ymax=%d\n", (int)ymin, (int)ymax);
|
||||
|
||||
int maxSpans = 0;
|
||||
for (int i = 0; i < m_chf->width*m_chf->height; ++i)
|
||||
{
|
||||
maxSpans = rcMax(maxSpans, (int)m_chf->cells[i].count);
|
||||
}
|
||||
printf("maxSpans = %d\n", maxSpans);
|
||||
}*/
|
||||
|
||||
|
||||
/* const float orig[3] = {0,0,0};
|
||||
m_navMesh = new dtNavMesh;
|
||||
m_navMesh->init(orig, 133.333f,133.333f, 2048, 4096, 4096);
|
||||
|
||||
unsigned char* data = 0;
|
||||
int dataSize = 0;
|
||||
|
||||
// Tile_-13_-14.bin is basically just the bytes that was output by Detour. It should be loaded at X: -13 and Y: -14.
|
||||
|
||||
dataSize = loadBin("Tile_-13_-13.bin", &data);
|
||||
if (dataSize > 0)
|
||||
{
|
||||
m_navMesh->addTileAt(-13,-13, data, dataSize, true);
|
||||
dtMeshHeader* header = (dtMeshHeader*)data;
|
||||
vcopy(m_bmin, header->bmin);
|
||||
vcopy(m_bmax, header->bmax);
|
||||
}
|
||||
|
||||
dataSize = loadBin("Tile_-13_-14.bin", &data);
|
||||
if (dataSize > 0)
|
||||
{
|
||||
m_navMesh->addTileAt(-13,-14, data, dataSize, true);
|
||||
}
|
||||
|
||||
dataSize = loadBin("Tile_-14_-14.bin", &data);
|
||||
if (dataSize > 0)
|
||||
{
|
||||
m_navMesh->addTileAt(-14,-14, data, dataSize, true);
|
||||
}
|
||||
|
||||
const float halfExtents[3] = {40,100,40};
|
||||
const float center[3] = { -1667.9491f, 135.52649f, -1680.6149f };
|
||||
dtQueryFilter filter;
|
||||
m_ref = m_navMesh->findNearestPoly(center, halfExtents, &filter, 0);
|
||||
|
||||
vcopy(m_halfExtents, halfExtents);
|
||||
vcopy(m_center, center);*/
|
||||
|
||||
|
||||
{
|
||||
m_cset = rcAllocContourSet();
|
||||
if (m_cset)
|
||||
{
|
||||
FileIO io;
|
||||
if (io.openForRead("PathSet_TMP_NA_PathingTestAReg1_1_2_CS.rc"))
|
||||
{
|
||||
duReadContourSet(*m_cset, &io);
|
||||
|
||||
printf("bmin=(%f,%f,%f) bmax=(%f,%f,%f)\n",
|
||||
m_cset->bmin[0], m_cset->bmin[1], m_cset->bmin[2],
|
||||
m_cset->bmax[0], m_cset->bmax[1], m_cset->bmax[2]);
|
||||
printf("cs=%f ch=%f\n", m_cset->cs, m_cset->ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("could not open test.cset\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Could not alloc cset\n");
|
||||
}
|
||||
|
||||
|
||||
/* if (m_cset)
|
||||
{
|
||||
m_pmesh = rcAllocPolyMesh();
|
||||
if (m_pmesh)
|
||||
{
|
||||
rcBuildPolyMesh(m_ctx, *m_cset, 6, *m_pmesh);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Sample_Debug::~Sample_Debug()
|
||||
{
|
||||
rcFreeCompactHeightfield(m_chf);
|
||||
rcFreeContourSet(m_cset);
|
||||
rcFreePolyMesh(m_pmesh);
|
||||
}
|
||||
|
||||
void Sample_Debug::handleSettings()
|
||||
{
|
||||
}
|
||||
|
||||
void Sample_Debug::handleTools()
|
||||
{
|
||||
}
|
||||
|
||||
void Sample_Debug::handleDebugMode()
|
||||
{
|
||||
}
|
||||
|
||||
void Sample_Debug::handleRender()
|
||||
{
|
||||
if (m_chf)
|
||||
{
|
||||
duDebugDrawCompactHeightfieldRegions(&m_dd, *m_chf);
|
||||
// duDebugDrawCompactHeightfieldSolid(&dd, *m_chf);
|
||||
}
|
||||
|
||||
if (m_navMesh)
|
||||
duDebugDrawNavMesh(&m_dd, *m_navMesh, DU_DRAWNAVMESH_OFFMESHCONS);
|
||||
|
||||
if (m_ref && m_navMesh)
|
||||
duDebugDrawNavMeshPoly(&m_dd, *m_navMesh, m_ref, duRGBA(255,0,0,128));
|
||||
|
||||
/* float bmin[3], bmax[3];
|
||||
rcVsub(bmin, m_center, m_halfExtents);
|
||||
rcVadd(bmax, m_center, m_halfExtents);
|
||||
duDebugDrawBoxWire(&dd, bmin[0],bmin[1],bmin[2], bmax[0],bmax[1],bmax[2], duRGBA(255,255,255,128), 1.0f);
|
||||
duDebugDrawCross(&dd, m_center[0], m_center[1], m_center[2], 1.0f, duRGBA(255,255,255,128), 2.0f);*/
|
||||
|
||||
if (m_cset)
|
||||
{
|
||||
duDebugDrawRawContours(&m_dd, *m_cset, 0.25f);
|
||||
duDebugDrawContours(&m_dd, *m_cset);
|
||||
}
|
||||
|
||||
if (m_pmesh)
|
||||
{
|
||||
duDebugDrawPolyMesh(&m_dd, *m_pmesh);
|
||||
}
|
||||
|
||||
/*
|
||||
dd.depthMask(false);
|
||||
{
|
||||
const float bmin[3] = {-32.000004f,-11.488281f,-115.343544f};
|
||||
const float cs = 0.300000f;
|
||||
const float ch = 0.200000f;
|
||||
const int verts[] = {
|
||||
158,46,336,0,
|
||||
157,47,331,0,
|
||||
161,53,330,0,
|
||||
162,52,335,0,
|
||||
158,46,336,0,
|
||||
154,46,339,5,
|
||||
161,46,365,5,
|
||||
171,46,385,5,
|
||||
174,46,400,5,
|
||||
177,46,404,5,
|
||||
177,46,410,5,
|
||||
183,46,416,5,
|
||||
188,49,416,5,
|
||||
193,52,411,6,
|
||||
194,53,382,6,
|
||||
188,52,376,6,
|
||||
188,57,363,6,
|
||||
174,57,349,6,
|
||||
174,60,342,6,
|
||||
168,58,336,6,
|
||||
167,59,328,6,
|
||||
162,55,324,6,
|
||||
159,53,324,5,
|
||||
152,46,328,5,
|
||||
151,46,336,5,
|
||||
154,46,339,5,
|
||||
158,46,336,0,
|
||||
160,46,340,0,
|
||||
164,52,339,0,
|
||||
168,55,343,0,
|
||||
168,50,351,0,
|
||||
182,54,364,0,
|
||||
182,47,378,0,
|
||||
188,50,383,0,
|
||||
188,49,409,0,
|
||||
183,46,409,0,
|
||||
183,46,403,0,
|
||||
180,46,399,0,
|
||||
177,46,384,0,
|
||||
165,46,359,0,
|
||||
160,46,340,0,
|
||||
};
|
||||
const int nverts = sizeof(verts)/(sizeof(int)*4);
|
||||
|
||||
const unsigned int colln = duRGBA(255,255,255,128);
|
||||
dd.begin(DU_DRAW_LINES, 1.0f);
|
||||
for (int i = 0, j = nverts-1; i < nverts; j=i++)
|
||||
{
|
||||
const int* va = &verts[j*4];
|
||||
const int* vb = &verts[i*4];
|
||||
dd.vertex(bmin[0]+va[0]*cs, bmin[1]+va[1]*ch+j*0.01f, bmin[2]+va[2]*cs, colln);
|
||||
dd.vertex(bmin[0]+vb[0]*cs, bmin[1]+vb[1]*ch+i*0.01f, bmin[2]+vb[2]*cs, colln);
|
||||
}
|
||||
dd.end();
|
||||
|
||||
const unsigned int colpt = duRGBA(255,255,255,255);
|
||||
dd.begin(DU_DRAW_POINTS, 3.0f);
|
||||
for (int i = 0, j = nverts-1; i < nverts; j=i++)
|
||||
{
|
||||
const int* va = &verts[j*4];
|
||||
dd.vertex(bmin[0]+va[0]*cs, bmin[1]+va[1]*ch+j*0.01f, bmin[2]+va[2]*cs, colpt);
|
||||
}
|
||||
dd.end();
|
||||
|
||||
extern int triangulate(int n, const int* verts, int* indices, int* tris);
|
||||
|
||||
static int indices[nverts];
|
||||
static int tris[nverts*3];
|
||||
for (int j = 0; j < nverts; ++j)
|
||||
indices[j] = j;
|
||||
|
||||
static int ntris = 0;
|
||||
if (!ntris)
|
||||
{
|
||||
ntris = triangulate(nverts, verts, &indices[0], &tris[0]);
|
||||
if (ntris < 0) ntris = -ntris;
|
||||
}
|
||||
|
||||
const unsigned int coltri = duRGBA(255,255,255,64);
|
||||
dd.begin(DU_DRAW_TRIS);
|
||||
for (int i = 0; i < ntris*3; ++i)
|
||||
{
|
||||
const int* va = &verts[indices[tris[i]]*4];
|
||||
dd.vertex(bmin[0]+va[0]*cs, bmin[1]+va[1]*ch, bmin[2]+va[2]*cs, coltri);
|
||||
}
|
||||
dd.end();
|
||||
|
||||
}
|
||||
dd.depthMask(true);*/
|
||||
}
|
||||
|
||||
void Sample_Debug::handleRenderOverlay(double* /*proj*/, double* /*model*/, int* /*view*/)
|
||||
{
|
||||
}
|
||||
|
||||
void Sample_Debug::handleMeshChanged(InputGeom* geom)
|
||||
{
|
||||
m_geom = geom;
|
||||
}
|
||||
|
||||
const float* Sample_Debug::getBoundsMin()
|
||||
{
|
||||
if (m_cset)
|
||||
return m_cset->bmin;
|
||||
if (m_chf)
|
||||
return m_chf->bmin;
|
||||
if (m_navMesh)
|
||||
return m_bmin;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const float* Sample_Debug::getBoundsMax()
|
||||
{
|
||||
if (m_cset)
|
||||
return m_cset->bmax;
|
||||
if (m_chf)
|
||||
return m_chf->bmax;
|
||||
if (m_navMesh)
|
||||
return m_bmax;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Sample_Debug::handleClick(const float* s, const float* p, bool shift)
|
||||
{
|
||||
if (m_tool)
|
||||
m_tool->handleClick(s, p, shift);
|
||||
}
|
||||
|
||||
void Sample_Debug::handleToggle()
|
||||
{
|
||||
if (m_tool)
|
||||
m_tool->handleToggle();
|
||||
}
|
||||
|
||||
bool Sample_Debug::handleBuild()
|
||||
{
|
||||
|
||||
if (m_chf)
|
||||
{
|
||||
rcFreeContourSet(m_cset);
|
||||
m_cset = 0;
|
||||
|
||||
// Create contours.
|
||||
m_cset = rcAllocContourSet();
|
||||
if (!m_cset)
|
||||
{
|
||||
m_ctx->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'cset'.");
|
||||
return false;
|
||||
}
|
||||
if (!rcBuildContours(m_ctx, *m_chf, /*m_cfg.maxSimplificationError*/1.3f, /*m_cfg.maxEdgeLen*/12, *m_cset))
|
||||
{
|
||||
m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not create contours.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,755 @@
|
||||
//
|
||||
// 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 <string.h>
|
||||
#include "SDL.h"
|
||||
#include "SDL_opengl.h"
|
||||
#include "imgui.h"
|
||||
#include "InputGeom.h"
|
||||
#include "Sample.h"
|
||||
#include "Sample_SoloMesh.h"
|
||||
#include "Recast.h"
|
||||
#include "RecastDebugDraw.h"
|
||||
#include "RecastDump.h"
|
||||
#include "DetourNavMesh.h"
|
||||
#include "DetourNavMeshBuilder.h"
|
||||
#include "DetourDebugDraw.h"
|
||||
#include "NavMeshTesterTool.h"
|
||||
#include "NavMeshPruneTool.h"
|
||||
#include "OffMeshConnectionTool.h"
|
||||
#include "ConvexVolumeTool.h"
|
||||
#include "CrowdTool.h"
|
||||
|
||||
#ifdef WIN32
|
||||
# define snprintf _snprintf
|
||||
#endif
|
||||
|
||||
|
||||
Sample_SoloMesh::Sample_SoloMesh() :
|
||||
m_keepInterResults(true),
|
||||
m_totalBuildTimeMs(0),
|
||||
m_triareas(0),
|
||||
m_solid(0),
|
||||
m_chf(0),
|
||||
m_cset(0),
|
||||
m_pmesh(0),
|
||||
m_dmesh(0),
|
||||
m_drawMode(DRAWMODE_NAVMESH)
|
||||
{
|
||||
setTool(new NavMeshTesterTool);
|
||||
}
|
||||
|
||||
Sample_SoloMesh::~Sample_SoloMesh()
|
||||
{
|
||||
cleanup();
|
||||
}
|
||||
|
||||
void Sample_SoloMesh::cleanup()
|
||||
{
|
||||
delete [] m_triareas;
|
||||
m_triareas = 0;
|
||||
rcFreeHeightField(m_solid);
|
||||
m_solid = 0;
|
||||
rcFreeCompactHeightfield(m_chf);
|
||||
m_chf = 0;
|
||||
rcFreeContourSet(m_cset);
|
||||
m_cset = 0;
|
||||
rcFreePolyMesh(m_pmesh);
|
||||
m_pmesh = 0;
|
||||
rcFreePolyMeshDetail(m_dmesh);
|
||||
m_dmesh = 0;
|
||||
dtFreeNavMesh(m_navMesh);
|
||||
m_navMesh = 0;
|
||||
}
|
||||
|
||||
void Sample_SoloMesh::handleSettings()
|
||||
{
|
||||
Sample::handleCommonSettings();
|
||||
|
||||
if (imguiCheck("Keep Itermediate Results", m_keepInterResults))
|
||||
m_keepInterResults = !m_keepInterResults;
|
||||
|
||||
imguiSeparator();
|
||||
|
||||
imguiIndent();
|
||||
imguiIndent();
|
||||
|
||||
if (imguiButton("Save"))
|
||||
{
|
||||
Sample::saveAll("solo_navmesh.bin", m_navMesh);
|
||||
}
|
||||
|
||||
if (imguiButton("Load"))
|
||||
{
|
||||
dtFreeNavMesh(m_navMesh);
|
||||
m_navMesh = Sample::loadAll("solo_navmesh.bin");
|
||||
m_navQuery->init(m_navMesh, 2048);
|
||||
}
|
||||
|
||||
imguiUnindent();
|
||||
imguiUnindent();
|
||||
|
||||
char msg[64];
|
||||
snprintf(msg, 64, "Build Time: %.1fms", m_totalBuildTimeMs);
|
||||
imguiLabel(msg);
|
||||
|
||||
imguiSeparator();
|
||||
}
|
||||
|
||||
void Sample_SoloMesh::handleTools()
|
||||
{
|
||||
int type = !m_tool ? TOOL_NONE : m_tool->type();
|
||||
|
||||
if (imguiCheck("Test Navmesh", type == TOOL_NAVMESH_TESTER))
|
||||
{
|
||||
setTool(new NavMeshTesterTool);
|
||||
}
|
||||
if (imguiCheck("Prune Navmesh", type == TOOL_NAVMESH_PRUNE))
|
||||
{
|
||||
setTool(new NavMeshPruneTool);
|
||||
}
|
||||
if (imguiCheck("Create Off-Mesh Connections", type == TOOL_OFFMESH_CONNECTION))
|
||||
{
|
||||
setTool(new OffMeshConnectionTool);
|
||||
}
|
||||
if (imguiCheck("Create Convex Volumes", type == TOOL_CONVEX_VOLUME))
|
||||
{
|
||||
setTool(new ConvexVolumeTool);
|
||||
}
|
||||
if (imguiCheck("Create Crowds", type == TOOL_CROWD))
|
||||
{
|
||||
setTool(new CrowdTool);
|
||||
}
|
||||
|
||||
imguiSeparatorLine();
|
||||
|
||||
imguiIndent();
|
||||
|
||||
if (m_tool)
|
||||
m_tool->handleMenu();
|
||||
|
||||
imguiUnindent();
|
||||
|
||||
}
|
||||
|
||||
void Sample_SoloMesh::handleDebugMode()
|
||||
{
|
||||
// Check which modes are valid.
|
||||
bool valid[MAX_DRAWMODE];
|
||||
for (int i = 0; i < MAX_DRAWMODE; ++i)
|
||||
valid[i] = false;
|
||||
|
||||
if (m_geom)
|
||||
{
|
||||
valid[DRAWMODE_NAVMESH] = m_navMesh != 0;
|
||||
valid[DRAWMODE_NAVMESH_TRANS] = m_navMesh != 0;
|
||||
valid[DRAWMODE_NAVMESH_BVTREE] = m_navMesh != 0;
|
||||
valid[DRAWMODE_NAVMESH_NODES] = m_navQuery != 0;
|
||||
valid[DRAWMODE_NAVMESH_INVIS] = m_navMesh != 0;
|
||||
valid[DRAWMODE_MESH] = true;
|
||||
valid[DRAWMODE_VOXELS] = m_solid != 0;
|
||||
valid[DRAWMODE_VOXELS_WALKABLE] = m_solid != 0;
|
||||
valid[DRAWMODE_COMPACT] = m_chf != 0;
|
||||
valid[DRAWMODE_COMPACT_DISTANCE] = m_chf != 0;
|
||||
valid[DRAWMODE_COMPACT_REGIONS] = m_chf != 0;
|
||||
valid[DRAWMODE_REGION_CONNECTIONS] = m_cset != 0;
|
||||
valid[DRAWMODE_RAW_CONTOURS] = m_cset != 0;
|
||||
valid[DRAWMODE_BOTH_CONTOURS] = m_cset != 0;
|
||||
valid[DRAWMODE_CONTOURS] = m_cset != 0;
|
||||
valid[DRAWMODE_POLYMESH] = m_pmesh != 0;
|
||||
valid[DRAWMODE_POLYMESH_DETAIL] = m_dmesh != 0;
|
||||
}
|
||||
|
||||
int unavail = 0;
|
||||
for (int i = 0; i < MAX_DRAWMODE; ++i)
|
||||
if (!valid[i]) unavail++;
|
||||
|
||||
if (unavail == MAX_DRAWMODE)
|
||||
return;
|
||||
|
||||
imguiLabel("Draw");
|
||||
if (imguiCheck("Input Mesh", m_drawMode == DRAWMODE_MESH, valid[DRAWMODE_MESH]))
|
||||
m_drawMode = DRAWMODE_MESH;
|
||||
if (imguiCheck("Navmesh", m_drawMode == DRAWMODE_NAVMESH, valid[DRAWMODE_NAVMESH]))
|
||||
m_drawMode = DRAWMODE_NAVMESH;
|
||||
if (imguiCheck("Navmesh Invis", m_drawMode == DRAWMODE_NAVMESH_INVIS, valid[DRAWMODE_NAVMESH_INVIS]))
|
||||
m_drawMode = DRAWMODE_NAVMESH_INVIS;
|
||||
if (imguiCheck("Navmesh Trans", m_drawMode == DRAWMODE_NAVMESH_TRANS, valid[DRAWMODE_NAVMESH_TRANS]))
|
||||
m_drawMode = DRAWMODE_NAVMESH_TRANS;
|
||||
if (imguiCheck("Navmesh BVTree", m_drawMode == DRAWMODE_NAVMESH_BVTREE, valid[DRAWMODE_NAVMESH_BVTREE]))
|
||||
m_drawMode = DRAWMODE_NAVMESH_BVTREE;
|
||||
if (imguiCheck("Navmesh Nodes", m_drawMode == DRAWMODE_NAVMESH_NODES, valid[DRAWMODE_NAVMESH_NODES]))
|
||||
m_drawMode = DRAWMODE_NAVMESH_NODES;
|
||||
if (imguiCheck("Voxels", m_drawMode == DRAWMODE_VOXELS, valid[DRAWMODE_VOXELS]))
|
||||
m_drawMode = DRAWMODE_VOXELS;
|
||||
if (imguiCheck("Walkable Voxels", m_drawMode == DRAWMODE_VOXELS_WALKABLE, valid[DRAWMODE_VOXELS_WALKABLE]))
|
||||
m_drawMode = DRAWMODE_VOXELS_WALKABLE;
|
||||
if (imguiCheck("Compact", m_drawMode == DRAWMODE_COMPACT, valid[DRAWMODE_COMPACT]))
|
||||
m_drawMode = DRAWMODE_COMPACT;
|
||||
if (imguiCheck("Compact Distance", m_drawMode == DRAWMODE_COMPACT_DISTANCE, valid[DRAWMODE_COMPACT_DISTANCE]))
|
||||
m_drawMode = DRAWMODE_COMPACT_DISTANCE;
|
||||
if (imguiCheck("Compact Regions", m_drawMode == DRAWMODE_COMPACT_REGIONS, valid[DRAWMODE_COMPACT_REGIONS]))
|
||||
m_drawMode = DRAWMODE_COMPACT_REGIONS;
|
||||
if (imguiCheck("Region Connections", m_drawMode == DRAWMODE_REGION_CONNECTIONS, valid[DRAWMODE_REGION_CONNECTIONS]))
|
||||
m_drawMode = DRAWMODE_REGION_CONNECTIONS;
|
||||
if (imguiCheck("Raw Contours", m_drawMode == DRAWMODE_RAW_CONTOURS, valid[DRAWMODE_RAW_CONTOURS]))
|
||||
m_drawMode = DRAWMODE_RAW_CONTOURS;
|
||||
if (imguiCheck("Both Contours", m_drawMode == DRAWMODE_BOTH_CONTOURS, valid[DRAWMODE_BOTH_CONTOURS]))
|
||||
m_drawMode = DRAWMODE_BOTH_CONTOURS;
|
||||
if (imguiCheck("Contours", m_drawMode == DRAWMODE_CONTOURS, valid[DRAWMODE_CONTOURS]))
|
||||
m_drawMode = DRAWMODE_CONTOURS;
|
||||
if (imguiCheck("Poly Mesh", m_drawMode == DRAWMODE_POLYMESH, valid[DRAWMODE_POLYMESH]))
|
||||
m_drawMode = DRAWMODE_POLYMESH;
|
||||
if (imguiCheck("Poly Mesh Detail", m_drawMode == DRAWMODE_POLYMESH_DETAIL, valid[DRAWMODE_POLYMESH_DETAIL]))
|
||||
m_drawMode = DRAWMODE_POLYMESH_DETAIL;
|
||||
|
||||
if (unavail)
|
||||
{
|
||||
imguiValue("Tick 'Keep Itermediate Results'");
|
||||
imguiValue("to see more debug mode options.");
|
||||
}
|
||||
}
|
||||
|
||||
void Sample_SoloMesh::handleRender()
|
||||
{
|
||||
if (!m_geom || !m_geom->getMesh())
|
||||
return;
|
||||
|
||||
glEnable(GL_FOG);
|
||||
glDepthMask(GL_TRUE);
|
||||
|
||||
const float texScale = 1.0f / (m_cellSize * 10.0f);
|
||||
|
||||
if (m_drawMode != DRAWMODE_NAVMESH_TRANS)
|
||||
{
|
||||
// Draw mesh
|
||||
duDebugDrawTriMeshSlope(&m_dd, m_geom->getMesh()->getVerts(), m_geom->getMesh()->getVertCount(),
|
||||
m_geom->getMesh()->getTris(), m_geom->getMesh()->getNormals(), m_geom->getMesh()->getTriCount(),
|
||||
m_agentMaxSlope, texScale);
|
||||
m_geom->drawOffMeshConnections(&m_dd);
|
||||
}
|
||||
|
||||
glDisable(GL_FOG);
|
||||
glDepthMask(GL_FALSE);
|
||||
|
||||
// Draw bounds
|
||||
const float* bmin = m_geom->getNavMeshBoundsMin();
|
||||
const float* bmax = m_geom->getNavMeshBoundsMax();
|
||||
duDebugDrawBoxWire(&m_dd, bmin[0],bmin[1],bmin[2], bmax[0],bmax[1],bmax[2], duRGBA(255,255,255,128), 1.0f);
|
||||
m_dd.begin(DU_DRAW_POINTS, 5.0f);
|
||||
m_dd.vertex(bmin[0],bmin[1],bmin[2],duRGBA(255,255,255,128));
|
||||
m_dd.end();
|
||||
|
||||
if (m_navMesh && m_navQuery &&
|
||||
(m_drawMode == DRAWMODE_NAVMESH ||
|
||||
m_drawMode == DRAWMODE_NAVMESH_TRANS ||
|
||||
m_drawMode == DRAWMODE_NAVMESH_BVTREE ||
|
||||
m_drawMode == DRAWMODE_NAVMESH_NODES ||
|
||||
m_drawMode == DRAWMODE_NAVMESH_INVIS))
|
||||
{
|
||||
if (m_drawMode != DRAWMODE_NAVMESH_INVIS)
|
||||
duDebugDrawNavMeshWithClosedList(&m_dd, *m_navMesh, *m_navQuery, m_navMeshDrawFlags);
|
||||
if (m_drawMode == DRAWMODE_NAVMESH_BVTREE)
|
||||
duDebugDrawNavMeshBVTree(&m_dd, *m_navMesh);
|
||||
if (m_drawMode == DRAWMODE_NAVMESH_NODES)
|
||||
duDebugDrawNavMeshNodes(&m_dd, *m_navQuery);
|
||||
duDebugDrawNavMeshPolysWithFlags(&m_dd, *m_navMesh, SAMPLE_POLYFLAGS_DISABLED, duRGBA(0,0,0,128));
|
||||
}
|
||||
|
||||
glDepthMask(GL_TRUE);
|
||||
|
||||
if (m_chf && m_drawMode == DRAWMODE_COMPACT)
|
||||
duDebugDrawCompactHeightfieldSolid(&m_dd, *m_chf);
|
||||
|
||||
if (m_chf && m_drawMode == DRAWMODE_COMPACT_DISTANCE)
|
||||
duDebugDrawCompactHeightfieldDistance(&m_dd, *m_chf);
|
||||
if (m_chf && m_drawMode == DRAWMODE_COMPACT_REGIONS)
|
||||
duDebugDrawCompactHeightfieldRegions(&m_dd, *m_chf);
|
||||
if (m_solid && m_drawMode == DRAWMODE_VOXELS)
|
||||
{
|
||||
glEnable(GL_FOG);
|
||||
duDebugDrawHeightfieldSolid(&m_dd, *m_solid);
|
||||
glDisable(GL_FOG);
|
||||
}
|
||||
if (m_solid && m_drawMode == DRAWMODE_VOXELS_WALKABLE)
|
||||
{
|
||||
glEnable(GL_FOG);
|
||||
duDebugDrawHeightfieldWalkable(&m_dd, *m_solid);
|
||||
glDisable(GL_FOG);
|
||||
}
|
||||
if (m_cset && m_drawMode == DRAWMODE_RAW_CONTOURS)
|
||||
{
|
||||
glDepthMask(GL_FALSE);
|
||||
duDebugDrawRawContours(&m_dd, *m_cset);
|
||||
glDepthMask(GL_TRUE);
|
||||
}
|
||||
if (m_cset && m_drawMode == DRAWMODE_BOTH_CONTOURS)
|
||||
{
|
||||
glDepthMask(GL_FALSE);
|
||||
duDebugDrawRawContours(&m_dd, *m_cset, 0.5f);
|
||||
duDebugDrawContours(&m_dd, *m_cset);
|
||||
glDepthMask(GL_TRUE);
|
||||
}
|
||||
if (m_cset && m_drawMode == DRAWMODE_CONTOURS)
|
||||
{
|
||||
glDepthMask(GL_FALSE);
|
||||
duDebugDrawContours(&m_dd, *m_cset);
|
||||
glDepthMask(GL_TRUE);
|
||||
}
|
||||
if (m_chf && m_cset && m_drawMode == DRAWMODE_REGION_CONNECTIONS)
|
||||
{
|
||||
duDebugDrawCompactHeightfieldRegions(&m_dd, *m_chf);
|
||||
|
||||
glDepthMask(GL_FALSE);
|
||||
duDebugDrawRegionConnections(&m_dd, *m_cset);
|
||||
glDepthMask(GL_TRUE);
|
||||
}
|
||||
if (m_pmesh && m_drawMode == DRAWMODE_POLYMESH)
|
||||
{
|
||||
glDepthMask(GL_FALSE);
|
||||
duDebugDrawPolyMesh(&m_dd, *m_pmesh);
|
||||
glDepthMask(GL_TRUE);
|
||||
}
|
||||
if (m_dmesh && m_drawMode == DRAWMODE_POLYMESH_DETAIL)
|
||||
{
|
||||
glDepthMask(GL_FALSE);
|
||||
duDebugDrawPolyMeshDetail(&m_dd, *m_dmesh);
|
||||
glDepthMask(GL_TRUE);
|
||||
}
|
||||
|
||||
m_geom->drawConvexVolumes(&m_dd);
|
||||
|
||||
if (m_tool)
|
||||
m_tool->handleRender();
|
||||
renderToolStates();
|
||||
|
||||
glDepthMask(GL_TRUE);
|
||||
}
|
||||
|
||||
void Sample_SoloMesh::handleRenderOverlay(double* proj, double* model, int* view)
|
||||
{
|
||||
if (m_tool)
|
||||
m_tool->handleRenderOverlay(proj, model, view);
|
||||
renderOverlayToolStates(proj, model, view);
|
||||
}
|
||||
|
||||
void Sample_SoloMesh::handleMeshChanged(class InputGeom* geom)
|
||||
{
|
||||
Sample::handleMeshChanged(geom);
|
||||
|
||||
dtFreeNavMesh(m_navMesh);
|
||||
m_navMesh = 0;
|
||||
|
||||
if (m_tool)
|
||||
{
|
||||
m_tool->reset();
|
||||
m_tool->init(this);
|
||||
}
|
||||
resetToolStates();
|
||||
initToolStates(this);
|
||||
}
|
||||
|
||||
|
||||
bool Sample_SoloMesh::handleBuild()
|
||||
{
|
||||
if (!m_geom || !m_geom->getMesh())
|
||||
{
|
||||
m_ctx->log(RC_LOG_ERROR, "buildNavigation: Input mesh is not specified.");
|
||||
return false;
|
||||
}
|
||||
|
||||
cleanup();
|
||||
|
||||
const float* bmin = m_geom->getNavMeshBoundsMin();
|
||||
const float* bmax = m_geom->getNavMeshBoundsMax();
|
||||
const float* verts = m_geom->getMesh()->getVerts();
|
||||
const int nverts = m_geom->getMesh()->getVertCount();
|
||||
const int* tris = m_geom->getMesh()->getTris();
|
||||
const int ntris = m_geom->getMesh()->getTriCount();
|
||||
|
||||
//
|
||||
// Step 1. Initialize build config.
|
||||
//
|
||||
|
||||
// Init build configuration from GUI
|
||||
memset(&m_cfg, 0, sizeof(m_cfg));
|
||||
m_cfg.cs = m_cellSize;
|
||||
m_cfg.ch = m_cellHeight;
|
||||
m_cfg.walkableSlopeAngle = m_agentMaxSlope;
|
||||
m_cfg.walkableHeight = (int)ceilf(m_agentHeight / m_cfg.ch);
|
||||
m_cfg.walkableClimb = (int)floorf(m_agentMaxClimb / m_cfg.ch);
|
||||
m_cfg.walkableRadius = (int)ceilf(m_agentRadius / m_cfg.cs);
|
||||
m_cfg.maxEdgeLen = (int)(m_edgeMaxLen / m_cellSize);
|
||||
m_cfg.maxSimplificationError = m_edgeMaxError;
|
||||
m_cfg.minRegionArea = (int)rcSqr(m_regionMinSize); // Note: area = size*size
|
||||
m_cfg.mergeRegionArea = (int)rcSqr(m_regionMergeSize); // Note: area = size*size
|
||||
m_cfg.maxVertsPerPoly = (int)m_vertsPerPoly;
|
||||
m_cfg.detailSampleDist = m_detailSampleDist < 0.9f ? 0 : m_cellSize * m_detailSampleDist;
|
||||
m_cfg.detailSampleMaxError = m_cellHeight * m_detailSampleMaxError;
|
||||
|
||||
// Set the area where the navigation will be build.
|
||||
// Here the bounds of the input mesh are used, but the
|
||||
// area could be specified by an user defined box, etc.
|
||||
rcVcopy(m_cfg.bmin, bmin);
|
||||
rcVcopy(m_cfg.bmax, bmax);
|
||||
rcCalcGridSize(m_cfg.bmin, m_cfg.bmax, m_cfg.cs, &m_cfg.width, &m_cfg.height);
|
||||
|
||||
// Reset build times gathering.
|
||||
m_ctx->resetTimers();
|
||||
|
||||
// Start the build process.
|
||||
m_ctx->startTimer(RC_TIMER_TOTAL);
|
||||
|
||||
m_ctx->log(RC_LOG_PROGRESS, "Building navigation:");
|
||||
m_ctx->log(RC_LOG_PROGRESS, " - %d x %d cells", m_cfg.width, m_cfg.height);
|
||||
m_ctx->log(RC_LOG_PROGRESS, " - %.1fK verts, %.1fK tris", nverts/1000.0f, ntris/1000.0f);
|
||||
|
||||
//
|
||||
// Step 2. Rasterize input polygon soup.
|
||||
//
|
||||
|
||||
// Allocate voxel heightfield where we rasterize our input data to.
|
||||
m_solid = rcAllocHeightfield();
|
||||
if (!m_solid)
|
||||
{
|
||||
m_ctx->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'solid'.");
|
||||
return false;
|
||||
}
|
||||
if (!rcCreateHeightfield(m_ctx, *m_solid, m_cfg.width, m_cfg.height, m_cfg.bmin, m_cfg.bmax, m_cfg.cs, m_cfg.ch))
|
||||
{
|
||||
m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not create solid heightfield.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Allocate array that can hold triangle area types.
|
||||
// If you have multiple meshes you need to process, allocate
|
||||
// and array which can hold the max number of triangles you need to process.
|
||||
m_triareas = new unsigned char[ntris];
|
||||
if (!m_triareas)
|
||||
{
|
||||
m_ctx->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'm_triareas' (%d).", ntris);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find triangles which are walkable based on their slope and rasterize them.
|
||||
// If your input data is multiple meshes, you can transform them here, calculate
|
||||
// the are type for each of the meshes and rasterize them.
|
||||
memset(m_triareas, 0, ntris*sizeof(unsigned char));
|
||||
rcMarkWalkableTriangles(m_ctx, m_cfg.walkableSlopeAngle, verts, nverts, tris, ntris, m_triareas);
|
||||
if (!rcRasterizeTriangles(m_ctx, verts, nverts, tris, m_triareas, ntris, *m_solid, m_cfg.walkableClimb))
|
||||
{
|
||||
m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not rasterize triangles.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_keepInterResults)
|
||||
{
|
||||
delete [] m_triareas;
|
||||
m_triareas = 0;
|
||||
}
|
||||
|
||||
//
|
||||
// Step 3. Filter walkables surfaces.
|
||||
//
|
||||
|
||||
// Once all geoemtry 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.
|
||||
if (m_filterLowHangingObstacles)
|
||||
rcFilterLowHangingWalkableObstacles(m_ctx, m_cfg.walkableClimb, *m_solid);
|
||||
if (m_filterLedgeSpans)
|
||||
rcFilterLedgeSpans(m_ctx, m_cfg.walkableHeight, m_cfg.walkableClimb, *m_solid);
|
||||
if (m_filterWalkableLowHeightSpans)
|
||||
rcFilterWalkableLowHeightSpans(m_ctx, m_cfg.walkableHeight, *m_solid);
|
||||
|
||||
|
||||
//
|
||||
// Step 4. Partition walkable surface to simple regions.
|
||||
//
|
||||
|
||||
// Compact the heightfield so that it is faster to handle from now on.
|
||||
// This will result more cache coherent data as well as the neighbours
|
||||
// between walkable cells will be calculated.
|
||||
m_chf = rcAllocCompactHeightfield();
|
||||
if (!m_chf)
|
||||
{
|
||||
m_ctx->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'chf'.");
|
||||
return false;
|
||||
}
|
||||
if (!rcBuildCompactHeightfield(m_ctx, m_cfg.walkableHeight, m_cfg.walkableClimb, *m_solid, *m_chf))
|
||||
{
|
||||
m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not build compact data.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_keepInterResults)
|
||||
{
|
||||
rcFreeHeightField(m_solid);
|
||||
m_solid = 0;
|
||||
}
|
||||
|
||||
// Erode the walkable area by agent radius.
|
||||
if (!rcErodeWalkableArea(m_ctx, m_cfg.walkableRadius, *m_chf))
|
||||
{
|
||||
m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not erode.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// (Optional) Mark areas.
|
||||
const ConvexVolume* vols = m_geom->getConvexVolumes();
|
||||
for (int i = 0; i < m_geom->getConvexVolumeCount(); ++i)
|
||||
rcMarkConvexPolyArea(m_ctx, vols[i].verts, vols[i].nverts, vols[i].hmin, vols[i].hmax, (unsigned char)vols[i].area, *m_chf);
|
||||
|
||||
|
||||
// Partition the heightfield so that we can use simple algorithm later to triangulate the walkable areas.
|
||||
// There are 3 martitioning methods, each with some pros and cons:
|
||||
// 1) Watershed partitioning
|
||||
// - the classic Recast partitioning
|
||||
// - creates the nicest tessellation
|
||||
// - usually slowest
|
||||
// - partitions the heightfield into nice regions without holes or overlaps
|
||||
// - the are some corner cases where this method creates produces holes and overlaps
|
||||
// - holes may appear when a small obstacles is close to large open area (triangulation can handle this)
|
||||
// - overlaps may occur if you have narrow spiral corridors (i.e stairs), this make triangulation to fail
|
||||
// * generally the best choice if you precompute the nacmesh, use this if you have large open areas
|
||||
// 2) Monotone partioning
|
||||
// - fastest
|
||||
// - partitions the heightfield into regions without holes and overlaps (guaranteed)
|
||||
// - creates long thin polygons, which sometimes causes paths with detours
|
||||
// * use this if you want fast navmesh generation
|
||||
// 3) Layer partitoining
|
||||
// - quite fast
|
||||
// - partitions the heighfield into non-overlapping regions
|
||||
// - relies on the triangulation code to cope with holes (thus slower than monotone partitioning)
|
||||
// - produces better triangles than monotone partitioning
|
||||
// - does not have the corner cases of watershed partitioning
|
||||
// - can be slow and create a bit ugly tessellation (still better than monotone)
|
||||
// if you have large open areas with small obstacles (not a problem if you use tiles)
|
||||
// * good choice to use for tiled navmesh with medium and small sized tiles
|
||||
|
||||
if (m_partitionType == SAMPLE_PARTITION_WATERSHED)
|
||||
{
|
||||
// Prepare for region partitioning, by calculating distance field along the walkable surface.
|
||||
if (!rcBuildDistanceField(m_ctx, *m_chf))
|
||||
{
|
||||
m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not build distance field.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Partition the walkable surface into simple regions without holes.
|
||||
if (!rcBuildRegions(m_ctx, *m_chf, 0, m_cfg.minRegionArea, m_cfg.mergeRegionArea))
|
||||
{
|
||||
m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not build watershed regions.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (m_partitionType == SAMPLE_PARTITION_MONOTONE)
|
||||
{
|
||||
// Partition the walkable surface into simple regions without holes.
|
||||
// Monotone partitioning does not need distancefield.
|
||||
if (!rcBuildRegionsMonotone(m_ctx, *m_chf, 0, m_cfg.minRegionArea, m_cfg.mergeRegionArea))
|
||||
{
|
||||
m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not build monotone regions.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else // SAMPLE_PARTITION_LAYERS
|
||||
{
|
||||
// Partition the walkable surface into simple regions without holes.
|
||||
if (!rcBuildLayerRegions(m_ctx, *m_chf, 0, m_cfg.minRegionArea))
|
||||
{
|
||||
m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not build layer regions.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Step 5. Trace and simplify region contours.
|
||||
//
|
||||
|
||||
// Create contours.
|
||||
m_cset = rcAllocContourSet();
|
||||
if (!m_cset)
|
||||
{
|
||||
m_ctx->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'cset'.");
|
||||
return false;
|
||||
}
|
||||
if (!rcBuildContours(m_ctx, *m_chf, m_cfg.maxSimplificationError, m_cfg.maxEdgeLen, *m_cset))
|
||||
{
|
||||
m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not create contours.");
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
// Step 6. Build polygons mesh from contours.
|
||||
//
|
||||
|
||||
// Build polygon navmesh from the contours.
|
||||
m_pmesh = rcAllocPolyMesh();
|
||||
if (!m_pmesh)
|
||||
{
|
||||
m_ctx->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'pmesh'.");
|
||||
return false;
|
||||
}
|
||||
if (!rcBuildPolyMesh(m_ctx, *m_cset, m_cfg.maxVertsPerPoly, *m_pmesh))
|
||||
{
|
||||
m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not triangulate contours.");
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
// Step 7. Create detail mesh which allows to access approximate height on each polygon.
|
||||
//
|
||||
|
||||
m_dmesh = rcAllocPolyMeshDetail();
|
||||
if (!m_dmesh)
|
||||
{
|
||||
m_ctx->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'pmdtl'.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!rcBuildPolyMeshDetail(m_ctx, *m_pmesh, *m_chf, m_cfg.detailSampleDist, m_cfg.detailSampleMaxError, *m_dmesh))
|
||||
{
|
||||
m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not build detail mesh.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_keepInterResults)
|
||||
{
|
||||
rcFreeCompactHeightfield(m_chf);
|
||||
m_chf = 0;
|
||||
rcFreeContourSet(m_cset);
|
||||
m_cset = 0;
|
||||
}
|
||||
|
||||
// At this point the navigation mesh data is ready, you can access it from m_pmesh.
|
||||
// See duDebugDrawPolyMesh or dtCreateNavMeshData as examples how to access the data.
|
||||
|
||||
//
|
||||
// (Optional) Step 8. Create Detour data from Recast poly mesh.
|
||||
//
|
||||
|
||||
// The GUI may allow more max points per polygon than Detour can handle.
|
||||
// Only build the detour navmesh if we do not exceed the limit.
|
||||
if (m_cfg.maxVertsPerPoly <= DT_VERTS_PER_POLYGON)
|
||||
{
|
||||
unsigned char* navData = 0;
|
||||
int navDataSize = 0;
|
||||
|
||||
// Update poly flags from areas.
|
||||
for (int i = 0; i < m_pmesh->npolys; ++i)
|
||||
{
|
||||
if (m_pmesh->areas[i] == RC_WALKABLE_AREA)
|
||||
m_pmesh->areas[i] = SAMPLE_POLYAREA_GROUND;
|
||||
|
||||
if (m_pmesh->areas[i] == SAMPLE_POLYAREA_GROUND ||
|
||||
m_pmesh->areas[i] == SAMPLE_POLYAREA_GRASS ||
|
||||
m_pmesh->areas[i] == SAMPLE_POLYAREA_ROAD)
|
||||
{
|
||||
m_pmesh->flags[i] = SAMPLE_POLYFLAGS_WALK;
|
||||
}
|
||||
else if (m_pmesh->areas[i] == SAMPLE_POLYAREA_WATER)
|
||||
{
|
||||
m_pmesh->flags[i] = SAMPLE_POLYFLAGS_SWIM;
|
||||
}
|
||||
else if (m_pmesh->areas[i] == SAMPLE_POLYAREA_DOOR)
|
||||
{
|
||||
m_pmesh->flags[i] = SAMPLE_POLYFLAGS_WALK | SAMPLE_POLYFLAGS_DOOR;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
dtNavMeshCreateParams params;
|
||||
memset(¶ms, 0, sizeof(params));
|
||||
params.verts = m_pmesh->verts;
|
||||
params.vertCount = m_pmesh->nverts;
|
||||
params.polys = m_pmesh->polys;
|
||||
params.polyAreas = m_pmesh->areas;
|
||||
params.polyFlags = m_pmesh->flags;
|
||||
params.polyCount = m_pmesh->npolys;
|
||||
params.nvp = m_pmesh->nvp;
|
||||
params.detailMeshes = m_dmesh->meshes;
|
||||
params.detailVerts = m_dmesh->verts;
|
||||
params.detailVertsCount = m_dmesh->nverts;
|
||||
params.detailTris = m_dmesh->tris;
|
||||
params.detailTriCount = m_dmesh->ntris;
|
||||
params.offMeshConVerts = m_geom->getOffMeshConnectionVerts();
|
||||
params.offMeshConRad = m_geom->getOffMeshConnectionRads();
|
||||
params.offMeshConDir = m_geom->getOffMeshConnectionDirs();
|
||||
params.offMeshConAreas = m_geom->getOffMeshConnectionAreas();
|
||||
params.offMeshConFlags = m_geom->getOffMeshConnectionFlags();
|
||||
params.offMeshConUserID = m_geom->getOffMeshConnectionId();
|
||||
params.offMeshConCount = m_geom->getOffMeshConnectionCount();
|
||||
params.walkableHeight = m_agentHeight;
|
||||
params.walkableRadius = m_agentRadius;
|
||||
params.walkableClimb = m_agentMaxClimb;
|
||||
rcVcopy(params.bmin, m_pmesh->bmin);
|
||||
rcVcopy(params.bmax, m_pmesh->bmax);
|
||||
params.cs = m_cfg.cs;
|
||||
params.ch = m_cfg.ch;
|
||||
params.buildBvTree = true;
|
||||
|
||||
if (!dtCreateNavMeshData(¶ms, &navData, &navDataSize))
|
||||
{
|
||||
m_ctx->log(RC_LOG_ERROR, "Could not build Detour navmesh.");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_navMesh = dtAllocNavMesh();
|
||||
if (!m_navMesh)
|
||||
{
|
||||
dtFree(navData);
|
||||
m_ctx->log(RC_LOG_ERROR, "Could not create Detour navmesh");
|
||||
return false;
|
||||
}
|
||||
|
||||
dtStatus status;
|
||||
|
||||
status = m_navMesh->init(navData, navDataSize, DT_TILE_FREE_DATA);
|
||||
if (dtStatusFailed(status))
|
||||
{
|
||||
dtFree(navData);
|
||||
m_ctx->log(RC_LOG_ERROR, "Could not init Detour navmesh");
|
||||
return false;
|
||||
}
|
||||
|
||||
status = m_navQuery->init(m_navMesh, 2048);
|
||||
if (dtStatusFailed(status))
|
||||
{
|
||||
m_ctx->log(RC_LOG_ERROR, "Could not init Detour navmesh query");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
m_ctx->stopTimer(RC_TIMER_TOTAL);
|
||||
|
||||
// Show performance stats.
|
||||
duLogBuildTimes(*m_ctx, m_ctx->getAccumulatedTime(RC_TIMER_TOTAL));
|
||||
m_ctx->log(RC_LOG_PROGRESS, ">> Polymesh: %d vertices %d polygons", m_pmesh->nverts, m_pmesh->npolys);
|
||||
|
||||
m_totalBuildTimeMs = m_ctx->getAccumulatedTime(RC_TIMER_TOTAL)/1000.0f;
|
||||
|
||||
if (m_tool)
|
||||
m_tool->init(this);
|
||||
initToolStates(this);
|
||||
|
||||
return true;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,464 @@
|
||||
//
|
||||
// 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 <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include "TestCase.h"
|
||||
#include "DetourNavMesh.h"
|
||||
#include "DetourNavMeshQuery.h"
|
||||
#include "DetourCommon.h"
|
||||
#include "SDL.h"
|
||||
#include "SDL_opengl.h"
|
||||
#ifdef __APPLE__
|
||||
# include <OpenGL/glu.h>
|
||||
#else
|
||||
# include <GL/glu.h>
|
||||
#endif
|
||||
#include "imgui.h"
|
||||
#include "PerfTimer.h"
|
||||
|
||||
#ifdef WIN32
|
||||
#define snprintf _snprintf
|
||||
#endif
|
||||
|
||||
TestCase::TestCase() :
|
||||
m_tests(0)
|
||||
{
|
||||
}
|
||||
|
||||
TestCase::~TestCase()
|
||||
{
|
||||
Test* iter = m_tests;
|
||||
while (iter)
|
||||
{
|
||||
Test* next = iter->next;
|
||||
delete iter;
|
||||
iter = next;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static char* parseRow(char* buf, char* bufEnd, char* row, int len)
|
||||
{
|
||||
bool start = true;
|
||||
bool done = false;
|
||||
int n = 0;
|
||||
while (!done && buf < bufEnd)
|
||||
{
|
||||
char c = *buf;
|
||||
buf++;
|
||||
// multirow
|
||||
switch (c)
|
||||
{
|
||||
case '\n':
|
||||
if (start) break;
|
||||
done = true;
|
||||
break;
|
||||
case '\r':
|
||||
break;
|
||||
case '\t':
|
||||
case ' ':
|
||||
if (start) break;
|
||||
// else falls through
|
||||
default:
|
||||
start = false;
|
||||
row[n++] = c;
|
||||
if (n >= len-1)
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
row[n] = '\0';
|
||||
return buf;
|
||||
}
|
||||
|
||||
static void copyName(std::string& dst, const char* src)
|
||||
{
|
||||
// Skip white spaces
|
||||
while (*src && isspace(*src))
|
||||
src++;
|
||||
dst = src;
|
||||
}
|
||||
|
||||
bool TestCase::load(const std::string& filePath)
|
||||
{
|
||||
char* buf = 0;
|
||||
FILE* fp = fopen(filePath.c_str(), "rb");
|
||||
if (!fp)
|
||||
return false;
|
||||
if (fseek(fp, 0, SEEK_END) != 0)
|
||||
{
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
long bufSize = ftell(fp);
|
||||
if (bufSize < 0)
|
||||
{
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
if (fseek(fp, 0, SEEK_SET) != 0)
|
||||
{
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
buf = new char[bufSize];
|
||||
if (!buf)
|
||||
{
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
size_t readLen = fread(buf, bufSize, 1, fp);
|
||||
fclose(fp);
|
||||
if (readLen != 1)
|
||||
{
|
||||
delete[] buf;
|
||||
return false;
|
||||
}
|
||||
|
||||
char* src = buf;
|
||||
char* srcEnd = buf + bufSize;
|
||||
char row[512];
|
||||
while (src < srcEnd)
|
||||
{
|
||||
// Parse one row
|
||||
row[0] = '\0';
|
||||
src = parseRow(src, srcEnd, row, sizeof(row)/sizeof(char));
|
||||
if (row[0] == 's')
|
||||
{
|
||||
// Sample name.
|
||||
copyName(m_sampleName, row+1);
|
||||
}
|
||||
else if (row[0] == 'f')
|
||||
{
|
||||
// File name.
|
||||
copyName(m_geomFileName, row+1);
|
||||
}
|
||||
else if (row[0] == 'p' && row[1] == 'f')
|
||||
{
|
||||
// Pathfind test.
|
||||
Test* test = new Test;
|
||||
memset(test, 0, sizeof(Test));
|
||||
test->type = TEST_PATHFIND;
|
||||
test->expand = false;
|
||||
test->next = m_tests;
|
||||
m_tests = test;
|
||||
sscanf(row+2, "%f %f %f %f %f %f %hx %hx",
|
||||
&test->spos[0], &test->spos[1], &test->spos[2],
|
||||
&test->epos[0], &test->epos[1], &test->epos[2],
|
||||
&test->includeFlags, &test->excludeFlags);
|
||||
}
|
||||
else if (row[0] == 'r' && row[1] == 'c')
|
||||
{
|
||||
// Pathfind test.
|
||||
Test* test = new Test;
|
||||
memset(test, 0, sizeof(Test));
|
||||
test->type = TEST_RAYCAST;
|
||||
test->expand = false;
|
||||
test->next = m_tests;
|
||||
m_tests = test;
|
||||
sscanf(row+2, "%f %f %f %f %f %f %hx %hx",
|
||||
&test->spos[0], &test->spos[1], &test->spos[2],
|
||||
&test->epos[0], &test->epos[1], &test->epos[2],
|
||||
&test->includeFlags, &test->excludeFlags);
|
||||
}
|
||||
}
|
||||
|
||||
delete [] buf;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void TestCase::resetTimes()
|
||||
{
|
||||
for (Test* iter = m_tests; iter; iter = iter->next)
|
||||
{
|
||||
iter->findNearestPolyTime = 0;
|
||||
iter->findPathTime = 0;
|
||||
iter->findStraightPathTime = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void TestCase::doTests(dtNavMesh* navmesh, dtNavMeshQuery* navquery)
|
||||
{
|
||||
if (!navmesh || !navquery)
|
||||
return;
|
||||
|
||||
resetTimes();
|
||||
|
||||
static const int MAX_POLYS = 256;
|
||||
dtPolyRef polys[MAX_POLYS];
|
||||
float straight[MAX_POLYS*3];
|
||||
const float polyPickExt[3] = {2,4,2};
|
||||
|
||||
for (Test* iter = m_tests; iter; iter = iter->next)
|
||||
{
|
||||
delete [] iter->polys;
|
||||
iter->polys = 0;
|
||||
iter->npolys = 0;
|
||||
delete [] iter->straight;
|
||||
iter->straight = 0;
|
||||
iter->nstraight = 0;
|
||||
|
||||
dtQueryFilter filter;
|
||||
filter.setIncludeFlags(iter->includeFlags);
|
||||
filter.setExcludeFlags(iter->excludeFlags);
|
||||
|
||||
// Find start points
|
||||
TimeVal findNearestPolyStart = getPerfTime();
|
||||
|
||||
dtPolyRef startRef, endRef;
|
||||
navquery->findNearestPoly(iter->spos, polyPickExt, &filter, &startRef, iter->nspos);
|
||||
navquery->findNearestPoly(iter->epos, polyPickExt, &filter, &endRef, iter->nepos);
|
||||
|
||||
TimeVal findNearestPolyEnd = getPerfTime();
|
||||
iter->findNearestPolyTime += getPerfTimeUsec(findNearestPolyEnd - findNearestPolyStart);
|
||||
|
||||
if (!startRef || ! endRef)
|
||||
continue;
|
||||
|
||||
if (iter->type == TEST_PATHFIND)
|
||||
{
|
||||
// Find path
|
||||
TimeVal findPathStart = getPerfTime();
|
||||
|
||||
navquery->findPath(startRef, endRef, iter->spos, iter->epos, &filter, polys, &iter->npolys, MAX_POLYS);
|
||||
|
||||
TimeVal findPathEnd = getPerfTime();
|
||||
iter->findPathTime += getPerfTimeUsec(findPathEnd - findPathStart);
|
||||
|
||||
// Find straight path
|
||||
if (iter->npolys)
|
||||
{
|
||||
TimeVal findStraightPathStart = getPerfTime();
|
||||
|
||||
navquery->findStraightPath(iter->spos, iter->epos, polys, iter->npolys,
|
||||
straight, 0, 0, &iter->nstraight, MAX_POLYS);
|
||||
TimeVal findStraightPathEnd = getPerfTime();
|
||||
iter->findStraightPathTime += getPerfTimeUsec(findStraightPathEnd - findStraightPathStart);
|
||||
}
|
||||
|
||||
// Copy results
|
||||
if (iter->npolys)
|
||||
{
|
||||
iter->polys = new dtPolyRef[iter->npolys];
|
||||
memcpy(iter->polys, polys, sizeof(dtPolyRef)*iter->npolys);
|
||||
}
|
||||
if (iter->nstraight)
|
||||
{
|
||||
iter->straight = new float[iter->nstraight*3];
|
||||
memcpy(iter->straight, straight, sizeof(float)*3*iter->nstraight);
|
||||
}
|
||||
}
|
||||
else if (iter->type == TEST_RAYCAST)
|
||||
{
|
||||
float t = 0;
|
||||
float hitNormal[3], hitPos[3];
|
||||
|
||||
iter->straight = new float[2*3];
|
||||
iter->nstraight = 2;
|
||||
|
||||
iter->straight[0] = iter->spos[0];
|
||||
iter->straight[1] = iter->spos[1];
|
||||
iter->straight[2] = iter->spos[2];
|
||||
|
||||
TimeVal findPathStart = getPerfTime();
|
||||
|
||||
navquery->raycast(startRef, iter->spos, iter->epos, &filter, &t, hitNormal, polys, &iter->npolys, MAX_POLYS);
|
||||
|
||||
TimeVal findPathEnd = getPerfTime();
|
||||
iter->findPathTime += getPerfTimeUsec(findPathEnd - findPathStart);
|
||||
|
||||
if (t > 1)
|
||||
{
|
||||
// No hit
|
||||
dtVcopy(hitPos, iter->epos);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Hit
|
||||
dtVlerp(hitPos, iter->spos, iter->epos, t);
|
||||
}
|
||||
// Adjust height.
|
||||
if (iter->npolys > 0)
|
||||
{
|
||||
float h = 0;
|
||||
navquery->getPolyHeight(polys[iter->npolys-1], hitPos, &h);
|
||||
hitPos[1] = h;
|
||||
}
|
||||
dtVcopy(&iter->straight[3], hitPos);
|
||||
|
||||
if (iter->npolys)
|
||||
{
|
||||
iter->polys = new dtPolyRef[iter->npolys];
|
||||
memcpy(iter->polys, polys, sizeof(dtPolyRef)*iter->npolys);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
printf("Test Results:\n");
|
||||
int n = 0;
|
||||
for (Test* iter = m_tests; iter; iter = iter->next)
|
||||
{
|
||||
const int total = iter->findNearestPolyTime + iter->findPathTime + iter->findStraightPathTime;
|
||||
printf(" - Path %02d: %.4f ms\n", n, (float)total/1000.0f);
|
||||
printf(" - poly: %.4f ms\n", (float)iter->findNearestPolyTime/1000.0f);
|
||||
printf(" - path: %.4f ms\n", (float)iter->findPathTime/1000.0f);
|
||||
printf(" - straight: %.4f ms\n", (float)iter->findStraightPathTime/1000.0f);
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
||||
void TestCase::handleRender()
|
||||
{
|
||||
glLineWidth(2.0f);
|
||||
glBegin(GL_LINES);
|
||||
for (Test* iter = m_tests; iter; iter = iter->next)
|
||||
{
|
||||
float dir[3];
|
||||
dtVsub(dir, iter->epos, iter->spos);
|
||||
dtVnormalize(dir);
|
||||
glColor4ub(128,25,0,192);
|
||||
glVertex3f(iter->spos[0],iter->spos[1]-0.3f,iter->spos[2]);
|
||||
glVertex3f(iter->spos[0],iter->spos[1]+0.3f,iter->spos[2]);
|
||||
glVertex3f(iter->spos[0],iter->spos[1]+0.3f,iter->spos[2]);
|
||||
glVertex3f(iter->spos[0]+dir[0]*0.3f,iter->spos[1]+0.3f+dir[1]*0.3f,iter->spos[2]+dir[2]*0.3f);
|
||||
glColor4ub(51,102,0,129);
|
||||
glVertex3f(iter->epos[0],iter->epos[1]-0.3f,iter->epos[2]);
|
||||
glVertex3f(iter->epos[0],iter->epos[1]+0.3f,iter->epos[2]);
|
||||
|
||||
if (iter->expand)
|
||||
{
|
||||
const float s = 0.1f;
|
||||
glColor4ub(255,32,0,128);
|
||||
glVertex3f(iter->spos[0]-s,iter->spos[1],iter->spos[2]);
|
||||
glVertex3f(iter->spos[0]+s,iter->spos[1],iter->spos[2]);
|
||||
glVertex3f(iter->spos[0],iter->spos[1],iter->spos[2]-s);
|
||||
glVertex3f(iter->spos[0],iter->spos[1],iter->spos[2]+s);
|
||||
glColor4ub(255,192,0,255);
|
||||
glVertex3f(iter->nspos[0]-s,iter->nspos[1],iter->nspos[2]);
|
||||
glVertex3f(iter->nspos[0]+s,iter->nspos[1],iter->nspos[2]);
|
||||
glVertex3f(iter->nspos[0],iter->nspos[1],iter->nspos[2]-s);
|
||||
glVertex3f(iter->nspos[0],iter->nspos[1],iter->nspos[2]+s);
|
||||
|
||||
glColor4ub(255,32,0,128);
|
||||
glVertex3f(iter->epos[0]-s,iter->epos[1],iter->epos[2]);
|
||||
glVertex3f(iter->epos[0]+s,iter->epos[1],iter->epos[2]);
|
||||
glVertex3f(iter->epos[0],iter->epos[1],iter->epos[2]-s);
|
||||
glVertex3f(iter->epos[0],iter->epos[1],iter->epos[2]+s);
|
||||
glColor4ub(255,192,0,255);
|
||||
glVertex3f(iter->nepos[0]-s,iter->nepos[1],iter->nepos[2]);
|
||||
glVertex3f(iter->nepos[0]+s,iter->nepos[1],iter->nepos[2]);
|
||||
glVertex3f(iter->nepos[0],iter->nepos[1],iter->nepos[2]-s);
|
||||
glVertex3f(iter->nepos[0],iter->nepos[1],iter->nepos[2]+s);
|
||||
}
|
||||
|
||||
if (iter->expand)
|
||||
glColor4ub(255,192,0,255);
|
||||
else
|
||||
glColor4ub(0,0,0,64);
|
||||
|
||||
for (int i = 0; i < iter->nstraight-1; ++i)
|
||||
{
|
||||
glVertex3f(iter->straight[i*3+0],iter->straight[i*3+1]+0.3f,iter->straight[i*3+2]);
|
||||
glVertex3f(iter->straight[(i+1)*3+0],iter->straight[(i+1)*3+1]+0.3f,iter->straight[(i+1)*3+2]);
|
||||
}
|
||||
}
|
||||
glEnd();
|
||||
glLineWidth(1.0f);
|
||||
}
|
||||
|
||||
bool TestCase::handleRenderOverlay(double* proj, double* model, int* view)
|
||||
{
|
||||
GLdouble x, y, z;
|
||||
char text[64], subtext[64];
|
||||
int n = 0;
|
||||
|
||||
static const float LABEL_DIST = 1.0f;
|
||||
|
||||
for (Test* iter = m_tests; iter; iter = iter->next)
|
||||
{
|
||||
float pt[3], dir[3];
|
||||
if (iter->nstraight)
|
||||
{
|
||||
dtVcopy(pt, &iter->straight[3]);
|
||||
if (dtVdist(pt, iter->spos) > LABEL_DIST)
|
||||
{
|
||||
dtVsub(dir, pt, iter->spos);
|
||||
dtVnormalize(dir);
|
||||
dtVmad(pt, iter->spos, dir, LABEL_DIST);
|
||||
}
|
||||
pt[1]+=0.5f;
|
||||
}
|
||||
else
|
||||
{
|
||||
dtVsub(dir, iter->epos, iter->spos);
|
||||
dtVnormalize(dir);
|
||||
dtVmad(pt, iter->spos, dir, LABEL_DIST);
|
||||
pt[1]+=0.5f;
|
||||
}
|
||||
|
||||
if (gluProject((GLdouble)pt[0], (GLdouble)pt[1], (GLdouble)pt[2],
|
||||
model, proj, view, &x, &y, &z))
|
||||
{
|
||||
snprintf(text, 64, "Path %d\n", n);
|
||||
unsigned int col = imguiRGBA(0,0,0,128);
|
||||
if (iter->expand)
|
||||
col = imguiRGBA(255,192,0,220);
|
||||
imguiDrawText((int)x, (int)(y-25), IMGUI_ALIGN_CENTER, text, col);
|
||||
}
|
||||
n++;
|
||||
}
|
||||
|
||||
static int resScroll = 0;
|
||||
bool mouseOverMenu = imguiBeginScrollArea("Test Results", 10, view[3] - 10 - 350, 200, 350, &resScroll);
|
||||
// mouseOverMenu = true;
|
||||
|
||||
n = 0;
|
||||
for (Test* iter = m_tests; iter; iter = iter->next)
|
||||
{
|
||||
const int total = iter->findNearestPolyTime + iter->findPathTime + iter->findStraightPathTime;
|
||||
snprintf(subtext, 64, "%.4f ms", (float)total/1000.0f);
|
||||
snprintf(text, 64, "Path %d", n);
|
||||
|
||||
if (imguiCollapse(text, subtext, iter->expand))
|
||||
iter->expand = !iter->expand;
|
||||
if (iter->expand)
|
||||
{
|
||||
snprintf(text, 64, "Poly: %.4f ms", (float)iter->findNearestPolyTime/1000.0f);
|
||||
imguiValue(text);
|
||||
|
||||
snprintf(text, 64, "Path: %.4f ms", (float)iter->findPathTime/1000.0f);
|
||||
imguiValue(text);
|
||||
|
||||
snprintf(text, 64, "Straight: %.4f ms", (float)iter->findStraightPathTime/1000.0f);
|
||||
imguiValue(text);
|
||||
|
||||
imguiSeparator();
|
||||
}
|
||||
|
||||
n++;
|
||||
}
|
||||
|
||||
imguiEndScrollArea();
|
||||
|
||||
return mouseOverMenu;
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
#include "ValueHistory.h"
|
||||
#include "imgui.h"
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef WIN32
|
||||
# define snprintf _snprintf
|
||||
#endif
|
||||
|
||||
ValueHistory::ValueHistory() :
|
||||
m_hsamples(0)
|
||||
{
|
||||
for (int i = 0; i < MAX_HISTORY; ++i)
|
||||
m_samples[i] = 0;
|
||||
}
|
||||
|
||||
float ValueHistory::getSampleMin() const
|
||||
{
|
||||
float val = m_samples[0];
|
||||
for (int i = 1; i < MAX_HISTORY; ++i)
|
||||
if (m_samples[i] < val)
|
||||
val = m_samples[i];
|
||||
return val;
|
||||
}
|
||||
|
||||
float ValueHistory::getSampleMax() const
|
||||
{
|
||||
float val = m_samples[0];
|
||||
for (int i = 1; i < MAX_HISTORY; ++i)
|
||||
if (m_samples[i] > val)
|
||||
val = m_samples[i];
|
||||
return val;
|
||||
}
|
||||
|
||||
float ValueHistory::getAverage() const
|
||||
{
|
||||
float val = 0;
|
||||
for (int i = 0; i < MAX_HISTORY; ++i)
|
||||
val += m_samples[i];
|
||||
return val/(float)MAX_HISTORY;
|
||||
}
|
||||
|
||||
void GraphParams::setRect(int ix, int iy, int iw, int ih, int ipad)
|
||||
{
|
||||
x = ix;
|
||||
y = iy;
|
||||
w = iw;
|
||||
h = ih;
|
||||
pad = ipad;
|
||||
}
|
||||
|
||||
void GraphParams::setValueRange(float ivmin, float ivmax, int indiv, const char* iunits)
|
||||
{
|
||||
vmin = ivmin;
|
||||
vmax = ivmax;
|
||||
ndiv = indiv;
|
||||
strcpy(units, iunits);
|
||||
}
|
||||
|
||||
void drawGraphBackground(const GraphParams* p)
|
||||
{
|
||||
// BG
|
||||
imguiDrawRoundedRect((float)p->x, (float)p->y, (float)p->w, (float)p->h, (float)p->pad, imguiRGBA(64,64,64,128));
|
||||
|
||||
const float sy = (p->h-p->pad*2) / (p->vmax-p->vmin);
|
||||
const float oy = p->y+p->pad-p->vmin*sy;
|
||||
|
||||
char text[64];
|
||||
|
||||
// Divider Lines
|
||||
for (int i = 0; i <= p->ndiv; ++i)
|
||||
{
|
||||
const float u = (float)i/(float)p->ndiv;
|
||||
const float v = p->vmin + (p->vmax-p->vmin)*u;
|
||||
snprintf(text, 64, "%.2f %s", v, p->units);
|
||||
const float fy = oy + v*sy;
|
||||
imguiDrawText(p->x + p->w - p->pad, (int)fy-4, IMGUI_ALIGN_RIGHT, text, imguiRGBA(0,0,0,255));
|
||||
imguiDrawLine((float)p->x + (float)p->pad, fy, (float)p->x + (float)p->w - (float)p->pad - 50, fy, 1.0f, imguiRGBA(0,0,0,64));
|
||||
}
|
||||
}
|
||||
|
||||
void drawGraph(const GraphParams* p, const ValueHistory* graph,
|
||||
int idx, const char* label, const unsigned int col)
|
||||
{
|
||||
const float sx = (p->w - p->pad*2) / (float)graph->getSampleCount();
|
||||
const float sy = (p->h - p->pad*2) / (p->vmax - p->vmin);
|
||||
const float ox = (float)p->x + (float)p->pad;
|
||||
const float oy = (float)p->y + (float)p->pad - p->vmin*sy;
|
||||
|
||||
// Values
|
||||
float px=0, py=0;
|
||||
for (int i = 0; i < graph->getSampleCount()-1; ++i)
|
||||
{
|
||||
const float x = ox + i*sx;
|
||||
const float y = oy + graph->getSample(i)*sy;
|
||||
if (i > 0)
|
||||
imguiDrawLine(px,py, x,y, 2.0f, col);
|
||||
px = x;
|
||||
py = y;
|
||||
}
|
||||
|
||||
// Label
|
||||
const int size = 15;
|
||||
const int spacing = 10;
|
||||
int ix = p->x + p->w + 5;
|
||||
int iy = p->y + p->h - (idx+1)*(size+spacing);
|
||||
|
||||
imguiDrawRoundedRect((float)ix, (float)iy, (float)size, (float)size, 2.0f, col);
|
||||
|
||||
char text[64];
|
||||
snprintf(text, 64, "%.2f %s", graph->getAverage(), p->units);
|
||||
imguiDrawText(ix+size+5, iy+3, IMGUI_ALIGN_LEFT, label, imguiRGBA(255,255,255,192));
|
||||
imguiDrawText(ix+size+150, iy+3, IMGUI_ALIGN_RIGHT, text, imguiRGBA(255,255,255,128));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,676 @@
|
||||
//
|
||||
// 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 <stdio.h>
|
||||
#include <string.h>
|
||||
#define _USE_MATH_DEFINES
|
||||
#include <math.h>
|
||||
#include "imgui.h"
|
||||
|
||||
#ifdef WIN32
|
||||
# define snprintf _snprintf
|
||||
#endif
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static const unsigned TEXT_POOL_SIZE = 50000;
|
||||
static char g_textPool[TEXT_POOL_SIZE];
|
||||
static unsigned g_textPoolSize = 0;
|
||||
static const char* allocText(const char* text)
|
||||
{
|
||||
unsigned len = static_cast<unsigned>(strlen(text)+1);
|
||||
if (g_textPoolSize + len >= TEXT_POOL_SIZE)
|
||||
return 0;
|
||||
char* dst = &g_textPool[g_textPoolSize];
|
||||
memcpy(dst, text, len);
|
||||
g_textPoolSize += len;
|
||||
return dst;
|
||||
}
|
||||
|
||||
static const unsigned GFXCMD_QUEUE_SIZE = 5000;
|
||||
static imguiGfxCmd g_gfxCmdQueue[GFXCMD_QUEUE_SIZE];
|
||||
static unsigned g_gfxCmdQueueSize = 0;
|
||||
|
||||
static void resetGfxCmdQueue()
|
||||
{
|
||||
g_gfxCmdQueueSize = 0;
|
||||
g_textPoolSize = 0;
|
||||
}
|
||||
|
||||
static void addGfxCmdScissor(int x, int y, int w, int h)
|
||||
{
|
||||
if (g_gfxCmdQueueSize >= GFXCMD_QUEUE_SIZE)
|
||||
return;
|
||||
imguiGfxCmd& cmd = g_gfxCmdQueue[g_gfxCmdQueueSize++];
|
||||
cmd.type = IMGUI_GFXCMD_SCISSOR;
|
||||
cmd.flags = x < 0 ? 0 : 1; // on/off flag.
|
||||
cmd.col = 0;
|
||||
cmd.rect.x = (short)x;
|
||||
cmd.rect.y = (short)y;
|
||||
cmd.rect.w = (short)w;
|
||||
cmd.rect.h = (short)h;
|
||||
}
|
||||
|
||||
static void addGfxCmdRect(float x, float y, float w, float h, unsigned int color)
|
||||
{
|
||||
if (g_gfxCmdQueueSize >= GFXCMD_QUEUE_SIZE)
|
||||
return;
|
||||
imguiGfxCmd& cmd = g_gfxCmdQueue[g_gfxCmdQueueSize++];
|
||||
cmd.type = IMGUI_GFXCMD_RECT;
|
||||
cmd.flags = 0;
|
||||
cmd.col = color;
|
||||
cmd.rect.x = (short)(x*8.0f);
|
||||
cmd.rect.y = (short)(y*8.0f);
|
||||
cmd.rect.w = (short)(w*8.0f);
|
||||
cmd.rect.h = (short)(h*8.0f);
|
||||
cmd.rect.r = 0;
|
||||
}
|
||||
|
||||
static void addGfxCmdLine(float x0, float y0, float x1, float y1, float r, unsigned int color)
|
||||
{
|
||||
if (g_gfxCmdQueueSize >= GFXCMD_QUEUE_SIZE)
|
||||
return;
|
||||
imguiGfxCmd& cmd = g_gfxCmdQueue[g_gfxCmdQueueSize++];
|
||||
cmd.type = IMGUI_GFXCMD_LINE;
|
||||
cmd.flags = 0;
|
||||
cmd.col = color;
|
||||
cmd.line.x0 = (short)(x0*8.0f);
|
||||
cmd.line.y0 = (short)(y0*8.0f);
|
||||
cmd.line.x1 = (short)(x1*8.0f);
|
||||
cmd.line.y1 = (short)(y1*8.0f);
|
||||
cmd.line.r = (short)(r*8.0f);
|
||||
}
|
||||
|
||||
static void addGfxCmdRoundedRect(float x, float y, float w, float h, float r, unsigned int color)
|
||||
{
|
||||
if (g_gfxCmdQueueSize >= GFXCMD_QUEUE_SIZE)
|
||||
return;
|
||||
imguiGfxCmd& cmd = g_gfxCmdQueue[g_gfxCmdQueueSize++];
|
||||
cmd.type = IMGUI_GFXCMD_RECT;
|
||||
cmd.flags = 0;
|
||||
cmd.col = color;
|
||||
cmd.rect.x = (short)(x*8.0f);
|
||||
cmd.rect.y = (short)(y*8.0f);
|
||||
cmd.rect.w = (short)(w*8.0f);
|
||||
cmd.rect.h = (short)(h*8.0f);
|
||||
cmd.rect.r = (short)(r*8.0f);
|
||||
}
|
||||
|
||||
static void addGfxCmdTriangle(int x, int y, int w, int h, int flags, unsigned int color)
|
||||
{
|
||||
if (g_gfxCmdQueueSize >= GFXCMD_QUEUE_SIZE)
|
||||
return;
|
||||
imguiGfxCmd& cmd = g_gfxCmdQueue[g_gfxCmdQueueSize++];
|
||||
cmd.type = IMGUI_GFXCMD_TRIANGLE;
|
||||
cmd.flags = (char)flags;
|
||||
cmd.col = color;
|
||||
cmd.rect.x = (short)(x*8.0f);
|
||||
cmd.rect.y = (short)(y*8.0f);
|
||||
cmd.rect.w = (short)(w*8.0f);
|
||||
cmd.rect.h = (short)(h*8.0f);
|
||||
}
|
||||
|
||||
static void addGfxCmdText(int x, int y, int align, const char* text, unsigned int color)
|
||||
{
|
||||
if (g_gfxCmdQueueSize >= GFXCMD_QUEUE_SIZE)
|
||||
return;
|
||||
imguiGfxCmd& cmd = g_gfxCmdQueue[g_gfxCmdQueueSize++];
|
||||
cmd.type = IMGUI_GFXCMD_TEXT;
|
||||
cmd.flags = 0;
|
||||
cmd.col = color;
|
||||
cmd.text.x = (short)x;
|
||||
cmd.text.y = (short)y;
|
||||
cmd.text.align = (short)align;
|
||||
cmd.text.text = allocText(text);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
struct GuiState
|
||||
{
|
||||
GuiState() :
|
||||
left(false), leftPressed(false), leftReleased(false),
|
||||
mx(-1), my(-1), scroll(0),
|
||||
active(0), hot(0), hotToBe(0), isHot(false), isActive(false), wentActive(false),
|
||||
dragX(0), dragY(0), dragOrig(0), widgetX(0), widgetY(0), widgetW(100),
|
||||
insideCurrentScroll(false), areaId(0), widgetId(0)
|
||||
{
|
||||
}
|
||||
|
||||
bool left;
|
||||
bool leftPressed, leftReleased;
|
||||
int mx,my;
|
||||
int scroll;
|
||||
unsigned int active;
|
||||
unsigned int hot;
|
||||
unsigned int hotToBe;
|
||||
bool isHot;
|
||||
bool isActive;
|
||||
bool wentActive;
|
||||
int dragX, dragY;
|
||||
float dragOrig;
|
||||
int widgetX, widgetY, widgetW;
|
||||
bool insideCurrentScroll;
|
||||
|
||||
unsigned int areaId;
|
||||
unsigned int widgetId;
|
||||
};
|
||||
|
||||
static GuiState g_state;
|
||||
|
||||
inline bool anyActive()
|
||||
{
|
||||
return g_state.active != 0;
|
||||
}
|
||||
|
||||
inline bool isActive(unsigned int id)
|
||||
{
|
||||
return g_state.active == id;
|
||||
}
|
||||
|
||||
inline bool isHot(unsigned int id)
|
||||
{
|
||||
return g_state.hot == id;
|
||||
}
|
||||
|
||||
inline bool inRect(int x, int y, int w, int h, bool checkScroll = true)
|
||||
{
|
||||
return (!checkScroll || g_state.insideCurrentScroll) && g_state.mx >= x && g_state.mx <= x+w && g_state.my >= y && g_state.my <= y+h;
|
||||
}
|
||||
|
||||
inline void clearInput()
|
||||
{
|
||||
g_state.leftPressed = false;
|
||||
g_state.leftReleased = false;
|
||||
g_state.scroll = 0;
|
||||
}
|
||||
|
||||
inline void clearActive()
|
||||
{
|
||||
g_state.active = 0;
|
||||
// mark all UI for this frame as processed
|
||||
clearInput();
|
||||
}
|
||||
|
||||
inline void setActive(unsigned int id)
|
||||
{
|
||||
g_state.active = id;
|
||||
g_state.wentActive = true;
|
||||
}
|
||||
|
||||
inline void setHot(unsigned int id)
|
||||
{
|
||||
g_state.hotToBe = id;
|
||||
}
|
||||
|
||||
|
||||
static bool buttonLogic(unsigned int id, bool over)
|
||||
{
|
||||
bool res = false;
|
||||
// process down
|
||||
if (!anyActive())
|
||||
{
|
||||
if (over)
|
||||
setHot(id);
|
||||
if (isHot(id) && g_state.leftPressed)
|
||||
setActive(id);
|
||||
}
|
||||
|
||||
// if button is active, then react on left up
|
||||
if (isActive(id))
|
||||
{
|
||||
g_state.isActive = true;
|
||||
if (over)
|
||||
setHot(id);
|
||||
if (g_state.leftReleased)
|
||||
{
|
||||
if (isHot(id))
|
||||
res = true;
|
||||
clearActive();
|
||||
}
|
||||
}
|
||||
|
||||
if (isHot(id))
|
||||
g_state.isHot = true;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static void updateInput(int mx, int my, unsigned char mbut, int scroll)
|
||||
{
|
||||
bool left = (mbut & IMGUI_MBUT_LEFT) != 0;
|
||||
|
||||
g_state.mx = mx;
|
||||
g_state.my = my;
|
||||
g_state.leftPressed = !g_state.left && left;
|
||||
g_state.leftReleased = g_state.left && !left;
|
||||
g_state.left = left;
|
||||
|
||||
g_state.scroll = scroll;
|
||||
}
|
||||
|
||||
void imguiBeginFrame(int mx, int my, unsigned char mbut, int scroll)
|
||||
{
|
||||
updateInput(mx,my,mbut,scroll);
|
||||
|
||||
g_state.hot = g_state.hotToBe;
|
||||
g_state.hotToBe = 0;
|
||||
|
||||
g_state.wentActive = false;
|
||||
g_state.isActive = false;
|
||||
g_state.isHot = false;
|
||||
|
||||
g_state.widgetX = 0;
|
||||
g_state.widgetY = 0;
|
||||
g_state.widgetW = 0;
|
||||
|
||||
g_state.areaId = 1;
|
||||
g_state.widgetId = 1;
|
||||
|
||||
resetGfxCmdQueue();
|
||||
}
|
||||
|
||||
void imguiEndFrame()
|
||||
{
|
||||
clearInput();
|
||||
}
|
||||
|
||||
const imguiGfxCmd* imguiGetRenderQueue()
|
||||
{
|
||||
return g_gfxCmdQueue;
|
||||
}
|
||||
|
||||
int imguiGetRenderQueueSize()
|
||||
{
|
||||
return g_gfxCmdQueueSize;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
static const int BUTTON_HEIGHT = 20;
|
||||
static const int SLIDER_HEIGHT = 20;
|
||||
static const int SLIDER_MARKER_WIDTH = 10;
|
||||
static const int CHECK_SIZE = 8;
|
||||
static const int DEFAULT_SPACING = 4;
|
||||
static const int TEXT_HEIGHT = 8;
|
||||
static const int SCROLL_AREA_PADDING = 6;
|
||||
static const int INDENT_SIZE = 16;
|
||||
static const int AREA_HEADER = 28;
|
||||
|
||||
static int g_scrollTop = 0;
|
||||
static int g_scrollBottom = 0;
|
||||
static int g_scrollRight = 0;
|
||||
static int g_scrollAreaTop = 0;
|
||||
static int* g_scrollVal = 0;
|
||||
static int g_focusTop = 0;
|
||||
static int g_focusBottom = 0;
|
||||
static unsigned int g_scrollId = 0;
|
||||
static bool g_insideScrollArea = false;
|
||||
|
||||
bool imguiBeginScrollArea(const char* name, int x, int y, int w, int h, int* scroll)
|
||||
{
|
||||
g_state.areaId++;
|
||||
g_state.widgetId = 0;
|
||||
g_scrollId = (g_state.areaId<<16) | g_state.widgetId;
|
||||
|
||||
g_state.widgetX = x + SCROLL_AREA_PADDING;
|
||||
g_state.widgetY = y+h-AREA_HEADER + (*scroll);
|
||||
g_state.widgetW = w - SCROLL_AREA_PADDING*4;
|
||||
g_scrollTop = y-AREA_HEADER+h;
|
||||
g_scrollBottom = y+SCROLL_AREA_PADDING;
|
||||
g_scrollRight = x+w - SCROLL_AREA_PADDING*3;
|
||||
g_scrollVal = scroll;
|
||||
|
||||
g_scrollAreaTop = g_state.widgetY;
|
||||
|
||||
g_focusTop = y-AREA_HEADER;
|
||||
g_focusBottom = y-AREA_HEADER+h;
|
||||
|
||||
g_insideScrollArea = inRect(x, y, w, h, false);
|
||||
g_state.insideCurrentScroll = g_insideScrollArea;
|
||||
|
||||
addGfxCmdRoundedRect((float)x, (float)y, (float)w, (float)h, 6, imguiRGBA(0,0,0,192));
|
||||
|
||||
addGfxCmdText(x+AREA_HEADER/2, y+h-AREA_HEADER/2-TEXT_HEIGHT/2, IMGUI_ALIGN_LEFT, name, imguiRGBA(255,255,255,128));
|
||||
|
||||
addGfxCmdScissor(x+SCROLL_AREA_PADDING, y+SCROLL_AREA_PADDING, w-SCROLL_AREA_PADDING*4, h-AREA_HEADER-SCROLL_AREA_PADDING);
|
||||
|
||||
return g_insideScrollArea;
|
||||
}
|
||||
|
||||
void imguiEndScrollArea()
|
||||
{
|
||||
// Disable scissoring.
|
||||
addGfxCmdScissor(-1,-1,-1,-1);
|
||||
|
||||
// Draw scroll bar
|
||||
int x = g_scrollRight+SCROLL_AREA_PADDING/2;
|
||||
int y = g_scrollBottom;
|
||||
int w = SCROLL_AREA_PADDING*2;
|
||||
int h = g_scrollTop - g_scrollBottom;
|
||||
|
||||
int stop = g_scrollAreaTop;
|
||||
int sbot = g_state.widgetY;
|
||||
int sh = stop - sbot; // The scrollable area height.
|
||||
|
||||
float barHeight = (float)h/(float)sh;
|
||||
|
||||
if (barHeight < 1)
|
||||
{
|
||||
float barY = (float)(y - sbot)/(float)sh;
|
||||
if (barY < 0) barY = 0;
|
||||
if (barY > 1) barY = 1;
|
||||
|
||||
// Handle scroll bar logic.
|
||||
unsigned int hid = g_scrollId;
|
||||
int hx = x;
|
||||
int hy = y + (int)(barY*h);
|
||||
int hw = w;
|
||||
int hh = (int)(barHeight*h);
|
||||
|
||||
const int range = h - (hh-1);
|
||||
bool over = inRect(hx, hy, hw, hh);
|
||||
buttonLogic(hid, over);
|
||||
if (isActive(hid))
|
||||
{
|
||||
float u = (float)(hy-y) / (float)range;
|
||||
if (g_state.wentActive)
|
||||
{
|
||||
g_state.dragY = g_state.my;
|
||||
g_state.dragOrig = u;
|
||||
}
|
||||
if (g_state.dragY != g_state.my)
|
||||
{
|
||||
u = g_state.dragOrig + (g_state.my - g_state.dragY) / (float)range;
|
||||
if (u < 0) u = 0;
|
||||
if (u > 1) u = 1;
|
||||
*g_scrollVal = (int)((1-u) * (sh - h));
|
||||
}
|
||||
}
|
||||
|
||||
// BG
|
||||
addGfxCmdRoundedRect((float)x, (float)y, (float)w, (float)h, (float)w/2-1, imguiRGBA(0,0,0,196));
|
||||
// Bar
|
||||
if (isActive(hid))
|
||||
addGfxCmdRoundedRect((float)hx, (float)hy, (float)hw, (float)hh, (float)w/2-1, imguiRGBA(255,196,0,196));
|
||||
else
|
||||
addGfxCmdRoundedRect((float)hx, (float)hy, (float)hw, (float)hh, (float)w/2-1, isHot(hid) ? imguiRGBA(255,196,0,96) : imguiRGBA(255,255,255,64));
|
||||
|
||||
// Handle mouse scrolling.
|
||||
if (g_insideScrollArea) // && !anyActive())
|
||||
{
|
||||
if (g_state.scroll)
|
||||
{
|
||||
*g_scrollVal += 20*g_state.scroll;
|
||||
if (*g_scrollVal < 0) *g_scrollVal = 0;
|
||||
if (*g_scrollVal > (sh - h)) *g_scrollVal = (sh - h);
|
||||
}
|
||||
}
|
||||
}
|
||||
g_state.insideCurrentScroll = false;
|
||||
}
|
||||
|
||||
bool imguiButton(const char* text, bool enabled)
|
||||
{
|
||||
g_state.widgetId++;
|
||||
unsigned int id = (g_state.areaId<<16) | g_state.widgetId;
|
||||
|
||||
int x = g_state.widgetX;
|
||||
int y = g_state.widgetY - BUTTON_HEIGHT;
|
||||
int w = g_state.widgetW;
|
||||
int h = BUTTON_HEIGHT;
|
||||
g_state.widgetY -= BUTTON_HEIGHT + DEFAULT_SPACING;
|
||||
|
||||
bool over = enabled && inRect(x, y, w, h);
|
||||
bool res = buttonLogic(id, over);
|
||||
|
||||
addGfxCmdRoundedRect((float)x, (float)y, (float)w, (float)h, (float)BUTTON_HEIGHT/2-1, imguiRGBA(128,128,128, isActive(id)?196:96));
|
||||
if (enabled)
|
||||
addGfxCmdText(x+BUTTON_HEIGHT/2, y+BUTTON_HEIGHT/2-TEXT_HEIGHT/2, IMGUI_ALIGN_LEFT, text, isHot(id) ? imguiRGBA(255,196,0,255) : imguiRGBA(255,255,255,200));
|
||||
else
|
||||
addGfxCmdText(x+BUTTON_HEIGHT/2, y+BUTTON_HEIGHT/2-TEXT_HEIGHT/2, IMGUI_ALIGN_LEFT, text, imguiRGBA(128,128,128,200));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool imguiItem(const char* text, bool enabled)
|
||||
{
|
||||
g_state.widgetId++;
|
||||
unsigned int id = (g_state.areaId<<16) | g_state.widgetId;
|
||||
|
||||
int x = g_state.widgetX;
|
||||
int y = g_state.widgetY - BUTTON_HEIGHT;
|
||||
int w = g_state.widgetW;
|
||||
int h = BUTTON_HEIGHT;
|
||||
g_state.widgetY -= BUTTON_HEIGHT + DEFAULT_SPACING;
|
||||
|
||||
bool over = enabled && inRect(x, y, w, h);
|
||||
bool res = buttonLogic(id, over);
|
||||
|
||||
if (isHot(id))
|
||||
addGfxCmdRoundedRect((float)x, (float)y, (float)w, (float)h, 2.0f, imguiRGBA(255,196,0,isActive(id)?196:96));
|
||||
|
||||
if (enabled)
|
||||
addGfxCmdText(x+BUTTON_HEIGHT/2, y+BUTTON_HEIGHT/2-TEXT_HEIGHT/2, IMGUI_ALIGN_LEFT, text, imguiRGBA(255,255,255,200));
|
||||
else
|
||||
addGfxCmdText(x+BUTTON_HEIGHT/2, y+BUTTON_HEIGHT/2-TEXT_HEIGHT/2, IMGUI_ALIGN_LEFT, text, imguiRGBA(128,128,128,200));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool imguiCheck(const char* text, bool checked, bool enabled)
|
||||
{
|
||||
g_state.widgetId++;
|
||||
unsigned int id = (g_state.areaId<<16) | g_state.widgetId;
|
||||
|
||||
int x = g_state.widgetX;
|
||||
int y = g_state.widgetY - BUTTON_HEIGHT;
|
||||
int w = g_state.widgetW;
|
||||
int h = BUTTON_HEIGHT;
|
||||
g_state.widgetY -= BUTTON_HEIGHT + DEFAULT_SPACING;
|
||||
|
||||
bool over = enabled && inRect(x, y, w, h);
|
||||
bool res = buttonLogic(id, over);
|
||||
|
||||
const int cx = x+BUTTON_HEIGHT/2-CHECK_SIZE/2;
|
||||
const int cy = y+BUTTON_HEIGHT/2-CHECK_SIZE/2;
|
||||
addGfxCmdRoundedRect((float)cx-3, (float)cy-3, (float)CHECK_SIZE+6, (float)CHECK_SIZE+6, 4, imguiRGBA(128,128,128, isActive(id)?196:96));
|
||||
if (checked)
|
||||
{
|
||||
if (enabled)
|
||||
addGfxCmdRoundedRect((float)cx, (float)cy, (float)CHECK_SIZE, (float)CHECK_SIZE, (float)CHECK_SIZE/2-1, imguiRGBA(255,255,255,isActive(id)?255:200));
|
||||
else
|
||||
addGfxCmdRoundedRect((float)cx, (float)cy, (float)CHECK_SIZE, (float)CHECK_SIZE, (float)CHECK_SIZE/2-1, imguiRGBA(128,128,128,200));
|
||||
}
|
||||
|
||||
if (enabled)
|
||||
addGfxCmdText(x+BUTTON_HEIGHT, y+BUTTON_HEIGHT/2-TEXT_HEIGHT/2, IMGUI_ALIGN_LEFT, text, isHot(id) ? imguiRGBA(255,196,0,255) : imguiRGBA(255,255,255,200));
|
||||
else
|
||||
addGfxCmdText(x+BUTTON_HEIGHT, y+BUTTON_HEIGHT/2-TEXT_HEIGHT/2, IMGUI_ALIGN_LEFT, text, imguiRGBA(128,128,128,200));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool imguiCollapse(const char* text, const char* subtext, bool checked, bool enabled)
|
||||
{
|
||||
g_state.widgetId++;
|
||||
unsigned int id = (g_state.areaId<<16) | g_state.widgetId;
|
||||
|
||||
int x = g_state.widgetX;
|
||||
int y = g_state.widgetY - BUTTON_HEIGHT;
|
||||
int w = g_state.widgetW;
|
||||
int h = BUTTON_HEIGHT;
|
||||
g_state.widgetY -= BUTTON_HEIGHT; // + DEFAULT_SPACING;
|
||||
|
||||
const int cx = x+BUTTON_HEIGHT/2-CHECK_SIZE/2;
|
||||
const int cy = y+BUTTON_HEIGHT/2-CHECK_SIZE/2;
|
||||
|
||||
bool over = enabled && inRect(x, y, w, h);
|
||||
bool res = buttonLogic(id, over);
|
||||
|
||||
if (checked)
|
||||
addGfxCmdTriangle(cx, cy, CHECK_SIZE, CHECK_SIZE, 2, imguiRGBA(255,255,255,isActive(id)?255:200));
|
||||
else
|
||||
addGfxCmdTriangle(cx, cy, CHECK_SIZE, CHECK_SIZE, 1, imguiRGBA(255,255,255,isActive(id)?255:200));
|
||||
|
||||
if (enabled)
|
||||
addGfxCmdText(x+BUTTON_HEIGHT, y+BUTTON_HEIGHT/2-TEXT_HEIGHT/2, IMGUI_ALIGN_LEFT, text, isHot(id) ? imguiRGBA(255,196,0,255) : imguiRGBA(255,255,255,200));
|
||||
else
|
||||
addGfxCmdText(x+BUTTON_HEIGHT, y+BUTTON_HEIGHT/2-TEXT_HEIGHT/2, IMGUI_ALIGN_LEFT, text, imguiRGBA(128,128,128,200));
|
||||
|
||||
if (subtext)
|
||||
addGfxCmdText(x+w-BUTTON_HEIGHT/2, y+BUTTON_HEIGHT/2-TEXT_HEIGHT/2, IMGUI_ALIGN_RIGHT, subtext, imguiRGBA(255,255,255,128));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void imguiLabel(const char* text)
|
||||
{
|
||||
int x = g_state.widgetX;
|
||||
int y = g_state.widgetY - BUTTON_HEIGHT;
|
||||
g_state.widgetY -= BUTTON_HEIGHT;
|
||||
addGfxCmdText(x, y+BUTTON_HEIGHT/2-TEXT_HEIGHT/2, IMGUI_ALIGN_LEFT, text, imguiRGBA(255,255,255,255));
|
||||
}
|
||||
|
||||
void imguiValue(const char* text)
|
||||
{
|
||||
const int x = g_state.widgetX;
|
||||
const int y = g_state.widgetY - BUTTON_HEIGHT;
|
||||
const int w = g_state.widgetW;
|
||||
g_state.widgetY -= BUTTON_HEIGHT;
|
||||
|
||||
addGfxCmdText(x+w-BUTTON_HEIGHT/2, y+BUTTON_HEIGHT/2-TEXT_HEIGHT/2, IMGUI_ALIGN_RIGHT, text, imguiRGBA(255,255,255,200));
|
||||
}
|
||||
|
||||
bool imguiSlider(const char* text, float* val, float vmin, float vmax, float vinc, bool enabled)
|
||||
{
|
||||
g_state.widgetId++;
|
||||
unsigned int id = (g_state.areaId<<16) | g_state.widgetId;
|
||||
|
||||
int x = g_state.widgetX;
|
||||
int y = g_state.widgetY - BUTTON_HEIGHT;
|
||||
int w = g_state.widgetW;
|
||||
int h = SLIDER_HEIGHT;
|
||||
g_state.widgetY -= SLIDER_HEIGHT + DEFAULT_SPACING;
|
||||
|
||||
addGfxCmdRoundedRect((float)x, (float)y, (float)w, (float)h, 4.0f, imguiRGBA(0,0,0,128));
|
||||
|
||||
const int range = w - SLIDER_MARKER_WIDTH;
|
||||
|
||||
float u = (*val - vmin) / (vmax-vmin);
|
||||
if (u < 0) u = 0;
|
||||
if (u > 1) u = 1;
|
||||
int m = (int)(u * range);
|
||||
|
||||
bool over = enabled && inRect(x+m, y, SLIDER_MARKER_WIDTH, SLIDER_HEIGHT);
|
||||
bool res = buttonLogic(id, over);
|
||||
bool valChanged = false;
|
||||
|
||||
if (isActive(id))
|
||||
{
|
||||
if (g_state.wentActive)
|
||||
{
|
||||
g_state.dragX = g_state.mx;
|
||||
g_state.dragOrig = u;
|
||||
}
|
||||
if (g_state.dragX != g_state.mx)
|
||||
{
|
||||
u = g_state.dragOrig + (float)(g_state.mx - g_state.dragX) / (float)range;
|
||||
if (u < 0) u = 0;
|
||||
if (u > 1) u = 1;
|
||||
*val = vmin + u*(vmax-vmin);
|
||||
*val = floorf(*val/vinc+0.5f)*vinc; // Snap to vinc
|
||||
m = (int)(u * range);
|
||||
valChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (isActive(id))
|
||||
addGfxCmdRoundedRect((float)(x+m), (float)y, (float)SLIDER_MARKER_WIDTH, (float)SLIDER_HEIGHT, 4.0f, imguiRGBA(255,255,255,255));
|
||||
else
|
||||
addGfxCmdRoundedRect((float)(x+m), (float)y, (float)SLIDER_MARKER_WIDTH, (float)SLIDER_HEIGHT, 4.0f, isHot(id) ? imguiRGBA(255,196,0,128) : imguiRGBA(255,255,255,64));
|
||||
|
||||
// TODO: fix this, take a look at 'nicenum'.
|
||||
int digits = (int)(ceilf(log10f(vinc)));
|
||||
char fmt[16];
|
||||
snprintf(fmt, 16, "%%.%df", digits >= 0 ? 0 : -digits);
|
||||
char msg[128];
|
||||
snprintf(msg, 128, fmt, *val);
|
||||
|
||||
if (enabled)
|
||||
{
|
||||
addGfxCmdText(x+SLIDER_HEIGHT/2, y+SLIDER_HEIGHT/2-TEXT_HEIGHT/2, IMGUI_ALIGN_LEFT, text, isHot(id) ? imguiRGBA(255,196,0,255) : imguiRGBA(255,255,255,200));
|
||||
addGfxCmdText(x+w-SLIDER_HEIGHT/2, y+SLIDER_HEIGHT/2-TEXT_HEIGHT/2, IMGUI_ALIGN_RIGHT, msg, isHot(id) ? imguiRGBA(255,196,0,255) : imguiRGBA(255,255,255,200));
|
||||
}
|
||||
else
|
||||
{
|
||||
addGfxCmdText(x+SLIDER_HEIGHT/2, y+SLIDER_HEIGHT/2-TEXT_HEIGHT/2, IMGUI_ALIGN_LEFT, text, imguiRGBA(128,128,128,200));
|
||||
addGfxCmdText(x+w-SLIDER_HEIGHT/2, y+SLIDER_HEIGHT/2-TEXT_HEIGHT/2, IMGUI_ALIGN_RIGHT, msg, imguiRGBA(128,128,128,200));
|
||||
}
|
||||
|
||||
return res || valChanged;
|
||||
}
|
||||
|
||||
|
||||
void imguiIndent()
|
||||
{
|
||||
g_state.widgetX += INDENT_SIZE;
|
||||
g_state.widgetW -= INDENT_SIZE;
|
||||
}
|
||||
|
||||
void imguiUnindent()
|
||||
{
|
||||
g_state.widgetX -= INDENT_SIZE;
|
||||
g_state.widgetW += INDENT_SIZE;
|
||||
}
|
||||
|
||||
void imguiSeparator()
|
||||
{
|
||||
g_state.widgetY -= DEFAULT_SPACING*3;
|
||||
}
|
||||
|
||||
void imguiSeparatorLine()
|
||||
{
|
||||
int x = g_state.widgetX;
|
||||
int y = g_state.widgetY - DEFAULT_SPACING*2;
|
||||
int w = g_state.widgetW;
|
||||
int h = 1;
|
||||
g_state.widgetY -= DEFAULT_SPACING*4;
|
||||
|
||||
addGfxCmdRect((float)x, (float)y, (float)w, (float)h, imguiRGBA(255,255,255,32));
|
||||
}
|
||||
|
||||
void imguiDrawText(int x, int y, int align, const char* text, unsigned int color)
|
||||
{
|
||||
addGfxCmdText(x, y, align, text, color);
|
||||
}
|
||||
|
||||
void imguiDrawLine(float x0, float y0, float x1, float y1, float r, unsigned int color)
|
||||
{
|
||||
addGfxCmdLine(x0, y0, x1, y1, r, color);
|
||||
}
|
||||
|
||||
void imguiDrawRect(float x, float y, float w, float h, unsigned int color)
|
||||
{
|
||||
addGfxCmdRect(x, y, w, h, color);
|
||||
}
|
||||
|
||||
void imguiDrawRoundedRect(float x, float y, float w, float h, float r, unsigned int color)
|
||||
{
|
||||
addGfxCmdRoundedRect(x, y, w, h, r, color);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,500 @@
|
||||
//
|
||||
// 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 <cmath>
|
||||
#include <cstdio>
|
||||
#include "imgui.h"
|
||||
#include "SDL.h"
|
||||
#include "SDL_opengl.h"
|
||||
|
||||
// Some math headers don't have PI defined.
|
||||
static const float PI = 3.14159265f;
|
||||
|
||||
void imguifree(void* ptr, void* userptr);
|
||||
void* imguimalloc(size_t size, void* userptr);
|
||||
|
||||
#define STBTT_malloc(x,y) imguimalloc(x,y)
|
||||
#define STBTT_free(x,y) imguifree(x,y)
|
||||
#define STB_TRUETYPE_IMPLEMENTATION
|
||||
#include "stb_truetype.h"
|
||||
|
||||
void imguifree(void* ptr, void* /*userptr*/)
|
||||
{
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
void* imguimalloc(size_t size, void* /*userptr*/)
|
||||
{
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
static const unsigned TEMP_COORD_COUNT = 100;
|
||||
static float g_tempCoords[TEMP_COORD_COUNT*2];
|
||||
static float g_tempNormals[TEMP_COORD_COUNT*2];
|
||||
|
||||
static const int CIRCLE_VERTS = 8*4;
|
||||
static float g_circleVerts[CIRCLE_VERTS*2];
|
||||
|
||||
static stbtt_bakedchar g_cdata[96]; // ASCII 32..126 is 95 glyphs
|
||||
static GLuint g_ftex = 0;
|
||||
|
||||
inline unsigned int RGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
|
||||
{
|
||||
return (r) | (g << 8) | (b << 16) | (a << 24);
|
||||
}
|
||||
|
||||
static void drawPolygon(const float* coords, unsigned numCoords, float r, unsigned int col)
|
||||
{
|
||||
if (numCoords > TEMP_COORD_COUNT) numCoords = TEMP_COORD_COUNT;
|
||||
|
||||
for (unsigned i = 0, j = numCoords-1; i < numCoords; j=i++)
|
||||
{
|
||||
const float* v0 = &coords[j*2];
|
||||
const float* v1 = &coords[i*2];
|
||||
float dx = v1[0] - v0[0];
|
||||
float dy = v1[1] - v0[1];
|
||||
float d = sqrtf(dx*dx+dy*dy);
|
||||
if (d > 0)
|
||||
{
|
||||
d = 1.0f/d;
|
||||
dx *= d;
|
||||
dy *= d;
|
||||
}
|
||||
g_tempNormals[j*2+0] = dy;
|
||||
g_tempNormals[j*2+1] = -dx;
|
||||
}
|
||||
|
||||
for (unsigned i = 0, j = numCoords-1; i < numCoords; j=i++)
|
||||
{
|
||||
float dlx0 = g_tempNormals[j*2+0];
|
||||
float dly0 = g_tempNormals[j*2+1];
|
||||
float dlx1 = g_tempNormals[i*2+0];
|
||||
float dly1 = g_tempNormals[i*2+1];
|
||||
float dmx = (dlx0 + dlx1) * 0.5f;
|
||||
float dmy = (dly0 + dly1) * 0.5f;
|
||||
float dmr2 = dmx*dmx + dmy*dmy;
|
||||
if (dmr2 > 0.000001f)
|
||||
{
|
||||
float scale = 1.0f / dmr2;
|
||||
if (scale > 10.0f) scale = 10.0f;
|
||||
dmx *= scale;
|
||||
dmy *= scale;
|
||||
}
|
||||
g_tempCoords[i*2+0] = coords[i*2+0]+dmx*r;
|
||||
g_tempCoords[i*2+1] = coords[i*2+1]+dmy*r;
|
||||
}
|
||||
|
||||
unsigned int colTrans = RGBA(col&0xff, (col>>8)&0xff, (col>>16)&0xff, 0);
|
||||
|
||||
glBegin(GL_TRIANGLES);
|
||||
|
||||
glColor4ubv((GLubyte*)&col);
|
||||
|
||||
for (unsigned i = 0, j = numCoords-1; i < numCoords; j=i++)
|
||||
{
|
||||
glVertex2fv(&coords[i*2]);
|
||||
glVertex2fv(&coords[j*2]);
|
||||
glColor4ubv((GLubyte*)&colTrans);
|
||||
glVertex2fv(&g_tempCoords[j*2]);
|
||||
|
||||
glVertex2fv(&g_tempCoords[j*2]);
|
||||
glVertex2fv(&g_tempCoords[i*2]);
|
||||
|
||||
glColor4ubv((GLubyte*)&col);
|
||||
glVertex2fv(&coords[i*2]);
|
||||
}
|
||||
|
||||
glColor4ubv((GLubyte*)&col);
|
||||
for (unsigned i = 2; i < numCoords; ++i)
|
||||
{
|
||||
glVertex2fv(&coords[0]);
|
||||
glVertex2fv(&coords[(i-1)*2]);
|
||||
glVertex2fv(&coords[i*2]);
|
||||
}
|
||||
|
||||
glEnd();
|
||||
}
|
||||
|
||||
static void drawRect(float x, float y, float w, float h, float fth, unsigned int col)
|
||||
{
|
||||
float verts[4*2] =
|
||||
{
|
||||
x+0.5f, y+0.5f,
|
||||
x+w-0.5f, y+0.5f,
|
||||
x+w-0.5f, y+h-0.5f,
|
||||
x+0.5f, y+h-0.5f,
|
||||
};
|
||||
drawPolygon(verts, 4, fth, col);
|
||||
}
|
||||
|
||||
/*
|
||||
static void drawEllipse(float x, float y, float w, float h, float fth, unsigned int col)
|
||||
{
|
||||
float verts[CIRCLE_VERTS*2];
|
||||
const float* cverts = g_circleVerts;
|
||||
float* v = verts;
|
||||
|
||||
for (int i = 0; i < CIRCLE_VERTS; ++i)
|
||||
{
|
||||
*v++ = x + cverts[i*2]*w;
|
||||
*v++ = y + cverts[i*2+1]*h;
|
||||
}
|
||||
|
||||
drawPolygon(verts, CIRCLE_VERTS, fth, col);
|
||||
}
|
||||
*/
|
||||
|
||||
static void drawRoundedRect(float x, float y, float w, float h, float r, float fth, unsigned int col)
|
||||
{
|
||||
const unsigned n = CIRCLE_VERTS/4;
|
||||
float verts[(n+1)*4*2];
|
||||
const float* cverts = g_circleVerts;
|
||||
float* v = verts;
|
||||
|
||||
for (unsigned i = 0; i <= n; ++i)
|
||||
{
|
||||
*v++ = x+w-r + cverts[i*2]*r;
|
||||
*v++ = y+h-r + cverts[i*2+1]*r;
|
||||
}
|
||||
|
||||
for (unsigned i = n; i <= n*2; ++i)
|
||||
{
|
||||
*v++ = x+r + cverts[i*2]*r;
|
||||
*v++ = y+h-r + cverts[i*2+1]*r;
|
||||
}
|
||||
|
||||
for (unsigned i = n*2; i <= n*3; ++i)
|
||||
{
|
||||
*v++ = x+r + cverts[i*2]*r;
|
||||
*v++ = y+r + cverts[i*2+1]*r;
|
||||
}
|
||||
|
||||
for (unsigned i = n*3; i < n*4; ++i)
|
||||
{
|
||||
*v++ = x+w-r + cverts[i*2]*r;
|
||||
*v++ = y+r + cverts[i*2+1]*r;
|
||||
}
|
||||
*v++ = x+w-r + cverts[0]*r;
|
||||
*v++ = y+r + cverts[1]*r;
|
||||
|
||||
drawPolygon(verts, (n+1)*4, fth, col);
|
||||
}
|
||||
|
||||
|
||||
static void drawLine(float x0, float y0, float x1, float y1, float r, float fth, unsigned int col)
|
||||
{
|
||||
float dx = x1-x0;
|
||||
float dy = y1-y0;
|
||||
float d = sqrtf(dx*dx+dy*dy);
|
||||
if (d > 0.0001f)
|
||||
{
|
||||
d = 1.0f/d;
|
||||
dx *= d;
|
||||
dy *= d;
|
||||
}
|
||||
float nx = dy;
|
||||
float ny = -dx;
|
||||
float verts[4*2];
|
||||
r -= fth;
|
||||
r *= 0.5f;
|
||||
if (r < 0.01f) r = 0.01f;
|
||||
dx *= r;
|
||||
dy *= r;
|
||||
nx *= r;
|
||||
ny *= r;
|
||||
|
||||
verts[0] = x0-dx-nx;
|
||||
verts[1] = y0-dy-ny;
|
||||
|
||||
verts[2] = x0-dx+nx;
|
||||
verts[3] = y0-dy+ny;
|
||||
|
||||
verts[4] = x1+dx+nx;
|
||||
verts[5] = y1+dy+ny;
|
||||
|
||||
verts[6] = x1+dx-nx;
|
||||
verts[7] = y1+dy-ny;
|
||||
|
||||
drawPolygon(verts, 4, fth, col);
|
||||
}
|
||||
|
||||
|
||||
bool imguiRenderGLInit(const char* fontpath)
|
||||
{
|
||||
for (int i = 0; i < CIRCLE_VERTS; ++i)
|
||||
{
|
||||
float a = (float)i/(float)CIRCLE_VERTS * PI*2;
|
||||
g_circleVerts[i*2+0] = cosf(a);
|
||||
g_circleVerts[i*2+1] = sinf(a);
|
||||
}
|
||||
|
||||
// Load font.
|
||||
FILE* fp = fopen(fontpath, "rb");
|
||||
if (!fp) return false;
|
||||
if (fseek(fp, 0, SEEK_END) != 0)
|
||||
{
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
long size = ftell(fp);
|
||||
if (size < 0)
|
||||
{
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
if (fseek(fp, 0, SEEK_SET) != 0)
|
||||
{
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned char* ttfBuffer = (unsigned char*)malloc(size);
|
||||
if (!ttfBuffer)
|
||||
{
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t readLen = fread(ttfBuffer, 1, size, fp);
|
||||
fclose(fp);
|
||||
if (readLen != static_cast<size_t>(size))
|
||||
{
|
||||
free(ttfBuffer);
|
||||
return false;
|
||||
}
|
||||
|
||||
fp = 0;
|
||||
|
||||
unsigned char* bmap = (unsigned char*)malloc(512*512);
|
||||
if (!bmap)
|
||||
{
|
||||
free(ttfBuffer);
|
||||
return false;
|
||||
}
|
||||
|
||||
stbtt_BakeFontBitmap(ttfBuffer,0, 15.0f, bmap,512,512, 32,96, g_cdata);
|
||||
|
||||
// can free ttf_buffer at this point
|
||||
glGenTextures(1, &g_ftex);
|
||||
glBindTexture(GL_TEXTURE_2D, g_ftex);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 512,512, 0, GL_ALPHA, GL_UNSIGNED_BYTE, bmap);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
|
||||
free(ttfBuffer);
|
||||
free(bmap);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void imguiRenderGLDestroy()
|
||||
{
|
||||
if (g_ftex)
|
||||
{
|
||||
glDeleteTextures(1, &g_ftex);
|
||||
g_ftex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void getBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, int char_index,
|
||||
float *xpos, float *ypos, stbtt_aligned_quad *q)
|
||||
{
|
||||
stbtt_bakedchar *b = chardata + char_index;
|
||||
int round_x = STBTT_ifloor(*xpos + b->xoff);
|
||||
int round_y = STBTT_ifloor(*ypos - b->yoff);
|
||||
|
||||
q->x0 = (float)round_x;
|
||||
q->y0 = (float)round_y;
|
||||
q->x1 = (float)round_x + b->x1 - b->x0;
|
||||
q->y1 = (float)round_y - b->y1 + b->y0;
|
||||
|
||||
q->s0 = b->x0 / (float)pw;
|
||||
q->t0 = b->y0 / (float)pw;
|
||||
q->s1 = b->x1 / (float)ph;
|
||||
q->t1 = b->y1 / (float)ph;
|
||||
|
||||
*xpos += b->xadvance;
|
||||
}
|
||||
|
||||
static const float g_tabStops[4] = {150, 210, 270, 330};
|
||||
|
||||
static float getTextLength(stbtt_bakedchar *chardata, const char* text)
|
||||
{
|
||||
float xpos = 0;
|
||||
float len = 0;
|
||||
while (*text)
|
||||
{
|
||||
int c = (unsigned char)*text;
|
||||
if (c == '\t')
|
||||
{
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
if (xpos < g_tabStops[i])
|
||||
{
|
||||
xpos = g_tabStops[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (c >= 32 && c < 128)
|
||||
{
|
||||
stbtt_bakedchar *b = chardata + c-32;
|
||||
int round_x = STBTT_ifloor((xpos + b->xoff) + 0.5);
|
||||
len = round_x + b->x1 - b->x0 + 0.5f;
|
||||
xpos += b->xadvance;
|
||||
}
|
||||
++text;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
static void drawText(float x, float y, const char *text, int align, unsigned int col)
|
||||
{
|
||||
if (!g_ftex) return;
|
||||
if (!text) return;
|
||||
|
||||
if (align == IMGUI_ALIGN_CENTER)
|
||||
x -= getTextLength(g_cdata, text)/2;
|
||||
else if (align == IMGUI_ALIGN_RIGHT)
|
||||
x -= getTextLength(g_cdata, text);
|
||||
|
||||
glColor4ub(col&0xff, (col>>8)&0xff, (col>>16)&0xff, (col>>24)&0xff);
|
||||
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
|
||||
// assume orthographic projection with units = screen pixels, origin at top left
|
||||
glBindTexture(GL_TEXTURE_2D, g_ftex);
|
||||
|
||||
glBegin(GL_TRIANGLES);
|
||||
|
||||
const float ox = x;
|
||||
|
||||
while (*text)
|
||||
{
|
||||
int c = (unsigned char)*text;
|
||||
if (c == '\t')
|
||||
{
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
if (x < g_tabStops[i]+ox)
|
||||
{
|
||||
x = g_tabStops[i]+ox;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (c >= 32 && c < 128)
|
||||
{
|
||||
stbtt_aligned_quad q;
|
||||
getBakedQuad(g_cdata, 512,512, c-32, &x,&y,&q);
|
||||
|
||||
glTexCoord2f(q.s0, q.t0);
|
||||
glVertex2f(q.x0, q.y0);
|
||||
glTexCoord2f(q.s1, q.t1);
|
||||
glVertex2f(q.x1, q.y1);
|
||||
glTexCoord2f(q.s1, q.t0);
|
||||
glVertex2f(q.x1, q.y0);
|
||||
|
||||
glTexCoord2f(q.s0, q.t0);
|
||||
glVertex2f(q.x0, q.y0);
|
||||
glTexCoord2f(q.s0, q.t1);
|
||||
glVertex2f(q.x0, q.y1);
|
||||
glTexCoord2f(q.s1, q.t1);
|
||||
glVertex2f(q.x1, q.y1);
|
||||
}
|
||||
++text;
|
||||
}
|
||||
|
||||
glEnd();
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
}
|
||||
|
||||
|
||||
void imguiRenderGLDraw()
|
||||
{
|
||||
const imguiGfxCmd* q = imguiGetRenderQueue();
|
||||
int nq = imguiGetRenderQueueSize();
|
||||
|
||||
const float s = 1.0f/8.0f;
|
||||
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
for (int i = 0; i < nq; ++i)
|
||||
{
|
||||
const imguiGfxCmd& cmd = q[i];
|
||||
if (cmd.type == IMGUI_GFXCMD_RECT)
|
||||
{
|
||||
if (cmd.rect.r == 0)
|
||||
{
|
||||
drawRect((float)cmd.rect.x*s+0.5f, (float)cmd.rect.y*s+0.5f,
|
||||
(float)cmd.rect.w*s-1, (float)cmd.rect.h*s-1,
|
||||
1.0f, cmd.col);
|
||||
}
|
||||
else
|
||||
{
|
||||
drawRoundedRect((float)cmd.rect.x*s+0.5f, (float)cmd.rect.y*s+0.5f,
|
||||
(float)cmd.rect.w*s-1, (float)cmd.rect.h*s-1,
|
||||
(float)cmd.rect.r*s, 1.0f, cmd.col);
|
||||
}
|
||||
}
|
||||
else if (cmd.type == IMGUI_GFXCMD_LINE)
|
||||
{
|
||||
drawLine(cmd.line.x0*s, cmd.line.y0*s, cmd.line.x1*s, cmd.line.y1*s, cmd.line.r*s, 1.0f, cmd.col);
|
||||
}
|
||||
else if (cmd.type == IMGUI_GFXCMD_TRIANGLE)
|
||||
{
|
||||
if (cmd.flags == 1)
|
||||
{
|
||||
const float verts[3*2] =
|
||||
{
|
||||
(float)cmd.rect.x*s+0.5f, (float)cmd.rect.y*s+0.5f,
|
||||
(float)cmd.rect.x*s+0.5f+(float)cmd.rect.w*s-1, (float)cmd.rect.y*s+0.5f+(float)cmd.rect.h*s/2-0.5f,
|
||||
(float)cmd.rect.x*s+0.5f, (float)cmd.rect.y*s+0.5f+(float)cmd.rect.h*s-1,
|
||||
};
|
||||
drawPolygon(verts, 3, 1.0f, cmd.col);
|
||||
}
|
||||
if (cmd.flags == 2)
|
||||
{
|
||||
const float verts[3*2] =
|
||||
{
|
||||
(float)cmd.rect.x*s+0.5f, (float)cmd.rect.y*s+0.5f+(float)cmd.rect.h*s-1,
|
||||
(float)cmd.rect.x*s+0.5f+(float)cmd.rect.w*s/2-0.5f, (float)cmd.rect.y*s+0.5f,
|
||||
(float)cmd.rect.x*s+0.5f+(float)cmd.rect.w*s-1, (float)cmd.rect.y*s+0.5f+(float)cmd.rect.h*s-1,
|
||||
};
|
||||
drawPolygon(verts, 3, 1.0f, cmd.col);
|
||||
}
|
||||
}
|
||||
else if (cmd.type == IMGUI_GFXCMD_TEXT)
|
||||
{
|
||||
drawText(cmd.text.x, cmd.text.y, cmd.text.text, cmd.text.align, cmd.col);
|
||||
}
|
||||
else if (cmd.type == IMGUI_GFXCMD_SCISSOR)
|
||||
{
|
||||
if (cmd.flags)
|
||||
{
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
glScissor(cmd.rect.x, cmd.rect.y, cmd.rect.w, cmd.rect.h);
|
||||
}
|
||||
else
|
||||
{
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
}
|
||||
}
|
||||
}
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
}
|
||||
@@ -0,0 +1,927 @@
|
||||
//
|
||||
// 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 <cstdio>
|
||||
#define _USE_MATH_DEFINES
|
||||
#include <cmath>
|
||||
|
||||
#include "SDL.h"
|
||||
#include "SDL_opengl.h"
|
||||
#ifdef __APPLE__
|
||||
# include <OpenGL/glu.h>
|
||||
#else
|
||||
# include <GL/glu.h>
|
||||
#endif
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include "imgui.h"
|
||||
#include "imguiRenderGL.h"
|
||||
|
||||
#include "Recast.h"
|
||||
#include "RecastDebugDraw.h"
|
||||
#include "InputGeom.h"
|
||||
#include "TestCase.h"
|
||||
#include "Filelist.h"
|
||||
#include "Sample_SoloMesh.h"
|
||||
#include "Sample_TileMesh.h"
|
||||
#include "Sample_TempObstacles.h"
|
||||
#include "Sample_Debug.h"
|
||||
|
||||
#ifdef WIN32
|
||||
# define snprintf _snprintf
|
||||
# define putenv _putenv
|
||||
#endif
|
||||
|
||||
using std::string;
|
||||
using std::vector;
|
||||
|
||||
struct SampleItem
|
||||
{
|
||||
Sample* (*create)();
|
||||
const string name;
|
||||
};
|
||||
Sample* createSolo() { return new Sample_SoloMesh(); }
|
||||
Sample* createTile() { return new Sample_TileMesh(); }
|
||||
Sample* createTempObstacle() { return new Sample_TempObstacles(); }
|
||||
Sample* createDebug() { return new Sample_Debug(); }
|
||||
static SampleItem g_samples[] =
|
||||
{
|
||||
{ createSolo, "Solo Mesh" },
|
||||
{ createTile, "Tile Mesh" },
|
||||
{ createTempObstacle, "Temp Obstacles" },
|
||||
};
|
||||
static const int g_nsamples = sizeof(g_samples) / sizeof(SampleItem);
|
||||
|
||||
int main(int /*argc*/, char** /*argv*/)
|
||||
{
|
||||
// Init SDL
|
||||
if (SDL_Init(SDL_INIT_EVERYTHING) != 0)
|
||||
{
|
||||
printf("Could not initialise SDL.\nError: %s\n", SDL_GetError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Enable depth buffer.
|
||||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
||||
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
|
||||
|
||||
// Set color channel depth.
|
||||
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
|
||||
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
|
||||
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
|
||||
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
|
||||
|
||||
// 4x MSAA.
|
||||
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
|
||||
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4);
|
||||
|
||||
SDL_DisplayMode displayMode;
|
||||
SDL_GetCurrentDisplayMode(0, &displayMode);
|
||||
|
||||
bool presentationMode = false;
|
||||
Uint32 flags = SDL_WINDOW_OPENGL;
|
||||
int width;
|
||||
int height;
|
||||
if (presentationMode)
|
||||
{
|
||||
// Create a fullscreen window at the native resolution.
|
||||
width = displayMode.w;
|
||||
height = displayMode.h;
|
||||
flags |= SDL_WINDOW_FULLSCREEN;
|
||||
}
|
||||
else
|
||||
{
|
||||
float aspect = 16.0f / 9.0f;
|
||||
width = rcMin(displayMode.w, (int)(displayMode.h * aspect)) - 80;
|
||||
height = displayMode.h - 80;
|
||||
}
|
||||
|
||||
SDL_Window* window;
|
||||
SDL_Renderer* renderer;
|
||||
int errorCode = SDL_CreateWindowAndRenderer(width, height, flags, &window, &renderer);
|
||||
|
||||
if (errorCode != 0 || !window || !renderer)
|
||||
{
|
||||
printf("Could not initialise SDL opengl\nError: %s\n", SDL_GetError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
|
||||
SDL_GL_CreateContext(window);
|
||||
|
||||
if (!imguiRenderGLInit("DroidSans.ttf"))
|
||||
{
|
||||
printf("Could not init GUI renderer.\n");
|
||||
SDL_Quit();
|
||||
return -1;
|
||||
}
|
||||
|
||||
float t = 0.0f;
|
||||
float timeAcc = 0.0f;
|
||||
Uint32 prevFrameTime = SDL_GetTicks();
|
||||
int mousePos[2] = {0, 0};
|
||||
int origMousePos[2] = {0, 0}; // Used to compute mouse movement totals across frames.
|
||||
|
||||
float cameraEulers[] = {45, -45};
|
||||
float cameraPos[] = {0, 0, 0};
|
||||
float camr = 1000;
|
||||
float origCameraEulers[] = {0, 0}; // Used to compute rotational changes across frames.
|
||||
|
||||
float moveFront = 0.0f, moveBack = 0.0f, moveLeft = 0.0f, moveRight = 0.0f, moveUp = 0.0f, moveDown = 0.0f;
|
||||
|
||||
float scrollZoom = 0;
|
||||
bool rotate = false;
|
||||
bool movedDuringRotate = false;
|
||||
float rayStart[3];
|
||||
float rayEnd[3];
|
||||
bool mouseOverMenu = false;
|
||||
|
||||
bool showMenu = !presentationMode;
|
||||
bool showLog = false;
|
||||
bool showTools = true;
|
||||
bool showLevels = false;
|
||||
bool showSample = false;
|
||||
bool showTestCases = false;
|
||||
|
||||
// Window scroll positions.
|
||||
int propScroll = 0;
|
||||
int logScroll = 0;
|
||||
int toolsScroll = 0;
|
||||
|
||||
string sampleName = "Choose Sample...";
|
||||
|
||||
vector<string> files;
|
||||
const string meshesFolder = "Meshes";
|
||||
string meshName = "Choose Mesh...";
|
||||
|
||||
float markerPosition[3] = {0, 0, 0};
|
||||
bool markerPositionSet = false;
|
||||
|
||||
InputGeom* geom = 0;
|
||||
Sample* sample = 0;
|
||||
|
||||
const string testCasesFolder = "TestCases";
|
||||
TestCase* test = 0;
|
||||
|
||||
BuildContext ctx;
|
||||
|
||||
// Fog.
|
||||
float fogColor[4] = { 0.32f, 0.31f, 0.30f, 1.0f };
|
||||
glEnable(GL_FOG);
|
||||
glFogi(GL_FOG_MODE, GL_LINEAR);
|
||||
glFogf(GL_FOG_START, camr * 0.1f);
|
||||
glFogf(GL_FOG_END, camr * 1.25f);
|
||||
glFogfv(GL_FOG_COLOR, fogColor);
|
||||
|
||||
glEnable(GL_CULL_FACE);
|
||||
glDepthFunc(GL_LEQUAL);
|
||||
|
||||
bool done = false;
|
||||
while(!done)
|
||||
{
|
||||
// Handle input events.
|
||||
int mouseScroll = 0;
|
||||
bool processHitTest = false;
|
||||
bool processHitTestShift = false;
|
||||
SDL_Event event;
|
||||
|
||||
while (SDL_PollEvent(&event))
|
||||
{
|
||||
switch (event.type)
|
||||
{
|
||||
case SDL_KEYDOWN:
|
||||
// Handle any key presses here.
|
||||
if (event.key.keysym.sym == SDLK_ESCAPE)
|
||||
{
|
||||
done = true;
|
||||
}
|
||||
else if (event.key.keysym.sym == SDLK_t)
|
||||
{
|
||||
showLevels = false;
|
||||
showSample = false;
|
||||
showTestCases = true;
|
||||
scanDirectory(testCasesFolder, ".txt", files);
|
||||
}
|
||||
else if (event.key.keysym.sym == SDLK_TAB)
|
||||
{
|
||||
showMenu = !showMenu;
|
||||
}
|
||||
else if (event.key.keysym.sym == SDLK_SPACE)
|
||||
{
|
||||
if (sample)
|
||||
sample->handleToggle();
|
||||
}
|
||||
else if (event.key.keysym.sym == SDLK_1)
|
||||
{
|
||||
if (sample)
|
||||
sample->handleStep();
|
||||
}
|
||||
else if (event.key.keysym.sym == SDLK_9)
|
||||
{
|
||||
if (sample && geom)
|
||||
{
|
||||
string savePath = meshesFolder + "/";
|
||||
BuildSettings settings;
|
||||
memset(&settings, 0, sizeof(settings));
|
||||
|
||||
rcVcopy(settings.navMeshBMin, geom->getNavMeshBoundsMin());
|
||||
rcVcopy(settings.navMeshBMax, geom->getNavMeshBoundsMax());
|
||||
|
||||
sample->collectSettings(settings);
|
||||
|
||||
geom->saveGeomSet(&settings);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_MOUSEWHEEL:
|
||||
if (event.wheel.y < 0)
|
||||
{
|
||||
// wheel down
|
||||
if (mouseOverMenu)
|
||||
{
|
||||
mouseScroll++;
|
||||
}
|
||||
else
|
||||
{
|
||||
scrollZoom += 1.0f;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mouseOverMenu)
|
||||
{
|
||||
mouseScroll--;
|
||||
}
|
||||
else
|
||||
{
|
||||
scrollZoom -= 1.0f;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
if (event.button.button == SDL_BUTTON_RIGHT)
|
||||
{
|
||||
if (!mouseOverMenu)
|
||||
{
|
||||
// Rotate view
|
||||
rotate = true;
|
||||
movedDuringRotate = false;
|
||||
origMousePos[0] = mousePos[0];
|
||||
origMousePos[1] = mousePos[1];
|
||||
origCameraEulers[0] = cameraEulers[0];
|
||||
origCameraEulers[1] = cameraEulers[1];
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_MOUSEBUTTONUP:
|
||||
// Handle mouse clicks here.
|
||||
if (event.button.button == SDL_BUTTON_RIGHT)
|
||||
{
|
||||
rotate = false;
|
||||
if (!mouseOverMenu)
|
||||
{
|
||||
if (!movedDuringRotate)
|
||||
{
|
||||
processHitTest = true;
|
||||
processHitTestShift = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (event.button.button == SDL_BUTTON_LEFT)
|
||||
{
|
||||
if (!mouseOverMenu)
|
||||
{
|
||||
processHitTest = true;
|
||||
processHitTestShift = (SDL_GetModState() & KMOD_SHIFT) ? true : false;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case SDL_MOUSEMOTION:
|
||||
mousePos[0] = event.motion.x;
|
||||
mousePos[1] = height-1 - event.motion.y;
|
||||
|
||||
if (rotate)
|
||||
{
|
||||
int dx = mousePos[0] - origMousePos[0];
|
||||
int dy = mousePos[1] - origMousePos[1];
|
||||
cameraEulers[0] = origCameraEulers[0] - dy * 0.25f;
|
||||
cameraEulers[1] = origCameraEulers[1] + dx * 0.25f;
|
||||
if (dx * dx + dy * dy > 3 * 3)
|
||||
{
|
||||
movedDuringRotate = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_QUIT:
|
||||
done = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned char mouseButtonMask = 0;
|
||||
if (SDL_GetMouseState(0, 0) & SDL_BUTTON_LMASK)
|
||||
mouseButtonMask |= IMGUI_MBUT_LEFT;
|
||||
if (SDL_GetMouseState(0, 0) & SDL_BUTTON_RMASK)
|
||||
mouseButtonMask |= IMGUI_MBUT_RIGHT;
|
||||
|
||||
Uint32 time = SDL_GetTicks();
|
||||
float dt = (time - prevFrameTime) / 1000.0f;
|
||||
prevFrameTime = time;
|
||||
|
||||
t += dt;
|
||||
|
||||
// Hit test mesh.
|
||||
if (processHitTest && geom && sample)
|
||||
{
|
||||
float hitTime;
|
||||
bool hit = geom->raycastMesh(rayStart, rayEnd, hitTime);
|
||||
|
||||
if (hit)
|
||||
{
|
||||
if (SDL_GetModState() & KMOD_CTRL)
|
||||
{
|
||||
// Marker
|
||||
markerPositionSet = true;
|
||||
markerPosition[0] = rayStart[0] + (rayEnd[0] - rayStart[0]) * hitTime;
|
||||
markerPosition[1] = rayStart[1] + (rayEnd[1] - rayStart[1]) * hitTime;
|
||||
markerPosition[2] = rayStart[2] + (rayEnd[2] - rayStart[2]) * hitTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
float pos[3];
|
||||
pos[0] = rayStart[0] + (rayEnd[0] - rayStart[0]) * hitTime;
|
||||
pos[1] = rayStart[1] + (rayEnd[1] - rayStart[1]) * hitTime;
|
||||
pos[2] = rayStart[2] + (rayEnd[2] - rayStart[2]) * hitTime;
|
||||
sample->handleClick(rayStart, pos, processHitTestShift);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (SDL_GetModState() & KMOD_CTRL)
|
||||
{
|
||||
// Marker
|
||||
markerPositionSet = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update sample simulation.
|
||||
const float SIM_RATE = 20;
|
||||
const float DELTA_TIME = 1.0f / SIM_RATE;
|
||||
timeAcc = rcClamp(timeAcc + dt, -1.0f, 1.0f);
|
||||
int simIter = 0;
|
||||
while (timeAcc > DELTA_TIME)
|
||||
{
|
||||
timeAcc -= DELTA_TIME;
|
||||
if (simIter < 5 && sample)
|
||||
{
|
||||
sample->handleUpdate(DELTA_TIME);
|
||||
}
|
||||
simIter++;
|
||||
}
|
||||
|
||||
// Clamp the framerate so that we do not hog all the CPU.
|
||||
const float MIN_FRAME_TIME = 1.0f / 40.0f;
|
||||
if (dt < MIN_FRAME_TIME)
|
||||
{
|
||||
int ms = (int)((MIN_FRAME_TIME - dt) * 1000.0f);
|
||||
if (ms > 10) ms = 10;
|
||||
if (ms >= 0) SDL_Delay(ms);
|
||||
}
|
||||
|
||||
// Set the viewport.
|
||||
glViewport(0, 0, width, height);
|
||||
GLint viewport[4];
|
||||
glGetIntegerv(GL_VIEWPORT, viewport);
|
||||
|
||||
// Clear the screen
|
||||
glClearColor(0.3f, 0.3f, 0.32f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
|
||||
// Compute the projection matrix.
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
gluPerspective(50.0f, (float)width/(float)height, 1.0f, camr);
|
||||
GLdouble projectionMatrix[16];
|
||||
glGetDoublev(GL_PROJECTION_MATRIX, projectionMatrix);
|
||||
|
||||
// Compute the modelview matrix.
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
glRotatef(cameraEulers[0], 1, 0, 0);
|
||||
glRotatef(cameraEulers[1], 0, 1, 0);
|
||||
glTranslatef(-cameraPos[0], -cameraPos[1], -cameraPos[2]);
|
||||
GLdouble modelviewMatrix[16];
|
||||
glGetDoublev(GL_MODELVIEW_MATRIX, modelviewMatrix);
|
||||
|
||||
// Get hit ray position and direction.
|
||||
GLdouble x, y, z;
|
||||
gluUnProject(mousePos[0], mousePos[1], 0.0f, modelviewMatrix, projectionMatrix, viewport, &x, &y, &z);
|
||||
rayStart[0] = (float)x;
|
||||
rayStart[1] = (float)y;
|
||||
rayStart[2] = (float)z;
|
||||
gluUnProject(mousePos[0], mousePos[1], 1.0f, modelviewMatrix, projectionMatrix, viewport, &x, &y, &z);
|
||||
rayEnd[0] = (float)x;
|
||||
rayEnd[1] = (float)y;
|
||||
rayEnd[2] = (float)z;
|
||||
|
||||
// Handle keyboard movement.
|
||||
const Uint8* keystate = SDL_GetKeyboardState(NULL);
|
||||
moveFront = rcClamp(moveFront + dt * 4 * ((keystate[SDL_SCANCODE_W] || keystate[SDL_SCANCODE_UP ]) ? 1 : -1), 0.0f, 1.0f);
|
||||
moveLeft = rcClamp(moveLeft + dt * 4 * ((keystate[SDL_SCANCODE_A] || keystate[SDL_SCANCODE_LEFT ]) ? 1 : -1), 0.0f, 1.0f);
|
||||
moveBack = rcClamp(moveBack + dt * 4 * ((keystate[SDL_SCANCODE_S] || keystate[SDL_SCANCODE_DOWN ]) ? 1 : -1), 0.0f, 1.0f);
|
||||
moveRight = rcClamp(moveRight + dt * 4 * ((keystate[SDL_SCANCODE_D] || keystate[SDL_SCANCODE_RIGHT ]) ? 1 : -1), 0.0f, 1.0f);
|
||||
moveUp = rcClamp(moveUp + dt * 4 * ((keystate[SDL_SCANCODE_Q] || keystate[SDL_SCANCODE_PAGEUP ]) ? 1 : -1), 0.0f, 1.0f);
|
||||
moveDown = rcClamp(moveDown + dt * 4 * ((keystate[SDL_SCANCODE_E] || keystate[SDL_SCANCODE_PAGEDOWN ]) ? 1 : -1), 0.0f, 1.0f);
|
||||
|
||||
float keybSpeed = 22.0f;
|
||||
if (SDL_GetModState() & KMOD_SHIFT)
|
||||
{
|
||||
keybSpeed *= 4.0f;
|
||||
}
|
||||
|
||||
float movex = (moveRight - moveLeft) * keybSpeed * dt;
|
||||
float movey = (moveBack - moveFront) * keybSpeed * dt + scrollZoom * 2.0f;
|
||||
scrollZoom = 0;
|
||||
|
||||
cameraPos[0] += movex * (float)modelviewMatrix[0];
|
||||
cameraPos[1] += movex * (float)modelviewMatrix[4];
|
||||
cameraPos[2] += movex * (float)modelviewMatrix[8];
|
||||
|
||||
cameraPos[0] += movey * (float)modelviewMatrix[2];
|
||||
cameraPos[1] += movey * (float)modelviewMatrix[6];
|
||||
cameraPos[2] += movey * (float)modelviewMatrix[10];
|
||||
|
||||
cameraPos[1] += (moveUp - moveDown) * keybSpeed * dt;
|
||||
|
||||
glEnable(GL_FOG);
|
||||
|
||||
if (sample)
|
||||
sample->handleRender();
|
||||
if (test)
|
||||
test->handleRender();
|
||||
|
||||
glDisable(GL_FOG);
|
||||
|
||||
// Render GUI
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
gluOrtho2D(0, width, 0, height);
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
|
||||
mouseOverMenu = false;
|
||||
|
||||
imguiBeginFrame(mousePos[0], mousePos[1], mouseButtonMask, mouseScroll);
|
||||
|
||||
if (sample)
|
||||
{
|
||||
sample->handleRenderOverlay((double*)projectionMatrix, (double*)modelviewMatrix, (int*)viewport);
|
||||
}
|
||||
if (test)
|
||||
{
|
||||
if (test->handleRenderOverlay((double*)projectionMatrix, (double*)modelviewMatrix, (int*)viewport))
|
||||
mouseOverMenu = true;
|
||||
}
|
||||
|
||||
// Help text.
|
||||
if (showMenu)
|
||||
{
|
||||
const char msg[] = "W/S/A/D: Move RMB: Rotate";
|
||||
imguiDrawText(280, height-20, IMGUI_ALIGN_LEFT, msg, imguiRGBA(255,255,255,128));
|
||||
}
|
||||
|
||||
if (showMenu)
|
||||
{
|
||||
if (imguiBeginScrollArea("Properties", width-250-10, 10, 250, height-20, &propScroll))
|
||||
mouseOverMenu = true;
|
||||
|
||||
if (imguiCheck("Show Log", showLog))
|
||||
showLog = !showLog;
|
||||
if (imguiCheck("Show Tools", showTools))
|
||||
showTools = !showTools;
|
||||
|
||||
imguiSeparator();
|
||||
imguiLabel("Sample");
|
||||
if (imguiButton(sampleName.c_str()))
|
||||
{
|
||||
if (showSample)
|
||||
{
|
||||
showSample = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
showSample = true;
|
||||
showLevels = false;
|
||||
showTestCases = false;
|
||||
}
|
||||
}
|
||||
|
||||
imguiSeparator();
|
||||
imguiLabel("Input Mesh");
|
||||
if (imguiButton(meshName.c_str()))
|
||||
{
|
||||
if (showLevels)
|
||||
{
|
||||
showLevels = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
showSample = false;
|
||||
showTestCases = false;
|
||||
showLevels = true;
|
||||
scanDirectory(meshesFolder, ".obj", files);
|
||||
scanDirectoryAppend(meshesFolder, ".gset", files);
|
||||
}
|
||||
}
|
||||
if (geom)
|
||||
{
|
||||
char text[64];
|
||||
snprintf(text, 64, "Verts: %.1fk Tris: %.1fk",
|
||||
geom->getMesh()->getVertCount()/1000.0f,
|
||||
geom->getMesh()->getTriCount()/1000.0f);
|
||||
imguiValue(text);
|
||||
}
|
||||
imguiSeparator();
|
||||
|
||||
if (geom && sample)
|
||||
{
|
||||
imguiSeparatorLine();
|
||||
|
||||
sample->handleSettings();
|
||||
|
||||
if (imguiButton("Build"))
|
||||
{
|
||||
ctx.resetLog();
|
||||
if (!sample->handleBuild())
|
||||
{
|
||||
showLog = true;
|
||||
logScroll = 0;
|
||||
}
|
||||
ctx.dumpLog("Build log %s:", meshName.c_str());
|
||||
|
||||
// Clear test.
|
||||
delete test;
|
||||
test = 0;
|
||||
}
|
||||
|
||||
imguiSeparator();
|
||||
}
|
||||
|
||||
if (sample)
|
||||
{
|
||||
imguiSeparatorLine();
|
||||
sample->handleDebugMode();
|
||||
}
|
||||
|
||||
imguiEndScrollArea();
|
||||
}
|
||||
|
||||
// Sample selection dialog.
|
||||
if (showSample)
|
||||
{
|
||||
static int levelScroll = 0;
|
||||
if (imguiBeginScrollArea("Choose Sample", width-10-250-10-200, height-10-250, 200, 250, &levelScroll))
|
||||
mouseOverMenu = true;
|
||||
|
||||
Sample* newSample = 0;
|
||||
for (int i = 0; i < g_nsamples; ++i)
|
||||
{
|
||||
if (imguiItem(g_samples[i].name.c_str()))
|
||||
{
|
||||
newSample = g_samples[i].create();
|
||||
if (newSample)
|
||||
sampleName = g_samples[i].name;
|
||||
}
|
||||
}
|
||||
if (newSample)
|
||||
{
|
||||
delete sample;
|
||||
sample = newSample;
|
||||
sample->setContext(&ctx);
|
||||
if (geom)
|
||||
{
|
||||
sample->handleMeshChanged(geom);
|
||||
}
|
||||
showSample = false;
|
||||
}
|
||||
|
||||
if (geom || sample)
|
||||
{
|
||||
const float* bmin = 0;
|
||||
const float* bmax = 0;
|
||||
if (geom)
|
||||
{
|
||||
bmin = geom->getNavMeshBoundsMin();
|
||||
bmax = geom->getNavMeshBoundsMax();
|
||||
}
|
||||
// Reset camera and fog to match the mesh bounds.
|
||||
if (bmin && bmax)
|
||||
{
|
||||
camr = sqrtf(rcSqr(bmax[0]-bmin[0]) +
|
||||
rcSqr(bmax[1]-bmin[1]) +
|
||||
rcSqr(bmax[2]-bmin[2])) / 2;
|
||||
cameraPos[0] = (bmax[0] + bmin[0]) / 2 + camr;
|
||||
cameraPos[1] = (bmax[1] + bmin[1]) / 2 + camr;
|
||||
cameraPos[2] = (bmax[2] + bmin[2]) / 2 + camr;
|
||||
camr *= 3;
|
||||
}
|
||||
cameraEulers[0] = 45;
|
||||
cameraEulers[1] = -45;
|
||||
glFogf(GL_FOG_START, camr*0.1f);
|
||||
glFogf(GL_FOG_END, camr*1.25f);
|
||||
}
|
||||
|
||||
imguiEndScrollArea();
|
||||
}
|
||||
|
||||
// Level selection dialog.
|
||||
if (showLevels)
|
||||
{
|
||||
static int levelScroll = 0;
|
||||
if (imguiBeginScrollArea("Choose Level", width - 10 - 250 - 10 - 200, height - 10 - 450, 200, 450, &levelScroll))
|
||||
mouseOverMenu = true;
|
||||
|
||||
vector<string>::const_iterator fileIter = files.begin();
|
||||
vector<string>::const_iterator filesEnd = files.end();
|
||||
vector<string>::const_iterator levelToLoad = filesEnd;
|
||||
for (; fileIter != filesEnd; ++fileIter)
|
||||
{
|
||||
if (imguiItem(fileIter->c_str()))
|
||||
{
|
||||
levelToLoad = fileIter;
|
||||
}
|
||||
}
|
||||
|
||||
if (levelToLoad != filesEnd)
|
||||
{
|
||||
meshName = *levelToLoad;
|
||||
showLevels = false;
|
||||
|
||||
delete geom;
|
||||
geom = 0;
|
||||
|
||||
string path = meshesFolder + "/" + meshName;
|
||||
|
||||
geom = new InputGeom;
|
||||
if (!geom->load(&ctx, path))
|
||||
{
|
||||
delete geom;
|
||||
geom = 0;
|
||||
|
||||
// Destroy the sample if it already had geometry loaded, as we've just deleted it!
|
||||
if (sample && sample->getInputGeom())
|
||||
{
|
||||
delete sample;
|
||||
sample = 0;
|
||||
}
|
||||
|
||||
showLog = true;
|
||||
logScroll = 0;
|
||||
ctx.dumpLog("Geom load log %s:", meshName.c_str());
|
||||
}
|
||||
if (sample && geom)
|
||||
{
|
||||
sample->handleMeshChanged(geom);
|
||||
}
|
||||
|
||||
if (geom || sample)
|
||||
{
|
||||
const float* bmin = 0;
|
||||
const float* bmax = 0;
|
||||
if (geom)
|
||||
{
|
||||
bmin = geom->getNavMeshBoundsMin();
|
||||
bmax = geom->getNavMeshBoundsMax();
|
||||
}
|
||||
// Reset camera and fog to match the mesh bounds.
|
||||
if (bmin && bmax)
|
||||
{
|
||||
camr = sqrtf(rcSqr(bmax[0]-bmin[0]) +
|
||||
rcSqr(bmax[1]-bmin[1]) +
|
||||
rcSqr(bmax[2]-bmin[2])) / 2;
|
||||
cameraPos[0] = (bmax[0] + bmin[0]) / 2 + camr;
|
||||
cameraPos[1] = (bmax[1] + bmin[1]) / 2 + camr;
|
||||
cameraPos[2] = (bmax[2] + bmin[2]) / 2 + camr;
|
||||
camr *= 3;
|
||||
}
|
||||
cameraEulers[0] = 45;
|
||||
cameraEulers[1] = -45;
|
||||
glFogf(GL_FOG_START, camr * 0.1f);
|
||||
glFogf(GL_FOG_END, camr * 1.25f);
|
||||
}
|
||||
}
|
||||
|
||||
imguiEndScrollArea();
|
||||
|
||||
}
|
||||
|
||||
// Test cases
|
||||
if (showTestCases)
|
||||
{
|
||||
static int testScroll = 0;
|
||||
if (imguiBeginScrollArea("Choose Test To Run", width-10-250-10-200, height-10-450, 200, 450, &testScroll))
|
||||
mouseOverMenu = true;
|
||||
|
||||
vector<string>::const_iterator fileIter = files.begin();
|
||||
vector<string>::const_iterator filesEnd = files.end();
|
||||
vector<string>::const_iterator testToLoad = filesEnd;
|
||||
for (; fileIter != filesEnd; ++fileIter)
|
||||
{
|
||||
if (imguiItem(fileIter->c_str()))
|
||||
{
|
||||
testToLoad = fileIter;
|
||||
}
|
||||
}
|
||||
|
||||
if (testToLoad != filesEnd)
|
||||
{
|
||||
string path = testCasesFolder + "/" + *testToLoad;
|
||||
test = new TestCase;
|
||||
if (test)
|
||||
{
|
||||
// Load the test.
|
||||
if (!test->load(path))
|
||||
{
|
||||
delete test;
|
||||
test = 0;
|
||||
}
|
||||
|
||||
// Create sample
|
||||
Sample* newSample = 0;
|
||||
for (int i = 0; i < g_nsamples; ++i)
|
||||
{
|
||||
if (g_samples[i].name == test->getSampleName())
|
||||
{
|
||||
newSample = g_samples[i].create();
|
||||
if (newSample)
|
||||
sampleName = g_samples[i].name;
|
||||
}
|
||||
}
|
||||
|
||||
delete sample;
|
||||
sample = newSample;
|
||||
|
||||
if (sample)
|
||||
{
|
||||
sample->setContext(&ctx);
|
||||
showSample = false;
|
||||
}
|
||||
|
||||
// Load geom.
|
||||
meshName = test->getGeomFileName();
|
||||
|
||||
|
||||
path = meshesFolder + "/" + meshName;
|
||||
|
||||
delete geom;
|
||||
geom = new InputGeom;
|
||||
if (!geom || !geom->load(&ctx, path))
|
||||
{
|
||||
delete geom;
|
||||
geom = 0;
|
||||
delete sample;
|
||||
sample = 0;
|
||||
showLog = true;
|
||||
logScroll = 0;
|
||||
ctx.dumpLog("Geom load log %s:", meshName.c_str());
|
||||
}
|
||||
if (sample && geom)
|
||||
{
|
||||
sample->handleMeshChanged(geom);
|
||||
}
|
||||
|
||||
// This will ensure that tile & poly bits are updated in tiled sample.
|
||||
if (sample)
|
||||
sample->handleSettings();
|
||||
|
||||
ctx.resetLog();
|
||||
if (sample && !sample->handleBuild())
|
||||
{
|
||||
ctx.dumpLog("Build log %s:", meshName.c_str());
|
||||
}
|
||||
|
||||
if (geom || sample)
|
||||
{
|
||||
const float* bmin = 0;
|
||||
const float* bmax = 0;
|
||||
if (geom)
|
||||
{
|
||||
bmin = geom->getNavMeshBoundsMin();
|
||||
bmax = geom->getNavMeshBoundsMax();
|
||||
}
|
||||
// Reset camera and fog to match the mesh bounds.
|
||||
if (bmin && bmax)
|
||||
{
|
||||
camr = sqrtf(rcSqr(bmax[0] - bmin[0]) +
|
||||
rcSqr(bmax[1] - bmin[1]) +
|
||||
rcSqr(bmax[2] - bmin[2])) / 2;
|
||||
cameraPos[0] = (bmax[0] + bmin[0]) / 2 + camr;
|
||||
cameraPos[1] = (bmax[1] + bmin[1]) / 2 + camr;
|
||||
cameraPos[2] = (bmax[2] + bmin[2]) / 2 + camr;
|
||||
camr *= 3;
|
||||
}
|
||||
cameraEulers[0] = 45;
|
||||
cameraEulers[1] = -45;
|
||||
glFogf(GL_FOG_START, camr * 0.2f);
|
||||
glFogf(GL_FOG_END, camr * 1.25f);
|
||||
}
|
||||
|
||||
// Do the tests.
|
||||
if (sample)
|
||||
test->doTests(sample->getNavMesh(), sample->getNavMeshQuery());
|
||||
}
|
||||
}
|
||||
|
||||
imguiEndScrollArea();
|
||||
}
|
||||
|
||||
|
||||
// Log
|
||||
if (showLog && showMenu)
|
||||
{
|
||||
if (imguiBeginScrollArea("Log", 250 + 20, 10, width - 300 - 250, 200, &logScroll))
|
||||
mouseOverMenu = true;
|
||||
for (int i = 0; i < ctx.getLogCount(); ++i)
|
||||
imguiLabel(ctx.getLogText(i));
|
||||
imguiEndScrollArea();
|
||||
}
|
||||
|
||||
// Left column tools menu
|
||||
if (!showTestCases && showTools && showMenu) // && geom && sample)
|
||||
{
|
||||
if (imguiBeginScrollArea("Tools", 10, 10, 250, height - 20, &toolsScroll))
|
||||
mouseOverMenu = true;
|
||||
|
||||
if (sample)
|
||||
sample->handleTools();
|
||||
|
||||
imguiEndScrollArea();
|
||||
}
|
||||
|
||||
// Marker
|
||||
if (markerPositionSet && gluProject((GLdouble)markerPosition[0], (GLdouble)markerPosition[1], (GLdouble)markerPosition[2],
|
||||
modelviewMatrix, projectionMatrix, viewport, &x, &y, &z))
|
||||
{
|
||||
// Draw marker circle
|
||||
glLineWidth(5.0f);
|
||||
glColor4ub(240,220,0,196);
|
||||
glBegin(GL_LINE_LOOP);
|
||||
const float r = 25.0f;
|
||||
for (int i = 0; i < 20; ++i)
|
||||
{
|
||||
const float a = (float)i / 20.0f * RC_PI*2;
|
||||
const float fx = (float)x + cosf(a)*r;
|
||||
const float fy = (float)y + sinf(a)*r;
|
||||
glVertex2f(fx,fy);
|
||||
}
|
||||
glEnd();
|
||||
glLineWidth(1.0f);
|
||||
}
|
||||
|
||||
imguiEndFrame();
|
||||
imguiRenderGLDraw();
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
SDL_GL_SwapWindow(window);
|
||||
}
|
||||
|
||||
imguiRenderGLDestroy();
|
||||
|
||||
SDL_Quit();
|
||||
|
||||
delete sample;
|
||||
delete geom;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,249 @@
|
||||
# - Find SDL2
|
||||
# Find the SDL2 headers and libraries
|
||||
#
|
||||
# SDL2::SDL2 - Imported target to use for building a library
|
||||
# SDL2::SDL2main - Imported interface target to use if you want SDL and SDLmain.
|
||||
# SDL2_FOUND - True if SDL2 was found.
|
||||
# SDL2_DYNAMIC - If we found a DLL version of SDL (meaning you might want to copy a DLL from SDL2::SDL2)
|
||||
#
|
||||
# Original Author:
|
||||
# 2015 Ryan Pavlik <ryan.pavlik@gmail.com> <abiryan@ryand.net>
|
||||
#
|
||||
# Copyright Sensics, Inc. 2015.
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE_1_0.txt or copy at
|
||||
# http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
# Set up architectures (for windows) and prefixes (for mingw builds)
|
||||
if(WIN32)
|
||||
if(MINGW)
|
||||
include(MinGWSearchPathExtras OPTIONAL)
|
||||
if(MINGWSEARCH_TARGET_TRIPLE)
|
||||
set(SDL2_PREFIX ${MINGWSEARCH_TARGET_TRIPLE})
|
||||
endif()
|
||||
endif()
|
||||
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
set(SDL2_LIB_PATH_SUFFIX lib/x64)
|
||||
if(NOT MSVC AND NOT SDL2_PREFIX)
|
||||
set(SDL2_PREFIX x86_64-w64-mingw32)
|
||||
endif()
|
||||
else()
|
||||
set(SDL2_LIB_PATH_SUFFIX lib/x86)
|
||||
if(NOT MSVC AND NOT SDL2_PREFIX)
|
||||
set(SDL2_PREFIX i686-w64-mingw32)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(SDL2_PREFIX)
|
||||
set(SDL2_ORIGPREFIXPATH ${CMAKE_PREFIX_PATH})
|
||||
if(SDL2_ROOT_DIR)
|
||||
list(APPEND CMAKE_PREFIX_PATH "${SDL2_ROOT_DIR}")
|
||||
endif()
|
||||
if(CMAKE_PREFIX_PATH)
|
||||
foreach(_prefix ${CMAKE_PREFIX_PATH})
|
||||
list(APPEND CMAKE_PREFIX_PATH "${_prefix}/${SDL2_PREFIX}")
|
||||
endforeach()
|
||||
endif()
|
||||
if(MINGWSEARCH_PREFIXES)
|
||||
list(APPEND CMAKE_PREFIX_PATH ${MINGWSEARCH_PREFIXES})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Invoke pkgconfig for hints
|
||||
find_package(PkgConfig QUIET)
|
||||
set(SDL2_INCLUDE_HINTS)
|
||||
set(SDL2_LIB_HINTS)
|
||||
if(PKG_CONFIG_FOUND)
|
||||
pkg_search_module(SDL2PC QUIET sdl2)
|
||||
if(SDL2PC_INCLUDE_DIRS)
|
||||
set(SDL2_INCLUDE_HINTS ${SDL2PC_INCLUDE_DIRS})
|
||||
endif()
|
||||
if(SDL2PC_LIBRARY_DIRS)
|
||||
set(SDL2_LIB_HINTS ${SDL2PC_LIBRARY_DIRS})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
|
||||
find_library(SDL2_LIBRARY
|
||||
NAMES
|
||||
SDL2
|
||||
HINTS
|
||||
${SDL2_LIB_HINTS}
|
||||
PATHS
|
||||
${SDL2_ROOT_DIR}
|
||||
ENV SDL2DIR
|
||||
PATH_SUFFIXES lib SDL2 ${SDL2_LIB_PATH_SUFFIX})
|
||||
|
||||
set(_sdl2_framework FALSE)
|
||||
# Some special-casing if we've found/been given a framework.
|
||||
# Handles whether we're given the library inside the framework or the framework itself.
|
||||
if(APPLE AND "${SDL2_LIBRARY}" MATCHES "(/[^/]+)*.framework(/.*)?$")
|
||||
set(_sdl2_framework TRUE)
|
||||
set(SDL2_FRAMEWORK "${SDL2_LIBRARY}")
|
||||
# Move up in the directory tree as required to get the framework directory.
|
||||
while("${SDL2_FRAMEWORK}" MATCHES "(/[^/]+)*.framework(/.*)$" AND NOT "${SDL2_FRAMEWORK}" MATCHES "(/[^/]+)*.framework$")
|
||||
get_filename_component(SDL2_FRAMEWORK "${SDL2_FRAMEWORK}" DIRECTORY)
|
||||
endwhile()
|
||||
if("${SDL2_FRAMEWORK}" MATCHES "(/[^/]+)*.framework$")
|
||||
set(SDL2_FRAMEWORK_NAME ${CMAKE_MATCH_1})
|
||||
# If we found a framework, do a search for the header ahead of time that will be more likely to get the framework header.
|
||||
find_path(SDL2_INCLUDE_DIR
|
||||
NAMES
|
||||
SDL_haptic.h # this file was introduced with SDL2
|
||||
HINTS
|
||||
"${SDL2_FRAMEWORK}/Headers/")
|
||||
else()
|
||||
# For some reason we couldn't get the framework directory itself.
|
||||
# Shouldn't happen, but might if something is weird.
|
||||
unset(SDL2_FRAMEWORK)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
find_path(SDL2_INCLUDE_DIR
|
||||
NAMES
|
||||
SDL_haptic.h # this file was introduced with SDL2
|
||||
HINTS
|
||||
${SDL2_INCLUDE_HINTS}
|
||||
PATHS
|
||||
${SDL2_ROOT_DIR}
|
||||
ENV SDL2DIR
|
||||
PATH_SUFFIXES include include/sdl2 include/SDL2 SDL2)
|
||||
|
||||
if(WIN32 AND SDL2_LIBRARY)
|
||||
find_file(SDL2_RUNTIME_LIBRARY
|
||||
NAMES
|
||||
SDL2.dll
|
||||
libSDL2.dll
|
||||
HINTS
|
||||
${SDL2_LIB_HINTS}
|
||||
PATHS
|
||||
${SDL2_ROOT_DIR}
|
||||
ENV SDL2DIR
|
||||
PATH_SUFFIXES bin lib ${SDL2_LIB_PATH_SUFFIX})
|
||||
endif()
|
||||
|
||||
|
||||
if(WIN32 OR ANDROID OR IOS OR (APPLE AND NOT _sdl2_framework))
|
||||
set(SDL2_EXTRA_REQUIRED SDL2_SDLMAIN_LIBRARY)
|
||||
find_library(SDL2_SDLMAIN_LIBRARY
|
||||
NAMES
|
||||
SDL2main
|
||||
PATHS
|
||||
${SDL2_ROOT_DIR}
|
||||
ENV SDL2DIR
|
||||
PATH_SUFFIXES lib ${SDL2_LIB_PATH_SUFFIX})
|
||||
endif()
|
||||
|
||||
if(MINGW AND NOT SDL2PC_FOUND)
|
||||
find_library(SDL2_MINGW_LIBRARY mingw32)
|
||||
find_library(SDL2_MWINDOWS_LIBRARY mwindows)
|
||||
endif()
|
||||
|
||||
if(SDL2_PREFIX)
|
||||
# Restore things the way they used to be.
|
||||
set(CMAKE_PREFIX_PATH ${SDL2_ORIGPREFIXPATH})
|
||||
endif()
|
||||
|
||||
# handle the QUIETLY and REQUIRED arguments and set QUATLIB_FOUND to TRUE if
|
||||
# all listed variables are TRUE
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(SDL2
|
||||
DEFAULT_MSG
|
||||
SDL2_LIBRARY
|
||||
SDL2_INCLUDE_DIR
|
||||
${SDL2_EXTRA_REQUIRED})
|
||||
|
||||
if(SDL2_FOUND)
|
||||
if(NOT TARGET SDL2::SDL2)
|
||||
# Create SDL2::SDL2
|
||||
if(WIN32 AND SDL2_RUNTIME_LIBRARY)
|
||||
set(SDL2_DYNAMIC TRUE)
|
||||
add_library(SDL2::SDL2 SHARED IMPORTED)
|
||||
set_target_properties(SDL2::SDL2
|
||||
PROPERTIES
|
||||
IMPORTED_IMPLIB "${SDL2_LIBRARY}"
|
||||
IMPORTED_LOCATION "${SDL2_RUNTIME_LIBRARY}"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${SDL2_INCLUDE_DIR}"
|
||||
)
|
||||
else()
|
||||
add_library(SDL2::SDL2 UNKNOWN IMPORTED)
|
||||
if(SDL2_FRAMEWORK AND SDL2_FRAMEWORK_NAME)
|
||||
# Handle the case that SDL2 is a framework and we were able to decompose it above.
|
||||
set_target_properties(SDL2::SDL2 PROPERTIES
|
||||
IMPORTED_LOCATION "${SDL2_FRAMEWORK}/${SDL2_FRAMEWORK_NAME}")
|
||||
elseif(_sdl2_framework AND SDL2_LIBRARY MATCHES "(/[^/]+)*.framework$")
|
||||
# Handle the case that SDL2 is a framework and SDL_LIBRARY is just the framework itself.
|
||||
|
||||
# This takes the basename of the framework, without the extension,
|
||||
# and sets it (as a child of the framework) as the imported location for the target.
|
||||
# This is the library symlink inside of the framework.
|
||||
set_target_properties(SDL2::SDL2 PROPERTIES
|
||||
IMPORTED_LOCATION "${SDL2_LIBRARY}/${CMAKE_MATCH_1}")
|
||||
else()
|
||||
# Handle non-frameworks (including non-Mac), as well as the case that we're given the library inside of the framework
|
||||
set_target_properties(SDL2::SDL2 PROPERTIES
|
||||
IMPORTED_LOCATION "${SDL2_LIBRARY}")
|
||||
endif()
|
||||
set_target_properties(SDL2::SDL2
|
||||
PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${SDL2_INCLUDE_DIR}"
|
||||
)
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
# Need Cocoa here, is always a framework
|
||||
find_library(SDL2_COCOA_LIBRARY Cocoa)
|
||||
list(APPEND SDL2_EXTRA_REQUIRED SDL2_COCOA_LIBRARY)
|
||||
if(SDL2_COCOA_LIBRARY)
|
||||
set_target_properties(SDL2::SDL2 PROPERTIES
|
||||
IMPORTED_LINK_INTERFACE_LIBRARIES ${SDL2_COCOA_LIBRARY})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
# Compute what to do with SDL2main
|
||||
set(SDL2MAIN_LIBRARIES SDL2::SDL2)
|
||||
add_library(SDL2::SDL2main INTERFACE IMPORTED)
|
||||
if(SDL2_SDLMAIN_LIBRARY)
|
||||
add_library(SDL2::SDL2main_real STATIC IMPORTED)
|
||||
set_target_properties(SDL2::SDL2main_real
|
||||
PROPERTIES
|
||||
IMPORTED_LOCATION "${SDL2_SDLMAIN_LIBRARY}")
|
||||
set(SDL2MAIN_LIBRARIES SDL2::SDL2main_real ${SDL2MAIN_LIBRARIES})
|
||||
endif()
|
||||
if(MINGW)
|
||||
# MinGW requires some additional libraries to appear earlier in the link line.
|
||||
if(SDL2PC_LIBRARIES)
|
||||
# Use pkgconfig-suggested extra libraries if available.
|
||||
list(REMOVE_ITEM SDL2PC_LIBRARIES SDL2main SDL2)
|
||||
set(SDL2MAIN_LIBRARIES ${SDL2PC_LIBRARIES} ${SDL2MAIN_LIBRARIES})
|
||||
else()
|
||||
# fall back to extra libraries specified in pkg-config in
|
||||
# an official binary distro of SDL2 for MinGW I downloaded
|
||||
if(SDL2_MINGW_LIBRARY)
|
||||
set(SDL2MAIN_LIBRARIES ${SDL2_MINGW_LIBRARY} ${SDL2MAIN_LIBRARIES})
|
||||
endif()
|
||||
if(SDL2_MWINDOWS_LIBRARY)
|
||||
set(SDL2MAIN_LIBRARIES ${SDL2_MWINDOWS_LIBRARY} ${SDL2MAIN_LIBRARIES})
|
||||
endif()
|
||||
endif()
|
||||
set_target_properties(SDL2::SDL2main
|
||||
PROPERTIES
|
||||
INTERFACE_COMPILE_DEFINITIONS "main=SDL_main")
|
||||
endif()
|
||||
set_target_properties(SDL2::SDL2main
|
||||
PROPERTIES
|
||||
INTERFACE_LINK_LIBRARIES "${SDL2MAIN_LIBRARIES}")
|
||||
endif()
|
||||
mark_as_advanced(SDL2_ROOT_DIR)
|
||||
endif()
|
||||
|
||||
mark_as_advanced(SDL2_LIBRARY
|
||||
SDL2_RUNTIME_LIBRARY
|
||||
SDL2_INCLUDE_DIR
|
||||
SDL2_SDLMAIN_LIBRARY
|
||||
SDL2_COCOA_LIBRARY
|
||||
SDL2_MINGW_LIBRARY
|
||||
SDL2_MWINDOWS_LIBRARY)
|
||||
@@ -0,0 +1,268 @@
|
||||
--
|
||||
-- premake5 file to build RecastDemo
|
||||
-- http://premake.github.io/
|
||||
--
|
||||
|
||||
local action = _ACTION or ""
|
||||
local todir = "Build/" .. action
|
||||
|
||||
solution "recastnavigation"
|
||||
configurations {
|
||||
"Debug",
|
||||
"Release"
|
||||
}
|
||||
|
||||
location (todir)
|
||||
|
||||
floatingpoint "Fast"
|
||||
symbols "On"
|
||||
exceptionhandling "Off"
|
||||
rtti "Off"
|
||||
flags { "FatalCompileWarnings" }
|
||||
|
||||
-- debug configs
|
||||
configuration "Debug*"
|
||||
defines { "DEBUG" }
|
||||
targetdir ( todir .. "/lib/Debug" )
|
||||
|
||||
-- release configs
|
||||
configuration "Release*"
|
||||
defines { "NDEBUG" }
|
||||
optimize "On"
|
||||
targetdir ( todir .. "/lib/Release" )
|
||||
|
||||
configuration "not windows"
|
||||
warnings "Extra"
|
||||
|
||||
-- windows specific
|
||||
configuration "windows"
|
||||
platforms { "Win32", "Win64" }
|
||||
defines { "WIN32", "_WINDOWS", "_CRT_SECURE_NO_WARNINGS", "_HAS_EXCEPTIONS=0" }
|
||||
-- warnings "Extra" uses /W4 which is too aggressive for us, so use W3 instead.
|
||||
-- Disable:
|
||||
-- * C4351: new behavior for array initialization
|
||||
buildoptions { "/W3", "/wd4351" }
|
||||
|
||||
filter "platforms:Win32"
|
||||
architecture "x32"
|
||||
|
||||
filter "platforms:Win64"
|
||||
architecture "x64"
|
||||
|
||||
project "DebugUtils"
|
||||
language "C++"
|
||||
kind "StaticLib"
|
||||
includedirs {
|
||||
"../DebugUtils/Include",
|
||||
"../Detour/Include",
|
||||
"../DetourTileCache/Include",
|
||||
"../Recast/Include"
|
||||
}
|
||||
files {
|
||||
"../DebugUtils/Include/*.h",
|
||||
"../DebugUtils/Source/*.cpp"
|
||||
}
|
||||
|
||||
project "Detour"
|
||||
language "C++"
|
||||
kind "StaticLib"
|
||||
includedirs {
|
||||
"../Detour/Include"
|
||||
}
|
||||
files {
|
||||
"../Detour/Include/*.h",
|
||||
"../Detour/Source/*.cpp"
|
||||
}
|
||||
-- linux library cflags and libs
|
||||
configuration { "linux", "gmake" }
|
||||
buildoptions {
|
||||
"-Wno-error=class-memaccess"
|
||||
}
|
||||
|
||||
|
||||
project "DetourCrowd"
|
||||
language "C++"
|
||||
kind "StaticLib"
|
||||
includedirs {
|
||||
"../DetourCrowd/Include",
|
||||
"../Detour/Include",
|
||||
"../Recast/Include"
|
||||
}
|
||||
files {
|
||||
"../DetourCrowd/Include/*.h",
|
||||
"../DetourCrowd/Source/*.cpp"
|
||||
}
|
||||
|
||||
project "DetourTileCache"
|
||||
language "C++"
|
||||
kind "StaticLib"
|
||||
includedirs {
|
||||
"../DetourTileCache/Include",
|
||||
"../Detour/Include",
|
||||
"../Recast/Include"
|
||||
}
|
||||
files {
|
||||
"../DetourTileCache/Include/*.h",
|
||||
"../DetourTileCache/Source/*.cpp"
|
||||
}
|
||||
|
||||
project "Recast"
|
||||
language "C++"
|
||||
kind "StaticLib"
|
||||
includedirs {
|
||||
"../Recast/Include"
|
||||
}
|
||||
files {
|
||||
"../Recast/Include/*.h",
|
||||
"../Recast/Source/*.cpp"
|
||||
}
|
||||
|
||||
project "RecastDemo"
|
||||
language "C++"
|
||||
kind "WindowedApp"
|
||||
includedirs {
|
||||
"../RecastDemo/Include",
|
||||
"../RecastDemo/Contrib",
|
||||
"../RecastDemo/Contrib/fastlz",
|
||||
"../DebugUtils/Include",
|
||||
"../Detour/Include",
|
||||
"../DetourCrowd/Include",
|
||||
"../DetourTileCache/Include",
|
||||
"../Recast/Include"
|
||||
}
|
||||
files {
|
||||
"../RecastDemo/Include/*.h",
|
||||
"../RecastDemo/Source/*.cpp",
|
||||
"../RecastDemo/Contrib/fastlz/*.h",
|
||||
"../RecastDemo/Contrib/fastlz/*.c"
|
||||
}
|
||||
|
||||
-- project dependencies
|
||||
links {
|
||||
"DebugUtils",
|
||||
"Detour",
|
||||
"DetourCrowd",
|
||||
"DetourTileCache",
|
||||
"Recast"
|
||||
}
|
||||
|
||||
-- distribute executable in RecastDemo/Bin directory
|
||||
targetdir "Bin"
|
||||
|
||||
-- linux library cflags and libs
|
||||
configuration { "linux", "gmake" }
|
||||
buildoptions {
|
||||
"`pkg-config --cflags sdl2`",
|
||||
"`pkg-config --cflags gl`",
|
||||
"`pkg-config --cflags glu`",
|
||||
"-Wno-ignored-qualifiers",
|
||||
"-Wno-error=class-memaccess"
|
||||
|
||||
}
|
||||
linkoptions {
|
||||
"`pkg-config --libs sdl2`",
|
||||
"`pkg-config --libs gl`",
|
||||
"`pkg-config --libs glu`"
|
||||
}
|
||||
|
||||
-- windows library cflags and libs
|
||||
configuration { "windows" }
|
||||
includedirs { "../RecastDemo/Contrib/SDL/include" }
|
||||
libdirs { "../RecastDemo/Contrib/SDL/lib/%{cfg.architecture:gsub('x86_64', 'x64')}" }
|
||||
debugdir "../RecastDemo/Bin/"
|
||||
links {
|
||||
"glu32",
|
||||
"opengl32",
|
||||
"SDL2",
|
||||
"SDL2main",
|
||||
}
|
||||
postbuildcommands {
|
||||
-- Copy the SDL2 dll to the Bin folder.
|
||||
'{COPY} "%{path.getabsolute("Contrib/SDL/lib/" .. cfg.architecture:gsub("x86_64", "x64") .. "/SDL2.dll")}" "%{cfg.targetdir}"'
|
||||
}
|
||||
|
||||
-- mac includes and libs
|
||||
configuration { "macosx" }
|
||||
kind "ConsoleApp" -- xcode4 failes to run the project if using WindowedApp
|
||||
includedirs { "/Library/Frameworks/SDL2.framework/Headers" }
|
||||
links {
|
||||
"OpenGL.framework",
|
||||
"SDL2.framework",
|
||||
"Cocoa.framework",
|
||||
}
|
||||
|
||||
project "Tests"
|
||||
language "C++"
|
||||
kind "ConsoleApp"
|
||||
|
||||
-- Catch requires RTTI and exceptions
|
||||
exceptionhandling "On"
|
||||
rtti "On"
|
||||
|
||||
includedirs {
|
||||
"../DebugUtils/Include",
|
||||
"../Detour/Include",
|
||||
"../DetourCrowd/Include",
|
||||
"../DetourTileCache/Include",
|
||||
"../Recast/Include",
|
||||
"../Recast/Source",
|
||||
"../Tests/Recast",
|
||||
"../Tests",
|
||||
}
|
||||
files {
|
||||
"../Tests/*.h",
|
||||
"../Tests/*.hpp",
|
||||
"../Tests/*.cpp",
|
||||
"../Tests/Recast/*.h",
|
||||
"../Tests/Recast/*.cpp",
|
||||
"../Tests/Detour/*.h",
|
||||
"../Tests/Detour/*.cpp",
|
||||
}
|
||||
|
||||
-- project dependencies
|
||||
links {
|
||||
"DebugUtils",
|
||||
"Detour",
|
||||
"DetourCrowd",
|
||||
"DetourTileCache",
|
||||
"Recast",
|
||||
}
|
||||
|
||||
-- distribute executable in RecastDemo/Bin directory
|
||||
targetdir "Bin"
|
||||
|
||||
-- linux library cflags and libs
|
||||
configuration { "linux", "gmake" }
|
||||
buildoptions {
|
||||
"`pkg-config --cflags sdl2`",
|
||||
"`pkg-config --cflags gl`",
|
||||
"`pkg-config --cflags glu`",
|
||||
"-Wno-parentheses" -- Disable parentheses warning for the Tests target, as Catch's macros generate this everywhere.
|
||||
}
|
||||
linkoptions {
|
||||
"`pkg-config --libs sdl2`",
|
||||
"`pkg-config --libs gl`",
|
||||
"`pkg-config --libs glu`"
|
||||
}
|
||||
|
||||
-- windows library cflags and libs
|
||||
configuration { "windows" }
|
||||
includedirs { "../RecastDemo/Contrib/SDL/include" }
|
||||
libdirs { "../RecastDemo/Contrib/SDL/lib/%{cfg.architecture:gsub('x86_64', 'x64')}" }
|
||||
debugdir "../RecastDemo/Bin/"
|
||||
links {
|
||||
"glu32",
|
||||
"opengl32",
|
||||
"SDL2",
|
||||
"SDL2main",
|
||||
}
|
||||
|
||||
-- mac includes and libs
|
||||
configuration { "macosx" }
|
||||
kind "ConsoleApp"
|
||||
includedirs { "/Library/Frameworks/SDL2.framework/Headers" }
|
||||
links {
|
||||
"OpenGL.framework",
|
||||
"SDL2.framework",
|
||||
"Cocoa.framework",
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 450 KiB |
Reference in New Issue
Block a user