Important information: this site is currently scheduled to go offline indefinitely by end of the year.

Total War: Warhammer, Noesis script

Post questions about game models here, or help out others!
Post Reply
jayn23
mega-veteran
mega-veteran
Posts: 250
Joined: Sun Jul 17, 2011 9:30 pm
Has thanked: 61 times
Been thanked: 241 times

Total War: Warhammer, Noesis script

Post by jayn23 »

Hi,

In order to learn how to write scripts in noesis i decided to take zaramot,Total War: Warhammer II script and convert it to noesis.
i have encountered 2 issues i could use help with.

1. Warhammer has 2 files .ANIM files to get the skeleton and .V2 to get mesh data
i have a working script to import the .ANIM files but dont know how to import in noesis 2 files at the same time (.ANIM, . V2) to run them both together.
if anyone could refer me to an existing script which does this that would be great.

2. my second issue is in the .V2 script, when trying to bind the vertices i dont get the correct data, verts are in half floats, but noesis offers only
BYTE, SHORT, FLOAT
i tried using SHORT or FLOAT but both give me odd shapes, using a python script i wrote i exported the data to .obj and the mesh loads correctly
so i dont think i have the wrong data

this is the code i used to bind the vertices

Code: Select all

rapi.rpgBindPositionBufferOfs(VertBuff, noesis.RPGEODATA_SHORT, 32, 0)
if there is any way to print out the verts used (not vertbuffer) to validate my data?

Edit:
This is the full working script - supports skinned and static mesh with uv, and loads all textures as long as they are in the same folder.
loading mesh works for TWH1 + TWH2, skeleton works for TWH2, for TWH1 i have issue i couldn't resolve where skeleton loads rotated and misaligned on X- axis
You do not have the required permissions to view the files attached to this post.
Last edited by jayn23 on Sun Feb 09, 2020 1:06 am, edited 6 times in total.
jayn23
mega-veteran
mega-veteran
Posts: 250
Joined: Sun Jul 17, 2011 9:30 pm
Has thanked: 61 times
Been thanked: 241 times

Re: Noesis script help

Post by jayn23 »

this is the script i am using for the mesh: (didnt add face data until i can get the verts correct)

Code: Select all

from inc_noesis import *
import noesis
import rapi

def registerNoesisTypes():
    '''Register the plugin. Just change the Game name and extension.'''
    
    handle = noesis.register("Warhammer Total War 2", ".rigid_model_v2")
    noesis.logPopup() # activates debug console
    noesis.setHandlerTypeCheck(handle, noepyCheckType)
    noesis.setHandlerLoadModel(handle, noepyLoadModel)
    return 1

def noepyCheckType(data):
    '''Verify that the format is supported by this plugin. Default yes'''
    
    return 1
    
def noepyLoadModel(data, mdlList):

    ctx = rapi.rpgCreateContext()
    f = NoeBitStream(data, NOE_LITTLEENDIAN)
    
    EndPos = len(data)
    
    Magic = noeStrFromBytes(f.readBytes(4), "ASCII")
    print(Magic)
    if Magic != "RMV2":
        print("worng file format")
    
    MeshCount = f.readInt()
    LodCount = f.readInt()

    f.seek(140,0) # seek 0x8c from start of file
    
    for i in range(LodCount):
        f.seek(28,1)
    
    Pos = f.tell()
    
    j = 0
    s = 0
    
    # get vertex, uv data
    #read first mesh for now
    while s < 1: # read until end of file

        id = f.readUInt() # I read unsigned int 
        LodStart = f.readUInt()
        VertexStart = f.readUInt()
        VertexCount = f.readUInt()
        FaceStart = f.readUInt()
        FaceCount = f.readUInt() 

        #can skip following data need to (seek 24,1)
        BBX1 = f.readFloat() 
        BBY1 = f.readFloat()
        BBZ1 = f.readFloat()

        BBX2 = f.readFloat()
        BBY2 = f.readFloat()
        BBZ2 = f.readFloat()

        MatType = f.readString() # read string of unknown size - need to check might need seek -1
   
        Unk1 = f.readFloat()
        Unk2 = f.readFloat()

        f.seek(12,1)

        Vtype = f.readShort() # h read short

        ModelName = noeStrFromBytes(f.readBytes(32), "ASCII") # read string of known size   
        MaterialName = noeStrFromBytes(f.readBytes(514), "ASCII")
    
        for i in range(0,3):
            f.seek(52,1)
   
        FFFF = f.readInt64() #  read long long
        
        BoneCount = f.readUInt()
        MatsCount = f.readUInt()
        f.seek(8,1)
        Unk4 = f.readUInt()

        f.seek(128,1)

        for i in range(0,BoneCount):
            f.seek(84,1)

        for i in range(0,MatsCount):
            f.seek(260,1)
    
        Unk4 = f.readUInt()
        LODCount = f.readUInt()

        PosV = f.tell()
        
        print(PosV)
        print(VertexCount)
        
        if Vtype == 4:
            VertBuff = f.readBytes(VertexCount * 32)           
            rapi.rpgBindPositionBufferOfs(VertBuff, noesis.RPGEODATA_SHORT, 32, 0) # vertex
            rapi.rpgBindUV1BufferOfs(VertBuff, noesis.RPGEODATA_SHORT, 32, 20) # uv
        if Vtype == 3:
            VertBuff = f.readBytes(VertexCount * 28)
            rapi.rpgBindPositionBufferOfs(VertBuff, noesis.RPGEODATA_SHORT, 28, 0)
            rapi.rpgBindUV1BufferOfs(VertBuff, noesis.RPGEODATA_SHORT, 28, 16)

        rapi.rpgCommitTriangles(None, noesis.RPGEODATA_USHORT, VertexCount, noesis.RPGEO_POINTS, 1)
        mdl = rapi.rpgConstructModel()
        mdlList.append(mdl)
        
        
        #need to convert to noesis       
        # get faces array
        #faceArray.append([])
        #for i in range(int(FaceCount/3)):           
            #f1 = f.readShort()
            #f2 = f.readShort()
            #f3 = f.readShort()
            #faceArray[s].append((f1,f2,f3))
            
        Pos = f.tell()
        s += 1
            
    rapi.rpgClearBufferBinds()
    return 1 
