Page 1 of 3

No More Heroes: Heroes' Paradise

Posted: Wed May 11, 2011 1:21 am
by chrrox
Here is a 3ds max importer for the xbox 360 models of this game. Supports bones and weights.
Image

Code: Select all

if (heapSize < 20000000) then
	heapSize = 200000000 -- allow ~ 40 MB instead of just 7.5 MB. Prevents "Runtime Error: Out of scripter memory"

fname = getOpenFileName \ 
caption:"Open No More Heroes 360 Model File" \
types:"No More Heroes 360 Model File(*model)|*model" \
historyCategory:"No More Heroes 360 ObjectPresets"
f = fopen fname "rb"

fn ReadBEShort fstream = (
short = readshort fstream #unsigned
short = bit.swapBytes short 2 1
b = (bit.get short 16)
for i = 17 to 32 do short = bit.set short i b
return short
)

fn PrintOffset Var =
(
	local Var = Var
print ("This is the offset 0x" + (bit.intAsHex Var) as string)
	Var
)

fn floatSwap2 f = 
(
	i = bit.floatAsInt f
	h = bit.intashex i
	while h.count < 8 do h = "0" + h
	
	s = (substring h 7 2) + (substring h 5 2) + (substring h 3 2) + (substring h 1 2)
	bit.intAsFloat (bit.hexasint s)
)	

