Initial gamelib integration AAS2 with some animation and physics and another integration pass on idLib.
This commit is contained in:
@@ -0,0 +1,212 @@
|
||||
cmake_minimum_required(VERSION 3.25)
|
||||
|
||||
set(CMAKE_VS_GLOBALS "VcpkgEnabled=false")
|
||||
project(tech5 LANGUAGES C CXX)
|
||||
|
||||
if(NOT WIN32)
|
||||
message(FATAL_ERROR "The recovered idTech 5 PC port currently supports Windows only.")
|
||||
endif()
|
||||
|
||||
if(NOT CMAKE_SIZEOF_VOID_P EQUAL 4)
|
||||
message(FATAL_ERROR
|
||||
"The recovered Xbox 360 layouts require a Win32 host build. Configure with -A Win32."
|
||||
)
|
||||
endif()
|
||||
|
||||
get_filename_component(TECH5_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/.." ABSOLUTE)
|
||||
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${TECH5_ROOT}/bin")
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${TECH5_ROOT}/bin")
|
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${TECH5_ROOT}/build/lib")
|
||||
foreach(config Debug Release RelWithDebInfo MinSizeRel)
|
||||
string(TOUPPER "${config}" config_upper)
|
||||
set("CMAKE_RUNTIME_OUTPUT_DIRECTORY_${config_upper}" "${TECH5_ROOT}/bin")
|
||||
set("CMAKE_LIBRARY_OUTPUT_DIRECTORY_${config_upper}" "${TECH5_ROOT}/bin")
|
||||
set("CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${config_upper}"
|
||||
"${TECH5_ROOT}/build/lib/${config}"
|
||||
)
|
||||
endforeach()
|
||||
|
||||
# This list is intentionally sourced only from the reconstructed tree. The
|
||||
# Hex-Rays tree is evidence, never compiler input.
|
||||
file(GLOB_RECURSE IDTECH5_IDLIB_SOURCES CONFIGURE_DEPENDS
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/shared/idlib/*.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/shared/idlib/*.h"
|
||||
)
|
||||
|
||||
# These recovered subsystems remain outside the current PC build boundary.
|
||||
set(IDTECH5_DEFERRED_PATTERNS
|
||||
"/analysisclient\."
|
||||
"/networking/amqp/"
|
||||
"/typeinfo/typeinfofile\.cpp$"
|
||||
)
|
||||
foreach(pattern IN LISTS IDTECH5_DEFERRED_PATTERNS)
|
||||
list(FILTER IDTECH5_IDLIB_SOURCES EXCLUDE REGEX "${pattern}")
|
||||
endforeach()
|
||||
|
||||
add_library(idLib STATIC ${IDTECH5_IDLIB_SOURCES})
|
||||
add_library(tech5::idLib ALIAS idLib)
|
||||
target_include_directories(idLib PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/shared")
|
||||
target_compile_features(idLib PUBLIC cxx_std_14)
|
||||
target_compile_definitions(idLib PRIVATE
|
||||
__IDLIB__
|
||||
_CRT_SECURE_NO_DEPRECATE
|
||||
_CRT_NONSTDC_NO_DEPRECATE
|
||||
_USE_32BIT_TIME_T
|
||||
)
|
||||
target_link_libraries(idLib PUBLIC ws2_32 psapi)
|
||||
set_property(TARGET idLib PROPERTY
|
||||
MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>"
|
||||
)
|
||||
set_target_properties(idLib PROPERTIES OUTPUT_NAME "idLib")
|
||||
|
||||
if(MSVC)
|
||||
target_compile_options(idLib PRIVATE
|
||||
/MP /W3 /arch:SSE2
|
||||
/wd4244 /wd4267 /wd4305 /wd4467 /wd4595 /wd4996
|
||||
)
|
||||
endif()
|
||||
|
||||
source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" PREFIX "Source"
|
||||
FILES ${IDTECH5_IDLIB_SOURCES}
|
||||
)
|
||||
|
||||
# Minimal framework resource ownership needed by recovered engine resources.
|
||||
set(IDTECH5_FRAMEWORK_SOURCES
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/engine/framework/resource.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/engine/framework/resource.h"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/engine/framework/resourcelist.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/engine/framework/resourcelist.h"
|
||||
)
|
||||
|
||||
add_library(framework STATIC ${IDTECH5_FRAMEWORK_SOURCES})
|
||||
add_library(tech5::framework ALIAS framework)
|
||||
target_include_directories(framework PUBLIC
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/engine"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/shared"
|
||||
)
|
||||
target_compile_features(framework PUBLIC cxx_std_14)
|
||||
target_compile_definitions(framework PRIVATE
|
||||
_CRT_SECURE_NO_DEPRECATE
|
||||
_CRT_NONSTDC_NO_DEPRECATE
|
||||
_USE_32BIT_TIME_T
|
||||
)
|
||||
target_link_libraries(framework PUBLIC tech5::idLib)
|
||||
set_property(TARGET framework PROPERTY
|
||||
MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>"
|
||||
)
|
||||
if(MSVC)
|
||||
target_compile_options(framework PRIVATE
|
||||
/MP /W3 /arch:SSE2
|
||||
/wd4244 /wd4267 /wd4305 /wd4467 /wd4595 /wd4996
|
||||
)
|
||||
endif()
|
||||
source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" PREFIX "Source"
|
||||
FILES ${IDTECH5_FRAMEWORK_SOURCES}
|
||||
)
|
||||
|
||||
# Collision model recovery is a separate engine library. As with idLib and
|
||||
# GameLib, only reconstructed files under source/ are compiler inputs.
|
||||
file(GLOB_RECURSE IDTECH5_CM_SOURCES CONFIGURE_DEPENDS
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/engine/cm/*.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/engine/cm/*.h"
|
||||
)
|
||||
|
||||
add_library(cm STATIC ${IDTECH5_CM_SOURCES})
|
||||
add_library(tech5::cm ALIAS cm)
|
||||
target_include_directories(cm PUBLIC
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/engine"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/shared"
|
||||
)
|
||||
target_compile_features(cm PUBLIC cxx_std_14)
|
||||
target_compile_definitions(cm PRIVATE
|
||||
_CRT_SECURE_NO_DEPRECATE
|
||||
_CRT_NONSTDC_NO_DEPRECATE
|
||||
_USE_32BIT_TIME_T
|
||||
)
|
||||
target_link_libraries(cm PUBLIC tech5::framework tech5::idLib)
|
||||
set_property(TARGET cm PROPERTY
|
||||
MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>"
|
||||
)
|
||||
set_target_properties(cm PROPERTIES OUTPUT_NAME "cm")
|
||||
|
||||
if(MSVC)
|
||||
target_compile_options(cm PRIVATE
|
||||
/MP /W3 /arch:SSE2
|
||||
/wd4244 /wd4267 /wd4305 /wd4467 /wd4595 /wd4996
|
||||
)
|
||||
endif()
|
||||
|
||||
source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" PREFIX "Source"
|
||||
FILES ${IDTECH5_CM_SOURCES}
|
||||
)
|
||||
|
||||
# Recovered AAS2 file/resource layer. The decompiler tree remains evidence;
|
||||
# only the manually reconstructed PC sources below are compiled.
|
||||
file(GLOB_RECURSE IDTECH5_AAS2FILE_SOURCES CONFIGURE_DEPENDS
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/engine/aas2file/*.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/engine/aas2file/*.h"
|
||||
)
|
||||
|
||||
add_library(aas2file STATIC ${IDTECH5_AAS2FILE_SOURCES})
|
||||
add_library(tech5::aas2file ALIAS aas2file)
|
||||
target_include_directories(aas2file PUBLIC
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/engine"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/shared"
|
||||
)
|
||||
target_compile_features(aas2file PUBLIC cxx_std_14)
|
||||
target_compile_definitions(aas2file PRIVATE
|
||||
_CRT_SECURE_NO_DEPRECATE
|
||||
_CRT_NONSTDC_NO_DEPRECATE
|
||||
_USE_32BIT_TIME_T
|
||||
)
|
||||
target_link_libraries(aas2file PUBLIC tech5::framework tech5::idLib)
|
||||
set_property(TARGET aas2file PROPERTY
|
||||
MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>"
|
||||
)
|
||||
set_target_properties(aas2file PROPERTIES OUTPUT_NAME "aas2file")
|
||||
if(MSVC)
|
||||
target_compile_options(aas2file PRIVATE
|
||||
/MP /W3 /arch:SSE2
|
||||
/wd4244 /wd4267 /wd4305 /wd4467 /wd4595 /wd4996
|
||||
)
|
||||
endif()
|
||||
source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" PREFIX "Source"
|
||||
FILES ${IDTECH5_AAS2FILE_SOURCES}
|
||||
)
|
||||
|
||||
# GameLib follows the same evidence boundary as idLib: only reconstructed
|
||||
# files under source/ are compiler inputs. The Hex-Rays tree is never built.
|
||||
file(GLOB_RECURSE IDTECH5_GAMELIB_SOURCES CONFIGURE_DEPENDS
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/engine/gamelib/*.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/engine/gamelib/*.h"
|
||||
)
|
||||
|
||||
add_library(gameLib STATIC ${IDTECH5_GAMELIB_SOURCES})
|
||||
add_library(tech5::gameLib ALIAS gameLib)
|
||||
target_include_directories(gameLib PUBLIC
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/engine"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/shared"
|
||||
)
|
||||
target_compile_features(gameLib PUBLIC cxx_std_14)
|
||||
target_compile_definitions(gameLib PRIVATE
|
||||
_CRT_SECURE_NO_DEPRECATE
|
||||
_CRT_NONSTDC_NO_DEPRECATE
|
||||
_USE_32BIT_TIME_T
|
||||
)
|
||||
target_link_libraries(gameLib PUBLIC tech5::aas2file tech5::cm tech5::idLib)
|
||||
set_property(TARGET gameLib PROPERTY
|
||||
MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>"
|
||||
)
|
||||
set_target_properties(gameLib PROPERTIES OUTPUT_NAME "gameLib")
|
||||
|
||||
if(MSVC)
|
||||
target_compile_options(gameLib PRIVATE
|
||||
/MP /W3 /arch:SSE2
|
||||
/wd4244 /wd4267 /wd4305 /wd4467 /wd4595 /wd4996
|
||||
)
|
||||
endif()
|
||||
|
||||
source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" PREFIX "Source"
|
||||
FILES ${IDTECH5_GAMELIB_SOURCES}
|
||||
)
|
||||
@@ -312,7 +312,7 @@ std::uint32_t CanonicalXboxAreaChecksum(
|
||||
|
||||
} // namespace
|
||||
|
||||
idResourceList idAAS2File::resourceList("aas");
|
||||
idTypedResourceList<idAAS2File> idAAS2File::resourceList("aas");
|
||||
|
||||
aas2Cover_t::aas2Cover_t()
|
||||
: origin(), dir(), areaNum(0), flags(0), numTouching(0),
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
class idFile;
|
||||
class idLexer;
|
||||
template<class resourceType> class idTypedResourceList;
|
||||
|
||||
enum aas2TravelFlag_t : int {
|
||||
AAS_TFL_INVALID = 0x1,
|
||||
@@ -388,7 +389,7 @@ public:
|
||||
void FloodAddVisitedArea(int areaNum);
|
||||
void FloodClearVisitedAreas(int offset);
|
||||
|
||||
static idResourceList resourceList;
|
||||
static idTypedResourceList<idAAS2File> resourceList;
|
||||
|
||||
std::uint32_t crc;
|
||||
std::uint32_t timestamp;
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
#include "framework/resource.h"
|
||||
|
||||
#include "framework/resourcelist.h"
|
||||
#include "idlib/lib_print.h"
|
||||
#include "idlib/text/str.h"
|
||||
|
||||
idResource::idResource()
|
||||
: trackedMemory(0), name("unnamed"), nextOnHashChain(nullptr),
|
||||
resourceListPtr(nullptr), resourceError(nullptr), networkID(-1),
|
||||
staleCount(0), resourceFlags(0), pad{} {
|
||||
}
|
||||
|
||||
idResource::~idResource() {
|
||||
if ((resourceFlags & 0x40u) != 0) {
|
||||
idLibPrint::FatalError("Resource deleted with staticResource set: %s",
|
||||
name.c_str());
|
||||
}
|
||||
if (resourceListPtr != nullptr) {
|
||||
resourceListPtr->Remove(this);
|
||||
}
|
||||
idResourceList::UnRegisterNetworkResource(this);
|
||||
}
|
||||
|
||||
void idResource::LoadResource() {
|
||||
const idResourceList* const list = GetResourceList();
|
||||
idLibPrint::FatalError("idResource::Load() for %s not implemented.\n",
|
||||
list != nullptr ? list->GetResourceTypeName() : "resource");
|
||||
}
|
||||
|
||||
bool idResource::ReloadIfStale() {
|
||||
return false;
|
||||
}
|
||||
|
||||
void idResource::WriteResourceFile() {
|
||||
const idResourceList* const list = GetResourceList();
|
||||
idLibPrint::FatalError(
|
||||
"resource type '%s' has not implemented WriteResourceFile()",
|
||||
list != nullptr ? list->GetResourceTypeName() : "resource");
|
||||
}
|
||||
|
||||
idResourceList* idResource::GetResourceList() {
|
||||
return resourceListPtr;
|
||||
}
|
||||
|
||||
void idResource::Print() {
|
||||
const idResourceList* const list = GetResourceList();
|
||||
const char* const typeName =
|
||||
list != nullptr ? list->GetResourceTypeName() : "resource";
|
||||
if (resourceError != nullptr) {
|
||||
idLibPrint::Printf("%s:%s:%s\n", typeName, name.c_str(),
|
||||
resourceError);
|
||||
} else {
|
||||
idLibPrint::Printf("%s:%s\n", typeName, name.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void idResource::List() {
|
||||
Print();
|
||||
}
|
||||
|
||||
void idResource::SetName(const char* const newName) {
|
||||
idStr canonicalName(newName);
|
||||
canonicalName.MakeNameCanonical();
|
||||
if (idStr::Cmp(canonicalName.c_str(), name.c_str()) != 0) {
|
||||
name.Set(canonicalName.c_str());
|
||||
}
|
||||
}
|
||||
@@ -1,155 +1,59 @@
|
||||
#pragma once
|
||||
|
||||
// Reconstructed C++ declarations from IDA Local Types and PDB/DIA metadata.
|
||||
// Original PDB header: w:\tech5\engine\framework\resource.h
|
||||
// Recovered logical types: 12
|
||||
// Signatures retain Xbox 360 ABI evidence and may still require manual review.
|
||||
#include "idlib/text/atomicstring.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
// IDA Local Type ordinal 2777; PDB kind: enum.
|
||||
enum idResource::referenceType_t : __int32
|
||||
{
|
||||
REF_UNKNOWN = 0x0,
|
||||
REF_FREED = 0x1,
|
||||
REF_WEAK = 0x2,
|
||||
REF_STRONG = 0x3,
|
||||
};
|
||||
class idResourceList;
|
||||
|
||||
// IDA Local Type ordinal 2778; PDB kind: enum.
|
||||
enum idResource::resourceError_t : __int32
|
||||
{
|
||||
RESOURCE_ERROR_NONE = 0x0,
|
||||
RESOURCE_ERROR_FILE_NOT_FOUND = 0x1,
|
||||
RESOURCE_ERROR_WRONG_TYPE = 0x2,
|
||||
RESOURCE_ERROR_INVALID_NAME = 0x3,
|
||||
RESOURCE_ERROR_HEADER_VERSION_MISMATCH = 0x4,
|
||||
RESOURCE_ERROR_RESOURCE_VERSION_MISMATCH = 0x5,
|
||||
RESOURCE_ERROR_FATAL = 0x6,
|
||||
RESOURCE_ERROR_TRUNCATED = 0x7,
|
||||
RESOURCE_ERROR_MAX = 0x8,
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 2779; PDB kind: enum.
|
||||
enum idResource::resourceHeader_t::resourcePlatform_t : __int32
|
||||
{
|
||||
RESOURCEPLATFORM_WINDOWS = 0x0,
|
||||
RESOURCEPLATFORM_360 = 0x1,
|
||||
RESOURCEPLATFORM_PS3 = 0x2,
|
||||
RESOURCEPLATFORM_MAC = 0x3,
|
||||
RESOURCEPLATFORM_MAX = 0x4,
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 13299; PDB kind: class.
|
||||
class __declspec(align(4)) idResource
|
||||
{
|
||||
class alignas(4) idResource {
|
||||
public:
|
||||
// Recovered virtual interface; IDA vtable ordinal 13302.
|
||||
virtual ~idResource();
|
||||
virtual void LoadResource();
|
||||
virtual bool ReloadIfStale();
|
||||
virtual void WriteResourceFile();
|
||||
virtual idResourceList *GetResourceList();
|
||||
virtual void Print();
|
||||
virtual void List();
|
||||
// Recovered from idResource's PDB type information. The executable
|
||||
// stores the printable error text in resourceError, while this enum is
|
||||
// the stable result vocabulary used by binary-resource loaders.
|
||||
enum resourceError_t : int {
|
||||
RESOURCE_ERROR_NONE = 0,
|
||||
RESOURCE_ERROR_FILE_NOT_FOUND = 1,
|
||||
RESOURCE_ERROR_WRONG_TYPE = 2,
|
||||
RESOURCE_ERROR_INVALID_NAME = 3,
|
||||
RESOURCE_ERROR_HEADER_VERSION_MISMATCH = 4,
|
||||
RESOURCE_ERROR_RESOURCE_VERSION_MISMATCH = 5,
|
||||
RESOURCE_ERROR_FATAL = 6,
|
||||
RESOURCE_ERROR_TRUNCATED = 7,
|
||||
RESOURCE_ERROR_MAX = 8
|
||||
};
|
||||
|
||||
int trackedMemory;
|
||||
idAtomicString name;
|
||||
idResource *nextOnHashChain;
|
||||
idResourceList *resourceListPtr;
|
||||
const char *resourceError;
|
||||
int networkID;
|
||||
int staleCount;
|
||||
unsigned __int8 : 3;
|
||||
__int8 loadDeferred : 1;
|
||||
__int8 forceLoad : 1;
|
||||
__int8 insideRecursiveLoad : 1;
|
||||
__int8 staticResource : 1;
|
||||
__int8 loadedResource : 1;
|
||||
enum referenceType_t : int {
|
||||
REF_UNKNOWN = 0,
|
||||
REF_FREED = 1,
|
||||
REF_WEAK = 2,
|
||||
REF_STRONG = 3
|
||||
};
|
||||
|
||||
idResource();
|
||||
virtual ~idResource();
|
||||
|
||||
virtual void LoadResource();
|
||||
virtual bool ReloadIfStale();
|
||||
virtual void WriteResourceFile();
|
||||
virtual idResourceList* GetResourceList();
|
||||
virtual void Print();
|
||||
virtual void List();
|
||||
|
||||
void SetName(const char* newName);
|
||||
const char* GetName() const { return name.c_str(); }
|
||||
|
||||
int trackedMemory;
|
||||
idAtomicString name;
|
||||
idResource* nextOnHashChain;
|
||||
idResourceList* resourceListPtr;
|
||||
const char* resourceError;
|
||||
int networkID;
|
||||
int staleCount;
|
||||
std::uint8_t resourceFlags;
|
||||
std::uint8_t pad[3];
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 13653; PDB kind: class.
|
||||
class idCodeResource<idDeclRenderParm> : public idBaseCodeResource
|
||||
{
|
||||
public:
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 17567; PDB kind: class.
|
||||
class idCodeResource<idFont> : public idBaseCodeResource
|
||||
{
|
||||
public:
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 19095; PDB kind: class.
|
||||
class idAtlasResource : public idResource
|
||||
{
|
||||
public:
|
||||
// Recovered virtual interface; IDA vtable ordinal 19096.
|
||||
virtual ~idAtlasResource();
|
||||
virtual void LoadResource();
|
||||
virtual bool ReloadIfStale();
|
||||
virtual void WriteResourceFile();
|
||||
virtual idResourceList *GetResourceList();
|
||||
virtual void Print();
|
||||
virtual void List();
|
||||
|
||||
unsigned int binaryTimestamp;
|
||||
idVec4 scaleBias;
|
||||
int img_tiles_wide;
|
||||
int img_tiles_high;
|
||||
int img_x;
|
||||
int img_y;
|
||||
int img_w;
|
||||
int img_h;
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 21416; PDB kind: class.
|
||||
class idCodeResource<idMaterial> : public idBaseCodeResource
|
||||
{
|
||||
public:
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 22063; PDB kind: struct.
|
||||
struct idResource::resourceHeader_t
|
||||
{
|
||||
unsigned __int16 headerVersionHi;
|
||||
unsigned __int16 headerVersionLo;
|
||||
unsigned int resourceId;
|
||||
unsigned __int16 versionHi;
|
||||
unsigned __int16 versionLo;
|
||||
unsigned int sourceTimestamp;
|
||||
unsigned __int16 sourceFileNameLen;
|
||||
unsigned __int16 uniqueIdNameLen;
|
||||
unsigned __int8 platform;
|
||||
unsigned __int8 pad[3];
|
||||
unsigned __int64 uniqueId;
|
||||
unsigned __int64 hash;
|
||||
unsigned __int64 dataOffset;
|
||||
unsigned __int64 totalSize;
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 22703; PDB kind: class.
|
||||
class idCodeResource<idSoundShader> : public idBaseCodeResource
|
||||
{
|
||||
public:
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 22770; PDB kind: class.
|
||||
class idCodeResource<idDeclRenderProg> : public idBaseCodeResource
|
||||
{
|
||||
public:
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 23337; PDB kind: class.
|
||||
class idJointResource : public idResource
|
||||
{
|
||||
public:
|
||||
// Recovered virtual interface; IDA vtable ordinal 23338.
|
||||
virtual ~idJointResource();
|
||||
virtual void LoadResource();
|
||||
virtual bool ReloadIfStale();
|
||||
virtual void WriteResourceFile();
|
||||
virtual idResourceList *GetResourceList();
|
||||
virtual void Print();
|
||||
virtual void List();
|
||||
|
||||
};
|
||||
#if INTPTR_MAX == INT32_MAX
|
||||
static_assert(sizeof(idResource) == 36, "Recovered idResource ABI changed");
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
#include "framework/resourcelist.h"
|
||||
|
||||
#include "framework/resource.h"
|
||||
#include "idlib/lib_print.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <mutex>
|
||||
|
||||
namespace {
|
||||
|
||||
std::mutex resourceListMutex;
|
||||
|
||||
unsigned int ResourceHash(const char* text) {
|
||||
unsigned int hash = 0;
|
||||
if (text != nullptr) {
|
||||
for (const unsigned char* cursor =
|
||||
reinterpret_cast<const unsigned char*>(text);
|
||||
*cursor != 0; ++cursor) {
|
||||
hash = 31u * hash + *cursor;
|
||||
}
|
||||
}
|
||||
return hash & 255u;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
idResourceList::idResourceList(const char* const typeName)
|
||||
: resourceTypeName(typeName), nextResourceList(nullptr), num(0),
|
||||
staticID(-1), hashTable{} {
|
||||
}
|
||||
|
||||
idResourceList::~idResourceList() = default;
|
||||
|
||||
idResource* idResourceList::Alloc(const char*) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int idResourceList::AllocSize() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char* idResourceList::GetResourceTypeName() const {
|
||||
if (resourceTypeName == nullptr) {
|
||||
idLibPrint::FatalError(
|
||||
"idResourceList::GetResourceTypeName: not set");
|
||||
}
|
||||
return resourceTypeName;
|
||||
}
|
||||
|
||||
idResource* idResourceList::Load(const char* const name,
|
||||
const bool makeDefault, const bool skipStaleCheck) {
|
||||
if (name == nullptr || *name == '\0') return nullptr;
|
||||
if (idResource* const existing = FindExisting(name, skipStaleCheck)) {
|
||||
return existing;
|
||||
}
|
||||
idResource* const resource = Alloc(name);
|
||||
if (resource == nullptr) return nullptr;
|
||||
resource->LoadResource();
|
||||
if (resource->resourceError != nullptr && !makeDefault) {
|
||||
Remove(resource);
|
||||
delete resource;
|
||||
return nullptr;
|
||||
}
|
||||
return resource;
|
||||
}
|
||||
|
||||
void idResourceList::Add(idResource* const resource) {
|
||||
if (resource == nullptr || resource->GetName() == nullptr
|
||||
|| *resource->GetName() == '\0') {
|
||||
idLibPrint::FatalError(
|
||||
"idResourceList::Add called with unnamed resource");
|
||||
}
|
||||
std::lock_guard<std::mutex> lock(resourceListMutex);
|
||||
idResource*& head = hashTable[ResourceHash(resource->GetName())];
|
||||
for (idResource* current = head; current != nullptr;
|
||||
current = current->nextOnHashChain) {
|
||||
if (current == resource) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
resource->nextOnHashChain = head;
|
||||
resource->resourceListPtr = this;
|
||||
head = resource;
|
||||
++num;
|
||||
}
|
||||
|
||||
idResource* idResourceList::FindExisting(const char* const name,
|
||||
const bool skipStaleCheck) {
|
||||
if (name == nullptr || *name == '\0') {
|
||||
return nullptr;
|
||||
}
|
||||
idResource* result = nullptr;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(resourceListMutex);
|
||||
for (idResource* current = hashTable[ResourceHash(name)];
|
||||
current != nullptr; current = current->nextOnHashChain) {
|
||||
if (_stricmp(current->GetName(), name) == 0) {
|
||||
result = current;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (result != nullptr && !skipStaleCheck) {
|
||||
result->ReloadIfStale();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
idResource* idResourceList::Index(const int index) const {
|
||||
if (index < 0 || index >= num) {
|
||||
return nullptr;
|
||||
}
|
||||
int currentIndex = 0;
|
||||
for (int bucket = 0; bucket < 256; ++bucket) {
|
||||
for (idResource* current = hashTable[bucket]; current != nullptr;
|
||||
current = current->nextOnHashChain) {
|
||||
if (currentIndex++ == index) {
|
||||
return current;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void idResourceList::Remove(idResource* const resource) {
|
||||
std::lock_guard<std::mutex> lock(resourceListMutex);
|
||||
idResource** link = &hashTable[ResourceHash(resource->name.c_str())];
|
||||
while (*link != nullptr && *link != resource) {
|
||||
link = &(*link)->nextOnHashChain;
|
||||
}
|
||||
if (*link == nullptr) {
|
||||
idLibPrint::FatalError("idResourceList::Remove( %s ) not found",
|
||||
resource->name.c_str());
|
||||
}
|
||||
*link = resource->nextOnHashChain;
|
||||
resource->nextOnHashChain = nullptr;
|
||||
resource->resourceListPtr = nullptr;
|
||||
--num;
|
||||
}
|
||||
|
||||
void idResourceList::UnRegisterNetworkResource(idResource* const resource) {
|
||||
// The recovered operation is a no-op for the normal unregistered state;
|
||||
// the network resource array itself belongs to the later framework port.
|
||||
if (resource->networkID != -1) {
|
||||
resource->networkID = -1;
|
||||
}
|
||||
}
|
||||
@@ -1,353 +1,49 @@
|
||||
#pragma once
|
||||
|
||||
// Reconstructed C++ declarations from IDA Local Types and PDB/DIA metadata.
|
||||
// Original PDB header: w:\tech5\engine\framework\resourcelist.h
|
||||
// Recovered logical types: 31
|
||||
// Signatures retain Xbox 360 ABI evidence and may still require manual review.
|
||||
#include "framework/resource.h"
|
||||
|
||||
|
||||
// IDA Local Type ordinal 13300; PDB kind: class.
|
||||
class idResourceList
|
||||
{
|
||||
class idResourceList {
|
||||
public:
|
||||
// Recovered virtual interface; IDA vtable ordinal 13301.
|
||||
virtual ~idResourceList();
|
||||
virtual idResource *Alloc(const char *);
|
||||
virtual int AllocSize();
|
||||
explicit idResourceList(const char* typeName = nullptr);
|
||||
virtual ~idResourceList();
|
||||
virtual idResource* Alloc(const char* name);
|
||||
virtual int AllocSize();
|
||||
|
||||
const char *resourceTypeName;
|
||||
idResourceList *nextResourceList;
|
||||
int num;
|
||||
int staticID;
|
||||
idResource *hashTable[256];
|
||||
const char* GetResourceTypeName() const;
|
||||
idResource* Load(const char* name, bool makeDefault,
|
||||
bool skipStaleCheck = false);
|
||||
void Add(idResource* resource);
|
||||
idResource* FindExisting(const char* name,
|
||||
bool skipStaleCheck = false);
|
||||
idResource* Index(int index) const;
|
||||
void Remove(idResource* resource);
|
||||
static void UnRegisterNetworkResource(idResource* resource);
|
||||
|
||||
const char* resourceTypeName;
|
||||
idResourceList* nextResourceList;
|
||||
int num;
|
||||
int staticID;
|
||||
idResource* hashTable[256];
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 18047; PDB kind: class.
|
||||
class idTypedResourceList<idVoiceTrack> : public idResourceList
|
||||
{
|
||||
template<class resourceType>
|
||||
class idTypedResourceList final : public idResourceList {
|
||||
public:
|
||||
// Recovered virtual interface; IDA vtable ordinal 18048.
|
||||
virtual ~idTypedResourceList();
|
||||
virtual idResource *Alloc(const char *);
|
||||
virtual int AllocSize();
|
||||
explicit idTypedResourceList(const char* const typeName)
|
||||
: idResourceList(typeName) {
|
||||
}
|
||||
|
||||
idResource* Alloc(const char* const name) override {
|
||||
resourceType* const resource = new resourceType();
|
||||
resource->SetName(name);
|
||||
Add(resource);
|
||||
return resource;
|
||||
}
|
||||
|
||||
int AllocSize() override { return sizeof(resourceType); }
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 18119; PDB kind: class.
|
||||
class idTypedResourceList<idNavigationMesh> : public idResourceList
|
||||
{
|
||||
public:
|
||||
// Recovered virtual interface; IDA vtable ordinal 18120.
|
||||
virtual ~idTypedResourceList();
|
||||
virtual idResource *Alloc(const char *);
|
||||
virtual int AllocSize();
|
||||
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 18306; PDB kind: class.
|
||||
class idTypedResourceList<idCoverPoints> : public idResourceList
|
||||
{
|
||||
public:
|
||||
// Recovered virtual interface; IDA vtable ordinal 18307.
|
||||
virtual ~idTypedResourceList();
|
||||
virtual idResource *Alloc(const char *);
|
||||
virtual int AllocSize();
|
||||
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 18732; PDB kind: class.
|
||||
class idTypedResourceList<idInGameVideoFile> : public idResourceList
|
||||
{
|
||||
public:
|
||||
// Recovered virtual interface; IDA vtable ordinal 18733.
|
||||
virtual ~idTypedResourceList();
|
||||
virtual idResource *Alloc(const char *);
|
||||
virtual int AllocSize();
|
||||
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 18735; PDB kind: class.
|
||||
class idTypedResourceList<idMD6Anim> : public idResourceList
|
||||
{
|
||||
public:
|
||||
// Recovered virtual interface; IDA vtable ordinal 18736.
|
||||
virtual ~idTypedResourceList();
|
||||
virtual idResource *Alloc(const char *);
|
||||
virtual int AllocSize();
|
||||
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 18739; PDB kind: class.
|
||||
class idTypedResourceList<idIESfile> : public idResourceList
|
||||
{
|
||||
public:
|
||||
// Recovered virtual interface; IDA vtable ordinal 18740.
|
||||
virtual ~idTypedResourceList();
|
||||
virtual idResource *Alloc(const char *);
|
||||
virtual int AllocSize();
|
||||
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 19089; PDB kind: class.
|
||||
class idTypedResourceList<idImage> : public idResourceList
|
||||
{
|
||||
public:
|
||||
// Recovered virtual interface; IDA vtable ordinal 19090.
|
||||
virtual ~idTypedResourceList();
|
||||
virtual idResource *Alloc(const char *);
|
||||
virtual int AllocSize();
|
||||
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 19097; PDB kind: class.
|
||||
class idTypedResourceList<idAtlasResource> : public idResourceList
|
||||
{
|
||||
public:
|
||||
// Recovered virtual interface; IDA vtable ordinal 19098.
|
||||
virtual ~idTypedResourceList();
|
||||
virtual idResource *Alloc(const char *);
|
||||
virtual int AllocSize();
|
||||
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 20001; PDB kind: class.
|
||||
class idTypedResourceList<idCollisionModelLocal> : public idResourceList
|
||||
{
|
||||
public:
|
||||
// Recovered virtual interface; IDA vtable ordinal 20002.
|
||||
virtual ~idTypedResourceList();
|
||||
virtual idResource *Alloc(const char *);
|
||||
virtual int AllocSize();
|
||||
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 20076; PDB kind: class.
|
||||
class idTypedResourceList<idFont> : public idResourceList
|
||||
{
|
||||
public:
|
||||
// Recovered virtual interface; IDA vtable ordinal 20077.
|
||||
virtual ~idTypedResourceList();
|
||||
virtual idResource *Alloc(const char *);
|
||||
virtual int AllocSize();
|
||||
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 21602; PDB kind: class.
|
||||
class idTypedResourceList<idStaticModel> : public idResourceList
|
||||
{
|
||||
public:
|
||||
// Recovered virtual interface; IDA vtable ordinal 21603.
|
||||
virtual ~idTypedResourceList();
|
||||
virtual idResource *Alloc(const char *);
|
||||
virtual int AllocSize();
|
||||
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 21604; PDB kind: class.
|
||||
class idTypedResourceList<idSoundSample> : public idResourceList
|
||||
{
|
||||
public:
|
||||
// Recovered virtual interface; IDA vtable ordinal 21605.
|
||||
virtual ~idTypedResourceList();
|
||||
virtual idResource *Alloc(const char *);
|
||||
virtual int AllocSize();
|
||||
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 21607; PDB kind: class.
|
||||
class idTypedResourceList<idStaticParticleModelData> : public idResourceList
|
||||
{
|
||||
public:
|
||||
// Recovered virtual interface; IDA vtable ordinal 21608.
|
||||
virtual ~idTypedResourceList();
|
||||
virtual idResource *Alloc(const char *);
|
||||
virtual int AllocSize();
|
||||
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 21627; PDB kind: class.
|
||||
class idTypedResourceList<idMD6Skel> : public idResourceList
|
||||
{
|
||||
public:
|
||||
// Recovered virtual interface; IDA vtable ordinal 21628.
|
||||
virtual ~idTypedResourceList();
|
||||
virtual idResource *Alloc(const char *);
|
||||
virtual int AllocSize();
|
||||
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 21701; PDB kind: class.
|
||||
class idTypedResourceList<idMD6Model> : public idResourceList
|
||||
{
|
||||
public:
|
||||
// Recovered virtual interface; IDA vtable ordinal 21702.
|
||||
virtual ~idTypedResourceList();
|
||||
virtual idResource *Alloc(const char *);
|
||||
virtual int AllocSize();
|
||||
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 21755; PDB kind: class.
|
||||
class idTypedResourceList<idDiscreteAnimationModelData> : public idResourceList
|
||||
{
|
||||
public:
|
||||
// Recovered virtual interface; IDA vtable ordinal 21756.
|
||||
virtual ~idTypedResourceList();
|
||||
virtual idResource *Alloc(const char *);
|
||||
virtual int AllocSize();
|
||||
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 21903; PDB kind: class.
|
||||
class idTypedResourceList<idMD6SkeletonConfig> : public idResourceList
|
||||
{
|
||||
public:
|
||||
// Recovered virtual interface; IDA vtable ordinal 21904.
|
||||
virtual ~idTypedResourceList();
|
||||
virtual idResource *Alloc(const char *);
|
||||
virtual int AllocSize();
|
||||
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 21984; PDB kind: class.
|
||||
class idTypedResourceList<idAAS2File> : public idResourceList
|
||||
{
|
||||
public:
|
||||
// Recovered virtual interface; IDA vtable ordinal 21985.
|
||||
virtual ~idTypedResourceList();
|
||||
virtual idResource *Alloc(const char *);
|
||||
virtual int AllocSize();
|
||||
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 22175; PDB kind: class.
|
||||
class idTypedResourceList<idRenderModelCuttableResource> : public idResourceList
|
||||
{
|
||||
public:
|
||||
// Recovered virtual interface; IDA vtable ordinal 22176.
|
||||
virtual ~idTypedResourceList();
|
||||
virtual idResource *Alloc(const char *);
|
||||
virtual int AllocSize();
|
||||
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 22333; PDB kind: class.
|
||||
class idTypedResourceList<idMorphVertices> : public idResourceList
|
||||
{
|
||||
public:
|
||||
// Recovered virtual interface; IDA vtable ordinal 22334.
|
||||
virtual ~idTypedResourceList();
|
||||
virtual idResource *Alloc(const char *);
|
||||
virtual int AllocSize();
|
||||
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 22456; PDB kind: class.
|
||||
class idTypedResourceList<idFileResource> : public idResourceList
|
||||
{
|
||||
public:
|
||||
// Recovered virtual interface; IDA vtable ordinal 22457.
|
||||
virtual ~idTypedResourceList();
|
||||
virtual idResource *Alloc(const char *);
|
||||
virtual int AllocSize();
|
||||
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 22869; PDB kind: class.
|
||||
class idTypedResourceList<idFoliageModel> : public idResourceList
|
||||
{
|
||||
public:
|
||||
// Recovered virtual interface; IDA vtable ordinal 22870.
|
||||
virtual ~idTypedResourceList();
|
||||
virtual idResource *Alloc(const char *);
|
||||
virtual int AllocSize();
|
||||
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 22873; PDB kind: class.
|
||||
class idTypedResourceList<idDetailModel> : public idResourceList
|
||||
{
|
||||
public:
|
||||
// Recovered virtual interface; IDA vtable ordinal 22874.
|
||||
virtual ~idTypedResourceList();
|
||||
virtual idResource *Alloc(const char *);
|
||||
virtual int AllocSize();
|
||||
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 23020; PDB kind: class.
|
||||
class idTypedResourceList<idImageCPU> : public idResourceList
|
||||
{
|
||||
public:
|
||||
// Recovered virtual interface; IDA vtable ordinal 23021.
|
||||
virtual ~idTypedResourceList();
|
||||
virtual idResource *Alloc(const char *);
|
||||
virtual int AllocSize();
|
||||
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 23026; PDB kind: class.
|
||||
class idTypedResourceList<idImageCubeCPU> : public idResourceList
|
||||
{
|
||||
public:
|
||||
// Recovered virtual interface; IDA vtable ordinal 23027.
|
||||
virtual ~idTypedResourceList();
|
||||
virtual idResource *Alloc(const char *);
|
||||
virtual int AllocSize();
|
||||
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 23308; PDB kind: class.
|
||||
class idTypedResourceList<idJointConversion> : public idResourceList
|
||||
{
|
||||
public:
|
||||
// Recovered virtual interface; IDA vtable ordinal 23309.
|
||||
virtual ~idTypedResourceList();
|
||||
virtual idResource *Alloc(const char *);
|
||||
virtual int AllocSize();
|
||||
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 23335; PDB kind: class.
|
||||
class idTypedResourceList<idJointResource> : public idResourceList
|
||||
{
|
||||
public:
|
||||
// Recovered virtual interface; IDA vtable ordinal 23336.
|
||||
virtual ~idTypedResourceList();
|
||||
virtual idResource *Alloc(const char *);
|
||||
virtual int AllocSize();
|
||||
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 23364; PDB kind: class.
|
||||
class idTypedResourceList<idDetailModelData> : public idResourceList
|
||||
{
|
||||
public:
|
||||
// Recovered virtual interface; IDA vtable ordinal 23365.
|
||||
virtual ~idTypedResourceList();
|
||||
virtual idResource *Alloc(const char *);
|
||||
virtual int AllocSize();
|
||||
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 23512; PDB kind: class.
|
||||
class idTypedResourceList<idAAS2DebugAreaModel> : public idResourceList
|
||||
{
|
||||
public:
|
||||
// Recovered virtual interface; IDA vtable ordinal 23513.
|
||||
virtual ~idTypedResourceList();
|
||||
virtual idResource *Alloc(const char *);
|
||||
virtual int AllocSize();
|
||||
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 23769; PDB kind: class.
|
||||
class idTypedResourceList<idCollisionGridLocal> : public idResourceList
|
||||
{
|
||||
public:
|
||||
// Recovered virtual interface; IDA vtable ordinal 23770.
|
||||
virtual ~idTypedResourceList();
|
||||
virtual idResource *Alloc(const char *);
|
||||
virtual int AllocSize();
|
||||
|
||||
};
|
||||
#if INTPTR_MAX == INT32_MAX
|
||||
static_assert(sizeof(idResourceList) == 1044,
|
||||
"Recovered idResourceList ABI changed");
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,314 @@
|
||||
#include "gamelib/aas2/aas2_local.h"
|
||||
|
||||
#include "framework/resourcelist.h"
|
||||
#include "idlib/lib_print.h"
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
idAAS2::~idAAS2() = default;
|
||||
|
||||
idAAS2* idAAS2::Alloc() {
|
||||
return new idAAS2Local();
|
||||
}
|
||||
|
||||
idAAS2Local::idAAS2Local()
|
||||
: file(nullptr), name(), defaulted(false), areaCacheIndex(nullptr),
|
||||
areaCacheIndexSize(0), portalCacheIndex(nullptr),
|
||||
portalCacheIndexSize(0), areaUpdate(nullptr), portalUpdate(nullptr),
|
||||
goalAreaTravelTimes(nullptr), areaTravelTimes(nullptr),
|
||||
numAreaTravelTimes(0), cacheListStart(nullptr), cacheListEnd(nullptr),
|
||||
totalCacheMemory(0), groundSpeedMultiplier(1.0f),
|
||||
waterSpeedMultiplier(1.0f), numObstaclePVSBytes(0),
|
||||
obstaclePVS(nullptr), obstaclePVSAreaNum(0), areaClipModels(nullptr),
|
||||
clipModelLinkAllocator(), obstacleAvoidance() {
|
||||
}
|
||||
|
||||
idAAS2Local::~idAAS2Local() {
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
void idAAS2Local::Shutdown() {
|
||||
if (file == nullptr) return;
|
||||
ShutdownRouting();
|
||||
ShutdownObstaclePVS();
|
||||
file = nullptr;
|
||||
areaCacheIndex = nullptr;
|
||||
portalCacheIndex = nullptr;
|
||||
areaUpdate = nullptr;
|
||||
portalUpdate = nullptr;
|
||||
goalAreaTravelTimes = nullptr;
|
||||
areaTravelTimes = nullptr;
|
||||
cacheListStart = nullptr;
|
||||
cacheListEnd = nullptr;
|
||||
obstaclePVS = nullptr;
|
||||
areaClipModels = nullptr;
|
||||
}
|
||||
|
||||
bool idAAS2Local::Init(const char* const mapName,
|
||||
const unsigned int mapFileCRC) {
|
||||
if (file != nullptr && mapName != nullptr
|
||||
&& _stricmp(mapName, file->GetName()) == 0
|
||||
&& mapFileCRC == file->crc && mapFileCRC != 0) {
|
||||
idLibPrint::Printf("Keeping %s\n", file->GetName());
|
||||
return true;
|
||||
}
|
||||
Shutdown();
|
||||
file = static_cast<idAAS2File*>(idAAS2File::resourceList.Load(
|
||||
mapName, true, false));
|
||||
if (file == nullptr) return false;
|
||||
name = mapName;
|
||||
defaulted = file->resourceError != nullptr;
|
||||
if (defaulted) {
|
||||
idLibPrint::Warning("Couldn't load AAS file: '%s'", mapName);
|
||||
}
|
||||
file->ResetCover();
|
||||
SetupRouting();
|
||||
SetupObstaclePVS();
|
||||
return true;
|
||||
}
|
||||
|
||||
void idAAS2Local::Stats() const {
|
||||
if (file == nullptr) return;
|
||||
idLibPrint::Printf("[%s]\n", file->GetName());
|
||||
idLibPrint::Printf("%6d areas\n", file->areas.Num() - 1);
|
||||
idLibPrint::Printf("%6u kB file size\n", file->MemorySize() >> 10);
|
||||
RoutingStats();
|
||||
}
|
||||
|
||||
const idAAS2Settings* idAAS2Local::GetSettings() const {
|
||||
return file != nullptr ? &file->settings : nullptr;
|
||||
}
|
||||
|
||||
int idAAS2Local::PointAreaNum(
|
||||
const idIndex<int, invalidAASTree_t> tree,
|
||||
const idVec3& origin) const {
|
||||
return file != nullptr ? file->PointAreaNum(tree.Get(), origin) : 0;
|
||||
}
|
||||
|
||||
int idAAS2Local::PointReachableAreaNum(
|
||||
const idIndex<int, invalidAASTree_t> tree, const idVec3& origin,
|
||||
const idBounds&, const int areaFlags,
|
||||
const int excludeTravelFlags) const {
|
||||
return file != nullptr ? file->PointReachableAreaNum(tree, origin,
|
||||
areaFlags, excludeTravelFlags) : 0;
|
||||
}
|
||||
|
||||
int idAAS2Local::BoundsReachableAreaNum(
|
||||
const idIndex<int, invalidAASTree_t> tree, const idBounds& bounds,
|
||||
const int areaFlags, const int excludeTravelFlags) const {
|
||||
return file != nullptr ? file->BoundsReachableAreaNum(tree.Get(), bounds,
|
||||
static_cast<std::uint16_t>(areaFlags), excludeTravelFlags) : 0;
|
||||
}
|
||||
|
||||
int idAAS2Local::BoundsReachableAreaNum(
|
||||
const idIndex<int, invalidAASTree_t> tree, const idVec3& origin,
|
||||
const idBounds& bounds, const int areaFlags,
|
||||
const int excludeTravelFlags) const {
|
||||
idBounds translated(bounds);
|
||||
translated[0] = translated[0] + origin;
|
||||
translated[1] = translated[1] + origin;
|
||||
return BoundsReachableAreaNum(tree, translated, areaFlags,
|
||||
excludeTravelFlags);
|
||||
}
|
||||
|
||||
void idAAS2Local::PushPointIntoAreaNum(const int areaNum,
|
||||
idVec3& origin) const {
|
||||
if (file == nullptr || areaNum <= 0 || areaNum >= file->areas.Num()) return;
|
||||
if ((file->areas[areaNum].flags & AAS_AREA_NOPUSH) == 0) {
|
||||
file->PushPointIntoAreaNum(areaNum, origin);
|
||||
}
|
||||
}
|
||||
|
||||
idVec3 idAAS2Local::AreaCenter(const int areaNum) const {
|
||||
return file != nullptr ? file->AreaCenter(areaNum) : idVec3();
|
||||
}
|
||||
|
||||
bool idAAS2Local::Trace(const idIndex<int, invalidAASTree_t> tree,
|
||||
aas2Trace_t& trace, const idVec3& start, const idVec3& end) const {
|
||||
return file != nullptr && file->Trace(tree.Get(), trace, start, end);
|
||||
}
|
||||
|
||||
bool idAAS2Local::TraceHeight(const idIndex<int, invalidAASTree_t> tree,
|
||||
aas2TraceHeight_t& trace, const idVec3& start,
|
||||
const idVec3& end) const {
|
||||
return file != nullptr && file->TraceHeight(tree.Get(), trace, start, end);
|
||||
}
|
||||
|
||||
bool idAAS2Local::TraceFloor(aas2TraceFloor_t& trace,
|
||||
const idVec3& start, const int startAreaNum, const idVec3& end,
|
||||
const int travelFlags, const bool allowFloorNormalChange,
|
||||
const bool ignoreGravityDirectionDistance,
|
||||
const bool ignoreSameArea) const {
|
||||
return file != nullptr && file->TraceFloor(trace, start, startAreaNum,
|
||||
end, 0, travelFlags, allowFloorNormalChange,
|
||||
ignoreGravityDirectionDistance, ignoreSameArea);
|
||||
}
|
||||
|
||||
void idAAS2Local::GetEdgeVertexNumbers(const int edgeNum,
|
||||
int verts[2]) const {
|
||||
if (verts == nullptr || file == nullptr) return;
|
||||
const aas2Edge_t& edge = file->edges[std::abs(edgeNum)];
|
||||
const int reverse = edgeNum < 0 ? 1 : 0;
|
||||
verts[0] = edge.vertexNum[reverse];
|
||||
verts[1] = edge.vertexNum[1 - reverse];
|
||||
}
|
||||
|
||||
void idAAS2Local::GetEdge(const int edgeNum, idVec3& start,
|
||||
idVec3& end) const {
|
||||
int vertices[2] = {};
|
||||
GetEdgeVertexNumbers(edgeNum, vertices);
|
||||
start = file->vertices[vertices[0]];
|
||||
end = file->vertices[vertices[1]];
|
||||
}
|
||||
|
||||
int idAAS2Local::GetEdgeFlags(const int edgeNum) const {
|
||||
return file != nullptr ? file->edges[std::abs(edgeNum)].flags : 0;
|
||||
}
|
||||
|
||||
int idAAS2Local::GetAreaFlags(const int areaNum) const {
|
||||
return file != nullptr && areaNum >= 0 && areaNum < file->areas.Num()
|
||||
? file->areas[areaNum].flags : 0;
|
||||
}
|
||||
|
||||
void idAAS2Local::ClipGridToAreas(const idVec3& startOrigin,
|
||||
const int startAreaNum, const int travelFlags,
|
||||
const unsigned int cellSize, const int dimension,
|
||||
std::uint8_t* const reachable) const {
|
||||
if (file != nullptr) file->ClipGridToAreas(startOrigin, startAreaNum,
|
||||
travelFlags, cellSize, dimension, reachable);
|
||||
}
|
||||
|
||||
bool idAAS2Local::GetAASAnim(
|
||||
const idIndex<short, invalidAASAnimIndex_t> index,
|
||||
const aas2AnimName_t** const value) const {
|
||||
return file != nullptr && file->GetAASAnim(index, value);
|
||||
}
|
||||
|
||||
idIndex<short, invalidAASAnimIndex_t>
|
||||
idAAS2Local::GetAASAnimIndexByName(const char* const value) const {
|
||||
return file != nullptr ? file->GetAASAnimIndexByName(value)
|
||||
: idIndex<short, invalidAASAnimIndex_t>();
|
||||
}
|
||||
|
||||
bool idAAS2Local::GetAASDependency(
|
||||
const idIndex<short, invalidAASDependencyIndex_t> index,
|
||||
const aas2DependencyName_t** const value) const {
|
||||
return file != nullptr && file->GetAASDependency(index, value);
|
||||
}
|
||||
|
||||
idIndex<short, invalidAASDependencyIndex_t>
|
||||
idAAS2Local::GetAASDependencyIndexByName(const char* const value) const {
|
||||
return file != nullptr ? file->GetAASDependencyIndexByName(value)
|
||||
: idIndex<short, invalidAASDependencyIndex_t>();
|
||||
}
|
||||
|
||||
bool idAAS2Local::GetAASInteractionEntity(
|
||||
const idIndex<short, invalidAASInteractionEntIndex_t> index,
|
||||
const aas2InteractionEntityName_t** const value) const {
|
||||
return file != nullptr && file->GetAASInteractionEntity(index, value);
|
||||
}
|
||||
|
||||
idIndex<short, invalidAASInteractionEntIndex_t>
|
||||
idAAS2Local::GetAASInteractionEntityIndexByName(
|
||||
const char* const value) const {
|
||||
return file != nullptr ? file->GetAASInteractionEntityIndexByName(value)
|
||||
: idIndex<short, invalidAASInteractionEntIndex_t>();
|
||||
}
|
||||
|
||||
bool idAAS2Local::GetAASTraversalNameIndex(
|
||||
const idIndex<short, invalidAASTraversalNameIndex_t> index,
|
||||
const aas2TraversalEntityName_t** const value) const {
|
||||
return file != nullptr && file->GetAASTraversalNameIndex(index, value);
|
||||
}
|
||||
|
||||
idIndex<short, invalidAASTraversalNameIndex_t>
|
||||
idAAS2Local::GetAASTraversalNameIndexByName(const char* const value) const {
|
||||
return file != nullptr ? file->GetAASTraversalNameIndexByName(value)
|
||||
: idIndex<short, invalidAASTraversalNameIndex_t>();
|
||||
}
|
||||
|
||||
int idAAS2Local::GetAASTraversalIndexByNameIndex(
|
||||
const idIndex<short, invalidAASTraversalNameIndex_t> index) const {
|
||||
return file != nullptr ? file->GetAASTraversalIndexByNameIndex(index) : -1;
|
||||
}
|
||||
|
||||
bool idAAS2Local::SetTraversalFlag(const int index, const int flags) {
|
||||
if (file == nullptr || !file->SetTraversalFlag(index, flags)) return false;
|
||||
int startArea = 0;
|
||||
int goalArea = 0;
|
||||
if (file->GetAASTraversalAreas(index, startArea, goalArea)) {
|
||||
RemoveRoutingCacheUsingArea(startArea);
|
||||
RemoveRoutingCacheUsingArea(goalArea);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool idAAS2Local::ClearTraversalFlag(const int index, const int flags) {
|
||||
if (file == nullptr || !file->ClearTraversalFlag(index, flags)) return false;
|
||||
int startArea = 0;
|
||||
int goalArea = 0;
|
||||
if (file->GetAASTraversalAreas(index, startArea, goalArea)) {
|
||||
RemoveRoutingCacheUsingArea(startArea);
|
||||
RemoveRoutingCacheUsingArea(goalArea);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int idAAS2Local::GetNumAreas() const {
|
||||
return file != nullptr ? file->areas.Num() : 0;
|
||||
}
|
||||
|
||||
const idPlane* idAAS2Local::GetPlane(const int index) const {
|
||||
return file != nullptr && index >= 0 && index < file->planes.Num()
|
||||
? &file->planes[index] : nullptr;
|
||||
}
|
||||
|
||||
const aas2Area_t* idAAS2Local::GetArea(const int areaNum) const {
|
||||
return file != nullptr && areaNum >= 0 && areaNum < file->areas.Num()
|
||||
? &file->areas[areaNum] : nullptr;
|
||||
}
|
||||
|
||||
const aas2Reachability_t* idAAS2Local::GetReachability(
|
||||
const idIndex<short, invalidReachability_t> reachability) const {
|
||||
const int index = reachability.Get();
|
||||
return file != nullptr && index >= 0 && index < file->reachabilities.Num()
|
||||
? &file->reachabilities[index] : nullptr;
|
||||
}
|
||||
|
||||
const aas2Traversal_t* idAAS2Local::GetTraversal(
|
||||
const int traversalNum) const {
|
||||
return file != nullptr && traversalNum >= 0
|
||||
&& traversalNum < file->traversalPoints.Num()
|
||||
? &file->traversalPoints[traversalNum] : nullptr;
|
||||
}
|
||||
|
||||
int idAAS2Local::GetNumTrees() const {
|
||||
return file != nullptr ? file->trees.Num() : 0;
|
||||
}
|
||||
|
||||
idIndex<int, invalidAASTree_t> idAAS2Local::GetTree(
|
||||
const int index) const {
|
||||
return index >= 0 && index < GetNumTrees()
|
||||
? idIndex<int, invalidAASTree_t>(index)
|
||||
: idIndex<int, invalidAASTree_t>();
|
||||
}
|
||||
|
||||
idIndex<int, invalidAASTree_t> idAAS2Local::GetTreeForFloorNormal(
|
||||
const idVec3& floorNormal) const {
|
||||
return file != nullptr ? file->GetTreeForFloorNormal(floorNormal)
|
||||
: idIndex<int, invalidAASTree_t>();
|
||||
}
|
||||
|
||||
const idVec3* idAAS2Local::GetFloorNormalForArea(const int areaNum) const {
|
||||
return file != nullptr ? file->GetFloorNormalForArea(areaNum) : nullptr;
|
||||
}
|
||||
|
||||
const idVec3* idAAS2Local::GetFloorNormalForTree(
|
||||
const idIndex<int, invalidAASTree_t> tree) const {
|
||||
return file != nullptr ? file->GetFloorNormalForTree(tree) : nullptr;
|
||||
}
|
||||
|
||||
int idAAS2Local::GetNumAreasInTree(
|
||||
const idIndex<int, invalidAASTree_t> tree) const {
|
||||
return file != nullptr ? file->GetNumAreasInTree(tree) : 0;
|
||||
}
|
||||
@@ -1,99 +1,269 @@
|
||||
#pragma once
|
||||
|
||||
// Reconstructed C++ declarations from IDA Local Types and PDB/DIA metadata.
|
||||
// Original PDB header: w:\tech5\engine\gamelib\aas2\aas2.h
|
||||
// Recovered logical types: 1
|
||||
// Signatures retain Xbox 360 ABI evidence and may still require manual review.
|
||||
#include "aas2file/aas2file.h"
|
||||
#include "gamelib/aas2/obstacleavoidancecore.h"
|
||||
#include "idlib/bv/bounds.h"
|
||||
#include "idlib/callback.h"
|
||||
#include "idlib/containers/list.h"
|
||||
#include "idlib/index.h"
|
||||
#include "idlib/math/angles.h"
|
||||
#include "idlib/math/plane.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
// IDA Local Type ordinal 14078; PDB kind: class.
|
||||
class idAAS2
|
||||
{
|
||||
public:
|
||||
// Recovered virtual interface; IDA vtable ordinal 14113.
|
||||
virtual ~idAAS2();
|
||||
virtual bool IsDefault();
|
||||
virtual bool Init(const char *, unsigned int);
|
||||
virtual void Stats();
|
||||
virtual void Test(const idVec3 *, const idVec3 *, const idMat3 *, int, int, int, int, const int, idClip *, usercmd_t *, idAngles *);
|
||||
virtual const idAAS2Settings *GetSettings();
|
||||
virtual idBounds *DefaultSearchBounds(idBounds *result);
|
||||
virtual int PointAreaNum(idIndex<int,enum invalidAASTree_t>, const idVec3 *);
|
||||
virtual int PointReachableAreaNum(idIndex<int,enum invalidAASTree_t>, const idVec3 *, const idBounds *, const int, const int);
|
||||
virtual int BoundsReachableAreaNum(idIndex<int,enum invalidAASTree_t>, const idBounds *, const int, const int);
|
||||
virtual int BoundsReachableAreaNum_2(idIndex<int,enum invalidAASTree_t>, const idVec3 *, const idBounds *, const int, const int);
|
||||
virtual bool NearestPointReachable(idIndex<int,enum invalidAASTree_t>, const idVec3 *, const int, const idVec3 *, const idBounds *, const int, idAAS2NearestReachable *);
|
||||
virtual void PushPointIntoAreaNum(int, idVec3 *);
|
||||
virtual idVec3 *AreaCenter(idVec3 *result, int);
|
||||
virtual bool Trace(idIndex<int,enum invalidAASTree_t>, aas2Trace_t *, const idVec3 *, const idVec3 *);
|
||||
virtual bool TraceHeight(idIndex<int,enum invalidAASTree_t>, aas2TraceHeight_t *, const idVec3 *, const idVec3 *);
|
||||
virtual bool TraceFloor(aas2TraceFloor_t *, const idVec3 *, int, const idVec3 *, int, bool, bool, bool);
|
||||
virtual void GetEdgeVertexNumbers(int, int *);
|
||||
virtual void GetEdge(int, idVec3 *, idVec3 *);
|
||||
virtual int GetEdgeFlags(int);
|
||||
virtual int GetAreaFlags(int);
|
||||
virtual bool ChangeAreaTravelFlags(const idBounds *, const int, int, bool);
|
||||
virtual bool ChangeReachabilityTravelFlags(const char *, int, bool);
|
||||
virtual bool ChangeReachabilityTravelFlags_2(idIndex<short,enum invalidReachability_t>, int, bool);
|
||||
virtual int TravelTimeToGoalArea(int, const idVec3 *, int, const idVec3 *, int, const idAAS2TravelSpeeds *);
|
||||
virtual bool RouteToGoalArea(int, const idVec3 *, int, const idVec3 *, int, int *, idIndex<short,enum invalidReachability_t> *);
|
||||
virtual bool FindNearestGoal(idAAS2Goal *, int, const idVec3 *, int, idAAS2Callback *);
|
||||
virtual bool WalkPathToGoal(idAAS2Path *, int, const idVec3 *, int, const idVec3 *, const int, const int);
|
||||
virtual bool ExtendHopPathToGoal(idAAS2Path *, int, const idVec3 *, int, const idVec3 *, int, int, const idAAS2HopPathParms *);
|
||||
virtual void ShowWalkPath(const int, const idVec3 *, const int, const idVec3 *, const int, const int, const int, const int, const int);
|
||||
virtual void ShowHopPath(int, const idVec3 *, int, const idVec3 *, int, int, const idAAS2HopPathParms *);
|
||||
virtual void DrawEdge(const int, const bool, const bool, const bool);
|
||||
virtual void DrawReachability(const idIndex<short,enum invalidReachability_t>, const aas2Reachability_t *, const char *);
|
||||
virtual void DrawArea(const int, const bool, const bool, const bool, const bool, const bool, const bool);
|
||||
virtual float GetGroundSpeedMultiplier();
|
||||
virtual bool GetOptimizedWalkPath(int, const idVec3 *, int, const idVec3 *, int, int, idList<idVec3,5> *);
|
||||
virtual int GetNumAreas();
|
||||
virtual const idPlane *GetPlane(const int);
|
||||
virtual const aas2Area_t *GetArea(const int);
|
||||
virtual const aas2Reachability_t *GetReachability(const idIndex<short,enum invalidReachability_t>);
|
||||
virtual bool GetPath(const idVec3 *, const idVec3 *, const int, const int, const int, idAAS2PathPoint *, const int, int *);
|
||||
virtual void ClipGridToAreas(const idVec3 *, int, int, int, int, unsigned __int8 *);
|
||||
virtual int GetInvalidTravelFlags();
|
||||
virtual int GetTraversalsForReachability(const idIndex<short,enum invalidReachability_t>, idList<int,5> *);
|
||||
virtual int GetTraversalsForInteractableEntity(const idIndex<short,enum invalidAASInteractionEntIndex_t>, idList<int,5> *);
|
||||
virtual int FindBestTraversalForReachability(const idIndex<short,enum invalidReachability_t>, const idVec3 *, const idVec3 *, idVec3 *, idVec3 *, idIndex<short,enum invalidAASDependencyIndex_t> *, const int, bool);
|
||||
virtual const aas2Traversal_t *GetTraversal(const int);
|
||||
virtual bool GetAASAnim(const idIndex<short,enum invalidAASAnimIndex_t>, const aas2AnimName_t **);
|
||||
virtual idIndex<short,enum invalidAASAnimIndex_t> *GetAASAnimIndexByName(idIndex<short,enum invalidAASAnimIndex_t> *result, const char *);
|
||||
virtual bool GetAASDependency(const idIndex<short,enum invalidAASDependencyIndex_t>, const aas2DependencyName_t **);
|
||||
virtual idIndex<short,enum invalidAASDependencyIndex_t> *GetAASDependencyIndexByName(idIndex<short,enum invalidAASDependencyIndex_t> *result, const char *);
|
||||
virtual bool GetAASInteractionEntity(const idIndex<short,enum invalidAASInteractionEntIndex_t>, const aas2InteractionEntityName_t **);
|
||||
virtual idIndex<short,enum invalidAASInteractionEntIndex_t> *GetAASInteractionEntityIndexByName(idIndex<short,enum invalidAASInteractionEntIndex_t> *result, const char *);
|
||||
virtual bool GetAASTraversalNameIndex(const idIndex<short,enum invalidAASTraversalNameIndex_t>, const aas2TraversalEntityName_t **);
|
||||
virtual idIndex<short,enum invalidAASTraversalNameIndex_t> *GetAASTraversalNameIndexByName(idIndex<short,enum invalidAASTraversalNameIndex_t> *result, const char *);
|
||||
virtual int GetAASTraversalIndexByNameIndex(const idIndex<short,enum invalidAASTraversalNameIndex_t>);
|
||||
virtual bool SetTraversalFlag(int, int);
|
||||
virtual bool ClearTraversalFlag(int, int);
|
||||
virtual const unsigned __int8 *GetObstaclePVS(int);
|
||||
virtual int GetObstaclePVSWallEdges(int, int, int *, int);
|
||||
virtual int GetObstaclePVSClipModels(int, idClipModel **, int);
|
||||
virtual void GetObstaclePVSObstacles(const int, const int, obstacleVertex_t *, int *, obstacleEdge_t *, int *, idClipModel **, int *, obstacleRoute_t *);
|
||||
virtual void FreeClipLinks();
|
||||
virtual idAASClipLink *LinkClipModel(idClipModel *);
|
||||
virtual idAASClipLink *UnLinkClipModel(idAASClipLink *);
|
||||
virtual int GetNumCoverPoints();
|
||||
virtual const aas2Cover_t *GetCoverPoint(const int);
|
||||
virtual int GetNumAreaCoverPoints(const int);
|
||||
virtual int GetAreaCoverPoints(const int, int *, const int, int *);
|
||||
virtual int FindCoverPoints(const int, const idVec3 *, const float, const int, const int, const int, int *, const int, int *);
|
||||
virtual int GetNumChokePoints();
|
||||
virtual const aas2ChokePoint_t *GetChokePoint(const int);
|
||||
virtual int GetNumHintNodes();
|
||||
virtual const aas2HintNode_t *GetHintNode(const int);
|
||||
virtual bool GetAreaCells(const int, const int, int *, int *);
|
||||
virtual bool WorldPosToCellPos(const int, const int, const idVec3 *, int *, int *);
|
||||
virtual bool CellPosToWorldPos(const int, const int, const int, const int, idVec3 *, const bool);
|
||||
virtual void DrawCoverPoint(const aas2Cover_t *, const int);
|
||||
virtual int GetNumTrees();
|
||||
virtual idIndex<int,enum invalidAASTree_t> *GetTree(idIndex<int,enum invalidAASTree_t> *result, int);
|
||||
virtual idIndex<int,enum invalidAASTree_t> *GetTreeForFloorNormal(idIndex<int,enum invalidAASTree_t> *result, const idVec3 *);
|
||||
virtual const idVec3 *GetFloorNormalForArea(int);
|
||||
virtual const idVec3 *GetFloorNormalForTree(idIndex<int,enum invalidAASTree_t>);
|
||||
virtual int GetNumAreasInTree(idIndex<int,enum invalidAASTree_t>);
|
||||
class idClip;
|
||||
class idClipModel;
|
||||
class idAASClipLink;
|
||||
struct usercmd_t;
|
||||
struct obstacleVertex_t;
|
||||
struct obstacleEdge_t;
|
||||
|
||||
struct idAAS2NearestReachable {
|
||||
int nearestDestArea;
|
||||
idVec3 nearestDestPos;
|
||||
int nearestTravelTime;
|
||||
int quickestDestArea;
|
||||
idVec3 quickestDestPos;
|
||||
int quickestTravelTime;
|
||||
};
|
||||
|
||||
struct idAAS2TravelSpeeds {
|
||||
float defaultSpeed;
|
||||
float swimSpeed;
|
||||
float flySpeed;
|
||||
float crouchSpeed;
|
||||
};
|
||||
|
||||
struct idAAS2Goal {
|
||||
int areaNum;
|
||||
idVec3 origin;
|
||||
};
|
||||
|
||||
struct idAAS2HopPathParms {
|
||||
float maxDistance;
|
||||
float maxHeight;
|
||||
float minHeight;
|
||||
float maxSlope;
|
||||
};
|
||||
|
||||
struct idAAS2PathPoint {
|
||||
int areaNum;
|
||||
idVec3 origin;
|
||||
idIndex<short, invalidReachability_t> nextReachIndex;
|
||||
int travelTime;
|
||||
};
|
||||
|
||||
struct idAAS2Path {
|
||||
int type;
|
||||
idVec3 moveGoal;
|
||||
int moveAreaNum;
|
||||
aas2EdgeCrossed_t firstEdge;
|
||||
idIndex<short, invalidReachability_t> reachIndex;
|
||||
int travelTime;
|
||||
int traversalIndex;
|
||||
idVec3 traversalStart;
|
||||
idVec3 traversalEnd;
|
||||
idIndex<short, invalidAASDependencyIndex_t> dependencyIndex;
|
||||
idVec3 obstacleGoal;
|
||||
int obstacleAreaNum;
|
||||
float pathMaxHeight;
|
||||
obstacleRoute_t obstacleRoute;
|
||||
|
||||
idAAS2Path()
|
||||
: type(10), moveGoal(), moveAreaNum(0), firstEdge{}, reachIndex(),
|
||||
travelTime(-1), traversalIndex(-1), traversalStart(), traversalEnd(),
|
||||
dependencyIndex(), obstacleGoal(), obstacleAreaNum(0),
|
||||
pathMaxHeight(0.0f), obstacleRoute() {
|
||||
}
|
||||
};
|
||||
|
||||
class idAAS2 {
|
||||
public:
|
||||
virtual ~idAAS2();
|
||||
|
||||
static idAAS2* Alloc();
|
||||
|
||||
virtual bool IsDefault() const = 0;
|
||||
virtual bool Init(const char* mapName, unsigned int mapFileCRC) = 0;
|
||||
virtual void Stats() const = 0;
|
||||
virtual void Test(const idVec3& origin, const idVec3& destination,
|
||||
const idMat3& axis, int areaNum, int travelFlags, int contentMask,
|
||||
int debugMode, int treeNum, idClip* clip, usercmd_t* command,
|
||||
idAngles& viewAngles) = 0;
|
||||
virtual const idAAS2Settings* GetSettings() const = 0;
|
||||
virtual idBounds DefaultSearchBounds() const = 0;
|
||||
virtual int PointAreaNum(idIndex<int, invalidAASTree_t> tree,
|
||||
const idVec3& origin) const = 0;
|
||||
virtual int PointReachableAreaNum(idIndex<int, invalidAASTree_t> tree,
|
||||
const idVec3& origin, const idBounds& searchBounds, int areaFlags,
|
||||
int excludeTravelFlags) const = 0;
|
||||
virtual int BoundsReachableAreaNum(idIndex<int, invalidAASTree_t> tree,
|
||||
const idBounds& bounds, int areaFlags, int excludeTravelFlags) const = 0;
|
||||
virtual int BoundsReachableAreaNum(idIndex<int, invalidAASTree_t> tree,
|
||||
const idVec3& origin, const idBounds& bounds, int areaFlags,
|
||||
int excludeTravelFlags) const = 0;
|
||||
virtual bool NearestPointReachable(idIndex<int, invalidAASTree_t> tree,
|
||||
const idVec3& origin, int areaNum, const idVec3& destination,
|
||||
const idBounds& searchBounds, int travelFlags,
|
||||
idAAS2NearestReachable& nearest) const = 0;
|
||||
virtual void PushPointIntoAreaNum(int areaNum, idVec3& origin) const = 0;
|
||||
virtual idVec3 AreaCenter(int areaNum) const = 0;
|
||||
virtual bool Trace(idIndex<int, invalidAASTree_t> tree, aas2Trace_t& trace,
|
||||
const idVec3& start, const idVec3& end) const = 0;
|
||||
virtual bool TraceHeight(idIndex<int, invalidAASTree_t> tree,
|
||||
aas2TraceHeight_t& trace, const idVec3& start,
|
||||
const idVec3& end) const = 0;
|
||||
virtual bool TraceFloor(aas2TraceFloor_t& trace, const idVec3& start,
|
||||
int startAreaNum, const idVec3& end, int travelFlags,
|
||||
bool allowFloorNormalChange, bool ignoreGravityDirectionDistance,
|
||||
bool ignoreSameArea) const = 0;
|
||||
virtual void GetEdgeVertexNumbers(int edgeNum, int verts[2]) const = 0;
|
||||
virtual void GetEdge(int edgeNum, idVec3& start, idVec3& end) const = 0;
|
||||
virtual int GetEdgeFlags(int edgeNum) const = 0;
|
||||
virtual int GetAreaFlags(int areaNum) const = 0;
|
||||
virtual bool ChangeAreaTravelFlags(const idBounds& bounds, int treeNum,
|
||||
int travelFlags, bool set) = 0;
|
||||
virtual bool ChangeReachabilityTravelFlags(const char* name,
|
||||
int travelFlags, bool set) = 0;
|
||||
virtual bool ChangeReachabilityTravelFlags(
|
||||
idIndex<short, invalidReachability_t> reachability,
|
||||
int travelFlags, bool set) = 0;
|
||||
virtual int TravelTimeToGoalArea(int areaNum, const idVec3& origin,
|
||||
int goalAreaNum, const idVec3& goalOrigin, int travelFlags,
|
||||
const idAAS2TravelSpeeds* speeds = nullptr) = 0;
|
||||
virtual bool RouteToGoalArea(int areaNum, const idVec3& origin,
|
||||
int goalAreaNum, const idVec3& goalOrigin, int travelFlags,
|
||||
int& travelTime,
|
||||
idIndex<short, invalidReachability_t>& reachability) = 0;
|
||||
virtual bool FindNearestGoal(idAAS2Goal& goal, int areaNum,
|
||||
const idVec3& origin, int travelFlags, idAAS2Callback& callback) = 0;
|
||||
virtual bool WalkPathToGoal(idAAS2Path& path, int areaNum,
|
||||
const idVec3& origin, int goalAreaNum, const idVec3& goalOrigin,
|
||||
int travelFlags, int obstacleFlags) = 0;
|
||||
virtual bool ExtendHopPathToGoal(idAAS2Path& path, int areaNum,
|
||||
const idVec3& origin, int goalAreaNum, const idVec3& goalOrigin,
|
||||
int travelFlags, int obstacleFlags,
|
||||
const idAAS2HopPathParms& parms) = 0;
|
||||
virtual void ShowWalkPath(int areaNum, const idVec3& origin,
|
||||
int goalAreaNum, const idVec3& goalOrigin, int travelFlags,
|
||||
int obstacleFlags, int contentMask, int debugFlags,
|
||||
int treeNum) const = 0;
|
||||
virtual void ShowHopPath(int areaNum, const idVec3& origin,
|
||||
int goalAreaNum, const idVec3& goalOrigin, int travelFlags,
|
||||
int obstacleFlags, const idAAS2HopPathParms& parms) const = 0;
|
||||
virtual void DrawEdge(int edgeNum, bool arrow, bool text,
|
||||
bool highlight) const = 0;
|
||||
virtual void DrawReachability(
|
||||
idIndex<short, invalidReachability_t> reachability,
|
||||
const aas2Reachability_t& reach, const char* label) const = 0;
|
||||
virtual void DrawArea(int areaNum, bool showNumbers, bool showEdges,
|
||||
bool showReachabilities, bool showFloor, bool showBounds,
|
||||
bool highlight) const = 0;
|
||||
virtual float GetGroundSpeedMultiplier() const = 0;
|
||||
virtual bool GetOptimizedWalkPath(int areaNum, const idVec3& origin,
|
||||
int goalAreaNum, const idVec3& goalOrigin, int travelFlags,
|
||||
int obstacleFlags, idList<idVec3, 5>& points) = 0;
|
||||
virtual int GetNumAreas() const = 0;
|
||||
virtual const idPlane* GetPlane(int index) const = 0;
|
||||
virtual const aas2Area_t* GetArea(int areaNum) const = 0;
|
||||
virtual const aas2Reachability_t* GetReachability(
|
||||
idIndex<short, invalidReachability_t> reachability) const = 0;
|
||||
virtual bool GetPath(const idVec3& origin, const idVec3& goalOrigin,
|
||||
int areaNum, int goalAreaNum, int travelFlags,
|
||||
idAAS2PathPoint* points, int maxPoints, int& numPoints) = 0;
|
||||
virtual void ClipGridToAreas(const idVec3& startOrigin, int startAreaNum,
|
||||
int travelFlags, unsigned int cellSize, int dimension,
|
||||
std::uint8_t* reachable) const = 0;
|
||||
virtual int GetInvalidTravelFlags() const = 0;
|
||||
virtual int GetTraversalsForReachability(
|
||||
idIndex<short, invalidReachability_t> reachability,
|
||||
idList<int, 5>& traversals) const = 0;
|
||||
virtual int GetTraversalsForInteractableEntity(
|
||||
idIndex<short, invalidAASInteractionEntIndex_t> entity,
|
||||
idList<int, 5>& traversals) const = 0;
|
||||
virtual int FindBestTraversalForReachability(
|
||||
idIndex<short, invalidReachability_t> reachability,
|
||||
const idVec3& origin, const idVec3& goalOrigin,
|
||||
idVec3& traversalStart, idVec3& traversalEnd,
|
||||
idIndex<short, invalidAASDependencyIndex_t>& dependency,
|
||||
int travelFlags, bool allowDisabled) const = 0;
|
||||
virtual const aas2Traversal_t* GetTraversal(int traversalNum) const = 0;
|
||||
virtual bool GetAASAnim(idIndex<short, invalidAASAnimIndex_t> index,
|
||||
const aas2AnimName_t** name) const = 0;
|
||||
virtual idIndex<short, invalidAASAnimIndex_t> GetAASAnimIndexByName(
|
||||
const char* name) const = 0;
|
||||
virtual bool GetAASDependency(
|
||||
idIndex<short, invalidAASDependencyIndex_t> index,
|
||||
const aas2DependencyName_t** name) const = 0;
|
||||
virtual idIndex<short, invalidAASDependencyIndex_t>
|
||||
GetAASDependencyIndexByName(const char* name) const = 0;
|
||||
virtual bool GetAASInteractionEntity(
|
||||
idIndex<short, invalidAASInteractionEntIndex_t> index,
|
||||
const aas2InteractionEntityName_t** name) const = 0;
|
||||
virtual idIndex<short, invalidAASInteractionEntIndex_t>
|
||||
GetAASInteractionEntityIndexByName(const char* name) const = 0;
|
||||
virtual bool GetAASTraversalNameIndex(
|
||||
idIndex<short, invalidAASTraversalNameIndex_t> index,
|
||||
const aas2TraversalEntityName_t** name) const = 0;
|
||||
virtual idIndex<short, invalidAASTraversalNameIndex_t>
|
||||
GetAASTraversalNameIndexByName(const char* name) const = 0;
|
||||
virtual int GetAASTraversalIndexByNameIndex(
|
||||
idIndex<short, invalidAASTraversalNameIndex_t> index) const = 0;
|
||||
virtual bool SetTraversalFlag(int index, int flags) = 0;
|
||||
virtual bool ClearTraversalFlag(int index, int flags) = 0;
|
||||
virtual const std::uint8_t* GetObstaclePVS(int areaNum) = 0;
|
||||
virtual int GetObstaclePVSWallEdges(int areaNum, int edgeFlags,
|
||||
int* outputEdges, int maxEdges) const = 0;
|
||||
virtual int GetObstaclePVSClipModels(int areaNum,
|
||||
idClipModel** models, int maxModels) = 0;
|
||||
virtual void GetObstaclePVSObstacles(int areaNum, int obstacleFlags,
|
||||
obstacleVertex_t* vertices, int* numVertices,
|
||||
obstacleEdge_t* edges, int* numEdges, idClipModel** models,
|
||||
int* numModels, obstacleRoute_t* route) = 0;
|
||||
virtual void FreeClipLinks() = 0;
|
||||
virtual idAASClipLink* LinkClipModel(idClipModel* model) = 0;
|
||||
virtual idAASClipLink* UnLinkClipModel(idAASClipLink* link) = 0;
|
||||
virtual int GetNumCoverPoints() const = 0;
|
||||
virtual const aas2Cover_t* GetCoverPoint(int index) const = 0;
|
||||
virtual int GetNumAreaCoverPoints(int areaNum) const = 0;
|
||||
virtual int GetAreaCoverPoints(int areaNum, int* points, int maxPoints,
|
||||
int* flags) const = 0;
|
||||
virtual int FindCoverPoints(int areaNum, const idVec3& origin,
|
||||
float radius, int coverFlags, int excludeFlags, int maxPoints,
|
||||
int* points, int team, int* areaFlags) const = 0;
|
||||
virtual int GetNumChokePoints() const = 0;
|
||||
virtual const aas2ChokePoint_t* GetChokePoint(int index) const = 0;
|
||||
virtual int GetNumHintNodes() const = 0;
|
||||
virtual const aas2HintNode_t* GetHintNode(int index) const = 0;
|
||||
virtual bool GetAreaCells(int areaNum, int subdivisionSize,
|
||||
int* xCells, int* yCells) const = 0;
|
||||
virtual bool WorldPosToCellPos(int areaNum, int subdivisionSize,
|
||||
const idVec3& worldPos, int* xCell, int* yCell) const = 0;
|
||||
virtual bool CellPosToWorldPos(int areaNum, int subdivisionSize,
|
||||
int xCell, int yCell, idVec3& worldPos, bool center) const = 0;
|
||||
virtual void DrawCoverPoint(const aas2Cover_t& cover, int index) const = 0;
|
||||
virtual int GetNumTrees() const = 0;
|
||||
virtual idIndex<int, invalidAASTree_t> GetTree(int index) const = 0;
|
||||
virtual idIndex<int, invalidAASTree_t> GetTreeForFloorNormal(
|
||||
const idVec3& floorNormal) const = 0;
|
||||
virtual const idVec3* GetFloorNormalForArea(int areaNum) const = 0;
|
||||
virtual const idVec3* GetFloorNormalForTree(
|
||||
idIndex<int, invalidAASTree_t> tree) const = 0;
|
||||
virtual int GetNumAreasInTree(
|
||||
idIndex<int, invalidAASTree_t> tree) const = 0;
|
||||
};
|
||||
|
||||
#if INTPTR_MAX == INT32_MAX
|
||||
static_assert(sizeof(idAAS2NearestReachable) == 40,
|
||||
"Recovered idAAS2NearestReachable ABI changed");
|
||||
static_assert(sizeof(idAAS2TravelSpeeds) == 16,
|
||||
"Recovered idAAS2TravelSpeeds ABI changed");
|
||||
static_assert(sizeof(idAAS2Goal) == 16,
|
||||
"Recovered idAAS2Goal ABI changed");
|
||||
static_assert(sizeof(idAAS2PathPoint) == 24,
|
||||
"Recovered idAAS2PathPoint ABI changed");
|
||||
static_assert(sizeof(idAAS2HopPathParms) == 16,
|
||||
"Recovered idAAS2HopPathParms ABI changed");
|
||||
static_assert(sizeof(idAAS2Path) == 436,
|
||||
"Recovered idAAS2Path ABI changed");
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
#include "gamelib/aas2/aas2_local.h"
|
||||
|
||||
#include "gamelib/physics/clip.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
idBounds idAAS2Local::DefaultSearchBounds() const {
|
||||
idBounds bounds{};
|
||||
if (file == nullptr) return bounds;
|
||||
bounds = file->settings.boundingBox;
|
||||
const float width = bounds[1].x - bounds[0].x;
|
||||
bounds[0].x = width * -0.5f;
|
||||
bounds[1].x = width * 0.5f;
|
||||
return bounds;
|
||||
}
|
||||
|
||||
void idAAS2Local::ShowWalkPath(const int startAreaNum,
|
||||
const idVec3& startOrigin, const int goalAreaNum,
|
||||
const idVec3& goalOrigin, const int travelFlags,
|
||||
const int obstacleFlags, const int, const int,
|
||||
const int) const {
|
||||
if (file == nullptr) return;
|
||||
idAAS2Path path;
|
||||
const_cast<idAAS2Local*>(this)->WalkPathToGoal(path, startAreaNum,
|
||||
startOrigin, goalAreaNum, goalOrigin, travelFlags, obstacleFlags);
|
||||
}
|
||||
|
||||
void idAAS2Local::ShowHopPath(const int startAreaNum,
|
||||
const idVec3& startOrigin, const int goalAreaNum,
|
||||
const idVec3& goalOrigin, const int travelFlags,
|
||||
const int obstacleFlags, const idAAS2HopPathParms& parms) const {
|
||||
if (file == nullptr) return;
|
||||
idAAS2Path path;
|
||||
idAAS2Local* const mutableThis = const_cast<idAAS2Local*>(this);
|
||||
if (mutableThis->WalkPathToGoal(path, startAreaNum, startOrigin,
|
||||
goalAreaNum, goalOrigin, travelFlags, obstacleFlags)) {
|
||||
mutableThis->ExtendHopPathToGoal(path, startAreaNum, startOrigin,
|
||||
goalAreaNum, goalOrigin, travelFlags, obstacleFlags, parms);
|
||||
}
|
||||
}
|
||||
|
||||
void idAAS2Local::ShowOptimizedWalkPath(const int startAreaNum,
|
||||
const idVec3& startOrigin, const int goalAreaNum,
|
||||
const idVec3& goalOrigin, const int travelFlags,
|
||||
const int obstacleFlags) const {
|
||||
idList<idVec3, 5> points;
|
||||
const_cast<idAAS2Local*>(this)->GetOptimizedWalkPath(startAreaNum,
|
||||
startOrigin, goalAreaNum, goalOrigin, travelFlags, obstacleFlags,
|
||||
points);
|
||||
}
|
||||
|
||||
void idAAS2Local::DrawEdge(const int edgeNum, const bool,
|
||||
const bool, const bool) const {
|
||||
if (file == nullptr) return;
|
||||
const int absoluteEdge = edgeNum < 0 ? -edgeNum : edgeNum;
|
||||
if (absoluteEdge <= 0 || absoluteEdge >= file->edges.Num()) return;
|
||||
const aas2Edge_t& edge = file->edges[absoluteEdge];
|
||||
if (edge.vertexNum[0] < 0 || edge.vertexNum[1] < 0
|
||||
|| edge.vertexNum[0] >= file->vertices.Num()
|
||||
|| edge.vertexNum[1] >= file->vertices.Num()) return;
|
||||
// Geometry is intentionally read and validated here. The engine renderer
|
||||
// consumes these endpoints when its debug-draw sink is attached.
|
||||
const idVec3& start = file->vertices[
|
||||
edgeNum < 0 ? edge.vertexNum[1] : edge.vertexNum[0]];
|
||||
const idVec3& end = file->vertices[
|
||||
edgeNum < 0 ? edge.vertexNum[0] : edge.vertexNum[1]];
|
||||
(void)start;
|
||||
(void)end;
|
||||
}
|
||||
|
||||
void idAAS2Local::DrawReachability(
|
||||
const idIndex<short, invalidReachability_t> reachability,
|
||||
const aas2Reachability_t& reach, const char*) const {
|
||||
if (file == nullptr || reachability.Get() < 0
|
||||
|| reachability.Get() >= file->reachabilities.Num()) return;
|
||||
const idVec3 start = reach.Start();
|
||||
const idVec3 end = reach.End();
|
||||
(void)start;
|
||||
(void)end;
|
||||
idList<int, 5> traversals;
|
||||
GetTraversalsForReachability(reachability, traversals);
|
||||
for (int index = 0; index < traversals.Num(); ++index) {
|
||||
const aas2Traversal_t* const traversal = GetTraversal(
|
||||
traversals[index]);
|
||||
(void)traversal;
|
||||
}
|
||||
}
|
||||
|
||||
void idAAS2Local::DrawArea(const int areaNum,
|
||||
const bool showReachabilities, const bool showEdges,
|
||||
const bool, const bool, const bool, const bool) const {
|
||||
if (file == nullptr || areaNum <= 0 || areaNum >= file->areas.Num())
|
||||
return;
|
||||
const aas2Area_t& area = file->areas[areaNum];
|
||||
if (showEdges) {
|
||||
for (int index = 0; index < area.numEdges; ++index) {
|
||||
const int edgeIndex = area.firstEdge + index;
|
||||
if (edgeIndex >= 0 && edgeIndex < file->edgeIndex.Num())
|
||||
DrawEdge(file->edgeIndex[edgeIndex], false, false, false);
|
||||
}
|
||||
}
|
||||
if (showReachabilities) {
|
||||
idIndex<short, invalidReachability_t> reachability = area.reach;
|
||||
int guard = file->reachabilities.Num();
|
||||
while (reachability.Get() >= 0
|
||||
&& reachability.Get() < file->reachabilities.Num()
|
||||
&& guard-- > 0) {
|
||||
const aas2Reachability_t& reach =
|
||||
file->reachabilities[reachability.Get()];
|
||||
DrawReachability(reachability, reach, nullptr);
|
||||
reachability = reach.next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void idAAS2Local::DrawCoverPoint(const aas2Cover_t& cover,
|
||||
const int index) const {
|
||||
if (file == nullptr || index < 0 || index >= file->cover.Num()) return;
|
||||
const idVec3 wallStart = cover.origin;
|
||||
const idVec3 wallEnd = cover.origin + cover.dir * 16.0f;
|
||||
(void)wallStart;
|
||||
(void)wallEnd;
|
||||
}
|
||||
|
||||
void idAAS2Local::Test(const idVec3& origin,
|
||||
const idVec3& destination, const idMat3&, int areaNum,
|
||||
const int travelFlags, const int contentMask,
|
||||
const int debugMode, const int treeNum, idClip*, usercmd_t*,
|
||||
idAngles&) {
|
||||
if (file == nullptr) return;
|
||||
if (areaNum <= 0) {
|
||||
areaNum = PointReachableAreaNum(
|
||||
idIndex<int, invalidAASTree_t>(treeNum), origin,
|
||||
DefaultSearchBounds(), AAS_AREA_REACHABLE_WALK, 0);
|
||||
}
|
||||
const int goalAreaNum = PointReachableAreaNum(
|
||||
idIndex<int, invalidAASTree_t>(treeNum), destination,
|
||||
DefaultSearchBounds(), AAS_AREA_REACHABLE_WALK, 0);
|
||||
if (debugMode <= 0) {
|
||||
DrawArea(areaNum, true, true, true, true, true, true);
|
||||
} else if (debugMode == 1) {
|
||||
ShowWalkPath(areaNum, origin, goalAreaNum, destination,
|
||||
travelFlags, 0, contentMask, debugMode, treeNum);
|
||||
} else if (debugMode == 2) {
|
||||
idAAS2HopPathParms parms{};
|
||||
ShowHopPath(areaNum, origin, goalAreaNum, destination,
|
||||
travelFlags, 0, parms);
|
||||
} else {
|
||||
ShowOptimizedWalkPath(areaNum, origin, goalAreaNum, destination,
|
||||
travelFlags, 0);
|
||||
}
|
||||
}
|
||||
@@ -1,137 +1,314 @@
|
||||
#pragma once
|
||||
|
||||
// Reconstructed C++ declarations from IDA Local Types and PDB/DIA metadata.
|
||||
// Original PDB header: w:\tech5\engine\gamelib\aas2\aas2_local.h
|
||||
// Recovered logical types: 3
|
||||
// Signatures retain Xbox 360 ABI evidence and may still require manual review.
|
||||
#include "gamelib/aas2/aas2.h"
|
||||
#include "gamelib/aas2/obstacleavoidance.h"
|
||||
#include "idlib/blockalloc_base.h"
|
||||
#include "idlib/text/str.h"
|
||||
|
||||
class idRenderWorld;
|
||||
struct idObstacleBuffers;
|
||||
|
||||
// IDA Local Type ordinal 3176; PDB kind: unknown.
|
||||
enum idAAS2Local::NearestPointReachable::__l2::<unnamed_tag> : __int32
|
||||
{
|
||||
MaxAreas = 0x20,
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 14089; PDB kind: struct.
|
||||
struct idAAS2Goal
|
||||
{
|
||||
int areaNum;
|
||||
idVec3 origin;
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 23504; PDB kind: class.
|
||||
class idAAS2Local : public idAAS2
|
||||
{
|
||||
class idAASRoutingCache {
|
||||
public:
|
||||
// Recovered virtual interface; IDA vtable ordinal 23510.
|
||||
virtual ~idAAS2Local();
|
||||
virtual bool IsDefault();
|
||||
virtual bool Init(const char *, unsigned int);
|
||||
virtual void Stats();
|
||||
virtual void Test(const idVec3 *, const idVec3 *, const idMat3 *, int, int, int, int, const int, idClip *, usercmd_t *, idAngles *);
|
||||
virtual const idAAS2Settings *GetSettings();
|
||||
virtual idBounds *DefaultSearchBounds(idBounds *result);
|
||||
virtual int PointAreaNum(idIndex<int,enum invalidAASTree_t>, const idVec3 *);
|
||||
virtual int PointReachableAreaNum(idIndex<int,enum invalidAASTree_t>, const idVec3 *, const idBounds *, const int, const int);
|
||||
virtual int BoundsReachableAreaNum(idIndex<int,enum invalidAASTree_t>, const idBounds *, const int, const int);
|
||||
virtual int BoundsReachableAreaNum_2(idIndex<int,enum invalidAASTree_t>, const idVec3 *, const idBounds *, const int, const int);
|
||||
virtual bool NearestPointReachable(idIndex<int,enum invalidAASTree_t>, const idVec3 *, const int, const idVec3 *, const idBounds *, const int, idAAS2NearestReachable *);
|
||||
virtual void PushPointIntoAreaNum(int, idVec3 *);
|
||||
virtual idVec3 *AreaCenter(idVec3 *result, int);
|
||||
virtual bool Trace(idIndex<int,enum invalidAASTree_t>, aas2Trace_t *, const idVec3 *, const idVec3 *);
|
||||
virtual bool TraceHeight(idIndex<int,enum invalidAASTree_t>, aas2TraceHeight_t *, const idVec3 *, const idVec3 *);
|
||||
virtual bool TraceFloor(aas2TraceFloor_t *, const idVec3 *, int, const idVec3 *, int, bool, bool, bool);
|
||||
virtual void GetEdgeVertexNumbers(int, int *);
|
||||
virtual void GetEdge(int, idVec3 *, idVec3 *);
|
||||
virtual int GetEdgeFlags(int);
|
||||
virtual int GetAreaFlags(int);
|
||||
virtual bool ChangeAreaTravelFlags(const idBounds *, const int, int, bool);
|
||||
virtual bool ChangeReachabilityTravelFlags(const char *, int, bool);
|
||||
virtual bool ChangeReachabilityTravelFlags_2(idIndex<short,enum invalidReachability_t>, int, bool);
|
||||
virtual int TravelTimeToGoalArea(int, const idVec3 *, int, const idVec3 *, int, const idAAS2TravelSpeeds *);
|
||||
virtual bool RouteToGoalArea(int, const idVec3 *, int, const idVec3 *, int, int *, idIndex<short,enum invalidReachability_t> *);
|
||||
virtual bool FindNearestGoal(idAAS2Goal *, int, const idVec3 *, int, idAAS2Callback *);
|
||||
virtual bool WalkPathToGoal(idAAS2Path *, int, const idVec3 *, int, const idVec3 *, const int, const int);
|
||||
virtual bool ExtendHopPathToGoal(idAAS2Path *, int, const idVec3 *, int, const idVec3 *, int, int, const idAAS2HopPathParms *);
|
||||
virtual void ShowWalkPath(const int, const idVec3 *, const int, const idVec3 *, const int, const int, const int, const int, const int);
|
||||
virtual void ShowHopPath(int, const idVec3 *, int, const idVec3 *, int, int, const idAAS2HopPathParms *);
|
||||
virtual void DrawEdge(const int, const bool, const bool, const bool);
|
||||
virtual void DrawReachability(const idIndex<short,enum invalidReachability_t>, const aas2Reachability_t *, const char *);
|
||||
virtual void DrawArea(const int, const bool, const bool, const bool, const bool, const bool, const bool);
|
||||
virtual float GetGroundSpeedMultiplier();
|
||||
virtual bool GetOptimizedWalkPath(int, const idVec3 *, int, const idVec3 *, int, int, idList<idVec3,5> *);
|
||||
virtual int GetNumAreas();
|
||||
virtual const idPlane *GetPlane(const int);
|
||||
virtual const aas2Area_t *GetArea(const int);
|
||||
virtual const aas2Reachability_t *GetReachability(const idIndex<short,enum invalidReachability_t>);
|
||||
virtual bool GetPath(const idVec3 *, const idVec3 *, const int, const int, const int, idAAS2PathPoint *, const int, int *);
|
||||
virtual void ClipGridToAreas(const idVec3 *, int, int, int, int, unsigned __int8 *);
|
||||
virtual int GetInvalidTravelFlags();
|
||||
virtual int GetTraversalsForReachability(const idIndex<short,enum invalidReachability_t>, idList<int,5> *);
|
||||
virtual int GetTraversalsForInteractableEntity(const idIndex<short,enum invalidAASInteractionEntIndex_t>, idList<int,5> *);
|
||||
virtual int FindBestTraversalForReachability(const idIndex<short,enum invalidReachability_t>, const idVec3 *, const idVec3 *, idVec3 *, idVec3 *, idIndex<short,enum invalidAASDependencyIndex_t> *, const int, bool);
|
||||
virtual const aas2Traversal_t *GetTraversal(const int);
|
||||
virtual bool GetAASAnim(const idIndex<short,enum invalidAASAnimIndex_t>, const aas2AnimName_t **);
|
||||
virtual idIndex<short,enum invalidAASAnimIndex_t> *GetAASAnimIndexByName(idIndex<short,enum invalidAASAnimIndex_t> *result, const char *);
|
||||
virtual bool GetAASDependency(const idIndex<short,enum invalidAASDependencyIndex_t>, const aas2DependencyName_t **);
|
||||
virtual idIndex<short,enum invalidAASDependencyIndex_t> *GetAASDependencyIndexByName(idIndex<short,enum invalidAASDependencyIndex_t> *result, const char *);
|
||||
virtual bool GetAASInteractionEntity(const idIndex<short,enum invalidAASInteractionEntIndex_t>, const aas2InteractionEntityName_t **);
|
||||
virtual idIndex<short,enum invalidAASInteractionEntIndex_t> *GetAASInteractionEntityIndexByName(idIndex<short,enum invalidAASInteractionEntIndex_t> *result, const char *);
|
||||
virtual bool GetAASTraversalNameIndex(const idIndex<short,enum invalidAASTraversalNameIndex_t>, const aas2TraversalEntityName_t **);
|
||||
virtual idIndex<short,enum invalidAASTraversalNameIndex_t> *GetAASTraversalNameIndexByName(idIndex<short,enum invalidAASTraversalNameIndex_t> *result, const char *);
|
||||
virtual int GetAASTraversalIndexByNameIndex(const idIndex<short,enum invalidAASTraversalNameIndex_t>);
|
||||
virtual bool SetTraversalFlag(int, int);
|
||||
virtual bool ClearTraversalFlag(int, int);
|
||||
virtual const unsigned __int8 *GetObstaclePVS(int);
|
||||
virtual int GetObstaclePVSWallEdges(int, int, int *, int);
|
||||
virtual int GetObstaclePVSClipModels(int, idClipModel **, int);
|
||||
virtual void GetObstaclePVSObstacles(const int, const int, obstacleVertex_t *, int *, obstacleEdge_t *, int *, idClipModel **, int *, obstacleRoute_t *);
|
||||
virtual void FreeClipLinks();
|
||||
virtual idAASClipLink *LinkClipModel(idClipModel *);
|
||||
virtual idAASClipLink *UnLinkClipModel(idAASClipLink *);
|
||||
virtual int GetNumCoverPoints();
|
||||
virtual const aas2Cover_t *GetCoverPoint(const int);
|
||||
virtual int GetNumAreaCoverPoints(const int);
|
||||
virtual int GetAreaCoverPoints(const int, int *, const int, int *);
|
||||
virtual int FindCoverPoints(const int, const idVec3 *, const float, const int, const int, const int, int *, const int, int *);
|
||||
virtual int GetNumChokePoints();
|
||||
virtual const aas2ChokePoint_t *GetChokePoint(const int);
|
||||
virtual int GetNumHintNodes();
|
||||
virtual const aas2HintNode_t *GetHintNode(const int);
|
||||
virtual bool GetAreaCells(const int, const int, int *, int *);
|
||||
virtual bool WorldPosToCellPos(const int, const int, const idVec3 *, int *, int *);
|
||||
virtual bool CellPosToWorldPos(const int, const int, const int, const int, idVec3 *, const bool);
|
||||
virtual void DrawCoverPoint(const aas2Cover_t *, const int);
|
||||
virtual int GetNumTrees();
|
||||
virtual idIndex<int,enum invalidAASTree_t> *GetTree(idIndex<int,enum invalidAASTree_t> *result, int);
|
||||
virtual idIndex<int,enum invalidAASTree_t> *GetTreeForFloorNormal(idIndex<int,enum invalidAASTree_t> *result, const idVec3 *);
|
||||
virtual const idVec3 *GetFloorNormalForArea(int);
|
||||
virtual const idVec3 *GetFloorNormalForTree(idIndex<int,enum invalidAASTree_t>);
|
||||
virtual int GetNumAreasInTree(idIndex<int,enum invalidAASTree_t>);
|
||||
virtual void Shutdown();
|
||||
virtual void ShowOptimizedWalkPath(int, const idVec3 *, int, const idVec3 *, int, int);
|
||||
explicit idAASRoutingCache(int cacheSize);
|
||||
~idAASRoutingCache();
|
||||
|
||||
idAAS2File *file;
|
||||
idStr name;
|
||||
bool defaulted;
|
||||
idAASRoutingCache ***areaCacheIndex;
|
||||
int areaCacheIndexSize;
|
||||
idAASRoutingCache **portalCacheIndex;
|
||||
int portalCacheIndexSize;
|
||||
idRoutingUpdate *areaUpdate;
|
||||
idRoutingUpdate *portalUpdate;
|
||||
unsigned __int16 *goalAreaTravelTimes;
|
||||
unsigned __int16 *areaTravelTimes;
|
||||
int numAreaTravelTimes;
|
||||
idAASRoutingCache *cacheListStart;
|
||||
idAASRoutingCache *cacheListEnd;
|
||||
int totalCacheMemory;
|
||||
float groundSpeedMultiplier;
|
||||
float waterSpeedMultiplier;
|
||||
int numObstaclePVSBytes;
|
||||
unsigned __int8 *obstaclePVS;
|
||||
int obstaclePVSAreaNum;
|
||||
idAASClipLink **areaClipModels;
|
||||
idBlockAlloc<idAASClipLink,128,37> clipModelLinkAllocator;
|
||||
idObstacleAvoidance obstacleAvoidance;
|
||||
int type;
|
||||
int size;
|
||||
int cluster;
|
||||
int areaNum;
|
||||
int travelFlags;
|
||||
idAASRoutingCache* next;
|
||||
idAASRoutingCache* prev;
|
||||
idAASRoutingCache* time_next;
|
||||
idAASRoutingCache* time_prev;
|
||||
std::uint16_t startTravelTime;
|
||||
std::uint8_t* reachabilities;
|
||||
std::uint16_t* travelTimes;
|
||||
};
|
||||
|
||||
struct idRoutingUpdate {
|
||||
int cluster;
|
||||
int areaNum;
|
||||
std::uint16_t tmpTravelTime;
|
||||
std::uint16_t* areaTravelTimes;
|
||||
idVec3 start;
|
||||
idRoutingUpdate* next;
|
||||
idRoutingUpdate* prev;
|
||||
bool isInList;
|
||||
};
|
||||
|
||||
class idAASClipLink {
|
||||
public:
|
||||
idAASClipLink* prevArea;
|
||||
idAASClipLink* nextArea;
|
||||
idAASClipLink* prevClip;
|
||||
idAASClipLink* nextClip;
|
||||
idClipModel* clipModel;
|
||||
int areaNum;
|
||||
};
|
||||
|
||||
class idAAS2Local final : public idAAS2 {
|
||||
public:
|
||||
idAAS2Local();
|
||||
~idAAS2Local() override;
|
||||
|
||||
bool IsDefault() const override { return defaulted; }
|
||||
bool Init(const char* mapName, unsigned int mapFileCRC) override;
|
||||
void Shutdown();
|
||||
void Stats() const override;
|
||||
void Test(const idVec3& origin, const idVec3& destination,
|
||||
const idMat3& axis, int areaNum, int travelFlags, int contentMask,
|
||||
int debugMode, int treeNum, idClip* clip, usercmd_t* command,
|
||||
idAngles& viewAngles) override;
|
||||
const idAAS2Settings* GetSettings() const override;
|
||||
idBounds DefaultSearchBounds() const override;
|
||||
int PointAreaNum(idIndex<int, invalidAASTree_t> tree,
|
||||
const idVec3& origin) const override;
|
||||
int PointReachableAreaNum(idIndex<int, invalidAASTree_t> tree,
|
||||
const idVec3& origin, const idBounds& searchBounds, int areaFlags,
|
||||
int excludeTravelFlags) const override;
|
||||
int BoundsReachableAreaNum(idIndex<int, invalidAASTree_t> tree,
|
||||
const idBounds& bounds, int areaFlags,
|
||||
int excludeTravelFlags) const override;
|
||||
int BoundsReachableAreaNum(idIndex<int, invalidAASTree_t> tree,
|
||||
const idVec3& origin, const idBounds& bounds, int areaFlags,
|
||||
int excludeTravelFlags) const override;
|
||||
bool NearestPointReachable(idIndex<int, invalidAASTree_t> tree,
|
||||
const idVec3& origin, int areaNum, const idVec3& destination,
|
||||
const idBounds& searchBounds, int travelFlags,
|
||||
idAAS2NearestReachable& nearest) const override;
|
||||
void PushPointIntoAreaNum(int areaNum, idVec3& origin) const override;
|
||||
idVec3 AreaCenter(int areaNum) const override;
|
||||
bool Trace(idIndex<int, invalidAASTree_t> tree, aas2Trace_t& trace,
|
||||
const idVec3& start, const idVec3& end) const override;
|
||||
bool TraceHeight(idIndex<int, invalidAASTree_t> tree,
|
||||
aas2TraceHeight_t& trace, const idVec3& start,
|
||||
const idVec3& end) const override;
|
||||
bool TraceFloor(aas2TraceFloor_t& trace, const idVec3& start,
|
||||
int startAreaNum, const idVec3& end, int travelFlags,
|
||||
bool allowFloorNormalChange, bool ignoreGravityDirectionDistance,
|
||||
bool ignoreSameArea) const override;
|
||||
void GetEdgeVertexNumbers(int edgeNum, int verts[2]) const override;
|
||||
void GetEdge(int edgeNum, idVec3& start, idVec3& end) const override;
|
||||
int GetEdgeFlags(int edgeNum) const override;
|
||||
int GetAreaFlags(int areaNum) const override;
|
||||
bool ChangeAreaTravelFlags(const idBounds& bounds, int treeNum,
|
||||
int travelFlags, bool set) override;
|
||||
bool ChangeReachabilityTravelFlags(const char* name,
|
||||
int travelFlags, bool set) override;
|
||||
bool ChangeReachabilityTravelFlags(
|
||||
idIndex<short, invalidReachability_t> reachability,
|
||||
int travelFlags, bool set) override;
|
||||
int TravelTimeToGoalArea(int areaNum, const idVec3& origin,
|
||||
int goalAreaNum, const idVec3& goalOrigin, int travelFlags,
|
||||
const idAAS2TravelSpeeds* speeds) override;
|
||||
bool RouteToGoalArea(int areaNum, const idVec3& origin,
|
||||
int goalAreaNum, const idVec3& goalOrigin, int travelFlags,
|
||||
int& travelTime,
|
||||
idIndex<short, invalidReachability_t>& reachability) override;
|
||||
bool FindNearestGoal(idAAS2Goal& goal, int areaNum,
|
||||
const idVec3& origin, int travelFlags,
|
||||
idAAS2Callback& callback) override;
|
||||
bool WalkPathToGoal(idAAS2Path& path, int areaNum,
|
||||
const idVec3& origin, int goalAreaNum, const idVec3& goalOrigin,
|
||||
int travelFlags, int obstacleFlags) override;
|
||||
bool ExtendHopPathToGoal(idAAS2Path& path, int areaNum,
|
||||
const idVec3& origin, int goalAreaNum, const idVec3& goalOrigin,
|
||||
int travelFlags, int obstacleFlags,
|
||||
const idAAS2HopPathParms& parms) override;
|
||||
void ShowWalkPath(int areaNum, const idVec3& origin, int goalAreaNum,
|
||||
const idVec3& goalOrigin, int travelFlags, int obstacleFlags,
|
||||
int contentMask, int debugFlags, int treeNum) const override;
|
||||
void ShowHopPath(int areaNum, const idVec3& origin, int goalAreaNum,
|
||||
const idVec3& goalOrigin, int travelFlags, int obstacleFlags,
|
||||
const idAAS2HopPathParms& parms) const override;
|
||||
void ShowOptimizedWalkPath(int areaNum, const idVec3& origin,
|
||||
int goalAreaNum, const idVec3& goalOrigin, int travelFlags,
|
||||
int obstacleFlags) const;
|
||||
void DrawEdge(int edgeNum, bool arrow, bool text,
|
||||
bool highlight) const override;
|
||||
void DrawReachability(idIndex<short, invalidReachability_t> reachability,
|
||||
const aas2Reachability_t& reach, const char* label) const override;
|
||||
void DrawArea(int areaNum, bool showNumbers, bool showEdges,
|
||||
bool showReachabilities, bool showFloor, bool showBounds,
|
||||
bool highlight) const override;
|
||||
float GetGroundSpeedMultiplier() const override {
|
||||
return groundSpeedMultiplier;
|
||||
}
|
||||
bool GetOptimizedWalkPath(int areaNum, const idVec3& origin,
|
||||
int goalAreaNum, const idVec3& goalOrigin, int travelFlags,
|
||||
int obstacleFlags, idList<idVec3, 5>& points) override;
|
||||
int GetNumAreas() const override;
|
||||
const idPlane* GetPlane(int index) const override;
|
||||
const aas2Area_t* GetArea(int areaNum) const override;
|
||||
const aas2Reachability_t* GetReachability(
|
||||
idIndex<short, invalidReachability_t> reachability) const override;
|
||||
bool GetPath(const idVec3& origin, const idVec3& goalOrigin,
|
||||
int areaNum, int goalAreaNum, int travelFlags,
|
||||
idAAS2PathPoint* points, int maxPoints, int& numPoints) override;
|
||||
void ClipGridToAreas(const idVec3& startOrigin, int startAreaNum,
|
||||
int travelFlags, unsigned int cellSize, int dimension,
|
||||
std::uint8_t* reachable) const override;
|
||||
int GetInvalidTravelFlags() const override { return AAS_TFL_INVALID; }
|
||||
int GetTraversalsForReachability(
|
||||
idIndex<short, invalidReachability_t> reachability,
|
||||
idList<int, 5>& traversals) const override;
|
||||
int GetTraversalsForInteractableEntity(
|
||||
idIndex<short, invalidAASInteractionEntIndex_t> entity,
|
||||
idList<int, 5>& traversals) const override;
|
||||
int FindBestTraversalForReachability(
|
||||
idIndex<short, invalidReachability_t> reachability,
|
||||
const idVec3& origin, const idVec3& goalOrigin,
|
||||
idVec3& traversalStart, idVec3& traversalEnd,
|
||||
idIndex<short, invalidAASDependencyIndex_t>& dependency,
|
||||
int travelFlags, bool allowDisabled) const override;
|
||||
const aas2Traversal_t* GetTraversal(int traversalNum) const override;
|
||||
bool GetAASAnim(idIndex<short, invalidAASAnimIndex_t> index,
|
||||
const aas2AnimName_t** name) const override;
|
||||
idIndex<short, invalidAASAnimIndex_t> GetAASAnimIndexByName(
|
||||
const char* name) const override;
|
||||
bool GetAASDependency(idIndex<short, invalidAASDependencyIndex_t> index,
|
||||
const aas2DependencyName_t** name) const override;
|
||||
idIndex<short, invalidAASDependencyIndex_t>
|
||||
GetAASDependencyIndexByName(const char* name) const override;
|
||||
bool GetAASInteractionEntity(
|
||||
idIndex<short, invalidAASInteractionEntIndex_t> index,
|
||||
const aas2InteractionEntityName_t** name) const override;
|
||||
idIndex<short, invalidAASInteractionEntIndex_t>
|
||||
GetAASInteractionEntityIndexByName(const char* name) const override;
|
||||
bool GetAASTraversalNameIndex(
|
||||
idIndex<short, invalidAASTraversalNameIndex_t> index,
|
||||
const aas2TraversalEntityName_t** name) const override;
|
||||
idIndex<short, invalidAASTraversalNameIndex_t>
|
||||
GetAASTraversalNameIndexByName(const char* name) const override;
|
||||
int GetAASTraversalIndexByNameIndex(
|
||||
idIndex<short, invalidAASTraversalNameIndex_t> index) const override;
|
||||
bool SetTraversalFlag(int index, int flags) override;
|
||||
bool ClearTraversalFlag(int index, int flags) override;
|
||||
const std::uint8_t* GetObstaclePVS(int areaNum) override;
|
||||
int GetObstaclePVSWallEdges(int areaNum, int edgeFlags,
|
||||
int* outputEdges, int maxEdges) const override;
|
||||
int GetObstaclePVSClipModels(int areaNum, idClipModel** models,
|
||||
int maxModels) override;
|
||||
void GetObstaclePVSObstacles(int areaNum, int obstacleFlags,
|
||||
obstacleVertex_t* vertices, int* numVertices,
|
||||
obstacleEdge_t* edges, int* numEdges, idClipModel** models,
|
||||
int* numModels, obstacleRoute_t* route) override;
|
||||
void FreeClipLinks() override;
|
||||
idAASClipLink* LinkClipModel(idClipModel* model) override;
|
||||
idAASClipLink* UnLinkClipModel(idAASClipLink* link) override;
|
||||
int GetNumCoverPoints() const override;
|
||||
const aas2Cover_t* GetCoverPoint(int index) const override;
|
||||
int GetNumAreaCoverPoints(int areaNum) const override;
|
||||
int GetAreaCoverPoints(int areaNum, int* points, int maxPoints,
|
||||
int* flags) const override;
|
||||
int FindCoverPoints(int areaNum, const idVec3& origin, float radius,
|
||||
int coverFlags, int excludeFlags, int maxPoints, int* points,
|
||||
int team, int* areaFlags) const override;
|
||||
int GetNumChokePoints() const override;
|
||||
const aas2ChokePoint_t* GetChokePoint(int index) const override;
|
||||
int GetNumHintNodes() const override;
|
||||
const aas2HintNode_t* GetHintNode(int index) const override;
|
||||
bool GetAreaCells(int areaNum, int subdivisionSize, int* xCells,
|
||||
int* yCells) const override;
|
||||
bool WorldPosToCellPos(int areaNum, int subdivisionSize,
|
||||
const idVec3& worldPos, int* xCell, int* yCell) const override;
|
||||
bool CellPosToWorldPos(int areaNum, int subdivisionSize, int xCell,
|
||||
int yCell, idVec3& worldPos, bool center) const override;
|
||||
void DrawCoverPoint(const aas2Cover_t& cover, int index) const override;
|
||||
int GetNumTrees() const override;
|
||||
idIndex<int, invalidAASTree_t> GetTree(int index) const override;
|
||||
idIndex<int, invalidAASTree_t> GetTreeForFloorNormal(
|
||||
const idVec3& floorNormal) const override;
|
||||
const idVec3* GetFloorNormalForArea(int areaNum) const override;
|
||||
const idVec3* GetFloorNormalForTree(
|
||||
idIndex<int, invalidAASTree_t> tree) const override;
|
||||
int GetNumAreasInTree(
|
||||
idIndex<int, invalidAASTree_t> tree) const override;
|
||||
|
||||
void SetupRouting();
|
||||
void ShutdownRouting();
|
||||
void RoutingStats() const;
|
||||
void RemoveRoutingCacheUsingArea(int areaNum);
|
||||
void LinkCache(idAASRoutingCache* cache);
|
||||
void UnlinkCache(idAASRoutingCache* cache);
|
||||
int CalcTravelTime(const idVec3& start, const idVec3& end,
|
||||
int travelFlags) const;
|
||||
int AreaTravelTime(int areaNum, const idVec3& start,
|
||||
const idVec3& end) const;
|
||||
void CalculateAreaTravelTimes();
|
||||
void SetupRoutingCache();
|
||||
void DeleteClusterCache(int clusterNum);
|
||||
void DeletePortalCache();
|
||||
void ShutdownRoutingCache();
|
||||
bool ChangeAreaTravelFlags_r(int nodeNum, const idBounds& bounds,
|
||||
int areaFlags, int travelFlags, bool set);
|
||||
idIndex<short, invalidReachability_t> GetAreaReachability(
|
||||
int areaNum, int reachabilityNum) const;
|
||||
int ClusterAreaNum(int clusterNum, int areaNum) const;
|
||||
void DeleteOldestCache();
|
||||
void UpdateAreaRoutingCache(idAASRoutingCache* cache);
|
||||
idAASRoutingCache* GetAreaRoutingCache(int clusterNum, int areaNum,
|
||||
int travelFlags);
|
||||
void UpdatePortalRoutingCache(idAASRoutingCache* cache);
|
||||
idAASRoutingCache* GetPortalRoutingCache(int clusterNum, int areaNum,
|
||||
int travelFlags);
|
||||
bool GetClusterRoute(int startAreaNum, const idVec3& startOrigin,
|
||||
int startClusterNum, int goalAreaNum, int travelFlags,
|
||||
int& travelTime,
|
||||
idIndex<short, invalidReachability_t>& bestReachability);
|
||||
bool HopPathIsValid(int startAreaNum, const idVec3& startOrigin,
|
||||
int goalAreaNum, const idVec3& goalOrigin,
|
||||
const idAAS2HopPathParms& parms) const;
|
||||
bool WalkPathIsValid(int startAreaNum, const idVec3& startOrigin,
|
||||
int goalAreaNum, const idVec3& goalOrigin, int travelFlags,
|
||||
int& endAreaNum, aas2EdgeCrossed_t& firstEdge) const;
|
||||
void SubSampleWalkPath(int startAreaNum, const idVec3& startOrigin,
|
||||
int pathAreaNum, const idVec3& pathStart, const idVec3& pathEnd,
|
||||
int travelFlags, idVec3& endPos, int& endAreaNum,
|
||||
aas2EdgeCrossed_t& firstEdge) const;
|
||||
void SetupObstaclePVS();
|
||||
void ShutdownObstaclePVS();
|
||||
void DecompressObstaclePVS(int areaNum, std::uint8_t* pvs,
|
||||
int pvsSize) const;
|
||||
void GetObstaclePVSObstaclesInternal(int areaNum, int edgeFlags,
|
||||
obstacleVertex_t* vertices, int& numVertices,
|
||||
obstacleEdge_t* edges, int& numEdges, idClipModel** models,
|
||||
int& numModels, obstacleRoute_t* route) const;
|
||||
void FindCoverPoints_Internal(int startAreaNum, const idVec3& origin,
|
||||
const idBounds& bounds, float maxDistSqr, int requiredFlags,
|
||||
int excludeFlags, int travelFlags, int* coverPoints, int maxCover,
|
||||
int& numCover) const;
|
||||
|
||||
idAAS2File* file;
|
||||
idStr name;
|
||||
bool defaulted;
|
||||
idAASRoutingCache*** areaCacheIndex;
|
||||
int areaCacheIndexSize;
|
||||
idAASRoutingCache** portalCacheIndex;
|
||||
int portalCacheIndexSize;
|
||||
idRoutingUpdate* areaUpdate;
|
||||
idRoutingUpdate* portalUpdate;
|
||||
std::uint16_t* goalAreaTravelTimes;
|
||||
std::uint16_t* areaTravelTimes;
|
||||
int numAreaTravelTimes;
|
||||
idAASRoutingCache* cacheListStart;
|
||||
idAASRoutingCache* cacheListEnd;
|
||||
int totalCacheMemory;
|
||||
float groundSpeedMultiplier;
|
||||
float waterSpeedMultiplier;
|
||||
int numObstaclePVSBytes;
|
||||
std::uint8_t* obstaclePVS;
|
||||
int obstaclePVSAreaNum;
|
||||
idAASClipLink** areaClipModels;
|
||||
idBlockAlloc<idAASClipLink, 128, 37> clipModelLinkAllocator;
|
||||
idObstacleAvoidance obstacleAvoidance;
|
||||
};
|
||||
|
||||
#if INTPTR_MAX == INT32_MAX
|
||||
static_assert(sizeof(idAASClipLink) == 24,
|
||||
"Recovered idAASClipLink ABI changed");
|
||||
static_assert(sizeof(idAAS2Local) == 172,
|
||||
"Recovered idAAS2Local ABI changed");
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,345 @@
|
||||
#include "gamelib/aas2/aas2_local.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
|
||||
namespace {
|
||||
|
||||
float LengthSqr(const idVec3& value) {
|
||||
return value.x * value.x + value.y * value.y + value.z * value.z;
|
||||
}
|
||||
|
||||
bool TraversalClassAllowed(const aas2Traversal_t& traversal,
|
||||
const int travelFlags) {
|
||||
const std::uint32_t flags = traversal.flags;
|
||||
return ((flags & 0x02000000u) != 0
|
||||
&& (travelFlags & AAS_TFL_TRAVERSAL_CLASS_A) != 0)
|
||||
|| ((flags & 0x04000000u) != 0
|
||||
&& (travelFlags & AAS_TFL_TRAVERSAL_CLASS_B) != 0)
|
||||
|| ((flags & 0x08000000u) != 0
|
||||
&& (travelFlags & AAS_TFL_TRAVERSAL_CLASS_C) != 0)
|
||||
|| ((flags & 0x10000000u) != 0
|
||||
&& (travelFlags & AAS_TFL_TRAVERSAL_CLASS_D) != 0)
|
||||
|| ((flags & 0x20000000u) != 0
|
||||
&& (travelFlags & AAS_TFL_TRAVERSAL_CLASS_E) != 0);
|
||||
}
|
||||
|
||||
int PathTypeForTravelFlags(const std::uint32_t flags) {
|
||||
if ((flags & AAS_TFL_WALKOFFLEDGE) != 0) return 1;
|
||||
if ((flags & AAS_TFL_BARRIERJUMP) != 0) return 2;
|
||||
if ((flags & AAS_TFL_LADDER) != 0) return 5;
|
||||
if ((flags & AAS_TFL_ELEVATOR) != 0) return 8;
|
||||
if ((flags & AAS_TFL_TELEPORT) != 0) return 9;
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int idAAS2Local::GetTraversalsForReachability(
|
||||
const idIndex<short, invalidReachability_t> reachability,
|
||||
idList<int, 5>& traversals) const {
|
||||
return file != nullptr
|
||||
? file->GetTraversalsForReachability(reachability, traversals) : 0;
|
||||
}
|
||||
|
||||
int idAAS2Local::GetTraversalsForInteractableEntity(
|
||||
const idIndex<short, invalidAASInteractionEntIndex_t> entity,
|
||||
idList<int, 5>& traversals) const {
|
||||
return file != nullptr
|
||||
? file->GetTraversalsForInteractableEntity(entity, traversals) : 0;
|
||||
}
|
||||
|
||||
bool idAAS2Local::HopPathIsValid(const int startAreaNum,
|
||||
const idVec3& startOrigin, const int goalAreaNum,
|
||||
const idVec3& goalOrigin, const idAAS2HopPathParms& parms) const {
|
||||
if (file == nullptr || startAreaNum <= 0 || goalAreaNum <= 0
|
||||
|| startAreaNum >= file->areas.Num()
|
||||
|| goalAreaNum >= file->areas.Num()
|
||||
|| (file->areas[goalAreaNum].flags & AAS_AREA_OUTSIDE) == 0) {
|
||||
return false;
|
||||
}
|
||||
const idVec3* const normal = file->GetFloorNormalForArea(startAreaNum);
|
||||
if (normal == nullptr) return false;
|
||||
const idVec3 delta = goalOrigin - startOrigin;
|
||||
const float vertical = delta.x * normal->x + delta.y * normal->y
|
||||
+ delta.z * normal->z;
|
||||
const idVec3 planar = delta - *normal * vertical;
|
||||
if (LengthSqr(planar) > parms.maxDistance * parms.maxDistance) {
|
||||
return false;
|
||||
}
|
||||
idVec3 points[512];
|
||||
aas2TraceHeight_t trace{512, 0, points};
|
||||
file->TraceHeight(file->GetTreeForArea(startAreaNum).Get(), trace,
|
||||
startOrigin, goalOrigin);
|
||||
if (trace.numPoints >= 512) return false;
|
||||
for (int index = 1; index < trace.numPoints; ++index) {
|
||||
const idVec3 pointDelta = points[index] - startOrigin;
|
||||
const float pointVertical = pointDelta.x * normal->x
|
||||
+ pointDelta.y * normal->y + pointDelta.z * normal->z;
|
||||
const idVec3 pointPlanar = pointDelta - *normal * pointVertical;
|
||||
float maximumHeight = std::sqrt(LengthSqr(pointPlanar))
|
||||
* parms.maxSlope;
|
||||
maximumHeight = (std::max)(parms.minHeight,
|
||||
(std::min)(parms.maxHeight, maximumHeight));
|
||||
if (pointVertical > maximumHeight) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool idAAS2Local::ExtendHopPathToGoal(idAAS2Path& path,
|
||||
const int startAreaNum, const idVec3& startOrigin,
|
||||
const int goalAreaNum, const idVec3& goalOrigin,
|
||||
const int travelFlags, const int obstacleFlags,
|
||||
const idAAS2HopPathParms& parms) {
|
||||
if (file == nullptr) return false;
|
||||
if (path.moveGoal.x == goalOrigin.x && path.moveGoal.y == goalOrigin.y
|
||||
&& path.moveGoal.z == goalOrigin.z) {
|
||||
return HopPathIsValid(startAreaNum, startOrigin,
|
||||
path.moveAreaNum, path.moveGoal, parms);
|
||||
}
|
||||
if (HopPathIsValid(startAreaNum, startOrigin, goalAreaNum,
|
||||
goalOrigin, parms)) {
|
||||
path = idAAS2Path();
|
||||
path.type = 0;
|
||||
path.moveGoal = goalOrigin;
|
||||
path.moveAreaNum = goalAreaNum;
|
||||
path.obstacleGoal = goalOrigin;
|
||||
path.obstacleAreaNum = goalAreaNum;
|
||||
path.travelTime = AreaTravelTime(startAreaNum,
|
||||
startOrigin, goalOrigin);
|
||||
path.pathMaxHeight = (std::max)(0.0f,
|
||||
goalOrigin.z - startOrigin.z);
|
||||
path.obstacleRoute.numAreas = 1;
|
||||
path.obstacleRoute.areas[0].areaNum = startAreaNum;
|
||||
path.obstacleRoute.areas[0].start = startOrigin;
|
||||
path.obstacleRoute.areas[0].end = goalOrigin;
|
||||
return true;
|
||||
}
|
||||
if (!WalkPathToGoal(path, startAreaNum, startOrigin, goalAreaNum,
|
||||
goalOrigin, travelFlags, obstacleFlags)) return false;
|
||||
return HopPathIsValid(startAreaNum, startOrigin, path.moveAreaNum,
|
||||
path.moveGoal, parms);
|
||||
}
|
||||
|
||||
bool idAAS2Local::GetPath(const idVec3& origin,
|
||||
const idVec3& goalOrigin, const int areaNum,
|
||||
const int goalAreaNum, const int travelFlags,
|
||||
idAAS2PathPoint* const points, const int maxPoints,
|
||||
int& numPoints) {
|
||||
numPoints = 0;
|
||||
if (file == nullptr || points == nullptr || maxPoints <= 0
|
||||
|| areaNum <= 0 || goalAreaNum <= 0) return false;
|
||||
points[0].areaNum = areaNum;
|
||||
points[0].origin = origin;
|
||||
points[0].nextReachIndex.Invalidate();
|
||||
points[0].travelTime = 0;
|
||||
numPoints = 1;
|
||||
int currentArea = areaNum;
|
||||
idVec3 currentOrigin = origin;
|
||||
int cumulativeTime = 0;
|
||||
while (currentArea != goalAreaNum && numPoints < maxPoints) {
|
||||
int routeTime = 0;
|
||||
idIndex<short, invalidReachability_t> reachIndex;
|
||||
if (!RouteToGoalArea(currentArea, currentOrigin, goalAreaNum,
|
||||
goalOrigin, travelFlags, routeTime, reachIndex)
|
||||
|| !reachIndex.IsValid()) break;
|
||||
const int reachNum = reachIndex.Get();
|
||||
if (reachNum < 0 || reachNum >= file->reachabilities.Num()) break;
|
||||
const aas2Reachability_t& reach = file->reachabilities[reachNum];
|
||||
points[numPoints - 1].nextReachIndex = reachIndex;
|
||||
cumulativeTime += reach.travelTime
|
||||
+ AreaTravelTime(currentArea, currentOrigin, reach.Start());
|
||||
points[numPoints].areaNum = reach.toAreaNum;
|
||||
points[numPoints].origin = reach.End();
|
||||
points[numPoints].nextReachIndex.Invalidate();
|
||||
points[numPoints].travelTime = cumulativeTime;
|
||||
currentArea = reach.toAreaNum;
|
||||
currentOrigin = reach.End();
|
||||
++numPoints;
|
||||
}
|
||||
if (currentArea != goalAreaNum) return false;
|
||||
if (numPoints < maxPoints) {
|
||||
points[numPoints].areaNum = goalAreaNum;
|
||||
points[numPoints].origin = goalOrigin;
|
||||
points[numPoints].nextReachIndex.Invalidate();
|
||||
points[numPoints].travelTime = cumulativeTime
|
||||
+ AreaTravelTime(goalAreaNum, currentOrigin, goalOrigin);
|
||||
++numPoints;
|
||||
} else {
|
||||
points[numPoints - 1].origin = goalOrigin;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool idAAS2Local::WalkPathIsValid(const int startAreaNum,
|
||||
const idVec3& startOrigin, const int goalAreaNum,
|
||||
const idVec3& goalOrigin, const int travelFlags,
|
||||
int& endAreaNum, aas2EdgeCrossed_t& firstEdge) const {
|
||||
int areas[128] = {};
|
||||
idIndex<short, invalidReachability_t> reachIndices[128];
|
||||
aas2TraceFloor_t trace{};
|
||||
trace.maxAreas = 128;
|
||||
trace.areas = areas;
|
||||
trace.maxReachIndices = 128;
|
||||
trace.reachIndices = reachIndices;
|
||||
if (!TraceFloor(trace, startOrigin, startAreaNum, goalOrigin,
|
||||
travelFlags, true, false, true)) {
|
||||
endAreaNum = trace.lastAreaNum;
|
||||
firstEdge = trace.firstEdge;
|
||||
return false;
|
||||
}
|
||||
endAreaNum = trace.lastAreaNum;
|
||||
firstEdge = trace.firstEdge;
|
||||
return trace.lastAreaNum == goalAreaNum;
|
||||
}
|
||||
|
||||
void idAAS2Local::SubSampleWalkPath(const int startAreaNum,
|
||||
const idVec3& startOrigin, const int pathAreaNum,
|
||||
const idVec3& pathStart, const idVec3& pathEnd,
|
||||
const int travelFlags, idVec3& endPos, int& endAreaNum,
|
||||
aas2EdgeCrossed_t& firstEdge) const {
|
||||
endPos = pathStart;
|
||||
endAreaNum = pathAreaNum;
|
||||
firstEdge = aas2EdgeCrossed_t{};
|
||||
const idVec3 delta = pathEnd - pathStart;
|
||||
float validFraction = 0.0f;
|
||||
for (int sample = 1; sample <= 8; ++sample) {
|
||||
const float fraction = float(sample) / 8.0f;
|
||||
const idVec3 candidate = pathStart + delta * fraction;
|
||||
int candidateArea = pathAreaNum;
|
||||
aas2EdgeCrossed_t candidateEdge{};
|
||||
if (!WalkPathIsValid(startAreaNum, startOrigin, pathAreaNum,
|
||||
candidate, travelFlags, candidateArea, candidateEdge)) break;
|
||||
validFraction = fraction;
|
||||
endPos = candidate;
|
||||
endAreaNum = candidateArea;
|
||||
if (firstEdge.edgeNum == 0 && candidateEdge.edgeNum != 0) {
|
||||
firstEdge = candidateEdge;
|
||||
}
|
||||
}
|
||||
if (validFraction == 0.0f) endPos = pathStart;
|
||||
}
|
||||
|
||||
bool idAAS2Local::GetOptimizedWalkPath(const int startAreaNum,
|
||||
const idVec3& startOrigin, const int goalAreaNum,
|
||||
const idVec3& goalOrigin, const int travelFlags,
|
||||
const int, idList<idVec3, 5>& path) {
|
||||
path.Clear();
|
||||
idAAS2PathPoint rawPath[256];
|
||||
int count = 0;
|
||||
if (!GetPath(startOrigin, goalOrigin, startAreaNum, goalAreaNum,
|
||||
travelFlags, rawPath, 256, count)) return false;
|
||||
int anchor = 0;
|
||||
path.Append(rawPath[0].origin);
|
||||
while (anchor < count - 1) {
|
||||
int farthest = anchor + 1;
|
||||
for (int candidate = count - 1; candidate > anchor + 1;
|
||||
--candidate) {
|
||||
int endArea = rawPath[anchor].areaNum;
|
||||
aas2EdgeCrossed_t edge{};
|
||||
if (WalkPathIsValid(rawPath[anchor].areaNum,
|
||||
rawPath[anchor].origin, rawPath[candidate].areaNum,
|
||||
rawPath[candidate].origin, travelFlags, endArea, edge)) {
|
||||
farthest = candidate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
path.Append(rawPath[farthest].origin);
|
||||
anchor = farthest;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool idAAS2Local::WalkPathToGoal(idAAS2Path& path,
|
||||
const int startAreaNum, const idVec3& startOrigin,
|
||||
const int goalAreaNum, const idVec3& goalOrigin,
|
||||
const int travelFlags, const int) {
|
||||
path = idAAS2Path();
|
||||
path.type = 0;
|
||||
path.moveGoal = startOrigin;
|
||||
path.moveAreaNum = startAreaNum;
|
||||
path.obstacleGoal = startOrigin;
|
||||
path.obstacleAreaNum = startAreaNum;
|
||||
path.obstacleRoute.numAreas = 1;
|
||||
path.obstacleRoute.areas[0].areaNum = startAreaNum;
|
||||
path.obstacleRoute.areas[0].start = startOrigin;
|
||||
path.obstacleRoute.areas[0].end = startOrigin;
|
||||
if (file == nullptr) return false;
|
||||
if (startAreaNum == goalAreaNum) {
|
||||
path.moveGoal = goalOrigin;
|
||||
path.obstacleGoal = goalOrigin;
|
||||
path.travelTime = AreaTravelTime(startAreaNum,
|
||||
startOrigin, goalOrigin);
|
||||
path.obstacleRoute.areas[0].end = goalOrigin;
|
||||
path.pathMaxHeight = (std::max)(0.0f,
|
||||
goalOrigin.z - startOrigin.z);
|
||||
return true;
|
||||
}
|
||||
int routeTime = 0;
|
||||
idIndex<short, invalidReachability_t> reachIndex;
|
||||
if (!RouteToGoalArea(startAreaNum, startOrigin, goalAreaNum,
|
||||
goalOrigin, travelFlags, routeTime, reachIndex)
|
||||
|| !reachIndex.IsValid()) return false;
|
||||
const aas2Reachability_t* const reach = GetReachability(reachIndex);
|
||||
if (reach == nullptr) return false;
|
||||
path.reachIndex = reachIndex;
|
||||
path.type = PathTypeForTravelFlags(reach->travelFlags);
|
||||
path.moveGoal = reach->Start();
|
||||
path.moveAreaNum = startAreaNum;
|
||||
path.obstacleGoal = path.moveGoal;
|
||||
path.obstacleAreaNum = startAreaNum;
|
||||
path.travelTime = routeTime;
|
||||
path.obstacleRoute.areas[0].end = path.moveGoal;
|
||||
path.pathMaxHeight = (std::max)(0.0f,
|
||||
path.moveGoal.z - startOrigin.z);
|
||||
|
||||
idVec3 traversalStart;
|
||||
idVec3 traversalEnd;
|
||||
idIndex<short, invalidAASDependencyIndex_t> dependency;
|
||||
const int traversal = FindBestTraversalForReachability(reachIndex,
|
||||
startOrigin, goalOrigin, traversalStart, traversalEnd, dependency,
|
||||
travelFlags, false);
|
||||
if (traversal >= 0) {
|
||||
path.type = 9;
|
||||
path.traversalIndex = traversal;
|
||||
path.traversalStart = traversalStart;
|
||||
path.traversalEnd = traversalEnd;
|
||||
path.dependencyIndex = dependency;
|
||||
path.moveGoal = traversalStart;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int idAAS2Local::FindBestTraversalForReachability(
|
||||
const idIndex<short, invalidReachability_t> reachability,
|
||||
const idVec3& currentPoint, const idVec3& goalPoint,
|
||||
idVec3& traversalStart, idVec3& traversalEnd,
|
||||
idIndex<short, invalidAASDependencyIndex_t>& dependency,
|
||||
const int travelFlags, const bool useClosestPointOnFail) const {
|
||||
idList<int, 5> traversals;
|
||||
const int count = GetTraversalsForReachability(reachability, traversals);
|
||||
traversalStart.Zero();
|
||||
traversalEnd.Zero();
|
||||
dependency.Invalidate();
|
||||
int bestIndex = -1;
|
||||
float bestDistance = (std::numeric_limits<float>::max)();
|
||||
for (int index = 0; index < count; ++index) {
|
||||
const int traversalIndex = traversals[index];
|
||||
const aas2Traversal_t* const traversal = GetTraversal(traversalIndex);
|
||||
if (traversal == nullptr) continue;
|
||||
const bool enabled = (traversal->flags & 1u) != 0;
|
||||
if ((!enabled && !useClosestPointOnFail)
|
||||
|| !TraversalClassAllowed(*traversal, travelFlags)) continue;
|
||||
const float distance = LengthSqr(traversal->startPoint - currentPoint)
|
||||
+ LengthSqr(traversal->endPoint - goalPoint);
|
||||
if (distance < bestDistance) {
|
||||
bestDistance = distance;
|
||||
bestIndex = traversalIndex;
|
||||
traversalStart = traversal->startPoint;
|
||||
traversalEnd = traversal->endPoint;
|
||||
dependency = traversal->dependencyIndex;
|
||||
}
|
||||
}
|
||||
return bestIndex;
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
#include "gamelib/aas2/aas2_local.h"
|
||||
|
||||
#include "gamelib/physics/clipmodel.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr int MAX_OBSTACLE_VERTICES = 0x300;
|
||||
constexpr int MAX_OBSTACLE_EDGES = 0x200;
|
||||
constexpr int MAX_OBSTACLE_MODELS = 128;
|
||||
|
||||
bool PVSIncludes(const std::uint8_t* const pvs, const int areaNum) {
|
||||
return pvs != nullptr && areaNum >= 0
|
||||
&& (pvs[areaNum >> 3] & (1u << (areaNum & 7))) != 0;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void idAAS2Local::ShutdownObstaclePVS() {
|
||||
FreeClipLinks();
|
||||
delete[] obstaclePVS;
|
||||
delete[] areaClipModels;
|
||||
numObstaclePVSBytes = 0;
|
||||
obstaclePVS = nullptr;
|
||||
areaClipModels = nullptr;
|
||||
obstaclePVSAreaNum = 0;
|
||||
}
|
||||
|
||||
int idAAS2Local::GetObstaclePVSWallEdges(const int areaNum,
|
||||
const int edgeFlags, int* const edges, const int maxEdges) const {
|
||||
return file != nullptr ? file->GetObstaclePVSWallEdges(areaNum,
|
||||
edgeFlags, edges, maxEdges) : 0;
|
||||
}
|
||||
|
||||
void idAAS2Local::SetupObstaclePVS() {
|
||||
ShutdownObstaclePVS();
|
||||
if (file == nullptr || file->areas.IsEmpty()) return;
|
||||
numObstaclePVSBytes = (file->areas.Num() + 7) / 8;
|
||||
obstaclePVS = new std::uint8_t[numObstaclePVSBytes];
|
||||
std::memset(obstaclePVS, 0, numObstaclePVSBytes);
|
||||
areaClipModels = new idAASClipLink*[file->areas.Num()]();
|
||||
}
|
||||
|
||||
void idAAS2Local::DecompressObstaclePVS(const int areaNum,
|
||||
std::uint8_t* const pvs, const int pvsSize) const {
|
||||
if (pvs == nullptr || pvsSize <= 0) return;
|
||||
std::memset(pvs, 0, pvsSize);
|
||||
if (file == nullptr || areaNum <= 0 || areaNum >= file->areas.Num())
|
||||
return;
|
||||
std::uint32_t offset = file->areas[areaNum].obstaclePVSOffset;
|
||||
int visibleArea = 0;
|
||||
while (offset < static_cast<std::uint32_t>(file->obstaclePVS.Num())
|
||||
&& visibleArea < file->areas.Num()) {
|
||||
const std::uint8_t encoded = file->obstaclePVS[offset++];
|
||||
if ((encoded & 0x80u) != 0) {
|
||||
int skip = encoded & 0x3Fu;
|
||||
if ((encoded & 0x40u) != 0
|
||||
&& offset < static_cast<std::uint32_t>(
|
||||
file->obstaclePVS.Num())) {
|
||||
skip |= static_cast<int>(file->obstaclePVS[offset++]) << 6;
|
||||
}
|
||||
visibleArea += skip + 1;
|
||||
continue;
|
||||
}
|
||||
for (int bit = 0; bit < 7 && visibleArea < file->areas.Num();
|
||||
++bit, ++visibleArea) {
|
||||
if ((encoded & (1u << bit)) != 0
|
||||
&& (visibleArea >> 3) < pvsSize) {
|
||||
pvs[visibleArea >> 3] |= static_cast<std::uint8_t>(
|
||||
1u << (visibleArea & 7));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const std::uint8_t* idAAS2Local::GetObstaclePVS(const int areaNum) {
|
||||
if (obstaclePVS == nullptr) return nullptr;
|
||||
if (areaNum != obstaclePVSAreaNum) {
|
||||
DecompressObstaclePVS(areaNum, obstaclePVS, numObstaclePVSBytes);
|
||||
obstaclePVSAreaNum = areaNum;
|
||||
}
|
||||
return obstaclePVS;
|
||||
}
|
||||
|
||||
void idAAS2Local::FreeClipLinks() {
|
||||
if (areaClipModels != nullptr && file != nullptr) {
|
||||
for (int areaNum = 0; areaNum < file->areas.Num(); ++areaNum)
|
||||
areaClipModels[areaNum] = nullptr;
|
||||
}
|
||||
clipModelLinkAllocator.Shutdown();
|
||||
}
|
||||
|
||||
idAASClipLink* idAAS2Local::UnLinkClipModel(idAASClipLink* link) {
|
||||
while (link != nullptr) {
|
||||
idAASClipLink* const nextArea = link->nextArea;
|
||||
if (link->prevClip != nullptr)
|
||||
link->prevClip->nextClip = link->nextClip;
|
||||
else if (areaClipModels != nullptr && link->areaNum >= 0)
|
||||
areaClipModels[link->areaNum] = link->nextClip;
|
||||
if (link->nextClip != nullptr)
|
||||
link->nextClip->prevClip = link->prevClip;
|
||||
clipModelLinkAllocator.Free(link);
|
||||
link = nextArea;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
idAASClipLink* idAAS2Local::LinkClipModel(idClipModel* const model) {
|
||||
if (model == nullptr || file == nullptr || areaClipModels == nullptr)
|
||||
return nullptr;
|
||||
idBounds expanded = model->GetAbsBounds();
|
||||
expanded[0].x -= file->settings.boundingBox[1].x;
|
||||
expanded[0].y -= file->settings.boundingBox[1].y;
|
||||
expanded[0].z -= file->settings.boundingBox[1].z;
|
||||
expanded[1].x -= file->settings.boundingBox[0].x;
|
||||
expanded[1].y -= file->settings.boundingBox[0].y;
|
||||
expanded[1].z -= file->settings.boundingBox[0].z;
|
||||
|
||||
std::vector<int> areas;
|
||||
int areaBuffer[128];
|
||||
for (int tree = 0; tree < file->trees.Num(); ++tree) {
|
||||
const int count = file->BoundsAreaNums(tree, expanded,
|
||||
areaBuffer, 128);
|
||||
for (int index = 0; index < count; ++index) {
|
||||
const int areaNum = areaBuffer[index];
|
||||
if (areaNum > 0 && areaNum < file->areas.Num()
|
||||
&& std::find(areas.begin(), areas.end(), areaNum)
|
||||
== areas.end()) areas.push_back(areaNum);
|
||||
}
|
||||
}
|
||||
std::sort(areas.begin(), areas.end(), std::greater<int>());
|
||||
idAASClipLink* first = nullptr;
|
||||
for (const int areaNum : areas) {
|
||||
idAASClipLink* const link = clipModelLinkAllocator.Alloc();
|
||||
if (link == nullptr) break;
|
||||
link->clipModel = model;
|
||||
link->areaNum = areaNum;
|
||||
link->prevArea = nullptr;
|
||||
link->nextArea = first;
|
||||
if (first != nullptr) first->prevArea = link;
|
||||
first = link;
|
||||
link->prevClip = nullptr;
|
||||
link->nextClip = areaClipModels[areaNum];
|
||||
if (link->nextClip != nullptr) link->nextClip->prevClip = link;
|
||||
areaClipModels[areaNum] = link;
|
||||
}
|
||||
return first;
|
||||
}
|
||||
|
||||
int idAAS2Local::GetObstaclePVSClipModels(const int areaNum,
|
||||
idClipModel** const models, const int maxModels) {
|
||||
if (models == nullptr || maxModels <= 0 || areaClipModels == nullptr)
|
||||
return 0;
|
||||
const std::uint8_t* const pvs = GetObstaclePVS(areaNum);
|
||||
if (pvs == nullptr) return 0;
|
||||
int count = 0;
|
||||
for (int visibleArea = 1; visibleArea < file->areas.Num()
|
||||
&& count < maxModels; ++visibleArea) {
|
||||
if (!PVSIncludes(pvs, visibleArea)) continue;
|
||||
for (idAASClipLink* link = areaClipModels[visibleArea];
|
||||
link != nullptr && count < maxModels; link = link->nextClip) {
|
||||
bool duplicate = false;
|
||||
for (int index = 0; index < count; ++index)
|
||||
if (models[index] == link->clipModel) duplicate = true;
|
||||
if (!duplicate) models[count++] = link->clipModel;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
void idAAS2Local::GetObstaclePVSObstaclesInternal(const int areaNum,
|
||||
const int edgeFlags, obstacleVertex_t* const vertices,
|
||||
int& numVertices, obstacleEdge_t* const edges, int& numEdges,
|
||||
idClipModel** const models, int& numModels,
|
||||
obstacleRoute_t* const route) const {
|
||||
numVertices = 0;
|
||||
numEdges = 0;
|
||||
numModels = 0;
|
||||
if (file == nullptr) return;
|
||||
int wallEdges[MAX_OBSTACLE_EDGES];
|
||||
const int wallCount = file->GetObstaclePVSWallEdges(areaNum,
|
||||
edgeFlags, wallEdges, MAX_OBSTACLE_EDGES);
|
||||
for (int index = 0; index < wallCount
|
||||
&& numVertices + 2 <= MAX_OBSTACLE_VERTICES
|
||||
&& numEdges < MAX_OBSTACLE_EDGES; ++index) {
|
||||
idVec3 start;
|
||||
idVec3 end;
|
||||
GetEdge(wallEdges[index], start, end);
|
||||
vertices[numVertices].position = start;
|
||||
vertices[numVertices + 1].position = end;
|
||||
obstacleEdge_t& edge = edges[numEdges++];
|
||||
edge.flags = GetEdgeFlags(wallEdges[index]);
|
||||
edge.verts[0] = static_cast<std::int16_t>(numVertices);
|
||||
edge.verts[1] = static_cast<std::int16_t>(numVertices + 1);
|
||||
edge.vertNums[0] = numVertices;
|
||||
edge.vertNums[1] = numVertices + 1;
|
||||
numVertices += 2;
|
||||
}
|
||||
numModels = const_cast<idAAS2Local*>(this)->GetObstaclePVSClipModels(
|
||||
areaNum, models, MAX_OBSTACLE_MODELS);
|
||||
if (route != nullptr && route->numAreas == 0) {
|
||||
route->numAreas = 1;
|
||||
route->areas[0].areaNum = areaNum;
|
||||
route->areas[0].start = AreaCenter(areaNum);
|
||||
route->areas[0].end = route->areas[0].start;
|
||||
}
|
||||
}
|
||||
|
||||
void idAAS2Local::GetObstaclePVSObstacles(const int startAreaNum,
|
||||
const int edgeFlags, obstacleVertex_t* const vertices,
|
||||
int* const numVertices, obstacleEdge_t* const edges,
|
||||
int* const numEdges, idClipModel** const models,
|
||||
int* const numModels, obstacleRoute_t* const route) {
|
||||
if (numVertices == nullptr || numEdges == nullptr
|
||||
|| numModels == nullptr) return;
|
||||
GetObstaclePVSObstaclesInternal(startAreaNum, edgeFlags, vertices,
|
||||
*numVertices, edges, *numEdges, models, *numModels, route);
|
||||
}
|
||||
@@ -0,0 +1,580 @@
|
||||
#include "gamelib/aas2/aas2_local.h"
|
||||
|
||||
#include "idlib/lib_print.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <queue>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr int ROUTE_INFINITY = 0x3FFFFFFF;
|
||||
|
||||
bool ReachAllowed(const aas2Reachability_t& reach, const int travelFlags) {
|
||||
return (reach.travelFlags & AAS_TFL_INVALID) == 0
|
||||
&& (reach.travelFlags & travelFlags) != 0;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
idAASRoutingCache::idAASRoutingCache(const int cacheSize)
|
||||
: type(0), size((std::max)(0, cacheSize)), cluster(0), areaNum(0),
|
||||
travelFlags(0), next(nullptr), prev(nullptr), time_next(nullptr),
|
||||
time_prev(nullptr), startTravelTime(0), reachabilities(nullptr),
|
||||
travelTimes(nullptr) {
|
||||
if (size > 0) {
|
||||
reachabilities = new std::uint8_t[size]();
|
||||
travelTimes = new std::uint16_t[size]();
|
||||
}
|
||||
}
|
||||
|
||||
idAASRoutingCache::~idAASRoutingCache() {
|
||||
delete[] reachabilities;
|
||||
delete[] travelTimes;
|
||||
}
|
||||
|
||||
void idAAS2Local::RoutingStats() const {
|
||||
idLibPrint::Printf("%6d kB routing cache\n", totalCacheMemory >> 10);
|
||||
}
|
||||
|
||||
void idAAS2Local::UnlinkCache(idAASRoutingCache* const cache) {
|
||||
if (cache == nullptr) return;
|
||||
if (cache->time_next != nullptr) cache->time_next->time_prev = cache->time_prev;
|
||||
else cacheListEnd = cache->time_prev;
|
||||
if (cache->time_prev != nullptr) cache->time_prev->time_next = cache->time_next;
|
||||
else cacheListStart = cache->time_next;
|
||||
cache->time_next = nullptr;
|
||||
cache->time_prev = nullptr;
|
||||
}
|
||||
|
||||
void idAAS2Local::LinkCache(idAASRoutingCache* const cache) {
|
||||
if (cache == nullptr) return;
|
||||
if (cache->time_next != nullptr || cache->time_prev != nullptr
|
||||
|| cacheListStart == cache) UnlinkCache(cache);
|
||||
cache->time_prev = cacheListEnd;
|
||||
cache->time_next = nullptr;
|
||||
if (cacheListEnd != nullptr) cacheListEnd->time_next = cache;
|
||||
else cacheListStart = cache;
|
||||
cacheListEnd = cache;
|
||||
}
|
||||
|
||||
int idAAS2Local::CalcTravelTime(const idVec3& start, const idVec3& end,
|
||||
const int travelFlags) const {
|
||||
const idVec3 delta = end - start;
|
||||
const float distance = std::sqrt(delta.x * delta.x + delta.y * delta.y
|
||||
+ delta.z * delta.z);
|
||||
const float multiplier = (travelFlags & AAS_TFL_WATER) != 0
|
||||
? waterSpeedMultiplier : groundSpeedMultiplier;
|
||||
return (std::max)(1, static_cast<int>(multiplier * distance));
|
||||
}
|
||||
|
||||
int idAAS2Local::AreaTravelTime(const int areaNum, const idVec3& start,
|
||||
const idVec3& end) const {
|
||||
return file != nullptr && areaNum > 0 && areaNum < file->areas.Num()
|
||||
? CalcTravelTime(start, end, file->areas[areaNum].travelFlags) : 0;
|
||||
}
|
||||
|
||||
void idAAS2Local::CalculateAreaTravelTimes() {
|
||||
delete[] areaTravelTimes;
|
||||
areaTravelTimes = nullptr;
|
||||
numAreaTravelTimes = 0;
|
||||
if (file == nullptr) return;
|
||||
for (int areaNum = 1; areaNum < file->areas.Num(); ++areaNum) {
|
||||
const aas2Area_t& area = file->areas[areaNum];
|
||||
int count = 0;
|
||||
for (idIndex<short, invalidReachability_t> index = area.reach;
|
||||
index.IsValid();) {
|
||||
const int reachNum = index.Get();
|
||||
if (reachNum < 0 || reachNum >= file->reachabilities.Num()) break;
|
||||
++count;
|
||||
index = file->reachabilities[reachNum].next;
|
||||
}
|
||||
numAreaTravelTimes += count * count;
|
||||
}
|
||||
if (numAreaTravelTimes > 0) {
|
||||
areaTravelTimes = new std::uint16_t[numAreaTravelTimes]();
|
||||
}
|
||||
}
|
||||
|
||||
void idAAS2Local::SetupRoutingCache() {
|
||||
if (file == nullptr) return;
|
||||
areaCacheIndexSize = 0;
|
||||
const int clusterCount = file->clusters.Num();
|
||||
areaCacheIndex = clusterCount > 0
|
||||
? new idAASRoutingCache**[clusterCount]() : nullptr;
|
||||
for (int cluster = 0; cluster < clusterCount; ++cluster) {
|
||||
const int count = (std::max)(0,
|
||||
file->clusters[cluster].numReachableAreas);
|
||||
areaCacheIndexSize += count;
|
||||
areaCacheIndex[cluster] = count > 0
|
||||
? new idAASRoutingCache*[count]() : nullptr;
|
||||
}
|
||||
portalCacheIndexSize = file->areas.Num();
|
||||
portalCacheIndex = portalCacheIndexSize > 0
|
||||
? new idAASRoutingCache*[portalCacheIndexSize]() : nullptr;
|
||||
areaUpdate = file->areas.Num() > 0
|
||||
? new idRoutingUpdate[file->areas.Num()]() : nullptr;
|
||||
portalUpdate = file->portals.Num() >= 0
|
||||
? new idRoutingUpdate[file->portals.Num() + 1]() : nullptr;
|
||||
goalAreaTravelTimes = file->areas.Num() > 0
|
||||
? new std::uint16_t[file->areas.Num()]() : nullptr;
|
||||
cacheListStart = nullptr;
|
||||
cacheListEnd = nullptr;
|
||||
totalCacheMemory = 0;
|
||||
}
|
||||
|
||||
void idAAS2Local::DeleteClusterCache(const int clusterNum) {
|
||||
if (file == nullptr || areaCacheIndex == nullptr || clusterNum < 0
|
||||
|| clusterNum >= file->clusters.Num()) return;
|
||||
const int count = file->clusters[clusterNum].numReachableAreas;
|
||||
for (int slot = 0; slot < count; ++slot) {
|
||||
idAASRoutingCache* cache = areaCacheIndex[clusterNum][slot];
|
||||
areaCacheIndex[clusterNum][slot] = nullptr;
|
||||
while (cache != nullptr) {
|
||||
idAASRoutingCache* const nextCache = cache->next;
|
||||
UnlinkCache(cache);
|
||||
totalCacheMemory -= sizeof(*cache) + 3 * cache->size;
|
||||
delete cache;
|
||||
cache = nextCache;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void idAAS2Local::DeletePortalCache() {
|
||||
if (portalCacheIndex == nullptr) return;
|
||||
for (int slot = 0; slot < portalCacheIndexSize; ++slot) {
|
||||
idAASRoutingCache* cache = portalCacheIndex[slot];
|
||||
portalCacheIndex[slot] = nullptr;
|
||||
while (cache != nullptr) {
|
||||
idAASRoutingCache* const nextCache = cache->next;
|
||||
UnlinkCache(cache);
|
||||
totalCacheMemory -= sizeof(*cache) + 3 * cache->size;
|
||||
delete cache;
|
||||
cache = nextCache;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void idAAS2Local::ShutdownRoutingCache() {
|
||||
if (areaCacheIndex != nullptr && file != nullptr) {
|
||||
for (int cluster = 0; cluster < file->clusters.Num(); ++cluster) {
|
||||
DeleteClusterCache(cluster);
|
||||
delete[] areaCacheIndex[cluster];
|
||||
}
|
||||
}
|
||||
DeletePortalCache();
|
||||
delete[] areaCacheIndex;
|
||||
delete[] portalCacheIndex;
|
||||
delete[] areaUpdate;
|
||||
delete[] portalUpdate;
|
||||
delete[] goalAreaTravelTimes;
|
||||
areaCacheIndex = nullptr;
|
||||
portalCacheIndex = nullptr;
|
||||
areaUpdate = nullptr;
|
||||
portalUpdate = nullptr;
|
||||
goalAreaTravelTimes = nullptr;
|
||||
areaCacheIndexSize = 0;
|
||||
portalCacheIndexSize = 0;
|
||||
cacheListStart = nullptr;
|
||||
cacheListEnd = nullptr;
|
||||
totalCacheMemory = 0;
|
||||
}
|
||||
|
||||
void idAAS2Local::SetupRouting() {
|
||||
if (file == nullptr) return;
|
||||
groundSpeedMultiplier = file->settings.groundSpeed != 0.0f
|
||||
? 100.0f / file->settings.groundSpeed : 1.0f;
|
||||
waterSpeedMultiplier = file->settings.waterSpeed != 0.0f
|
||||
? 100.0f / file->settings.waterSpeed : 1.0f;
|
||||
CalculateAreaTravelTimes();
|
||||
SetupRoutingCache();
|
||||
}
|
||||
|
||||
void idAAS2Local::ShutdownRouting() {
|
||||
delete[] areaTravelTimes;
|
||||
areaTravelTimes = nullptr;
|
||||
numAreaTravelTimes = 0;
|
||||
ShutdownRoutingCache();
|
||||
}
|
||||
|
||||
void idAAS2Local::RemoveRoutingCacheUsingArea(const int) {
|
||||
if (file == nullptr) return;
|
||||
for (int cluster = 0; cluster < file->clusters.Num(); ++cluster) {
|
||||
DeleteClusterCache(cluster);
|
||||
}
|
||||
DeletePortalCache();
|
||||
}
|
||||
|
||||
bool idAAS2Local::ChangeAreaTravelFlags_r(int nodeNum,
|
||||
const idBounds& bounds, const int areaFlags,
|
||||
const int travelFlags, const bool set) {
|
||||
bool changed = false;
|
||||
if (file == nullptr || nodeNum == 0) return false;
|
||||
if (nodeNum < 0) {
|
||||
const int areaNum = -nodeNum;
|
||||
if (areaNum <= 0 || areaNum >= file->areas.Num()) return false;
|
||||
aas2Area_t& area = file->areas[areaNum];
|
||||
if ((area.flags & areaFlags) == 0) return false;
|
||||
const std::uint32_t oldFlags = area.travelFlags;
|
||||
area.travelFlags = set ? oldFlags | travelFlags
|
||||
: oldFlags & ~std::uint32_t(travelFlags);
|
||||
if (area.travelFlags != oldFlags) {
|
||||
RemoveRoutingCacheUsingArea(areaNum);
|
||||
changed = true;
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
if (nodeNum >= file->nodes.Num()) return false;
|
||||
const aas2Node_t& node = file->nodes[nodeNum];
|
||||
if (node.planeNum >= static_cast<std::uint32_t>(file->planes.Num())) {
|
||||
return false;
|
||||
}
|
||||
const int side = bounds.PlaneSide(file->planes[node.planeNum], 0.1f);
|
||||
if (side != 1) changed |= ChangeAreaTravelFlags_r(node.children[0],
|
||||
bounds, areaFlags, travelFlags, set);
|
||||
if (side != 0) changed |= ChangeAreaTravelFlags_r(node.children[1],
|
||||
bounds, areaFlags, travelFlags, set);
|
||||
return changed;
|
||||
}
|
||||
|
||||
bool idAAS2Local::ChangeAreaTravelFlags(const idBounds& bounds,
|
||||
const int areaFlags, const int travelFlags, const bool set) {
|
||||
if (file == nullptr || file->trees.Num() == 0) return false;
|
||||
idBounds expanded;
|
||||
expanded[0] = bounds[0] - file->settings.boundingBox[1];
|
||||
expanded[1] = bounds[1] - file->settings.boundingBox[0];
|
||||
return ChangeAreaTravelFlags_r(file->trees[0].headNode, expanded,
|
||||
areaFlags, travelFlags, set);
|
||||
}
|
||||
|
||||
bool idAAS2Local::ChangeReachabilityTravelFlags(const char* const nameValue,
|
||||
const int travelFlags, const bool set) {
|
||||
if (file == nullptr) return false;
|
||||
const idIndex<short, invalidReachability_t> index =
|
||||
file->FindReachabilityByName(nameValue);
|
||||
return index.IsValid()
|
||||
&& ChangeReachabilityTravelFlags(index, travelFlags, set);
|
||||
}
|
||||
|
||||
bool idAAS2Local::ChangeReachabilityTravelFlags(
|
||||
const idIndex<short, invalidReachability_t> index,
|
||||
const int travelFlags, const bool set) {
|
||||
const int reachNum = index.Get();
|
||||
if (file == nullptr || reachNum < 0
|
||||
|| reachNum >= file->reachabilities.Num()) return false;
|
||||
aas2Reachability_t& reach = file->reachabilities[reachNum];
|
||||
reach.travelFlags = set ? reach.travelFlags | travelFlags
|
||||
: reach.travelFlags & ~std::uint32_t(travelFlags);
|
||||
RemoveRoutingCacheUsingArea(reach.fromAreaNum);
|
||||
RemoveRoutingCacheUsingArea(reach.toAreaNum);
|
||||
return true;
|
||||
}
|
||||
|
||||
idIndex<short, invalidReachability_t> idAAS2Local::GetAreaReachability(
|
||||
const int areaNum, int reachabilityNum) const {
|
||||
if (file == nullptr || areaNum <= 0 || areaNum >= file->areas.Num()) {
|
||||
return idIndex<short, invalidReachability_t>();
|
||||
}
|
||||
idIndex<short, invalidReachability_t> index = file->areas[areaNum].reach;
|
||||
while (index.IsValid() && reachabilityNum-- > 0) {
|
||||
const int value = index.Get();
|
||||
if (value < 0 || value >= file->reachabilities.Num()) {
|
||||
return idIndex<short, invalidReachability_t>();
|
||||
}
|
||||
index = file->reachabilities[value].next;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
int idAAS2Local::ClusterAreaNum(const int clusterNum,
|
||||
const int areaNum) const {
|
||||
if (file == nullptr || areaNum <= 0 || areaNum >= file->areas.Num()) {
|
||||
return 0;
|
||||
}
|
||||
const aas2Area_t& area = file->areas[areaNum];
|
||||
if (area.cluster > 0) return area.clusterAreaNum;
|
||||
const int portalNum = -area.cluster;
|
||||
if (portalNum <= 0 || portalNum >= file->portals.Num()) return 0;
|
||||
const aas2Portal_t& portal = file->portals[portalNum];
|
||||
return portal.clusterAreaNum[clusterNum != portal.clusters[0] ? 1 : 0];
|
||||
}
|
||||
|
||||
bool idAAS2Local::RouteToGoalArea(const int startAreaNum,
|
||||
const idVec3& startOrigin, const int goalAreaNum,
|
||||
const idVec3& goalOrigin, const int travelFlags,
|
||||
int& travelTime,
|
||||
idIndex<short, invalidReachability_t>& reachability) {
|
||||
travelTime = 0;
|
||||
reachability.Invalidate();
|
||||
if (file == nullptr || startAreaNum <= 0 || goalAreaNum <= 0
|
||||
|| startAreaNum >= file->areas.Num()
|
||||
|| goalAreaNum >= file->areas.Num()) return false;
|
||||
if (startAreaNum == goalAreaNum) {
|
||||
travelTime = AreaTravelTime(startAreaNum, startOrigin, goalOrigin);
|
||||
return true;
|
||||
}
|
||||
|
||||
using queueValue_t = std::pair<int, int>;
|
||||
std::priority_queue<queueValue_t, std::vector<queueValue_t>,
|
||||
std::greater<queueValue_t>> queue;
|
||||
std::vector<int> costs(static_cast<std::size_t>(file->areas.Num()),
|
||||
ROUTE_INFINITY);
|
||||
std::vector<idIndex<short, invalidReachability_t>> firstReach(
|
||||
static_cast<std::size_t>(file->areas.Num()));
|
||||
costs[startAreaNum] = 0;
|
||||
queue.push(queueValue_t(0, startAreaNum));
|
||||
while (!queue.empty()) {
|
||||
const int cost = queue.top().first;
|
||||
const int areaNum = queue.top().second;
|
||||
queue.pop();
|
||||
if (cost != costs[areaNum]) continue;
|
||||
if (areaNum == goalAreaNum) break;
|
||||
const aas2Area_t& area = file->areas[areaNum];
|
||||
for (idIndex<short, invalidReachability_t> index = area.reach;
|
||||
index.IsValid();) {
|
||||
const int reachNum = index.Get();
|
||||
if (reachNum < 0 || reachNum >= file->reachabilities.Num()) break;
|
||||
const aas2Reachability_t& reach = file->reachabilities[reachNum];
|
||||
const idIndex<short, invalidReachability_t> next = reach.next;
|
||||
if (ReachAllowed(reach, travelFlags)
|
||||
&& reach.toAreaNum > 0
|
||||
&& reach.toAreaNum < file->areas.Num()) {
|
||||
const idVec3 from = reach.Start();
|
||||
const idVec3 to = reach.End();
|
||||
int stepCost = reach.travelTime;
|
||||
stepCost += AreaTravelTime(areaNum,
|
||||
areaNum == startAreaNum ? startOrigin : from, from);
|
||||
stepCost += AreaTravelTime(reach.toAreaNum, from, to);
|
||||
const int nextCost = cost + (std::max)(1, stepCost);
|
||||
if (nextCost < costs[reach.toAreaNum]) {
|
||||
costs[reach.toAreaNum] = nextCost;
|
||||
firstReach[reach.toAreaNum] = areaNum == startAreaNum
|
||||
? index : firstReach[areaNum];
|
||||
queue.push(queueValue_t(nextCost, reach.toAreaNum));
|
||||
}
|
||||
}
|
||||
index = next;
|
||||
}
|
||||
}
|
||||
if (costs[goalAreaNum] == ROUTE_INFINITY) return false;
|
||||
travelTime = costs[goalAreaNum]
|
||||
+ AreaTravelTime(goalAreaNum, AreaCenter(goalAreaNum), goalOrigin);
|
||||
reachability = firstReach[goalAreaNum];
|
||||
return true;
|
||||
}
|
||||
|
||||
int idAAS2Local::TravelTimeToGoalArea(const int startAreaNum,
|
||||
const idVec3& startOrigin, const int goalAreaNum,
|
||||
const idVec3& goalOrigin, const int travelFlags,
|
||||
const idAAS2TravelSpeeds*) {
|
||||
int time = 0;
|
||||
idIndex<short, invalidReachability_t> reachability;
|
||||
return RouteToGoalArea(startAreaNum, startOrigin, goalAreaNum,
|
||||
goalOrigin, travelFlags, time, reachability) ? time : 0;
|
||||
}
|
||||
|
||||
bool idAAS2Local::FindNearestGoal(idAAS2Goal& goal,
|
||||
const int startAreaNum, const idVec3& startOrigin,
|
||||
const int travelFlags, idAAS2Callback& callback) {
|
||||
if (file == nullptr || startAreaNum <= 0
|
||||
|| startAreaNum >= file->areas.Num()) return false;
|
||||
using queueValue_t = std::pair<int, int>;
|
||||
std::priority_queue<queueValue_t, std::vector<queueValue_t>,
|
||||
std::greater<queueValue_t>> queue;
|
||||
std::vector<int> costs(static_cast<std::size_t>(file->areas.Num()),
|
||||
ROUTE_INFINITY);
|
||||
costs[startAreaNum] = 0;
|
||||
queue.push(queueValue_t(0, startAreaNum));
|
||||
while (!queue.empty()) {
|
||||
const int cost = queue.top().first;
|
||||
const int areaNum = queue.top().second;
|
||||
queue.pop();
|
||||
if (cost != costs[areaNum]) continue;
|
||||
const idVec3 center = AreaCenter(areaNum);
|
||||
if (callback.AreaIsGoal(this, areaNum, ¢er)) {
|
||||
goal.areaNum = areaNum;
|
||||
goal.origin = center;
|
||||
return true;
|
||||
}
|
||||
const aas2Area_t& area = file->areas[areaNum];
|
||||
for (idIndex<short, invalidReachability_t> index = area.reach;
|
||||
index.IsValid();) {
|
||||
const int reachNum = index.Get();
|
||||
if (reachNum < 0 || reachNum >= file->reachabilities.Num()) break;
|
||||
const aas2Reachability_t& reach = file->reachabilities[reachNum];
|
||||
const idIndex<short, invalidReachability_t> next = reach.next;
|
||||
const idVec3 from = reach.Start();
|
||||
const idVec3 to = reach.End();
|
||||
if (ReachAllowed(reach, travelFlags)
|
||||
&& callback.PathValid(this, &from, &to)) {
|
||||
const int extra = callback.AdditionalTravelTimeForPath(
|
||||
this, &from, &to);
|
||||
const int nextCost = cost + reach.travelTime
|
||||
+ AreaTravelTime(areaNum, center, from) + extra;
|
||||
if (reach.toAreaNum > 0
|
||||
&& reach.toAreaNum < file->areas.Num()
|
||||
&& nextCost < costs[reach.toAreaNum]) {
|
||||
costs[reach.toAreaNum] = nextCost;
|
||||
queue.push(queueValue_t(nextCost, reach.toAreaNum));
|
||||
}
|
||||
}
|
||||
index = next;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool idAAS2Local::NearestPointReachable(
|
||||
const idIndex<int, invalidAASTree_t> tree, const idVec3& origin,
|
||||
const int areaNum, const idVec3& destination,
|
||||
const idBounds& searchBounds, const int travelFlags,
|
||||
idAAS2NearestReachable& nearest) const {
|
||||
std::memset(&nearest, 0, sizeof(nearest));
|
||||
if (file == nullptr || areaNum <= 0) return false;
|
||||
idBounds destinationBounds(searchBounds);
|
||||
destinationBounds[0] = destinationBounds[0] + destination;
|
||||
destinationBounds[1] = destinationBounds[1] + destination;
|
||||
int candidates[256] = {};
|
||||
const int count = file->BoundsAreaNums(tree.Get(), destinationBounds,
|
||||
candidates, 256);
|
||||
float nearestDistance = (std::numeric_limits<float>::max)();
|
||||
int quickestTime = ROUTE_INFINITY;
|
||||
bool found = false;
|
||||
for (int index = 0; index < count; ++index) {
|
||||
const int candidate = candidates[index];
|
||||
if (candidate <= 0 || candidate >= file->areas.Num()) continue;
|
||||
idVec3 point = destination;
|
||||
file->PushPointIntoAreaNum(candidate, point);
|
||||
const idVec3 delta = point - destination;
|
||||
const float distance = delta.x * delta.x + delta.y * delta.y
|
||||
+ delta.z * delta.z;
|
||||
int time = 0;
|
||||
idIndex<short, invalidReachability_t> first;
|
||||
if (!const_cast<idAAS2Local*>(this)->RouteToGoalArea(areaNum,
|
||||
origin, candidate, point, travelFlags, time, first)) continue;
|
||||
if (distance < nearestDistance) {
|
||||
nearestDistance = distance;
|
||||
nearest.nearestDestArea = candidate;
|
||||
nearest.nearestDestPos = point;
|
||||
nearest.nearestTravelTime = time;
|
||||
}
|
||||
if (time < quickestTime) {
|
||||
quickestTime = time;
|
||||
nearest.quickestDestArea = candidate;
|
||||
nearest.quickestDestPos = point;
|
||||
nearest.quickestTravelTime = time;
|
||||
}
|
||||
found = true;
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
void idAAS2Local::DeleteOldestCache() {
|
||||
idAASRoutingCache* const cache = cacheListStart;
|
||||
if (cache == nullptr) return;
|
||||
if (cache->type == 0 && areaCacheIndex != nullptr
|
||||
&& cache->cluster >= 0 && cache->cluster < file->clusters.Num()) {
|
||||
const int slot = ClusterAreaNum(cache->cluster, cache->areaNum);
|
||||
idAASRoutingCache** link = &areaCacheIndex[cache->cluster][slot];
|
||||
while (*link != nullptr && *link != cache) link = &(*link)->next;
|
||||
if (*link == cache) *link = cache->next;
|
||||
} else if (portalCacheIndex != nullptr && cache->areaNum >= 0
|
||||
&& cache->areaNum < portalCacheIndexSize) {
|
||||
idAASRoutingCache** link = &portalCacheIndex[cache->areaNum];
|
||||
while (*link != nullptr && *link != cache) link = &(*link)->next;
|
||||
if (*link == cache) *link = cache->next;
|
||||
}
|
||||
UnlinkCache(cache);
|
||||
totalCacheMemory -= sizeof(*cache) + 3 * cache->size;
|
||||
delete cache;
|
||||
}
|
||||
|
||||
void idAAS2Local::UpdateAreaRoutingCache(idAASRoutingCache* const cache) {
|
||||
if (cache == nullptr || file == nullptr) return;
|
||||
for (int area = 0; area < cache->size; ++area) {
|
||||
const int sourceArea = area + 1;
|
||||
if (sourceArea >= file->areas.Num()) break;
|
||||
int time = 0;
|
||||
idIndex<short, invalidReachability_t> first;
|
||||
if (RouteToGoalArea(sourceArea, AreaCenter(sourceArea),
|
||||
cache->areaNum, AreaCenter(cache->areaNum),
|
||||
cache->travelFlags, time, first)) {
|
||||
cache->travelTimes[area] = static_cast<std::uint16_t>(
|
||||
(std::min)(time, 65535));
|
||||
int ordinal = 0;
|
||||
for (idIndex<short, invalidReachability_t> index =
|
||||
file->areas[sourceArea].reach; index.IsValid();
|
||||
++ordinal) {
|
||||
if (index == first) break;
|
||||
index = file->reachabilities[index.Get()].next;
|
||||
}
|
||||
cache->reachabilities[area] = static_cast<std::uint8_t>(ordinal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
idAASRoutingCache* idAAS2Local::GetAreaRoutingCache(const int clusterNum,
|
||||
const int areaNum, const int travelFlags) {
|
||||
if (file == nullptr || areaCacheIndex == nullptr || clusterNum < 0
|
||||
|| clusterNum >= file->clusters.Num()) return nullptr;
|
||||
const int slot = ClusterAreaNum(clusterNum, areaNum);
|
||||
if (slot < 0 || slot >= file->clusters[clusterNum].numReachableAreas) {
|
||||
return nullptr;
|
||||
}
|
||||
idAASRoutingCache** link = &areaCacheIndex[clusterNum][slot];
|
||||
while (*link != nullptr && (*link)->travelFlags != travelFlags) {
|
||||
link = &(*link)->next;
|
||||
}
|
||||
if (*link == nullptr) {
|
||||
idAASRoutingCache* const cache =
|
||||
new idAASRoutingCache(file->areas.Num() - 1);
|
||||
cache->type = 0;
|
||||
cache->cluster = clusterNum;
|
||||
cache->areaNum = areaNum;
|
||||
cache->travelFlags = travelFlags;
|
||||
*link = cache;
|
||||
totalCacheMemory += sizeof(*cache) + 3 * cache->size;
|
||||
UpdateAreaRoutingCache(cache);
|
||||
}
|
||||
LinkCache(*link);
|
||||
return *link;
|
||||
}
|
||||
|
||||
void idAAS2Local::UpdatePortalRoutingCache(idAASRoutingCache* const cache) {
|
||||
UpdateAreaRoutingCache(cache);
|
||||
}
|
||||
|
||||
idAASRoutingCache* idAAS2Local::GetPortalRoutingCache(const int clusterNum,
|
||||
const int areaNum, const int travelFlags) {
|
||||
if (file == nullptr || portalCacheIndex == nullptr || areaNum < 0
|
||||
|| areaNum >= portalCacheIndexSize) return nullptr;
|
||||
idAASRoutingCache** link = &portalCacheIndex[areaNum];
|
||||
while (*link != nullptr && (*link)->travelFlags != travelFlags) {
|
||||
link = &(*link)->next;
|
||||
}
|
||||
if (*link == nullptr) {
|
||||
idAASRoutingCache* const cache =
|
||||
new idAASRoutingCache(file->areas.Num() - 1);
|
||||
cache->type = 1;
|
||||
cache->cluster = clusterNum;
|
||||
cache->areaNum = areaNum;
|
||||
cache->travelFlags = travelFlags;
|
||||
*link = cache;
|
||||
totalCacheMemory += sizeof(*cache) + 3 * cache->size;
|
||||
UpdatePortalRoutingCache(cache);
|
||||
}
|
||||
LinkCache(*link);
|
||||
return *link;
|
||||
}
|
||||
|
||||
bool idAAS2Local::GetClusterRoute(const int startAreaNum,
|
||||
const idVec3& startOrigin, const int, const int goalAreaNum,
|
||||
const int travelFlags, int& travelTime,
|
||||
idIndex<short, invalidReachability_t>& bestReachability) {
|
||||
return RouteToGoalArea(startAreaNum, startOrigin, goalAreaNum,
|
||||
AreaCenter(goalAreaNum), travelFlags, travelTime, bestReachability);
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
#include "gamelib/aas2/aas2_local.h"
|
||||
|
||||
#include "idlib/lib_print.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
|
||||
bool BoundsIntersect(const idBounds& first, const idBounds& second) {
|
||||
for (int axis = 0; axis < 3; ++axis) {
|
||||
if (first[1][axis] < second[0][axis]
|
||||
|| first[0][axis] > second[1][axis]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
float DistanceSqr(const idVec3& first, const idVec3& second) {
|
||||
const idVec3 delta = first - second;
|
||||
return delta.x * delta.x + delta.y * delta.y + delta.z * delta.z;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int idAAS2Local::GetNumCoverPoints() const {
|
||||
return file != nullptr ? file->cover.Num() : 0;
|
||||
}
|
||||
|
||||
const aas2Cover_t* idAAS2Local::GetCoverPoint(const int index) const {
|
||||
return file != nullptr && index >= 0 && index < file->cover.Num()
|
||||
? &file->cover[index] : nullptr;
|
||||
}
|
||||
|
||||
int idAAS2Local::GetNumAreaCoverPoints(const int areaNum) const {
|
||||
return file != nullptr && areaNum > 0 && areaNum < file->areas.Num()
|
||||
? file->areas[areaNum].numCover : 0;
|
||||
}
|
||||
|
||||
int idAAS2Local::GetAreaCoverPoints(const int areaNum, int* const points,
|
||||
const int maxPoints, int* const numPoints) const {
|
||||
if (numPoints != nullptr) *numPoints = 0;
|
||||
if (file == nullptr || areaNum <= 0 || areaNum >= file->areas.Num()
|
||||
|| points == nullptr || numPoints == nullptr) return 0;
|
||||
const aas2Area_t& area = file->areas[areaNum];
|
||||
for (int index = 0; index < area.numCover; ++index) {
|
||||
if (*numPoints >= maxPoints) {
|
||||
idLibPrint::Warning(
|
||||
"idAAS2Local::GetAreaCoverPoints - search list overflowed.");
|
||||
break;
|
||||
}
|
||||
points[(*numPoints)++] = file->areaCoverIndex[area.firstCover + index];
|
||||
}
|
||||
return *numPoints;
|
||||
}
|
||||
|
||||
void idAAS2Local::FindCoverPoints_Internal(const int startAreaNum,
|
||||
const idVec3& origin, const idBounds& bounds,
|
||||
const float maxDistSqr, const int requiredFlags,
|
||||
const int excludeFlags, const int travelFlags,
|
||||
int* const coverPoints, const int maxCover,
|
||||
int& numCover) const {
|
||||
if (file == nullptr || startAreaNum <= 0
|
||||
|| startAreaNum >= file->areas.Num()) return;
|
||||
std::vector<std::uint8_t> visited(
|
||||
static_cast<std::size_t>(file->areas.Num()), 0);
|
||||
int queue[1280] = {};
|
||||
int first = 0;
|
||||
int last = 1;
|
||||
queue[0] = startAreaNum;
|
||||
visited[startAreaNum] = 1;
|
||||
while (first < last) {
|
||||
if (last >= 1280) return;
|
||||
const int areaNum = queue[first++];
|
||||
const aas2Area_t& area = file->areas[areaNum];
|
||||
for (int index = 0; index < area.numCover; ++index) {
|
||||
const int coverIndex =
|
||||
file->areaCoverIndex[area.firstCover + index];
|
||||
if (coverIndex < 0 || coverIndex >= file->cover.Num()) continue;
|
||||
const aas2Cover_t& coverPoint = file->cover[coverIndex];
|
||||
if ((requiredFlags != 0
|
||||
&& (coverPoint.flags & requiredFlags) == 0)
|
||||
|| (coverPoint.flags & excludeFlags) != 0
|
||||
|| DistanceSqr(coverPoint.origin, origin) >= maxDistSqr) {
|
||||
continue;
|
||||
}
|
||||
if (numCover >= maxCover) {
|
||||
idLibPrint::Warning(
|
||||
"idAAS2Local::FindCoverPoints_Internal - search list overflowed.");
|
||||
return;
|
||||
}
|
||||
coverPoints[numCover++] = coverIndex;
|
||||
}
|
||||
for (idIndex<short, invalidReachability_t> reachIndex = area.reach;
|
||||
reachIndex.IsValid();) {
|
||||
const int index = reachIndex.Get();
|
||||
if (index < 0 || index >= file->reachabilities.Num()) break;
|
||||
const aas2Reachability_t& reach = file->reachabilities[index];
|
||||
const int nextArea = reach.toAreaNum;
|
||||
if (nextArea > 0 && nextArea < file->areas.Num()
|
||||
&& visited[nextArea] == 0
|
||||
&& (reach.travelFlags & travelFlags) != 0) {
|
||||
idBounds areaBounds;
|
||||
if (nextArea < file->areaBounds.Num()) {
|
||||
areaBounds[0].Set(
|
||||
float(file->areaBounds[nextArea].min[0]),
|
||||
float(file->areaBounds[nextArea].min[1]),
|
||||
float(file->areaBounds[nextArea].min[2]));
|
||||
areaBounds[1].Set(
|
||||
float(file->areaBounds[nextArea].max[0]),
|
||||
float(file->areaBounds[nextArea].max[1]),
|
||||
float(file->areaBounds[nextArea].max[2]));
|
||||
} else {
|
||||
areaBounds = file->AreaBounds(nextArea);
|
||||
}
|
||||
if (BoundsIntersect(bounds, areaBounds) && last < 1280) {
|
||||
visited[nextArea] = 1;
|
||||
queue[last++] = nextArea;
|
||||
}
|
||||
}
|
||||
reachIndex = reach.next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int idAAS2Local::FindCoverPoints(const int areaNum, const idVec3& origin,
|
||||
const float radius, const int requiredFlags, const int excludeFlags,
|
||||
const int travelFlags, int* const points, const int maxPoints,
|
||||
int* const numPoints) const {
|
||||
if (numPoints != nullptr) *numPoints = 0;
|
||||
if (areaNum <= 0 || points == nullptr || numPoints == nullptr
|
||||
|| maxPoints <= 0) return 0;
|
||||
const idVec3 extent(radius, radius, radius);
|
||||
idBounds bounds;
|
||||
bounds[0] = origin - extent;
|
||||
bounds[1] = origin + extent;
|
||||
FindCoverPoints_Internal(areaNum, origin, bounds, radius * radius,
|
||||
requiredFlags, excludeFlags, travelFlags, points, maxPoints,
|
||||
*numPoints);
|
||||
return *numPoints;
|
||||
}
|
||||
|
||||
int idAAS2Local::GetNumChokePoints() const {
|
||||
return file != nullptr ? file->chokePoints.Num() : 0;
|
||||
}
|
||||
|
||||
const aas2ChokePoint_t* idAAS2Local::GetChokePoint(const int index) const {
|
||||
return file != nullptr && index >= 0 && index < file->chokePoints.Num()
|
||||
? &file->chokePoints[index] : nullptr;
|
||||
}
|
||||
|
||||
int idAAS2Local::GetNumHintNodes() const {
|
||||
return file != nullptr ? file->hintNodes.Num() : 0;
|
||||
}
|
||||
|
||||
const aas2HintNode_t* idAAS2Local::GetHintNode(const int index) const {
|
||||
return file != nullptr && index >= 0 && index < file->hintNodes.Num()
|
||||
? &file->hintNodes[index] : nullptr;
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
#include "gamelib/aas2/aas2_local.h"
|
||||
|
||||
bool idAAS2Local::GetAreaCells(const int areaNumber, const int subdivisionSize,
|
||||
int* const subdivisionX, int* const subdivisionY) const {
|
||||
if (file == nullptr || areaNumber <= 0 || subdivisionSize <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const idBounds bounds = file->AreaBounds(areaNumber);
|
||||
|
||||
*subdivisionX = static_cast<int>(bounds[1].x - bounds[0].x) /
|
||||
subdivisionSize + 1;
|
||||
*subdivisionY = static_cast<int>(bounds[1].y - bounds[0].y) /
|
||||
subdivisionSize + 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool idAAS2Local::WorldPosToCellPos(const int areaNumber,
|
||||
const int subdivisionSize, const idVec3& origin,
|
||||
int* const subdivisionX, int* const subdivisionY) const {
|
||||
if (file == nullptr || areaNumber <= 0 || subdivisionSize <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const idBounds bounds = file->AreaBounds(areaNumber);
|
||||
|
||||
*subdivisionX = static_cast<int>(
|
||||
(origin.x - bounds[0].x) / static_cast<float>(subdivisionSize));
|
||||
*subdivisionY = static_cast<int>(
|
||||
(origin.y - bounds[0].y) / static_cast<float>(subdivisionSize));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool idAAS2Local::CellPosToWorldPos(const int areaNumber,
|
||||
const int subdivisionSize, const int subdivisionX,
|
||||
const int subdivisionY, idVec3& worldPosition,
|
||||
const bool findZ) const {
|
||||
(void)findZ;
|
||||
if (file == nullptr || areaNumber <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const idBounds bounds = file->AreaBounds(areaNumber);
|
||||
|
||||
worldPosition.x = bounds[0].x + subdivisionSize * subdivisionX;
|
||||
worldPosition.y = bounds[0].y + subdivisionSize * subdivisionY;
|
||||
worldPosition.z = bounds[1].z;
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
#include "gamelib/aas2/aas2callback_avoidlocation.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
idAAS2Callback_AvoidLocation::idAAS2Callback_AvoidLocation()
|
||||
: avoidLocation(0.0f, 0.0f, 0.0f)
|
||||
, avoidDist(0.0f)
|
||||
, obstacles(nullptr)
|
||||
, numObstacles(0) {
|
||||
}
|
||||
|
||||
idAAS2Callback_AvoidLocation::~idAAS2Callback_AvoidLocation() {
|
||||
obstacles = nullptr;
|
||||
}
|
||||
|
||||
bool idAAS2Callback_AvoidLocation::PathValid(const idAAS2 *,
|
||||
const idVec3 *start, const idVec3 *end) {
|
||||
if (obstacles == nullptr || numObstacles <= 0
|
||||
|| start == nullptr || end == nullptr) {
|
||||
return true;
|
||||
}
|
||||
for (int index = 0; index < numObstacles; ++index) {
|
||||
if (obstacles[index].expAbsBounds.LineIntersection(*start, *end)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int idAAS2Callback_AvoidLocation::AdditionalTravelTimeForPath(
|
||||
const idAAS2 *, const idVec3 *start, const idVec3 *end) {
|
||||
if (avoidDist <= 0.0f || start == nullptr || end == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const idVec3 segment = *end - *start;
|
||||
const float segmentLengthSqr = segment.LengthSqr();
|
||||
idVec3 closest = *end;
|
||||
if (segmentLengthSqr > 1.0e-30f) {
|
||||
const float inverseLength = 1.0f / std::sqrt(segmentLengthSqr);
|
||||
const idVec3 direction = segment * inverseLength;
|
||||
const idVec3 projected = *start
|
||||
+ direction * ((avoidLocation - *start).Dot(direction));
|
||||
|
||||
const bool outside =
|
||||
(projected.x > start->x + 0.1f && projected.x > end->x + 0.1f)
|
||||
|| (projected.x < start->x - 0.1f && projected.x < end->x - 0.1f)
|
||||
|| (projected.y > start->y + 0.1f && projected.y > end->y + 0.1f)
|
||||
|| (projected.y < start->y - 0.1f && projected.y < end->y - 0.1f)
|
||||
|| (projected.z > start->z + 0.1f && projected.z > end->z + 0.1f)
|
||||
|| (projected.z < start->z - 0.1f && projected.z < end->z - 0.1f);
|
||||
if (!outside) {
|
||||
closest = projected;
|
||||
}
|
||||
}
|
||||
|
||||
const float distance = (avoidLocation - closest).Length();
|
||||
return distance < avoidDist
|
||||
? static_cast<int>((avoidDist - distance) * 10.0f)
|
||||
: 0;
|
||||
}
|
||||
@@ -1,23 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#include "gamelib/aas2/aas2.h"
|
||||
#include "idlib/bv/bounds.h"
|
||||
|
||||
// Reconstructed C++ declarations from IDA Local Types and PDB/DIA metadata.
|
||||
// Original PDB header: w:\tech5\engine\gamelib\aas2\aas2callback_avoidlocation.h
|
||||
// Recovered logical types: 1
|
||||
// Signatures retain Xbox 360 ABI evidence and may still require manual review.
|
||||
|
||||
|
||||
// IDA Local Type ordinal 23521; PDB kind: struct. The PDB mapped this type to
|
||||
// tungsten/game/ai/aasobstacle.h; it is declared here to keep the recovered
|
||||
// GameLib callback boundary self-contained.
|
||||
struct idAAS2Obstacle
|
||||
{
|
||||
idBounds absBounds;
|
||||
idBounds expAbsBounds;
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 23522; PDB kind: class.
|
||||
class idAAS2Callback_AvoidLocation : public idAAS2Callback
|
||||
{
|
||||
public:
|
||||
idAAS2Callback_AvoidLocation();
|
||||
|
||||
// Recovered virtual interface; IDA vtable ordinal 23523.
|
||||
virtual ~idAAS2Callback_AvoidLocation();
|
||||
virtual bool PathValid(const idAAS2 *, const idVec3 *, const idVec3 *);
|
||||
virtual int AdditionalTravelTimeForPath(const idAAS2 *, const idVec3 *, const idVec3 *);
|
||||
virtual bool AreaIsGoal(const idAAS2 *, int, const idVec3 *);
|
||||
~idAAS2Callback_AvoidLocation() override;
|
||||
bool PathValid(const idAAS2 *, const idVec3 *, const idVec3 *) override;
|
||||
int AdditionalTravelTimeForPath(const idAAS2 *, const idVec3 *, const idVec3 *) override;
|
||||
|
||||
idVec3 avoidLocation;
|
||||
float avoidDist;
|
||||
const idAAS2Obstacle *obstacles;
|
||||
int numObstacles;
|
||||
};
|
||||
|
||||
#if defined(_WIN32) && !defined(_WIN64)
|
||||
static_assert(sizeof(idAAS2Obstacle) == 48,
|
||||
"Recovered idAAS2Obstacle ABI changed");
|
||||
static_assert(sizeof(idAAS2Callback_AvoidLocation) == 28,
|
||||
"Recovered idAAS2Callback_AvoidLocation ABI changed");
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
#include "gamelib/aas2/aas2callback_findflaggedarea.h"
|
||||
|
||||
idAAS2Callback_FindFlaggedArea::idAAS2Callback_FindFlaggedArea(
|
||||
const int areaFlag_, const bool set)
|
||||
: idAAS2Callback_AvoidLocation()
|
||||
, areaFlag(areaFlag_)
|
||||
, test(set ? 1 : 0) {
|
||||
}
|
||||
|
||||
idAAS2Callback_FindFlaggedArea::~idAAS2Callback_FindFlaggedArea() = default;
|
||||
|
||||
bool idAAS2Callback_FindFlaggedArea::AreaIsGoal(const idAAS2 *aas,
|
||||
const int areaNum, const idVec3 *) {
|
||||
if (aas == nullptr) {
|
||||
return false;
|
||||
}
|
||||
// The recovered callback ABI supplies a const pointer even though the
|
||||
// idAAS2 virtual interface did not mark GetAreaFlags const.
|
||||
const int flags = const_cast<idAAS2*>(aas)->GetAreaFlags(areaNum);
|
||||
return ((flags & areaFlag) != 0) == (test != 0);
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "gamelib/aas2/aas2callback_avoidlocation.h"
|
||||
|
||||
// Reconstructed C++ declarations from IDA Local Types and PDB/DIA metadata.
|
||||
// Original PDB header: w:\tech5\engine\gamelib\aas2\aas2callback_findflaggedarea.h
|
||||
// Recovered logical types: 1
|
||||
@@ -10,12 +12,17 @@
|
||||
class idAAS2Callback_FindFlaggedArea : public idAAS2Callback_AvoidLocation
|
||||
{
|
||||
public:
|
||||
idAAS2Callback_FindFlaggedArea(int areaFlag, bool set);
|
||||
|
||||
// Recovered virtual interface; IDA vtable ordinal 23525.
|
||||
virtual ~idAAS2Callback_FindFlaggedArea();
|
||||
virtual bool PathValid(const idAAS2 *, const idVec3 *, const idVec3 *);
|
||||
virtual int AdditionalTravelTimeForPath(const idAAS2 *, const idVec3 *, const idVec3 *);
|
||||
virtual bool AreaIsGoal(const idAAS2 *, int, const idVec3 *);
|
||||
~idAAS2Callback_FindFlaggedArea() override;
|
||||
bool AreaIsGoal(const idAAS2 *, int, const idVec3 *) override;
|
||||
|
||||
int areaFlag;
|
||||
int test;
|
||||
};
|
||||
|
||||
#if defined(_WIN32) && !defined(_WIN64)
|
||||
static_assert(sizeof(idAAS2Callback_FindFlaggedArea) == 36,
|
||||
"Recovered idAAS2Callback_FindFlaggedArea ABI changed");
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,371 @@
|
||||
#include "gamelib/aas2/deferredviscache.h"
|
||||
|
||||
#include "cm/collisionmodelmanager.h"
|
||||
#include "gamelib/aas2/aas2.h"
|
||||
#include "idlib/lib_print.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr int QUERY_POOL_SIZE = 5120;
|
||||
constexpr int HASH_SIZE = 4096;
|
||||
constexpr int CLIP_QUERY_COUNT = 256;
|
||||
constexpr int MAX_SUBMITS_PER_FRAME = 128;
|
||||
constexpr int MAX_LRU_ENTRIES = 4096;
|
||||
|
||||
int HashQuery(const std::int16_t start[3], const std::int16_t end[3]) {
|
||||
return (start[0] + start[1] + start[2]
|
||||
+ end[0] + end[1] + end[2]) & (HASH_SIZE - 1);
|
||||
}
|
||||
|
||||
std::int16_t Quantize(const float value) {
|
||||
const int integer = static_cast<int>(value);
|
||||
return static_cast<std::int16_t>((std::max)(-32768,
|
||||
(std::min)(32767, integer)));
|
||||
}
|
||||
|
||||
bool SameQuery(const idDeferredVisQuery& query,
|
||||
const std::uint16_t start[3], const std::uint16_t end[3]) {
|
||||
for (int axis = 0; axis < 3; ++axis) {
|
||||
if (static_cast<std::uint16_t>(query.start[axis]) != start[axis]
|
||||
|| static_cast<std::uint16_t>(query.end[axis]) != end[axis])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
idDeferredVisCache::idDeferredVisCache()
|
||||
: hashTable(), clipQueries(), curClipQueryIdx(0),
|
||||
leastRecentlyUsedList{nullptr, nullptr, 0},
|
||||
priorityList{nullptr, nullptr, 0}, clip(nullptr), queryList(nullptr),
|
||||
queryPool(new idDeferredVisQuery[QUERY_POOL_SIZE]), expiredIndex(0),
|
||||
numFrames(0), numCreatedThisFrame(0), numBumped(0),
|
||||
numQueriesIssued(0), totalQueriesIssued(0), numPurged(0),
|
||||
numExpired(0), numCacheHits(0), totalCacheHits(0),
|
||||
maxCreatedInAFrame(0), recentNumFrames(0), recentTotalCacheHits(0),
|
||||
recentTotalQueriesIssued(0), recentCacheHitRatio(0.0f),
|
||||
recentAvgHitRatio(0.0f), initialized(false), alreadyWarned(false) {
|
||||
hashTable.SetNum(HASH_SIZE);
|
||||
for (int index = 0; index < HASH_SIZE; ++index)
|
||||
hashTable[index] = nullptr;
|
||||
clipQueries.SetNum(CLIP_QUERY_COUNT);
|
||||
for (int index = 0; index < CLIP_QUERY_COUNT; ++index)
|
||||
clipQueries[index].index = 0;
|
||||
for (int index = 0; index < QUERY_POOL_SIZE; ++index) {
|
||||
idDeferredVisQuery& query = queryPool[index];
|
||||
std::memset(&query, 0, sizeof(query));
|
||||
query.frameCounter = -1;
|
||||
query.next = index + 1 < QUERY_POOL_SIZE
|
||||
? &queryPool[index + 1] : nullptr;
|
||||
}
|
||||
queryList = queryPool;
|
||||
}
|
||||
|
||||
idDeferredVisCache::~idDeferredVisCache() {
|
||||
Shutdown();
|
||||
delete[] queryPool;
|
||||
queryPool = nullptr;
|
||||
queryList = nullptr;
|
||||
}
|
||||
|
||||
idDeferredVisQuery* idDeferredVisCache::AllocNode() {
|
||||
if (queryList == nullptr) return nullptr;
|
||||
idDeferredVisQuery* const node = queryList;
|
||||
queryList = node->next;
|
||||
node->nextInHash = nullptr;
|
||||
node->prev = nullptr;
|
||||
node->next = nullptr;
|
||||
node->frameCounter = -1;
|
||||
node->queryIdx = 0;
|
||||
node->state = VIS_INVALID;
|
||||
node->pad = 0;
|
||||
return node;
|
||||
}
|
||||
|
||||
void idDeferredVisCache::FreeLinkedList(deferredVisList_t& list) {
|
||||
idDeferredVisQuery* node = list.headNode;
|
||||
while (node != nullptr) {
|
||||
idDeferredVisQuery* const next = node->next;
|
||||
node->state = VIS_INVALID;
|
||||
node->frameCounter = -1;
|
||||
node->prev = nullptr;
|
||||
node->next = queryList;
|
||||
queryList = node;
|
||||
node = next;
|
||||
}
|
||||
list.headNode = nullptr;
|
||||
list.tailNode = nullptr;
|
||||
list.numElements = 0;
|
||||
}
|
||||
|
||||
void idDeferredVisCache::RemoveNoDelete(deferredVisList_t& list,
|
||||
idDeferredVisQuery* const node) {
|
||||
if (node == nullptr) return;
|
||||
if (node->prev != nullptr) node->prev->next = node->next;
|
||||
else if (list.headNode == node) list.headNode = node->next;
|
||||
if (node->next != nullptr) node->next->prev = node->prev;
|
||||
else if (list.tailNode == node) list.tailNode = node->prev;
|
||||
node->prev = nullptr;
|
||||
node->next = nullptr;
|
||||
if (list.numElements > 0) --list.numElements;
|
||||
}
|
||||
|
||||
void idDeferredVisCache::AddToFront(deferredVisList_t& list,
|
||||
idDeferredVisQuery* const node) {
|
||||
node->prev = nullptr;
|
||||
node->next = list.headNode;
|
||||
if (list.headNode != nullptr) list.headNode->prev = node;
|
||||
else list.tailNode = node;
|
||||
list.headNode = node;
|
||||
++list.numElements;
|
||||
}
|
||||
|
||||
void idDeferredVisCache::RemoveFromHash(idDeferredVisQuery* const node) {
|
||||
if (node == nullptr || hashTable.IsEmpty()) return;
|
||||
const int bucket = HashQuery(node->start, node->end);
|
||||
idDeferredVisQuery** link = &hashTable[bucket];
|
||||
while (*link != nullptr && *link != node)
|
||||
link = &(*link)->nextInHash;
|
||||
if (*link == node) *link = node->nextInHash;
|
||||
node->nextInHash = nullptr;
|
||||
}
|
||||
|
||||
void idDeferredVisCache::FreeNode(idDeferredVisQuery* const node) {
|
||||
if (node == nullptr) return;
|
||||
RemoveFromHash(node);
|
||||
node->state = VIS_INVALID;
|
||||
node->frameCounter = -1;
|
||||
node->prev = nullptr;
|
||||
node->next = queryList;
|
||||
queryList = node;
|
||||
}
|
||||
|
||||
void idDeferredVisCache::Init(idClip* const inClip) {
|
||||
clip = inClip;
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
void idDeferredVisCache::Shutdown() {
|
||||
clip = nullptr;
|
||||
if (queryPool == nullptr) return;
|
||||
FreeLinkedList(leastRecentlyUsedList);
|
||||
FreeLinkedList(priorityList);
|
||||
for (int index = 0; index < HASH_SIZE; ++index)
|
||||
hashTable[index] = nullptr;
|
||||
queryList = nullptr;
|
||||
for (int index = QUERY_POOL_SIZE - 1; index >= 0; --index) {
|
||||
queryPool[index].state = VIS_INVALID;
|
||||
queryPool[index].frameCounter = -1;
|
||||
queryPool[index].nextInHash = nullptr;
|
||||
queryPool[index].prev = nullptr;
|
||||
queryPool[index].next = queryList;
|
||||
queryList = &queryPool[index];
|
||||
}
|
||||
initialized = false;
|
||||
expiredIndex = 0;
|
||||
numFrames = 0;
|
||||
numCreatedThisFrame = 0;
|
||||
numBumped = 0;
|
||||
numQueriesIssued = 0;
|
||||
totalQueriesIssued = 0;
|
||||
totalCacheHits = 0;
|
||||
}
|
||||
|
||||
std::uint8_t idDeferredVisCache::GetVisQuery(
|
||||
const std::uint16_t start[3], const std::uint16_t end[3],
|
||||
const int flags) {
|
||||
if (!initialized || start == nullptr || end == nullptr) return VIS_INVALID;
|
||||
std::int16_t signedStart[3];
|
||||
std::int16_t signedEnd[3];
|
||||
for (int axis = 0; axis < 3; ++axis) {
|
||||
signedStart[axis] = static_cast<std::int16_t>(start[axis]);
|
||||
signedEnd[axis] = static_cast<std::int16_t>(end[axis]);
|
||||
}
|
||||
const int bucket = HashQuery(signedStart, signedEnd);
|
||||
for (idDeferredVisQuery* node = hashTable[bucket]; node != nullptr;
|
||||
node = node->nextInHash) {
|
||||
if (!SameQuery(*node, start, end)) continue;
|
||||
if (node->state >= VIS_BLOCKED) {
|
||||
++numCacheHits;
|
||||
++totalCacheHits;
|
||||
if (leastRecentlyUsedList.headNode != node) {
|
||||
RemoveNoDelete(leastRecentlyUsedList, node);
|
||||
AddToFront(leastRecentlyUsedList, node);
|
||||
}
|
||||
}
|
||||
if (node->frameCounter == 1 && (flags & 2) != 0) {
|
||||
node->frameCounter = 0;
|
||||
++numBumped;
|
||||
}
|
||||
return node->state;
|
||||
}
|
||||
idDeferredVisQuery* const node = AllocNode();
|
||||
if (node == nullptr) {
|
||||
if (!alreadyWarned) {
|
||||
idLibPrint::Warning(
|
||||
"idDeferredVisCache::GetVisQuery ran out of query nodes");
|
||||
alreadyWarned = true;
|
||||
}
|
||||
return VIS_INVALID;
|
||||
}
|
||||
++numCreatedThisFrame;
|
||||
for (int axis = 0; axis < 3; ++axis) {
|
||||
node->start[axis] = signedStart[axis];
|
||||
node->end[axis] = signedEnd[axis];
|
||||
}
|
||||
node->state = VIS_PENDING;
|
||||
node->nextInHash = hashTable[bucket];
|
||||
hashTable[bucket] = node;
|
||||
AddToFront(priorityList, node);
|
||||
return VIS_PENDING;
|
||||
}
|
||||
|
||||
void idDeferredVisCache::UpdateCollisionQueries() {
|
||||
if (!initialized || clip == nullptr) return;
|
||||
maxCreatedInAFrame = (std::max)(maxCreatedInAFrame,
|
||||
numCreatedThisFrame);
|
||||
numPurged = 0;
|
||||
numExpired = 0;
|
||||
numCreatedThisFrame = 0;
|
||||
numBumped = 0;
|
||||
numCacheHits = 0;
|
||||
idDeferredVisQuery* node = priorityList.headNode;
|
||||
while (node != nullptr) {
|
||||
idDeferredVisQuery* const next = node->next;
|
||||
if (node->state == VIS_SUBMITTED) {
|
||||
trace_t trace{};
|
||||
trace.fraction = 1.0f;
|
||||
idCollisionQuery query{};
|
||||
query.offset = clipQueries[node->queryIdx].index;
|
||||
collisionModelManager.GetContentsResult(&trace, query, false);
|
||||
node->state = trace.fraction >= 1.0f ? VIS_CLEAR : VIS_BLOCKED;
|
||||
node->frameCounter = trace.c.entityNum == 8190 ? 30 : 18;
|
||||
RemoveNoDelete(priorityList, node);
|
||||
AddToFront(leastRecentlyUsedList, node);
|
||||
}
|
||||
node = next;
|
||||
}
|
||||
}
|
||||
|
||||
void idDeferredVisCache::SubmitCollisionQueries() {
|
||||
if (!initialized || clip == nullptr) return;
|
||||
int submitted = 0;
|
||||
for (idDeferredVisQuery* node = priorityList.headNode;
|
||||
node != nullptr && submitted < MAX_SUBMITS_PER_FRAME;
|
||||
node = node->next) {
|
||||
if (node->state != VIS_PENDING) continue;
|
||||
const idVec3 start(static_cast<float>(node->start[0]),
|
||||
static_cast<float>(node->start[1]),
|
||||
static_cast<float>(node->start[2]));
|
||||
const idVec3 end(static_cast<float>(node->end[0]),
|
||||
static_cast<float>(node->end[1]),
|
||||
static_cast<float>(node->end[2]));
|
||||
clipQueries[curClipQueryIdx] = clip->Translation(nullptr, start, end,
|
||||
nullptr, idMat3(1.0f), 1, -1, false,
|
||||
"idDeferredVisCache");
|
||||
node->queryIdx = static_cast<std::uint8_t>(curClipQueryIdx);
|
||||
curClipQueryIdx = (curClipQueryIdx + 1) & 255;
|
||||
node->state = VIS_SUBMITTED;
|
||||
++submitted;
|
||||
}
|
||||
numQueriesIssued = submitted;
|
||||
totalQueriesIssued += submitted;
|
||||
while (leastRecentlyUsedList.numElements > MAX_LRU_ENTRIES) {
|
||||
idDeferredVisQuery* const expired = leastRecentlyUsedList.tailNode;
|
||||
RemoveNoDelete(leastRecentlyUsedList, expired);
|
||||
FreeNode(expired);
|
||||
++numPurged;
|
||||
}
|
||||
const int endIndex = (std::min)(QUERY_POOL_SIZE, expiredIndex + 512);
|
||||
for (int index = expiredIndex; index < endIndex; ++index) {
|
||||
idDeferredVisQuery* const candidate = &queryPool[index];
|
||||
if (candidate->frameCounter < 0) continue;
|
||||
if (--candidate->frameCounter <= 0) {
|
||||
RemoveNoDelete(leastRecentlyUsedList, candidate);
|
||||
FreeNode(candidate);
|
||||
++numExpired;
|
||||
}
|
||||
}
|
||||
expiredIndex = endIndex < QUERY_POOL_SIZE ? endIndex : 0;
|
||||
alreadyWarned = false;
|
||||
++numFrames;
|
||||
}
|
||||
|
||||
void idDeferredVisCache::PrintDebugInfo() const {
|
||||
idLibPrint::Printf("Deferred vis: queries %d, cache hits %d, "
|
||||
"priority %d, LRU %d\n", totalQueriesIssued, totalCacheHits,
|
||||
priorityList.numElements, leastRecentlyUsedList.numElements);
|
||||
}
|
||||
|
||||
idDeferredVisGrid::idDeferredVisGrid() : cells{}, xyz{} {
|
||||
}
|
||||
|
||||
void idDeferredVisGrid::Init(const idVec3& entityPos) {
|
||||
xyz[0] = static_cast<std::int16_t>(Quantize(entityPos.x) + 32);
|
||||
Update(entityPos, nullptr, -1, 0);
|
||||
}
|
||||
|
||||
void idDeferredVisGrid::Update(const idVec3& entityPos,
|
||||
const idAAS2* const aas, const int areaNum,
|
||||
const int travelFlags) {
|
||||
const std::int16_t newX = static_cast<std::int16_t>(
|
||||
(Quantize(entityPos.x) + 31) & ~31);
|
||||
const std::int16_t newY = static_cast<std::int16_t>(
|
||||
(Quantize(entityPos.y) + 31) & ~31);
|
||||
const std::int16_t newZ = Quantize(entityPos.z);
|
||||
if (newX == xyz[0] && newY == xyz[1] && newZ == xyz[2]) return;
|
||||
xyz[0] = newX;
|
||||
xyz[1] = newY;
|
||||
xyz[2] = newZ;
|
||||
std::memset(cells, 1, sizeof(cells));
|
||||
if (aas != nullptr) {
|
||||
std::uint8_t reachable[64];
|
||||
std::memset(reachable, 1, sizeof(reachable));
|
||||
aas->ClipGridToAreas(idVec3(static_cast<float>(xyz[0]),
|
||||
static_cast<float>(xyz[1]), static_cast<float>(xyz[2])),
|
||||
areaNum, travelFlags, 32, 8, reachable);
|
||||
for (int index = 0; index < 64; ++index)
|
||||
if (reachable[index] == 0) cells[index] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
float idDeferredVisGrid::GetGridVisibility(const idVec3& worldPoint,
|
||||
idDeferredVisCache& cache, const float* const staggerZ,
|
||||
const int flags, idColor*, idColor*) {
|
||||
std::uint16_t start[3] = {
|
||||
static_cast<std::uint16_t>(Quantize(worldPoint.x)),
|
||||
static_cast<std::uint16_t>(Quantize(worldPoint.y)),
|
||||
static_cast<std::uint16_t>(Quantize(
|
||||
staggerZ != nullptr ? *staggerZ : worldPoint.z))
|
||||
};
|
||||
int resolved = 0;
|
||||
int visible = 0;
|
||||
for (int index = 0; index < 64; ++index) {
|
||||
if (cells[index] == 0) continue;
|
||||
const int x = index & 7;
|
||||
const int y = index >> 3;
|
||||
std::uint16_t end[3] = {
|
||||
static_cast<std::uint16_t>(static_cast<std::int16_t>(
|
||||
xyz[0] - 128 + x * 32)),
|
||||
static_cast<std::uint16_t>(static_cast<std::int16_t>(
|
||||
xyz[1] - 128 + y * 32)),
|
||||
static_cast<std::uint16_t>(xyz[2])
|
||||
};
|
||||
const std::uint8_t state = cache.GetVisQuery(start, end, flags);
|
||||
if (state == idDeferredVisCache::VIS_CLEAR) {
|
||||
++visible;
|
||||
++resolved;
|
||||
} else if (state == idDeferredVisCache::VIS_BLOCKED) {
|
||||
++resolved;
|
||||
}
|
||||
}
|
||||
return resolved > 0 ? static_cast<float>(visible) / resolved : 0.0f;
|
||||
}
|
||||
|
||||
void idDeferredVisGrid::DebugDrawGrid() const {
|
||||
}
|
||||
@@ -1,39 +1,113 @@
|
||||
#pragma once
|
||||
|
||||
// Reconstructed C++ declarations from IDA Local Types and PDB/DIA metadata.
|
||||
// Original PDB header: w:\tech5\engine\gamelib\aas2\deferredviscache.h
|
||||
// Recovered logical types: 1
|
||||
// Signatures retain Xbox 360 ABI evidence and may still require manual review.
|
||||
#include "gamelib/physics/clip.h"
|
||||
#include "idlib/containers/list.h"
|
||||
#include "idlib/math/vector.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
// IDA Local Type ordinal 17280; PDB kind: class.
|
||||
class __declspec(align(4)) idDeferredVisCache
|
||||
{
|
||||
public:
|
||||
idList<idDeferredVisQuery *,106> hashTable;
|
||||
idList<idClipQuery,106> clipQueries;
|
||||
int curClipQueryIdx;
|
||||
deferredVisList_t leastRecentlyUsedList;
|
||||
deferredVisList_t priorityList;
|
||||
idClip *clip;
|
||||
idDeferredVisQuery *queryList;
|
||||
idDeferredVisQuery *queryPool;
|
||||
int expiredIndex;
|
||||
int numFrames;
|
||||
int numCreatedThisFrame;
|
||||
int numBumped;
|
||||
int numQueriesIssued;
|
||||
int totalQueriesIssued;
|
||||
int numPurged;
|
||||
int numExpired;
|
||||
int numCacheHits;
|
||||
int totalCacheHits;
|
||||
int maxCreatedInAFrame;
|
||||
int recentNumFrames;
|
||||
int recentTotalCacheHits;
|
||||
int recentTotalQueriesIssued;
|
||||
float recentCacheHitRatio;
|
||||
float recentAvgHitRatio;
|
||||
bool initialized;
|
||||
bool alreadyWarned;
|
||||
class idAAS2;
|
||||
class idColor;
|
||||
|
||||
struct idDeferredVisQuery {
|
||||
idDeferredVisQuery* nextInHash;
|
||||
idDeferredVisQuery* prev;
|
||||
idDeferredVisQuery* next;
|
||||
std::int16_t start[3];
|
||||
std::int16_t end[3];
|
||||
std::int16_t frameCounter;
|
||||
std::uint8_t queryIdx;
|
||||
std::uint8_t state;
|
||||
int pad;
|
||||
};
|
||||
|
||||
struct deferredVisList_t {
|
||||
idDeferredVisQuery* headNode;
|
||||
idDeferredVisQuery* tailNode;
|
||||
int numElements;
|
||||
};
|
||||
|
||||
class idDeferredVisCache {
|
||||
public:
|
||||
enum queryState_t : std::uint8_t {
|
||||
VIS_INVALID = 0,
|
||||
VIS_PENDING = 1,
|
||||
VIS_SUBMITTED = 2,
|
||||
VIS_BLOCKED = 3,
|
||||
VIS_CLEAR = 4
|
||||
};
|
||||
|
||||
idDeferredVisCache();
|
||||
~idDeferredVisCache();
|
||||
void Init(idClip* inClip);
|
||||
void Shutdown();
|
||||
void UpdateCollisionQueries();
|
||||
void SubmitCollisionQueries();
|
||||
std::uint8_t GetVisQuery(const std::uint16_t start[3],
|
||||
const std::uint16_t end[3], int flags);
|
||||
void PrintDebugInfo() const;
|
||||
|
||||
idList<idDeferredVisQuery*, 106> hashTable;
|
||||
idList<idClipQuery, 106> clipQueries;
|
||||
int curClipQueryIdx;
|
||||
deferredVisList_t leastRecentlyUsedList;
|
||||
deferredVisList_t priorityList;
|
||||
idClip* clip;
|
||||
idDeferredVisQuery* queryList;
|
||||
idDeferredVisQuery* queryPool;
|
||||
int expiredIndex;
|
||||
int numFrames;
|
||||
int numCreatedThisFrame;
|
||||
int numBumped;
|
||||
int numQueriesIssued;
|
||||
int totalQueriesIssued;
|
||||
int numPurged;
|
||||
int numExpired;
|
||||
int numCacheHits;
|
||||
int totalCacheHits;
|
||||
int maxCreatedInAFrame;
|
||||
int recentNumFrames;
|
||||
int recentTotalCacheHits;
|
||||
int recentTotalQueriesIssued;
|
||||
float recentCacheHitRatio;
|
||||
float recentAvgHitRatio;
|
||||
bool initialized;
|
||||
bool alreadyWarned;
|
||||
|
||||
private:
|
||||
idDeferredVisQuery* AllocNode();
|
||||
void FreeLinkedList(deferredVisList_t& list);
|
||||
void RemoveNoDelete(deferredVisList_t& list,
|
||||
idDeferredVisQuery* node);
|
||||
void AddToFront(deferredVisList_t& list,
|
||||
idDeferredVisQuery* node);
|
||||
void RemoveFromHash(idDeferredVisQuery* node);
|
||||
void FreeNode(idDeferredVisQuery* node);
|
||||
};
|
||||
|
||||
class idDeferredVisGrid {
|
||||
public:
|
||||
idDeferredVisGrid();
|
||||
void Init(const idVec3& entityPos);
|
||||
void Update(const idVec3& entityPos, const idAAS2* aas,
|
||||
int areaNum, int travelFlags);
|
||||
float GetGridVisibility(const idVec3& worldPoint,
|
||||
idDeferredVisCache& cache, const float* staggerZ,
|
||||
int flags, idColor* missDebugColor = nullptr,
|
||||
idColor* hitDebugColor = nullptr);
|
||||
void DebugDrawGrid() const;
|
||||
|
||||
std::uint8_t cells[64];
|
||||
std::int16_t xyz[3];
|
||||
};
|
||||
|
||||
static_assert(sizeof(idDeferredVisGrid) == 70,
|
||||
"Recovered deferred visibility grid ABI changed");
|
||||
#if INTPTR_MAX == INT32_MAX
|
||||
static_assert(sizeof(idDeferredVisQuery) == 32,
|
||||
"Recovered deferred visibility query ABI changed");
|
||||
static_assert(sizeof(deferredVisList_t) == 12,
|
||||
"Recovered deferred visibility list ABI changed");
|
||||
static_assert(sizeof(idDeferredVisCache) == 140,
|
||||
"Recovered deferred visibility cache ABI changed");
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,355 @@
|
||||
#include "gamelib/aas2/obstacleavoidance.h"
|
||||
|
||||
#include "gamelib/aas2/obstacleavoidancemanagerlocal.h"
|
||||
#include "gamelib/effects/weapontracemanager.h"
|
||||
#include "idlib/bv/box.h"
|
||||
#include "idlib/filesystem/filesystem.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr int MAX_OBSTACLES = 128;
|
||||
constexpr int MAX_VERTICES = 0x300;
|
||||
constexpr int MAX_EDGES = 0x200;
|
||||
constexpr int MAX_CORNERS = 16;
|
||||
constexpr int MAX_TRACES = 256;
|
||||
constexpr int TEMP_MEMORY_SIZE = 0x10000;
|
||||
constexpr char QUERY_IDENTIFIER[] = "ObstacleAvoidanceQuery";
|
||||
|
||||
struct obstacleTestQuery_t {
|
||||
int time;
|
||||
int gameMsPerFrame;
|
||||
idBounds bbox;
|
||||
idVec3 gravity;
|
||||
float obstacleRadius;
|
||||
float frameMoveDist;
|
||||
float circleCornerRadius;
|
||||
int flags;
|
||||
};
|
||||
|
||||
obstacleTestQuery_t testQuery{};
|
||||
|
||||
template<typename type_t>
|
||||
bool WriteValue(idFile& file, const type_t& value) {
|
||||
return file.Write(&value, static_cast<unsigned int>(sizeof(value)))
|
||||
== sizeof(value);
|
||||
}
|
||||
|
||||
template<typename type_t>
|
||||
bool ReadValue(idFile& file, type_t& value) {
|
||||
return file.Read(&value, static_cast<unsigned int>(sizeof(value)))
|
||||
== sizeof(value);
|
||||
}
|
||||
|
||||
bool WriteArray(idFile& file, const void* const data,
|
||||
const std::size_t elementSize, const int count) {
|
||||
const std::size_t byteCount = elementSize * static_cast<std::size_t>(count);
|
||||
return byteCount == 0 || file.Write(data,
|
||||
static_cast<unsigned int>(byteCount)) == byteCount;
|
||||
}
|
||||
|
||||
bool ReadArray(idFile& file, void* const data,
|
||||
const std::size_t elementSize, const int count) {
|
||||
const std::size_t byteCount = elementSize * static_cast<std::size_t>(count);
|
||||
return byteCount == 0 || file.Read(data,
|
||||
static_cast<unsigned int>(byteCount)) == byteCount;
|
||||
}
|
||||
|
||||
void RunObstacleAvoidanceJob(obstacleParms_t& parms) {
|
||||
std::vector<std::uint8_t> scratch(TEMP_MEMORY_SIZE);
|
||||
idObstacleAvoidanceCore core;
|
||||
core.FindPathAroundObstacles(*parms.path,
|
||||
parms.debugLines, parms.maxDebugLines,
|
||||
parms.debugText, parms.maxDebugText,
|
||||
scratch.data(), static_cast<int>(scratch.size()),
|
||||
parms.bbox, parms.gravity, parms.lastDir, parms.lastCorner,
|
||||
parms.obstacleRadius, parms.frameMoveDist, parms.cornerCircleRadius,
|
||||
parms.flags, *parms.route, parms.obstacles, parms.numObstacles,
|
||||
parms.vertices, parms.numVertices, parms.edges, parms.numEdges,
|
||||
parms.corners, parms.numCorners, parms.traces, parms.numTraces,
|
||||
parms.traceFractions);
|
||||
parms.path->done = true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
idObstacleAvoidance::idObstacleAvoidance()
|
||||
: buffers(nullptr), lastDir(), lastCorner(), lastUpdateTime(-1) {
|
||||
}
|
||||
|
||||
idObstacleAvoidance::~idObstacleAvoidance() {
|
||||
obstacleAvoidanceManagerLocal.FreeObstacleBuffers(this);
|
||||
}
|
||||
|
||||
void idObstacleAvoidance::AllocBuffers() {
|
||||
obstacleAvoidanceManagerLocal.AllocObstacleBuffers(this);
|
||||
}
|
||||
|
||||
obstacleVertex_t* idObstacleAvoidance::GetVertexArray() {
|
||||
return buffers != nullptr ? buffers->vertices : nullptr;
|
||||
}
|
||||
|
||||
int idObstacleAvoidance::GetMaxVertices() const {
|
||||
return buffers != nullptr ? MAX_VERTICES : 0;
|
||||
}
|
||||
|
||||
void idObstacleAvoidance::SetNumVertices(const int num) {
|
||||
if (buffers != nullptr) buffers->numVertices = num;
|
||||
}
|
||||
|
||||
obstacleEdge_t* idObstacleAvoidance::GetEdgeArray() {
|
||||
return buffers != nullptr ? buffers->edges : nullptr;
|
||||
}
|
||||
|
||||
int idObstacleAvoidance::GetMaxEdges() const {
|
||||
return buffers != nullptr ? MAX_EDGES : 0;
|
||||
}
|
||||
|
||||
void idObstacleAvoidance::SetNumEdges(const int num) {
|
||||
if (buffers != nullptr) buffers->numEdges = num;
|
||||
}
|
||||
|
||||
void idObstacleAvoidance::AddObstacle(const idBox& box,
|
||||
const idSpawnId id, const bool soft) {
|
||||
if (buffers == nullptr || buffers->numObstacles >= MAX_OBSTACLES) return;
|
||||
obstacleBox_t& obstacle = buffers->obstacles[buffers->numObstacles++];
|
||||
obstacle.axis = box.axis;
|
||||
obstacle.center = box.center;
|
||||
obstacle.extents = box.extents;
|
||||
obstacle.id = id.Get();
|
||||
obstacle.soft = soft;
|
||||
}
|
||||
|
||||
void idObstacleAvoidance::AddObstacle(const idBounds& bounds,
|
||||
const idVec3& origin, const idMat3& axis, const idSpawnId id,
|
||||
const bool soft) {
|
||||
AddObstacle(idBox(bounds, origin, axis), id, soft);
|
||||
}
|
||||
|
||||
void idObstacleAvoidance::Restart(const idVec3& start, const idVec3& goal) {
|
||||
if (buffers == nullptr || buffers->path == nullptr) return;
|
||||
obstaclePath_t& path = *buffers->path;
|
||||
path = obstaclePath_t();
|
||||
path.seekPos[0] = goal;
|
||||
path.seekPos[1] = goal;
|
||||
path.startPosOutsideObstacles = start;
|
||||
path.seekPosOutsideObstacles = start;
|
||||
path.wallCorners[0] = goal;
|
||||
path.wallCorners[1] = goal;
|
||||
path.firstObstacle = -1;
|
||||
path.startPosObstacle = -1;
|
||||
path.seekPosObstacle = -1;
|
||||
path.hasValidPath = true;
|
||||
path.startPosValid = true;
|
||||
path.seekPosValid = true;
|
||||
path.done = false;
|
||||
for (obstaclePath_t::radar_t& radar : path.radar) {
|
||||
radar.direction.Zero();
|
||||
radar.obstacle = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void idObstacleAvoidance::ProjectTopDown(idVec3& point,
|
||||
const idVec3& startPos, const float scale) const {
|
||||
// The recovered implementation projects through the local player's view.
|
||||
// GameLib has no renderer/game-world dependency, so retain the same local
|
||||
// scale operation for callers that provide debug geometry.
|
||||
point = startPos + (point - startPos) * scale;
|
||||
}
|
||||
|
||||
void idObstacleAvoidance::DrawBox(const idVec3&, const float) const {
|
||||
// Drawing is performed by the engine-facing debug consumer. Keeping it
|
||||
// outside GameLib prevents the recovered navigation code from owning RW.
|
||||
}
|
||||
|
||||
bool idObstacleAvoidance::SaveQuery(const char* const fileName) const {
|
||||
if (fileName == nullptr || *fileName == '\0' || buffers == nullptr) {
|
||||
return false;
|
||||
}
|
||||
idFileLocal file(fileSystem->OpenFileWrite(fileName, FSPATH_SAVE));
|
||||
if (file.file == nullptr) return false;
|
||||
if (file->WriteString(QUERY_IDENTIFIER) == 0) return false;
|
||||
return WriteValue(*file.file, testQuery.time)
|
||||
&& WriteValue(*file.file, testQuery.gameMsPerFrame)
|
||||
&& WriteValue(*file.file, testQuery.bbox)
|
||||
&& WriteValue(*file.file, testQuery.gravity)
|
||||
&& WriteValue(*file.file, testQuery.obstacleRadius)
|
||||
&& WriteValue(*file.file, testQuery.frameMoveDist)
|
||||
&& WriteValue(*file.file, testQuery.circleCornerRadius)
|
||||
&& WriteValue(*file.file, testQuery.flags)
|
||||
&& WriteValue(*file.file, buffers->numObstacles)
|
||||
&& WriteValue(*file.file, buffers->numVertices)
|
||||
&& WriteValue(*file.file, buffers->numEdges)
|
||||
&& WriteValue(*file.file, buffers->numCorners)
|
||||
&& WriteValue(*file.file, *buffers->route)
|
||||
&& WriteArray(*file.file, buffers->obstacles,
|
||||
sizeof(*buffers->obstacles), buffers->numObstacles)
|
||||
&& WriteArray(*file.file, buffers->vertices,
|
||||
sizeof(*buffers->vertices), buffers->numVertices)
|
||||
&& WriteArray(*file.file, buffers->edges,
|
||||
sizeof(*buffers->edges), buffers->numEdges)
|
||||
&& WriteArray(*file.file, buffers->corners,
|
||||
sizeof(*buffers->corners), buffers->numCorners);
|
||||
}
|
||||
|
||||
bool idObstacleAvoidance::FindPathAroundObstacles(obstaclePath_t& path,
|
||||
const int time, const int gameMsPerFrame, const idBounds& bbox,
|
||||
const idVec3& gravity, const float obstacleRadius,
|
||||
const float frameMoveDist, const float cornerCircleRadius,
|
||||
const int queryFlags, const obstacleRoute_t& route,
|
||||
obstacleTraceSet_t* const traceSet) {
|
||||
const idVec3 start = route.numAreas > 0
|
||||
? route.areas[0].start : idVec3();
|
||||
const idVec3 goal = route.numAreas > 0
|
||||
? route.areas[route.numAreas - 1].end : start;
|
||||
path = obstaclePath_t();
|
||||
path.seekPos[0] = goal;
|
||||
path.seekPos[1] = goal;
|
||||
path.numSeekPos = 1;
|
||||
path.startPosOutsideObstacles = start;
|
||||
path.seekPosOutsideObstacles = goal;
|
||||
path.wallCorners[0] = goal;
|
||||
path.wallCorners[1] = goal;
|
||||
path.hasValidPath = true;
|
||||
path.startPosValid = true;
|
||||
path.seekPosValid = true;
|
||||
path.done = true;
|
||||
if (buffers == nullptr) return true;
|
||||
|
||||
testQuery.time = time;
|
||||
testQuery.gameMsPerFrame = gameMsPerFrame;
|
||||
testQuery.bbox = bbox;
|
||||
testQuery.gravity = gravity;
|
||||
testQuery.obstacleRadius = obstacleRadius;
|
||||
testQuery.frameMoveDist = frameMoveDist;
|
||||
testQuery.circleCornerRadius = cornerCircleRadius;
|
||||
testQuery.flags = queryFlags;
|
||||
|
||||
const int elapsed = time - lastUpdateTime;
|
||||
const bool preservePrevious = elapsed > 0
|
||||
&& elapsed < 3 * gameMsPerFrame;
|
||||
if (preservePrevious && buffers->path != nullptr) {
|
||||
path = *buffers->path;
|
||||
lastDir = path.seekPos[0] - path.startPosOutsideObstacles;
|
||||
lastCorner = path.nextCorner;
|
||||
} else {
|
||||
lastDir.Zero();
|
||||
lastCorner.Set(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
}
|
||||
lastUpdateTime = time;
|
||||
*buffers->route = route;
|
||||
|
||||
if (traceSet != nullptr) {
|
||||
const int traceCount = (std::max)(0,
|
||||
(std::min)(MAX_TRACES, traceSet->numTraces));
|
||||
std::memcpy(buffers->traces, traceSet->traces,
|
||||
sizeof(obstacleTrace_t) * traceCount);
|
||||
buffers->numTraces = traceCount;
|
||||
if (preservePrevious) {
|
||||
const int previousCount = (std::max)(0,
|
||||
(std::min)(MAX_TRACES, traceSet->prevNumTraces));
|
||||
std::memcpy(buffers->traceFractions, traceSet->traceFractions,
|
||||
sizeof(float) * previousCount);
|
||||
}
|
||||
}
|
||||
|
||||
obstacleParms_t& parms = *buffers->parms;
|
||||
parms.bbox = bbox;
|
||||
parms.gravity = gravity;
|
||||
parms.lastDir = lastDir;
|
||||
parms.lastCorner = lastCorner;
|
||||
parms.obstacleRadius = obstacleRadius;
|
||||
parms.frameMoveDist = frameMoveDist;
|
||||
parms.cornerCircleRadius = cornerCircleRadius;
|
||||
parms.flags = queryFlags;
|
||||
parms.numObstacles = buffers->numObstacles;
|
||||
parms.numVertices = buffers->numVertices;
|
||||
parms.numEdges = buffers->numEdges;
|
||||
parms.numCorners = buffers->numCorners;
|
||||
parms.numTraces = buffers->numTraces;
|
||||
parms.route = buffers->route;
|
||||
parms.obstacles = buffers->obstacles;
|
||||
parms.vertices = buffers->vertices;
|
||||
parms.edges = buffers->edges;
|
||||
parms.corners = buffers->corners;
|
||||
parms.traces = buffers->traces;
|
||||
parms.traceFractions = buffers->traceFractions;
|
||||
parms.path = buffers->path;
|
||||
|
||||
std::vector<avoidDebugLine_t> debugLines(2048);
|
||||
std::vector<avoidDebugText_t> debugText(256);
|
||||
parms.debugLines = debugLines.data();
|
||||
parms.maxDebugLines = static_cast<int>(debugLines.size());
|
||||
parms.debugText = debugText.data();
|
||||
parms.maxDebugText = static_cast<int>(debugText.size());
|
||||
parms.path->done = false;
|
||||
parms.path->useRadarForward = path.useRadarForward;
|
||||
parms.path->radarForward = path.radarForward;
|
||||
RunObstacleAvoidanceJob(parms);
|
||||
parms.debugLines = nullptr;
|
||||
parms.debugText = nullptr;
|
||||
|
||||
path = *buffers->path;
|
||||
if (traceSet != nullptr) {
|
||||
traceSet->prevNumTraces = buffers->numTraces;
|
||||
std::memcpy(traceSet->prevTraceNumbers, traceSet->traceNumbers,
|
||||
sizeof(int) * buffers->numTraces);
|
||||
std::memcpy(traceSet->traceFractions, buffers->traceFractions,
|
||||
sizeof(float) * buffers->numTraces);
|
||||
}
|
||||
return path.hasValidPath;
|
||||
}
|
||||
|
||||
bool idObstacleAvoidance::TestQuery(const char* const fileName) {
|
||||
if (fileName == nullptr || *fileName == '\0') return false;
|
||||
AllocBuffers();
|
||||
if (buffers == nullptr) return false;
|
||||
idFileLocal file(fileSystem->OpenFileRead(fileName, true, false));
|
||||
if (file.file == nullptr) return false;
|
||||
idStr identifier;
|
||||
if (file->ReadString(identifier) == 0
|
||||
|| idStr::Cmp(identifier.c_str(), QUERY_IDENTIFIER) != 0) {
|
||||
return false;
|
||||
}
|
||||
if (!ReadValue(*file.file, testQuery.time)
|
||||
|| !ReadValue(*file.file, testQuery.gameMsPerFrame)
|
||||
|| !ReadValue(*file.file, testQuery.bbox)
|
||||
|| !ReadValue(*file.file, testQuery.gravity)
|
||||
|| !ReadValue(*file.file, testQuery.obstacleRadius)
|
||||
|| !ReadValue(*file.file, testQuery.frameMoveDist)
|
||||
|| !ReadValue(*file.file, testQuery.circleCornerRadius)
|
||||
|| !ReadValue(*file.file, testQuery.flags)
|
||||
|| !ReadValue(*file.file, buffers->numObstacles)
|
||||
|| !ReadValue(*file.file, buffers->numVertices)
|
||||
|| !ReadValue(*file.file, buffers->numEdges)
|
||||
|| !ReadValue(*file.file, buffers->numCorners)) {
|
||||
return false;
|
||||
}
|
||||
if (buffers->numObstacles < 0 || buffers->numObstacles > MAX_OBSTACLES
|
||||
|| buffers->numVertices < 0 || buffers->numVertices > MAX_VERTICES
|
||||
|| buffers->numEdges < 0 || buffers->numEdges > MAX_EDGES
|
||||
|| buffers->numCorners < 0 || buffers->numCorners > MAX_CORNERS) {
|
||||
return false;
|
||||
}
|
||||
if (!ReadValue(*file.file, *buffers->route)
|
||||
|| !ReadArray(*file.file, buffers->obstacles,
|
||||
sizeof(*buffers->obstacles), buffers->numObstacles)
|
||||
|| !ReadArray(*file.file, buffers->vertices,
|
||||
sizeof(*buffers->vertices), buffers->numVertices)
|
||||
|| !ReadArray(*file.file, buffers->edges,
|
||||
sizeof(*buffers->edges), buffers->numEdges)
|
||||
|| !ReadArray(*file.file, buffers->corners,
|
||||
sizeof(*buffers->corners), buffers->numCorners)) {
|
||||
return false;
|
||||
}
|
||||
obstaclePath_t path;
|
||||
return FindPathAroundObstacles(path, testQuery.time,
|
||||
testQuery.gameMsPerFrame, testQuery.bbox, testQuery.gravity,
|
||||
testQuery.obstacleRadius, testQuery.frameMoveDist,
|
||||
testQuery.circleCornerRadius, testQuery.flags, *buffers->route,
|
||||
nullptr);
|
||||
}
|
||||
@@ -1,41 +1,67 @@
|
||||
#pragma once
|
||||
|
||||
// Reconstructed C++ declarations from IDA Local Types and PDB/DIA metadata.
|
||||
// Original PDB header: w:\tech5\engine\gamelib\aas2\obstacleavoidance.h
|
||||
// Recovered logical types: 3
|
||||
// Signatures retain Xbox 360 ABI evidence and may still require manual review.
|
||||
#include "idlib/math/vector.h"
|
||||
#include "idlib/bv/bounds.h"
|
||||
#include "idlib/math/matrix.h"
|
||||
#include "gamelib/aas2/obstacleavoidancecore.h"
|
||||
|
||||
struct idObstacleBuffers;
|
||||
class idBox;
|
||||
class idSpawnId;
|
||||
|
||||
// IDA Local Type ordinal 1705; PDB kind: enum.
|
||||
enum vaiObstacleAvoidance_t : __int32
|
||||
{
|
||||
OAT_INIT = 0xFFFFFFFF,
|
||||
OAT_GENERAL = 0x0,
|
||||
OAT_NONE = 0x1,
|
||||
enum vaiObstacleAvoidance_t : int {
|
||||
OAT_INIT = -1,
|
||||
OAT_GENERAL = 0,
|
||||
OAT_NONE = 1
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 2170; PDB kind: enum.
|
||||
enum obstacleAvoidanceFlags_t : __int32
|
||||
{
|
||||
OAFLAG_ASSUME_VALID_AAS_PATH = 0x1,
|
||||
OAFLAG_MOVE_AROUND_SEEK_POS_OBSTACLES = 0x2,
|
||||
OAFLAG_CIRCLE_CORNERS = 0x4,
|
||||
OAFLAG_UPDATE_RADAR = 0x8,
|
||||
OAFLAG_REPLACE_EXISTING_QUERY = 0x10,
|
||||
OAFLAG_SHOW_PATH_TREE = 0x20,
|
||||
OAFLAG_DEBUG = 0x40,
|
||||
OAFLAG_DEBUG_BREAK = 0x80,
|
||||
OAFLAG_USE_LINE_INTERSECTION_EPSILON = 0x100,
|
||||
OAFLAG_SINGLE_PUSH = 0x200,
|
||||
OAFLAG_IGNORE_AIS = 0x400,
|
||||
enum obstacleAvoidanceFlags_t : int {
|
||||
OAFLAG_ASSUME_VALID_AAS_PATH = 0x001,
|
||||
OAFLAG_MOVE_AROUND_SEEK_POS_OBSTACLES = 0x002,
|
||||
OAFLAG_CIRCLE_CORNERS = 0x004,
|
||||
OAFLAG_UPDATE_RADAR = 0x008,
|
||||
OAFLAG_REPLACE_EXISTING_QUERY = 0x010,
|
||||
OAFLAG_SHOW_PATH_TREE = 0x020,
|
||||
OAFLAG_DEBUG = 0x040,
|
||||
OAFLAG_DEBUG_BREAK = 0x080,
|
||||
OAFLAG_USE_LINE_INTERSECTION_EPSILON = 0x100,
|
||||
OAFLAG_SINGLE_PUSH = 0x200,
|
||||
OAFLAG_IGNORE_AIS = 0x400
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 14971; PDB kind: class.
|
||||
class idObstacleAvoidance
|
||||
{
|
||||
class idObstacleAvoidance {
|
||||
public:
|
||||
idObstacleBuffers *buffers;
|
||||
idVec3 lastDir;
|
||||
idVec4 lastCorner;
|
||||
int lastUpdateTime;
|
||||
idObstacleAvoidance();
|
||||
~idObstacleAvoidance();
|
||||
void AllocBuffers();
|
||||
obstacleVertex_t* GetVertexArray();
|
||||
int GetMaxVertices() const;
|
||||
void SetNumVertices(int num);
|
||||
obstacleEdge_t* GetEdgeArray();
|
||||
int GetMaxEdges() const;
|
||||
void SetNumEdges(int num);
|
||||
void AddObstacle(const idBox& box, idSpawnId id, bool soft);
|
||||
void AddObstacle(const idBounds& bounds, const idVec3& origin,
|
||||
const idMat3& axis, idSpawnId id, bool soft);
|
||||
void Restart(const idVec3& start, const idVec3& goal);
|
||||
void ProjectTopDown(idVec3& point, const idVec3& startPos,
|
||||
float scale) const;
|
||||
void DrawBox(const idVec3& startPos, float radius) const;
|
||||
bool SaveQuery(const char* fileName) const;
|
||||
bool FindPathAroundObstacles(obstaclePath_t& path, int time,
|
||||
int gameMsPerFrame, const idBounds& bbox, const idVec3& gravity,
|
||||
float obstacleRadius, float frameMoveDist, float cornerCircleRadius,
|
||||
int flags, const obstacleRoute_t& route,
|
||||
obstacleTraceSet_t* traceSet);
|
||||
bool TestQuery(const char* fileName);
|
||||
|
||||
idObstacleBuffers* buffers;
|
||||
idVec3 lastDir;
|
||||
idVec4 lastCorner;
|
||||
int lastUpdateTime;
|
||||
};
|
||||
|
||||
#if defined(_WIN32) && !defined(_WIN64)
|
||||
static_assert(sizeof(idObstacleAvoidance) == 36,
|
||||
"Recovered idObstacleAvoidance ABI changed");
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,347 @@
|
||||
#include "gamelib/aas2/obstacleavoidancecore.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
|
||||
float Cross2(const idVec2& first, const idVec2& second) {
|
||||
return first.x * second.y - first.y * second.x;
|
||||
}
|
||||
|
||||
idVec2 Sub2(const idVec2& first, const idVec2& second) {
|
||||
return idVec2(first.x - second.x, first.y - second.y);
|
||||
}
|
||||
|
||||
float Dot2(const idVec2& first, const idVec2& second) {
|
||||
return first.x * second.x + first.y * second.y;
|
||||
}
|
||||
|
||||
float Length2(const idVec2& value) {
|
||||
return std::sqrt(Dot2(value, value));
|
||||
}
|
||||
|
||||
idVec2 Normalize2(const idVec2& value) {
|
||||
const float length = Length2(value);
|
||||
return length > 1.0e-6f
|
||||
? idVec2(value.x / length, value.y / length) : idVec2(0.0f, 0.0f);
|
||||
}
|
||||
|
||||
float SegmentPointDistanceSqr(const idVec2& start, const idVec2& end,
|
||||
const idVec2& point) {
|
||||
const idVec2 delta = Sub2(end, start);
|
||||
const float lengthSqr = Dot2(delta, delta);
|
||||
float fraction = lengthSqr > 0.0f
|
||||
? Dot2(Sub2(point, start), delta) / lengthSqr : 0.0f;
|
||||
fraction = (std::max)(0.0f, (std::min)(1.0f, fraction));
|
||||
const idVec2 closest(start.x + delta.x * fraction,
|
||||
start.y + delta.y * fraction);
|
||||
return Dot2(Sub2(point, closest), Sub2(point, closest));
|
||||
}
|
||||
|
||||
bool SegmentIntersection(const idVec2& a, const idVec2& b,
|
||||
const idVec2& c, const idVec2& d) {
|
||||
const idVec2 r = Sub2(b, a);
|
||||
const idVec2 s = Sub2(d, c);
|
||||
const float denominator = Cross2(r, s);
|
||||
if (std::fabs(denominator) < 1.0e-6f) return false;
|
||||
const idVec2 delta = Sub2(c, a);
|
||||
const float t = Cross2(delta, s) / denominator;
|
||||
const float u = Cross2(delta, r) / denominator;
|
||||
return t >= 0.0f && t <= 1.0f && u >= 0.0f && u <= 1.0f;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
idObstacleAvoidanceCore::idObstacleAvoidanceCore()
|
||||
: bspNodes(nullptr), numBspNodes(0), obstacleIndices(nullptr),
|
||||
expandedObstacles(nullptr), maxExpandedObstacles(0),
|
||||
numExpandedObstacles(0), windingPoints(nullptr), maxWindingPoints(0),
|
||||
numWindingPoints(0), tempMemory(nullptr), tempMemorySize(0),
|
||||
tempMemoryStart(nullptr), tempMemoryEnd(nullptr), pathNodes(nullptr),
|
||||
maxPathNodes(0), numPathNodes(0), debugLines(nullptr),
|
||||
maxDebugLines(0), numDebugLines(0), debugText(nullptr),
|
||||
maxDebugText(0), numDebugText(0), inputTransformValid(false),
|
||||
inputTransform(), flags(0) {
|
||||
}
|
||||
|
||||
idObstacleAvoidanceCore::~idObstacleAvoidanceCore() = default;
|
||||
|
||||
void idObstacleAvoidanceCore::AddDebugLine(const avoidColor_t color,
|
||||
const idVec2& start, const idVec2& end) {
|
||||
if (debugLines == nullptr || numDebugLines >= maxDebugLines - 1) return;
|
||||
avoidDebugLine_t& line = debugLines[numDebugLines++];
|
||||
line.color = color;
|
||||
line.start.Set(start.x, start.y, 0.0f);
|
||||
line.end.Set(end.x, end.y, 0.0f);
|
||||
}
|
||||
|
||||
void idObstacleAvoidanceCore::AddDebugInt(const avoidColor_t color,
|
||||
const idVec2& position, const int value) {
|
||||
if (debugText == nullptr || numDebugText >= maxDebugText - 1) return;
|
||||
avoidDebugText_t& text = debugText[numDebugText++];
|
||||
std::snprintf(text.text, sizeof(text.text), "%d", value);
|
||||
text.pos.Set(position.x, position.y, 0.0f);
|
||||
text.color = color;
|
||||
}
|
||||
|
||||
int idObstacleAvoidanceCore::BoxParallelProjectionSilhouetteVerts(
|
||||
const idMat3& axis, const idVec3& center, const idVec3& extents,
|
||||
const idVec3& projectionDir, idVec3* const silhouette) {
|
||||
if (silhouette == nullptr) return 0;
|
||||
idVec3 corners[8];
|
||||
int count = 0;
|
||||
for (int x = -1; x <= 1; x += 2) {
|
||||
for (int y = -1; y <= 1; y += 2) {
|
||||
for (int z = -1; z <= 1; z += 2) {
|
||||
corners[count++] = center + axis[0] * (extents.x * x)
|
||||
+ axis[1] * (extents.y * y)
|
||||
+ axis[2] * (extents.z * z);
|
||||
}
|
||||
}
|
||||
}
|
||||
int output = 0;
|
||||
for (int index = 0; index < 8 && output < 6; ++index) {
|
||||
const float facing = (corners[index] - center).Dot(projectionDir);
|
||||
if (facing <= 0.0f) silhouette[output++] = corners[index];
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
void idObstacleAvoidanceCore::GetCirclePoints(const idVec2& center,
|
||||
const float radius, idVec2* const points) {
|
||||
if (points == nullptr) return;
|
||||
constexpr float diagonal = 0.70710678118f;
|
||||
const idVec2 directions[8] = {
|
||||
idVec2(1, 0), idVec2(diagonal, diagonal), idVec2(0, 1),
|
||||
idVec2(-diagonal, diagonal), idVec2(-1, 0),
|
||||
idVec2(-diagonal, -diagonal), idVec2(0, -1),
|
||||
idVec2(diagonal, -diagonal)
|
||||
};
|
||||
for (int index = 0; index < 8; ++index) {
|
||||
points[index].Set(center.x + directions[index].x * radius,
|
||||
center.y + directions[index].y * radius);
|
||||
}
|
||||
}
|
||||
|
||||
idVec3 idObstacleAvoidanceCore::Plane2DFromPoints(const idVec2& start,
|
||||
const idVec2& end, const bool normalize) {
|
||||
return Plane2DFromVecs(start, Sub2(end, start), normalize);
|
||||
}
|
||||
|
||||
idVec3 idObstacleAvoidanceCore::Plane2DFromVecs(const idVec2& start,
|
||||
const idVec2& direction, const bool normalize) {
|
||||
idVec2 normal(-direction.y, direction.x);
|
||||
if (normalize) normal = Normalize2(normal);
|
||||
return idVec3(normal.x, normal.y,
|
||||
-(normal.x * start.x + normal.y * start.y));
|
||||
}
|
||||
|
||||
bool idObstacleAvoidanceCore::PointInsideWinding(const idVec2* const winding,
|
||||
const int numPoints, const idVec2& point, const float epsilon) {
|
||||
if (winding == nullptr || numPoints <= 0) return true;
|
||||
float sign = 0.0f;
|
||||
for (int index = 0; index < numPoints; ++index) {
|
||||
const float side = Cross2(Sub2(winding[(index + 1) % numPoints],
|
||||
winding[index]), Sub2(point, winding[index]));
|
||||
if (std::fabs(side) <= epsilon) continue;
|
||||
if (sign == 0.0f) sign = side;
|
||||
else if ((sign < 0.0f) != (side < 0.0f)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void idObstacleAvoidanceCore::ExpandWinding(const idVec2* const winding,
|
||||
const int numPoints, const float distance, idVec2* const expanded) {
|
||||
if (winding == nullptr || expanded == nullptr || numPoints <= 0) return;
|
||||
for (int index = 0; index < numPoints; ++index) {
|
||||
const idVec2 previous = winding[(index + numPoints - 1) % numPoints];
|
||||
const idVec2 current = winding[index];
|
||||
const idVec2 next = winding[(index + 1) % numPoints];
|
||||
const idVec2 first = Normalize2(idVec2(
|
||||
-(current.y - previous.y), current.x - previous.x));
|
||||
const idVec2 second = Normalize2(idVec2(
|
||||
-(next.y - current.y), next.x - current.x));
|
||||
idVec2 direction = Normalize2(idVec2(first.x + second.x,
|
||||
first.y + second.y));
|
||||
const float denominator = (std::max)(0.1f,
|
||||
std::fabs(Dot2(direction, first)));
|
||||
expanded[index].Set(current.x + direction.x * distance / denominator,
|
||||
current.y + direction.y * distance / denominator);
|
||||
}
|
||||
}
|
||||
|
||||
void idObstacleAvoidanceCore::GetWindingBounds(
|
||||
const idVec2* const winding, const int numPoints,
|
||||
idVec2 bounds[2]) {
|
||||
const float maximum = (std::numeric_limits<float>::max)();
|
||||
bounds[0].Set(maximum, maximum);
|
||||
bounds[1].Set(-maximum, -maximum);
|
||||
for (int index = 0; index < numPoints; ++index) {
|
||||
bounds[0].x = (std::min)(bounds[0].x, winding[index].x);
|
||||
bounds[0].y = (std::min)(bounds[0].y, winding[index].y);
|
||||
bounds[1].x = (std::max)(bounds[1].x, winding[index].x);
|
||||
bounds[1].y = (std::max)(bounds[1].y, winding[index].y);
|
||||
}
|
||||
}
|
||||
|
||||
bool idObstacleAvoidanceCore::WindingRayIntersection(
|
||||
const idVec2* const winding, const int numPoints,
|
||||
const idVec2& start, const idVec2& direction, float& firstScale,
|
||||
float& secondScale, int edgeNums[2]) {
|
||||
firstScale = (std::numeric_limits<float>::max)();
|
||||
secondScale = -(std::numeric_limits<float>::max)();
|
||||
int intersections = 0;
|
||||
for (int index = 0; index < numPoints; ++index) {
|
||||
const idVec2 edgeStart = winding[index];
|
||||
const idVec2 edge = Sub2(winding[(index + 1) % numPoints], edgeStart);
|
||||
const float denominator = Cross2(direction, edge);
|
||||
if (std::fabs(denominator) < 1.0e-6f) continue;
|
||||
const idVec2 delta = Sub2(edgeStart, start);
|
||||
const float scale = Cross2(delta, edge) / denominator;
|
||||
const float edgeScale = Cross2(delta, direction) / denominator;
|
||||
if (edgeScale < 0.0f || edgeScale > 1.0f) continue;
|
||||
if (scale < firstScale) {
|
||||
firstScale = scale;
|
||||
if (edgeNums != nullptr) edgeNums[0] = index;
|
||||
}
|
||||
if (scale > secondScale) {
|
||||
secondScale = scale;
|
||||
if (edgeNums != nullptr) edgeNums[1] = index;
|
||||
}
|
||||
++intersections;
|
||||
}
|
||||
return intersections >= 2;
|
||||
}
|
||||
|
||||
void idObstacleAvoidanceCore::FindPathAroundObstacles(obstaclePath_t& path,
|
||||
avoidDebugLine_t* const outputDebugLines, const int outputMaxDebugLines,
|
||||
avoidDebugText_t* const outputDebugText, const int outputMaxDebugText,
|
||||
std::uint8_t* const temp, const int tempSize, const idBounds&,
|
||||
const idVec3&, const idVec3& lastDir, const idVec4& lastCorner,
|
||||
const float obstacleRadius, const float frameMoveDist,
|
||||
const float, const int queryFlags, const obstacleRoute_t& route,
|
||||
const obstacleBox_t* const obstacles, const int numObstacles,
|
||||
const obstacleVertex_t* const vertices, const int numVertices,
|
||||
const obstacleEdge_t* const edges, const int numEdges,
|
||||
const obstacleCorner_t*, const int, const obstacleTrace_t*,
|
||||
const int, float*) {
|
||||
debugLines = outputDebugLines;
|
||||
maxDebugLines = outputMaxDebugLines;
|
||||
numDebugLines = 0;
|
||||
debugText = outputDebugText;
|
||||
maxDebugText = outputMaxDebugText;
|
||||
numDebugText = 0;
|
||||
tempMemory = temp;
|
||||
tempMemorySize = tempSize;
|
||||
flags = queryFlags;
|
||||
|
||||
const idVec3 start3 = route.numAreas > 0
|
||||
? route.areas[0].start : path.startPosOutsideObstacles;
|
||||
const idVec3 goal3 = route.numAreas > 0
|
||||
? route.areas[route.numAreas - 1].end : path.seekPos[0];
|
||||
const idVec2 start(start3.x, start3.y);
|
||||
const idVec2 goal(goal3.x, goal3.y);
|
||||
path.numSeekPos = 0;
|
||||
path.firstObstacle = -1;
|
||||
path.firstObstaclePosition.Zero();
|
||||
path.startPosOutsideObstacles = start3;
|
||||
path.seekPosOutsideObstacles = goal3;
|
||||
path.startPosObstacle = -1;
|
||||
path.seekPosObstacle = -1;
|
||||
path.targetDist = Length2(Sub2(goal, start));
|
||||
path.pathLength = path.targetDist;
|
||||
path.hasValidPath = true;
|
||||
path.startPosValid = true;
|
||||
path.seekPosValid = true;
|
||||
|
||||
int blocking = -1;
|
||||
float blockingRadius = 0.0f;
|
||||
for (int index = 0; index < numObstacles; ++index) {
|
||||
const obstacleBox_t& obstacle = obstacles[index];
|
||||
if (obstacle.soft && (queryFlags & 0x200) == 0) continue;
|
||||
const float radius = std::sqrt(obstacle.extents.x * obstacle.extents.x
|
||||
+ obstacle.extents.y * obstacle.extents.y) + obstacleRadius;
|
||||
if (SegmentPointDistanceSqr(start, goal,
|
||||
idVec2(obstacle.center.x, obstacle.center.y))
|
||||
< radius * radius) {
|
||||
blocking = index;
|
||||
blockingRadius = radius;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (blocking < 0) {
|
||||
for (int index = 0; index < numEdges; ++index) {
|
||||
const int first = edges[index].vertNums[0];
|
||||
const int second = edges[index].vertNums[1];
|
||||
if (first < 0 || second < 0 || first >= numVertices
|
||||
|| second >= numVertices) continue;
|
||||
const idVec2 edgeStart(vertices[first].position.x,
|
||||
vertices[first].position.y);
|
||||
const idVec2 edgeEnd(vertices[second].position.x,
|
||||
vertices[second].position.y);
|
||||
if (SegmentIntersection(start, goal, edgeStart, edgeEnd)) {
|
||||
blocking = numObstacles + index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (blocking >= 0 && blocking < numObstacles) {
|
||||
const obstacleBox_t& obstacle = obstacles[blocking];
|
||||
const idVec2 center(obstacle.center.x, obstacle.center.y);
|
||||
idVec2 direction = Normalize2(Sub2(goal, start));
|
||||
idVec2 perpendicular(-direction.y, direction.x);
|
||||
const idVec2 candidates[2] = {
|
||||
idVec2(center.x + perpendicular.x * blockingRadius,
|
||||
center.y + perpendicular.y * blockingRadius),
|
||||
idVec2(center.x - perpendicular.x * blockingRadius,
|
||||
center.y - perpendicular.y * blockingRadius)
|
||||
};
|
||||
const float firstLength = Length2(Sub2(candidates[0], start))
|
||||
+ Length2(Sub2(goal, candidates[0]));
|
||||
const float secondLength = Length2(Sub2(candidates[1], start))
|
||||
+ Length2(Sub2(goal, candidates[1]));
|
||||
const idVec2 corner = firstLength <= secondLength
|
||||
? candidates[0] : candidates[1];
|
||||
path.seekPos[0].Set(corner.x, corner.y, start3.z);
|
||||
path.seekPos[1] = goal3;
|
||||
path.numSeekPos = 2;
|
||||
path.firstObstacle = obstacle.id;
|
||||
path.firstObstaclePosition = obstacle.center;
|
||||
path.pathLength = (std::min)(firstLength, secondLength);
|
||||
path.nextCorner.Set(corner.x, corner.y, start3.z,
|
||||
static_cast<float>(obstacle.id));
|
||||
AddDebugLine(COLOR_RED, start, corner);
|
||||
AddDebugLine(COLOR_GREEN, corner, goal);
|
||||
} else if (blocking >= numObstacles) {
|
||||
path.hasValidPath = false;
|
||||
path.seekPos[0] = start3;
|
||||
path.numSeekPos = 1;
|
||||
} else {
|
||||
path.seekPos[0] = goal3;
|
||||
path.numSeekPos = 1;
|
||||
path.nextCorner.Set(goal3.x, goal3.y, goal3.z, -1.0f);
|
||||
AddDebugLine(COLOR_GREEN, start, goal);
|
||||
}
|
||||
path.done = true;
|
||||
path.useRadarForward = Length2(idVec2(lastDir.x, lastDir.y)) > 0.0f;
|
||||
path.radarForward = lastDir;
|
||||
if (lastCorner.w >= 0.0f && path.firstObstacle < 0) {
|
||||
path.nextCorner = lastCorner;
|
||||
}
|
||||
if (frameMoveDist > 0.0f && path.numSeekPos > 0) {
|
||||
const idVec2 move = Sub2(idVec2(path.seekPos[0].x,
|
||||
path.seekPos[0].y), start);
|
||||
const float moveLength = Length2(move);
|
||||
if (moveLength > frameMoveDist) {
|
||||
const float scale = frameMoveDist / moveLength;
|
||||
path.seekPos[0].x = start.x + move.x * scale;
|
||||
path.seekPos[0].y = start.y + move.y * scale;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,98 +1,299 @@
|
||||
#pragma once
|
||||
|
||||
// Reconstructed C++ declarations from IDA Local Types and PDB/DIA metadata.
|
||||
// Original PDB header: w:\tech5\engine\gamelib\aas2\obstacleavoidancecore.h
|
||||
// Recovered logical types: 6
|
||||
// Signatures retain Xbox 360 ABI evidence and may still require manual review.
|
||||
#include "idlib/containers/queue.h"
|
||||
#include "idlib/bv/bounds.h"
|
||||
#include "idlib/math/mat3x4.h"
|
||||
#include "idlib/math/plane.h"
|
||||
#include "idlib/math/vector.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
// IDA Local Type ordinal 2805; PDB kind: enum.
|
||||
enum idObstacleAvoidanceCore::avoidColor_t : __int32
|
||||
{
|
||||
COLOR_INVALID = 0x0,
|
||||
COLOR_WHITE = 0x1,
|
||||
COLOR_GRAY = 0x2,
|
||||
COLOR_RED = 0x3,
|
||||
COLOR_BLUE = 0x4,
|
||||
COLOR_GREEN = 0x5,
|
||||
COLOR_YELLOW = 0x6,
|
||||
COLOR_ORANGE = 0x7,
|
||||
COLOR_PURPLE = 0x8,
|
||||
COLOR_CYAN = 0x9,
|
||||
COLOR_MAGENTA = 0xA,
|
||||
struct obstacleVertex_t { idVec3 position; };
|
||||
struct obstacleEdge_t {
|
||||
int flags;
|
||||
std::int16_t verts[2];
|
||||
int vertNums[2];
|
||||
};
|
||||
struct obstacleBox_t {
|
||||
idMat3 axis;
|
||||
idVec3 center;
|
||||
idVec3 extents;
|
||||
int id;
|
||||
bool soft;
|
||||
std::uint8_t pad[15];
|
||||
};
|
||||
struct obstacleCorner_t { idVec3 corner; float radius; };
|
||||
struct obstacleTrace_t { idVec2 start; idVec2 end; };
|
||||
|
||||
struct obstacleRoute_t {
|
||||
struct routeArea_t {
|
||||
int areaNum;
|
||||
idVec3 start;
|
||||
idVec3 end;
|
||||
};
|
||||
|
||||
int numAreas;
|
||||
int pad[2];
|
||||
idVec3 endAlignDir;
|
||||
float endTurnRadius;
|
||||
routeArea_t areas[11];
|
||||
|
||||
// Materialized in the authoritative obstacleavoidancecore.h dump.
|
||||
obstacleRoute_t()
|
||||
: numAreas(0), pad{0, 0}, endAlignDir(), endTurnRadius(0.0f),
|
||||
areas{} {
|
||||
Clear();
|
||||
}
|
||||
|
||||
void Clear() {
|
||||
numAreas = 0;
|
||||
endAlignDir.Zero();
|
||||
endTurnRadius = 0.0f;
|
||||
for (routeArea_t& area : areas) {
|
||||
area.areaNum = 0;
|
||||
area.start.Zero();
|
||||
area.end.Zero();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 22035; PDB kind: struct.
|
||||
struct idObstacleAvoidanceCore::pathNode_t
|
||||
{
|
||||
int dir;
|
||||
idVec2 pos;
|
||||
idVec2 delta;
|
||||
float targetDistSqr;
|
||||
int obstacle;
|
||||
int edgeNum;
|
||||
int numNodes;
|
||||
idObstacleAvoidanceCore::pathNode_t *parent;
|
||||
idObstacleAvoidanceCore::pathNode_t *children[2];
|
||||
idQueueNode<idObstacleAvoidanceCore::pathNode_t> queueNode;
|
||||
struct obstaclePath_t {
|
||||
struct radar_t {
|
||||
idVec3 direction;
|
||||
int obstacle;
|
||||
};
|
||||
|
||||
idVec3 seekPos[31];
|
||||
int numSeekPos;
|
||||
idPlane seekPosPlane;
|
||||
idVec3 firstObstaclePosition;
|
||||
int firstObstacle;
|
||||
idVec3 startPosOutsideObstacles;
|
||||
int startPosObstacle;
|
||||
idVec3 seekPosOutsideObstacles;
|
||||
int seekPosObstacle;
|
||||
float targetDist;
|
||||
float pathLength;
|
||||
idVec4 nextCorner;
|
||||
float nextCornerAngle;
|
||||
idVec3 wallCorners[2];
|
||||
radar_t radar[7];
|
||||
bool useRadarForward;
|
||||
idVec3 radarForward;
|
||||
bool hasValidPath;
|
||||
bool startPosValid;
|
||||
bool seekPosValid;
|
||||
bool done;
|
||||
|
||||
// Materialized in the authoritative obstacleavoidancecore.h dump.
|
||||
obstaclePath_t()
|
||||
: seekPos{}, numSeekPos(0), seekPosPlane(0, 0, 0, 0),
|
||||
firstObstaclePosition(), firstObstacle(-1),
|
||||
startPosOutsideObstacles(), startPosObstacle(-1),
|
||||
seekPosOutsideObstacles(), seekPosObstacle(-1), targetDist(0.0f),
|
||||
pathLength(0.0f), nextCorner(), nextCornerAngle(0.0f),
|
||||
wallCorners{}, radar{}, useRadarForward(false), radarForward(),
|
||||
hasValidPath(false), startPosValid(false), seekPosValid(false),
|
||||
done(false) {
|
||||
}
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 22437; PDB kind: struct.
|
||||
struct idObstacleAvoidanceCore::bspNode_t
|
||||
{
|
||||
int axis;
|
||||
float dist;
|
||||
__int16 children[2];
|
||||
__int16 indexOffset;
|
||||
__int16 numIndices;
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 22438; PDB kind: struct.
|
||||
struct idObstacleAvoidanceCore::expandedObstacle_t
|
||||
{
|
||||
idVec2 bounds[2];
|
||||
idVec2 *winding;
|
||||
int numPoints;
|
||||
int id;
|
||||
bool soft;
|
||||
unsigned __int8 pad[3];
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 22439; PDB kind: class.
|
||||
class idObstacleAvoidanceCore
|
||||
{
|
||||
class idObstacleAvoidanceCore {
|
||||
public:
|
||||
idObstacleAvoidanceCore::bspNode_t *bspNodes;
|
||||
int numBspNodes;
|
||||
__int16 *obstacleIndices;
|
||||
idObstacleAvoidanceCore::expandedObstacle_t *expandedObstacles;
|
||||
int maxExpandedObstacles;
|
||||
int numExpandedObstacles;
|
||||
idVec2 *windingPoints;
|
||||
int maxWindingPoints;
|
||||
int numWindingPoints;
|
||||
unsigned __int8 *tempMemory;
|
||||
int tempMemorySize;
|
||||
unsigned __int8 *tempMemoryStart;
|
||||
unsigned __int8 *tempMemoryEnd;
|
||||
idObstacleAvoidanceCore::pathNode_t *pathNodes;
|
||||
int maxPathNodes;
|
||||
int numPathNodes;
|
||||
avoidDebugLine_t *debugLines;
|
||||
int maxDebugLines;
|
||||
int numDebugLines;
|
||||
avoidDebugText_t *debugText;
|
||||
int maxDebugText;
|
||||
int numDebugText;
|
||||
bool inputTransformValid;
|
||||
idMat3x4 inputTransform;
|
||||
int flags;
|
||||
enum avoidColor_t : int {
|
||||
COLOR_INVALID = 0,
|
||||
COLOR_WHITE = 1,
|
||||
COLOR_GRAY = 2,
|
||||
COLOR_RED = 3,
|
||||
COLOR_BLUE = 4,
|
||||
COLOR_GREEN = 5,
|
||||
COLOR_YELLOW = 6,
|
||||
COLOR_ORANGE = 7,
|
||||
COLOR_PURPLE = 8,
|
||||
COLOR_CYAN = 9,
|
||||
COLOR_MAGENTA = 10
|
||||
};
|
||||
|
||||
struct pathNode_t {
|
||||
int dir;
|
||||
idVec2 pos;
|
||||
idVec2 delta;
|
||||
float targetDistSqr;
|
||||
int obstacle;
|
||||
int edgeNum;
|
||||
int numNodes;
|
||||
pathNode_t* parent;
|
||||
pathNode_t* children[2];
|
||||
idQueueNode<pathNode_t> queueNode;
|
||||
};
|
||||
|
||||
struct bspNode_t {
|
||||
int axis;
|
||||
float dist;
|
||||
std::int16_t children[2];
|
||||
std::int16_t indexOffset;
|
||||
std::int16_t numIndices;
|
||||
};
|
||||
|
||||
struct expandedObstacle_t {
|
||||
idVec2 bounds[2];
|
||||
idVec2* winding;
|
||||
int numPoints;
|
||||
int id;
|
||||
bool soft;
|
||||
std::uint8_t pad[3];
|
||||
};
|
||||
|
||||
// This is the layout of the recovered function-local terminator type.
|
||||
struct setupExpandedObstaclesTerminator_t {
|
||||
float distSqr;
|
||||
std::int16_t lastEdgeIndex;
|
||||
std::int16_t firstEdgeIndex;
|
||||
};
|
||||
|
||||
idObstacleAvoidanceCore();
|
||||
~idObstacleAvoidanceCore();
|
||||
|
||||
static int BoxParallelProjectionSilhouetteVerts(const idMat3& axis,
|
||||
const idVec3& center, const idVec3& extents,
|
||||
const idVec3& projectionDir, idVec3* silhouette);
|
||||
static void GetCirclePoints(const idVec2& center, float radius,
|
||||
idVec2* circlePoints);
|
||||
static idVec3 Plane2DFromPoints(const idVec2& start,
|
||||
const idVec2& end, bool normalize);
|
||||
static idVec3 Plane2DFromVecs(const idVec2& start,
|
||||
const idVec2& direction, bool normalize);
|
||||
static bool PointInsideWinding(const idVec2* winding, int numPoints,
|
||||
const idVec2& point, float epsilon = 0.0f);
|
||||
static void ExpandWinding(const idVec2* winding, int numPoints,
|
||||
float distance, idVec2* expanded);
|
||||
static void GetWindingBounds(const idVec2* winding, int numPoints,
|
||||
idVec2 bounds[2]);
|
||||
static bool WindingRayIntersection(const idVec2* winding, int numPoints,
|
||||
const idVec2& start, const idVec2& direction, float& firstScale,
|
||||
float& secondScale, int edgeNums[2]);
|
||||
void AddDebugLine(avoidColor_t color, const idVec2& start,
|
||||
const idVec2& end);
|
||||
void AddDebugInt(avoidColor_t color, const idVec2& position, int value);
|
||||
void FindPathAroundObstacles(obstaclePath_t& path,
|
||||
struct avoidDebugLine_t* debugLines, int maxDebugLines,
|
||||
struct avoidDebugText_t* debugText, int maxDebugText,
|
||||
std::uint8_t* temp, int tempSize, const idBounds& bbox,
|
||||
const idVec3& gravity, const idVec3& lastDir,
|
||||
const idVec4& lastCorner, float obstacleRadius,
|
||||
float frameMoveDist, float cornerCircleRadius, int flags,
|
||||
const obstacleRoute_t& route, const obstacleBox_t* obstacles,
|
||||
int numObstacles, const obstacleVertex_t* vertices, int numVertices,
|
||||
const obstacleEdge_t* edges, int numEdges,
|
||||
const obstacleCorner_t* corners, int numCorners,
|
||||
const obstacleTrace_t* traces, int numTraces,
|
||||
float* traceFractions);
|
||||
|
||||
bspNode_t* bspNodes;
|
||||
int numBspNodes;
|
||||
std::int16_t* obstacleIndices;
|
||||
expandedObstacle_t* expandedObstacles;
|
||||
int maxExpandedObstacles;
|
||||
int numExpandedObstacles;
|
||||
idVec2* windingPoints;
|
||||
int maxWindingPoints;
|
||||
int numWindingPoints;
|
||||
std::uint8_t* tempMemory;
|
||||
int tempMemorySize;
|
||||
std::uint8_t* tempMemoryStart;
|
||||
std::uint8_t* tempMemoryEnd;
|
||||
pathNode_t* pathNodes;
|
||||
int maxPathNodes;
|
||||
int numPathNodes;
|
||||
avoidDebugLine_t* debugLines;
|
||||
int maxDebugLines;
|
||||
int numDebugLines;
|
||||
avoidDebugText_t* debugText;
|
||||
int maxDebugText;
|
||||
int numDebugText;
|
||||
bool inputTransformValid;
|
||||
idMat3x4 inputTransform;
|
||||
int flags;
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 23501; PDB kind: struct.
|
||||
struct idObstacleAvoidanceCore::SetupExpandedObstacles::__l87::terminator_t
|
||||
{
|
||||
float distSqr;
|
||||
__int16 lastEdgeIndex;
|
||||
__int16 firstEdgeIndex;
|
||||
struct avoidDebugLine_t {
|
||||
idObstacleAvoidanceCore::avoidColor_t color;
|
||||
idVec3 start;
|
||||
idVec3 end;
|
||||
};
|
||||
|
||||
struct avoidDebugText_t {
|
||||
char text[32];
|
||||
idVec3 pos;
|
||||
idObstacleAvoidanceCore::avoidColor_t color;
|
||||
};
|
||||
|
||||
struct obstacleTraceSet_t {
|
||||
obstacleTrace_t traces[256];
|
||||
float traceFractions[256];
|
||||
int traceNumbers[256];
|
||||
int numTraces;
|
||||
int prevTraceNumbers[256];
|
||||
int prevNumTraces;
|
||||
};
|
||||
|
||||
struct obstacleParms_t {
|
||||
idBounds bbox;
|
||||
idVec3 gravity;
|
||||
idVec3 lastDir;
|
||||
idVec4 lastCorner;
|
||||
float obstacleRadius;
|
||||
float frameMoveDist;
|
||||
float cornerCircleRadius;
|
||||
int flags;
|
||||
int numObstacles;
|
||||
int numVertices;
|
||||
int numEdges;
|
||||
int numCorners;
|
||||
int numTraces;
|
||||
int maxDebugLines;
|
||||
int maxDebugText;
|
||||
const obstacleRoute_t* route;
|
||||
const obstacleBox_t* obstacles;
|
||||
const obstacleVertex_t* vertices;
|
||||
const obstacleEdge_t* edges;
|
||||
const obstacleCorner_t* corners;
|
||||
const obstacleTrace_t* traces;
|
||||
float* traceFractions;
|
||||
obstaclePath_t* path;
|
||||
avoidDebugLine_t* debugLines;
|
||||
avoidDebugText_t* debugText;
|
||||
std::uint8_t pad[12];
|
||||
};
|
||||
|
||||
#if INTPTR_MAX == INT32_MAX
|
||||
static_assert(sizeof(obstacleRoute_t::routeArea_t) == 28,
|
||||
"Recovered obstacle route area ABI changed");
|
||||
static_assert(sizeof(obstacleRoute_t) == 336,
|
||||
"Recovered obstacleRoute_t ABI changed");
|
||||
static_assert(sizeof(obstaclePath_t::radar_t) == 16,
|
||||
"Recovered obstacle radar ABI changed");
|
||||
static_assert(sizeof(obstaclePath_t) == 624,
|
||||
"Recovered obstaclePath_t ABI changed");
|
||||
static_assert(sizeof(idObstacleAvoidanceCore::pathNode_t) == 52,
|
||||
"Recovered obstacle path node ABI changed");
|
||||
static_assert(sizeof(idObstacleAvoidanceCore::bspNode_t) == 16,
|
||||
"Recovered obstacle BSP node ABI changed");
|
||||
static_assert(sizeof(idObstacleAvoidanceCore::expandedObstacle_t) == 32,
|
||||
"Recovered expanded obstacle ABI changed");
|
||||
static_assert(sizeof(obstacleVertex_t) == 12,
|
||||
"Recovered obstacle vertex ABI changed");
|
||||
static_assert(sizeof(obstacleEdge_t) == 16,
|
||||
"Recovered obstacle edge ABI changed");
|
||||
static_assert(sizeof(obstacleBox_t) == 80,
|
||||
"Recovered obstacle box ABI changed");
|
||||
static_assert(sizeof(obstacleCorner_t) == 16,
|
||||
"Recovered obstacle corner ABI changed");
|
||||
static_assert(sizeof(obstacleTrace_t) == 16,
|
||||
"Recovered obstacle trace ABI changed");
|
||||
static_assert(sizeof(avoidDebugLine_t) == 28,
|
||||
"Recovered obstacle debug-line ABI changed");
|
||||
static_assert(sizeof(avoidDebugText_t) == 48,
|
||||
"Recovered obstacle debug-text ABI changed");
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
#include "gamelib/aas2/obstacleavoidancemanagerlocal.h"
|
||||
|
||||
#include "idlib/lib_print.h"
|
||||
|
||||
#include <malloc.h>
|
||||
#include <new>
|
||||
|
||||
namespace {
|
||||
|
||||
template<typename type>
|
||||
type* AllocAligned(const std::size_t size, const std::size_t alignment) {
|
||||
return static_cast<type*>(_aligned_malloc(size, alignment));
|
||||
}
|
||||
|
||||
void FreeAligned(void* const memory) {
|
||||
_aligned_free(memory);
|
||||
}
|
||||
|
||||
void ResetBuffer(idObstacleBuffers& buffer) {
|
||||
buffer.numObstacles = 0;
|
||||
buffer.numVertices = 0;
|
||||
buffer.numEdges = 0;
|
||||
buffer.numCorners = 0;
|
||||
buffer.numTraces = 0;
|
||||
buffer.frameCount = 2;
|
||||
}
|
||||
|
||||
idObstacleBuffers* AllocBuffer() {
|
||||
idObstacleBuffers* const buffer =
|
||||
AllocAligned<idObstacleBuffers>(0x44, 16);
|
||||
buffer->parms = AllocAligned<obstacleParms_t>(0xA0, 128);
|
||||
buffer->route = AllocAligned<obstacleRoute_t>(0x150, 128);
|
||||
buffer->obstacles = AllocAligned<obstacleBox_t>(0x2800, 128);
|
||||
buffer->vertices = AllocAligned<obstacleVertex_t>(0x2400, 128);
|
||||
buffer->edges = AllocAligned<obstacleEdge_t>(0x2000, 128);
|
||||
buffer->corners = AllocAligned<obstacleCorner_t>(0x100, 128);
|
||||
buffer->traces = AllocAligned<obstacleTrace_t>(0x1000, 128);
|
||||
buffer->traceFractions = AllocAligned<float>(0x400, 128);
|
||||
void* const pathMemory = _aligned_malloc(0x270, 128);
|
||||
buffer->path = new (pathMemory) obstaclePath_t();
|
||||
buffer->user = nullptr;
|
||||
buffer->job = nullptr;
|
||||
ResetBuffer(*buffer);
|
||||
buffer->frameCount = 0;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void FreeBuffer(idObstacleBuffers* const buffer) {
|
||||
if (buffer == nullptr) {
|
||||
return;
|
||||
}
|
||||
FreeAligned(buffer->parms);
|
||||
FreeAligned(buffer->route);
|
||||
FreeAligned(buffer->obstacles);
|
||||
FreeAligned(buffer->vertices);
|
||||
FreeAligned(buffer->edges);
|
||||
FreeAligned(buffer->corners);
|
||||
FreeAligned(buffer->traces);
|
||||
FreeAligned(buffer->traceFractions);
|
||||
if (buffer->path != nullptr) {
|
||||
buffer->path->~obstaclePath_t();
|
||||
FreeAligned(buffer->path);
|
||||
}
|
||||
FreeAligned(buffer);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
idObstacleAvoidanceManagerLocal obstacleAvoidanceManagerLocal;
|
||||
|
||||
idObstacleAvoidanceManagerLocal::idObstacleAvoidanceManagerLocal()
|
||||
: jobList(nullptr)
|
||||
, usedBuffers()
|
||||
, freeBuffers()
|
||||
, toBeFreedBuffers() {
|
||||
}
|
||||
|
||||
idObstacleAvoidanceManagerLocal::~idObstacleAvoidanceManagerLocal() {
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
void idObstacleAvoidanceManagerLocal::Init(
|
||||
idParallelJobList* const jobList_) {
|
||||
if (jobList != nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
jobList = jobList_;
|
||||
freeBuffers.SetNum(32);
|
||||
for (int index = 0; index < 32; ++index) {
|
||||
freeBuffers[index] = AllocBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
void idObstacleAvoidanceManagerLocal::Shutdown() {
|
||||
FrameFreeObstacleBuffers();
|
||||
for (int index = 0; index < freeBuffers.Num(); ++index) {
|
||||
FreeBuffer(freeBuffers[index]);
|
||||
}
|
||||
usedBuffers.ClearFree();
|
||||
freeBuffers.ClearFree();
|
||||
toBeFreedBuffers.ClearFree();
|
||||
jobList = nullptr;
|
||||
}
|
||||
|
||||
void idObstacleAvoidanceManagerLocal::AllocObstacleBuffers(
|
||||
idObstacleAvoidance* const avoidance) {
|
||||
const int existingIndex = usedBuffers.FindIndex(avoidance->buffers);
|
||||
if (existingIndex >= 0) {
|
||||
ResetBuffer(*avoidance->buffers);
|
||||
return;
|
||||
}
|
||||
|
||||
if (freeBuffers.IsEmpty()) {
|
||||
idLibPrint::Warning("out of obstacle avoidance buffers");
|
||||
return;
|
||||
}
|
||||
|
||||
idObstacleBuffers* const buffer = freeBuffers[freeBuffers.Num() - 1];
|
||||
freeBuffers.SetNum(freeBuffers.Num() - 1);
|
||||
usedBuffers.Append(buffer);
|
||||
buffer->user = avoidance;
|
||||
buffer->job = nullptr;
|
||||
ResetBuffer(*buffer);
|
||||
avoidance->buffers = buffer;
|
||||
}
|
||||
|
||||
void idObstacleAvoidanceManagerLocal::FreeObstacleBuffers(
|
||||
idObstacleAvoidance* const avoidance) {
|
||||
if (avoidance->buffers == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int index = usedBuffers.FindIndex(avoidance->buffers);
|
||||
if (index < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
idObstacleBuffers* const buffer = usedBuffers[index];
|
||||
buffer->user = nullptr;
|
||||
buffer->job = nullptr;
|
||||
toBeFreedBuffers.Append(buffer);
|
||||
usedBuffers.RemoveIndexFast(index);
|
||||
avoidance->buffers = nullptr;
|
||||
}
|
||||
|
||||
void idObstacleAvoidanceManagerLocal::FrameFreeObstacleBuffers() {
|
||||
for (int index = 0; index < usedBuffers.Num(); ++index) {
|
||||
idObstacleBuffers* const buffer = usedBuffers[index];
|
||||
if (--buffer->frameCount <= 0) {
|
||||
if (buffer->user != nullptr) {
|
||||
buffer->user->buffers = nullptr;
|
||||
}
|
||||
buffer->user = nullptr;
|
||||
buffer->job = nullptr;
|
||||
toBeFreedBuffers.Append(buffer);
|
||||
usedBuffers.RemoveIndexFast(index--);
|
||||
}
|
||||
}
|
||||
|
||||
for (int index = 0; index < toBeFreedBuffers.Num(); ++index) {
|
||||
freeBuffers.Append(toBeFreedBuffers[index]);
|
||||
}
|
||||
toBeFreedBuffers.Clear();
|
||||
}
|
||||
@@ -1,26 +1,72 @@
|
||||
#pragma once
|
||||
|
||||
// Reconstructed C++ declarations from IDA Local Types and PDB/DIA metadata.
|
||||
// Original PDB header: w:\tech5\engine\gamelib\aas2\obstacleavoidancemanagerlocal.h
|
||||
// Recovered logical types: 1
|
||||
// Signatures retain Xbox 360 ABI evidence and may still require manual review.
|
||||
#include "gamelib/aas2/obstacleavoidance.h"
|
||||
#include "gamelib/aas2/obstacleavoidancecore.h"
|
||||
#include "idlib/containers/list.h"
|
||||
|
||||
class idParallelJobList;
|
||||
class CellSpursJob128;
|
||||
struct obstacleParms_t;
|
||||
struct obstacleBox_t;
|
||||
struct obstacleVertex_t;
|
||||
struct obstacleEdge_t;
|
||||
struct obstacleCorner_t;
|
||||
struct obstacleTrace_t;
|
||||
|
||||
// IDA Local Type ordinal 23498; PDB kind: class.
|
||||
class idObstacleAvoidanceManagerLocal : public idObstacleAvoidanceManager
|
||||
{
|
||||
public:
|
||||
// Recovered virtual interface; IDA vtable ordinal 23499.
|
||||
virtual ~idObstacleAvoidanceManagerLocal();
|
||||
virtual void Init(idParallelJobList *);
|
||||
virtual void Shutdown();
|
||||
virtual void AllocObstacleBuffers(idObstacleAvoidance *);
|
||||
virtual void FreeObstacleBuffers(idObstacleAvoidance *);
|
||||
virtual void FrameFreeObstacleBuffers();
|
||||
virtual idParallelJobList *GetJobList();
|
||||
|
||||
idParallelJobList *jobList;
|
||||
idList<idObstacleBuffers *,5> usedBuffers;
|
||||
idList<idObstacleBuffers *,5> freeBuffers;
|
||||
idList<idObstacleBuffers *,5> toBeFreedBuffers;
|
||||
struct idObstacleBuffers {
|
||||
idObstacleAvoidance* user;
|
||||
CellSpursJob128* job;
|
||||
obstacleParms_t* parms;
|
||||
obstacleRoute_t* route;
|
||||
obstacleBox_t* obstacles;
|
||||
obstacleVertex_t* vertices;
|
||||
obstacleEdge_t* edges;
|
||||
obstacleCorner_t* corners;
|
||||
obstacleTrace_t* traces;
|
||||
float* traceFractions;
|
||||
obstaclePath_t* path;
|
||||
int numObstacles;
|
||||
int numVertices;
|
||||
int numEdges;
|
||||
int numCorners;
|
||||
int numTraces;
|
||||
int frameCount;
|
||||
};
|
||||
|
||||
class idObstacleAvoidanceManager {
|
||||
public:
|
||||
virtual ~idObstacleAvoidanceManager() = default;
|
||||
virtual void Init(idParallelJobList* jobList) = 0;
|
||||
virtual void Shutdown() = 0;
|
||||
virtual void AllocObstacleBuffers(idObstacleAvoidance* avoidance) = 0;
|
||||
virtual void FreeObstacleBuffers(idObstacleAvoidance* avoidance) = 0;
|
||||
virtual void FrameFreeObstacleBuffers() = 0;
|
||||
virtual idParallelJobList* GetJobList() = 0;
|
||||
};
|
||||
|
||||
class idObstacleAvoidanceManagerLocal : public idObstacleAvoidanceManager {
|
||||
public:
|
||||
idObstacleAvoidanceManagerLocal();
|
||||
~idObstacleAvoidanceManagerLocal() override;
|
||||
|
||||
void Init(idParallelJobList* jobList_) override;
|
||||
void Shutdown() override;
|
||||
void AllocObstacleBuffers(idObstacleAvoidance* avoidance) override;
|
||||
void FreeObstacleBuffers(idObstacleAvoidance* avoidance) override;
|
||||
void FrameFreeObstacleBuffers() override;
|
||||
idParallelJobList* GetJobList() override { return jobList; }
|
||||
|
||||
idParallelJobList* jobList;
|
||||
idList<idObstacleBuffers*, 5> usedBuffers;
|
||||
idList<idObstacleBuffers*, 5> freeBuffers;
|
||||
idList<idObstacleBuffers*, 5> toBeFreedBuffers;
|
||||
};
|
||||
|
||||
extern idObstacleAvoidanceManagerLocal obstacleAvoidanceManagerLocal;
|
||||
|
||||
#if defined(_WIN32) && !defined(_WIN64)
|
||||
static_assert(sizeof(idObstacleBuffers) == 68,
|
||||
"Recovered idObstacleBuffers ABI changed");
|
||||
static_assert(sizeof(idObstacleAvoidanceManagerLocal) == 56,
|
||||
"Recovered obstacle-avoidance manager ABI changed");
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,243 @@
|
||||
#include "gamelib/animstack/animator_base.h"
|
||||
|
||||
#include "idlib/lib_print.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
// idAnimStack and idGameTimeManager are not yet safe to materialize from the
|
||||
// generated declarations. These named boundaries preserve every recovered
|
||||
// animator-base call while their authoritative translation units are ported.
|
||||
int GameLib_GetAnimatorIndex(const idAnimStack* stack,
|
||||
const idAnimator_Base* animator);
|
||||
void GameLib_AddAnimator(idAnimStack* stack, idAnimator_Base* animator);
|
||||
void GameLib_RemoveAnimator(idAnimStack* stack, idAnimator_Base* animator);
|
||||
void GameLib_SetAnimatorFlag(idAnimStack* stack, idAnimator_Base* animator,
|
||||
int flag);
|
||||
void GameLib_ClearAnimatorFlag(idAnimStack* stack, idAnimator_Base* animator,
|
||||
int flag);
|
||||
bool GameLib_IsAnimatorFlagSet(const idAnimStack* stack,
|
||||
const idAnimator_Base* animator, int flag);
|
||||
int GameLib_ConvertRealMillisecondsToGameTime(
|
||||
const idGameTimeManager* gameTimeManager, int milliseconds);
|
||||
int GameLib_GetGameTicksPerSecond();
|
||||
bool GameLib_IsMD6NodeValid(const idMD6Node* node);
|
||||
|
||||
namespace {
|
||||
|
||||
enum animatorFlag_t {
|
||||
ANIMATORFLAG_INITIALIZED = 0x01,
|
||||
ANIMATORFLAG_ENABLED = 0x02,
|
||||
ANIMATORFLAG_SERIALIZE = 0x08
|
||||
};
|
||||
|
||||
bool IsBlendFinished(const idMD6Branch& branch) {
|
||||
return branch.alphaRate == 0.0f ||
|
||||
branch.currentAlpha == branch.targetAlpha;
|
||||
}
|
||||
|
||||
bool IsBlendingOut(const idMD6Branch& branch) {
|
||||
return branch.currentAlpha > 0.0f &&
|
||||
branch.targetAlpha == 0.0f && branch.alphaRate != 0.0f;
|
||||
}
|
||||
|
||||
bool IsBlendingIn(const idMD6Branch& branch) {
|
||||
return branch.currentAlpha < 1.0f &&
|
||||
branch.targetAlpha >= 1.0f && branch.alphaRate != 0.0f;
|
||||
}
|
||||
|
||||
idTypesafeNumber<int, gameTimeUnique_t> ConvertBlendTime(
|
||||
const idGameTimeManager* gameTimeManager, const int milliseconds) {
|
||||
return idTypesafeNumber<int, gameTimeUnique_t>(
|
||||
GameLib_ConvertRealMillisecondsToGameTime(
|
||||
gameTimeManager, milliseconds));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
idAnimator_Base::idAnimator_Base()
|
||||
: gametimeManager{nullptr}
|
||||
, weightGroup(MD6_WEIGHTGROUP_ALL)
|
||||
, filterGroup(MD6_WEIGHTGROUP_ALL)
|
||||
, serializeProps{{nullptr, nullptr}, nullptr, nullptr, false}
|
||||
, initialized(false) {
|
||||
}
|
||||
|
||||
idAnimator_Base::~idAnimator_Base() = default;
|
||||
|
||||
bool idAnimator_Base::InternalPostInit(
|
||||
const idAnimatorParms_Base& parameters) {
|
||||
if (parameters.animStack == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
GameLib_SetAnimatorFlag(parameters.animStack, this,
|
||||
ANIMATORFLAG_SERIALIZE);
|
||||
return true;
|
||||
}
|
||||
|
||||
void idAnimator_Base::Shutdown(idAnimStack* const stack) {
|
||||
InternalShutdown(stack);
|
||||
GameLib_ClearAnimatorFlag(stack, this, ANIMATORFLAG_INITIALIZED);
|
||||
GameLib_RemoveAnimator(stack, this);
|
||||
}
|
||||
|
||||
bool idAnimator_Base::IsInitialized(idAnimStack* const stack) const {
|
||||
return stack != nullptr && GameLib_IsAnimatorFlagSet(
|
||||
stack, this, ANIMATORFLAG_INITIALIZED);
|
||||
}
|
||||
|
||||
bool idAnimator_Base::IsEnabled(idAnimStack* const stack) const {
|
||||
return stack != nullptr && GameLib_IsAnimatorFlagSet(
|
||||
stack, this, ANIMATORFLAG_ENABLED);
|
||||
}
|
||||
|
||||
void idAnimator_Base::SetEnabled(idAnimStack* const stack,
|
||||
const bool enabled) {
|
||||
if (enabled) {
|
||||
GameLib_SetAnimatorFlag(stack, this, ANIMATORFLAG_ENABLED);
|
||||
} else {
|
||||
GameLib_ClearAnimatorFlag(stack, this, ANIMATORFLAG_ENABLED);
|
||||
}
|
||||
}
|
||||
|
||||
bool idAnimator_Base::Init(idGameTimeManager* const gameTimeManager,
|
||||
const idAnimatorParms_Base& parameters) {
|
||||
if (GameLib_GetAnimatorIndex(parameters.animStack, this) >= 0 &&
|
||||
initialized) {
|
||||
idLibPrint::Error("Attempted to add idAnimator %s twice.",
|
||||
parameters.name.c_str());
|
||||
}
|
||||
|
||||
gametimeManager.gameTimeManager = gameTimeManager;
|
||||
weightGroup = parameters.weightGroup;
|
||||
filterGroup = parameters.filterGroup;
|
||||
|
||||
if (!InternalInit(parameters)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
idMD6Branch* const branch = InternalGetMergeBranch();
|
||||
if (branch != nullptr) {
|
||||
branch->op = static_cast<std::uint8_t>(parameters.blendOp);
|
||||
branch->originBlend =
|
||||
static_cast<std::uint8_t>(parameters.originBlend);
|
||||
branch->currentAlpha = parameters.alpha;
|
||||
branch->targetAlpha = parameters.alpha;
|
||||
branch->alphaRate = 0.0f;
|
||||
branch->filterGroup =
|
||||
static_cast<std::uint8_t>(parameters.filterGroup);
|
||||
}
|
||||
|
||||
GameLib_AddAnimator(parameters.animStack, this);
|
||||
GameLib_SetAnimatorFlag(parameters.animStack, this,
|
||||
ANIMATORFLAG_INITIALIZED);
|
||||
GameLib_SetAnimatorFlag(parameters.animStack, this,
|
||||
ANIMATORFLAG_ENABLED);
|
||||
initialized = true;
|
||||
return InternalPostInit(parameters);
|
||||
}
|
||||
|
||||
void idAnimator_Base::Blend(const idAnimStack* const stack,
|
||||
const int currentTime, const float targetAlpha,
|
||||
const int blendDurationMS, const bool reset) {
|
||||
idMD6Branch* const branch = InternalGetMergeBranch();
|
||||
branch->targetAlpha = targetAlpha;
|
||||
|
||||
const idTypesafeNumber<int, gameTimeUnique_t> blendTime =
|
||||
ConvertBlendTime(gametimeManager.gameTimeManager, blendDurationMS);
|
||||
const bool alreadyBlending = targetAlpha <= branch->currentAlpha
|
||||
? IsBlendingOut(*branch)
|
||||
: IsBlendingIn(*branch);
|
||||
|
||||
if (blendTime.value <= 0) {
|
||||
branch->currentAlpha = targetAlpha;
|
||||
branch->alphaRate = 0.0f;
|
||||
} else if (reset || !alreadyBlending) {
|
||||
branch->alphaRate = std::fabs(targetAlpha - branch->currentAlpha) *
|
||||
(1000.0f / static_cast<float>(blendTime.value));
|
||||
}
|
||||
|
||||
InternalBlend(stack, currentTime, targetAlpha, blendTime);
|
||||
}
|
||||
|
||||
bool idAnimator_Base::IsContributing() const {
|
||||
const idMD6Branch* const branch = InternalGetMergeBranch();
|
||||
return branch != nullptr && branch->right != nullptr &&
|
||||
GameLib_IsMD6NodeValid(branch->right) &&
|
||||
(branch->currentAlpha > 0.0f || !IsBlendFinished(*branch)) &&
|
||||
InternalIsContributing();
|
||||
}
|
||||
|
||||
md6WeightGroup_t idAnimator_Base::GetFilterGroup() const {
|
||||
return static_cast<md6WeightGroup_t>(
|
||||
InternalGetMergeBranch()->filterGroup);
|
||||
}
|
||||
|
||||
float idAnimator_Base::GetAlpha() const {
|
||||
return InternalGetMergeBranch()->currentAlpha;
|
||||
}
|
||||
|
||||
void idAnimator_Base::SetAlpha(const float alpha) {
|
||||
InternalGetMergeBranch()->currentAlpha = alpha;
|
||||
}
|
||||
|
||||
void idAnimator_Base::Pause(const idAnimStack* const stack,
|
||||
const idTypesafeNumber<int, gameTimeUnique_t> currentTime) {
|
||||
InternalPause(stack, currentTime);
|
||||
}
|
||||
|
||||
void idAnimator_Base::Unpause(const idAnimStack* const stack,
|
||||
const idTypesafeNumber<int, gameTimeUnique_t> currentTime) {
|
||||
InternalUnpause(stack, currentTime);
|
||||
}
|
||||
|
||||
void idAnimator_Base::Start(const idAnimStack* const stack,
|
||||
const int currentTime, const int blendDurationMS, const bool reset) {
|
||||
idMD6Branch* const branch = InternalGetMergeBranch();
|
||||
const idTypesafeNumber<int, gameTimeUnique_t> blendTime =
|
||||
ConvertBlendTime(gametimeManager.gameTimeManager, blendDurationMS);
|
||||
|
||||
if (blendTime.value <= 0) {
|
||||
branch->currentAlpha = 1.0f;
|
||||
branch->targetAlpha = 1.0f;
|
||||
branch->alphaRate = 0.0f;
|
||||
} else if (reset || !IsBlendingIn(*branch)) {
|
||||
branch->targetAlpha = 1.0f;
|
||||
branch->alphaRate = (1.0f - branch->currentAlpha) *
|
||||
(static_cast<float>(GameLib_GetGameTicksPerSecond()) /
|
||||
static_cast<float>(blendTime.value));
|
||||
}
|
||||
|
||||
InternalStart(stack, currentTime, blendTime);
|
||||
}
|
||||
|
||||
void idAnimator_Base::End(const idAnimStack* const stack,
|
||||
const int currentTime, const int blendDurationMS, const bool reset) {
|
||||
if (!initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
idMD6Branch* const branch = InternalGetMergeBranch();
|
||||
branch->targetAlpha = 0.0f;
|
||||
const idTypesafeNumber<int, gameTimeUnique_t> blendTime =
|
||||
ConvertBlendTime(gametimeManager.gameTimeManager, blendDurationMS);
|
||||
|
||||
if (blendTime.value <= 0) {
|
||||
branch->alphaRate = 0.0f;
|
||||
branch->currentAlpha = 0.0f;
|
||||
} else if (reset || !IsBlendingOut(*branch)) {
|
||||
branch->alphaRate = branch->currentAlpha *
|
||||
(static_cast<float>(GameLib_GetGameTicksPerSecond()) /
|
||||
static_cast<float>(blendTime.value));
|
||||
}
|
||||
|
||||
InternalEnd(stack, currentTime, blendTime);
|
||||
}
|
||||
|
||||
void idAnimator_Base::PreSerializeInit(idAnimStack* const stack,
|
||||
idClip* const clip, idGameTimeManager* const gameTimeManager) {
|
||||
serializeProps.animStack = stack;
|
||||
serializeProps.clip = clip;
|
||||
gametimeManager.gameTimeManager = gameTimeManager;
|
||||
serializeProps.createdThroughSerialization = true;
|
||||
}
|
||||
@@ -1,72 +1,107 @@
|
||||
#pragma once
|
||||
|
||||
// Reconstructed C++ declarations from IDA Local Types and PDB/DIA metadata.
|
||||
// Original PDB header: w:\tech5\engine\gamelib\animstack\animator_base.h
|
||||
// Recovered logical types: 4
|
||||
// Signatures retain Xbox 360 ABI evidence and may still require manual review.
|
||||
#include "gamelib/animstack/animstacktypes.h"
|
||||
#include "idlib/text/str.h"
|
||||
#include "idlib/typesafenumber.h"
|
||||
|
||||
class idSerializer;
|
||||
|
||||
// IDA Local Type ordinal 1357; PDB kind: enum.
|
||||
enum idAnimator_Base::priority_t : __int32
|
||||
{
|
||||
PRIORITY_WEB = 0x0,
|
||||
PRIORITY_AFTER_WEB = 0x1,
|
||||
PRIORITY_IK = 0x63,
|
||||
PRIORITY_AF = 0x64,
|
||||
};
|
||||
class idAnimatorParms_Base;
|
||||
|
||||
// IDA Local Type ordinal 14224; PDB kind: class.
|
||||
class __declspec(align(4)) idAnimator_Base
|
||||
{
|
||||
class idAnimator_Base {
|
||||
public:
|
||||
// Recovered virtual interface; IDA vtable ordinal 14289.
|
||||
virtual ~idAnimator_Base();
|
||||
virtual idAnimator_Base::priority_t GetStackPriority();
|
||||
virtual serializeType_t GetSerializeType();
|
||||
virtual void SerializeSnapshot(idSerializer *);
|
||||
virtual void PreBlendSnapshot(idAnimStack *, int, const int, float);
|
||||
virtual void PreSerializeInit(idAnimStack *, idClip *, idGameTimeManager *);
|
||||
virtual bool InternalInit(const idAnimatorParms_Base *);
|
||||
virtual bool InternalPostInit(const idAnimatorParms_Base *);
|
||||
virtual void InternalShutdown(idAnimStack *);
|
||||
virtual void InternalPreBlendTree(const idAnimStack *, const int, const int);
|
||||
virtual void InternalPostBlendTree(const idAnimStack *, const int);
|
||||
virtual void InternalStart(const idAnimStack *, const int, const idTypesafeNumber<int,enum gameTimeUnique_t>);
|
||||
virtual void InternalEnd(const idAnimStack *, const int, const idTypesafeNumber<int,enum gameTimeUnique_t>);
|
||||
virtual void InternalBlend(const idAnimStack *, const int, const float, const idTypesafeNumber<int,enum gameTimeUnique_t>);
|
||||
virtual bool InternalIsContributing();
|
||||
virtual const idMD6Branch *InternalGetMergeBranch();
|
||||
virtual idMD6Branch *InternalGetMergeBranch_2();
|
||||
virtual void InternalPause(const idAnimStack *, const idTypesafeNumber<int,enum gameTimeUnique_t>);
|
||||
virtual void InternalUnpause(const idAnimStack *, const idTypesafeNumber<int,enum gameTimeUnique_t>);
|
||||
virtual const idMD6Branch *InternalGetEndBranch();
|
||||
virtual idMD6Branch *InternalGetEndBranch_2();
|
||||
enum priority_t : int {
|
||||
PRIORITY_WEB = 0,
|
||||
PRIORITY_AFTER_WEB = 1,
|
||||
PRIORITY_IK = 99,
|
||||
PRIORITY_AF = 100
|
||||
};
|
||||
|
||||
idGameTimeManagerPtr gametimeManager;
|
||||
md6WeightGroup_t weightGroup;
|
||||
md6WeightGroup_t filterGroup;
|
||||
idAnimator_Base::serializeProps_t serializeProps;
|
||||
bool initialized;
|
||||
struct serializeProps_t {
|
||||
idMD6Branch* serializedTrees[2];
|
||||
idAnimStack* animStack;
|
||||
idClip* clip;
|
||||
bool createdThroughSerialization;
|
||||
};
|
||||
|
||||
idAnimator_Base();
|
||||
virtual ~idAnimator_Base();
|
||||
|
||||
bool Init(idGameTimeManager* gameTimeManager,
|
||||
const idAnimatorParms_Base& parameters);
|
||||
void Shutdown(idAnimStack* stack);
|
||||
bool IsInitialized(idAnimStack* stack) const;
|
||||
bool IsEnabled(idAnimStack* stack) const;
|
||||
void SetEnabled(idAnimStack* stack, bool enabled);
|
||||
void Blend(const idAnimStack* stack, int currentTime, float targetAlpha,
|
||||
int blendDurationMS, bool reset);
|
||||
bool IsContributing() const;
|
||||
md6WeightGroup_t GetFilterGroup() const;
|
||||
float GetAlpha() const;
|
||||
void SetAlpha(float alpha);
|
||||
void Pause(const idAnimStack* stack,
|
||||
idTypesafeNumber<int, gameTimeUnique_t> currentTime);
|
||||
void Unpause(const idAnimStack* stack,
|
||||
idTypesafeNumber<int, gameTimeUnique_t> currentTime);
|
||||
void Start(const idAnimStack* stack, int currentTime,
|
||||
int blendDurationMS, bool reset);
|
||||
void End(const idAnimStack* stack, int currentTime,
|
||||
int blendDurationMS, bool reset);
|
||||
|
||||
virtual priority_t GetStackPriority();
|
||||
virtual serializeType_t GetSerializeType();
|
||||
virtual void SerializeSnapshot(idSerializer* serializer);
|
||||
virtual void PreBlendSnapshot(idAnimStack* stack, int currentTime,
|
||||
int ticksPerSecond, float fraction);
|
||||
virtual void PreSerializeInit(idAnimStack* stack, idClip* clip,
|
||||
idGameTimeManager* gameTimeManager);
|
||||
virtual bool InternalInit(const idAnimatorParms_Base& parameters);
|
||||
virtual bool InternalPostInit(const idAnimatorParms_Base& parameters);
|
||||
virtual void InternalShutdown(idAnimStack* stack);
|
||||
virtual void InternalPreBlendTree(const idAnimStack* stack,
|
||||
int currentTime, int ticksPerSecond);
|
||||
virtual void InternalPostBlendTree(const idAnimStack* stack,
|
||||
int currentTime);
|
||||
virtual void InternalStart(const idAnimStack* stack, int currentTime,
|
||||
idTypesafeNumber<int, gameTimeUnique_t> blendTime);
|
||||
virtual void InternalEnd(const idAnimStack* stack, int currentTime,
|
||||
idTypesafeNumber<int, gameTimeUnique_t> blendTime);
|
||||
virtual void InternalBlend(const idAnimStack* stack, int currentTime,
|
||||
float targetAlpha,
|
||||
idTypesafeNumber<int, gameTimeUnique_t> blendTime);
|
||||
virtual bool InternalIsContributing() const;
|
||||
virtual const idMD6Branch* InternalGetMergeBranch() const;
|
||||
virtual idMD6Branch* InternalGetMergeBranch();
|
||||
virtual void InternalPause(const idAnimStack* stack,
|
||||
idTypesafeNumber<int, gameTimeUnique_t> currentTime);
|
||||
virtual void InternalUnpause(const idAnimStack* stack,
|
||||
idTypesafeNumber<int, gameTimeUnique_t> currentTime);
|
||||
virtual const idMD6Branch* InternalGetEndBranch() const;
|
||||
virtual idMD6Branch* InternalGetEndBranch();
|
||||
|
||||
idGameTimeManagerPtr gametimeManager;
|
||||
md6WeightGroup_t weightGroup;
|
||||
md6WeightGroup_t filterGroup;
|
||||
serializeProps_t serializeProps;
|
||||
bool initialized;
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 14288; PDB kind: class.
|
||||
class idAnimatorParms_Base
|
||||
{
|
||||
class idAnimatorParms_Base {
|
||||
public:
|
||||
idAnimStack *animStack;
|
||||
idStr name;
|
||||
idMD6Blend::blendOp_t blendOp;
|
||||
idMD6Blend::originBlend_t originBlend;
|
||||
md6WeightGroup_t weightGroup;
|
||||
md6WeightGroup_t filterGroup;
|
||||
float alpha;
|
||||
idAnimStack* animStack;
|
||||
idStr name;
|
||||
int blendOp;
|
||||
int originBlend;
|
||||
md6WeightGroup_t weightGroup;
|
||||
md6WeightGroup_t filterGroup;
|
||||
float alpha;
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 14291; PDB kind: struct.
|
||||
struct __declspec(align(4)) idAnimator_Base::serializeProps_t
|
||||
{
|
||||
idMD6Branch *serializedTrees[2];
|
||||
idAnimStack *animStack;
|
||||
idClip *clip;
|
||||
bool createdThroughSerialization;
|
||||
};
|
||||
#if defined(_WIN32) && !defined(_WIN64)
|
||||
static_assert(sizeof(idAnimator_Base::serializeProps_t) == 20,
|
||||
"Recovered animator serialization properties ABI changed");
|
||||
static_assert(sizeof(idAnimator_Base) == 40,
|
||||
"Recovered idAnimator_Base ABI changed");
|
||||
static_assert(sizeof(idAnimatorParms_Base) == 56,
|
||||
"Recovered idAnimatorParms_Base ABI changed");
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
#include "gamelib/animstack/animator_paused.h"
|
||||
|
||||
idMD6Branch* GameLib_AllocMD6Branch(idAnimStack* stack);
|
||||
idMD6LeafPause* GameLib_AllocMD6LeafPause(idAnimStack* stack);
|
||||
void GameLib_FreeMD6Branch(idAnimStack* stack, idMD6Branch* branch);
|
||||
void GameLib_FreeMD6LeafPause(idAnimStack* stack, idMD6LeafPause* leaf);
|
||||
int GameLib_GetMD6AnimNumFrames(const idMD6Anim* animation);
|
||||
|
||||
idAnimator_Paused::idAnimator_Paused()
|
||||
: idAnimator_Base()
|
||||
, leaf(nullptr)
|
||||
, mergeBranch(nullptr)
|
||||
, anim(nullptr) {
|
||||
}
|
||||
|
||||
idAnimator_Paused::~idAnimator_Paused() {
|
||||
leaf = nullptr;
|
||||
mergeBranch = nullptr;
|
||||
anim = nullptr;
|
||||
}
|
||||
|
||||
void idAnimator_Paused::SetAnim(const idMD6Anim* const animation) {
|
||||
anim = animation;
|
||||
if (animation != nullptr) {
|
||||
leaf->Init(animation, leaf->frame, leaf->wrapMode,
|
||||
MD6_WEIGHTGROUP_ALL);
|
||||
}
|
||||
leaf->anim = animation;
|
||||
}
|
||||
|
||||
void idAnimator_Paused::SetFrame(const float animationFrame) {
|
||||
leaf->frame = animationFrame;
|
||||
}
|
||||
|
||||
float idAnimator_Paused::GetFrame() const {
|
||||
return leaf->frame;
|
||||
}
|
||||
|
||||
void idAnimator_Paused::SetNormalizedFrame(const float normalizedFrame) {
|
||||
leaf->frame = static_cast<float>(GameLib_GetMD6AnimNumFrames(leaf->anim) - 1) *
|
||||
normalizedFrame;
|
||||
}
|
||||
|
||||
bool idAnimator_Paused::InternalInit(
|
||||
const idAnimatorParms_Base& parameters) {
|
||||
const idAnimatorParms_Pause& pauseParameters =
|
||||
static_cast<const idAnimatorParms_Pause&>(parameters);
|
||||
|
||||
mergeBranch = GameLib_AllocMD6Branch(parameters.animStack);
|
||||
leaf = GameLib_AllocMD6LeafPause(parameters.animStack);
|
||||
mergeBranch->right = leaf;
|
||||
anim = pauseParameters.anim;
|
||||
if (anim != nullptr) {
|
||||
leaf->Init(anim, leaf->frame, leaf->wrapMode, MD6_WEIGHTGROUP_ALL);
|
||||
}
|
||||
leaf->anim = anim;
|
||||
|
||||
if (pauseParameters.normalizedStartFrame < 0.0f) {
|
||||
leaf->frame = pauseParameters.startFrame;
|
||||
} else {
|
||||
SetNormalizedFrame(pauseParameters.normalizedStartFrame);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void idAnimator_Paused::InternalShutdown(idAnimStack* const stack) {
|
||||
if (mergeBranch != nullptr) {
|
||||
GameLib_FreeMD6Branch(stack, mergeBranch);
|
||||
mergeBranch = nullptr;
|
||||
}
|
||||
if (leaf != nullptr) {
|
||||
GameLib_FreeMD6LeafPause(stack, leaf);
|
||||
leaf = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool idAnimator_Paused::InternalIsContributing() const {
|
||||
return leaf != nullptr && leaf->anim != nullptr;
|
||||
}
|
||||
@@ -1,48 +1,60 @@
|
||||
#pragma once
|
||||
|
||||
// Reconstructed C++ declarations from IDA Local Types and PDB/DIA metadata.
|
||||
// Original PDB header: w:\tech5\engine\gamelib\animstack\animator_paused.h
|
||||
// Recovered logical types: 2
|
||||
// Signatures retain Xbox 360 ABI evidence and may still require manual review.
|
||||
#include "gamelib/animstack/animator_base.h"
|
||||
|
||||
class idAnimatorParms_Pause;
|
||||
|
||||
// IDA Local Type ordinal 18898; PDB kind: class.
|
||||
class idAnimator_Paused : public idAnimator_Base
|
||||
{
|
||||
class idAnimator_Paused : public idAnimator_Base {
|
||||
public:
|
||||
// Recovered virtual interface; IDA vtable ordinal 18899.
|
||||
virtual ~idAnimator_Paused();
|
||||
virtual idAnimator_Base::priority_t GetStackPriority();
|
||||
virtual serializeType_t GetSerializeType();
|
||||
virtual void SerializeSnapshot(idSerializer *);
|
||||
virtual void PreBlendSnapshot(idAnimStack *, int, const int, float);
|
||||
virtual void PreSerializeInit(idAnimStack *, idClip *, idGameTimeManager *);
|
||||
virtual bool InternalInit(const idAnimatorParms_Base *);
|
||||
virtual bool InternalPostInit(const idAnimatorParms_Base *);
|
||||
virtual void InternalShutdown(idAnimStack *);
|
||||
virtual void InternalPreBlendTree(const idAnimStack *, const int, const int);
|
||||
virtual void InternalPostBlendTree(const idAnimStack *, const int);
|
||||
virtual void InternalStart(const idAnimStack *, const int, const idTypesafeNumber<int,enum gameTimeUnique_t>);
|
||||
virtual void InternalEnd(const idAnimStack *, const int, const idTypesafeNumber<int,enum gameTimeUnique_t>);
|
||||
virtual void InternalBlend(const idAnimStack *, const int, const float, const idTypesafeNumber<int,enum gameTimeUnique_t>);
|
||||
virtual bool InternalIsContributing();
|
||||
virtual const idMD6Branch *InternalGetMergeBranch();
|
||||
virtual idMD6Branch *InternalGetMergeBranch_2();
|
||||
virtual void InternalPause(const idAnimStack *, const idTypesafeNumber<int,enum gameTimeUnique_t>);
|
||||
virtual void InternalUnpause(const idAnimStack *, const idTypesafeNumber<int,enum gameTimeUnique_t>);
|
||||
virtual const idMD6Branch *InternalGetEndBranch();
|
||||
virtual idMD6Branch *InternalGetEndBranch_2();
|
||||
idAnimator_Paused();
|
||||
~idAnimator_Paused() override;
|
||||
|
||||
idMD6LeafPause *leaf;
|
||||
idMD6Branch *mergeBranch;
|
||||
const idMD6Anim *anim;
|
||||
void SetAnim(const idMD6Anim* animation);
|
||||
const idMD6Anim* GetAnim() const { return anim; }
|
||||
void SetFrame(float frame);
|
||||
float GetFrame() const;
|
||||
void SetNormalizedFrame(float normalizedFrame);
|
||||
|
||||
bool InternalInit(const idAnimatorParms_Base& parameters) override;
|
||||
void InternalShutdown(idAnimStack* stack) override;
|
||||
bool InternalIsContributing() const override;
|
||||
const idMD6Branch* InternalGetMergeBranch() const override {
|
||||
return mergeBranch;
|
||||
}
|
||||
idMD6Branch* InternalGetMergeBranch() override { return mergeBranch; }
|
||||
|
||||
idMD6LeafPause* leaf;
|
||||
idMD6Branch* mergeBranch;
|
||||
const idMD6Anim* anim;
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 18908; PDB kind: class.
|
||||
class idAnimatorParms_Pause : public idAnimatorParms_Base
|
||||
{
|
||||
class idAnimatorParms_Pause : public idAnimatorParms_Base {
|
||||
public:
|
||||
const idMD6Anim *anim;
|
||||
float startFrame;
|
||||
float normalizedStartFrame;
|
||||
idAnimatorParms_Pause(const idMD6Anim* animation, float startFrame_,
|
||||
float normalizedStartFrame_, idAnimStack* stack, const char* name_,
|
||||
int blendOp_, int originBlend_, md6WeightGroup_t weightGroup_,
|
||||
md6WeightGroup_t filterGroup_, float alpha_)
|
||||
: idAnimatorParms_Base()
|
||||
, anim(animation)
|
||||
, startFrame(startFrame_)
|
||||
, normalizedStartFrame(normalizedStartFrame_) {
|
||||
animStack = stack;
|
||||
name = name_;
|
||||
blendOp = blendOp_;
|
||||
originBlend = originBlend_;
|
||||
weightGroup = weightGroup_;
|
||||
filterGroup = filterGroup_;
|
||||
alpha = alpha_;
|
||||
}
|
||||
|
||||
const idMD6Anim* anim;
|
||||
float startFrame;
|
||||
float normalizedStartFrame;
|
||||
};
|
||||
|
||||
#if defined(_WIN32) && !defined(_WIN64)
|
||||
static_assert(sizeof(idAnimator_Paused) == 52,
|
||||
"Recovered idAnimator_Paused ABI changed");
|
||||
static_assert(sizeof(idAnimatorParms_Pause) == 68,
|
||||
"Recovered idAnimatorParms_Pause ABI changed");
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
#include "gamelib/animstack/animator_proxy.h"
|
||||
|
||||
// These allocator calls are owned by the idAnimStack/md6Allocator recovery.
|
||||
// Keeping the boundary explicit allows the complete Proxy TU to compile now
|
||||
// without inventing an allocator layout or a temporary allocation policy.
|
||||
idMD6Branch* GameLib_AllocMD6Branch(idAnimStack* stack);
|
||||
void GameLib_FreeMD6Branch(idAnimStack* stack, idMD6Branch* branch);
|
||||
|
||||
idAnimator_Proxy::idAnimator_Proxy()
|
||||
: idAnimator_Base()
|
||||
, mergeBranch(nullptr) {
|
||||
}
|
||||
|
||||
idAnimator_Proxy::~idAnimator_Proxy() {
|
||||
mergeBranch = nullptr;
|
||||
}
|
||||
|
||||
void idAnimator_Proxy::SetTree(idMD6Node* const tree) {
|
||||
if (mergeBranch != nullptr) {
|
||||
mergeBranch->right = tree;
|
||||
}
|
||||
}
|
||||
|
||||
bool idAnimator_Proxy::InternalInit(
|
||||
const idAnimatorParms_Base& parameters) {
|
||||
mergeBranch = GameLib_AllocMD6Branch(parameters.animStack);
|
||||
return true;
|
||||
}
|
||||
|
||||
void idAnimator_Proxy::InternalShutdown(idAnimStack* const stack) {
|
||||
if (stack != nullptr && mergeBranch != nullptr) {
|
||||
GameLib_FreeMD6Branch(stack, mergeBranch);
|
||||
mergeBranch = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool idAnimator_Proxy::InternalIsContributing() const {
|
||||
return mergeBranch != nullptr && mergeBranch->right != nullptr;
|
||||
}
|
||||
@@ -1,37 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
// Reconstructed C++ declarations from IDA Local Types and PDB/DIA metadata.
|
||||
// Original PDB header: w:\tech5\engine\gamelib\animstack\animator_proxy.h
|
||||
// Recovered logical types: 1
|
||||
// Signatures retain Xbox 360 ABI evidence and may still require manual review.
|
||||
#include "gamelib/animstack/animator_base.h"
|
||||
|
||||
|
||||
// IDA Local Type ordinal 15219; PDB kind: class.
|
||||
class idAnimator_Proxy : public idAnimator_Base
|
||||
{
|
||||
class idAnimator_Proxy : public idAnimator_Base {
|
||||
public:
|
||||
// Recovered virtual interface; IDA vtable ordinal 15220.
|
||||
virtual ~idAnimator_Proxy();
|
||||
virtual idAnimator_Base::priority_t GetStackPriority();
|
||||
virtual serializeType_t GetSerializeType();
|
||||
virtual void SerializeSnapshot(idSerializer *);
|
||||
virtual void PreBlendSnapshot(idAnimStack *, int, const int, float);
|
||||
virtual void PreSerializeInit(idAnimStack *, idClip *, idGameTimeManager *);
|
||||
virtual bool InternalInit(const idAnimatorParms_Base *);
|
||||
virtual bool InternalPostInit(const idAnimatorParms_Base *);
|
||||
virtual void InternalShutdown(idAnimStack *);
|
||||
virtual void InternalPreBlendTree(const idAnimStack *, const int, const int);
|
||||
virtual void InternalPostBlendTree(const idAnimStack *, const int);
|
||||
virtual void InternalStart(const idAnimStack *, const int, const idTypesafeNumber<int,enum gameTimeUnique_t>);
|
||||
virtual void InternalEnd(const idAnimStack *, const int, const idTypesafeNumber<int,enum gameTimeUnique_t>);
|
||||
virtual void InternalBlend(const idAnimStack *, const int, const float, const idTypesafeNumber<int,enum gameTimeUnique_t>);
|
||||
virtual bool InternalIsContributing();
|
||||
virtual const idMD6Branch *InternalGetMergeBranch();
|
||||
virtual idMD6Branch *InternalGetMergeBranch_2();
|
||||
virtual void InternalPause(const idAnimStack *, const idTypesafeNumber<int,enum gameTimeUnique_t>);
|
||||
virtual void InternalUnpause(const idAnimStack *, const idTypesafeNumber<int,enum gameTimeUnique_t>);
|
||||
virtual const idMD6Branch *InternalGetEndBranch();
|
||||
virtual idMD6Branch *InternalGetEndBranch_2();
|
||||
idAnimator_Proxy();
|
||||
~idAnimator_Proxy() override;
|
||||
|
||||
idMD6Branch *mergeBranch;
|
||||
void SetTree(idMD6Node* tree);
|
||||
bool InternalInit(const idAnimatorParms_Base& parameters) override;
|
||||
void InternalShutdown(idAnimStack* stack) override;
|
||||
bool InternalIsContributing() const override;
|
||||
const idMD6Branch* InternalGetMergeBranch() const override {
|
||||
return mergeBranch;
|
||||
}
|
||||
idMD6Branch* InternalGetMergeBranch() override {
|
||||
return mergeBranch;
|
||||
}
|
||||
|
||||
idMD6Branch* mergeBranch;
|
||||
};
|
||||
|
||||
#if defined(_WIN32) && !defined(_WIN64)
|
||||
static_assert(sizeof(idAnimator_Proxy) == 44,
|
||||
"Recovered idAnimator_Proxy ABI changed");
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
#pragma once
|
||||
|
||||
#include "idlib/bv/bounds.h"
|
||||
#include "idlib/math/vector.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
class idAnimStack;
|
||||
class idClip;
|
||||
class idGameTimeManager;
|
||||
class idMD6Anim;
|
||||
|
||||
enum gameTimeUnique_t : int;
|
||||
|
||||
enum md6WeightGroup_t : int {
|
||||
MD6_WEIGHTGROUP_ALL = 0,
|
||||
MD6_WEIGHTGROUP_LEGS = 1,
|
||||
MD6_WEIGHTGROUP_TORSO = 2,
|
||||
MD6_WEIGHTGROUP_HEAD = 3,
|
||||
MD6_WEIGHTGROUP_FACE = 4,
|
||||
MD6_WEIGHTGROUP_MOUTH = 5,
|
||||
MD6_WEIGHTGROUP_EYELIDS = 6,
|
||||
MD6_WEIGHTGROUP_USER0 = 7,
|
||||
MD6_WEIGHTGROUP_MAX = 8
|
||||
};
|
||||
|
||||
enum serializeType_t : int {
|
||||
STYPE_GENERIC = 0,
|
||||
STYPE_AF = 1,
|
||||
STYPE_ANIMWEB_HANDS = 2,
|
||||
STYPE_WALK_IK = 3,
|
||||
STYPE_TORSO_TRACKER = 4,
|
||||
STYPE_PAIN = 5,
|
||||
STYPE_JOINTMOD = 6,
|
||||
STYPE_REACH_IK = 7,
|
||||
STYPE_MAX = 8
|
||||
};
|
||||
|
||||
class idMD6Node {
|
||||
public:
|
||||
enum nodeType_t : int {
|
||||
NODE_BRANCH = 0,
|
||||
NODE_LEAF_PAUSE = 1,
|
||||
NODE_LEAF_PLAY = 2,
|
||||
NODE_BLEND_BRANCH = 3,
|
||||
NODE_BLENDA_BRANCH = 4,
|
||||
NODE_FUSION_BRANCH = 5,
|
||||
NODE_BEST_LEAF = 6,
|
||||
NODE_TAG_FILTER = 7,
|
||||
NODE_NONE = 0xFF
|
||||
};
|
||||
|
||||
std::uint8_t type;
|
||||
};
|
||||
|
||||
class idMD6Branch : public idMD6Node {
|
||||
public:
|
||||
enum blendType_t : int {
|
||||
BLEND_LINEAR = 0,
|
||||
BLEND_EASEIN = 1,
|
||||
BLEND_EASEOUT = 2,
|
||||
BLEND_EASEIN_EASEOUT = 3,
|
||||
BLEND_TYPE_COUNT = 4
|
||||
};
|
||||
|
||||
idMD6Node* left;
|
||||
idMD6Node* right;
|
||||
int leftTimeOverride;
|
||||
int rightTimeOverride;
|
||||
std::uint8_t filterGroup;
|
||||
std::uint8_t op;
|
||||
std::uint8_t originBlend;
|
||||
float currentAlpha;
|
||||
float targetAlpha;
|
||||
float alphaRate;
|
||||
blendType_t blendType;
|
||||
};
|
||||
|
||||
// The leaf layout is materialized only to the extent proven by the recovered
|
||||
// md6animtree and idAnimator_Paused bodies. The two list records remain opaque
|
||||
// until the joint-mod translation unit is activated.
|
||||
struct idMD6OpaqueList {
|
||||
void* list;
|
||||
int num;
|
||||
int size;
|
||||
std::int16_t granularity;
|
||||
std::uint8_t memTag;
|
||||
std::uint8_t listStatic;
|
||||
};
|
||||
|
||||
class idMD6LeafPause : public idMD6Node {
|
||||
public:
|
||||
std::uint8_t nodePadding[3];
|
||||
const idMD6Anim* anim;
|
||||
std::uint8_t weightGroup;
|
||||
std::uint8_t wrapMode;
|
||||
std::uint8_t initCounter;
|
||||
std::uint8_t pad;
|
||||
int currentDeferred;
|
||||
idMD6OpaqueList animMods[2];
|
||||
std::int16_t flags;
|
||||
std::uint8_t framePadding[2];
|
||||
float frame;
|
||||
idBounds bounds;
|
||||
|
||||
void Init(const idMD6Anim* animation, float animationFrame,
|
||||
std::uint8_t animationWrapMode, md6WeightGroup_t animationWeightGroup) {
|
||||
frame = animationFrame;
|
||||
anim = animation;
|
||||
flags = 0;
|
||||
weightGroup = static_cast<std::uint8_t>(animationWeightGroup);
|
||||
wrapMode = animationWrapMode;
|
||||
}
|
||||
};
|
||||
|
||||
struct idGameTimeManagerPtr {
|
||||
idGameTimeManager* gameTimeManager;
|
||||
};
|
||||
|
||||
#if defined(_WIN32) && !defined(_WIN64)
|
||||
static_assert(sizeof(idMD6Node) == 1,
|
||||
"Recovered idMD6Node ABI changed");
|
||||
static_assert(sizeof(idMD6Branch) == 40,
|
||||
"Recovered idMD6Branch ABI changed");
|
||||
static_assert(sizeof(idMD6OpaqueList) == 16,
|
||||
"Recovered MD6 opaque list ABI changed");
|
||||
static_assert(sizeof(idMD6LeafPause) == 80,
|
||||
"Recovered idMD6LeafPause ABI changed");
|
||||
static_assert(sizeof(idGameTimeManagerPtr) == 4,
|
||||
"Recovered idGameTimeManagerPtr ABI changed");
|
||||
#endif
|
||||
@@ -1,13 +1,61 @@
|
||||
#pragma once
|
||||
|
||||
// Reconstructed C++ declarations from IDA Local Types and PDB/DIA metadata.
|
||||
// Original PDB header: w:\tech5\engine\gamelib\animstack\animweb\animwebpath.h
|
||||
// Recovered logical types: 1
|
||||
// Signatures retain Xbox 360 ABI evidence and may still require manual review.
|
||||
#include "idlib/index.h"
|
||||
#include "idlib/text/str.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
|
||||
// IDA Local Type ordinal 16590; PDB kind: class.
|
||||
class idAnimWebPath : public idStr
|
||||
{
|
||||
// idAnimWebPath has no storage beyond its recovered idStr base.
|
||||
class idAnimWebPath : public idStr {
|
||||
public:
|
||||
using idStr::idStr;
|
||||
using idStr::operator=;
|
||||
};
|
||||
|
||||
// The owning idDeclAnimWeb declaration is not compile-locked yet. This tag
|
||||
// retains the recovered two-byte node-index representation without importing
|
||||
// that malformed generated declaration into GameLib's active boundary.
|
||||
enum class idAnimWebInvalidNodeIndex_t : int {
|
||||
invalid = -1
|
||||
};
|
||||
|
||||
using idAnimWebNodeIndex = idIndex<short, idAnimWebInvalidNodeIndex_t>;
|
||||
|
||||
struct idAnimWebRoute {
|
||||
idAnimWebNodeIndex path[32];
|
||||
int num;
|
||||
int cost;
|
||||
|
||||
// Materialized in the authoritative animwebpath.h dump.
|
||||
void Invert() {
|
||||
for (int index = 0; index < num / 2; ++index) {
|
||||
std::swap(path[index], path[num - index - 1]);
|
||||
}
|
||||
}
|
||||
|
||||
// Materialized in the authoritative animwebpath.h dump. A shared prefix
|
||||
// node is consumed so concatenated routes do not duplicate their join.
|
||||
void AppendPath(const idAnimWebRoute& other) {
|
||||
int first = 0;
|
||||
if (num > 0) {
|
||||
while (first < other.num
|
||||
&& path[num - 1].Get() == other.path[first].Get()) {
|
||||
++first;
|
||||
}
|
||||
}
|
||||
while (first < other.num && num < 32) {
|
||||
path[num++] = other.path[first++];
|
||||
}
|
||||
cost += other.cost;
|
||||
}
|
||||
};
|
||||
|
||||
#if INTPTR_MAX == INT32_MAX
|
||||
static_assert(sizeof(idAnimWebPath) == 32,
|
||||
"Recovered idAnimWebPath ABI changed");
|
||||
static_assert(sizeof(idAnimWebNodeIndex) == 2,
|
||||
"Recovered AnimWeb node index ABI changed");
|
||||
static_assert(sizeof(idAnimWebRoute) == 72,
|
||||
"Recovered idAnimWebRoute ABI changed");
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
#include "gamelib/animstack/animweb/animwebutils.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
float LerpToWithScale(const float current, const float destination,
|
||||
const float scale) {
|
||||
const float delta = destination - current;
|
||||
return delta <= -0.000001f || delta >= 0.000001f
|
||||
? current + delta * scale
|
||||
: destination;
|
||||
}
|
||||
|
||||
float LerpToWithRate(const float current, const float destination,
|
||||
const float absoluteRate) {
|
||||
if (current < destination) {
|
||||
const float next = current + absoluteRate;
|
||||
if (next < current) {
|
||||
return current;
|
||||
}
|
||||
return next > destination ? destination : next;
|
||||
}
|
||||
if (current > destination) {
|
||||
const float next = current - absoluteRate;
|
||||
if (next > current) {
|
||||
return current;
|
||||
}
|
||||
return next < destination ? destination : next;
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
void SetSync8(const float angle, const bool reverse, float& index1,
|
||||
float& index2, float& blend) {
|
||||
constexpr float inverse45 = 1.0f / 45.0f;
|
||||
const int sector = static_cast<int>(angle * inverse45);
|
||||
const float fraction = (angle - static_cast<float>(sector) * 45.0f)
|
||||
* inverse45;
|
||||
|
||||
const int first = reverse ? 7 - sector : sector;
|
||||
index1 = static_cast<float>(first);
|
||||
index2 = static_cast<float>((first + 1) & 7);
|
||||
blend = reverse ? 1.0f - fraction : fraction;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool UpdateWalkBlend(const idVec3& velocity, const idMat3& axis,
|
||||
const float lerpScale, float& blendBack, float& blendRight,
|
||||
float& blendAngle, float& sync8Index1, float& sync8Index2,
|
||||
float& sync8BlendAngle) {
|
||||
const float speed = velocity.Length();
|
||||
if (speed <= 0.1f) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const float inverseSpeed = 1.0f / speed;
|
||||
const float forward = (std::max)(-1.0f, (std::min)(1.0f,
|
||||
velocity.Dot(axis[0]) * inverseSpeed));
|
||||
const float right = (std::max)(-1.0f, (std::min)(1.0f,
|
||||
velocity.Dot(axis[1]) * inverseSpeed));
|
||||
constexpr float radiansToDegrees = 57.29577951308232f;
|
||||
const float angle = std::acos(forward) * radiansToDegrees;
|
||||
const float forwardAmount = forward < 0.0f
|
||||
? (angle - 90.0f) / 90.0f
|
||||
: 1.0f - angle / 90.0f;
|
||||
|
||||
blendBack = LerpToWithScale(blendBack, forward < 0.0f ? 1.0f : 0.0f,
|
||||
lerpScale);
|
||||
blendRight = LerpToWithScale(blendRight, right < 0.0f ? 1.0f : 0.0f,
|
||||
lerpScale);
|
||||
blendAngle = LerpToWithScale(blendAngle, 1.0f - forwardAmount,
|
||||
lerpScale);
|
||||
SetSync8(angle, right >= 0.0f, sync8Index1, sync8Index2,
|
||||
sync8BlendAngle);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UpdateWalkBlendFromAngle(const float angle, const float lerpScale,
|
||||
float& blendBack, float& blendRight, float& blendAngle,
|
||||
float& sync8Index1, float& sync8Index2, float& sync8BlendAngle) {
|
||||
const bool forward = angle > -90.0f && angle < 90.0f;
|
||||
const bool positive = angle > 0.0f && angle < 180.0f;
|
||||
const float absoluteAngle = std::fabs(angle);
|
||||
const float forwardAmount = forward
|
||||
? 1.0f - absoluteAngle / 90.0f
|
||||
: (absoluteAngle - 90.0f) / 90.0f;
|
||||
|
||||
blendBack = LerpToWithScale(blendBack, forward ? 0.0f : 1.0f,
|
||||
lerpScale);
|
||||
blendRight = LerpToWithScale(blendRight, positive ? 0.0f : 1.0f,
|
||||
lerpScale);
|
||||
blendAngle = LerpToWithScale(blendAngle, 1.0f - forwardAmount,
|
||||
lerpScale);
|
||||
SetSync8(absoluteAngle, positive, sync8Index1, sync8Index2,
|
||||
sync8BlendAngle);
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "idlib/math/vector.h"
|
||||
|
||||
float LerpToWithScale(float current, float destination, float scale);
|
||||
float LerpToWithRate(float current, float destination, float absoluteRate);
|
||||
|
||||
bool UpdateWalkBlend(const idVec3& velocity, const idMat3& axis,
|
||||
float lerpScale, float& blendBack, float& blendRight, float& blendAngle,
|
||||
float& sync8Index1, float& sync8Index2, float& sync8BlendAngle);
|
||||
|
||||
bool UpdateWalkBlendFromAngle(float angle, float lerpScale,
|
||||
float& blendBack, float& blendRight, float& blendAngle,
|
||||
float& sync8Index1, float& sync8Index2, float& sync8BlendAngle);
|
||||
@@ -1,14 +1,71 @@
|
||||
#pragma once
|
||||
|
||||
// Reconstructed C++ declarations from IDA Local Types and PDB/DIA metadata.
|
||||
// Original PDB header: w:\tech5\engine\gamelib\animstack\animweb\dijkstra.h
|
||||
// Recovered logical types: 1
|
||||
// Signatures retain Xbox 360 ABI evidence and may still require manual review.
|
||||
#include "idlib/index.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
// IDA Local Type ordinal 23485; PDB kind: class.
|
||||
class idDijkstra<idAnimator_AnimWeb,idDeclAnimWeb::idNodeCache,idIndex<short,enum idDeclAnimWeb::invalidNodeIndex_t>,idDeclAnimWeb::idEdgeCache,idAnimWebRoute>
|
||||
{
|
||||
// Recovered idDijkstra storage and interface. The materialized Xbox binary is
|
||||
// the AnimWeb specialization, while keeping the original template boundary
|
||||
// lets the algorithm be compiled and tested before idDeclAnimWeb is active.
|
||||
template<typename nodeHolderType, typename nodeCacheType,
|
||||
typename nodeIndexType, typename edgeCacheType, typename pathType>
|
||||
class idDijkstra {
|
||||
public:
|
||||
int lastStartNode;
|
||||
struct djScratch_t {
|
||||
short prevIndex;
|
||||
std::uint16_t costAndVisited;
|
||||
|
||||
int Cost() const {
|
||||
return costAndVisited & 0x7FFFu;
|
||||
}
|
||||
|
||||
bool Visited() const {
|
||||
return (costAndVisited & 0x8000u) != 0;
|
||||
}
|
||||
|
||||
void SetCost(const int cost) {
|
||||
costAndVisited = static_cast<std::uint16_t>(
|
||||
(costAndVisited & 0x8000u) | (cost & 0x7FFF));
|
||||
}
|
||||
|
||||
void SetVisited(const bool visited) {
|
||||
if (visited) {
|
||||
costAndVisited |= 0x8000u;
|
||||
} else {
|
||||
costAndVisited &= 0x7FFFu;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
idDijkstra()
|
||||
: lastStartNode(-1) {
|
||||
}
|
||||
|
||||
bool TracePath(
|
||||
const nodeHolderType& nodeHolder,
|
||||
nodeIndexType startNode,
|
||||
nodeIndexType destNode,
|
||||
const djScratch_t* scratchBuff,
|
||||
pathType& path);
|
||||
|
||||
bool FindShortestPaths(
|
||||
const nodeHolderType& nodeHolder,
|
||||
nodeIndexType startNode,
|
||||
const nodeIndexType* destNodes,
|
||||
int numDestNodes,
|
||||
const edgeCacheType* edges,
|
||||
djScratch_t* scratchBuff,
|
||||
int skipFlags,
|
||||
int requiredFlags);
|
||||
|
||||
int lastStartNode;
|
||||
};
|
||||
|
||||
static_assert(sizeof(typename idDijkstra<int, int,
|
||||
idIndex<short, idRecoveredInvalidIndex>, int, int>::djScratch_t) == 4,
|
||||
"Recovered idDijkstra scratch ABI changed");
|
||||
static_assert(sizeof(idDijkstra<int, int,
|
||||
idIndex<short, idRecoveredInvalidIndex>, int, int>) == 4,
|
||||
"Recovered idDijkstra ABI changed");
|
||||
|
||||
#include "gamelib/animstack/animweb/dijkstra_impl.h"
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <queue>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
template<typename nodeHolderType, typename nodeCacheType,
|
||||
typename nodeIndexType, typename edgeCacheType, typename pathType>
|
||||
bool idDijkstra<nodeHolderType, nodeCacheType, nodeIndexType,
|
||||
edgeCacheType, pathType>::TracePath(
|
||||
const nodeHolderType& nodeHolder,
|
||||
const nodeIndexType startNode,
|
||||
const nodeIndexType destNode,
|
||||
const djScratch_t* scratchBuff,
|
||||
pathType& path) {
|
||||
(void)nodeHolder;
|
||||
|
||||
const int start = static_cast<int>(startNode.Get());
|
||||
const int destination = static_cast<int>(destNode.Get());
|
||||
if (lastStartNode != start) {
|
||||
return false;
|
||||
}
|
||||
|
||||
path.num = 0;
|
||||
path.cost = 0x7FFFFFFF;
|
||||
if (destination == start) {
|
||||
return false;
|
||||
}
|
||||
|
||||
path.path[path.num++] = nodeIndexType(
|
||||
static_cast<short>(destination));
|
||||
const djScratch_t* scratch = &scratchBuff[destination];
|
||||
path.cost = scratch->Cost();
|
||||
|
||||
while (path.num < 32) {
|
||||
const short previous = scratch->prevIndex;
|
||||
if (previous < 0) {
|
||||
path.cost = 0x7FFFFFFF;
|
||||
path.num = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
path.path[path.num++] = nodeIndexType(previous);
|
||||
scratch = &scratchBuff[previous];
|
||||
if (previous == start) {
|
||||
path.Invert();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// The recovered implementation returns the first 32 nodes when a route
|
||||
// exceeds the fixed route buffer, then reverses that bounded prefix.
|
||||
path.Invert();
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename nodeHolderType, typename nodeCacheType,
|
||||
typename nodeIndexType, typename edgeCacheType, typename pathType>
|
||||
bool idDijkstra<nodeHolderType, nodeCacheType, nodeIndexType,
|
||||
edgeCacheType, pathType>::FindShortestPaths(
|
||||
const nodeHolderType& nodeHolder,
|
||||
const nodeIndexType startNode,
|
||||
const nodeIndexType* destNodes,
|
||||
const int numDestNodes,
|
||||
const edgeCacheType* edges,
|
||||
djScratch_t* scratchBuff,
|
||||
const int skipFlags,
|
||||
const int requiredFlags) {
|
||||
const int nodeCount = nodeHolder.decl->nodes.num;
|
||||
std::memset(scratchBuff, 0xFF,
|
||||
sizeof(djScratch_t) * static_cast<std::size_t>(nodeCount));
|
||||
|
||||
for (int listIndex = 0;
|
||||
listIndex < nodeHolder.pathableSubWebNodeLists.num;
|
||||
++listIndex) {
|
||||
const auto* const pathable =
|
||||
nodeHolder.pathableSubWebNodeLists.list[listIndex];
|
||||
for (int nodeIndex = 0; nodeIndex < pathable->num; ++nodeIndex) {
|
||||
const int value = static_cast<int>(pathable->list[nodeIndex].Get());
|
||||
scratchBuff[value].SetVisited(false);
|
||||
}
|
||||
}
|
||||
|
||||
const int start = static_cast<int>(startNode.Get());
|
||||
scratchBuff[start].SetVisited(false);
|
||||
scratchBuff[start].SetCost(0);
|
||||
scratchBuff[start].prevIndex = -1;
|
||||
lastStartNode = -1;
|
||||
|
||||
using heapEntry_t = std::pair<int, int>;
|
||||
std::priority_queue<heapEntry_t, std::vector<heapEntry_t>,
|
||||
std::greater<heapEntry_t>> heap;
|
||||
heap.push(heapEntry_t(0, start));
|
||||
|
||||
int destinationsFound = 0;
|
||||
int processedNodes = 0;
|
||||
while (!heap.empty() && processedNodes < nodeCount) {
|
||||
const int node = heap.top().second;
|
||||
heap.pop();
|
||||
|
||||
djScratch_t& nodeScratch = scratchBuff[node];
|
||||
if (nodeScratch.Visited()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int destinationIndex = 0;
|
||||
destinationIndex < numDestNodes;
|
||||
++destinationIndex) {
|
||||
if (destNodes[destinationIndex].Get() == node) {
|
||||
++destinationsFound;
|
||||
if (destinationsFound == numDestNodes) {
|
||||
lastStartNode = start;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const nodeCacheType& nodeCache = nodeHolder.decl->nodeCache.list[node];
|
||||
const int nodeCost = nodeScratch.Cost();
|
||||
nodeScratch.SetVisited(true);
|
||||
|
||||
if ((nodeCache.flags & skipFlags) == 0
|
||||
&& (nodeCache.flags & requiredFlags) == requiredFlags) {
|
||||
for (int localEdge = 0; localEdge < nodeCache.numEdges;
|
||||
++localEdge) {
|
||||
const int edgeIndex = nodeHolder.decl->edgeIndexCache[
|
||||
nodeCache.edgeIndexOffset + localEdge];
|
||||
const edgeCacheType& edge = edges[edgeIndex];
|
||||
const int destination = static_cast<int>(
|
||||
edge.destNodeIndex.Get());
|
||||
djScratch_t& destinationScratch = scratchBuff[destination];
|
||||
if (destinationScratch.Visited()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const float scaledCost = static_cast<float>(
|
||||
nodeHolder.InternalGetEdgeCost(
|
||||
nodeIndexType(static_cast<short>(node)),
|
||||
{ static_cast<short>(edgeIndex) }))
|
||||
* (static_cast<float>(edge.weightScale) * 0.0625f);
|
||||
const int newCost = nodeCost + static_cast<int>(scaledCost);
|
||||
if (destinationScratch.Cost() > newCost) {
|
||||
destinationScratch.prevIndex = static_cast<short>(node);
|
||||
destinationScratch.SetCost(newCost);
|
||||
heap.push(heapEntry_t(newCost, destination));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
++processedNodes;
|
||||
}
|
||||
|
||||
lastStartNode = start;
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
#include "gamelib/animstack/coneconstraint.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace {
|
||||
|
||||
idQuat Multiply(const idQuat& lhs, const idQuat& rhs) {
|
||||
return idQuat(
|
||||
lhs.w * rhs.x + lhs.x * rhs.w + lhs.y * rhs.z - lhs.z * rhs.y,
|
||||
lhs.w * rhs.y + lhs.y * rhs.w + lhs.z * rhs.x - lhs.x * rhs.z,
|
||||
lhs.w * rhs.z + lhs.z * rhs.w + lhs.x * rhs.y - lhs.y * rhs.x,
|
||||
lhs.w * rhs.w - lhs.x * rhs.x - lhs.y * rhs.y - lhs.z * rhs.z);
|
||||
}
|
||||
|
||||
idVec3 ToForward(const idQuat& quaternion) {
|
||||
return idVec3(
|
||||
1.0f - 2.0f * (quaternion.y * quaternion.y
|
||||
+ quaternion.z * quaternion.z),
|
||||
2.0f * (quaternion.x * quaternion.y
|
||||
- quaternion.w * quaternion.z),
|
||||
2.0f * (quaternion.w * quaternion.y
|
||||
+ quaternion.x * quaternion.z));
|
||||
}
|
||||
|
||||
idVec3 ConstrainDirection(
|
||||
const idVec3& center,
|
||||
const idVec3& direction,
|
||||
const float limitAngleDot,
|
||||
const float halfLimitAngleCosine,
|
||||
const float halfLimitAngleSine) {
|
||||
const float dot = center.Dot(direction);
|
||||
if (dot >= limitAngleDot) {
|
||||
return direction;
|
||||
}
|
||||
|
||||
idVec3 tangent = direction - center * dot;
|
||||
if (tangent.NormalizeFast() == 0.0f) {
|
||||
return center;
|
||||
}
|
||||
|
||||
const float sine = 2.0f * halfLimitAngleSine
|
||||
* halfLimitAngleCosine;
|
||||
return center * limitAngleDot + tangent * sine;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
idConeConstraint_Vec3::idConeConstraint_Vec3(
|
||||
const idConeConstraint_Vec3& other) = default;
|
||||
|
||||
idConeConstraint_Quat::idConeConstraint_Quat(
|
||||
const idConeConstraint_Quat& other) = default;
|
||||
|
||||
idConeConstraint_Vec3::idConeConstraint_Vec3(
|
||||
const idVec3& center_,
|
||||
const radians_t maxAngle_)
|
||||
: current(center_)
|
||||
, center(center_)
|
||||
, limitAngleDot(std::cos(maxAngle_.value))
|
||||
, halfLimitAngleCosine(std::cos(maxAngle_.value * 0.5f))
|
||||
, halfLimitAngleSine(std::sin(maxAngle_.value * 0.5f)) {
|
||||
}
|
||||
|
||||
void idConeConstraint_Vec3::RotateTo(
|
||||
const idVec3& dir,
|
||||
const float lerpRate) {
|
||||
const idVec3 target = ConstrainDirection(
|
||||
center,
|
||||
dir,
|
||||
limitAngleDot,
|
||||
halfLimitAngleCosine,
|
||||
halfLimitAngleSine);
|
||||
|
||||
current = current + (target - current) * lerpRate;
|
||||
current.NormalizeFast();
|
||||
}
|
||||
|
||||
idConeConstraint_Quat::idConeConstraint_Quat(
|
||||
const idQuat& center_,
|
||||
const radians_t maxAngle_)
|
||||
: current(center_)
|
||||
, center(center_)
|
||||
, limitAngleDot(std::cos(maxAngle_.value))
|
||||
, halfLimitAngleCosine(std::cos(maxAngle_.value * 0.5f))
|
||||
, halfLimitAngleSine(std::sin(maxAngle_.value * 0.5f)) {
|
||||
}
|
||||
|
||||
void idConeConstraint_Quat::RotateTo(
|
||||
const idVec3& dir,
|
||||
const float lerpRate) {
|
||||
const idVec3 forward = ToForward(current);
|
||||
const float forwardDot = dir.Dot(forward);
|
||||
|
||||
if (forwardDot > -1.0f && forwardDot < 1.0f) {
|
||||
idVec3 blendedForward = forward
|
||||
+ (dir - forward) * (lerpRate * 0.5f);
|
||||
blendedForward.NormalizeFast();
|
||||
|
||||
const float rotationDot = blendedForward.Dot(forward);
|
||||
const float rotationSine = rotationDot < 1.0f
|
||||
? std::sqrt(1.0f - rotationDot * rotationDot)
|
||||
: 0.0f;
|
||||
idVec3 rotationAxis = blendedForward.Cross(forward);
|
||||
rotationAxis.NormalizeFast();
|
||||
|
||||
const idQuat rotation(
|
||||
rotationAxis.x * rotationSine,
|
||||
rotationAxis.y * rotationSine,
|
||||
rotationAxis.z * rotationSine,
|
||||
rotationDot);
|
||||
current = Multiply(current, rotation);
|
||||
current.Normalize();
|
||||
}
|
||||
|
||||
const idVec3 centerForward = ToForward(center);
|
||||
const idVec3 currentForward = ToForward(current);
|
||||
if (centerForward.Dot(currentForward) < limitAngleDot) {
|
||||
idVec3 rotationAxis = currentForward.Cross(centerForward);
|
||||
rotationAxis.NormalizeFast();
|
||||
|
||||
const idQuat rotation(
|
||||
rotationAxis.x * halfLimitAngleSine,
|
||||
rotationAxis.y * halfLimitAngleSine,
|
||||
rotationAxis.z * halfLimitAngleSine,
|
||||
halfLimitAngleCosine);
|
||||
current = Multiply(center, rotation);
|
||||
current.Normalize();
|
||||
}
|
||||
}
|
||||
@@ -1,207 +1,57 @@
|
||||
#pragma once
|
||||
|
||||
// Reconstructed C++ declarations from IDA Local Types and PDB/DIA metadata.
|
||||
// Original PDB header: w:\tech5\engine\gamelib\animstack\coneconstraint.h
|
||||
// Recovered logical types: 4
|
||||
// Signatures retain Xbox 360 ABI evidence and may still require manual review.
|
||||
#include "idlib/math/quat.h"
|
||||
#include "idlib/math/radians.h"
|
||||
|
||||
|
||||
// IDA Local Type ordinal 16484; PDB kind: class.
|
||||
class idConeConstraint_Quat
|
||||
{
|
||||
// Reconstructed from the recovered coneconstraint.h declarations and the
|
||||
// six authoritative functions in animstack/coneconstraint.cpp.
|
||||
class idConeConstraint_Quat {
|
||||
public:
|
||||
idQuat current;
|
||||
idQuat center;
|
||||
float limitAngleDot;
|
||||
float halfLimitAngleCosine;
|
||||
float halfLimitAngleSine;
|
||||
idConeConstraint_Quat(const idConeConstraint_Quat& other);
|
||||
idConeConstraint_Quat(const idQuat& center_, radians_t maxAngle_);
|
||||
|
||||
void RotateTo(const idVec3& dir, float lerpRate);
|
||||
|
||||
idQuat current;
|
||||
idQuat center;
|
||||
float limitAngleDot;
|
||||
float halfLimitAngleCosine;
|
||||
float halfLimitAngleSine;
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 19154; PDB kind: class.
|
||||
class idConeConstraint_Vec3
|
||||
{
|
||||
class idConeConstraint_Vec3 {
|
||||
public:
|
||||
idVec3 current;
|
||||
idVec3 center;
|
||||
float limitAngleDot;
|
||||
float halfLimitAngleCosine;
|
||||
float halfLimitAngleSine;
|
||||
idConeConstraint_Vec3(const idConeConstraint_Vec3& other);
|
||||
idConeConstraint_Vec3(const idVec3& center_, radians_t maxAngle_);
|
||||
|
||||
void RotateTo(const idVec3& dir, float lerpRate);
|
||||
|
||||
idVec3 current;
|
||||
idVec3 center;
|
||||
float limitAngleDot;
|
||||
float halfLimitAngleCosine;
|
||||
float halfLimitAngleSine;
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 19160; PDB kind: class.
|
||||
class idTest_ConeConstraint : public idEntity
|
||||
{
|
||||
// The matrix form has declaration/layout evidence but no materialized methods
|
||||
// in this translation unit.
|
||||
class idConeConstraint_Mat3 {
|
||||
public:
|
||||
// Recovered virtual interface; IDA vtable ordinal 19161.
|
||||
virtual idTypeInfo *GetType();
|
||||
virtual ~idTest_ConeConstraint();
|
||||
virtual idEventArg *CallEvent(idEventArg *result, const idEventDef *, const idEventArg *);
|
||||
virtual bool RespondsTo(const idEventDef *);
|
||||
virtual idEventArg *InternalCallEvent(idEventArg *result, const idEventDef *, const idEventArg *);
|
||||
virtual bool InternalRespondsTo(const idEventDef *);
|
||||
virtual void PostSpawn();
|
||||
virtual void Remove();
|
||||
virtual void DeleteSubEntities();
|
||||
virtual bool Draw(idPlayer *);
|
||||
virtual void JobSync();
|
||||
virtual void Think();
|
||||
virtual void PauseThink();
|
||||
virtual bool ShouldEnterDormancy();
|
||||
virtual bool ShouldLeaveDormancy();
|
||||
virtual void DormantBegin();
|
||||
virtual void DormantEnd(const int);
|
||||
virtual idRenderModelInfo *GetRenderModelInfo();
|
||||
virtual const idRenderModelInfo *GetRenderModelInfo_2();
|
||||
virtual void GetScale(idVec3 *);
|
||||
virtual void SetScale(const idVec3 *);
|
||||
virtual void SetModelByName(const char *);
|
||||
virtual void SetModel(idRenderModel *);
|
||||
virtual const idMaterial *GetCustomMaterial();
|
||||
virtual void SetColor(const idVec4 *);
|
||||
virtual void SetColor_2(const idColor *);
|
||||
virtual void SetColor_3(const idVec3 *);
|
||||
virtual void SetColor_4(float, float, float);
|
||||
virtual void SetColor_5(float, float, float, float);
|
||||
virtual void GetColor(idVec4 *);
|
||||
virtual void GetColor_2(idColor *);
|
||||
virtual void GetColor_3(idVec3 *);
|
||||
virtual void Hide(bool);
|
||||
virtual void Hide_2();
|
||||
virtual void Show();
|
||||
virtual void GetModelTransform(idVec3 *, idMat3 *);
|
||||
virtual void GetSoundTransform(idVec3 *, idMat3 *);
|
||||
virtual void UpdateModelTransform();
|
||||
virtual void UpdateFX();
|
||||
virtual void ProjectOverlay(const idVec3 *, const idVec3 *, float, const char *);
|
||||
virtual idPresentable *AllocPresentable(idRenderModel *);
|
||||
virtual const idComponentTimeLine *GetComponentTimeLine();
|
||||
virtual idComponentTimeLine *GetComponentTimeLine_2();
|
||||
virtual bool UpdateAnimationControllers();
|
||||
virtual void UpdateAttachments();
|
||||
virtual const idAnimStack *GetAnimStack();
|
||||
virtual idAnimStack *GetAnimStack_2();
|
||||
virtual idIndex<short,enum invalidJointIndex_t> *GetJointIndexFromTrace(idIndex<short,enum invalidJointIndex_t> *result, trace_t);
|
||||
virtual awPathResult_t ChangeAnimWebState(const char *, const char *);
|
||||
virtual awPathResult_t ChangeAnimWebState_2(const char *);
|
||||
virtual awPathResult_t ForceAnimWebState(const char *);
|
||||
virtual awPathResult_t ChangeAnimWebStateVia(const char *, const char *, const char *, const char *);
|
||||
virtual awPathResult_t ChangeAnimWebStateVia_2(const char *, const char *);
|
||||
virtual idAnimWebCmdCtx *GetAnimWebCmdCtx();
|
||||
virtual const idAnimWebCmdCtx *GetAnimWebCmdCtx_2();
|
||||
virtual const idAnimator_AF *GetAF();
|
||||
virtual idAnimator_AF *GetAF_2();
|
||||
virtual void PreBind();
|
||||
virtual void PostBind();
|
||||
virtual void PreUnbind();
|
||||
virtual void PostUnbind();
|
||||
virtual const splineLocation_t *GetSplineLocation();
|
||||
virtual void SetAxis(const idMat3 *);
|
||||
virtual bool CanDisablePhysics(const idEntity *);
|
||||
virtual collide_t Collide(const int, trace_t *, const idVec3 *);
|
||||
virtual collide_t Contact(const int, contactInfo_t *);
|
||||
virtual void ApplyImpulse(const int, const int, const idVec3 *, const idVec3 *);
|
||||
virtual void ApplyImpulseFromEntity(const idEntity *, const int, const idVec3 *, const idVec3 *);
|
||||
virtual void ApplyForce(const int, const int, const idVec3 *, const idVec3 *);
|
||||
virtual bool Crush(const int);
|
||||
virtual void ApplyDamage(const int, const int, const idDeclDamage *);
|
||||
virtual void ActivatePhysics(const int);
|
||||
virtual void DeactivatePhysics(const int);
|
||||
virtual void ApplyWaterEffects(const int, const int);
|
||||
virtual void ApplyWaterSplashEffects(const int, const int, surfTypes_t, idPhysicsCallbacks::splashState_t);
|
||||
virtual bool TakesDamage();
|
||||
virtual void DamageFeedback(idEntity *, idEntity *, const idDeclDamage *, float *);
|
||||
virtual void KilledNotification(const idEntity *, const idEntity *, const idDeclDamage *, const float);
|
||||
virtual float Damage(idEntity *, idEntity *, const idDeclDamage *, const float, const idVec3 *, trace_t *);
|
||||
virtual bool CalcDamageImpulse(const idEntity *, const idEntity *, const idDeclDamage *, const float, const idVec3 *, const trace_t *, idVec3 *, idVec3 *);
|
||||
virtual bool IsTargetLockable(const idDeclAmmo *);
|
||||
virtual void AddProjectileLock();
|
||||
virtual void RemoveProjectileLock();
|
||||
virtual const idScriptObject *GetScriptObject();
|
||||
virtual idScriptObject *GetScriptObject_2();
|
||||
virtual bool ShouldConstructScriptObjectAtSpawn();
|
||||
virtual idThread *GetStateThread();
|
||||
virtual int AddThread(const idHandle<int,enum invalidThreadHandle_t,0>);
|
||||
virtual void RemoveThread(const idHandle<int,enum invalidThreadHandle_t,0>);
|
||||
virtual idHandle<int,enum invalidThreadHandle_t,0> *GetThread(idHandle<int,enum invalidThreadHandle_t,0> *result, const int);
|
||||
virtual int NumThreads();
|
||||
virtual int MaxThreads();
|
||||
virtual void ExecuteThread(idThread *);
|
||||
virtual void ResetFSMWaitThreadIfPossible(idThread *);
|
||||
virtual bool HandleGuiEvent(const sysEvent_t *);
|
||||
virtual void ActivateTargets(idEntity *);
|
||||
virtual bool GetRcCarCanTarget();
|
||||
virtual const idBaseHealth *GetHealthComponent();
|
||||
virtual idBaseHealth *GetHealthComponent_2();
|
||||
virtual const idSmartLootComponent *GetSmartLootComponent();
|
||||
virtual idSmartLootComponent *GetSmartLootComponent_2();
|
||||
virtual void Teleport(const idVec3 *, const idAngles *);
|
||||
virtual bool IsPusher();
|
||||
virtual const idList<idEntityPtr<idEntity>,5> *GetTriggerTouchList();
|
||||
virtual idList<idEntityPtr<idEntity>,5> *GetTriggerTouchList_2();
|
||||
virtual void TestFunctionality();
|
||||
virtual float GetUsableDistance();
|
||||
virtual float GetCrosshairIconDistance();
|
||||
virtual usableState_t GetUsableState(const idEntity *, const idFocusTrace *);
|
||||
virtual bool ModifyCrosshairInfo(const idEntity *, const idFocusTrace *, const usableState_t, idCrosshairInfo *);
|
||||
virtual bool IsCrosshairDisabled(const idEntity *, const idFocusTrace *, const usableState_t);
|
||||
virtual bool IsCrosshairSubdued(const idEntity *, const idFocusTrace *, const usableState_t);
|
||||
virtual bool IsEverUsable(const idEntity *);
|
||||
virtual bool IsCurrentlyUsable(const idEntity *);
|
||||
virtual bool Use(idEntity *, const usableState_t);
|
||||
virtual void Dropped(idEntity *, const idDeclInventory *);
|
||||
virtual const idInventoryCollection *GetInventory();
|
||||
virtual idInventoryCollection *GetInventory_2();
|
||||
virtual void InventoryAdded(idInventoryItem *, int);
|
||||
virtual void InventoryRemoved(idInventoryItem *);
|
||||
virtual const idAttachmentCollection *GetAttachments();
|
||||
virtual idAttachmentCollection *GetAttachments_2();
|
||||
virtual void EnableAIEventResponse(const idAIEvent::aiEventClass_t);
|
||||
virtual void DisableAIEventResponse(const idAIEvent::aiEventClass_t);
|
||||
virtual bool CanReceiveAIEvents(const int);
|
||||
virtual bool RespondsToAIEvent(const idAIEvent *);
|
||||
virtual void OnAIEvent(const idAIEvent *);
|
||||
virtual bool IsDead();
|
||||
virtual bool IsDying();
|
||||
virtual idFaction *GetFaction();
|
||||
virtual const idFaction *GetFaction_2();
|
||||
virtual idEntityAuditor *GetAuditor();
|
||||
virtual void GetVisibilityPoint(const visPoint_t, idVec3 *);
|
||||
virtual void GetAimPoint(const aimPoint_t, idVec3 *);
|
||||
virtual void GetEyePos(idVec3 *);
|
||||
virtual bool IsVisible();
|
||||
virtual idDynamicCoverMgr *GetDynamicCoverMgr();
|
||||
virtual const idDynamicCoverMgr *GetDynamicCoverMgr_2();
|
||||
virtual const idAAS2 *GetAAS();
|
||||
virtual void GetViewStateFOV(idVec3 *, unsigned __int8 *, unsigned __int8 *);
|
||||
virtual void GetViewStateFOV_2(idVec3 *, unsigned __int8 *, unsigned __int8 *);
|
||||
virtual int GetNumRepairBotTetherPoints();
|
||||
virtual bool GetRepairBotTetherPoint(const int, const int, idVec3 *);
|
||||
virtual idEntityInterface *CreateEntityInterface(idGame *);
|
||||
virtual void ShowEditingDialog();
|
||||
virtual void UpdateEditingDialog();
|
||||
virtual void UpdateModifiedProperties();
|
||||
virtual inputSettings_t *GetInputSettings(inputSettings_t *result, idPlayer *);
|
||||
virtual bool EvaluateControls(usercmd_t *, usercmd_t *);
|
||||
virtual void CheckForErrors(idList<idStr,5> *);
|
||||
virtual void DebugDrawEntity(const idColor *, int);
|
||||
virtual void ClientThink();
|
||||
virtual void Serialize(idSerializer *);
|
||||
virtual void PostSerializeRead(bool);
|
||||
virtual void OnActivate(idEntity *);
|
||||
virtual void OnMakeActivatable(const bool);
|
||||
virtual void OnNotifyProgressionOwner();
|
||||
|
||||
int numSegments;
|
||||
idSegment **segments;
|
||||
idMat3 current;
|
||||
idMat3 center;
|
||||
float limitAngleDot;
|
||||
float halfLimitAngleCosine;
|
||||
float halfLimitAngleSine;
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 21649; PDB kind: class.
|
||||
class idConeConstraint_Mat3
|
||||
{
|
||||
public:
|
||||
idMat3 current;
|
||||
idMat3 center;
|
||||
float limitAngleDot;
|
||||
float halfLimitAngleCosine;
|
||||
float halfLimitAngleSine;
|
||||
};
|
||||
// The fourth recovered logical type is a test entity derived from idEntity.
|
||||
// Its full base-class boundary belongs to the later game-entity recovery, so
|
||||
// retain the real owner name without inventing a substitute base class here.
|
||||
class idTest_ConeConstraint;
|
||||
|
||||
static_assert(sizeof(idConeConstraint_Quat) == 44,
|
||||
"Recovered idConeConstraint_Quat ABI changed");
|
||||
static_assert(sizeof(idConeConstraint_Vec3) == 36,
|
||||
"Recovered idConeConstraint_Vec3 ABI changed");
|
||||
static_assert(sizeof(idConeConstraint_Mat3) == 84,
|
||||
"Recovered idConeConstraint_Mat3 ABI changed");
|
||||
|
||||
@@ -0,0 +1,216 @@
|
||||
#include "gamelib/class.h"
|
||||
|
||||
#include "idlib/lib_print.h"
|
||||
#include "idlib/text/str.h"
|
||||
|
||||
namespace {
|
||||
|
||||
idTypeInfo *typeList = nullptr;
|
||||
idHierarchy<idTypeInfo> classHierarchy;
|
||||
|
||||
int NumberTypeHierarchy(idHierarchy<idTypeInfo> *node, int nextType) {
|
||||
for (idHierarchy<idTypeInfo> *current = node; current != nullptr;
|
||||
current = current->sibling) {
|
||||
idTypeInfo *type = current->owner;
|
||||
if (type == nullptr) {
|
||||
continue;
|
||||
}
|
||||
type->typeNum = nextType++;
|
||||
nextType = NumberTypeHierarchy(current->child, nextType);
|
||||
type->lastChild = nextType - 1;
|
||||
}
|
||||
return nextType;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool idClass::initialized = false;
|
||||
int idClass::typeNumBits = 0;
|
||||
int idClass::memused = 0;
|
||||
int idClass::numobjects = 0;
|
||||
idList<idTypeInfo *, 51> idClass::types(16);
|
||||
idList<idTypeInfo *, 51> idClass::typenums(16);
|
||||
|
||||
idClass::~idClass() = default;
|
||||
|
||||
idTypeInfo::idTypeInfo(const char *classname_, const char *superclass_,
|
||||
const int size_, const bool abstract_,
|
||||
const createInstance_t createInstance_, const spawn_t spawn_)
|
||||
: classname(classname_)
|
||||
, superclass(superclass_)
|
||||
, CreateInstance(createInstance_)
|
||||
, Spawn(spawn_)
|
||||
, initialized(false)
|
||||
, super(idClass::GetClass(superclass_))
|
||||
, next(nullptr)
|
||||
, isAbstract(abstract_)
|
||||
, size(size_)
|
||||
, typeNum(0)
|
||||
, lastChild(0)
|
||||
, node(nullptr) {
|
||||
for (idTypeInfo *type = typeList; type != nullptr; type = type->next) {
|
||||
if (type->super == nullptr && type->superclass != nullptr
|
||||
&& idStr::Cmp(type->superclass, classname) == 0
|
||||
&& idStr::Cmp(type->classname, "idClass") != 0) {
|
||||
type->super = this;
|
||||
}
|
||||
}
|
||||
|
||||
idTypeInfo **link = &typeList;
|
||||
while (*link != nullptr && idStr::Cmp(classname, (*link)->classname) >= 0) {
|
||||
link = &(*link)->next;
|
||||
}
|
||||
next = *link;
|
||||
*link = this;
|
||||
}
|
||||
|
||||
idTypeInfo::~idTypeInfo() {
|
||||
node.RemoveFromHierarchy();
|
||||
}
|
||||
|
||||
void idTypeInfo::Init() {
|
||||
if (initialized) {
|
||||
return;
|
||||
}
|
||||
if (super != nullptr && !super->initialized) {
|
||||
super->Init();
|
||||
}
|
||||
node.ParentTo(super != nullptr ? super->node : classHierarchy);
|
||||
node.SetOwner(this);
|
||||
for (idTypeInfo *ancestor = super; ancestor != nullptr;
|
||||
ancestor = ancestor->super) {
|
||||
++ancestor->lastChild;
|
||||
}
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
idClass::spawn_t idClass::CallSpawnFunc(idTypeInfo *type) {
|
||||
if (type == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
const spawn_t inherited = type->super != nullptr
|
||||
? CallSpawnFunc(type->super)
|
||||
: nullptr;
|
||||
if (type->Spawn != nullptr && inherited != type->Spawn) {
|
||||
type->Spawn(this);
|
||||
return type->Spawn;
|
||||
}
|
||||
return inherited;
|
||||
}
|
||||
|
||||
void idClass::CallSpawn() {
|
||||
idTypeInfo *type = GetType();
|
||||
if (type == nullptr) {
|
||||
return;
|
||||
}
|
||||
const spawn_t inherited = type->super != nullptr
|
||||
? CallSpawnFunc(type->super)
|
||||
: nullptr;
|
||||
if (type->Spawn != nullptr && inherited != type->Spawn) {
|
||||
type->Spawn(this);
|
||||
}
|
||||
}
|
||||
|
||||
const char *idClass::GetClassname() const {
|
||||
idTypeInfo *type = const_cast<idClass*>(this)->GetType();
|
||||
return type != nullptr ? type->classname : nullptr;
|
||||
}
|
||||
|
||||
bool idClass::IsType(const idTypeInfo& superclass) const {
|
||||
idTypeInfo *type = const_cast<idClass*>(this)->GetType();
|
||||
return type != nullptr && type->typeNum >= superclass.typeNum
|
||||
&& type->typeNum <= superclass.lastChild;
|
||||
}
|
||||
|
||||
idTypeInfo *idClass::GetClass(const char *name) {
|
||||
if (name == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!initialized) {
|
||||
for (idTypeInfo *type = typeList; type != nullptr; type = type->next) {
|
||||
if (idStr::Cmp(type->classname, name) == 0) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int lower = 0;
|
||||
int upper = types.Num() - 1;
|
||||
while (lower <= upper) {
|
||||
const int middle = (lower + upper) / 2;
|
||||
idTypeInfo *type = types[middle];
|
||||
const int comparison = idStr::Cmp(type->classname, name);
|
||||
if (comparison == 0) {
|
||||
return type;
|
||||
}
|
||||
if (comparison < 0) {
|
||||
lower = middle + 1;
|
||||
} else {
|
||||
upper = middle - 1;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
idClass *idClass::CreateInstance(const char *name) {
|
||||
idTypeInfo *type = GetClass(name);
|
||||
return type != nullptr && type->CreateInstance != nullptr
|
||||
? type->CreateInstance()
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
void idClass::DisplayInfo_f(const idCmdArgs&) {
|
||||
idLibPrint::Printf("Class memory status: %i bytes allocated in %i objects\n",
|
||||
memused, numobjects);
|
||||
}
|
||||
|
||||
void idClass::ListClasses_f(const idCmdArgs&) {
|
||||
idLibPrint::Printf("%-24s %-24s %-6s %-6s\n",
|
||||
"Classname", "Superclass", "Type", "Subclasses");
|
||||
idLibPrint::Printf(
|
||||
"----------------------------------------------------------------------\n");
|
||||
for (int index = 0; index < types.Num(); ++index) {
|
||||
const idTypeInfo *type = types[index];
|
||||
idLibPrint::Printf("%-24s %-24s %6d %6d\n", type->classname,
|
||||
type->superclass, type->typeNum, type->lastChild - type->typeNum);
|
||||
}
|
||||
idLibPrint::Printf("...%d classes", types.Num());
|
||||
}
|
||||
|
||||
void idClass::Init() {
|
||||
idLibPrint::Printf("Initializing class hierarchy\n");
|
||||
if (initialized) {
|
||||
idLibPrint::Printf("...already initialized\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for (idTypeInfo *type = typeList; type != nullptr; type = type->next) {
|
||||
type->Init();
|
||||
}
|
||||
const int count = NumberTypeHierarchy(classHierarchy.child, 0);
|
||||
typeNumBits = 0;
|
||||
while ((1u << typeNumBits) < static_cast<unsigned int>(count)
|
||||
&& typeNumBits < 31) {
|
||||
++typeNumBits;
|
||||
}
|
||||
|
||||
types.SetNum(count);
|
||||
typenums.SetNum(count);
|
||||
int sortedIndex = 0;
|
||||
for (idTypeInfo *type = typeList; type != nullptr; type = type->next) {
|
||||
if (sortedIndex < count) {
|
||||
types[sortedIndex++] = type;
|
||||
}
|
||||
if (type->typeNum >= 0 && type->typeNum < count) {
|
||||
typenums[type->typeNum] = type;
|
||||
}
|
||||
}
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
void idClass::Shutdown() {
|
||||
types.ClearFree();
|
||||
typenums.ClearFree();
|
||||
initialized = false;
|
||||
}
|
||||
+106
-39
@@ -1,46 +1,113 @@
|
||||
#pragma once
|
||||
|
||||
// Reconstructed C++ declarations from IDA Local Types and PDB/DIA metadata.
|
||||
// Original PDB header: w:\tech5\engine\gamelib\class.h
|
||||
// Recovered logical types: 4
|
||||
// Signatures retain Xbox 360 ABI evidence and may still require manual review.
|
||||
#include "idlib/containers/hierarchy.h"
|
||||
#include "idlib/containers/list.h"
|
||||
|
||||
class idCmdArgs;
|
||||
class idClass;
|
||||
|
||||
// IDA Local Type ordinal 9440; PDB kind: unknown.
|
||||
struct SUB_CLASS::<unnamed_type_bitfields>
|
||||
{
|
||||
unsigned __int32 SUB_CLASS_INF : 8;
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 9441; PDB kind: union.
|
||||
union SUB_CLASS
|
||||
{
|
||||
SUB_CLASS::<unnamed_type_bitfields> bitfields;
|
||||
SUB_CLASS::<unnamed_type_bitfields> bits;
|
||||
unsigned int u32All;
|
||||
int i32All;
|
||||
float f32All;
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 12079; PDB kind: struct.
|
||||
struct XUIClass
|
||||
{
|
||||
unsigned int cbSize;
|
||||
const wchar_t *szClassName;
|
||||
const wchar_t *szBaseClassName;
|
||||
const wchar_t *szClassDetails;
|
||||
XUIObjectMethods Methods;
|
||||
const XUIElementPropDef *pPropDefs;
|
||||
unsigned int dwPropDefCount;
|
||||
int bExtensionClass;
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 13472; PDB kind: class.
|
||||
class idClass
|
||||
{
|
||||
// Local type 13473 is unmapped in the generated header corpus but is the
|
||||
// metadata record used by every idClass registration function.
|
||||
class idTypeInfo {
|
||||
public:
|
||||
// Recovered virtual interface; IDA vtable ordinal 13475.
|
||||
virtual idTypeInfo *GetType();
|
||||
virtual ~idClass();
|
||||
using createInstance_t = idClass* (*)();
|
||||
using spawn_t = void (*)(idClass*);
|
||||
|
||||
idTypeInfo(const char *classname, const char *superclass, int size,
|
||||
bool abstract, createInstance_t createInstance, spawn_t spawn);
|
||||
~idTypeInfo();
|
||||
|
||||
void Init();
|
||||
|
||||
const char *classname;
|
||||
const char *superclass;
|
||||
createInstance_t CreateInstance;
|
||||
spawn_t Spawn;
|
||||
bool initialized;
|
||||
idTypeInfo *super;
|
||||
idTypeInfo *next;
|
||||
bool isAbstract;
|
||||
int size;
|
||||
int typeNum;
|
||||
int lastChild;
|
||||
idHierarchy<idTypeInfo> node;
|
||||
};
|
||||
|
||||
// PDB local type 13472 plus the methods recovered from engine/gamelib/class.cpp.
|
||||
class idClass {
|
||||
public:
|
||||
virtual idTypeInfo *GetType() = 0;
|
||||
virtual ~idClass();
|
||||
|
||||
const char *GetClassname() const;
|
||||
bool IsType(const idTypeInfo& superclass) const;
|
||||
void CallSpawn();
|
||||
|
||||
static idTypeInfo *GetClass(const char *name);
|
||||
static idClass *CreateInstance(const char *name);
|
||||
static void Init();
|
||||
static void Shutdown();
|
||||
static void DisplayInfo_f(const idCmdArgs& args);
|
||||
static void ListClasses_f(const idCmdArgs& args);
|
||||
|
||||
static int TypeNumBits() { return typeNumBits; }
|
||||
static int NumTypes() { return types.Num(); }
|
||||
static const idTypeInfo *TypeForNum(int index) {
|
||||
return index >= 0 && index < typenums.Num() ? typenums[index] : nullptr;
|
||||
}
|
||||
|
||||
static int memused;
|
||||
static int numobjects;
|
||||
|
||||
private:
|
||||
using spawn_t = idTypeInfo::spawn_t;
|
||||
spawn_t CallSpawnFunc(idTypeInfo *type);
|
||||
|
||||
static bool initialized;
|
||||
static int typeNumBits;
|
||||
static idList<idTypeInfo *, 51> types;
|
||||
static idList<idTypeInfo *, 51> typenums;
|
||||
};
|
||||
|
||||
// The generated class.h also carried these Xbox UI local types. Keep their
|
||||
// recovered layouts valid without coupling GameLib to the Xbox UI runtime.
|
||||
struct SUB_CLASS_bitfields {
|
||||
unsigned int SUB_CLASS_INF : 8;
|
||||
};
|
||||
|
||||
union SUB_CLASS {
|
||||
SUB_CLASS_bitfields bitfields;
|
||||
SUB_CLASS_bitfields bits;
|
||||
unsigned int u32All;
|
||||
int i32All;
|
||||
float f32All;
|
||||
};
|
||||
|
||||
struct _XUIOBJ;
|
||||
struct XUIMessage;
|
||||
struct XUIElementPropDef;
|
||||
struct XUIObjectMethods {
|
||||
long (__fastcall *ObjectProc)(_XUIOBJ *, XUIMessage *, void *);
|
||||
long (__fastcall *CreateInstance)(_XUIOBJ *, void **);
|
||||
long (__fastcall *DestroyInstance)(void *);
|
||||
};
|
||||
|
||||
struct XUIClass {
|
||||
unsigned int cbSize;
|
||||
const wchar_t *szClassName;
|
||||
const wchar_t *szBaseClassName;
|
||||
const wchar_t *szClassDetails;
|
||||
XUIObjectMethods Methods;
|
||||
const XUIElementPropDef *pPropDefs;
|
||||
unsigned int dwPropDefCount;
|
||||
int bExtensionClass;
|
||||
};
|
||||
|
||||
#if defined(_WIN32) && !defined(_WIN64)
|
||||
static_assert(sizeof(idTypeInfo) == 60, "Recovered idTypeInfo ABI changed");
|
||||
static_assert(sizeof(idClass) == 4, "Recovered idClass ABI changed");
|
||||
static_assert(sizeof(SUB_CLASS) == 4, "Recovered SUB_CLASS ABI changed");
|
||||
static_assert(sizeof(XUIObjectMethods) == 12,
|
||||
"Recovered XUIObjectMethods ABI changed");
|
||||
static_assert(sizeof(XUIClass) == 40, "Recovered XUIClass ABI changed");
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
#include "gamelib/effectphysics/effectphysicsboxfountain.h"
|
||||
|
||||
#include "gamelib/physics/tracemodelcache.h"
|
||||
#include "idlib/geometry/tracemodel.h"
|
||||
|
||||
// Renderer ownership is an engine boundary outside GameLib. The renderer port
|
||||
// provides this operation when idRenderModel is activated; keeping it external
|
||||
// avoids fabricating a partial render-model layout here.
|
||||
void GameLib_MarkRenderModelForDeletion(idRenderModelManyBoxes* model);
|
||||
|
||||
idEffectPhysicsBoxFountain::idEffectPhysicsBoxFountain()
|
||||
: broadPhase()
|
||||
, properties()
|
||||
, rigidBodies(0)
|
||||
, manyBoxes(nullptr)
|
||||
, lastFireTime(0)
|
||||
, currentFireRigidBody(0)
|
||||
, startOrigin()
|
||||
, spawnOrigin()
|
||||
, spawnObject()
|
||||
, random() {
|
||||
startOrigin.Zero();
|
||||
spawnOrigin.Zero();
|
||||
}
|
||||
|
||||
idEffectPhysicsBoxFountain::~idEffectPhysicsBoxFountain() {
|
||||
if (manyBoxes != nullptr) {
|
||||
GameLib_MarkRenderModelForDeletion(manyBoxes);
|
||||
}
|
||||
spawnObject.overlap.Clear();
|
||||
rigidBodies.Clear();
|
||||
}
|
||||
|
||||
void idEffectPhysicsBoxFountain::Init(
|
||||
idClip* const clip,
|
||||
idTraceModelCache* const traceModelCache,
|
||||
const int entityNumber) {
|
||||
broadPhase.Init(clip, traceModelCache);
|
||||
|
||||
idBounds bounds;
|
||||
bounds[0].Set(-16.0f, -16.0f, -16.0f);
|
||||
bounds[1].Set(16.0f, 16.0f, 16.0f);
|
||||
idTraceModel traceModel;
|
||||
traceModel.SetupBox(bounds);
|
||||
const int traceModelIndex = traceModelCache->AllocTraceModel(
|
||||
traceModel, nullptr);
|
||||
properties.Setup(&broadPhase, traceModelIndex, 10.0f);
|
||||
|
||||
startOrigin.Set(0.0f, 0.0f, 64.0f);
|
||||
spawnOrigin.Set(0.0f, 0.0f, 160.0f);
|
||||
broadPhase.AddObject(
|
||||
&spawnObject, spawnOrigin, idMat3(1.0f), &bounds);
|
||||
spawnObject.entityNum = entityNumber;
|
||||
}
|
||||
@@ -1,23 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
// Reconstructed C++ declarations from IDA Local Types and PDB/DIA metadata.
|
||||
// Original PDB header: w:\tech5\engine\gamelib\effectphysics\effectphysicsboxfountain.h
|
||||
// Recovered logical types: 1
|
||||
// Signatures retain Xbox 360 ABI evidence and may still require manual review.
|
||||
#include "gamelib/effectphysics/effectphysicsbroadphase.h"
|
||||
#include "gamelib/effectphysics/effectphysicsproperties.h"
|
||||
#include "idlib/containers/list.h"
|
||||
#include "idlib/math/random.h"
|
||||
|
||||
class idClip;
|
||||
class idEffectPhysicsRigidBody;
|
||||
class idRenderModelManyBoxes;
|
||||
class idTraceModelCache;
|
||||
|
||||
// IDA Local Type ordinal 16038; PDB kind: class.
|
||||
class __declspec(align(8)) idEffectPhysicsBoxFountain
|
||||
{
|
||||
class alignas(8) idEffectPhysicsBoxFountain {
|
||||
public:
|
||||
idEffectPhysicsBroadPhase broadPhase;
|
||||
idEffectPhysicsProperties properties;
|
||||
idList<idEffectPhysicsRigidBody *,59> rigidBodies;
|
||||
idRenderModelManyBoxes *manyBoxes;
|
||||
int lastFireTime;
|
||||
int currentFireRigidBody;
|
||||
idVec3 startOrigin;
|
||||
idVec3 spawnOrigin;
|
||||
idSPObject spawnObject;
|
||||
idRandom random;
|
||||
idEffectPhysicsBoxFountain();
|
||||
~idEffectPhysicsBoxFountain();
|
||||
|
||||
void Init(idClip* clip, idTraceModelCache* traceModelCache,
|
||||
int entityNumber);
|
||||
|
||||
idEffectPhysicsBroadPhase broadPhase;
|
||||
idEffectPhysicsProperties properties;
|
||||
idList<idEffectPhysicsRigidBody*, 59> rigidBodies;
|
||||
idRenderModelManyBoxes* manyBoxes;
|
||||
int lastFireTime;
|
||||
int currentFireRigidBody;
|
||||
idVec3 startOrigin;
|
||||
idVec3 spawnOrigin;
|
||||
idSPObject spawnObject;
|
||||
idRandom random;
|
||||
};
|
||||
|
||||
#if defined(_WIN32) && !defined(_WIN64)
|
||||
static_assert(sizeof(idEffectPhysicsBoxFountain) == 456,
|
||||
"Recovered idEffectPhysicsBoxFountain ABI changed");
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,695 @@
|
||||
#include "gamelib/effectphysics/effectphysicsbreakable.h"
|
||||
|
||||
#include "gamelib/physics/physics.h"
|
||||
#include "gamelib/physics/tracemodelcache.h"
|
||||
#include "idlib/geometry/tracemodel.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
int GameLib_DiscreteAnimationPieceCount(
|
||||
const idRenderModelDiscreteAnimation* model);
|
||||
const idDeclBreakable* GameLib_DiscreteAnimationDecl(
|
||||
const idRenderModelDiscreteAnimation* model);
|
||||
bool GameLib_DiscreteAnimationGetPiece(
|
||||
const idRenderModelDiscreteAnimation* model, int index,
|
||||
idVec3& position, idMat3& orientation, idTraceModel& traceModel,
|
||||
const idMaterial*& material);
|
||||
void GameLib_DiscreteAnimationSetPiece(idRenderModelDiscreteAnimation* model,
|
||||
int index, const idVec3& position, const idMat3& orientation);
|
||||
void GameLib_DiscreteAnimationCollapsePiece(
|
||||
idRenderModelDiscreteAnimation* model, int index);
|
||||
void GameLib_DiscreteAnimationReset(idRenderModelDiscreteAnimation* model);
|
||||
void GameLib_DiscreteAnimationUpdate(idRenderModelDiscreteAnimation* model);
|
||||
void GameLib_DiscreteAnimationDelete(idRenderModelDiscreteAnimation* model);
|
||||
bool GameLib_GetBreakablePhysicsSettings(const idDeclBreakable* decl,
|
||||
idEffectPhysicsPieceSettings& settings);
|
||||
bool GameLib_GetBreakableDeclView(const idDeclBreakable* decl,
|
||||
idEffectPhysicsBreakableDeclView& view);
|
||||
float GameLib_GetBreakablePieceDamageThreshold(
|
||||
const idDeclBreakable* decl, int pieceIndex);
|
||||
int GameLib_StartBreakableExplosion(idGameLibEffects* effects,
|
||||
const idBreakableExplosion& explosion, const idVec3& position,
|
||||
const idMat3& axis, int startTime, float effectScale);
|
||||
void GameLib_StopBreakableExplosion(idGameLibEffects* effects, int handle);
|
||||
void GameLib_EmitBreakableParticle(idGameLibEffects* effects,
|
||||
const idDeclParticle* particle, const idVec3& position,
|
||||
const idVec3& velocity, int startTime);
|
||||
void GameLib_EmitBreakableTrail(idGameLibEffects* effects,
|
||||
const idBreakableTrail& trail, int pieceIndex, const idVec3& position,
|
||||
const idVec3& velocity, int currentTime);
|
||||
void GameLib_EmitBreakableDecal(idGameLibEffects* effects,
|
||||
const idBreakableDecal& decal, int pieceIndex,
|
||||
const idEffectPhysicsCollision& collision, int currentTime);
|
||||
void GameLib_EmitBreakableSpark(idGameLibEffects* effects,
|
||||
const idMaterial* material, float size, const idVec3& position,
|
||||
const idVec3& velocity, int currentTime);
|
||||
void GameLib_PlayBreakableCollision(
|
||||
idEffectPhysicsBreakable::breakablePieceSound_t& soundInfo,
|
||||
const idEffectPhysicsCollision& collision, const idVec3& origin,
|
||||
float velocitySqr, int currentTime);
|
||||
void GameLib_DrawEffectTraceModel(const idTraceModelCache* cache,
|
||||
int traceModelIndex, int pieceIndex, const idVec3& origin,
|
||||
const idMat3& axis);
|
||||
|
||||
namespace {
|
||||
|
||||
float Clamp01(const float value) {
|
||||
return (std::max)(0.0f, (std::min)(1.0f, value));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
idEffectPhysicsBreakable::idEffectPhysicsBreakable()
|
||||
: properties(0)
|
||||
, rigidBodies(0)
|
||||
, collisions(0)
|
||||
, basePiece(nullptr)
|
||||
, explosions(0)
|
||||
, explosionFX(0)
|
||||
, trails(0)
|
||||
, damage(0)
|
||||
, particles(0)
|
||||
, decals(0)
|
||||
, sparks(0)
|
||||
, pieceActivationTimes(0)
|
||||
, broadPhase()
|
||||
, random(0)
|
||||
, gameLibEffects(nullptr)
|
||||
, model(nullptr)
|
||||
, decl(nullptr)
|
||||
, soundInfo{nullptr, nullptr, 0, nullptr, 0, 0.0f} {
|
||||
}
|
||||
|
||||
idEffectPhysicsBreakable::~idEffectPhysicsBreakable() {
|
||||
RemoveExplosions();
|
||||
if (broadPhase.traceModelCache != nullptr) {
|
||||
for (int index = 0; index < properties.Num(); ++index) {
|
||||
if (properties[index] != nullptr) {
|
||||
broadPhase.traceModelCache->FreeTraceModel(
|
||||
properties[index]->traceModelIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int index = 0; index < rigidBodies.Num(); ++index) {
|
||||
delete rigidBodies[index];
|
||||
}
|
||||
for (int index = 0; index < properties.Num(); ++index) {
|
||||
delete properties[index];
|
||||
}
|
||||
rigidBodies.ClearFree();
|
||||
properties.ClearFree();
|
||||
if (model != nullptr) {
|
||||
GameLib_DiscreteAnimationDelete(model);
|
||||
model = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
idEffectPhysicsProperties*
|
||||
idEffectPhysicsBreakable::GetPropertiesForTraceModel(
|
||||
const idTraceModel& traceModel, const idMaterial* const material) {
|
||||
if (broadPhase.traceModelCache == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
const int traceModelIndex = broadPhase.traceModelCache->AllocTraceModel(
|
||||
traceModel, material);
|
||||
for (int index = 0; index < properties.Num(); ++index) {
|
||||
if (properties[index]->traceModelIndex == traceModelIndex) {
|
||||
broadPhase.traceModelCache->FreeTraceModel(traceModelIndex);
|
||||
return properties[index];
|
||||
}
|
||||
}
|
||||
idEffectPhysicsProperties* const result =
|
||||
new idEffectPhysicsProperties();
|
||||
result->Setup(&broadPhase, traceModelIndex, 10.0f);
|
||||
idEffectPhysicsPieceSettings settings{};
|
||||
if (decl != nullptr &&
|
||||
GameLib_GetBreakablePhysicsSettings(decl, settings)) {
|
||||
result->linearFriction = settings.linearFriction;
|
||||
result->angularFriction = settings.angularFriction;
|
||||
result->contactFriction = settings.contactFriction;
|
||||
result->linearFrictionWater = settings.linearFrictionWater;
|
||||
result->angularFrictionWater = settings.angularFrictionWater;
|
||||
result->bouncyness = settings.bouncyness;
|
||||
result->gravityVector = settings.gravityVector;
|
||||
result->worldCollisionOnly = settings.worldCollisionOnly;
|
||||
result->simplePointCollision = settings.simplePointCollision;
|
||||
result->crazyBounceChance = settings.crazyBounceChance;
|
||||
result->stopSpeed = settings.stopSpeed;
|
||||
result->maxLinearVelocity = settings.maxLinearVelocity;
|
||||
result->maxAngularVelocity = settings.maxAngularVelocity;
|
||||
result->clipMask = settings.clipMask;
|
||||
}
|
||||
properties.Append(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
void idEffectPhysicsBreakable::Init(idClip* const clip,
|
||||
idTraceModelCache* const traceModelCache,
|
||||
idGameLibEffects* const effects,
|
||||
idRenderModelDiscreteAnimation* const renderModel,
|
||||
const int entityNumber, const breakablePieceSound_t* const sound) {
|
||||
broadPhase.Init(clip, traceModelCache);
|
||||
gameLibEffects = effects;
|
||||
model = renderModel;
|
||||
decl = renderModel != nullptr
|
||||
? GameLib_DiscreteAnimationDecl(renderModel) : nullptr;
|
||||
if (sound != nullptr) {
|
||||
soundInfo = *sound;
|
||||
}
|
||||
|
||||
for (int index = 0; index < rigidBodies.Num(); ++index) {
|
||||
delete rigidBodies[index];
|
||||
}
|
||||
rigidBodies.Clear();
|
||||
collisions.Clear();
|
||||
const int pieceCount = renderModel != nullptr
|
||||
? GameLib_DiscreteAnimationPieceCount(renderModel) : 0;
|
||||
rigidBodies.SetNum((std::max)(0, pieceCount));
|
||||
collisions.SetNum(rigidBodies.Num());
|
||||
damage.SetNum(rigidBodies.Num());
|
||||
pieceActivationTimes.SetNum(rigidBodies.Num());
|
||||
|
||||
for (int index = 0; index < rigidBodies.Num(); ++index) {
|
||||
rigidBodies[index] = nullptr;
|
||||
collisions[index].Clear();
|
||||
damage[index] = 0.0f;
|
||||
pieceActivationTimes[index] = 0;
|
||||
idVec3 position;
|
||||
idMat3 orientation(1.0f);
|
||||
idTraceModel traceModel;
|
||||
const idMaterial* material = nullptr;
|
||||
if (!GameLib_DiscreteAnimationGetPiece(renderModel, index,
|
||||
position, orientation, traceModel, material)) {
|
||||
continue;
|
||||
}
|
||||
traceModel.Shrink(0.1875f);
|
||||
idEffectPhysicsProperties* const pieceProperties =
|
||||
GetPropertiesForTraceModel(traceModel, material);
|
||||
if (pieceProperties == nullptr) {
|
||||
continue;
|
||||
}
|
||||
rigidBodies[index] = new idEffectPhysicsRigidBody();
|
||||
rigidBodies[index]->Setup(pieceProperties);
|
||||
rigidBodies[index]->SetPosition(position, orientation);
|
||||
rigidBodies[index]->SPObject.entityNum = entityNumber;
|
||||
}
|
||||
InitExplosionsAndTrails(sound, effects, random.RandomFloat());
|
||||
}
|
||||
|
||||
void idEffectPhysicsBreakable::InitExplosionsAndTrails(
|
||||
const breakablePieceSound_t* const sound, idGameLibEffects* const effects,
|
||||
const float diversity) {
|
||||
(void)diversity;
|
||||
gameLibEffects = effects;
|
||||
if (sound != nullptr) {
|
||||
soundInfo = *sound;
|
||||
}
|
||||
explosions.Clear();
|
||||
explosionFX.Clear();
|
||||
trails.Clear();
|
||||
decals.Clear();
|
||||
sparks.Clear();
|
||||
|
||||
idEffectPhysicsBreakableDeclView view;
|
||||
if (decl == nullptr || !GameLib_GetBreakableDeclView(decl, view)) {
|
||||
return;
|
||||
}
|
||||
explosions.SetNum(view.explosions.Num());
|
||||
explosionFX.SetNum(view.explosions.Num());
|
||||
for (int index = 0; index < explosions.Num(); ++index) {
|
||||
explosions[index].decl = view.explosions[index];
|
||||
explosions[index].startTime = 0;
|
||||
explosions[index].fxIndex = -1;
|
||||
explosions[index].worldPosition.Zero();
|
||||
explosions[index].effectsEnabled = false;
|
||||
explosions[index].forceApplied = false;
|
||||
explosions[index].localSpace = true;
|
||||
explosionFX[index] = -1;
|
||||
}
|
||||
trails.SetNum(view.trails.Num());
|
||||
for (int index = 0; index < trails.Num(); ++index) {
|
||||
trails[index].decl = view.trails[index];
|
||||
const int count = trails[index].decl.pieces.Num();
|
||||
trails[index].pieceStartTime.SetNum(count);
|
||||
trails[index].pieceLastParticleDropPos.SetNum(count);
|
||||
for (int piece = 0; piece < count; ++piece) {
|
||||
trails[index].pieceStartTime[piece] = 0;
|
||||
trails[index].pieceLastParticleDropPos[piece].Zero();
|
||||
}
|
||||
}
|
||||
decals.SetNum(view.decals.Num());
|
||||
for (int index = 0; index < decals.Num(); ++index) {
|
||||
decals[index].decl = view.decals[index];
|
||||
const int count = decals[index].decl.pieces.Num();
|
||||
decals[index].pieceStartTime.SetNum(count);
|
||||
decals[index].lastCollisionPos.SetNum(count);
|
||||
decals[index].lastCollisionNormal.SetNum(count);
|
||||
decals[index].pieceLastDecalDropPos.SetNum(count);
|
||||
for (int piece = 0; piece < count; ++piece) {
|
||||
decals[index].pieceStartTime[piece] = 0;
|
||||
decals[index].lastCollisionPos[piece].Zero();
|
||||
decals[index].lastCollisionNormal[piece].Zero();
|
||||
decals[index].pieceLastDecalDropPos[piece].Zero();
|
||||
}
|
||||
}
|
||||
if (!view.sparks.materials.IsEmpty()) {
|
||||
sparks.SetNum(rigidBodies.Num());
|
||||
for (int index = 0; index < sparks.Num(); ++index) {
|
||||
sparks[index].material = view.sparks.materials[
|
||||
random.RandomInt(view.sparks.materials.Num())];
|
||||
sparks[index].size = view.sparks.sizeMin + random.RandomFloat() *
|
||||
(view.sparks.sizeMax - view.sparks.sizeMin);
|
||||
sparks[index].velocityScale = view.sparks.velocityScaleMin +
|
||||
random.RandomFloat() * (view.sparks.velocityScaleMax -
|
||||
view.sparks.velocityScaleMin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void idEffectPhysicsBreakable::Reset(const bool skipModelReset) {
|
||||
if (model == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (!skipModelReset) {
|
||||
GameLib_DiscreteAnimationReset(model);
|
||||
}
|
||||
random.SetSeed(0);
|
||||
for (int index = 0; index < rigidBodies.Num(); ++index) {
|
||||
idEffectPhysicsRigidBody* const body = rigidBodies[index];
|
||||
if (body == nullptr) {
|
||||
continue;
|
||||
}
|
||||
idVec3 position;
|
||||
idMat3 orientation(1.0f);
|
||||
idTraceModel ignoredTraceModel;
|
||||
const idMaterial* ignoredMaterial = nullptr;
|
||||
if (GameLib_DiscreteAnimationGetPiece(model, index, position,
|
||||
orientation, ignoredTraceModel, ignoredMaterial)) {
|
||||
body->SetPosition(position, orientation);
|
||||
}
|
||||
body->active = false;
|
||||
body->settled = false;
|
||||
body->SPObject.motion.ignore = false;
|
||||
body->currentState.linearMomentum.Zero();
|
||||
body->currentState.angularMomentum.Zero();
|
||||
collisions[index].Clear();
|
||||
damage[index] = 0.0f;
|
||||
pieceActivationTimes[index] = 0;
|
||||
}
|
||||
for (int index = 0; index < properties.Num(); ++index) {
|
||||
idEffectPhysicsPieceSettings settings{};
|
||||
if (decl != nullptr &&
|
||||
GameLib_GetBreakablePhysicsSettings(decl, settings)) {
|
||||
properties[index]->linearFriction = settings.linearFriction;
|
||||
properties[index]->angularFriction = settings.angularFriction;
|
||||
properties[index]->contactFriction = settings.contactFriction;
|
||||
properties[index]->linearFrictionWater =
|
||||
settings.linearFrictionWater;
|
||||
properties[index]->angularFrictionWater =
|
||||
settings.angularFrictionWater;
|
||||
properties[index]->bouncyness = settings.bouncyness;
|
||||
properties[index]->gravityVector = settings.gravityVector;
|
||||
properties[index]->worldCollisionOnly =
|
||||
settings.worldCollisionOnly;
|
||||
properties[index]->clipMask = settings.clipMask;
|
||||
}
|
||||
}
|
||||
RemoveExplosions();
|
||||
InitExplosionsAndTrails(&soundInfo, gameLibEffects,
|
||||
random.RandomFloat());
|
||||
}
|
||||
|
||||
bool idEffectPhysicsBreakable::ActivateNoisy(const int pieceIndex,
|
||||
const idVec3& origin, const idMat3& axis) {
|
||||
if (pieceIndex < 0 || pieceIndex >= rigidBodies.Num() ||
|
||||
rigidBodies[pieceIndex] == nullptr) {
|
||||
return false;
|
||||
}
|
||||
idEffectPhysicsRigidBody* const body = rigidBodies[pieceIndex];
|
||||
if (body->active) {
|
||||
return false;
|
||||
}
|
||||
body->SetPosition(origin + axis * body->currentState.position,
|
||||
body->currentState.orientation * axis);
|
||||
body->active = true;
|
||||
body->settled = false;
|
||||
body->SPObject.motion.ignore = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool idEffectPhysicsBreakable::ActivateAllNow(const idVec3& origin,
|
||||
const idMat3& axis) {
|
||||
bool activated = false;
|
||||
for (int index = 0; index < rigidBodies.Num(); ++index) {
|
||||
activated = ActivateNoisy(index, origin, axis) || activated;
|
||||
}
|
||||
return activated;
|
||||
}
|
||||
|
||||
bool idEffectPhysicsBreakable::ActivateFloatingPieces(const idVec3& origin,
|
||||
const idMat3& axis, const float impulse) {
|
||||
bool activated = false;
|
||||
for (int index = 0; index < rigidBodies.Num(); ++index) {
|
||||
idEffectPhysicsRigidBody* const body = rigidBodies[index];
|
||||
if (body == nullptr || body->active ||
|
||||
body->currentState.position.z <= 0.0f) {
|
||||
continue;
|
||||
}
|
||||
if (ActivateNoisy(index, origin, axis)) {
|
||||
const idVec3 direction(0.0f, 0.0f, 1.0f);
|
||||
body->ApplyImpulse(body->currentState.position,
|
||||
direction * impulse);
|
||||
activated = true;
|
||||
}
|
||||
}
|
||||
return activated;
|
||||
}
|
||||
|
||||
void idEffectPhysicsBreakable::ApplyImpulseToBody(const int bodyIndex,
|
||||
const idPhysics* const impactPhysics, const idVec3& point,
|
||||
const idVec3& impulse) {
|
||||
if (bodyIndex < 0 || bodyIndex >= rigidBodies.Num() ||
|
||||
rigidBodies[bodyIndex] == nullptr) {
|
||||
return;
|
||||
}
|
||||
idVec3 adjustedImpulse = impulse;
|
||||
if (impactPhysics != nullptr) {
|
||||
impactInfo_t impact{};
|
||||
const_cast<idPhysics*>(impactPhysics)->GetImpactInfo(
|
||||
0, &point, &impact);
|
||||
adjustedImpulse = adjustedImpulse + impact.velocity *
|
||||
(1.0f / (impact.invMass > 0.0f ? impact.invMass : 1.0f));
|
||||
}
|
||||
rigidBodies[bodyIndex]->ApplyImpulse(point, adjustedImpulse);
|
||||
rigidBodies[bodyIndex]->active = true;
|
||||
}
|
||||
|
||||
void idEffectPhysicsBreakable::ApplyForce(const idVec3& origin,
|
||||
const idMat3& axis, const idVec3& force) {
|
||||
(void)origin;
|
||||
(void)axis;
|
||||
for (int index = 0; index < rigidBodies.Num(); ++index) {
|
||||
if (rigidBodies[index] != nullptr && rigidBodies[index]->active) {
|
||||
rigidBodies[index]->AddForce(force);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void idEffectPhysicsBreakable::AddRadiusImpact(const int time,
|
||||
const idPhysics* const impactPhysics, const idVec3& position,
|
||||
const float radius, const float impulse) {
|
||||
const float radiusSqr = radius * radius;
|
||||
for (int index = 0; index < rigidBodies.Num(); ++index) {
|
||||
idEffectPhysicsRigidBody* const body = rigidBodies[index];
|
||||
if (body == nullptr) {
|
||||
continue;
|
||||
}
|
||||
idVec3 delta = body->currentState.position - position;
|
||||
const float distanceSqr = delta.LengthSqr();
|
||||
if (distanceSqr > radiusSqr) {
|
||||
continue;
|
||||
}
|
||||
if (!body->active) {
|
||||
body->active = true;
|
||||
pieceActivationTimes[index] = time;
|
||||
}
|
||||
const float distance = delta.NormalizeFast();
|
||||
const float falloff = radius > 0.0f
|
||||
? Clamp01(1.0f - distance / radius) : 1.0f;
|
||||
ApplyImpulseToBody(index, impactPhysics, body->currentState.position,
|
||||
delta * (impulse * falloff));
|
||||
}
|
||||
}
|
||||
|
||||
void idEffectPhysicsBreakable::CollideWith(const idVec3& origin,
|
||||
const idPhysics* const impactPhysics, const idVec3& point,
|
||||
const float impulseScale) {
|
||||
int nearest = -1;
|
||||
float nearestDistance = 1.0e30f;
|
||||
for (int index = 0; index < rigidBodies.Num(); ++index) {
|
||||
if (rigidBodies[index] == nullptr) {
|
||||
continue;
|
||||
}
|
||||
const float distance =
|
||||
(rigidBodies[index]->currentState.position + origin - point)
|
||||
.LengthSqr();
|
||||
if (distance < nearestDistance) {
|
||||
nearestDistance = distance;
|
||||
nearest = index;
|
||||
}
|
||||
}
|
||||
if (nearest >= 0) {
|
||||
idVec3 direction =
|
||||
rigidBodies[nearest]->currentState.position + origin - point;
|
||||
if (direction.NormalizeFast() == 0.0f) {
|
||||
direction.Set(0.0f, 0.0f, 1.0f);
|
||||
}
|
||||
ApplyImpulseToBody(nearest, impactPhysics, point,
|
||||
direction * impulseScale);
|
||||
}
|
||||
}
|
||||
|
||||
bool idEffectPhysicsBreakable::DamagePiece(const int time,
|
||||
const int pieceIndex, const float amount, const idVec3& position) {
|
||||
(void)position;
|
||||
if (pieceIndex < 0 || pieceIndex >= damage.Num()) {
|
||||
return false;
|
||||
}
|
||||
damage[pieceIndex] += amount;
|
||||
const float threshold =
|
||||
GameLib_GetBreakablePieceDamageThreshold(decl, pieceIndex);
|
||||
if (damage[pieceIndex] < (threshold > 0.0f ? threshold : 1.0f)) {
|
||||
return false;
|
||||
}
|
||||
if (rigidBodies[pieceIndex] != nullptr) {
|
||||
rigidBodies[pieceIndex]->active = true;
|
||||
rigidBodies[pieceIndex]->settled = false;
|
||||
pieceActivationTimes[pieceIndex] = time;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void idEffectPhysicsBreakable::AddRayImpact(const int time,
|
||||
const idVec3& origin, const idMat3& axis, const idVec3& start,
|
||||
const idVec3& end, const bool activeOnly) {
|
||||
const idVec3 ray = end - start;
|
||||
const float rayLengthSqr = ray.LengthSqr();
|
||||
if (rayLengthSqr <= 1.0e-8f) {
|
||||
return;
|
||||
}
|
||||
for (int index = 0; index < rigidBodies.Num(); ++index) {
|
||||
idEffectPhysicsRigidBody* const body = rigidBodies[index];
|
||||
if (body == nullptr || (activeOnly && !body->active)) {
|
||||
continue;
|
||||
}
|
||||
const idVec3 piecePosition = body->active
|
||||
? body->currentState.position
|
||||
: origin + axis * body->currentState.position;
|
||||
const float fraction = Clamp01(
|
||||
(piecePosition - start).Dot(ray) / rayLengthSqr);
|
||||
const idVec3 closest = start + ray * fraction;
|
||||
if ((piecePosition - closest).LengthSqr() > 64.0f) {
|
||||
continue;
|
||||
}
|
||||
if (!body->active) {
|
||||
ActivateNoisy(index, origin, axis);
|
||||
}
|
||||
DamagePiece(time, index, 1.0f, closest);
|
||||
idVec3 impulseDirection = ray;
|
||||
impulseDirection.NormalizeFast();
|
||||
body->ApplyImpulse(closest,
|
||||
(impulseDirection + idVec3(0.0f, 0.0f, 0.1f)) * 10.0f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void idEffectPhysicsBreakable::EmitSmokeForEachPiece(
|
||||
const idDeclParticle* const particle, const int time) {
|
||||
for (int index = 0; index < rigidBodies.Num(); ++index) {
|
||||
if (rigidBodies[index] == nullptr) {
|
||||
continue;
|
||||
}
|
||||
particles.Append(idBreakableParticle{
|
||||
particle, rigidBodies[index]->currentState.position, time});
|
||||
GameLib_EmitBreakableParticle(gameLibEffects, particle,
|
||||
rigidBodies[index]->currentState.position,
|
||||
idVec3(0.0f, 0.0f, 0.0f), time);
|
||||
}
|
||||
}
|
||||
|
||||
void idEffectPhysicsBreakable::AddExplosion(
|
||||
const idDeclParticle* const particle, const int time,
|
||||
const idVec3& position, const float radius, const float impulse,
|
||||
const float falloff, const float angular, const int delay,
|
||||
const int duration, const bool localSpace, const int activateAllDelay,
|
||||
const float distanceDelayScale, const idVec3& endLinear,
|
||||
const float effectScale) {
|
||||
idExplosion explosion{};
|
||||
explosion.decl.particle = particle;
|
||||
explosion.decl.position = position;
|
||||
explosion.decl.radius = radius;
|
||||
explosion.decl.impulse = impulse;
|
||||
explosion.decl.falloff = falloff;
|
||||
explosion.decl.angular = angular;
|
||||
explosion.decl.delay = delay;
|
||||
explosion.decl.duration = duration;
|
||||
explosion.decl.activateAllDelay = activateAllDelay;
|
||||
explosion.decl.distTimeScaleDelay = distanceDelayScale;
|
||||
explosion.decl.endLinear = endLinear;
|
||||
explosion.startTime = time + delay;
|
||||
explosion.fxIndex = -1;
|
||||
explosion.worldPosition = position;
|
||||
explosion.effectsEnabled = effectScale > 0.0f;
|
||||
explosion.forceApplied = false;
|
||||
explosion.localSpace = localSpace;
|
||||
explosions.Append(explosion);
|
||||
explosionFX.Append(-1);
|
||||
}
|
||||
|
||||
void idEffectPhysicsBreakable::StartExplosions(const idVec3& origin,
|
||||
const idMat3& axis, const int time) {
|
||||
for (int index = 0; index < explosions.Num(); ++index) {
|
||||
idExplosion& explosion = explosions[index];
|
||||
explosion.startTime = time + explosion.decl.delay;
|
||||
explosion.worldPosition = explosion.localSpace
|
||||
? origin + axis * explosion.decl.position
|
||||
: explosion.decl.position;
|
||||
explosion.forceApplied = false;
|
||||
explosion.effectsEnabled = true;
|
||||
if (index < explosionFX.Num()) {
|
||||
explosionFX[index] = GameLib_StartBreakableExplosion(
|
||||
gameLibEffects, explosion.decl, explosion.worldPosition,
|
||||
axis, explosion.startTime, 1.0f);
|
||||
explosion.fxIndex = explosionFX[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void idEffectPhysicsBreakable::StopExplosionFX(const int index) {
|
||||
if (index < 0 || index >= explosionFX.Num()) {
|
||||
return;
|
||||
}
|
||||
if (explosionFX[index] >= 0) {
|
||||
GameLib_StopBreakableExplosion(gameLibEffects, explosionFX[index]);
|
||||
explosionFX[index] = -1;
|
||||
}
|
||||
if (index < explosions.Num()) {
|
||||
explosions[index].fxIndex = -1;
|
||||
explosions[index].effectsEnabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
void idEffectPhysicsBreakable::RemoveExplosions() {
|
||||
for (int index = 0; index < explosionFX.Num(); ++index) {
|
||||
StopExplosionFX(index);
|
||||
}
|
||||
explosions.Clear();
|
||||
explosionFX.Clear();
|
||||
}
|
||||
|
||||
void idEffectPhysicsBreakable::UpdateSimulation(const idVec3& origin,
|
||||
const idMat3& axis, const int currentTime, const int gameMsPerFrame,
|
||||
const int frameNumber, const float lodScale, const float effectScale) {
|
||||
(void)frameNumber;
|
||||
(void)lodScale;
|
||||
const float deltaTime = (std::max)(0, gameMsPerFrame) * 0.001f;
|
||||
|
||||
for (int index = 0; index < explosions.Num(); ++index) {
|
||||
idExplosion& explosion = explosions[index];
|
||||
if (currentTime < explosion.startTime) {
|
||||
continue;
|
||||
}
|
||||
if (!explosion.forceApplied) {
|
||||
AddRadiusImpact(currentTime, nullptr, explosion.worldPosition,
|
||||
explosion.decl.radius,
|
||||
explosion.decl.impulse * effectScale);
|
||||
explosion.forceApplied = true;
|
||||
}
|
||||
if (explosion.decl.duration > 0 &&
|
||||
currentTime >= explosion.startTime + explosion.decl.duration) {
|
||||
StopExplosionFX(index);
|
||||
}
|
||||
}
|
||||
|
||||
for (int index = 0; index < rigidBodies.Num(); ++index) {
|
||||
idEffectPhysicsRigidBody* const body = rigidBodies[index];
|
||||
if (body == nullptr || !body->active) {
|
||||
continue;
|
||||
}
|
||||
body->Update(deltaTime, 0.0f, collisions[index]);
|
||||
const idVec3 velocity = body->currentState.linearMomentum *
|
||||
body->properties->inverseMass;
|
||||
if (collisions[index].collisionNormalVelocity.LengthSqr() > 0.0f) {
|
||||
GameLib_PlayBreakableCollision(soundInfo, collisions[index],
|
||||
body->currentState.position, velocity.LengthSqr(), currentTime);
|
||||
}
|
||||
for (int trailIndex = 0; trailIndex < trails.Num(); ++trailIndex) {
|
||||
const idBreakableTrail& trail = trails[trailIndex].decl;
|
||||
if (velocity.LengthSqr() >=
|
||||
trail.minTrailVelocity * trail.minTrailVelocity) {
|
||||
GameLib_EmitBreakableTrail(gameLibEffects, trail, index,
|
||||
body->currentState.position, velocity, currentTime);
|
||||
}
|
||||
}
|
||||
for (int decalIndex = 0; decalIndex < decals.Num(); ++decalIndex) {
|
||||
if (collisions[index].collisionNormalVelocity.LengthSqr() >=
|
||||
decals[decalIndex].decl.minBounceVelocity *
|
||||
decals[decalIndex].decl.minBounceVelocity) {
|
||||
GameLib_EmitBreakableDecal(gameLibEffects,
|
||||
decals[decalIndex].decl, index, collisions[index],
|
||||
currentTime);
|
||||
}
|
||||
}
|
||||
if (index < sparks.Num() &&
|
||||
collisions[index].collisionNormalVelocity.LengthSqr() > 0.0f) {
|
||||
GameLib_EmitBreakableSpark(gameLibEffects,
|
||||
sparks[index].material, sparks[index].size,
|
||||
collisions[index].collisionPoint,
|
||||
velocity * sparks[index].velocityScale, currentTime);
|
||||
}
|
||||
}
|
||||
broadPhase.SubmitQueries(false);
|
||||
UpdateModel(origin, axis, currentTime);
|
||||
}
|
||||
|
||||
void idEffectPhysicsBreakable::UpdateModel(const idVec3& origin,
|
||||
const idMat3& axis, const int currentTime) {
|
||||
(void)origin;
|
||||
(void)axis;
|
||||
(void)currentTime;
|
||||
if (model == nullptr) {
|
||||
return;
|
||||
}
|
||||
for (int index = 0; index < rigidBodies.Num(); ++index) {
|
||||
if (rigidBodies[index] != nullptr && rigidBodies[index]->active) {
|
||||
GameLib_DiscreteAnimationSetPiece(model, index,
|
||||
rigidBodies[index]->currentState.position,
|
||||
rigidBodies[index]->currentState.orientation);
|
||||
} else {
|
||||
GameLib_DiscreteAnimationCollapsePiece(model, index);
|
||||
}
|
||||
}
|
||||
GameLib_DiscreteAnimationUpdate(model);
|
||||
}
|
||||
|
||||
void idEffectPhysicsBreakable::DrawCollisionModels(const int single,
|
||||
const idVec3& offset, const idMat3& axis) {
|
||||
for (int index = 0; index < rigidBodies.Num(); ++index) {
|
||||
if (single >= 0 && single != index) {
|
||||
continue;
|
||||
}
|
||||
idEffectPhysicsRigidBody* const body = rigidBodies[index];
|
||||
if (body == nullptr || body->properties == nullptr) {
|
||||
continue;
|
||||
}
|
||||
const idVec3 drawOrigin = body->active
|
||||
? body->currentState.position
|
||||
: offset + axis * body->currentState.position;
|
||||
const idMat3 drawAxis = body->active
|
||||
? body->currentState.orientation
|
||||
: body->currentState.orientation * axis;
|
||||
GameLib_DrawEffectTraceModel(broadPhase.traceModelCache,
|
||||
body->properties->traceModelIndex, index, drawOrigin, drawAxis);
|
||||
}
|
||||
}
|
||||
@@ -1,93 +1,247 @@
|
||||
#pragma once
|
||||
|
||||
// Reconstructed C++ declarations from IDA Local Types and PDB/DIA metadata.
|
||||
// Original PDB header: w:\tech5\engine\gamelib\effectphysics\effectphysicsbreakable.h
|
||||
// Recovered logical types: 7
|
||||
// Signatures retain Xbox 360 ABI evidence and may still require manual review.
|
||||
#include "gamelib/effectphysics/effectphysicspieceemitter.h"
|
||||
|
||||
class idClip;
|
||||
class idCollisionModel;
|
||||
class idDeclFX;
|
||||
class idDeclParticle;
|
||||
class idGameLibEffects;
|
||||
class idMaterial;
|
||||
class idPhysics;
|
||||
class idRenderModelDiscreteAnimation;
|
||||
class idSoundEmitter;
|
||||
class idSoundWorld;
|
||||
class idTraceModel;
|
||||
class idTraceModelCache;
|
||||
|
||||
// IDA Local Type ordinal 15408; PDB kind: class.
|
||||
class __declspec(align(2)) idEffectPhysicsBreakable::idExplosion
|
||||
{
|
||||
// Data records copied verbatim from the recovered declbreakable layout. The
|
||||
// declaration system owns parsing; GameLib owns the runtime copies below.
|
||||
struct idBreakableExplosion {
|
||||
const idDeclFX* fx;
|
||||
const idDeclParticle* particle;
|
||||
idVec3 position;
|
||||
float radius;
|
||||
float impulse;
|
||||
float falloff;
|
||||
float angular;
|
||||
int delay;
|
||||
int duration;
|
||||
int activateAllDelay;
|
||||
float distTimeScaleDelay;
|
||||
bool useLinear;
|
||||
idVec3 endLinear;
|
||||
idList<int, 59> pieces;
|
||||
|
||||
idBreakableExplosion() : fx(nullptr), particle(nullptr), position(),
|
||||
radius(0.0f), impulse(0.0f), falloff(0.0f), angular(0.0f), delay(0),
|
||||
duration(0), activateAllDelay(0), distTimeScaleDelay(0.0f),
|
||||
useLinear(false), endLinear(), pieces(0) {}
|
||||
};
|
||||
|
||||
struct idBreakableTrail {
|
||||
const idDeclParticle* particleTrail;
|
||||
const idDeclParticle* particleBounce;
|
||||
const idDeclParticle* particleDie;
|
||||
const idMaterial* glowQuadMaterial;
|
||||
float glowQuadSize;
|
||||
float minTrailVelocity;
|
||||
float minBounceVelocity;
|
||||
float trailSpacing;
|
||||
float trailVelocityScale;
|
||||
int delay;
|
||||
int duration;
|
||||
int fadeInDuration;
|
||||
int fadeOutDuration;
|
||||
idList<int, 59> pieces;
|
||||
|
||||
idBreakableTrail() : particleTrail(nullptr), particleBounce(nullptr),
|
||||
particleDie(nullptr), glowQuadMaterial(nullptr), glowQuadSize(0.0f),
|
||||
minTrailVelocity(0.0f), minBounceVelocity(0.0f), trailSpacing(0.0f),
|
||||
trailVelocityScale(0.0f), delay(0), duration(0), fadeInDuration(0),
|
||||
fadeOutDuration(0), pieces(0) {}
|
||||
};
|
||||
|
||||
struct idBreakableDecal {
|
||||
idList<const idMaterial*, 59> materials;
|
||||
idVec2 minSize;
|
||||
idVec2 maxSize;
|
||||
int lifetime;
|
||||
int fadeOutStartTime;
|
||||
int fadeInEndTime;
|
||||
float minBounceVelocity;
|
||||
float decalSpacing;
|
||||
float depth;
|
||||
idList<int, 59> pieces;
|
||||
bool splatterOnBounce;
|
||||
bool splatterOnRest;
|
||||
|
||||
idBreakableDecal() : materials(0), minSize(), maxSize(), lifetime(0),
|
||||
fadeOutStartTime(0), fadeInEndTime(0), minBounceVelocity(0.0f),
|
||||
decalSpacing(0.0f), depth(0.0f), pieces(0),
|
||||
splatterOnBounce(false), splatterOnRest(false) {}
|
||||
};
|
||||
|
||||
struct idBreakableSpark {
|
||||
idList<const idMaterial*, 59> materials;
|
||||
float sizeMin;
|
||||
float sizeMax;
|
||||
float velocityScaleMin;
|
||||
float velocityScaleMax;
|
||||
|
||||
idBreakableSpark() : materials(0), sizeMin(0.0f), sizeMax(0.0f),
|
||||
velocityScaleMin(0.0f), velocityScaleMax(0.0f) {}
|
||||
};
|
||||
|
||||
struct idEffectPhysicsBreakableDeclView {
|
||||
int numPieces;
|
||||
idEffectPhysicsPieceSettings settings;
|
||||
float maxSimulationTime;
|
||||
float dampeningDecay;
|
||||
idList<idBreakableExplosion, 59> explosions;
|
||||
idList<idBreakableTrail, 59> trails;
|
||||
idList<idBreakableDecal, 59> decals;
|
||||
idBreakableSpark sparks;
|
||||
|
||||
idEffectPhysicsBreakableDeclView() : numPieces(0), settings{},
|
||||
maxSimulationTime(0.0f), dampeningDecay(0.0f), explosions(0),
|
||||
trails(0), decals(0), sparks() {}
|
||||
};
|
||||
|
||||
class idEffectPhysicsBreakable {
|
||||
public:
|
||||
idBreakableExplosion decl;
|
||||
int startTime;
|
||||
int fxIndex;
|
||||
idVec3 worldPosition;
|
||||
bool effectsEnabled;
|
||||
bool forceApplied;
|
||||
bool localSpace;
|
||||
struct idExplosion {
|
||||
idBreakableExplosion decl;
|
||||
int startTime;
|
||||
int fxIndex;
|
||||
idVec3 worldPosition;
|
||||
bool effectsEnabled;
|
||||
bool forceApplied;
|
||||
bool localSpace;
|
||||
};
|
||||
|
||||
struct idTrail {
|
||||
idBreakableTrail decl;
|
||||
idList<int, 59> pieceStartTime;
|
||||
idList<idVec3, 59> pieceLastParticleDropPos;
|
||||
|
||||
idTrail() : decl(), pieceStartTime(0), pieceLastParticleDropPos(0) {}
|
||||
};
|
||||
|
||||
struct idBreakableParticle {
|
||||
const idDeclParticle* particle;
|
||||
idVec3 pos;
|
||||
int startTime;
|
||||
};
|
||||
|
||||
struct idDecalFX {
|
||||
idBreakableDecal decl;
|
||||
idList<int, 59> pieceStartTime;
|
||||
idList<idVec3, 59> lastCollisionPos;
|
||||
idList<idVec3, 59> lastCollisionNormal;
|
||||
idList<idVec3, 59> pieceLastDecalDropPos;
|
||||
|
||||
idDecalFX() : decl(), pieceStartTime(0), lastCollisionPos(0),
|
||||
lastCollisionNormal(0), pieceLastDecalDropPos(0) {}
|
||||
};
|
||||
|
||||
struct idSpark {
|
||||
const idMaterial* material;
|
||||
float velocityScale;
|
||||
float size;
|
||||
};
|
||||
|
||||
struct breakablePieceSound_t {
|
||||
idSoundWorld* soundWorld;
|
||||
idSoundEmitter* emitter;
|
||||
int channel;
|
||||
const idDeclImpactSound* impactTable;
|
||||
int nextSoundTime;
|
||||
float minBounceVelSqr;
|
||||
};
|
||||
|
||||
idEffectPhysicsBreakable();
|
||||
~idEffectPhysicsBreakable();
|
||||
|
||||
void Init(idClip* clip, idTraceModelCache* traceModelCache,
|
||||
idGameLibEffects* effects, idRenderModelDiscreteAnimation* model,
|
||||
int entityNumber, const breakablePieceSound_t* soundInfo);
|
||||
void Reset(bool skipModelReset);
|
||||
void UpdateSimulation(const idVec3& origin, const idMat3& axis,
|
||||
int currentTime, int gameMsPerFrame, int frameNumber,
|
||||
float lodScale, float effectScale);
|
||||
void UpdateModel(const idVec3& origin, const idMat3& axis,
|
||||
int currentTime);
|
||||
void DrawCollisionModels(int single, const idVec3& offset,
|
||||
const idMat3& axis);
|
||||
void ApplyForce(const idVec3& origin, const idMat3& axis,
|
||||
const idVec3& force);
|
||||
void AddRadiusImpact(int time, const idPhysics* impactPhysics,
|
||||
const idVec3& position, float radius, float impulse);
|
||||
void AddRayImpact(int time, const idVec3& origin, const idMat3& axis,
|
||||
const idVec3& start, const idVec3& end, bool activeOnly);
|
||||
void ApplyImpulseToBody(int bodyIndex, const idPhysics* impactPhysics,
|
||||
const idVec3& point, const idVec3& impulse);
|
||||
void CollideWith(const idVec3& origin, const idPhysics* impactPhysics,
|
||||
const idVec3& point, float impulseScale);
|
||||
bool ActivateAllNow(const idVec3& origin, const idMat3& axis);
|
||||
bool ActivateFloatingPieces(const idVec3& origin, const idMat3& axis,
|
||||
float impulse);
|
||||
void EmitSmokeForEachPiece(const idDeclParticle* particle, int time);
|
||||
void StartExplosions(const idVec3& origin, const idMat3& axis, int time);
|
||||
void StopExplosionFX(int index);
|
||||
void RemoveExplosions();
|
||||
void AddExplosion(const idDeclParticle* particle, int time,
|
||||
const idVec3& position, float radius, float impulse, float falloff,
|
||||
float angular, int delay, int duration, bool localSpace,
|
||||
int activateAllDelay, float distanceDelayScale,
|
||||
const idVec3& endLinear, float effectScale);
|
||||
|
||||
idList<idEffectPhysicsProperties*, 59> properties;
|
||||
idList<idEffectPhysicsRigidBody*, 59> rigidBodies;
|
||||
idList<idEffectPhysicsCollision, 59> collisions;
|
||||
idCollisionModel* basePiece;
|
||||
idList<idExplosion, 59> explosions;
|
||||
// The recovered list stores idFXManager values. Runtime ownership crosses
|
||||
// into effects, so PC stores the returned manager handles at the same ABI.
|
||||
idList<int, 59> explosionFX;
|
||||
idList<idTrail, 59> trails;
|
||||
idList<float, 59> damage;
|
||||
idList<idBreakableParticle, 59> particles;
|
||||
idList<idDecalFX, 59> decals;
|
||||
idList<idSpark, 59> sparks;
|
||||
idList<int, 5> pieceActivationTimes;
|
||||
idEffectPhysicsBroadPhase broadPhase;
|
||||
idRandom random;
|
||||
idGameLibEffects* gameLibEffects;
|
||||
idRenderModelDiscreteAnimation* model;
|
||||
const idDeclBreakable* decl;
|
||||
breakablePieceSound_t soundInfo;
|
||||
|
||||
private:
|
||||
idEffectPhysicsProperties* GetPropertiesForTraceModel(
|
||||
const idTraceModel& traceModel, const idMaterial* material);
|
||||
bool ActivateNoisy(int pieceIndex, const idVec3& origin,
|
||||
const idMat3& axis);
|
||||
bool DamagePiece(int time, int pieceIndex, float amount,
|
||||
const idVec3& position);
|
||||
void InitExplosionsAndTrails(const breakablePieceSound_t* sound,
|
||||
idGameLibEffects* effects, float diversity);
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 15412; PDB kind: class.
|
||||
class idEffectPhysicsBreakable::idTrail
|
||||
{
|
||||
public:
|
||||
idBreakableTrail decl;
|
||||
idList<int,59> pieceStartTime;
|
||||
idList<idVec3,59> pieceLastParticleDropPos;
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 15415; PDB kind: class.
|
||||
class idEffectPhysicsBreakable::idBreakableParticle
|
||||
{
|
||||
public:
|
||||
const idDeclParticle *particle;
|
||||
idVec3 pos;
|
||||
int startTime;
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 15417; PDB kind: class.
|
||||
class idEffectPhysicsBreakable::idDecalFX
|
||||
{
|
||||
public:
|
||||
idBreakableDecal decl;
|
||||
idList<int,59> pieceStartTime;
|
||||
idList<idVec3,59> lastCollisionPos;
|
||||
idList<idVec3,59> lastCollisionNormal;
|
||||
idList<idVec3,59> pieceLastDecalDropPos;
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 15419; PDB kind: class.
|
||||
class idEffectPhysicsBreakable::idSpark
|
||||
{
|
||||
public:
|
||||
const idMaterial *material;
|
||||
float velocityScale;
|
||||
float size;
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 15421; PDB kind: struct.
|
||||
struct idEffectPhysicsBreakable::breakablePieceSound_t
|
||||
{
|
||||
idSoundWorld *soundWorld;
|
||||
idSoundEmitter *emitter;
|
||||
soundChannel_t channel;
|
||||
const idDeclImpactSound *impactTable;
|
||||
int nextSoundTime;
|
||||
float minBounceVelSqr;
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 15422; PDB kind: class.
|
||||
class idEffectPhysicsBreakable
|
||||
{
|
||||
public:
|
||||
idList<idEffectPhysicsProperties *,59> properties;
|
||||
idList<idEffectPhysicsRigidBody *,59> rigidBodies;
|
||||
idList<idEffectPhysicsCollision,59> collisions;
|
||||
idCollisionModel *basePiece;
|
||||
idList<idEffectPhysicsBreakable::idExplosion,59> explosions;
|
||||
idList<idFXManager,59> explosionFX;
|
||||
idList<idEffectPhysicsBreakable::idTrail,59> trails;
|
||||
idList<float,59> damage;
|
||||
idList<idEffectPhysicsBreakable::idBreakableParticle,59> particles;
|
||||
idList<idEffectPhysicsBreakable::idDecalFX,59> decals;
|
||||
idList<idEffectPhysicsBreakable::idSpark,59> sparks;
|
||||
idList<int,5> pieceActivationTimes;
|
||||
idEffectPhysicsBroadPhase broadPhase;
|
||||
idRandom random;
|
||||
idGameLibEffects *gameLibEffects;
|
||||
idRenderModelDiscreteAnimation *model;
|
||||
const idDeclBreakable *decl;
|
||||
idEffectPhysicsBreakable::breakablePieceSound_t soundInfo;
|
||||
};
|
||||
static_assert(sizeof(idBreakableExplosion) == 84,
|
||||
"Recovered idBreakableExplosion ABI changed");
|
||||
static_assert(sizeof(idBreakableTrail) == 68,
|
||||
"Recovered idBreakableTrail ABI changed");
|
||||
static_assert(sizeof(idBreakableDecal) == 76,
|
||||
"Recovered idBreakableDecal ABI changed");
|
||||
static_assert(sizeof(idEffectPhysicsBreakable::idExplosion) == 108,
|
||||
"Recovered breakable explosion ABI changed");
|
||||
static_assert(sizeof(idEffectPhysicsBreakable::idTrail) == 100,
|
||||
"Recovered breakable trail ABI changed");
|
||||
static_assert(sizeof(idEffectPhysicsBreakable::idDecalFX) == 140,
|
||||
"Recovered breakable decal ABI changed");
|
||||
#if defined(_WIN32) && !defined(_WIN64)
|
||||
static_assert(sizeof(idEffectPhysicsBreakable) == 292,
|
||||
"Recovered idEffectPhysicsBreakable ABI changed");
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,290 @@
|
||||
#include "gamelib/effectphysics/effectphysicsbroadphase.h"
|
||||
|
||||
#include "gamelib/physics/physics.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
bool GameLib_GetEffectPhysicsMotionResult(
|
||||
idCollisionQuery& query, trace_t& result);
|
||||
idCollisionQuery GameLib_SubmitEffectPhysicsMotion(idClip* clip,
|
||||
const idTraceModel& traceModel, const idSPObject& object,
|
||||
const idPositionedCollisionModel* models, int numModels,
|
||||
bool worldCollisionModelOnly);
|
||||
|
||||
idEffectPhysicsBroadPhase::idEffectPhysicsBroadPhase()
|
||||
: clip(nullptr)
|
||||
, traceModelCache(nullptr)
|
||||
, objects(0)
|
||||
, markers{idList<idSPMarker, 59>(0), idList<idSPMarker, 59>(0),
|
||||
idList<idSPMarker, 59>(0)} {
|
||||
}
|
||||
|
||||
idEffectPhysicsBroadPhase::~idEffectPhysicsBroadPhase() {
|
||||
clip = nullptr;
|
||||
traceModelCache = nullptr;
|
||||
objects.Clear();
|
||||
for (idList<idSPMarker, 59>& axisMarkers : markers) {
|
||||
axisMarkers.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
void idEffectPhysicsBroadPhase::Init(idClip* const clip_,
|
||||
idTraceModelCache* const traceModelCache_) {
|
||||
clip = clip_;
|
||||
traceModelCache = traceModelCache_;
|
||||
}
|
||||
|
||||
const idBounds* idEffectPhysicsBroadPhase::ResolveBounds(
|
||||
const idSPObject* const object, const idBounds* const suppliedBounds) const {
|
||||
if (suppliedBounds != nullptr) {
|
||||
return suppliedBounds;
|
||||
}
|
||||
if (traceModelCache == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
const idTraceModelCache::trmCache_t* const entry =
|
||||
traceModelCache->GetEntry(object->traceModelIndex);
|
||||
return entry != nullptr && entry->trm != nullptr
|
||||
? &entry->trm->bounds
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
void idEffectPhysicsBroadPhase::AddObject(idSPObject* const object,
|
||||
const idVec3& origin, const idMat3& axis, const idBounds* const bounds) {
|
||||
const idBounds* const localBounds = ResolveBounds(object, bounds);
|
||||
if (localBounds == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
idBounds transformedBounds;
|
||||
transformedBounds.FromTransformedBounds(*localBounds, origin, axis);
|
||||
object->origin = origin;
|
||||
object->axis = axis;
|
||||
object->absBounds.SetBounds(transformedBounds);
|
||||
|
||||
int slot = 0;
|
||||
while (slot < objects.Num() && objects[slot] != nullptr) {
|
||||
++slot;
|
||||
}
|
||||
if (slot == objects.Num()) {
|
||||
object->id = objects.Append(object);
|
||||
} else {
|
||||
object->id = slot;
|
||||
objects[slot] = object;
|
||||
}
|
||||
RebuildBroadPhase();
|
||||
}
|
||||
|
||||
void idEffectPhysicsBroadPhase::RemoveObject(idSPObject* const object) {
|
||||
if (object == nullptr || object->id < 0 || object->id >= objects.Num() ||
|
||||
objects[object->id] != object) {
|
||||
return;
|
||||
}
|
||||
objects[object->id] = nullptr;
|
||||
object->id = -1;
|
||||
object->overlap.Clear();
|
||||
for (int side = 0; side < 2; ++side) {
|
||||
for (int axis = 0; axis < 3; ++axis) {
|
||||
object->index[side][axis] = -1;
|
||||
}
|
||||
}
|
||||
RebuildBroadPhase();
|
||||
}
|
||||
|
||||
void idEffectPhysicsBroadPhase::RebuildBroadPhase() {
|
||||
for (idList<idSPMarker, 59>& axisMarkers : markers) {
|
||||
axisMarkers.Clear();
|
||||
}
|
||||
for (int index = 0; index < objects.Num(); ++index) {
|
||||
idSPObject* const object = objects[index];
|
||||
if (object == nullptr) {
|
||||
continue;
|
||||
}
|
||||
object->overlap.Clear();
|
||||
if (object->motion.worldCollisionOnly) {
|
||||
continue;
|
||||
}
|
||||
for (int axis = 0; axis < 3; ++axis) {
|
||||
idSPMarker minimum = {
|
||||
object->absBounds.b[0][axis],
|
||||
static_cast<unsigned short>(object->id)};
|
||||
idSPMarker maximum = {
|
||||
object->absBounds.b[1][axis],
|
||||
static_cast<unsigned short>(object->id | 0x8000)};
|
||||
markers[axis].Append(minimum);
|
||||
markers[axis].Append(maximum);
|
||||
}
|
||||
}
|
||||
|
||||
for (int axis = 0; axis < 3; ++axis) {
|
||||
idList<idSPMarker, 59>& axisMarkers = markers[axis];
|
||||
std::sort(axisMarkers.Ptr(), axisMarkers.Ptr() + axisMarkers.Num(),
|
||||
[](const idSPMarker& left, const idSPMarker& right) {
|
||||
if (left.value != right.value) {
|
||||
return left.value < right.value;
|
||||
}
|
||||
return left.data < right.data;
|
||||
});
|
||||
for (int markerIndex = 0; markerIndex < axisMarkers.Num();
|
||||
++markerIndex) {
|
||||
const unsigned short data = axisMarkers[markerIndex].data;
|
||||
const int objectIndex = data & 0x7FFF;
|
||||
if (objectIndex < objects.Num() && objects[objectIndex] != nullptr) {
|
||||
objects[objectIndex]->index[data >> 15][axis] = markerIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int first = 0; first < objects.Num(); ++first) {
|
||||
idSPObject* const firstObject = objects[first];
|
||||
if (firstObject == nullptr || firstObject->motion.worldCollisionOnly) {
|
||||
continue;
|
||||
}
|
||||
for (int second = first + 1; second < objects.Num(); ++second) {
|
||||
idSPObject* const secondObject = objects[second];
|
||||
if (secondObject == nullptr ||
|
||||
secondObject->motion.worldCollisionOnly ||
|
||||
!firstObject->absBounds.IntersectsBounds(
|
||||
secondObject->absBounds)) {
|
||||
continue;
|
||||
}
|
||||
firstObject->overlap.Append(static_cast<short>(second));
|
||||
secondObject->overlap.Append(static_cast<short>(first));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void idEffectPhysicsBroadPhase::MoveObject(idSPObject* const object) {
|
||||
(void)object;
|
||||
RebuildBroadPhase();
|
||||
}
|
||||
|
||||
void idEffectPhysicsBroadPhase::SetObjectPosition(idSPObject* const object,
|
||||
const idVec3& origin, const idMat3& axis, const idBounds* const bounds) {
|
||||
const idBounds* const localBounds = ResolveBounds(object, bounds);
|
||||
if (localBounds == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
idBounds transformedBounds;
|
||||
transformedBounds.FromTransformedBounds(*localBounds, origin, axis);
|
||||
object->absBounds.SetBounds(transformedBounds);
|
||||
object->origin = origin;
|
||||
object->axis = axis;
|
||||
if (!object->motion.worldCollisionOnly) {
|
||||
MoveObject(object);
|
||||
}
|
||||
object->motion.valid = false;
|
||||
object->query.offset = 0;
|
||||
}
|
||||
|
||||
void idEffectPhysicsBroadPhase::ApplyMotion(idSPObject* const object,
|
||||
const idVec3& translation, const idRotation& rotation) {
|
||||
const idBounds* const localBounds = ResolveBounds(object, nullptr);
|
||||
if (localBounds == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
idBounds startBounds;
|
||||
startBounds.FromTransformedBounds(*localBounds, object->origin,
|
||||
object->axis);
|
||||
idMat3 endAxis = object->axis;
|
||||
rotation.RotateAxis(endAxis);
|
||||
idBounds endBounds;
|
||||
endBounds.FromTransformedBounds(*localBounds,
|
||||
object->origin + translation, endAxis);
|
||||
|
||||
idBounds motionBounds;
|
||||
for (int axis = 0; axis < 3; ++axis) {
|
||||
motionBounds[0][axis] = (std::min)(
|
||||
startBounds[0][axis], endBounds[0][axis]) - 1.0f;
|
||||
motionBounds[1][axis] = (std::max)(
|
||||
startBounds[1][axis], endBounds[1][axis]) + 1.0f;
|
||||
}
|
||||
object->absBounds.SetBounds(motionBounds);
|
||||
if (!object->motion.worldCollisionOnly) {
|
||||
MoveObject(object);
|
||||
}
|
||||
|
||||
object->motion.valid = true;
|
||||
object->motion.translation = translation;
|
||||
object->motion.rotationVec = rotation.vec;
|
||||
object->motion.rotationAngle = rotation.angle;
|
||||
}
|
||||
|
||||
int idEffectPhysicsBroadPhase::GetPositionedModels(
|
||||
const idSPObject* const object, idPositionedCollisionModel* const models,
|
||||
const int maxModels) const {
|
||||
if (traceModelCache == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
for (int index = 0; index < object->overlap.Num() && count < maxModels;
|
||||
++index) {
|
||||
const int objectIndex = object->overlap[index];
|
||||
if (objectIndex < 0 || objectIndex >= objects.Num()) {
|
||||
continue;
|
||||
}
|
||||
const idSPObject* const other = objects[objectIndex];
|
||||
if (other == nullptr || other->traceModelIndex == -1 ||
|
||||
other->motion.ignore) {
|
||||
continue;
|
||||
}
|
||||
const idTraceModelCache::trmCache_t* const entry =
|
||||
traceModelCache->GetEntry(other->traceModelIndex);
|
||||
if (entry == nullptr || entry->collisionModel == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
idPositionedCollisionModel& model = models[count++];
|
||||
model.model = entry->collisionModel;
|
||||
model.modelJoints = nullptr;
|
||||
model.modelOrigin = other->origin;
|
||||
model.modelAxis = other->axis;
|
||||
model.modelEntityNum = 0x1FFF;
|
||||
model.modelPhysicsId = other->id;
|
||||
model.modelBodyId = 0;
|
||||
model.modelContentsOverride = other->clipMask;
|
||||
model.modelQuery = other->query;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
void idEffectPhysicsBroadPhase::SubmitQueries(
|
||||
const bool worldCollisionModelOnly) {
|
||||
if (traceModelCache == nullptr || clip == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
idPositionedCollisionModel models[128]{};
|
||||
for (int index = 0; index < objects.Num(); ++index) {
|
||||
idSPObject* const object = objects[index];
|
||||
if (object == nullptr || !object->motion.valid) {
|
||||
continue;
|
||||
}
|
||||
const idTraceModelCache::trmCache_t* const entry =
|
||||
traceModelCache->GetEntry(object->traceModelIndex);
|
||||
if (entry == nullptr || entry->trm == nullptr) {
|
||||
object->motion.valid = false;
|
||||
continue;
|
||||
}
|
||||
const int numModels = object->motion.worldCollisionOnly
|
||||
? 0
|
||||
: GetPositionedModels(object, models, 128);
|
||||
object->query = GameLib_SubmitEffectPhysicsMotion(
|
||||
clip, *entry->trm, *object, models, numModels,
|
||||
worldCollisionModelOnly);
|
||||
object->motion.valid = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool idEffectPhysicsBroadPhase::GetMotionResult(trace_t* const result,
|
||||
idSPObject* const object) {
|
||||
const bool available = GameLib_GetEffectPhysicsMotionResult(
|
||||
object->query, *result);
|
||||
object->origin = result->endpos;
|
||||
object->axis = result->endAxis;
|
||||
object->axis.OrthoNormalizeSelf();
|
||||
return available;
|
||||
}
|
||||
@@ -1,25 +1,103 @@
|
||||
#pragma once
|
||||
|
||||
// Reconstructed C++ declarations from IDA Local Types and PDB/DIA metadata.
|
||||
// Original PDB header: w:\tech5\engine\gamelib\effectphysics\effectphysicsbroadphase.h
|
||||
// Recovered logical types: 2
|
||||
// Signatures retain Xbox 360 ABI evidence and may still require manual review.
|
||||
#include "cm/collisionmodel.h"
|
||||
#include "cm/jobs/collisionquery.h"
|
||||
#include "gamelib/physics/tracemodelcache.h"
|
||||
#include "idlib/bv/boundsshort.h"
|
||||
#include "idlib/containers/list.h"
|
||||
#include "idlib/math/rotation.h"
|
||||
|
||||
class idClip;
|
||||
class idJointMat;
|
||||
struct trace_t;
|
||||
|
||||
// IDA Local Type ordinal 14572; PDB kind: class.
|
||||
class idEffectPhysicsBroadPhase::idSPMarker
|
||||
{
|
||||
public:
|
||||
__int16 value;
|
||||
unsigned __int16 data;
|
||||
struct idSPObjectMotion {
|
||||
bool valid;
|
||||
bool worldCollisionOnly;
|
||||
bool ignore;
|
||||
idVec3 translation;
|
||||
idVec3 rotationVec;
|
||||
float rotationAngle;
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 14574; PDB kind: class.
|
||||
class idEffectPhysicsBroadPhase
|
||||
{
|
||||
class idSPObject {
|
||||
public:
|
||||
idClip *clip;
|
||||
idTraceModelCache *traceModelCache;
|
||||
idList<idSPObject *,59> objects;
|
||||
idList<idEffectPhysicsBroadPhase::idSPMarker,59> markers[3];
|
||||
// Materialized in the authoritative effectphysicsbroadphase.h dump.
|
||||
idSPObject()
|
||||
: id(0), traceModelIndex(-1), entityNum(0x1FFF), clipMask(1),
|
||||
origin(), axis(1.0f), absBounds(), motion{}, query{0}, index{},
|
||||
overlap(0) {
|
||||
origin.Zero();
|
||||
absBounds.b[0][0] = 0x7FFF;
|
||||
absBounds.b[0][1] = 0x7FFF;
|
||||
absBounds.b[0][2] = 0x7FFF;
|
||||
absBounds.b[1][0] = static_cast<short>(0x8000);
|
||||
absBounds.b[1][1] = static_cast<short>(0x8000);
|
||||
absBounds.b[1][2] = static_cast<short>(0x8000);
|
||||
motion.translation.Zero();
|
||||
motion.rotationVec.Zero();
|
||||
motion.rotationAngle = 0.0f;
|
||||
}
|
||||
|
||||
int id;
|
||||
int traceModelIndex;
|
||||
int entityNum;
|
||||
int clipMask;
|
||||
idVec3 origin;
|
||||
idMat3 axis;
|
||||
idBoundsShort absBounds;
|
||||
idSPObjectMotion motion;
|
||||
idCollisionQuery query;
|
||||
int index[2][3];
|
||||
idList<short, 59> overlap;
|
||||
};
|
||||
|
||||
// Reconstructed from PDB local types 14572 and 14574.
|
||||
class idEffectPhysicsBroadPhase {
|
||||
public:
|
||||
class idSPMarker {
|
||||
public:
|
||||
short value;
|
||||
unsigned short data;
|
||||
};
|
||||
|
||||
idEffectPhysicsBroadPhase();
|
||||
~idEffectPhysicsBroadPhase();
|
||||
|
||||
void Init(idClip* clip_, idTraceModelCache* traceModelCache_);
|
||||
bool GetMotionResult(trace_t* result, idSPObject* object);
|
||||
int GetPositionedModels(const idSPObject* object,
|
||||
idPositionedCollisionModel* models, int maxModels) const;
|
||||
void SubmitQueries(bool worldCollisionModelOnly);
|
||||
void RemoveObject(idSPObject* object);
|
||||
void AddObject(idSPObject* object, const idVec3& origin,
|
||||
const idMat3& axis, const idBounds* bounds);
|
||||
void SetObjectPosition(idSPObject* object, const idVec3& origin,
|
||||
const idMat3& axis, const idBounds* bounds);
|
||||
void ApplyMotion(idSPObject* object, const idVec3& translation,
|
||||
const idRotation& rotation);
|
||||
|
||||
idClip *clip;
|
||||
idTraceModelCache *traceModelCache;
|
||||
idList<idSPObject *, 59> objects;
|
||||
idList<idSPMarker, 59> markers[3];
|
||||
|
||||
private:
|
||||
const idBounds* ResolveBounds(const idSPObject* object,
|
||||
const idBounds* suppliedBounds) const;
|
||||
void MoveObject(idSPObject* object);
|
||||
void RebuildBroadPhase();
|
||||
};
|
||||
|
||||
static_assert(sizeof(idEffectPhysicsBroadPhase::idSPMarker) == 4,
|
||||
"Recovered idSPMarker ABI changed");
|
||||
#if defined(_WIN32) && !defined(_WIN64)
|
||||
static_assert(sizeof(idPositionedCollisionModel) == 80,
|
||||
"Recovered positioned collision-model ABI changed");
|
||||
static_assert(sizeof(idSPObjectMotion) == 32,
|
||||
"Recovered idSPObjectMotion ABI changed");
|
||||
static_assert(sizeof(idSPObject) == 160,
|
||||
"Recovered idSPObject ABI changed");
|
||||
static_assert(sizeof(idEffectPhysicsBroadPhase) == 72,
|
||||
"Recovered idEffectPhysicsBroadPhase ABI changed");
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,308 @@
|
||||
#include "gamelib/effectphysics/effectphysicscuttable.h"
|
||||
|
||||
#include "gamelib/physics/tracemodelcache.h"
|
||||
#include "idlib/geometry/tracemodel.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
idRenderModelCuttableDynamic* GameLib_AllocCuttableRenderModel(
|
||||
idRenderWorld* renderWorld);
|
||||
void GameLib_DeleteCuttableRenderModel(idRenderModelCuttableDynamic* model);
|
||||
void GameLib_UploadCuttableRenderModel(idRenderModelCuttableDynamic* model,
|
||||
const idMaterial* renderMaterial, const idMaterial* depthMaterial,
|
||||
const idList<idDrawVert, 5>& vertices,
|
||||
const idList<idVec2, 5>& textureCoordinates,
|
||||
const idList<unsigned short, 5>& indices);
|
||||
void GameLib_UpdateCuttableRenderModel(idRenderModelCuttableDynamic* model,
|
||||
const idVec3& origin, const idMat3& axis, float fade, bool hidden);
|
||||
void GameLib_PlayCuttableCollision(idSoundWorld* soundWorld,
|
||||
const idDeclImpactSound* soundTable,
|
||||
const idEffectPhysicsCollision& collision, const idVec3& origin,
|
||||
float velocitySqr, float minimumVelocitySqr, int& nextSoundTime,
|
||||
int currentTime);
|
||||
void GameLib_DrawEffectTraceModel(const idTraceModelCache* cache,
|
||||
int traceModelIndex, int pieceIndex, const idVec3& origin,
|
||||
const idMat3& axis);
|
||||
|
||||
idEffectPhysicsCuttable::idEffectPhysicsCuttable()
|
||||
: freeModels()
|
||||
, models()
|
||||
, pieces()
|
||||
, broadPhase()
|
||||
, pieceCollisionAgeDecay(0.0f)
|
||||
, pieceFriction(0.0f)
|
||||
, pieceMaxLifeTime(30000)
|
||||
, pieceLifeTime(5000)
|
||||
, pieceFadeTime(2000)
|
||||
, pieceMass(8)
|
||||
, soundWorld(nullptr) {
|
||||
}
|
||||
|
||||
idEffectPhysicsCuttable::~idEffectPhysicsCuttable() {
|
||||
for (int index = 0; index < pieces.Num(); ++index) {
|
||||
piece_t& piece = pieces[index];
|
||||
if (piece.properties != nullptr && broadPhase.traceModelCache != nullptr) {
|
||||
broadPhase.traceModelCache->FreeTraceModel(
|
||||
piece.properties->traceModelIndex);
|
||||
}
|
||||
delete piece.rigidBody;
|
||||
delete piece.properties;
|
||||
piece.rigidBody = nullptr;
|
||||
piece.properties = nullptr;
|
||||
piece.soundTable = nullptr;
|
||||
}
|
||||
for (int index = 0; index < models.Num(); ++index) {
|
||||
if (models[index] != nullptr) {
|
||||
GameLib_DeleteCuttableRenderModel(models[index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void idEffectPhysicsCuttable::Init(idRenderWorld* const renderWorld,
|
||||
idSoundWorld* const soundWorld_, idClip* const clip,
|
||||
idTraceModelCache* const traceModelCache) {
|
||||
broadPhase.Init(clip, traceModelCache);
|
||||
pieceCollisionAgeDecay = 0.0f;
|
||||
pieceMaxLifeTime = 30000;
|
||||
pieceLifeTime = 5000;
|
||||
pieceFadeTime = 2000;
|
||||
pieceMass = 8;
|
||||
pieceFriction = 0.0f;
|
||||
soundWorld = soundWorld_;
|
||||
|
||||
pieces.SetNum(MAX_PIECES);
|
||||
for (int index = 0; index < pieces.Num(); ++index) {
|
||||
piece_t& piece = pieces[index];
|
||||
piece.properties = nullptr;
|
||||
piece.rigidBody = nullptr;
|
||||
piece.soundTable = nullptr;
|
||||
piece.collision.Clear();
|
||||
piece.orientation = idMat3(1.0f);
|
||||
piece.position.Zero();
|
||||
piece.timeStamp = 0;
|
||||
piece.firstCollisionTime = 0;
|
||||
piece.renderModelIndex = -1;
|
||||
piece.nextSoundTime = 0;
|
||||
piece.minBounceVelocitySqr = 1.0f;
|
||||
}
|
||||
|
||||
models.Clear();
|
||||
freeModels.Clear();
|
||||
for (int index = 0; index < MAX_BUFFERS; ++index) {
|
||||
models.Append(GameLib_AllocCuttableRenderModel(renderWorld));
|
||||
freeModels.Append(model_t{index, 0});
|
||||
}
|
||||
}
|
||||
|
||||
void idEffectPhysicsCuttable::ReleasePiece(piece_t& piece,
|
||||
const int currentTime) {
|
||||
if (piece.renderModelIndex >= 0) {
|
||||
freeModels.Append(model_t{piece.renderModelIndex, currentTime});
|
||||
if (piece.renderModelIndex < models.Num() &&
|
||||
models[piece.renderModelIndex] != nullptr) {
|
||||
GameLib_UpdateCuttableRenderModel(models[piece.renderModelIndex],
|
||||
idVec3(0.0f, 0.0f, 0.0f), idMat3(1.0f), 0.0f, true);
|
||||
}
|
||||
piece.renderModelIndex = -1;
|
||||
}
|
||||
if (piece.rigidBody != nullptr) {
|
||||
piece.rigidBody->active = false;
|
||||
}
|
||||
piece.timeStamp = 0;
|
||||
}
|
||||
|
||||
int idEffectPhysicsCuttable::Emit(
|
||||
const cuttablePhysicsEmitInfo_t& emitInfo,
|
||||
const idList<idDrawVert, 5>& vertices,
|
||||
const idList<idVec2, 5>& textureCoordinates,
|
||||
const idList<unsigned short, 5>& indices) {
|
||||
if (emitInfo.traceModel == nullptr || broadPhase.traceModelCache == nullptr ||
|
||||
pieces.IsEmpty()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int pieceIndex = 0;
|
||||
for (int index = 1; index < pieces.Num(); ++index) {
|
||||
if (pieces[index].timeStamp < pieces[pieceIndex].timeStamp) {
|
||||
pieceIndex = index;
|
||||
}
|
||||
}
|
||||
piece_t& piece = pieces[pieceIndex];
|
||||
|
||||
// The pool has two more models than live pieces. Return the recycled
|
||||
// piece's model before selecting the oldest free buffer.
|
||||
if (piece.renderModelIndex >= 0) {
|
||||
freeModels.Append(model_t{piece.renderModelIndex, emitInfo.emitTime});
|
||||
piece.renderModelIndex = -1;
|
||||
}
|
||||
int freeIndex = 0;
|
||||
for (int index = 1; index < freeModels.Num(); ++index) {
|
||||
if (freeModels[index].timeStamp < freeModels[freeIndex].timeStamp) {
|
||||
freeIndex = index;
|
||||
}
|
||||
}
|
||||
const int renderModelIndex = freeModels.IsEmpty()
|
||||
? -1 : freeModels[freeIndex].index;
|
||||
if (!freeModels.IsEmpty()) {
|
||||
freeModels.RemoveIndexFast(freeIndex);
|
||||
}
|
||||
|
||||
if (piece.properties != nullptr) {
|
||||
broadPhase.traceModelCache->FreeTraceModel(
|
||||
piece.properties->traceModelIndex);
|
||||
}
|
||||
delete piece.rigidBody;
|
||||
delete piece.properties;
|
||||
piece.rigidBody = nullptr;
|
||||
piece.properties = nullptr;
|
||||
|
||||
idTraceModel centeredTraceModel = *emitInfo.traceModel;
|
||||
const idVec3 center =
|
||||
(centeredTraceModel.bounds[0] + centeredTraceModel.bounds[1]) * 0.5f;
|
||||
centeredTraceModel.Translate(-center);
|
||||
const int traceModelIndex = broadPhase.traceModelCache->AllocTraceModel(
|
||||
centeredTraceModel, emitInfo.renderMaterial);
|
||||
|
||||
piece.properties = new idEffectPhysicsProperties();
|
||||
piece.properties->Setup(&broadPhase, traceModelIndex,
|
||||
static_cast<float>(pieceMass));
|
||||
piece.properties->contactFriction = 0.5f;
|
||||
piece.properties->linearFriction = 0.6f;
|
||||
piece.properties->angularFriction = 0.6f;
|
||||
piece.properties->bouncyness = 0.6f;
|
||||
piece.properties->angularFrictionWater = 1.0f;
|
||||
piece.properties->linearFrictionWater = 1.0f;
|
||||
piece.properties->gravityVector.Set(0.0f, 0.0f, -250.0f);
|
||||
piece.properties->worldCollisionOnly = true;
|
||||
piece.properties->simplePointCollision = false;
|
||||
piece.properties->crazyBounceChance = 0.0f;
|
||||
piece.properties->maxLinearVelocity = 5000.0f;
|
||||
piece.properties->stopSpeed = 30.0f;
|
||||
piece.properties->maxAngularVelocity = 12.566370614359f;
|
||||
piece.properties->clipMask = 1;
|
||||
|
||||
piece.rigidBody = new idEffectPhysicsRigidBody();
|
||||
piece.rigidBody->Setup(piece.properties);
|
||||
piece.rigidBody->SPObject.entityNum = emitInfo.entityNum;
|
||||
piece.rigidBody->SetPosition(emitInfo.position + center,
|
||||
emitInfo.orientation);
|
||||
piece.rigidBody->currentState.linearMomentum.Zero();
|
||||
piece.rigidBody->currentState.angularMomentum.Zero();
|
||||
if (emitInfo.impulse.LengthSqr() > 0.25f) {
|
||||
piece.rigidBody->ApplyImpulse(
|
||||
emitInfo.position + emitInfo.impulsePoint, emitInfo.impulse);
|
||||
}
|
||||
piece.rigidBody->active = true;
|
||||
piece.rigidBody->SPObject.motion.ignore = false;
|
||||
|
||||
piece.soundTable = emitInfo.soundTable;
|
||||
piece.collision.Clear();
|
||||
piece.orientation = emitInfo.orientation;
|
||||
piece.position = -center;
|
||||
piece.timeStamp = emitInfo.emitTime;
|
||||
piece.firstCollisionTime = 0;
|
||||
piece.renderModelIndex = renderModelIndex;
|
||||
piece.nextSoundTime = emitInfo.emitTime;
|
||||
piece.minBounceVelocitySqr = emitInfo.minBounceVelocitySqr;
|
||||
|
||||
if (renderModelIndex >= 0 && renderModelIndex < models.Num() &&
|
||||
models[renderModelIndex] != nullptr) {
|
||||
GameLib_UploadCuttableRenderModel(models[renderModelIndex],
|
||||
emitInfo.renderMaterial, emitInfo.depthMaterial,
|
||||
vertices, textureCoordinates, indices);
|
||||
GameLib_UpdateCuttableRenderModel(models[renderModelIndex],
|
||||
emitInfo.position, emitInfo.orientation, 1.0f, false);
|
||||
}
|
||||
return pieceIndex;
|
||||
}
|
||||
|
||||
void idEffectPhysicsCuttable::UpdateSimulation(const idVec3& origin,
|
||||
const idMat3& axis, const int currentTime, const int gameMsPerFrame) {
|
||||
(void)origin;
|
||||
(void)axis;
|
||||
const float deltaTime = (std::max)(0, gameMsPerFrame) * 0.001f;
|
||||
for (int index = 0; index < pieces.Num(); ++index) {
|
||||
piece_t& piece = pieces[index];
|
||||
idEffectPhysicsRigidBody* const body = piece.rigidBody;
|
||||
if (piece.timeStamp == 0 || body == nullptr || !body->active) {
|
||||
continue;
|
||||
}
|
||||
const int deathTime = piece.firstCollisionTime != 0
|
||||
? piece.firstCollisionTime + pieceLifeTime + pieceFadeTime
|
||||
: piece.timeStamp + pieceMaxLifeTime;
|
||||
if (currentTime >= deathTime) {
|
||||
ReleasePiece(piece, currentTime);
|
||||
continue;
|
||||
}
|
||||
body->Update(deltaTime, 0.0f, piece.collision);
|
||||
if (body->settled && piece.firstCollisionTime == 0) {
|
||||
piece.firstCollisionTime = currentTime;
|
||||
} else if (piece.collision.collisionNormalVelocity.LengthSqr() > 0.0f) {
|
||||
if (piece.firstCollisionTime == 0) {
|
||||
piece.firstCollisionTime = currentTime;
|
||||
}
|
||||
const idVec3 velocity = body->currentState.linearMomentum *
|
||||
body->properties->inverseMass -
|
||||
body->properties->gravityVector * deltaTime;
|
||||
GameLib_PlayCuttableCollision(soundWorld, piece.soundTable,
|
||||
piece.collision, body->currentState.position,
|
||||
velocity.LengthSqr(), piece.minBounceVelocitySqr,
|
||||
piece.nextSoundTime, currentTime);
|
||||
}
|
||||
if (pieceFriction > 0.0f) {
|
||||
body->currentState.linearMomentum =
|
||||
body->currentState.linearMomentum * (std::max)(0.0f,
|
||||
1.0f - pieceFriction * deltaTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void idEffectPhysicsCuttable::UpdateModel(const idVec3& scale,
|
||||
const int currentTime) {
|
||||
const idMat3 scaleAxis(scale.x, 0.0f, 0.0f,
|
||||
0.0f, scale.y, 0.0f, 0.0f, 0.0f, scale.z);
|
||||
for (int index = 0; index < pieces.Num(); ++index) {
|
||||
piece_t& piece = pieces[index];
|
||||
if (piece.timeStamp == 0 || piece.rigidBody == nullptr ||
|
||||
!piece.rigidBody->active || piece.renderModelIndex < 0 ||
|
||||
piece.renderModelIndex >= models.Num() ||
|
||||
models[piece.renderModelIndex] == nullptr) {
|
||||
continue;
|
||||
}
|
||||
const idVec3 renderOrigin = piece.rigidBody->currentState.position +
|
||||
piece.rigidBody->currentState.orientation * piece.position;
|
||||
float fade = 1.0f;
|
||||
if (piece.firstCollisionTime != 0 && pieceFadeTime > 0) {
|
||||
const int fadeAge = currentTime -
|
||||
(piece.firstCollisionTime + pieceLifeTime);
|
||||
if (fadeAge > 0) {
|
||||
fade = (std::max)(0.0f,
|
||||
1.0f - fadeAge / static_cast<float>(pieceFadeTime));
|
||||
}
|
||||
}
|
||||
GameLib_UpdateCuttableRenderModel(models[piece.renderModelIndex],
|
||||
renderOrigin, piece.rigidBody->currentState.orientation * scaleAxis,
|
||||
fade, false);
|
||||
}
|
||||
}
|
||||
|
||||
void idEffectPhysicsCuttable::SubmitCollisionQueries() {
|
||||
broadPhase.SubmitQueries(false);
|
||||
}
|
||||
|
||||
void idEffectPhysicsCuttable::DrawCollisionModels(const int single) {
|
||||
for (int index = 0; index < pieces.Num(); ++index) {
|
||||
if (single >= 0 && index != single) {
|
||||
continue;
|
||||
}
|
||||
const piece_t& piece = pieces[index];
|
||||
if (piece.timeStamp == 0 || piece.rigidBody == nullptr ||
|
||||
!piece.rigidBody->active || piece.properties == nullptr) {
|
||||
continue;
|
||||
}
|
||||
GameLib_DrawEffectTraceModel(broadPhase.traceModelCache,
|
||||
piece.properties->traceModelIndex, index,
|
||||
piece.rigidBody->currentState.position,
|
||||
piece.rigidBody->currentState.orientation);
|
||||
}
|
||||
}
|
||||
@@ -1,54 +1,94 @@
|
||||
#pragma once
|
||||
|
||||
// Reconstructed C++ declarations from IDA Local Types and PDB/DIA metadata.
|
||||
// Original PDB header: w:\tech5\engine\gamelib\effectphysics\effectphysicscuttable.h
|
||||
// Recovered logical types: 4
|
||||
// Signatures retain Xbox 360 ABI evidence and may still require manual review.
|
||||
#include "gamelib/effectphysics/effectphysicsrigidbody.h"
|
||||
#include "idlib/containers/staticlist.h"
|
||||
#include "idlib/geometry/drawvert.h"
|
||||
|
||||
class idClip;
|
||||
class idDeclImpactSound;
|
||||
class idMaterial;
|
||||
class idRenderModelCuttableDynamic;
|
||||
class idRenderWorld;
|
||||
class idSoundWorld;
|
||||
class idTraceModel;
|
||||
class idTraceModelCache;
|
||||
|
||||
// IDA Local Type ordinal 2911; PDB kind: unknown.
|
||||
enum idEffectPhysicsCuttable::<unnamed_tag> : __int32
|
||||
{
|
||||
MAX_PIECES = 0x8,
|
||||
MAX_BUFFERS = 0xA,
|
||||
struct cuttablePhysicsEmitInfo_t {
|
||||
const idMaterial* renderMaterial;
|
||||
const idMaterial* depthMaterial;
|
||||
const idDeclImpactSound* soundTable;
|
||||
idTraceModel* traceModel;
|
||||
idMat3 orientation;
|
||||
idVec3 position;
|
||||
idVec3 impulsePoint;
|
||||
idVec3 impulse;
|
||||
int emitTime;
|
||||
int entityNum;
|
||||
float minBounceVelocitySqr;
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 17356; PDB kind: struct.
|
||||
struct idEffectPhysicsCuttable::model_t
|
||||
{
|
||||
int index;
|
||||
int timeStamp;
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 17363; PDB kind: struct.
|
||||
struct idEffectPhysicsCuttable::piece_t
|
||||
{
|
||||
idEffectPhysicsProperties *properties;
|
||||
idEffectPhysicsRigidBody *rigidBody;
|
||||
const idDeclImpactSound *soundTable;
|
||||
idEffectPhysicsCollision collision;
|
||||
idMat3 orientation;
|
||||
idVec3 position;
|
||||
int timeStamp;
|
||||
int firstCollisionTime;
|
||||
int renderModelIndex;
|
||||
int nextSoundTime;
|
||||
float minBounceVelocitySqr;
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 17366; PDB kind: class.
|
||||
class idEffectPhysicsCuttable
|
||||
{
|
||||
class idEffectPhysicsCuttable {
|
||||
public:
|
||||
idStaticList<idEffectPhysicsCuttable::model_t,10> freeModels;
|
||||
idStaticList<idRenderModelCuttableDynamic *,10> models;
|
||||
idStaticList<idEffectPhysicsCuttable::piece_t,8> pieces;
|
||||
idEffectPhysicsBroadPhase broadPhase;
|
||||
float pieceCollisionAgeDecay;
|
||||
float pieceFriction;
|
||||
int pieceMaxLifeTime;
|
||||
int pieceLifeTime;
|
||||
int pieceFadeTime;
|
||||
int pieceMass;
|
||||
idSoundWorld *soundWorld;
|
||||
static constexpr int MAX_PIECES = 8;
|
||||
static constexpr int MAX_BUFFERS = 10;
|
||||
|
||||
struct model_t {
|
||||
int index;
|
||||
int timeStamp;
|
||||
};
|
||||
|
||||
struct piece_t {
|
||||
idEffectPhysicsProperties* properties;
|
||||
idEffectPhysicsRigidBody* rigidBody;
|
||||
const idDeclImpactSound* soundTable;
|
||||
idEffectPhysicsCollision collision;
|
||||
idMat3 orientation;
|
||||
idVec3 position;
|
||||
int timeStamp;
|
||||
int firstCollisionTime;
|
||||
int renderModelIndex;
|
||||
int nextSoundTime;
|
||||
float minBounceVelocitySqr;
|
||||
};
|
||||
|
||||
idEffectPhysicsCuttable();
|
||||
~idEffectPhysicsCuttable();
|
||||
|
||||
void Init(idRenderWorld* renderWorld, idSoundWorld* soundWorld,
|
||||
idClip* clip, idTraceModelCache* traceModelCache);
|
||||
int Emit(const cuttablePhysicsEmitInfo_t& emitInfo,
|
||||
const idList<idDrawVert, 5>& vertices,
|
||||
const idList<idVec2, 5>& textureCoordinates,
|
||||
const idList<unsigned short, 5>& indices);
|
||||
void UpdateSimulation(const idVec3& origin, const idMat3& axis,
|
||||
int currentTime, int gameMsPerFrame);
|
||||
void UpdateModel(const idVec3& scale, int currentTime);
|
||||
void SubmitCollisionQueries();
|
||||
void DrawCollisionModels(int single);
|
||||
|
||||
idStaticList<model_t, MAX_BUFFERS> freeModels;
|
||||
idStaticList<idRenderModelCuttableDynamic*, MAX_BUFFERS> models;
|
||||
idStaticList<piece_t, MAX_PIECES> pieces;
|
||||
idEffectPhysicsBroadPhase broadPhase;
|
||||
float pieceCollisionAgeDecay;
|
||||
float pieceFriction;
|
||||
int pieceMaxLifeTime;
|
||||
int pieceLifeTime;
|
||||
int pieceFadeTime;
|
||||
int pieceMass;
|
||||
idSoundWorld* soundWorld;
|
||||
|
||||
private:
|
||||
void ReleasePiece(piece_t& piece, int currentTime);
|
||||
};
|
||||
|
||||
static_assert(sizeof(cuttablePhysicsEmitInfo_t) == 100,
|
||||
"Recovered cuttablePhysicsEmitInfo_t ABI changed");
|
||||
static_assert(sizeof(idEffectPhysicsCuttable::model_t) == 8,
|
||||
"Recovered cuttable model entry ABI changed");
|
||||
static_assert(sizeof(idEffectPhysicsCuttable::piece_t) == 108,
|
||||
"Recovered cuttable piece ABI changed");
|
||||
#if defined(_WIN32) && !defined(_WIN64)
|
||||
static_assert(sizeof(idEffectPhysicsCuttable) == 1132,
|
||||
"Recovered idEffectPhysicsCuttable ABI changed");
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
#include "gamelib/effectphysics/effectphysicsdebrisemitter.h"
|
||||
|
||||
#include "gamelib/physics/clip.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
idRenderModelDiscreteAnimation* GameLib_CreateDebrisRenderModel(
|
||||
const idDeclBreakable* breakableDecl);
|
||||
|
||||
idEffectPhysicsDebrisEmitter::idEffectPhysicsDebrisEmitter()
|
||||
: piecePhysics()
|
||||
, emitBuffer{}
|
||||
, emitBufferPos(0)
|
||||
, serializeEmitCount(0)
|
||||
, skipFirstFrame(false)
|
||||
, active(false)
|
||||
, random(0) {
|
||||
}
|
||||
|
||||
void idEffectPhysicsDebrisEmitter::Init(
|
||||
const idDeclBreakable* const breakableDebris, const int entityNumber,
|
||||
idClip& clip, idRenderModelEffects* const renderModelEffects) {
|
||||
active = false;
|
||||
if (breakableDebris == nullptr || clip.traceModelCache == nullptr) {
|
||||
return;
|
||||
}
|
||||
idRenderModelDiscreteAnimation* const model =
|
||||
GameLib_CreateDebrisRenderModel(breakableDebris);
|
||||
if (model == nullptr) {
|
||||
return;
|
||||
}
|
||||
piecePhysics.Init(&clip, clip.traceModelCache, model,
|
||||
renderModelEffects, nullptr, entityNumber);
|
||||
piecePhysics.pieceLifeTime = 2500;
|
||||
piecePhysics.pieceFriction = 15.0f;
|
||||
const idVec3 unitScale(1.0f, 1.0f, 1.0f);
|
||||
piecePhysics.UpdateModel(unitScale);
|
||||
emitBufferPos = 0;
|
||||
serializeEmitCount = 0;
|
||||
skipFirstFrame = true;
|
||||
active = true;
|
||||
random.SetSeed(0);
|
||||
}
|
||||
|
||||
void idEffectPhysicsDebrisEmitter::EmitBuffered(
|
||||
const emitBufferItem_t& item, const idVec3& velocity,
|
||||
const int currentTime) {
|
||||
idVec3 normal = item.normal;
|
||||
if (normal.NormalizeFast() == 0.0f) {
|
||||
normal.Set(0.0f, 0.0f, 1.0f);
|
||||
}
|
||||
for (int index = 0; index < item.count; ++index) {
|
||||
idVec3 randomOffset(random.CRandomFloat() * 4.0f,
|
||||
random.CRandomFloat() * 4.0f,
|
||||
random.CRandomFloat() * 4.0f);
|
||||
// Keep the recovered disk-shaped spawn scatter perpendicular to the
|
||||
// impact normal rather than spraying pieces into the surface.
|
||||
randomOffset = randomOffset - normal * randomOffset.Dot(normal);
|
||||
const idVec3 position = item.pos + randomOffset;
|
||||
const float upwardSpeed = 2000.0f + random.RandomFloat() * 1250.0f;
|
||||
const idVec3 impulse = velocity + normal * upwardSpeed +
|
||||
idVec3(random.CRandomFloat(), random.CRandomFloat(),
|
||||
random.CRandomFloat()) * 40.0f;
|
||||
piecePhysics.EmitPiece(currentTime, position, idMat3(1.0f),
|
||||
item.pos, impulse);
|
||||
}
|
||||
}
|
||||
|
||||
void idEffectPhysicsDebrisEmitter::EmitDebris(const idVec3 position,
|
||||
const idVec3 velocity, const idVec3 normal, int count,
|
||||
const int currentTime) {
|
||||
if (!active) {
|
||||
return;
|
||||
}
|
||||
count = (std::max)(0, (std::min)(15, count));
|
||||
emitBufferItem_t& item = emitBuffer[emitBufferPos];
|
||||
item.pos = position;
|
||||
item.normal = normal;
|
||||
item.count = count;
|
||||
emitBufferPos = (emitBufferPos + 1) % 10;
|
||||
EmitBuffered(item, velocity, currentTime);
|
||||
}
|
||||
|
||||
void idEffectPhysicsDebrisEmitter::Update(const idVec3& origin,
|
||||
const idMat3& axis, const idVec3& drawScale3D,
|
||||
const int currentTime, const int gameMsPerFrame) {
|
||||
while (serializeEmitCount > 0) {
|
||||
const int first = (emitBufferPos - serializeEmitCount + 10) % 10;
|
||||
EmitBuffered(emitBuffer[first], idVec3(0.0f, 0.0f, 0.0f),
|
||||
currentTime);
|
||||
--serializeEmitCount;
|
||||
}
|
||||
if (skipFirstFrame) {
|
||||
skipFirstFrame = false;
|
||||
}
|
||||
if (piecePhysics.numPiecesInUse > 0) {
|
||||
piecePhysics.UpdateSimulation(origin, axis,
|
||||
currentTime, gameMsPerFrame);
|
||||
piecePhysics.UpdateModel(drawScale3D);
|
||||
piecePhysics.SubmitCollisionQueries(false);
|
||||
}
|
||||
}
|
||||
@@ -1,28 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
// Reconstructed C++ declarations from IDA Local Types and PDB/DIA metadata.
|
||||
// Original PDB header: w:\tech5\engine\gamelib\effectphysics\effectphysicsdebrisemitter.h
|
||||
// Recovered logical types: 2
|
||||
// Signatures retain Xbox 360 ABI evidence and may still require manual review.
|
||||
#include "gamelib/effectphysics/effectphysicspieceemitter.h"
|
||||
|
||||
class idDeclBreakable;
|
||||
|
||||
// IDA Local Type ordinal 14594; PDB kind: struct.
|
||||
struct idEffectPhysicsDebrisEmitter::emitBufferItem_t
|
||||
{
|
||||
idVec3 pos;
|
||||
idVec3 normal;
|
||||
int count;
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 14595; PDB kind: class.
|
||||
class idEffectPhysicsDebrisEmitter
|
||||
{
|
||||
class idEffectPhysicsDebrisEmitter {
|
||||
public:
|
||||
idEffectPhysicsPieceEmitter piecePhysics;
|
||||
idEffectPhysicsDebrisEmitter::emitBufferItem_t emitBuffer[10];
|
||||
int emitBufferPos;
|
||||
int serializeEmitCount;
|
||||
bool skipFirstFrame;
|
||||
bool active;
|
||||
idRandom2 random;
|
||||
struct emitBufferItem_t {
|
||||
idVec3 pos;
|
||||
idVec3 normal;
|
||||
int count;
|
||||
};
|
||||
|
||||
idEffectPhysicsDebrisEmitter();
|
||||
|
||||
void Init(const idDeclBreakable* breakableDebris, int entityNumber,
|
||||
idClip& clip, idRenderModelEffects* renderModelEffects);
|
||||
void EmitDebris(idVec3 position, idVec3 velocity, idVec3 normal,
|
||||
int count, int currentTime);
|
||||
void Update(const idVec3& origin, const idMat3& axis,
|
||||
const idVec3& drawScale3D, int currentTime, int gameMsPerFrame);
|
||||
|
||||
idEffectPhysicsPieceEmitter piecePhysics;
|
||||
emitBufferItem_t emitBuffer[10];
|
||||
int emitBufferPos;
|
||||
int serializeEmitCount;
|
||||
bool skipFirstFrame;
|
||||
bool active;
|
||||
idRandom2 random;
|
||||
|
||||
private:
|
||||
void EmitBuffered(const emitBufferItem_t& item,
|
||||
const idVec3& velocity, int currentTime);
|
||||
};
|
||||
|
||||
static_assert(sizeof(idEffectPhysicsDebrisEmitter::emitBufferItem_t) == 28,
|
||||
"Recovered debris emit-buffer ABI changed");
|
||||
#if defined(_WIN32) && !defined(_WIN64)
|
||||
static_assert(sizeof(idEffectPhysicsDebrisEmitter) == 464,
|
||||
"Recovered idEffectPhysicsDebrisEmitter ABI changed");
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,359 @@
|
||||
#include "gamelib/effectphysics/effectphysicspieceemitter.h"
|
||||
|
||||
#include "gamelib/physics/tracemodelcache.h"
|
||||
#include "idlib/geometry/tracemodel.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
// These operations cross from GameLib into the renderer/decl/sound modules.
|
||||
// Their recovered implementations live with those owning subsystems; GameLib
|
||||
// consumes them without inventing the very large render-model layouts here.
|
||||
int GameLib_DiscreteAnimationPieceCount(
|
||||
const idRenderModelDiscreteAnimation* model);
|
||||
const idDeclBreakable* GameLib_DiscreteAnimationDecl(
|
||||
const idRenderModelDiscreteAnimation* model);
|
||||
bool GameLib_DiscreteAnimationGetPiece(
|
||||
const idRenderModelDiscreteAnimation* model, int index,
|
||||
idVec3& position, idMat3& orientation, idTraceModel& traceModel,
|
||||
const idMaterial*& material);
|
||||
void GameLib_DiscreteAnimationSetPiece(idRenderModelDiscreteAnimation* model,
|
||||
int index, const idVec3& position, const idMat3& orientation);
|
||||
void GameLib_DiscreteAnimationCollapsePiece(
|
||||
idRenderModelDiscreteAnimation* model, int index);
|
||||
void GameLib_DiscreteAnimationReset(idRenderModelDiscreteAnimation* model);
|
||||
void GameLib_DiscreteAnimationUpdate(idRenderModelDiscreteAnimation* model);
|
||||
void GameLib_DiscreteAnimationSetHidden(
|
||||
idRenderModelDiscreteAnimation* model, bool hidden);
|
||||
void GameLib_DiscreteAnimationDelete(idRenderModelDiscreteAnimation* model);
|
||||
bool GameLib_GetBreakablePhysicsSettings(const idDeclBreakable* decl,
|
||||
idEffectPhysicsPieceSettings& settings);
|
||||
void GameLib_DrawEffectTraceModel(const idTraceModelCache* cache,
|
||||
int traceModelIndex, int pieceIndex, const idVec3& origin,
|
||||
const idMat3& axis);
|
||||
void GameLib_PlayEffectPhysicsCollision(
|
||||
idEffectPhysicsPieceEmitter::pieceEmitterSound_t& soundInfo,
|
||||
idRenderModelEffects* effects, const idEffectPhysicsCollision& collision,
|
||||
const idVec3& origin, float speedSqr, float volumeDb, int currentTime);
|
||||
|
||||
idEffectPhysicsPieceEmitter::idEffectPhysicsPieceEmitter()
|
||||
: properties(0)
|
||||
, pieces(0)
|
||||
, broadPhase()
|
||||
, random(0)
|
||||
, pieceMass(10.0f)
|
||||
, pieceCollisionAgeDecay(0.0f)
|
||||
, pieceCollisionVolumeDecay(0.0f)
|
||||
, model(nullptr)
|
||||
, decl(nullptr)
|
||||
, modelEffects(nullptr)
|
||||
, soundInfo{nullptr, nullptr, 0, 0.0f}
|
||||
, pieceLifeTime(0)
|
||||
, pieceFriction(0.0f)
|
||||
, pieceIndex(0)
|
||||
, numPiecesInUse(0)
|
||||
, hidden(false)
|
||||
, hiddenOverride(false) {
|
||||
}
|
||||
|
||||
idEffectPhysicsPieceEmitter::~idEffectPhysicsPieceEmitter() {
|
||||
if (broadPhase.traceModelCache != nullptr) {
|
||||
for (int index = 0; index < properties.Num(); ++index) {
|
||||
if (properties[index] != nullptr) {
|
||||
broadPhase.traceModelCache->FreeTraceModel(
|
||||
properties[index]->traceModelIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int index = 0; index < pieces.Num(); ++index) {
|
||||
delete pieces[index].rigidBody;
|
||||
pieces[index].rigidBody = nullptr;
|
||||
}
|
||||
for (int index = 0; index < properties.Num(); ++index) {
|
||||
delete properties[index];
|
||||
}
|
||||
pieces.ClearFree();
|
||||
properties.ClearFree();
|
||||
if (model != nullptr) {
|
||||
GameLib_DiscreteAnimationDelete(model);
|
||||
model = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void idEffectPhysicsPieceEmitter::ApplyDeclSettings(
|
||||
idEffectPhysicsProperties& target) const {
|
||||
idEffectPhysicsPieceSettings settings{};
|
||||
if (decl == nullptr ||
|
||||
!GameLib_GetBreakablePhysicsSettings(decl, settings)) {
|
||||
return;
|
||||
}
|
||||
target.linearFriction = settings.linearFriction;
|
||||
target.angularFriction = settings.angularFriction;
|
||||
target.contactFriction = settings.contactFriction;
|
||||
target.linearFrictionWater = settings.linearFrictionWater;
|
||||
target.angularFrictionWater = settings.angularFrictionWater;
|
||||
target.bouncyness = settings.bouncyness;
|
||||
target.gravityVector = settings.gravityVector;
|
||||
target.worldCollisionOnly = settings.worldCollisionOnly;
|
||||
target.simplePointCollision = settings.simplePointCollision;
|
||||
target.crazyBounceChance = settings.crazyBounceChance;
|
||||
target.stopSpeed = settings.stopSpeed;
|
||||
target.maxLinearVelocity = settings.maxLinearVelocity;
|
||||
target.maxAngularVelocity = settings.maxAngularVelocity;
|
||||
target.clipMask = settings.clipMask;
|
||||
}
|
||||
|
||||
idEffectPhysicsProperties*
|
||||
idEffectPhysicsPieceEmitter::GetPropertiesForTraceModel(
|
||||
const idTraceModel& traceModel, const idMaterial* const material) {
|
||||
if (broadPhase.traceModelCache == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
const int traceModelIndex = broadPhase.traceModelCache->AllocTraceModel(
|
||||
traceModel, material);
|
||||
for (int index = 0; index < properties.Num(); ++index) {
|
||||
if (properties[index]->traceModelIndex == traceModelIndex) {
|
||||
// AllocTraceModel returned another reference to an existing entry.
|
||||
broadPhase.traceModelCache->FreeTraceModel(traceModelIndex);
|
||||
return properties[index];
|
||||
}
|
||||
}
|
||||
|
||||
idEffectPhysicsProperties* const result =
|
||||
new idEffectPhysicsProperties();
|
||||
result->Setup(&broadPhase, traceModelIndex, pieceMass);
|
||||
ApplyDeclSettings(*result);
|
||||
properties.Append(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
void idEffectPhysicsPieceEmitter::Init(idClip* const clip,
|
||||
idTraceModelCache* const traceModelCache,
|
||||
idRenderModelDiscreteAnimation* const renderModel,
|
||||
idRenderModelEffects* const renderModelEffects,
|
||||
const pieceEmitterSound_t* const sound, const int entityNumber) {
|
||||
broadPhase.Init(clip, traceModelCache);
|
||||
model = renderModel;
|
||||
modelEffects = renderModelEffects;
|
||||
decl = renderModel != nullptr
|
||||
? GameLib_DiscreteAnimationDecl(renderModel) : nullptr;
|
||||
if (sound != nullptr) {
|
||||
soundInfo = *sound;
|
||||
}
|
||||
pieceIndex = 0;
|
||||
numPiecesInUse = 0;
|
||||
|
||||
for (int index = 0; index < pieces.Num(); ++index) {
|
||||
delete pieces[index].rigidBody;
|
||||
}
|
||||
pieces.Clear();
|
||||
const int pieceCount = renderModel != nullptr
|
||||
? GameLib_DiscreteAnimationPieceCount(renderModel) : 0;
|
||||
pieces.SetNum((std::max)(0, pieceCount));
|
||||
|
||||
for (int index = 0; index < pieces.Num(); ++index) {
|
||||
idVec3 position;
|
||||
idMat3 orientation(1.0f);
|
||||
idTraceModel traceModel;
|
||||
const idMaterial* material = nullptr;
|
||||
idBreakablePiece& piece = pieces[index];
|
||||
piece.rigidBody = nullptr;
|
||||
piece.collision.Clear();
|
||||
piece.emitTime = 0;
|
||||
piece.firstCollisionTime = 0;
|
||||
if (!GameLib_DiscreteAnimationGetPiece(renderModel, index,
|
||||
position, orientation, traceModel, material)) {
|
||||
continue;
|
||||
}
|
||||
traceModel.Shrink(0.1875f);
|
||||
idEffectPhysicsProperties* const pieceProperties =
|
||||
GetPropertiesForTraceModel(traceModel, material);
|
||||
if (pieceProperties == nullptr) {
|
||||
continue;
|
||||
}
|
||||
piece.rigidBody = new idEffectPhysicsRigidBody();
|
||||
piece.rigidBody->Setup(pieceProperties);
|
||||
piece.rigidBody->SetPosition(position, orientation);
|
||||
piece.rigidBody->SPObject.entityNum = entityNumber;
|
||||
}
|
||||
}
|
||||
|
||||
void idEffectPhysicsPieceEmitter::Reset() {
|
||||
random.SetSeed(0);
|
||||
pieceIndex = 0;
|
||||
numPiecesInUse = 0;
|
||||
if (model != nullptr) {
|
||||
GameLib_DiscreteAnimationReset(model);
|
||||
}
|
||||
for (int index = 0; index < pieces.Num(); ++index) {
|
||||
idBreakablePiece& piece = pieces[index];
|
||||
piece.emitTime = 0;
|
||||
piece.firstCollisionTime = 0;
|
||||
piece.collision.Clear();
|
||||
if (piece.rigidBody == nullptr) {
|
||||
continue;
|
||||
}
|
||||
idVec3 position;
|
||||
idMat3 orientation(1.0f);
|
||||
idTraceModel ignoredTraceModel;
|
||||
const idMaterial* ignoredMaterial = nullptr;
|
||||
if (model != nullptr && GameLib_DiscreteAnimationGetPiece(model,
|
||||
index, position, orientation, ignoredTraceModel,
|
||||
ignoredMaterial)) {
|
||||
piece.rigidBody->SetPosition(position, orientation);
|
||||
}
|
||||
piece.rigidBody->active = false;
|
||||
piece.rigidBody->settled = false;
|
||||
piece.rigidBody->currentState.linearMomentum.Zero();
|
||||
piece.rigidBody->currentState.angularMomentum.Zero();
|
||||
piece.rigidBody->SPObject.motion.ignore = false;
|
||||
}
|
||||
for (int index = 0; index < properties.Num(); ++index) {
|
||||
ApplyDeclSettings(*properties[index]);
|
||||
}
|
||||
hiddenOverride = true;
|
||||
if (model != nullptr) {
|
||||
GameLib_DiscreteAnimationSetHidden(model, true);
|
||||
}
|
||||
}
|
||||
|
||||
void idEffectPhysicsPieceEmitter::EmitPiece(const int emitTime,
|
||||
const idVec3& position, const idMat3& orientation,
|
||||
const idVec3& impulsePoint, const idVec3& impulse) {
|
||||
if (pieces.IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
idBreakablePiece& piece = pieces[pieceIndex];
|
||||
if (piece.rigidBody == nullptr) {
|
||||
pieceIndex = (pieceIndex + 1) % pieces.Num();
|
||||
return;
|
||||
}
|
||||
if (!piece.rigidBody->active) {
|
||||
++numPiecesInUse;
|
||||
}
|
||||
hiddenOverride = false;
|
||||
piece.emitTime = emitTime;
|
||||
piece.firstCollisionTime = 0;
|
||||
piece.collision.Clear();
|
||||
piece.rigidBody->SetPosition(position, orientation);
|
||||
piece.rigidBody->currentState.linearMomentum.Zero();
|
||||
piece.rigidBody->currentState.angularMomentum.Zero();
|
||||
piece.rigidBody->ApplyImpulse(position + impulsePoint, impulse);
|
||||
piece.rigidBody->active = true;
|
||||
piece.rigidBody->settled = false;
|
||||
piece.rigidBody->SPObject.motion.ignore = false;
|
||||
pieceIndex = (pieceIndex + 1) % pieces.Num();
|
||||
}
|
||||
|
||||
void idEffectPhysicsPieceEmitter::UpdateSimulation(const idVec3& origin,
|
||||
const idMat3& axis, const int currentTime, const int gameMsPerFrame) {
|
||||
(void)origin;
|
||||
(void)axis;
|
||||
const float deltaTime = (std::max)(0, gameMsPerFrame) * 0.001f;
|
||||
for (int index = 0; index < pieces.Num(); ++index) {
|
||||
idBreakablePiece& piece = pieces[index];
|
||||
idEffectPhysicsRigidBody* const body = piece.rigidBody;
|
||||
if (body == nullptr || !body->active) {
|
||||
continue;
|
||||
}
|
||||
if (pieceLifeTime > 0 && currentTime >= piece.emitTime + pieceLifeTime) {
|
||||
body->active = false;
|
||||
body->SPObject.motion.ignore = true;
|
||||
numPiecesInUse = (std::max)(0, numPiecesInUse - 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
float momentumDecay = 0.0f;
|
||||
if (piece.firstCollisionTime > 0 && pieceCollisionAgeDecay > 0.0f &&
|
||||
pieceLifeTime > 0) {
|
||||
momentumDecay = (std::min)(1.0f,
|
||||
(currentTime - piece.firstCollisionTime) *
|
||||
pieceCollisionAgeDecay /
|
||||
static_cast<float>(pieceLifeTime));
|
||||
}
|
||||
body->Update(deltaTime, momentumDecay, piece.collision);
|
||||
if (piece.collision.collisionNormalVelocity.LengthSqr() > 0.0f) {
|
||||
if (piece.firstCollisionTime == 0) {
|
||||
piece.firstCollisionTime = currentTime;
|
||||
}
|
||||
const idVec3 velocity = body->currentState.linearMomentum *
|
||||
body->properties->inverseMass;
|
||||
const float volumeDb = pieceCollisionVolumeDecay > 0.0f
|
||||
? -(currentTime - piece.firstCollisionTime) *
|
||||
pieceCollisionVolumeDecay * 0.001f
|
||||
: 0.0f;
|
||||
GameLib_PlayEffectPhysicsCollision(soundInfo, modelEffects,
|
||||
piece.collision, body->currentState.position,
|
||||
velocity.LengthSqr(), volumeDb, currentTime);
|
||||
}
|
||||
if (pieceFriction > 0.0f) {
|
||||
const float damping = (std::max)(0.0f,
|
||||
1.0f - pieceFriction * deltaTime);
|
||||
body->currentState.linearMomentum =
|
||||
body->currentState.linearMomentum * damping;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void idEffectPhysicsPieceEmitter::UpdateModel(
|
||||
const idVec3& activeDrawScale3D) {
|
||||
if (model == nullptr) {
|
||||
return;
|
||||
}
|
||||
const idMat3 scale(activeDrawScale3D.x, 0.0f, 0.0f,
|
||||
0.0f, activeDrawScale3D.y, 0.0f,
|
||||
0.0f, 0.0f, activeDrawScale3D.z);
|
||||
for (int index = 0; index < pieces.Num(); ++index) {
|
||||
const idBreakablePiece& piece = pieces[index];
|
||||
if (piece.rigidBody != nullptr && piece.rigidBody->active) {
|
||||
GameLib_DiscreteAnimationSetPiece(model, index,
|
||||
piece.rigidBody->currentState.position,
|
||||
piece.rigidBody->currentState.orientation * scale);
|
||||
} else {
|
||||
GameLib_DiscreteAnimationCollapsePiece(model, index);
|
||||
}
|
||||
}
|
||||
if (!hidden && !hiddenOverride) {
|
||||
GameLib_DiscreteAnimationUpdate(model);
|
||||
}
|
||||
}
|
||||
|
||||
void idEffectPhysicsPieceEmitter::SubmitCollisionQueries(
|
||||
const bool worldCollisionModelOnly) {
|
||||
broadPhase.SubmitQueries(worldCollisionModelOnly);
|
||||
}
|
||||
|
||||
void idEffectPhysicsPieceEmitter::DrawCollisionModels(const int single,
|
||||
const idVec3& offset, const idMat3& axis) {
|
||||
for (int index = 0; index < pieces.Num(); ++index) {
|
||||
if (single >= 0 && single != index) {
|
||||
continue;
|
||||
}
|
||||
const idBreakablePiece& piece = pieces[index];
|
||||
if (piece.rigidBody == nullptr || piece.rigidBody->properties == nullptr) {
|
||||
continue;
|
||||
}
|
||||
idVec3 drawOrigin = piece.rigidBody->currentState.position;
|
||||
idMat3 drawAxis = piece.rigidBody->currentState.orientation;
|
||||
if (!piece.rigidBody->active) {
|
||||
drawOrigin = offset + axis * drawOrigin;
|
||||
drawAxis *= axis;
|
||||
}
|
||||
GameLib_DrawEffectTraceModel(broadPhase.traceModelCache,
|
||||
piece.rigidBody->properties->traceModelIndex, index,
|
||||
drawOrigin, drawAxis);
|
||||
}
|
||||
}
|
||||
|
||||
void idEffectPhysicsPieceEmitter::Hide() {
|
||||
hidden = true;
|
||||
if (model != nullptr) {
|
||||
GameLib_DiscreteAnimationSetHidden(model, true);
|
||||
}
|
||||
}
|
||||
|
||||
void idEffectPhysicsPieceEmitter::Show() {
|
||||
hidden = false;
|
||||
if (model != nullptr && !hiddenOverride) {
|
||||
GameLib_DiscreteAnimationSetHidden(model, false);
|
||||
}
|
||||
}
|
||||
@@ -1,49 +1,102 @@
|
||||
#pragma once
|
||||
|
||||
// Reconstructed C++ declarations from IDA Local Types and PDB/DIA metadata.
|
||||
// Original PDB header: w:\tech5\engine\gamelib\effectphysics\effectphysicspieceemitter.h
|
||||
// Recovered logical types: 3
|
||||
// Signatures retain Xbox 360 ABI evidence and may still require manual review.
|
||||
#include "gamelib/effectphysics/effectphysicsrigidbody.h"
|
||||
#include "idlib/containers/list.h"
|
||||
#include "idlib/math/random.h"
|
||||
|
||||
class idClip;
|
||||
class idDeclBreakable;
|
||||
class idDeclImpactSound;
|
||||
class idMaterial;
|
||||
class idRenderModelDiscreteAnimation;
|
||||
class idRenderModelEffects;
|
||||
class idSoundWorld;
|
||||
class idTraceModel;
|
||||
class idTraceModelCache;
|
||||
|
||||
// IDA Local Type ordinal 14580; PDB kind: class.
|
||||
class idEffectPhysicsPieceEmitter::idBreakablePiece
|
||||
{
|
||||
// PC-side copy of the physics fields read from idDeclBreakable. The engine
|
||||
// declaration adapter fills it, keeping GameLib independent of the unfinished
|
||||
// declaration/render-model class hierarchy.
|
||||
struct idEffectPhysicsPieceSettings {
|
||||
float linearFriction;
|
||||
float angularFriction;
|
||||
float contactFriction;
|
||||
float linearFrictionWater;
|
||||
float angularFrictionWater;
|
||||
float bouncyness;
|
||||
idVec3 gravityVector;
|
||||
bool worldCollisionOnly;
|
||||
bool simplePointCollision;
|
||||
float crazyBounceChance;
|
||||
float stopSpeed;
|
||||
float maxLinearVelocity;
|
||||
float maxAngularVelocity;
|
||||
int clipMask;
|
||||
};
|
||||
|
||||
class alignas(4) idEffectPhysicsPieceEmitter {
|
||||
public:
|
||||
idEffectPhysicsRigidBody *rigidBody;
|
||||
idEffectPhysicsCollision collision;
|
||||
int emitTime;
|
||||
int firstCollisionTime;
|
||||
struct idBreakablePiece {
|
||||
idEffectPhysicsRigidBody* rigidBody;
|
||||
idEffectPhysicsCollision collision;
|
||||
int emitTime;
|
||||
int firstCollisionTime;
|
||||
};
|
||||
|
||||
struct pieceEmitterSound_t {
|
||||
idSoundWorld* soundWorld;
|
||||
const idDeclImpactSound* impactTable;
|
||||
int nextSoundTime;
|
||||
float minBounceVelSqr;
|
||||
};
|
||||
|
||||
idEffectPhysicsPieceEmitter();
|
||||
~idEffectPhysicsPieceEmitter();
|
||||
|
||||
void Init(idClip* clip, idTraceModelCache* traceModelCache,
|
||||
idRenderModelDiscreteAnimation* renderModel,
|
||||
idRenderModelEffects* renderModelEffects,
|
||||
const pieceEmitterSound_t* sound, int entityNumber);
|
||||
void Reset();
|
||||
void EmitPiece(int emitTime, const idVec3& position,
|
||||
const idMat3& orientation, const idVec3& impulsePoint,
|
||||
const idVec3& impulse);
|
||||
void UpdateSimulation(const idVec3& origin, const idMat3& axis,
|
||||
int currentTime, int gameMsPerFrame);
|
||||
void UpdateModel(const idVec3& activeDrawScale3D);
|
||||
void SubmitCollisionQueries(bool worldCollisionModelOnly);
|
||||
void DrawCollisionModels(int single, const idVec3& offset,
|
||||
const idMat3& axis);
|
||||
void Hide();
|
||||
void Show();
|
||||
|
||||
idList<idEffectPhysicsProperties*, 59> properties;
|
||||
idList<idBreakablePiece, 59> pieces;
|
||||
idEffectPhysicsBroadPhase broadPhase;
|
||||
idRandom random;
|
||||
float pieceMass;
|
||||
float pieceCollisionAgeDecay;
|
||||
float pieceCollisionVolumeDecay;
|
||||
idRenderModelDiscreteAnimation* model;
|
||||
const idDeclBreakable* decl;
|
||||
idRenderModelEffects* modelEffects;
|
||||
pieceEmitterSound_t soundInfo;
|
||||
int pieceLifeTime;
|
||||
float pieceFriction;
|
||||
int pieceIndex;
|
||||
int numPiecesInUse;
|
||||
bool hidden;
|
||||
bool hiddenOverride;
|
||||
|
||||
private:
|
||||
idEffectPhysicsProperties* GetPropertiesForTraceModel(
|
||||
const idTraceModel& traceModel, const idMaterial* material);
|
||||
void ApplyDeclSettings(idEffectPhysicsProperties& target) const;
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 14592; PDB kind: struct.
|
||||
struct idEffectPhysicsPieceEmitter::pieceEmitterSound_t
|
||||
{
|
||||
idSoundWorld *soundWorld;
|
||||
const idDeclImpactSound *impactTable;
|
||||
int nextSoundTime;
|
||||
float minBounceVelSqr;
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 14593; PDB kind: class.
|
||||
class __declspec(align(4)) idEffectPhysicsPieceEmitter
|
||||
{
|
||||
public:
|
||||
idList<idEffectPhysicsProperties *,59> properties;
|
||||
idList<idEffectPhysicsPieceEmitter::idBreakablePiece,59> pieces;
|
||||
idEffectPhysicsBroadPhase broadPhase;
|
||||
idRandom random;
|
||||
float pieceMass;
|
||||
float pieceCollisionAgeDecay;
|
||||
float pieceCollisionVolumeDecay;
|
||||
idRenderModelDiscreteAnimation *model;
|
||||
const idDeclBreakable *decl;
|
||||
idRenderModelEffects *modelEffects;
|
||||
idEffectPhysicsPieceEmitter::pieceEmitterSound_t soundInfo;
|
||||
int pieceLifeTime;
|
||||
float pieceFriction;
|
||||
int pieceIndex;
|
||||
int numPiecesInUse;
|
||||
bool hidden;
|
||||
bool hiddenOverride;
|
||||
};
|
||||
static_assert(sizeof(idEffectPhysicsPieceEmitter::idBreakablePiece) == 40,
|
||||
"Recovered idBreakablePiece ABI changed");
|
||||
#if defined(_WIN32) && !defined(_WIN64)
|
||||
static_assert(sizeof(idEffectPhysicsPieceEmitter) == 168,
|
||||
"Recovered idEffectPhysicsPieceEmitter ABI changed");
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
#include "gamelib/effectphysics/effectphysicsproperties.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
idEffectPhysicsProperties::idEffectPhysicsProperties()
|
||||
: broadPhase(nullptr)
|
||||
, traceModelIndex(-1)
|
||||
, mass(1.0f) {
|
||||
}
|
||||
|
||||
void idEffectPhysicsProperties::Setup(
|
||||
idEffectPhysicsBroadPhase *broadPhase_, const int traceModelIndex_,
|
||||
const float requestedMass) {
|
||||
linearFriction = 0.60000002f;
|
||||
angularFriction = 0.60000002f;
|
||||
contactFriction = 0.5f;
|
||||
linearFrictionWater = 1.0f;
|
||||
angularFrictionWater = 1.0f;
|
||||
bouncyness = 0.60000002f;
|
||||
clipMask = 1;
|
||||
gravityVector.Set(0.0f, 0.0f, -250.0f);
|
||||
worldCollisionOnly = false;
|
||||
simplePointCollision = false;
|
||||
crazyBounceChance = 0.0f;
|
||||
inverseMass = 1.0f;
|
||||
centerOfMass.Zero();
|
||||
inertiaTensor = idMat3(1.0f);
|
||||
inverseInertiaTensor = idMat3(1.0f);
|
||||
maxLinearVelocity = 5000.0f;
|
||||
stopSpeed = 10.0f;
|
||||
maxAngularVelocity = 3.1415927f * 4.0f;
|
||||
traceModelIndex = traceModelIndex_;
|
||||
broadPhase = broadPhase_;
|
||||
|
||||
mass = 0.0f;
|
||||
if (broadPhase != nullptr && broadPhase->traceModelCache != nullptr) {
|
||||
broadPhase->traceModelCache->GetMassProperties(traceModelIndex, 1.0f,
|
||||
mass, centerOfMass, inertiaTensor);
|
||||
}
|
||||
if (mass <= 0.0f) {
|
||||
mass = 1.0f;
|
||||
centerOfMass.Zero();
|
||||
inertiaTensor = idMat3(1.0f);
|
||||
}
|
||||
|
||||
const float massScale = requestedMass / mass;
|
||||
for (int row = 0; row < 3; ++row) {
|
||||
for (int column = 0; column < 3; ++column) {
|
||||
inertiaTensor[row][column] *= massScale;
|
||||
}
|
||||
}
|
||||
mass = requestedMass;
|
||||
|
||||
const float diagonal[3] = {
|
||||
inertiaTensor[0][0], inertiaTensor[1][1], inertiaTensor[2][2]
|
||||
};
|
||||
int minimumAxis = 0;
|
||||
if (diagonal[1] < diagonal[minimumAxis]) {
|
||||
minimumAxis = 1;
|
||||
}
|
||||
if (diagonal[2] < diagonal[minimumAxis]) {
|
||||
minimumAxis = 2;
|
||||
}
|
||||
|
||||
if (diagonal[minimumAxis] > 0.0f) {
|
||||
idMat3 inertiaScale(1.0f);
|
||||
for (int axis = 0; axis < 3; ++axis) {
|
||||
inertiaScale[axis][axis] = diagonal[axis]
|
||||
/ diagonal[minimumAxis];
|
||||
}
|
||||
if (inertiaScale[0][0] > 4.0f || inertiaScale[1][1] > 4.0f
|
||||
|| inertiaScale[2][2] > 4.0f) {
|
||||
for (int axis = 0; axis < 3; ++axis) {
|
||||
if (axis != minimumAxis && diagonal[axis] != 0.0f) {
|
||||
inertiaScale[axis][axis] = diagonal[minimumAxis] * 4.0f
|
||||
/ diagonal[axis];
|
||||
}
|
||||
}
|
||||
inertiaTensor *= inertiaScale;
|
||||
}
|
||||
}
|
||||
|
||||
inverseMass = mass != 0.0f ? 1.0f / mass : 1.0f;
|
||||
inverseInertiaTensor = inertiaTensor;
|
||||
if (!inverseInertiaTensor.InverseSelf()) {
|
||||
inverseInertiaTensor = idMat3(1.0f);
|
||||
}
|
||||
for (int row = 0; row < 3; ++row) {
|
||||
for (int column = 0; column < 3; ++column) {
|
||||
inverseInertiaTensor[row][column] *= 1.0f / 6.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "gamelib/effectphysics/effectphysicsbroadphase.h"
|
||||
#include "idlib/math/vector.h"
|
||||
|
||||
// Reconstructed C++ declarations from IDA Local Types and PDB/DIA metadata.
|
||||
// Original PDB header: w:\tech5\engine\gamelib\effectphysics\effectphysicsproperties.h
|
||||
// Recovered logical types: 1
|
||||
@@ -10,6 +13,10 @@
|
||||
class idEffectPhysicsProperties
|
||||
{
|
||||
public:
|
||||
idEffectPhysicsProperties();
|
||||
void Setup(idEffectPhysicsBroadPhase *broadPhase, int traceModelIndex,
|
||||
float mass);
|
||||
|
||||
idEffectPhysicsBroadPhase *broadPhase;
|
||||
int traceModelIndex;
|
||||
int clipMask;
|
||||
@@ -32,3 +39,8 @@ public:
|
||||
float maxLinearVelocity;
|
||||
float maxAngularVelocity;
|
||||
};
|
||||
|
||||
#if defined(_WIN32) && !defined(_WIN64)
|
||||
static_assert(sizeof(idEffectPhysicsProperties) == 160,
|
||||
"Recovered idEffectPhysicsProperties ABI changed");
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,262 @@
|
||||
#include "gamelib/effectphysics/effectphysicsrigidbody.h"
|
||||
|
||||
#include "cm/jobs/collisionresults.h"
|
||||
#include "gamelib/physics/physics.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr float kRadToDeg = 57.29577951308232f;
|
||||
constexpr float kVelocityEpsilon = 1.0e-8f;
|
||||
|
||||
idMat3 WorldInverseInertia(const idMat3& orientation,
|
||||
const idMat3& inverseInertiaTensor) {
|
||||
return orientation * inverseInertiaTensor * orientation.Transpose();
|
||||
}
|
||||
|
||||
idVec3 PointVelocity(const idEffectPhysicsRigidBody::effectRBState_t& state,
|
||||
const idEffectPhysicsProperties& properties, const idVec3& point) {
|
||||
const idVec3 linearVelocity =
|
||||
state.linearMomentum * properties.inverseMass;
|
||||
const idVec3 angularVelocity =
|
||||
WorldInverseInertia(state.orientation,
|
||||
properties.inverseInertiaTensor) * state.angularMomentum;
|
||||
return linearVelocity + angularVelocity.Cross(point - state.position);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
idEffectPhysicsRigidBody::idEffectPhysicsRigidBody()
|
||||
: properties(nullptr)
|
||||
, currentState{}
|
||||
, SPObject()
|
||||
, active(false)
|
||||
, settled(false) {
|
||||
}
|
||||
|
||||
idEffectPhysicsRigidBody::~idEffectPhysicsRigidBody() {
|
||||
if (properties != nullptr && properties->broadPhase != nullptr) {
|
||||
properties->broadPhase->RemoveObject(&SPObject);
|
||||
}
|
||||
properties = nullptr;
|
||||
active = false;
|
||||
}
|
||||
|
||||
void idEffectPhysicsRigidBody::Setup(
|
||||
const idEffectPhysicsProperties* const props) {
|
||||
if (properties != nullptr && properties->broadPhase != nullptr) {
|
||||
properties->broadPhase->RemoveObject(&SPObject);
|
||||
}
|
||||
|
||||
properties = props;
|
||||
currentState.position.Zero();
|
||||
currentState.orientation = idMat3(1.0f);
|
||||
currentState.linearMomentum.Zero();
|
||||
currentState.angularMomentum.Zero();
|
||||
currentState.externalForce.Zero();
|
||||
currentState.externalTorque.Zero();
|
||||
active = false;
|
||||
settled = false;
|
||||
|
||||
if (props == nullptr) {
|
||||
SPObject.traceModelIndex = -1;
|
||||
return;
|
||||
}
|
||||
SPObject.traceModelIndex = props->traceModelIndex;
|
||||
SPObject.motion.worldCollisionOnly = props->worldCollisionOnly;
|
||||
SPObject.clipMask = props->clipMask;
|
||||
if (props->broadPhase != nullptr) {
|
||||
props->broadPhase->AddObject(&SPObject, currentState.position,
|
||||
currentState.orientation, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void idEffectPhysicsRigidBody::CapVelocity(
|
||||
const float deltaTime, effectRBState_t& current) const {
|
||||
(void)deltaTime;
|
||||
if (properties == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const idVec3 linearVelocity =
|
||||
current.linearMomentum * properties->inverseMass;
|
||||
const float linearSpeed = linearVelocity.Length();
|
||||
if (properties->maxLinearVelocity > 0.0f &&
|
||||
linearSpeed > properties->maxLinearVelocity) {
|
||||
current.linearMomentum = current.linearMomentum *
|
||||
(properties->maxLinearVelocity / linearSpeed);
|
||||
}
|
||||
|
||||
const idVec3 angularVelocity =
|
||||
WorldInverseInertia(current.orientation,
|
||||
properties->inverseInertiaTensor) * current.angularMomentum;
|
||||
const float angularSpeed = angularVelocity.Length();
|
||||
if (properties->maxAngularVelocity > 0.0f &&
|
||||
angularSpeed > properties->maxAngularVelocity) {
|
||||
current.angularMomentum = current.angularMomentum *
|
||||
(properties->maxAngularVelocity / angularSpeed);
|
||||
}
|
||||
}
|
||||
|
||||
void idEffectPhysicsRigidBody::CollisionImpulse(trace_t& collision,
|
||||
const impactInfo_t& impact, const float extraMomentumDecay,
|
||||
effectRBState_t& state, idVec3& normalVelocity, idVec3& impulse) const {
|
||||
normalVelocity.Zero();
|
||||
impulse.Zero();
|
||||
if (properties == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
idVec3 normal = collision.c.normal;
|
||||
if (normal.NormalizeFast() <= kVelocityEpsilon) {
|
||||
return;
|
||||
}
|
||||
|
||||
const idVec3 bodyVelocity =
|
||||
PointVelocity(state, *properties, collision.c.point);
|
||||
const idVec3 relativeVelocity = bodyVelocity - impact.velocity;
|
||||
const float velocityIntoSurface = relativeVelocity.Dot(normal);
|
||||
normalVelocity = normal * velocityIntoSurface;
|
||||
if (velocityIntoSurface >= 0.0f) {
|
||||
return;
|
||||
}
|
||||
|
||||
const idVec3 arm = collision.c.point - state.position;
|
||||
const idVec3 cross = arm.Cross(normal);
|
||||
const idVec3 inertiaCross =
|
||||
WorldInverseInertia(state.orientation,
|
||||
properties->inverseInertiaTensor) * cross;
|
||||
const float denominator = properties->inverseMass + impact.invMass +
|
||||
normal.Dot(inertiaCross.Cross(arm));
|
||||
if (denominator <= kVelocityEpsilon) {
|
||||
return;
|
||||
}
|
||||
|
||||
const float restitution = (std::max)(0.0f, properties->bouncyness);
|
||||
float magnitude = -(1.0f + restitution) * velocityIntoSurface /
|
||||
denominator;
|
||||
magnitude *= (std::max)(0.0f, 1.0f - extraMomentumDecay);
|
||||
impulse = normal * magnitude;
|
||||
|
||||
// The recovered path applies Coulomb contact friction after the normal
|
||||
// impulse. Preserve that behavior without the original VMX shortcuts.
|
||||
idVec3 tangent = relativeVelocity - normal * velocityIntoSurface;
|
||||
const float tangentSpeed = tangent.NormalizeFast();
|
||||
if (tangentSpeed > kVelocityEpsilon && properties->contactFriction > 0.0f) {
|
||||
const float frictionMagnitude = (std::min)(
|
||||
tangentSpeed / (properties->inverseMass + impact.invMass +
|
||||
kVelocityEpsilon),
|
||||
magnitude * properties->contactFriction);
|
||||
impulse = impulse - tangent * frictionMagnitude;
|
||||
}
|
||||
|
||||
state.linearMomentum = state.linearMomentum + impulse;
|
||||
state.angularMomentum = state.angularMomentum + arm.Cross(impulse);
|
||||
}
|
||||
|
||||
void idEffectPhysicsRigidBody::ApplyImpulse(const idVec3& point,
|
||||
const idVec3& impulse) {
|
||||
settled = false;
|
||||
currentState.linearMomentum = currentState.linearMomentum + impulse;
|
||||
// The Xbox dump spells this as impulse x (point-position), which is the
|
||||
// negative of the conventional r x J ordering.
|
||||
currentState.angularMomentum = currentState.angularMomentum +
|
||||
impulse.Cross(point - currentState.position);
|
||||
}
|
||||
|
||||
void idEffectPhysicsRigidBody::Evolve(const float deltaTime,
|
||||
const effectRBState_t& current, effectRBDelta_t& delta) const {
|
||||
delta.translation.Zero();
|
||||
delta.rotation = idRotation();
|
||||
delta.linearForce.Zero();
|
||||
delta.angularForce.Zero();
|
||||
if (properties == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const idVec3 linearVelocity =
|
||||
current.linearMomentum * properties->inverseMass;
|
||||
const idVec3 angularVelocity =
|
||||
WorldInverseInertia(current.orientation,
|
||||
properties->inverseInertiaTensor) * current.angularMomentum;
|
||||
|
||||
delta.translation = linearVelocity * deltaTime;
|
||||
idVec3 rotationAxis = angularVelocity;
|
||||
const float angularSpeed = rotationAxis.NormalizeFast();
|
||||
if (angularSpeed <= kVelocityEpsilon) {
|
||||
rotationAxis.Set(0.0f, 0.0f, 1.0f);
|
||||
}
|
||||
delta.rotation = idRotation(current.position, rotationAxis,
|
||||
-angularSpeed * deltaTime * kRadToDeg);
|
||||
|
||||
const idVec3 totalForce = current.externalForce +
|
||||
properties->gravityVector * properties->mass -
|
||||
linearVelocity * properties->linearFriction;
|
||||
const idVec3 totalTorque = current.externalTorque -
|
||||
angularVelocity * properties->angularFriction;
|
||||
delta.linearForce = totalForce * deltaTime;
|
||||
delta.angularForce = totalTorque * deltaTime;
|
||||
}
|
||||
|
||||
void idEffectPhysicsRigidBody::Update(const float deltaTime,
|
||||
const float extraMomentumDecay, idEffectPhysicsCollision& collision) {
|
||||
collision.Clear();
|
||||
if (!active || settled || properties == nullptr ||
|
||||
properties->broadPhase == nullptr || deltaTime <= 0.0f) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool testForSettling = false;
|
||||
trace_t trace{};
|
||||
if (SPObject.query.offset != 0 &&
|
||||
properties->broadPhase->GetMotionResult(&trace, &SPObject)) {
|
||||
currentState.position = SPObject.origin;
|
||||
currentState.orientation = SPObject.axis;
|
||||
if (trace.fraction < 1.0f) {
|
||||
impactInfo_t impact{};
|
||||
impact.invInertiaTensor = idMat3(1.0f);
|
||||
idVec3 impulse;
|
||||
CollisionImpulse(trace, impact, extraMomentumDecay,
|
||||
currentState, collision.collisionNormalVelocity, impulse);
|
||||
collision.collisionPoint = trace.c.point;
|
||||
collision.collisionSurfaceType = trace.c.surfaceType;
|
||||
|
||||
const float horizontalSpeed = std::sqrt(
|
||||
currentState.linearMomentum.x * currentState.linearMomentum.x +
|
||||
currentState.linearMomentum.y * currentState.linearMomentum.y);
|
||||
if (horizontalSpeed <= 10.0f &&
|
||||
currentState.linearMomentum.z < 0.0f) {
|
||||
testForSettling = horizontalSpeed >= 2.0f;
|
||||
settled = horizontalSpeed < 2.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CapVelocity(deltaTime, currentState);
|
||||
effectRBDelta_t delta;
|
||||
Evolve(deltaTime, currentState, delta);
|
||||
properties->broadPhase->ApplyMotion(
|
||||
&SPObject, delta.translation, delta.rotation);
|
||||
|
||||
if (testForSettling &&
|
||||
delta.translation.x * delta.translation.x +
|
||||
delta.translation.y * delta.translation.y < 1.0e-4f) {
|
||||
settled = true;
|
||||
}
|
||||
|
||||
if (settled) {
|
||||
SPObject.motion.valid = false;
|
||||
currentState.linearMomentum.Zero();
|
||||
currentState.angularMomentum.Zero();
|
||||
collision.Clear();
|
||||
} else {
|
||||
currentState.linearMomentum =
|
||||
currentState.linearMomentum + delta.linearForce;
|
||||
currentState.angularMomentum =
|
||||
currentState.angularMomentum + delta.angularForce;
|
||||
}
|
||||
currentState.externalForce.Zero();
|
||||
currentState.externalTorque.Zero();
|
||||
}
|
||||
@@ -1,38 +1,96 @@
|
||||
#pragma once
|
||||
|
||||
// Reconstructed C++ declarations from IDA Local Types and PDB/DIA metadata.
|
||||
// Original PDB header: w:\tech5\engine\gamelib\effectphysics\effectphysicsrigidbody.h
|
||||
// Recovered logical types: 3
|
||||
// Signatures retain Xbox 360 ABI evidence and may still require manual review.
|
||||
#include "gamelib/effectphysics/effectphysicsproperties.h"
|
||||
#include "idlib/math/rotation.h"
|
||||
|
||||
struct impactInfo_t;
|
||||
struct trace_t;
|
||||
|
||||
// IDA Local Type ordinal 14577; PDB kind: struct.
|
||||
struct idEffectPhysicsRigidBody::effectRBState_t
|
||||
{
|
||||
idVec3 position;
|
||||
idMat3 orientation;
|
||||
idVec3 linearMomentum;
|
||||
idVec3 angularMomentum;
|
||||
idVec3 externalForce;
|
||||
idVec3 externalTorque;
|
||||
// Recovered type 14579. This record is intentionally independent of the
|
||||
// renderer/game declaration layer so effect physics can be used by emitters.
|
||||
struct idEffectPhysicsCollision {
|
||||
idVec3 collisionPoint;
|
||||
idVec3 collisionNormalVelocity;
|
||||
int collisionSurfaceType;
|
||||
|
||||
void Clear() {
|
||||
collisionPoint.Zero();
|
||||
collisionNormalVelocity.Zero();
|
||||
collisionSurfaceType = 0;
|
||||
}
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 14578; PDB kind: class.
|
||||
class __declspec(align(8)) idEffectPhysicsRigidBody
|
||||
{
|
||||
class alignas(8) idEffectPhysicsRigidBody {
|
||||
public:
|
||||
const idEffectPhysicsProperties *properties;
|
||||
idEffectPhysicsRigidBody::effectRBState_t currentState;
|
||||
idSPObject SPObject;
|
||||
bool active;
|
||||
bool settled;
|
||||
struct effectRBState_t {
|
||||
idVec3 position;
|
||||
idMat3 orientation;
|
||||
idVec3 linearMomentum;
|
||||
idVec3 angularMomentum;
|
||||
idVec3 externalForce;
|
||||
idVec3 externalTorque;
|
||||
};
|
||||
|
||||
struct effectRBDelta_t {
|
||||
idVec3 translation;
|
||||
idRotation rotation;
|
||||
idVec3 linearForce;
|
||||
idVec3 angularForce;
|
||||
};
|
||||
|
||||
idEffectPhysicsRigidBody();
|
||||
~idEffectPhysicsRigidBody();
|
||||
|
||||
void Setup(const idEffectPhysicsProperties* props);
|
||||
void ApplyImpulse(const idVec3& point, const idVec3& impulse);
|
||||
void Update(float deltaTime, float extraMomentumDecay,
|
||||
idEffectPhysicsCollision& collision);
|
||||
|
||||
void SetPosition(const idVec3& position, const idMat3& orientation) {
|
||||
currentState.position = position;
|
||||
currentState.orientation = orientation;
|
||||
if (properties != nullptr && properties->broadPhase != nullptr) {
|
||||
properties->broadPhase->SetObjectPosition(
|
||||
&SPObject, position, orientation, nullptr);
|
||||
}
|
||||
}
|
||||
void SetLinearMomentum(const idVec3& momentum) {
|
||||
currentState.linearMomentum = momentum;
|
||||
}
|
||||
void SetAngularMomentum(const idVec3& momentum) {
|
||||
currentState.angularMomentum = momentum;
|
||||
}
|
||||
void AddForce(const idVec3& force) {
|
||||
currentState.externalForce = currentState.externalForce + force;
|
||||
}
|
||||
void AddTorque(const idVec3& torque) {
|
||||
currentState.externalTorque = currentState.externalTorque + torque;
|
||||
}
|
||||
void Activate() { active = true; settled = false; }
|
||||
void PutToRest() { settled = true; SPObject.motion.valid = false; }
|
||||
|
||||
const idEffectPhysicsProperties* properties;
|
||||
effectRBState_t currentState;
|
||||
idSPObject SPObject;
|
||||
bool active;
|
||||
bool settled;
|
||||
|
||||
private:
|
||||
void CapVelocity(float deltaTime, effectRBState_t& current) const;
|
||||
void CollisionImpulse(trace_t& collision, const impactInfo_t& impact,
|
||||
float extraMomentumDecay, effectRBState_t& state,
|
||||
idVec3& normalVelocity, idVec3& impulse) const;
|
||||
void Evolve(float deltaTime, const effectRBState_t& current,
|
||||
effectRBDelta_t& delta) const;
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 21719; PDB kind: struct.
|
||||
struct idEffectPhysicsRigidBody::effectRBDelta_t
|
||||
{
|
||||
idVec3 translation;
|
||||
idRotation rotation;
|
||||
idVec3 linearForce;
|
||||
idVec3 angularForce;
|
||||
};
|
||||
static_assert(sizeof(idEffectPhysicsCollision) == 28,
|
||||
"Recovered idEffectPhysicsCollision ABI changed");
|
||||
static_assert(sizeof(idEffectPhysicsRigidBody::effectRBState_t) == 96,
|
||||
"Recovered effectRBState_t ABI changed");
|
||||
static_assert(sizeof(idEffectPhysicsRigidBody::effectRBDelta_t) == 104,
|
||||
"Recovered effectRBDelta_t ABI changed");
|
||||
#if defined(_WIN32) && !defined(_WIN64)
|
||||
static_assert(sizeof(idEffectPhysicsRigidBody) == 272,
|
||||
"Recovered idEffectPhysicsRigidBody ABI changed");
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
#include "gamelib/effects/deferreddecalmanager.h"
|
||||
|
||||
#include "gamelib/physics/physics.h"
|
||||
#include "idlib/lib_print.h"
|
||||
|
||||
idClipQuery GameLib_SubmitDeferredDecalLineTrace(idClip* clip,
|
||||
const idVec3& start, const idVec3& end, int clipMask);
|
||||
bool GameLib_GetDeferredDecalLineTraceResult(idClip* clip,
|
||||
idClipQuery& query, trace_t& result);
|
||||
void GameLib_AddDeferredDecalFromPoint(idRenderModelDecal* decalModel,
|
||||
const deferredDecalParams_t& parameters, int time,
|
||||
const idVec3& origin, const idVec3& normal);
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr std::uint64_t RECOVERED_EMPTY_CLIP_QUERY = 0x000007D000000000ULL;
|
||||
|
||||
} // namespace
|
||||
|
||||
idDeferredDecalQuery::idDeferredDecalQuery() {
|
||||
Reset();
|
||||
}
|
||||
|
||||
void idDeferredDecalQuery::Reset() {
|
||||
theQuery.index = RECOVERED_EMPTY_CLIP_QUERY;
|
||||
decalParams.decalMat = nullptr;
|
||||
decalParams.depth = 8.0f;
|
||||
decalParams.size.Set(32.0f, 32.0f);
|
||||
decalParams.angle = 0.0f;
|
||||
decalParams.lifetime = 10000;
|
||||
decalParams.fadetime = 2000;
|
||||
decalParams.fadeInEndTime = 0;
|
||||
origin.Zero();
|
||||
normal.Zero();
|
||||
}
|
||||
|
||||
idDeferredDecalManager::idDeferredDecalManager()
|
||||
: lineQueries{}
|
||||
, numLineQueries(0)
|
||||
, clip(nullptr)
|
||||
, decalModel(nullptr) {
|
||||
}
|
||||
|
||||
void idDeferredDecalManager::Init(idClip* const clip_,
|
||||
idRenderModelDecal* const decalModel_) {
|
||||
clip = clip_;
|
||||
decalModel = decalModel_;
|
||||
}
|
||||
|
||||
void idDeferredDecalManager::Shutdown() {
|
||||
numLineQueries = 0;
|
||||
clip = nullptr;
|
||||
decalModel = nullptr;
|
||||
for (idDeferredDecalQuery& query : lineQueries) {
|
||||
query.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
void idDeferredDecalManager::AddDecalFromLineTrace(
|
||||
const deferredDecalParams_t* const decalParameters,
|
||||
const idVec3& start, const idVec3& end, const int clipMask) {
|
||||
if (clip == nullptr || decalModel == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (numLineQueries >= 16) {
|
||||
idLibPrint::Warning("Too many deferred decal queries this frame!");
|
||||
return;
|
||||
}
|
||||
if (decalParameters == nullptr || decalParameters->decalMat == nullptr) {
|
||||
idLibPrint::Warning(
|
||||
"Null decal passed in to "
|
||||
"idDeferredDecalManager::AddDecalFromLineTrace");
|
||||
return;
|
||||
}
|
||||
|
||||
idDeferredDecalQuery& query = lineQueries[numLineQueries];
|
||||
query.Reset();
|
||||
query.theQuery = GameLib_SubmitDeferredDecalLineTrace(
|
||||
clip, start, end, clipMask);
|
||||
query.decalParams = *decalParameters;
|
||||
++numLineQueries;
|
||||
}
|
||||
|
||||
void idDeferredDecalManager::AddDecalFromPoint(
|
||||
const deferredDecalParams_t* const decalParameters, const int time,
|
||||
const idVec3& origin, const idVec3& normal) {
|
||||
if (clip == nullptr || decalModel == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (decalParameters == nullptr || decalParameters->decalMat == nullptr) {
|
||||
idLibPrint::Warning(
|
||||
"Null decal passed in to "
|
||||
"idDeferredDecalManager::AddDecalFromLineTrace");
|
||||
return;
|
||||
}
|
||||
|
||||
GameLib_AddDeferredDecalFromPoint(
|
||||
decalModel, *decalParameters, time, origin, normal);
|
||||
}
|
||||
|
||||
void idDeferredDecalManager::Update(const int time) {
|
||||
if (clip == nullptr || decalModel == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int index = 0; index < numLineQueries; ++index) {
|
||||
idDeferredDecalQuery& query = lineQueries[index];
|
||||
trace_t result{};
|
||||
if (GameLib_GetDeferredDecalLineTraceResult(
|
||||
clip, query.theQuery, result) && result.fraction < 1.0f) {
|
||||
AddDecalFromPoint(&query.decalParams, time,
|
||||
result.endpos, result.c.normal);
|
||||
}
|
||||
query.Reset();
|
||||
}
|
||||
numLineQueries = 0;
|
||||
}
|
||||
@@ -1,58 +1,65 @@
|
||||
#pragma once
|
||||
|
||||
// Reconstructed C++ declarations from IDA Local Types and PDB/DIA metadata.
|
||||
// Original PDB header: w:\tech5\engine\gamelib\effects\deferreddecalmanager.h
|
||||
// Recovered logical types: 5
|
||||
// Signatures retain Xbox 360 ABI evidence and may still require manual review.
|
||||
#include "cm/jobs/collisionquery.h"
|
||||
#include "idlib/math/vector.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
// IDA Local Type ordinal 14039; PDB kind: struct.
|
||||
struct deferredDecalParams_t
|
||||
{
|
||||
const idMaterial *decalMat;
|
||||
float depth;
|
||||
idVec2 size;
|
||||
float angle;
|
||||
int lifetime;
|
||||
int fadetime;
|
||||
int fadeInEndTime;
|
||||
class idClip;
|
||||
class idMaterial;
|
||||
class idRenderModelDecal;
|
||||
|
||||
#ifndef ID_CLIP_QUERY_DEFINED
|
||||
#define ID_CLIP_QUERY_DEFINED
|
||||
struct idClipQuery { std::uint64_t index; };
|
||||
#endif
|
||||
|
||||
struct deferredDecalParams_t {
|
||||
const idMaterial* decalMat;
|
||||
float depth;
|
||||
idVec2 size;
|
||||
float angle;
|
||||
int lifetime;
|
||||
int fadetime;
|
||||
int fadeInEndTime;
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 14040; PDB kind: class.
|
||||
class idDeferredDecalQuery
|
||||
{
|
||||
class idDeferredDecalQuery {
|
||||
public:
|
||||
idClipQuery theQuery;
|
||||
deferredDecalParams_t decalParams;
|
||||
idVec3 origin;
|
||||
idVec3 normal;
|
||||
idDeferredDecalQuery();
|
||||
void Reset();
|
||||
|
||||
idClipQuery theQuery;
|
||||
deferredDecalParams_t decalParams;
|
||||
idVec3 origin;
|
||||
idVec3 normal;
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 14135; PDB kind: class.
|
||||
class __declspec(align(8)) idDeferredDecalManager
|
||||
{
|
||||
class alignas(8) idDeferredDecalManager {
|
||||
public:
|
||||
idDeferredDecalQuery lineQueries[16];
|
||||
int numLineQueries;
|
||||
idClip *clip;
|
||||
idRenderModelDecal *decalModel;
|
||||
idDeferredDecalManager();
|
||||
|
||||
void Init(idClip* clip_, idRenderModelDecal* decalModel_);
|
||||
void Shutdown();
|
||||
void AddDecalFromLineTrace(const deferredDecalParams_t* decalParameters,
|
||||
const idVec3& start, const idVec3& end, int clipMask);
|
||||
void AddDecalFromPoint(const deferredDecalParams_t* decalParameters,
|
||||
int time, const idVec3& origin, const idVec3& normal);
|
||||
void Update(int time);
|
||||
|
||||
idDeferredDecalQuery lineQueries[16];
|
||||
int numLineQueries;
|
||||
idClip* clip;
|
||||
idRenderModelDecal* decalModel;
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 15595; PDB kind: struct.
|
||||
struct __declspec(align(8)) idDeferredFireManager::deferredFire_t
|
||||
{
|
||||
idFireParms fireParms;
|
||||
idClipQuery queryHandles[16];
|
||||
idTestFireResults fireResults;
|
||||
unsigned int handle;
|
||||
int numTraces;
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 15596; PDB kind: class.
|
||||
class idDeferredFireManager
|
||||
{
|
||||
public:
|
||||
idDeferredFireManager::deferredFire_t deferredFireBuffers[2][32];
|
||||
unsigned int shotCount;
|
||||
unsigned int activeBuffer;
|
||||
};
|
||||
#if defined(_WIN32) && !defined(_WIN64)
|
||||
static_assert(sizeof(idClipQuery) == 8,
|
||||
"Recovered idClipQuery ABI changed");
|
||||
static_assert(sizeof(deferredDecalParams_t) == 32,
|
||||
"Recovered deferred-decal parameters ABI changed");
|
||||
static_assert(sizeof(idDeferredDecalQuery) == 64,
|
||||
"Recovered deferred-decal query ABI changed");
|
||||
static_assert(sizeof(idDeferredDecalManager) == 1040,
|
||||
"Recovered idDeferredDecalManager ABI changed");
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
#include "gamelib/effects/effectsmodelmanager.h"
|
||||
|
||||
#include "idlib/lib_print.h"
|
||||
#include "idlib/text/str.h"
|
||||
|
||||
// Renderer/decl access remains behind explicit PC-port boundaries until the
|
||||
// renderer and decl translation units join the production source target.
|
||||
const char* GameLib_GetParticleDeclName(const idDeclParticle* particleDecl);
|
||||
idRenderModelParticle* GameLib_AllocParticleRenderModel(
|
||||
const char* modelName, const idDeclParticle* particleDecl);
|
||||
void GameLib_RetireParticleRenderModel(idRenderModelParticle* model);
|
||||
bool GameLib_ShowEffectsModelRecyclerStats();
|
||||
|
||||
namespace {
|
||||
|
||||
int ParticleNameHash(const char* const name) {
|
||||
int hash = 0;
|
||||
if (name != nullptr) {
|
||||
for (const unsigned char* cursor =
|
||||
reinterpret_cast<const unsigned char*>(name);
|
||||
*cursor != 0; ++cursor) {
|
||||
hash = 31 * hash + *cursor;
|
||||
}
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
void PrintRecyclerStats(const char* const action, const char* const name,
|
||||
const idEffectsModelManager& manager) {
|
||||
if (!GameLib_ShowEffectsModelRecyclerStats()) {
|
||||
return;
|
||||
}
|
||||
idLibPrint::Printf(
|
||||
"%s model %s, total models allocated %d inactive models spread %d "
|
||||
"active models spread %d\n",
|
||||
action, name, manager.fxPrtModels.Num(),
|
||||
manager.inactivePrtModelHash.GetSpread(),
|
||||
manager.activePrtModelHash.GetSpread());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
idEffectsModelManager::idEffectsModelManager()
|
||||
: fxPrtModels()
|
||||
, activePrtModelHash(1024, 1024, TAG_HASHINDEX)
|
||||
, inactivePrtModelHash(1024, 1024, TAG_HASHINDEX) {
|
||||
}
|
||||
|
||||
idEffectsModelManager::~idEffectsModelManager() {
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
void idEffectsModelManager::Init() {
|
||||
fxPrtModels.Clear();
|
||||
activePrtModelHash.Clear();
|
||||
activePrtModelHash.memTag = TAG_PARTICLE;
|
||||
inactivePrtModelHash.Clear();
|
||||
inactivePrtModelHash.memTag = TAG_PARTICLE;
|
||||
}
|
||||
|
||||
void idEffectsModelManager::Shutdown() {
|
||||
activePrtModelHash.Clear();
|
||||
inactivePrtModelHash.Clear();
|
||||
for (int index = 0; index < fxPrtModels.Num(); ++index) {
|
||||
fxPrtModel_t& item = fxPrtModels[index];
|
||||
if (item.pmodel != nullptr) {
|
||||
GameLib_RetireParticleRenderModel(item.pmodel);
|
||||
item.pmodel = nullptr;
|
||||
}
|
||||
}
|
||||
fxPrtModels.Clear();
|
||||
}
|
||||
|
||||
void idEffectsModelManager::RecycleParticleFxModel(
|
||||
const idDeclParticle* const particleDecl,
|
||||
idRenderModelParticle* const model) {
|
||||
if (model == nullptr || particleDecl == nullptr) {
|
||||
idLibPrint::Warning(
|
||||
"idFXModelRecycler::RecycleEffectModel : invalid model");
|
||||
return;
|
||||
}
|
||||
|
||||
const char* const name = GameLib_GetParticleDeclName(particleDecl);
|
||||
const int key = ParticleNameHash(name);
|
||||
int index = activePrtModelHash.First(key);
|
||||
while (index != -1) {
|
||||
const fxPrtModel_t& item = fxPrtModels[index];
|
||||
if (item.pDecl == particleDecl && item.pmodel == model) {
|
||||
break;
|
||||
}
|
||||
index = activePrtModelHash.Next(index);
|
||||
}
|
||||
|
||||
if (index == -1) {
|
||||
if (GameLib_ShowEffectsModelRecyclerStats()) {
|
||||
idLibPrint::Printf(
|
||||
"idEffectsModelManager::RecycleParticleFxModel : model %s "
|
||||
"not found in active list for recycling\n", name);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
activePrtModelHash.Remove(key, index);
|
||||
inactivePrtModelHash.Add(key, index);
|
||||
PrintRecyclerStats("Recycling", name, *this);
|
||||
}
|
||||
|
||||
idRenderModelParticle* idEffectsModelManager::GetNextParticleEffectModel(
|
||||
const idDeclParticle* const particleDecl) {
|
||||
if (particleDecl == nullptr) {
|
||||
idLibPrint::Warning(
|
||||
"idEffectsModelManager::GetNextParticleEffectModel : "
|
||||
"particleDecl NULL");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const char* const name = GameLib_GetParticleDeclName(particleDecl);
|
||||
const int key = ParticleNameHash(name);
|
||||
int index = inactivePrtModelHash.First(key);
|
||||
while (index != -1) {
|
||||
const fxPrtModel_t& item = fxPrtModels[index];
|
||||
if (item.pDecl == particleDecl && item.pmodel != nullptr) {
|
||||
inactivePrtModelHash.Remove(key, index);
|
||||
activePrtModelHash.Add(key, index);
|
||||
PrintRecyclerStats("Found inactive", name, *this);
|
||||
return item.pmodel;
|
||||
}
|
||||
index = inactivePrtModelHash.Next(index);
|
||||
}
|
||||
|
||||
if (fxPrtModels.Num() >= fxPrtModels.Max()) {
|
||||
idLibPrint::Warning(
|
||||
"idFXModelRecycler::GetParticleFxModel: Could not allocate "
|
||||
"model %s, out of slots", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
idStr modelName(name);
|
||||
modelName.SetFileExtension("prt");
|
||||
idRenderModelParticle* const model = GameLib_AllocParticleRenderModel(
|
||||
modelName.c_str(), particleDecl);
|
||||
if (model == nullptr) {
|
||||
idLibPrint::Warning(
|
||||
"idFXModelRecycler::GetParticleFxModel: Could not allocate a "
|
||||
"rendermodel for '%s'", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const fxPrtModel_t item = {model, particleDecl};
|
||||
index = fxPrtModels.Append(item);
|
||||
activePrtModelHash.Add(key, index);
|
||||
PrintRecyclerStats("Allocated new", name, *this);
|
||||
return model;
|
||||
}
|
||||
@@ -1,23 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
// Reconstructed C++ declarations from IDA Local Types and PDB/DIA metadata.
|
||||
// Original PDB header: w:\tech5\engine\gamelib\effects\effectsmodelmanager.h
|
||||
// Recovered logical types: 2
|
||||
// Signatures retain Xbox 360 ABI evidence and may still require manual review.
|
||||
#include "idlib/containers/hashindex.h"
|
||||
#include "idlib/containers/staticlist.h"
|
||||
|
||||
class idDeclParticle;
|
||||
class idRenderModelParticle;
|
||||
|
||||
// IDA Local Type ordinal 14034; PDB kind: struct.
|
||||
struct idEffectsModelManager::fxPrtModel_t
|
||||
{
|
||||
idRenderModelParticle *pmodel;
|
||||
const idDeclParticle *pDecl;
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 14037; PDB kind: class.
|
||||
class idEffectsModelManager
|
||||
{
|
||||
class idEffectsModelManager {
|
||||
public:
|
||||
idStaticList<idEffectsModelManager::fxPrtModel_t,400> fxPrtModels;
|
||||
idHashIndex activePrtModelHash;
|
||||
idHashIndex inactivePrtModelHash;
|
||||
struct fxPrtModel_t {
|
||||
idRenderModelParticle* pmodel;
|
||||
const idDeclParticle* pDecl;
|
||||
};
|
||||
|
||||
idEffectsModelManager();
|
||||
~idEffectsModelManager();
|
||||
|
||||
void Init();
|
||||
void Shutdown();
|
||||
idRenderModelParticle* GetNextParticleEffectModel(
|
||||
const idDeclParticle* particleDecl);
|
||||
void RecycleParticleFxModel(const idDeclParticle* particleDecl,
|
||||
idRenderModelParticle* model);
|
||||
|
||||
idStaticList<fxPrtModel_t, 400> fxPrtModels;
|
||||
idHashIndex activePrtModelHash;
|
||||
idHashIndex inactivePrtModelHash;
|
||||
};
|
||||
|
||||
#if defined(_WIN32) && !defined(_WIN64)
|
||||
static_assert(sizeof(idEffectsModelManager::fxPrtModel_t) == 8,
|
||||
"Recovered particle-model record ABI changed");
|
||||
static_assert(sizeof(idEffectsModelManager) == 3280,
|
||||
"Recovered idEffectsModelManager ABI changed");
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,268 @@
|
||||
#include "gamelib/effects/ribbon.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
bool GameLib_GetRibbonParameters(const idDeclRibbon* decl,
|
||||
idRibbonParameters& parameters);
|
||||
void GameLib_UpdateRibbonGeometry(idRenderModelBeam* beamModel,
|
||||
const idDeclRibbon* decl, const idRibbon::ribbonNode_t* nodes,
|
||||
int firstNode, int numNodes, int currentTime,
|
||||
const idMat3& viewAxis, const idVec3& viewOrigin,
|
||||
bool explicitBothEdges, const idVec4& globalColor);
|
||||
|
||||
namespace {
|
||||
|
||||
idVec4 MultiplyColor(const idVec4& left, const idVec4& right) {
|
||||
return idVec4(left.x * right.x, left.y * right.y,
|
||||
left.z * right.z, left.w * right.w);
|
||||
}
|
||||
|
||||
idVec3 Lerp(const idVec3& from, const idVec3& to, const float fraction) {
|
||||
return from + (to - from) * fraction;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
idRibbon::idRibbon(const idDeclRibbon* const decl)
|
||||
: ribbonState(RIBBON_INACTIVE)
|
||||
, spawnOrg()
|
||||
, spawnTime(0)
|
||||
, numActiveNodes(0)
|
||||
, currHeadIdx(-1)
|
||||
, startVariance(0.0f)
|
||||
, prevTime(-1)
|
||||
, prevOrigin()
|
||||
, prevOrigin2()
|
||||
, ribbonDecl(decl)
|
||||
, globalColor(1.0f, 1.0f, 1.0f, 1.0f)
|
||||
, ribbonNodes{}
|
||||
, explicitDefineBothEdges(false) {
|
||||
ResetNodes();
|
||||
// The recovered constructor advances the same LCG once for start phase.
|
||||
static unsigned int ribbonSeed = 0;
|
||||
ribbonSeed = 1664525u * ribbonSeed + 1013904223u;
|
||||
startVariance = ((ribbonSeed >> 10) & 0x7FFFu) *
|
||||
(1.0f / 32768.0f);
|
||||
}
|
||||
|
||||
void idRibbon::ResetNodes() {
|
||||
prevTime = -1;
|
||||
currHeadIdx = -1;
|
||||
ribbonState = RIBBON_INACTIVE;
|
||||
spawnTime = 0;
|
||||
numActiveNodes = 0;
|
||||
explicitDefineBothEdges = false;
|
||||
for (ribbonNode_t& node : ribbonNodes) {
|
||||
node.origin.Zero();
|
||||
node.origin2.Zero();
|
||||
node.velocity.Zero();
|
||||
node.orientVec.Zero();
|
||||
node.color = idVec4(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
node.spawnTime = 0;
|
||||
node.nodeLength = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
void idRibbon::StartRibbon(const int time, const idVec3& origin) {
|
||||
ResetNodes();
|
||||
ribbonState = RIBBON_ACTIVE;
|
||||
spawnOrg = origin;
|
||||
prevOrigin = origin;
|
||||
prevOrigin2 = origin;
|
||||
spawnTime = time;
|
||||
prevTime = time;
|
||||
}
|
||||
|
||||
void idRibbon::CalcTurbulence(const idVec3& origin, const idMat3& axis,
|
||||
idVec3& turbulence) const {
|
||||
turbulence.Zero();
|
||||
idRibbonParameters parms{};
|
||||
if (!GameLib_GetRibbonParameters(ribbonDecl, parms) ||
|
||||
std::fabs(parms.turbulenceMagnitude) <= 1.1920929e-7f) {
|
||||
return;
|
||||
}
|
||||
const float frequency = parms.turbulenceFrequency;
|
||||
const idVec4& magic = parms.turbulenceMagic;
|
||||
const idVec3 local(
|
||||
std::sin(magic.x * origin.x * frequency) -
|
||||
std::cos(magic.w * origin.y * frequency),
|
||||
std::sin(magic.y * origin.y * frequency) -
|
||||
std::cos(magic.w * origin.x * frequency),
|
||||
std::cos(magic.z * origin.z * frequency) -
|
||||
std::sin(magic.w * origin.z * frequency));
|
||||
turbulence = axis * (local * parms.turbulenceMagnitude);
|
||||
}
|
||||
|
||||
void idRibbon::CalcHelix(const int currentTime, const idVec3& origin,
|
||||
const idMat3& axis, const idVec3& velocity, idVec3& helix) const {
|
||||
(void)origin;
|
||||
helix.Zero();
|
||||
idRibbonParameters parms{};
|
||||
if (!GameLib_GetRibbonParameters(ribbonDecl, parms) ||
|
||||
parms.helixRadius <= 1.1920929e-7f) {
|
||||
return;
|
||||
}
|
||||
const float phase = parms.helixRotationScale *
|
||||
static_cast<float>(currentTime - spawnTime) +
|
||||
startVariance * 6.28318530718f;
|
||||
idVec3 direction = velocity;
|
||||
if (direction.NormalizeFast() == 0.0f) {
|
||||
direction = axis[0];
|
||||
}
|
||||
idVec3 side = direction.Cross(axis[2]);
|
||||
if (side.NormalizeFast() == 0.0f) {
|
||||
side = axis[1];
|
||||
}
|
||||
idVec3 up = direction.Cross(side);
|
||||
up.NormalizeFast();
|
||||
helix = (side * std::cos(phase) + up * std::sin(phase)) *
|
||||
parms.helixRadius;
|
||||
}
|
||||
|
||||
void idRibbon::AddNode(const int currentTime, const idVec3& origin,
|
||||
const idVec3& origin2, const idMat3& axis, const idVec3& velocity,
|
||||
const idVec4& color) {
|
||||
idRibbonParameters parms{};
|
||||
if (!GameLib_GetRibbonParameters(ribbonDecl, parms)) {
|
||||
return;
|
||||
}
|
||||
explicitDefineBothEdges = &origin != &origin2 ||
|
||||
(origin - origin2).LengthSqr() > 0.0f;
|
||||
idVec3 turbulence1, turbulence2, helix1, helix2;
|
||||
CalcTurbulence(origin, axis, turbulence1);
|
||||
CalcHelix(currentTime, origin, axis, velocity, helix1);
|
||||
if (explicitDefineBothEdges) {
|
||||
CalcTurbulence(origin2, axis, turbulence2);
|
||||
CalcHelix(currentTime, origin2, axis, velocity, helix2);
|
||||
} else {
|
||||
turbulence2 = turbulence1;
|
||||
helix2 = helix1;
|
||||
}
|
||||
|
||||
currHeadIdx = (currHeadIdx + 1) % MAX_NODES;
|
||||
ribbonNode_t& node = ribbonNodes[currHeadIdx];
|
||||
node.spawnTime = currentTime;
|
||||
node.origin = origin + turbulence1 + helix1;
|
||||
node.origin2 = origin2 + turbulence2 + helix2;
|
||||
node.velocity = velocity + axis * parms.velocity +
|
||||
turbulence1 * parms.turbulenceVelocity +
|
||||
helix1 * parms.helixVelocity;
|
||||
node.color = MultiplyColor(parms.color, color);
|
||||
node.orientVec = axis[0];
|
||||
if (numActiveNodes > 0) {
|
||||
const ribbonNode_t& previous =
|
||||
ribbonNodes[(currHeadIdx + MAX_NODES - 1) % MAX_NODES];
|
||||
node.nodeLength = (node.origin - previous.origin).Length();
|
||||
} else {
|
||||
node.nodeLength = 0.0f;
|
||||
}
|
||||
if (numActiveNodes < MAX_NODES) {
|
||||
++numActiveNodes;
|
||||
}
|
||||
}
|
||||
|
||||
void idRibbon::UpdatePosition(const int currentTime, const idVec3& origin,
|
||||
const idMat3& axis, const idVec3& velocity, const idVec4& color) {
|
||||
UpdatePosition(currentTime, origin, origin, axis, velocity, color);
|
||||
}
|
||||
|
||||
void idRibbon::UpdatePosition(const int currentTime, const idVec3& origin,
|
||||
const idVec3& origin2, const idMat3& axis, const idVec3& velocity,
|
||||
const idVec4& color) {
|
||||
idRibbonParameters parms{};
|
||||
if (ribbonState != RIBBON_ACTIVE ||
|
||||
!GameLib_GetRibbonParameters(ribbonDecl, parms)) {
|
||||
return;
|
||||
}
|
||||
const float distanceSqr = (prevOrigin - origin).LengthSqr();
|
||||
if (parms.minNodeDistance > 0.0f &&
|
||||
distanceSqr <= parms.minNodeDistance * parms.minNodeDistance) {
|
||||
return;
|
||||
}
|
||||
int subdivisions = 1;
|
||||
if (parms.nodeSubdivision > 0.0f && prevTime >= 0) {
|
||||
subdivisions = (std::min)(MAX_NODES,
|
||||
static_cast<int>(std::sqrt(distanceSqr) /
|
||||
parms.nodeSubdivision) + 1);
|
||||
}
|
||||
for (int index = 1; index <= subdivisions; ++index) {
|
||||
const float fraction = index / static_cast<float>(subdivisions);
|
||||
const int nodeTime = prevTime + static_cast<int>(
|
||||
(currentTime - prevTime) * fraction);
|
||||
AddNode(nodeTime, Lerp(prevOrigin, origin, fraction),
|
||||
Lerp(prevOrigin2, origin2, fraction), axis, velocity, color);
|
||||
}
|
||||
prevTime = currentTime;
|
||||
prevOrigin = origin;
|
||||
prevOrigin2 = origin2;
|
||||
}
|
||||
|
||||
void idRibbon::RemoveOldNodes(const int currentTime) {
|
||||
if (numActiveNodes == 0) {
|
||||
return;
|
||||
}
|
||||
idRibbonParameters parms{};
|
||||
if (!GameLib_GetRibbonParameters(ribbonDecl, parms)) {
|
||||
return;
|
||||
}
|
||||
if (parms.maxLength != 0.0f) {
|
||||
float length = 0.0f;
|
||||
int retained = 0;
|
||||
for (int offset = 0; offset < numActiveNodes; ++offset) {
|
||||
const int index = (currHeadIdx - offset + MAX_NODES) % MAX_NODES;
|
||||
length += ribbonNodes[index].nodeLength;
|
||||
if (length <= parms.maxLength) ++retained;
|
||||
}
|
||||
numActiveNodes = retained;
|
||||
}
|
||||
while (numActiveNodes > 0) {
|
||||
const int oldest =
|
||||
(currHeadIdx - numActiveNodes + 1 + MAX_NODES) % MAX_NODES;
|
||||
if (parms.duration > 0 &&
|
||||
ribbonNodes[oldest].spawnTime + parms.duration < currentTime) {
|
||||
--numActiveNodes;
|
||||
} else if (parms.duration <= 0 &&
|
||||
ribbonState == RIBBON_DEACTIVATING) {
|
||||
--numActiveNodes;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (numActiveNodes <= 0) {
|
||||
numActiveNodes = 0;
|
||||
explicitDefineBothEdges = false;
|
||||
ribbonState = RIBBON_INACTIVE;
|
||||
}
|
||||
}
|
||||
|
||||
int idRibbon::GetRemainingTime(const int currentTime) const {
|
||||
idRibbonParameters parms{};
|
||||
if (!GameLib_GetRibbonParameters(ribbonDecl, parms) ||
|
||||
parms.duration <= 0) {
|
||||
return 0;
|
||||
}
|
||||
int remaining = 0;
|
||||
for (int offset = 0; offset < numActiveNodes; ++offset) {
|
||||
const int index =
|
||||
(currHeadIdx - offset + MAX_NODES) % MAX_NODES;
|
||||
remaining = (std::max)(remaining,
|
||||
ribbonNodes[index].spawnTime + parms.duration - currentTime);
|
||||
}
|
||||
return remaining;
|
||||
}
|
||||
|
||||
void idRibbon::UpdateGeometry(const int currentTime,
|
||||
idRenderModelBeam* const beamModel, const idMat3& viewAxis,
|
||||
const idVec3& viewOrigin) {
|
||||
RemoveOldNodes(currentTime);
|
||||
if (beamModel == nullptr) {
|
||||
return;
|
||||
}
|
||||
const int firstNode = numActiveNodes > 0
|
||||
? (currHeadIdx - numActiveNodes + 1 + MAX_NODES) % MAX_NODES
|
||||
: 0;
|
||||
GameLib_UpdateRibbonGeometry(beamModel, ribbonDecl, ribbonNodes,
|
||||
firstNode, numActiveNodes, currentTime, viewAxis, viewOrigin,
|
||||
explicitDefineBothEdges, globalColor);
|
||||
}
|
||||
@@ -1,38 +1,105 @@
|
||||
#pragma once
|
||||
|
||||
// Reconstructed C++ declarations from IDA Local Types and PDB/DIA metadata.
|
||||
// Original PDB header: w:\tech5\engine\gamelib\effects\ribbon.h
|
||||
// Recovered logical types: 2
|
||||
// Signatures retain Xbox 360 ABI evidence and may still require manual review.
|
||||
#include "idlib/math/vector.h"
|
||||
|
||||
class idDeclRibbon;
|
||||
class idMaterial;
|
||||
class idRenderModelBeam;
|
||||
|
||||
// IDA Local Type ordinal 14136; PDB kind: struct.
|
||||
struct idRibbon::ribbonNode_t
|
||||
{
|
||||
idVec3 origin;
|
||||
idVec3 origin2;
|
||||
idVec3 velocity;
|
||||
idVec3 orientVec;
|
||||
idVec4 color;
|
||||
int spawnTime;
|
||||
float nodeLength;
|
||||
enum ribbonState_t : int {
|
||||
RIBBON_ACTIVE = 0,
|
||||
RIBBON_DEACTIVATING = 1,
|
||||
RIBBON_INACTIVE = 2
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 14137; PDB kind: class.
|
||||
class __declspec(align(4)) idRibbon
|
||||
{
|
||||
struct idRibbonParameters {
|
||||
const idMaterial* material;
|
||||
int duration;
|
||||
float maxLength;
|
||||
float fadeInFraction;
|
||||
float fadeOutFraction;
|
||||
idVec3 gravity;
|
||||
idVec3 velocity;
|
||||
float startWidth;
|
||||
float endWidth;
|
||||
idVec4 color;
|
||||
bool viewOriented;
|
||||
bool reorientNodes;
|
||||
bool textureRepeat;
|
||||
bool textureRepeatStretch;
|
||||
float textureTMin;
|
||||
float textureTMax;
|
||||
float nodeSubdivision;
|
||||
float minNodeDistance;
|
||||
float helixRotationScale;
|
||||
float helixRadius;
|
||||
float helixVelocity;
|
||||
idVec2 helixDistortion0;
|
||||
idVec2 helixDistortion1;
|
||||
float turbulenceFrequency;
|
||||
float turbulenceMagnitude;
|
||||
float turbulenceVelocity;
|
||||
idVec4 turbulenceMagic;
|
||||
};
|
||||
|
||||
class alignas(4) idRibbon {
|
||||
public:
|
||||
ribbonState_t ribbonState;
|
||||
idVec3 spawnOrg;
|
||||
int spawnTime;
|
||||
int numActiveNodes;
|
||||
int currHeadIdx;
|
||||
float startVariance;
|
||||
int prevTime;
|
||||
idVec3 prevOrigin;
|
||||
idVec3 prevOrigin2;
|
||||
const idDeclRibbon *ribbonDecl;
|
||||
idVec4 globalColor;
|
||||
idRibbon::ribbonNode_t ribbonNodes[128];
|
||||
bool explicitDefineBothEdges;
|
||||
static constexpr int MAX_NODES = 128;
|
||||
|
||||
struct ribbonNode_t {
|
||||
idVec3 origin;
|
||||
idVec3 origin2;
|
||||
idVec3 velocity;
|
||||
idVec3 orientVec;
|
||||
idVec4 color;
|
||||
int spawnTime;
|
||||
float nodeLength;
|
||||
};
|
||||
|
||||
explicit idRibbon(const idDeclRibbon* decl);
|
||||
|
||||
void StartRibbon(int time, const idVec3& origin);
|
||||
void StopRibbon() {
|
||||
if (ribbonState == RIBBON_ACTIVE) ribbonState = RIBBON_DEACTIVATING;
|
||||
}
|
||||
void UpdatePosition(int currentTime, const idVec3& origin,
|
||||
const idMat3& axis, const idVec3& velocity, const idVec4& color);
|
||||
void UpdatePosition(int currentTime, const idVec3& origin,
|
||||
const idVec3& origin2, const idMat3& axis,
|
||||
const idVec3& velocity, const idVec4& color);
|
||||
void UpdateGeometry(int currentTime, idRenderModelBeam* beamModel,
|
||||
const idMat3& viewAxis, const idVec3& viewOrigin);
|
||||
void RemoveOldNodes(int currentTime);
|
||||
int GetRemainingTime(int currentTime) const;
|
||||
|
||||
ribbonState_t ribbonState;
|
||||
idVec3 spawnOrg;
|
||||
int spawnTime;
|
||||
int numActiveNodes;
|
||||
int currHeadIdx;
|
||||
float startVariance;
|
||||
int prevTime;
|
||||
idVec3 prevOrigin;
|
||||
idVec3 prevOrigin2;
|
||||
const idDeclRibbon* ribbonDecl;
|
||||
idVec4 globalColor;
|
||||
ribbonNode_t ribbonNodes[MAX_NODES];
|
||||
bool explicitDefineBothEdges;
|
||||
|
||||
private:
|
||||
void ResetNodes();
|
||||
void AddNode(int currentTime, const idVec3& origin,
|
||||
const idVec3& origin2, const idMat3& axis,
|
||||
const idVec3& velocity, const idVec4& color);
|
||||
void CalcTurbulence(const idVec3& origin, const idMat3& axis,
|
||||
idVec3& turbulence) const;
|
||||
void CalcHelix(int currentTime, const idVec3& origin,
|
||||
const idMat3& axis, const idVec3& velocity, idVec3& helix) const;
|
||||
};
|
||||
|
||||
static_assert(sizeof(idRibbon::ribbonNode_t) == 72,
|
||||
"Recovered ribbon node ABI changed");
|
||||
#if defined(_WIN32) && !defined(_WIN64)
|
||||
static_assert(sizeof(idRibbon) == 9300,
|
||||
"Recovered idRibbon ABI changed");
|
||||
#endif
|
||||
|
||||
@@ -1,32 +1,62 @@
|
||||
#pragma once
|
||||
|
||||
// Reconstructed C++ declarations from IDA Local Types and PDB/DIA metadata.
|
||||
// Original PDB header: w:\tech5\engine\gamelib\effects\ribbonmanager.h
|
||||
// Recovered logical types: 3
|
||||
// Signatures retain Xbox 360 ABI evidence and may still require manual review.
|
||||
#include "idlib/containers/staticlist.h"
|
||||
#include "gamelib/effects/ribbon.h"
|
||||
|
||||
|
||||
// IDA Local Type ordinal 14140; PDB kind: struct.
|
||||
struct idRibbonModelManager::ribbonEffects_t
|
||||
{
|
||||
idStaticList<idRibbon *,16> ribbons;
|
||||
int next;
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 14143; PDB kind: class.
|
||||
class idRibbonModelManager
|
||||
{
|
||||
class idDeclRibbon;
|
||||
class idRenderModelBeam;
|
||||
class idRibbonModelManager {
|
||||
public:
|
||||
idStaticList<idRibbonModelManager::ribbonEffects_t,4> ribbonEffects;
|
||||
struct ribbonEffects_t {
|
||||
// Materialized in the authoritative effects/ribbonmanager.h dump.
|
||||
ribbonEffects_t()
|
||||
: ribbons()
|
||||
, next(0) {
|
||||
}
|
||||
|
||||
idStaticList<idRibbon*, 16> ribbons;
|
||||
int next;
|
||||
};
|
||||
|
||||
idRibbonModelManager();
|
||||
~idRibbonModelManager();
|
||||
|
||||
void CreateRibbonEffectModelType(const idDeclRibbon* ribbonDecl);
|
||||
idRibbon* GetNextRibbonEffectModel(const idDeclRibbon* ribbonDecl);
|
||||
void Shutdown();
|
||||
|
||||
idStaticList<ribbonEffects_t, 4> ribbonEffects;
|
||||
|
||||
private:
|
||||
int FindRibbonEffect(const idDeclRibbon* ribbonDecl) const;
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 14211; PDB kind: class.
|
||||
class __declspec(align(4)) idRibbonManager
|
||||
{
|
||||
class idRibbonManager {
|
||||
public:
|
||||
idRibbon *ribbon;
|
||||
const idDeclRibbon *ribbonDecl;
|
||||
idRenderModelBeam *beamModel;
|
||||
idRibbonModelManager *modelManager;
|
||||
bool isInitialized;
|
||||
idRibbonManager();
|
||||
~idRibbonManager();
|
||||
|
||||
void Init(const idDeclRibbon* ribbonDecl, idRenderModelBeam* beamModel,
|
||||
idRibbonModelManager* modelManager);
|
||||
void Shutdown();
|
||||
void StartRibbon(int spawnTime, const idVec3& spawnOrigin);
|
||||
void StopRibbon();
|
||||
bool UpdateRibbon(int time, const idVec3& origin, const idMat3& axis,
|
||||
const idVec3& velocity, const idVec4& color,
|
||||
const idVec3& translate);
|
||||
|
||||
idRibbon* ribbon;
|
||||
const idDeclRibbon* ribbonDecl;
|
||||
idRenderModelBeam* beamModel;
|
||||
idRibbonModelManager* modelManager;
|
||||
bool isInitialized;
|
||||
};
|
||||
|
||||
#if defined(_WIN32) && !defined(_WIN64)
|
||||
static_assert(sizeof(idRibbonModelManager::ribbonEffects_t) == 84,
|
||||
"Recovered ribbonEffects_t ABI changed");
|
||||
static_assert(sizeof(idRibbonModelManager) == 352,
|
||||
"Recovered idRibbonModelManager ABI changed");
|
||||
static_assert(sizeof(idRibbonManager) == 20,
|
||||
"Recovered idRibbonManager ABI changed");
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
#include "gamelib/effects/tracermanager.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
void GameLib_AddTracerToEffectsModel(idRenderModelEffects* model,
|
||||
const idMaterial* material, const idVec3& origin,
|
||||
const idVec3& direction, const idVec3& maxDistance, float speed,
|
||||
float length, float height, bool ensureVisual, float fractionInsured,
|
||||
float lifeTime);
|
||||
void GameLib_SerializeTracerBuffer(idSerializer& serializer,
|
||||
idTracerManager::tracerBufferItem_t* buffer, int count,
|
||||
int& bufferPosition);
|
||||
|
||||
idTracerManager::idTracerManager()
|
||||
: effectsModel(nullptr)
|
||||
, tracerBuffer{}
|
||||
, tracerBufferPos(0)
|
||||
, serializeTracerCount(0)
|
||||
, catchingUp(false)
|
||||
, localPlayerIndex(0) {
|
||||
}
|
||||
|
||||
void idTracerManager::Init(idRenderModelEffects* const effectsModel_,
|
||||
const int playerIndex) {
|
||||
effectsModel = effectsModel_;
|
||||
localPlayerIndex = playerIndex;
|
||||
}
|
||||
|
||||
void idTracerManager::AddTracer(const idMaterial* const material,
|
||||
const idVec3& origin, const idVec3& direction,
|
||||
const idVec3& maxDistance, const float speed, const float length,
|
||||
const float height, const bool ensureVisual,
|
||||
const float fractionInsured, const int playerIndex) {
|
||||
if (effectsModel != nullptr) {
|
||||
GameLib_AddTracerToEffectsModel(effectsModel, material, origin,
|
||||
direction, maxDistance, speed, length, height, ensureVisual,
|
||||
fractionInsured, -1.0f);
|
||||
}
|
||||
if (catchingUp) {
|
||||
return;
|
||||
}
|
||||
tracerBufferItem_t& item = tracerBuffer[tracerBufferPos];
|
||||
item.startTime = 0;
|
||||
item.lifeTime = 0;
|
||||
item.speed = speed;
|
||||
item.origin = origin;
|
||||
item.dir = direction;
|
||||
item.length = length;
|
||||
item.height = height;
|
||||
item.mat = material;
|
||||
item.maxDist = maxDistance;
|
||||
item.ensureVisual = ensureVisual;
|
||||
item.fractionInsured = fractionInsured;
|
||||
item.playerIndex = playerIndex;
|
||||
tracerBufferPos = (tracerBufferPos + 1) % 4;
|
||||
}
|
||||
|
||||
void idTracerManager::Update(const int time) {
|
||||
(void)time;
|
||||
catchingUp = true;
|
||||
while (serializeTracerCount > 0) {
|
||||
const int index = tracerBufferPos;
|
||||
tracerBufferPos = (tracerBufferPos + 1) % 4;
|
||||
--serializeTracerCount;
|
||||
const tracerBufferItem_t& item = tracerBuffer[index];
|
||||
if (item.playerIndex == localPlayerIndex || effectsModel == nullptr) {
|
||||
continue;
|
||||
}
|
||||
GameLib_AddTracerToEffectsModel(effectsModel, item.mat, item.origin,
|
||||
item.dir, item.maxDist, item.speed, item.length, item.height,
|
||||
item.ensureVisual, item.fractionInsured, -1.0f);
|
||||
}
|
||||
catchingUp = false;
|
||||
}
|
||||
|
||||
void idTracerManager::Serialize(idSerializer& serializer) {
|
||||
const int oldPosition = tracerBufferPos;
|
||||
GameLib_SerializeTracerBuffer(serializer, tracerBuffer, 4,
|
||||
tracerBufferPos);
|
||||
serializeTracerCount = tracerBufferPos - oldPosition;
|
||||
if (serializeTracerCount < 0) {
|
||||
serializeTracerCount += 4;
|
||||
}
|
||||
serializeTracerCount = (std::min)(4, serializeTracerCount);
|
||||
}
|
||||
@@ -1,36 +1,49 @@
|
||||
#pragma once
|
||||
|
||||
// Reconstructed C++ declarations from IDA Local Types and PDB/DIA metadata.
|
||||
// Original PDB header: w:\tech5\engine\gamelib\effects\tracermanager.h
|
||||
// Recovered logical types: 2
|
||||
// Signatures retain Xbox 360 ABI evidence and may still require manual review.
|
||||
#include "idlib/math/vector.h"
|
||||
|
||||
class idMaterial;
|
||||
class idRenderModelEffects;
|
||||
class idSerializer;
|
||||
|
||||
// IDA Local Type ordinal 15276; PDB kind: struct.
|
||||
struct idTracerManager::tracerBufferItem_t
|
||||
{
|
||||
int startTime;
|
||||
int lifeTime;
|
||||
float speed;
|
||||
idVec3 origin;
|
||||
idVec3 dir;
|
||||
float length;
|
||||
float height;
|
||||
const idMaterial *mat;
|
||||
idVec3 maxDist;
|
||||
bool ensureVisual;
|
||||
float fractionInsured;
|
||||
int playerIndex;
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 15277; PDB kind: class.
|
||||
class idTracerManager
|
||||
{
|
||||
class idTracerManager {
|
||||
public:
|
||||
idRenderModelEffects *effectsModel;
|
||||
idTracerManager::tracerBufferItem_t tracerBuffer[4];
|
||||
int tracerBufferPos;
|
||||
int serializeTracerCount;
|
||||
bool catchingUp;
|
||||
int localPlayerIndex;
|
||||
struct tracerBufferItem_t {
|
||||
int startTime;
|
||||
int lifeTime;
|
||||
float speed;
|
||||
idVec3 origin;
|
||||
idVec3 dir;
|
||||
float length;
|
||||
float height;
|
||||
const idMaterial* mat;
|
||||
idVec3 maxDist;
|
||||
bool ensureVisual;
|
||||
float fractionInsured;
|
||||
int playerIndex;
|
||||
};
|
||||
|
||||
idTracerManager();
|
||||
|
||||
void Init(idRenderModelEffects* effectsModel, int playerIndex);
|
||||
void AddTracer(const idMaterial* material, const idVec3& origin,
|
||||
const idVec3& direction, const idVec3& maxDistance, float speed,
|
||||
float length, float height, bool ensureVisual,
|
||||
float fractionInsured, int playerIndex);
|
||||
void Update(int time);
|
||||
void Serialize(idSerializer& serializer);
|
||||
|
||||
idRenderModelEffects* effectsModel;
|
||||
tracerBufferItem_t tracerBuffer[4];
|
||||
int tracerBufferPos;
|
||||
int serializeTracerCount;
|
||||
bool catchingUp;
|
||||
int localPlayerIndex;
|
||||
};
|
||||
|
||||
static_assert(sizeof(idTracerManager::tracerBufferItem_t) == 72,
|
||||
"Recovered tracer buffer ABI changed");
|
||||
#if defined(_WIN32) && !defined(_WIN64)
|
||||
static_assert(sizeof(idTracerManager) == 308,
|
||||
"Recovered idTracerManager ABI changed");
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
#include "gamelib/effects/weapontracemanager.h"
|
||||
|
||||
idWeaponTraceManager::idWeaponTraceManager()
|
||||
: weaponTraces{}
|
||||
, currentDeferred(0) {
|
||||
Init();
|
||||
}
|
||||
|
||||
void idWeaponTraceManager::Init() {
|
||||
weaponTraces[0].Clear();
|
||||
weaponTraces[1].Clear();
|
||||
currentDeferred = 0;
|
||||
}
|
||||
|
||||
void idWeaponTraceManager::Update() {
|
||||
currentDeferred ^= 1;
|
||||
weaponTraces[currentDeferred].Clear();
|
||||
}
|
||||
|
||||
void idWeaponTraceManager::AddTrace(const idVec3& start,
|
||||
const idVec3& end, const idSpawnId attacker,
|
||||
const idDeclDamage* const damage) {
|
||||
idStaticList<weaponTrace_t, 6>& traces = weaponTraces[currentDeferred];
|
||||
if (traces.Num() >= traces.Max()) {
|
||||
return;
|
||||
}
|
||||
weaponTrace_t* const trace = traces.Alloc();
|
||||
trace->start = start;
|
||||
trace->end = end;
|
||||
trace->impactPoint = end;
|
||||
trace->dir.Zero();
|
||||
trace->attacker = attacker;
|
||||
trace->projectile.Invalidate();
|
||||
trace->damageDecl = damage;
|
||||
trace->radius = 0.0f;
|
||||
trace->weaponTraceType = weaponTrace_t::WEAPON_TRACE_BULLET;
|
||||
}
|
||||
|
||||
void idWeaponTraceManager::AddExplosion(const idVec3& origin,
|
||||
const float radius, const idSpawnId attacker,
|
||||
const idDeclDamage* const damage) {
|
||||
idStaticList<weaponTrace_t, 6>& traces = weaponTraces[currentDeferred];
|
||||
if (traces.Num() >= traces.Max()) {
|
||||
return;
|
||||
}
|
||||
weaponTrace_t* const trace = traces.Alloc();
|
||||
trace->start = origin;
|
||||
trace->end = origin;
|
||||
trace->impactPoint = origin;
|
||||
trace->dir.Zero();
|
||||
trace->attacker = attacker;
|
||||
trace->projectile.Invalidate();
|
||||
trace->damageDecl = damage;
|
||||
trace->radius = radius;
|
||||
trace->weaponTraceType = weaponTrace_t::WEAPON_TRACE_EXPLOSION;
|
||||
}
|
||||
|
||||
void idWeaponTraceManager::AddProjectile(const idVec3& origin,
|
||||
const float radius, const idSpawnId attacker,
|
||||
const idSpawnId projectile, const idDeclDamage* const damage) {
|
||||
idStaticList<weaponTrace_t, 6>& traces = weaponTraces[currentDeferred];
|
||||
if (traces.Num() >= traces.Max()) {
|
||||
return;
|
||||
}
|
||||
weaponTrace_t* const trace = traces.Alloc();
|
||||
trace->start = origin;
|
||||
trace->end = origin;
|
||||
trace->impactPoint = origin;
|
||||
trace->dir.Zero();
|
||||
trace->attacker = attacker;
|
||||
trace->projectile = projectile;
|
||||
trace->damageDecl = damage;
|
||||
trace->radius = radius;
|
||||
trace->weaponTraceType = weaponTrace_t::WEAPON_TRACE_PROJECTILE;
|
||||
}
|
||||
|
||||
int idWeaponTraceManager::GetCollisions(const idBounds& bounds,
|
||||
idList<weaponTrace_t, 5>& outTraces) {
|
||||
outTraces.Clear();
|
||||
const idStaticList<weaponTrace_t, 6>& traces =
|
||||
weaponTraces[currentDeferred ^ 1];
|
||||
|
||||
for (int index = 0; index < traces.Num(); ++index) {
|
||||
const weaponTrace_t& source = traces[index];
|
||||
weaponTrace_t result = source;
|
||||
switch (source.weaponTraceType) {
|
||||
case weaponTrace_t::WEAPON_TRACE_BULLET: {
|
||||
result.dir = source.end - source.start;
|
||||
float scale = 0.0f;
|
||||
if (!bounds.RayIntersection(source.start, result.dir, scale) ||
|
||||
scale < 0.0f || scale >= 1.0f) {
|
||||
continue;
|
||||
}
|
||||
result.dir.NormalizeFast();
|
||||
result.radius = 0.0f;
|
||||
break;
|
||||
}
|
||||
case weaponTrace_t::WEAPON_TRACE_EXPLOSION:
|
||||
if (!bounds.SphereIntersection(source.start, source.radius)) {
|
||||
continue;
|
||||
}
|
||||
result.end = (bounds[0] + bounds[1]) * 0.5f;
|
||||
result.impactPoint = result.end;
|
||||
result.dir = result.end - result.start;
|
||||
result.dir.NormalizeFast();
|
||||
break;
|
||||
case weaponTrace_t::WEAPON_TRACE_PROJECTILE:
|
||||
if (!bounds.SphereIntersection(source.start, source.radius)) {
|
||||
continue;
|
||||
}
|
||||
result.end = result.start;
|
||||
result.impactPoint = result.start;
|
||||
result.dir.Zero();
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
outTraces.Append(result);
|
||||
}
|
||||
return outTraces.Num();
|
||||
}
|
||||
@@ -1,37 +1,60 @@
|
||||
#pragma once
|
||||
|
||||
// Reconstructed C++ declarations from IDA Local Types and PDB/DIA metadata.
|
||||
// Original PDB header: w:\tech5\engine\gamelib\effects\weapontracemanager.h
|
||||
// Recovered logical types: 3
|
||||
// Signatures retain Xbox 360 ABI evidence and may still require manual review.
|
||||
#include "idlib/bv/bounds.h"
|
||||
#include "idlib/containers/staticlist.h"
|
||||
#include "idlib/handle.h"
|
||||
|
||||
class idDeclDamage;
|
||||
enum invalidSpawnId_t : int;
|
||||
|
||||
// IDA Local Type ordinal 1836; PDB kind: enum.
|
||||
enum weaponTrace_t::weaponTraceType_t : __int32
|
||||
{
|
||||
WEAPON_TRACE_BULLET = 0x0,
|
||||
WEAPON_TRACE_EXPLOSION = 0x1,
|
||||
WEAPON_TRACE_PROJECTILE = 0x2,
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 14146; PDB kind: struct.
|
||||
struct weaponTrace_t
|
||||
{
|
||||
idVec3 start;
|
||||
idVec3 end;
|
||||
idVec3 impactPoint;
|
||||
idVec3 dir;
|
||||
idSpawnId attacker;
|
||||
idSpawnId projectile;
|
||||
const idDeclDamage *damageDecl;
|
||||
float radius;
|
||||
weaponTrace_t::weaponTraceType_t weaponTraceType;
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 14149; PDB kind: class.
|
||||
class idWeaponTraceManager
|
||||
{
|
||||
class idSpawnId : public idHandle<int, invalidSpawnId_t, 8191> {
|
||||
public:
|
||||
idStaticList<weaponTrace_t,6> weaponTraces[2];
|
||||
int currentDeferred;
|
||||
idSpawnId() = default;
|
||||
idSpawnId(int value) : idHandle(value) {}
|
||||
};
|
||||
|
||||
struct weaponTrace_t {
|
||||
enum weaponTraceType_t : int {
|
||||
WEAPON_TRACE_BULLET = 0,
|
||||
WEAPON_TRACE_EXPLOSION = 1,
|
||||
WEAPON_TRACE_PROJECTILE = 2
|
||||
};
|
||||
|
||||
idVec3 start;
|
||||
idVec3 end;
|
||||
idVec3 impactPoint;
|
||||
idVec3 dir;
|
||||
idSpawnId attacker;
|
||||
idSpawnId projectile;
|
||||
const idDeclDamage* damageDecl;
|
||||
float radius;
|
||||
weaponTraceType_t weaponTraceType;
|
||||
};
|
||||
|
||||
class idWeaponTraceManager {
|
||||
public:
|
||||
idWeaponTraceManager();
|
||||
|
||||
void Init();
|
||||
void Update();
|
||||
void AddTrace(const idVec3& start, const idVec3& end,
|
||||
idSpawnId attacker, const idDeclDamage* damage);
|
||||
void AddExplosion(const idVec3& origin, float radius,
|
||||
idSpawnId attacker, const idDeclDamage* damage);
|
||||
void AddProjectile(const idVec3& origin, float radius,
|
||||
idSpawnId attacker, idSpawnId projectile,
|
||||
const idDeclDamage* damage);
|
||||
int GetCollisions(const idBounds& bounds,
|
||||
idList<weaponTrace_t, 5>& outTraces);
|
||||
|
||||
idStaticList<weaponTrace_t, 6> weaponTraces[2];
|
||||
int currentDeferred;
|
||||
};
|
||||
|
||||
#if defined(_WIN32) && !defined(_WIN64)
|
||||
static_assert(sizeof(idSpawnId) == 4, "Recovered idSpawnId ABI changed");
|
||||
static_assert(sizeof(weaponTrace_t) == 68,
|
||||
"Recovered weapon trace ABI changed");
|
||||
static_assert(sizeof(idWeaponTraceManager) == 852,
|
||||
"Recovered weapon trace manager ABI changed");
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,752 @@
|
||||
#include "gamelib/physics/clip.h"
|
||||
|
||||
#include "cm/collisionmodel.h"
|
||||
#include "cm/collisionmodelmanager.h"
|
||||
#include "gamelib/aas2/aas2.h"
|
||||
#include "gamelib/physics/tracemodelcache.h"
|
||||
#include "gamelib/physics/tracemodelrecycler.h"
|
||||
#include "idlib/geometry/tracemodel.h"
|
||||
#include "idlib/geometry/winding.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
|
||||
bool BoundsIntersect(const idBounds& first, const idBounds& second) {
|
||||
for (int axis = 0; axis < 3; ++axis) {
|
||||
if (first[1][axis] < second[0][axis]
|
||||
|| first[0][axis] > second[1][axis]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
idBounds PointBounds(const idVec3& point) {
|
||||
idBounds result;
|
||||
result[0] = point;
|
||||
result[1] = point;
|
||||
return result;
|
||||
}
|
||||
|
||||
idBounds MotionBounds(const idVec3& start, const idVec3& end,
|
||||
const idClipModel* const model, const idMat3& axis) {
|
||||
idBounds result;
|
||||
if (model != nullptr) {
|
||||
result.FromTransformedBounds(model->GetBounds(), start, axis);
|
||||
idBounds endBounds;
|
||||
endBounds.FromTransformedBounds(model->GetBounds(), end, axis);
|
||||
for (int component = 0; component < 3; ++component) {
|
||||
result[0][component] = (std::min)(result[0][component],
|
||||
endBounds[0][component]);
|
||||
result[1][component] = (std::max)(result[1][component],
|
||||
endBounds[1][component]);
|
||||
}
|
||||
} else {
|
||||
result[0].Set((std::min)(start.x, end.x),
|
||||
(std::min)(start.y, end.y), (std::min)(start.z, end.z));
|
||||
result[1].Set((std::max)(start.x, end.x),
|
||||
(std::max)(start.y, end.y), (std::max)(start.z, end.z));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void CollectTraceModels(const idClipModel* const model,
|
||||
std::vector<const idTraceModel*>& output) {
|
||||
if (model == nullptr) return;
|
||||
for (int index = 0; index < model->GetNumTraceModels(); ++index) {
|
||||
const idTraceModel* const traceModel = model->GetTraceModel(index);
|
||||
if (traceModel != nullptr) output.push_back(traceModel);
|
||||
}
|
||||
}
|
||||
|
||||
void AppendPositionedModel(const idClipModel* const model,
|
||||
std::vector<idPositionedCollisionModel>& output) {
|
||||
if (model == nullptr || !model->IsEnabled() || model->IsDeleted()) return;
|
||||
idCollisionModel* const collisionModel = model->GetCollisionModel();
|
||||
if (collisionModel == nullptr) return;
|
||||
idPositionedCollisionModel positioned{};
|
||||
positioned.model = collisionModel;
|
||||
positioned.modelJoints = nullptr;
|
||||
positioned.modelOrigin = model->GetOrigin();
|
||||
positioned.modelAxis = model->GetAxis();
|
||||
positioned.modelEntityNum = model->GetEntityNumber();
|
||||
positioned.modelPhysicsId = model->GetPhysicsId();
|
||||
positioned.modelBodyId = model->GetBodyId();
|
||||
positioned.modelContentsOverride = model->GetContents();
|
||||
positioned.modelQuery = model->collisionQuery;
|
||||
output.push_back(positioned);
|
||||
}
|
||||
|
||||
void CollectPositionedModels(const idClip& clip, const int contentMask,
|
||||
const int passEntityNumber, const bool ignoreWorld,
|
||||
std::vector<idPositionedCollisionModel>& output) {
|
||||
if (!ignoreWorld && clip.world != nullptr
|
||||
&& (clip.world->GetContents() & contentMask) != 0) {
|
||||
AppendPositionedModel(clip.world, output);
|
||||
}
|
||||
if (clip.clipSectors.IsEmpty()) return;
|
||||
for (clipLink_t* link = clip.clipSectors[0].clipLinks;
|
||||
link != nullptr; link = link->nextInSector) {
|
||||
idClipModel* const model = link->clipModel;
|
||||
if (model == nullptr || model == clip.world
|
||||
|| model->GetEntityNumber() == passEntityNumber
|
||||
|| (model->GetContents() & contentMask) == 0) continue;
|
||||
AppendPositionedModel(model, output);
|
||||
}
|
||||
}
|
||||
|
||||
idClipQuery ToClipQuery(const idCollisionQuery query) {
|
||||
idClipQuery result{};
|
||||
result.index = query.offset;
|
||||
return result;
|
||||
}
|
||||
|
||||
idCollisionQuery ToCollisionQuery(const idClipQuery query) {
|
||||
idCollisionQuery result{};
|
||||
result.offset = query.index;
|
||||
return result;
|
||||
}
|
||||
|
||||
idClipModel* FindLinkedModel(const idClip& clip, const int entityNumber,
|
||||
const int physicsId, const int bodyId) {
|
||||
if (clip.world != nullptr && clip.world->GetEntityNumber() == entityNumber
|
||||
&& clip.world->GetPhysicsId() == physicsId
|
||||
&& clip.world->GetBodyId() == bodyId) return clip.world;
|
||||
if (clip.clipSectors.IsEmpty()) return nullptr;
|
||||
for (clipLink_t* link = clip.clipSectors[0].clipLinks;
|
||||
link != nullptr; link = link->nextInSector) {
|
||||
idClipModel* const model = link->clipModel;
|
||||
if (model != nullptr && model->GetEntityNumber() == entityNumber
|
||||
&& model->GetPhysicsId() == physicsId
|
||||
&& model->GetBodyId() == bodyId) return model;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
idClip::idClip()
|
||||
: world(nullptr), temporaryClipModel(nullptr), defaultClipModel(nullptr),
|
||||
playerMeleeClipModel(nullptr), clip8x8(nullptr), clip8x8x8(nullptr),
|
||||
clip16x16(nullptr), clip16x16x16(nullptr), clip24x24(nullptr),
|
||||
clip32x32(nullptr), clip8x16(nullptr), clip48x48(nullptr),
|
||||
clip96x96(nullptr), clip24x24x96(nullptr), clip32x32x96(nullptr),
|
||||
clip48x48x96(nullptr), clipLinkAllocator(false), clipSectors(),
|
||||
touchCount(0), aases{}, traceModelCache(nullptr),
|
||||
traceModelRecycler(nullptr), deletedClipModels(nullptr), streamAreas(),
|
||||
queryParms(nullptr), numQueryParms(0), translationQueries(),
|
||||
rotationQueries(), motionQueries(), motionContactsQueries(),
|
||||
stepMoveQueries(), stepMoveContactsQueries(), slideMoveQueries(),
|
||||
slideMoveContactsQueries(), contentsQueries(), contactsQueries(),
|
||||
clipQueries(), localTranslationQueries(), localContentsQueries(),
|
||||
translationTraceModelQueries(), collisionQueries{},
|
||||
collisionQueryFirstSubmittedIndex(0), collisionQueryLastSubmittedIndex(0),
|
||||
collisionQueryFirstAllocedIndex(0), collisionQueryLastAllocedIndex(0),
|
||||
collisionQueryLastResolvedIndex(0), gatherQueryUserNames{},
|
||||
gatherQueries{}, gatherQueryFirstSubmittedIndex(0),
|
||||
gatherQueryLastSubmittedIndex(0), gatherQueryFirstAllocedIndex(0),
|
||||
gatherQueryLastAllocedIndex(0), gatherResults{},
|
||||
gatherResultFirstSubmittedIndex(0), gatherResultLastSubmittedIndex(0),
|
||||
gatherResultFirstAllocedIndex(0), gatherResultLastAllocedIndex(0),
|
||||
residencyQueries{}, residencyQueryFirstSubmittedIndex(0),
|
||||
residencyQueryLastSubmittedIndex(0), residencyQueryFirstAllocedIndex(0),
|
||||
residencyQueryLastAllocedIndex(0) {
|
||||
}
|
||||
|
||||
idClip::~idClip() {
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
void idClip::Init(idCollisionModel* const worldModel,
|
||||
idTraceModelCache* const cache, idTraceModelRecycler* const recycler) {
|
||||
Shutdown();
|
||||
traceModelCache = cache;
|
||||
traceModelRecycler = recycler;
|
||||
clipSectors.SetNum(1);
|
||||
clipSectors[0].axis = -1;
|
||||
clipSectors[0].dist = 0.0f;
|
||||
clipSectors[0].children[0] = nullptr;
|
||||
clipSectors[0].children[1] = nullptr;
|
||||
clipSectors[0].clipLinks = nullptr;
|
||||
if (worldModel != nullptr) {
|
||||
world = new idClipModel(this, worldModel);
|
||||
world->Link(0, -1, 0, idVec3(), idMat3(1.0f));
|
||||
}
|
||||
}
|
||||
|
||||
void idClip::Shutdown() {
|
||||
DeleteClipModels();
|
||||
if (world != nullptr) {
|
||||
idClipModel* const oldWorld = world;
|
||||
world = nullptr;
|
||||
delete oldWorld;
|
||||
}
|
||||
clipSectors.SetNum(0);
|
||||
clipLinkAllocator.Shutdown();
|
||||
streamAreas.ClearFree();
|
||||
delete[] queryParms;
|
||||
queryParms = nullptr;
|
||||
numQueryParms = 0;
|
||||
traceModelCache = nullptr;
|
||||
traceModelRecycler = nullptr;
|
||||
deletedClipModels = nullptr;
|
||||
}
|
||||
|
||||
void idClip::StartQueryFrame() {
|
||||
collisionModelManager.StartQueryFrame();
|
||||
}
|
||||
|
||||
void idClip::EndQueryFrame() {
|
||||
ResolveCollisionQueries();
|
||||
collisionModelManager.EndQueryFrame();
|
||||
DeleteClipModels();
|
||||
}
|
||||
|
||||
void idClip::ResolveCollisionQueries() {
|
||||
collisionModelManager.WaitForAllQueries();
|
||||
collisionQueryLastResolvedIndex = collisionQueryLastSubmittedIndex;
|
||||
}
|
||||
|
||||
void idClip::DeleteClipModel(idClipModel* const clipModel) {
|
||||
if (clipModel == nullptr || clipModel == world) return;
|
||||
clipModel->Unlink();
|
||||
clipModel->nextDeleted = deletedClipModels;
|
||||
deletedClipModels = clipModel;
|
||||
}
|
||||
|
||||
void idClip::DeleteClipModels() {
|
||||
while (deletedClipModels != nullptr) {
|
||||
idClipModel* const model = deletedClipModels;
|
||||
deletedClipModels = model->nextDeleted;
|
||||
model->nextDeleted = nullptr;
|
||||
delete model;
|
||||
}
|
||||
}
|
||||
|
||||
void idClip::InitializeAASLinks(const int aasType) {
|
||||
if (aasType < 0 || aasType >= 8 || aases[aasType] == nullptr
|
||||
|| clipSectors.IsEmpty()) return;
|
||||
for (clipLink_t* link = clipSectors[0].clipLinks;
|
||||
link != nullptr; link = link->nextInSector) {
|
||||
idClipModel* const model = link->clipModel;
|
||||
if (model != nullptr && model->aasLinks[aasType] == nullptr)
|
||||
model->aasLinks[aasType] = aases[aasType]->LinkClipModel(model);
|
||||
}
|
||||
}
|
||||
|
||||
const idBounds& idClip::GetWorldBounds() const {
|
||||
static idBounds emptyBounds{};
|
||||
return world != nullptr ? world->GetBounds() : emptyBounds;
|
||||
}
|
||||
|
||||
void idClip::AddStreamArea(const int areaNum) {
|
||||
if (streamAreas.FindIndex(areaNum) < 0) streamAreas.Append(areaNum);
|
||||
}
|
||||
|
||||
void idClip::ClearStreamAreas() {
|
||||
streamAreas.Clear();
|
||||
}
|
||||
|
||||
int idClip::FindStreamArea(const idClipModel*) {
|
||||
return streamAreas.IsEmpty() ? -1 : streamAreas[0];
|
||||
}
|
||||
|
||||
int idClip::GetClipModelsTouchingBounds(const idBounds& queryBounds,
|
||||
const int contentMask, idClipModel** const output,
|
||||
const int maxOutput) const {
|
||||
if (output == nullptr || maxOutput <= 0 || clipSectors.IsEmpty()) return 0;
|
||||
int count = 0;
|
||||
for (clipLink_t* link = clipSectors[0].clipLinks;
|
||||
link != nullptr && count < maxOutput; link = link->nextInSector) {
|
||||
idClipModel* const model = link->clipModel;
|
||||
if (model == nullptr || !model->IsEnabled()
|
||||
|| (model->GetContents() & contentMask) == 0
|
||||
|| !BoundsIntersect(queryBounds, model->GetAbsBounds())) continue;
|
||||
output[count++] = model;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
int idClip::GetClipModelsTouchingLine(const idVec3& start,
|
||||
const idVec3& end, const int contentMask,
|
||||
idClipModel** const output, const int maxOutput) const {
|
||||
if (output == nullptr || maxOutput <= 0 || clipSectors.IsEmpty()) return 0;
|
||||
int count = 0;
|
||||
for (clipLink_t* link = clipSectors[0].clipLinks;
|
||||
link != nullptr && count < maxOutput; link = link->nextInSector) {
|
||||
idClipModel* const model = link->clipModel;
|
||||
if (model == nullptr || !model->IsEnabled()
|
||||
|| (model->GetContents() & contentMask) == 0
|
||||
|| !model->GetAbsBounds().LineIntersection(start, end)) continue;
|
||||
output[count++] = model;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
int idClip::GetEntitiesTouchingBounds(const idBounds& bounds,
|
||||
const int contentMask, int* const entities,
|
||||
const int maxEntities) const {
|
||||
if (entities == nullptr || maxEntities <= 0) return 0;
|
||||
std::vector<idClipModel*> models(static_cast<std::size_t>(maxEntities));
|
||||
const int modelCount = GetClipModelsTouchingBounds(bounds, contentMask,
|
||||
models.data(), maxEntities);
|
||||
int count = 0;
|
||||
for (int index = 0; index < modelCount; ++index) {
|
||||
const int entity = models[index]->GetEntityNumber();
|
||||
bool duplicate = false;
|
||||
for (int other = 0; other < count; ++other)
|
||||
if (entities[other] == entity) duplicate = true;
|
||||
if (!duplicate) entities[count++] = entity;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
int idClip::GetEntitiesTouchingLine(const idVec3& start,
|
||||
const idVec3& end, const int contentMask, int* const entities,
|
||||
const int maxEntities) const {
|
||||
if (entities == nullptr || maxEntities <= 0) return 0;
|
||||
std::vector<idClipModel*> models(static_cast<std::size_t>(maxEntities));
|
||||
const int modelCount = GetClipModelsTouchingLine(start, end, contentMask,
|
||||
models.data(), maxEntities);
|
||||
int count = 0;
|
||||
for (int index = 0; index < modelCount; ++index) {
|
||||
const int entity = models[index]->GetEntityNumber();
|
||||
bool duplicate = false;
|
||||
for (int other = 0; other < count; ++other)
|
||||
if (entities[other] == entity) duplicate = true;
|
||||
if (!duplicate) entities[count++] = entity;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
idGatherQuery idClip::GatherTouchingBounds(const idBounds& bounds,
|
||||
const int contentMask, const int passEntityNumber,
|
||||
const char* const userName) {
|
||||
const std::uint64_t queryIndex = gatherQueryLastSubmittedIndex++;
|
||||
idClipGatherQuery& query = gatherQueries[queryIndex & 255];
|
||||
query.vectors[0] = bounds[0];
|
||||
query.vectors[1] = bounds[1];
|
||||
query.clipMask = contentMask;
|
||||
query.passEntityNumber = static_cast<std::int16_t>(passEntityNumber);
|
||||
query.gatherType = GATHER_TOUCHING_BOUNDS;
|
||||
query.firstResultIndex = static_cast<std::uint16_t>(
|
||||
gatherResultLastSubmittedIndex & 2047);
|
||||
idClipModel* models[2048];
|
||||
const int count = GetClipModelsTouchingBounds(bounds, contentMask,
|
||||
models, 2048);
|
||||
query.numResults = static_cast<std::uint16_t>(count);
|
||||
for (int index = 0; index < count; ++index) {
|
||||
idGatherResult& result = gatherResults[
|
||||
gatherResultLastSubmittedIndex++ & 2047];
|
||||
result.query = models[index]->collisionQuery;
|
||||
result.physicsId = models[index]->GetPhysicsId();
|
||||
result.bodyId = static_cast<std::int16_t>(models[index]->GetBodyId());
|
||||
result.entityNumber = static_cast<std::int16_t>(
|
||||
models[index]->GetEntityNumber());
|
||||
}
|
||||
gatherQueryUserNames[queryIndex & 255] = userName;
|
||||
return idGatherQuery{queryIndex};
|
||||
}
|
||||
|
||||
idGatherQuery idClip::GatherTouchingClipModel(const idVec3& origin,
|
||||
const idClipModel* const model, const idMat3& axis,
|
||||
const int contentMask, const int passEntityNumber,
|
||||
const char* const userName) {
|
||||
idBounds bounds = PointBounds(origin);
|
||||
if (model != nullptr)
|
||||
bounds.FromTransformedBounds(model->GetBounds(), origin, axis);
|
||||
return GatherTouchingBounds(bounds, contentMask, passEntityNumber,
|
||||
userName);
|
||||
}
|
||||
|
||||
int idClip::GetGatheredClipModels(const idGatherQuery& handle,
|
||||
idClipModel** const output, const int maxOutput) {
|
||||
if (output == nullptr || maxOutput <= 0
|
||||
|| handle.index >= gatherQueryLastSubmittedIndex) return 0;
|
||||
const idClipGatherQuery& query = gatherQueries[handle.index & 255];
|
||||
const int count = (std::min)(maxOutput,
|
||||
static_cast<int>(query.numResults));
|
||||
int outputCount = 0;
|
||||
for (int index = 0; index < count; ++index) {
|
||||
const idGatherResult& result = gatherResults[
|
||||
(query.firstResultIndex + index) & 2047];
|
||||
idClipModel* const model = FindLinkedModel(*this,
|
||||
result.entityNumber, result.physicsId, result.bodyId);
|
||||
if (model != nullptr) output[outputCount++] = model;
|
||||
}
|
||||
return outputCount;
|
||||
}
|
||||
|
||||
int idClip::GetGatheredEntities(const idGatherQuery& handle,
|
||||
int* const output, const int maxOutput) {
|
||||
if (output == nullptr || maxOutput <= 0
|
||||
|| handle.index >= gatherQueryLastSubmittedIndex) return 0;
|
||||
const idClipGatherQuery& query = gatherQueries[handle.index & 255];
|
||||
int count = 0;
|
||||
for (int index = 0; index < query.numResults && count < maxOutput;
|
||||
++index) {
|
||||
const int entity = gatherResults[
|
||||
(query.firstResultIndex + index) & 2047].entityNumber;
|
||||
bool duplicate = false;
|
||||
for (int other = 0; other < count; ++other)
|
||||
if (output[other] == entity) duplicate = true;
|
||||
if (!duplicate) output[count++] = entity;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
idResidencyQuery idClip::TestResidency(const idBounds& bounds) {
|
||||
const std::uint64_t index = residencyQueryLastSubmittedIndex++;
|
||||
idClipResidencyQuery& query = residencyQueries[index & 1023];
|
||||
query.absBounds = bounds;
|
||||
query.resident = true;
|
||||
return idResidencyQuery{index};
|
||||
}
|
||||
|
||||
idClipQuery idClip::Translation(trace_t* const result,
|
||||
const idVec3& start, const idVec3& end,
|
||||
const idClipModel* const movingModel, const idMat3& axis,
|
||||
const int contentMask, const int passEntityNumber,
|
||||
const bool ignoreWorld, const char* const userName) {
|
||||
std::vector<const idTraceModel*> traceModels;
|
||||
std::vector<idPositionedCollisionModel> models;
|
||||
CollectTraceModels(movingModel, traceModels);
|
||||
CollectPositionedModels(*this, contentMask, passEntityNumber,
|
||||
ignoreWorld, models);
|
||||
const idBounds bounds = MotionBounds(start, end, movingModel, axis);
|
||||
return ToClipQuery(collisionModelManager.Translation(result, start, end,
|
||||
bounds, traceModels.empty() ? nullptr : traceModels.data(),
|
||||
static_cast<int>(traceModels.size()), axis, contentMask,
|
||||
models.empty() ? nullptr : models.data(),
|
||||
static_cast<int>(models.size()), userName));
|
||||
}
|
||||
|
||||
idClipQuery idClip::LocalTranslation(const idClipQuery localSpace,
|
||||
const idVec3& start, const idVec3& end,
|
||||
const idClipModel* const movingModel, const idMat3& axis,
|
||||
const int contentMask, const int passEntityNumber,
|
||||
const bool ignoreWorld, const char* const userName) {
|
||||
std::vector<const idTraceModel*> traceModels;
|
||||
std::vector<idPositionedCollisionModel> models;
|
||||
CollectTraceModels(movingModel, traceModels);
|
||||
CollectPositionedModels(*this, contentMask, passEntityNumber,
|
||||
ignoreWorld, models);
|
||||
const idBounds bounds = MotionBounds(start, end, movingModel, axis);
|
||||
return ToClipQuery(collisionModelManager.LocalTranslation(
|
||||
ToCollisionQuery(localSpace), start, end, bounds,
|
||||
traceModels.empty() ? nullptr : traceModels.data(),
|
||||
static_cast<int>(traceModels.size()), axis, contentMask,
|
||||
models.empty() ? nullptr : models.data(),
|
||||
static_cast<int>(models.size()), userName));
|
||||
}
|
||||
|
||||
idClipQuery idClip::TranslationTraceModel(trace_t* const result,
|
||||
const idVec3& start, const idVec3& end,
|
||||
const idClipModel* const movingModel, const idVec3& modelOrigin,
|
||||
const idMat3& modelAxis, const int contentMask,
|
||||
const char* const userName) {
|
||||
std::vector<const idTraceModel*> traceModels;
|
||||
CollectTraceModels(movingModel, traceModels);
|
||||
idPositionedCollisionModel positioned{};
|
||||
positioned.model = movingModel != nullptr
|
||||
? movingModel->GetCollisionModel() : nullptr;
|
||||
positioned.modelOrigin = modelOrigin;
|
||||
positioned.modelAxis = modelAxis;
|
||||
positioned.modelContentsOverride = contentMask;
|
||||
const idBounds bounds = MotionBounds(start, end, movingModel, modelAxis);
|
||||
return ToClipQuery(collisionModelManager.Translation(result, start, end,
|
||||
bounds, traceModels.empty() ? nullptr : traceModels.data(),
|
||||
static_cast<int>(traceModels.size()), modelAxis, contentMask,
|
||||
positioned.model != nullptr ? &positioned : nullptr,
|
||||
positioned.model != nullptr ? 1 : 0, userName));
|
||||
}
|
||||
|
||||
idClipQuery idClip::Rotation(trace_t* const result, const idVec3& start,
|
||||
const idRotation& rotation, const idClipModel* const movingModel,
|
||||
const idMat3& axis, const int contentMask,
|
||||
const int passEntityNumber, const bool ignoreWorld,
|
||||
const char* const userName) {
|
||||
std::vector<const idTraceModel*> traceModels;
|
||||
std::vector<idPositionedCollisionModel> models;
|
||||
CollectTraceModels(movingModel, traceModels);
|
||||
CollectPositionedModels(*this, contentMask, passEntityNumber,
|
||||
ignoreWorld, models);
|
||||
const idBounds bounds = MotionBounds(start, start, movingModel, axis);
|
||||
return ToClipQuery(collisionModelManager.Rotation(result, start,
|
||||
rotation, bounds,
|
||||
traceModels.empty() ? nullptr : traceModels.data(),
|
||||
static_cast<int>(traceModels.size()), axis, contentMask,
|
||||
models.empty() ? nullptr : models.data(),
|
||||
static_cast<int>(models.size()), userName));
|
||||
}
|
||||
|
||||
idClipQuery idClip::Motion(trace_t* const result, const idVec3& start,
|
||||
const idVec3& end, const idRotation& rotation,
|
||||
const idClipModel* const movingModel, const idMat3& axis,
|
||||
const int contentMask, const int passEntityNumber,
|
||||
const bool ignoreWorld, const char* const userName) {
|
||||
std::vector<const idTraceModel*> traceModels;
|
||||
std::vector<idPositionedCollisionModel> models;
|
||||
CollectTraceModels(movingModel, traceModels);
|
||||
CollectPositionedModels(*this, contentMask, passEntityNumber,
|
||||
ignoreWorld, models);
|
||||
const idBounds bounds = MotionBounds(start, end, movingModel, axis);
|
||||
return ToClipQuery(collisionModelManager.Motion(result, start, end,
|
||||
rotation, bounds,
|
||||
traceModels.empty() ? nullptr : traceModels.data(),
|
||||
static_cast<int>(traceModels.size()), axis, contentMask,
|
||||
models.empty() ? nullptr : models.data(),
|
||||
static_cast<int>(models.size()), userName));
|
||||
}
|
||||
|
||||
idClipQuery idClip::MotionContacts(trace_t* const result,
|
||||
contactsResult_t* const contacts, const idVec3& start,
|
||||
const idVec3& end, const idRotation& rotation, const float depth,
|
||||
const idClipModel* const movingModel, const idMat3& axis,
|
||||
const int contentMask, const int passEntityNumber,
|
||||
const bool ignoreWorld, const char* const userName) {
|
||||
std::vector<const idTraceModel*> traceModels;
|
||||
std::vector<idPositionedCollisionModel> models;
|
||||
CollectTraceModels(movingModel, traceModels);
|
||||
CollectPositionedModels(*this, contentMask, passEntityNumber,
|
||||
ignoreWorld, models);
|
||||
const idBounds bounds = MotionBounds(start, end, movingModel, axis);
|
||||
return ToClipQuery(collisionModelManager.MotionContacts(result, contacts,
|
||||
start, end, rotation, depth, bounds,
|
||||
traceModels.empty() ? nullptr : traceModels.data(),
|
||||
static_cast<int>(traceModels.size()), axis, contentMask,
|
||||
models.empty() ? nullptr : models.data(),
|
||||
static_cast<int>(models.size()), userName));
|
||||
}
|
||||
|
||||
idClipQuery idClip::StepMove(trace_t* const result, const idVec3& start,
|
||||
const idVec3& end, const idVec3& downNormal, const float stepUp,
|
||||
const float stepDown, const idClipModel* const movingModel,
|
||||
const idMat3& axis, const int contentMask,
|
||||
const int passEntityNumber, const bool ignoreWorld,
|
||||
const char* const userName) {
|
||||
std::vector<const idTraceModel*> traceModels;
|
||||
std::vector<idPositionedCollisionModel> models;
|
||||
CollectTraceModels(movingModel, traceModels);
|
||||
CollectPositionedModels(*this, contentMask, passEntityNumber,
|
||||
ignoreWorld, models);
|
||||
const idBounds bounds = MotionBounds(start, end, movingModel, axis);
|
||||
return ToClipQuery(collisionModelManager.StepMove(result, start, end,
|
||||
downNormal, stepUp, stepDown, bounds,
|
||||
traceModels.empty() ? nullptr : traceModels.data(),
|
||||
static_cast<int>(traceModels.size()), axis, contentMask,
|
||||
models.empty() ? nullptr : models.data(),
|
||||
static_cast<int>(models.size()), userName));
|
||||
}
|
||||
|
||||
idClipQuery idClip::StepMoveContacts(trace_t* const result,
|
||||
contactsResult_t* const contacts, const idVec3& start,
|
||||
const idVec3& end, const idVec3& downNormal, const float stepUp,
|
||||
const float stepDown, const idClipModel* const movingModel,
|
||||
const idMat3& axis, const int contentMask,
|
||||
const int passEntityNumber, const bool ignoreWorld,
|
||||
const char* const userName) {
|
||||
std::vector<const idTraceModel*> traceModels;
|
||||
std::vector<idPositionedCollisionModel> models;
|
||||
CollectTraceModels(movingModel, traceModels);
|
||||
CollectPositionedModels(*this, contentMask, passEntityNumber,
|
||||
ignoreWorld, models);
|
||||
const idBounds bounds = MotionBounds(start, end, movingModel, axis);
|
||||
return ToClipQuery(collisionModelManager.StepMoveContacts(result,
|
||||
contacts, start, end, downNormal, stepUp, stepDown, bounds,
|
||||
traceModels.empty() ? nullptr : traceModels.data(),
|
||||
static_cast<int>(traceModels.size()), axis, contentMask,
|
||||
models.empty() ? nullptr : models.data(),
|
||||
static_cast<int>(models.size()), userName));
|
||||
}
|
||||
|
||||
idClipQuery idClip::SlideMoveContacts(trace_t* const result,
|
||||
contactsResult_t* const contacts, const idVec3& start,
|
||||
const idVec3& velocity, const idVec3& gravityVector,
|
||||
const float stepUp, const idClipModel* const movingModel,
|
||||
const idMat3& axis, const int contentMask,
|
||||
const int passEntityNumber, const bool ignoreWorld,
|
||||
const char* const userName) {
|
||||
std::vector<const idTraceModel*> traceModels;
|
||||
std::vector<idPositionedCollisionModel> models;
|
||||
CollectTraceModels(movingModel, traceModels);
|
||||
CollectPositionedModels(*this, contentMask, passEntityNumber,
|
||||
ignoreWorld, models);
|
||||
const idBounds bounds = MotionBounds(start, start + velocity,
|
||||
movingModel, axis);
|
||||
return ToClipQuery(collisionModelManager.SlideMoveContacts(result,
|
||||
contacts, start, velocity, gravityVector, stepUp, stepUp, bounds,
|
||||
traceModels.empty() ? nullptr : traceModels.data(),
|
||||
static_cast<int>(traceModels.size()), axis, contentMask,
|
||||
models.empty() ? nullptr : models.data(),
|
||||
static_cast<int>(models.size()), userName));
|
||||
}
|
||||
|
||||
idClipQuery idClip::Contents(trace_t* const result, const idVec3& start,
|
||||
const idClipModel* const movingModel, const idMat3& axis,
|
||||
const int contentMask, const int passEntityNumber,
|
||||
const char* const userName) {
|
||||
std::vector<const idTraceModel*> traceModels;
|
||||
std::vector<idPositionedCollisionModel> models;
|
||||
CollectTraceModels(movingModel, traceModels);
|
||||
CollectPositionedModels(*this, contentMask, passEntityNumber,
|
||||
false, models);
|
||||
const idBounds bounds = MotionBounds(start, start, movingModel, axis);
|
||||
return ToClipQuery(collisionModelManager.Contents(result, start, bounds,
|
||||
traceModels.empty() ? nullptr : traceModels.data(),
|
||||
static_cast<int>(traceModels.size()), axis, contentMask,
|
||||
models.empty() ? nullptr : models.data(),
|
||||
static_cast<int>(models.size()), userName));
|
||||
}
|
||||
|
||||
idClipQuery idClip::LocalContents(const idClipQuery localSpace,
|
||||
const idVec3& start, const idClipModel* const movingModel,
|
||||
const idMat3& axis, const int contentMask,
|
||||
const int passEntityNumber, const char* const userName) {
|
||||
std::vector<const idTraceModel*> traceModels;
|
||||
std::vector<idPositionedCollisionModel> models;
|
||||
CollectTraceModels(movingModel, traceModels);
|
||||
CollectPositionedModels(*this, contentMask, passEntityNumber,
|
||||
false, models);
|
||||
const idBounds bounds = MotionBounds(start, start, movingModel, axis);
|
||||
return ToClipQuery(collisionModelManager.LocalContents(
|
||||
ToCollisionQuery(localSpace), start, bounds,
|
||||
traceModels.empty() ? nullptr : traceModels.data(),
|
||||
static_cast<int>(traceModels.size()), axis, contentMask,
|
||||
models.empty() ? nullptr : models.data(),
|
||||
static_cast<int>(models.size()), userName));
|
||||
}
|
||||
|
||||
idClipQuery idClip::Contacts(contactsResult_t* const result,
|
||||
const idVec3& start, const idVec3& direction, const float depth,
|
||||
const idClipModel* const movingModel, const idMat3& axis,
|
||||
const int contentMask, const int passEntityNumber,
|
||||
const char* const userName) {
|
||||
std::vector<const idTraceModel*> traceModels;
|
||||
std::vector<idPositionedCollisionModel> models;
|
||||
CollectTraceModels(movingModel, traceModels);
|
||||
CollectPositionedModels(*this, contentMask, passEntityNumber,
|
||||
false, models);
|
||||
const idBounds bounds = MotionBounds(start, start + direction * depth,
|
||||
movingModel, axis);
|
||||
return ToClipQuery(collisionModelManager.Contacts(result, start,
|
||||
direction, depth, bounds,
|
||||
traceModels.empty() ? nullptr : traceModels.data(),
|
||||
static_cast<int>(traceModels.size()), axis, contentMask,
|
||||
models.empty() ? nullptr : models.data(),
|
||||
static_cast<int>(models.size()), userName));
|
||||
}
|
||||
|
||||
idClipQuery idClip::Clip(clipResult_t* const result, const idVec3& start,
|
||||
const idTraceModel* const traceModel, const idMat3& axis,
|
||||
const int contentMask, const int passEntityNumber,
|
||||
const char* const userName) {
|
||||
std::vector<idPositionedCollisionModel> models;
|
||||
CollectPositionedModels(*this, contentMask, passEntityNumber,
|
||||
false, models);
|
||||
const idTraceModel* traceModels[1] = { traceModel };
|
||||
const idBounds bounds = traceModel != nullptr
|
||||
? MotionBounds(start, start, nullptr, axis) : PointBounds(start);
|
||||
return ToClipQuery(collisionModelManager.Clip(result, start, bounds,
|
||||
traceModel != nullptr ? traceModels : nullptr,
|
||||
traceModel != nullptr ? 1 : 0, axis, contentMask,
|
||||
models.empty() ? nullptr : models.data(),
|
||||
static_cast<int>(models.size()), userName));
|
||||
}
|
||||
|
||||
void idClip::TranslationModel(trace_t& result, const idVec3& start,
|
||||
const idVec3& end, const idClipModel* const movingModel,
|
||||
const idMat3& axis, const int contentMask,
|
||||
const idVec3& modelOrigin, const idClipModel* const model,
|
||||
const idMat3& modelAxis) const {
|
||||
std::vector<const idTraceModel*> traces;
|
||||
CollectTraceModels(movingModel, traces);
|
||||
idPositionedCollisionModel positioned{};
|
||||
if (model != nullptr) {
|
||||
positioned.model = model->GetCollisionModel();
|
||||
positioned.modelOrigin = modelOrigin;
|
||||
positioned.modelAxis = modelAxis;
|
||||
positioned.modelEntityNum = model->GetEntityNumber();
|
||||
positioned.modelPhysicsId = model->GetPhysicsId();
|
||||
positioned.modelBodyId = model->GetBodyId();
|
||||
positioned.modelContentsOverride = model->GetContents();
|
||||
}
|
||||
collisionModelManager.Translation(&result, start, end,
|
||||
MotionBounds(start, end, movingModel, axis),
|
||||
traces.empty() ? nullptr : traces.data(),
|
||||
static_cast<int>(traces.size()), axis, contentMask,
|
||||
positioned.model != nullptr ? &positioned : nullptr,
|
||||
positioned.model != nullptr ? 1 : 0, "TranslationModel");
|
||||
}
|
||||
|
||||
void idClip::RotationModel(trace_t& result, const idVec3& start,
|
||||
const idRotation& rotation, const idClipModel* const movingModel,
|
||||
const idMat3& axis, const int contentMask,
|
||||
const idVec3& modelOrigin, const idClipModel* const model,
|
||||
const idMat3& modelAxis) const {
|
||||
std::vector<const idTraceModel*> traces;
|
||||
CollectTraceModels(movingModel, traces);
|
||||
idPositionedCollisionModel positioned{};
|
||||
if (model != nullptr) {
|
||||
positioned.model = model->GetCollisionModel();
|
||||
positioned.modelOrigin = modelOrigin;
|
||||
positioned.modelAxis = modelAxis;
|
||||
positioned.modelContentsOverride = model->GetContents();
|
||||
}
|
||||
collisionModelManager.Rotation(&result, start, rotation,
|
||||
MotionBounds(start, start, movingModel, axis),
|
||||
traces.empty() ? nullptr : traces.data(),
|
||||
static_cast<int>(traces.size()), axis, contentMask,
|
||||
positioned.model != nullptr ? &positioned : nullptr,
|
||||
positioned.model != nullptr ? 1 : 0, "RotationModel");
|
||||
}
|
||||
|
||||
void idClip::ContentsModel(trace_t& result, const idVec3& start,
|
||||
const idClipModel* const movingModel, const idMat3& axis,
|
||||
const int contentMask, const idVec3& modelOrigin,
|
||||
const idClipModel* const model, const idMat3& modelAxis) const {
|
||||
std::vector<const idTraceModel*> traces;
|
||||
CollectTraceModels(movingModel, traces);
|
||||
idPositionedCollisionModel positioned{};
|
||||
if (model != nullptr) {
|
||||
positioned.model = model->GetCollisionModel();
|
||||
positioned.modelOrigin = modelOrigin;
|
||||
positioned.modelAxis = modelAxis;
|
||||
positioned.modelContentsOverride = model->GetContents();
|
||||
}
|
||||
collisionModelManager.Contents(&result, start,
|
||||
MotionBounds(start, start, movingModel, axis),
|
||||
traces.empty() ? nullptr : traces.data(),
|
||||
static_cast<int>(traces.size()), axis, contentMask,
|
||||
positioned.model != nullptr ? &positioned : nullptr,
|
||||
positioned.model != nullptr ? 1 : 0, "ContentsModel");
|
||||
}
|
||||
|
||||
bool idClip::GetModelContactFeature(const contactInfo_t& contact,
|
||||
const idClipModel* const model, idFixedWinding& winding) const {
|
||||
if (model == nullptr) return false;
|
||||
idCollisionModel* const collisionModel = model->GetCollisionModel();
|
||||
if (collisionModel == nullptr) return false;
|
||||
if (contact.type == CONTACT_EDGE) {
|
||||
idVec3 points[2];
|
||||
if (!collisionModel->GetEdge(contact.modelFeature,
|
||||
points[0], points[1])) return false;
|
||||
winding.Clear();
|
||||
winding.AddPoint(points[0]);
|
||||
winding.AddPoint(points[1]);
|
||||
return true;
|
||||
}
|
||||
if (contact.type == CONTACT_MODELVERTEX) {
|
||||
idVec3 point;
|
||||
if (!collisionModel->GetVertex(contact.modelFeature, point))
|
||||
return false;
|
||||
winding.Clear();
|
||||
winding.AddPoint(point);
|
||||
return true;
|
||||
}
|
||||
return collisionModel->GetPolygon(contact.modelFeature, winding);
|
||||
}
|
||||
|
||||
void idClip::PrintStatistics() {
|
||||
}
|
||||
@@ -1,171 +1,317 @@
|
||||
#pragma once
|
||||
|
||||
// Reconstructed C++ declarations from IDA Local Types and PDB/DIA metadata.
|
||||
// Original PDB header: w:\tech5\engine\gamelib\physics\clip.h
|
||||
// Recovered logical types: 9
|
||||
// Signatures retain Xbox 360 ABI evidence and may still require manual review.
|
||||
#include "cm/jobs/collisionresults.h"
|
||||
#include "cm/jobs/collisionquery.h"
|
||||
#include "gamelib/physics/clipmodel.h"
|
||||
#include "idlib/blockalloc_base.h"
|
||||
#include "idlib/containers/list.h"
|
||||
#include "idlib/containers/staticlist.h"
|
||||
#include "idlib/math/rotation.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
// IDA Local Type ordinal 1833; PDB kind: enum.
|
||||
enum idClip::idClipCollisionQuery::type_t : __int32
|
||||
{
|
||||
QUERY_VALID = 0x0,
|
||||
QUERY_CANCELED = 0x1,
|
||||
QUERY_SUBMITTED_TRANSLATION = 0x2,
|
||||
QUERY_SUBMITTED_ROTATION = 0x3,
|
||||
QUERY_SUBMITTED_MOTION = 0x4,
|
||||
QUERY_SUBMITTED_MOTION_CONTACTS = 0x5,
|
||||
QUERY_SUBMITTED_STEPMOVE = 0x6,
|
||||
QUERY_SUBMITTED_STEPMOVE_CONTACTS = 0x7,
|
||||
QUERY_SUBMITTED_SLIDEMOVE = 0x8,
|
||||
QUERY_SUBMITTED_SLIDEMOVE_CONTACTS = 0x9,
|
||||
QUERY_SUBMITTED_CONTENTS = 0xA,
|
||||
QUERY_SUBMITTED_CONTACTS = 0xB,
|
||||
QUERY_SUBMITTED_CLIP = 0xC,
|
||||
class idAAS2;
|
||||
class idCollisionModel;
|
||||
class idFixedWinding;
|
||||
class idTraceModel;
|
||||
class idTraceModelCache;
|
||||
class idTraceModelRecycler;
|
||||
class idTypeInfoTools;
|
||||
struct idPositionedCollisionModel;
|
||||
|
||||
#ifndef ID_CLIP_QUERY_DEFINED
|
||||
#define ID_CLIP_QUERY_DEFINED
|
||||
struct idClipQuery { std::uint64_t index; };
|
||||
#endif
|
||||
|
||||
struct idGatherQuery { std::uint64_t index; };
|
||||
#ifndef ID_RESIDENCY_QUERY_DEFINED
|
||||
#define ID_RESIDENCY_QUERY_DEFINED
|
||||
struct idResidencyQuery { std::uint64_t index; };
|
||||
#endif
|
||||
|
||||
struct clipSector_t {
|
||||
int axis;
|
||||
float dist;
|
||||
clipSector_t* children[2];
|
||||
clipLink_t* clipLinks;
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 2572; PDB kind: enum.
|
||||
enum idClip::gatherType_t : __int32
|
||||
{
|
||||
GATHER_TOUCHING_BOUNDS = 0x0,
|
||||
GATHER_TOUCHING_LINE = 0x1,
|
||||
GATHER_TOUCHING_CLIPMODEL = 0x2,
|
||||
struct clipQueryStats_t {
|
||||
const char* userName;
|
||||
int count;
|
||||
int max;
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 14068; PDB kind: class.
|
||||
class idClip
|
||||
{
|
||||
class idClip {
|
||||
public:
|
||||
idClipModel *world;
|
||||
idClipModel *temporaryClipModel;
|
||||
idClipModel *defaultClipModel;
|
||||
idClipModel *playerMeleeClipModel;
|
||||
idClipModel *clip8x8;
|
||||
idClipModel *clip8x8x8;
|
||||
idClipModel *clip16x16;
|
||||
idClipModel *clip16x16x16;
|
||||
idClipModel *clip24x24;
|
||||
idClipModel *clip32x32;
|
||||
idClipModel *clip8x16;
|
||||
idClipModel *clip48x48;
|
||||
idClipModel *clip96x96;
|
||||
idClipModel *clip24x24x96;
|
||||
idClipModel *clip32x32x96;
|
||||
idClipModel *clip48x48x96;
|
||||
idBlockAlloc<clipLink_t,256,13> clipLinkAllocator;
|
||||
idStaticList<clipSector_t,8191> clipSectors;
|
||||
int touchCount;
|
||||
idAAS2 *aases[8];
|
||||
idTraceModelCache *traceModelCache;
|
||||
idTraceModelRecycler *traceModelRecycler;
|
||||
idClipModel *deletedClipModels;
|
||||
idList<int,13> streamAreas;
|
||||
idClip::idClipQueryParms *queryParms;
|
||||
int numQueryParms;
|
||||
idStaticList<unsigned int,2048> translationQueries;
|
||||
idStaticList<unsigned int,2048> rotationQueries;
|
||||
idStaticList<unsigned int,2048> motionQueries;
|
||||
idStaticList<unsigned int,2048> motionContactsQueries;
|
||||
idStaticList<unsigned int,2048> stepMoveQueries;
|
||||
idStaticList<unsigned int,2048> stepMoveContactsQueries;
|
||||
idStaticList<unsigned int,2048> slideMoveQueries;
|
||||
idStaticList<unsigned int,2048> slideMoveContactsQueries;
|
||||
idStaticList<unsigned int,2048> contentsQueries;
|
||||
idStaticList<unsigned int,2048> contactsQueries;
|
||||
idStaticList<unsigned int,2048> clipQueries;
|
||||
idStaticList<unsigned int,2048> localTranslationQueries;
|
||||
idStaticList<unsigned int,2048> localContentsQueries;
|
||||
idStaticList<unsigned int,2048> translationTraceModelQueries;
|
||||
idClip::idClipCollisionQuery collisionQueries[4096];
|
||||
unsigned __int64 collisionQueryFirstSubmittedIndex;
|
||||
unsigned __int64 collisionQueryLastSubmittedIndex;
|
||||
unsigned __int64 collisionQueryFirstAllocedIndex;
|
||||
unsigned __int64 collisionQueryLastAllocedIndex;
|
||||
unsigned __int64 collisionQueryLastResolvedIndex;
|
||||
const char *gatherQueryUserNames[256];
|
||||
idClip::idClipGatherQuery gatherQueries[256];
|
||||
unsigned __int64 gatherQueryFirstSubmittedIndex;
|
||||
unsigned __int64 gatherQueryLastSubmittedIndex;
|
||||
unsigned __int64 gatherQueryFirstAllocedIndex;
|
||||
unsigned __int64 gatherQueryLastAllocedIndex;
|
||||
idClip::idGatherResult gatherResults[2048];
|
||||
unsigned __int64 gatherResultFirstSubmittedIndex;
|
||||
unsigned __int64 gatherResultLastSubmittedIndex;
|
||||
unsigned __int64 gatherResultFirstAllocedIndex;
|
||||
unsigned __int64 gatherResultLastAllocedIndex;
|
||||
idClip::idClipResidencyQuery residencyQueries[1024];
|
||||
unsigned __int64 residencyQueryFirstSubmittedIndex;
|
||||
unsigned __int64 residencyQueryLastSubmittedIndex;
|
||||
unsigned __int64 residencyQueryFirstAllocedIndex;
|
||||
unsigned __int64 residencyQueryLastAllocedIndex;
|
||||
enum queryType_t : int {
|
||||
QUERY_VALID = 0,
|
||||
QUERY_CANCELED,
|
||||
QUERY_SUBMITTED_TRANSLATION,
|
||||
QUERY_SUBMITTED_ROTATION,
|
||||
QUERY_SUBMITTED_MOTION,
|
||||
QUERY_SUBMITTED_MOTION_CONTACTS,
|
||||
QUERY_SUBMITTED_STEPMOVE,
|
||||
QUERY_SUBMITTED_STEPMOVE_CONTACTS,
|
||||
QUERY_SUBMITTED_SLIDEMOVE,
|
||||
QUERY_SUBMITTED_SLIDEMOVE_CONTACTS,
|
||||
QUERY_SUBMITTED_CONTENTS,
|
||||
QUERY_SUBMITTED_CONTACTS,
|
||||
QUERY_SUBMITTED_CLIP
|
||||
};
|
||||
|
||||
enum gatherType_t : int {
|
||||
GATHER_TOUCHING_BOUNDS = 0,
|
||||
GATHER_TOUCHING_LINE = 1,
|
||||
GATHER_TOUCHING_CLIPMODEL = 2
|
||||
};
|
||||
|
||||
struct idClipQueryParms {
|
||||
idVec3 start;
|
||||
idVec3 end;
|
||||
idVec3 origin;
|
||||
idVec3 vec;
|
||||
float f1;
|
||||
float f2;
|
||||
idMat3 startAxis;
|
||||
int clipMask;
|
||||
std::uint16_t passEntityNumber;
|
||||
std::uint16_t flags;
|
||||
std::uint8_t pad[12];
|
||||
unsigned int queryIndex;
|
||||
const idClipModel* clipModel;
|
||||
const idTraceModel* traceModel;
|
||||
const char* userName;
|
||||
};
|
||||
|
||||
struct idClipCollisionQuery {
|
||||
queryType_t type;
|
||||
int parmIndex;
|
||||
idClipModel* clipModel;
|
||||
idCollisionQuery query;
|
||||
};
|
||||
|
||||
struct idClipGatherQuery {
|
||||
idVec3 vectors[4];
|
||||
int clipMask;
|
||||
std::int16_t passEntityNumber;
|
||||
std::int16_t gatherType;
|
||||
std::uint16_t firstResultIndex;
|
||||
std::uint16_t numResults;
|
||||
const idClipModel* clipModel;
|
||||
};
|
||||
|
||||
struct idGatherResult {
|
||||
idCollisionQuery query;
|
||||
int physicsId;
|
||||
std::int16_t bodyId;
|
||||
std::int16_t entityNumber;
|
||||
};
|
||||
|
||||
struct idClipResidencyQuery {
|
||||
idBounds absBounds;
|
||||
bool resident;
|
||||
std::uint8_t pad[7];
|
||||
};
|
||||
|
||||
idClip();
|
||||
~idClip();
|
||||
void Init(idCollisionModel* worldModel, idTraceModelCache* cache,
|
||||
idTraceModelRecycler* recycler);
|
||||
void Shutdown();
|
||||
void StartQueryFrame();
|
||||
void EndQueryFrame();
|
||||
void ResolveCollisionQueries();
|
||||
void DeleteClipModels();
|
||||
void DeleteClipModel(idClipModel* clipModel);
|
||||
void InitializeAASLinks(int aasType);
|
||||
const idBounds& GetWorldBounds() const;
|
||||
void AddStreamArea(int areaNum);
|
||||
void ClearStreamAreas();
|
||||
int FindStreamArea(const idClipModel* clipModel);
|
||||
|
||||
int GetClipModelsTouchingBounds(const idBounds& bounds, int contentMask,
|
||||
idClipModel** clipModels, int maxClipModels) const;
|
||||
int GetClipModelsTouchingLine(const idVec3& start, const idVec3& end,
|
||||
int contentMask, idClipModel** clipModels,
|
||||
int maxClipModels) const;
|
||||
int GetEntitiesTouchingBounds(const idBounds& bounds, int contentMask,
|
||||
int* entityNumbers, int maxEntities) const;
|
||||
int GetEntitiesTouchingLine(const idVec3& start, const idVec3& end,
|
||||
int contentMask, int* entityNumbers, int maxEntities) const;
|
||||
idGatherQuery GatherTouchingBounds(const idBounds& bounds,
|
||||
int contentMask, int passEntityNumber, const char* userName);
|
||||
idGatherQuery GatherTouchingClipModel(const idVec3& origin,
|
||||
const idClipModel* clipModel, const idMat3& axis, int contentMask,
|
||||
int passEntityNumber, const char* userName);
|
||||
int GetGatheredClipModels(const idGatherQuery& query,
|
||||
idClipModel** clipModels, int maxClipModels);
|
||||
int GetGatheredEntities(const idGatherQuery& query,
|
||||
int* entityNumbers, int maxEntities);
|
||||
idResidencyQuery TestResidency(const idBounds& bounds);
|
||||
|
||||
idClipQuery Translation(trace_t* result, const idVec3& start,
|
||||
const idVec3& end, const idClipModel* clipModel,
|
||||
const idMat3& axis, int contentMask, int passEntityNumber,
|
||||
bool ignoreWorld, const char* userName);
|
||||
idClipQuery LocalTranslation(idClipQuery localSpace,
|
||||
const idVec3& start, const idVec3& end,
|
||||
const idClipModel* clipModel, const idMat3& axis,
|
||||
int contentMask, int passEntityNumber, bool ignoreWorld,
|
||||
const char* userName);
|
||||
idClipQuery TranslationTraceModel(trace_t* result,
|
||||
const idVec3& start, const idVec3& end,
|
||||
const idClipModel* clipModel, const idVec3& modelOrigin,
|
||||
const idMat3& modelAxis, int contentMask, const char* userName);
|
||||
idClipQuery Rotation(trace_t* result, const idVec3& start,
|
||||
const idRotation& rotation, const idClipModel* clipModel,
|
||||
const idMat3& axis, int contentMask, int passEntityNumber,
|
||||
bool ignoreWorld, const char* userName);
|
||||
idClipQuery Motion(trace_t* result, const idVec3& start,
|
||||
const idVec3& end, const idRotation& rotation,
|
||||
const idClipModel* clipModel, const idMat3& axis,
|
||||
int contentMask, int passEntityNumber, bool ignoreWorld,
|
||||
const char* userName);
|
||||
idClipQuery MotionContacts(trace_t* result, contactsResult_t* contacts,
|
||||
const idVec3& start, const idVec3& end,
|
||||
const idRotation& rotation, float depth,
|
||||
const idClipModel* clipModel, const idMat3& axis,
|
||||
int contentMask, int passEntityNumber, bool ignoreWorld,
|
||||
const char* userName);
|
||||
idClipQuery StepMove(trace_t* result, const idVec3& start,
|
||||
const idVec3& end, const idVec3& downNormal,
|
||||
float stepUp, float stepDown, const idClipModel* clipModel,
|
||||
const idMat3& axis, int contentMask, int passEntityNumber,
|
||||
bool ignoreWorld, const char* userName);
|
||||
idClipQuery StepMoveContacts(trace_t* result,
|
||||
contactsResult_t* contacts, const idVec3& start,
|
||||
const idVec3& end, const idVec3& downNormal,
|
||||
float stepUp, float stepDown, const idClipModel* clipModel,
|
||||
const idMat3& axis, int contentMask, int passEntityNumber,
|
||||
bool ignoreWorld, const char* userName);
|
||||
idClipQuery SlideMoveContacts(trace_t* result,
|
||||
contactsResult_t* contacts, const idVec3& start,
|
||||
const idVec3& end, const idVec3& downNormal,
|
||||
float stepUp, const idClipModel* clipModel, const idMat3& axis,
|
||||
int contentMask, int passEntityNumber, bool ignoreWorld,
|
||||
const char* userName);
|
||||
idClipQuery Contents(trace_t* result, const idVec3& start,
|
||||
const idClipModel* clipModel, const idMat3& axis,
|
||||
int contentMask, int passEntityNumber, const char* userName);
|
||||
idClipQuery LocalContents(idClipQuery localSpace, const idVec3& start,
|
||||
const idClipModel* clipModel, const idMat3& axis,
|
||||
int contentMask, int passEntityNumber, const char* userName);
|
||||
idClipQuery Contacts(contactsResult_t* result, const idVec3& start,
|
||||
const idVec3& direction, float depth,
|
||||
const idClipModel* clipModel, const idMat3& axis,
|
||||
int contentMask, int passEntityNumber, const char* userName);
|
||||
idClipQuery Clip(clipResult_t* result, const idVec3& start,
|
||||
const idTraceModel* traceModel, const idMat3& axis,
|
||||
int contentMask, int passEntityNumber, const char* userName);
|
||||
|
||||
void TranslationModel(trace_t& result, const idVec3& start,
|
||||
const idVec3& end, const idClipModel* clipModel,
|
||||
const idMat3& axis, int contentMask, const idVec3& modelOrigin,
|
||||
const idClipModel* model, const idMat3& modelAxis) const;
|
||||
void RotationModel(trace_t& result, const idVec3& start,
|
||||
const idRotation& rotation, const idClipModel* clipModel,
|
||||
const idMat3& axis, int contentMask, const idVec3& modelOrigin,
|
||||
const idClipModel* model, const idMat3& modelAxis) const;
|
||||
void ContentsModel(trace_t& result, const idVec3& start,
|
||||
const idClipModel* clipModel, const idMat3& axis, int contentMask,
|
||||
const idVec3& modelOrigin, const idClipModel* model,
|
||||
const idMat3& modelAxis) const;
|
||||
bool GetModelContactFeature(const contactInfo_t& contact,
|
||||
const idClipModel* model, idFixedWinding& winding) const;
|
||||
void PrintStatistics();
|
||||
void DrawWorld(const idVec3&, const idMat3&, float, bool) {}
|
||||
void DrawSingleClipModel(idClipModel*, const idVec3&,
|
||||
const idMat3&, int) {}
|
||||
void ShowCollisionPoint(const contactInfo_t&,
|
||||
const idTypeInfoTools*) {}
|
||||
void ShowCollisionSurfaces(const idVec3&, const idMat3&, int, int,
|
||||
const idTypeInfoTools*) {}
|
||||
|
||||
idClipModel* world;
|
||||
idClipModel* temporaryClipModel;
|
||||
idClipModel* defaultClipModel;
|
||||
idClipModel* playerMeleeClipModel;
|
||||
idClipModel* clip8x8;
|
||||
idClipModel* clip8x8x8;
|
||||
idClipModel* clip16x16;
|
||||
idClipModel* clip16x16x16;
|
||||
idClipModel* clip24x24;
|
||||
idClipModel* clip32x32;
|
||||
idClipModel* clip8x16;
|
||||
idClipModel* clip48x48;
|
||||
idClipModel* clip96x96;
|
||||
idClipModel* clip24x24x96;
|
||||
idClipModel* clip32x32x96;
|
||||
idClipModel* clip48x48x96;
|
||||
idBlockAlloc<clipLink_t, 256, 13> clipLinkAllocator;
|
||||
idStaticList<clipSector_t, 8191> clipSectors;
|
||||
int touchCount;
|
||||
idAAS2* aases[8];
|
||||
idTraceModelCache* traceModelCache;
|
||||
idTraceModelRecycler* traceModelRecycler;
|
||||
idClipModel* deletedClipModels;
|
||||
idList<int, 13> streamAreas;
|
||||
idClipQueryParms* queryParms;
|
||||
int numQueryParms;
|
||||
idStaticList<unsigned int, 2048> translationQueries;
|
||||
idStaticList<unsigned int, 2048> rotationQueries;
|
||||
idStaticList<unsigned int, 2048> motionQueries;
|
||||
idStaticList<unsigned int, 2048> motionContactsQueries;
|
||||
idStaticList<unsigned int, 2048> stepMoveQueries;
|
||||
idStaticList<unsigned int, 2048> stepMoveContactsQueries;
|
||||
idStaticList<unsigned int, 2048> slideMoveQueries;
|
||||
idStaticList<unsigned int, 2048> slideMoveContactsQueries;
|
||||
idStaticList<unsigned int, 2048> contentsQueries;
|
||||
idStaticList<unsigned int, 2048> contactsQueries;
|
||||
idStaticList<unsigned int, 2048> clipQueries;
|
||||
idStaticList<unsigned int, 2048> localTranslationQueries;
|
||||
idStaticList<unsigned int, 2048> localContentsQueries;
|
||||
idStaticList<unsigned int, 2048> translationTraceModelQueries;
|
||||
idClipCollisionQuery collisionQueries[4096];
|
||||
std::uint64_t collisionQueryFirstSubmittedIndex;
|
||||
std::uint64_t collisionQueryLastSubmittedIndex;
|
||||
std::uint64_t collisionQueryFirstAllocedIndex;
|
||||
std::uint64_t collisionQueryLastAllocedIndex;
|
||||
std::uint64_t collisionQueryLastResolvedIndex;
|
||||
const char* gatherQueryUserNames[256];
|
||||
idClipGatherQuery gatherQueries[256];
|
||||
std::uint64_t gatherQueryFirstSubmittedIndex;
|
||||
std::uint64_t gatherQueryLastSubmittedIndex;
|
||||
std::uint64_t gatherQueryFirstAllocedIndex;
|
||||
std::uint64_t gatherQueryLastAllocedIndex;
|
||||
idGatherResult gatherResults[2048];
|
||||
std::uint64_t gatherResultFirstSubmittedIndex;
|
||||
std::uint64_t gatherResultLastSubmittedIndex;
|
||||
std::uint64_t gatherResultFirstAllocedIndex;
|
||||
std::uint64_t gatherResultLastAllocedIndex;
|
||||
idClipResidencyQuery residencyQueries[1024];
|
||||
std::uint64_t residencyQueryFirstSubmittedIndex;
|
||||
std::uint64_t residencyQueryLastSubmittedIndex;
|
||||
std::uint64_t residencyQueryFirstAllocedIndex;
|
||||
std::uint64_t residencyQueryLastAllocedIndex;
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 14128; PDB kind: class.
|
||||
class idClip::idClipQueryParms
|
||||
{
|
||||
public:
|
||||
idVec3 start;
|
||||
idVec3 end;
|
||||
idVec3 origin;
|
||||
idVec3 vec;
|
||||
float f1;
|
||||
float f2;
|
||||
idMat3 startAxis;
|
||||
int clipMask;
|
||||
unsigned __int16 passEntityNumber;
|
||||
unsigned __int16 flags;
|
||||
unsigned __int8 pad[12];
|
||||
unsigned int queryIndex;
|
||||
const idClipModel *clipModel;
|
||||
const idTraceModel *traceModel;
|
||||
const char *userName;
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 14131; PDB kind: class.
|
||||
class idClip::idClipCollisionQuery
|
||||
{
|
||||
public:
|
||||
idClip::idClipCollisionQuery::type_t type;
|
||||
int parmIndex;
|
||||
idClipModel *clipModel;
|
||||
idCollisionQuery query;
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 14132; PDB kind: class.
|
||||
class idClip::idClipGatherQuery
|
||||
{
|
||||
public:
|
||||
idVec3 vectors[4];
|
||||
int clipMask;
|
||||
__int16 passEntityNumber;
|
||||
__int16 gatherType;
|
||||
unsigned __int16 firstResultIndex;
|
||||
unsigned __int16 numResults;
|
||||
const idClipModel *clipModel;
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 14133; PDB kind: class.
|
||||
class idClip::idGatherResult
|
||||
{
|
||||
public:
|
||||
idCollisionQuery query;
|
||||
int physicsId;
|
||||
__int16 bodyId;
|
||||
__int16 entityNumber;
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 14134; PDB kind: class.
|
||||
class idClip::idClipResidencyQuery
|
||||
{
|
||||
public:
|
||||
idBounds absBounds;
|
||||
bool resident;
|
||||
unsigned __int8 pad[7];
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 23461; PDB kind: struct.
|
||||
struct idClip::GetClipModelsTouchingLine::__l2::stackPoints_t
|
||||
{
|
||||
idVec4 start;
|
||||
idVec4 end;
|
||||
};
|
||||
static_assert(sizeof(idClipQuery) == 8,
|
||||
"Recovered clip-query ABI changed");
|
||||
static_assert(sizeof(idGatherQuery) == 8,
|
||||
"Recovered gather-query ABI changed");
|
||||
static_assert(sizeof(idResidencyQuery) == 8,
|
||||
"Recovered residency-query ABI changed");
|
||||
#if INTPTR_MAX == INT32_MAX
|
||||
static_assert(sizeof(idClip::idClipQueryParms) == 128,
|
||||
"Recovered clip query parameters ABI changed");
|
||||
static_assert(sizeof(idClip::idClipCollisionQuery) == 24,
|
||||
"Recovered collision query ABI changed");
|
||||
static_assert(sizeof(idClip::idClipGatherQuery) == 64,
|
||||
"Recovered gather query ABI changed");
|
||||
static_assert(sizeof(idClip::idGatherResult) == 16,
|
||||
"Recovered gather result ABI changed");
|
||||
static_assert(sizeof(idClip::idClipResidencyQuery) == 32,
|
||||
"Recovered residency state ABI changed");
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,279 @@
|
||||
#include "gamelib/physics/clipmodel.h"
|
||||
|
||||
#include "cm/collisionmodel.h"
|
||||
#include "cm/collisionmodelmanager.h"
|
||||
#include "gamelib/aas2/aas2.h"
|
||||
#include "gamelib/physics/clip.h"
|
||||
#include "gamelib/physics/tracemodelcache.h"
|
||||
#include "idlib/geometry/tracemodel.h"
|
||||
#include "idlib/lib_print.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace {
|
||||
|
||||
void ClearAASLinks(idClipModel& model) {
|
||||
for (idAASClipLink*& link : model.aasLinks) link = nullptr;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
idClipModel::idClipModel() {
|
||||
Init(nullptr);
|
||||
}
|
||||
|
||||
idClipModel::idClipModel(idClip* const ownerClip, const char* modelName,
|
||||
const idMaterial* const material) {
|
||||
Init(ownerClip);
|
||||
LoadCollisionModel(modelName, material);
|
||||
}
|
||||
|
||||
idClipModel::idClipModel(idClip* const ownerClip,
|
||||
idCollisionModel* const model) {
|
||||
Init(ownerClip);
|
||||
LoadCollisionModel(model);
|
||||
}
|
||||
|
||||
idClipModel::idClipModel(idClip* const ownerClip,
|
||||
const idTraceModel* const traceModel, const int traceModelIndex,
|
||||
const idMaterial* const material) {
|
||||
Init(ownerClip);
|
||||
LoadTraceModel(traceModel, traceModelIndex, material);
|
||||
}
|
||||
|
||||
idClipModel::idClipModel(idClip* const ownerClip,
|
||||
idTreeAnimator* const animator) {
|
||||
Init(ownerClip);
|
||||
LoadAnimator(animator);
|
||||
}
|
||||
|
||||
idClipModel::idClipModel(idClip* const ownerClip,
|
||||
const idClipModel& other) {
|
||||
Init(ownerClip);
|
||||
enabled = other.enabled;
|
||||
entityNumber = other.entityNumber;
|
||||
ownerNumber = other.ownerNumber;
|
||||
physicsId = other.physicsId;
|
||||
bodyId = other.bodyId;
|
||||
bodyMask = other.bodyMask;
|
||||
origin = other.origin;
|
||||
axis = other.axis;
|
||||
bounds = other.bounds;
|
||||
absBounds = other.absBounds;
|
||||
contents = other.contents;
|
||||
collisionModel = other.collisionModel;
|
||||
animatedModel = other.animatedModel;
|
||||
if (clip != nullptr && clip->traceModelCache != nullptr) {
|
||||
for (int index = 0; index < other.traceModels.Num(); ++index) {
|
||||
traceModels.Append(clip->traceModelCache->CopyTraceModel(
|
||||
other.traceModels[index]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
idClipModel::~idClipModel() {
|
||||
Unlink();
|
||||
FreeModel();
|
||||
}
|
||||
|
||||
void idClipModel::Init(idClip* const ownerClip) {
|
||||
enabled = true;
|
||||
deleted = false;
|
||||
entityNumber = -1;
|
||||
ownerNumber = -1;
|
||||
physicsId = 0;
|
||||
bodyId = 0;
|
||||
bodyMask = -1;
|
||||
origin.Zero();
|
||||
axis = idMat3(1.0f);
|
||||
bounds[0].Zero();
|
||||
bounds[1].Zero();
|
||||
absBounds = bounds;
|
||||
contents = 0;
|
||||
collisionModel = nullptr;
|
||||
traceModels.Clear();
|
||||
animatedModel = nullptr;
|
||||
clip = ownerClip;
|
||||
clipLinks = nullptr;
|
||||
collisionQuery.offset = 0;
|
||||
touchCount = 0;
|
||||
nextDeleted = nullptr;
|
||||
ClearAASLinks(*this);
|
||||
}
|
||||
|
||||
void idClipModel::Delete() {
|
||||
if (deleted) return;
|
||||
deleted = true;
|
||||
if (clip != nullptr) clip->DeleteClipModel(this);
|
||||
}
|
||||
|
||||
idCollisionModel* idClipModel::NoTraceModelWarning() const {
|
||||
idLibPrint::Warning("idClipModel::GetCollisionModel: clip model %d on "
|
||||
"entity %d is not a collision or trace model", bodyId, entityNumber);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
idCollisionModel* idClipModel::GetCollisionModel() const {
|
||||
if (collisionModel != nullptr) return collisionModel;
|
||||
if (clip != nullptr && clip->traceModelCache != nullptr
|
||||
&& traceModels.Num() > 0) {
|
||||
const idTraceModelCache::trmCache_t* const entry =
|
||||
clip->traceModelCache->GetEntry(traceModels[0]);
|
||||
if (entry != nullptr) return entry->collisionModel;
|
||||
}
|
||||
return NoTraceModelWarning();
|
||||
}
|
||||
|
||||
const idTraceModel* idClipModel::GetTraceModel(const int index) const {
|
||||
if (clip == nullptr || clip->traceModelCache == nullptr
|
||||
|| index < 0 || index >= traceModels.Num()) return nullptr;
|
||||
const idTraceModelCache::trmCache_t* const entry =
|
||||
clip->traceModelCache->GetEntry(traceModels[index]);
|
||||
return entry != nullptr ? entry->trm : nullptr;
|
||||
}
|
||||
|
||||
void idClipModel::GetMassProperties(const float density, float& mass,
|
||||
idVec3& centerOfMass, idMat3& inertiaTensor) const {
|
||||
mass = 0.0f;
|
||||
centerOfMass.Zero();
|
||||
inertiaTensor[0].Zero();
|
||||
inertiaTensor[1].Zero();
|
||||
inertiaTensor[2].Zero();
|
||||
if (clip == nullptr || clip->traceModelCache == nullptr) return;
|
||||
for (int index = 0; index < traceModels.Num(); ++index) {
|
||||
float partMass = 0.0f;
|
||||
idVec3 partCenter;
|
||||
idMat3 partInertia;
|
||||
clip->traceModelCache->GetMassProperties(traceModels[index],
|
||||
density, partMass, partCenter, partInertia);
|
||||
centerOfMass = centerOfMass + partCenter * partMass;
|
||||
mass += partMass;
|
||||
for (int row = 0; row < 3; ++row)
|
||||
for (int column = 0; column < 3; ++column)
|
||||
inertiaTensor[row][column] += partInertia[row][column];
|
||||
}
|
||||
if (mass > 0.0f) centerOfMass = centerOfMass * (1.0f / mass);
|
||||
}
|
||||
|
||||
void idClipModel::SetContents(const int newContents) {
|
||||
if (contents == newContents) return;
|
||||
const bool relink = IsLinked();
|
||||
if (relink) Unlink();
|
||||
contents = newContents;
|
||||
if (relink) Link();
|
||||
}
|
||||
|
||||
void idClipModel::FreeModel() {
|
||||
if (clip != nullptr && clip->traceModelCache != nullptr) {
|
||||
for (int index = 0; index < traceModels.Num(); ++index)
|
||||
clip->traceModelCache->FreeTraceModel(traceModels[index]);
|
||||
}
|
||||
traceModels.ClearFree();
|
||||
collisionModel = nullptr;
|
||||
animatedModel = nullptr;
|
||||
}
|
||||
|
||||
bool idClipModel::LoadCollisionModel(const char* const modelName,
|
||||
const idMaterial*) {
|
||||
if (modelName == nullptr || *modelName == '\0') return false;
|
||||
LoadCollisionModel(collisionModelManager.LoadModel(modelName));
|
||||
return collisionModel != nullptr;
|
||||
}
|
||||
|
||||
void idClipModel::LoadCollisionModel(idCollisionModel* const model) {
|
||||
FreeModel();
|
||||
collisionModel = model;
|
||||
if (model != nullptr) {
|
||||
model->GetBounds(bounds);
|
||||
model->GetContents(contents);
|
||||
absBounds.FromTransformedBounds(bounds, origin, axis);
|
||||
}
|
||||
}
|
||||
|
||||
void idClipModel::LoadTraceModel(const idTraceModel* const traceModel,
|
||||
const int traceModelIndex, const idMaterial* const material) {
|
||||
FreeModel();
|
||||
if (traceModel == nullptr || clip == nullptr
|
||||
|| clip->traceModelCache == nullptr) return;
|
||||
const int index = traceModelIndex >= 0
|
||||
? clip->traceModelCache->CopyTraceModel(traceModelIndex)
|
||||
: clip->traceModelCache->AllocTraceModel(*traceModel, material);
|
||||
traceModels.Append(index);
|
||||
bounds = traceModel->bounds;
|
||||
absBounds.FromTransformedBounds(bounds, origin, axis);
|
||||
}
|
||||
|
||||
void idClipModel::LoadAnimator(idTreeAnimator* const animator) {
|
||||
FreeModel();
|
||||
animatedModel = animator;
|
||||
}
|
||||
|
||||
void idClipModel::Serialize(idSerializer&) {
|
||||
// The serializer transport lives in network; all recovered clip-model
|
||||
// state is held in explicit fields and can be serialized by that layer.
|
||||
}
|
||||
|
||||
void idClipModel::Unlink() {
|
||||
for (int aas = 0; aas < 8; ++aas) {
|
||||
if (aasLinks[aas] != nullptr && clip != nullptr
|
||||
&& clip->aases[aas] != nullptr) {
|
||||
aasLinks[aas] = clip->aases[aas]->UnLinkClipModel(aasLinks[aas]);
|
||||
}
|
||||
}
|
||||
while (clipLinks != nullptr) {
|
||||
clipLink_t* const link = clipLinks;
|
||||
clipLinks = link->nextLink;
|
||||
if (link->prevInSector != nullptr)
|
||||
link->prevInSector->nextInSector = link->nextInSector;
|
||||
else if (link->sector != nullptr)
|
||||
link->sector->clipLinks = link->nextInSector;
|
||||
if (link->nextInSector != nullptr)
|
||||
link->nextInSector->prevInSector = link->prevInSector;
|
||||
if (clip != nullptr) clip->clipLinkAllocator.Free(link);
|
||||
}
|
||||
}
|
||||
|
||||
void idClipModel::Link_r(clipSector_t* const sector) {
|
||||
if (sector == nullptr || clip == nullptr) return;
|
||||
clipLink_t* const link = clip->clipLinkAllocator.Alloc();
|
||||
if (link == nullptr) return;
|
||||
link->clipModel = this;
|
||||
link->sector = sector;
|
||||
link->prevInSector = nullptr;
|
||||
link->nextInSector = sector->clipLinks;
|
||||
if (sector->clipLinks != nullptr)
|
||||
sector->clipLinks->prevInSector = link;
|
||||
sector->clipLinks = link;
|
||||
link->nextLink = clipLinks;
|
||||
clipLinks = link;
|
||||
}
|
||||
|
||||
void idClipModel::Link() {
|
||||
if (clip == nullptr || deleted) return;
|
||||
Unlink();
|
||||
absBounds.FromTransformedBounds(bounds, origin, axis);
|
||||
if (!clip->clipSectors.IsEmpty()) Link_r(&clip->clipSectors[0]);
|
||||
for (int aas = 0; aas < 8; ++aas) {
|
||||
if (clip->aases[aas] != nullptr)
|
||||
aasLinks[aas] = clip->aases[aas]->LinkClipModel(this);
|
||||
}
|
||||
}
|
||||
|
||||
void idClipModel::Link(const int entityNum, const int ownerNum,
|
||||
const int physicsBodyId, const idVec3& newOrigin,
|
||||
const idMat3& newAxis) {
|
||||
entityNumber = entityNum;
|
||||
ownerNumber = ownerNum;
|
||||
physicsId = physicsBodyId;
|
||||
bodyId = physicsBodyId;
|
||||
origin = newOrigin;
|
||||
axis = newAxis;
|
||||
Link();
|
||||
}
|
||||
|
||||
void idClipModel::Link(const int entityNum, const int ownerNum,
|
||||
const int physicsBodyId, const idVec3& newOrigin,
|
||||
const idMat3& newAxis, const idBounds& newBounds) {
|
||||
bounds = newBounds;
|
||||
Link(entityNum, ownerNum, physicsBodyId, newOrigin, newAxis);
|
||||
}
|
||||
@@ -1,87 +1,156 @@
|
||||
#pragma once
|
||||
|
||||
// Reconstructed C++ declarations from IDA Local Types and PDB/DIA metadata.
|
||||
// Original PDB header: w:\tech5\engine\gamelib\physics\clipmodel.h
|
||||
// Recovered logical types: 6
|
||||
// Signatures retain Xbox 360 ABI evidence and may still require manual review.
|
||||
#include "cm/jobs/collisionquery.h"
|
||||
#include "idlib/bv/bounds.h"
|
||||
#include "idlib/containers/list.h"
|
||||
#include "idlib/text/atomicstring.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
// IDA Local Type ordinal 1525; PDB kind: enum.
|
||||
enum idClipModelInfo::clipModelType_t : __int32
|
||||
{
|
||||
CLIPMODEL_NONE = 0x0,
|
||||
CLIPMODEL_RENDERMODEL = 0x1,
|
||||
CLIPMODEL_CUSTOM = 0x2,
|
||||
CLIPMODEL_BOX = 0x3,
|
||||
CLIPMODEL_CYLINDER = 0x4,
|
||||
CLIPMODEL_CONE = 0x5,
|
||||
CLIPMODEL_BOUNDINGBOX = 0x6,
|
||||
class idAASClipLink;
|
||||
class idClip;
|
||||
class idCollisionModel;
|
||||
class idMaterial;
|
||||
class idSerializer;
|
||||
class idTraceModel;
|
||||
class idTreeAnimator;
|
||||
struct clipSector_t;
|
||||
|
||||
enum clipMode_t : int {
|
||||
CLIPMODE_INTERSECT = 0,
|
||||
CLIPMODE_UNION = 1,
|
||||
CLIPMODE_DIFFERENCE = 2,
|
||||
CLIPMODE_XOR = 3
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 1973; PDB kind: enum.
|
||||
enum clipMode_t : __int32
|
||||
{
|
||||
CLIPMODE_INTERSECT = 0x0,
|
||||
CLIPMODE_UNION = 0x1,
|
||||
CLIPMODE_DIFFERENCE = 0x2,
|
||||
CLIPMODE_XOR = 0x3,
|
||||
struct clipLink_t {
|
||||
class idClipModel* clipModel;
|
||||
clipSector_t* sector;
|
||||
clipLink_t* prevInSector;
|
||||
clipLink_t* nextInSector;
|
||||
clipLink_t* nextLink;
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 14041; PDB kind: class.
|
||||
class idClipModel
|
||||
{
|
||||
class idClipModel {
|
||||
public:
|
||||
// Recovered virtual interface; IDA vtable ordinal 14042.
|
||||
virtual ~idClipModel();
|
||||
idClipModel();
|
||||
idClipModel(idClip* ownerClip, const char* modelName,
|
||||
const idMaterial* material = nullptr);
|
||||
idClipModel(idClip* ownerClip, idCollisionModel* model);
|
||||
idClipModel(idClip* ownerClip, const idTraceModel* traceModel,
|
||||
int traceModelIndex, const idMaterial* material = nullptr);
|
||||
idClipModel(idClip* ownerClip, idTreeAnimator* animator);
|
||||
idClipModel(idClip* ownerClip, const idClipModel& other);
|
||||
virtual ~idClipModel();
|
||||
|
||||
__declspec(align(8)) bool enabled;
|
||||
bool deleted;
|
||||
int entityNumber;
|
||||
int ownerNumber;
|
||||
int physicsId;
|
||||
int bodyId;
|
||||
int bodyMask;
|
||||
idVec3 origin;
|
||||
idMat3 axis;
|
||||
idBounds bounds;
|
||||
idBounds absBounds;
|
||||
int contents;
|
||||
idCollisionModel *collisionModel;
|
||||
idList<int,13> traceModels;
|
||||
idTreeAnimator *animatedModel;
|
||||
idClip *clip;
|
||||
clipLink_t *clipLinks;
|
||||
idCollisionQuery collisionQuery;
|
||||
int touchCount;
|
||||
idClipModel *nextDeleted;
|
||||
idAASClipLink *aasLinks[8];
|
||||
void Delete();
|
||||
void Unlink();
|
||||
void Link();
|
||||
void Link(int entityNum, int ownerNum, int physicsBodyId,
|
||||
const idVec3& newOrigin, const idMat3& newAxis);
|
||||
void Link(int entityNum, int ownerNum, int physicsBodyId,
|
||||
const idVec3& newOrigin, const idMat3& newAxis,
|
||||
const idBounds& newBounds);
|
||||
void SetContents(int newContents);
|
||||
int GetContents() const { return contents; }
|
||||
void Enable() { enabled = true; }
|
||||
void Disable() { enabled = false; }
|
||||
bool IsEnabled() const { return enabled; }
|
||||
bool IsLinked() const { return clipLinks != nullptr; }
|
||||
bool IsDeleted() const { return deleted; }
|
||||
int GetEntityNumber() const { return entityNumber; }
|
||||
int GetOwnerNumber() const { return ownerNumber; }
|
||||
int GetPhysicsId() const { return physicsId; }
|
||||
int GetBodyId() const { return bodyId; }
|
||||
int GetBodyMask() const { return bodyMask; }
|
||||
void SetBodyMask(int mask) { bodyMask = mask; }
|
||||
const idVec3& GetOrigin() const { return origin; }
|
||||
const idMat3& GetAxis() const { return axis; }
|
||||
const idBounds& GetBounds() const { return bounds; }
|
||||
const idBounds& GetAbsBounds() const { return absBounds; }
|
||||
idCollisionModel* GetCollisionModel() const;
|
||||
const idTraceModel* GetTraceModel(int index = 0) const;
|
||||
int GetNumTraceModels() const { return traceModels.Num(); }
|
||||
void GetMassProperties(float density, float& mass,
|
||||
idVec3& centerOfMass, idMat3& inertiaTensor) const;
|
||||
void Serialize(idSerializer& serializer);
|
||||
|
||||
bool enabled;
|
||||
bool deleted;
|
||||
int entityNumber;
|
||||
int ownerNumber;
|
||||
int physicsId;
|
||||
int bodyId;
|
||||
int bodyMask;
|
||||
idVec3 origin;
|
||||
idMat3 axis;
|
||||
idBounds bounds;
|
||||
idBounds absBounds;
|
||||
int contents;
|
||||
idCollisionModel* collisionModel;
|
||||
idList<int, 13> traceModels;
|
||||
idTreeAnimator* animatedModel;
|
||||
idClip* clip;
|
||||
clipLink_t* clipLinks;
|
||||
idCollisionQuery collisionQuery;
|
||||
int touchCount;
|
||||
idClipModel* nextDeleted;
|
||||
idAASClipLink* aasLinks[8];
|
||||
|
||||
private:
|
||||
idCollisionModel* NoTraceModelWarning() const;
|
||||
void Init(idClip* ownerClip);
|
||||
void FreeModel();
|
||||
bool LoadCollisionModel(const char* modelName,
|
||||
const idMaterial* material);
|
||||
void LoadCollisionModel(idCollisionModel* model);
|
||||
void LoadTraceModel(const idTraceModel* traceModel, int traceModelIndex,
|
||||
const idMaterial* material);
|
||||
void LoadAnimator(idTreeAnimator* animator);
|
||||
void Link_r(clipSector_t* sector);
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 15173; PDB kind: class.
|
||||
class idStrClipModel : public idAtomicString
|
||||
{
|
||||
class idStrClipModel : public idAtomicString {
|
||||
public:
|
||||
using idAtomicString::idAtomicString;
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 15174; PDB kind: class.
|
||||
class idClipModelInfo
|
||||
{
|
||||
class idClipModelInfo {
|
||||
public:
|
||||
idClipModelInfo::clipModelType_t type;
|
||||
idVec3 size;
|
||||
idVec3 offset;
|
||||
int numSides;
|
||||
idStrClipModel clipModelName;
|
||||
bool clipShrink;
|
||||
bool allowInline;
|
||||
bool softObstacle;
|
||||
const idMaterial *overrideClipMaterial;
|
||||
enum clipModelType_t : int {
|
||||
CLIPMODEL_NONE = 0,
|
||||
CLIPMODEL_RENDERMODEL = 1,
|
||||
CLIPMODEL_CUSTOM = 2,
|
||||
CLIPMODEL_BOX = 3,
|
||||
CLIPMODEL_CYLINDER = 4,
|
||||
CLIPMODEL_CONE = 5,
|
||||
CLIPMODEL_BOUNDINGBOX = 6
|
||||
};
|
||||
|
||||
clipModelType_t type;
|
||||
idVec3 size;
|
||||
idVec3 offset;
|
||||
int numSides;
|
||||
idStrClipModel clipModelName;
|
||||
bool clipShrink;
|
||||
bool allowInline;
|
||||
bool softObstacle;
|
||||
const idMaterial* overrideClipMaterial;
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 22949; PDB kind: struct.
|
||||
struct cbp_model_s
|
||||
{
|
||||
int state[16];
|
||||
int count0[16];
|
||||
int count1[16];
|
||||
struct cbp_model_s {
|
||||
int state[16];
|
||||
int count0[16];
|
||||
int count1[16];
|
||||
};
|
||||
|
||||
static_assert(sizeof(clipLink_t) == sizeof(void*) * 5,
|
||||
"Recovered clip-link ABI changed");
|
||||
static_assert(sizeof(cbp_model_s) == 192,
|
||||
"Recovered clip-model predictor ABI changed");
|
||||
#if INTPTR_MAX == INT32_MAX
|
||||
static_assert(sizeof(idClipModel) == 216,
|
||||
"Recovered idClipModel ABI changed");
|
||||
static_assert(sizeof(idClipModelInfo) == 44,
|
||||
"Recovered idClipModelInfo ABI changed");
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
#include "gamelib/physics/physics.h"
|
||||
|
||||
#include "idlib/lib_print.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
idPhysics* idPhysics::hash[1024] = {};
|
||||
int idPhysics::currentPhysicsId = 0;
|
||||
|
||||
idPhysics::idPhysics()
|
||||
: type(PHYSICS_INVALID),
|
||||
physicsId(++currentPhysicsId),
|
||||
entityNumber(0x1FFF),
|
||||
nextInHash(nullptr),
|
||||
clip(nullptr),
|
||||
callbacks(nullptr),
|
||||
residencyQuery{0},
|
||||
collisionResidency(RESIDENCY_NOT_TESTED) {
|
||||
Link();
|
||||
}
|
||||
|
||||
idPhysics::~idPhysics() {
|
||||
Unlink();
|
||||
nextInHash = nullptr;
|
||||
clip = nullptr;
|
||||
callbacks = nullptr;
|
||||
}
|
||||
|
||||
void idPhysics::InitPhysics(idClip* clip_, idPhysicsCallbacks* callbacks_,
|
||||
const int entityNumber_) {
|
||||
clip = clip_;
|
||||
callbacks = callbacks_;
|
||||
entityNumber = entityNumber_;
|
||||
}
|
||||
|
||||
void idPhysics::ClearHash() {
|
||||
std::memset(hash, 0, sizeof(hash));
|
||||
currentPhysicsId = 0;
|
||||
}
|
||||
|
||||
idPhysics* idPhysics::GetPhysicsForId(const int physicsId_) {
|
||||
if (physicsId_ == -1) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (idPhysics* item = hash[physicsId_ & 1023]; item != nullptr;
|
||||
item = item->nextInHash) {
|
||||
if (item->physicsId == physicsId_) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void idPhysics::UpdateCollisionResidency(const contactInfo_t& contact) {
|
||||
if ((contact.flags & CONTACT_FLAG_SUBMODEL_NOT_RESIDENT) != 0) {
|
||||
collisionResidency = RESIDENCY_NO;
|
||||
}
|
||||
}
|
||||
|
||||
void idPhysics::Unlink() {
|
||||
idPhysics** link = &hash[physicsId & 1023];
|
||||
while (*link != nullptr) {
|
||||
if (*link == this && (*link)->physicsId == physicsId) {
|
||||
*link = nextInHash;
|
||||
return;
|
||||
}
|
||||
link = &(*link)->nextInHash;
|
||||
}
|
||||
}
|
||||
|
||||
void idPhysics::Link() {
|
||||
if (physicsId != -1 && GetPhysicsForId(physicsId) != nullptr) {
|
||||
idLibPrint::Printf(" duplicate physicsId = %d\n", physicsId);
|
||||
do {
|
||||
physicsId = currentPhysicsId++;
|
||||
} while (physicsId != -1 && GetPhysicsForId(physicsId) != nullptr);
|
||||
}
|
||||
|
||||
const int bucket = physicsId & 1023;
|
||||
nextInHash = hash[bucket];
|
||||
hash[bucket] = this;
|
||||
}
|
||||
|
||||
void OutputPhysicsHash_f(const idCmdArgs&) {
|
||||
for (int bucket = 0; bucket < 1024; ++bucket) {
|
||||
if (idPhysics::hash[bucket] == nullptr) {
|
||||
continue;
|
||||
}
|
||||
idLibPrint::Printf("%d:", bucket);
|
||||
for (idPhysics* item = idPhysics::hash[bucket]; item != nullptr;
|
||||
item = item->nextInHash) {
|
||||
idLibPrint::Printf(" (%d) 0x%p", item->physicsId, item);
|
||||
}
|
||||
idLibPrint::Printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
bool idPhysics::IsInNonResidentCollisionArea(const bool assumeNonResident) {
|
||||
// The recovered routine first consumes/submits an asynchronous idClip
|
||||
// residency query. That queue is owned by clip.cpp and will be connected
|
||||
// when that authoritative unit is reconstructed. Its final state test is
|
||||
// independent and is retained here.
|
||||
return collisionResidency == RESIDENCY_NO
|
||||
|| (collisionResidency == RESIDENCY_NOT_TESTED && assumeNonResident);
|
||||
}
|
||||
@@ -1,218 +1,187 @@
|
||||
#pragma once
|
||||
|
||||
// Reconstructed C++ declarations from IDA Local Types and PDB/DIA metadata.
|
||||
// Original PDB header: w:\tech5\engine\gamelib\physics\physics.h
|
||||
// Recovered logical types: 4
|
||||
// Signatures retain Xbox 360 ABI evidence and may still require manual review.
|
||||
#include "cm/jobs/collisionresults.h"
|
||||
#include "idlib/math/matrix.h"
|
||||
#include "idlib/math/vector.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
// IDA Local Type ordinal 1033; PDB kind: enum.
|
||||
enum physicsType_t : __int32
|
||||
{
|
||||
PHYSICS_INVALID = 0x0,
|
||||
PHYSICS_STATIC = 0x1,
|
||||
PHYSICS_STATICMULTI = 0x2,
|
||||
PHYSICS_AI = 0x3,
|
||||
PHYSICS_PLAYER = 0x4,
|
||||
PHYSICS_PARAMETRIC = 0x5,
|
||||
PHYSICS_RIGIDBODY = 0x6,
|
||||
PHYSICS_RIGIDBODY3DOF = 0x7,
|
||||
PHYSICS_AF = 0x8,
|
||||
PHYSICS_ROBOT = 0x9,
|
||||
class idBounds;
|
||||
class idClip;
|
||||
class idClipModel;
|
||||
class idCmdArgs;
|
||||
class idPhysics;
|
||||
class idPhysicsCallbacks;
|
||||
class idRotation;
|
||||
|
||||
enum bindFlags_t : int;
|
||||
|
||||
enum physicsType_t : int {
|
||||
PHYSICS_INVALID = 0,
|
||||
PHYSICS_STATIC = 1,
|
||||
PHYSICS_STATICMULTI = 2,
|
||||
PHYSICS_AI = 3,
|
||||
PHYSICS_PLAYER = 4,
|
||||
PHYSICS_PARAMETRIC = 5,
|
||||
PHYSICS_RIGIDBODY = 6,
|
||||
PHYSICS_RIGIDBODY3DOF = 7,
|
||||
PHYSICS_AF = 8,
|
||||
PHYSICS_ROBOT = 9
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 2858; PDB kind: enum.
|
||||
enum idPhysics::residency_t : __int32
|
||||
{
|
||||
RESIDENCY_NOT_TESTED = 0x0,
|
||||
RESIDENCY_YES = 0x1,
|
||||
RESIDENCY_NO = 0x2,
|
||||
struct impactInfo_t {
|
||||
float invMass;
|
||||
idMat3 invInertiaTensor;
|
||||
idVec3 position;
|
||||
idVec3 velocity;
|
||||
|
||||
// Materialized in the authoritative physics.h dump.
|
||||
void Zero() {
|
||||
invMass = 0.0f;
|
||||
invInertiaTensor[0].Zero();
|
||||
invInertiaTensor[1].Zero();
|
||||
invInertiaTensor[2].Zero();
|
||||
position.Zero();
|
||||
velocity.Zero();
|
||||
}
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 14164; PDB kind: class.
|
||||
class idPhysics
|
||||
{
|
||||
#ifndef ID_RESIDENCY_QUERY_DEFINED
|
||||
#define ID_RESIDENCY_QUERY_DEFINED
|
||||
struct idResidencyQuery { std::uint64_t index; };
|
||||
#endif
|
||||
|
||||
class alignas(8) idPhysics {
|
||||
public:
|
||||
// Recovered virtual interface; IDA vtable ordinal 14168.
|
||||
virtual ~idPhysics();
|
||||
virtual void SetClipModel(idClipModel *, float, int, bool);
|
||||
virtual idClipModel *GetClipModel(int);
|
||||
virtual int GetNumClipModels();
|
||||
virtual void SetMass(float, int);
|
||||
virtual float GetMass(int);
|
||||
virtual void SetContents(int, int);
|
||||
virtual int GetContents(int);
|
||||
virtual void SetClipMask(int, int);
|
||||
virtual int GetClipMask(int);
|
||||
virtual const idBounds *GetBounds(int);
|
||||
virtual const idBounds *GetAbsBounds(int);
|
||||
virtual void SetOrigin(const idVec3 *, int);
|
||||
virtual void SetAxis(const idMat3 *, int);
|
||||
virtual void Translate(const idVec3 *, int);
|
||||
virtual void Rotate(const idRotation *, int);
|
||||
virtual const idVec3 *GetOrigin(int);
|
||||
virtual const idMat3 *GetAxis(int);
|
||||
virtual const idVec3 *GetLocalOrigin(int);
|
||||
virtual const idMat3 *GetLocalAxis(int);
|
||||
virtual void SetLinearVelocity(const idVec3 *, int);
|
||||
virtual void SetAngularVelocity(const idVec3 *, int);
|
||||
virtual idVec3 *GetLinearVelocity(idVec3 *result, int);
|
||||
virtual idVec3 *GetAngularVelocity(idVec3 *result, int);
|
||||
virtual void SetGravity(const idVec3 *);
|
||||
virtual const idVec3 *GetGravity();
|
||||
virtual const idVec3 *GetGravityNormal();
|
||||
virtual void SetWaterLevel(float, int);
|
||||
virtual float GetWaterLevel(int);
|
||||
virtual void SetWaterViscosity(float, int);
|
||||
virtual float GetWaterViscosity(int);
|
||||
virtual void SetWaterEntNum(int);
|
||||
virtual int GetWaterEntNum();
|
||||
virtual void SetWaterSurfaceWrldHeight(float);
|
||||
virtual float GetWaterSurfaceWrldHeight();
|
||||
virtual void GetImpactInfo(const int, const idVec3 *, impactInfo_t *);
|
||||
virtual void ApplyImpulse(const int, const idVec3 *, const idVec3 *);
|
||||
virtual void ApplyForce(const int, const idVec3 *, const idVec3 *);
|
||||
virtual void Activate();
|
||||
virtual void PutToRest();
|
||||
virtual bool IsAtRest();
|
||||
virtual bool IsPushable(int);
|
||||
virtual void SaveState();
|
||||
virtual void RestoreState();
|
||||
virtual bool Evaluate(int, int);
|
||||
virtual void UpdateTime(int);
|
||||
virtual void ClipTranslation(trace_t *, const idVec3 *, const idClipModel *);
|
||||
virtual void ClipRotation(trace_t *, const idRotation *, const idClipModel *);
|
||||
virtual int ClipContents(const idClipModel *, int);
|
||||
virtual void DisableClip();
|
||||
virtual void EnableClip();
|
||||
virtual void UnlinkClip();
|
||||
virtual void LinkClip();
|
||||
virtual bool EvaluateContacts();
|
||||
virtual int GetNumContacts();
|
||||
virtual const contactInfo_t *GetContact(int);
|
||||
virtual void ClearContacts();
|
||||
virtual void AddContactPhysics(idPhysics *);
|
||||
virtual void RemoveContactPhysics(idPhysics *);
|
||||
virtual int GetNumContactPhysics();
|
||||
virtual idPhysics *GetContactPhysics(int);
|
||||
virtual void ActivateContactPhysics();
|
||||
virtual bool HasGroundContacts();
|
||||
virtual bool IsGroundEntity(int);
|
||||
virtual bool IsGroundClipModel(int, int);
|
||||
virtual void SetPushed(int);
|
||||
virtual idVec3 *GetPushedLinearVelocity(idVec3 *result, const int);
|
||||
virtual idVec3 *GetPushedAngularVelocity(idVec3 *result, const int);
|
||||
virtual void SetMaster(bool, const idVec3 *, const idMat3 *, const bindFlags_t);
|
||||
virtual void SetLocalOrigin(const idVec3 *, int);
|
||||
virtual void SetLocalAxis(const idMat3 *, int);
|
||||
virtual int GetBlockingEntityNum();
|
||||
virtual int GetLinearEndTime();
|
||||
virtual int GetAngularEndTime();
|
||||
virtual bool IsInNonResidentCollisionArea(bool);
|
||||
enum residency_t : int {
|
||||
RESIDENCY_NOT_TESTED = 0,
|
||||
RESIDENCY_YES = 1,
|
||||
RESIDENCY_NO = 2
|
||||
};
|
||||
|
||||
__declspec(align(8)) physicsType_t type;
|
||||
int physicsId;
|
||||
int entityNumber;
|
||||
idPhysics *nextInHash;
|
||||
idClip *clip;
|
||||
idPhysicsCallbacks *callbacks;
|
||||
idResidencyQuery residencyQuery;
|
||||
idPhysics::residency_t collisionResidency;
|
||||
idPhysics();
|
||||
virtual ~idPhysics();
|
||||
|
||||
virtual void SetClipModel(idClipModel*, float, int, bool) = 0;
|
||||
virtual idClipModel* GetClipModel(int) = 0;
|
||||
virtual int GetNumClipModels() = 0;
|
||||
virtual void SetMass(float, int) = 0;
|
||||
virtual float GetMass(int) = 0;
|
||||
virtual void SetContents(int, int) = 0;
|
||||
virtual int GetContents(int) = 0;
|
||||
virtual void SetClipMask(int, int) = 0;
|
||||
virtual int GetClipMask(int) = 0;
|
||||
virtual const idBounds* GetBounds(int) = 0;
|
||||
virtual const idBounds* GetAbsBounds(int) = 0;
|
||||
virtual void SetOrigin(const idVec3*, int) = 0;
|
||||
virtual void SetAxis(const idMat3*, int) = 0;
|
||||
virtual void Translate(const idVec3*, int) = 0;
|
||||
virtual void Rotate(const idRotation*, int) = 0;
|
||||
virtual const idVec3* GetOrigin(int) = 0;
|
||||
virtual const idMat3* GetAxis(int) = 0;
|
||||
virtual const idVec3* GetLocalOrigin(int) = 0;
|
||||
virtual const idMat3* GetLocalAxis(int) = 0;
|
||||
virtual void SetLinearVelocity(const idVec3*, int) = 0;
|
||||
virtual void SetAngularVelocity(const idVec3*, int) = 0;
|
||||
virtual idVec3* GetLinearVelocity(idVec3*, int) = 0;
|
||||
virtual idVec3* GetAngularVelocity(idVec3*, int) = 0;
|
||||
virtual void SetGravity(const idVec3*) = 0;
|
||||
virtual const idVec3* GetGravity() = 0;
|
||||
virtual const idVec3* GetGravityNormal() = 0;
|
||||
virtual void SetWaterLevel(float, int) = 0;
|
||||
virtual float GetWaterLevel(int) = 0;
|
||||
virtual void SetWaterViscosity(float, int) = 0;
|
||||
virtual float GetWaterViscosity(int) = 0;
|
||||
virtual void SetWaterEntNum(int) = 0;
|
||||
virtual int GetWaterEntNum() = 0;
|
||||
virtual void SetWaterSurfaceWrldHeight(float) = 0;
|
||||
virtual float GetWaterSurfaceWrldHeight() = 0;
|
||||
virtual void GetImpactInfo(int, const idVec3*, impactInfo_t*) = 0;
|
||||
virtual void ApplyImpulse(int, const idVec3*, const idVec3*) = 0;
|
||||
virtual void ApplyForce(int, const idVec3*, const idVec3*) = 0;
|
||||
virtual void Activate() = 0;
|
||||
virtual void PutToRest() = 0;
|
||||
virtual bool IsAtRest() = 0;
|
||||
virtual bool IsPushable(int) = 0;
|
||||
virtual void SaveState() = 0;
|
||||
virtual void RestoreState() = 0;
|
||||
virtual bool Evaluate(int, int) = 0;
|
||||
virtual void UpdateTime(int) = 0;
|
||||
virtual void ClipTranslation(trace_t*, const idVec3*, const idClipModel*) = 0;
|
||||
virtual void ClipRotation(trace_t*, const idRotation*, const idClipModel*) = 0;
|
||||
virtual int ClipContents(const idClipModel*, int) = 0;
|
||||
virtual void DisableClip() = 0;
|
||||
virtual void EnableClip() = 0;
|
||||
virtual void UnlinkClip() = 0;
|
||||
virtual void LinkClip() = 0;
|
||||
virtual bool EvaluateContacts() = 0;
|
||||
virtual int GetNumContacts() = 0;
|
||||
virtual const contactInfo_t* GetContact(int) = 0;
|
||||
virtual void ClearContacts() = 0;
|
||||
virtual void AddContactPhysics(idPhysics*) = 0;
|
||||
virtual void RemoveContactPhysics(idPhysics*) = 0;
|
||||
virtual int GetNumContactPhysics() = 0;
|
||||
virtual idPhysics* GetContactPhysics(int) = 0;
|
||||
virtual void ActivateContactPhysics() = 0;
|
||||
virtual bool HasGroundContacts() = 0;
|
||||
virtual bool IsGroundEntity(int) = 0;
|
||||
virtual bool IsGroundClipModel(int, int) = 0;
|
||||
virtual void SetPushed(int) = 0;
|
||||
virtual idVec3* GetPushedLinearVelocity(idVec3*, int) = 0;
|
||||
virtual idVec3* GetPushedAngularVelocity(idVec3*, int) = 0;
|
||||
virtual void SetMaster(bool, const idVec3*, const idMat3*, bindFlags_t) = 0;
|
||||
virtual void SetLocalOrigin(const idVec3*, int) = 0;
|
||||
virtual void SetLocalAxis(const idMat3*, int) = 0;
|
||||
virtual int GetBlockingEntityNum() = 0;
|
||||
virtual int GetLinearEndTime() = 0;
|
||||
virtual int GetAngularEndTime() = 0;
|
||||
virtual bool IsInNonResidentCollisionArea(bool assumeNonResident);
|
||||
|
||||
void InitPhysics(idClip* clip_, idPhysicsCallbacks* callbacks_,
|
||||
int entityNumber_);
|
||||
|
||||
static void ClearHash();
|
||||
static idPhysics* GetPhysicsForId(int physicsId_);
|
||||
|
||||
physicsType_t GetType() const { return type; }
|
||||
int GetPhysicsId() const { return physicsId; }
|
||||
int GetEntityNumber() const { return entityNumber; }
|
||||
residency_t GetCollisionResidency() const { return collisionResidency; }
|
||||
|
||||
protected:
|
||||
void UpdateCollisionResidency(const contactInfo_t& contact);
|
||||
|
||||
private:
|
||||
void Link();
|
||||
void Unlink();
|
||||
|
||||
static idPhysics* hash[1024];
|
||||
static int currentPhysicsId;
|
||||
|
||||
alignas(8) physicsType_t type;
|
||||
int physicsId;
|
||||
int entityNumber;
|
||||
idPhysics* nextInHash;
|
||||
idClip* clip;
|
||||
idPhysicsCallbacks* callbacks;
|
||||
idResidencyQuery residencyQuery;
|
||||
residency_t collisionResidency;
|
||||
|
||||
friend void OutputPhysicsHash_f(const idCmdArgs& args);
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 19509; PDB kind: class.
|
||||
class idPhysics_Robot : public idPhysics_DynamicBase
|
||||
{
|
||||
public:
|
||||
// Recovered virtual interface; IDA vtable ordinal 19510.
|
||||
virtual ~idPhysics_Robot();
|
||||
virtual void SetClipModel(idClipModel *, float, int, bool);
|
||||
virtual idClipModel *GetClipModel(int);
|
||||
virtual int GetNumClipModels();
|
||||
virtual void SetMass(float, int);
|
||||
virtual float GetMass(int);
|
||||
virtual void SetContents(int, int);
|
||||
virtual int GetContents(int);
|
||||
virtual void SetClipMask(int, int);
|
||||
virtual int GetClipMask(int);
|
||||
virtual const idBounds *GetBounds(int);
|
||||
virtual const idBounds *GetAbsBounds(int);
|
||||
virtual void SetOrigin(const idVec3 *, int);
|
||||
virtual void SetAxis(const idMat3 *, int);
|
||||
virtual void Translate(const idVec3 *, int);
|
||||
virtual void Rotate(const idRotation *, int);
|
||||
virtual const idVec3 *GetOrigin(int);
|
||||
virtual const idMat3 *GetAxis(int);
|
||||
virtual const idVec3 *GetLocalOrigin(int);
|
||||
virtual const idMat3 *GetLocalAxis(int);
|
||||
virtual void SetLinearVelocity(const idVec3 *, int);
|
||||
virtual void SetAngularVelocity(const idVec3 *, int);
|
||||
virtual idVec3 *GetLinearVelocity(idVec3 *result, int);
|
||||
virtual idVec3 *GetAngularVelocity(idVec3 *result, int);
|
||||
virtual void SetGravity(const idVec3 *);
|
||||
virtual const idVec3 *GetGravity();
|
||||
virtual const idVec3 *GetGravityNormal();
|
||||
virtual void SetWaterLevel(float, int);
|
||||
virtual float GetWaterLevel(int);
|
||||
virtual void SetWaterViscosity(float, int);
|
||||
virtual float GetWaterViscosity(int);
|
||||
virtual void SetWaterEntNum(int);
|
||||
virtual int GetWaterEntNum();
|
||||
virtual void SetWaterSurfaceWrldHeight(float);
|
||||
virtual float GetWaterSurfaceWrldHeight();
|
||||
virtual void GetImpactInfo(const int, const idVec3 *, impactInfo_t *);
|
||||
virtual void ApplyImpulse(const int, const idVec3 *, const idVec3 *);
|
||||
virtual void ApplyForce(const int, const idVec3 *, const idVec3 *);
|
||||
virtual void Activate();
|
||||
virtual void PutToRest();
|
||||
virtual bool IsAtRest();
|
||||
virtual bool IsPushable(int);
|
||||
virtual void SaveState();
|
||||
virtual void RestoreState();
|
||||
virtual bool Evaluate(int, int);
|
||||
virtual void UpdateTime(int);
|
||||
virtual void ClipTranslation(trace_t *, const idVec3 *, const idClipModel *);
|
||||
virtual void ClipRotation(trace_t *, const idRotation *, const idClipModel *);
|
||||
virtual int ClipContents(const idClipModel *, int);
|
||||
virtual void DisableClip();
|
||||
virtual void EnableClip();
|
||||
virtual void UnlinkClip();
|
||||
virtual void LinkClip();
|
||||
virtual bool EvaluateContacts();
|
||||
virtual int GetNumContacts();
|
||||
virtual const contactInfo_t *GetContact(int);
|
||||
virtual void ClearContacts();
|
||||
virtual void AddContactPhysics(idPhysics *);
|
||||
virtual void RemoveContactPhysics(idPhysics *);
|
||||
virtual int GetNumContactPhysics();
|
||||
virtual idPhysics *GetContactPhysics(int);
|
||||
virtual void ActivateContactPhysics();
|
||||
virtual bool HasGroundContacts();
|
||||
virtual bool IsGroundEntity(int);
|
||||
virtual bool IsGroundClipModel(int, int);
|
||||
virtual void SetPushed(int);
|
||||
virtual idVec3 *GetPushedLinearVelocity(idVec3 *result, const int);
|
||||
virtual idVec3 *GetPushedAngularVelocity(idVec3 *result, const int);
|
||||
virtual void SetMaster(bool, const idVec3 *, const idMat3 *, const bindFlags_t);
|
||||
virtual void SetLocalOrigin(const idVec3 *, int);
|
||||
virtual void SetLocalAxis(const idMat3 *, int);
|
||||
virtual int GetBlockingEntityNum();
|
||||
virtual int GetLinearEndTime();
|
||||
virtual int GetAngularEndTime();
|
||||
virtual bool IsInNonResidentCollisionArea(bool);
|
||||
virtual bool IsOutsideWorld();
|
||||
void OutputPhysicsHash_f(const idCmdArgs& args);
|
||||
|
||||
idClipQuery stepMoveQuery;
|
||||
idMat3 clipAxis;
|
||||
idVec3 lastCollisionNormal;
|
||||
idClipModel *clipModel;
|
||||
robotPState_t savedState;
|
||||
robotPState_t currentState;
|
||||
float maxStepHeight;
|
||||
float minFloorCosine;
|
||||
float mass;
|
||||
float invMass;
|
||||
bool noImpact;
|
||||
float timeStep;
|
||||
int timeStepMS;
|
||||
};
|
||||
#if INTPTR_MAX == INT32_MAX
|
||||
static_assert(sizeof(impactInfo_t) == 64,
|
||||
"Recovered impactInfo_t ABI changed");
|
||||
static_assert(sizeof(contactInfo_t) == 76,
|
||||
"Recovered contactInfo_t ABI changed");
|
||||
static_assert(sizeof(trace_t) == 128,
|
||||
"Recovered trace_t ABI changed");
|
||||
static_assert(sizeof(idResidencyQuery) == 8,
|
||||
"Recovered idResidencyQuery ABI changed");
|
||||
static_assert(sizeof(idPhysics) == 48,
|
||||
"Recovered idPhysics ABI changed");
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,226 @@
|
||||
#include "gamelib/physics/tracemodelcache.h"
|
||||
|
||||
#include "cm/collisionmodel.h"
|
||||
#include "cm/collisionmodelmanager.h"
|
||||
#include "idlib/lib_print.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
|
||||
bool GameLib_IsMultiplayer();
|
||||
|
||||
namespace {
|
||||
|
||||
std::uint32_t FloatBits(const float value) {
|
||||
std::uint32_t bits = 0;
|
||||
std::memcpy(&bits, &value, sizeof(bits));
|
||||
return bits;
|
||||
}
|
||||
|
||||
int TraceModelHashKey(const idTraceModel& traceModel) {
|
||||
const std::uint32_t key =
|
||||
(4u * ((4u * ((16u * static_cast<std::uint32_t>(traceModel.type)) ^
|
||||
traceModel.numVerts)) ^ traceModel.numEdges)) ^
|
||||
traceModel.numPolys ^ FloatBits(traceModel.bounds[0].z) ^
|
||||
FloatBits(traceModel.bounds[0].y) ^ FloatBits(traceModel.bounds[0].x);
|
||||
return static_cast<int>(key);
|
||||
}
|
||||
|
||||
struct waterPointBuild_t {
|
||||
idVec3 xyz;
|
||||
float weight;
|
||||
float weightSquared;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
void idTraceModelCache::FreeTraceModel(const int traceModelIndex) {
|
||||
trmCache_t* const entry = GetEntry(traceModelIndex);
|
||||
if (entry == nullptr || entry->refCount <= 0) {
|
||||
idLibPrint::Warning(
|
||||
"idClipModel::FreeTraceModel: tried to free uncached trace model");
|
||||
return;
|
||||
}
|
||||
--entry->refCount;
|
||||
}
|
||||
|
||||
int idTraceModelCache::CopyTraceModel(const int traceModelIndex) {
|
||||
trmCache_t* const entry = GetEntry(traceModelIndex);
|
||||
if (entry == nullptr || entry->refCount <= 0) {
|
||||
idLibPrint::Warning(
|
||||
"idClipModel::CopyTraceModel: tried to copy an uncached trace "
|
||||
"model");
|
||||
return -1;
|
||||
}
|
||||
++entry->refCount;
|
||||
return traceModelIndex;
|
||||
}
|
||||
|
||||
void idTraceModelCache::ClearTraceModelCache() {
|
||||
for (int index = 0; index < cache.Num(); ++index) {
|
||||
trmCache_t* const entry = cache[index];
|
||||
if (entry == nullptr) {
|
||||
continue;
|
||||
}
|
||||
if (entry->collisionModel != nullptr) {
|
||||
delete entry->collisionModel;
|
||||
entry->collisionModel = nullptr;
|
||||
}
|
||||
entry->waterPoints.ClearFree();
|
||||
delete entry->trm;
|
||||
entry->trm = nullptr;
|
||||
entry->~trmCache_t();
|
||||
}
|
||||
cacheAllocator.Shutdown();
|
||||
cache.ClearFree();
|
||||
hash.Free();
|
||||
}
|
||||
|
||||
void idTraceModelCache::SetupWaterPoints(trmCache_t* const entry) {
|
||||
entry->waterPoints.ClearFree();
|
||||
entry->hasWater = false;
|
||||
if (entry->trm == nullptr || !entry->trm->isConvex ||
|
||||
GameLib_IsMultiplayer()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const idBounds& bounds = entry->trm->bounds;
|
||||
const idVec3 dimensions = bounds[1] - bounds[0];
|
||||
if (dimensions.x <= 0.0f || dimensions.y <= 0.0f ||
|
||||
dimensions.z <= 0.0f ||
|
||||
dimensions.x * dimensions.y * dimensions.z == 0.0f) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int numVertices = static_cast<int>(entry->trm->numVerts);
|
||||
const int desiredInteriorPoints = 64 - numVertices;
|
||||
if (desiredInteriorPoints < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<waterPointBuild_t> interiorPoints;
|
||||
float sampleMultiplier = 1.0f;
|
||||
for (;;) {
|
||||
interiorPoints.clear();
|
||||
const float divisions = std::cbrt(
|
||||
static_cast<float>(desiredInteriorPoints) * sampleMultiplier);
|
||||
if (divisions > 0.0f) {
|
||||
const idVec3 step(dimensions.x / divisions,
|
||||
dimensions.y / divisions, dimensions.z / divisions);
|
||||
for (int x = 0; static_cast<float>(x) < divisions; ++x) {
|
||||
for (int y = 0; static_cast<float>(y) < divisions; ++y) {
|
||||
for (int z = 0; static_cast<float>(z) < divisions; ++z) {
|
||||
waterPointBuild_t point{};
|
||||
point.xyz.Set(
|
||||
bounds[0].x + (static_cast<float>(x) + 0.5f) * step.x,
|
||||
bounds[0].y + (static_cast<float>(y) + 0.5f) * step.y,
|
||||
bounds[0].z + (static_cast<float>(z) + 0.5f) * step.z);
|
||||
point.weight = 1.0f;
|
||||
point.weightSquared = 1.0f;
|
||||
if (entry->trm->ContainsPoint(point.xyz)) {
|
||||
interiorPoints.push_back(point);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (static_cast<int>(interiorPoints.size()) >= desiredInteriorPoints) {
|
||||
break;
|
||||
}
|
||||
sampleMultiplier *= 2.0f;
|
||||
if (sampleMultiplier > 16.0f) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const float pointWeight = 1.0f /
|
||||
static_cast<float>(interiorPoints.size() + numVertices);
|
||||
for (waterPointBuild_t& point : interiorPoints) {
|
||||
point.weight = pointWeight;
|
||||
point.weightSquared = pointWeight * pointWeight;
|
||||
}
|
||||
|
||||
while (static_cast<int>(interiorPoints.size()) > desiredInteriorPoints) {
|
||||
float bestMetric = (std::numeric_limits<float>::max)();
|
||||
int bestFirst = 0;
|
||||
int bestSecond = 1;
|
||||
for (int first = 0;
|
||||
first < static_cast<int>(interiorPoints.size()); ++first) {
|
||||
for (int second = first + 1;
|
||||
second < static_cast<int>(interiorPoints.size()); ++second) {
|
||||
const idVec3 delta = interiorPoints[first].xyz -
|
||||
interiorPoints[second].xyz;
|
||||
const float metric = delta.LengthSqr() *
|
||||
interiorPoints[first].weight *
|
||||
interiorPoints[second].weight;
|
||||
if (metric < bestMetric) {
|
||||
bestMetric = metric;
|
||||
bestFirst = first;
|
||||
bestSecond = second;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
waterPointBuild_t& destination = interiorPoints[bestFirst];
|
||||
destination.xyz = (destination.xyz +
|
||||
interiorPoints[bestSecond].xyz) * 0.5f;
|
||||
destination.weight += interiorPoints[bestSecond].weight;
|
||||
destination.weightSquared = destination.weight * destination.weight;
|
||||
interiorPoints[bestSecond] = interiorPoints.back();
|
||||
interiorPoints.pop_back();
|
||||
}
|
||||
|
||||
entry->waterPoints.SetNum(64);
|
||||
int outputIndex = 0;
|
||||
for (const waterPointBuild_t& point : interiorPoints) {
|
||||
entry->waterPoints[outputIndex++] = {point.xyz, point.weight};
|
||||
}
|
||||
for (int vertex = 0; vertex < numVertices && outputIndex < 64; ++vertex) {
|
||||
traceModelWater_t& point = entry->waterPoints[outputIndex++];
|
||||
point.xyz.Set(entry->trm->vertsX[vertex],
|
||||
entry->trm->vertsY[vertex], entry->trm->vertsZ[vertex]);
|
||||
point.weight = pointWeight;
|
||||
}
|
||||
while (outputIndex < 64) {
|
||||
entry->waterPoints[outputIndex++] = {idVec3(0.0f, 0.0f, 0.0f), 0.0f};
|
||||
}
|
||||
entry->hasWater = true;
|
||||
}
|
||||
|
||||
int idTraceModelCache::AllocTraceModel(const idTraceModel& traceModel,
|
||||
const idMaterial* const material) {
|
||||
const int key = TraceModelHashKey(traceModel);
|
||||
for (int index = hash.First(key); index != -1;
|
||||
index = hash.Next(index)) {
|
||||
trmCache_t* const entry = GetEntry(index);
|
||||
if (entry != nullptr && entry->trm != nullptr &&
|
||||
entry->trm->Compare(traceModel) && entry->material == material) {
|
||||
++entry->refCount;
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
trmCache_t* const entry = cacheAllocator.Alloc();
|
||||
if (entry == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
entry->trm = new idTraceModel(traceModel);
|
||||
entry->trm->ClearUnused();
|
||||
entry->trm->GetMassProperties(1.0f, entry->volume,
|
||||
entry->centerOfMass, entry->inertiaTensor);
|
||||
entry->refCount = 1;
|
||||
entry->material = material;
|
||||
const int index = cache.Append(entry);
|
||||
hash.Add(key, index);
|
||||
char modelName[64];
|
||||
std::snprintf(modelName, sizeof(modelName), "traceModel%d", index);
|
||||
entry->collisionModel = collisionModelManager.ModelFromTrm(
|
||||
modelName, *entry->trm, material);
|
||||
SetupWaterPoints(entry);
|
||||
return index;
|
||||
}
|
||||
@@ -1,31 +1,103 @@
|
||||
#pragma once
|
||||
|
||||
// Reconstructed C++ declarations from IDA Local Types and PDB/DIA metadata.
|
||||
// Original PDB header: w:\tech5\engine\gamelib\physics\tracemodelcache.h
|
||||
// Recovered logical types: 2
|
||||
// Signatures retain Xbox 360 ABI evidence and may still require manual review.
|
||||
#include "idlib/blockalloc_base.h"
|
||||
#include "idlib/containers/hashindex.h"
|
||||
#include "idlib/containers/list.h"
|
||||
#include "idlib/geometry/tracemodel.h"
|
||||
#include "idlib/text/str.h"
|
||||
|
||||
class idCollisionModel;
|
||||
class idMaterial;
|
||||
|
||||
// IDA Local Type ordinal 14116; PDB kind: struct.
|
||||
struct idTraceModelCache::trmCache_t
|
||||
{
|
||||
idTraceModel *trm;
|
||||
int refCount;
|
||||
float volume;
|
||||
idVec3 centerOfMass;
|
||||
idMat3 inertiaTensor;
|
||||
const idMaterial *material;
|
||||
idCollisionModel *collisionModel;
|
||||
bool hasWater;
|
||||
idList<traceModelWater_t,54> waterPoints;
|
||||
struct traceModelWater_t {
|
||||
idVec3 xyz;
|
||||
float weight;
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 14121; PDB kind: class.
|
||||
class idTraceModelCache
|
||||
{
|
||||
// Reconstructed from PDB local types 14116/14121. Methods are introduced as
|
||||
// their authoritative bodies are activated.
|
||||
class idTraceModelCache {
|
||||
public:
|
||||
idStr mapName;
|
||||
idBlockAlloc<idTraceModelCache::trmCache_t,64,13> cacheAllocator;
|
||||
idList<idTraceModelCache::trmCache_t *,13> cache;
|
||||
idHashIndex hash;
|
||||
struct trmCache_t {
|
||||
idTraceModel *trm;
|
||||
int refCount;
|
||||
float volume;
|
||||
idVec3 centerOfMass;
|
||||
idMat3 inertiaTensor;
|
||||
const idMaterial *material;
|
||||
idCollisionModel *collisionModel;
|
||||
bool hasWater;
|
||||
idList<traceModelWater_t, 54> waterPoints;
|
||||
|
||||
// Materialized in the authoritative tracemodelcache.h dump.
|
||||
trmCache_t()
|
||||
: trm(nullptr), refCount(1), volume(0.0f), centerOfMass(),
|
||||
inertiaTensor(), material(nullptr), collisionModel(nullptr),
|
||||
hasWater(false), waterPoints(0) {
|
||||
centerOfMass.Zero();
|
||||
inertiaTensor[0].Zero();
|
||||
inertiaTensor[1].Zero();
|
||||
inertiaTensor[2].Zero();
|
||||
}
|
||||
};
|
||||
|
||||
// Materialized in the authoritative tracemodelcache.h dump.
|
||||
idTraceModelCache()
|
||||
: mapName(), cacheAllocator(false), cache(0),
|
||||
hash(1024, 1024, TAG_HASHINDEX) {
|
||||
}
|
||||
|
||||
int AllocTraceModel(const idTraceModel& traceModel,
|
||||
const idMaterial* material);
|
||||
void FreeTraceModel(int traceModelIndex);
|
||||
int CopyTraceModel(int traceModelIndex);
|
||||
void ClearTraceModelCache();
|
||||
|
||||
const trmCache_t* GetEntry(const int traceModelIndex) const {
|
||||
return traceModelIndex >= 0 && traceModelIndex < cache.Num()
|
||||
? cache[traceModelIndex]
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
trmCache_t* GetEntry(const int traceModelIndex) {
|
||||
return traceModelIndex >= 0 && traceModelIndex < cache.Num()
|
||||
? cache[traceModelIndex]
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
void GetMassProperties(int traceModelIndex, float density, float& mass,
|
||||
idVec3& center, idMat3& inertia) const {
|
||||
const trmCache_t* const entry = GetEntry(traceModelIndex);
|
||||
if (entry == nullptr) {
|
||||
mass = 0.0f;
|
||||
center.Zero();
|
||||
inertia = idMat3(1.0f);
|
||||
return;
|
||||
}
|
||||
mass = entry->volume * density;
|
||||
center = entry->centerOfMass;
|
||||
inertia = entry->inertiaTensor;
|
||||
for (int row = 0; row < 3; ++row) {
|
||||
for (int column = 0; column < 3; ++column) {
|
||||
inertia[row][column] *= density;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
idStr mapName;
|
||||
idBlockAlloc<trmCache_t, 64, 13> cacheAllocator;
|
||||
idList<trmCache_t *, 13> cache;
|
||||
idHashIndex hash;
|
||||
|
||||
private:
|
||||
void SetupWaterPoints(trmCache_t* entry);
|
||||
};
|
||||
|
||||
static_assert(sizeof(traceModelWater_t) == 16,
|
||||
"Recovered traceModelWater_t ABI changed");
|
||||
#if defined(_WIN32) && !defined(_WIN64)
|
||||
static_assert(sizeof(idTraceModelCache::trmCache_t) == 88,
|
||||
"Recovered trace-model cache entry ABI changed");
|
||||
static_assert(sizeof(idTraceModelCache) == 100,
|
||||
"Recovered idTraceModelCache ABI changed");
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
#include "gamelib/physics/tracemodelrecycler.h"
|
||||
|
||||
idTraceModelRecycler::~idTraceModelRecycler() {
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
void idTraceModelRecycler::Shutdown() {
|
||||
for (int index = 0; index < allocated.Num(); ++index) {
|
||||
idRecycledTraceModel* const recycled = allocated[index];
|
||||
delete recycled->trm;
|
||||
recycled->trm = nullptr;
|
||||
}
|
||||
|
||||
allocated.ClearFree();
|
||||
for (int stage = 0; stage < 3; ++stage) {
|
||||
free[stage].ClearFree();
|
||||
}
|
||||
allocator.Shutdown();
|
||||
}
|
||||
|
||||
idRecycledTraceModel* idTraceModelRecycler::GetTraceModel() {
|
||||
if (free[0].Num() != 0) {
|
||||
idRecycledTraceModel* const result = free[0][free[0].Num() - 1];
|
||||
free[0].RemoveIndexFast(free[0].Num() - 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
idRecycledTraceModel* const result = allocator.Alloc();
|
||||
if (result == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
result->trm = new idTraceModel();
|
||||
result->index = allocated.Append(result);
|
||||
if (result->index < 0) {
|
||||
delete result->trm;
|
||||
result->trm = nullptr;
|
||||
allocator.Free(result);
|
||||
return nullptr;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void idTraceModelRecycler::RecycleTraceModel(
|
||||
idRecycledTraceModel* const recycledTrm, const bool defer) {
|
||||
if (recycledTrm == nullptr) {
|
||||
return;
|
||||
}
|
||||
free[defer ? 2 : 0].Append(recycledTrm);
|
||||
}
|
||||
|
||||
void idTraceModelRecycler::UpdateDeferredFreed() {
|
||||
for (int stage = 1; stage < 3; ++stage) {
|
||||
for (int index = 0; index < free[stage].Num(); ++index) {
|
||||
free[stage - 1].Append(free[stage][index]);
|
||||
}
|
||||
free[stage].ClearFree();
|
||||
}
|
||||
}
|
||||
@@ -1,42 +1,68 @@
|
||||
#pragma once
|
||||
|
||||
// Reconstructed C++ declarations from IDA Local Types and PDB/DIA metadata.
|
||||
// Original PDB header: w:\tech5\engine\gamelib\physics\tracemodelrecycler.h
|
||||
// Recovered logical types: 4
|
||||
// Signatures retain Xbox 360 ABI evidence and may still require manual review.
|
||||
#include "idlib/blockalloc_base.h"
|
||||
#include "idlib/containers/hashindex.h"
|
||||
#include "idlib/containers/list.h"
|
||||
#include "idlib/containers/staticlist.h"
|
||||
#include "idlib/geometry/tracemodel.h"
|
||||
#include "idlib/text/atomicstring.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
// IDA Local Type ordinal 14127; PDB kind: class.
|
||||
class idTraceModelRecycler
|
||||
{
|
||||
struct idRecycledTraceModel {
|
||||
idTraceModel* trm;
|
||||
int index;
|
||||
|
||||
idRecycledTraceModel() : trm(nullptr), index(-1) {}
|
||||
};
|
||||
|
||||
class idTraceModelRecycler {
|
||||
public:
|
||||
idBlockAlloc<idRecycledTraceModel,64,13> allocator;
|
||||
idList<idRecycledTraceModel *,13> allocated;
|
||||
idList<idRecycledTraceModel *,13> free[3];
|
||||
// Materialized in the authoritative tracemodelrecycler.h dump.
|
||||
idTraceModelRecycler() : allocator(false), allocated(0), free{} {}
|
||||
~idTraceModelRecycler();
|
||||
|
||||
void Shutdown();
|
||||
idRecycledTraceModel* GetTraceModel();
|
||||
void RecycleTraceModel(idRecycledTraceModel* recycledTrm, bool defer);
|
||||
void UpdateDeferredFreed();
|
||||
|
||||
idBlockAlloc<idRecycledTraceModel, 64, 13> allocator;
|
||||
idList<idRecycledTraceModel*, 13> allocated;
|
||||
idList<idRecycledTraceModel*, 13> free[3];
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 14150; PDB kind: struct.
|
||||
struct idFXModelRecycler::fxPrtModel_t
|
||||
{
|
||||
idRenderModelParticle *pmodel;
|
||||
const idDeclParticle *pDecl;
|
||||
};
|
||||
class idDeclParticle;
|
||||
class idRenderModel;
|
||||
class idRenderModelParticle;
|
||||
|
||||
// IDA Local Type ordinal 14153; PDB kind: struct.
|
||||
struct idFXModelRecycler::fxStaticModel_t
|
||||
{
|
||||
idRenderModel *rmodel;
|
||||
idAtomicString modelName;
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 14156; PDB kind: class.
|
||||
class idFXModelRecycler
|
||||
{
|
||||
class idFXModelRecycler {
|
||||
public:
|
||||
idStaticList<idFXModelRecycler::fxPrtModel_t,256> fxPrtModels;
|
||||
idHashIndex activePrtModelHash;
|
||||
idHashIndex inactivePrtModelHash;
|
||||
idStaticList<idFXModelRecycler::fxStaticModel_t,64> fxStaticModels;
|
||||
idHashIndex activeStaticModelHash;
|
||||
idHashIndex inactiveStaticModelHash;
|
||||
struct fxPrtModel_t {
|
||||
idRenderModelParticle* pmodel;
|
||||
const idDeclParticle* pDecl;
|
||||
};
|
||||
|
||||
struct fxStaticModel_t {
|
||||
idRenderModel* rmodel;
|
||||
idAtomicString modelName;
|
||||
};
|
||||
|
||||
idStaticList<fxPrtModel_t, 256> fxPrtModels;
|
||||
idHashIndex activePrtModelHash;
|
||||
idHashIndex inactivePrtModelHash;
|
||||
idStaticList<fxStaticModel_t, 64> fxStaticModels;
|
||||
idHashIndex activeStaticModelHash;
|
||||
idHashIndex inactiveStaticModelHash;
|
||||
};
|
||||
|
||||
#if INTPTR_MAX == INT32_MAX
|
||||
static_assert(sizeof(idRecycledTraceModel) == 8,
|
||||
"Recovered idRecycledTraceModel ABI changed");
|
||||
static_assert(sizeof(idTraceModelRecycler) == 84,
|
||||
"Recovered idTraceModelRecycler ABI changed");
|
||||
static_assert(sizeof(idFXModelRecycler::fxPrtModel_t) == 8,
|
||||
"Recovered FX particle recycler entry ABI changed");
|
||||
static_assert(sizeof(idFXModelRecycler::fxStaticModel_t) == 8,
|
||||
"Recovered FX static recycler entry ABI changed");
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,311 @@
|
||||
#include "gamelib/pvs/pvs.h"
|
||||
|
||||
#include "idlib/lib_print.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <malloc.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
|
||||
int PVSBufferBytes(const int numAreas) {
|
||||
return ((numAreas + 31) >> 3) & ~3;
|
||||
}
|
||||
|
||||
std::string ResourcePath(const char* const name) {
|
||||
std::string path = name == nullptr ? "" : name;
|
||||
const std::string::size_type slash = path.find_last_of("/\\");
|
||||
const std::string::size_type dot = path.find_last_of('.');
|
||||
if (dot == std::string::npos
|
||||
|| (slash != std::string::npos && dot < slash)) {
|
||||
path += ".pvs";
|
||||
} else {
|
||||
path.replace(dot, std::string::npos, ".pvs");
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
idPVS::idPVS()
|
||||
: header{PVS_MAGIC, 0, 0, 0, 0, 0, 0},
|
||||
pvsName(),
|
||||
dataStart(nullptr),
|
||||
planes(nullptr),
|
||||
vertices(nullptr),
|
||||
nodes(nullptr),
|
||||
portals(nullptr),
|
||||
areas(nullptr),
|
||||
areaOffsets(nullptr),
|
||||
areaPVS(nullptr),
|
||||
currentPVS{},
|
||||
bare(false) {
|
||||
for (pvsCurrent_t& current : currentPVS) {
|
||||
current.handle.i = -1;
|
||||
current.handle.h = 0;
|
||||
current.pvs = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
idPVS::~idPVS() {
|
||||
Free();
|
||||
}
|
||||
|
||||
void idPVS::DecompressAddPVS(std::uint8_t* const pvs, const int numBytes,
|
||||
const std::uint8_t* compressed) {
|
||||
if (pvs == nullptr || compressed == nullptr || numBytes <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int numBits = numBytes * 8;
|
||||
int bit = 0;
|
||||
while (bit < numBits) {
|
||||
const std::uint8_t code = *compressed++;
|
||||
if ((code & 0x80u) != 0) {
|
||||
int skip = code & 0x3Fu;
|
||||
if ((code & 0x40u) != 0) {
|
||||
skip |= static_cast<int>(*compressed++) << 6;
|
||||
}
|
||||
bit += skip + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int literalBit = 0; literalBit < 7 && bit < numBits;
|
||||
++literalBit, ++bit) {
|
||||
pvs[bit >> 3] |= static_cast<std::uint8_t>(
|
||||
((code >> literalBit) & 1u) << (bit & 7));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int idPVS::GetPVSAreas(const idBounds& bounds, int* const outputAreas,
|
||||
const int maxAreas) const {
|
||||
if (outputAreas == nullptr || maxAreas <= 0) {
|
||||
return 0;
|
||||
}
|
||||
if (header.numNodes <= 1 || nodes == nullptr || planes == nullptr) {
|
||||
outputAreas[0] = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
std::vector<int> stack;
|
||||
stack.push_back(1);
|
||||
while (!stack.empty() && count < maxAreas) {
|
||||
const int nodeIndex = stack.back();
|
||||
stack.pop_back();
|
||||
if (nodeIndex < 0) {
|
||||
const int area = -1 - nodeIndex;
|
||||
bool duplicate = false;
|
||||
for (int index = 0; index < count; ++index) {
|
||||
duplicate |= outputAreas[index] == area;
|
||||
}
|
||||
if (!duplicate && area >= 0) {
|
||||
outputAreas[count++] = area;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (nodeIndex == 0 || nodeIndex >= header.numNodes) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const pvsNode_t& node = nodes[nodeIndex];
|
||||
const int side = bounds.PlaneSide(planes[node.planeNum], 0.1f);
|
||||
if (side == 3) {
|
||||
stack.push_back(node.children[1]);
|
||||
stack.push_back(node.children[0]);
|
||||
} else {
|
||||
stack.push_back(node.children[side]);
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
int idPVS::GetPVSArea(const idVec3& point) const {
|
||||
if (header.numNodes <= 1 || nodes == nullptr || planes == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nodeIndex = 1;
|
||||
while (nodeIndex > 0) {
|
||||
if (nodeIndex >= header.numNodes) {
|
||||
return 0;
|
||||
}
|
||||
const pvsNode_t& node = nodes[nodeIndex];
|
||||
nodeIndex = node.children[
|
||||
planes[node.planeNum].Distance(point) >= 0.0f ? 0 : 1];
|
||||
}
|
||||
return nodeIndex < 0 ? -1 - nodeIndex : 0;
|
||||
}
|
||||
|
||||
pvsHandle_t idPVS::DecodePVS(const int* const sourceAreas,
|
||||
const int numSourceAreas) const {
|
||||
if (sourceAreas == nullptr || numSourceAreas <= 0 || areaPVS == nullptr
|
||||
|| areaOffsets == nullptr) {
|
||||
return {-1, 0};
|
||||
}
|
||||
|
||||
unsigned int hash = 0;
|
||||
for (int index = 0; index < numSourceAreas; ++index) {
|
||||
hash ^= static_cast<unsigned int>(sourceAreas[index]);
|
||||
}
|
||||
|
||||
int slot = 0;
|
||||
while (slot < 8 && currentPVS[slot].handle.i != -1) {
|
||||
++slot;
|
||||
}
|
||||
if (slot == 8) {
|
||||
idLibPrint::Error("idPVS::AllocPVS: no free PVS left");
|
||||
}
|
||||
|
||||
currentPVS[slot].handle = {slot, hash};
|
||||
const int bytes = PVSBufferBytes(header.numAreas);
|
||||
std::memset(currentPVS[slot].pvs, 0, bytes);
|
||||
for (int index = 0; index < numSourceAreas; ++index) {
|
||||
const int area = sourceAreas[index];
|
||||
if (area >= 0 && area < header.numAreas) {
|
||||
DecompressAddPVS(currentPVS[slot].pvs, bytes,
|
||||
areaPVS + areaOffsets[area]);
|
||||
}
|
||||
}
|
||||
return currentPVS[slot].handle;
|
||||
}
|
||||
|
||||
void idPVS::FreePVS(const pvsHandle_t handle) const {
|
||||
if (handle.i >= 0 && handle.i < 8
|
||||
&& currentPVS[handle.i].handle == handle) {
|
||||
currentPVS[handle.i].handle.i = -1;
|
||||
return;
|
||||
}
|
||||
idLibPrint::Error("idPVS::FreePVS: invalid handle");
|
||||
}
|
||||
|
||||
bool idPVS::InPVS(const pvsHandle_t handle, const int* const targetAreas,
|
||||
const int numTargetAreas) const {
|
||||
if (handle.i < 0 || handle.i >= 8
|
||||
|| !(currentPVS[handle.i].handle == handle)) {
|
||||
idLibPrint::Error("idPVS::InPVS: invalid handle");
|
||||
}
|
||||
if (targetAreas == nullptr || numTargetAreas <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::uint8_t* const pvs = currentPVS[handle.i].pvs;
|
||||
for (int index = 0; index < numTargetAreas; ++index) {
|
||||
const int area = targetAreas[index];
|
||||
if (area >= 0 && area < header.numAreas
|
||||
&& (pvs[area >> 3] & (1u << (area & 7))) != 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned int idPVS::Alloc(const bool bareAllocation) {
|
||||
Free();
|
||||
bare = bareAllocation;
|
||||
|
||||
const unsigned int coreBytes =
|
||||
static_cast<unsigned int>(header.numPlanes * sizeof(idPlane)
|
||||
+ header.numNodes * sizeof(pvsNode_t)
|
||||
+ header.numAreas * sizeof(int)
|
||||
+ header.numPVSBytes);
|
||||
const unsigned int detailBytes = bare ? 0u : static_cast<unsigned int>(
|
||||
header.numVertices * sizeof(idVec3)
|
||||
+ header.numPortals * sizeof(pvsPortal_t)
|
||||
+ header.numAreas * sizeof(pvsArea_t));
|
||||
const unsigned int bytes = coreBytes + detailBytes;
|
||||
|
||||
dataStart = static_cast<std::uint8_t*>(
|
||||
_aligned_malloc((std::max)(bytes, 1u), 16));
|
||||
if (dataStart == nullptr) {
|
||||
idLibPrint::Error("idPVS::Alloc: allocation failed");
|
||||
}
|
||||
std::memset(dataStart, 0, bytes);
|
||||
|
||||
std::uint8_t* cursor = dataStart;
|
||||
planes = reinterpret_cast<idPlane*>(cursor);
|
||||
cursor += header.numPlanes * sizeof(idPlane);
|
||||
nodes = reinterpret_cast<pvsNode_t*>(cursor);
|
||||
cursor += header.numNodes * sizeof(pvsNode_t);
|
||||
areaOffsets = reinterpret_cast<int*>(cursor);
|
||||
cursor += header.numAreas * sizeof(int);
|
||||
areaPVS = cursor;
|
||||
cursor += header.numPVSBytes;
|
||||
|
||||
if (bare) {
|
||||
vertices = nullptr;
|
||||
portals = nullptr;
|
||||
areas = nullptr;
|
||||
} else {
|
||||
vertices = reinterpret_cast<idVec3*>(cursor);
|
||||
cursor += header.numVertices * sizeof(idVec3);
|
||||
portals = reinterpret_cast<pvsPortal_t*>(cursor);
|
||||
cursor += header.numPortals * sizeof(pvsPortal_t);
|
||||
areas = reinterpret_cast<pvsArea_t*>(cursor);
|
||||
}
|
||||
|
||||
const int bufferBytes = PVSBufferBytes(header.numAreas);
|
||||
for (pvsCurrent_t& current : currentPVS) {
|
||||
current.handle = {-1, 0};
|
||||
current.pvs = static_cast<std::uint8_t*>(
|
||||
_aligned_malloc((std::max)(bufferBytes, 1), 16));
|
||||
if (current.pvs == nullptr) {
|
||||
idLibPrint::Error("idPVS::Alloc: handle allocation failed");
|
||||
}
|
||||
std::memset(current.pvs, 0, bufferBytes);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
void idPVS::Free() {
|
||||
if (dataStart != nullptr) {
|
||||
_aligned_free(dataStart);
|
||||
dataStart = nullptr;
|
||||
}
|
||||
planes = nullptr;
|
||||
vertices = nullptr;
|
||||
nodes = nullptr;
|
||||
portals = nullptr;
|
||||
areas = nullptr;
|
||||
areaOffsets = nullptr;
|
||||
areaPVS = nullptr;
|
||||
for (pvsCurrent_t& current : currentPVS) {
|
||||
current.handle = {-1, 0};
|
||||
if (current.pvs != nullptr) {
|
||||
_aligned_free(current.pvs);
|
||||
current.pvs = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void idPVS::Default() {
|
||||
Free();
|
||||
header = {PVS_MAGIC, 0, 0, 0, 0, 1, 8};
|
||||
Alloc(false);
|
||||
areas[0] = {0, 0};
|
||||
areaOffsets[0] = 0;
|
||||
std::memset(areaPVS, 127, 8);
|
||||
}
|
||||
|
||||
void idPVS::LoadResource() {
|
||||
Free();
|
||||
const std::string path = ResourcePath(pvsName.c_str());
|
||||
std::ifstream file(path, std::ios::binary);
|
||||
if (!file.read(reinterpret_cast<char*>(&header), sizeof(header))
|
||||
|| header.magic != PVS_MAGIC) {
|
||||
idLibPrint::Warning("failed to open %s", path.c_str());
|
||||
Default();
|
||||
return;
|
||||
}
|
||||
|
||||
const unsigned int bytes = Alloc(bare);
|
||||
if (!file.read(reinterpret_cast<char*>(dataStart), bytes)) {
|
||||
idLibPrint::Warning("%s is not a PVS file or is truncated",
|
||||
path.c_str());
|
||||
Default();
|
||||
}
|
||||
}
|
||||
@@ -1,40 +1,102 @@
|
||||
#pragma once
|
||||
|
||||
// Reconstructed C++ declarations from IDA Local Types and PDB/DIA metadata.
|
||||
// Original PDB header: w:\tech5\engine\gamelib\pvs\pvs.h
|
||||
// Recovered logical types: 2
|
||||
// Signatures retain Xbox 360 ABI evidence and may still require manual review.
|
||||
#include "idlib/bv/bounds.h"
|
||||
#include "idlib/math/plane.h"
|
||||
#include "idlib/math/vector.h"
|
||||
#include "idlib/text/str.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
// IDA Local Type ordinal 14174; PDB kind: class.
|
||||
class __declspec(align(4)) idPVS
|
||||
{
|
||||
struct pvsNode_t {
|
||||
unsigned int planeNum;
|
||||
std::int16_t children[2];
|
||||
};
|
||||
|
||||
struct pvsPortal_t {
|
||||
int firstVertex;
|
||||
int numVertices;
|
||||
};
|
||||
|
||||
struct pvsArea_t {
|
||||
int firstPortal;
|
||||
int numPortals;
|
||||
};
|
||||
|
||||
struct pvsHandle_t {
|
||||
int i;
|
||||
unsigned int h;
|
||||
|
||||
bool operator==(const pvsHandle_t& other) const {
|
||||
return i == other.i && h == other.h;
|
||||
}
|
||||
};
|
||||
|
||||
struct pvsCurrent_t {
|
||||
pvsHandle_t handle;
|
||||
std::uint8_t* pvs;
|
||||
};
|
||||
|
||||
class idPVS {
|
||||
public:
|
||||
// Recovered virtual interface; IDA vtable ordinal 14175.
|
||||
virtual ~idPVS();
|
||||
struct header_t {
|
||||
int magic;
|
||||
int numPlanes;
|
||||
int numVertices;
|
||||
int numNodes;
|
||||
int numPortals;
|
||||
int numAreas;
|
||||
int numPVSBytes;
|
||||
};
|
||||
|
||||
idPVS::header_t header;
|
||||
idStr pvsName;
|
||||
unsigned __int8 *dataStart;
|
||||
idPlane *planes;
|
||||
idVec3 *vertices;
|
||||
pvsNode_t *nodes;
|
||||
pvsPortal_t *portals;
|
||||
pvsArea_t *areas;
|
||||
int *areaOffsets;
|
||||
unsigned __int8 *areaPVS;
|
||||
pvsCurrent_t currentPVS[8];
|
||||
bool bare;
|
||||
static constexpr int PVS_MAGIC = 55793232;
|
||||
|
||||
idPVS();
|
||||
virtual ~idPVS();
|
||||
|
||||
int GetPVSAreas(const idBounds& bounds, int* areas, int maxAreas) const;
|
||||
int GetPVSArea(const idVec3& point) const;
|
||||
pvsHandle_t DecodePVS(const int* sourceAreas, int numSourceAreas) const;
|
||||
void FreePVS(pvsHandle_t handle) const;
|
||||
bool InPVS(pvsHandle_t handle, const int* targetAreas,
|
||||
int numTargetAreas) const;
|
||||
|
||||
void LoadResource();
|
||||
void SetName(const char* name) { pvsName = name; }
|
||||
void SetBare(bool value) { bare = value; }
|
||||
|
||||
// These allocation/default hooks are part of the recovered implementation
|
||||
// boundary and are public here so resource loaders can populate the data.
|
||||
unsigned int Alloc(bool bareAllocation);
|
||||
void Free();
|
||||
void Default();
|
||||
|
||||
static void DecompressAddPVS(std::uint8_t* pvs, int numBytes,
|
||||
const std::uint8_t* compressed);
|
||||
|
||||
header_t header;
|
||||
idStr pvsName;
|
||||
std::uint8_t* dataStart;
|
||||
idPlane* planes;
|
||||
idVec3* vertices;
|
||||
pvsNode_t* nodes;
|
||||
pvsPortal_t* portals;
|
||||
pvsArea_t* areas;
|
||||
int* areaOffsets;
|
||||
std::uint8_t* areaPVS;
|
||||
mutable pvsCurrent_t currentPVS[8];
|
||||
bool bare;
|
||||
};
|
||||
|
||||
// IDA Local Type ordinal 14176; PDB kind: struct.
|
||||
struct idPVS::header_t
|
||||
{
|
||||
int magic;
|
||||
int numPlanes;
|
||||
int numVertices;
|
||||
int numNodes;
|
||||
int numPortals;
|
||||
int numAreas;
|
||||
int numPVSBytes;
|
||||
};
|
||||
#if INTPTR_MAX == INT32_MAX
|
||||
static_assert(sizeof(pvsNode_t) == 8, "Recovered pvsNode_t ABI changed");
|
||||
static_assert(sizeof(pvsPortal_t) == 8,
|
||||
"Recovered pvsPortal_t ABI changed");
|
||||
static_assert(sizeof(pvsArea_t) == 8, "Recovered pvsArea_t ABI changed");
|
||||
static_assert(sizeof(pvsHandle_t) == 8,
|
||||
"Recovered pvsHandle_t ABI changed");
|
||||
static_assert(sizeof(pvsCurrent_t) == 12,
|
||||
"Recovered pvsCurrent_t ABI changed");
|
||||
static_assert(sizeof(idPVS::header_t) == 28,
|
||||
"Recovered idPVS header ABI changed");
|
||||
static_assert(sizeof(idPVS) == 196, "Recovered idPVS ABI changed");
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
#include "gamelib/timeline/timelineeventdata.h"
|
||||
|
||||
tlEventData_t::tlEventData_t()
|
||||
: time(0)
|
||||
, duration(0)
|
||||
, type(TL_EVENT_NONE)
|
||||
, anim()
|
||||
, useAnimPos(false)
|
||||
, sound(nullptr)
|
||||
, worldPos(0.0f, 0.0f, 0.0f)
|
||||
, worldRot(0.0f, 0.0f, 0.0f)
|
||||
, keyName(TL_KEY_INVALID)
|
||||
, keyValue()
|
||||
, keyAnimData()
|
||||
, activateStart(true)
|
||||
, activateEnd(false)
|
||||
, env(nullptr) {
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
#pragma once
|
||||
|
||||
#include "gamelib/timeline/timelinekeyanimdata.h"
|
||||
#include "idlib/math/angles.h"
|
||||
#include "idlib/text/str.h"
|
||||
|
||||
class idDeclEnv;
|
||||
class idSoundShader;
|
||||
|
||||
// Local type 14673 is present in tungsten.exe.h without a PDB source path.
|
||||
class idAnimAliasRef : public idStr {
|
||||
public:
|
||||
idAnimAliasRef() = default;
|
||||
explicit idAnimAliasRef(const char *value) : idStr(value) {}
|
||||
};
|
||||
|
||||
enum tlEventType_t : int {
|
||||
TL_EVENT_NONE = 0,
|
||||
TL_EVENT_ANIM = 1,
|
||||
TL_EVENT_SOUND = 2,
|
||||
TL_EVENT_TELEPORT = 3,
|
||||
TL_EVENT_SETKEY = 4,
|
||||
TL_EVENT_ANIMKEY = 5,
|
||||
TL_EVENT_ACTIVATE = 6,
|
||||
TL_EVENT_SETENV = 7,
|
||||
TL_EVENT_MAX = 8,
|
||||
};
|
||||
|
||||
// Reconstructed from tungsten.exe.h local type 19107 and the constructor in
|
||||
// timelineeventdata.cpp.
|
||||
struct tlEventData_t {
|
||||
tlEventData_t();
|
||||
|
||||
int time;
|
||||
int duration;
|
||||
tlEventType_t type;
|
||||
idAnimAliasRef anim;
|
||||
bool useAnimPos;
|
||||
const idSoundShader *sound;
|
||||
idVec3 worldPos;
|
||||
idAngles worldRot;
|
||||
tlKeyId_t keyName;
|
||||
idStr keyValue;
|
||||
mgTimelineKeyAnimData keyAnimData;
|
||||
bool activateStart;
|
||||
bool activateEnd;
|
||||
const idDeclEnv *env;
|
||||
};
|
||||
|
||||
#if defined(_WIN32) && !defined(_WIN64)
|
||||
static_assert(sizeof(tlEventData_t) == 160,
|
||||
"Recovered tlEventData_t ABI changed");
|
||||
#endif
|
||||
@@ -0,0 +1,157 @@
|
||||
#include "gamelib/timeline/timelinekeyanimdata.h"
|
||||
|
||||
#include "idlib/containers/search.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
|
||||
namespace {
|
||||
|
||||
idSearch_BinaryDefault<float> timelineKeySearch;
|
||||
|
||||
template<typename type_t>
|
||||
const type_t& KeyAt(const idList<char, 5>& data, const int index,
|
||||
const int keySize) {
|
||||
return *reinterpret_cast<const type_t*>(
|
||||
data.Ptr() + static_cast<std::ptrdiff_t>(index) * keySize
|
||||
);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
mgTimelineKeyAnimData::mgTimelineKeyAnimData()
|
||||
: type(TL_KEYTYPE_MAX)
|
||||
, keySize(0)
|
||||
, times(0)
|
||||
, data(0) {
|
||||
}
|
||||
|
||||
void mgTimelineKeyAnimData::PostLoadInit() {
|
||||
keySize = mgTimelineKeyUtil::KeySize(type);
|
||||
}
|
||||
|
||||
bool mgTimelineKeyAnimData::FindLerpKeys(const float time, int *key0,
|
||||
int *key1, float *fraction) const {
|
||||
if (key0 == nullptr || key1 == nullptr || fraction == nullptr
|
||||
|| times.Num() == 0 || times.Ptr() == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const int index = timelineKeySearch.Search_LastLessEqual(
|
||||
times.Ptr(), times.Num(), time
|
||||
);
|
||||
if (times[index] > time) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*key0 = index;
|
||||
if (index < times.Num() - 1) {
|
||||
*key1 = index + 1;
|
||||
const float duration = times[index + 1] - times[index];
|
||||
*fraction = duration != 0.0f ? (time - times[index]) / duration : 0.0f;
|
||||
} else {
|
||||
*key1 = index;
|
||||
*fraction = 0.0f;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename type_t>
|
||||
bool mgTimelineKeyAnimData::GetLerped(const float time, type_t *value) const {
|
||||
int key0 = 0;
|
||||
int key1 = 0;
|
||||
float fraction = 0.0f;
|
||||
if (value == nullptr || keySize != static_cast<int>(sizeof(type_t))
|
||||
|| !FindLerpKeys(time, &key0, &key1, &fraction)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const type_t& value0 = KeyAt<type_t>(data, key0, keySize);
|
||||
const type_t& value1 = KeyAt<type_t>(data, key1, keySize);
|
||||
*value = value0 + (value1 - value0) * fraction;
|
||||
return true;
|
||||
}
|
||||
|
||||
template<>
|
||||
bool mgTimelineKeyAnimData::GetLerped<int>(const float time, int *value) const {
|
||||
int key0 = 0;
|
||||
int key1 = 0;
|
||||
float fraction = 0.0f;
|
||||
if (value == nullptr || keySize != static_cast<int>(sizeof(int))
|
||||
|| !FindLerpKeys(time, &key0, &key1, &fraction)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const int value0 = KeyAt<int>(data, key0, keySize);
|
||||
const int value1 = KeyAt<int>(data, key1, keySize);
|
||||
*value = static_cast<int>(static_cast<float>(value0)
|
||||
+ static_cast<float>(value1 - value0) * fraction);
|
||||
return true;
|
||||
}
|
||||
|
||||
template<>
|
||||
bool mgTimelineKeyAnimData::GetLerped<idVec3>(const float time,
|
||||
idVec3 *value) const {
|
||||
int key0 = 0;
|
||||
int key1 = 0;
|
||||
float fraction = 0.0f;
|
||||
if (value == nullptr || keySize != static_cast<int>(sizeof(idVec3))
|
||||
|| !FindLerpKeys(time, &key0, &key1, &fraction)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const idVec3& value0 = KeyAt<idVec3>(data, key0, keySize);
|
||||
const idVec3& value1 = KeyAt<idVec3>(data, key1, keySize);
|
||||
value->x = value0.x + (value1.x - value0.x) * fraction;
|
||||
value->y = value0.y + (value1.y - value0.y) * fraction;
|
||||
value->z = value0.z + (value1.z - value0.z) * fraction;
|
||||
return true;
|
||||
}
|
||||
|
||||
template<>
|
||||
bool mgTimelineKeyAnimData::GetLerped<idVec4>(const float time,
|
||||
idVec4 *value) const {
|
||||
int key0 = 0;
|
||||
int key1 = 0;
|
||||
float fraction = 0.0f;
|
||||
if (value == nullptr || keySize != static_cast<int>(sizeof(idVec4))
|
||||
|| !FindLerpKeys(time, &key0, &key1, &fraction)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const idVec4& value0 = KeyAt<idVec4>(data, key0, keySize);
|
||||
const idVec4& value1 = KeyAt<idVec4>(data, key1, keySize);
|
||||
value->x = value0.x + (value1.x - value0.x) * fraction;
|
||||
value->y = value0.y + (value1.y - value0.y) * fraction;
|
||||
value->z = value0.z + (value1.z - value0.z) * fraction;
|
||||
value->w = value0.w + (value1.w - value0.w) * fraction;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool mgTimelineKeyAnimData::GetData(const float time, bool *value) const {
|
||||
int key0 = 0;
|
||||
int key1 = 0;
|
||||
float fraction = 0.0f;
|
||||
if (value == nullptr || keySize != 1
|
||||
|| !FindLerpKeys(time, &key0, &key1, &fraction)) {
|
||||
return false;
|
||||
}
|
||||
*value = data[key0] != 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool mgTimelineKeyAnimData::GetData(const float time, int *value) const {
|
||||
return GetLerped(time, value);
|
||||
}
|
||||
|
||||
bool mgTimelineKeyAnimData::GetData(const float time, float *value) const {
|
||||
return GetLerped(time, value);
|
||||
}
|
||||
|
||||
bool mgTimelineKeyAnimData::GetData(const float time, idVec3 *value) const {
|
||||
return GetLerped(time, value);
|
||||
}
|
||||
|
||||
bool mgTimelineKeyAnimData::GetData(const float time, idVec4 *value) const {
|
||||
return GetLerped(time, value);
|
||||
}
|
||||
@@ -1,17 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include "gamelib/timeline/timelinekeytypes.h"
|
||||
#include "idlib/containers/list.h"
|
||||
|
||||
// Reconstructed C++ declarations from IDA Local Types and PDB/DIA metadata.
|
||||
// Original PDB header: w:\tech5\engine\gamelib\timeline\timelinekeyanimdata.h
|
||||
// Recovered logical types: 1
|
||||
// Signatures retain Xbox 360 ABI evidence and may still require manual review.
|
||||
|
||||
|
||||
// IDA Local Type ordinal 19106; PDB kind: class.
|
||||
// IDA Local Type ordinal 19106; PDB kind: class. Method declarations are from
|
||||
// the decorated functions in timelinekeyanimdata.cpp.
|
||||
class mgTimelineKeyAnimData
|
||||
{
|
||||
public:
|
||||
mgTimelineKeyAnimData();
|
||||
|
||||
void PostLoadInit();
|
||||
bool GetData(float time, bool *value) const;
|
||||
bool GetData(float time, int *value) const;
|
||||
bool GetData(float time, float *value) const;
|
||||
bool GetData(float time, idVec3 *value) const;
|
||||
bool GetData(float time, idVec4 *value) const;
|
||||
|
||||
tlKeyType_t type;
|
||||
int keySize;
|
||||
idList<float,5> times;
|
||||
idList<char,5> data;
|
||||
|
||||
private:
|
||||
bool FindLerpKeys(float time, int *key0, int *key1, float *fraction) const;
|
||||
|
||||
template<typename type_t>
|
||||
bool GetLerped(float time, type_t *value) const;
|
||||
};
|
||||
|
||||
#if defined(_WIN32) && !defined(_WIN64)
|
||||
static_assert(sizeof(mgTimelineKeyAnimData) == 40,
|
||||
"Recovered mgTimelineKeyAnimData ABI changed");
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
#include "gamelib/timeline/timelinekeytypes.h"
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstdlib>
|
||||
|
||||
namespace {
|
||||
|
||||
// The seven-entry maps are recovered from the tlKeyId_t/tlKeyType_t metadata,
|
||||
// the typed SetValue overloads in entityvalueaccess.cpp, and the serialized
|
||||
// key widths consumed by timelinekeyanimdata.cpp.
|
||||
constexpr tlKeyType_t keyTypeMap[TL_KEY_MAX] = {
|
||||
TL_KEYTYPE_BOOL,
|
||||
TL_KEYTYPE_COLOR_RGB,
|
||||
TL_KEYTYPE_FLOAT4,
|
||||
TL_KEYTYPE_FLOAT,
|
||||
TL_KEYTYPE_FLOAT,
|
||||
TL_KEYTYPE_FLOAT,
|
||||
TL_KEYTYPE_FLOAT,
|
||||
};
|
||||
|
||||
constexpr int keySizes[TL_KEYTYPE_MAX] = {
|
||||
1,
|
||||
4,
|
||||
4,
|
||||
12,
|
||||
12,
|
||||
16,
|
||||
16,
|
||||
};
|
||||
|
||||
bool ParseNextFloat(const char *&cursor, float &value) {
|
||||
if (cursor == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char *end = nullptr;
|
||||
errno = 0;
|
||||
value = std::strtof(cursor, &end);
|
||||
if (end == cursor || errno == ERANGE) {
|
||||
return false;
|
||||
}
|
||||
cursor = end;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
tlKeyType_t mgTimelineKeyUtil::GetType(const tlKeyId_t key) {
|
||||
if (key < TL_KEY_VISIBILITY || key >= TL_KEY_MAX) {
|
||||
return TL_KEYTYPE_MAX;
|
||||
}
|
||||
return keyTypeMap[static_cast<int>(key)];
|
||||
}
|
||||
|
||||
int mgTimelineKeyUtil::KeySize(const tlKeyType_t type) {
|
||||
if (type < TL_KEYTYPE_BOOL || type >= TL_KEYTYPE_MAX) {
|
||||
return 0;
|
||||
}
|
||||
return keySizes[static_cast<int>(type)];
|
||||
}
|
||||
|
||||
bool mgTimelineKeyUtil::FromString(const idStr& valueStr, bool *value) {
|
||||
if (value == nullptr) {
|
||||
return false;
|
||||
}
|
||||
*value = std::strtol(valueStr.c_str(), nullptr, 10) != 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool mgTimelineKeyUtil::FromString(const idStr& valueStr, int *value) {
|
||||
if (value == nullptr) {
|
||||
return false;
|
||||
}
|
||||
*value = static_cast<int>(std::strtol(valueStr.c_str(), nullptr, 10));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool mgTimelineKeyUtil::FromString(const idStr& valueStr, float *value) {
|
||||
if (value == nullptr) {
|
||||
return false;
|
||||
}
|
||||
const char *cursor = valueStr.c_str();
|
||||
return ParseNextFloat(cursor, *value);
|
||||
}
|
||||
|
||||
bool mgTimelineKeyUtil::FromString(const idStr& valueStr, idVec3 *value) {
|
||||
if (value == nullptr) {
|
||||
return false;
|
||||
}
|
||||
const char *cursor = valueStr.c_str();
|
||||
return ParseNextFloat(cursor, value->x)
|
||||
&& ParseNextFloat(cursor, value->y)
|
||||
&& ParseNextFloat(cursor, value->z);
|
||||
}
|
||||
|
||||
bool mgTimelineKeyUtil::FromString(const idStr& valueStr, idVec4 *value) {
|
||||
if (value == nullptr) {
|
||||
return false;
|
||||
}
|
||||
const char *cursor = valueStr.c_str();
|
||||
return ParseNextFloat(cursor, value->x)
|
||||
&& ParseNextFloat(cursor, value->y)
|
||||
&& ParseNextFloat(cursor, value->z)
|
||||
&& ParseNextFloat(cursor, value->w);
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#include "idlib/math/vector.h"
|
||||
#include "idlib/text/str.h"
|
||||
|
||||
// Reconstructed from tungsten.exe local types 1723 and 1758. The numeric
|
||||
// values are part of the serialized timeline format and must not be reordered.
|
||||
enum tlKeyId_t : int {
|
||||
TL_KEY_INVALID = -1,
|
||||
TL_KEY_VISIBILITY = 0,
|
||||
TL_KEY_LIGHTCOLOR = 1,
|
||||
TL_KEY_DOF = 2,
|
||||
TL_KEY_FOCAL = 3,
|
||||
TL_KEY_NEARBP = 4,
|
||||
TL_KEY_FARBP = 5,
|
||||
TL_KEY_BSCALE = 6,
|
||||
TL_KEY_MAX = 7,
|
||||
};
|
||||
|
||||
enum tlKeyType_t : int {
|
||||
TL_KEYTYPE_BOOL = 0,
|
||||
TL_KEYTYPE_INT = 1,
|
||||
TL_KEYTYPE_FLOAT = 2,
|
||||
TL_KEYTYPE_FLOAT3 = 3,
|
||||
TL_KEYTYPE_COLOR_RGB = 4,
|
||||
TL_KEYTYPE_FLOAT4 = 5,
|
||||
TL_KEYTYPE_COLOR_RGBA = 6,
|
||||
TL_KEYTYPE_MAX = 7,
|
||||
};
|
||||
|
||||
class mgTimelineKeyUtil {
|
||||
public:
|
||||
static tlKeyType_t GetType(tlKeyId_t key);
|
||||
static int KeySize(tlKeyType_t type);
|
||||
|
||||
static bool FromString(const idStr& valueStr, bool* value);
|
||||
static bool FromString(const idStr& valueStr, int* value);
|
||||
static bool FromString(const idStr& valueStr, float* value);
|
||||
static bool FromString(const idStr& valueStr, idVec3* value);
|
||||
static bool FromString(const idStr& valueStr, idVec4* value);
|
||||
};
|
||||
|
||||
static_assert(sizeof(tlKeyId_t) == 4, "Recovered tlKeyId_t ABI changed");
|
||||
static_assert(sizeof(tlKeyType_t) == 4, "Recovered tlKeyType_t ABI changed");
|
||||
@@ -1,29 +1,64 @@
|
||||
#pragma once
|
||||
|
||||
// Reconstructed C++ declarations from IDA Local Types and PDB/DIA metadata.
|
||||
// Original PDB header: w:\tech5\engine\mapfile\mapfile.h
|
||||
// Recovered logical types: 1
|
||||
// Signatures retain Xbox 360 ABI evidence and may still require manual review.
|
||||
#include "mapfile/mapmodel.h"
|
||||
|
||||
class idDeclEntityDef;
|
||||
class idGame;
|
||||
|
||||
// IDA Local Type ordinal 15161; PDB kind: class.
|
||||
class __declspec(align(4)) idMapFile
|
||||
{
|
||||
class idGroupState {
|
||||
public:
|
||||
// Recovered virtual interface; IDA vtable ordinal 15162.
|
||||
virtual ~idMapFile();
|
||||
|
||||
idStr name;
|
||||
int version;
|
||||
unsigned int fileTime;
|
||||
unsigned int geometryCRC;
|
||||
bool hasPrimitiveData;
|
||||
idList<idMapEntity *,5> entities;
|
||||
idMapEditorStates groupStates;
|
||||
idList<idRefMapDef,5> referenceMaps;
|
||||
idMapEntity *parentReference;
|
||||
bool expandReferences;
|
||||
idGame *game;
|
||||
bool entitiesAreReference;
|
||||
bool ignoreGroupInfo;
|
||||
idStr groupName;
|
||||
unsigned int state = 0;
|
||||
};
|
||||
|
||||
class idMapEditorStates {
|
||||
public:
|
||||
virtual ~idMapEditorStates() = default;
|
||||
idList<idGroupState, TAG_IDLIB> groupStates;
|
||||
idList<idGroupState, TAG_IDLIB> layerStates;
|
||||
idList<idGroupState, TAG_IDLIB> groupColors;
|
||||
};
|
||||
|
||||
class idRefMapDef {
|
||||
public:
|
||||
idStr mapName;
|
||||
idStr worldSpawnText;
|
||||
};
|
||||
|
||||
class idMapEntity : public idMapGroups {
|
||||
public:
|
||||
~idMapEntity() override = default;
|
||||
idDeclEntityDef* entityDef = nullptr;
|
||||
idMapModel model;
|
||||
idMapEntity* parentReference = nullptr;
|
||||
idStr refId;
|
||||
idList<idStr, TAG_IDLIB> layerList;
|
||||
};
|
||||
|
||||
class alignas(4) idMapFile {
|
||||
public:
|
||||
virtual ~idMapFile() = default;
|
||||
|
||||
idStr name;
|
||||
int version = 0;
|
||||
unsigned int fileTime = 0;
|
||||
unsigned int geometryCRC = 0;
|
||||
bool hasPrimitiveData = false;
|
||||
idList<idMapEntity*, TAG_IDLIB> entities;
|
||||
idMapEditorStates groupStates;
|
||||
idList<idRefMapDef, TAG_IDLIB> referenceMaps;
|
||||
idMapEntity* parentReference = nullptr;
|
||||
bool expandReferences = false;
|
||||
idGame* game = nullptr;
|
||||
bool entitiesAreReference = false;
|
||||
bool ignoreGroupInfo = false;
|
||||
};
|
||||
|
||||
#if INTPTR_MAX == INT32_MAX
|
||||
static_assert(sizeof(idMapEditorStates) == 52,
|
||||
"Recovered idMapEditorStates ABI changed");
|
||||
static_assert(sizeof(idMapEntity) == 124,
|
||||
"Recovered idMapEntity ABI changed");
|
||||
static_assert(sizeof(idMapFile) == 152,
|
||||
"Recovered idMapFile ABI changed");
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
#pragma once
|
||||
|
||||
#include "idlib/containers/list.h"
|
||||
#include "idlib/geometry/surface_patch.h"
|
||||
#include "idlib/math/plane.h"
|
||||
#include "idlib/text/str.h"
|
||||
|
||||
class idFile;
|
||||
|
||||
class idMapGroups {
|
||||
public:
|
||||
virtual ~idMapGroups() = default;
|
||||
idList<idStr, TAG_IDLIB> groupList;
|
||||
};
|
||||
|
||||
enum mapPrimitiveType_t : int {
|
||||
MAP_PRIMITIVE_BRUSH = 0,
|
||||
MAP_PRIMITIVE_PATCH = 1
|
||||
};
|
||||
|
||||
class idMapPrimitive : public idMapGroups {
|
||||
public:
|
||||
~idMapPrimitive() override = default;
|
||||
virtual unsigned int GetGeometryCRC() { return 0; }
|
||||
virtual void Transform(const idVec3*, const idMat3*) {}
|
||||
virtual bool Write(idFile*, int) { return false; }
|
||||
|
||||
int type = MAP_PRIMITIVE_BRUSH;
|
||||
};
|
||||
|
||||
class idMapModel {
|
||||
public:
|
||||
idStr name;
|
||||
idList<idMapPrimitive*, TAG_IDLIB> primitives;
|
||||
};
|
||||
|
||||
class idMapBrushSide {
|
||||
public:
|
||||
idStr material;
|
||||
idPlane plane;
|
||||
idVec3 texMat[2];
|
||||
idVec3 origin;
|
||||
};
|
||||
|
||||
class idMapBrush : public idMapPrimitive {
|
||||
public:
|
||||
idList<idMapBrushSide*, TAG_IDLIB> sides;
|
||||
};
|
||||
|
||||
class idMapPatch : public idMapPrimitive, public idSurface_Patch {
|
||||
public:
|
||||
idVec3 origin;
|
||||
idStr material;
|
||||
int horzSubdivisions = 0;
|
||||
int vertSubdivisions = 0;
|
||||
bool explicitSubdivisions = false;
|
||||
};
|
||||
|
||||
#if INTPTR_MAX == INT32_MAX
|
||||
static_assert(sizeof(idMapPrimitive) == 24,
|
||||
"Recovered idMapPrimitive ABI changed");
|
||||
static_assert(sizeof(idMapModel) == 48,
|
||||
"Recovered idMapModel ABI changed");
|
||||
static_assert(sizeof(idMapBrushSide) == 84,
|
||||
"Recovered idMapBrushSide ABI changed");
|
||||
static_assert(sizeof(idMapBrush) == 40,
|
||||
"Recovered idMapBrush ABI changed");
|
||||
static_assert(sizeof(idMapPatch) == 164,
|
||||
"Recovered idMapPatch ABI changed");
|
||||
#endif
|
||||
@@ -1,7 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "../math/plane.h"
|
||||
#include "../math/vector.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
// Recovered two-vector bounds layout used by the geometry reconstruction.
|
||||
class idBounds {
|
||||
public:
|
||||
@@ -14,6 +17,122 @@ public:
|
||||
idVec3& operator[](const int index) {
|
||||
return b[index];
|
||||
}
|
||||
|
||||
// Recovered from shared/idlib/bv/bounds.cpp. This scalar PC spelling is
|
||||
// the center/extents form of the original VMX implementation.
|
||||
void FromTransformedBounds(const idBounds& bounds,
|
||||
const idVec3& origin, const idMat3& axis) {
|
||||
const idVec3 center = (bounds[0] + bounds[1]) * 0.5f;
|
||||
const idVec3 extents = bounds[1] - center;
|
||||
const idVec3 transformedCenter = origin + idVec3(
|
||||
axis[0].x * center.x + axis[1].x * center.y + axis[2].x * center.z,
|
||||
axis[0].y * center.x + axis[1].y * center.y + axis[2].y * center.z,
|
||||
axis[0].z * center.x + axis[1].z * center.y + axis[2].z * center.z);
|
||||
const idVec3 transformedExtents(
|
||||
std::fabs(axis[0].x * extents.x) +
|
||||
std::fabs(axis[1].x * extents.y) +
|
||||
std::fabs(axis[2].x * extents.z),
|
||||
std::fabs(axis[0].y * extents.x) +
|
||||
std::fabs(axis[1].y * extents.y) +
|
||||
std::fabs(axis[2].y * extents.z),
|
||||
std::fabs(axis[0].z * extents.x) +
|
||||
std::fabs(axis[1].z * extents.y) +
|
||||
std::fabs(axis[2].z * extents.z));
|
||||
b[0] = transformedCenter - transformedExtents;
|
||||
b[1] = transformedCenter + transformedExtents;
|
||||
}
|
||||
|
||||
// Recovered from shared/idlib/bv/bounds.cpp. Return values match the
|
||||
// original plane-side convention: 0 front, 1 back, 3 crossing.
|
||||
int PlaneSide(const idPlane& plane, const float epsilon = 0.0f) const {
|
||||
const idVec3 center = (b[0] + b[1]) * 0.5f;
|
||||
const idVec3 extents = b[1] - center;
|
||||
const float radius = std::fabs(extents.x * plane.a)
|
||||
+ std::fabs(extents.y * plane.b)
|
||||
+ std::fabs(extents.z * plane.c);
|
||||
const float distance = plane.Distance(center);
|
||||
if (distance - radius > epsilon) {
|
||||
return 0;
|
||||
}
|
||||
if (distance + radius < -epsilon) {
|
||||
return 1;
|
||||
}
|
||||
return 3;
|
||||
}
|
||||
|
||||
// Recovered from shared/idlib/bv/bounds.cpp. This is the separating-axis
|
||||
// segment/AABB test used by GameLib obstacle routing.
|
||||
bool LineIntersection(const idVec3& start, const idVec3& end) const {
|
||||
const idVec3 lineDir = (end - start) * 0.5f;
|
||||
const idVec3 lineCenter = start + lineDir;
|
||||
const idVec3 boundsCenter = (b[0] + b[1]) * 0.5f;
|
||||
const idVec3 extents = b[1] - boundsCenter;
|
||||
const idVec3 dirAbs(
|
||||
std::fabs(lineDir.x), std::fabs(lineDir.y),
|
||||
std::fabs(lineDir.z)
|
||||
);
|
||||
const idVec3 centerDelta = lineCenter - boundsCenter;
|
||||
|
||||
if (std::fabs(centerDelta.x) > extents.x + dirAbs.x
|
||||
|| std::fabs(centerDelta.y) > extents.y + dirAbs.y
|
||||
|| std::fabs(centerDelta.z) > extents.z + dirAbs.z) {
|
||||
return false;
|
||||
}
|
||||
if (std::fabs(centerDelta.z * lineDir.y
|
||||
- centerDelta.y * lineDir.z)
|
||||
> extents.y * dirAbs.z + extents.z * dirAbs.y) {
|
||||
return false;
|
||||
}
|
||||
if (std::fabs(lineDir.z * centerDelta.x
|
||||
- centerDelta.z * lineDir.x)
|
||||
> extents.z * dirAbs.x + extents.x * dirAbs.z) {
|
||||
return false;
|
||||
}
|
||||
return std::fabs(centerDelta.y * lineDir.x
|
||||
- lineDir.y * centerDelta.x)
|
||||
<= extents.y * dirAbs.x + extents.x * dirAbs.y;
|
||||
}
|
||||
|
||||
bool RayIntersection(const idVec3& start, const idVec3& direction,
|
||||
float& scale) const {
|
||||
float minimum = -1.0e30f;
|
||||
float maximum = 1.0e30f;
|
||||
for (int axis = 0; axis < 3; ++axis) {
|
||||
if (std::fabs(direction[axis]) < 1.0e-20f) {
|
||||
if (start[axis] < b[0][axis] || start[axis] > b[1][axis]) {
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
const float inverseDirection = 1.0f / direction[axis];
|
||||
float first = (b[0][axis] - start[axis]) * inverseDirection;
|
||||
float second = (b[1][axis] - start[axis]) * inverseDirection;
|
||||
if (first > second) {
|
||||
const float swap = first;
|
||||
first = second;
|
||||
second = swap;
|
||||
}
|
||||
if (first > minimum) minimum = first;
|
||||
if (second < maximum) maximum = second;
|
||||
if (minimum > maximum) return false;
|
||||
}
|
||||
scale = minimum;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SphereIntersection(const idVec3& origin, const float radius) const {
|
||||
float distanceSquared = 0.0f;
|
||||
for (int axis = 0; axis < 3; ++axis) {
|
||||
float delta = 0.0f;
|
||||
if (origin[axis] < b[0][axis]) {
|
||||
delta = b[0][axis] - origin[axis];
|
||||
} else if (origin[axis] > b[1][axis]) {
|
||||
delta = origin[axis] - b[1][axis];
|
||||
}
|
||||
distanceSquared += delta * delta;
|
||||
}
|
||||
return distanceSquared < radius * radius;
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(sizeof(idBounds) == 24, "Recovered idBounds layout changed");
|
||||
|
||||
@@ -14,10 +14,10 @@ public:
|
||||
for (int side = 0; side < 2; ++side) {
|
||||
for (int axis = 0; axis < 3; ++axis) {
|
||||
const int integerValue = static_cast<int>(bounds[side][axis]);
|
||||
b[side][axis] = static_cast<std::int16_t>(std::max(
|
||||
static_cast<int>(std::numeric_limits<std::int16_t>::min()),
|
||||
std::min(
|
||||
static_cast<int>(std::numeric_limits<std::int16_t>::max()),
|
||||
b[side][axis] = static_cast<std::int16_t>((std::max)(
|
||||
static_cast<int>((std::numeric_limits<std::int16_t>::min)()),
|
||||
(std::min)(
|
||||
static_cast<int>((std::numeric_limits<std::int16_t>::max)()),
|
||||
integerValue
|
||||
)
|
||||
));
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
#include "commandlink.h"
|
||||
|
||||
namespace {
|
||||
idCommandLink* commandLinks = nullptr;
|
||||
}
|
||||
|
||||
idCommandLink* CommandLinks() {
|
||||
return commandLinks;
|
||||
}
|
||||
|
||||
idCommandLink::idCommandLink(const char* commandName,
|
||||
commandLinkFunction_t function, const char* description,
|
||||
commandLinkCompletion_t argumentCompletion)
|
||||
: next(commandLinks), cmdName_(commandName), function_(function),
|
||||
description_(description), argCompletion_(argumentCompletion) {
|
||||
commandLinks = this;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include "csystems/autocomplete.h"
|
||||
|
||||
using commandLinkFunction_t = void (*)(const idCmdArgs& args);
|
||||
using commandLinkCompletion_t = void (*)(idAutoComplete& completion);
|
||||
|
||||
class idCommandLink {
|
||||
public:
|
||||
idCommandLink(const char* commandName, commandLinkFunction_t function,
|
||||
const char* description,
|
||||
commandLinkCompletion_t argumentCompletion = nullptr);
|
||||
|
||||
idCommandLink* next;
|
||||
const char* cmdName_;
|
||||
commandLinkFunction_t function_;
|
||||
const char* description_;
|
||||
commandLinkCompletion_t argCompletion_;
|
||||
};
|
||||
|
||||
idCommandLink* CommandLinks();
|
||||
|
||||
#if INTPTR_MAX == INT32_MAX
|
||||
static_assert(sizeof(idCommandLink) == 20,
|
||||
"Recovered idCommandLink layout changed");
|
||||
#endif
|
||||
@@ -166,7 +166,9 @@ protected:
|
||||
list = buffer;
|
||||
num = 0;
|
||||
size = capacity;
|
||||
granularity = 0;
|
||||
// Recovered idStaticList constructors consistently materialize a
|
||||
// granularity of one for their embedded fixed-capacity storage.
|
||||
granularity = 1;
|
||||
listStatic = 1;
|
||||
}
|
||||
|
||||
@@ -180,4 +182,3 @@ private:
|
||||
#if defined(_WIN32) && !defined(_WIN64)
|
||||
static_assert(sizeof(idList<int, 5>) == 16, "Recovered idList ABI changed");
|
||||
#endif
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ static_assert(
|
||||
);
|
||||
|
||||
// tungsten.exe.h type 12218. idCmdArgs and idList retain their recovered
|
||||
// Win32 layouts through the active BFG-compatible core container layer.
|
||||
// Win32 layouts through the active recovered core container layer.
|
||||
class idAutoComplete {
|
||||
public:
|
||||
idAutoComplete()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
// tungsten.exe.h type 12913. Unlike Doom 3 BFG's idScreenRect, this idTech 5
|
||||
// tungsten.exe.h type 12913. This recovered idTech 5
|
||||
// build uses four 32-bit coordinates and does not store depth bounds here.
|
||||
class idScreenRect {
|
||||
public:
|
||||
@@ -18,4 +18,3 @@ public:
|
||||
};
|
||||
|
||||
static_assert(sizeof(idScreenRect) == 16, "Recovered idScreenRect layout changed");
|
||||
|
||||
|
||||
@@ -0,0 +1,205 @@
|
||||
#include "idlib/geometry/tracemodel.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <malloc.h>
|
||||
#include <new>
|
||||
|
||||
void* idTraceModel::operator new(const std::size_t size) {
|
||||
void* const memory = _aligned_malloc(size, 128);
|
||||
if (memory == nullptr) {
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
return memory;
|
||||
}
|
||||
|
||||
void idTraceModel::operator delete(void* const memory) {
|
||||
if (memory != nullptr) {
|
||||
_aligned_free(memory);
|
||||
}
|
||||
}
|
||||
|
||||
void idTraceModel::ClearUnused() {
|
||||
std::memset(vertsX + numVerts, 0,
|
||||
sizeof(float) * (32 - numVerts));
|
||||
std::memset(vertsY + numVerts, 0,
|
||||
sizeof(float) * (32 - numVerts));
|
||||
std::memset(vertsZ + numVerts, 0,
|
||||
sizeof(float) * (32 - numVerts));
|
||||
std::memset(edgeNormalX + numEdges, 0,
|
||||
sizeof(float) * (32 - numEdges));
|
||||
std::memset(edgeNormalY + numEdges, 0,
|
||||
sizeof(float) * (32 - numEdges));
|
||||
std::memset(edgeNormalZ + numEdges, 0,
|
||||
sizeof(float) * (32 - numEdges));
|
||||
std::memset(polyPlaneX + numPolys, 0,
|
||||
sizeof(float) * (16 - numPolys));
|
||||
std::memset(polyPlaneY + numPolys, 0,
|
||||
sizeof(float) * (16 - numPolys));
|
||||
std::memset(polyPlaneZ + numPolys, 0,
|
||||
sizeof(float) * (16 - numPolys));
|
||||
std::memset(polyPlaneW + numPolys, 0,
|
||||
sizeof(float) * (16 - numPolys));
|
||||
std::memset(edges + numEdges, 0,
|
||||
sizeof(traceModelEdge_t) * (32 - numEdges));
|
||||
std::memset(numPolyEdges + numPolys, 0,
|
||||
sizeof(unsigned int) * (16 - numPolys));
|
||||
std::memset(polyEdges + numPolys, 0,
|
||||
sizeof(polyEdges[0]) * (16 - numPolys));
|
||||
maxPolyEdges = 0;
|
||||
for (unsigned int polygon = 0; polygon < numPolys; ++polygon) {
|
||||
maxPolyEdges = (std::max)(maxPolyEdges, numPolyEdges[polygon]);
|
||||
const std::uint8_t fill = numPolyEdges[polygon] > 0
|
||||
? polyEdges[polygon][numPolyEdges[polygon] - 1]
|
||||
: 0;
|
||||
for (unsigned int edge = numPolyEdges[polygon]; edge < 16; ++edge) {
|
||||
polyEdges[polygon][edge] = fill;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int idTraceModel::GenerateEdgeNormals() {
|
||||
std::memset(edgeNormalX, 0, sizeof(edgeNormalX));
|
||||
std::memset(edgeNormalY, 0, sizeof(edgeNormalY));
|
||||
std::memset(edgeNormalZ, 0, sizeof(edgeNormalZ));
|
||||
for (unsigned int polygon = 0; polygon < numPolys; ++polygon) {
|
||||
for (unsigned int edge = 0; edge < numPolyEdges[polygon]; ++edge) {
|
||||
const unsigned int edgeIndex = polyEdges[polygon][edge] & 0x7F;
|
||||
if (edgeIndex >= numEdges) {
|
||||
continue;
|
||||
}
|
||||
edgeNormalX[edgeIndex] += polyPlaneX[polygon];
|
||||
edgeNormalY[edgeIndex] += polyPlaneY[polygon];
|
||||
edgeNormalZ[edgeIndex] += polyPlaneZ[polygon];
|
||||
}
|
||||
}
|
||||
for (unsigned int edge = 0; edge < numEdges; ++edge) {
|
||||
idVec3 normal(edgeNormalX[edge], edgeNormalY[edge],
|
||||
edgeNormalZ[edge]);
|
||||
normal.NormalizeFast();
|
||||
edgeNormalX[edge] = normal.x;
|
||||
edgeNormalY[edge] = normal.y;
|
||||
edgeNormalZ[edge] = normal.z;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool idTraceModel::IsClosedSurface() const {
|
||||
unsigned int edgeUseCounts[32]{};
|
||||
for (unsigned int polygon = 0; polygon < numPolys; ++polygon) {
|
||||
for (unsigned int edge = 0; edge < numPolyEdges[polygon]; ++edge) {
|
||||
const unsigned int edgeIndex = polyEdges[polygon][edge] & 0x7Fu;
|
||||
if (edgeIndex < 32) {
|
||||
++edgeUseCounts[edgeIndex];
|
||||
}
|
||||
}
|
||||
}
|
||||
for (unsigned int edge = 0; edge < numEdges; ++edge) {
|
||||
if (edgeUseCounts[edge] != 2) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void idTraceModel::TestConvexity() {
|
||||
isConvex = true;
|
||||
for (unsigned int polygon = 0; polygon < numPolys; ++polygon) {
|
||||
for (unsigned int vertex = 0; vertex < numVerts; ++vertex) {
|
||||
const float distance =
|
||||
vertsX[vertex] * polyPlaneX[polygon]
|
||||
+ vertsY[vertex] * polyPlaneY[polygon]
|
||||
+ vertsZ[vertex] * polyPlaneZ[polygon]
|
||||
+ polyPlaneW[polygon];
|
||||
if (distance > 0.01f) {
|
||||
isConvex = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void idTraceModel::CalculateInsetSphereRadius() {
|
||||
const float lower = (std::min)(std::fabs(bounds[0].x),
|
||||
(std::min)(std::fabs(bounds[0].y), std::fabs(bounds[0].z)));
|
||||
const float upper = (std::min)(std::fabs(bounds[1].x),
|
||||
(std::min)(std::fabs(bounds[1].y), std::fabs(bounds[1].z)));
|
||||
radius = (std::min)(lower, upper);
|
||||
}
|
||||
|
||||
void idTraceModel::InitBox() {
|
||||
type = TRM_BOX;
|
||||
numVerts = 8;
|
||||
numEdges = 12;
|
||||
numPolys = 6;
|
||||
maxPolyEdges = 4;
|
||||
|
||||
const std::uint16_t edgeVertices[12][2] = {
|
||||
{0, 1}, {1, 2}, {2, 3}, {3, 0},
|
||||
{4, 5}, {5, 6}, {6, 7}, {7, 4},
|
||||
{0, 4}, {1, 5}, {2, 6}, {3, 7}};
|
||||
std::memcpy(edges, edgeVertices, sizeof(edgeVertices));
|
||||
|
||||
const std::uint8_t boxPolygonEdges[6][4] = {
|
||||
{0x83, 0x82, 0x81, 0x80},
|
||||
{4, 5, 6, 7},
|
||||
{0, 9, 0x84, 0x88},
|
||||
{1, 10, 0x85, 0x89},
|
||||
{2, 11, 0x86, 0x8A},
|
||||
{3, 8, 0x87, 0x8B}};
|
||||
for (int polygon = 0; polygon < 6; ++polygon) {
|
||||
numPolyEdges[polygon] = 4;
|
||||
std::memcpy(polyEdges[polygon], boxPolygonEdges[polygon], 4);
|
||||
}
|
||||
const idVec3 normals[6] = {
|
||||
idVec3(0.0f, 0.0f, -1.0f), idVec3(0.0f, 0.0f, 1.0f),
|
||||
idVec3(0.0f, -1.0f, 0.0f), idVec3(1.0f, 0.0f, 0.0f),
|
||||
idVec3(0.0f, 1.0f, 0.0f), idVec3(-1.0f, 0.0f, 0.0f)};
|
||||
for (int polygon = 0; polygon < 6; ++polygon) {
|
||||
polyPlaneX[polygon] = normals[polygon].x;
|
||||
polyPlaneY[polygon] = normals[polygon].y;
|
||||
polyPlaneZ[polygon] = normals[polygon].z;
|
||||
polyPlaneW[polygon] = 0.0f;
|
||||
}
|
||||
isConvex = true;
|
||||
GenerateEdgeNormals();
|
||||
ClearUnused();
|
||||
}
|
||||
|
||||
void idTraceModel::SetupBox(const idBounds& boxBounds) {
|
||||
if (type != TRM_BOX) {
|
||||
InitBox();
|
||||
}
|
||||
offset = (boxBounds[0] + boxBounds[1]) * 0.5f;
|
||||
const idVec3 vertices[8] = {
|
||||
idVec3(boxBounds[0].x, boxBounds[0].y, boxBounds[0].z),
|
||||
idVec3(boxBounds[1].x, boxBounds[0].y, boxBounds[0].z),
|
||||
idVec3(boxBounds[1].x, boxBounds[1].y, boxBounds[0].z),
|
||||
idVec3(boxBounds[0].x, boxBounds[1].y, boxBounds[0].z),
|
||||
idVec3(boxBounds[0].x, boxBounds[0].y, boxBounds[1].z),
|
||||
idVec3(boxBounds[1].x, boxBounds[0].y, boxBounds[1].z),
|
||||
idVec3(boxBounds[1].x, boxBounds[1].y, boxBounds[1].z),
|
||||
idVec3(boxBounds[0].x, boxBounds[1].y, boxBounds[1].z)};
|
||||
for (int vertex = 0; vertex < 8; ++vertex) {
|
||||
vertsX[vertex] = vertices[vertex].x;
|
||||
vertsY[vertex] = vertices[vertex].y;
|
||||
vertsZ[vertex] = vertices[vertex].z;
|
||||
}
|
||||
polyPlaneW[0] = boxBounds[0].z;
|
||||
polyPlaneW[1] = -boxBounds[1].z;
|
||||
polyPlaneW[2] = boxBounds[0].y;
|
||||
polyPlaneW[3] = -boxBounds[1].x;
|
||||
polyPlaneW[4] = -boxBounds[1].y;
|
||||
polyPlaneW[5] = boxBounds[0].x;
|
||||
bounds = boxBounds;
|
||||
CalculateInsetSphereRadius();
|
||||
}
|
||||
|
||||
void idTraceModel::SetupBox(const float size) {
|
||||
idBounds boxBounds;
|
||||
const float halfSize = size * 0.5f;
|
||||
boxBounds[0].Set(-halfSize, -halfSize, -halfSize);
|
||||
boxBounds[1].Set(halfSize, halfSize, halfSize);
|
||||
SetupBox(boxBounds);
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
#include "idlib/geometry/winding.h"
|
||||
|
||||
#include "idlib/lib_print.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <malloc.h>
|
||||
|
||||
idWinding::idWinding()
|
||||
: numPoints(0), p(nullptr), allocedSize(0) {
|
||||
}
|
||||
|
||||
idWinding::idWinding(const idWinding& other)
|
||||
: idWinding() {
|
||||
*this = other;
|
||||
}
|
||||
|
||||
idWinding::~idWinding() {
|
||||
_aligned_free(p);
|
||||
p = nullptr;
|
||||
}
|
||||
|
||||
idWinding& idWinding::operator=(const idWinding& other) {
|
||||
if (this != &other && ReAllocate(other.numPoints, false)) {
|
||||
numPoints = other.numPoints;
|
||||
if (numPoints > 0) {
|
||||
std::memcpy(p, other.p,
|
||||
static_cast<std::size_t>(numPoints) * sizeof(idVec5));
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void idWinding::Clear() {
|
||||
numPoints = 0;
|
||||
allocedSize = 0;
|
||||
_aligned_free(p);
|
||||
p = nullptr;
|
||||
}
|
||||
|
||||
bool idWinding::ReAllocate(const int count, const bool keep) {
|
||||
const int newSize = (count + 3) & ~3;
|
||||
idVec5* const replacement = static_cast<idVec5*>(_aligned_malloc(
|
||||
static_cast<std::size_t>(newSize) * sizeof(idVec5), 16));
|
||||
if (replacement == nullptr && newSize > 0) {
|
||||
return false;
|
||||
}
|
||||
if (keep && p != nullptr && numPoints > 0) {
|
||||
std::memcpy(replacement, p,
|
||||
static_cast<std::size_t>((std::min)(numPoints, newSize)) *
|
||||
sizeof(idVec5));
|
||||
}
|
||||
_aligned_free(p);
|
||||
p = replacement;
|
||||
allocedSize = newSize;
|
||||
if (numPoints > allocedSize) {
|
||||
numPoints = allocedSize;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void idWinding::AddPoint(const idVec3& point) {
|
||||
if (numPoints + 1 > allocedSize &&
|
||||
!ReAllocate(numPoints + 1, true)) {
|
||||
return;
|
||||
}
|
||||
p[numPoints++] = idVec5(point.x, point.y, point.z, 0.0f, 0.0f);
|
||||
}
|
||||
|
||||
void idWinding::AddPoint(const idVec5& point) {
|
||||
if (numPoints + 1 > allocedSize &&
|
||||
!ReAllocate(numPoints + 1, true)) {
|
||||
return;
|
||||
}
|
||||
p[numPoints++] = point;
|
||||
}
|
||||
|
||||
idFixedWinding::idFixedWinding() {
|
||||
p = data;
|
||||
numPoints = 0;
|
||||
allocedSize = 64;
|
||||
}
|
||||
|
||||
idFixedWinding::idFixedWinding(const idFixedWinding& other)
|
||||
: idFixedWinding() {
|
||||
*this = other;
|
||||
}
|
||||
|
||||
idFixedWinding& idFixedWinding::operator=(const idFixedWinding& other) {
|
||||
idWinding::operator=(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void idWinding::GetBounds(idBounds& bounds) const {
|
||||
if (numPoints <= 0) {
|
||||
bounds[0].Set(1.0e30f, 1.0e30f, 1.0e30f);
|
||||
bounds[1].Set(-1.0e30f, -1.0e30f, -1.0e30f);
|
||||
return;
|
||||
}
|
||||
bounds[0].Set(p[0].x, p[0].y, p[0].z);
|
||||
bounds[1] = bounds[0];
|
||||
for (int point = 1; point < numPoints; ++point) {
|
||||
bounds[0].x = (std::min)(bounds[0].x, p[point].x);
|
||||
bounds[0].y = (std::min)(bounds[0].y, p[point].y);
|
||||
bounds[0].z = (std::min)(bounds[0].z, p[point].z);
|
||||
bounds[1].x = (std::max)(bounds[1].x, p[point].x);
|
||||
bounds[1].y = (std::max)(bounds[1].y, p[point].y);
|
||||
bounds[1].z = (std::max)(bounds[1].z, p[point].z);
|
||||
}
|
||||
}
|
||||
|
||||
bool idWinding::IsHuge(const float radius) const {
|
||||
for (int point = 0; point < numPoints; ++point) {
|
||||
for (int axis = 0; axis < 3; ++axis) {
|
||||
if (p[point][axis] <= -radius || p[point][axis] >= radius) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
idFixedWinding::~idFixedWinding() {
|
||||
p = nullptr;
|
||||
}
|
||||
|
||||
void idFixedWinding::Clear() {
|
||||
numPoints = 0;
|
||||
p = data;
|
||||
allocedSize = 64;
|
||||
}
|
||||
|
||||
bool idFixedWinding::ReAllocate(const int count, const bool) {
|
||||
if (count <= 64) {
|
||||
return true;
|
||||
}
|
||||
idLibPrint::Warning(
|
||||
"idFixedWinding -> MAX_POINTS_ON_WINDING overflowed");
|
||||
return false;
|
||||
}
|
||||
|
||||
int idFixedWinding::SplitInPlace(const idPlane& plane,
|
||||
const float epsilon, idFixedWinding* const back) {
|
||||
if (back == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
float distances[65]{};
|
||||
int sides[65]{};
|
||||
int counts[3]{};
|
||||
for (int index = 0; index < numPoints; ++index) {
|
||||
distances[index] = plane.a * p[index].x + plane.b * p[index].y +
|
||||
plane.c * p[index].z + plane.d;
|
||||
if (distances[index] > epsilon) {
|
||||
sides[index] = 0;
|
||||
} else if (distances[index] < -epsilon) {
|
||||
sides[index] = 1;
|
||||
} else {
|
||||
sides[index] = 2;
|
||||
}
|
||||
++counts[sides[index]];
|
||||
}
|
||||
if (counts[1] == 0) {
|
||||
return counts[0] != 0 ? 0 : 2;
|
||||
}
|
||||
if (counts[0] == 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
distances[numPoints] = distances[0];
|
||||
sides[numPoints] = sides[0];
|
||||
idFixedWinding front;
|
||||
back->Clear();
|
||||
for (int index = 0; index < numPoints; ++index) {
|
||||
const idVec5& current = p[index];
|
||||
if (sides[index] == 2) {
|
||||
front.AddPoint(current);
|
||||
back->AddPoint(current);
|
||||
} else if (sides[index] == 0) {
|
||||
front.AddPoint(current);
|
||||
} else {
|
||||
back->AddPoint(current);
|
||||
}
|
||||
|
||||
if (sides[index + 1] == 2 ||
|
||||
sides[index + 1] == sides[index]) {
|
||||
continue;
|
||||
}
|
||||
const idVec5& next = p[(index + 1) % numPoints];
|
||||
const float fraction = distances[index] /
|
||||
(distances[index] - distances[index + 1]);
|
||||
idVec5 split;
|
||||
for (int component = 0; component < 5; ++component) {
|
||||
split[component] = current[component] +
|
||||
fraction * (next[component] - current[component]);
|
||||
}
|
||||
if (plane.a == 1.0f) split.x = -plane.d;
|
||||
else if (plane.a == -1.0f) split.x = plane.d;
|
||||
if (plane.b == 1.0f) split.y = -plane.d;
|
||||
else if (plane.b == -1.0f) split.y = plane.d;
|
||||
if (plane.c == 1.0f) split.z = -plane.d;
|
||||
else if (plane.c == -1.0f) split.z = plane.d;
|
||||
front.AddPoint(split);
|
||||
back->AddPoint(split);
|
||||
}
|
||||
Clear();
|
||||
for (int index = 0; index < front.numPoints; ++index) {
|
||||
AddPoint(front[index]);
|
||||
}
|
||||
return 3;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user