link to sample file:
https://drive.google.com/open?id=1hV7lz ... eIEiGwAjRd
Last edited by jayn23 on Wed Sep 25, 2019 7:04 pm, edited 1 time in total.
User avatar
Bigchillghost
double-veteran
double-veteran
Posts: 1030
Joined: Tue Jul 05, 2016 9:37 am
Has thanked: 32 times
Been thanked: 1215 times

Re: Noesis script help

Post by Bigchillghost »

jayn23 wrote: Tue Sep 24, 2019 7:45 pmdont know how to import in noesis 2 files at the same time (.ANIM, . V2) to run them both together.
Use the following workflow to open another source data file:

Code: Select all

animFile = rapi.loadIntoByteArray(animFileName)
if animFile == None:
	return 0
animBS = NoeBitStream(animFile)
jayn23 wrote: Tue Sep 24, 2019 7:45 pmbut noesis offers only BYTE, SHORT, FLOAT
There is actually a readHalfFloat() method in the NoeBitStream class. Also a RPGEODATA_HALFFLOAT constant available.
May you find peace in this puzzle-solving game. Say it with action: click the Image when you get helped.:)
Acewell
VIP member
VIP member
Posts: 1330
Joined: Wed Nov 05, 2008 12:16 pm
Has thanked: 2710 times
Been thanked: 884 times

Re: Noesis script help

Post by Acewell »

jayn23 wrote: Tue Sep 24, 2019 7:45 pm 2. my second issue is in the .V2 script, when trying to bind the vertices i dont get the correct data, verts are in half floats, but noesis offers only
BYTE, SHORT, FLOAT
RPGEODATA_HALFFLOAT seems to still work fine when
i tried in this basic script with hardcoded values. :D

Code: Select all

from inc_noesis import *
import noesis
import rapi

def registerNoesisTypes():
    handle = noesis.register("Warhammer Total War 2", ".rigid_model_v2")
    noesis.setHandlerTypeCheck(handle, noeCheckGeneric)
    #noesis.setHandlerTypeCheck(handle, noepyCheckType)
    noesis.setHandlerLoadModel(handle, noepyLoadModel)
    #noesis.logPopup()
    return 1

def noepyLoadModel(data, mdlList):
    ctx = rapi.rpgCreateContext()
    bs = NoeBitStream(data)
    FIOffset = 0x25fd4
    FCount = 23229
    VOffset = 0xdb4
    VBytes = 32
    VCount = 4753
    bs.seek(FIOffset)
    IBuf = bs.readBytes(FCount * 2)
    bs.seek(VOffset)
    VBuf = bs.readBytes(VCount * VBytes)
    rapi.rpgBindPositionBufferOfs(VBuf, noesis.RPGEODATA_HALFFLOAT, VBytes, 0)
    rapi.rpgBindUV1BufferOfs(VBuf, noesis.RPGEODATA_HALFFLOAT, VBytes, 20)
    rapi.rpgCommitTriangles(IBuf, noesis.RPGEODATA_SHORT, FCount, noesis.RPGEO_TRIANGLE, 1)
    mdl = rapi.rpgConstructModel()                                                          
    mdlList.append(mdl)
    rapi.rpgClearBufferBinds()
    return 1
test.png
You do not have the required permissions to view the files attached to this post.
User avatar
shakotay2
MEGAVETERAN
MEGAVETERAN
Posts: 4291
Joined: Fri Apr 20, 2012 9:24 am
Location: Nexus, searching for Jim Kirk
Has thanked: 1151 times
Been thanked: 2244 times

Re: Noesis script help

Post by shakotay2 »

jayn23 wrote: Wed Sep 25, 2019 5:40 am this is the script i am using for the mesh: (didnt add face data until i can get the verts correct)

Code: Select all

from inc_noesis import *
import noesis
import rapi

def registerNoesisTypes():
    '''Register the plugin. Just change the Game name and extension.'''
    
    handle = noesis.register("Warhammer Total War 2", ".rigid_model_v2")
    noesis.logPopup() # activates debug console
    noesis.setHandlerTypeCheck(handle, noepyCheckType)
    noesis.setHandlerLoadModel(handle, noepyLoadModel)
    return 1

def noepyCheckType(data):
    '''Verify that the format is supported by this plugin. Default yes'''
    
    return 1
    