fn ReadBEword fstream = (
return (bit.swapBytes (readshort fstream #unsigned) 1 2)
)

 	fn convertTo32 input16 = (
 		inputAsInt = input16
 		sign = bit.get inputAsInt 16
 		exponent = (bit.shift (bit.and inputAsInt (bit.hexasint "7C00")) -10) as integer - 16
 		fraction = bit.and inputAsInt (bit.hexasint "03FF")
 		if sign==true then sign = 1 else sign = 0
 		exponentF = exponent + 127
 		--Ouput 32 bit integer representing a 32 bit float
 		outputAsFloat = bit.or (bit.or (bit.shift fraction 13) (bit.shift exponentF 23)) (bit.shift sign 31)
 		--Output Check	
 		return bit.intasfloat outputasfloat
 	)

fn ReadBEHalfFloat fstream = (
return convertTo32(ReadBEword fstream)
)

fn ReadBElong fstream = (
long = readlong fstream
long = bit.swapBytes long 1 4
long = bit.swapBytes long 2 3
return long
)

fn ReadBEfloat fstream = (
return floatSwap2(readfloat fstream)
)

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

struct weight_data
(
	boneids,weights
)

struct Mesh_Info_Struct
(
	Mesh_Name,vertstart,vertend,vertcount,facecount,InfoStart,MeshGroup,FaceStart
)
MNKS0101 = ReadFixedString f 8
Version = ReadBElong f
null = ReadBElong f
Modelname = ReadFixedString f 32
total_size = getFileSize fname
RXET_array = #()
TNOJ_array = #()
ARTM_array = #()
RDHS_array = #()
KSEM_array = #()
PHSB_array = #()
struct type_Info_Struct
(
	Pos,type,typesize
)
while (ftell f) != total_size Do (
Pos = (ftell f)
type = ReadFixedString f 4
typesize = ReadBElong f
fseek f (pos + typesize)#seek_set
case of
(
(type == "RXET"): append RXET_array (type_Info_Struct Pos:Pos type:type typesize:typesize)
(type == "TNOJ"): append TNOJ_array (type_Info_Struct Pos:Pos type:type typesize:typesize)
(type == "ARTM"): append ARTM_array (type_Info_Struct Pos:Pos type:type typesize:typesize)
(type == "RDHS"): append RDHS_array (type_Info_Struct Pos:Pos type:type typesize:typesize)
(type == "KSEM"): append KSEM_array (type_Info_Struct Pos:Pos type:type typesize:typesize)
(type == "PHSB"): append PHSB_array (type_Info_Struct Pos:Pos type:type typesize:typesize)
default: print type
)
)
BNArr = #()
for a =1 to TNOJ_array.count Do (
fseek f (TNOJ_array[a].Pos + 8)#seek_set
Size1 = ReadBElong f
Null1 = ReadBElong f
SkelName = ReadFixedString f 32
BoneCount = ReadBElong f
Version = ReadBElong f
fseek f 0x8#seek_cur

for b = 1 to BoneCount do (
BoneName = ReadFixedString f 0x20
m11 = ReadBEFloat f; m12 = ReadBEFloat f; m13 = ReadBEFloat f; m14 = ReadBEFloat f
m21 = ReadBEFloat f; m22 = ReadBEFloat f; m23 = ReadBEFloat f; m24 = ReadBEFloat f
m31 = ReadBEFloat f; m32 = ReadBEFloat f; m33 = ReadBEFloat f; m34 = ReadBEFloat f
m41 = ReadBElong f; m42 = ReadBElong f; m43 = ReadBElong f; m44 = ReadBElong f
tfm = (quat m21 m22 m23 m24) as matrix3
tfm.row4 = [m11,m12,m13]

newBone = bonesys.createbone	\
				  tfm.row4	\
				  (tfm.row4 + 0.01 * (normalize tfm.row1)) \
				  (normalize tfm.row3)
			newBone.name = BoneName
			newBone.width  = 0.01
			newBone.height = 0.01
			newBone.transform = tfm
			newBone.setBoneEnable false 0
			newBone.wirecolor = yellow
			newbone.showlinks = true
			newBone.pos.controller      = TCB_position ()
			newBone.rotation.controller = TCB_rotation ()


 if (m41 != -1) then
 newBone.parent = BNArr[m41 + 1]
append BNArr newBone	
)
)

struct Mesh_Info2_Struct
(
	Vert_Start,FaceStart,VertTotal,FaceTotal,vertsize
)
Mesh_Info2_Array = #()
for a =1 to KSEM_array.count Do (
fseek f (KSEM_array[a].Pos + 8)#seek_set
Size1 = ReadBElong f
Null1 = ReadBElong f
SkinName = ReadFixedString f 32	
MeshSections = ReadBElong f
unk01 = ReadBElong f
MeshCount = ReadBEword f
fseek f 0x6#seek_cur
for b = 1 to MeshSections Do (
SceneName = ReadFixedString f 32
VertTotal = ReadBElong f
FaceTotal = ReadBElong f
vertsize = ReadBEword f
facesize = ReadBEword f
Unk01 = ReadBElong f
Vert_Start = (ftell f)
fseek f (Vert_Start + VertTotal)#seek_set
if mod (ftell f) 16 != 0 do (
fseek f (16 - (mod (ftell f) 16))#seek_cur
)
FaceStart = (ftell f)
fseek f (FaceStart + FaceTotal)#seek_set
if mod (ftell f) 16 != 0 do (
fseek f (16 - (mod (ftell f) 16))#seek_cur
)
append Mesh_Info2_Array (Mesh_Info2_Struct Vert_Start:Vert_Start FaceStart:FaceStart VertTotal:VertTotal FaceTotal:FaceTotal vertsize:vertsize)
)
MeshInfoOff = (ftell f)
Mesh_Info_Array = #()
PrintOffset (ftell f)

for b = 1 to MeshCount Do (
InfoStart = (ftell f)
Mesh_Name = ReadFixedString f 32
facecount = ReadBEword f
vertstart = ReadBEword f
vertend = ReadBEword f
vertcount = ReadBEword f
Unk03 = readbyte f#unsigned
Unk04 = readbyte f#unsigned
Unk05 = readbyte f#unsigned
MeshGroup = ((readbyte f#unsigned) + 1)
Unk01 = ReadBElong f
Unk02 = ReadBElong f
FaceStart = ReadBElong f
fseek f 0x68#seek_cur
append Mesh_Info_Array (Mesh_Info_Struct Mesh_Name:Mesh_Name vertstart:vertstart vertend:vertend vertcount:vertcount facecount:facecount InfoStart:InfoStart MeshGroup:MeshGroup FaceStart:FaceStart)
)
print Mesh_Info_Array


for b = 1 to MeshCount do (
Vert_array = #()
Normal_array = #()
UV_array = #()
Face_array = #()
Weight_array = #()
BaseVert = Mesh_Info2_Array[(Mesh_Info_Array[b].MeshGroup)].Vert_Start
vertsize =  Mesh_Info2_Array[(Mesh_Info_Array[b].MeshGroup)].vertsize
fseek f (BaseVert + (Mesh_Info_Array[b].vertstart * vertsize))#seek_set
printoffset (ftell f)
for c = 1 to Mesh_Info_Array[b].vertcount do (

if vertsize == 48 do (
vx = ReadBEfloat f
vy = ReadBEfloat f 
vz = ReadBEfloat f
COLOR1 = readlong f
weight1 = readbyte f#unsigned
weight2 = readbyte f#unsigned
weight3 = readbyte f#unsigned
weight4 = readbyte f#unsigned
bone1 = readbyte f#unsigned
bone2 = readbyte f#unsigned
bone3 = readbyte f#unsigned
bone4 = readbyte f#unsigned
xy = ReadBEfloat f
xz = ReadBEfloat f
tu = (ReadBEHalfFloat f) * 2
tv = (ReadBEHalfFloat f) * -2
nx = ReadBEfloat f
ny = ReadBEfloat f
nz = ReadBEfloat f

)

if vertsize == 44 do (
vx = ReadBEfloat f
vy = ReadBEfloat f 
vz = ReadBEfloat f
COLOR1 = readlong f
weight1 = readbyte f#unsigned
weight2 = readbyte f#unsigned
weight3 = readbyte f#unsigned
weight4 = readbyte f#unsigned
bone1 = readbyte f#unsigned
bone2 = readbyte f#unsigned
bone3 = readbyte f#unsigned
bone4 = readbyte f#unsigned
xy = ReadBEfloat f
xz = ReadBEfloat f
tu = (ReadBEHalfFloat f) * 2
tv = (ReadBEHalfFloat f) * -2
nx = ReadBEfloat f
ny = ReadBEfloat f
nz = ReadBEfloat f
)

if 	vertsize == 40 do (
vx = ReadBEfloat f
vy = ReadBEfloat f 
vz = ReadBEfloat f
COLOR1 = readlong f
weight1 = readbyte f#unsigned
weight2 = readbyte f#unsigned
weight3 = readbyte f#unsigned
weight4 = readbyte f#unsigned
bone1 = readbyte f#unsigned
bone2 = readbyte f#unsigned
bone3 = readbyte f#unsigned
bone4 = readbyte f#unsigned
tu = (ReadBEHalfFloat f) * 2
tv = (ReadBEHalfFloat f) * -2
nx = ReadBEfloat f
ny = ReadBEfloat f
nz = ReadBEfloat f
)
if 	vertsize == 36 do (
vx = ReadBEfloat f
vy = ReadBEfloat f 
vz = ReadBEfloat f
COLOR1 = readlong f
weight1 = readbyte f#unsigned
weight2 = readbyte f#unsigned
weight3 = readbyte f#unsigned
weight4 = readbyte f#unsigned
bone1 = readbyte f#unsigned
bone2 = readbyte f#unsigned
bone3 = readbyte f#unsigned
bone4 = readbyte f#unsigned
tu = (ReadBEHalfFloat f) * 2
tv = (ReadBEHalfFloat f) * -2
fseek f 0x8#seek_cur
)
if 	vertsize == 32 do (
vx = ReadBEfloat f
vy = ReadBEfloat f 
vz = ReadBEfloat f
COLOR1 = readlong f
weight1 = 255.0
weight2 = 0
weight3 = 0
weight4 = 0
bone1 = 0
bone2 = 0
bone3 = 0
bone4 = 0
tu = (ReadBEHalfFloat f) * 2
tv = (ReadBEHalfFloat f) * -2
fseek f 0x4#seek_cur
COLOR2 = readlong f
COLOR3 = readlong f
)
w = (weight_data boneids:#() weights:#())
maxweight = 0
if(weight1 != 0) then
	maxweight = maxweight + weight1
if(weight2 != 0) then
	maxweight = maxweight + weight2
if(weight3 != 0) then
	maxweight = maxweight + weight3
if(weight4 != 0) then
	maxweight = maxweight + weight4

if(maxweight != 0) then (
		if(weight1 != 0) then (
			w1 = weight1 as float
			append w.boneids (bone1 + 1)
			append w.weights w1
		)
		if(weight2 != 0) then (
			w2 = weight2 as float
			append w.boneids (bone2 + 1)
			append w.weights w2
		)
		if(weight3 != 0) then (
			w3 = weight3 as float
			append w.boneids (bone3 + 1)
			append w.weights w3
		)
		if(weight4 != 0) then (
			w4 = weight4 as float
			append w.boneids (bone4 + 1)
			append w.weights w4
		)		
	)
append Weight_array w
append Vert_array [vx,vy,vz]
--append Normal_array [nx,ny,nz]
append UV_array [tu,tv,0]

)
BaseFace = Mesh_Info2_Array[(Mesh_Info_Array[b].MeshGroup)].FaceStart
fseek f ((Mesh_Info_Array[b].FaceStart * 2) + BaseFace)#seek_set
for d = 1 to Mesh_Info_Array[b].facecount do (
f1 = ReadBEword f + 1
f2 = ReadBEword f + 1
f3 = ReadBEword f + 1
append Face_array [f1,f2,f3]
)

fseek f (Mesh_Info_Array[b].InfoStart + 0x50)#seek_set
usedbones = ReadBElong f
null = ReadBElong f
bidArray = #()
for c = 1 to usedbones do (
bid = ((readbyte f#unsigned) + 1)
append bidArray bid
)
msh = mesh vertices:Vert_array faces:Face_array
msh.numTVerts = UV_array.count
buildTVFaces msh
msh.name = Mesh_Info_Array[b].Mesh_Name + "_" + b as string
for j = 1 to UV_array.count do setTVert msh j UV_array[j]
for j = 1 to Face_array.count do setTVFace msh j Face_array[j]
for j = 1 to Normal_array.count do setNormal msh j Normal_array[j]
	
max modify mode
select msh
skinMod = skin ()
addModifier msh skinMod
for i = 1 to bidArray.count do
(
	maxbone = BNArr[(bidArray[i])]
	if i != bidArray.count then
		skinOps.addBone skinMod maxbone 0
	else
		skinOps.addBone skinMod maxbone 1
)
modPanel.setCurrentObject skinMod
for i = 1 to Weight_array.count do
(
	w = Weight_array[i]
	bi = #()
	wv = #()
	for j = 1 to w.boneids.count do
	(
		boneid = w.boneids[j]
		weight = w.weights[j]
		append bi boneid
		append wv weight
	)	
	skinOps.ReplaceVertexWeights skinMod i bi wv
)


modPanel.setCurrentObject $.baseObject prompt:false
modPanel.addModToSelection (Vertex_Weld ()) ui:on
$.modifiers[#Vertex_Weld].threshold = 0.001
max create mode

)

)

fclose f

Re: No More Heroes: Heroes' Paradise

Posted: Wed May 11, 2011 1:44 am
by mariokart64n
awesome, thanks man. NMH is my favorite game, such a shame the dev closed the book on the series.

EDIT
Image

Code: Select all

if vertsize == 0x20 do
(
vx = ReadBEfloat f
vy = ReadBEfloat f
vz = ReadBEfloat f
fseek f 0x4#seek_cur -- FFs
tu = (ReadBEHalfFloat f) * 2
tv = (ReadBEHalfFloat f) * -2
nx = ReadBEfloat f
ny = ReadBEfloat f
nz = ReadBEfloat f
)

Re: No More Heroes: Heroes' Paradise

Posted: Wed May 11, 2011 7:32 am
by Nazaroff
Hi, chrrox.
You always write
Supports bones and weights.
But never write about animation. You never tried to import animations too? I mean not this game only. :roll:

Re: No More Heroes: Heroes' Paradise

Posted: Wed May 11, 2011 9:10 am
by Ares722
Thank you so much^^ amazing work as usually,...

Re: No More Heroes: Heroes' Paradise

Posted: Sat May 14, 2011 7:23 am
by daisuki
Could you explain how to extract the model file ,please?

Re: No More Heroes: Heroes' Paradise

Posted: Sun May 15, 2011 5:07 am
by chrrox
Model BMS
run it on the 360 file default.fpd

Code: Select all

#Quickbms script
#No more heroes xbox 360 .model extractor
#by chrrox
for
findloc start string "MNKS0101"
goto start
savepos offset
findloc npos string KSEM
math npos + 0x40
goto npos
getdstring name 0x20
string name + .model
findloc end string " FOE"
set size end
math size - start
log name offset size
goto end
next

Re: No More Heroes: Heroes' Paradise

Posted: Sun May 15, 2011 5:47 am
by daisuki
Thank you very much.
Master chrrox.

Re: No More Heroes: Heroes' Paradise

Posted: Wed May 18, 2011 8:10 pm
by noazett
The contents of this post was deleted because of possible forum rules violation.

Re: No More Heroes: Heroes' Paradise

Posted: Wed May 18, 2011 9:24 pm
by chrrox
You extracted 360 models correct?
the ps3 models will not work.

Re: No More Heroes: Heroes' Paradise

Posted: Wed May 18, 2011 10:10 pm
by noazett
yes these are model files from 360 version. I used xbox backup creator to extract default.fpd from the game

Re: No More Heroes: Heroes' Paradise

Posted: Wed May 18, 2011 10:19 pm
by The Chief
I test the files you posted on max 9 and 2009 but they dont work , max just freez every
time i test one of the files.

Re: No More Heroes: Heroes' Paradise

Posted: Wed May 18, 2011 10:29 pm
by noazett
The Chief wrote:I test the files you posted on max 9 and 2009 but they dont work , max just freez every
time i test one of the files.

yes this is exactly the same issue i 've got. maybe the files are corrupted or something.
i will try to extract them with another tool.
thx

Re: No More Heroes: Heroes' Paradise

Posted: Wed May 18, 2011 11:43 pm
by chrrox
Ah i fixed the bms one line copy paste error now it should work.

Re: No More Heroes: Heroes' Paradise

Posted: Thu May 19, 2011 1:03 pm
by noazett
thanks a lot chrrox :)

Re: No More Heroes: Heroes' Paradise

Posted: Thu May 19, 2011 10:17 pm
by rexil
Thanks, awesome as always.