initial commit, 4.5 stable
Some checks failed
🔗 GHA / 📊 Static checks (push) Has been cancelled
🔗 GHA / 🤖 Android (push) Has been cancelled
🔗 GHA / 🍏 iOS (push) Has been cancelled
🔗 GHA / 🐧 Linux (push) Has been cancelled
🔗 GHA / 🍎 macOS (push) Has been cancelled
🔗 GHA / 🏁 Windows (push) Has been cancelled
🔗 GHA / 🌐 Web (push) Has been cancelled
Some checks failed
🔗 GHA / 📊 Static checks (push) Has been cancelled
🔗 GHA / 🤖 Android (push) Has been cancelled
🔗 GHA / 🍏 iOS (push) Has been cancelled
🔗 GHA / 🐧 Linux (push) Has been cancelled
🔗 GHA / 🍎 macOS (push) Has been cancelled
🔗 GHA / 🏁 Windows (push) Has been cancelled
🔗 GHA / 🌐 Web (push) Has been cancelled
This commit is contained in:
18
thirdparty/recastnavigation/License.txt
vendored
Normal file
18
thirdparty/recastnavigation/License.txt
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
Copyright (c) 2009 Mikko Mononen memon@inside.org
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
1336
thirdparty/recastnavigation/Recast/Include/Recast.h
vendored
Normal file
1336
thirdparty/recastnavigation/Recast/Include/Recast.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
372
thirdparty/recastnavigation/Recast/Include/RecastAlloc.h
vendored
Normal file
372
thirdparty/recastnavigation/Recast/Include/RecastAlloc.h
vendored
Normal file
@@ -0,0 +1,372 @@
|
||||
//
|
||||
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//
|
||||
|
||||
#ifndef RECASTALLOC_H
|
||||
#define RECASTALLOC_H
|
||||
|
||||
#include "RecastAssert.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/// Provides hint values to the memory allocator on how long the
|
||||
/// memory is expected to be used.
|
||||
enum rcAllocHint
|
||||
{
|
||||
RC_ALLOC_PERM, ///< Memory will persist after a function call.
|
||||
RC_ALLOC_TEMP ///< Memory used temporarily within a function.
|
||||
};
|
||||
|
||||
/// A memory allocation function.
|
||||
// @param[in] size The size, in bytes of memory, to allocate.
|
||||
// @param[in] rcAllocHint A hint to the allocator on how long the memory is expected to be in use.
|
||||
// @return A pointer to the beginning of the allocated memory block, or null if the allocation failed.
|
||||
/// @see rcAllocSetCustom
|
||||
typedef void* (rcAllocFunc)(size_t size, rcAllocHint hint);
|
||||
|
||||
/// A memory deallocation function.
|
||||
/// @param[in] ptr A pointer to a memory block previously allocated using #rcAllocFunc.
|
||||
/// @see rcAllocSetCustom
|
||||
typedef void (rcFreeFunc)(void* ptr);
|
||||
|
||||
/// Sets the base custom allocation functions to be used by Recast.
|
||||
/// @param[in] allocFunc The memory allocation function to be used by #rcAlloc
|
||||
/// @param[in] freeFunc The memory de-allocation function to be used by #rcFree
|
||||
///
|
||||
/// @see rcAlloc, rcFree
|
||||
void rcAllocSetCustom(rcAllocFunc *allocFunc, rcFreeFunc *freeFunc);
|
||||
|
||||
/// Allocates a memory block.
|
||||
///
|
||||
/// @param[in] size The size, in bytes of memory, to allocate.
|
||||
/// @param[in] hint A hint to the allocator on how long the memory is expected to be in use.
|
||||
/// @return A pointer to the beginning of the allocated memory block, or null if the allocation failed.
|
||||
///
|
||||
/// @see rcFree, rcAllocSetCustom
|
||||
void* rcAlloc(size_t size, rcAllocHint hint);
|
||||
|
||||
/// Deallocates a memory block. If @p ptr is NULL, this does nothing.
|
||||
///
|
||||
/// @warning This function leaves the value of @p ptr unchanged. So it still
|
||||
/// points to the same (now invalid) location, and not to null.
|
||||
///
|
||||
/// @param[in] ptr A pointer to a memory block previously allocated using #rcAlloc.
|
||||
///
|
||||
/// @see rcAlloc, rcAllocSetCustom
|
||||
void rcFree(void* ptr);
|
||||
|
||||
/// An implementation of operator new usable for placement new. The default one is part of STL (which we don't use).
|
||||
/// rcNewTag is a dummy type used to differentiate our operator from the STL one, in case users import both Recast
|
||||
/// and STL.
|
||||
struct rcNewTag {};
|
||||
inline void* operator new(size_t, const rcNewTag&, void* p) { return p; }
|
||||
inline void operator delete(void*, const rcNewTag&, void*) {}
|
||||
|
||||
/// Signed to avoid warnnings when comparing to int loop indexes, and common error with comparing to zero.
|
||||
/// MSVC2010 has a bug where ssize_t is unsigned (!!!).
|
||||
typedef intptr_t rcSizeType;
|
||||
#define RC_SIZE_MAX INTPTR_MAX
|
||||
|
||||
/// Macros to hint to the compiler about the likeliest branch. Please add a benchmark that demonstrates a performance
|
||||
/// improvement before introducing use cases.
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#define rcLikely(x) __builtin_expect((x), true)
|
||||
#define rcUnlikely(x) __builtin_expect((x), false)
|
||||
#else
|
||||
#define rcLikely(x) (x)
|
||||
#define rcUnlikely(x) (x)
|
||||
#endif
|
||||
|
||||
/// Variable-sized storage type. Mimics the interface of std::vector<T> with some notable differences:
|
||||
/// * Uses rcAlloc()/rcFree() to handle storage.
|
||||
/// * No support for a custom allocator.
|
||||
/// * Uses signed size instead of size_t to avoid warnings in for loops: "for (int i = 0; i < foo.size(); i++)"
|
||||
/// * Omits methods of limited utility: insert/erase, (bad performance), at (we don't use exceptions), operator=.
|
||||
/// * assign() and the pre-sizing constructor follow C++11 semantics -- they don't construct a temporary if no value is provided.
|
||||
/// * push_back() and resize() support adding values from the current vector. Range-based constructors and assign(begin, end) do not.
|
||||
/// * No specialization for bool.
|
||||
template <typename T, rcAllocHint H>
|
||||
class rcVectorBase {
|
||||
rcSizeType m_size;
|
||||
rcSizeType m_cap;
|
||||
T* m_data;
|
||||
// Constructs a T at the give address with either the copy constructor or the default.
|
||||
static void construct(T* p, const T& v) { ::new(rcNewTag(), (void*)p) T(v); }
|
||||
static void construct(T* p) { ::new(rcNewTag(), (void*)p) T; }
|
||||
static void construct_range(T* begin, T* end);
|
||||
static void construct_range(T* begin, T* end, const T& value);
|
||||
static void copy_range(T* dst, const T* begin, const T* end);
|
||||
void destroy_range(rcSizeType begin, rcSizeType end);
|
||||
// Creates an array of the given size, copies all of this vector's data into it, and returns it.
|
||||
T* allocate_and_copy(rcSizeType size);
|
||||
void resize_impl(rcSizeType size, const T* value);
|
||||
// Requires: min_capacity > m_cap.
|
||||
rcSizeType get_new_capacity(rcSizeType min_capacity);
|
||||
public:
|
||||
typedef rcSizeType size_type;
|
||||
typedef T value_type;
|
||||
|
||||
rcVectorBase() : m_size(0), m_cap(0), m_data(0) {}
|
||||
rcVectorBase(const rcVectorBase<T, H>& other) : m_size(0), m_cap(0), m_data(0) { assign(other.begin(), other.end()); }
|
||||
explicit rcVectorBase(rcSizeType count) : m_size(0), m_cap(0), m_data(0) { resize(count); }
|
||||
rcVectorBase(rcSizeType count, const T& value) : m_size(0), m_cap(0), m_data(0) { resize(count, value); }
|
||||
rcVectorBase(const T* begin, const T* end) : m_size(0), m_cap(0), m_data(0) { assign(begin, end); }
|
||||
~rcVectorBase() { destroy_range(0, m_size); rcFree(m_data); }
|
||||
|
||||
// Unlike in std::vector, we return a bool to indicate whether the alloc was successful.
|
||||
bool reserve(rcSizeType size);
|
||||
|
||||
void assign(rcSizeType count, const T& value) { clear(); resize(count, value); }
|
||||
void assign(const T* begin, const T* end);
|
||||
|
||||
void resize(rcSizeType size) { resize_impl(size, NULL); }
|
||||
void resize(rcSizeType size, const T& value) { resize_impl(size, &value); }
|
||||
// Not implemented as resize(0) because resize requires T to be default-constructible.
|
||||
void clear() { destroy_range(0, m_size); m_size = 0; }
|
||||
|
||||
void push_back(const T& value);
|
||||
void pop_back() { rcAssert(m_size > 0); back().~T(); m_size--; }
|
||||
|
||||
rcSizeType size() const { return m_size; }
|
||||
rcSizeType capacity() const { return m_cap; }
|
||||
bool empty() const { return size() == 0; }
|
||||
|
||||
const T& operator[](rcSizeType i) const { rcAssert(i >= 0 && i < m_size); return m_data[i]; }
|
||||
T& operator[](rcSizeType i) { rcAssert(i >= 0 && i < m_size); return m_data[i]; }
|
||||
|
||||
const T& front() const { rcAssert(m_size); return m_data[0]; }
|
||||
T& front() { rcAssert(m_size); return m_data[0]; }
|
||||
const T& back() const { rcAssert(m_size); return m_data[m_size - 1]; }
|
||||
T& back() { rcAssert(m_size); return m_data[m_size - 1]; }
|
||||
const T* data() const { return m_data; }
|
||||
T* data() { return m_data; }
|
||||
|
||||
T* begin() { return m_data; }
|
||||
T* end() { return m_data + m_size; }
|
||||
const T* begin() const { return m_data; }
|
||||
const T* end() const { return m_data + m_size; }
|
||||
|
||||
void swap(rcVectorBase<T, H>& other);
|
||||
|
||||
// Explicitly deleted.
|
||||
rcVectorBase& operator=(const rcVectorBase<T, H>& other);
|
||||
};
|
||||
|
||||
template<typename T, rcAllocHint H>
|
||||
bool rcVectorBase<T, H>::reserve(rcSizeType count) {
|
||||
if (count <= m_cap) {
|
||||
return true;
|
||||
}
|
||||
T* new_data = allocate_and_copy(count);
|
||||
if (!new_data) {
|
||||
return false;
|
||||
}
|
||||
destroy_range(0, m_size);
|
||||
rcFree(m_data);
|
||||
m_data = new_data;
|
||||
m_cap = count;
|
||||
return true;
|
||||
}
|
||||
template <typename T, rcAllocHint H>
|
||||
T* rcVectorBase<T, H>::allocate_and_copy(rcSizeType size) {
|
||||
rcAssert(RC_SIZE_MAX / static_cast<rcSizeType>(sizeof(T)) >= size);
|
||||
T* new_data = static_cast<T*>(rcAlloc(sizeof(T) * size, H));
|
||||
if (new_data) {
|
||||
copy_range(new_data, m_data, m_data + m_size);
|
||||
}
|
||||
return new_data;
|
||||
}
|
||||
template <typename T, rcAllocHint H>
|
||||
void rcVectorBase<T, H>::assign(const T* begin, const T* end) {
|
||||
clear();
|
||||
reserve(end - begin);
|
||||
m_size = end - begin;
|
||||
copy_range(m_data, begin, end);
|
||||
}
|
||||
template <typename T, rcAllocHint H>
|
||||
void rcVectorBase<T, H>::push_back(const T& value) {
|
||||
// rcLikely increases performance by ~50% on BM_rcVector_PushPreallocated,
|
||||
// and by ~2-5% on BM_rcVector_Push.
|
||||
if (rcLikely(m_size < m_cap)) {
|
||||
construct(m_data + m_size++, value);
|
||||
return;
|
||||
}
|
||||
|
||||
const rcSizeType new_cap = get_new_capacity(m_cap + 1);
|
||||
T* data = allocate_and_copy(new_cap);
|
||||
// construct between allocate and destroy+free in case value is
|
||||
// in this vector.
|
||||
construct(data + m_size, value);
|
||||
destroy_range(0, m_size);
|
||||
m_size++;
|
||||
m_cap = new_cap;
|
||||
rcFree(m_data);
|
||||
m_data = data;
|
||||
}
|
||||
|
||||
template <typename T, rcAllocHint H>
|
||||
rcSizeType rcVectorBase<T, H>::get_new_capacity(rcSizeType min_capacity) {
|
||||
rcAssert(min_capacity <= RC_SIZE_MAX);
|
||||
if (rcUnlikely(m_cap >= RC_SIZE_MAX / 2))
|
||||
return RC_SIZE_MAX;
|
||||
return 2 * m_cap > min_capacity ? 2 * m_cap : min_capacity;
|
||||
}
|
||||
|
||||
template <typename T, rcAllocHint H>
|
||||
void rcVectorBase<T, H>::resize_impl(rcSizeType size, const T* value) {
|
||||
if (size < m_size) {
|
||||
destroy_range(size, m_size);
|
||||
m_size = size;
|
||||
} else if (size > m_size) {
|
||||
if (size <= m_cap) {
|
||||
if (value) {
|
||||
construct_range(m_data + m_size, m_data + size, *value);
|
||||
} else {
|
||||
construct_range(m_data + m_size, m_data + size);
|
||||
}
|
||||
m_size = size;
|
||||
} else {
|
||||
const rcSizeType new_cap = get_new_capacity(size);
|
||||
T* new_data = allocate_and_copy(new_cap);
|
||||
// We defer deconstructing/freeing old data until after constructing
|
||||
// new elements in case "value" is there.
|
||||
if (value) {
|
||||
construct_range(new_data + m_size, new_data + size, *value);
|
||||
} else {
|
||||
construct_range(new_data + m_size, new_data + size);
|
||||
}
|
||||
destroy_range(0, m_size);
|
||||
rcFree(m_data);
|
||||
m_data = new_data;
|
||||
m_cap = new_cap;
|
||||
m_size = size;
|
||||
}
|
||||
}
|
||||
}
|
||||
template <typename T, rcAllocHint H>
|
||||
void rcVectorBase<T, H>::swap(rcVectorBase<T, H>& other) {
|
||||
// TODO: Reorganize headers so we can use rcSwap here.
|
||||
rcSizeType tmp_cap = other.m_cap;
|
||||
rcSizeType tmp_size = other.m_size;
|
||||
T* tmp_data = other.m_data;
|
||||
|
||||
other.m_cap = m_cap;
|
||||
other.m_size = m_size;
|
||||
other.m_data = m_data;
|
||||
|
||||
m_cap = tmp_cap;
|
||||
m_size = tmp_size;
|
||||
m_data = tmp_data;
|
||||
}
|
||||
// static
|
||||
template <typename T, rcAllocHint H>
|
||||
void rcVectorBase<T, H>::construct_range(T* begin, T* end) {
|
||||
for (T* p = begin; p < end; p++) {
|
||||
construct(p);
|
||||
}
|
||||
}
|
||||
// static
|
||||
template <typename T, rcAllocHint H>
|
||||
void rcVectorBase<T, H>::construct_range(T* begin, T* end, const T& value) {
|
||||
for (T* p = begin; p < end; p++) {
|
||||
construct(p, value);
|
||||
}
|
||||
}
|
||||
// static
|
||||
template <typename T, rcAllocHint H>
|
||||
void rcVectorBase<T, H>::copy_range(T* dst, const T* begin, const T* end) {
|
||||
for (rcSizeType i = 0 ; i < end - begin; i++) {
|
||||
construct(dst + i, begin[i]);
|
||||
}
|
||||
}
|
||||
template <typename T, rcAllocHint H>
|
||||
void rcVectorBase<T, H>::destroy_range(rcSizeType begin, rcSizeType end) {
|
||||
for (rcSizeType i = begin; i < end; i++) {
|
||||
m_data[i].~T();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
class rcTempVector : public rcVectorBase<T, RC_ALLOC_TEMP> {
|
||||
typedef rcVectorBase<T, RC_ALLOC_TEMP> Base;
|
||||
public:
|
||||
rcTempVector() : Base() {}
|
||||
explicit rcTempVector(rcSizeType size) : Base(size) {}
|
||||
rcTempVector(rcSizeType size, const T& value) : Base(size, value) {}
|
||||
rcTempVector(const rcTempVector<T>& other) : Base(other) {}
|
||||
rcTempVector(const T* begin, const T* end) : Base(begin, end) {}
|
||||
};
|
||||
template <typename T>
|
||||
class rcPermVector : public rcVectorBase<T, RC_ALLOC_PERM> {
|
||||
typedef rcVectorBase<T, RC_ALLOC_PERM> Base;
|
||||
public:
|
||||
rcPermVector() : Base() {}
|
||||
explicit rcPermVector(rcSizeType size) : Base(size) {}
|
||||
rcPermVector(rcSizeType size, const T& value) : Base(size, value) {}
|
||||
rcPermVector(const rcPermVector<T>& other) : Base(other) {}
|
||||
rcPermVector(const T* begin, const T* end) : Base(begin, end) {}
|
||||
};
|
||||
|
||||
|
||||
/// Legacy class. Prefer rcVector<int>.
|
||||
class rcIntArray
|
||||
{
|
||||
rcTempVector<int> m_impl;
|
||||
public:
|
||||
rcIntArray() {}
|
||||
rcIntArray(int n) : m_impl(n, 0) {}
|
||||
void push(int item) { m_impl.push_back(item); }
|
||||
void resize(int size) { m_impl.resize(size); }
|
||||
void clear() { m_impl.clear(); }
|
||||
int pop()
|
||||
{
|
||||
int v = m_impl.back();
|
||||
m_impl.pop_back();
|
||||
return v;
|
||||
}
|
||||
int size() const { return static_cast<int>(m_impl.size()); }
|
||||
int& operator[](int index) { return m_impl[index]; }
|
||||
int operator[](int index) const { return m_impl[index]; }
|
||||
};
|
||||
|
||||
/// A simple helper class used to delete an array when it goes out of scope.
|
||||
/// @note This class is rarely if ever used by the end user.
|
||||
template<class T> class rcScopedDelete
|
||||
{
|
||||
T* ptr;
|
||||
public:
|
||||
|
||||
/// Constructs an instance with a null pointer.
|
||||
inline rcScopedDelete() : ptr(0) {}
|
||||
|
||||
/// Constructs an instance with the specified pointer.
|
||||
/// @param[in] p An pointer to an allocated array.
|
||||
inline rcScopedDelete(T* p) : ptr(p) {}
|
||||
inline ~rcScopedDelete() { rcFree(ptr); }
|
||||
|
||||
/// The root array pointer.
|
||||
/// @return The root array pointer.
|
||||
inline operator T*() { return ptr; }
|
||||
|
||||
private:
|
||||
// Explicitly disabled copy constructor and copy assignment operator.
|
||||
rcScopedDelete(const rcScopedDelete&);
|
||||
rcScopedDelete& operator=(const rcScopedDelete&);
|
||||
};
|
||||
|
||||
#endif
|
53
thirdparty/recastnavigation/Recast/Include/RecastAssert.h
vendored
Normal file
53
thirdparty/recastnavigation/Recast/Include/RecastAssert.h
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
//
|
||||
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//
|
||||
|
||||
#ifndef RECASTASSERT_H
|
||||
#define RECASTASSERT_H
|
||||
|
||||
#ifdef NDEBUG
|
||||
|
||||
// From https://web.archive.org/web/20210117002833/http://cnicholson.net/2009/02/stupid-c-tricks-adventures-in-assert/
|
||||
# define rcAssert(x) do { (void)sizeof(x); } while ((void)(__LINE__==-1), false)
|
||||
|
||||
#else
|
||||
|
||||
/// An assertion failure function.
|
||||
// @param[in] expression asserted expression.
|
||||
// @param[in] file Filename of the failed assertion.
|
||||
// @param[in] line Line number of the failed assertion.
|
||||
/// @see rcAssertFailSetCustom
|
||||
typedef void (rcAssertFailFunc)(const char* expression, const char* file, int line);
|
||||
|
||||
/// Sets the base custom assertion failure function to be used by Recast.
|
||||
/// @param[in] assertFailFunc The function to be used in case of failure of #dtAssert
|
||||
void rcAssertFailSetCustom(rcAssertFailFunc* assertFailFunc);
|
||||
|
||||
/// Gets the base custom assertion failure function to be used by Recast.
|
||||
rcAssertFailFunc* rcAssertFailGetCustom();
|
||||
|
||||
# include <assert.h>
|
||||
# define rcAssert(expression) \
|
||||
{ \
|
||||
rcAssertFailFunc* failFunc = rcAssertFailGetCustom(); \
|
||||
if (failFunc == NULL) { assert(expression); } \
|
||||
else if (!(expression)) { (*failFunc)(#expression, __FILE__, __LINE__); } \
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif // RECASTASSERT_H
|
542
thirdparty/recastnavigation/Recast/Source/Recast.cpp
vendored
Normal file
542
thirdparty/recastnavigation/Recast/Source/Recast.cpp
vendored
Normal file
@@ -0,0 +1,542 @@
|
||||
//
|
||||
// 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 "Recast.h"
|
||||
#include "RecastAlloc.h"
|
||||
#include "RecastAssert.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
/// Allocates and constructs an object of the given type, returning a pointer.
|
||||
/// @param[in] allocLifetime Allocation lifetime hint
|
||||
template<typename T>
|
||||
T* rcNew(const rcAllocHint allocLifetime)
|
||||
{
|
||||
T* ptr = (T*)rcAlloc(sizeof(T), allocLifetime);
|
||||
::new(rcNewTag(), (void*)ptr) T();
|
||||
return ptr;
|
||||
}
|
||||
|
||||
/// Destroys and frees an object allocated with rcNew.
|
||||
/// @param[in] ptr The object pointer to delete.
|
||||
template<typename T>
|
||||
void rcDelete(T* ptr)
|
||||
{
|
||||
if (ptr)
|
||||
{
|
||||
ptr->~T();
|
||||
rcFree((void*)ptr);
|
||||
}
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
float rcSqrt(float x)
|
||||
{
|
||||
return sqrtf(x);
|
||||
}
|
||||
|
||||
void rcContext::log(const rcLogCategory category, const char* format, ...)
|
||||
{
|
||||
if (!m_logEnabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
static const int MSG_SIZE = 512;
|
||||
char msg[MSG_SIZE];
|
||||
va_list argList;
|
||||
va_start(argList, format);
|
||||
int len = vsnprintf(msg, MSG_SIZE, format, argList);
|
||||
if (len >= MSG_SIZE)
|
||||
{
|
||||
len = MSG_SIZE - 1;
|
||||
msg[MSG_SIZE - 1] = '\0';
|
||||
|
||||
const char* errorMessage = "Log message was truncated";
|
||||
doLog(RC_LOG_ERROR, errorMessage, (int)strlen(errorMessage));
|
||||
}
|
||||
va_end(argList);
|
||||
doLog(category, msg, len);
|
||||
}
|
||||
|
||||
void rcContext::doResetLog()
|
||||
{
|
||||
// Defined out of line to fix the weak v-tables warning
|
||||
}
|
||||
|
||||
rcHeightfield* rcAllocHeightfield()
|
||||
{
|
||||
return rcNew<rcHeightfield>(RC_ALLOC_PERM);
|
||||
}
|
||||
|
||||
void rcFreeHeightField(rcHeightfield* heightfield)
|
||||
{
|
||||
rcDelete(heightfield);
|
||||
}
|
||||
|
||||
rcHeightfield::rcHeightfield()
|
||||
: width()
|
||||
, height()
|
||||
, bmin()
|
||||
, bmax()
|
||||
, cs()
|
||||
, ch()
|
||||
, spans()
|
||||
, pools()
|
||||
, freelist()
|
||||
{
|
||||
}
|
||||
|
||||
rcHeightfield::~rcHeightfield()
|
||||
{
|
||||
// Delete span array.
|
||||
rcFree(spans);
|
||||
// Delete span pools.
|
||||
while (pools)
|
||||
{
|
||||
rcSpanPool* next = pools->next;
|
||||
rcFree(pools);
|
||||
pools = next;
|
||||
}
|
||||
}
|
||||
|
||||
rcCompactHeightfield* rcAllocCompactHeightfield()
|
||||
{
|
||||
return rcNew<rcCompactHeightfield>(RC_ALLOC_PERM);
|
||||
}
|
||||
|
||||
void rcFreeCompactHeightfield(rcCompactHeightfield* compactHeightfield)
|
||||
{
|
||||
rcDelete(compactHeightfield);
|
||||
}
|
||||
|
||||
rcCompactHeightfield::rcCompactHeightfield()
|
||||
: width()
|
||||
, height()
|
||||
, spanCount()
|
||||
, walkableHeight()
|
||||
, walkableClimb()
|
||||
, borderSize()
|
||||
, maxDistance()
|
||||
, maxRegions()
|
||||
, bmin()
|
||||
, bmax()
|
||||
, cs()
|
||||
, ch()
|
||||
, cells()
|
||||
, spans()
|
||||
, dist()
|
||||
, areas()
|
||||
{
|
||||
}
|
||||
|
||||
rcCompactHeightfield::~rcCompactHeightfield()
|
||||
{
|
||||
rcFree(cells);
|
||||
rcFree(spans);
|
||||
rcFree(dist);
|
||||
rcFree(areas);
|
||||
}
|
||||
|
||||
rcHeightfieldLayerSet* rcAllocHeightfieldLayerSet()
|
||||
{
|
||||
return rcNew<rcHeightfieldLayerSet>(RC_ALLOC_PERM);
|
||||
}
|
||||
|
||||
void rcFreeHeightfieldLayerSet(rcHeightfieldLayerSet* layerSet)
|
||||
{
|
||||
rcDelete(layerSet);
|
||||
}
|
||||
|
||||
rcHeightfieldLayerSet::rcHeightfieldLayerSet()
|
||||
: layers()
|
||||
, nlayers()
|
||||
{
|
||||
}
|
||||
|
||||
rcHeightfieldLayerSet::~rcHeightfieldLayerSet()
|
||||
{
|
||||
for (int i = 0; i < nlayers; ++i)
|
||||
{
|
||||
rcFree(layers[i].heights);
|
||||
rcFree(layers[i].areas);
|
||||
rcFree(layers[i].cons);
|
||||
}
|
||||
rcFree(layers);
|
||||
}
|
||||
|
||||
|
||||
rcContourSet* rcAllocContourSet()
|
||||
{
|
||||
return rcNew<rcContourSet>(RC_ALLOC_PERM);
|
||||
}
|
||||
|
||||
void rcFreeContourSet(rcContourSet* contourSet)
|
||||
{
|
||||
rcDelete(contourSet);
|
||||
}
|
||||
|
||||
rcContourSet::rcContourSet()
|
||||
: conts()
|
||||
, nconts()
|
||||
, bmin()
|
||||
, bmax()
|
||||
, cs()
|
||||
, ch()
|
||||
, width()
|
||||
, height()
|
||||
, borderSize()
|
||||
, maxError()
|
||||
{
|
||||
}
|
||||
|
||||
rcContourSet::~rcContourSet()
|
||||
{
|
||||
for (int i = 0; i < nconts; ++i)
|
||||
{
|
||||
rcFree(conts[i].verts);
|
||||
rcFree(conts[i].rverts);
|
||||
}
|
||||
rcFree(conts);
|
||||
}
|
||||
|
||||
rcPolyMesh* rcAllocPolyMesh()
|
||||
{
|
||||
return rcNew<rcPolyMesh>(RC_ALLOC_PERM);
|
||||
}
|
||||
|
||||
void rcFreePolyMesh(rcPolyMesh* polyMesh)
|
||||
{
|
||||
rcDelete(polyMesh);
|
||||
}
|
||||
|
||||
rcPolyMesh::rcPolyMesh()
|
||||
: verts()
|
||||
, polys()
|
||||
, regs()
|
||||
, flags()
|
||||
, areas()
|
||||
, nverts()
|
||||
, npolys()
|
||||
, maxpolys()
|
||||
, nvp()
|
||||
, bmin()
|
||||
, bmax()
|
||||
, cs()
|
||||
, ch()
|
||||
, borderSize()
|
||||
, maxEdgeError()
|
||||
{
|
||||
}
|
||||
|
||||
rcPolyMesh::~rcPolyMesh()
|
||||
{
|
||||
rcFree(verts);
|
||||
rcFree(polys);
|
||||
rcFree(regs);
|
||||
rcFree(flags);
|
||||
rcFree(areas);
|
||||
}
|
||||
|
||||
rcPolyMeshDetail* rcAllocPolyMeshDetail()
|
||||
{
|
||||
return rcNew<rcPolyMeshDetail>(RC_ALLOC_PERM);
|
||||
}
|
||||
|
||||
void rcFreePolyMeshDetail(rcPolyMeshDetail* detailMesh)
|
||||
{
|
||||
if (detailMesh == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
rcFree(detailMesh->meshes);
|
||||
rcFree(detailMesh->verts);
|
||||
rcFree(detailMesh->tris);
|
||||
rcFree(detailMesh);
|
||||
}
|
||||
|
||||
rcPolyMeshDetail::rcPolyMeshDetail()
|
||||
: meshes()
|
||||
, verts()
|
||||
, tris()
|
||||
, nmeshes()
|
||||
, nverts()
|
||||
, ntris()
|
||||
{
|
||||
}
|
||||
|
||||
void rcCalcBounds(const float* verts, int numVerts, float* minBounds, float* maxBounds)
|
||||
{
|
||||
// Calculate bounding box.
|
||||
rcVcopy(minBounds, verts);
|
||||
rcVcopy(maxBounds, verts);
|
||||
for (int i = 1; i < numVerts; ++i)
|
||||
{
|
||||
const float* v = &verts[i * 3];
|
||||
rcVmin(minBounds, v);
|
||||
rcVmax(maxBounds, v);
|
||||
}
|
||||
}
|
||||
|
||||
void rcCalcGridSize(const float* minBounds, const float* maxBounds, const float cellSize, int* sizeX, int* sizeZ)
|
||||
{
|
||||
*sizeX = (int)((maxBounds[0] - minBounds[0]) / cellSize + 0.5f);
|
||||
*sizeZ = (int)((maxBounds[2] - minBounds[2]) / cellSize + 0.5f);
|
||||
}
|
||||
|
||||
bool rcCreateHeightfield(rcContext* context, rcHeightfield& heightfield, int sizeX, int sizeZ,
|
||||
const float* minBounds, const float* maxBounds,
|
||||
float cellSize, float cellHeight)
|
||||
{
|
||||
rcIgnoreUnused(context);
|
||||
|
||||
heightfield.width = sizeX;
|
||||
heightfield.height = sizeZ;
|
||||
rcVcopy(heightfield.bmin, minBounds);
|
||||
rcVcopy(heightfield.bmax, maxBounds);
|
||||
heightfield.cs = cellSize;
|
||||
heightfield.ch = cellHeight;
|
||||
heightfield.spans = (rcSpan**)rcAlloc(sizeof(rcSpan*) * heightfield.width * heightfield.height, RC_ALLOC_PERM);
|
||||
if (!heightfield.spans)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
memset(heightfield.spans, 0, sizeof(rcSpan*) * heightfield.width * heightfield.height);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void calcTriNormal(const float* v0, const float* v1, const float* v2, float* faceNormal)
|
||||
{
|
||||
float e0[3], e1[3];
|
||||
rcVsub(e0, v1, v0);
|
||||
rcVsub(e1, v2, v0);
|
||||
rcVcross(faceNormal, e0, e1);
|
||||
rcVnormalize(faceNormal);
|
||||
}
|
||||
|
||||
void rcMarkWalkableTriangles(rcContext* context, const float walkableSlopeAngle,
|
||||
const float* verts, const int numVerts,
|
||||
const int* tris, const int numTris,
|
||||
unsigned char* triAreaIDs)
|
||||
{
|
||||
rcIgnoreUnused(context);
|
||||
rcIgnoreUnused(numVerts);
|
||||
|
||||
const float walkableThr = cosf(walkableSlopeAngle / 180.0f * RC_PI);
|
||||
|
||||
float norm[3];
|
||||
|
||||
for (int i = 0; i < numTris; ++i)
|
||||
{
|
||||
const int* tri = &tris[i * 3];
|
||||
calcTriNormal(&verts[tri[0] * 3], &verts[tri[1] * 3], &verts[tri[2] * 3], norm);
|
||||
// Check if the face is walkable.
|
||||
if (norm[1] > walkableThr)
|
||||
{
|
||||
triAreaIDs[i] = RC_WALKABLE_AREA;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void rcClearUnwalkableTriangles(rcContext* context, const float walkableSlopeAngle,
|
||||
const float* verts, int numVerts,
|
||||
const int* tris, int numTris,
|
||||
unsigned char* triAreaIDs)
|
||||
{
|
||||
rcIgnoreUnused(context);
|
||||
rcIgnoreUnused(numVerts);
|
||||
|
||||
// The minimum Y value for a face normal of a triangle with a walkable slope.
|
||||
const float walkableLimitY = cosf(walkableSlopeAngle / 180.0f * RC_PI);
|
||||
|
||||
float faceNormal[3];
|
||||
for (int i = 0; i < numTris; ++i)
|
||||
{
|
||||
const int* tri = &tris[i * 3];
|
||||
calcTriNormal(&verts[tri[0] * 3], &verts[tri[1] * 3], &verts[tri[2] * 3], faceNormal);
|
||||
// Check if the face is walkable.
|
||||
if (faceNormal[1] <= walkableLimitY)
|
||||
{
|
||||
triAreaIDs[i] = RC_NULL_AREA;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int rcGetHeightFieldSpanCount(rcContext* context, const rcHeightfield& heightfield)
|
||||
{
|
||||
rcIgnoreUnused(context);
|
||||
|
||||
const int numCols = heightfield.width * heightfield.height;
|
||||
int spanCount = 0;
|
||||
for (int columnIndex = 0; columnIndex < numCols; ++columnIndex)
|
||||
{
|
||||
for (rcSpan* span = heightfield.spans[columnIndex]; span != NULL; span = span->next)
|
||||
{
|
||||
if (span->area != RC_NULL_AREA)
|
||||
{
|
||||
spanCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return spanCount;
|
||||
}
|
||||
|
||||
bool rcBuildCompactHeightfield(rcContext* context, const int walkableHeight, const int walkableClimb,
|
||||
const rcHeightfield& heightfield, rcCompactHeightfield& compactHeightfield)
|
||||
{
|
||||
rcAssert(context);
|
||||
|
||||
rcScopedTimer timer(context, RC_TIMER_BUILD_COMPACTHEIGHTFIELD);
|
||||
|
||||
const int xSize = heightfield.width;
|
||||
const int zSize = heightfield.height;
|
||||
const int spanCount = rcGetHeightFieldSpanCount(context, heightfield);
|
||||
|
||||
// Fill in header.
|
||||
compactHeightfield.width = xSize;
|
||||
compactHeightfield.height = zSize;
|
||||
compactHeightfield.spanCount = spanCount;
|
||||
compactHeightfield.walkableHeight = walkableHeight;
|
||||
compactHeightfield.walkableClimb = walkableClimb;
|
||||
compactHeightfield.maxRegions = 0;
|
||||
rcVcopy(compactHeightfield.bmin, heightfield.bmin);
|
||||
rcVcopy(compactHeightfield.bmax, heightfield.bmax);
|
||||
compactHeightfield.bmax[1] += walkableHeight * heightfield.ch;
|
||||
compactHeightfield.cs = heightfield.cs;
|
||||
compactHeightfield.ch = heightfield.ch;
|
||||
compactHeightfield.cells = (rcCompactCell*)rcAlloc(sizeof(rcCompactCell) * xSize * zSize, RC_ALLOC_PERM);
|
||||
if (!compactHeightfield.cells)
|
||||
{
|
||||
context->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Out of memory 'chf.cells' (%d)", xSize * zSize);
|
||||
return false;
|
||||
}
|
||||
memset(compactHeightfield.cells, 0, sizeof(rcCompactCell) * xSize * zSize);
|
||||
compactHeightfield.spans = (rcCompactSpan*)rcAlloc(sizeof(rcCompactSpan) * spanCount, RC_ALLOC_PERM);
|
||||
if (!compactHeightfield.spans)
|
||||
{
|
||||
context->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Out of memory 'chf.spans' (%d)", spanCount);
|
||||
return false;
|
||||
}
|
||||
memset(compactHeightfield.spans, 0, sizeof(rcCompactSpan) * spanCount);
|
||||
compactHeightfield.areas = (unsigned char*)rcAlloc(sizeof(unsigned char) * spanCount, RC_ALLOC_PERM);
|
||||
if (!compactHeightfield.areas)
|
||||
{
|
||||
context->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Out of memory 'chf.areas' (%d)", spanCount);
|
||||
return false;
|
||||
}
|
||||
memset(compactHeightfield.areas, RC_NULL_AREA, sizeof(unsigned char) * spanCount);
|
||||
|
||||
const int MAX_HEIGHT = 0xffff;
|
||||
|
||||
// Fill in cells and spans.
|
||||
int currentCellIndex = 0;
|
||||
const int numColumns = xSize * zSize;
|
||||
for (int columnIndex = 0; columnIndex < numColumns; ++columnIndex)
|
||||
{
|
||||
const rcSpan* span = heightfield.spans[columnIndex];
|
||||
|
||||
// If there are no spans at this cell, just leave the data to index=0, count=0.
|
||||
if (span == NULL)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
rcCompactCell& cell = compactHeightfield.cells[columnIndex];
|
||||
cell.index = currentCellIndex;
|
||||
cell.count = 0;
|
||||
|
||||
for (; span != NULL; span = span->next)
|
||||
{
|
||||
if (span->area != RC_NULL_AREA)
|
||||
{
|
||||
const int bot = (int)span->smax;
|
||||
const int top = span->next ? (int)span->next->smin : MAX_HEIGHT;
|
||||
compactHeightfield.spans[currentCellIndex].y = (unsigned short)rcClamp(bot, 0, 0xffff);
|
||||
compactHeightfield.spans[currentCellIndex].h = (unsigned char)rcClamp(top - bot, 0, 0xff);
|
||||
compactHeightfield.areas[currentCellIndex] = span->area;
|
||||
currentCellIndex++;
|
||||
cell.count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find neighbour connections.
|
||||
const int MAX_LAYERS = RC_NOT_CONNECTED - 1;
|
||||
int maxLayerIndex = 0;
|
||||
const int zStride = xSize; // for readability
|
||||
for (int z = 0; z < zSize; ++z)
|
||||
{
|
||||
for (int x = 0; x < xSize; ++x)
|
||||
{
|
||||
const rcCompactCell& cell = compactHeightfield.cells[x + z * zStride];
|
||||
for (int i = (int)cell.index, ni = (int)(cell.index + cell.count); i < ni; ++i)
|
||||
{
|
||||
rcCompactSpan& span = compactHeightfield.spans[i];
|
||||
|
||||
for (int dir = 0; dir < 4; ++dir)
|
||||
{
|
||||
rcSetCon(span, dir, RC_NOT_CONNECTED);
|
||||
const int neighborX = x + rcGetDirOffsetX(dir);
|
||||
const int neighborZ = z + rcGetDirOffsetY(dir);
|
||||
// First check that the neighbour cell is in bounds.
|
||||
if (neighborX < 0 || neighborZ < 0 || neighborX >= xSize || neighborZ >= zSize)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Iterate over all neighbour spans and check if any of the is
|
||||
// accessible from current cell.
|
||||
const rcCompactCell& neighborCell = compactHeightfield.cells[neighborX + neighborZ * zStride];
|
||||
for (int k = (int)neighborCell.index, nk = (int)(neighborCell.index + neighborCell.count); k < nk; ++k)
|
||||
{
|
||||
const rcCompactSpan& neighborSpan = compactHeightfield.spans[k];
|
||||
const int bot = rcMax(span.y, neighborSpan.y);
|
||||
const int top = rcMin(span.y + span.h, neighborSpan.y + neighborSpan.h);
|
||||
|
||||
// Check that the gap between the spans is walkable,
|
||||
// and that the climb height between the gaps is not too high.
|
||||
if ((top - bot) >= walkableHeight && rcAbs((int)neighborSpan.y - (int)span.y) <= walkableClimb)
|
||||
{
|
||||
// Mark direction as walkable.
|
||||
const int layerIndex = k - (int)neighborCell.index;
|
||||
if (layerIndex < 0 || layerIndex > MAX_LAYERS)
|
||||
{
|
||||
maxLayerIndex = rcMax(maxLayerIndex, layerIndex);
|
||||
continue;
|
||||
}
|
||||
rcSetCon(span, dir, layerIndex);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (maxLayerIndex > MAX_LAYERS)
|
||||
{
|
||||
context->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Heightfield has too many layers %d (max: %d)",
|
||||
maxLayerIndex, MAX_LAYERS);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
51
thirdparty/recastnavigation/Recast/Source/RecastAlloc.cpp
vendored
Normal file
51
thirdparty/recastnavigation/Recast/Source/RecastAlloc.cpp
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
//
|
||||
// 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 "RecastAlloc.h"
|
||||
|
||||
static void* rcAllocDefault(size_t size, rcAllocHint)
|
||||
{
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
static void rcFreeDefault(void *ptr)
|
||||
{
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
static rcAllocFunc* sRecastAllocFunc = rcAllocDefault;
|
||||
static rcFreeFunc* sRecastFreeFunc = rcFreeDefault;
|
||||
|
||||
void rcAllocSetCustom(rcAllocFunc* allocFunc, rcFreeFunc* freeFunc)
|
||||
{
|
||||
sRecastAllocFunc = allocFunc ? allocFunc : rcAllocDefault;
|
||||
sRecastFreeFunc = freeFunc ? freeFunc : rcFreeDefault;
|
||||
}
|
||||
|
||||
void* rcAlloc(size_t size, rcAllocHint hint)
|
||||
{
|
||||
return sRecastAllocFunc(size, hint);
|
||||
}
|
||||
|
||||
void rcFree(void* ptr)
|
||||
{
|
||||
if (ptr != NULL)
|
||||
{
|
||||
sRecastFreeFunc(ptr);
|
||||
}
|
||||
}
|
590
thirdparty/recastnavigation/Recast/Source/RecastArea.cpp
vendored
Normal file
590
thirdparty/recastnavigation/Recast/Source/RecastArea.cpp
vendored
Normal file
@@ -0,0 +1,590 @@
|
||||
//
|
||||
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//
|
||||
|
||||
#include <float.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include "Recast.h"
|
||||
#include "RecastAlloc.h"
|
||||
#include "RecastAssert.h"
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// Basically, any spans that are closer to a boundary or obstruction than the specified radius
|
||||
/// are marked as unwalkable.
|
||||
///
|
||||
/// This method is usually called immediately after the heightfield has been built.
|
||||
///
|
||||
/// @see rcCompactHeightfield, rcBuildCompactHeightfield, rcConfig::walkableRadius
|
||||
bool rcErodeWalkableArea(rcContext* ctx, int radius, rcCompactHeightfield& chf)
|
||||
{
|
||||
rcAssert(ctx);
|
||||
|
||||
const int w = chf.width;
|
||||
const int h = chf.height;
|
||||
|
||||
rcScopedTimer timer(ctx, RC_TIMER_ERODE_AREA);
|
||||
|
||||
unsigned char* dist = (unsigned char*)rcAlloc(sizeof(unsigned char)*chf.spanCount, RC_ALLOC_TEMP);
|
||||
if (!dist)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "erodeWalkableArea: Out of memory 'dist' (%d).", chf.spanCount);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Init distance.
|
||||
memset(dist, 0xff, sizeof(unsigned char)*chf.spanCount);
|
||||
|
||||
// Mark boundary cells.
|
||||
for (int y = 0; y < h; ++y)
|
||||
{
|
||||
for (int x = 0; x < w; ++x)
|
||||
{
|
||||
const rcCompactCell& c = chf.cells[x+y*w];
|
||||
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
|
||||
{
|
||||
if (chf.areas[i] == RC_NULL_AREA)
|
||||
{
|
||||
dist[i] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
const rcCompactSpan& s = chf.spans[i];
|
||||
int nc = 0;
|
||||
for (int dir = 0; dir < 4; ++dir)
|
||||
{
|
||||
if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
|
||||
{
|
||||
const int nx = x + rcGetDirOffsetX(dir);
|
||||
const int ny = y + rcGetDirOffsetY(dir);
|
||||
const int nidx = (int)chf.cells[nx+ny*w].index + rcGetCon(s, dir);
|
||||
if (chf.areas[nidx] != RC_NULL_AREA)
|
||||
{
|
||||
nc++;
|
||||
}
|
||||
}
|
||||
}
|
||||
// At least one missing neighbour.
|
||||
if (nc != 4)
|
||||
dist[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned char nd;
|
||||
|
||||
// Pass 1
|
||||
for (int y = 0; y < h; ++y)
|
||||
{
|
||||
for (int x = 0; x < w; ++x)
|
||||
{
|
||||
const rcCompactCell& c = chf.cells[x+y*w];
|
||||
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
|
||||
{
|
||||
const rcCompactSpan& s = chf.spans[i];
|
||||
|
||||
if (rcGetCon(s, 0) != RC_NOT_CONNECTED)
|
||||
{
|
||||
// (-1,0)
|
||||
const int ax = x + rcGetDirOffsetX(0);
|
||||
const int ay = y + rcGetDirOffsetY(0);
|
||||
const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 0);
|
||||
const rcCompactSpan& as = chf.spans[ai];
|
||||
nd = (unsigned char)rcMin((int)dist[ai]+2, 255);
|
||||
if (nd < dist[i])
|
||||
dist[i] = nd;
|
||||
|
||||
// (-1,-1)
|
||||
if (rcGetCon(as, 3) != RC_NOT_CONNECTED)
|
||||
{
|
||||
const int aax = ax + rcGetDirOffsetX(3);
|
||||
const int aay = ay + rcGetDirOffsetY(3);
|
||||
const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 3);
|
||||
nd = (unsigned char)rcMin((int)dist[aai]+3, 255);
|
||||
if (nd < dist[i])
|
||||
dist[i] = nd;
|
||||
}
|
||||
}
|
||||
if (rcGetCon(s, 3) != RC_NOT_CONNECTED)
|
||||
{
|
||||
// (0,-1)
|
||||
const int ax = x + rcGetDirOffsetX(3);
|
||||
const int ay = y + rcGetDirOffsetY(3);
|
||||
const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 3);
|
||||
const rcCompactSpan& as = chf.spans[ai];
|
||||
nd = (unsigned char)rcMin((int)dist[ai]+2, 255);
|
||||
if (nd < dist[i])
|
||||
dist[i] = nd;
|
||||
|
||||
// (1,-1)
|
||||
if (rcGetCon(as, 2) != RC_NOT_CONNECTED)
|
||||
{
|
||||
const int aax = ax + rcGetDirOffsetX(2);
|
||||
const int aay = ay + rcGetDirOffsetY(2);
|
||||
const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 2);
|
||||
nd = (unsigned char)rcMin((int)dist[aai]+3, 255);
|
||||
if (nd < dist[i])
|
||||
dist[i] = nd;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pass 2
|
||||
for (int y = h-1; y >= 0; --y)
|
||||
{
|
||||
for (int x = w-1; x >= 0; --x)
|
||||
{
|
||||
const rcCompactCell& c = chf.cells[x+y*w];
|
||||
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
|
||||
{
|
||||
const rcCompactSpan& s = chf.spans[i];
|
||||
|
||||
if (rcGetCon(s, 2) != RC_NOT_CONNECTED)
|
||||
{
|
||||
// (1,0)
|
||||
const int ax = x + rcGetDirOffsetX(2);
|
||||
const int ay = y + rcGetDirOffsetY(2);
|
||||
const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 2);
|
||||
const rcCompactSpan& as = chf.spans[ai];
|
||||
nd = (unsigned char)rcMin((int)dist[ai]+2, 255);
|
||||
if (nd < dist[i])
|
||||
dist[i] = nd;
|
||||
|
||||
// (1,1)
|
||||
if (rcGetCon(as, 1) != RC_NOT_CONNECTED)
|
||||
{
|
||||
const int aax = ax + rcGetDirOffsetX(1);
|
||||
const int aay = ay + rcGetDirOffsetY(1);
|
||||
const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 1);
|
||||
nd = (unsigned char)rcMin((int)dist[aai]+3, 255);
|
||||
if (nd < dist[i])
|
||||
dist[i] = nd;
|
||||
}
|
||||
}
|
||||
if (rcGetCon(s, 1) != RC_NOT_CONNECTED)
|
||||
{
|
||||
// (0,1)
|
||||
const int ax = x + rcGetDirOffsetX(1);
|
||||
const int ay = y + rcGetDirOffsetY(1);
|
||||
const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 1);
|
||||
const rcCompactSpan& as = chf.spans[ai];
|
||||
nd = (unsigned char)rcMin((int)dist[ai]+2, 255);
|
||||
if (nd < dist[i])
|
||||
dist[i] = nd;
|
||||
|
||||
// (-1,1)
|
||||
if (rcGetCon(as, 0) != RC_NOT_CONNECTED)
|
||||
{
|
||||
const int aax = ax + rcGetDirOffsetX(0);
|
||||
const int aay = ay + rcGetDirOffsetY(0);
|
||||
const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 0);
|
||||
nd = (unsigned char)rcMin((int)dist[aai]+3, 255);
|
||||
if (nd < dist[i])
|
||||
dist[i] = nd;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const unsigned char thr = (unsigned char)(radius*2);
|
||||
for (int i = 0; i < chf.spanCount; ++i)
|
||||
if (dist[i] < thr)
|
||||
chf.areas[i] = RC_NULL_AREA;
|
||||
|
||||
rcFree(dist);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void insertSort(unsigned char* a, const int n)
|
||||
{
|
||||
int i, j;
|
||||
for (i = 1; i < n; i++)
|
||||
{
|
||||
const unsigned char value = a[i];
|
||||
for (j = i - 1; j >= 0 && a[j] > value; j--)
|
||||
a[j+1] = a[j];
|
||||
a[j+1] = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// This filter is usually applied after applying area id's using functions
|
||||
/// such as #rcMarkBoxArea, #rcMarkConvexPolyArea, and #rcMarkCylinderArea.
|
||||
///
|
||||
/// @see rcCompactHeightfield
|
||||
bool rcMedianFilterWalkableArea(rcContext* ctx, rcCompactHeightfield& chf)
|
||||
{
|
||||
rcAssert(ctx);
|
||||
|
||||
const int w = chf.width;
|
||||
const int h = chf.height;
|
||||
|
||||
rcScopedTimer timer(ctx, RC_TIMER_MEDIAN_AREA);
|
||||
|
||||
unsigned char* areas = (unsigned char*)rcAlloc(sizeof(unsigned char)*chf.spanCount, RC_ALLOC_TEMP);
|
||||
if (!areas)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "medianFilterWalkableArea: Out of memory 'areas' (%d).", chf.spanCount);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Init distance.
|
||||
memset(areas, 0xff, sizeof(unsigned char)*chf.spanCount);
|
||||
|
||||
for (int y = 0; y < h; ++y)
|
||||
{
|
||||
for (int x = 0; x < w; ++x)
|
||||
{
|
||||
const rcCompactCell& c = chf.cells[x+y*w];
|
||||
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
|
||||
{
|
||||
const rcCompactSpan& s = chf.spans[i];
|
||||
if (chf.areas[i] == RC_NULL_AREA)
|
||||
{
|
||||
areas[i] = chf.areas[i];
|
||||
continue;
|
||||
}
|
||||
|
||||
unsigned char nei[9];
|
||||
for (int j = 0; j < 9; ++j)
|
||||
nei[j] = chf.areas[i];
|
||||
|
||||
for (int dir = 0; dir < 4; ++dir)
|
||||
{
|
||||
if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
|
||||
{
|
||||
const int ax = x + rcGetDirOffsetX(dir);
|
||||
const int ay = y + rcGetDirOffsetY(dir);
|
||||
const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir);
|
||||
if (chf.areas[ai] != RC_NULL_AREA)
|
||||
nei[dir*2+0] = chf.areas[ai];
|
||||
|
||||
const rcCompactSpan& as = chf.spans[ai];
|
||||
const int dir2 = (dir+1) & 0x3;
|
||||
if (rcGetCon(as, dir2) != RC_NOT_CONNECTED)
|
||||
{
|
||||
const int ax2 = ax + rcGetDirOffsetX(dir2);
|
||||
const int ay2 = ay + rcGetDirOffsetY(dir2);
|
||||
const int ai2 = (int)chf.cells[ax2+ay2*w].index + rcGetCon(as, dir2);
|
||||
if (chf.areas[ai2] != RC_NULL_AREA)
|
||||
nei[dir*2+1] = chf.areas[ai2];
|
||||
}
|
||||
}
|
||||
}
|
||||
insertSort(nei, 9);
|
||||
areas[i] = nei[4];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(chf.areas, areas, sizeof(unsigned char)*chf.spanCount);
|
||||
|
||||
rcFree(areas);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// The value of spacial parameters are in world units.
|
||||
///
|
||||
/// @see rcCompactHeightfield, rcMedianFilterWalkableArea
|
||||
void rcMarkBoxArea(rcContext* ctx, const float* bmin, const float* bmax, unsigned char areaId,
|
||||
rcCompactHeightfield& chf)
|
||||
{
|
||||
rcAssert(ctx);
|
||||
|
||||
rcScopedTimer timer(ctx, RC_TIMER_MARK_BOX_AREA);
|
||||
|
||||
int minx = (int)((bmin[0]-chf.bmin[0])/chf.cs);
|
||||
int miny = (int)((bmin[1]-chf.bmin[1])/chf.ch);
|
||||
int minz = (int)((bmin[2]-chf.bmin[2])/chf.cs);
|
||||
int maxx = (int)((bmax[0]-chf.bmin[0])/chf.cs);
|
||||
int maxy = (int)((bmax[1]-chf.bmin[1])/chf.ch);
|
||||
int maxz = (int)((bmax[2]-chf.bmin[2])/chf.cs);
|
||||
|
||||
if (maxx < 0) return;
|
||||
if (minx >= chf.width) return;
|
||||
if (maxz < 0) return;
|
||||
if (minz >= chf.height) return;
|
||||
|
||||
if (minx < 0) minx = 0;
|
||||
if (maxx >= chf.width) maxx = chf.width-1;
|
||||
if (minz < 0) minz = 0;
|
||||
if (maxz >= chf.height) maxz = chf.height-1;
|
||||
|
||||
for (int z = minz; z <= maxz; ++z)
|
||||
{
|
||||
for (int x = minx; x <= maxx; ++x)
|
||||
{
|
||||
const rcCompactCell& c = chf.cells[x+z*chf.width];
|
||||
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
|
||||
{
|
||||
rcCompactSpan& s = chf.spans[i];
|
||||
if ((int)s.y >= miny && (int)s.y <= maxy)
|
||||
{
|
||||
if (chf.areas[i] != RC_NULL_AREA)
|
||||
chf.areas[i] = areaId;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// The value of spacial parameters are in world units.
|
||||
///
|
||||
/// The y-values of the polygon vertices are ignored. So the polygon is effectively
|
||||
/// projected onto the xz-plane at @p hmin, then extruded to @p hmax.
|
||||
///
|
||||
/// @see rcCompactHeightfield, rcMedianFilterWalkableArea
|
||||
void rcMarkConvexPolyArea(rcContext* ctx, const float* verts, const int nverts,
|
||||
const float hmin, const float hmax, unsigned char areaId,
|
||||
rcCompactHeightfield& chf)
|
||||
{
|
||||
rcAssert(ctx);
|
||||
|
||||
rcScopedTimer timer(ctx, RC_TIMER_MARK_CONVEXPOLY_AREA);
|
||||
|
||||
float bmin[3], bmax[3];
|
||||
rcVcopy(bmin, verts);
|
||||
rcVcopy(bmax, verts);
|
||||
for (int i = 1; i < nverts; ++i)
|
||||
{
|
||||
rcVmin(bmin, &verts[i*3]);
|
||||
rcVmax(bmax, &verts[i*3]);
|
||||
}
|
||||
bmin[1] = hmin;
|
||||
bmax[1] = hmax;
|
||||
|
||||
int minx = (int)((bmin[0]-chf.bmin[0])/chf.cs);
|
||||
int miny = (int)((bmin[1]-chf.bmin[1])/chf.ch);
|
||||
int minz = (int)((bmin[2]-chf.bmin[2])/chf.cs);
|
||||
int maxx = (int)((bmax[0]-chf.bmin[0])/chf.cs);
|
||||
int maxy = (int)((bmax[1]-chf.bmin[1])/chf.ch);
|
||||
int maxz = (int)((bmax[2]-chf.bmin[2])/chf.cs);
|
||||
|
||||
if (maxx < 0) return;
|
||||
if (minx >= chf.width) return;
|
||||
if (maxz < 0) return;
|
||||
if (minz >= chf.height) return;
|
||||
|
||||
if (minx < 0) minx = 0;
|
||||
if (maxx >= chf.width) maxx = chf.width-1;
|
||||
if (minz < 0) minz = 0;
|
||||
if (maxz >= chf.height) maxz = chf.height-1;
|
||||
|
||||
|
||||
// TODO: Optimize.
|
||||
for (int z = minz; z <= maxz; ++z)
|
||||
{
|
||||
for (int x = minx; x <= maxx; ++x)
|
||||
{
|
||||
const rcCompactCell& c = chf.cells[x+z*chf.width];
|
||||
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
|
||||
{
|
||||
rcCompactSpan& s = chf.spans[i];
|
||||
if (chf.areas[i] == RC_NULL_AREA)
|
||||
continue;
|
||||
if ((int)s.y >= miny && (int)s.y <= maxy)
|
||||
{
|
||||
float p[3];
|
||||
p[0] = chf.bmin[0] + (x+0.5f)*chf.cs;
|
||||
p[1] = 0;
|
||||
p[2] = chf.bmin[2] + (z+0.5f)*chf.cs;
|
||||
|
||||
if (pointInPoly(nverts, verts, p))
|
||||
{
|
||||
chf.areas[i] = areaId;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int rcOffsetPoly(const float* verts, const int nverts, const float offset,
|
||||
float* outVerts, const int maxOutVerts)
|
||||
{
|
||||
const float MITER_LIMIT = 1.20f;
|
||||
|
||||
int n = 0;
|
||||
|
||||
for (int i = 0; i < nverts; i++)
|
||||
{
|
||||
const int a = (i+nverts-1) % nverts;
|
||||
const int b = i;
|
||||
const int c = (i+1) % nverts;
|
||||
const float* va = &verts[a*3];
|
||||
const float* vb = &verts[b*3];
|
||||
const float* vc = &verts[c*3];
|
||||
float dx0 = vb[0] - va[0];
|
||||
float dy0 = vb[2] - va[2];
|
||||
float d0 = dx0*dx0 + dy0*dy0;
|
||||
if (d0 > 1e-6f)
|
||||
{
|
||||
d0 = 1.0f/rcSqrt(d0);
|
||||
dx0 *= d0;
|
||||
dy0 *= d0;
|
||||
}
|
||||
float dx1 = vc[0] - vb[0];
|
||||
float dy1 = vc[2] - vb[2];
|
||||
float d1 = dx1*dx1 + dy1*dy1;
|
||||
if (d1 > 1e-6f)
|
||||
{
|
||||
d1 = 1.0f/rcSqrt(d1);
|
||||
dx1 *= d1;
|
||||
dy1 *= d1;
|
||||
}
|
||||
const float dlx0 = -dy0;
|
||||
const float dly0 = dx0;
|
||||
const float dlx1 = -dy1;
|
||||
const float dly1 = dx1;
|
||||
float cross = dx1*dy0 - dx0*dy1;
|
||||
float dmx = (dlx0 + dlx1) * 0.5f;
|
||||
float dmy = (dly0 + dly1) * 0.5f;
|
||||
float dmr2 = dmx*dmx + dmy*dmy;
|
||||
bool bevel = dmr2 * MITER_LIMIT*MITER_LIMIT < 1.0f;
|
||||
if (dmr2 > 1e-6f)
|
||||
{
|
||||
const float scale = 1.0f / dmr2;
|
||||
dmx *= scale;
|
||||
dmy *= scale;
|
||||
}
|
||||
|
||||
if (bevel && cross < 0.0f)
|
||||
{
|
||||
if (n+2 >= maxOutVerts)
|
||||
return 0;
|
||||
float d = (1.0f - (dx0*dx1 + dy0*dy1))*0.5f;
|
||||
outVerts[n*3+0] = vb[0] + (-dlx0+dx0*d)*offset;
|
||||
outVerts[n*3+1] = vb[1];
|
||||
outVerts[n*3+2] = vb[2] + (-dly0+dy0*d)*offset;
|
||||
n++;
|
||||
outVerts[n*3+0] = vb[0] + (-dlx1-dx1*d)*offset;
|
||||
outVerts[n*3+1] = vb[1];
|
||||
outVerts[n*3+2] = vb[2] + (-dly1-dy1*d)*offset;
|
||||
n++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (n+1 >= maxOutVerts)
|
||||
return 0;
|
||||
outVerts[n*3+0] = vb[0] - dmx*offset;
|
||||
outVerts[n*3+1] = vb[1];
|
||||
outVerts[n*3+2] = vb[2] - dmy*offset;
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// The value of spacial parameters are in world units.
|
||||
///
|
||||
/// @see rcCompactHeightfield, rcMedianFilterWalkableArea
|
||||
void rcMarkCylinderArea(rcContext* ctx, const float* pos,
|
||||
const float r, const float h, unsigned char areaId,
|
||||
rcCompactHeightfield& chf)
|
||||
{
|
||||
rcAssert(ctx);
|
||||
|
||||
rcScopedTimer timer(ctx, RC_TIMER_MARK_CYLINDER_AREA);
|
||||
|
||||
float bmin[3], bmax[3];
|
||||
bmin[0] = pos[0] - r;
|
||||
bmin[1] = pos[1];
|
||||
bmin[2] = pos[2] - r;
|
||||
bmax[0] = pos[0] + r;
|
||||
bmax[1] = pos[1] + h;
|
||||
bmax[2] = pos[2] + r;
|
||||
const float r2 = r*r;
|
||||
|
||||
int minx = (int)((bmin[0]-chf.bmin[0])/chf.cs);
|
||||
int miny = (int)((bmin[1]-chf.bmin[1])/chf.ch);
|
||||
int minz = (int)((bmin[2]-chf.bmin[2])/chf.cs);
|
||||
int maxx = (int)((bmax[0]-chf.bmin[0])/chf.cs);
|
||||
int maxy = (int)((bmax[1]-chf.bmin[1])/chf.ch);
|
||||
int maxz = (int)((bmax[2]-chf.bmin[2])/chf.cs);
|
||||
|
||||
if (maxx < 0) return;
|
||||
if (minx >= chf.width) return;
|
||||
if (maxz < 0) return;
|
||||
if (minz >= chf.height) return;
|
||||
|
||||
if (minx < 0) minx = 0;
|
||||
if (maxx >= chf.width) maxx = chf.width-1;
|
||||
if (minz < 0) minz = 0;
|
||||
if (maxz >= chf.height) maxz = chf.height-1;
|
||||
|
||||
|
||||
for (int z = minz; z <= maxz; ++z)
|
||||
{
|
||||
for (int x = minx; x <= maxx; ++x)
|
||||
{
|
||||
const rcCompactCell& c = chf.cells[x+z*chf.width];
|
||||
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
|
||||
{
|
||||
rcCompactSpan& s = chf.spans[i];
|
||||
|
||||
if (chf.areas[i] == RC_NULL_AREA)
|
||||
continue;
|
||||
|
||||
if ((int)s.y >= miny && (int)s.y <= maxy)
|
||||
{
|
||||
const float sx = chf.bmin[0] + (x+0.5f)*chf.cs;
|
||||
const float sz = chf.bmin[2] + (z+0.5f)*chf.cs;
|
||||
const float dx = sx - pos[0];
|
||||
const float dz = sz - pos[2];
|
||||
|
||||
if (dx*dx + dz*dz < r2)
|
||||
{
|
||||
chf.areas[i] = areaId;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
35
thirdparty/recastnavigation/Recast/Source/RecastAssert.cpp
vendored
Normal file
35
thirdparty/recastnavigation/Recast/Source/RecastAssert.cpp
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
//
|
||||
// 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 "RecastAssert.h"
|
||||
|
||||
#ifndef NDEBUG
|
||||
|
||||
static rcAssertFailFunc* sRecastAssertFailFunc = 0;
|
||||
|
||||
void rcAssertFailSetCustom(rcAssertFailFunc* assertFailFunc)
|
||||
{
|
||||
sRecastAssertFailFunc = assertFailFunc;
|
||||
}
|
||||
|
||||
rcAssertFailFunc* rcAssertFailGetCustom()
|
||||
{
|
||||
return sRecastAssertFailFunc;
|
||||
}
|
||||
|
||||
#endif
|
1104
thirdparty/recastnavigation/Recast/Source/RecastContour.cpp
vendored
Normal file
1104
thirdparty/recastnavigation/Recast/Source/RecastContour.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
184
thirdparty/recastnavigation/Recast/Source/RecastFilter.cpp
vendored
Normal file
184
thirdparty/recastnavigation/Recast/Source/RecastFilter.cpp
vendored
Normal file
@@ -0,0 +1,184 @@
|
||||
//
|
||||
// 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 "Recast.h"
|
||||
#include "RecastAssert.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
void rcFilterLowHangingWalkableObstacles(rcContext* context, const int walkableClimb, rcHeightfield& heightfield)
|
||||
{
|
||||
rcAssert(context);
|
||||
|
||||
rcScopedTimer timer(context, RC_TIMER_FILTER_LOW_OBSTACLES);
|
||||
|
||||
const int xSize = heightfield.width;
|
||||
const int zSize = heightfield.height;
|
||||
|
||||
for (int z = 0; z < zSize; ++z)
|
||||
{
|
||||
for (int x = 0; x < xSize; ++x)
|
||||
{
|
||||
rcSpan* previousSpan = NULL;
|
||||
bool previousWasWalkable = false;
|
||||
unsigned char previousArea = RC_NULL_AREA;
|
||||
|
||||
for (rcSpan* span = heightfield.spans[x + z * xSize]; span != NULL; previousSpan = span, span = span->next)
|
||||
{
|
||||
const bool walkable = span->area != RC_NULL_AREA;
|
||||
// If current span is not walkable, but there is walkable
|
||||
// span just below it, mark the span above it walkable too.
|
||||
if (!walkable && previousWasWalkable)
|
||||
{
|
||||
if (rcAbs((int)span->smax - (int)previousSpan->smax) <= walkableClimb)
|
||||
{
|
||||
span->area = previousArea;
|
||||
}
|
||||
}
|
||||
// Copy walkable flag so that it cannot propagate
|
||||
// past multiple non-walkable objects.
|
||||
previousWasWalkable = walkable;
|
||||
previousArea = span->area;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void rcFilterLedgeSpans(rcContext* context, const int walkableHeight, const int walkableClimb,
|
||||
rcHeightfield& heightfield)
|
||||
{
|
||||
rcAssert(context);
|
||||
|
||||
rcScopedTimer timer(context, RC_TIMER_FILTER_BORDER);
|
||||
|
||||
const int xSize = heightfield.width;
|
||||
const int zSize = heightfield.height;
|
||||
const int MAX_HEIGHT = 0xffff; // TODO (graham): Move this to a more visible constant and update usages.
|
||||
|
||||
// Mark border spans.
|
||||
for (int z = 0; z < zSize; ++z)
|
||||
{
|
||||
for (int x = 0; x < xSize; ++x)
|
||||
{
|
||||
for (rcSpan* span = heightfield.spans[x + z * xSize]; span; span = span->next)
|
||||
{
|
||||
// Skip non walkable spans.
|
||||
if (span->area == RC_NULL_AREA)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const int bot = (int)(span->smax);
|
||||
const int top = span->next ? (int)(span->next->smin) : MAX_HEIGHT;
|
||||
|
||||
// Find neighbours minimum height.
|
||||
int minNeighborHeight = MAX_HEIGHT;
|
||||
|
||||
// Min and max height of accessible neighbours.
|
||||
int accessibleNeighborMinHeight = span->smax;
|
||||
int accessibleNeighborMaxHeight = span->smax;
|
||||
|
||||
for (int direction = 0; direction < 4; ++direction)
|
||||
{
|
||||
int dx = x + rcGetDirOffsetX(direction);
|
||||
int dy = z + rcGetDirOffsetY(direction);
|
||||
// Skip neighbours which are out of bounds.
|
||||
if (dx < 0 || dy < 0 || dx >= xSize || dy >= zSize)
|
||||
{
|
||||
minNeighborHeight = rcMin(minNeighborHeight, -walkableClimb - bot);
|
||||
continue;
|
||||
}
|
||||
|
||||
// From minus infinity to the first span.
|
||||
const rcSpan* neighborSpan = heightfield.spans[dx + dy * xSize];
|
||||
int neighborBot = -walkableClimb;
|
||||
int neighborTop = neighborSpan ? (int)neighborSpan->smin : MAX_HEIGHT;
|
||||
|
||||
// Skip neighbour if the gap between the spans is too small.
|
||||
if (rcMin(top, neighborTop) - rcMax(bot, neighborBot) > walkableHeight)
|
||||
{
|
||||
minNeighborHeight = rcMin(minNeighborHeight, neighborBot - bot);
|
||||
}
|
||||
|
||||
// Rest of the spans.
|
||||
for (neighborSpan = heightfield.spans[dx + dy * xSize]; neighborSpan; neighborSpan = neighborSpan->next)
|
||||
{
|
||||
neighborBot = (int)neighborSpan->smax;
|
||||
neighborTop = neighborSpan->next ? (int)neighborSpan->next->smin : MAX_HEIGHT;
|
||||
|
||||
// Skip neighbour if the gap between the spans is too small.
|
||||
if (rcMin(top, neighborTop) - rcMax(bot, neighborBot) > walkableHeight)
|
||||
{
|
||||
minNeighborHeight = rcMin(minNeighborHeight, neighborBot - bot);
|
||||
|
||||
// Find min/max accessible neighbour height.
|
||||
if (rcAbs(neighborBot - bot) <= walkableClimb)
|
||||
{
|
||||
if (neighborBot < accessibleNeighborMinHeight) accessibleNeighborMinHeight = neighborBot;
|
||||
if (neighborBot > accessibleNeighborMaxHeight) accessibleNeighborMaxHeight = neighborBot;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The current span is close to a ledge if the drop to any
|
||||
// neighbour span is less than the walkableClimb.
|
||||
if (minNeighborHeight < -walkableClimb)
|
||||
{
|
||||
span->area = RC_NULL_AREA;
|
||||
}
|
||||
// If the difference between all neighbours is too large,
|
||||
// we are at steep slope, mark the span as ledge.
|
||||
else if ((accessibleNeighborMaxHeight - accessibleNeighborMinHeight) > walkableClimb)
|
||||
{
|
||||
span->area = RC_NULL_AREA;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void rcFilterWalkableLowHeightSpans(rcContext* context, const int walkableHeight, rcHeightfield& heightfield)
|
||||
{
|
||||
rcAssert(context);
|
||||
|
||||
rcScopedTimer timer(context, RC_TIMER_FILTER_WALKABLE);
|
||||
|
||||
const int xSize = heightfield.width;
|
||||
const int zSize = heightfield.height;
|
||||
const int MAX_HEIGHT = 0xffff;
|
||||
|
||||
// Remove walkable flag from spans which do not have enough
|
||||
// space above them for the agent to stand there.
|
||||
for (int z = 0; z < zSize; ++z)
|
||||
{
|
||||
for (int x = 0; x < xSize; ++x)
|
||||
{
|
||||
for (rcSpan* span = heightfield.spans[x + z*xSize]; span; span = span->next)
|
||||
{
|
||||
const int bot = (int)(span->smax);
|
||||
const int top = span->next ? (int)(span->next->smin) : MAX_HEIGHT;
|
||||
if ((top - bot) < walkableHeight)
|
||||
{
|
||||
span->area = RC_NULL_AREA;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
656
thirdparty/recastnavigation/Recast/Source/RecastLayers.cpp
vendored
Normal file
656
thirdparty/recastnavigation/Recast/Source/RecastLayers.cpp
vendored
Normal file
@@ -0,0 +1,656 @@
|
||||
//
|
||||
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//
|
||||
|
||||
#include <float.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include "Recast.h"
|
||||
#include "RecastAlloc.h"
|
||||
#include "RecastAssert.h"
|
||||
|
||||
|
||||
// Must be 255 or smaller (not 256) because layer IDs are stored as
|
||||
// a byte where 255 is a special value.
|
||||
#ifndef RC_MAX_LAYERS_DEF
|
||||
#define RC_MAX_LAYERS_DEF 63
|
||||
#endif
|
||||
|
||||
#if RC_MAX_LAYERS_DEF > 255
|
||||
#error RC_MAX_LAYERS_DEF must be 255 or smaller
|
||||
#endif
|
||||
|
||||
#ifndef RC_MAX_NEIS_DEF
|
||||
#define RC_MAX_NEIS_DEF 16
|
||||
#endif
|
||||
|
||||
// Keep type checking.
|
||||
static const int RC_MAX_LAYERS = RC_MAX_LAYERS_DEF;
|
||||
static const int RC_MAX_NEIS = RC_MAX_NEIS_DEF;
|
||||
|
||||
struct rcLayerRegion
|
||||
{
|
||||
unsigned char layers[RC_MAX_LAYERS];
|
||||
unsigned char neis[RC_MAX_NEIS];
|
||||
unsigned short ymin, ymax;
|
||||
unsigned char layerId; // Layer ID
|
||||
unsigned char nlayers; // Layer count
|
||||
unsigned char nneis; // Neighbour count
|
||||
unsigned char base; // Flag indicating if the region is the base of merged regions.
|
||||
};
|
||||
|
||||
|
||||
static bool contains(const unsigned char* a, const unsigned char an, const unsigned char v)
|
||||
{
|
||||
const int n = (int)an;
|
||||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
if (a[i] == v)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool addUnique(unsigned char* a, unsigned char& an, int anMax, unsigned char v)
|
||||
{
|
||||
if (contains(a, an, v))
|
||||
return true;
|
||||
|
||||
if ((int)an >= anMax)
|
||||
return false;
|
||||
|
||||
a[an] = v;
|
||||
an++;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
inline bool overlapRange(const unsigned short amin, const unsigned short amax,
|
||||
const unsigned short bmin, const unsigned short bmax)
|
||||
{
|
||||
return (amin > bmax || amax < bmin) ? false : true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct rcLayerSweepSpan
|
||||
{
|
||||
unsigned short ns; // number samples
|
||||
unsigned char id; // region id
|
||||
unsigned char nei; // neighbour id
|
||||
};
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// See the #rcConfig documentation for more information on the configuration parameters.
|
||||
///
|
||||
/// @see rcAllocHeightfieldLayerSet, rcCompactHeightfield, rcHeightfieldLayerSet, rcConfig
|
||||
bool rcBuildHeightfieldLayers(rcContext* ctx, const rcCompactHeightfield& chf,
|
||||
const int borderSize, const int walkableHeight,
|
||||
rcHeightfieldLayerSet& lset)
|
||||
{
|
||||
rcAssert(ctx);
|
||||
|
||||
rcScopedTimer timer(ctx, RC_TIMER_BUILD_LAYERS);
|
||||
|
||||
const int w = chf.width;
|
||||
const int h = chf.height;
|
||||
|
||||
rcScopedDelete<unsigned char> srcReg((unsigned char*)rcAlloc(sizeof(unsigned char)*chf.spanCount, RC_ALLOC_TEMP));
|
||||
if (!srcReg)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'srcReg' (%d).", chf.spanCount);
|
||||
return false;
|
||||
}
|
||||
memset(srcReg,0xff,sizeof(unsigned char)*chf.spanCount);
|
||||
|
||||
const int nsweeps = chf.width;
|
||||
rcScopedDelete<rcLayerSweepSpan> sweeps((rcLayerSweepSpan*)rcAlloc(sizeof(rcLayerSweepSpan)*nsweeps, RC_ALLOC_TEMP));
|
||||
if (!sweeps)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'sweeps' (%d).", nsweeps);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Partition walkable area into monotone regions.
|
||||
int prevCount[256];
|
||||
unsigned char regId = 0;
|
||||
|
||||
for (int y = borderSize; y < h-borderSize; ++y)
|
||||
{
|
||||
memset(prevCount,0,sizeof(int)*regId);
|
||||
unsigned char sweepId = 0;
|
||||
|
||||
for (int x = borderSize; x < w-borderSize; ++x)
|
||||
{
|
||||
const rcCompactCell& c = chf.cells[x+y*w];
|
||||
|
||||
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
|
||||
{
|
||||
const rcCompactSpan& s = chf.spans[i];
|
||||
if (chf.areas[i] == RC_NULL_AREA) continue;
|
||||
|
||||
unsigned char sid = 0xff;
|
||||
|
||||
// -x
|
||||
if (rcGetCon(s, 0) != RC_NOT_CONNECTED)
|
||||
{
|
||||
const int ax = x + rcGetDirOffsetX(0);
|
||||
const int ay = y + rcGetDirOffsetY(0);
|
||||
const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 0);
|
||||
if (chf.areas[ai] != RC_NULL_AREA && srcReg[ai] != 0xff)
|
||||
sid = srcReg[ai];
|
||||
}
|
||||
|
||||
if (sid == 0xff)
|
||||
{
|
||||
sid = sweepId++;
|
||||
sweeps[sid].nei = 0xff;
|
||||
sweeps[sid].ns = 0;
|
||||
}
|
||||
|
||||
// -y
|
||||
if (rcGetCon(s,3) != RC_NOT_CONNECTED)
|
||||
{
|
||||
const int ax = x + rcGetDirOffsetX(3);
|
||||
const int ay = y + rcGetDirOffsetY(3);
|
||||
const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 3);
|
||||
const unsigned char nr = srcReg[ai];
|
||||
if (nr != 0xff)
|
||||
{
|
||||
// Set neighbour when first valid neighbour is encoutered.
|
||||
if (sweeps[sid].ns == 0)
|
||||
sweeps[sid].nei = nr;
|
||||
|
||||
if (sweeps[sid].nei == nr)
|
||||
{
|
||||
// Update existing neighbour
|
||||
sweeps[sid].ns++;
|
||||
prevCount[nr]++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is hit if there is nore than one neighbour.
|
||||
// Invalidate the neighbour.
|
||||
sweeps[sid].nei = 0xff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
srcReg[i] = sid;
|
||||
}
|
||||
}
|
||||
|
||||
// Create unique ID.
|
||||
for (int i = 0; i < sweepId; ++i)
|
||||
{
|
||||
// If the neighbour is set and there is only one continuous connection to it,
|
||||
// the sweep will be merged with the previous one, else new region is created.
|
||||
if (sweeps[i].nei != 0xff && prevCount[sweeps[i].nei] == (int)sweeps[i].ns)
|
||||
{
|
||||
sweeps[i].id = sweeps[i].nei;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (regId == 255)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Region ID overflow.");
|
||||
return false;
|
||||
}
|
||||
sweeps[i].id = regId++;
|
||||
}
|
||||
}
|
||||
|
||||
// Remap local sweep ids to region ids.
|
||||
for (int x = borderSize; x < w-borderSize; ++x)
|
||||
{
|
||||
const rcCompactCell& c = chf.cells[x+y*w];
|
||||
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
|
||||
{
|
||||
if (srcReg[i] != 0xff)
|
||||
srcReg[i] = sweeps[srcReg[i]].id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Allocate and init layer regions.
|
||||
const int nregs = (int)regId;
|
||||
rcScopedDelete<rcLayerRegion> regs((rcLayerRegion*)rcAlloc(sizeof(rcLayerRegion)*nregs, RC_ALLOC_TEMP));
|
||||
if (!regs)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'regs' (%d).", nregs);
|
||||
return false;
|
||||
}
|
||||
memset(regs, 0, sizeof(rcLayerRegion)*nregs);
|
||||
for (int i = 0; i < nregs; ++i)
|
||||
{
|
||||
regs[i].layerId = 0xff;
|
||||
regs[i].ymin = 0xffff;
|
||||
regs[i].ymax = 0;
|
||||
}
|
||||
|
||||
// Find region neighbours and overlapping regions.
|
||||
for (int y = 0; y < h; ++y)
|
||||
{
|
||||
for (int x = 0; x < w; ++x)
|
||||
{
|
||||
const rcCompactCell& c = chf.cells[x+y*w];
|
||||
|
||||
unsigned char lregs[RC_MAX_LAYERS];
|
||||
int nlregs = 0;
|
||||
|
||||
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
|
||||
{
|
||||
const rcCompactSpan& s = chf.spans[i];
|
||||
const unsigned char ri = srcReg[i];
|
||||
if (ri == 0xff) continue;
|
||||
|
||||
regs[ri].ymin = rcMin(regs[ri].ymin, s.y);
|
||||
regs[ri].ymax = rcMax(regs[ri].ymax, s.y);
|
||||
|
||||
// Collect all region layers.
|
||||
if (nlregs < RC_MAX_LAYERS)
|
||||
lregs[nlregs++] = ri;
|
||||
|
||||
// Update neighbours
|
||||
for (int dir = 0; dir < 4; ++dir)
|
||||
{
|
||||
if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
|
||||
{
|
||||
const int ax = x + rcGetDirOffsetX(dir);
|
||||
const int ay = y + rcGetDirOffsetY(dir);
|
||||
const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir);
|
||||
const unsigned char rai = srcReg[ai];
|
||||
if (rai != 0xff && rai != ri)
|
||||
{
|
||||
// Don't check return value -- if we cannot add the neighbor
|
||||
// it will just cause a few more regions to be created, which
|
||||
// is fine.
|
||||
addUnique(regs[ri].neis, regs[ri].nneis, RC_MAX_NEIS, rai);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Update overlapping regions.
|
||||
for (int i = 0; i < nlregs-1; ++i)
|
||||
{
|
||||
for (int j = i+1; j < nlregs; ++j)
|
||||
{
|
||||
if (lregs[i] != lregs[j])
|
||||
{
|
||||
rcLayerRegion& ri = regs[lregs[i]];
|
||||
rcLayerRegion& rj = regs[lregs[j]];
|
||||
|
||||
if (!addUnique(ri.layers, ri.nlayers, RC_MAX_LAYERS, lregs[j]) ||
|
||||
!addUnique(rj.layers, rj.nlayers, RC_MAX_LAYERS, lregs[i]))
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: layer overflow (too many overlapping walkable platforms). Try increasing RC_MAX_LAYERS.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Create 2D layers from regions.
|
||||
unsigned char layerId = 0;
|
||||
|
||||
static const int MAX_STACK = 64;
|
||||
unsigned char stack[MAX_STACK];
|
||||
int nstack = 0;
|
||||
|
||||
for (int i = 0; i < nregs; ++i)
|
||||
{
|
||||
rcLayerRegion& root = regs[i];
|
||||
// Skip already visited.
|
||||
if (root.layerId != 0xff)
|
||||
continue;
|
||||
|
||||
// Start search.
|
||||
root.layerId = layerId;
|
||||
root.base = 1;
|
||||
|
||||
nstack = 0;
|
||||
stack[nstack++] = (unsigned char)i;
|
||||
|
||||
while (nstack)
|
||||
{
|
||||
// Pop front
|
||||
rcLayerRegion& reg = regs[stack[0]];
|
||||
nstack--;
|
||||
for (int j = 0; j < nstack; ++j)
|
||||
stack[j] = stack[j+1];
|
||||
|
||||
const int nneis = (int)reg.nneis;
|
||||
for (int j = 0; j < nneis; ++j)
|
||||
{
|
||||
const unsigned char nei = reg.neis[j];
|
||||
rcLayerRegion& regn = regs[nei];
|
||||
// Skip already visited.
|
||||
if (regn.layerId != 0xff)
|
||||
continue;
|
||||
// Skip if the neighbour is overlapping root region.
|
||||
if (contains(root.layers, root.nlayers, nei))
|
||||
continue;
|
||||
// Skip if the height range would become too large.
|
||||
const int ymin = rcMin(root.ymin, regn.ymin);
|
||||
const int ymax = rcMax(root.ymax, regn.ymax);
|
||||
if ((ymax - ymin) >= 255)
|
||||
continue;
|
||||
|
||||
if (nstack < MAX_STACK)
|
||||
{
|
||||
// Deepen
|
||||
stack[nstack++] = (unsigned char)nei;
|
||||
|
||||
// Mark layer id
|
||||
regn.layerId = layerId;
|
||||
// Merge current layers to root.
|
||||
for (int k = 0; k < regn.nlayers; ++k)
|
||||
{
|
||||
if (!addUnique(root.layers, root.nlayers, RC_MAX_LAYERS, regn.layers[k]))
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: layer overflow (too many overlapping walkable platforms). Try increasing RC_MAX_LAYERS.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
root.ymin = rcMin(root.ymin, regn.ymin);
|
||||
root.ymax = rcMax(root.ymax, regn.ymax);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
layerId++;
|
||||
}
|
||||
|
||||
// Merge non-overlapping regions that are close in height.
|
||||
const unsigned short mergeHeight = (unsigned short)walkableHeight * 4;
|
||||
|
||||
for (int i = 0; i < nregs; ++i)
|
||||
{
|
||||
rcLayerRegion& ri = regs[i];
|
||||
if (!ri.base) continue;
|
||||
|
||||
unsigned char newId = ri.layerId;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
unsigned char oldId = 0xff;
|
||||
|
||||
for (int j = 0; j < nregs; ++j)
|
||||
{
|
||||
if (i == j) continue;
|
||||
rcLayerRegion& rj = regs[j];
|
||||
if (!rj.base) continue;
|
||||
|
||||
// Skip if the regions are not close to each other.
|
||||
if (!overlapRange(ri.ymin,ri.ymax+mergeHeight, rj.ymin,rj.ymax+mergeHeight))
|
||||
continue;
|
||||
// Skip if the height range would become too large.
|
||||
const int ymin = rcMin(ri.ymin, rj.ymin);
|
||||
const int ymax = rcMax(ri.ymax, rj.ymax);
|
||||
if ((ymax - ymin) >= 255)
|
||||
continue;
|
||||
|
||||
// Make sure that there is no overlap when merging 'ri' and 'rj'.
|
||||
bool overlap = false;
|
||||
// Iterate over all regions which have the same layerId as 'rj'
|
||||
for (int k = 0; k < nregs; ++k)
|
||||
{
|
||||
if (regs[k].layerId != rj.layerId)
|
||||
continue;
|
||||
// Check if region 'k' is overlapping region 'ri'
|
||||
// Index to 'regs' is the same as region id.
|
||||
if (contains(ri.layers,ri.nlayers, (unsigned char)k))
|
||||
{
|
||||
overlap = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Cannot merge of regions overlap.
|
||||
if (overlap)
|
||||
continue;
|
||||
|
||||
// Can merge i and j.
|
||||
oldId = rj.layerId;
|
||||
break;
|
||||
}
|
||||
|
||||
// Could not find anything to merge with, stop.
|
||||
if (oldId == 0xff)
|
||||
break;
|
||||
|
||||
// Merge
|
||||
for (int j = 0; j < nregs; ++j)
|
||||
{
|
||||
rcLayerRegion& rj = regs[j];
|
||||
if (rj.layerId == oldId)
|
||||
{
|
||||
rj.base = 0;
|
||||
// Remap layerIds.
|
||||
rj.layerId = newId;
|
||||
// Add overlaid layers from 'rj' to 'ri'.
|
||||
for (int k = 0; k < rj.nlayers; ++k)
|
||||
{
|
||||
if (!addUnique(ri.layers, ri.nlayers, RC_MAX_LAYERS, rj.layers[k]))
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: layer overflow (too many overlapping walkable platforms). Try increasing RC_MAX_LAYERS.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Update height bounds.
|
||||
ri.ymin = rcMin(ri.ymin, rj.ymin);
|
||||
ri.ymax = rcMax(ri.ymax, rj.ymax);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compact layerIds
|
||||
unsigned char remap[256];
|
||||
memset(remap, 0, 256);
|
||||
|
||||
// Find number of unique layers.
|
||||
layerId = 0;
|
||||
for (int i = 0; i < nregs; ++i)
|
||||
remap[regs[i].layerId] = 1;
|
||||
for (int i = 0; i < 256; ++i)
|
||||
{
|
||||
if (remap[i])
|
||||
remap[i] = layerId++;
|
||||
else
|
||||
remap[i] = 0xff;
|
||||
}
|
||||
// Remap ids.
|
||||
for (int i = 0; i < nregs; ++i)
|
||||
regs[i].layerId = remap[regs[i].layerId];
|
||||
|
||||
// No layers, return empty.
|
||||
if (layerId == 0)
|
||||
return true;
|
||||
|
||||
// Create layers.
|
||||
rcAssert(lset.layers == 0);
|
||||
|
||||
const int lw = w - borderSize*2;
|
||||
const int lh = h - borderSize*2;
|
||||
|
||||
// Build contracted bbox for layers.
|
||||
float bmin[3], bmax[3];
|
||||
rcVcopy(bmin, chf.bmin);
|
||||
rcVcopy(bmax, chf.bmax);
|
||||
bmin[0] += borderSize*chf.cs;
|
||||
bmin[2] += borderSize*chf.cs;
|
||||
bmax[0] -= borderSize*chf.cs;
|
||||
bmax[2] -= borderSize*chf.cs;
|
||||
|
||||
lset.nlayers = (int)layerId;
|
||||
|
||||
lset.layers = (rcHeightfieldLayer*)rcAlloc(sizeof(rcHeightfieldLayer)*lset.nlayers, RC_ALLOC_PERM);
|
||||
if (!lset.layers)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'layers' (%d).", lset.nlayers);
|
||||
return false;
|
||||
}
|
||||
memset(lset.layers, 0, sizeof(rcHeightfieldLayer)*lset.nlayers);
|
||||
|
||||
|
||||
// Store layers.
|
||||
for (int i = 0; i < lset.nlayers; ++i)
|
||||
{
|
||||
unsigned char curId = (unsigned char)i;
|
||||
|
||||
rcHeightfieldLayer* layer = &lset.layers[i];
|
||||
|
||||
const int gridSize = sizeof(unsigned char)*lw*lh;
|
||||
|
||||
layer->heights = (unsigned char*)rcAlloc(gridSize, RC_ALLOC_PERM);
|
||||
if (!layer->heights)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'heights' (%d).", gridSize);
|
||||
return false;
|
||||
}
|
||||
memset(layer->heights, 0xff, gridSize);
|
||||
|
||||
layer->areas = (unsigned char*)rcAlloc(gridSize, RC_ALLOC_PERM);
|
||||
if (!layer->areas)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'areas' (%d).", gridSize);
|
||||
return false;
|
||||
}
|
||||
memset(layer->areas, 0, gridSize);
|
||||
|
||||
layer->cons = (unsigned char*)rcAlloc(gridSize, RC_ALLOC_PERM);
|
||||
if (!layer->cons)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'cons' (%d).", gridSize);
|
||||
return false;
|
||||
}
|
||||
memset(layer->cons, 0, gridSize);
|
||||
|
||||
// Find layer height bounds.
|
||||
int hmin = 0, hmax = 0;
|
||||
for (int j = 0; j < nregs; ++j)
|
||||
{
|
||||
if (regs[j].base && regs[j].layerId == curId)
|
||||
{
|
||||
hmin = (int)regs[j].ymin;
|
||||
hmax = (int)regs[j].ymax;
|
||||
}
|
||||
}
|
||||
|
||||
layer->width = lw;
|
||||
layer->height = lh;
|
||||
layer->cs = chf.cs;
|
||||
layer->ch = chf.ch;
|
||||
|
||||
// Adjust the bbox to fit the heightfield.
|
||||
rcVcopy(layer->bmin, bmin);
|
||||
rcVcopy(layer->bmax, bmax);
|
||||
layer->bmin[1] = bmin[1] + hmin*chf.ch;
|
||||
layer->bmax[1] = bmin[1] + hmax*chf.ch;
|
||||
layer->hmin = hmin;
|
||||
layer->hmax = hmax;
|
||||
|
||||
// Update usable data region.
|
||||
layer->minx = layer->width;
|
||||
layer->maxx = 0;
|
||||
layer->miny = layer->height;
|
||||
layer->maxy = 0;
|
||||
|
||||
// Copy height and area from compact heightfield.
|
||||
for (int y = 0; y < lh; ++y)
|
||||
{
|
||||
for (int x = 0; x < lw; ++x)
|
||||
{
|
||||
const int cx = borderSize+x;
|
||||
const int cy = borderSize+y;
|
||||
const rcCompactCell& c = chf.cells[cx+cy*w];
|
||||
for (int j = (int)c.index, nj = (int)(c.index+c.count); j < nj; ++j)
|
||||
{
|
||||
const rcCompactSpan& s = chf.spans[j];
|
||||
// Skip unassigned regions.
|
||||
if (srcReg[j] == 0xff)
|
||||
continue;
|
||||
// Skip of does nto belong to current layer.
|
||||
unsigned char lid = regs[srcReg[j]].layerId;
|
||||
if (lid != curId)
|
||||
continue;
|
||||
|
||||
// Update data bounds.
|
||||
layer->minx = rcMin(layer->minx, x);
|
||||
layer->maxx = rcMax(layer->maxx, x);
|
||||
layer->miny = rcMin(layer->miny, y);
|
||||
layer->maxy = rcMax(layer->maxy, y);
|
||||
|
||||
// Store height and area type.
|
||||
const int idx = x+y*lw;
|
||||
layer->heights[idx] = (unsigned char)(s.y - hmin);
|
||||
layer->areas[idx] = chf.areas[j];
|
||||
|
||||
// Check connection.
|
||||
unsigned char portal = 0;
|
||||
unsigned char con = 0;
|
||||
for (int dir = 0; dir < 4; ++dir)
|
||||
{
|
||||
if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
|
||||
{
|
||||
const int ax = cx + rcGetDirOffsetX(dir);
|
||||
const int ay = cy + rcGetDirOffsetY(dir);
|
||||
const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir);
|
||||
unsigned char alid = srcReg[ai] != 0xff ? regs[srcReg[ai]].layerId : 0xff;
|
||||
// Portal mask
|
||||
if (chf.areas[ai] != RC_NULL_AREA && lid != alid)
|
||||
{
|
||||
portal |= (unsigned char)(1<<dir);
|
||||
// Update height so that it matches on both sides of the portal.
|
||||
const rcCompactSpan& as = chf.spans[ai];
|
||||
if (as.y > hmin)
|
||||
layer->heights[idx] = rcMax(layer->heights[idx], (unsigned char)(as.y - hmin));
|
||||
}
|
||||
// Valid connection mask
|
||||
if (chf.areas[ai] != RC_NULL_AREA && lid == alid)
|
||||
{
|
||||
const int nx = ax - borderSize;
|
||||
const int ny = ay - borderSize;
|
||||
if (nx >= 0 && ny >= 0 && nx < lw && ny < lh)
|
||||
con |= (unsigned char)(1<<dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
layer->cons[idx] = (portal << 4) | con;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (layer->minx > layer->maxx)
|
||||
layer->minx = layer->maxx = 0;
|
||||
if (layer->miny > layer->maxy)
|
||||
layer->miny = layer->maxy = 0;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
1549
thirdparty/recastnavigation/Recast/Source/RecastMesh.cpp
vendored
Normal file
1549
thirdparty/recastnavigation/Recast/Source/RecastMesh.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1463
thirdparty/recastnavigation/Recast/Source/RecastMeshDetail.cpp
vendored
Normal file
1463
thirdparty/recastnavigation/Recast/Source/RecastMeshDetail.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
556
thirdparty/recastnavigation/Recast/Source/RecastRasterization.cpp
vendored
Normal file
556
thirdparty/recastnavigation/Recast/Source/RecastRasterization.cpp
vendored
Normal file
@@ -0,0 +1,556 @@
|
||||
//
|
||||
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//
|
||||
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include "Recast.h"
|
||||
#include "RecastAlloc.h"
|
||||
#include "RecastAssert.h"
|
||||
|
||||
/// Check whether two bounding boxes overlap
|
||||
///
|
||||
/// @param[in] aMin Min axis extents of bounding box A
|
||||
/// @param[in] aMax Max axis extents of bounding box A
|
||||
/// @param[in] bMin Min axis extents of bounding box B
|
||||
/// @param[in] bMax Max axis extents of bounding box B
|
||||
/// @returns true if the two bounding boxes overlap. False otherwise.
|
||||
static bool overlapBounds(const float* aMin, const float* aMax, const float* bMin, const float* bMax)
|
||||
{
|
||||
return
|
||||
aMin[0] <= bMax[0] && aMax[0] >= bMin[0] &&
|
||||
aMin[1] <= bMax[1] && aMax[1] >= bMin[1] &&
|
||||
aMin[2] <= bMax[2] && aMax[2] >= bMin[2];
|
||||
}
|
||||
|
||||
/// Allocates a new span in the heightfield.
|
||||
/// Use a memory pool and free list to minimize actual allocations.
|
||||
///
|
||||
/// @param[in] hf The heightfield
|
||||
/// @returns A pointer to the allocated or re-used span memory.
|
||||
static rcSpan* allocSpan(rcHeightfield& hf)
|
||||
{
|
||||
// If necessary, allocate new page and update the freelist.
|
||||
if (hf.freelist == NULL || hf.freelist->next == NULL)
|
||||
{
|
||||
// Create new page.
|
||||
// Allocate memory for the new pool.
|
||||
rcSpanPool* spanPool = (rcSpanPool*)rcAlloc(sizeof(rcSpanPool), RC_ALLOC_PERM);
|
||||
if (spanPool == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Add the pool into the list of pools.
|
||||
spanPool->next = hf.pools;
|
||||
hf.pools = spanPool;
|
||||
|
||||
// Add new spans to the free list.
|
||||
rcSpan* freeList = hf.freelist;
|
||||
rcSpan* head = &spanPool->items[0];
|
||||
rcSpan* it = &spanPool->items[RC_SPANS_PER_POOL];
|
||||
do
|
||||
{
|
||||
--it;
|
||||
it->next = freeList;
|
||||
freeList = it;
|
||||
}
|
||||
while (it != head);
|
||||
hf.freelist = it;
|
||||
}
|
||||
|
||||
// Pop item from the front of the free list.
|
||||
rcSpan* newSpan = hf.freelist;
|
||||
hf.freelist = hf.freelist->next;
|
||||
return newSpan;
|
||||
}
|
||||
|
||||
/// Releases the memory used by the span back to the heightfield, so it can be re-used for new spans.
|
||||
/// @param[in] hf The heightfield.
|
||||
/// @param[in] span A pointer to the span to free
|
||||
static void freeSpan(rcHeightfield& hf, rcSpan* span)
|
||||
{
|
||||
if (span == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Add the span to the front of the free list.
|
||||
span->next = hf.freelist;
|
||||
hf.freelist = span;
|
||||
}
|
||||
|
||||
/// Adds a span to the heightfield. If the new span overlaps existing spans,
|
||||
/// it will merge the new span with the existing ones.
|
||||
///
|
||||
/// @param[in] hf Heightfield to add spans to
|
||||
/// @param[in] x The new span's column cell x index
|
||||
/// @param[in] z The new span's column cell z index
|
||||
/// @param[in] min The new span's minimum cell index
|
||||
/// @param[in] max The new span's maximum cell index
|
||||
/// @param[in] areaID The new span's area type ID
|
||||
/// @param[in] flagMergeThreshold How close two spans maximum extents need to be to merge area type IDs
|
||||
static bool addSpan(rcHeightfield& hf,
|
||||
const int x, const int z,
|
||||
const unsigned short min, const unsigned short max,
|
||||
const unsigned char areaID, const int flagMergeThreshold)
|
||||
{
|
||||
// Create the new span.
|
||||
rcSpan* newSpan = allocSpan(hf);
|
||||
if (newSpan == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
newSpan->smin = min;
|
||||
newSpan->smax = max;
|
||||
newSpan->area = areaID;
|
||||
newSpan->next = NULL;
|
||||
|
||||
const int columnIndex = x + z * hf.width;
|
||||
rcSpan* previousSpan = NULL;
|
||||
rcSpan* currentSpan = hf.spans[columnIndex];
|
||||
|
||||
// Insert the new span, possibly merging it with existing spans.
|
||||
while (currentSpan != NULL)
|
||||
{
|
||||
if (currentSpan->smin > newSpan->smax)
|
||||
{
|
||||
// Current span is completely after the new span, break.
|
||||
break;
|
||||
}
|
||||
|
||||
if (currentSpan->smax < newSpan->smin)
|
||||
{
|
||||
// Current span is completely before the new span. Keep going.
|
||||
previousSpan = currentSpan;
|
||||
currentSpan = currentSpan->next;
|
||||
}
|
||||
else
|
||||
{
|
||||
// The new span overlaps with an existing span. Merge them.
|
||||
if (currentSpan->smin < newSpan->smin)
|
||||
{
|
||||
newSpan->smin = currentSpan->smin;
|
||||
}
|
||||
if (currentSpan->smax > newSpan->smax)
|
||||
{
|
||||
newSpan->smax = currentSpan->smax;
|
||||
}
|
||||
|
||||
// Merge flags.
|
||||
if (rcAbs((int)newSpan->smax - (int)currentSpan->smax) <= flagMergeThreshold)
|
||||
{
|
||||
// Higher area ID numbers indicate higher resolution priority.
|
||||
newSpan->area = rcMax(newSpan->area, currentSpan->area);
|
||||
}
|
||||
|
||||
// Remove the current span since it's now merged with newSpan.
|
||||
// Keep going because there might be other overlapping spans that also need to be merged.
|
||||
rcSpan* next = currentSpan->next;
|
||||
freeSpan(hf, currentSpan);
|
||||
if (previousSpan)
|
||||
{
|
||||
previousSpan->next = next;
|
||||
}
|
||||
else
|
||||
{
|
||||
hf.spans[columnIndex] = next;
|
||||
}
|
||||
currentSpan = next;
|
||||
}
|
||||
}
|
||||
|
||||
// Insert new span after prev
|
||||
if (previousSpan != NULL)
|
||||
{
|
||||
newSpan->next = previousSpan->next;
|
||||
previousSpan->next = newSpan;
|
||||
}
|
||||
else
|
||||
{
|
||||
// This span should go before the others in the list
|
||||
newSpan->next = hf.spans[columnIndex];
|
||||
hf.spans[columnIndex] = newSpan;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool rcAddSpan(rcContext* context, rcHeightfield& heightfield,
|
||||
const int x, const int z,
|
||||
const unsigned short spanMin, const unsigned short spanMax,
|
||||
const unsigned char areaID, const int flagMergeThreshold)
|
||||
{
|
||||
rcAssert(context);
|
||||
|
||||
if (!addSpan(heightfield, x, z, spanMin, spanMax, areaID, flagMergeThreshold))
|
||||
{
|
||||
context->log(RC_LOG_ERROR, "rcAddSpan: Out of memory.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
enum rcAxis
|
||||
{
|
||||
RC_AXIS_X = 0,
|
||||
RC_AXIS_Y = 1,
|
||||
RC_AXIS_Z = 2
|
||||
};
|
||||
|
||||
/// Divides a convex polygon of max 12 vertices into two convex polygons
|
||||
/// across a separating axis.
|
||||
///
|
||||
/// @param[in] inVerts The input polygon vertices
|
||||
/// @param[in] inVertsCount The number of input polygon vertices
|
||||
/// @param[out] outVerts1 Resulting polygon 1's vertices
|
||||
/// @param[out] outVerts1Count The number of resulting polygon 1 vertices
|
||||
/// @param[out] outVerts2 Resulting polygon 2's vertices
|
||||
/// @param[out] outVerts2Count The number of resulting polygon 2 vertices
|
||||
/// @param[in] axisOffset THe offset along the specified axis
|
||||
/// @param[in] axis The separating axis
|
||||
static void dividePoly(const float* inVerts, int inVertsCount,
|
||||
float* outVerts1, int* outVerts1Count,
|
||||
float* outVerts2, int* outVerts2Count,
|
||||
float axisOffset, rcAxis axis)
|
||||
{
|
||||
rcAssert(inVertsCount <= 12);
|
||||
|
||||
// How far positive or negative away from the separating axis is each vertex.
|
||||
float inVertAxisDelta[12];
|
||||
for (int inVert = 0; inVert < inVertsCount; ++inVert)
|
||||
{
|
||||
inVertAxisDelta[inVert] = axisOffset - inVerts[inVert * 3 + axis];
|
||||
}
|
||||
|
||||
int poly1Vert = 0;
|
||||
int poly2Vert = 0;
|
||||
for (int inVertA = 0, inVertB = inVertsCount - 1; inVertA < inVertsCount; inVertB = inVertA, ++inVertA)
|
||||
{
|
||||
// If the two vertices are on the same side of the separating axis
|
||||
bool sameSide = (inVertAxisDelta[inVertA] >= 0) == (inVertAxisDelta[inVertB] >= 0);
|
||||
|
||||
if (!sameSide)
|
||||
{
|
||||
float s = inVertAxisDelta[inVertB] / (inVertAxisDelta[inVertB] - inVertAxisDelta[inVertA]);
|
||||
outVerts1[poly1Vert * 3 + 0] = inVerts[inVertB * 3 + 0] + (inVerts[inVertA * 3 + 0] - inVerts[inVertB * 3 + 0]) * s;
|
||||
outVerts1[poly1Vert * 3 + 1] = inVerts[inVertB * 3 + 1] + (inVerts[inVertA * 3 + 1] - inVerts[inVertB * 3 + 1]) * s;
|
||||
outVerts1[poly1Vert * 3 + 2] = inVerts[inVertB * 3 + 2] + (inVerts[inVertA * 3 + 2] - inVerts[inVertB * 3 + 2]) * s;
|
||||
rcVcopy(&outVerts2[poly2Vert * 3], &outVerts1[poly1Vert * 3]);
|
||||
poly1Vert++;
|
||||
poly2Vert++;
|
||||
|
||||
// add the inVertA point to the right polygon. Do NOT add points that are on the dividing line
|
||||
// since these were already added above
|
||||
if (inVertAxisDelta[inVertA] > 0)
|
||||
{
|
||||
rcVcopy(&outVerts1[poly1Vert * 3], &inVerts[inVertA * 3]);
|
||||
poly1Vert++;
|
||||
}
|
||||
else if (inVertAxisDelta[inVertA] < 0)
|
||||
{
|
||||
rcVcopy(&outVerts2[poly2Vert * 3], &inVerts[inVertA * 3]);
|
||||
poly2Vert++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// add the inVertA point to the right polygon. Addition is done even for points on the dividing line
|
||||
if (inVertAxisDelta[inVertA] >= 0)
|
||||
{
|
||||
rcVcopy(&outVerts1[poly1Vert * 3], &inVerts[inVertA * 3]);
|
||||
poly1Vert++;
|
||||
if (inVertAxisDelta[inVertA] != 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
rcVcopy(&outVerts2[poly2Vert * 3], &inVerts[inVertA * 3]);
|
||||
poly2Vert++;
|
||||
}
|
||||
}
|
||||
|
||||
*outVerts1Count = poly1Vert;
|
||||
*outVerts2Count = poly2Vert;
|
||||
}
|
||||
|
||||
/// Rasterize a single triangle to the heightfield.
|
||||
///
|
||||
/// This code is extremely hot, so much care should be given to maintaining maximum perf here.
|
||||
///
|
||||
/// @param[in] v0 Triangle vertex 0
|
||||
/// @param[in] v1 Triangle vertex 1
|
||||
/// @param[in] v2 Triangle vertex 2
|
||||
/// @param[in] areaID The area ID to assign to the rasterized spans
|
||||
/// @param[in] hf Heightfield to rasterize into
|
||||
/// @param[in] hfBBMin The min extents of the heightfield bounding box
|
||||
/// @param[in] hfBBMax The max extents of the heightfield bounding box
|
||||
/// @param[in] cellSize The x and z axis size of a voxel in the heightfield
|
||||
/// @param[in] inverseCellSize 1 / cellSize
|
||||
/// @param[in] inverseCellHeight 1 / cellHeight
|
||||
/// @param[in] flagMergeThreshold The threshold in which area flags will be merged
|
||||
/// @returns true if the operation completes successfully. false if there was an error adding spans to the heightfield.
|
||||
static bool rasterizeTri(const float* v0, const float* v1, const float* v2,
|
||||
const unsigned char areaID, rcHeightfield& hf,
|
||||
const float* hfBBMin, const float* hfBBMax,
|
||||
const float cellSize, const float inverseCellSize, const float inverseCellHeight,
|
||||
const int flagMergeThreshold)
|
||||
{
|
||||
// Calculate the bounding box of the triangle.
|
||||
float triBBMin[3];
|
||||
rcVcopy(triBBMin, v0);
|
||||
rcVmin(triBBMin, v1);
|
||||
rcVmin(triBBMin, v2);
|
||||
|
||||
float triBBMax[3];
|
||||
rcVcopy(triBBMax, v0);
|
||||
rcVmax(triBBMax, v1);
|
||||
rcVmax(triBBMax, v2);
|
||||
|
||||
// If the triangle does not touch the bounding box of the heightfield, skip the triangle.
|
||||
if (!overlapBounds(triBBMin, triBBMax, hfBBMin, hfBBMax))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
const int w = hf.width;
|
||||
const int h = hf.height;
|
||||
const float by = hfBBMax[1] - hfBBMin[1];
|
||||
|
||||
// Calculate the footprint of the triangle on the grid's z-axis
|
||||
int z0 = (int)((triBBMin[2] - hfBBMin[2]) * inverseCellSize);
|
||||
int z1 = (int)((triBBMax[2] - hfBBMin[2]) * inverseCellSize);
|
||||
|
||||
// use -1 rather than 0 to cut the polygon properly at the start of the tile
|
||||
z0 = rcClamp(z0, -1, h - 1);
|
||||
z1 = rcClamp(z1, 0, h - 1);
|
||||
|
||||
// Clip the triangle into all grid cells it touches.
|
||||
float buf[7 * 3 * 4];
|
||||
float* in = buf;
|
||||
float* inRow = buf + 7 * 3;
|
||||
float* p1 = inRow + 7 * 3;
|
||||
float* p2 = p1 + 7 * 3;
|
||||
|
||||
rcVcopy(&in[0], v0);
|
||||
rcVcopy(&in[1 * 3], v1);
|
||||
rcVcopy(&in[2 * 3], v2);
|
||||
int nvRow;
|
||||
int nvIn = 3;
|
||||
|
||||
for (int z = z0; z <= z1; ++z)
|
||||
{
|
||||
// Clip polygon to row. Store the remaining polygon as well
|
||||
const float cellZ = hfBBMin[2] + (float)z * cellSize;
|
||||
dividePoly(in, nvIn, inRow, &nvRow, p1, &nvIn, cellZ + cellSize, RC_AXIS_Z);
|
||||
rcSwap(in, p1);
|
||||
|
||||
if (nvRow < 3)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (z < 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// find X-axis bounds of the row
|
||||
float minX = inRow[0];
|
||||
float maxX = inRow[0];
|
||||
for (int vert = 1; vert < nvRow; ++vert)
|
||||
{
|
||||
if (minX > inRow[vert * 3])
|
||||
{
|
||||
minX = inRow[vert * 3];
|
||||
}
|
||||
if (maxX < inRow[vert * 3])
|
||||
{
|
||||
maxX = inRow[vert * 3];
|
||||
}
|
||||
}
|
||||
int x0 = (int)((minX - hfBBMin[0]) * inverseCellSize);
|
||||
int x1 = (int)((maxX - hfBBMin[0]) * inverseCellSize);
|
||||
if (x1 < 0 || x0 >= w)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
x0 = rcClamp(x0, -1, w - 1);
|
||||
x1 = rcClamp(x1, 0, w - 1);
|
||||
|
||||
int nv;
|
||||
int nv2 = nvRow;
|
||||
|
||||
for (int x = x0; x <= x1; ++x)
|
||||
{
|
||||
// Clip polygon to column. store the remaining polygon as well
|
||||
const float cx = hfBBMin[0] + (float)x * cellSize;
|
||||
dividePoly(inRow, nv2, p1, &nv, p2, &nv2, cx + cellSize, RC_AXIS_X);
|
||||
rcSwap(inRow, p2);
|
||||
|
||||
if (nv < 3)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (x < 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Calculate min and max of the span.
|
||||
float spanMin = p1[1];
|
||||
float spanMax = p1[1];
|
||||
for (int vert = 1; vert < nv; ++vert)
|
||||
{
|
||||
spanMin = rcMin(spanMin, p1[vert * 3 + 1]);
|
||||
spanMax = rcMax(spanMax, p1[vert * 3 + 1]);
|
||||
}
|
||||
spanMin -= hfBBMin[1];
|
||||
spanMax -= hfBBMin[1];
|
||||
|
||||
// Skip the span if it's completely outside the heightfield bounding box
|
||||
if (spanMax < 0.0f)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (spanMin > by)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Clamp the span to the heightfield bounding box.
|
||||
if (spanMin < 0.0f)
|
||||
{
|
||||
spanMin = 0;
|
||||
}
|
||||
if (spanMax > by)
|
||||
{
|
||||
spanMax = by;
|
||||
}
|
||||
|
||||
// Snap the span to the heightfield height grid.
|
||||
unsigned short spanMinCellIndex = (unsigned short)rcClamp((int)floorf(spanMin * inverseCellHeight), 0, RC_SPAN_MAX_HEIGHT);
|
||||
unsigned short spanMaxCellIndex = (unsigned short)rcClamp((int)ceilf(spanMax * inverseCellHeight), (int)spanMinCellIndex + 1, RC_SPAN_MAX_HEIGHT);
|
||||
|
||||
if (!addSpan(hf, x, z, spanMinCellIndex, spanMaxCellIndex, areaID, flagMergeThreshold))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool rcRasterizeTriangle(rcContext* context,
|
||||
const float* v0, const float* v1, const float* v2,
|
||||
const unsigned char areaID, rcHeightfield& heightfield, const int flagMergeThreshold)
|
||||
{
|
||||
rcAssert(context != NULL);
|
||||
|
||||
rcScopedTimer timer(context, RC_TIMER_RASTERIZE_TRIANGLES);
|
||||
|
||||
// Rasterize the single triangle.
|
||||
const float inverseCellSize = 1.0f / heightfield.cs;
|
||||
const float inverseCellHeight = 1.0f / heightfield.ch;
|
||||
if (!rasterizeTri(v0, v1, v2, areaID, heightfield, heightfield.bmin, heightfield.bmax, heightfield.cs, inverseCellSize, inverseCellHeight, flagMergeThreshold))
|
||||
{
|
||||
context->log(RC_LOG_ERROR, "rcRasterizeTriangle: Out of memory.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool rcRasterizeTriangles(rcContext* context,
|
||||
const float* verts, const int /*nv*/,
|
||||
const int* tris, const unsigned char* triAreaIDs, const int numTris,
|
||||
rcHeightfield& heightfield, const int flagMergeThreshold)
|
||||
{
|
||||
rcAssert(context != NULL);
|
||||
|
||||
rcScopedTimer timer(context, RC_TIMER_RASTERIZE_TRIANGLES);
|
||||
|
||||
// Rasterize the triangles.
|
||||
const float inverseCellSize = 1.0f / heightfield.cs;
|
||||
const float inverseCellHeight = 1.0f / heightfield.ch;
|
||||
for (int triIndex = 0; triIndex < numTris; ++triIndex)
|
||||
{
|
||||
const float* v0 = &verts[tris[triIndex * 3 + 0] * 3];
|
||||
const float* v1 = &verts[tris[triIndex * 3 + 1] * 3];
|
||||
const float* v2 = &verts[tris[triIndex * 3 + 2] * 3];
|
||||
if (!rasterizeTri(v0, v1, v2, triAreaIDs[triIndex], heightfield, heightfield.bmin, heightfield.bmax, heightfield.cs, inverseCellSize, inverseCellHeight, flagMergeThreshold))
|
||||
{
|
||||
context->log(RC_LOG_ERROR, "rcRasterizeTriangles: Out of memory.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool rcRasterizeTriangles(rcContext* context,
|
||||
const float* verts, const int /*nv*/,
|
||||
const unsigned short* tris, const unsigned char* triAreaIDs, const int numTris,
|
||||
rcHeightfield& heightfield, const int flagMergeThreshold)
|
||||
{
|
||||
rcAssert(context != NULL);
|
||||
|
||||
rcScopedTimer timer(context, RC_TIMER_RASTERIZE_TRIANGLES);
|
||||
|
||||
// Rasterize the triangles.
|
||||
const float inverseCellSize = 1.0f / heightfield.cs;
|
||||
const float inverseCellHeight = 1.0f / heightfield.ch;
|
||||
for (int triIndex = 0; triIndex < numTris; ++triIndex)
|
||||
{
|
||||
const float* v0 = &verts[tris[triIndex * 3 + 0] * 3];
|
||||
const float* v1 = &verts[tris[triIndex * 3 + 1] * 3];
|
||||
const float* v2 = &verts[tris[triIndex * 3 + 2] * 3];
|
||||
if (!rasterizeTri(v0, v1, v2, triAreaIDs[triIndex], heightfield, heightfield.bmin, heightfield.bmax, heightfield.cs, inverseCellSize, inverseCellHeight, flagMergeThreshold))
|
||||
{
|
||||
context->log(RC_LOG_ERROR, "rcRasterizeTriangles: Out of memory.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool rcRasterizeTriangles(rcContext* context,
|
||||
const float* verts, const unsigned char* triAreaIDs, const int numTris,
|
||||
rcHeightfield& heightfield, const int flagMergeThreshold)
|
||||
{
|
||||
rcAssert(context != NULL);
|
||||
|
||||
rcScopedTimer timer(context, RC_TIMER_RASTERIZE_TRIANGLES);
|
||||
|
||||
// Rasterize the triangles.
|
||||
const float inverseCellSize = 1.0f / heightfield.cs;
|
||||
const float inverseCellHeight = 1.0f / heightfield.ch;
|
||||
for (int triIndex = 0; triIndex < numTris; ++triIndex)
|
||||
{
|
||||
const float* v0 = &verts[(triIndex * 3 + 0) * 3];
|
||||
const float* v1 = &verts[(triIndex * 3 + 1) * 3];
|
||||
const float* v2 = &verts[(triIndex * 3 + 2) * 3];
|
||||
if (!rasterizeTri(v0, v1, v2, triAreaIDs[triIndex], heightfield, heightfield.bmin, heightfield.bmax, heightfield.cs, inverseCellSize, inverseCellHeight, flagMergeThreshold))
|
||||
{
|
||||
context->log(RC_LOG_ERROR, "rcRasterizeTriangles: Out of memory.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
1811
thirdparty/recastnavigation/Recast/Source/RecastRegion.cpp
vendored
Normal file
1811
thirdparty/recastnavigation/Recast/Source/RecastRegion.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user