def noepyLoadModel(data, mdlList):

    ctx = rapi.rpgCreateContext()
    f = NoeBitStream(data, NOE_LITTLEENDIAN)
    
    EndPos = len(data)
    
    Magic = noeStrFromBytes(f.readBytes(4), "ASCII")
    print(Magic)
    if Magic != "RMV2":
        print("worng file format")
    
    MeshCount = f.readInt()
    LodCount = f.readInt()

    f.seek(140,0) # seek 0x8c from start of file
    
    for i in range(LodCount):
        f.seek(28,1)
    
    Pos = f.tell()
    
    j = 0
    s = 0
    
    # get vertex, uv data
    #read first mesh for now
    while s < 1: # read until end of file

        id = f.readUInt() # I read unsigned int 
        LodStart = f.readUInt()
        VertexStart = f.readUInt()
        VertexCount = f.readUInt()
        FaceStart = f.readUInt()
        FaceCount = f.readUInt() 

        #can skip following data need to (seek 24,1)
        BBX1 = f.readFloat() 
        BBY1 = f.readFloat()
        BBZ1 = f.readFloat()

        BBX2 = f.readFloat()
        BBY2 = f.readFloat()
        BBZ2 = f.readFloat()

        MatType = f.readString() # read string of unknown size - need to check might need seek -1
   
        Unk1 = f.readFloat()
        Unk2 = f.readFloat()

        f.seek(12,1)

        Vtype = f.readShort() # h read short

        ModelName = noeStrFromBytes(f.readBytes(32), "ASCII") # read string of known size   
        MaterialName = noeStrFromBytes(f.readBytes(514), "ASCII")
    
        for i in range(0,3):
            f.seek(52,1)
   
        FFFF = f.readInt64() #  read long long
        
        BoneCount = f.readUInt()
        MatsCount = f.readUInt()
        f.seek(8,1)
        Unk4 = f.readUInt()

        f.seek(128,1)

        for i in range(0,BoneCount):
            f.seek(84,1)

        for i in range(0,MatsCount):
            f.seek(260,1)
    
        Unk4 = f.readUInt()
        LODCount = f.readUInt()

        PosV = f.tell()
        
        print(PosV)
        print(VertexCount)
        
        if Vtype == 4:
            VertBuff = f.readBytes(VertexCount * 32)           
            rapi.rpgBindPositionBufferOfs(VertBuff, noesis.RPGEODATA_SHORT, 32, 0) # vertex
            rapi.rpgBindUV1BufferOfs(VertBuff, noesis.RPGEODATA_SHORT, 32, 20) # uv
        if Vtype == 3:
            VertBuff = f.readBytes(VertexCount * 28)
            rapi.rpgBindPositionBufferOfs(VertBuff, noesis.RPGEODATA_SHORT, 28, 0)
            rapi.rpgBindUV1BufferOfs(VertBuff, noesis.RPGEODATA_SHORT, 28, 16)

        rapi.rpgCommitTriangles(None, noesis.RPGEODATA_USHORT, VertexCount, noesis.RPGEO_POINTS, 1)
        mdl = rapi.rpgConstructModel()
        mdlList.append(mdl)
        
        
        #need to convert to noesis       
        # get faces array
        #faceArray.append([])
        #for i in range(int(FaceCount/3)):           
            #f1 = f.readShort()
            #f2 = f.readShort()
            #f3 = f.readShort()
            #faceArray[s].append((f1,f2,f3))
            
        Pos = f.tell()
        s += 1
            
    rapi.rpgClearBufferBinds()
    return 
I wonder how you get this script to run? Which Noesis version do you use?
I always get a Type error (Noesis debug output):

Code: Select all

Detected file type: Warhammer Total War 2
RMV2
201810 3256 4753 155352 23229
-0.738558828830719 -0.0019068646943196654 -2.2086567878723145
0.7385584712028503 2.200343370437622 0.940676212310791 default_dry
bst_centigor_body_01 variantmeshes/wh_variantmodels/ce1/bst/bst_centigor/tex/
-1 12 5
Unk4 1
0 1
0xdb4
4753
0x25fd4
TypeError: an integer is required
Tuts: a) Bigchillghost, viewtopic.php?f=29&t=17889
b) Extracting simple models: http://forum.xentax.com/viewtopic.php?f=29&t=10894
"Quoting the whole thing. Would u ever stop this nonsense?"
Acewell
VIP member
VIP member
Posts: 1330
Joined: Wed Nov 05, 2008 12:16 pm
Has thanked: 2710 times
Been thanked: 884 times

Re: Noesis script help

Post by Acewell »

shakotay2 wrote: Wed Sep 25, 2019 9:28 am I wonder how you get this script to run? Which Noesis version do you use?
I always get a Type error (Noesis debug output):
i think is because his script is posted with incomplete last line:
return should be return 1 :D
User avatar
shakotay2
MEGAVETERAN
MEGAVETERAN
Posts: 4291
Joined: Fri Apr 20, 2012 9:24 am
Location: Nexus, searching for Jim Kirk
Has thanked: 1151 times
Been thanked: 2244 times

Re: Noesis script help

Post by shakotay2 »

Ace, you're simply the best! ("better than all the rest" :D , A chance is going to come, Tina Turner)
.
not as nice as your's (how did you smooth it? edit: well, see, I missed another "1" :D ):
.
bst_centigor_body_01-rigid_model_v2-Noesis-py.jpg
You do not have the required permissions to view the files attached to this post.
Tuts: a) Bigchillghost, viewtopic.php?f=29&t=17889
b) Extracting simple models: http://forum.xentax.com/viewtopic.php?f=29&t=10894
"Quoting the whole thing. Would u ever stop this nonsense?"
jayn23
mega-veteran
mega-veteran
Posts: 250
Joined: Sun Jul 17, 2011 9:30 pm
Has thanked: 61 times
Been thanked: 241 times

Re: Noesis script help

Post by jayn23 »

Hi

thanks guys for all your help, i got it working :)

