READ THE RULES: Click here

Follow us on Facebook: https://www.facebook.com/xentax/ :)

Sudden Attack - Noesis Script Creation

Post questions about game models here, or help out others!
TRDaz
mega-veteran
mega-veteran
Posts: 203
Joined: Sat Sep 24, 2011 7:06 pm
Has thanked: 67 times
Been thanked: 24 times

Sudden Attack - Noesis Script Creation

Post by TRDaz » Mon Mar 13, 2017 5:23 pm

So I've been working on a Noesis script for Sudden Attack (I saw AceWell edited a script for it previously on zenhax, but it doesn't work for every model so I wanted to do it so most of them worked, and also so it gives me experience with Noesis scripts) and I have used the video tutorial that mariokart64n had explaining how he made a script for a Monopoly model. However this tutorial doesn't cover UVs or possibly submeshes (the file I originally tested this on has no submeshes, but there are other files that do), so I was wondering if someone could assist me on how I would get them working?
I looked at AceWell's edited script and tried to edit some lines in there to see if UVs work in my script, I'm really not sure I added them correctly or even wrote in the numbers where needed correctly.

Below is the script I have in progress:

Code: Select all

from inc_noesis import *
import noesis
import rapi

def registerNoesisTypes():
	handle = noesis.register("Sudden Attack", ".ltb")
	noesis.setHandlerTypeCheck(handle, noepyCheckType)
	noesis.setHandlerLoadModel(handle, noepyLoadModel)
	noesis.logPopup()
	return 1

def noepyCheckType(data):
    return 1
	
class ltb_format:
	def __init__(self, data):
		self.vertex_count = 0
		self.face_count = 0
		self.vertArray = []
		self.faceArray = []
		self.setUVs = []
	def ReadFromFile(self, f):
		f.seek(0xA9, NOESEEK_ABS)
		self.vertex_count = f.readUInt()
		self.face_count = f.readUInt()
		self.vertArray = [ NoeVec3() ] * self.vertex_count
		self.faceArray = [ 0 ] * (self.face_count * 3)
		#vertBuff = f.readBytes(self.vertex_count * 0x44)
		#rapi.rpgBindPositionBufferOfs(vertBuff, noesis.RPGEODATA_FLOAT, 68, 0)
		#rapi.rpgBindNormalBufferOfs(vertBuff, noesis.RPGEODATA_FLOAT, 68, 20)
		#rapi.rpgBindUV1BufferOfs(vertBuff, noesis.RPGEODATA_FLOAT, 68, 36)
		#faceBuff = f.readBytes(self.face_count * 2)
		f.seek(0xCB, NOESEEK_ABS)
		for i in range(0, self.vertex_count):
			f.seek(0xCB + (i * 68)), NOESEEK_ABS
			self.vertArray[i] = NoeVec3((f.readFloat(), f.readFloat(), f.readFloat()))
		f.seek(0xCB + (self.vertex_count * 68), NOESEEK_ABS)
		for i in range(0, self.face_count * 3):
			self.faceArray[i] = f.readUShort()
		print(f.tell())
	
def noepyLoadModel(data, mdlList):
	ctx = rapi.rpgCreateContext()
	f = NoeBitStream(data)
	ltb = ltb_format(data)
	ltb.ReadFromFile(f)
	msh = NoeMesh ([], [], "mesh0", "mat0")
	msh.setIndices(ltb.faceArray)
	msh.setPositions(ltb.vertArray)
	mdlList.append(NoeModel([msh], [], []))
	return 1
The # parts are the lines I tried to add in to have UVs work which are most likely wrong. The mesh loads correctly, it just doesn't have any UVs.
I also have used hex2obj on these models before so I kind of know the basics of the files, I just don't know how to interpret that into a Noesis script.
I have uploaded two samples, hyuna_s1 is the file I began with for the script, chorong is a file that has multiple meshes. https://1drv.ms/u/s!AoEEkLgybKv7hEhbCrI5kXH75i13
I would also like to try and get bones/weights at some point but I don't want to get ahead of myself yet.
Thanks to anyone that helps.

