/* Corsix and fneep's RGM Tools for Company of Heroes Copyright (C) 2007 Corsix and fneep This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* This file loads RGM files into max and also handles the GUI parts of the script. Version history: 0.1 (Oct 8th 2006): - Initial release - Loads vertex X,Y,Z and faces 0.11 (Feb 19th 2007): - Bugfix and tweaks - Loads Material IDs - Parses Texture chunks and spits out data into listener 0.12 (Feb 23th 2007): - Textures loaded 0.15 (Mar 1st 2007): - Loads meshes as seperate objects - Bugfixes and tweaks 0.151 (Mar 1st 2007): - Fixed bug where script did not load infantry models 0.152 (Mar 3rd 2007): - Minor cleanup and commented - XREF warning messages added 0.16 (Mar 18th 2007): - Loading of FOLDTRIM chunks added - Fixed bug where only first mesh chunk was loaded 0.17 (Mar 23rd 2007): - Added directory and recursive directory imports - Fixed bug where texture paths were loaded incorrectly 0.20 (May 30rd 2007): - Full vertex loading capabilities prep added */ include "Corsix_Chunky.ms" -- include Corsix's chunky library ClearListener() -- Clear the listener window to make for easier reading of data printed by script. /* Gets the filename of the ini file for this script */ fn Corsix_RGMImportIni = ( local ini_dir = getINISetting (getMAXIniFile()) "Directories" "Startup Scripts" return ini_dir + "\\Corsix_RGM_Import.ini" ) /* Handles a DATADATA chunk from an RGM file oRgm - the RGM file oChunk - the DATADATA chunk returns - nothing */ fn Corsix_RgmIntoMax_Mesh_DataData oRgm oChunk = ( fseek oRgm.fFile (oChunk.iDataPosition + 1) #seek_set -- jump to chunk /* Objects */ struct sGlobalMeshInfo -- holds global mesh data to compare to and collect from ( iVertCount = 0, aVertArray = #(), aFaceArray = #() ) struct sObjectID -- holds data for the Object identifiers ( iVertCount = 0, aVertArray = #(), aUVArray = #(), iFaceCount = 0, aFaceList = #() ) struct sObjectStruct -- holds data for a single object ( sObjectName = "", sOIDStruct = sObjectID() ) global oMeshInfo = sGlobalMeshInfo() -- holds all mesh data local iObjectCount = ReadLong oRgm.fFile #unsigned -- number of separate objects local aObjectList = #() for i=1 to iObjectCount do ( local oObject = sObjectStruct() oObject.sOIDStruct.iFaceCount = (ReadLong oRgm.fFile #unsigned)/3 -- number of faces as the verts that make them. Meshes are triangles so divide by 3 for i=1 to oObject.sOIDStruct.iFaceCount do ( local aFace = point3 0 0 0 aFace.x = (ReadShort oRgm.fFile) + 1 -- vertex ID's given as vertnumber - 1 because of the way that C++ handles arrays (the exporter is in C++). To convert to maxscript, add one. aFace.z = (ReadShort oRgm.fFile) + 1 aFace.y = (ReadShort oRgm.fFile) + 1 append oObject.sOIDStruct.aFaceList [aFace.x, aFace.y, aFace.z] ) fseek oRgm.fFile 13 #seek_cur -- skip unknown local iStrLen = ReadLong oRgm.fFile #unsigned oObject.sObjectName = Corsix_ReadStringN oRgm.fFile iStrLen append aObjectList oObject ) /* Per-Vertex Component Data */ local iComponentCount = ReadLong oRgm.fFile #unsigned struct sPerVertStruct ( iVertComponent = 0, iVectorStorageType = 0, iDataType = 0 ) local aComponentVector = #() for i=1 to iComponentCount do ( oComponent = sPerVertStruct() oComponent.iVertComponent = ReadLong oRgm.fFile #unsigned oComponent.iVectorStorageType = ReadLong oRgm.fFile #unsigned oComponent.iDataType = ReadLong oRgm.fFile #unsigned append aComponentVector oComponent ) Print aComponentVector as string /* Per-Vertex Data */ struct sVertStruct ( p3Position = point3 0 0 0, iBoneIndex = 0, iBoneWeight = 0, iNormal = 0, iBiNormal = 0, iTangent = 0, iDiffuseColour = color 0 0 0 0, iSpecularColour = color 0 0 0 0, p3UVCh1 = point3 0 0 0, p3UVCh2 = point3 0 0 0, p3UVCh3 = point3 0 0 0 ) local iVertCount = ReadLong oRgm.fFile #unsigned local iVertexSize = ReadLong oRgm.fFile #unsigned -- number of bytes taken up by each vertex on disk. oMeshInfo.iVertCount = iVertCount for i=1 to iVertCount do ( local oVertex = sVertStruct() for oComponent in aComponentVector do ( case oComponent.iVertComponent of ( /* 0 = Position */ 0: ( oVertex.p3Position.x = -(ReadFloat oRgm.fFile) oVertex.p3Position.z = ReadFloat oRgm.fFile oVertex.p3Position.y = -(ReadFloat oRgm.fFile) ) /* 1 = Bone Index */ 1: ( oVertex.iBoneIndex = ReadLong oRgm.fFile #unsigned ) /* 2 = Bone Weight */ 2: ( oVertex.iBoneWeight = ReadLong oRgm.fFile #unsigned ) /* 3 = Normal */ 3: ( oVertex.iNormal = ReadLong oRgm.fFile #unsigned ) /* 4 = BiNormal */ 4: ( oVertex.iBiNormal = ReadLong oRgm.fFile #unsigned ) /* 5 = Tangent */ 5: ( oVertex.iTangent = ReadLong oRgm.fFile #unsigned ) /* 6 = Diffuse Vertex Colour */ 6: ( oVertex.iDiffuseColour.r = ReadByte oRgm.fFile oVertex.iDiffuseColour.g = ReadByte oRgm.fFile oVertex.iDiffuseColour.b = ReadByte oRgm.fFile oVertex.iDiffuseColour.a = ReadByte oRgm.fFile ) /* 7 = Specular Vertex Colour */ 7: ( oVertex.iSpecularColour.r = ReadByte oRgm.fFile oVertex.iSpecularColour.g = ReadByte oRgm.fFile oVertex.iSpecularColour.b = ReadByte oRgm.fFile oVertex.iSpecularColour.a = ReadByte oRgm.fFile ) /* 8 = UV Channel 1 b0 = ReadByte #unsigned b1 = ReadByte #unsigned b2 = ReadByte #unsigned b3 = ReadByte #unsigned f0=(float)b0/255.f; f1=(float)b1/255.f; f2=(float)b2/255.f; f3=(float)b3/255.f; f3=1.f - f3; U = f0+(f2/32.f); V = f1+(f3/32.f); */ 8: ( case oComponent.iDataType of ( 2: ( iByte0 = (ReadByte oRgm.fFile #unsigned) as float iByte1 = (ReadByte oRgm.fFile #unsigned) as float iByte2 = (ReadByte oRgm.fFile #unsigned) as float iByte3 = (ReadByte oRgm.fFile #unsigned) as float Print ("iByte0: " + iByte0 as string) Print ("iByte1: " + iByte1 as string) Print ("iByte2: " + iByte2 as string) Print ("iByte3: " + iByte3 as string) fFloat0 = iByte0/255.0 fFloat1 = iByte1/255.0 fFloat2 = iByte2/255.0 fFloat3 = iByte3/255.0 fFloat3 = 1.0 - fFloat3 Print ("fFloat0: " + fFloat0 as string) Print ("fFloat1: " + fFloat1 as string) Print ("fFloat2: " + fFloat2 as string) Print ("fFloat3: " + fFloat3 as string) oVertex.p3UVCh1.x = fFloat0 + (fFloat2/32.0) oVertex.p3UVCh1.y = fFloat1 + (fFloat3/32.0) Print ("oVertex.p3UVCh1.x: " + oVertex.p3UVCh1.x as string) Print ("oVertex.p3UVCh1.y: " + oVertex.p3UVCh1.y as string) ) 3: ( oVertex.p3UVCh1.x = ReadFloat oRgm.fFile oVertex.p3UVCh1.y = ReadFloat oRgm.fFile ) ) ) /* 9 = UV Channel 2 */ 9: ( case oComponent.iDataType of ( 2: ( iByte0 = (ReadByte oRgm.fFile #unsigned) as float iByte1 = (ReadByte oRgm.fFile #unsigned) as float iByte2 = (ReadByte oRgm.fFile #unsigned) as float iByte3 = (ReadByte oRgm.fFile #unsigned) as float fFloat0 = iByte0/255.0 fFloat1 = iByte1/255.0 fFloat2 = iByte2/255.0 fFloat3 = iByte3/255.0 fFloat3 = 1.0 - fFloat3 oVertex.p3UVCh1.x = fFloat0 + (fFloat2/32.0) oVertex.p3UVCh1.y = fFloat1 + (fFloat3/32.0) ) 3: ( oVertex.p3UVCh2.x = ReadFloat oRgm.fFile oVertex.p3UVCh2.y = ReadFloat oRgm.fFile ) ) ) /* 10 = UV Channel 3 */ 10: ( case oComponent.iDataType of ( 2: ( iByte0 = (ReadByte oRgm.fFile #unsigned) as float iByte1 = (ReadByte oRgm.fFile #unsigned) as float iByte2 = (ReadByte oRgm.fFile #unsigned) as float iByte3 = (ReadByte oRgm.fFile #unsigned) as float fFloat0 = iByte0/255.0 fFloat1 = iByte1/255.0 fFloat2 = iByte2/255.0 fFloat3 = iByte3/255.0 fFloat3 = 1.0 - fFloat3 oVertex.p3UVCh1.x = fFloat0 + (fFloat2/32.0) oVertex.p3UVCh1.y = fFloat1 + (fFloat3/32.0) ) 3: ( oVertex.p3UVCh1.x = ReadFloat oRgm.fFile oVertex.p3UVCh1.y = ReadFloat oRgm.fFile ) ) ) ) ) append oMeshInfo.aVertArray oVertex ) --Print oMeshInfo.aVertArray as string local iVertUnknown = ReadLong oRgm.fFile /* Faces and Material IDs */ local iFaceCount = ReadLong oRgm.fFile #unsigned FaceUnknown = ReadLong oRgm.fFile FaceUnknown = ReadLong oRgm.fFile iFaceCount = iFaceCount / 3 for i=1 to iFaceCount do ( local aFace = point3 0 0 0 aFace.x = (ReadShort oRgm.fFile) + 1 aFace.z = (ReadShort oRgm.fFile) + 1 aFace.y = (ReadShort oRgm.fFile) + 1 append oMeshInfo.aFaceArray [aFace.x, aFace.y, aFace.z] ) local iStrLen = ReadLong oRgm.fFile #unsigned sMaterialName = Corsix_ReadStringN oRgm.fFile iStrLen local iMatIDFace = 0 iMatIDFace = findItem aMatIDArray sMaterialName /* Organise the verts for the per object mesh construction Each object contains a list of faces. Each of these faces contains 3 indexes to verticies in the global vertex list. We want to copy these verticies to the local vertex/UV list and update the index to be into the local list. */ local aVertexIdMap = #() for itrObject in aObjectList do ( -- The object should already be set to this, but make sure: itrObject.sOIDStruct.iVertCount = 0 itrObject.sOIDStruct.aVertArray = #() itrObject.sOIDStruct.aUVArray = #() -- Set the mapping to indicate that no global verticies have equivalent local verticies for i=1 to oMeshInfo.iVertCount do ( aVertexIdMap[i] = 0 ) for i=1 to itrObject.sOIDStruct.iFaceCount do ( local iGlobalVertId = 0 local iLocalVertId = 0 -----[ X ]----- iGlobalVertId = itrObject.sOIDStruct.aFaceList[i].x -- Get the first global vertex index (the x component of the face) iLocalVertId = aVertexIdMap[iGlobalVertId] -- See if that vertex has been mapped to a local one if iLocalVertId == 0 then -- If it hasn't, then we need to map it: ( append itrObject.sOIDStruct.aVertArray oMeshInfo.aVertArray[iGlobalVertId].p3Position -- copy over position append itrObject.sOIDStruct.aUVArray oMeshInfo.aVertArray[iGlobalVertId].p3UVCh1 -- copy over UV itrObject.sOIDStruct.iVertCount = itrObject.sOIDStruct.iVertCount + 1 -- guess what? we have a new vertex iLocalVertId = itrObject.sOIDStruct.iVertCount -- we now have a local vertex ID (the vertex we just copied) aVertexIdMap[iGlobalVertId] = iLocalVertId -- update the mapping to show the global vertex has an equivalent local one ) itrObject.sOIDStruct.aFaceList[i].x = iLocalVertId -- Change the global index to a local index -----[ Y ]----- iGlobalVertId = itrObject.sOIDStruct.aFaceList[i].y -- Get the second global vertex index (the y component of the face) iLocalVertId = aVertexIdMap[iGlobalVertId] -- See if that vertex has been mapped to a local one if iLocalVertId == 0 then -- If it hasn't, then we need to map it: ( append itrObject.sOIDStruct.aVertArray oMeshInfo.aVertArray[iGlobalVertId].p3Position -- copy over position append itrObject.sOIDStruct.aUVArray oMeshInfo.aVertArray[iGlobalVertId].p3UVCh1 -- copy over UV itrObject.sOIDStruct.iVertCount = itrObject.sOIDStruct.iVertCount + 1 -- guess what? we have a new vertex iLocalVertId = itrObject.sOIDStruct.iVertCount -- we now have a local vertex ID (the vertex we just copied) aVertexIdMap[iGlobalVertId] = iLocalVertId -- update the mapping to show the global vertex has an equivalent local one ) itrObject.sOIDStruct.aFaceList[i].y = iLocalVertId -- Change the global index to a local index -----[ Z ]----- iGlobalVertId = itrObject.sOIDStruct.aFaceList[i].z -- Get the third global vertex index (the z component of the face) iLocalVertId = aVertexIdMap[iGlobalVertId] -- See if that vertex has been mapped to a local one if iLocalVertId == 0 then -- If it hasn't, then we need to map it: ( append itrObject.sOIDStruct.aVertArray oMeshInfo.aVertArray[iGlobalVertId].p3Position -- copy over position append itrObject.sOIDStruct.aUVArray oMeshInfo.aVertArray[iGlobalVertId].p3UVCh1 -- copy over UV itrObject.sOIDStruct.iVertCount = itrObject.sOIDStruct.iVertCount + 1 -- guess what? we have a new vertex iLocalVertId = itrObject.sOIDStruct.iVertCount -- we now have a local vertex ID (the vertex we just copied) aVertexIdMap[iGlobalVertId] = iLocalVertId -- update the mapping to show the global vertex has an equivalent local one ) itrObject.sOIDStruct.aFaceList[i].z = iLocalVertId -- Change the global index to a local index ) ) /* Construct Meshes */ for oTempObject in aObjectList do ( Print oTempObject as string oMesh = mesh vertices:(oTempObject.sOIDStruct.aVertArray) faces:(oTempObject.sOIDStruct.aFaceList) -- construct mesh. oMesh = current mesh handle. oMesh.name = oTempObject.sObjectName -- set object name oMesh.wireColor = (color 0 0 0) -- set wire colour to black oMesh.material = mCorsixMultiMat -- assign material update oMesh deselect oMesh setMesh oMesh tverts:oTempObject.sOIDStruct.aUVArray -- setMesh oMesh mesh tverts:oTempObject.sOIDStruct.aUVArray buildTVFaces oMesh for i = 1 to oMesh.numfaces do ( oFace = (getFace oMesh i) setTVFace oMesh i oFace.x oFace.y oFace.z setFaceSmoothGroup oMesh i 0 -- clear smoothing groups (o = none) setFaceMatID oMesh i iMatIDFace -- set the material ID ) meshop.weldVertsByThreshold oMesh oMesh.verts 0.00001 -- weld our new mesh Print ("Mesh: " + oMesh.name + " built!") ) ) /* Handles a FOLDTRIM/DATADATA chunk from an RGM file oRgm - the RGM file oChunk - the DATADATA chunk sName - the name of the mesh returns - nothing */ fn Corsix_RgmIntoMax_FoldTrim_DataData oRgm oChunk sName = ( fseek oRgm.fFile (oChunk.iDataPosition) #seek_set -- jump to chunk /* Per-Vertex Component Data */ local iComponentCount = ReadLong oRgm.fFile #unsigned struct sPerVertStruct ( iVertComponent = 0, iUnknown = 0, iDataType = 0 ) local aComponentVector = #() for i=1 to iComponentCount do ( oComponent = sPerVertStruct() oComponent.iVertComponent = ReadLong oRgm.fFile #unsigned oComponent.iUnknown = ReadLong oRgm.fFile #unsigned -- This number should usually be three. Its current purpose is unknown oComponent.iDataType = ReadLong oRgm.fFile #unsigned append aComponentVector oComponent ) Print aComponentVector as string /* Per-Vertex Data */ struct sVertStruct ( p3Position = point3 0 0 0, iBoneIndex = 0, iBoneWeight = 0, iNormal = 0, iBiNormal = 0, iTangent = 0, iDiffuseColour = color 0 0 0 0, iSpecularColour = color 0 0 0 0, p3UVCh1 = point3 0 0 0, p3UVCh2 = point3 0 0 0, p3UVCh3 = point3 0 0 0 ) local aVerticies = #() local iVertCount = ReadLong oRgm.fFile #unsigned local iVertexSize = ReadLong oRgm.fFile #unsigned -- number of bytes taken up by each vertex on disk. for i=1 to iVertCount do ( local oVertex = sVertStruct() for oComponent in aComponentVector do ( case oComponent.iVertComponent of ( /* 0 = Position */ 0: ( oVertex.p3Position.x = -(ReadFloat oRgm.fFile) oVertex.p3Position.z = ReadFloat oRgm.fFile oVertex.p3Position.y = -(ReadFloat oRgm.fFile) ) /* 1 = Bone Index */ 1: ( oVertex.iBoneIndex = ReadLong oRgm.fFile #unsigned ) /* 2 = Bone Weight */ 2: ( oVertex.iBoneWeight = ReadLong oRgm.fFile #unsigned ) /* 3 = Normal */ 3: ( oVertex.iNormal = ReadLong oRgm.fFile #unsigned ) /* 4 = BiNormal */ 4: ( oVertex.iBiNormal = ReadLong oRgm.fFile #unsigned ) /* 5 = Tangent */ 5: ( oVertex.iTangent = ReadLong oRgm.fFile #unsigned ) /* 6 = Diffuse Vertex Colour */ 6: ( oVertex.iDiffuseColour.r = ReadByte oRgm.fFile oVertex.iDiffuseColour.g = ReadByte oRgm.fFile oVertex.iDiffuseColour.b = ReadByte oRgm.fFile oVertex.iDiffuseColour.a = ReadByte oRgm.fFile ) /* 7 = Specular Vertex Colour */ 7: ( oVertex.iSpecularColour.r = ReadByte oRgm.fFile oVertex.iSpecularColour.g = ReadByte oRgm.fFile oVertex.iSpecularColour.b = ReadByte oRgm.fFile oVertex.iSpecularColour.a = ReadByte oRgm.fFile ) /* 8 = UV Channel 1 */ 8: ( if oComponent.iDataType == 3 then ( oVertex.p3UVCh1.x = ReadFloat oRgm.fFile oVertex.p3UVCh1.y = ReadFloat oRgm.fFile ) else ( oVertex.p3UVCh1.x = ReadFloat oRgm.fFile oVertex.p3UVCh1.y = ReadFloat oRgm.fFile ) ) /* 9 = UV Channel 2 */ 9: ( if oComponent.iDataType == 3 then ( oVertex.p3UVCh2.x = ReadFloat oRgm.fFile oVertex.p3UVCh2.y = ReadFloat oRgm.fFile ) else ( oVertex.p3UVCh2.x = ReadLong oRgm.fFile ) ) /* 10 = UV Channel 3 */ 10: ( if oComponent.iDataType == 3 then ( oVertex.p3UVCh3.x = ReadFloat oRgm.fFile oVertex.p3UVCh3.y = ReadFloat oRgm.fFile ) else ( oVertex.p3UVCh3.x = ReadLong oRgm.fFile ) ) ) ) append aVerticies oVertex ) --Print aVerticies as string fseek oRgm.fFile 8 #seek_cur -- skip unknown /* Faces and Material IDs */ local aFaces = #() local iVertsPerFace = ReadLong oRgm.fFile -- number of verts in a face local iFaceCount = ReadLong oRgm.fFile iFaceCount = iFaceCount / iVertsPerFace for i=1 to iFaceCount do ( local aFace = point3 0 0 0 aFace.x = (ReadShort oRgm.fFile) + 1 aFace.z = (ReadShort oRgm.fFile) + 1 aFace.y = (ReadShort oRgm.fFile) + 1 append aFaces [aFace.x, aFace.y, aFace.z] ) local iStrLen = ReadLong oRgm.fFile #unsigned sMaterialName = Corsix_ReadStringN oRgm.fFile iStrLen local iMatIDFace = 0 iMatIDFace = findItem aMatIDArray sMaterialName /* Construct Mesh */ /* oMesh = mesh vertices:aVerticies faces:aFaces -- construct mesh. oMesh.name = sName -- set object name oMesh.wireColor = (color 0 0 0) -- set wire colour to black oMesh.material = mCorsixMultiMat -- assign material update oMesh deselect oMesh for i = 1 to oMesh.numfaces do ( oFace = (getFace oMesh i) setFaceSmoothGroup oMesh i 0 -- clear smoothing groups (o = none) setFaceMatID oMesh i iMatIDFace -- set the material ID ) meshop.weldVertsByThreshold oMesh oMesh.verts 0.00001 -- weld our new mesh Print ("Mesh: " + oMesh.name + " built!") */ ) /* Handles a FOLDMRGM chunk from an RGM file oRgm - the RGM file oChunk - the FOLDMRGM chunk returns - nothing */ fn Corsix_RgmIntoMax_FoldMrgm oRgm oChunk = ( iDataDataCount = 0 local i = 1 while i <= oChunk.iChildCount do ( case oChunk.aChildren[i].sType of ( "DATADATA": ( if iDataDataCount == 0 then ( Corsix_RgmIntoMax_Mesh_DataData oRgm oChunk.aChildren[i] iDataDataCount = 1 ) ) ) i += 1 ) ) /* Handles a FOLDMGRP chunk from an RGM file (Mesh Group) oRgm - the RGM file oChunk - the FOLDMGRP chunk returns - nothing */ fn Corsix_RgmIntoMax_FoldMgrp oRgm oChunk = ( local i = 1 while i <= oChunk.iChildCount do ( case oChunk.aChildren[i].sType of ( "FOLDMESH": Corsix_RgmIntoMax_FoldMesh oRgm oChunk.aChildren[i] ) i += 1 ) ) /* Handles a FOLDMESH chunk from an RGM file oRgm - the RGM file oChunk - the FOLDMESH chunk returns - nothing */ fn Corsix_RgmIntoMax_FoldMesh oRgm oChunk = ( local i = 1 while i <= oChunk.iChildCount do ( case oChunk.aChildren[i].sType of ( "FOLDMGRP": Corsix_RgmIntoMax_FoldMgrp oRgm oChunk.aChildren[i] "FOLDMRGM": Corsix_RgmIntoMax_FoldMrgm oRgm oChunk.aChildren[i] "FOLDTRIM": Corsix_RgmIntoMax_FoldTrim oRgm oChunk.aChildren[i] ) i += 1 ) ) /* Handles a FOLDTRIM chunk from an RGM file oRgm - the RGM file oChunk - the FOLDMESH chunk returns - nothing */ fn Corsix_RgmIntoMax_FoldTrim oRgm oChunk = ( iDataDataCount = 0 local i = 1 while i <= oChunk.iChildCount do ( case oChunk.aChildren[i].sType of ( "DATADATA": if iDataDataCount == 0 then ( Corsix_RgmIntoMax_FoldTrim_DataData oRgm oChunk.aChildren[i] oChunk.sName iDataDataCount = 1 ) ) i += 1 ) ) /* Handles a FOLDSKEL chunk from an RGM file (Skeleton data) oRgm - the RGM file oChunk - the FOLDSKEL chunk returns - nothing */ fn Corsix_RgmIntoMax_FoldMesh_FoldSkel oRgm oChunk = ( local i = 1 while i <= oChunk.iChildCount do ( case oChunk.aChildren[i].sType of ( "DATABONE": Corsix_RgmIntoMax_FoldMesh_FoldSkel_DataBone oRgm oChunk.aChildren[i] ) i += 1 ) ) /* Handles a DATABONE chunk from an RGM file (Bone data) oRgm - the RGM file oChunk - the DATABONE chunk sName - the name of the bone returns - nothing */ fn Corsix_RgmIntoMax_FoldMesh_FoldSkel_DataBone oRgm oChunk sName = ( fseek oRgm.fFile (oChunk.iDataPosition + 1) #seek_set -- jump to chunk ) /* Handles a FOLDMTRL chunk from an RGM file oRgm - the RGM file oChunk - the FOLDMTRL chunk returns - nothing */ fn Corsix_RgmIntoMax_FoldMtrl oRgm oChunk = ( /* Handles a DATAINFO chunk from an RGM file (Shader Names) oRgm - the RGM file oChunk - the DATAINFO chunk returns - nothing */ fn Corsix_RgmIntoMax_FoldMtrl_DataInfo oRgm oChunk = ( fseek oRgm.fFile oChunk.iDataPosition #seek_set -- jump to chunk local iStrLen = ReadLong oRgm.fFile #unsigned sShaderName = Corsix_ReadStringN oRgm.fFile iStrLen -- read the shader name print (" Shader: " + sShaderName as string + "\n") -- and spit it inot the listener ) /* Handles a DATAVAR chunk from an RGM file (Material Variables) oRgm - the RGM file oChunk - the DATAVAR chunk returns - nothing */ fn Corsix_RgmIntoMax_FoldMtrl_DataVar oRgm oChunk = ( fseek oRgm.fFile oChunk.iDataPosition #seek_set -- jump to chunk local iStrLen = ReadLong oRgm.fFile #unsigned sTexType = Corsix_ReadStringN oRgm.fFile iStrLen Print (" Texture type: " + sTexType as string) fseek oRgm.fFile 4 #seek_cur -- skip unknown 4 bytes local iStrLen = ReadLong oRgm.fFile #unsigned sTexPath = Corsix_ReadStringN oRgm.fFile iStrLen if sTexType != "camouflagecolour" and sTexType != "teamcolour" and sTexType != "diffuse_offsetu" and sTexType != "diffuse_offsetv" and sTexType != "dirtvisibility" and sTexType != "diffuseOffsetScale" and sTexType != "material_specular" and sTexType != "material_specularExp" then -- these ones don't have texture paths. Just colour values, offset values etc. ( Print (" Path: " + sTexPath as string + "\n") ) texturepath = (sCohPath as string + "\\DataSrc\\" + sModFolder as string + "\\" + sTexPath + ".tga") -- construct the texture path texture_map = bitmaptexture filename:texturepath -- new bitmap texture to hold our newly read path case sTexType of ( "diffusetex": oMaterial.diffusetex = texture_map -- only handle the ones that have texture paths "normalmap": oMaterial.normalmap = texture_map "teamtex": oMaterial.teamtex = texture_map "occlusiontex": oMaterial.occlusiontex = texture_map "glosstex": oMaterial.glosstex = texture_map "speculartex": oMaterial.speculartex = texture_map ) global mCorsixMaterial = standardMaterial diffuseMap: oMaterial.diffusetex name:(aMatIDArray[aMatIDArray.count] as string) specularMap:oMaterial.speculartex glossinessMap:oMaterial.glosstex showInViewport:true mCorsixMultiMat[mCorsixMultiMat.numsubs] = mCorsixMaterial mCorsixMultiMat.names[mCorsixMultiMat.numsubs] = aMatIDArray[aMatIDArray.count] setMeditMaterial 1 mCorsixMultiMat -- set mCorsixMultiMat to slot one in the ME ) local i = 1 while i <= oChunk.iChildCount do ( case oChunk.aChildren[i].sType of ( "DATAVAR": Corsix_RgmIntoMax_FoldMtrl_DataVar oRgm oChunk.aChildren[i] "DATAINFO": Corsix_RgmIntoMax_FoldMtrl_DataInfo oRgm oChunk.aChildren[i] ) i += 1 ) ) /* Handles a FOLDMODL chunk from an RGM file oRgm - the RGM file oChunk - the FOLDMODL chunk returns - nothing */ fn Corsix_RgmIntoMax_FoldModl oRgm oChunk = ( local i = 1 local iMatID = 0 while i <= oChunk.iChildCount do ( case oChunk.aChildren[i].sType of ( "FOLDMESH": Corsix_RgmIntoMax_FoldMesh oRgm oChunk.aChildren[i] "FOLDSKEL": Corsix_RgmIntoMax_FoldMesh oRgm oChunk.aChildren[i] "FOLDMTRL": ( iMatID += 1 Print "----------------------------------------------" print (" Material ID Name: " + oChunk.aChildren[i].sName as string) append aMatIDArray oChunk.aChildren[i].sName print (" Material ID: " + iMatID as string) struct sShaderTexturesStruct ( diffusetex = noMaterial(), -- noMaterial() will set the texture to 'none in the ME. Other wise we will get undefined errors teamtex = noMaterial(), normalmap = noMaterial(), occlusiontex = noMaterial(), glosstex = noMaterial(), speculartex = noMaterial() ) global oMaterial = sShaderTexturesStruct() -- an object to hold each material as we read them Corsix_RgmIntoMax_FoldMtrl oRgm oChunk.aChildren[i] -- handle the FOLDMTRL and its sub chunks mCorsixMultiMat.numsubs += 1 -- another one please! Print "----------------------------------------------" ) ) i += 1 ) ) /* Handles an RGM file oRgm - the RGM file returns - nothing */ fn Corsix_RgmIntoMax oRgm = ( local i = 1 while i <= oRgm.iChunkCount do ( case oRgm.aChunks[i].sType of ( "FOLDMODL": Corsix_RgmIntoMax_FoldModl oRgm oRgm.aChunks[i] ) i += 1 ) ) /* Cleans up the scene after import */ fn Corsix_RgmIntoMax_SceneCleanup oRgm = ( mCorsixMultiMat.numsubs -= 1 -- remove empty multimaterial stub viewport.setLayout #layout_1 -- set all sorts of viewport related things viewport.setType #view_persp_user viewport.SetRenderLevel #smooth viewport.SetShowEdgeFaces true max tool zoomextents all if iDataDataCount != 0 then ( Print (sModelName as string + " imported successfully!") ) else ( messagebox "All meshes X-Reffed. No meshes to be loaded in file." title:"RGM importer" ) ) /* Cleans up the scene after directory and recursive directory import */ fn Corsix_RgmIntoMax_SceneCleanup_DirImport oRgm = ( mCorsixMultiMat.numsubs -= 1 -- remove empty multimaterial stub viewport.setLayout #layout_1 -- set all sorts of viewport related things viewport.setType #view_persp_user viewport.SetRenderLevel #smooth viewport.SetShowEdgeFaces true max tool zoomextents all if iDataDataCount != 0 then ( Print (sModelName as string + " imported successfully!") ) else ( (Print "Import failed. All meshes X-REFFED. No meshes to load in file: " + sModelName as string) ) ) /* Recursively gets all RGM files under the root directory (from maxscript docs. No use in reinventing the wheel) */ fn Corsix_RgmIntoMax_RecDirFileFind dirRecDir = ( aDirectories = GetDirectories (dirRecDir + "/*") for d in aDirectories do ( join aDirectories (GetDirectories (d + "/*")) ) aFiles = #() for f in aDirectories do ( join aFiles (getFiles (f + "*.rgm")) ) return aFiles ) /* Graphical user interface stuff */ utility Corsix_RGM_Import "RGM Import" width:166 height:137 ( button btn_RGM_Import "Import RGM" pos:[13,5] width:140 height:21 toolTip:"Import an RGM file" button btn_Dir_Import "RGM Directory Import" pos:[13,32] width:140 height:21 toolTip:"Import all RGM files in a folder" button btn_RecDir_Import "Recursive Directory Import" pos:[13,58] width:140 height:21 toolTip:"Import RGM files from a directory recursively" button btn_Set_CoH_Directory "Set CoH Dir" pos:[13,84] width:140 height:21 toolTip:"Set the location of your Company of Heroes directory" button btn_Set_Mod_Directory "Set Mod Dir" pos:[13,110] width:140 height:21 toolTip:"Set the location of your mod directory" on btn_RGM_Import pressed do ( ClearListener() global sModelName = "" global iDataDataCount = 0 -- global variable to check if there are any DATADATA chunks in a file global aMatIDArray = #() -- global array to hold materials and their IDs global mCorsixMultiMat = MultiMaterial numsubs:1 -- a nice new multimaterial global sCohPath = getINISetting (Corsix_RGMImportIni()) "Directories" "Company_Of_Heroes" -- load various bits of info from the ini file global sModFolder = getINISetting (Corsix_RGMImportIni()) "Directories" "Mod_Folder" global sLastFile = getINISetting (Corsix_RGMImportIni()) "Files" "Last_File" local sFilename = getOpenFileName caption:"Select a RGM file to import" filename:sLastFile types:"RGM (*.rgm)|*.rgm" if sFilename != undefined then ( setINISetting (Corsix_RGMImportIni()) "Files" "Last_File" sFilename sModelName = getFilenameFile sFilename local oRgm = Corsix_Chunky() -- Yay! New object! if (oRgm.loadFromFile sFilename) == false then -- Boo. We failed. ( messagebox "Unable to load file" ) else ( resetMaxFile #noPrompt -- reset max if sCohPath != undefined then ( local sSavePath = getSavePath caption:"Select a where you want to save your file" initialDir:sCohPath if sSavePath != undefined then ( Makedir (sSavePath + "Model\\") Makedir (sSavePath + "Animations\\") Corsix_RgmIntoMax oRgm -- IMPORT! Corsix_RgmIntoMax_SceneCleanup oRGM -- contains all the post import functions SaveMaxfile (sSavePath + "Model\\Reference.max") ) else ( messagebox "The model will be loaded but not saved" title:"RGM importer" -- Ah! The joys of messageboxes! Corsix_RgmIntoMax oRgm Corsix_RgmIntoMax_SceneCleanup oRGM ) ) else ( messagebox "Please locate your Company of Heroes directory and try again" title:"RGM importer" ) ) ) ) on btn_Dir_Import pressed do ( ClearListener() global sModelName = "" global iDataDataCount = 0 -- global variable to check if there are any DATADATA chunks in a file global aMatIDArray = #() -- global array to hold materials and their IDs global mCorsixMultiMat = MultiMaterial numsubs:1 -- a nice new multimaterial global sCohPath = getINISetting (Corsix_RGMImportIni()) "Directories" "Company_Of_Heroes" -- load various bits of info from the ini file global sModFolder = getINISetting (Corsix_RGMImportIni()) "Directories" "Mod_Folder" global sLastFile = getINISetting (Corsix_RGMImportIni()) "Files" "Last_File" local sRgmImportFolder = getSavePath caption:"Locate folder to import RGMs from..." initialDir:sCohPath local sRgmSaveFolder = getSavePath caption:"Locate location to save loaded files..." initialDir:sCohPath if sRgmImportFolder != undefined and sRgmSaveFolder != undefined then ( aRgmImportFiles = getFiles (sRgmImportFolder + "\\*.RGM") for fFile in aRgmImportFiles do ( sModelName = getFilenameFile fFile local oRgm = Corsix_Chunky() if (oRgm.loadFromFile fFile) == false then -- Boo. We failed. ( messagebox "Unable to load file" ) else -- it worked ( resetMaxFile #noPrompt if sCohPath != undefined then ( Makedir (sRgmSaveFolder + "\\" + sModelName + "\\") Makedir (sRgmSaveFolder + "\\" + sModelName + "\\Model\\") Makedir (sRgmSaveFolder + "\\" + sModelName + "\\Animations\\") Corsix_RgmIntoMax oRgm -- IMPORT! Corsix_RgmIntoMax_SceneCleanup_DirImport oRGM -- contains directory import specific cleanup functions SaveMaxfile (sRgmSaveFolder + "\\" + sModelName + "\\Model\\Reference.max") Print "\n" ) else ( messagebox "Please locate your Company of Heroes directory and try again" title:"RGM importer" ) ) ) resetMaxFile #noPrompt messagebox "All models imported and saved successfully!" title:"Directory Import" ) else ( messagebox "Please locate both the folder to import and the folder to save to and try again." title:"Directory Import" ) ) on btn_RecDir_Import pressed do ( ClearListener() global sModelName = "" global iDataDataCount = 0 -- global variable to check if there are any DATADATA chunks in a file global aMatIDArray = #() -- global array to hold materials and their IDs global mCorsixMultiMat = MultiMaterial numsubs:1 -- a nice new multimaterial global sCohPath = getINISetting (Corsix_RGMImportIni()) "Directories" "Company_Of_Heroes" -- load various bits of info from the ini file global sModFolder = getINISetting (Corsix_RGMImportIni()) "Directories" "Mod_Folder" global sLastFile = getINISetting (Corsix_RGMImportIni()) "Files" "Last_File" local sRgmImportFolder = getSavePath caption:"Locate top level folder to import RGMs from..." initialDir:sCohPath local sRgmSaveFolder = getSavePath caption:"Locate location to save loaded files..." initialDir:sCohPath if sRgmImportFolder != undefined and sRgmSaveFolder != undefined then ( local aRgmImportFiles = Corsix_RgmIntoMax_RecDirFileFind sRgmImportFolder for fFile in aRgmImportFiles do ( sModelName = getFilenameFile fFile local oRgm = Corsix_Chunky() if (oRgm.loadFromFile fFile) == false then -- Boo. We failed. ( messagebox "Unable to load file" ) else -- it worked ( resetMaxFile #noPrompt if sCohPath != undefined then ( Makedir (sRgmSaveFolder + "\\" + sModelName + "\\") Makedir (sRgmSaveFolder + "\\" + sModelName + "\\Model\\") Makedir (sRgmSaveFolder + "\\" + sModelName + "\\Animations\\") Corsix_RgmIntoMax oRgm -- IMPORT! Corsix_RgmIntoMax_SceneCleanup_DirImport oRGM -- contains directory import specific cleanup functions SaveMaxfile (sRgmSaveFolder + "\\" + sModelName + "\\Model\\Reference.max") Print "\n" ) else ( messagebox "Please locate your Company of Heroes directory and try again" title:"RGM importer" ) ) ) resetMaxFile #noPrompt messagebox "All models imported and saved successfully!" title:"Recursive Directory Import" ) else ( messagebox "Please locate both the folder to import and the folder to save to and try again." title:"Recursive Directory Import" ) ) on btn_Set_CoH_Directory pressed do ( local sCohPath = getSavePath caption:"Locate Company of Heroes directory" if sCohPath != undefined then ( setINISetting (Corsix_RGMImportIni()) "Directories" "Company_Of_Heroes" sCohPath ) ) on btn_Set_Mod_Directory pressed do ( local sCohFolder = getINISetting (Corsix_RGMImportIni()) "Directories" "Company_Of_Heroes" local sModFolder = getSavePath "Locate mod directory" sCohFolder if sModFolder != undefined then ( local aModFolder = filterString sModFolder "\\" sModFolder = aModFolder[aModFolder.count] setINISetting (Corsix_RGMImportIni()) "Directories" "Mod_Folder" sModFolder ) ) )/* Corsix and fneep's RGM Tools for Company of Heroes Copyright (C) 2007 Corsix and fneep This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* This file loads RGM files into max and also handles the GUI parts of the script. Version history: 0.1 (Oct 8th 2006): - Initial release - Loads vertex X,Y,Z and faces 0.11 (Feb 19th 2007): - Bugfix and tweaks - Loads Material IDs - Parses Texture chunks and spits out data into listener 0.12 (Feb 23th 2007): - Textures loaded 0.15 (Mar 1st 2007): - Loads meshes as seperate objects - Bugfixes and tweaks 0.151 (Mar 1st 2007): - Fixed bug where script did not load infantry models 0.152 (Mar 3rd 2007): - Minor cleanup and commented - XREF warning messages added 0.16 (Mar 18th 2007): - Loading of FOLDTRIM chunks added - Fixed bug where only first mesh chunk was loaded 0.17 (Mar 23rd 2007): - Added directory and recursive directory imports - Fixed bug where texture paths were loaded incorrectly 0.20 (May 30rd 2007): - Full vertex loading capabilities prep added */ include "Corsix_Chunky.ms" -- include Corsix's chunky library ClearListener() -- Clear the listener window to make for easier reading of data printed by script. /* Gets the filename of the ini file for this script */ fn Corsix_RGMImportIni = ( local ini_dir = getINISetting (getMAXIniFile()) "Directories" "Startup Scripts" return ini_dir + "\\Corsix_RGM_Import.ini" ) /* Handles a DATADATA chunk from an RGM file oRgm - the RGM file oChunk - the DATADATA chunk returns - nothing */ fn Corsix_RgmIntoMax_Mesh_DataData oRgm oChunk = ( fseek oRgm.fFile (oChunk.iDataPosition + 1) #seek_set -- jump to chunk /* Objects */ struct sGlobalMeshInfo -- holds global mesh data to compare to and collect from ( iVertCount = 0, aVertArray = #(), aFaceArray = #() ) struct sObjectID -- holds data for the Object identifiers ( iVertCount = 0, aVertArray = #(), aUVArray = #(), iFaceCount = 0, aFaceList = #() ) struct sObjectStruct -- holds data for a single object ( sObjectName = "", sOIDStruct = sObjectID() ) global oMeshInfo = sGlobalMeshInfo() -- holds all mesh data local iObjectCount = ReadLong oRgm.fFile #unsigned -- number of separate objects local aObjectList = #() for i=1 to iObjectCount do ( local oObject = sObjectStruct() oObject.sOIDStruct.iFaceCount = (ReadLong oRgm.fFile #unsigned)/3 -- number of faces as the verts that make them. Meshes are triangles so divide by 3 for i=1 to oObject.sOIDStruct.iFaceCount do ( local aFace = point3 0 0 0 aFace.x = (ReadShort oRgm.fFile) + 1 -- vertex ID's given as vertnumber - 1 because of the way that C++ handles arrays (the exporter is in C++). To convert to maxscript, add one. aFace.z = (ReadShort oRgm.fFile) + 1 aFace.y = (ReadShort oRgm.fFile) + 1 append oObject.sOIDStruct.aFaceList [aFace.x, aFace.y, aFace.z] ) fseek oRgm.fFile 13 #seek_cur -- skip unknown local iStrLen = ReadLong oRgm.fFile #unsigned oObject.sObjectName = Corsix_ReadStringN oRgm.fFile iStrLen append aObjectList oObject ) /* Per-Vertex Component Data */ local iComponentCount = ReadLong oRgm.fFile #unsigned struct sPerVertStruct ( iVertComponent = 0, iVectorStorageType = 0, iDataType = 0 ) local aComponentVector = #() for i=1 to iComponentCount do ( oComponent = sPerVertStruct() oComponent.iVertComponent = ReadLong oRgm.fFile #unsigned oComponent.iVectorStorageType = ReadLong oRgm.fFile #unsigned oComponent.iDataType = ReadLong oRgm.fFile #unsigned append aComponentVector oComponent ) Print aComponentVector as string /* Per-Vertex Data */ struct sVertStruct ( p3Position = point3 0 0 0, iBoneIndex = 0, iBoneWeight = 0, iNormal = 0, iBiNormal = 0, iTangent = 0, iDiffuseColour = color 0 0 0 0, iSpecularColour = color 0 0 0 0, p3UVCh1 = point3 0 0 0, p3UVCh2 = point3 0 0 0, p3UVCh3 = point3 0 0 0 ) local iVertCount = ReadLong oRgm.fFile #unsigned local iVertexSize = ReadLong oRgm.fFile #unsigned -- number of bytes taken up by each vertex on disk. oMeshInfo.iVertCount = iVertCount for i=1 to iVertCount do ( local oVertex = sVertStruct() for oComponent in aComponentVector do ( case oComponent.iVertComponent of ( /* 0 = Position */ 0: ( oVertex.p3Position.x = -(ReadFloat oRgm.fFile) oVertex.p3Position.z = ReadFloat oRgm.fFile oVertex.p3Position.y = -(ReadFloat oRgm.fFile) ) /* 1 = Bone Index */ 1: ( oVertex.iBoneIndex = ReadLong oRgm.fFile #unsigned ) /* 2 = Bone Weight */ 2: ( oVertex.iBoneWeight = ReadLong oRgm.fFile #unsigned ) /* 3 = Normal */ 3: ( oVertex.iNormal = ReadLong oRgm.fFile #unsigned ) /* 4 = BiNormal */ 4: ( oVertex.iBiNormal = ReadLong oRgm.fFile #unsigned ) /* 5 = Tangent */ 5: ( oVertex.iTangent = ReadLong oRgm.fFile #unsigned ) /* 6 = Diffuse Vertex Colour */ 6: ( oVertex.iDiffuseColour.r = ReadByte oRgm.fFile oVertex.iDiffuseColour.g = ReadByte oRgm.fFile oVertex.iDiffuseColour.b = ReadByte oRgm.fFile oVertex.iDiffuseColour.a = ReadByte oRgm.fFile ) /* 7 = Specular Vertex Colour */ 7: ( oVertex.iSpecularColour.r = ReadByte oRgm.fFile oVertex.iSpecularColour.g = ReadByte oRgm.fFile oVertex.iSpecularColour.b = ReadByte oRgm.fFile oVertex.iSpecularColour.a = ReadByte oRgm.fFile ) /* 8 = UV Channel 1 b0 = ReadByte #unsigned b1 = ReadByte #unsigned b2 = ReadByte #unsigned b3 = ReadByte #unsigned f0=(float)b0/255.f; f1=(float)b1/255.f; f2=(float)b2/255.f; f3=(float)b3/255.f; f3=1.f - f3; U = f0+(f2/32.f); V = f1+(f3/32.f); */ 8: ( case oComponent.iDataType of ( 2: ( iByte0 = (ReadByte oRgm.fFile #unsigned) as float iByte1 = (ReadByte oRgm.fFile #unsigned) as float iByte2 = (ReadByte oRgm.fFile #unsigned) as float iByte3 = (ReadByte oRgm.fFile #unsigned) as float Print ("iByte0: " + iByte0 as string) Print ("iByte1: " + iByte1 as string) Print ("iByte2: " + iByte2 as string) Print ("iByte3: " + iByte3 as string) fFloat0 = iByte0/255.0 fFloat1 = iByte1/255.0 fFloat2 = iByte2/255.0 fFloat3 = iByte3/255.0 fFloat3 = 1.0 - fFloat3 Print ("fFloat0: " + fFloat0 as string) Print ("fFloat1: " + fFloat1 as string) Print ("fFloat2: " + fFloat2 as string) Print ("fFloat3: " + fFloat3 as string) oVertex.p3UVCh1.x = fFloat0 + (fFloat2/32.0) oVertex.p3UVCh1.y = fFloat1 + (fFloat3/32.0) Print ("oVertex.p3UVCh1.x: " + oVertex.p3UVCh1.x as string) Print ("oVertex.p3UVCh1.y: " + oVertex.p3UVCh1.y as string) ) 3: ( oVertex.p3UVCh1.x = ReadFloat oRgm.fFile oVertex.p3UVCh1.y = ReadFloat oRgm.fFile ) ) ) /* 9 = UV Channel 2 */ 9: ( case oComponent.iDataType of ( 2: ( iByte0 = (ReadByte oRgm.fFile #unsigned) as float iByte1 = (ReadByte oRgm.fFile #unsigned) as float iByte2 = (ReadByte oRgm.fFile #unsigned) as float iByte3 = (ReadByte oRgm.fFile #unsigned) as float fFloat0 = iByte0/255.0 fFloat1 = iByte1/255.0 fFloat2 = iByte2/255.0 fFloat3 = iByte3/255.0 fFloat3 = 1.0 - fFloat3 oVertex.p3UVCh1.x = fFloat0 + (fFloat2/32.0) oVertex.p3UVCh1.y = fFloat1 + (fFloat3/32.0) ) 3: ( oVertex.p3UVCh2.x = ReadFloat oRgm.fFile oVertex.p3UVCh2.y = ReadFloat oRgm.fFile ) ) ) /* 10 = UV Channel 3 */ 10: ( case oComponent.iDataType of ( 2: ( iByte0 = (ReadByte oRgm.fFile #unsigned) as float iByte1 = (ReadByte oRgm.fFile #unsigned) as float iByte2 = (ReadByte oRgm.fFile #unsigned) as float iByte3 = (ReadByte oRgm.fFile #unsigned) as float fFloat0 = iByte0/255.0 fFloat1 = iByte1/255.0 fFloat2 = iByte2/255.0 fFloat3 = iByte3/255.0 fFloat3 = 1.0 - fFloat3 oVertex.p3UVCh1.x = fFloat0 + (fFloat2/32.0) oVertex.p3UVCh1.y = fFloat1 + (fFloat3/32.0) ) 3: ( oVertex.p3UVCh1.x = ReadFloat oRgm.fFile oVertex.p3UVCh1.y = ReadFloat oRgm.fFile ) ) ) ) ) append oMeshInfo.aVertArray oVertex ) --Print oMeshInfo.aVertArray as string local iVertUnknown = ReadLong oRgm.fFile /* Faces and Material IDs */ local iFaceCount = ReadLong oRgm.fFile #unsigned FaceUnknown = ReadLong oRgm.fFile FaceUnknown = ReadLong oRgm.fFile iFaceCount = iFaceCount / 3 for i=1 to iFaceCount do ( local aFace = point3 0 0 0 aFace.x = (ReadShort oRgm.fFile) + 1 aFace.z = (ReadShort oRgm.fFile) + 1 aFace.y = (ReadShort oRgm.fFile) + 1 append oMeshInfo.aFaceArray [aFace.x, aFace.y, aFace.z] ) local iStrLen = ReadLong oRgm.fFile #unsigned sMaterialName = Corsix_ReadStringN oRgm.fFile iStrLen local iMatIDFace = 0 iMatIDFace = findItem aMatIDArray sMaterialName /* Organise the verts for the per object mesh construction Each object contains a list of faces. Each of these faces contains 3 indexes to verticies in the global vertex list. We want to copy these verticies to the local vertex/UV list and update the index to be into the local list. */ local aVertexIdMap = #() for itrObject in aObjectList do ( -- The object should already be set to this, but make sure: itrObject.sOIDStruct.iVertCount = 0 itrObject.sOIDStruct.aVertArray = #() itrObject.sOIDStruct.aUVArray = #() -- Set the mapping to indicate that no global verticies have equivalent local verticies for i=1 to oMeshInfo.iVertCount do ( aVertexIdMap[i] = 0 ) for i=1 to itrObject.sOIDStruct.iFaceCount do ( local iGlobalVertId = 0 local iLocalVertId = 0 -----[ X ]----- iGlobalVertId = itrObject.sOIDStruct.aFaceList[i].x -- Get the first global vertex index (the x component of the face) iLocalVertId = aVertexIdMap[iGlobalVertId] -- See if that vertex has been mapped to a local one if iLocalVertId == 0 then -- If it hasn't, then we need to map it: ( append itrObject.sOIDStruct.aVertArray oMeshInfo.aVertArray[iGlobalVertId].p3Position -- copy over position append itrObject.sOIDStruct.aUVArray oMeshInfo.aVertArray[iGlobalVertId].p3UVCh1 -- copy over UV itrObject.sOIDStruct.iVertCount = itrObject.sOIDStruct.iVertCount + 1 -- guess what? we have a new vertex iLocalVertId = itrObject.sOIDStruct.iVertCount -- we now have a local vertex ID (the vertex we just copied) aVertexIdMap[iGlobalVertId] = iLocalVertId -- update the mapping to show the global vertex has an equivalent local one ) itrObject.sOIDStruct.aFaceList[i].x = iLocalVertId -- Change the global index to a local index -----[ Y ]----- iGlobalVertId = itrObject.sOIDStruct.aFaceList[i].y -- Get the second global vertex index (the y component of the face) iLocalVertId = aVertexIdMap[iGlobalVertId] -- See if that vertex has been mapped to a local one if iLocalVertId == 0 then -- If it hasn't, then we need to map it: ( append itrObject.sOIDStruct.aVertArray oMeshInfo.aVertArray[iGlobalVertId].p3Position -- copy over position append itrObject.sOIDStruct.aUVArray oMeshInfo.aVertArray[iGlobalVertId].p3UVCh1 -- copy over UV itrObject.sOIDStruct.iVertCount = itrObject.sOIDStruct.iVertCount + 1 -- guess what? we have a new vertex iLocalVertId = itrObject.sOIDStruct.iVertCount -- we now have a local vertex ID (the vertex we just copied) aVertexIdMap[iGlobalVertId] = iLocalVertId -- update the mapping to show the global vertex has an equivalent local one ) itrObject.sOIDStruct.aFaceList[i].y = iLocalVertId -- Change the global index to a local index -----[ Z ]----- iGlobalVertId = itrObject.sOIDStruct.aFaceList[i].z -- Get the third global vertex index (the z component of the face) iLocalVertId = aVertexIdMap[iGlobalVertId] -- See if that vertex has been mapped to a local one if iLocalVertId == 0 then -- If it hasn't, then we need to map it: ( append itrObject.sOIDStruct.aVertArray oMeshInfo.aVertArray[iGlobalVertId].p3Position -- copy over position append itrObject.sOIDStruct.aUVArray oMeshInfo.aVertArray[iGlobalVertId].p3UVCh1 -- copy over UV itrObject.sOIDStruct.iVertCount = itrObject.sOIDStruct.iVertCount + 1 -- guess what? we have a new vertex iLocalVertId = itrObject.sOIDStruct.iVertCount -- we now have a local vertex ID (the vertex we just copied) aVertexIdMap[iGlobalVertId] = iLocalVertId -- update the mapping to show the global vertex has an equivalent local one ) itrObject.sOIDStruct.aFaceList[i].z = iLocalVertId -- Change the global index to a local index ) ) /* Construct Meshes */ for oTempObject in aObjectList do ( Print oTempObject as string oMesh = mesh vertices:(oTempObject.sOIDStruct.aVertArray) faces:(oTempObject.sOIDStruct.aFaceList) -- construct mesh. oMesh = current mesh handle. oMesh.name = oTempObject.sObjectName -- set object name oMesh.wireColor = (color 0 0 0) -- set wire colour to black oMesh.material = mCorsixMultiMat -- assign material update oMesh deselect oMesh setMesh oMesh tverts:oTempObject.sOIDStruct.aUVArray -- setMesh oMesh mesh tverts:oTempObject.sOIDStruct.aUVArray buildTVFaces oMesh for i = 1 to oMesh.numfaces do ( oFace = (getFace oMesh i) setTVFace oMesh i oFace.x oFace.y oFace.z setFaceSmoothGroup oMesh i 0 -- clear smoothing groups (o = none) setFaceMatID oMesh i iMatIDFace -- set the material ID ) meshop.weldVertsByThreshold oMesh oMesh.verts 0.00001 -- weld our new mesh Print ("Mesh: " + oMesh.name + " built!") ) ) /* Handles a FOLDTRIM/DATADATA chunk from an RGM file oRgm - the RGM file oChunk - the DATADATA chunk sName - the name of the mesh returns - nothing */ fn Corsix_RgmIntoMax_FoldTrim_DataData oRgm oChunk sName = ( fseek oRgm.fFile (oChunk.iDataPosition) #seek_set -- jump to chunk /* Per-Vertex Component Data */ local iComponentCount = ReadLong oRgm.fFile #unsigned struct sPerVertStruct ( iVertComponent = 0, iUnknown = 0, iDataType = 0 ) local aComponentVector = #() for i=1 to iComponentCount do ( oComponent = sPerVertStruct() oComponent.iVertComponent = ReadLong oRgm.fFile #unsigned oComponent.iUnknown = ReadLong oRgm.fFile #unsigned -- This number should usually be three. Its current purpose is unknown oComponent.iDataType = ReadLong oRgm.fFile #unsigned append aComponentVector oComponent ) Print aComponentVector as string /* Per-Vertex Data */ struct sVertStruct ( p3Position = point3 0 0 0, iBoneIndex = 0, iBoneWeight = 0, iNormal = 0, iBiNormal = 0, iTangent = 0, iDiffuseColour = color 0 0 0 0, iSpecularColour = color 0 0 0 0, p3UVCh1 = point3 0 0 0, p3UVCh2 = point3 0 0 0, p3UVCh3 = point3 0 0 0 ) local aVerticies = #() local iVertCount = ReadLong oRgm.fFile #unsigned local iVertexSize = ReadLong oRgm.fFile #unsigned -- number of bytes taken up by each vertex on disk. for i=1 to iVertCount do ( local oVertex = sVertStruct() for oComponent in aComponentVector do ( case oComponent.iVertComponent of ( /* 0 = Position */ 0: ( oVertex.p3Position.x = -(ReadFloat oRgm.fFile) oVertex.p3Position.z = ReadFloat oRgm.fFile oVertex.p3Position.y = -(ReadFloat oRgm.fFile) ) /* 1 = Bone Index */ 1: ( oVertex.iBoneIndex = ReadLong oRgm.fFile #unsigned ) /* 2 = Bone Weight */ 2: ( oVertex.iBoneWeight = ReadLong oRgm.fFile #unsigned ) /* 3 = Normal */ 3: ( oVertex.iNormal = ReadLong oRgm.fFile #unsigned ) /* 4 = BiNormal */ 4: ( oVertex.iBiNormal = ReadLong oRgm.fFile #unsigned ) /* 5 = Tangent */ 5: ( oVertex.iTangent = ReadLong oRgm.fFile #unsigned ) /* 6 = Diffuse Vertex Colour */ 6: ( oVertex.iDiffuseColour.r = ReadByte oRgm.fFile oVertex.iDiffuseColour.g = ReadByte oRgm.fFile oVertex.iDiffuseColour.b = ReadByte oRgm.fFile oVertex.iDiffuseColour.a = ReadByte oRgm.fFile ) /* 7 = Specular Vertex Colour */ 7: ( oVertex.iSpecularColour.r = ReadByte oRgm.fFile oVertex.iSpecularColour.g = ReadByte oRgm.fFile oVertex.iSpecularColour.b = ReadByte oRgm.fFile oVertex.iSpecularColour.a = ReadByte oRgm.fFile ) /* 8 = UV Channel 1 */ 8: ( if oComponent.iDataType == 3 then ( oVertex.p3UVCh1.x = ReadFloat oRgm.fFile oVertex.p3UVCh1.y = ReadFloat oRgm.fFile ) else ( oVertex.p3UVCh1.x = ReadFloat oRgm.fFile oVertex.p3UVCh1.y = ReadFloat oRgm.fFile ) ) /* 9 = UV Channel 2 */ 9: ( if oComponent.iDataType == 3 then ( oVertex.p3UVCh2.x = ReadFloat oRgm.fFile oVertex.p3UVCh2.y = ReadFloat oRgm.fFile ) else ( oVertex.p3UVCh2.x = ReadLong oRgm.fFile ) ) /* 10 = UV Channel 3 */ 10: ( if oComponent.iDataType == 3 then ( oVertex.p3UVCh3.x = ReadFloat oRgm.fFile oVertex.p3UVCh3.y = ReadFloat oRgm.fFile ) else ( oVertex.p3UVCh3.x = ReadLong oRgm.fFile ) ) ) ) append aVerticies oVertex ) --Print aVerticies as string fseek oRgm.fFile 8 #seek_cur -- skip unknown /* Faces and Material IDs */ local aFaces = #() local iVertsPerFace = ReadLong oRgm.fFile -- number of verts in a face local iFaceCount = ReadLong oRgm.fFile iFaceCount = iFaceCount / iVertsPerFace for i=1 to iFaceCount do ( local aFace = point3 0 0 0 aFace.x = (ReadShort oRgm.fFile) + 1 aFace.z = (ReadShort oRgm.fFile) + 1 aFace.y = (ReadShort oRgm.fFile) + 1 append aFaces [aFace.x, aFace.y, aFace.z] ) local iStrLen = ReadLong oRgm.fFile #unsigned sMaterialName = Corsix_ReadStringN oRgm.fFile iStrLen local iMatIDFace = 0 iMatIDFace = findItem aMatIDArray sMaterialName /* Construct Mesh */ /* oMesh = mesh vertices:aVerticies faces:aFaces -- construct mesh. oMesh.name = sName -- set object name oMesh.wireColor = (color 0 0 0) -- set wire colour to black oMesh.material = mCorsixMultiMat -- assign material update oMesh deselect oMesh for i = 1 to oMesh.numfaces do ( oFace = (getFace oMesh i) setFaceSmoothGroup oMesh i 0 -- clear smoothing groups (o = none) setFaceMatID oMesh i iMatIDFace -- set the material ID ) meshop.weldVertsByThreshold oMesh oMesh.verts 0.00001 -- weld our new mesh Print ("Mesh: " + oMesh.name + " built!") */ ) /* Handles a FOLDMRGM chunk from an RGM file oRgm - the RGM file oChunk - the FOLDMRGM chunk returns - nothing */ fn Corsix_RgmIntoMax_FoldMrgm oRgm oChunk = ( iDataDataCount = 0 local i = 1 while i <= oChunk.iChildCount do ( case oChunk.aChildren[i].sType of ( "DATADATA": ( if iDataDataCount == 0 then ( Corsix_RgmIntoMax_Mesh_DataData oRgm oChunk.aChildren[i] iDataDataCount = 1 ) ) ) i += 1 ) ) /* Handles a FOLDMGRP chunk from an RGM file (Mesh Group) oRgm - the RGM file oChunk - the FOLDMGRP chunk returns - nothing */ fn Corsix_RgmIntoMax_FoldMgrp oRgm oChunk = ( local i = 1 while i <= oChunk.iChildCount do ( case oChunk.aChildren[i].sType of ( "FOLDMESH": Corsix_RgmIntoMax_FoldMesh oRgm oChunk.aChildren[i] ) i += 1 ) ) /* Handles a FOLDMESH chunk from an RGM file oRgm - the RGM file oChunk - the FOLDMESH chunk returns - nothing */ fn Corsix_RgmIntoMax_FoldMesh oRgm oChunk = ( local i = 1 while i <= oChunk.iChildCount do ( case oChunk.aChildren[i].sType of ( "FOLDMGRP": Corsix_RgmIntoMax_FoldMgrp oRgm oChunk.aChildren[i] "FOLDMRGM": Corsix_RgmIntoMax_FoldMrgm oRgm oChunk.aChildren[i] "FOLDTRIM": Corsix_RgmIntoMax_FoldTrim oRgm oChunk.aChildren[i] ) i += 1 ) ) /* Handles a FOLDTRIM chunk from an RGM file oRgm - the RGM file oChunk - the FOLDMESH chunk returns - nothing */ fn Corsix_RgmIntoMax_FoldTrim oRgm oChunk = ( iDataDataCount = 0 local i = 1 while i <= oChunk.iChildCount do ( case oChunk.aChildren[i].sType of ( "DATADATA": if iDataDataCount == 0 then ( Corsix_RgmIntoMax_FoldTrim_DataData oRgm oChunk.aChildren[i] oChunk.sName iDataDataCount = 1 ) ) i += 1 ) ) /* Handles a FOLDSKEL chunk from an RGM file (Skeleton data) oRgm - the RGM file oChunk - the FOLDSKEL chunk returns - nothing */ fn Corsix_RgmIntoMax_FoldMesh_FoldSkel oRgm oChunk = ( local i = 1 while i <= oChunk.iChildCount do ( case oChunk.aChildren[i].sType of ( "DATABONE": Corsix_RgmIntoMax_FoldMesh_FoldSkel_DataBone oRgm oChunk.aChildren[i] ) i += 1 ) ) /* Handles a DATABONE chunk from an RGM file (Bone data) oRgm - the RGM file oChunk - the DATABONE chunk sName - the name of the bone returns - nothing */ fn Corsix_RgmIntoMax_FoldMesh_FoldSkel_DataBone oRgm oChunk sName = ( fseek oRgm.fFile (oChunk.iDataPosition + 1) #seek_set -- jump to chunk ) /* Handles a FOLDMTRL chunk from an RGM file oRgm - the RGM file oChunk - the FOLDMTRL chunk returns - nothing */ fn Corsix_RgmIntoMax_FoldMtrl oRgm oChunk = ( /* Handles a DATAINFO chunk from an RGM file (Shader Names) oRgm - the RGM file oChunk - the DATAINFO chunk returns - nothing */ fn Corsix_RgmIntoMax_FoldMtrl_DataInfo oRgm oChunk = ( fseek oRgm.fFile oChunk.iDataPosition #seek_set -- jump to chunk local iStrLen = ReadLong oRgm.fFile #unsigned sShaderName = Corsix_ReadStringN oRgm.fFile iStrLen -- read the shader name print (" Shader: " + sShaderName as string + "\n") -- and spit it inot the listener ) /* Handles a DATAVAR chunk from an RGM file (Material Variables) oRgm - the RGM file oChunk - the DATAVAR chunk returns - nothing */ fn Corsix_RgmIntoMax_FoldMtrl_DataVar oRgm oChunk = ( fseek oRgm.fFile oChunk.iDataPosition #seek_set -- jump to chunk local iStrLen = ReadLong oRgm.fFile #unsigned sTexType = Corsix_ReadStringN oRgm.fFile iStrLen Print (" Texture type: " + sTexType as string) fseek oRgm.fFile 4 #seek_cur -- skip unknown 4 bytes local iStrLen = ReadLong oRgm.fFile #unsigned sTexPath = Corsix_ReadStringN oRgm.fFile iStrLen if sTexType != "camouflagecolour" and sTexType != "teamcolour" and sTexType != "diffuse_offsetu" and sTexType != "diffuse_offsetv" and sTexType != "dirtvisibility" and sTexType != "diffuseOffsetScale" and sTexType != "material_specular" and sTexType != "material_specularExp" then -- these ones don't have texture paths. Just colour values, offset values etc. ( Print (" Path: " + sTexPath as string + "\n") ) texturepath = (sCohPath as string + "\\DataSrc\\" + sModFolder as string + "\\" + sTexPath + ".tga") -- construct the texture path texture_map = bitmaptexture filename:texturepath -- new bitmap texture to hold our newly read path case sTexType of ( "diffusetex": oMaterial.diffusetex = texture_map -- only handle the ones that have texture paths "normalmap": oMaterial.normalmap = texture_map "teamtex": oMaterial.teamtex = texture_map "occlusiontex": oMaterial.occlusiontex = texture_map "glosstex": oMaterial.glosstex = texture_map "speculartex": oMaterial.speculartex = texture_map ) global mCorsixMaterial = standardMaterial diffuseMap: oMaterial.diffusetex name:(aMatIDArray[aMatIDArray.count] as string) specularMap:oMaterial.speculartex glossinessMap:oMaterial.glosstex showInViewport:true mCorsixMultiMat[mCorsixMultiMat.numsubs] = mCorsixMaterial mCorsixMultiMat.names[mCorsixMultiMat.numsubs] = aMatIDArray[aMatIDArray.count] setMeditMaterial 1 mCorsixMultiMat -- set mCorsixMultiMat to slot one in the ME ) local i = 1 while i <= oChunk.iChildCount do ( case oChunk.aChildren[i].sType of ( "DATAVAR": Corsix_RgmIntoMax_FoldMtrl_DataVar oRgm oChunk.aChildren[i] "DATAINFO": Corsix_RgmIntoMax_FoldMtrl_DataInfo oRgm oChunk.aChildren[i] ) i += 1 ) ) /* Handles a FOLDMODL chunk from an RGM file oRgm - the RGM file oChunk - the FOLDMODL chunk returns - nothing */ fn Corsix_RgmIntoMax_FoldModl oRgm oChunk = ( local i = 1 local iMatID = 0 while i <= oChunk.iChildCount do ( case oChunk.aChildren[i].sType of ( "FOLDMESH": Corsix_RgmIntoMax_FoldMesh oRgm oChunk.aChildren[i] "FOLDSKEL": Corsix_RgmIntoMax_FoldMesh oRgm oChunk.aChildren[i] "FOLDMTRL": ( iMatID += 1 Print "----------------------------------------------" print (" Material ID Name: " + oChunk.aChildren[i].sName as string) append aMatIDArray oChunk.aChildren[i].sName print (" Material ID: " + iMatID as string) struct sShaderTexturesStruct ( diffusetex = noMaterial(), -- noMaterial() will set the texture to 'none in the ME. Other wise we will get undefined errors teamtex = noMaterial(), normalmap = noMaterial(), occlusiontex = noMaterial(), glosstex = noMaterial(), speculartex = noMaterial() ) global oMaterial = sShaderTexturesStruct() -- an object to hold each material as we read them Corsix_RgmIntoMax_FoldMtrl oRgm oChunk.aChildren[i] -- handle the FOLDMTRL and its sub chunks mCorsixMultiMat.numsubs += 1 -- another one please! Print "----------------------------------------------" ) ) i += 1 ) ) /* Handles an RGM file oRgm - the RGM file returns - nothing */ fn Corsix_RgmIntoMax oRgm = ( local i = 1 while i <= oRgm.iChunkCount do ( case oRgm.aChunks[i].sType of ( "FOLDMODL": Corsix_RgmIntoMax_FoldModl oRgm oRgm.aChunks[i] ) i += 1 ) ) /* Cleans up the scene after import */ fn Corsix_RgmIntoMax_SceneCleanup oRgm = ( mCorsixMultiMat.numsubs -= 1 -- remove empty multimaterial stub viewport.setLayout #layout_1 -- set all sorts of viewport related things viewport.setType #view_persp_user viewport.SetRenderLevel #smooth viewport.SetShowEdgeFaces true max tool zoomextents all if iDataDataCount != 0 then ( Print (sModelName as string + " imported successfully!") ) else ( messagebox "All meshes X-Reffed. No meshes to be loaded in file." title:"RGM importer" ) ) /* Cleans up the scene after directory and recursive directory import */ fn Corsix_RgmIntoMax_SceneCleanup_DirImport oRgm = ( mCorsixMultiMat.numsubs -= 1 -- remove empty multimaterial stub viewport.setLayout #layout_1 -- set all sorts of viewport related things viewport.setType #view_persp_user viewport.SetRenderLevel #smooth viewport.SetShowEdgeFaces true max tool zoomextents all if iDataDataCount != 0 then ( Print (sModelName as string + " imported successfully!") ) else ( (Print "Import failed. All meshes X-REFFED. No meshes to load in file: " + sModelName as string) ) ) /* Recursively gets all RGM files under the root directory (from maxscript docs. No use in reinventing the wheel) */ fn Corsix_RgmIntoMax_RecDirFileFind dirRecDir = ( aDirectories = GetDirectories (dirRecDir + "/*") for d in aDirectories do ( join aDirectories (GetDirectories (d + "/*")) ) aFiles = #() for f in aDirectories do ( join aFiles (getFiles (f + "*.rgm")) ) return aFiles ) /* Graphical user interface stuff */ utility Corsix_RGM_Import "RGM Import" width:166 height:137 ( button btn_RGM_Import "Import RGM" pos:[13,5] width:140 height:21 toolTip:"Import an RGM file" button btn_Dir_Import "RGM Directory Import" pos:[13,32] width:140 height:21 toolTip:"Import all RGM files in a folder" button btn_RecDir_Import "Recursive Directory Import" pos:[13,58] width:140 height:21 toolTip:"Import RGM files from a directory recursively" button btn_Set_CoH_Directory "Set CoH Dir" pos:[13,84] width:140 height:21 toolTip:"Set the location of your Company of Heroes directory" button btn_Set_Mod_Directory "Set Mod Dir" pos:[13,110] width:140 height:21 toolTip:"Set the location of your mod directory" on btn_RGM_Import pressed do ( ClearListener() global sModelName = "" global iDataDataCount = 0 -- global variable to check if there are any DATADATA chunks in a file global aMatIDArray = #() -- global array to hold materials and their IDs global mCorsixMultiMat = MultiMaterial numsubs:1 -- a nice new multimaterial global sCohPath = getINISetting (Corsix_RGMImportIni()) "Directories" "Company_Of_Heroes" -- load various bits of info from the ini file global sModFolder = getINISetting (Corsix_RGMImportIni()) "Directories" "Mod_Folder" global sLastFile = getINISetting (Corsix_RGMImportIni()) "Files" "Last_File" local sFilename = getOpenFileName caption:"Select a RGM file to import" filename:sLastFile types:"RGM (*.rgm)|*.rgm" if sFilename != undefined then ( setINISetting (Corsix_RGMImportIni()) "Files" "Last_File" sFilename sModelName = getFilenameFile sFilename local oRgm = Corsix_Chunky() -- Yay! New object! if (oRgm.loadFromFile sFilename) == false then -- Boo. We failed. ( messagebox "Unable to load file" ) else ( resetMaxFile #noPrompt -- reset max if sCohPath != undefined then ( local sSavePath = getSavePath caption:"Select a where you want to save your file" initialDir:sCohPath if sSavePath != undefined then ( Makedir (sSavePath + "Model\\") Makedir (sSavePath + "Animations\\") Corsix_RgmIntoMax oRgm -- IMPORT! Corsix_RgmIntoMax_SceneCleanup oRGM -- contains all the post import functions SaveMaxfile (sSavePath + "Model\\Reference.max") ) else ( messagebox "The model will be loaded but not saved" title:"RGM importer" -- Ah! The joys of messageboxes! Corsix_RgmIntoMax oRgm Corsix_RgmIntoMax_SceneCleanup oRGM ) ) else ( messagebox "Please locate your Company of Heroes directory and try again" title:"RGM importer" ) ) ) ) on btn_Dir_Import pressed do ( ClearListener() global sModelName = "" global iDataDataCount = 0 -- global variable to check if there are any DATADATA chunks in a file global aMatIDArray = #() -- global array to hold materials and their IDs global mCorsixMultiMat = MultiMaterial numsubs:1 -- a nice new multimaterial global sCohPath = getINISetting (Corsix_RGMImportIni()) "Directories" "Company_Of_Heroes" -- load various bits of info from the ini file global sModFolder = getINISetting (Corsix_RGMImportIni()) "Directories" "Mod_Folder" global sLastFile = getINISetting (Corsix_RGMImportIni()) "Files" "Last_File" local sRgmImportFolder = getSavePath caption:"Locate folder to import RGMs from..." initialDir:sCohPath local sRgmSaveFolder = getSavePath caption:"Locate location to save loaded files..." initialDir:sCohPath if sRgmImportFolder != undefined and sRgmSaveFolder != undefined then ( aRgmImportFiles = getFiles (sRgmImportFolder + "\\*.RGM") for fFile in aRgmImportFiles do ( sModelName = getFilenameFile fFile local oRgm = Corsix_Chunky() if (oRgm.loadFromFile fFile) == false then -- Boo. We failed. ( messagebox "Unable to load file" ) else -- it worked ( resetMaxFile #noPrompt if sCohPath != undefined then ( Makedir (sRgmSaveFolder + "\\" + sModelName + "\\") Makedir (sRgmSaveFolder + "\\" + sModelName + "\\Model\\") Makedir (sRgmSaveFolder + "\\" + sModelName + "\\Animations\\") Corsix_RgmIntoMax oRgm -- IMPORT! Corsix_RgmIntoMax_SceneCleanup_DirImport oRGM -- contains directory import specific cleanup functions SaveMaxfile (sRgmSaveFolder + "\\" + sModelName + "\\Model\\Reference.max") Print "\n" ) else ( messagebox "Please locate your Company of Heroes directory and try again" title:"RGM importer" ) ) ) resetMaxFile #noPrompt messagebox "All models imported and saved successfully!" title:"Directory Import" ) else ( messagebox "Please locate both the folder to import and the folder to save to and try again." title:"Directory Import" ) ) on btn_RecDir_Import pressed do ( ClearListener() global sModelName = "" global iDataDataCount = 0 -- global variable to check if there are any DATADATA chunks in a file global aMatIDArray = #() -- global array to hold materials and their IDs global mCorsixMultiMat = MultiMaterial numsubs:1 -- a nice new multimaterial global sCohPath = getINISetting (Corsix_RGMImportIni()) "Directories" "Company_Of_Heroes" -- load various bits of info from the ini file global sModFolder = getINISetting (Corsix_RGMImportIni()) "Directories" "Mod_Folder" global sLastFile = getINISetting (Corsix_RGMImportIni()) "Files" "Last_File" local sRgmImportFolder = getSavePath caption:"Locate top level folder to import RGMs from..." initialDir:sCohPath local sRgmSaveFolder = getSavePath caption:"Locate location to save loaded files..." initialDir:sCohPath if sRgmImportFolder != undefined and sRgmSaveFolder != undefined then ( local aRgmImportFiles = Corsix_RgmIntoMax_RecDirFileFind sRgmImportFolder for fFile in aRgmImportFiles do ( sModelName = getFilenameFile fFile local oRgm = Corsix_Chunky() if (oRgm.loadFromFile fFile) == false then -- Boo. We failed. ( messagebox "Unable to load file" ) else -- it worked ( resetMaxFile #noPrompt if sCohPath != undefined then ( Makedir (sRgmSaveFolder + "\\" + sModelName + "\\") Makedir (sRgmSaveFolder + "\\" + sModelName + "\\Model\\") Makedir (sRgmSaveFolder + "\\" + sModelName + "\\Animations\\") Corsix_RgmIntoMax oRgm -- IMPORT! Corsix_RgmIntoMax_SceneCleanup_DirImport oRGM -- contains directory import specific cleanup functions SaveMaxfile (sRgmSaveFolder + "\\" + sModelName + "\\Model\\Reference.max") Print "\n" ) else ( messagebox "Please locate your Company of Heroes directory and try again" title:"RGM importer" ) ) ) resetMaxFile #noPrompt messagebox "All models imported and saved successfully!" title:"Recursive Directory Import" ) else ( messagebox "Please locate both the folder to import and the folder to save to and try again." title:"Recursive Directory Import" ) ) on btn_Set_CoH_Directory pressed do ( local sCohPath = getSavePath caption:"Locate Company of Heroes directory" if sCohPath != undefined then ( setINISetting (Corsix_RGMImportIni()) "Directories" "Company_Of_Heroes" sCohPath ) ) on btn_Set_Mod_Directory pressed do ( local sCohFolder = getINISetting (Corsix_RGMImportIni()) "Directories" "Company_Of_Heroes" local sModFolder = getSavePath "Locate mod directory" sCohFolder if sModFolder != undefined then ( local aModFolder = filterString sModFolder "\\" sModFolder = aModFolder[aModFolder.count] setINISetting (Corsix_RGMImportIni()) "Directories" "Mod_Folder" sModFolder ) ) )