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

A World of Warcraft M2 GMax model importer


The script loads the mesh with texture coordinates into GMax. For each submesh in the file an own mesh is created. Just load the texture files into GMax too and assign it to the meshes. Each submesh is loaded with the skinned bone data, though animations are not part of the exporter, the code to load bone animations is already present. Only problem at the moment are the rotations which do not seem to be as expected. If you want to experiment with Warcraft animations then you can uncomment the line at the bottom of the code WOW2_Create_BoneAnimations bones_read. If you find anything usefull, then contact me, or make it public so somebody will be able to use it.

The information for the m2 importer would not be possible without the help of http://wowmapview.sourceforge.net where the file format can be looked up.

The image below shows the script in action, with an imported horse from World of Warcraft.

GMax import script in action with a horse loaded


Copy and paste the code below into a new Script file, then replace the global variable filename with the name of the m2 file you want to load and hit Str+E in GMax for the script to be evaluated. The script could work with 3D Studio Max too.


--

--

-- 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. The bone animations are not fully working as the values

-- seem to go wired after a few frames. Have fun.

--

-- Change the global variable filename to the m2 file you want to load. the press

-- strg-e to evaluate the script. After the script finishes you can assign texture

-- files also found in WOW. 

--



--

-- GLOBALS AND STRUCTURES





global filename = "c:\\tmp\\Horse.m2" --HighElfMale_Warrior.m2" --Ghoul.m2"

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 = #()







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,unknown0,parentbone,geoset,

	trans,rot,scale,pivot,max_name

)



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,unkown1,unknown2,

	unknown3,unknown4,n3floats,

	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].max_name)

		$.boxsize = [0.1,0.1,0.1]

		--$.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)].max_name) \

				exact:true

		)

	)

)

-- read bone animation data frames

-- bone arrays should be already filled

-- @param filled WOW2_Bones array 

-- @TODO finish this function

fn WOW2_CreateBoneScaleAnim ba =

(

	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.max_name) 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"

					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.max_name) 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"

					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.max_name) 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)

						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 =

(

	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].max_name)

		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 = (ReadShort bstream )

			bone.unknown0 = (ReadShort bstream )

			bone.parentbone = (ReadShort bstream ) -- -1 or bone # (0 is first)

			bone.geoset = (ReadShort 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.max_name = ("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.unkown1 = 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.n3floats = p

	--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]].max_name 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 =

(

	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

-----------------------------------------------------------------



try

(

if objects.count > 0 then

(

echo "--Cleaning Scene--"

select objects

delete $

)

--if false then

--(

echo "--Start--"

	WOW2_Open(filename)

	WOW2_Read_Header()

	WOW2_Read_Name()

	WOW2_Read_Bones()

	

	WOW2_Read_Verts()

	WOW2_Read_Views()

	WOW2_Read_Meshes()

--echo "--Sequences--"

--	WOW2_Read_GlobalSequences()

--echo "--Animations--"

--	WOW2_Read_Animations()

--	WOW2_Create_BoneAnimations bones_read

	WOW2_Close()

echo "--Model loaded--"

--deselect *

--)

	gc()

	true

)

catch

(

	format "-- Failed in \n" step to:listener

	if bstream != undefined then WOW2_Close()

	gc()

	false

)




Something missing? Mail to cc


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.