now i just need to add the weight data, and combine it with the .ANIM script
for the weight data i couldn't find anything similar to "rapi.rpgBindPositionBufferOfs(VertBuff, noesis.RPGEODATA_SHORT, 32, 0)"
so i just store it an an array that i create? or is there some class or function that is already built in?
animFile = rapi.loadIntoByteArray(animFileName)
if animFile == None:
return 0
animBS = NoeBitStream(animFile)
i am not sure i understand what to do with this?
i understand the code where i would load a second file, but how do i choose the file that needs loading?
for the first file you just double click a file and that's the file noesis loads, how do you get noesis to ask to open/choose the second file?
i think is because his script is posted with incomplete last line:
return should be return 1 :D
my bad :D fixed

thanks again really appreciate the help
Last edited by jayn23 on Wed Sep 25, 2019 8:12 pm, edited 1 time in total.
User avatar
shakotay2
MEGAVETERAN
MEGAVETERAN
Posts: 4291
Joined: Fri Apr 20, 2012 9:24 am
Location: Nexus, searching for Jim Kirk
Has thanked: 1151 times
Been thanked: 2244 times

Re: Noesis script help

Post by shakotay2 »

jayn23 wrote: Wed Sep 25, 2019 7:18 pmhow do you get noesis it to ask to open/choose the second file?
try out something like this:

Code: Select all

texFileName = rapi.getDirForFilePath(rapi.getInputName()) + "../texture/texture.cpr"
if (rapi.checkFileExists(texFileName)):
	cprTexData = rapi.loadIntoByteArray(texFileName)
(It's from fmt_bulletwitch_cpr.py.)

btw: didn't test it; hope, rapi.getInputName() will open a file browser...
but if so I wouldn't know why should one check, whether the "FileExists"?

Makes sense in case the files (mesh/anim) share the same basename, so you loaded the mesh (bubba.msh), cut of its extension, add ".ani"
and then you had to check whether bubba.ani exists.

edit: well, all the time I felt I must have a deja vue but my brain is empty (as always),
then loadPairedFile was coming into mind.

You might check this post/similar problem:
tainhx wrote: Sat Mar 16, 2019 8:05 am

Code: Select all

    filePath = rapi.getInputName()   # get file path
    fileExt = filePath.split('.')[-1]  # get file extesion

    boneData = rapi.loadPairedFile("YulgangVN - BON", ".bon")
    aniData = rapi.loadPairedFile("YulgangVN - Ani", ".ani")
Tuts: a) Bigchillghost, viewtopic.php?f=29&t=17889
b) Extracting simple models: http://forum.xentax.com/viewtopic.php?f=29&t=10894
"Quoting the whole thing. Would u ever stop this nonsense?"
jayn23
mega-veteran
mega-veteran
Posts: 250
Joined: Sun Jul 17, 2011 9:30 pm
Has thanked: 61 times
Been thanked: 241 times

Re: Noesis script help

Post by jayn23 »

i found a work around in an old script fmt_C9_r3cm.py basically what i did:

Code: Select all

    dirPath = rapi.getDirForFilePath(rapi.getInputName())
    fileList = [file for file in os.listdir(dirPath) if file.lower().endswith(".anim")]
    for file in fileList:
        filename, ext = os.path.splitext(file)
        bs = open(dirPath + file, 'rb')
        bs_data = bs.read()

bones = skeleton_data(bs_data)
for this to work mesh and skeleton files must be in the same directory and no other . anim files in folder, i am going to try the "rapi.loadPairedFile" sound like exactly what i am looking for.

for now what i am getting is...interesting
my mesh and skeleton dont Quite match up in size [roll]
i guess i need to work an my weight implementation.
model.JPG
You do not have the required permissions to view the files attached to this post.
jayn23
mega-veteran
mega-veteran
Posts: 250
Joined: Sun Jul 17, 2011 9:30 pm
Has thanked: 61 times
Been thanked: 241 times

Re: Noesis script help

Post by jayn23 »

then loadPairedFile was coming into mind.
worked perfectly thanks for that.

just one more issue i could some help with,
bindings the weights to skeleton/mesh - i dont understand the syntax that is needed:

Code: Select all

from os.path import *
from math import *
from inc_noesis import *



def registerNoesisTypes():
    handle = noesis.register("Warhammer Total War 2", ".anim;.rigid_model_v2")
    noesis.setHandlerTypeCheck(handle, noepyCheckType)
    noesis.setHandlerLoadModel(handle, noepyLoadModel)
    # noesis.setHandlerWriteModel(handle, noepyWriteModel)
    # noesis.setHandlerWriteAnim(handle, noepyWriteAnim)

    noesis.logPopup()
    return 1


def noepyCheckType(data):
    '''Verify that the format is supported by this plugin. Default yes'''
    
    return 1



