summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorrobojumper <robojumper@gmail.com>2025-03-28 18:42:09 +0100
committerrobojumper <robojumper@gmail.com>2025-03-28 18:42:09 +0100
commitd35c4adeeffc0629f705518aedd9e8d4b4570364 (patch)
treed005df9c3ec37e9bdb48824efc880b830a0a4c1b /src
parent0766801142a25efcad59e98b77bca43b1c02bae0 (diff)
Running out of ideas for G3DUtility
Diffstat (limited to 'src')
-rw-r--r--src/PowerPC_EABI_Support/MSL/MSL_C/MSL_Common/Include/cstring2
-rw-r--r--src/PowerPC_EABI_Support/MSL/MSL_C/MSL_Common/Include/string.h1
-rw-r--r--src/egg/gfx/eggG3DUtility.cpp378
-rw-r--r--src/egg/gfx/eggLightTextureMgr.cpp12
-rw-r--r--src/egg/gfx/eggModelEx.cpp11
-rw-r--r--src/nw4r/g3d/res/g3d_resmdl.cpp5
-rw-r--r--src/toBeSorted/arc_callback_handler.cpp5
7 files changed, 397 insertions, 17 deletions
diff --git a/src/PowerPC_EABI_Support/MSL/MSL_C/MSL_Common/Include/cstring b/src/PowerPC_EABI_Support/MSL/MSL_C/MSL_Common/Include/cstring
index 8b388ec0..8a62a363 100644
--- a/src/PowerPC_EABI_Support/MSL/MSL_C/MSL_Common/Include/cstring
+++ b/src/PowerPC_EABI_Support/MSL/MSL_C/MSL_Common/Include/cstring
@@ -19,7 +19,7 @@ using ::strlen;
using ::strncat;
using ::strncmp;
using ::strncpy;
-// using ::strstr;
+using ::strstr;
} // namespace std
#endif
diff --git a/src/PowerPC_EABI_Support/MSL/MSL_C/MSL_Common/Include/string.h b/src/PowerPC_EABI_Support/MSL/MSL_C/MSL_Common/Include/string.h
index 86c421b3..8e36bf83 100644
--- a/src/PowerPC_EABI_Support/MSL/MSL_C/MSL_Common/Include/string.h
+++ b/src/PowerPC_EABI_Support/MSL/MSL_C/MSL_Common/Include/string.h
@@ -17,6 +17,7 @@ char *strrchr(const char *str, int c);
char *strchr(const char *str, int c);
int strncmp(const char *str1, const char *str2, size_t n);
int strcmp(const char *str1, const char *str2);
+char *strstr(const char *str1, const char *str2);
char *strncat(char *dst, const char *src, size_t n);
char *strcat(char *dst, const char *src);
char *strncpy(char *dst, const char *src, size_t n);
diff --git a/src/egg/gfx/eggG3DUtility.cpp b/src/egg/gfx/eggG3DUtility.cpp
index b8da6920..a61cefc6 100644
--- a/src/egg/gfx/eggG3DUtility.cpp
+++ b/src/egg/gfx/eggG3DUtility.cpp
@@ -1,3 +1,379 @@
#include "egg/gfx/eggG3DUtility.h"
-namespace EGG {} // namespace EGG
+#include "common.h"
+#include "egg/core/eggHeap.h"
+#include "nw4r/g3d/g3d_light.h"
+#include "nw4r/g3d/g3d_state.h"
+#include "nw4r/g3d/res/g3d_resanmamblight.h"
+#include "nw4r/g3d/res/g3d_resanmlight.h"
+#include "nw4r/g3d/res/g3d_resanmscn.h"
+#include "nw4r/g3d/res/g3d_reslightset.h"
+#include "nw4r/g3d/res/g3d_resmat.h"
+#include "nw4r/g3d/res/g3d_resnode.h"
+#include "nw4r/g3d/res/g3d_restev.h"
+#include "nw4r/math/math_types.h"
+#include "rvl/GX/GXTexture.h"
+#include "rvl/GX/GXTypes.h"
+#include "rvl/MTX/mtx.h"
+
+#include <cstring>
+
+namespace EGG {
+
+void G3DUtility::create(u32 size, Heap *pHeap) {
+ if (pHeap == nullptr) {
+ pHeap = Heap::getCurrentHeap();
+ }
+
+ if (size != 0) {
+ sBuf = operator new[](size, pHeap, 0x20);
+ sSize = size;
+ } else {
+ sBuf = nullptr;
+ sSize = 0;
+ }
+
+ sOffset = 0;
+
+ nw4r::g3d::G3DState::ScnDependentTexMtxFuncPtr pFunc;
+ u32 id = 5;
+ while (true) {
+ if (nw4r::g3d::G3DState::GetScnDependentTexMtxFunc(id, &pFunc, nullptr) && pFunc == defaultTexMtxFunc) {
+ break;
+ }
+ id++;
+ if (id >= 256) {
+ id = 5;
+ nw4r::g3d::G3DState::ScnDependentTexMtxFuncPtr pFunc2;
+ while (true) {
+ if (nw4r::g3d::G3DState::GetScnDependentTexMtxFunc(id, &pFunc2, nullptr) &&
+ pFunc2 == nw4r::g3d::detail::ScnDependentMtxFunc::DefaultMapping) {
+ sId = id;
+ nw4r::g3d::G3DState::SetScnDependentTexMtxFunc(
+ id, defaultTexMtxFunc, nw4r::g3d::G3DState::SCNDEPENDENT_TEXMTX_FUNCTYPE_SRC_POS
+ );
+ break;
+ }
+ id++;
+ if (id >= 256) {
+ break;
+ }
+ }
+ return;
+ }
+ }
+}
+
+void G3DUtility::defaultTexMtxFunc(nw4r::math::MTX34 *pMtx, s8 camRef, s8 lightRef) {
+ const nw4r::math::MTX34 *mtxPtr = nw4r::g3d::G3DState::GetInvCameraMtxPtr();
+ PSMTXCopy(mtxPtr->m, pMtx->m);
+}
+
+bool G3DUtility::setUpLightSet(nw4r::g3d::LightSetting &lightSet, nw4r::g3d::ResAnmScn scn, int refNumber) {
+ if (!scn.IsValid()) {
+ return false;
+ }
+
+ int end = scn.GetResLightSetMaxRefNumber() < lightSet.GetNumLightSet() ? scn.GetResLightSetMaxRefNumber() :
+ lightSet.GetNumLightSet();
+ int start = 0;
+ if (refNumber >= 0) {
+ start = refNumber;
+ end = refNumber + 1;
+ }
+
+ for (; start < end; start++) {
+ nw4r::g3d::LightSet set = lightSet.GetLightSet(start);
+ nw4r::g3d::ResLightSet resSet = scn.GetResLightSetByRefNumber(start);
+
+ if (!set.IsValid()) {
+ return false;
+ }
+
+ if (resSet.IsValid()) {
+ u32 counter = 7;
+ u32 j = 0;
+ u32 numLight = resSet.GetNumLight();
+ if (numLight != 0) {
+ for (; j < numLight; j++) {
+ if (resSet.GetLightID(j) != 0xFFFF) {
+ nw4r::g3d::ResAnmLight anmLight = scn.GetResAnmLight(resSet.GetLightID(j));
+ set.SelectLightObj(j, anmLight.GetRefNumber());
+ if (anmLight.HasSpecularLight()) {
+ set.SelectLightObj(counter--, anmLight.GetSpecularLightIdx());
+ }
+ } else {
+ set.SelectLightObj(j, -1);
+ }
+ }
+ }
+
+ for (; numLight <= counter; numLight++) {
+ set.SelectLightObj(numLight, -1);
+ }
+
+ if (resSet.HasAmbLight()) {
+ nw4r::g3d::ResAnmAmbLight anmAmbLight = scn.GetResAnmAmbLight(resSet.GetAmbLightID());
+ set.SelectAmbLightObj(anmAmbLight.GetRefNumber());
+ } else {
+ set.SelectAmbLightObj(-1);
+ }
+ }
+ }
+
+ return true;
+}
+
+inline bool searchInner(
+ const char *name, const char *candidate, bool exactMatch, G3DUtility::SearchResult *pResults, int i, int resultIdx,
+ int maxNumResults
+) {
+ if (name != nullptr) {
+ bool found;
+ if (exactMatch) {
+ found = !std::strcmp(candidate, name);
+ } else {
+ found = std::strstr(candidate, name);
+ }
+ if (!found) {
+ goto err;
+ }
+ }
+
+ if (resultIdx < maxNumResults) {
+ pResults[resultIdx].nodeIdx = i;
+ pResults[resultIdx].name = candidate;
+ }
+ return true;
+
+err:
+ return false;
+}
+
+int G3DUtility::searchStringResNode(
+ nw4r::g3d::ResMdl mdl, const char *name, bool exactMatch, SearchResult *pResults, int maxNumResults
+) {
+ u16 resultIdx = 0;
+ for (int i = 0; i < mdl.GetResNodeNumEntries(); i++) {
+ nw4r::g3d::ResNode nd = mdl.GetResNode(i);
+ const char *nodeName = nd.GetName();
+
+ if (searchInner(name, nodeName, exactMatch, pResults, i, resultIdx, maxNumResults)) {
+ resultIdx++;
+ }
+ }
+
+ return resultIdx;
+}
+
+int G3DUtility::searchStringResMat(
+ nw4r::g3d::ResMdl mdl, const char *name, bool exactMatch, SearchResult *pResults, int maxNumResults
+) {
+ u16 resultIdx = 0;
+ for (int i = 0; i < mdl.GetResMatNumEntries(); i++) {
+ nw4r::g3d::ResMat mat = mdl.GetResMat(i);
+ const char *nodeName = mat.GetName();
+
+ if (searchInner(name, nodeName, exactMatch, pResults, i, resultIdx, maxNumResults)) {
+ resultIdx++;
+ }
+ }
+
+ return resultIdx;
+}
+
+int G3DUtility::searchStringResTexPlttInfo(
+ nw4r::g3d::ResMdl mdl, const char *name, bool exactMatch, SearchResult *pResults, int maxNumResults
+) {
+ u16 resultIdx = 0;
+ for (u32 i = 0; i < mdl.GetResTexPlttInfoOffsetFromTexNameNumEntries(); i++) {
+ nw4r::g3d::ResTexPlttInfoOffset info = mdl.GetResTexPlttInfoOffsetFromTexName(i);
+ for (u16 j = 0; j < info.GetNumData(); j++) {
+ const char *nodeName = info.GetPllt(j).GetTexName();
+ if (searchInner(name, nodeName, exactMatch, pResults, i, resultIdx, maxNumResults)) {
+ resultIdx++;
+ }
+ }
+ }
+
+ return resultIdx;
+}
+
+u16 G3DUtility::SetTexture(
+ nw4r::g3d::ResMat resMat, nw4r::g3d::ScnMdl::CopiedMatAccess *matAccess, const char *name, const GXTexObj *texObj,
+ bool copy, SetTextureResult *pResult, int maxNumResults, int param_8
+) {
+ if (!resMat.IsValid()) {
+ return 0;
+ }
+
+ u16 resultIdx = 0;
+ for (u16 i = 0; i < resMat.GetNumResTexPlttInfo(); i++) {
+ nw4r::g3d::ResTexPlttInfo plltInfo = resMat.GetResTexPlttInfo(i);
+ const char *plltName = plltInfo.GetTexName();
+ if (std::strcmp(name, plltName)) {
+ continue;
+ }
+ for (int j = 0; j < 2; j++) {
+ nw4r::g3d::ResTexObj resTexObj(nullptr);
+ if (j == 0) {
+ if ((matAccess == nullptr || param_8 == 1) ||
+ (param_8 == 0 && !matAccess->GetResTexObj(false).IsValid())) {
+ resTexObj = resMat.GetResTexObj();
+ }
+ } else if (matAccess != nullptr) {
+ resTexObj = param_8 == 0 ? matAccess->GetResTexObjEx() : matAccess->GetResTexObj(param_8 == 3);
+ }
+
+ if (resTexObj.IsValid()) {
+ GXTexObj *obj = resTexObj.GetTexObj(plltInfo.ref().mapID);
+ if (copy) {
+ void *pImage;
+ u16 width;
+ u16 height;
+ GXTexFmt format;
+ GXTexWrapMode wrapS;
+ GXTexWrapMode wrapT;
+ u8 mipmap;
+ GXGetTexObjAll(obj, &pImage, &width, &height, &format, &wrapS, &wrapT, &mipmap);
+
+ GXTexFilter minFilter;
+ GXTexFilter magFilter;
+ f32 minLOD;
+ f32 maxLOD;
+ f32 LODBias;
+ u8 biasClampEnable;
+ u8 edgeLODEnable;
+ GXAnisotropy anisotropy;
+ GXGetTexObjLODAll(
+ obj, &minFilter, &magFilter, &minLOD, &maxLOD, &LODBias, &biasClampEnable, &edgeLODEnable,
+ &anisotropy
+ );
+
+ *obj = *texObj;
+ GXInitTexObjFilter(obj, minFilter, magFilter);
+ GXInitTexObjWrapMode(obj, wrapS, wrapT);
+ } else {
+ *obj = *texObj;
+ }
+ }
+ }
+
+ if (pResult != nullptr && resultIdx < maxNumResults) {
+ pResult[resultIdx].texCoordId = -1;
+ pResult[resultIdx].texMapId = -1;
+
+ nw4r::g3d::ResTev resTev = resMat.GetResTev();
+ for (u8 stage = 0; stage < resTev.GetNumTevStages(); stage++) {
+ GXTexCoordID coord;
+ GXTexMapID map;
+ if (resTev.GXGetTevOrder(static_cast<GXTevStageID>(stage), &coord, &map, nullptr) &&
+ map == plltInfo.ref().mapID) {
+ pResult[resultIdx].texMapId = map;
+ pResult[resultIdx].texCoordId = coord;
+ }
+ }
+ }
+ resultIdx++;
+ }
+
+ return resultIdx;
+}
+
+int G3DUtility::ApplyLightMat(nw4r::g3d::ResMdl mdl, const char *prefix) {
+ GXTexCoordID matchingLayerCoords[8];
+
+ int prefixLen = std::strlen(prefix);
+ int result = 0;
+
+ u32 i = 0;
+ u32 numMats = mdl.GetResMatNumEntries();
+ if (numMats != 0) {
+ for (; i < numMats; i++) {
+ int numMatchingLayers = 0;
+ nw4r::g3d::ResMat mat = mdl.GetResMat(i);
+ if (mat.GetResGenMode().GXGetNumTexGens() >= mat.GetNumResTexPlttInfo()) {
+ for (u32 j = 0; j < mat.GetNumResTexPlttInfo(); j++) {
+ const char *texName = mat.GetResTexPlttInfo(j).GetTexName();
+ bool prefixMatch = true;
+ int rem = prefixLen;
+ for (int i = 0; i < prefixLen; i++) {
+ if (prefix[i] != texName[i]) {
+ prefixMatch = false;
+ break;
+ }
+ }
+ if (prefixMatch) {
+ matchingLayerCoords[numMatchingLayers++] = static_cast<GXTexCoordID>(j);
+ }
+ }
+
+ if (numMatchingLayers > 1) {
+ nw4r::g3d::ResGenMode genMode = mat.GetResGenMode();
+ int numTexGens = genMode.GXGetNumTexGens();
+ nw4r::g3d::ResMatTexCoordGen resMatTexCoordGen = mat.GetResMatTexCoordGen();
+ nw4r::g3d::ResTexSrt resTexSrt = mat.GetResTexSrt();
+ nw4r::g3d::ResTev resTev = mat.GetResTev();
+ GXTexGenType texGenType;
+ GXTexGenSrc param;
+ GXBool normalize;
+ u32 postMtx;
+ resMatTexCoordGen.GXGetTexCoordGen2(
+ matchingLayerCoords[0], &texGenType, &param, &normalize, &postMtx
+ );
+ numMatchingLayers--;
+ for (; numMatchingLayers > 0; numMatchingLayers--) {
+ GXTexCoordID idx = matchingLayerCoords[numMatchingLayers];
+ if (idx == numTexGens - 1) {
+ u32 numStages = resTev.GetNumTevStages();
+ for (u32 stage = 0; stage < numStages; stage++) {
+ GXTexCoordID coord;
+ GXTexMapID map;
+ GXChannelID channel;
+ if (resTev.GXGetTevOrder(static_cast<GXTevStageID>(stage), &coord, &map, &channel) &&
+ coord == idx) {
+ resTev.GXSetTevOrder(
+ static_cast<GXTevStageID>(stage),
+ matchingLayerCoords[0], map, channel
+ );
+ }
+ }
+
+ int stage = 0;
+ int nInds = genMode.GXGetNumIndStages();
+ for (; stage < nInds; stage++) {
+ GXTexCoordID coord;
+ GXTexMapID map;
+ if (resTev.GXGetIndTexOrder(static_cast<GXIndTexStageID>(stage), &coord, &map) &&
+ coord == idx) {
+ resTev.GXSetIndTexOrder(
+ static_cast<GXIndTexStageID>(stage),
+ static_cast<GXTexCoordID>(matchingLayerCoords[0]), map
+ );
+ }
+ }
+ resMatTexCoordGen.Disable(idx);
+ result++;
+ resTexSrt.Disable(idx);
+ numTexGens--;
+ }
+ }
+ resMatTexCoordGen.DCStore(false);
+ resTev.DCStore(false);
+ genMode.GXSetNumTexGens(numTexGens);
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+void *G3DUtility::BumpAlloc(u32 size, u32 align) {
+ u32 next = sOffset % align != 0 ? sOffset + (align - sOffset % align) : sOffset;
+ sOffset = next + size;
+ // REGSWAP
+ return reinterpret_cast<u8 *>(sBuf) + next;
+}
+
+} // namespace EGG
diff --git a/src/egg/gfx/eggLightTextureMgr.cpp b/src/egg/gfx/eggLightTextureMgr.cpp
index b1aa0cee..4acbff26 100644
--- a/src/egg/gfx/eggLightTextureMgr.cpp
+++ b/src/egg/gfx/eggLightTextureMgr.cpp
@@ -106,15 +106,15 @@ bool LightTextureManager::deleteTexture(int idx) {
return false;
}
-int LightTextureManager::replaceModelTextures(nw4r::g3d::ResMdl mdl) const {
- int count = 0;
+u16 LightTextureManager::replaceModelTextures(nw4r::g3d::ResMdl mdl) const {
+ u16 count = 0;
for (u16 i = 0; i < mTextureCount; i++) {
count += replaceModelTexture(i, mdl);
}
return count;
}
-int LightTextureManager::replaceModelTexture(int tex, nw4r::g3d::ResMdl mdl) const {
+u16 LightTextureManager::replaceModelTexture(int tex, nw4r::g3d::ResMdl mdl) const {
if (mpTextures[tex] == nullptr) {
return 0;
}
@@ -122,12 +122,12 @@ int LightTextureManager::replaceModelTexture(int tex, nw4r::g3d::ResMdl mdl) con
G3DUtility::SetTextureResult buf[256];
GXTexObj obj;
mpTextures[tex]->getTexObj(&obj);
- int count = 0;
+ u16 count = 0;
for (int i = 0; i < mdl.GetResMatNumEntries(); i++) {
nw4r::g3d::ResMat mat = mdl.GetResMat(i);
- int res = G3DUtility::SetTexture(mat, nullptr, mpTextures[tex]->getName(), &obj, false, buf, 0xFF, 2);
- for (int j = 0; j < (u16)res; j++) {
+ u16 res = G3DUtility::SetTexture(mat, nullptr, mpTextures[tex]->getName(), &obj, false, buf, 0xFF, 2);
+ for (int j = 0; j < res; j++) {
if (buf[j].texCoordId != -1) {
fn_804AE340(mat, static_cast<GXTexCoordID>(buf[j].texCoordId));
}
diff --git a/src/egg/gfx/eggModelEx.cpp b/src/egg/gfx/eggModelEx.cpp
index daa9d196..14c10293 100644
--- a/src/egg/gfx/eggModelEx.cpp
+++ b/src/egg/gfx/eggModelEx.cpp
@@ -46,7 +46,7 @@ static const G3DUtility::MdlSearch sMdlSearch[6] = {
};
void thisProbablyGotDeadStripped() {
- sMdlSearch[0](g3d::ResMdl(nullptr), "", nullptr, 0);
+ sMdlSearch[0](g3d::ResMdl(nullptr), "", false, nullptr, 0);
}
extern "C" s32 lbl_8057682C;
@@ -57,11 +57,14 @@ void ModelEx::getShapeMinMax(u16 shapeIndex, math::VEC3 *pMin, math::VEC3 *pMax,
case cType_ScnMdl:
case cType_ScnMdl1Mat1Shp: {
g3d::ResShp shp = getResShp(shapeIndex);
- lbl_8057682C = 0;
+
+ G3DUtility::ResetBumpAlloc();
+
g3d::ResMdl mdl = getResMdl();
math::MTX34 tmpMtx;
g3d::ResMdlInfo info = mdl.GetResMdlInfo();
- math::MTX34 *pMtxArr = G3DUtility::GetWorldMtxArray(info.GetNumPosNrmMtx() * sizeof(math::MTX34), 1);
+ math::MTX34 *pMtxArr =
+ reinterpret_cast<math::MTX34 *>(G3DUtility::BumpAlloc(info.GetNumPosNrmMtx() * sizeof(math::MTX34), 1));
if (doCalcWorld) {
PSMTXIdentity(tmpMtx);
calcWorld(pMtxArr, &tmpMtx);
@@ -92,7 +95,7 @@ void ModelEx::getShapeMinMax(u16 shapeIndex, math::VEC3 *pMin, math::VEC3 *pMax,
math::VEC3Maximize(pMax, pMax, &tmpVec);
}
}
- lbl_8057682C = 0;
+ G3DUtility::ResetBumpAlloc();
break;
}
case cType_ScnProcModel:
diff --git a/src/nw4r/g3d/res/g3d_resmdl.cpp b/src/nw4r/g3d/res/g3d_resmdl.cpp
index 1e49d94e..145b525b 100644
--- a/src/nw4r/g3d/res/g3d_resmdl.cpp
+++ b/src/nw4r/g3d/res/g3d_resmdl.cpp
@@ -1,4 +1,5 @@
#include "nw4r/g3d.h" // IWYU pragma: export
+#include "nw4r/g3d/res/g3d_resmat.h"
namespace nw4r {
namespace g3d {
@@ -192,8 +193,8 @@ u32 ResMdl::GetResShpNumEntries() const {
* ResTexPlttInfo
*
******************************************************************************/
-ResTexPlttInfo ResMdl::GetResTexPlttInfoOffsetFromTexName(int idx) const {
- return ResTexPlttInfo(ofs_to_obj<ResDic>(ref().toResTexNameToTexPlttInfoDic)[idx]);
+ ResTexPlttInfoOffset ResMdl::GetResTexPlttInfoOffsetFromTexName(int idx) const {
+ return ResTexPlttInfoOffset(ofs_to_obj<ResDic>(ref().toResTexNameToTexPlttInfoDic)[idx]);
}
u32 ResMdl::GetResTexPlttInfoOffsetFromTexNameNumEntries() const {
diff --git a/src/toBeSorted/arc_callback_handler.cpp b/src/toBeSorted/arc_callback_handler.cpp
index f2016729..aca65dd4 100644
--- a/src/toBeSorted/arc_callback_handler.cpp
+++ b/src/toBeSorted/arc_callback_handler.cpp
@@ -1,6 +1,7 @@
#include "d/col/bg/d_bg_s.h"
#include "d/col/bg/d_bg_w_kcol.h"
#include "d/d_rawarchive.h"
+#include "egg/gfx/eggG3DUtility.h"
#include "egg/gfx/eggLightTextureMgr.h"
#include "egg/gfx/eggLightManager.h"
#include "m/m3d/m3d.h"
@@ -16,8 +17,6 @@ ArcCallbackHandler ArcCallbackHandler::sInstance;
#define NAME_OARC 'oarc'
#define NAME_RARC 'rarc'
-extern "C" void FUN_804a7260(nw4r::g3d::ResMdl, const char *prefix);
-
void BindSystemModelsAndLighting(nw4r::g3d::ResFile file) {
nw4r::g3d::ResFile sysFile(OarcManager::GetInstance()->getMdlFromArc2("System"));
if (sysFile.IsValid()) {
@@ -30,7 +29,7 @@ void BindSystemModelsAndLighting(nw4r::g3d::ResFile file) {
for (int i = 0; i < file.GetResMdlNumEntries(); i++) {
nw4r::g3d::ResMdl mdl = file.GetResMdl(i);
lightTexMgr->replaceModelTextures(mdl);
- FUN_804a7260(mdl, "Lm");
+ EGG::G3DUtility::ApplyLightMat(mdl, "Lm");
for (int j = 0; j < mdl.GetResMatNumEntries(); j++) {
nw4r::g3d::ResMat mat = mdl.GetResMat(j);
if (mat.IsOpaque()) {