Recent Topics

Ads

Charmesh to OBJ

Talk about the development of the emulator and related services.
Check out new and upcoming features to be implemented onto the server.
Forum rules
Before posting on this forum, be sure to read the Terms of Use
User avatar
Jalmood
Posts: 6

Re: Charmesh to OBJ

Post#21 » Mon Mar 02, 2015 7:54 pm

Absolutely amazing, Thanks mate for that, would you be interested in creating a parser for the .xac and .xsm files? seems everyone in the modding community for other games are looking for a parser for those format, i would really love to create some nice fighting cgi movies of war characters in 3ds max. you might find these links helpful. Cheers!

.xac format
https://dl.dropboxusercontent.com/u/60681258/xr/xac.txt

.xsm format
https://dl.dropboxusercontent.com/u/60681258/xr/xsm.txt

Ads
Londo
Posts: 217

Re: Charmesh to OBJ

Post#22 » Wed Mar 04, 2015 8:52 pm

They absolutely are helpful! I will try to plug in skeleton and animation information into current parser I wrote.

Getting hands on GameBryo from 2006 would be helpful also. Mythic's asset pipeline was 3d studio -> GameBryo->MYP Packer.

Still have some issues with mythic textures. Its a custom mythic wrapper around DXT1 and DXT5 dds compression formats.

I will change my parser to export FBX instead (includes bone animation). Then it will be just a matter of implementing FBX->GEOM/XAC/XSM and updating *.csv files in /data/gamedata in MYP to add new assets to the game.

Dictator
Posts: 13

Re: Charmesh to OBJ

Post#23 » Sat Mar 07, 2015 3:50 pm

Londo, i sent you a message private! :)

w4kfu
Posts: 1

Re: Charmesh to OBJ

Post#24 » Mon Apr 13, 2015 7:19 pm

Interesting thread!

I have been working on the same stuff.

