Diverse
3D Engine
3D Links
Wow Importer
WoW Importer 2
WoW Importer 2 for Max 9
WoW Importer 3
 
Diverse Jme Mangalore Nebula Project1 Project2 Tabletop

Extended World of Warcraft M2 Model importer for 3D Studio Max 9


This is a script is provided by Arthur as an update of the WoW Importer V2. The script has been modified to be running on max 9. The code is provided as is without any warranties. Use at your own risk.

You can find some other needfull thing like a wow model viewer on www.wowmodelviewer.org



-- Script to import M2 files found in the mpq files of world of warcraft
-- into GMax or 3d studio max. The script loads vertices with uv coords 
-- and submeshes as found in the M2 file. Textures are not loaded but can be 
-- assigned in a later step manually. The submeshes are created with dummy cubes 
-- as bones for skinning.


-- INSTALLATION
-- Place me in 3dsmax7\stdplugs\stdscripts\
-- restart 3ds max, find WOW menu on main menu bar

-- If menu item don't work normaly or do not exists use RunScript button from MaxScript menu and run this scipt file.


-- UI work and animation bug fixed by me (aka DeLazarus), April 2006.
-- Modifications was tested only on 3ds max 7

-- Some models imports v. Be brary slowly. Be brave :)

-- Injoy!






-- GLOBALS AND STRUCTURES


--global filename = "c:\\tmp\\Horse.m2" --HighElfMale_Warrior.m2" --Ghoul.m2"
global filename = "d:\m2files\cat.m2"--"D:\\mpq\\succubus\\Succubus.m2" --HighElfMale_Warrior.m2" --Ghoul.m2"

-- there are no any warrnings of situation like this: LoadAnimation=true LoadBones=false, UI must work over this
-- and btw some models do not load if bones are not loaded. That is why I froze its check box
global LoadBones = true
global LoadAnimation = false

global head
global bstream
global step

global verts_read 
global views_read
global view_inds_read
global view_tris_read 
global bones_read 
global name_read
global sequences_read 
global animations_read 

fn InitLoader = 
(
	verts_read = #()
	views_read = #()
	view_inds_read = #()
	view_tris_read = #()
	bones_read = #()
	name_read = ""
	sequences_read = #()
	animations_read = #()
)

struct WOW2_Header
(
	magic,version,namelen,ofsname,
	un1,nGlobalSequences,ofsGlobalSequences,nAnimations,
	ofsAnimations,nC,ofsC,nD,ofsD,nBones,ofsBones,nF,ofsF,
	nVertices,ofsVertices,nViews,ofsViews,nG,ofsG,nTextures,
	ofsTextures,nH,ofsH,nI,ofsI,nJ,ofsJ,nK,ofsK,nX,ofsX,
	nBoneGroups,ofsBoneGroups,nTexLookup,ofsTexLookup,nL,ofsL,nM,ofsM,nN,
	ofsN,n14floats,nBoundingTriangles,ofsBoundingTriangles,
	nBoundingVertices,ofsBoundingVertices,nBoundingNormals,
	ofsBoundingNormals,nO,ofsO,nP,ofsP,nQ,ofsQ,nR,ofsR,nS,
	ofsS,nT,ofsT,nU,ofsU,nV,ofsV
	--nG Magic1 ofsG Magic2 
)

struct WOW2_Vertex
(
	pos,bw1,bw2,bw3,bw4,bi1,bi2,bi3,bi4,normal,uv,n2floats
)

struct WOW2_Views
(
	nindex,ofsnindex, ntris,ofsntris,nverts,ofsnverts,
	nsubmesh,ofsnsubmesh,ntextures,ofsntextures,unknown1,
	ind,tri -- loaded vert and tri index
)

-- nAnimations 
struct WOW2_Animations
(
	animid,startseq,endseq,speed,unknown1,unknown2,unknown3,
	unknown4,unknown5,bbox,radius,unknown6,unknown7
)

-- the actual animation info used for different animation types
struct WOW2_Anim
(
	type,flags,linetypes,linetypeofs,frames,framesofs,data,dataofs
)

-- bones including animation data
struct WOW2_Bones
(
	geosetanim,flags,parentbone,unknown1,geoset,
	trans,rot,scale,pivot,maxname
)

struct WOW2_BoneWeightIndex
(	cnt,bw,bi
)
-- one submesh part with its verts stored in vs, faces in fs, textures in ts 
-- and boneidexweights in bs, bones holds bone raw data
struct WOW2_Submesh
(
	id,ofsvert,nverts,oftri,tris,unknown1,unknown2,
	unknown3,unknown4,n3floats1,n3floats2,
	vs,fs,ts,bs,bones
)

struct WOW2_Texture
(
	flags,submesh1,submesh2,color,texpass,texunit,unknown1,
	lookupindex,texunit2,lightemitt
)

struct WOW2_Texturefile
(
	-- type:
	--0  Texture given in filename  
	--1  Body + clothes  
	--2  Cape  
	--6  Hair, beard  
	--8  Tauren fur  
	--11  Skin for creatures  

	type,flags,namelen,ofsname  
)

fn echo msg =
(
	format "%\n" (msg) to:listener
)