#load the skeleton data
def Skeleton(data):
    bs = NoeBitStream(data, NOE_LITTLEENDIAN)
    bs.seek(12,0)
    SkelNameSize = bs.readShort()   
    SkelName = bs.readString()
    print(SkelName)
    bs.seek(-1,1) # for some reason read string is reading 1 byte extra
    Check = bs.readUInt()
    print(Check)
    
    if Check == 0:
        bs.seek(4,1)
    
    if Check != 0:
        bs.seek(0,1)
    
    BoneCount = bs.readUInt()
    print(BoneCount)
    
    parent_arr = []
    BoneNameArray = []
    
    for i in range(BoneCount):
        BNameSize = bs.readShort()
        BoneName = noeStrFromBytes(bs.readBytes(BNameSize), "ASCII")
        BoneParent = bs.readInt()
        parent_arr.append(BoneParent) #parr in maxscript
        BoneNameArray.append(BoneName)
        
 
    BMapArray = []
    bones = []
    
    for i in range(BoneCount):
        BoneId = bs.readUInt() + 1 # need t ocheck if i need this + 1
        BMapArray.append(BoneId)
        
    #dont think i need this
    for i in range(BoneCount):
        BoneId = bs.readUInt()
        
    if Check == 0:
        bs.seek(8,1)
        
    #dont think i need this, can skip?
    BoneCount1 = bs.readInt()
    BoneCount2 = bs.readInt()    
    Count = bs.readInt()
    
    translation_arr = []
    rotation_arr = []
    Quat = [0]*4
    
    for i in range(BoneCount):
        Tran = NoeVec3.fromBytes(bs.readBytes(12)) 
        translation_arr.append(Tran) # in maxscript * 100 to make bigger
    #print(translation_arr)
  
    for i in range(BoneCount): 
        #a = bs.tell()
        #print(a)
        b11 = bs.readShort()
        b12 = bs.readShort()
        b13 = bs.readShort()
        b14 = bs.readShort()
        #print("b14 = ", b14)
        Rot = NoeQuat((b11,b12,b13,b14))
        #print("Rot = ", Rot)
        Rot = Rot.normalize()
        boneMat = Rot.toMat43(transposed = 1)
        boneMat[3] = translation_arr[i]
        #print("Norm = ", Rot)
        #print("boneMat = ", boneMat)
        bones.append( NoeBone(i, BoneNameArray[i], boneMat, None, parent_arr[i]) )
        


    # Converting local matrix to world space
    for i in range(0, BoneCount):
        j = bones[i].parentIndex
        #print("j = ", j)
        if j != -1:
            bones[i].setMatrix(bones[i].getMatrix().__mul__(bones[j].getMatrix()))

    return bones


#load mesh data
def Mesh(data):
    weights = []
    boneids = []

    f = NoeBitStream(data, NOE_LITTLEENDIAN)
    
    EndPos = len(data)
    
    Magic = noeStrFromBytes(f.readBytes(4), "ASCII")
    print(Magic)
    if Magic != "RMV2":
        print("worng file format")
    
    MeshCount = f.readInt()
    LodCount = f.readInt()

    f.seek(140,0) # seek 0x8c from start of file
    
    for i in range(LodCount):
        f.seek(28,1)
    
    Pos = f.tell()
    
    j = 0
    s = 0
    
    # get vertex, weight, uv data
    #read first mesh for now
    while s < (MeshCount + LodCount): # read until end of file

        id = f.readUInt() # I read unsigned int 
        LodStart = f.readUInt()
        VertexStart = f.readUInt()
        VertexCount = f.readUInt()
        FaceStart = f.readUInt()
        FaceCount = f.readUInt() #  facecount/3 
        print("face count = ",FaceCount/3)
        print("VertexCount = ",VertexCount)
        print("\n")

        #can skip following data need to (seek 24,1)
        BBX1 = f.readFloat() 
        BBY1 = f.readFloat()
        BBZ1 = f.readFloat()

        BBX2 = f.readFloat()
        BBY2 = f.readFloat()
        BBZ2 = f.readFloat()

        MatType = f.readString() # read string of unknown size - need to check might need seek -1
   
        Unk1 = f.readFloat()
        Unk2 = f.readFloat()

        f.seek(12,1)

        Vtype = f.readShort() # h read short

        ModelName = noeStrFromBytes(f.readBytes(32), "ASCII") # read string of known size   
        MaterialName = noeStrFromBytes(f.readBytes(514), "ASCII")
    
        for i in range(0,3):
            f.seek(52,1)
   
        FFFF = f.readInt64() #  read long long
        
        BoneCount = f.readUInt()
        MatsCount = f.readUInt()
        f.seek(8,1)
        Unk4 = f.readUInt()

        f.seek(128,1)

        for i in range(0,BoneCount):
            f.seek(84,1)

        for i in range(0,MatsCount):
            f.seek(260,1)
    
        Unk4 = f.readUInt()
        LODCount = f.readUInt()

        PosV = f.tell()
        
        #get weight data
        if Vtype == 4:
            for i in range(VertexCount):

                weights.append([])
                boneids.append([])
                
                f.seek(8,1)
                bone1 = f.readUByte()
                bone2 = f.readUByte()
                bone3 = f.readUByte()
                bone4 = f.readUByte()
                
                weight1 = f.readUByte()
                weight2 = f.readUByte()
                weight3 = f.readUByte()
                weight4 = f.readUByte()
                
                f.seek(16,1)
                
                maxweight = 0

                if weight1 != 0:
                    maxweight = maxweight + weight1
                if weight2 != 0:
                    maxweight = maxweight + weight2
                if weight3 != 0:
                    maxweight = maxweight + weight3
                if weight4 != 0:
                    maxweight = maxweight + weight4
                    
                if maxweight != 0 and weight1 != 0:
                        w1 = float(weight1)
                        boneids[j].append(bone1)
                        weights[j].append(w1/255.0)

                if maxweight != 0 and weight2 != 0 :
                        w2 = float(weight2)
                        boneids[j].append(bone2)
                        weights[j].append(w2/255.0)

                if maxweight != 0 and weight3 != 0:
                        w3 = float(weight3)
                        boneids[j].append(bone3)
                        weights[j].append(w3/255.0)

                if maxweight != 0 and weight4 != 0:
                        w4 = float(weight4)
                        boneids[j].append(bone4)
                        weights[j].append(w4/255.0) 

                        
        if Vtype == 3:

            for i in range(VertexCount):
            
                weights.append([])
                boneids.append([])
                
                f.seek(8,1)
                bone1 = f.readUByte()
                bone2 = f.readUByte()
                
                weight1 = f.readUByte()
                weight2 = 1 - (weight1/255.0)
                #print(weight1)

                
                f.seek(17,1)
                
                maxweight = 0

                if weight1 != 0:
                    maxweight = maxweight + weight1
                if weight2 != 0:
                    maxweight = maxweight + weight2

                    
                if maxweight != 0 and weight1 != 0:
                        w1 = float(weight1)
                        boneids[j].append(bone1)
                        weights[j].append(w1/255.0)

                if maxweight != 0 and weight2 != 0 :
                        w2 = float(weight2)
                        boneids[j].append(bone2)
                        weights[j].append(w2)
                        

     
        # go back to start of vertex block
        f.seek(PosV,0)
        print(Vtype)
        if Vtype == 4:
            VertBuff = f.readBytes(VertexCount * 32)           
            rapi.rpgBindPositionBufferOfs(VertBuff, noesis.RPGEODATA_HALFFLOAT, 32, 0) # vertex
            rapi.rpgBindUV1BufferOfs(VertBuff, noesis.RPGEODATA_HALFFLOAT, 32, 20) # uv
            #rapi.rpgBindBoneIndexBuffer(VertBuff, noesis.RPGEODATA_INT, 4*boneids[j], weights[j])
            #rapi.rpgBindBoneWeightBuffer(VertBuff, noesis.RPGEODATA_FLOAT, len(weights[j]), weights[j])
        if Vtype == 3:
            VertBuff = f.readBytes(VertexCount * 28)
            rapi.rpgBindPositionBufferOfs(VertBuff, noesis.RPGEODATA_HALFFLOAT, 28, 0)
            rapi.rpgBindUV1BufferOfs(VertBuff, noesis.RPGEODATA_HALFFLOAT, 28, 16)
            #rapi.rpgBindBoneIndexBuffer(VertBuff, noesis.RPGEODATA_INT, 4*fw.weightsPerVert, fw.weightsPerVert)
            #rapi.rpgBindBoneWeightBuffer(VertBuff, noesis.RPGEODATA_FLOAT, 4*fw.weightsPerVert, fw.weightsPerVert)
        
            
        FaceBuff = f.readBytes(FaceCount * 2)

        #rapi.rpgCommitTriangles(None, noesis.RPGEODATA_USHORT, VertexCount, noesis.RPGEO_POINTS, 1)
        rapi.rpgCommitTriangles(FaceBuff, noesis.RPGEODATA_USHORT, FaceCount, noesis.RPGEO_TRIANGLE, 1)


        s += 1
   
   
