All pastes #522307 Raw Edit

RGM Importer Maxscript

public text v1 · immutable
#522307 ·published 2007-05-30 11:40 UTC
rendered paste body
/*
	Corsix and fneep's RGM Tools for Company of Heroes
	Copyright (C) 2006 Corsix (and fneep :D)
	
	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	 
*/

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 = #(),
		aUVArray = #(),
		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()
	)
	
	oMeshInfo = sGlobalMeshInfo() -- holds all mesh data
	
	local iObjectCount = ReadLong oRgm.fFile #unsigned -- number of separate objects
	
	aObjectList = #()
	
	for i=1 to iObjectCount do
	(
		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
	)
	/*
		Unknowns
	*/
	local iUnknownCount = ReadLong oRgm.fFile #unsigned
	for i=1 to iUnknownCount do -- skip the rest of the data (as yet unknown)
	(
		local aUnknown = point3 0 0 0
		
		aUnknown.x = ReadLong oRgm.fFile
		aUnknown.z = ReadLong oRgm.fFile
		aUnknown.y = ReadLong oRgm.fFile			
	)

	
	/*
		Verticies
	*/
	local aVerticies = #()
	oMeshInfo.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 oMeshInfo.iVertCount do
	(
		local aVertex = point3 0 0 0

		aVertex.x = -(ReadFloat oRgm.fFile)
		aVertex.z = ReadFloat oRgm.fFile
		aVertex.y = -(ReadFloat oRgm.fFile)
		
		append aVerticies [aVertex.x, aVertex.y, aVertex.z]

		for i=1 to (iVertexSize - 12) do -- skip the rest of the data (as yet unknown)
		(
		temp1 = ReadByte oRgm.fFile
		)
	)
	oMeshInfo.aVertArray = aVerticies -- we have collected all the vert data in aVerticies. We now need to add this to the global mesh structure
	VertUnknown = ReadLong oRgm.fFile

	/*
		Faces and Material IDs
	*/
	local aFaces = #()
	local iFaceCount = ReadLong oRgm.fFile #unsigned
	FaceUnknown = ReadFloat oRgm.fFile
	FaceUnknown = ReadFloat 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 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
	oMeshInfo.aFaceArray = aFaces

	/*
		Organise the verts for the per object mesh construction
	*/
	
	for oTempObject in aObjectList do
	(
		aRelativeFaceArray = #()
		for i=1 to oTempObject.sOIDStruct.aFaceList.count do
		(
			aVert = oTempObject.sOIDStruct.aFaceList[i] -- find the vert
			
			----------[ X Vert ]----------
			aRelativeVert = oMeshInfo.aVertArray[aVert.x]
			if (finditem oTempObject.sOIDStruct.aVertArray aRelativeVert) == 0 then
			(
				append oTempObject.sOIDStruct.aVertArray aRelativeVert 	 -- and send it to the correct object structure
			)
			----------[ Y Vert ]----------
			aRelativeVert = oMeshInfo.aVertArray[aVert.y]
			if (finditem oTempObject.sOIDStruct.aVertArray aRelativeVert) == 0 then
			(
				append oTempObject.sOIDStruct.aVertArray aRelativeVert
			)

			----------[ Z Vert ]----------
			aRelativeVert = oMeshInfo.aVertArray[aVert.z]
			if (finditem oTempObject.sOIDStruct.aVertArray aRelativeVert) == 0 then
			(
				append oTempObject.sOIDStruct.aVertArray aRelativeVert
			)

		)
			for oFace in oTempObject.sOIDStruct.aFaceList do
			(
				iRelativeFace = point3 0 0 0 -- new face
				iRelativeFace.x = finditem oTempObject.sOIDStruct.aVertArray oMeshInfo.aVertArray[oFace.x] -- find the vertex from our global vert array
				iRelativeFace.y = finditem oTempObject.sOIDStruct.aVertArray oMeshInfo.aVertArray[oFace.y]
				iRelativeFace.z = finditem oTempObject.sOIDStruct.aVertArray oMeshInfo.aVertArray[oFace.z]
				
				append aRelativeFaceArray iRelativeFace
			)	
			
		oTempObject.sOIDStruct.aFaceList = aRelativeFaceArray
	)	

	/*
		Construct Meshes
	*/

	for oTempObject in aObjectList do
	(
		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
		
		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 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 = ReadLong 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
		)
	)
)