#include "cm/collisionmodelmanager.h" #include "cm/collisiongrid.h" #include "cm/collisionmodel.h" #include "cm/collisionmodelbuilder.h" #include "cm/jobs/polygonmodel/polygonmodel.h" #include "cm/jobs/polygonmodel/polygonmodeldata.h" #include "idlib/sys/sys_alloc.h" #include "idlib/filesystem/file.h" #include "idlib/filesystem/filesystem.h" #include "idlib/geometry/tracemodel.h" #include "idlib/lib_print.h" #include "idlib/text/cmdargs.h" #include "framework/resourcelist.h" #include #include #include #include #include idCollisionModelManager collisionModelManager; namespace { idCollisionQueryJobManager cdQuery; idTraceWork* baseTraceWork = nullptr; void EnsureQueryManager() { if (cdQuery.queryData == nullptr) { cdQuery.Init(); } if (baseTraceWork == nullptr) { baseTraceWork = idPolygonModelCollisionDetection::AllocTraceWork(); } } } // namespace idCollisionGridState::idCollisionGridState() : material(nullptr), numActive(0), firstActive(-1), active(TAG_BITARRAY) { } void idCollisionGridState::Create(const int num, const idMaterial* const material_) { active.Alloc(static_cast(num)); for (int index = 0; index < num; ++index) { active.Set(static_cast(index)); } numActive = num; material = material_; firstActive = num > 0 ? 0 : -1; } void idCollisionGridState::Activate(const unsigned int id) { if (active.Get(id)) { return; } if (firstActive < 0 || static_cast(id) < firstActive) { firstActive = static_cast(id); } active.Set(id); ++numActive; } int idCollisionGridState::NextActive(const int id) const { for (unsigned int candidate = static_cast(id + 1); candidate < active.Num(); ++candidate) { if (active.Get(candidate)) { return static_cast(candidate); } } return -1; } void idCollisionGridState::Inactivate(const unsigned int id) { if (!active.Get(id)) { return; } active.Clear(id); --numActive; if (numActive == 0) { firstActive = -1; } else if (firstActive == static_cast(id)) { firstActive = NextActive(static_cast(id)); } } int idCollisionGridState::FirstActive() const { return numActive > 0 ? firstActive : -1; } idCollisionModel* idCollisionModelManager::ModelFromTrm( const char* const modelName, const idTraceModel& traceModel, const idMaterial* const material) { idCollisionModelLocal* const model = new idCollisionModelLocal(); if (model == nullptr) { return nullptr; } if (!idCollisionModelBuilder::BuildForTrm( model, modelName, traceModel, material)) { delete model; return nullptr; } return model; } idCollisionModel* idCollisionModelManager::BuildModel( const char* const modelName, const idStaticModel* const staticModel, const int* const subModelIndices, const int numSubModelIndices) { if (staticModel == nullptr) { return nullptr; } idCollisionModelLocal* const model = new idCollisionModelLocal(); if (model == nullptr || !idCollisionModelBuilder::BuildForStaticModel( model, staticModel, subModelIndices, numSubModelIndices, modelName)) { delete model; return nullptr; } idCollisionModelLocal::resourceList.Add(model); return model; } void idCollisionModelManager::BuildModelsForMapFile( const idMapFile* const mapFile, const bool mapModelOnly) { idCollisionModelBuilder::BuildForMapFile(mapFile, true, mapModelOnly); } idCollisionModel* idCollisionModelManager::ModelFromRender( const char*, const idRenderModel* const renderModel) { if (renderModel == nullptr) { return nullptr; } idCollisionModelLocal* const model = new idCollisionModelLocal(); if (model == nullptr || !idCollisionModelBuilder::BuildForRenderModel(model, renderModel)) { delete model; return nullptr; } return model; } idCollisionModel* idCollisionModelManager::ModelFromGrid( const char* const modelName, const idCollisionGrid* const grid, const idCollisionGridState& state, const idMaterial* const material) { const idCollisionGridLocal* const localGrid = dynamic_cast(grid); if (localGrid == nullptr) { return nullptr; } idCollisionModelLocal* const model = new idCollisionModelLocal(); if (!idCollisionModelBuilder::BuildForGrid(model, modelName, localGrid->grid, state, material)) { delete model; return nullptr; } return model; } void idCollisionModelManager::Init() { EnsureQueryManager(); } void idCollisionModelManager::Shutdown() { if (baseTraceWork != nullptr) { baseTraceWork->~idTraceWork(); mem.Free(baseTraceWork, ALIGN_16); baseTraceWork = nullptr; } cdQuery.Shutdown(); } void idCollisionModelManager::StartQueryFrame() { EnsureQueryManager(); cdQuery.StartFrame(); } void idCollisionModelManager::EndQueryFrame() { EnsureQueryManager(); cdQuery.EndFrame(); } void idCollisionModelManager::SubmitQueries() { EnsureQueryManager(); cdQuery.SubmitQueries(); } void idCollisionModelManager::WaitForAllQueries() { EnsureQueryManager(); cdQuery.WaitForAllQueries(); } idCollisionModel* idCollisionModelManager::LoadModel( const char* const modelName) { if (modelName == nullptr || *modelName == '\0') { return nullptr; } idCollisionModelLocal* const model = new idCollisionModelLocal(); model->SetName(modelName); model->LoadResource(); return model; } idCollisionGrid* idCollisionModelManager::LoadGrid( const char* const modelName) { if (modelName == nullptr || *modelName == '\0') { return nullptr; } idCollisionGridLocal* const grid = new idCollisionGridLocal(); grid->SetName(modelName); grid->LoadResource(); return grid; } int idCollisionModelManager::FindStreamArea(idCollisionModel* const model, const char* const areaName) { idCollisionModelLocal* const local = dynamic_cast(model); if (local == nullptr || local->streamAreas == nullptr || areaName == nullptr) { return -1; } streamAreasPtrs_t pointers{}; SetupStreamAreaPtrs(local->streamAreas, pointers); for (int index = 0; index < local->streamAreas->numStreamAreas; ++index) { const char* const name = pointers.streamAreaNameBytes + pointers.streamAreas[index].volumeNameOffset; if (_stricmp(name, areaName) == 0) { return index; } } return -1; } void idCollisionModelManager::StreamModel(idCollisionModel* const model, const int* const areaIndices, const int numAreaIndices, const bool wait) { idCollisionModelLocal* const local = dynamic_cast(model); if (local == nullptr || !local->isStreamed || local->streamAreas == nullptr || (numAreaIndices != -1 && (numAreaIndices < 0 || (numAreaIndices > 0 && areaIndices == nullptr)))) { return; } // The recovered interface describes the complete desired-area set. The // final flag only controls whether the Xenon implementation waits for its // asynchronous I/O passes; the PC port performs those reads synchronously. (void)wait; std::vector desired( static_cast(local->polygonModel.numSubModels), false); streamAreasPtrs_t areas{}; SetupStreamAreaPtrs(local->streamAreas, areas); if (numAreaIndices == -1) { std::fill(desired.begin(), desired.end(), true); } else { for (int areaListIndex = 0; areaListIndex < numAreaIndices; ++areaListIndex) { const int areaIndex = areaIndices[areaListIndex]; if (areaIndex < 0 || areaIndex >= local->streamAreas->numStreamAreas) { continue; } const streamArea_t& area = areas.streamAreas[areaIndex]; for (int index = 0; index < area.numSubModels; ++index) { const int subModelIndex = areas.streamAreaSubModels[ area.subModelsOffset + index]; if (subModelIndex >= 0 && subModelIndex < local->polygonModel.numSubModels) { desired[static_cast(subModelIndex)] = true; } } } } for (int subModelIndex = 0; subModelIndex < local->polygonModel.numSubModels; ++subModelIndex) { cm_subModel_t& subModel = local->polygonModel.subModels[subModelIndex]; if (!desired[static_cast(subModelIndex)]) { if (*subModel.state == SUBMODEL_STATE_LOADED && subModel.numUsers == 0 && local->memoryMappedFile == nullptr) { *subModel.state = SUBMODEL_STATE_UNLOADED; _aligned_free(subModel.data); subModel.data = nullptr; } continue; } if (*subModel.state == SUBMODEL_STATE_LOADED || local->streamFilePtr == nullptr || subModel.fileOffset < 0) { continue; } cm_subModelData_t* const data = static_cast(_aligned_malloc( subModel.header.totalSize, 16)); if (data != nullptr && local->streamFilePtr->ReadOfs( subModel.fileOffset, data, subModel.header.totalSize) == static_cast(subModel.header.totalSize)) { subModel.data = data; *subModel.state = SUBMODEL_STATE_LOADED; } else { _aligned_free(data); } } } bool idCollisionModelManager::IsResident(const idBounds&, const idPositionedCollisionModel* const models, const int numModels) { if (models == nullptr || numModels <= 0) { return true; } for (int modelIndex = 0; modelIndex < numModels; ++modelIndex) { const idCollisionModelLocal* const model = dynamic_cast( models[modelIndex].model); if (model == nullptr || model->modelType != CM_POLYGONMODEL) { continue; } for (int subModelIndex = 0; subModelIndex < model->polygonModel.numSubModels; ++subModelIndex) { if (*model->polygonModel.subModels[subModelIndex].state != SUBMODEL_STATE_LOADED) { return false; } } } return true; } void PrintSubModelInfo(const cm_subModelData_t* const data) { if (data == nullptr) { return; } idLibPrint::Printf(" %6i nodes (%i kB)\n", data->numNodes, data->numNodes * 16 / 1024); idLibPrint::Printf(" %6i primitive indices (%i kB)\n", data->numPrimitiveIndices, data->numPrimitiveIndices * 2 / 1024); idLibPrint::Printf(" %6i materials (%i kB)\n", data->numMaterials, data->numMaterials * 16 / 1024); idLibPrint::Printf(" %6i polygons (%i kB)\n", data->numPolygons, data->numPolygons * 16 / 1024); idLibPrint::Printf(" %6i polygon edges (%i kB)\n", data->numPolygonEdges, data->numPolygonEdges * 2 / 1024); idLibPrint::Printf(" %6i edges (%i kB)\n", data->numEdges, data->numEdges * 4 / 1024); idLibPrint::Printf(" %6i vertices (%i kB)\n", data->numVertices, data->numVertices * 16 / 1024); idLibPrint::Printf(" %6i polytopes (%i kB)\n", data->numPolytopes, data->numPolytopes * 16 / 1024); idLibPrint::Printf(" %6i polytope planes (%i kB)\n", data->numPolytopePlanes, data->numPolytopePlanes * 16 / 1024); } void AddCollisionModelInfo(const idCollisionModelLocal* const model, cm_subModelData_t* const stats) { if (model == nullptr || stats == nullptr || model->modelType != CM_POLYGONMODEL) { return; } for (int index = 0; index < model->polygonModel.numSubModels; ++index) { const cm_subModel_t& subModel = model->polygonModel.subModels[index]; const cm_subModelData_t* const data = AcquireSubModelData(subModel); if (data != nullptr && data->header.loadedSize != 32) { stats->numNodes += data->numNodes; stats->numPrimitiveIndices += data->numPrimitiveIndices; stats->numMaterials += data->numMaterials; stats->numPolygons += data->numPolygons; stats->numPolygonEdges += data->numPolygonEdges; stats->numEdges += data->numEdges; stats->numVertices += data->numVertices; stats->numPolytopes += data->numPolytopes; stats->numPolytopePlanes += data->numPolytopePlanes; } ReleaseSubModelData(subModel, data); } } void PrintCollisionModelInfo(const idCollisionModelLocal* const model) { if (model == nullptr) { return; } idLibPrint::Printf("collision model %s: %i total / %i loaded bytes\n", model->GetName(), model->GetTotalMemory(), model->GetLoadedMemory()); if (model->modelType != CM_POLYGONMODEL) { return; } for (int index = 0; index < model->polygonModel.numSubModels; ++index) { const cm_subModel_t& subModel = model->polygonModel.subModels[index]; const cm_subModelData_t* const data = AcquireSubModelData(subModel); if (data != nullptr && data->header.loadedSize != 32) { PrintSubModelInfo(data); } ReleaseSubModelData(subModel, data); } } bool idTrmFromSubModel::TrmFromSubModel( const idCollisionModelLocal* const model, const cm_subModelData_t* const data, idTraceModel& trm) { if (model == nullptr || data == nullptr || data->header.loadedSize == 32 || data->numVertices > 32 || data->numEdges > 32 || data->numPolygons > 16 || data->numVertices <= 3) { return false; } std::memset(&trm, 0, sizeof(trm)); trm.type = TRM_CUSTOM; trm.bounds[0].Set(FLT_MAX, FLT_MAX, FLT_MAX); trm.bounds[1].Set(-FLT_MAX, -FLT_MAX, -FLT_MAX); cm_subModelPtrs_t pointers{}; idPolygonModelCollisionDetection::SetupSubModelPtrsFromData( pointers, data); trm.numPolys = data->numPolygons; trm.maxPolyEdges = 0; for (int polygonIndex = 0; polygonIndex < data->numPolygons; ++polygonIndex) { const cm_polygon_t& polygon = pointers.polygons[polygonIndex]; idPlane plane; CM_GetPolygonPlane(pointers, polygon, plane); trm.polyPlaneX[polygonIndex] = plane.a; trm.polyPlaneY[polygonIndex] = plane.b; trm.polyPlaneZ[polygonIndex] = plane.c; trm.polyPlaneW[polygonIndex] = plane.d; trm.numPolyEdges[polygonIndex] = polygon.numEdges; trm.maxPolyEdges = (std::max)(trm.maxPolyEdges, static_cast(polygon.numEdges)); for (int edge = 0; edge < polygon.numEdges; ++edge) { const std::uint16_t reference = pointers.polygonEdges[ polygon.firstEdge + edge]; trm.polyEdges[polygonIndex][edge] = static_cast( CM_EdgeIndex(reference) | ((reference & 0x8000) != 0 ? 0x80 : 0)); } } trm.numVerts = data->numVertices; for (int index = 0; index < data->numVertices; ++index) { const idVec3& vertex = pointers.vertices[index].p; trm.vertsX[index] = vertex.x; trm.vertsY[index] = vertex.y; trm.vertsZ[index] = vertex.z; for (int axis = 0; axis < 3; ++axis) { trm.bounds[0][axis] = (std::min)(trm.bounds[0][axis], vertex[axis]); trm.bounds[1][axis] = (std::max)(trm.bounds[1][axis], vertex[axis]); } } trm.numEdges = data->numEdges; for (int index = 0; index < data->numEdges; ++index) { trm.edges[index].v[0] = pointers.edges[index].vertexNum[0]; trm.edges[index].v[1] = pointers.edges[index].vertexNum[1]; } if (!trm.IsClosedSurface()) { return false; } trm.CalculateInsetSphereRadius(); trm.offset = (trm.bounds[0] + trm.bounds[1]) * 0.5f; trm.GenerateEdgeNormals(); trm.TestConvexity(); trm.ClearUnused(); return true; } bool TrmFromModel(idCollisionModelLocal* const model, idTraceModel* const trm) { if (model == nullptr || trm == nullptr || model->modelType != CM_POLYGONMODEL || model->polygonModel.numSubModels != 1) { return false; } const cm_subModel_t& subModel = model->polygonModel.subModels[0]; const cm_subModelData_t* const data = AcquireSubModelData(subModel); const bool result = idTrmFromSubModel::TrmFromSubModel( model, data, *trm); ReleaseSubModelData(subModel, data); return result; } bool idCollisionModelManager::TrmFromModel(const char* const modelName, idTraceModel& trm) { idCollisionModelLocal* const model = dynamic_cast( LoadModel(modelName)); if (model == nullptr) { return false; } const bool result = ::TrmFromModel(model, &trm); delete model; return result; } int idCollisionModelManager::CompoundTrmFromModel( const char* const modelName, idTraceModel* const trms, const int maxTrms, int* const invalidSubmodelIndices, int* const numInvalidIndices) { idCollisionModelLocal* const model = dynamic_cast( LoadModel(modelName)); if (model == nullptr || trms == nullptr || maxTrms <= 0 || model->modelType != CM_POLYGONMODEL || model->polygonModel.numSubModels > maxTrms) { delete model; return 0; } const int invalidCapacity = numInvalidIndices != nullptr ? *numInvalidIndices : 0; if (numInvalidIndices != nullptr) { *numInvalidIndices = 0; } int count = 0; for (int index = 0; index < model->polygonModel.numSubModels; ++index) { const cm_subModel_t& subModel = model->polygonModel.subModels[index]; const cm_subModelData_t* const data = AcquireSubModelData(subModel); const bool valid = idTrmFromSubModel::TrmFromSubModel( model, data, trms[count]); ReleaseSubModelData(subModel, data); if (valid) { ++count; } else if (invalidSubmodelIndices != nullptr && numInvalidIndices != nullptr && *numInvalidIndices < invalidCapacity) { invalidSubmodelIndices[(*numInvalidIndices)++] = index + 1; } else { delete model; return 0; } } delete model; return count; } idCollisionQuery idCollisionModelManager::Translation(trace_t* const result, const idVec3& start, const idVec3& end, const idBounds& globalBounds, const idTraceModel** const trms, const int numTrms, const idMat3& trmAxis, const int contentMask, const idPositionedCollisionModel* const models, const int numModels, const char* const userName) { EnsureQueryManager(); idCollisionQuery query = cdQuery.SubmitTranslationQuery(start, end, globalBounds, trms, numTrms, trmAxis, contentMask, models, numModels, userName); if (result != nullptr) { cdQuery.GetTraceResult(result, query, false); } return query; } idCollisionQuery idCollisionModelManager::LocalTranslation( const idCollisionQuery localSpace, const idVec3& start, const idVec3& end, const idBounds& globalBounds, const idTraceModel** const trms, const int numTrms, const idMat3& trmAxis, const int contentMask, const idPositionedCollisionModel* const models, const int numModels, const char* const userName) { EnsureQueryManager(); return cdQuery.SubmitLocalTranslationQuery(localSpace, start, end, globalBounds, trms, numTrms, trmAxis, contentMask, models, numModels, userName); } idCollisionQuery idCollisionModelManager::Rotation(trace_t* const result, const idVec3& start, const idRotation& rotation, const idBounds& globalBounds, const idTraceModel** const trms, const int numTrms, const idMat3& trmAxis, const int contentMask, const idPositionedCollisionModel* const models, const int numModels, const char* const userName) { EnsureQueryManager(); idCollisionQuery query = cdQuery.SubmitRotationQuery(start, rotation, globalBounds, trms, numTrms, trmAxis, contentMask, models, numModels, userName); if (result != nullptr) { cdQuery.GetRotationResult(baseTraceWork, result, query, false); } return query; } idCollisionQuery idCollisionModelManager::Motion(trace_t* const result, const idVec3& start, const idVec3& end, const idRotation& rotation, const idBounds& globalBounds, const idTraceModel** const trms, const int numTrms, const idMat3& trmAxis, const int contentMask, const idPositionedCollisionModel* const models, const int numModels, const char* const userName) { EnsureQueryManager(); idCollisionQuery query = cdQuery.SubmitMotionQuery(start, end, rotation, globalBounds, trms, numTrms, trmAxis, contentMask, models, numModels, userName); if (result != nullptr) { cdQuery.GetTraceResult(result, query, false); } return query; } idCollisionQuery idCollisionModelManager::MotionContacts( trace_t* const result, contactsResult_t* const contacts, const idVec3& start, const idVec3& end, const idRotation& rotation, const float depth, const idBounds& globalBounds, const idTraceModel** const trms, const int numTrms, const idMat3& trmAxis, const int contentMask, const idPositionedCollisionModel* const models, const int numModels, const char* const userName) { EnsureQueryManager(); idCollisionQuery query = cdQuery.SubmitMotionContactsQuery(start, end, rotation, depth, globalBounds, trms, numTrms, trmAxis, contentMask, models, numModels, userName); if (result != nullptr || contacts != nullptr) { cdQuery.GetMotionContactsResult(baseTraceWork, result, contacts, query, false); } return query; } idCollisionQuery idCollisionModelManager::Contents(trace_t* const result, const idVec3& start, const idBounds& globalBounds, const idTraceModel** const trms, const int numTrms, const idMat3& trmAxis, const int contentMask, const idPositionedCollisionModel* const models, const int numModels, const char* const userName) { EnsureQueryManager(); idCollisionQuery query = cdQuery.SubmitContentsQuery(start, globalBounds, trms, numTrms, trmAxis, contentMask, models, numModels, userName); if (result != nullptr) { cdQuery.GetTraceResult(result, query, false); } return query; } idCollisionQuery idCollisionModelManager::LocalContents( const idCollisionQuery localSpace, const idVec3& start, const idBounds& globalBounds, const idTraceModel** const trms, const int numTrms, const idMat3& trmAxis, const int contentMask, const idPositionedCollisionModel* const models, const int numModels, const char* const userName) { EnsureQueryManager(); return cdQuery.SubmitLocalContentsQuery(localSpace, start, globalBounds, trms, numTrms, trmAxis, contentMask, models, numModels, userName); } idCollisionQuery idCollisionModelManager::Contacts( contactsResult_t* const result, const idVec3& start, const idVec3& direction, const float depth, const idBounds& globalBounds, const idTraceModel** const trms, const int numTrms, const idMat3& trmAxis, const int contentMask, const idPositionedCollisionModel* const models, const int numModels, const char* const userName) { EnsureQueryManager(); idCollisionQuery query = cdQuery.SubmitContactsQuery(start, direction, depth, globalBounds, trms, numTrms, trmAxis, contentMask, models, numModels, userName); if (result != nullptr) { cdQuery.GetContactsResult(result, query, false); } return query; } idCollisionQuery idCollisionModelManager::Clip(clipResult_t* const result, const idVec3& start, const idBounds& globalBounds, const idTraceModel** const trms, const int numTrms, const idMat3& trmAxis, const int contentMask, const idPositionedCollisionModel* const models, const int numModels, const char* const userName) { EnsureQueryManager(); idCollisionQuery query = cdQuery.SubmitClipQuery(start, globalBounds, trms, numTrms, trmAxis, contentMask, models, numModels, userName); if (result != nullptr) { cdQuery.GetClipResult(baseTraceWork, result, query, false); } return query; } idCollisionQuery idCollisionModelManager::StepMove(trace_t* const result, const idVec3& start, const idVec3& end, const idVec3& downNormal, const float stepUp, const float stepDown, const idBounds& globalBounds, const idTraceModel** const trms, const int numTrms, const idMat3& trmAxis, const int contentMask, const idPositionedCollisionModel* const models, const int numModels, const char* const userName) { EnsureQueryManager(); idCollisionQuery query = cdQuery.SubmitStepMoveQuery(start, end, downNormal, stepUp, stepDown, globalBounds, trms, numTrms, trmAxis, contentMask, models, numModels, userName); if (result != nullptr) { cdQuery.GetTraceResult(result, query, false); } return query; } idCollisionQuery idCollisionModelManager::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 idBounds& globalBounds, const idTraceModel** const trms, const int numTrms, const idMat3& trmAxis, const int contentMask, const idPositionedCollisionModel* const models, const int numModels, const char* const userName) { EnsureQueryManager(); idCollisionQuery query = cdQuery.SubmitStepMoveContactsQuery(start, end, downNormal, stepUp, stepDown, globalBounds, trms, numTrms, trmAxis, contentMask, models, numModels, userName); if (result != nullptr || contacts != nullptr) { cdQuery.GetSlideMoveContactsResult(baseTraceWork, result, contacts, query, false); } return query; } idCollisionQuery idCollisionModelManager::SlideMove(trace_t* const result, const idVec3& start, const idVec3& velocity, const idVec3& gravityVector, const float stepUp, const float stepDown, const idBounds& globalBounds, const idTraceModel** const trms, const int numTrms, const idMat3& trmAxis, const int contentMask, const idPositionedCollisionModel* const models, const int numModels, const char* const userName) { EnsureQueryManager(); idCollisionQuery query = cdQuery.SubmitSlideMoveQuery(start, velocity, gravityVector, stepUp, stepDown, globalBounds, trms, numTrms, trmAxis, contentMask, models, numModels, userName); if (result != nullptr) { cdQuery.GetTraceResult(result, query, false); } return query; } idCollisionQuery idCollisionModelManager::SlideMoveContacts( trace_t* const result, contactsResult_t* const contacts, const idVec3& start, const idVec3& velocity, const idVec3& gravityVector, const float stepUp, const float stepDown, const idBounds& globalBounds, const idTraceModel** const trms, const int numTrms, const idMat3& trmAxis, const int contentMask, const idPositionedCollisionModel* const models, const int numModels, const char* const userName) { EnsureQueryManager(); idCollisionQuery query = cdQuery.SubmitSlideMoveContactsQuery(start, velocity, gravityVector, stepUp, stepDown, globalBounds, trms, numTrms, trmAxis, contentMask, models, numModels, userName); if (result != nullptr || contacts != nullptr) { cdQuery.GetSlideMoveContactsResult(baseTraceWork, result, contacts, query, false); } return query; } bool idCollisionModelManager::GetContentsResult(trace_t* const result, idCollisionQuery& query, const bool peek) { EnsureQueryManager(); return cdQuery.GetTraceResult(result, query, peek); } bool idCollisionModelManager::GetMotionContactsResult(trace_t* const result, contactsResult_t* const contacts, idCollisionQuery& query, const bool peek) { EnsureQueryManager(); return cdQuery.GetMotionContactsResult(baseTraceWork, result, contacts, query, peek); } bool idCollisionModelManager::GetClipResult(clipResult_t* const result, idCollisionQuery& query, const bool peek) { EnsureQueryManager(); return cdQuery.GetClipResult(baseTraceWork, result, query, peek); } bool idCollisionModelManager::GetStepMoveContactsResult( trace_t* const result, contactsResult_t* const contacts, idCollisionQuery& query, const bool peek) { EnsureQueryManager(); return cdQuery.GetSlideMoveContactsResult(baseTraceWork, result, contacts, query, peek); } void StepMoveInternal(trace_t* const result, const idVec3& start, const idVec3& end, const idVec3& downNormal, const float stepUp, const float stepDown, const idBounds& globalBounds, const idTraceModel** const traceModels, const int numTraceModels, const idMat3& traceModelAxis, const int contentMask, const idPositionedCollisionModel* const models, const int numModels, const char* const userName) { collisionModelManager.StepMove(result, start, end, downNormal, stepUp, stepDown, globalBounds, traceModels, numTraceModels, traceModelAxis, contentMask, models, numModels, userName); } void ListCollisionModels() { int totalMemory = 0; int loadedMemory = 0; int maxResidentMemory = 0; for (int index = 0; index < idCollisionModelLocal::resourceList.num; ++index) { const idCollisionModelLocal* const model = static_cast( idCollisionModelLocal::resourceList.Index(index)); if (model != nullptr) { const int modelTotal = model->GetTotalMemory(); const int modelLoaded = model->GetLoadedMemory(); const int modelResident = model->GetMaxResidentMemory(nullptr); idLibPrint::Printf( "%4d: %6d kB / %6d kB / %6d kB / %4d / %s\n", index, modelTotal >> 10, modelLoaded >> 10, modelResident >> 10, model->modelType == CM_POLYGONMODEL ? model->polygonModel.numSubModels : 0, model->GetName()); totalMemory += modelTotal; loadedMemory += modelLoaded; maxResidentMemory += modelResident; } } idLibPrint::Printf( "%d kB in %d models (%d kB loaded, %d kB max resident)\n", totalMemory >> 10, idCollisionModelLocal::resourceList.num, loadedMemory >> 10, maxResidentMemory >> 10); } void ListCollisionModels_f(const idCmdArgs*) { ListCollisionModels(); } void CollisionModelInfo_f(const idCmdArgs* const args) { if (args == nullptr || args->Argc() < 2) { idLibPrint::Printf( "usage: collisionModelInfo ; use -1 for totals\n"); return; } const int index = std::atoi(args->Argv(1)); if (index >= 0) { PrintCollisionModelInfo(static_cast( idCollisionModelLocal::resourceList.Index(index))); return; } cm_subModelData_t totals{}; for (int modelIndex = 0; modelIndex < idCollisionModelLocal::resourceList.num; ++modelIndex) { AddCollisionModelInfo(static_cast( idCollisionModelLocal::resourceList.Index(modelIndex)), &totals); } PrintSubModelInfo(&totals); } void ExportCollisionModel_f(const idCmdArgs* const args) { if (args == nullptr || args->Argc() < 2) { idLibPrint::Printf( "usage: exportCollisionModel [output.obj]\n"); return; } idCollisionModelLocal* const model = dynamic_cast( collisionModelManager.LoadModel(args->Argv(1))); if (model == nullptr || model->modelType != CM_POLYGONMODEL) { delete model; idLibPrint::Warning("could not load collision model %s", args->Argv(1)); return; } idStr outputName(args->Argc() > 2 ? args->Argv(2) : args->Argv(1)); if (args->Argc() <= 2) { outputName.SetFileExtension("obj"); } else if (std::strrchr(outputName.c_str(), '.') == nullptr) { outputName.Append(".obj"); } idFileLocal output(fileSystem->OpenFileWrite(outputName.c_str(), FSPATH_BASE)); if (output.file == nullptr) { delete model; idLibPrint::Warning("could not create %s", outputName.c_str()); return; } output->Printf("# recovered idTech 5 collision model %s\n", model->GetName()); int vertexBase = 1; for (int subModelIndex = 0; subModelIndex < model->polygonModel.numSubModels; ++subModelIndex) { const cm_subModel_t& subModel = model->polygonModel.subModels[subModelIndex]; const cm_subModelData_t* const data = AcquireSubModelData(subModel); if (data == nullptr || data->header.loadedSize == 32) { ReleaseSubModelData(subModel, data); continue; } cm_subModelPtrs_t pointers{}; idPolygonModelCollisionDetection::SetupSubModelPtrsFromData( pointers, data); for (int vertex = 0; vertex < data->numVertices; ++vertex) { const idVec3& point = pointers.vertices[vertex].p; output->Printf("v %.9g %.9g %.9g\n", point.x, point.y, point.z); } output->Printf("g submodel_%d\n", subModelIndex); for (int polygonIndex = 0; polygonIndex < data->numPolygons; ++polygonIndex) { const cm_polygon_t& polygon = pointers.polygons[polygonIndex]; if (polygon.numEdges < 3) { continue; } output->Printf("f"); for (int edgeIndex = 0; edgeIndex < polygon.numEdges; ++edgeIndex) { const std::uint16_t reference = pointers.polygonEdges[ polygon.firstEdge + edgeIndex]; const cm_edge_t& edge = pointers.edges[ CM_EdgeIndex(reference)]; output->Printf(" %d", vertexBase + CM_EdgeStartVertex(edge, reference)); } output->Printf("\n"); } vertexBase += data->numVertices; ReleaseSubModelData(subModel, data); } delete model; idLibPrint::Printf("wrote %s\n", outputName.c_str()); } void BuildCollisionModelsForMap_f(const idCmdArgs* const args) { if (args == nullptr || args->Argc() < 2) { idLibPrint::Printf("usage: bcm [-entityOnly]\n"); return; } idLibPrint::Warning( "bcm command requires the framework map-file loader for %s", args->Argv(1)); } void StripBCM_f(const idCmdArgs* const args) { if (args == nullptr || args->Argc() < 2) { idLibPrint::Printf("usage: stripBCM \n"); return; } idLibPrint::Warning( "stripBCM requires the recovered map-specific strip volume set"); }