def noepyLoadModel(data, mdlList):
        ctx = rapi.rpgCreateContext()
        filePath = rapi.getInputName()   # get file path
        #fileExt = filePath.split('.')[-1]  # get file extesion

        skel_data = rapi.loadPairedFile("template - 1", ".anim")
        mesh_data = rapi.loadPairedFile("template - 2", ".rigid_model_v2")
                
        bones = Skeleton(skel_data) # get skeleton data
        Mesh(mesh_data) # get mesh data
        
        mdl = rapi.rpgConstructModel()
        #when i want to load only skeleton
        #mdl = NoeModel()        
        mdl.setBones(bones)
        mdlList.append(mdl)
        
        rapi.rpgClearBufferBinds()
         
        return 1                     
i found in a script the following lines to bind verts to bone by i dont understand what goes in every part (1%,2%,3% etc..) :
rapi.rpgBindBoneIndexBuffer(VertBuff, noesis.RPGEODATA_INT, 1%, 2%)
rapi.rpgBindBoneWeightBuffer(VertBuff, noesis.RPGEODATA_FLOAT, 3%, 4%)

currently i have boneids[] and coresponding weights[] arrays

thanks in advance for you help

heres a smaple of the .anim file used:
https://drive.google.com/open?id=1Jm1Ox ... XrgDnLAPOG

on the bright side i got my size issue fixed :D
Capture.JPG
You do not have the required permissions to view the files attached to this post.
Last edited by jayn23 on Fri Sep 27, 2019 12:25 pm, edited 1 time in total.
User avatar
shakotay2
MEGAVETERAN
MEGAVETERAN
Posts: 4291
Joined: Fri Apr 20, 2012 9:24 am
Location: Nexus, searching for Jim Kirk
Has thanked: 1151 times
Been thanked: 2244 times

Re: Noesis script help

Post by shakotay2 »

jayn23 wrote: Fri Sep 27, 2019 8:45 am
then loadPairedFile was coming into mind.
worked perfectly thanks for that.

just one more issue i could some help with,
bindings the weights to skeleton/mesh - i dont understand the syntax that is needed:

i found in a script the following lines to bind verts to bone by i dont understand what goes in every part (1%,2%,3% etc..) :
rapi.rpgBindBoneIndexBuffer(VertBuff, noesis.RPGEODATA_INT, 1%, 2%)
rapi.rpgBindBoneWeightBuffer(VertBuff, noesis.RPGEODATA_FLOAT, 3%, 4%)
look at pluginsource\pluginshare.h which comes with Noesis:
void (*rpgBindBoneIndexBuffer)(void *data, rpgeoDataType_e dataType, int stride, int numWeightsPerVert);
void (*rpgBindBoneWeightBuffer)(void *data, rpgeoDataType_e dataType, int stride, int numWeightsPerVert);