Here are my notes about .geom file format (I didn't finish to figer out everything).

Everything is stored in little-endian.

The header is stored in 0x20 bytes, and have the following specifications:

Header spec:

Code: Select all

+ 0x00 :       signature            [DWORD]         // 0x464c7367 'gsLF'
+ 0x04 :       version              [DWORD]
+ 0x08 :       file_size            [DWORD]
+ 0x0C :       unk_dword_00         [DWORD]         // CRC?
+ 0x10 :       nb_bones             [DWORD]
+ 0x14 :       offset_bones         [DWORD]
+ 0x18 :       nb_meshes            [DWORD]
+ 0x1C :       offset_meshes        [DWORD]
Meshes data are stored in 0x20 bytes (offset of meshes is from the beginning of the file).

Mesh data spec:

Code: Select all

+ 0x00 :       unk_word_00          [WORD]
+ 0x02 :       nb_vertices          [WORD]
+ 0x04 :       offset_vertices      [DWORD]
+ 0x08 :       nb_triangles         [DWORD]
+ 0x0C :       offset_triangles     [DWORD]
+ 0x10 :       unk_dword_00         [DWORD]
+ 0x14 :       unk_dword_01         [DWORD]
+ 0x18 :       unk_dword_02         [DWORD]
+ 0x1C :       unk_dword_03         [DWORD]
Vertex data are stored in 0x20 bytes (offset of vertices are computed from the actual offset of the actual meshes, FileHeader->offset_meshes + Meshes->offset_vertices + num_meshes * sizeof (Meshes)):

Vertex data spec:

Code: Select all

+ 0x00 :       position_x           [DWORD]
+ 0x04 :       position_y           [DWORD]
+ 0x08 :       position_z           [DWORD]
+ 0x0C :       normal_x             [DWORD]
+ 0x10 :       normal_y             [DWORD]
+ 0x14 :       normal_z             [DWORD]
+ 0x18 :       texture_u            [DWORD]
+ 0x1C :       texture_v            [DWORD]
And triangles are stored in 0x6 bytes (offset of triangles are computed from the actual offset of the actual meshes, FileHeader->offset_meshes + Meshes->offset_triangles + num_meshes * sizeof (Meshes)):

Triangle data spec:

Code: Select all

+ 0x00 :       vertex_indice_1      [WORD]
+ 0x02 :       vertex_indice_2      [WORD]
+ 0x04 :       vertex_indice_3      [WORD]
With all thoses informations it is really easy to output (for testing purpose) WaveFront obj file (http://en.wikipedia.org/wiki/Wavefront_.obj_file), bender (http://www.blender.org/) can import them!

I wrote a parser in python for .geom files and another script to convert the data to .obj.
https://github.com/w4kfu/waronline_fun/ ... z/art/geom, Enjoy!

For the actual bones data (maybe 0x6C bytes long), the only field I have identified so far, is the first one, which is an offset to the name of the bone.

If you have more informations regarding .geom, I'm quite interested!

Londo
Posts: 217

Re: Charmesh to OBJ

Post#25 » Mon Apr 13, 2015 7:40 pm

I load the model (using irrlicht) like this

Code: Select all

     MemoryStream ms = new MemoryStream(File.ReadAllBytes(@"G:\WarExtract\assetdb\charmesh\fg.0.0.cmm_base_beard_b_04.geom"));

            GeomReader reader = new GeomReader();
            reader.Load(ms);

            foreach (Mesh mesh in m.Meshes)
            {
                MeshSceneNode meshSceneNode = null;
                MeshBuffer mb = MeshBuffer.Create(VertexType.Standard, IndexType._16Bit);
                List<Vertex3D> vlist = new List<Vertex3D>();
                List<ushort> indexList = new List<ushort>();
                foreach (p3 vertextInfo in mesh.VertexData.VertexList)
                {
                    float x = vertextInfo.x;
                    float y = vertextInfo.y;
                    float z = vertextInfo.z;

                    float u = vertextInfo.u;
                    float v = vertextInfo.v;
                    float w = 0;// vertextInfo.h;

                    Vertex3D vertex = new Vertex3D(x, y, z);
                    vertex.Color = Color.OpaqueWhite;
                    vertex.TCoords = new IrrlichtLime.Core.Vector2Df(u, v);
                    vlist.Add(vertex);

                }

                for (int i = 0; i < mesh.IndexList.Count; i++)
                {
                    indexList.Add(mesh.IndexList[i].b1);
                }

                mb.Append(vlist.ToArray(), indexList.ToArray());
            
                Mesh newMesh = Mesh.Create();
            
                newMesh.AddMeshBuffer(mb);
                meshSceneNode = device.SceneManager.AddMeshSceneNode(newMesh);


                Material irrMaterial = meshSceneNode.GetMaterial(0);
                meshSceneNode.Scale = new IrrlichtLime.Core.Vector3Df(10, 10, 10);
                meshSceneNode.SetMaterialFlag(MaterialFlag.Lighting, false);
                meshSceneNode.SetMaterialFlag(MaterialFlag.BackFaceCulling, false);
                meshSceneNode.DebugDataVisible = DebugSceneType.BBoxAll;

            }
Here are some of the parsing classing I have (From reversed engineered Machinima Studio, which has complete geom parser)

Code: Select all

 public class GeomReader
    {
        public class GEOMHeader
        {
            public string FileCode = "";
            public uint unk_c;
            public uint unk_d;
            public uint unk_e;
            public uint GeoSetCount;
            public uint GeoSetStartPos;
            public uint MeshCount;
            public uint DataStartPos;

            public void Load(BinaryReader reader)
            {
                FileCode = new String(reader.ReadChars(4));
                if (FileCode != "gsLF")
                    throw new Exception("Expected gsLF file header");

                unk_c = reader.ReadUInt32();
                unk_d = reader.ReadUInt32();
                unk_e = reader.ReadUInt32();
                GeoSetCount = reader.ReadUInt32();
                GeoSetStartPos = reader.ReadUInt32();
                MeshCount = reader.ReadUInt32();
                DataStartPos = reader.ReadUInt32();
            }
        }

        public class Mesh
        {
            public class MeshHeader
            {
                public short a;
                public ushort b;
                public uint c;
                public uint d;
                public uint e;
                public uint f;
                public uint g;
                public uint h;
                public uint i;

                public void Load(BinaryReader reader)
                {
                    a = reader.ReadInt16();
                    b= reader.ReadUInt16();
                    c = reader.ReadUInt32();
                    d = reader.ReadUInt32();
                    e = reader.ReadUInt32();
                    f = reader.ReadUInt32();
                    g = reader.ReadUInt32();
                    h = reader.ReadUInt32();
                    i = reader.ReadUInt32();
                }
            }
            public class UnkInfo1
            {
                public short a;
                public ushort b;
                public uint c;
                public void Load(BinaryReader reader)
                {
                    a = reader.ReadInt16();
                    b = reader.ReadUInt16();
                    c = reader.ReadUInt32();
                }
            }

            public class Vertex
            {
                public float z;
                public float x;
                public float y;
                public float unk1;
                public float unk2;
                public float unk3;
                public float u;
                public float v;
                public void Load(BinaryReader reader)
                {
                    z = reader.ReadSingle();
                    x = reader.ReadSingle();
                    y = reader.ReadSingle();
                    unk1 = reader.ReadSingle();
                    unk2 = reader.ReadSingle();
                    unk3 = reader.ReadSingle();
                    u = reader.ReadSingle();
                    v = reader.ReadSingle();
                }
            }


            public MeshHeader Header = new MeshHeader();
            public List<Vertex> VertexList = new List<Vertex>();
            public List<a12> IndexList = new List<a12>();
            public UnkInfo1 Unk1 = new UnkInfo1();
            public List<aqn> e = new List<aqn>();

            public void Load(BinaryReader reader)
            {
                Header.Load(reader);
            }
        }

        public class GeoSetInfo
        {
            public uint Offset;
            public float[] Unk2 = new float[26];

            public void Load(BinaryReader reader)
            {
                Offset = reader.ReadUInt32();
                for (int i = 0; i < Unk2.Length; i++)
                    Unk2[i] = reader.ReadSingle();
            }
        }

        public GEOMHeader Header = new GEOMHeader();
        public List<GeoSetInfo> GetSetInfo = new List<GeoSetInfo>();
        public List<Mesh> Meshes = new List<Mesh>();

        public void Load(Stream stream)
        {
            BinaryReader reader = new BinaryReader(stream);
            Header.Load(reader);
            
            //move to geoset definitions
            stream.Seek(Header.GeoSetStartPos, SeekOrigin.Begin);

            //read geoset info
            for (int i = 0; i < Header.GeoSetCount; i++)
            {
                GeoSetInfo data = new GeoSetInfo();
                data.Load(reader);
                GetSetInfo.Add(data);
            }

            long pos = 0;
            if (GetSetInfo.Count > 0)
                pos = GetSetInfo[0].Offset;
            reader.BaseStream.Seek(pos, SeekOrigin.Begin);


            for (int i = 0; i < Header.MeshCount; i++)
            {
                Mesh mesh = new Mesh();
                mesh.Load(reader);
                Meshes.Add(mesh);
            }

            for (int i = 0; i < Header.MeshCount; i++)
            {
                Mesh mesh = Meshes[i];
                if (mesh.Header.f != 0)
                {
                    mesh.Unk1.Load(reader);
                }
            }

            for (int i = 0; i < this.Header.MeshCount; i++)
            {
                Mesh mesh = Meshes[i];
                long offset = i * 32;
                pos = (this.Header.GeoSetStartPos + mesh.Header.c) + offset;
                reader.BaseStream.Seek(pos, SeekOrigin.Begin);

                Mesh.Vertex vertex = new Mesh.Vertex();
                vertex.Load(reader);
                mesh.VertexList.Add(vertex);

                pos = (this.Header.GeoSetStartPos + mesh.Header.e) + offset; //jump to index
                reader.BaseStream.Seek(pos, SeekOrigin.Begin);

                for (int n = 0; n < (mesh.Header.d * 3); n++)
                {
                    a12 a = new a12();
                    a.b(reader);
                    mesh.IndexList.Add(a);
                }
                for (int num9 = 0; num9 < mesh.d.b; num9++)
                {
                    aqn aqn = new aqn();
                    aqn.b(reader);
                    mesh.e.Add(aqn);
                }
            }

        }
    }

User avatar
Jalmood
Posts: 6

Re: Charmesh to OBJ

Post#26 » Thu Apr 16, 2015 6:26 am

Awesome work guys, i hope you figure out the the various game formates, i think with machinima studio, there is no need for a .geom parser, i hope you could work on a diffuse parser and bones with animation, shame to see all these lovely models go to waste. so much talent was invested in creating them. here is a little video i made , just messing with the models in 3ds max. i think there is good possibilty to mod the game given that we have a convertor to geom formate. there are plenty of new unused models and armor sets that may be implemented in the game.


Click here to watch on YouTube

Londo
Posts: 217

Re: Charmesh to OBJ

Post#27 » Thu Apr 16, 2015 6:54 am

Jalmood wrote:Awesome work guys, i hope you figure out the the various game formates, i think with machinima studio, there is no need for a .geom parser, i hope you could work on a diffuse parser and bones with animation, shame to see all these lovely models go to waste. so much talent was invested in creating them. here is a little video i made , just messing with the models in 3ds max. i think there is good possibilty to mod the game given that we have a convertor to geom formate. there are plenty of new unused models and armor sets that may be implemented in the game.


Click here to watch on YouTube
Nice work!.

It would be nice to add new assets to the game eventually. My more immediate issue is line of sight calculations on the server for AOE damage. I have it almost working (using gpu on the server to load world geometry). Afterwards I can simply cast a ray from caster to target and see if any world geometry is hit.

Here is TOVL loaded into video memory on server
Image

Image

slor
Posts: 1

Re: Charmesh to OBJ

Post#28 » Mon Apr 04, 2016 5:26 am

Londo have you made your tool available? on Git or such. I dont play WAR anymore, but I'm modding another game and would love to not let these marvelous models go to waste.

Ads
User avatar
Toldavf
Posts: 1586

Re: Charmesh to OBJ

Post#29 » Mon Apr 04, 2016 5:32 am

Well now this is quiet intriguing. Amazing work can't wait to see the fruit it bears.
Khorlar, Thorvold, Sjohgar, Anareth, Toldavf, Hartwin, Gotrin and others -_-

Image

User avatar
Glorian
Posts: 4976

Re: Charmesh to OBJ

Post#30 » Mon Apr 04, 2016 7:44 am

Great Stuff! ;)
The apearance junky in me just jumped a meter into the air.

Who is online

Users browsing this forum: No registered users and 11 guests