User avatar
shakotay2
MEGAVETERAN
MEGAVETERAN
Posts: 2802
Joined: Fri Apr 20, 2012 9:24 am
Location: Nexus, searching for Jim Kirk
Has thanked: 698 times
Been thanked: 1442 times

Re: Sudden Attack - Noesis Script Creation

Post by shakotay2 » Thu Mar 16, 2017 4:51 am

A working script for ltb (except for meshtype 8, vstride 68) was from finale00, so I guess, it would be nice to mention him.
TRDaz wrote: The # parts are the lines I tried to add in to have UVs work which are most likely wrong. The mesh loads correctly, it just doesn't have any UVs.
Introduce an additional meshtype 8 and you're done (named it '8', just a wild guess; obviously detected as 4 by fmt_ZuOnline_ltb.py):

Code: Select all

        elif meshType == 8:
            vertBuff = self.inFile.readBytes(numVerts * 68)
            rapi.rpgBindPositionBufferOfs(vertBuff, noesis.RPGEODATA_FLOAT, 68, 0)
            rapi.rpgBindNormalBufferOfs(vertBuff, noesis.RPGEODATA_FLOAT, 68, 24)
            rapi.rpgBindUV1BufferOfs(vertBuff, noesis.RPGEODATA_FLOAT, 68, 36)
The uv offset 36 is in your addition (commented out, though), so dunno how you confused yourself?
I also have used hex2obj on these models before so I kind of know the basics of the files, I just don't know how to interpret that into a Noesis script.
making a vstride map is the base of all, normals in green rectangle:
(see AceWell used 20 as normals offset, so you've to check in blender for example which are the correct ones)
hyuna_H2O.JPG
btw: this is the submeshes log of hyuna from the patched ZuOnline script:
Detected file type: LithTech
bones: 27, meshes: 1
parse mesh: 0x62
name: body, subMeshes: 5
parse submesh, count: 0x88 , 5
meshtype: 8
parse vertices, count: 0xcb , 7088
parse faces, count: 0x75b8b , 27135
0
meshtype: 8
parse vertices, count: 0x83128 , 5799
parse faces, count: 0xe3584 , 21399
0
meshtype: 8
parse vertices, count: 0xede2d , 4621
parse faces, count: 0x13a9a1 , 15378
0
meshtype: 8
parse vertices, count: 0x142358 , 3145
parse faces, count: 0x1766bc , 9159
0
meshtype: 8
parse vertices, count: 0x17afdd , 3145
parse faces, count: 0x1af341 , 9159
0
-----------------------

ChoRong is more complicated:
Detected file type: LithTech
bones: 27, meshes: 3
parse mesh: 0x62
name: ChoRong_face, subMeshes: 5
parse submesh, count: 0x90 , 5
parse vertices, count: 0xd3 , 1397
parse faces, count: 0xc547 , 7800
0
parse vertices, count: 0x1028d , 1034
parse faces, count: 0x193f5 , 5748
0
parse vertices, count: 0x1c133 , 622
parse faces, count: 0x218ab , 3348
0
parse vertices, count: 0x23329 , 226
parse faces, count: 0x252f1 , 1161
0
parse vertices, count: 0x25c59 , 123
parse faces, count: 0x26da5 , 552
0
name: ChoRong_hair, subMeshes: 5
parse submesh, count: 0x27242 , 5
parse vertices, count: 0x27285 , 452
parse faces, count: 0x2b215 , 1992
parse vertices, count: 0x2c207 , 340
parse faces, count: 0x2f1d7 , 1419
parse vertices, count: 0x2fd4f , 265
parse faces, count: 0x32293 , 1044
parse vertices, count: 0x32b1d , 191
parse faces, count: 0x345f9 , 678
parse vertices, count: 0x34ba7 , 144
parse faces, count: 0x35fe7 , 474
name: ChoRong_body, subMeshes: 5
parse submesh, count: 0x363e8 , 5
parse vertices, count: 0x3642b , 4197
parse faces, count: 0x63587 , 15099
parse vertices, count: 0x6acf7 , 3307
parse faces, count: 0x8e55b , 11466
parse vertices, count: 0x940a5 , 2749
parse faces, count: 0xb1921 , 8949
parse vertices, count: 0xb60f1 , 1485
parse faces, count: 0xc602d , 4080
parse vertices, count: 0xc81b7 , 1332
parse faces, count: 0xd66a7 , 3597
You do not have the required permissions to view the files attached to this post.
Bigchillghost, Reverse Engineering a Game Model: viewtopic.php?f=29&t=17889
extracting simple models: viewtopic.php?f=29&t=10894
Make_H2O-ForzaHor3-jm9.zip
"You quoted the whole thing, what a mess."

TRDaz
mega-veteran
mega-veteran
Posts: 203
Joined: Sat Sep 24, 2011 7:06 pm
Has thanked: 67 times
Been thanked: 24 times

Re: Sudden Attack - Noesis Script Creation

Post by TRDaz » Thu Mar 16, 2017 9:40 am

Thank you for your help, I should have mentioned finale00, sorry about that.

However I have tried to do adding the meshtype, but the script keeps saying it is invalid syntax and I can't see any other mention of meshtype in the other scripts that could make it work. Any idea why? Sorry if I sound stupid lol.

User avatar
shakotay2
MEGAVETERAN
MEGAVETERAN
Posts: 2802
Joined: Fri Apr 20, 2012 9:24 am
Location: Nexus, searching for Jim Kirk
Has thanked: 698 times
Been thanked: 1442 times

Re: Sudden Attack - Noesis Script Creation

Post by shakotay2 » Thu Mar 16, 2017 11:15 am

I've added this in finale00's Zuonline script:
in
def parse_vertices(self, numVerts, meshType):

Code: Select all

        elif meshType == 8:
            vertBuff = self.inFile.readBytes(numVerts * 68)
            rapi.rpgBindPositionBufferOfs(vertBuff, noesis.RPGEODATA_FLOAT, 68, 0)
            rapi.rpgBindNormalBufferOfs(vertBuff, noesis.RPGEODATA_FLOAT, 68, 20)
            rapi.rpgBindUV1BufferOfs(vertBuff, noesis.RPGEODATA_FLOAT, 68, 36)
        else:
            print("unknown meshType: %d" %meshType)
in
def parse_submesh(self, numSubmesh):

Code: Select all

            #sectionSize could be 0
            if sectionSize:
                start = self.inFile.tell()
                numVerts = self.inFile.readUInt()
                numIdx = self.inFile.readUInt() * 3
                meshType = self.inFile.readUInt()
                # nonsense patch by me:D
                if numVerts == 7088:
                    meshType = 8
                if numVerts == 5799:
                    meshType = 8
                if numVerts == 4621:
                    meshType = 8
                if numVerts == 3145:
                    meshType = 8
This is very sloppy, and works for hyuna only, it's cause I don't like python :D

btw; great, that you care for coding now! :)
Bigchillghost, Reverse Engineering a Game Model: viewtopic.php?f=29&t=17889
extracting simple models: viewtopic.php?f=29&t=10894
Make_H2O-ForzaHor3-jm9.zip
"You quoted the whole thing, what a mess."