fn ReadFixedString bstream fixedLen=
(
	local str = ""
	for i = 1 to fixedLen do
	(
		str += bit.intAsChar (ReadByte bstream #unsigned)
	)
	str
)

fn SkipBytes bstream count=
(
	local unknown
	case count of
	(
		2: unknown = ReadShort bstream #unsigned
		4: unknown = ReadLong bstream #unsigned
		default:
		(
			for i = 1 to count do
			(
				unknown = ReadByte bstream #unsigned
			)
		)
	)
)
	
fn LongToString num=
(
	local str = ""
	for i = 1 to 4 do
	(
		str += bit.intAsChar (bit.and num 0xff)
		-- num = bit.shift num -8
		num /= 256
	)
	str
)



fn WOW2_Open fname =
(
		bstream = fopen fname "rb"
		head = WOW2_Header ()
 )	 
	
fn WOW2_Close =
(
	step = "Close"
	fclose bstream
)
	
	
	fn WOW2_Read_Header =
	(
		if head == undefined or bstream == undefined then
			echo "Nothing to read"

		format "reading\n" to:listener
		step = "Read header"
		head.magic = ReadLong bstream #unsigned
		echo ("4cc: "+(LongToString head.magic))
		head.version = ReadLong bstream #unsigned
		echo ("Version: "+(LongToString head.version))
		head.namelen = ReadLong bstream #unsigned
		--echo ("Name len:"+ (head.namelen as string))
		head.ofsname = ReadLong bstream #unsigned
		--echo ("Name offs:"+ head.ofsname as string)
		head.un1 = ReadLong bstream #unsigned
		--echo (LongToString head.un1)
		--echo ("before glob ftell: "+(ftell bstream) as string)
		head.nGlobalSequences = ReadLong bstream #unsigned
		--echo ("Global seq:"+head.nGlobalSequences as string)
		head.ofsGlobalSequences = ReadLong bstream #unsigned
		--echo ("Global seq ofs:"+ head.ofsGlobalSequences as string)
		--echo ("before anims: "+(ftell bstream) as string)
		head.nAnimations = ReadLong bstream #unsigned
		--echo ("Anims :"+ head.nAnimations as string)
		head.ofsAnimations = ReadLong bstream #unsigned
		--echo ("Anims ofs:" + head.ofsAnimations as string)
		head.nC = ReadLong bstream #unsigned
		--echo (LongToString head.nC)
		head.ofsC = ReadLong bstream #unsigned
		--echo ("ofsC:" + head.ofsC as string)
		head.nD = ReadLong bstream #unsigned
		--echo (LongToString head.nD)
		head.ofsD = ReadLong bstream #unsigned
		--echo ("ofsD:" + head.ofsD as string)
		head.nBones = ReadLong bstream
		--echo ("Bones:"+head.nBones as string)
		head.ofsBones = ReadLong bstream #unsigned
		--echo ("Bones ofs:"+head.ofsBones as string)
		head.nF = ReadLong bstream #unsigned
		--echo ("nF:" + head.nF as string)
		head.ofsF = ReadLong bstream #unsigned
		--echo ("ofsF:" + head.ofsF as string)
		--echo ("before verts ftell: "+(ftell bstream) as string)
		head.nVertices = ReadLong bstream #unsigned
		--echo ("Vertices:"+head.nVertices as string)
		head.ofsVertices = ReadLong bstream #unsigned
		--echo ("Vertices of:"+head.ofsVertices as string)
		head.nViews = ReadLong bstream #unsigned
		--echo ("Views:"+head.nViews as string)
		head.ofsViews = ReadLong bstream #unsigned
		--echo ("ofsViews:"+head.ofsViews as string)
		head.nG = ReadLong bstream #unsigned
		head.ofsG = ReadLong bstream #unsigned
		--echo ("ofsG:"+head.ofsG as string)
		head.nTextures = ReadLong bstream #unsigned
		--echo ("Textures:"+head.nTextures as string)
		head.ofsTextures = ReadLong bstream #unsigned
		--echo ("ofsTex:"+head.ofsTextures as string)
		head.nH = ReadLong bstream #unsigned
		head.ofsH = ReadLong bstream #unsigned
		--echo ("ofsH:"+head.ofsH as string)
		head.nI = ReadLong bstream #unsigned
		head.ofsI = ReadLong bstream #unsigned
		--echo ("ofsI:"+head.ofsI as string)
		head.nJ = ReadLong bstream #unsigned
		head.ofsJ = ReadLong bstream #unsigned
		--echo ("ofsJ:"+head.ofsJ as string)
		head.nK = ReadLong bstream #unsigned
		head.ofsK = ReadLong bstream #unsigned
		--echo ("ofsK:"+head.ofsK as string)
		head.nX = ReadLong bstream #unsigned
		head.ofsX = ReadLong bstream #unsigned
		--echo ("ofsX:"+head.ofsX as string)
		head.nBoneGroups = ReadLong bstream #unsigned
		head.ofsBoneGroups = ReadLong bstream #unsigned
		echo ("BoneGroups:"+head.nBoneGroups as string)
		head.nTexLookup = ReadLong bstream #unsigned
		--echo ("TexLookup:"+head.nTexLookup as string)
		head.ofsTexLookup = ReadLong bstream #unsigned
		--echo ("ofsTexlkup:"+head.ofsTexLookup as string)
		head.nL = ReadLong bstream #unsigned
		head.ofsL = ReadLong bstream #unsigned
		--echo ("ofsL:"+head.ofsL as string)
		head.nM = ReadLong bstream #unsigned
		head.ofsM = ReadLong bstream #unsigned
		--echo ("ofsM:"+head.ofsM as string)
		head.nN = ReadLong bstream #unsigned
		head.ofsN = ReadLong bstream #unsigned
		---echo ("ofsN:"+head.ofsN as string)
		head.n14floats = undefined
		for i = 1 to 14 do
		( 
			undef = ReadFloat bstream
		)
		head.nBoundingTriangles = ReadLong bstream #unsigned
		head.ofsBoundingTriangles = ReadLong bstream #unsigned
		--echo ("ofsBoundTri:"+head.ofsBoundingTriangles as string)
		head.nBoundingVertices = ReadLong bstream #unsigned
		head.ofsBoundingVertices = ReadLong bstream #unsigned
		--echo ("ofsBoundingVerts:"+head.ofsBoundingVertices as string)
		head.nBoundingNormals = ReadLong bstream #unsigned
		head.ofsBoundingNormals = ReadLong bstream #unsigned
		--echo ("ofsBoundNormals:"+head.ofsBoundingNormals as string)
		head.nO = ReadLong bstream #unsigned
		head.ofsO = ReadLong bstream #unsigned
		--echo ("ofsO:"+head.ofsO as string)
		head.nP = ReadLong bstream #unsigned
		head.ofsP = ReadLong bstream #unsigned
		--echo ("ofsP:"+head.ofsP as string)
		head.nQ = ReadLong bstream #unsigned
		head.ofsQ = ReadLong bstream #unsigned
		--echo ("ofsQ:"+head.ofsQ as string)
		head.nR = ReadLong bstream #unsigned
		head.ofsR = ReadLong bstream #unsigned
		--echo ("ofsR:"+head.ofsR as string)
		head.nS = ReadLong bstream #unsigned
		head.ofsS = ReadLong bstream #unsigned
		--echo ("ofsS:"+head.ofsS as string)
		head.nT = ReadLong bstream #unsigned
		head.ofsT = ReadLong bstream #unsigned
		--echo ("ofsT:"+head.ofsT as string)
		head.nU = ReadLong bstream #unsigned
		head.ofsU = ReadLong bstream #unsigned
		--echo ("ofsU:"+head.ofsU as string)
		head.nV = ReadLong bstream #unsigned
		head.ofsV   = ReadLong bstream #unsigned
		--echo ("ofsV:"+head.ofsV as string)
		echo "---- Header finished ----"
		ok
	)


fn WOW2_Read_Name =
(
	step = "Read Name"
	if (fseek bstream head.ofsname #seek_set ) then
	(
		name_read = (ReadFixedString bstream head.namelen)
		echo name_read	
	)
	else
		echo "unable to read name"
)

fn WOW2_Read_Verts =
(
	step = "Read Verts"
	if (fseek bstream head.ofsVertices #seek_set ) then
	(
		step = "Read verts prep"
		
		for i=1 to head.nVertices  do
		(
			local vert = WOW2_Vertex ()
			local v4 = [0.0,0.0,0.0]
			local v3 = [0.0,0.0,0.0]
			local v2 = [0.0,0.0,0.0]
			local v1 = [0.0,0.0]
			
			v4.x = ReadFloat bstream
			v4.y = ReadFloat bstream
			v4.z = ReadFloat bstream
			vert.pos = v4
			vert.bw1 = ReadByte bstream #unsigned
			vert.bw2 = ReadByte bstream #unsigned
			vert.bw3 = ReadByte bstream #unsigned
			vert.bw4 = ReadByte bstream #unsigned
			vert.bi1 = ReadByte bstream #unsigned
			vert.bi2 = ReadByte bstream #unsigned
			vert.bi3 = ReadByte bstream #unsigned
			vert.bi4 = ReadByte bstream #unsigned
			v3.x = ReadFloat bstream
			v3.y = ReadFloat bstream
			v3.z = ReadFloat bstream
			vert.normal = v3
			v2.x = ReadFloat bstream
			v2.y = ReadFloat bstream
			v2.z = 0.0 --is empty (0.0)
			vert.uv = v2
			v1.x = ReadFloat bstream
			v1.y = ReadFloat bstream
			vert.n2floats = v1

			append verts_read vert
		)	
		
--		echo ("Verts read: " + verts_read.count as string )
	)
	else
		echo "unable to read vertices"
)

fn WOW2_Read_Views =
(
	step = "Read Views"
	if (fseek bstream head.ofsViews #seek_set ) then
	(
		for i=1 to head.nViews  do
		(
			local view = WOW2_Views ()
			view.nindex = ReadLong bstream #unsigned
			view.ofsnindex = ReadLong bstream #unsigned
			view.ntris = ReadLong bstream #unsigned
			view.ofsntris = ReadLong bstream #unsigned
			view.nverts = ReadLong bstream #unsigned
			view.ofsnverts = ReadLong bstream #unsigned 			
			view.nsubmesh = ReadLong bstream #unsigned
			view.ofsnsubmesh = ReadLong bstream #unsigned
			view.ntextures = ReadLong bstream #unsigned
			view.ofsntextures = ReadLong bstream #unsigned
			view.unknown1 = ReadLong bstream #unsigned
			
			append views_read view
			step = "Read Views Mod 3"
			if((mod (view.ntris) 3) != 0.0 )then
				echo ("  View["+i as string+"].ntris is not a multiple of 3!")
			
			if false then --i == 1 then
			(	echo ("\nView nind  : "+view.nindex as string)
				echo ("View ofsind : "+view.ofsnindex as string)
				echo ("View ntris : "+view.ntris as string)
				echo ("View ofsntris : "+view.ofsntris as string)
				echo ("View nverts: "+view.nverts as string)
				echo ("View ofsnverts : "+view.ofsnverts as string)
				echo ("View nsubm : "+view.nsubmesh as string)
				echo ("View ntex  : "+view.ntextures as string)
			)
		)
	)
	else
		echo "unable to read views"
		
	step = "Read View index lists"
	--load tri and vert index lists
	for i=1 to views_read.count do
	(
		local ind = #()
		local tri = #()
		step = "Read View index"
		if (fseek bstream (views_read[i].ofsnindex) #seek_set ) then
		(
			for j = 1 to views_read[i].nindex do
			(
				local x = ReadShort bstream #unsigned
				append ind x
			)
			views_read[i].ind = ind
		)
		else
			echo "unable to read view indices"
		
		step = "Read View faces"
		if (fseek bstream views_read[i].ofsntris #seek_set ) then
		(
			for j= 1 to views_read[i].ntris do
			(
				local x = ReadShort bstream #unsigned
				append tri x
			)
			views_read[i].tri = tri
		)
		else
			echo "unable to read view faces"
	)
)

/*
fn WOW2_Read_Bone_Groups =
(
--nBoneGroups,ofsBoneGroups
	bonegroups = #()
--echo ("bonegroups:"+head.nBoneGroups as string)
	if (fseek bstream head.ofsBoneGroups #seek_set ) then
	(
		for i = 1 to head.nBoneGroups do
		(
			local id = (ReadShort bstream)
--echo ("bonegroupID:"+id as string)
			append bonegroups id
		)
	)
	
	return bonegroups
)
*/
fn WOW2_Create_Bones bonearr =
(
	step = "Bone creation"
	-- previous bone must have been created
	for i = 1 to bonearr.count do
	(
		Dummy position:(bonearr[i].pivot) isSelected:on name:(bonearr[i].maxname)
		$.boxsize = [0.05,0.05,0.05]
		--$.pos.controller = TCB_position ()
		$.rotation.controller = Local_Euler_XYZ ()
		--$.scale.controller = TCB_scale ()
		if (bonearr[i].parentbone >= 0) then
		(
			$.parent = getNodeByName \
				(bonearr[(bonearr[i].parentbone + 1)].maxname) \
				exact:true
				
			echo ($.parent as string)
		)
	)
)
-- read bone animation data frames
-- bone arrays should be already filled
-- @param filled WOW2_Bones array 
-- @TODO finish this function
fn WOW2_CreateBoneScaleAnim ba =
(
	--echo(ba.maxname)
	
	if ba.scale.frames > 0 then
	(
		step = "Framesoffset reading"
		if (fseek bstream ba.scale.framesofs #seek_set ) then
		(
			local t = #()
			local maxx = 0
			local x = 0
			currobj = getNodeByName (ba.maxname) exact:true
			step = "Reading Translation frames"
			for j = 1 to ba.scale.frames do
			(
				append t (ReadLong bstream )
if t[j] > maxx then
 maxx = t[j]				
			)
			step = "Dataoffset reading"
			if (fseek bstream ba.scale.dataofs #seek_set ) then
			(
--echo ("max Anim ="+maxx as string)
				step = "Reading Translation data"
				animationRange = interval 10 maxx
				with animate on
				(
					for j = 1 to ba.scale.data do
					(
--step = "1"				
						local v3 = [0.0,0.0,0.0]
						v3.x = (ReadFloat bstream )
						v3.y = (ReadFloat bstream )
						v3.z = (ReadFloat bstream )
--step = "2"


						if( j > 1 ) then -- let him to create start keys himself
						(
							addNewKey currobj.scale.controller t[j] 
							currobj.scale.controller.keys[j+1].value = currobj.scale.controller.keys[1].value
						)

						in coordsys local
							at time (t[j]) 
								currobj.scale = v3
					)
				)
			)
			else
				echo "Failed to read bstream start for bone translation frames"
		)
		else
			echo "Failed to read bstream start for bone translation data"	
	)
)

-- read bone animation data frames
-- bone arrays should be already filled
-- @param filled WOW2_Bones array 
-- @TODO finish this function
fn WOW2_CreateBoneTransAnim ba =
(
	if ba.trans.frames > 0 then
	(
		step = "Framesoffset reading"
		if (fseek bstream ba.trans.framesofs #seek_set ) then
		(
			local t = #()
			local maxx = 0
			local x = 0
			currobj = getNodeByName (ba.maxname) exact:true
			step = "Reading Translation frames"
			for j = 1 to ba.trans.frames do
			(
				append t (ReadLong bstream )
if t[j] > maxx then
 maxx = t[j]				
			)
			step = "Dataoffset reading"
			if (fseek bstream ba.trans.dataofs #seek_set ) then
			(
--echo ("max Anim ="+maxx as string)
				step = "Reading Translation data"
				animationRange = interval 10 maxx
				with animate on
				(
					for j = 1 to ba.trans.data do
					(
--step = "1"				
						local v3 = [0.0,0.0,0.0]
						v3.x = (ReadFloat bstream )
						v3.y = (ReadFloat bstream )
						v3.z = (ReadFloat bstream )
--step = "2"

						if( j > 1 ) then
						(
							addNewKey currobj.pos.controller.X_position.controller t[j] 
							addNewKey currobj.pos.controller.Y_position.controller t[j] 
							addNewKey currobj.pos.controller.Z_position.controller t[j] 
							currobj.pos.controller.X_position.keys[j+1].value = currobj.pos.controller.X_position.keys[1].value
							currobj.pos.controller.Y_position.keys[j+1].value = currobj.pos.controller.Y_position.keys[1].value
							currobj.pos.controller.Z_position.keys[j+1].value = currobj.pos.controller.Z_position.keys[1].value
						)

						in coordsys local
							at time (t[j]) 
								currobj.pos =  v3
					)
				)
			)
			else
				echo "Failed to read bstream start for bone translation frames"
		)
		else
			echo "Failed to read bstream start for bone translation data"	
	)
)

-- read bone animation data frames
-- bone arrays should be already filled
-- @param filled WOW2_Bones array 
-- @TODO finish this function
fn WOW2_CreateBoneRotateAnim ba =
(
	if ba.rot.frames > 0 then
	(
		step = "Framesoffset reading"
		if (fseek bstream ba.rot.framesofs #seek_set ) then
		(
			local t = #()
			local maxx = 0
			local x = 0
			currobj = getNodeByName (ba.maxname) exact:true
			step = "Reading Rotation frames"
			for j = 1 to ba.rot.frames do
			(
				append t (ReadLong bstream )
if t[j] > maxx then
 maxx = t[j]				
			)
			step = "Dataoffset reading"
			if (fseek bstream ba.rot.dataofs #seek_set ) then
			(
--echo ("max Anim ="+maxx as string)
				step = "Reading Rotation data"
				animationRange = interval 10 maxx
				with animate on
				(
					for j = 1 to ba.rot.data do
					(
--step = "1"				
						local v3 = [0.0,0.0,0.0]
						local deg = 0
						local q = quat deg v3
						q.x = (ReadFloat bstream )
						q.y = (ReadFloat bstream )
						q.z = (ReadFloat bstream )
						q.w = (ReadFloat bstream )
--step = "2"
local b = (quatToEuler q) 
if b.x > 360 or b.y > 360 or b.z > 360 then
	echo ("oversized:"+q as string + " = "+ (quatToEuler q) as string)

						if( j > 1 ) then
						(
							addNewKey currobj.rotation.controller.Local_X_Rotation.controller t[j] 
							addNewKey currobj.rotation.controller.Local_Y_Rotation.controller t[j] 
							addNewKey currobj.rotation.controller.Local_Z_Rotation.controller t[j] 
							currobj.rotation.controller.local_x_rotation.keys[j+1].value = currobj.rotation.controller.local_x_rotation.keys[1].value
							currobj.rotation.controller.local_y_rotation.keys[j+1].value = currobj.rotation.controller.local_Y_rotation.keys[1].value
							currobj.rotation.controller.local_z_rotation.keys[j+1].value = currobj.rotation.controller.local_Z_rotation.keys[1].value
						)

						in coordsys local
							at time (t[j]) 
								currobj.rotation = q
					)
				)
--echo "-----------#########------------------"
			)
			else
				echo "Failed to read bstream start for bone rotation frames"
		)
		else
			echo "Failed to read bstream start for bone rotation data"	
	)
)


fn WOW2_Create_BoneAnimations ba =
(
	echo "Trying create Bone Animations"
	for i = 1 to ba.count do
	(
		step = "Compare of Data and Frame counts"
		if ba[i].trans.frames != ba[i].trans.data then
			echo ("Translate frames not even on bone #" + i as string)

		WOW2_CreateBoneTransAnim (ba[i])

		if ba[i].rot.frames != ba[i].rot.data then
			echo ("Rotate frames not even on bone #" + i as string)

		WOW2_CreateBoneRotateAnim (ba[i])
		
		if ba[i].scale.frames != ba[i].scale.data then
			echo ("Scale frames not even on bone #" + i as string)
		if ba[i].scale.frames > 0 then
			echo ("bone scaling found on bone "+ ba[i].maxname)
		WOW2_CreateBoneScaleAnim (ba[i])
	)
)

-- read bone animation information
-- ofsets and types for the animations
-- identical for scale, rotate, translate
-- the final data is found starting at dataofs
-- expects the bstream pointer to be at the correct position
-- @see WOW2_ReadBones
-- @return A filled WOW2_Anim structure
fn WOW2_Read_BoneAnim =
(
	local a = WOW2_Anim ()
	a.type = (ReadShort bstream )
	a.flags = (ReadShort bstream )
	a.linetypes = (ReadLong bstream )
	a.linetypeofs = (ReadLong bstream )
	a.frames = (ReadLong bstream )
	a.framesofs = (ReadLong bstream )
	a.data = (ReadLong bstream )
	a.dataofs = (ReadLong bstream )

	return a
)

fn WOW2_Read_Bones =
(
	step = "Read Views"
	if ( head.nBones <= 0 ) then  -- static models do not have bones
	(
		echo "--model without bones !"
		return true
	)
	
	local bonearr = #()
	if (fseek bstream head.ofsBones #seek_set ) then
	(
		for i = 1 to head.nBones do
		( 
			step = ("Reading Bone #"+ i as string)
			local bone = WOW2_Bones ()
			local piv = [0.0,0.0,0.0]
			bone.geosetanim = (ReadLong bstream ) 
			bone.flags = (ReadLong bstream )
			bone.parentbone = (ReadShort bstream ) -- -1 or bone # (0 is first)
			bone.unknown1 = (ReadShort bstream )
			--bone.unknown2 = (ReadLong bstream)
			bone.geoset = (ReadLong bstream )
			step = ("Reading Bone #"+ i as string + " anims")
			bone.trans = WOW2_Read_BoneAnim ()
			bone.rot = WOW2_Read_BoneAnim ()
			bone.scale = WOW2_Read_BoneAnim ()
			piv.x = (ReadFloat bstream )
			piv.y = (ReadFloat bstream )
			piv.z = (ReadFloat bstream )
			bone.pivot = piv
			bone.maxname = ("bone_"+ i as string + "_" + bone.flags as string)
			step = ("Appending Bone #"+ i as string)
			append bonearr bone
		)
		

	)
	else
		echo "unable to read bones"
	
	join bones_read bonearr -- add bones to global bones array
	--WOW2_Read_Bone_Groups ()  -- not needed at the moment
	WOW2_Create_Bones bonearr
		
	return bonearr		
)

fn WOW2_Create_Faces sm view =
(
	step = "Init"
	sm.fs = #() -- faces
	sm.vs = #() -- verts
	sm.ts = #() -- uvs
	sm.bs = #() -- boneindexweight per vertex
	--step = "Create Faces"
	sm.bones = #() -- bones
	--echo ("t ofs "+(sm.oftri as string)+" tot tris: "+(sm.tris as string))
	--if (fseek bstream (view.ofsntris + sm.oftri) #seek_set ) then
	--(
	--local done = 0
		local up = sm.tris as integer -1
	--echo ("/3 check :"+((up+1)/3) as string + " mod "+(mod (up+1) 3)as string)
	if((mod (up+1) 3) != 0.0 )then
	(
		echo "#ERROR sm.tris not a multiple of 3!"
	)
--	else
--		echo "#INFO sm.tris check passed!"
	
		local ofs = sm.oftri as integer
--echo ("oftri = "+ ofs as string)
		for i=1 to (up) by 3 do
		(
		try
		(
		step = "Create Faces" +(i as string)
			local a = view.tri[(ofs+ i)] + 1
			local b = view.tri[(ofs+ i +1)] + 1
			local c = view.tri[(ofs+ i +2)] + 1
		step = "Create Faces erg lookup " + a as string + \
			" " + b as string + " "+ c as string + " " 
--			echo ([a,b,c])
			local erg = ([(view.ind[a] + 1),(view.ind[b] + 1),\
					(view.ind[c] + 1)] as point3 )
--echo "-----------------------------------------------"
			--echo (erg as string)
		step = "Create Faces f append"
			append sm.fs [(i),(i+1),(i+2)] --erg
--			echo ([(i),(i+1),(i+2)] as string)
		step = "Create Faces v append"
			append sm.vs verts_read[erg.x].pos
			append sm.vs verts_read[erg.y].pos
			append sm.vs verts_read[erg.z].pos
		step = "Create Faces t append"
			append sm.ts verts_read[erg.x].uv
			append sm.ts verts_read[erg.y].uv
			append sm.ts verts_read[erg.z].uv 
		step = "Collect bones"
			local belms = #() -- bone id's used by this submesh
-- @TODO create a copy & paste less solution for boneweights and index reading
			local xw = WOW2_BoneWeightIndex ()
			local yw = WOW2_BoneWeightIndex ()
			local zw = WOW2_BoneWeightIndex ()
			xw.bw = #()
			xw.bi = #()
			xw.cnt = 0
			yw.bw = #()
			yw.bi = #()
			yw.cnt = 0
			zw.bw = #()
			zw.bi = #()
			zw.cnt = 0
			if (verts_read[erg.x].bi1 >= 0 and verts_read[erg.x].bw1 > 0) then
			(	
				-- count from 0 so add 1 for max
				append belms (verts_read[erg.x].bi1 + 1)
				append xw.bi (verts_read[erg.x].bi1 + 1)
				append xw.bw verts_read[erg.x].bw1
				xw.cnt = xw.cnt +1
			)
			if (verts_read[erg.x].bi2 >= 0 and verts_read[erg.x].bw2 > 0) then
			(
				append belms (verts_read[erg.x].bi2 + 1)
				append xw.bi (verts_read[erg.x].bi2 + 1)
				append xw.bw verts_read[erg.x].bw2
				xw.cnt = xw.cnt +1
				
			)
			if (verts_read[erg.x].bi3 >= 0 and verts_read[erg.x].bw3 > 0) then
			(
				append belms (verts_read[erg.x].bi3 + 1)
				append xw.bi (verts_read[erg.x].bi3 + 1)
				append xw.bw verts_read[erg.x].bw3
				xw.cnt = xw.cnt +1
				
			)
			if (verts_read[erg.x].bi4 >= 0 and verts_read[erg.x].bw4 > 0) then
			(
				append belms (verts_read[erg.x].bi4 + 1)
				append xw.bi (verts_read[erg.x].bi4 + 1)
				append xw.bw verts_read[erg.x].bw4
				xw.cnt = xw.cnt +1
				
			)
			append sm.bs xw
			if (verts_read[erg.y].bi1 >= 0 and verts_read[erg.y].bw1 > 0) then
			(
				append belms (verts_read[erg.y].bi1 + 1)
				append yw.bi (verts_read[erg.y].bi1 + 1)
				append yw.bw verts_read[erg.y].bw1
				yw.cnt = yw.cnt +1
				
			)
			if (verts_read[erg.y].bi2 >= 0 and verts_read[erg.y].bw2 > 0) then
			(
				append belms (verts_read[erg.y].bi2 + 1)
				append yw.bi (verts_read[erg.y].bi2 + 1)
				append yw.bw verts_read[erg.y].bw2
				yw.cnt = yw.cnt +1
				
			)
			if (verts_read[erg.y].bi3 >= 0 and verts_read[erg.y].bw3 > 0) then
			(
				append belms (verts_read[erg.y].bi3 + 1)
				append yw.bi (verts_read[erg.y].bi3 + 1)
				append yw.bw verts_read[erg.y].bw3
				yw.cnt = yw.cnt +1
				
			)
			if (verts_read[erg.y].bi4 >= 0 and verts_read[erg.y].bw4 > 0) then
			(
				append belms (verts_read[erg.y].bi4 + 1)
				append yw.bi (verts_read[erg.y].bi4 + 1)
				append yw.bw verts_read[erg.y].bw4
				yw.cnt = yw.cnt +1
				
			)
			append sm.bs yw
			if (verts_read[erg.z].bi1 >= 0 and verts_read[erg.z].bw1 > 0) then
			(
				append belms (verts_read[erg.z].bi1 + 1)
				append zw.bi (verts_read[erg.z].bi1 + 1)
				append zw.bw verts_read[erg.z].bw1
				zw.cnt = zw.cnt +1
				
			)
			if (verts_read[erg.z].bi2 >= 0 and verts_read[erg.z].bw2 > 0) then
			(
				append belms (verts_read[erg.z].bi2 + 1)
				append zw.bi (verts_read[erg.z].bi2 + 1)
				append zw.bw verts_read[erg.z].bw2
				zw.cnt = zw.cnt +1
				
			)
			if (verts_read[erg.z].bi3 >= 0 and verts_read[erg.z].bw3 > 0) then
			(
				append belms (verts_read[erg.z].bi3 + 1)
				append zw.bi (verts_read[erg.z].bi3 + 1)
				append zw.bw verts_read[erg.z].bw3
				zw.cnt = zw.cnt +1
			)
			if (verts_read[erg.z].bi4 >= 0 and verts_read[erg.z].bw4 > 0) then
			(
				append belms (verts_read[erg.z].bi4 + 1)
				append zw.bi (verts_read[erg.z].bi4 + 1)
				append zw.bw verts_read[erg.z].bw4
				zw.cnt = zw.cnt +1
			)
			append sm.bs zw
		step = "unique bones"
--echo ("belms:" + belms.count as string)
			for elm = 1 to belms.count do
			( 
				if ((finditem sm.bones belms[elm]) == 0) then
				(
					append sm.bones belms[elm]
				)
			)
		)
		catch
		(
			echo (getCurrentException())
			throw ()
		)	
						
		)
--		echo "done"
	--)
	-- only sort this one :-)
	sort sm.bones
step = "new"

	-- set sm.bs[i].bi (bone index) to the index of the bone in sm.bones
	-- for all boneindex/weight pairs in this submesh
	for i = 1 to sm.bs.count do
	(	-- for all bone indices for this vertex
		for j = 1 to sm.bs[i].bi.count do
		(
			-- assign the index by lookup of the global index 
			-- number in the local bones index array
			sm.bs[i].bi[j] = findItem sm.bones (sm.bs[i].bi[j])
			-- max uses weights from 0.0 to 1.0 WOW uses 0 to 255
			sm.bs[i].bw[j] = (sm.bs[i].bw[j] / 255.0)
		)
	)
--echo ("MAX WEIGHT IS:"+maxbW as string)
 	--sm.vs = v
	--sm.fs = f
	--sm.ts = t
	--sm.bones = bones
	sm --f
)

fn WOW2_Create_Submeshes view =
(
	step = ("Create Submesh "+ view as string )
	if (fseek bstream views_read[view].ofsnsubmesh #seek_set ) then
	(
		for i=1 to views_read[view].nsubmesh do
		(
			local sm = WOW2_Submesh ()
			sm.id = ReadLong bstream #unsigned
--			echo ("sm.id "+ sm.id as string)
			sm.ofsvert = ReadShort bstream #unsigned
--			echo ("sm.ofsvert "+ sm.ofsvert as string)
			sm.nverts = ReadShort bstream #unsigned
--			echo ("sm.nverts "+ sm.nverts as string)
			sm.oftri = ReadShort bstream #unsigned
--			echo ("sm.oftri "+ sm.oftri as string)
			sm.tris = ReadShort bstream #unsigned
--			echo ("sm.tris "+ sm.tris as string)
			sm.unknown1 = ReadShort bstream #unsigned
			sm.unknown2 = ReadShort bstream #unsigned
			sm.unknown3 = ReadShort bstream #unsigned
			sm.unknown4 = ReadShort bstream #unsigned
			local p = [0.0,0.0,0.0]
			p.x = ReadFloat bstream
			p.y = ReadFloat bstream
			p.z = ReadFloat bstream
			sm.n3floats1 = p
			
			local q = [0.0,0.0,0.0]
			q.x = ReadFloat bstream
			q.y = ReadFloat bstream
			q.z = ReadFloat bstream
			sm.n3floats2 = q
	--continue	

if true then --sm.id != 0 then
(			
--			local vs = #()
			--vs = WOW2_Create_Verts sm views_read[view]
--			local ts = #()
			--ts = WOW2_Create_Faces sm views_read[view]
			WOW2_Create_Faces sm views_read[view]		

--		local uvs = #()
			--uvs = WOW2_Create_UV sm views_read[view]
--step = "out"
--echo ("bones count on sm#"+ i as string +" is "+ sm.bones.count as string)
--local outstr = "bones: "
--for bcnt = 1 to sm.bones.count do
--	outstr = outstr + (sm.bones[bcnt] as string) + " "

--echo (outstr)
--echo (sm.vs.count)
--echo (sm.bs.count)
--echo (sm.ts.count)
--for i= 1 to uvs.count do
--	echo (uvs[i] as string)
--for i= 1 to ts.count do
--	echo (ts[i] as string)
 step = "mat"
local skinMaterial = standardMaterial name:(name_read +"_"+view as string +"_"+i as string)
step = ("Mesh init ")
local theMesh = undefined
try
(
			theMesh = mesh vertices:(sm.vs) faces:(sm.fs) \
				name:(name_read +"_"+view as string +"_"+i as string) \
				material:skinMaterial tverts:(sm.ts) 
)
catch
(
echo (getCurrentException())
throw ()
)

step = "Build tv's"
try
(
			--Set texcoord faces (so user just needs to load a converted skin PCX file) 
			buildTVFaces theMesh false
			for i = 1 to sm.fs.count do
			(
				--local tcVert = sm.fs[i]
--				echo ((sm.ts[(sm.fs[i].x)]) as string)
--				echo ((sm.ts[(sm.fs[i].y)]) as string)
--				echo ((sm.ts[(sm.fs[i].z)]) as string)
--
--				echo (sm.fs[i] as string)
				setTVFace theMesh i (sm.fs[i])
			)
			update theMesh
)
catch
(
echo (getCurrentException())
throw ()
)

step = "Build Skin"
if (head.nBones > 0 and theMesh != undefined and sm.bones.count > 0) then
(
	try
	( 		step = "Skin prepare"
		max modify mode
		select theMesh
		subObjectLevel = 0
		local newskin = Skin()
		addModifier theMesh newskin
		local mysk = theMesh.modifiers[#Skin]
		subobjectLevel = 1
modPanel.setCurrentObject theMesh.modifiers[#Skin]
subobjectLevel = 1
		step = "Add "+ sm.bones.count as string + " bones to submesh"
		for i = 1 to sm.bones.count do
		(
			step = ("AddBone " + i as string) 
			local bone = (getNodeByName bones_read[sm.bones[i]].maxname exact:true )
			--skinOps.addBone newskin bone 0
			skinOps.addBone theMesh.modifiers[#Skin] bone -1
		)
update theMesh		

		step = ("Set Vertex weights for " + theMesh.name)
-- NEVER delete this, the lines will update the vertex cache of the skin object it seems
-- if this lines are omitted the vertex count will be 0 and an exception will be thrown
-- on adding data to the first vertex 
throwaway = theMesh.numverts 
		for i = 1 to sm.bs.count do
		(
			if sm.bs[i].cnt > 0 then
			(
				skinOps.setVertexWeights theMesh.modifiers[#Skin] \
					i (sm.bs[i].bi) (sm.bs[i].bw)	
			)
		)
subObjectLevel = 0
deselect theMesh
	)
	catch
	(
		echo (getCurrentException())
		throw ()
	)
)
			update theMesh
			--sm = undefined
			gc()
--return 0
)-- if sm.id != 0
		)
	)
)


fn WOW2_Read_Meshes =
(
	step = "Read Meshes"
	for viewcnt = 1 to 1 do --views_read.count do
	(
		--read indices to verts and tris of view
		--view_inds_read
		--view_triss_read
		
		WOW2_Create_Submeshes 1
	)
)

-- read nGlobalSequences into sequences_read
-- @sideeffect global array sequences_read is filled
fn WOW2_Read_GlobalSequences =
(
echo ("Sequences:"+head.nGlobalSequences as string)
	if (fseek bstream head.ofsGlobalSequences #seek_set ) then
	(
		for i = 1 to head.nGlobalSequences do
		(
			append sequences_read (ReadLong bstream )			
		)
	)
)

-- read nAnimations into animations_read
-- @sideeffect global array animations_read is filled
fn WOW2_Read_Animations =
(
	echo "Trying read Animations"
	
	if (fseek bstream head.ofsAnimations #seek_set ) then
	(
		for i = 1 to head.nAnimations do--
		(
			local anim = WOW2_Animations ()
			anim.bbox = #()
			anim.animid = (ReadLong bstream )
			anim.startseq = (ReadLong bstream )
			anim.endseq = (ReadLong bstream )
			anim.speed = (ReadFloat bstream )
			anim.unknown1 = (ReadLong bstream )
			anim.unknown2 = (ReadLong bstream )
			anim.unknown3 = (ReadLong bstream )
			anim.unknown4 = (ReadLong bstream )
			anim.unknown5 = (ReadLong bstream )
			for j = 1 to 6 do
			(
				append anim.bbox  (ReadFloat bstream )
			)
			anim.radius = (ReadFloat bstream )
			anim.unknown6 = (ReadShort bstream )
			anim.unknown7 = (ReadShort bstream )
			
			append animations_read anim
		)
	)
)

-----------------------------------------------------------------
-- MAIN
-----------------------------------------------------------------
fn MainFunc =
(
ECHO("START")
InitLoader()
ECHO("INITIALIZATION COMPLETE")
try
(
if objects.count > 0 then
(
echo "--Cleaning Scene--"
select objects
delete $
)
--if false then
--(
echo ("--Start--")
	WOW2_Open(filename)
	WOW2_Read_Header()
echo ("Trying read Name")
	WOW2_Read_Name()

	if( LoadBones ) then 
	(
		echo ("Trying read bones")
		WOW2_Read_Bones()
	)

	echo ("Trying read verts"	)
	WOW2_Read_Verts()
	
	echo ("Trying read views")
	WOW2_Read_Views()

	echo ("Trying read Meshes")
	WOW2_Read_Meshes()
--echo "--Sequences--"
--	WOW2_Read_GlobalSequences()
--echo "--Animations--"

	if( LoadAnimation ) then 
	(
		WOW2_Read_Animations()
		WOW2_Create_BoneAnimations bones_read
	)
	WOW2_Close()
echo "--Model loaded--"
--deselect *
--)
	gc()
	true
)
catch
(
	format "-- Failed in \n" 
	echo(  step )
-- to:listener
	if bstream != undefined then WOW2_Close()
	gc()
	false
)

)

macroScript OpenWow2ImportFloater category:"WOW"
(

	rollout WoW2_Import_Roll "WoW2_Import" width:250 
	(
		button theButton "Open file dialog..." width:240
		Label lbl "...or enter name manualy:"
		edittext  edrit  "" labelOnTop:true fieldwidth:240 height:36
		label tl1 "\n"
		checkbox cbLoadBones "Load bones" checked:false triState: 0 enabled:false
		checkbox cbLoadAnimation "Load animation" checked:false triState: 0
		label tl21 "\n"

		fn CheckCB ch = 
		(
			if( LoadBones ==  false ) then
			(
				LoadAnimation = false
				cbLoadAnimation.checked=false
				cbLoadAnimation.enabled=false
			)
			else
			(
				cbLoadAnimation.enabled=true
			)
		)
		
		on cbLoadBones changed ch do
		(
			LoadBones = ch
			CheckCB ch
		)
		
		on cbLoadAnimation changed ch do
		(
			LoadAnimation = ch
		)
		
		label tl2 ""
		button gogo "Import it NOW"
		
		fn CheckFile fname = 
		(
			local fs = getFileSize fname
		
			if( fs > 0 ) then
			(
				gogo.enabled = true
				tl2.text = ""
			)
			else
			(
				tl2.text = "File name is Incorrect"
				gogo.enabled = false
			)
		)
		
		on WoW2_Import_Roll open do
		(
			edrit.text = (filename  as string)			
			CheckFile filename

			cbLoadAnimation.checked = LoadAnimation 
			cbLoadBones.checked = LoadBones 
			CheckCB ch
		) 
		
		on theButton pressed do 
		(
			local f = getOpenFileName types:"Wow model(*.m2)|*.m2|All|*.*|" caption:"Open A Test File:" filename:edrit.text
			
			try
			(
				filename = f + ""			
				edrit.text = filename
			)
			catch()
		)
		
		
		on edrit changed newtext do
		(
			CheckFile newtext
			if( gogo.enabled ) then ( filename = newtext )
		)
		
		on gogo pressed do 
		(
			CheckFile filename
			if( gogo.enabled ) then 
			(
				if( MainFunc ()	== false ) then
				(
					messagebox "IMPORT FAILED. Look MAXScript listener for details"
				)
			)
		)
 	)
	
	on isChecked do WoW2_Import_Roll.open --return true if rollout is open
	

	on execute do createDialog WoW2_Import_Roll

	
	on closeDialogs do destroyDialog WoW2_Import_Roll

)
	


 /*
WowMenu = mainMenuBar.findMenu "WOW"
if( WoWMenu != undefined) then
(
	menuMan.unRegisterMenu WowMenu
	WowMenu = undefined
)

*/

if menuMan.registerMenuContext 0x1aa76d7d then
(
	-- Get the main menu bar
	local mainMenuBar = menuMan.getMainMenuBar()
	-- Create a new menu
	local subMenu = menuMan.createMenu "WOW"
	
	-- create a menu item that calls the sample macroScript
	local testItem = menuMan.createActionItem "OpenWow2ImportFloater" "WOW" 
	
	testitem.setUseCustomTitle true
	
	testitem.setTitle  "Open importer"
	
	-- Add the item to the menu
	subMenu.addItem testItem -1
	
	-- Create a new menu item with the menu as it's sub-menu
	local subMenuItem = menuMan.createSubMenuItem "WOW" subMenu
	
	-- compute the index of the next-to-last menu item in the main menu bar
	local subMenuIndex = mainMenuBar.numItems() - 1
	
	-- Add the sub-menu just at the second to last slot
	mainMenuBar.addItem subMenuItem subMenuIndex
	
	-- redraw the menu bar with the new item
	menuMan.updateMenuBar()
)








Something missing? Mail to mail address


2009-06-01
WoW Importer for Max
The World of Warcraft tool for 3D Studio has been updated. It now converts model files from WoW Client version 2.x (upto 2.7) and displays correct animations for multi mesh models. The script can be found here....
2007-03-07
nGUI explained
If you ever wanted some more details on the nebula2 nGUI System you can find it in the nGUI System article.
2006-10-17
Mangalore entity ID's
If you need information about the mangalore entity ID usage have a look here..
2006-08-06
Mangalore Articles
Added a new section about the mangalore game framework from radonlabs. The section contains some articles about my experience with mangalore. Read more here:
2006-03-10
Free models
Finally some free models for the Radonlabs SDK. You can download them here.