Initial gamelib integration AAS2 with some animation and physics and another integration pass on idLib.

This commit is contained in:
Justin Marshall
2026-08-09 03:52:45 -07:00
parent cdf7d52214
commit 6d92a71d19
111 changed files with 15204 additions and 2275 deletions
+212
View File
@@ -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}
)
+1 -1
View File
@@ -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),
+2 -1
View File
@@ -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;
+67
View File
@@ -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());
}
}
+50 -146
View File
@@ -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
+147
View File
@@ -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;
}
}
+38 -342
View File
@@ -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
+314
View File
@@ -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;
}
+263 -93
View File
@@ -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
+152
View File
@@ -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);
}
}
+307 -130
View File
@@ -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
+345
View File
@@ -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;
}
+221
View File
@@ -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);
}
+580
View File
@@ -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, &center)) {
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);
}
+157
View File
@@ -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 {
}
+108 -34
View File
@@ -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);
}
+57 -31
View File
@@ -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;
}
+95 -60
View File
@@ -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();
}
}
+45 -195
View File
@@ -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");
+216
View File
@@ -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
View File
@@ -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
+268
View File
@@ -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);
}
+97 -30
View File
@@ -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
+54 -24
View File
@@ -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);
}
+43 -30
View File
@@ -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
+752
View File
@@ -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() {
}
+306 -160
View File
@@ -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
+279
View File
@@ -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);
}
+137 -68
View File
@@ -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
+106
View File
@@ -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);
}
+174 -205
View File
@@ -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;
}
+95 -23
View File
@@ -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
+311
View File
@@ -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();
}
}
+94 -32
View File
@@ -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");
+58 -23
View File
@@ -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
+70
View File
@@ -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
+119
View File
@@ -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");
+4 -4
View File
@@ -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
)
));
+17
View File
@@ -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;
}
+26
View File
@@ -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
+3 -2
View File
@@ -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
+1 -1
View File
@@ -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 -2
View File
@@ -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");
+205
View File
@@ -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);
}
+212
View File
@@ -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