Skip to content

Commit

Permalink
Merge pull request #2 from Jayveer/feature/mgs2
Browse files Browse the repository at this point in the history
add support for mgs files
  • Loading branch information
Jayveer authored Nov 11, 2023
2 parents d929772 + 159b271 commit d62bdfa
Show file tree
Hide file tree
Showing 15 changed files with 872 additions and 82 deletions.
16 changes: 10 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
# Metal Gear Solid Master Collection Noesis Plugin

This is a plugin for [Noesis](https://richwhitehouse.com/index.php?content=inc_projects.php&showproject=91) which allows the user to view textured 3D Models and animations from the PC Master Collection version of the game Metal Gear Solid 3.
This is a plugin for [Noesis](https://richwhitehouse.com/index.php?content=inc_projects.php&showproject=91) which allows the user to view textured 3D Models and animations from the PC Master Collection version of the games Metal Gear Solid 2 and 3.

It currently only supports mdl files for models, tri files for textures and mtar files for mtar files for animation. I am not sure why the game still contains these files as I can't imagine it uses them.
It currently supports kms, evm and mdl files for models, tri files for textures and mtar or mar files for animation. I am not sure why the game still contains these files as I can't imagine it uses them other than as a lookup.

The winding order issue is still prevelant in this version and thus the models are rendered two sided.
The winding order in mgs3 mdl files issue is still prevelant in this version and thus those models are rendered two sided.

### Latest Changes
- Added support for viewing MGS2 KMS, EVM models and MAR motions
- Added preliminary support for viewing Master Collection CMDL models

## Usage.

Drag the dll file into the plugins folder of your Noesis folder, and find and locate the MDL file you wish to view. This program assumes models are in a mdl folder in assets, and textures are in a tri file in assets. If textures are available they will be applied automatically, this may take a while.

If you also have my mgs3 noesis plugin you may have to swap it out to use this. It's a rather inelgant solution than what I'd usually make, but at this point I don't care, the leechers can do that much at least.
If you also have my mgs3 or mgs2 noesis plugins you may have to swap it out to use this. It's a rather inelgant solution than what I'd usually make, but at this point I don't care, the leechers can do that much at least.

There is only one option which when checked allows you to load Mtar animation files for the model if it has bones.
There is only one option which when checked allows you to load Mtar or Mar animation files for the model if it has bones.

##### Prompt for Motion Archive
This option will allow you to choose an Mtar file after the model has loaded. This allows you to view animations provided the bones match.
This option will allow you to choose an Mtar or Mar file after the model has loaded. This allows you to view animations provided the bones match.
44 changes: 44 additions & 0 deletions bone.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#pragma once
#include "mgs/common/util.h"
#include "mgs/model/kms/kms.h"
#include "mgs/model/evm/evm.h"
#include "mgs/model/mdl/mdl.h"
#include "noesis/plugin/pluginshare.h"

Expand Down Expand Up @@ -28,6 +30,48 @@ modelBone_t* bindBones(MdlBone* bones, int numBones, noeRAPI_t* rapi) {
noeBones[i].eData.parent = &noeBones[bones[i].parent];
}

rapi->rpgSetExData_Bones(noeBones, numBones);
return noeBones;
}

inline
modelBone_t* bindKMSBones(KmsMesh* mesh, int numBones, noeRAPI_t* rapi)
{
modelBone_t* noeBones = rapi->Noesis_AllocBones(numBones);

for (int i = 0; i < numBones; i++)
{
RichVec3 bonePosV3 = { mesh[i].pos.x, mesh[i].pos.y, mesh[i].pos.z };
memcpy_s(&noeBones[i].mat.o, 12, &bonePosV3, 12);

if (mesh[i].parent > -1)
{
noeBones[i].eData.parent = &noeBones[mesh[i].parent];
RichMat43 cMat(noeBones[i].mat);
RichMat43 pMat(noeBones[i].eData.parent->mat);
noeBones[i].mat = (cMat * pMat).m;
}

}

rapi->rpgSetExData_Bones(noeBones, numBones);
return noeBones;
}

inline
modelBone_t* bindEVMBones(EvmBone* bones, int numBones, noeRAPI_t* rapi)
{
modelBone_t* noeBones = rapi->Noesis_AllocBones(numBones);

for (int i = 0; i < numBones; i++)
{
RichVec3 bonePosV3 = { bones[i].worldPos.x, bones[i].worldPos.y, bones[i].worldPos.z };
memcpy_s(&noeBones[i].mat.o, 12, &bonePosV3, 12);

if (bones[i].parent > -1)
noeBones[i].eData.parent = &noeBones[bones[i].parent];
}

rapi->rpgSetExData_Bones(noeBones, numBones);
return noeBones;
}
210 changes: 196 additions & 14 deletions mesh.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#pragma once
#include "mat.h"
#include <vector>
#include "mgs/model/kms/kms.h"
#include "mgs/model/evm/evm.h"
#include "mgs/model/mdl/mdl.h"

inline
Expand Down Expand Up @@ -64,55 +66,84 @@ void bindFace(int16_t* fvf, uint16_t fc, std::vector<uint16_t>& faceBuffer, bool
}

inline
void bindUV(int16_t* fvf, uint32_t flag, std::vector<float>& uvBuffer, std::vector<float>& uvBuffer2, std::vector<float>& uvBuffer3) {
void bindUV(int16_t* fvf, uint32_t flag, std::vector<float>& uvBuffer, std::vector<float>& uvBuffer2, std::vector<float>& uvBuffer3)
{
float scale = 1.0f / 4096.0f;

if (flag & 0x40) {
if (flag & 0x40)
{
uvBuffer.push_back(fvf[8] * scale);
uvBuffer.push_back(fvf[9] * scale);
}

if (flag & 0x80) {
if (flag & 0x80)
{
uvBuffer2.push_back(fvf[12] * scale);
uvBuffer2.push_back(fvf[13] * scale);
}

if (flag & 0x100) {
if (flag & 0x100)
{
uvBuffer3.push_back(fvf[16] * scale);
uvBuffer3.push_back(fvf[17] * scale);
}
}

inline
void bindSkin(int16_t* fvf, uint16_t numWeights, uint8_t* skinningTable, std::vector<float>& weightBuffer, std::vector<uint8_t>& boneBuffer, bool isExtended) {
if (isExtended) {
void bindUV(int16_t* uvs, std::vector<float>& uvBuffer) {
float scale = 1.0f / 4096.0f;
uvBuffer.push_back(uvs[0] * scale);
uvBuffer.push_back(uvs[1] * scale);
}

for (int x = 7; x < 4 + (numWeights * 4); x += 4) {
inline
void bindSkin(int16_t* fvf, uint16_t numWeights, uint8_t* skinningTable, std::vector<float>& weightBuffer, std::vector<uint8_t>& boneBuffer, bool isExtended)
{
if (isExtended)
{
for (int x = 7; x < 4 + (numWeights * 4); x += 4)
{
int idx = fvf[x] >> 2;
boneBuffer.push_back(skinningTable[idx]);
}

for (int x = 20; x < 20 + numWeights; x++) {
for (int x = 20; x < 20 + numWeights; x++)
{
weightBuffer.push_back(fvf[x] / 4096.0);
}
}
else {
else
{
float weight = fvf[7] / 4096.0;
int idx = fvf[11] >> 2;

boneBuffer.push_back(skinningTable[0]); weightBuffer.push_back(weight);
boneBuffer.push_back(skinningTable[idx]); weightBuffer.push_back(1.0 - weight);

}
}

inline
void setOrigin(MdlMesh* mesh, noeRAPI_t* rapi) {
void setOrigin(MdlMesh* mesh, noeRAPI_t* rapi)
{
modelMatrix_t t = g_identityMatrix;
g_mfn->Math_VecCopy(&mesh->min.x, t.o);
rapi->rpgSetTransform(&t);
}

inline
void setOrigin(KmsMesh* mesh, modelBone_t* noeBone, noeRAPI_t* rapi)
{
modelMatrix_t t = g_identityMatrix;
g_mfn->Math_VecCopy(noeBone->mat.o, t.o);
rapi->rpgSetTransform(&t);
}

inline
void resetWinding(int16_t* index, bool& flip)
{
if (index[3] & 0x8000) flip = 1;
}

inline
void bindMesh(MdlMesh* mesh, BYTE* fileBuffer, noeRAPI_t* rapi, CArrayList<noesisMaterial_t*>& matList, CArrayList<noesisTex_t*>& texList, bool isMDB, bool isMDC1)
{
Expand Down Expand Up @@ -153,13 +184,13 @@ void bindMesh(MdlMesh* mesh, BYTE* fileBuffer, noeRAPI_t* rapi, CArrayList<noesi

uint32_t textures[3] = { vertDefAB.textureStrcode, vertDefAB.texture2Strcode, vertDefAB.texture3Strcode };
bindMat(textures, fileBuffer, rapi, matList, texList);

if (!isMDB)
{
rapi->rpgBindBoneWeightBuffer(&weightBuffer[0], RPGEODATA_FLOAT, vertDefAB.numWeights * 4, vertDefAB.numWeights);
rapi->rpgBindBoneIndexBuffer(&boneBuffer[0], RPGEODATA_UBYTE, vertDefAB.numWeights, vertDefAB.numWeights);
}

if (!uvBuffer.empty()) rapi->rpgBindUV1BufferSafe(&uvBuffer[0], RPGEODATA_FLOAT, 8, uvBuffer.size() * 4);
if (!uvBuffer2.empty()) rapi->rpgBindUV2BufferSafe(&uvBuffer2[0], RPGEODATA_FLOAT, 8, uvBuffer2.size() * 4);
if (!uvBuffer3.empty()) rapi->rpgBindUVXBufferSafe(&uvBuffer3[0], RPGEODATA_FLOAT, 8, 2, 2, uvBuffer3.size() * 4);
Expand All @@ -169,4 +200,155 @@ void bindMesh(MdlMesh* mesh, BYTE* fileBuffer, noeRAPI_t* rapi, CArrayList<noesi
rapi->rpgCommitTriangles(&faceBuffer[0], RPGEODATA_USHORT, faceBuffer.size(), RPGEO_TRIANGLE, 0);
rapi->rpgClearBufferBinds();
}
}
}

inline
void bindKMSNormal(int16_t* normals, std::vector<float>& normalBuffer)
{
float scale = 1.0f / 4096.0f;
normalBuffer.push_back(normals[0] * scale);
normalBuffer.push_back(normals[1] * scale);
normalBuffer.push_back(normals[2] * scale);
}

inline
void bindKMSSkin(int16_t* vertices, KmsMesh* mesh, int meshNum, std::vector<float>& weightBuffer, std::vector<uint8_t>& boneBuffer)
{
float weight = vertices[3] / 4096.0f;
weightBuffer.push_back(weight);
boneBuffer.push_back(meshNum);

if (mesh->parent > -1)
{
weightBuffer.push_back(1.0f - weight);
boneBuffer.push_back(mesh->parent);
}
}

inline
void bindKMSMesh(KmsMesh* mesh, modelBone_t* noeBone, int meshNum, BYTE* fileBuffer, noeRAPI_t* rapi, CArrayList<noesisMaterial_t*>& matList, CArrayList<noesisTex_t*>& texList)
{
setOrigin(mesh, noeBone, rapi);

KmsVertexDefinition* vDef = (KmsVertexDefinition*)&fileBuffer[mesh->vertexDefinitionOffset];

float scale = 1.0f;

for (int i = 0; i < mesh->numVertexDefinition; i++) {
std::vector<float> vertexBuffer, normalBuffer, uvBuffer, uvBuffer2, uvBuffer3, weightBuffer;
std::vector<uint16_t> faceBuffer;
std::vector<uint8_t> boneBuffer;

int16_t* uvIndex = (int16_t*)&fileBuffer[vDef[i].uvOffset];
int16_t* uv2Index = (int16_t*)&fileBuffer[vDef[i].uv2Offset];
int16_t* uv3Index = (int16_t*)&fileBuffer[vDef[i].uv3Offset];
int16_t* vertexIndex = (int16_t*)&fileBuffer[vDef[i].vertexOffset];
int16_t* normalIndex = (int16_t*)&fileBuffer[vDef[i].normalOffset];

bool flip = 1;
for (int j = 0; j < vDef[i].numVertex; j++) {

bindVertex(vertexIndex, vertexBuffer, scale);
bindKMSNormal(normalIndex, normalBuffer);
bindFace(normalIndex, j, faceBuffer, flip);

if (vDef->uvOffset) bindUV(uvIndex, uvBuffer);
if (vDef->uv2Offset) bindUV(uv2Index, uvBuffer2);
if (vDef->uv3Offset) bindUV(uv3Index, uvBuffer3);

resetWinding(normalIndex, flip);
bindKMSSkin(vertexIndex, mesh, meshNum, weightBuffer, boneBuffer);

uvIndex += 2;
uv2Index += 2;
uv3Index += 2;
vertexIndex += 4;
normalIndex += 4;
}

uint32_t textures[3] = { vDef[i].textureStrcode, vDef[i].texture2Strcode, vDef[i].texture3Strcode };
bindMat(textures, fileBuffer, rapi, matList, texList);

int numWeight = 1 + (mesh->parent > -1);

if (!uvBuffer.empty()) rapi->rpgBindUV1BufferSafe(&uvBuffer[0], RPGEODATA_FLOAT, 8, uvBuffer.size() * 4);
if (!uvBuffer2.empty()) rapi->rpgBindUV2BufferSafe(&uvBuffer2[0], RPGEODATA_FLOAT, 8, uvBuffer2.size() * 4);
if (!uvBuffer3.empty()) rapi->rpgBindUVXBufferSafe(&uvBuffer3[0], RPGEODATA_FLOAT, 8, 2, 2, uvBuffer3.size() * 4);

rapi->rpgBindBoneIndexBuffer(&boneBuffer[0], RPGEODATA_UBYTE, numWeight, numWeight);
rapi->rpgBindBoneWeightBuffer(&weightBuffer[0], RPGEODATA_FLOAT, numWeight * 4, numWeight);
rapi->rpgBindNormalBufferSafe(&normalBuffer[0], RPGEODATA_FLOAT, 12, normalBuffer.size() * 4);
rapi->rpgBindPositionBufferSafe(&vertexBuffer[0], RPGEODATA_FLOAT, 12, vertexBuffer.size() * 4);
rapi->rpgCommitTriangles(&faceBuffer[0], RPGEODATA_USHORT, faceBuffer.size(), RPGEO_TRIANGLE, 0);

rapi->rpgClearBufferBinds();
}

}

inline
void bindEVMSkin(uint8_t* skin, uint8_t* skinningTable, int32_t numSkin, std::vector<float>& weightBuffer, std::vector<uint8_t>& boneBuffer) {
for (int i = 0; i < numSkin; i++) {
weightBuffer.push_back(skin[i] / 128.0f);
}

for (int i = 4; i < 4 + numSkin; i++) {
int idx = skin[i] >> 2;
boneBuffer.push_back(skinningTable[idx]);
}
}

inline
void bindEVMMesh(EvmVertexDefinition* vDef, BYTE* fileBuffer, noeRAPI_t* rapi, CArrayList<noesisMaterial_t*>& matList, CArrayList<noesisTex_t*>& texList) {
std::vector<float> vertexBuffer, normalBuffer, uvBuffer, uvBuffer2, uvBuffer3, weightBuffer;
std::vector<uint16_t> faceBuffer;
std::vector<uint8_t> boneBuffer;

int16_t* uvIndex = (int16_t*)&fileBuffer[vDef->uvOffset];
int16_t* uv2Index = (int16_t*)&fileBuffer[vDef->uv2Offset];
int16_t* uv3Index = (int16_t*)&fileBuffer[vDef->uv3Offset];
uint8_t* weightIndex = (uint8_t*)&fileBuffer[vDef->weightOffset];
int16_t* vertexIndex = (int16_t*)&fileBuffer[vDef->vertexOffset];
int16_t* normalIndex = (int16_t*)&fileBuffer[vDef->normalOffset];

float scale = 1.0f / 16.0f;

bool flip = 1;
for (int i = 0; i < vDef->numVertex; i++)
{

if (vDef->vertexOffset) bindVertex(vertexIndex, vertexBuffer, scale);
if (vDef->vertexOffset) bindFace(vertexIndex, i, faceBuffer, flip);
if (vDef->normalOffset) bindKMSNormal(normalIndex, normalBuffer);

if (vDef->uvOffset) bindUV(uvIndex, uvBuffer);
if (vDef->uv2Offset) bindUV(uv2Index, uvBuffer2);
if (vDef->uv3Offset) bindUV(uv3Index, uvBuffer3);

resetWinding(vertexIndex, flip);
if (vDef->weightOffset) bindEVMSkin(weightIndex, vDef->skinningTable, vDef->numSkin, weightBuffer, boneBuffer);

uvIndex += 4;
uv2Index += 4;
uv3Index += 4;
weightIndex += 8;
vertexIndex += 4;
normalIndex += 4;
}

uint32_t textures[3] = { vDef->textureStrcode, vDef->texture2Strcode, vDef->texture3Strcode };
bindMat(textures, fileBuffer, rapi, matList, texList);

if (!uvBuffer.empty()) rapi->rpgBindUV1BufferSafe(&uvBuffer[0], RPGEODATA_FLOAT, 8, uvBuffer.size() * 4);
if (!uvBuffer2.empty()) rapi->rpgBindUV2BufferSafe(&uvBuffer2[0], RPGEODATA_FLOAT, 8, uvBuffer2.size() * 4);
if (!uvBuffer3.empty()) rapi->rpgBindUVXBufferSafe(&uvBuffer3[0], RPGEODATA_FLOAT, 8, 2, 2, uvBuffer3.size() * 4);
if (!normalBuffer.empty()) rapi->rpgBindNormalBufferSafe(&normalBuffer[0], RPGEODATA_FLOAT, 12, normalBuffer.size() * 4);
if (!vertexBuffer.empty()) rapi->rpgBindPositionBufferSafe(&vertexBuffer[0], RPGEODATA_FLOAT, 12, vertexBuffer.size() * 4);

if (!weightBuffer.empty()) rapi->rpgBindBoneIndexBuffer(&boneBuffer[0], RPGEODATA_UBYTE, vDef->numSkin, vDef->numSkin);
if (!weightBuffer.empty()) rapi->rpgBindBoneWeightBuffer(&weightBuffer[0], RPGEODATA_FLOAT, vDef->numSkin * 4, vDef->numSkin);

if (!faceBuffer.empty()) rapi->rpgCommitTriangles(&faceBuffer[0], RPGEODATA_USHORT, faceBuffer.size(), RPGEO_TRIANGLE, 0);
rapi->rpgClearBufferBinds();
}

Loading

0 comments on commit d62bdfa

Please sign in to comment.