From dd14988bad4e64f4d9a14185a35f86133c23c703 Mon Sep 17 00:00:00 2001 From: LJW-Dev <48092720+LJW-Dev@users.noreply.github.com> Date: Sun, 22 Mar 2026 19:22:19 +0800 Subject: [PATCH] feat: xmodles have toggleable shadows --- src/ObjLoading/Game/T6/BSP/BSP.h | 2 ++ src/ObjLoading/Game/T6/BSP/BSPCreator.cpp | 12 ++++++++++ .../Game/T6/BSP/Linker/GfxWorldLinker.cpp | 23 ++++++++++++++----- 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/src/ObjLoading/Game/T6/BSP/BSP.h b/src/ObjLoading/Game/T6/BSP/BSP.h index 731435b6..ef4f5e7b 100644 --- a/src/ObjLoading/Game/T6/BSP/BSP.h +++ b/src/ObjLoading/Game/T6/BSP/BSP.h @@ -58,6 +58,8 @@ namespace BSP bool areBoundsValid; vec3_t mins; vec3_t maxs; + + bool doesCastShadow; }; struct BSPWorld diff --git a/src/ObjLoading/Game/T6/BSP/BSPCreator.cpp b/src/ObjLoading/Game/T6/BSP/BSPCreator.cpp index 69043ab0..93a05722 100644 --- a/src/ObjLoading/Game/T6/BSP/BSPCreator.cpp +++ b/src/ObjLoading/Game/T6/BSP/BSPCreator.cpp @@ -548,6 +548,18 @@ namespace throw GltfLoadException("Xmodel has no name."); xmodel.name = *node.extras->xmodel; + xmodel.doesCastShadow = true; + if (node.extras->flags) + { + std::vector flagStrVec = utils::StringSplit(*node.extras->flags, ','); + for (std::string& flag : flagStrVec) + if (!flag.compare(surfaceTypeToNameMap[SURF_TYPE_NOCASTSHADOW])) + { + xmodel.doesCastShadow = false; + break; + } + } + Eigen::Vector4f position(0, 0, 0, 1.0f); Eigen::Vector4f transformedPosition = nodeMatrix * position; xmodel.origin.x = transformedPosition.x(); diff --git a/src/ObjLoading/Game/T6/BSP/Linker/GfxWorldLinker.cpp b/src/ObjLoading/Game/T6/BSP/Linker/GfxWorldLinker.cpp index df787655..57d27677 100644 --- a/src/ObjLoading/Game/T6/BSP/Linker/GfxWorldLinker.cpp +++ b/src/ObjLoading/Game/T6/BSP/Linker/GfxWorldLinker.cpp @@ -182,9 +182,12 @@ namespace BSP BSPUtil::convertQuaternionToAxis(&bspModel.rotationQuaternion, currModel->placement.axis); currModel->placement.scale = bspModel.scale; - currModel->cullDist = 10000.0f; currModel->flags = 0; - currModel->primaryLightIndex = 0; + if (!bspModel.doesCastShadow) + currModel->flags |= STATIC_MODEL_FLAG_NO_SHADOW; + + currModel->cullDist = 10000.0f; + currModel->primaryLightIndex = 1; currModel->reflectionProbeIndex = 0; currModel->smid = modelIdx; @@ -357,14 +360,22 @@ namespace BSP gfxWorld->shadowGeom = m_memory.Alloc(gfxWorld->primaryLightCount); for (unsigned int lightIdx = 0; lightIdx < gfxWorld->primaryLightCount; lightIdx++) { - + // smodelCount and smodelIndex is filled next loop gfxWorld->shadowGeom[lightIdx].smodelCount = 0; - gfxWorld->shadowGeom[lightIdx].smodelIndex = nullptr; + gfxWorld->shadowGeom[lightIdx].smodelIndex = m_memory.Alloc(gfxWorld->dpvs.smodelCount); - // sorted surfs is written to by the game + // sorted surfs and surfaceCount is recalculated each frame gfxWorld->shadowGeom[lightIdx].surfaceCount = gfxWorld->dpvs.staticSurfaceCount; gfxWorld->shadowGeom[lightIdx].sortedSurfIndex = m_memory.Alloc(gfxWorld->dpvs.staticSurfaceCount); } + for (unsigned int modelIdx = 0; modelIdx < gfxWorld->dpvs.smodelCount; modelIdx++) + { + if ((gfxWorld->dpvs.smodelDrawInsts[modelIdx].flags & STATIC_MODEL_FLAG_NO_SHADOW) != 0) + continue; + char lightIndex = gfxWorld->dpvs.smodelDrawInsts[modelIdx].primaryLightIndex; + gfxWorld->shadowGeom[lightIndex].smodelIndex[gfxWorld->shadowGeom[lightIndex].smodelCount] = modelIdx; + gfxWorld->shadowGeom[lightIndex].smodelCount++; + } gfxWorld->lightRegion = m_memory.Alloc(gfxWorld->primaryLightCount); for (unsigned int lightIdx = 0; lightIdx < gfxWorld->primaryLightCount; lightIdx++) @@ -779,7 +790,7 @@ namespace BSP loadLightGrid(gfxWorld); - loadGfxLights(bsp, gfxWorld); + loadGfxLights(bsp, gfxWorld); // requires xmodels and surfaces loadModels(gfxWorld);