mirror of
https://github.com/johndoe6345789/metabuilder.git
synced 2026-05-04 02:34:52 +00:00
Add Quake 3 BSP entity interactions
This commit is contained in:
@@ -285,6 +285,7 @@ if(BUILD_SDL3_APP)
|
||||
src/services/impl/workflow/geometry/workflow_geometry_cube_generate_step.cpp
|
||||
src/services/impl/workflow/rendering/workflow_bsp_build_collision_step.cpp
|
||||
src/services/impl/workflow/rendering/workflow_bsp_build_geometry_step.cpp
|
||||
src/services/impl/workflow/rendering/workflow_bsp_entity_update_step.cpp
|
||||
src/services/impl/workflow/rendering/workflow_bsp_extract_textures_step.cpp
|
||||
src/services/impl/workflow/rendering/workflow_bsp_lightmap_atlas_step.cpp
|
||||
src/services/impl/workflow/rendering/workflow_bsp_load_step.cpp
|
||||
@@ -394,4 +395,4 @@ if(BUILD_SDL3_APP)
|
||||
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/packages")
|
||||
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/packages" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -39,6 +39,12 @@
|
||||
"typeVersion": 1,
|
||||
"position": [500, 0]
|
||||
},
|
||||
{
|
||||
"id": "bsp_entities_update",
|
||||
"type": "bsp.entities.update",
|
||||
"typeVersion": 1,
|
||||
"position": [550, 0]
|
||||
},
|
||||
{
|
||||
"id": "camera_update",
|
||||
"type": "camera.fps.update",
|
||||
@@ -129,7 +135,8 @@
|
||||
"input_poll": { "main": { "0": [{ "node": "physics_move", "type": "main", "index": 0 }] } },
|
||||
"physics_move": { "main": { "0": [{ "node": "physics_step", "type": "main", "index": 0 }] } },
|
||||
"physics_step": { "main": { "0": [{ "node": "sync_transforms", "type": "main", "index": 0 }] } },
|
||||
"sync_transforms": { "main": { "0": [{ "node": "camera_update", "type": "main", "index": 0 }] } },
|
||||
"sync_transforms": { "main": { "0": [{ "node": "bsp_entities_update", "type": "main", "index": 0 }] } },
|
||||
"bsp_entities_update": { "main": { "0": [{ "node": "camera_update", "type": "main", "index": 0 }] } },
|
||||
"camera_update": { "main": { "0": [{ "node": "render_prepare", "type": "main", "index": 0 }] } },
|
||||
"render_prepare": { "main": { "0": [{ "node": "frame_begin", "type": "main", "index": 0 }] } },
|
||||
"frame_begin": { "main": { "0": [{ "node": "draw_map", "type": "main", "index": 0 }] } },
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
#include "services/interfaces/workflow/rendering/workflow_bsp_entity_update_step.hpp"
|
||||
|
||||
#include <btBulletDynamicsCommon.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <string>
|
||||
|
||||
namespace sdl3cpp::services::impl {
|
||||
|
||||
namespace {
|
||||
|
||||
bool HasPrefix(const std::string& value, const std::string& prefix) {
|
||||
return value.rfind(prefix, 0) == 0;
|
||||
}
|
||||
|
||||
bool IsPickupClass(const std::string& classname) {
|
||||
return HasPrefix(classname, "weapon_") ||
|
||||
HasPrefix(classname, "ammo_") ||
|
||||
HasPrefix(classname, "item_") ||
|
||||
HasPrefix(classname, "holdable_");
|
||||
}
|
||||
|
||||
bool ReadVec3(const nlohmann::json& value, btVector3& out) {
|
||||
if (!value.is_array() || value.size() != 3) return false;
|
||||
out = btVector3(value[0].get<float>(), value[1].get<float>(), value[2].get<float>());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InBounds(const btVector3& p, const nlohmann::json& bounds, float pad) {
|
||||
if (!bounds.is_object() || !bounds.contains("min") || !bounds.contains("max")) return false;
|
||||
btVector3 mn, mx;
|
||||
if (!ReadVec3(bounds["min"], mn) || !ReadVec3(bounds["max"], mx)) return false;
|
||||
return p.x() >= mn.x() - pad && p.x() <= mx.x() + pad &&
|
||||
p.y() >= mn.y() - pad && p.y() <= mx.y() + pad &&
|
||||
p.z() >= mn.z() - pad && p.z() <= mx.z() + pad;
|
||||
}
|
||||
|
||||
void TeleportBody(btRigidBody* body, const btVector3& dest) {
|
||||
btTransform xform;
|
||||
xform.setIdentity();
|
||||
xform.setOrigin(dest);
|
||||
body->setWorldTransform(xform);
|
||||
if (body->getMotionState()) body->getMotionState()->setWorldTransform(xform);
|
||||
body->setLinearVelocity(btVector3(0, 0, 0));
|
||||
body->setAngularVelocity(btVector3(0, 0, 0));
|
||||
body->clearForces();
|
||||
body->activate(true);
|
||||
}
|
||||
|
||||
btVector3 JumpPadVelocity(const btVector3& from, const btVector3& target) {
|
||||
constexpr float kGravity = 9.81f;
|
||||
const btVector3 delta = target - from;
|
||||
const float horiz = std::sqrt(delta.x() * delta.x() + delta.z() * delta.z());
|
||||
const float t = std::clamp(horiz / 18.0f, 0.55f, 1.20f);
|
||||
return btVector3(delta.x() / t,
|
||||
(delta.y() + 0.5f * kGravity * t * t) / t,
|
||||
delta.z() / t);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
WorkflowBspEntityUpdateStep::WorkflowBspEntityUpdateStep(std::shared_ptr<ILogger> logger)
|
||||
: logger_(std::move(logger)) {}
|
||||
|
||||
std::string WorkflowBspEntityUpdateStep::GetPluginId() const {
|
||||
return "bsp.entities.update";
|
||||
}
|
||||
|
||||
void WorkflowBspEntityUpdateStep::Execute(const WorkflowStepDefinition& step, WorkflowContext& context) {
|
||||
const auto* entities = context.TryGet<nlohmann::json>("bsp.entities");
|
||||
if (!entities || !entities->is_array()) return;
|
||||
|
||||
const std::string playerName = context.GetString("physics_player_body", "");
|
||||
if (playerName.empty()) return;
|
||||
|
||||
auto* body = context.Get<btRigidBody*>("physics_body_" + playerName, nullptr);
|
||||
if (!body) return;
|
||||
|
||||
btTransform xform;
|
||||
body->getMotionState()->getWorldTransform(xform);
|
||||
const btVector3 playerPos = xform.getOrigin();
|
||||
const uint32_t frame = static_cast<uint32_t>(context.GetDouble("loop.iteration", 0.0));
|
||||
|
||||
auto collected = context.Get<nlohmann::json>("q3.collected", nlohmann::json::object());
|
||||
auto inventory = context.Get<nlohmann::json>("q3.inventory", nlohmann::json::object());
|
||||
auto cooldowns = context.Get<nlohmann::json>("q3.trigger_cooldowns", nlohmann::json::object());
|
||||
|
||||
auto setInventoryFlag = [&](const std::string& key) {
|
||||
inventory[key] = true;
|
||||
if (HasPrefix(key, "weapon_")) {
|
||||
context.Set<std::string>("q3.current_weapon", key);
|
||||
}
|
||||
};
|
||||
|
||||
for (const auto& ent : *entities) {
|
||||
const std::string classname = ent.value("classname", std::string{});
|
||||
const std::string id = ent.value("id", std::string{});
|
||||
|
||||
if (IsPickupClass(classname)) {
|
||||
if (id.empty() || collected.value(id, false)) continue;
|
||||
btVector3 itemPos;
|
||||
if (!ent.contains("position") || !ReadVec3(ent["position"], itemPos)) continue;
|
||||
if ((itemPos - playerPos).length2() > 1.2f * 1.2f) continue;
|
||||
|
||||
collected[id] = true;
|
||||
setInventoryFlag(classname);
|
||||
if (logger_) logger_->Info("bsp.entities.update: picked up " + classname);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (classname != "trigger_push" && classname != "trigger_teleport") continue;
|
||||
if (!ent.contains("bounds") || !InBounds(playerPos, ent["bounds"], 0.15f)) continue;
|
||||
|
||||
const uint32_t lastFrame = cooldowns.value(id, 0u);
|
||||
const uint32_t cooldownFrames = classname == "trigger_teleport" ? 45u : 15u;
|
||||
if (lastFrame != 0u && frame < lastFrame + cooldownFrames) continue;
|
||||
cooldowns[id] = frame == 0u ? 1u : frame;
|
||||
|
||||
btVector3 target;
|
||||
if (!ent.contains("target_position") || !ReadVec3(ent["target_position"], target)) continue;
|
||||
|
||||
if (classname == "trigger_teleport") {
|
||||
target += btVector3(0, 1.0f, 0);
|
||||
TeleportBody(body, target);
|
||||
if (logger_) logger_->Info("bsp.entities.update: teleported player via " + id);
|
||||
} else {
|
||||
body->setLinearVelocity(JumpPadVelocity(playerPos, target));
|
||||
body->activate(true);
|
||||
if (logger_) logger_->Info("bsp.entities.update: jump pad launch via " + id);
|
||||
}
|
||||
}
|
||||
|
||||
context.Set("q3.collected", collected);
|
||||
context.Set("q3.inventory", inventory);
|
||||
context.Set("q3.trigger_cooldowns", cooldowns);
|
||||
}
|
||||
|
||||
} // namespace sdl3cpp::services::impl
|
||||
+218
-25
@@ -3,14 +3,138 @@
|
||||
#include "services/interfaces/workflow/workflow_step_parameter_resolver.hpp"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cctype>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace sdl3cpp::services::impl {
|
||||
|
||||
namespace {
|
||||
|
||||
bool HasPrefix(const std::string& value, const std::string& prefix) {
|
||||
return value.rfind(prefix, 0) == 0;
|
||||
}
|
||||
|
||||
bool IsPickupClass(const std::string& classname) {
|
||||
return HasPrefix(classname, "weapon_") ||
|
||||
HasPrefix(classname, "ammo_") ||
|
||||
HasPrefix(classname, "item_") ||
|
||||
HasPrefix(classname, "holdable_");
|
||||
}
|
||||
|
||||
void SkipWhitespace(const std::string& text, size_t& pos) {
|
||||
while (pos < text.size() && std::isspace(static_cast<unsigned char>(text[pos]))) ++pos;
|
||||
}
|
||||
|
||||
bool ReadQuotedToken(const std::string& text, size_t& pos, std::string& out) {
|
||||
SkipWhitespace(text, pos);
|
||||
if (pos >= text.size() || text[pos] != '"') return false;
|
||||
++pos;
|
||||
|
||||
out.clear();
|
||||
while (pos < text.size()) {
|
||||
const char c = text[pos++];
|
||||
if (c == '"') return true;
|
||||
if (c == '\\' && pos < text.size()) {
|
||||
out.push_back(text[pos++]);
|
||||
} else {
|
||||
out.push_back(c);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<std::map<std::string, std::string>> ParseEntityLump(const std::string& entities) {
|
||||
std::vector<std::map<std::string, std::string>> parsed;
|
||||
size_t pos = 0;
|
||||
|
||||
while (pos < entities.size()) {
|
||||
SkipWhitespace(entities, pos);
|
||||
if (pos >= entities.size()) break;
|
||||
if (entities[pos] != '{') {
|
||||
++pos;
|
||||
continue;
|
||||
}
|
||||
|
||||
++pos;
|
||||
std::map<std::string, std::string> values;
|
||||
while (pos < entities.size()) {
|
||||
SkipWhitespace(entities, pos);
|
||||
if (pos >= entities.size()) break;
|
||||
if (entities[pos] == '}') {
|
||||
++pos;
|
||||
break;
|
||||
}
|
||||
|
||||
std::string key;
|
||||
std::string value;
|
||||
if (!ReadQuotedToken(entities, pos, key) || !ReadQuotedToken(entities, pos, value)) {
|
||||
break;
|
||||
}
|
||||
values[key] = value;
|
||||
}
|
||||
|
||||
if (!values.empty()) parsed.push_back(std::move(values));
|
||||
}
|
||||
|
||||
return parsed;
|
||||
}
|
||||
|
||||
bool ParseVec3(const std::string& text, float& x, float& y, float& z) {
|
||||
return std::sscanf(text.c_str(), "%f %f %f", &x, &y, &z) == 3;
|
||||
}
|
||||
|
||||
std::array<float, 3> ConvertQ3Point(float qx, float qy, float qz, float scale) {
|
||||
return {qx * scale, qz * scale, -qy * scale};
|
||||
}
|
||||
|
||||
nlohmann::json PointJson(const std::array<float, 3>& p) {
|
||||
return nlohmann::json::array({p[0], p[1], p[2]});
|
||||
}
|
||||
|
||||
nlohmann::json ConvertModelBounds(const BspModel& model, float scale) {
|
||||
std::array<float, 3> mn{
|
||||
std::numeric_limits<float>::max(),
|
||||
std::numeric_limits<float>::max(),
|
||||
std::numeric_limits<float>::max()
|
||||
};
|
||||
std::array<float, 3> mx{
|
||||
std::numeric_limits<float>::lowest(),
|
||||
std::numeric_limits<float>::lowest(),
|
||||
std::numeric_limits<float>::lowest()
|
||||
};
|
||||
|
||||
for (int x = 0; x < 2; ++x) {
|
||||
for (int y = 0; y < 2; ++y) {
|
||||
for (int z = 0; z < 2; ++z) {
|
||||
const auto p = ConvertQ3Point(x ? model.maxs[0] : model.mins[0],
|
||||
y ? model.maxs[1] : model.mins[1],
|
||||
z ? model.maxs[2] : model.mins[2],
|
||||
scale);
|
||||
for (int axis = 0; axis < 3; ++axis) {
|
||||
mn[axis] = std::min(mn[axis], p[axis]);
|
||||
mx[axis] = std::max(mx[axis], p[axis]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nlohmann::json{{"min", PointJson(mn)}, {"max", PointJson(mx)}};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
WorkflowBspParseSpawnStep::WorkflowBspParseSpawnStep(std::shared_ptr<ILogger> logger)
|
||||
: logger_(std::move(logger)) {}
|
||||
|
||||
@@ -29,39 +153,97 @@ void WorkflowBspParseSpawnStep::Execute(const WorkflowStepDefinition& step, Work
|
||||
auto* lumps = reinterpret_cast<const BspLump*>(bspData.data() + sizeof(BspHeader));
|
||||
const auto& entLump = lumps[LUMP_ENTITIES];
|
||||
std::string entities(reinterpret_cast<const char*>(bspData.data() + entLump.offset), entLump.length);
|
||||
const auto parsedEntities = ParseEntityLump(entities);
|
||||
|
||||
std::vector<BspModel> models;
|
||||
const auto& modelLump = lumps[LUMP_MODELS];
|
||||
if (modelLump.length >= 0 && modelLump.offset >= 0 &&
|
||||
static_cast<size_t>(modelLump.offset + modelLump.length) <= bspData.size()) {
|
||||
const auto* modelData = reinterpret_cast<const BspModel*>(bspData.data() + modelLump.offset);
|
||||
const size_t modelCount = static_cast<size_t>(modelLump.length) / sizeof(BspModel);
|
||||
models.assign(modelData, modelData + modelCount);
|
||||
}
|
||||
|
||||
float spawnX = 0, spawnY = 5, spawnZ = 0;
|
||||
float spawnAngle = 0;
|
||||
nlohmann::json entityJson = nlohmann::json::array();
|
||||
std::unordered_map<std::string, std::array<float, 3>> targets;
|
||||
std::map<std::string, int> classCounts;
|
||||
int pickupCount = 0;
|
||||
int jumpPadCount = 0;
|
||||
int teleporterCount = 0;
|
||||
|
||||
size_t pos = entities.find("info_player_deathmatch");
|
||||
if (pos != std::string::npos) {
|
||||
size_t blockStart = entities.rfind('{', pos);
|
||||
size_t blockEnd = entities.find('}', pos);
|
||||
if (blockStart != std::string::npos && blockEnd != std::string::npos) {
|
||||
std::string block = entities.substr(blockStart, blockEnd - blockStart);
|
||||
for (size_t i = 0; i < parsedEntities.size(); ++i) {
|
||||
const auto& values = parsedEntities[i];
|
||||
nlohmann::json ent = nlohmann::json::object();
|
||||
for (const auto& [key, value] : values) {
|
||||
ent[key] = value;
|
||||
}
|
||||
|
||||
size_t oPos = block.find("\"origin\"");
|
||||
if (oPos != std::string::npos) {
|
||||
size_t qStart = block.find('"', oPos + 8);
|
||||
size_t qEnd = block.find('"', qStart + 1);
|
||||
if (qStart != std::string::npos && qEnd != std::string::npos) {
|
||||
std::string origin = block.substr(qStart + 1, qEnd - qStart - 1);
|
||||
float ox = 0, oy = 0, oz = 0;
|
||||
if (std::sscanf(origin.c_str(), "%f %f %f", &ox, &oy, &oz) == 3) {
|
||||
spawnX = ox * scale;
|
||||
spawnY = oz * scale + 1.0f;
|
||||
spawnZ = -oy * scale;
|
||||
}
|
||||
const std::string classname = ent.value("classname", std::string{});
|
||||
const int classIndex = classCounts[classname]++;
|
||||
ent["id"] = classname.empty()
|
||||
? "ent_" + std::to_string(i)
|
||||
: classname + "_" + std::to_string(classIndex);
|
||||
|
||||
auto originIt = values.find("origin");
|
||||
if (originIt != values.end()) {
|
||||
float ox = 0, oy = 0, oz = 0;
|
||||
if (ParseVec3(originIt->second, ox, oy, oz)) {
|
||||
const auto p = ConvertQ3Point(ox, oy, oz, scale);
|
||||
ent["position"] = PointJson(p);
|
||||
|
||||
auto targetNameIt = values.find("targetname");
|
||||
if (targetNameIt != values.end()) {
|
||||
targets[targetNameIt->second] = p;
|
||||
}
|
||||
|
||||
if (classname == "info_player_deathmatch" && spawnY == 5.0f) {
|
||||
spawnX = p[0];
|
||||
spawnY = p[1] + 1.0f;
|
||||
spawnZ = p[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t aPos = block.find("\"angle\"");
|
||||
if (aPos != std::string::npos) {
|
||||
size_t qStart = block.find('"', aPos + 7);
|
||||
size_t qEnd = block.find('"', qStart + 1);
|
||||
if (qStart != std::string::npos && qEnd != std::string::npos) {
|
||||
spawnAngle = std::stof(block.substr(qStart + 1, qEnd - qStart - 1));
|
||||
}
|
||||
auto angleIt = values.find("angle");
|
||||
if (classname == "info_player_deathmatch" && angleIt != values.end()) {
|
||||
try {
|
||||
spawnAngle = std::stof(angleIt->second);
|
||||
} catch (...) {
|
||||
spawnAngle = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
auto modelIt = values.find("model");
|
||||
if (modelIt != values.end() && modelIt->second.size() > 1 && modelIt->second[0] == '*') {
|
||||
const int modelIndex = std::atoi(modelIt->second.c_str() + 1);
|
||||
if (modelIndex >= 0 && static_cast<size_t>(modelIndex) < models.size()) {
|
||||
ent["model_index"] = modelIndex;
|
||||
ent["bounds"] = ConvertModelBounds(models[static_cast<size_t>(modelIndex)], scale);
|
||||
}
|
||||
}
|
||||
|
||||
if (IsPickupClass(classname)) {
|
||||
ent["kind"] = "pickup";
|
||||
++pickupCount;
|
||||
} else if (classname == "trigger_push") {
|
||||
ent["kind"] = "jump_pad";
|
||||
++jumpPadCount;
|
||||
} else if (classname == "trigger_teleport") {
|
||||
ent["kind"] = "teleporter";
|
||||
++teleporterCount;
|
||||
}
|
||||
|
||||
entityJson.push_back(std::move(ent));
|
||||
}
|
||||
|
||||
for (auto& ent : entityJson) {
|
||||
const std::string target = ent.value("target", std::string{});
|
||||
if (!target.empty()) {
|
||||
auto targetIt = targets.find(target);
|
||||
if (targetIt != targets.end()) {
|
||||
ent["target_position"] = PointJson(targetIt->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -69,11 +251,22 @@ void WorkflowBspParseSpawnStep::Execute(const WorkflowStepDefinition& step, Work
|
||||
context.Set("bsp.spawn", nlohmann::json{
|
||||
{"x", spawnX}, {"y", spawnY}, {"z", spawnZ}, {"angle", spawnAngle}
|
||||
});
|
||||
context.Set("bsp.entities", entityJson);
|
||||
context.Set("bsp.entity_counts", nlohmann::json{
|
||||
{"total", entityJson.size()},
|
||||
{"pickups", pickupCount},
|
||||
{"jump_pads", jumpPadCount},
|
||||
{"teleporters", teleporterCount}
|
||||
});
|
||||
|
||||
if (logger_) {
|
||||
logger_->Info("bsp.parse_spawn: Spawn at (" + std::to_string(spawnX) + ", " +
|
||||
std::to_string(spawnY) + ", " + std::to_string(spawnZ) +
|
||||
") angle=" + std::to_string(spawnAngle));
|
||||
logger_->Info("bsp.parse_spawn: Parsed " + std::to_string(entityJson.size()) +
|
||||
" entities (" + std::to_string(pickupCount) + " pickups, " +
|
||||
std::to_string(jumpPadCount) + " jump pads, " +
|
||||
std::to_string(teleporterCount) + " teleporters)");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
#include "services/interfaces/workflow/rendering/workflow_bsp_load_step.hpp"
|
||||
#include "services/interfaces/workflow/rendering/workflow_bsp_lightmap_atlas_step.hpp"
|
||||
#include "services/interfaces/workflow/rendering/workflow_bsp_parse_spawn_step.hpp"
|
||||
#include "services/interfaces/workflow/rendering/workflow_bsp_entity_update_step.hpp"
|
||||
#include "services/interfaces/workflow/rendering/workflow_bsp_build_geometry_step.hpp"
|
||||
#include "services/interfaces/workflow/rendering/workflow_bsp_extract_textures_step.hpp"
|
||||
#include "services/interfaces/workflow/rendering/workflow_bsp_upload_geometry_step.hpp"
|
||||
@@ -295,6 +296,7 @@ void WorkflowRegistrar::RegisterSteps(std::shared_ptr<IWorkflowStepRegistry> reg
|
||||
registry->RegisterStep(std::make_shared<WorkflowBspLoadStep>(logger_));
|
||||
registry->RegisterStep(std::make_shared<WorkflowBspLightmapAtlasStep>(logger_));
|
||||
registry->RegisterStep(std::make_shared<WorkflowBspParseSpawnStep>(logger_));
|
||||
registry->RegisterStep(std::make_shared<WorkflowBspEntityUpdateStep>(logger_));
|
||||
registry->RegisterStep(std::make_shared<WorkflowBspBuildGeometryStep>(logger_));
|
||||
registry->RegisterStep(std::make_shared<WorkflowBspExtractTexturesStep>(logger_));
|
||||
registry->RegisterStep(std::make_shared<WorkflowBspUploadGeometryStep>(logger_));
|
||||
|
||||
@@ -66,6 +66,15 @@ struct BspPlane {
|
||||
float dist;
|
||||
};
|
||||
|
||||
struct BspModel {
|
||||
float mins[3];
|
||||
float maxs[3];
|
||||
int32_t firstFace;
|
||||
int32_t numFaces;
|
||||
int32_t firstBrush;
|
||||
int32_t numBrushes;
|
||||
};
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
// Lump indices
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include "services/interfaces/i_workflow_step.hpp"
|
||||
#include "services/interfaces/i_logger.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace sdl3cpp::services::impl {
|
||||
|
||||
class WorkflowBspEntityUpdateStep final : public IWorkflowStep {
|
||||
public:
|
||||
explicit WorkflowBspEntityUpdateStep(std::shared_ptr<ILogger> logger);
|
||||
std::string GetPluginId() const override;
|
||||
void Execute(const WorkflowStepDefinition& step, WorkflowContext& context) override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<ILogger> logger_;
|
||||
};
|
||||
|
||||
} // namespace sdl3cpp::services::impl
|
||||
Reference in New Issue
Block a user