(numWeightsPerVert is usually 4, sometimes 3 only)
Tuts: a) Bigchillghost, viewtopic.php?f=29&t=17889
b) Extracting simple models: http://forum.xentax.com/viewtopic.php?f=29&t=10894
"Quoting the whole thing. Would u ever stop this nonsense?"
jayn23
mega-veteran
mega-veteran
Posts: 250
Joined: Sun Jul 17, 2011 9:30 pm
Has thanked: 61 times
Been thanked: 241 times

Re: Noesis script help

Post by jayn23 »

Thanks a lot i got it working now :mrgreen:

once i get some small bugs sorted out ill post the finished script for anyone who might want to use it
jayn23
mega-veteran
mega-veteran
Posts: 250
Joined: Sun Jul 17, 2011 9:30 pm
Has thanked: 61 times
Been thanked: 241 times

Re: Noesis script help

Post by jayn23 »

for anyone who is interested
Here is my finished script for Total War: Warhammer II, i have only tested it on 2 different models so i hope it works for the rest

Edit:
added support to static mesh, due to issue with static mesh and me being to lazy to try to fix it i now load only load mesh without lower res meshes

Code: Select all

# script for Total War: Warhammer II by jayn23
# Based on maxscript by Zaramot
# Thanks for all the help on the xentax fourm 

from os.path import *
from math import *
from inc_noesis import *


def registerNoesisTypes():
    handle = noesis.register("Warhammer Total War 2", ".anim;.rigid_model_v2")    
    noesis.setHandlerTypeCheck(handle, noepyCheckType)
    noesis.setHandlerLoadModel(handle, noepyLoadModel)
    #opens debug consle
    #noesis.logPopup()
    return 1


def noepyCheckType(data):
    '''Verify that the format is supported by this plugin. Default yes'''
    #I preform my check while reading mesh data
    return 1


#load skeleton data
def Skeleton(data):
    bs = NoeBitStream(data, NOE_LITTLEENDIAN)
    bs.seek(12,0)
    SkelNameSize = bs.readShort()   
    SkelName = bs.readString()
    print(SkelName)
    bs.seek(-1,1) # for some reason read string is reading 1 byte extra
    Check = bs.readUInt()
    print(Check)
    
    if Check == 0:
        bs.seek(4,1)
    
    if Check != 0:
        bs.seek(0,1)
    
    BoneCount = bs.readUInt()
    print(BoneCount)
    
    parent_arr = []
    BoneNameArray = []
    
    for i in range(BoneCount):
        BNameSize = bs.readShort()
        BoneName = noeStrFromBytes(bs.readBytes(BNameSize), "ASCII")
        BoneParent = bs.readInt()
        parent_arr.append(BoneParent) 
        BoneNameArray.append(BoneName)
        
 
    BMapArray = []
    bones = []
    
    for i in range(BoneCount):
        BoneId = bs.readUInt() #in maxscript has +1, dont think i need it
        BMapArray.append(BoneId)
        
    #dont think i need this
    for i in range(BoneCount):
        BoneId = bs.readUInt()
        
    if Check == 0:
        bs.seek(8,1)
        
    #dont think i need this, can skip?
    BoneCount1 = bs.readInt()
    BoneCount2 = bs.readInt()    
    Count = bs.readInt()
    
    translation_arr = []
    rotation_arr = []
    Quat = [0]*4
    
    for i in range(BoneCount):
        Tran = NoeVec3.fromBytes(bs.readBytes(12)) 
        translation_arr.append(Tran) 
  
    for i in range(BoneCount): 
        b11 = bs.readShort()
        b12 = bs.readShort()
        b13 = bs.readShort()
        b14 = bs.readShort()
        Rot = NoeQuat((b11,b12,b13,b14))
        Rot = Rot.normalize()
        boneMat = Rot.toMat43(transposed = 1)
        boneMat[3] = translation_arr[i]
        bones.append( NoeBone(i, BoneNameArray[i], boneMat, None, parent_arr[i]) )
        
    # Converting local matrix to world space
    for i in range(0, BoneCount):
        j = bones[i].parentIndex
        #print("j = ", j)
        if j != -1:
            bones[i].setMatrix(bones[i].getMatrix().__mul__(bones[j].getMatrix()))

    return bones