TRDaz
mega-veteran
mega-veteran
Posts: 203
Joined: Sat Sep 24, 2011 7:06 pm
Has thanked: 67 times
Been thanked: 24 times

Re: Sudden Attack - Noesis Script Creation

Post by TRDaz » Thu Mar 16, 2017 10:19 pm

I see, I've tried to add this into my own script, and there are no errors (there used to be with invalid syntax but not anymore) but the UVs still don't seem to load? I'm not really sure why.
If I am unable to get this to work I may just edit the current scripts as you have been doing to try and increase my knowledge before attempting a full script of my own.

Haha, I've cared for coding for a while, I just never had much knowledge in that until recently and since I've used hex2obj (thanks for the program and your tutorial with it, without that I wouldn't be trying any of this now lol.)

EDIT: I just re-tested the script since I actually missed out the "meshType = 8" coding, and that just gives me:
Image

User avatar
chrrox
Moderator
Posts: 2552
Joined: Sun May 18, 2008 3:01 pm
Has thanked: 57 times
Been thanked: 1289 times

Re: Sudden Attack - Noesis Script Creation

Post by chrrox » Thu Mar 16, 2017 10:53 pm

if meshtype == 8:

TRDaz
mega-veteran
mega-veteran
Posts: 203
Joined: Sat Sep 24, 2011 7:06 pm
Has thanked: 67 times
Been thanked: 24 times

Re: Sudden Attack - Noesis Script Creation

Post by TRDaz » Thu Mar 16, 2017 11:03 pm

Oh thanks that fixed the invalid syntax, it still doesn't seem to load up UVs though which is weird.

User avatar
shakotay2
MEGAVETERAN
MEGAVETERAN
Posts: 2802
Joined: Fri Apr 20, 2012 9:24 am
Location: Nexus, searching for Jim Kirk
Has thanked: 698 times
Been thanked: 1442 times

Re: Sudden Attack - Noesis Script Creation

Post by shakotay2 » Thu Mar 16, 2017 11:27 pm

Is #rapi.rpgBindUV1BufferOfs() still commented out in your code?
delete the # then (which disables the line from being "executed", so it's just a comment)

I'd suggest to read chrrox' Noesis tutorial to gain more understanding
otherwise you'll fall from one trouble into the other, I guess.

did you follow mariokart64n's tutorial?
" msh.setIndices(ltb.faceArray)"
" msh.setPositions(ltb.vertArray)"
is a little bit uncommon to me (doesn't seem to be part of the rapi.rpg interface)

You might be required to use self.setUVs([]) to get the uvs.
Bigchillghost, Reverse Engineering a Game Model: viewtopic.php?f=29&t=17889
extracting simple models: viewtopic.php?f=29&t=10894
Make_H2O-ForzaHor3-jm9.zip
"You quoted the whole thing, what a mess."

User avatar
MrAdults
Moderator
Posts: 1007
Joined: Mon Mar 23, 2009 2:57 am
Has thanked: 44 times
Been thanked: 498 times

Re: Sudden Attack - Noesis Script Creation

Post by MrAdults » Fri Mar 17, 2017 3:13 am

mariokart64n's tutorial uses the more manual means of constructing a mesh, which probably does seem more familiar to someone coming from Max. But it's actually much slower to do things that way - using the rpg interface is generally recommended for performance reasons. (and it's usually more convenient, once you understand the interface)

User avatar
mariokart64n
ultra-veteran
ultra-veteran
Posts: 529
Joined: Sun Jun 05, 2005 12:00 pm
Location: Ontario, Canada
Has thanked: 23 times
Been thanked: 163 times
Contact:

Re: Sudden Attack - Noesis Script Creation

Post by mariokart64n » Fri Mar 17, 2017 5:48 am

Once I get more comfortable with python and the noesis module I'll try out rapi, it seems the easiest way to read buffers :D

@ TRDaz
The two file samples you posted have two different vertex structures, and I failed to locate a vertex definition for them.
I think you need to look at a few samples and look for a vertex type id, which you will then need to study and map for each id.

Here is a video I did working with your script, sorry the video is in real time and is an hour long. :cry:

https://youtu.be/fhESPMNrtu8

Image

This is my take on the script, but it is still a work in progress

Code: Select all

from inc_noesis import *

def registerNoesisTypes():
	handle = noesis.register("Sudden Attack", ".ltb")
	noesis.setHandlerTypeCheck(handle, noepyCheckType)
	noesis.setHandlerLoadModel(handle, noepyLoadModel)
	noesis.logPopup()
	return 1

def noepyCheckType(data):
	return 1

class geometry:
	def __init__(self):
		self.positions = [NoeVec3((0.0, 0.0, 0.0))]
		self.normals = [NoeVec3((0.0, 0.0, 0.0))]
		self.texcoord = [NoeVec3((0.0, 0.0, 0.0))]
		self.indices = [0]
	def sizeArrays(self, vertex_count, face_count):
		self.positions = [NoeVec3((0.0, 0.0, 0.0))] * vertex_count
		self.normals = [NoeVec3((0.0, 0.0, 0.0))] * vertex_count
		self.texcoord = [NoeVec3((0.0, 0.0, 0.0))] * vertex_count
		self.indicies = [0] * face_count

class ltb_format:
	def __init__(self):
		self.vertex_count = 0
		self.face_count = 0
	def ReadFromFile(self, f, g):
		f.seek(0x62, NOESEEK_ABS)
		f.seek(f.readUShort(), NOESEEK_REL)
		f.seek(0x41, NOESEEK_REL)
		self.vertex_count = f.readUInt()
		self.face_count = f.readUInt()
		unk01 = f.readUInt()
		f.seek(0x16, NOESEEK_REL)
		print("Vertex Count: " + str(self.vertex_count))
		print("Face Count: " + str(self.face_count))
		g.sizeArrays(self.vertex_count, self.face_count * 3)
		vertex_addr = f.tell()
		vertex_stride = 68
		if unk01 == 0x04:
			vertex_stride = 68
		elif unk01 == 0x02:
			vertex_stride = 36
		print("Vert Addr: " + str(f.tell()))
		for i in range(0, self.vertex_count):
			f.seek(vertex_addr + (i * vertex_stride), NOESEEK_ABS)
			g.positions[i] = NoeVec3((f.readFloat(), f.readFloat(), f.readFloat()))
			f.seek(12, NOESEEK_REL)
			g.normals[i] = NoeVec3((f.readFloat(), f.readFloat(), f.readFloat()))
			g.texcoord[i] = NoeVec3((f.readFloat(), f.readFloat(), 0))
		f.seek(vertex_addr + (vertex_stride * self.vertex_count), NOESEEK_ABS)
		print("face Addr: " + str(f.tell()))
		for i in range(0, self.face_count * 3):
			g.indicies[i] = f.readUShort()
		print(f.tell())

def noepyLoadModel(data, mdlList):
	g = geometry()
	f = NoeBitStream(data)
	ltb = ltb_format()
	ltb.ReadFromFile(f, g)
	msh = NoeMesh([], [], "mesh0", "mat0")
	msh.setPositions(g.positions)
	msh.setNormals(g.normals)
	msh.setUVs(g.texcoord)
	msh.setIndices(g.indicies)
	mdlList.append(NoeModel([msh], [], []))
	return 1
Research: [DOA2U] [DOA5U]

TRDaz
mega-veteran
mega-veteran
Posts: 203
Joined: Sat Sep 24, 2011 7:06 pm
Has thanked: 67 times
Been thanked: 24 times

Re: Sudden Attack - Noesis Script Creation

Post by TRDaz » Fri Mar 17, 2017 10:42 am

Thank you for everyone's help!
Chrrox spoke to me yesterday about working with the normal way of writing scripts and how to understand some information in the header, but the video you have made will help me, so thanks!

User avatar
Acewell
VIP member
VIP member
Posts: 1269
Joined: Wed Nov 05, 2008 12:16 pm
Has thanked: 2408 times
Been thanked: 740 times

Re: Sudden Attack - Noesis Script Creation

Post by Acewell » Sat Mar 18, 2017 10:51 am

i'm not so sure this format is a good one to learn with, when you look at enough sample you see the "meshType"
can be the same but have different vertex strides in different models, i think the "meshType" variable may either be
completely wrong or partially correct, this crucial part needs further examination.
example:
the meshType 4 in hyuna_s1.ltb has a 68 byte stride
the meshType 4 in chorong.ltb has a 44 byte stride

since the indices seem consistent i guess you could subtract that buffer and submesh/LOD header
from the "sectionsize" then divide that remainder by the number of vertices to get the stride.

finale00's ZuOnline script can already read all submeshes and LODs in the chorong.ltb sample but they are clumped together
http://himeworks.com/redirect.php?type= ... Online_ltb
the modified wolfteam_ltb.py script i attached here works with your hyuna_s1.ltb sample
http://zenhax.com/viewtopic.php?f=5&t=2777


i made a newbified version of mariokart64n's script that does the exact same thing, no classes or multi functions here,
as a hobby i will avoid that at all cost unless i am being paid to write python code, in only that case will i embrace it. :D
the script reads only the first submesh and first LOD of both your samples the same as his.

Code: Select all

from inc_noesis import *
import noesis
import rapi

def registerNoesisTypes():
    handle = noesis.register("Sudden Attack", ".ltb")
    noesis.setHandlerTypeCheck(handle, noepyCheckType)
    noesis.setHandlerLoadModel(handle, noepyLoadModel)
    #noesis.logPopup()
    return 1

def noepyCheckType(data):
    return 1

def noepyLoadModel(data, mdlList):
    ctx = rapi.rpgCreateContext()
    bs = NoeBitStream(data)
    bs.seek(0x62, NOESEEK_ABS)
    meshNamesize = bs.readUShort()
    meshName = bs.readBytes(meshNamesize).decode("ASCII")
    #rapi.rpgSetName(meshName)
    numLODs = bs.readUInt()
    bs.seek(0x39, NOESEEK_REL) 
    sectionSize = bs.readUInt()
    vertex_count = bs.readUInt()   
    face_count = bs.readUInt() * 3  
    meshType = bs.readUInt()
    if meshType == 2:
        vertex_stride = 36
    elif meshType == 4:
        vertex_stride = 68                       
    bs.seek(0x16, NOESEEK_REL)
    VertexBuffer = bs.readBytes(vertex_count * vertex_stride)
    if meshType == 2:
        rapi.rpgBindPositionBufferOfs(VertexBuffer, noesis.RPGEODATA_FLOAT, vertex_stride, 0)
        rapi.rpgBindNormalBufferOfs(VertexBuffer, noesis.RPGEODATA_FLOAT, vertex_stride, 16) #??
        rapi.rpgBindUV1BufferOfs(VertexBuffer, noesis.RPGEODATA_FLOAT, vertex_stride, 28)
    elif meshType == 4:
        rapi.rpgBindPositionBufferOfs(VertexBuffer, noesis.RPGEODATA_FLOAT, vertex_stride, 0)
        rapi.rpgBindNormalBufferOfs(VertexBuffer, noesis.RPGEODATA_FLOAT, vertex_stride, 24) 
        rapi.rpgBindUV1BufferOfs(VertexBuffer, noesis.RPGEODATA_FLOAT, vertex_stride, 36)
    else:
        print("This mesh not yet supported")
    FaceBuffer = bs.readBytes(face_count * 2)
    rapi.rpgCommitTriangles(FaceBuffer, noesis.RPGEODATA_USHORT, face_count, noesis.RPGEO_TRIANGLE, 1)
    mdl = rapi.rpgConstructModel()
    mdlList.append(mdl)
    rapi.rpgClearBufferBinds()   
    return 1

TRDaz
mega-veteran
mega-veteran
Posts: 203
Joined: Sat Sep 24, 2011 7:06 pm
Has thanked: 67 times
Been thanked: 24 times

Re: Sudden Attack - Noesis Script Creation

Post by TRDaz » Sun Mar 19, 2017 12:57 am

Thanks for this AceWell! It has helped me improve my script and it now works with more files than previously, it still needs some adjusting to work with others but all of this will help me.

User avatar
Acewell
VIP member
VIP member
Posts: 1269
Joined: Wed Nov 05, 2008 12:16 pm
Has thanked: 2408 times
Been thanked: 740 times

Re: Sudden Attack - Noesis Script Creation

Post by Acewell » Sun Mar 19, 2017 12:39 pm

i've been wanting to newbify and universally extending that ZuOnline script for SA for a while now
and this thread has renewed my interest in it, here is the result. :D
fmt_SuddenAttack_ltb.zip
it now has universal stride checking with the math i mentioned previously and supports all 8 known vertex stride types.

Code: Select all

vertex_stride = (sectionSize - 0x32 - (face_count * 2)) // vertex_count
it reads and draws all LODs and names each appropriately so you can identify and delete what you don't want later.
you may need to play around with the normals position to get them just right though.
try it out on more samples and see how it goes. :D
You do not have the required permissions to view the files attached to this post.
Last edited by Acewell on Sun Mar 19, 2017 6:16 pm, edited 2 times in total.

TRDaz
mega-veteran
mega-veteran
Posts: 203
Joined: Sat Sep 24, 2011 7:06 pm
Has thanked: 67 times
Been thanked: 24 times

Re: Sudden Attack - Noesis Script Creation

Post by TRDaz » Sun Mar 19, 2017 1:46 pm

Most samples seem to work with this vertex stride method, however there are a few that don't, these are two that give an error:
https://1drv.ms/u/s!AoEEkLgybKv7hEn_fefVTVVVBS7l

So far all of this is helping understand it better though :D

Post Reply