Jump to content

MESSIAH MeshToOBJ [*.mesh/*.model]


Go to solution Solved by Durik256,

Recommended Posts

  • Durik256 changed the title to MESSIAH MeshToOBJ [*.mesh/*.model]
  • 5 months later...
Posted
On 11/10/2023 at 12:28 AM, Durik256 said:

MESSIAH

game support : +(Diablo Immortal, Ace Racing, Fantasy Westward Journey, 1 unk)

\\\\\\\\\\\MeshToOBJ.exe
Link on github: MeshToOBJ.exe

c8eaab16cae0377395088a2ee550fea7.png.79161fdce08e1fdf6e80edc6d50e503a.png

\\\\\\\\\\\Noesis
for Noesis for *.mesh/*.model
link on github: fmt_mesh_diablo.py

14a0b3c67be3a938f393eff98a50adc1(1).png.7b8823c89f53bb9487412dd3ad0ab027.png

How did you obtain these models? I tried parsing the visible strings, MESSIAH, Compressed_P_C_N_UV1_TB, W4B_I4B, but I couldn't get any clue. The parsed vertex data, xyz, all contain abnormal values. Could you please share how you successfully read and converted the .mesh file to .obj? Thank you very much!

I know mesh to obj can read the models, but I want to know the method so that I can integrate it into my Python script for execution. I'm researching ACE Racing.

mesh.rar

  • Engineers
Posted (edited)
2 hours ago, Rinorsi said:

How did you obtain these models? I tried parsing the visible strings, MESSIAH, Compressed_P_C_N_UV1_TB, W4B_I4B ...

Compressed:
P - (12bytes) as 3float
C -  (4bytes)
N - (4bytes)
UV1/2 - (4bytes) as 2HalfFloat
TB - (8bytes)
Other:
template: [target][count][type]
sample: W4B = target:Weight, count:4, type:Byte

On my githab there is a plugin(python) for diablo, it also uses MESSIAH. (you can see the implementation there)

Edited by Durik256
  • Like 1
Posted

Thank you, I spent the whole day trying to understand,

but even after modifying the Noesis plugin (fmt_mesh_diablo.py) according to the data you provided, it still doesn't seem to work and causes Noesis to crash.

Or perhaps, how can I batch convert .mesh files to .obj using Python? Besides models, AceRacing also includes many FX or particles that are compressed with LZMA.

I want to use a Python script to batch convert models to precisely extract the parts that belong to models from all the decompressed files.

If you can assist me, I would be very grateful.

44a58c7279b47747f44d7a30dd424b0d.png

AceRacing-Mesh.rar

  • Engineers
  • Solution
Posted (edited)
On 7/27/2024 at 4:15 PM, Rinorsi said:

Noesis plugin (fmt_mesh_diablo.py)

 

why did you remove (author's name) from the script?

MeshToOBJ supports batch processing.

i made plugin for Noesis for AceRacing fmt_mesh_AceRacer.py

*(Later I’ll make a universal for everyone messiah)

Edited by Durik256
  • Like 1
Posted (edited)
13 hours ago, Durik256 said:

why did you remove (author's name) from the script?

MeshToOBJ supports batch processing.

i made plugin for Noesis for AceRacing fmt_mesh_AceRacing.py

*(Later I’ll make a universal for everyone messiah)

thx!!!
I apologize, I use chatgpt so i dont know why it remove author's name from the script.
……

I have encountered some issues that seem to be unable to execute properly, so I have modified the plugin to ensure that it can now execute properly.

I fixed some bugs in fmt_mesh_AceRacing.py, and now it can correctly read .messiah model information (perhaps the data differs between the Chinese version and the global version; I have only tested models obtained from the Chinese version with .MESSIAH, which can be read correctly).

Noesis-Plugins/fmt_mesh_AceRacing.py at master · Durik256/Noesis-Plugins (github.com)

 

Fixes include:

Changed the file header check in the noepyCheckType function to match the first 8 bytes (.MESSIAH) instead of bytes 1 to 8.

Replaced bs.read with bs.readBytes in the noepyLoadModel function to correctly handle the reading of binary data. This ensures the read operations correctly interpret the data formats.

Corrected the method name for binding UV buffers from rapi.rpgBindUVXBufferOfs to rapi.rpgBindUV1BufferOfs, which is the correct method available in the Noesis API for binding UV buffers.

 

c3a9dbf7333c8cd9bf7025e892c7051d.png

 

Edited by Rinorsi
  • Engineers
Posted (edited)
3 hours ago, Rinorsi said:

Fixes include:

I'll duplicate the answer from Github:

1)magic check is correct. (it doesn’t matter how many bytes you check, it’s just to check the validity of the file).
I just skip the first dot (I don’t remember why I did this, maybe in other MESSIAH games there was another symbol instead of a dot)

2)reading bytes through bs.read() is the same as through bs.readBytes(), this can be seen in the Noesis code
```
	def read(self, fmtStr):
		if isinstance(fmtStr, str):
			self.toUnpacker(); r = noeSuper(self).read(fmtStr); self.fromUnpacker(); return r
		else:
			return self.readBytes(fmtStr) #assume byte count
```

3)rpgBindUVXBufferOfs is used to set the UVS to the desired channel. It is not necessary to use an explicit method for the desired channel.
especially when there are several channels or more than 2
from Noesis documentation:
```
{"rpgBindUVXBuffer", RPGBindUVXBuffer, METH_VARARGS, "binds uvx buffer. (Oiiii)"}, //args=bytes for uv2's, dataType, stride, uv index, uv elem count
By replacing rpgBindUVXBufferOfs with rpgBindUV1BufferOfs, you broke the code, since some models have several UVs channels. you always set UVs to the zero channel. that is, if there are multiple UVs you will only get the last one.
```

so I think all your changes are unnecessary
the only thing that can be used is the magic check.
*(send me the file that gave the error)

The plugin is absolutely functional and does not need any corrections

-1.png.17e43a399c623c70557e29fa4917b64a.png

Edited by Durik256
  • Like 1
Posted
5 minutes ago, Durik256 said:

I'll duplicate the answer from Github:


magic check is correct. (it doesn’t matter how many bytes you check, it’s just to check the validity of the file).
I just skip the first dot (I don’t remember why I did this, maybe in other MESSIAH games there was another symbol instead of a dot)
2)
reading bytes through bs.read() is the same as through bs.readBytes(), this can be seen in the Noesis code

	def read(self, fmtStr):
		if isinstance(fmtStr, str):
			self.toUnpacker(); r = noeSuper(self).read(fmtStr); self.fromUnpacker(); return r
		else:
			return self.readBytes(fmtStr) #assume byte count
rpgBindUVXBufferOfs is used to set the UVS to the desired channel. It is not necessary to use an explicit method for the desired channel.
especially when there are several channels or more than 2
from Noesis documentation:

{"rpgBindUVXBuffer", RPGBindUVXBuffer, METH_VARARGS, "binds uvx buffer. (Oiiii)"}, //args=bytes for uv2's, dataType, stride, uv index, uv elem count
By replacing rpgBindUVXBufferOfs with rpgBindUV1BufferOfs, you broke the code, since some models have several UVs channels. you always set UVs to the zero channel. that is, if there are multiple UVs you will only get the last one.

so I think all your changes are unnecessary
the only thing that can be used is the magic check.
*(send me the file that gave the error)

该插件绝对功能强大,不需要任何更正

-1.png.17e43a399c623c70557e29fa4917b64a.png

Okay, I'm not quite sure what the problem might be, maybe it's a version issue?
Anyway, I sincerely thank you for your help!

  • Like 1
  • 1 year later...
  • 2 weeks later...
Posted (edited)
On 1/19/2026 at 4:47 PM, monowire said:

Could you add support to Where Winds Meet?

1087182900_.png.b7f702d330db84c1b4726ee4ee95bed8.png

After testing, meshtoobj.exe supports WWM models

Edited by Kizurin
  • 3 weeks later...
  • Members
Posted
On 1/28/2026 at 10:10 PM, Kizurin said:

After testing, meshtoobj.exe supports WWM models

From what i know, not entirely. Because WMM's .mesh has its differences from other games .mesh. The program will convert a lot of mesh to obj, but also a lot will fail, because the mesh support is not specific to this game. So that is what i was talking about, a full support to WWM's messiah .mesh or a Noesis plugin to visualize WWM .mesh.

image.png.89c00225893103de27463948514da8c8.png

  • Like 1
  • Supporter
Posted
On 2/19/2026 at 6:03 PM, monowire said:

From what i know, not entirely. Because WMM's .mesh has its differences from other games .mesh. The program will convert a lot of mesh to obj, but also a lot will fail, because the mesh support is not specific to this game. So that is what i was talking about, a full support to WWM's messiah .mesh or a Noesis plugin to visualize WWM .mesh.

image.png.89c00225893103de27463948514da8c8.png

I've looked at a few meshes that won't load and I can't explain what the problem is, but it seems like the indexes and vertex data aren't working properly

First we should understand the basic structure of the grid of this game

The file header includes

8-byte magic number (.MESSIAH)

4 bytes padding

4 bytes data length

2 byte identifier - normal is 0, this game is 2

2-byte subgrid number

4-byte vertex number

4-byte index number

4 identifiers

Each identifier has a length of 2 bytes

Bounding box - 40 bytes

Subgrid data - 16 bytes

 

The data area includes

Index data - each index takes two bytes

After the index is the vertex data. The vertex determines how many bytes a vertex data uses according to the structure given by the descriptor.

There is 52 bytes of bounding box data at the end of the file

 

Take this file example I provided

Total file length: 8407

Number of subgrids: 1

Vertex: 378

Index: 378

File metadata occupies 122 bytes, followed by index data. If parsed by two bytes per index, the index length occupies 756 bytes.

Four descriptors, two of which have valid vertex data

First descriptor: P3F_N4B_T2F

Contains 12 bytes for position, 4 bytes for normal, and 8 bytes for UV

378*24=9072 bytes

Second descriptor: T4H_B4H

Contains 8 bytes for tangents and 8 bytes for side tangents

378*16=6048 bytes

End of file: 52 bytes

122+756+9072+6048+52=16050

Almost half of the data is missing

653232324553ccbd.zip

  • Like 1
  • Engineers
Posted (edited)
2 hours ago, wq223 said:

The file header includes

8-byte magic number (.MESSIAH)

4 bytes padding

4 bytes data length

2 byte identifier - normal is 0, this game is 2

2-byte subgrid number

4-byte vertex number

4-byte index number

4 identifiers

Each identifier has a length of 2 bytes

Bounding box - 40 bytes

Subgrid data - 16 bytes

Thanks. But without showing the header it's hard to follow. "Each identifier has a length of 2 bytes" is not fully correct, it's string lengths, not identifiers,

the strings ("descriptors") follow, as you mentioned then.

So 0B00 P3F_N4B_T2F

0700 T4H_B4H

0400 None

0400 None

The bounding box is

-0.156100 -0.068000 -0.150600
0.156100 0.068000 0.150600

(24 bytes and 4 floats subgrid)

...

btw, what is the "subgrid" exactly and is the attached file compressed?

(Giving the start addresses of blocks of your file sample could help a lot.)

 

Messiah_header.png

Edited by shak-otay
  • Supporter
Posted
1 hour ago, shak-otay said:

Thanks. But without showing the header it's hard to follow. "Each identifier has a length of 2 bytes" is not fully correct, it's string lengths, not identifiers,

the strings ("descriptors") follow, as you mentioned then.

So 0B00 P3F_N4B_T2F

0700 T4H_B4H

0400 None

0400 None

The bounding box is

-0.156100 -0.068000 -0.150600
0.156100 0.068000 0.150600

(24 bytes and 4 floats subgrid)

...

btw, what is the "subgrid" exactly and is the attached file compressed?

(Giving the start addresses of blocks of your file sample could help a lot.)

 

Messiah_header.png

It should be a problem caused by translation

Here the number of subgrids is determined by the 2 bytes after the flag. After the bounding box data is the information of each subgrid. Normally, each subgrid information is 16 bytes. Multiple subgrids mean that there are multiple repeated 16 bytes. These 16 bytes are

4-byte starting index

4-byte index number

4-byte starting vertex

4-byte vertex number

The subgrids will eventually be merged into one grid, and this file only has one grid

0200-logo/0100-1 grid

It seems that many meshes in this game have this anomaly. The index and vertex data are completely larger than the entire file. It does look like it is compressed, but it doesn't feel like it.

  • Like 1
  • Supporter
Posted
1 hour ago, shak-otay said:

Thanks! Could you give the start address of the first vertex block in your sample? (don't have the time to trick around)

I'm also not sure if the vertex position is normal. If the file is normal, the vertex should start around 878 bytes.

But I think the index and vertex data are compressed

  • Engineers
Posted (edited)

878 bytes, so vertex start at 0x36E? That looks random.

Anyways the descriptor tell that floats (F) and halffloats (H) are used. But I couldn't seem to find any of them.

btw, the data starting from 0x1100 appears to be shifted by one byte each 16 bytes block:

messsiah.png

edit: although shorts are not mentioned I used them and got this (not a mesh) :

v 65.636719 0.246094 -49.625000
v -43.585938 -123.339844 28.015625
v -43.585938 -123.339844 28.015625
v -43.585938 -123.339844 28.015625
v -43.585938 -123.339844 28.015625
v 72.289062 0.750000 30.734375
v 72.289062 0.750000 30.734375
v 72.289062 0.750000 30.734375
v 72.289062 0.750000 30.734375
v -43.585938 -123.339844 28.015625
v -43.585938 -123.339844 28.015625
v 72.289062 0.750000 30.734375
v 72.289062 0.750000 30.734375
v 72.289062 70.750000 26.613281
v 72.300781 70.750000 26.613281
v 72.300781 70.750000 26.613281
v 72.300781 0.750000 30.734375
v 72.289062 70.750000 26.613281
v 72.289062 70.750000 26.613281
v 72.289062 0.750000 30.734375
v 85.109375 -15.171875 77.968750
v 85.109375 -15.171875 77.968750
v 72.296875 70.750000 26.613281
v 72.300781 70.750000 26.613281
v 85.121094 -15.171875 77.968750
v 74.933594 0.957031 78.734375
v 85.121094 -15.171875 77.968750
v 74.921875 0.957031 78.734375
v 74.921875 0.957031 78.734375
v 74.921875 0.957031 78.734375
v 85.121094 -15.171875 77.968750

 

Edited by shak-otay
  • 1 month later...
  • Supporter
Posted

I added skeleton support to Where Winds Meet, I'm currently reverse engineering the animation files, it may take a long time as I'm still struggling with the compressed meshes for version 2, I won't be releasing the script for a while until it's finished

Image_1774801110418.png

Image_1774801617691.png

Image_1774801614420.png

  • Like 2
  • Supporter
Posted
7 hours ago, Durik256 said:

bandicam_2024_09_04_01_33_47_621_1_ezgif

I did this a long time ago

Well done, I will work hard to make it. The good news so far is that I have found the animation file processing function, which may be able to make up for some details I don’t know.

  • 3 weeks later...
Posted (edited)
On 3/30/2026 at 6:31 AM, Durik256 said:

bandicam_2024_09_04_01_33_47_621_1_ezgif

I did this a long time ago

Sorry for bothering again, but the other messiah game (for all times) updated their model to include skeleton anim (and possibly shapekeys or bone controls on eyes and mouth) too. is it possible to add support to them too?

decrypted samples attached ><sample.zip

Edited by Jenny1367
  • Supporter
Posted
1 hour ago, Jenny1367 said:

Sorry for bothering again, but the other messiah game (for all times) updated their model to include skeleton anim (and possibly shapekeys or bone controls on eyes and mouth) too. is it possible to add support to them too?

decrypted samples attached ><sample.zip

There are some errors in the renaming. Send me your resource library file and renaming script.

Posted
Just now, wq223 said:

There are some errors in the renaming. Send me your resource library file and renaming script.

res.zip some files (anim and base.skeleton) were already named in the resource dir btw

  • Supporter
Posted
11 minutes ago, Jenny1367 said:

res.zip some files (anim and base.skeleton) were already named in the resource dir btw

The order of model and skin is reversed, add lz4 decompression

resource.py

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...