#load mesh data
def Mesh(data):

    f = NoeBitStream(data, NOE_LITTLEENDIAN)
    
    EndPos = len(data)
    
    Magic = noeStrFromBytes(f.readBytes(4), "ASCII")
    print(Magic)
    if Magic != "RMV2":
        print("worng file format")
    
    MeshCount = f.readInt()
    LodCount = f.readInt()

    f.seek(140,0) # seek 0x8c from start of file
    
    for i in range(LodCount):
        f.seek(28,1)
    
    Pos = f.tell()
    
    j = 0
    s = 0
    
    # get vertex, weight, uv data
    while s < (LodCount):

        id = f.readUInt() # read unsigned int 
        LodStart = f.readUInt()
        VertexStart = f.readUInt()
        VertexCount = f.readUInt()
        FaceStart = f.readUInt()
        FaceCount = f.readUInt() #  facecount/3 = number of tris
        print("face count = ",FaceCount/3)
        print("VertexCount = ",VertexCount)

        f.seek(24,1)

        MatType = f.readString() # read string of unknown size 
   
        Unk1 = f.readFloat()
        Unk2 = f.readFloat()

        f.seek(12,1)

        Vtype = f.readShort()

        ModelName = noeStrFromBytes(f.readBytes(32), "ASCII") # read string of known size 
        print("ModelName = ", ModelName)
        PosV = f.tell()
        print("PosV == ", PosV)
        MaterialName = noeStrFromBytes(f.readBytes(510), "ASCII") #should be 514 but noesis has some bug at byte 512 so i skip it with seek instaed
        print("MaterialName = ", MaterialName)
        f.seek(4,1)
    
        for i in range(0,3):
            f.seek(52,1)
   
        FFFF = f.readInt64() #  read long long
        
        BoneCount = f.readUInt()
        MatsCount = f.readUInt()
        f.seek(8,1)
        Unk4 = f.readUInt()

        f.seek(128,1)

        for i in range(0,BoneCount):
            f.seek(84,1)

        for i in range(0,MatsCount):
            f.seek(260,1)
    
        Unk4 = f.readUInt()
        LODCount = f.readUInt()

        PosV = f.tell()
            
        print(Vtype)
        if Vtype == 0:
            VertBuff = f.readBytes(VertexCount * 32)           
            rapi.rpgBindPositionBufferOfs(VertBuff, noesis.RPGEODATA_HALFFLOAT, 32, 0) # bind vertex
            rapi.rpgBindUV1BufferOfs(VertBuff, noesis.RPGEODATA_HALFFLOAT, 32, 8) # bind uv
            
        if Vtype == 4:
            VertBuff = f.readBytes(VertexCount * 32)           
            rapi.rpgBindPositionBufferOfs(VertBuff, noesis.RPGEODATA_HALFFLOAT, 32, 0) # bind vertex
            rapi.rpgBindUV1BufferOfs(VertBuff, noesis.RPGEODATA_HALFFLOAT, 32, 20) # bind uv
            #rapi.rpgBindBoneIndexBufferOfs  (Data Buffer, noesis.RPGEODATA_TYPE,siz of each vertex buffer, boneIndex Offset, num_of_weights)            
            rapi.rpgBindBoneIndexBufferOfs  (VertBuff, noesis.RPGEODATA_UBYTE, 32, 8, 4) 
            #rapi.rpgBindBoneWeightBufferOfs (Data Buffer, noesis.RPGEODATA_TYPE, siz of each vertex buffer,boneWeight Offset, num_of_weights)
            rapi.rpgBindBoneWeightBufferOfs (VertBuff, noesis.RPGEODATA_UBYTE, 32, 12, 4)

        if Vtype == 3:
            VertBuff = f.readBytes(VertexCount * 28)
            rapi.rpgBindPositionBufferOfs(VertBuff, noesis.RPGEODATA_HALFFLOAT, 28, 0)
            rapi.rpgBindUV1BufferOfs(VertBuff, noesis.RPGEODATA_HALFFLOAT, 28, 16)
            rapi.rpgBindBoneIndexBufferOfs  (VertBuff, noesis.RPGEODATA_UBYTE, 28, 8, 1)
            rapi.rpgBindBoneWeightBufferOfs (VertBuff, noesis.RPGEODATA_UBYTE, 28, 10, 1)
        
            
        FaceBuff = f.readBytes(FaceCount * 2)
        #rapi.rpgCommitTriangles(None, noesis.RPGEODATA_USHORT, VertexCount, noesis.RPGEO_POINTS, 1)
        rapi.rpgCommitTriangles(FaceBuff, noesis.RPGEODATA_USHORT, FaceCount, noesis.RPGEO_TRIANGLE, 1) #bind polygons

        s += 1
   
   
def noepyLoadModel(data, mdlList):
        typecheck = 0
        ctx = rapi.rpgCreateContext()
        
        try:
            skel_data = rapi.loadPairedFile("skel_file- 1", ".anim")
            mesh_data = rapi.loadPairedFile("mesh_file - 2", ".rigid_model_v2")
        except:
            mesh_data = rapi.loadPairedFile("mesh_file - 2", ".rigid_model_v2")
            typecheck = 1
        
        if typecheck == 0:
            bones = Skeleton(skel_data) # get skeleton data
        Mesh(mesh_data) # get mesh data
        
        mdl = rapi.rpgConstructModel()
        #when i want to load only skeleton
        #mdl = NoeModel()
        if typecheck == 0:
            mdl.setBones(bones)
        mdlList.append(mdl)
        
        rapi.rpgClearBufferBinds()
         
        return 1                     
jayn23
mega-veteran
mega-veteran
Posts: 250
Joined: Sun Jul 17, 2011 9:30 pm
Has thanked: 61 times
Been thanked: 241 times

Re: Noesis script help

Post by jayn23 »

Hi,

to continue learning 3d models and noesis i made a script from scratch (not based on anyone's existing scripts) for Total War: Warhammer 1,
figured the structure would be very similar so it wouldn't be very hard and it was (.anim from total war 2 was exactly the same).
** i took a look again to find the differences with tww2 script for . V2 rigid files hoping to find the answer there and apparently they are identical , so basicly i rewrote the same script [roll] , just this time i analyzed the hex data on my own and added texture support **

i do have 1 issue i haven't been able to figure out and was hoping you could point me in the right direction.
as you can see in the picture my mesh and skeleton are loading misaligned, the skeleton is rotated 180 deg from the mesh,
i tried playing with the quats, randomly adding a (-) sign and got it rotated correctly but then the translation on X axis is slightly off.
rotatedv1.jpg
since i am reading the data from a file i dont understand why i have this mismatch and would appreciate any insight i could get.
here is a link to sample files.
https://drive.google.com/open?id=1fnZeq ... lf94eOAZ5y
the folder also contains my script
You do not have the required permissions to view the files attached to this post.
Post Reply