Page 1 of 6

Perfect World Games for noesis

Posted: Tue Oct 29, 2019 10:53 pm
by jayn23
Since this thread has turned into a Perfect world games thread heres a summery of everything found in here:

1.Saint Seiya Online
Support for .ski, .bon, .stck, .mox, .bmd
skinned mesh + animation + static mesh

2.Perfect World
Support for .ski, .bon, .stck, .mox, .bmd
skinned mesh + animation + static mesh

3.Jade Dynasty
Support for .ski, .bon
skinned mesh

4.Forsaken World
Support .ski, .bon, .mox, .bmd (ski works with Perfect World script )

5.Swordsman Online
Support for .ski, .bon
skinned mesh

.........................................................................................................................
Hi,

In order to complete my basic noesis study i started working on a noesis script for .stck animation based of the following topic:
viewtopic.php?f=16&t=11776&hilit=animation#p98283

iv been working on this for the past few days - most of them trying to resolve an issue where my weights were being assigned to the wrong
bone id, had to ditch the .rapi and learn NoeMesh so i could manipulate the data - finally got it fixed. but now i am stuck with the animation part and have some issues i cant seem to resolve.

i tried following both shakotay2 and killercracker script. (also credit to zaramot - used his script to try and t/s my issues)
i am able to get the correct quat and translation vector but for some frames they are not equal, for example frame 8 has 93 translation vectors and 97 rotation quats, so what do i do with the missing translation? does it mean translation for quat is 0?

additionally what is "at time((i-1) * timeMult)"? is this a maxscript thing? or something that is relevant for my understanding of animations?
in shakotay2 script keyframe 200 was used didnt understand what that was for either?
also the line marked in black box - i understand changing numFrames, but what is rotFrames?

thanks in advance
for xentax1.png
my current noesis script (animation not working)

Code: Select all

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


def ReadStringKnown(f, size): # read string of known size
    String = []
    k = 0
    for i in range(size): 
        num = int(f.readUByte()) # bug reading 31 as ASCII 0 end existing
        if (num > 64 and num < 91) or(num > 45 and num < 58) or (num > 96 and num < 123) or num == 95 : #num == 95 add _ charecter
            if k == 0:
                k += 1
                Name = chr(num)
            else:
                Name = Name + chr(num)
    return Name



def registerNoesisTypes():
    handle = noesis.register("Perefect World Mesh", ".ski;.bon;.stck")    
    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):
    bones = []
    
    g = NoeBitStream(data, NOE_LITTLEENDIAN)
    g.seek(16,1)
    BoneCount = g.readUInt()
    g.seek(80,1)
    ####
    for i in range(BoneCount):#BoneCount
        boneNameLength = g.readUInt()
        boneName = ReadStringKnown(g, boneNameLength)
        boneID = g.readUInt()
        parentID = g.readInt()
        siblingID = g.readUInt()
        unkcount = g.readUInt()
        tfm2 = NoeMat44.fromBytes( g.readBytes(0x40), NOE_LITTLEENDIAN )
        tfm = NoeMat44.fromBytes( g.readBytes(0x40), NOE_LITTLEENDIAN )
        #boneMat = tfm.toMat43()
        boneMat = tfm.toMat43().inverse()
        #print("boneMat =", boneMat)

        for j in range(unkcount):
            g.seek(4,1)

        bones.append( NoeBone(i, boneName, boneMat, None, parentID) )

    return bones

def animation (data, bones): #def animation (data, bones):
    s = 0
    BoneMat = []
    Rotation_Arr = []
    translation_arr = []
    anims = []
    animFrameMats = []
    animName = "test"
    animFrameRate = 30

    f = NoeBitStream(data, NOE_LITTLEENDIAN)
    Magic = ReadStringKnown(f, 8)
    print("anim magic")
    print(Magic)
    unk = f.readUInt()
    boneCount = f.readUInt()
    f.seek(20,0)
    numFrames = f.readUInt()
    ukw = f.readUInt()

    for i in range (boneCount):
        
        boneID = f.readUInt()
        TransCount = f.readUInt() # number of translation vectors
        f.seek(8,1)

        for k in range(TransCount):
            #Tran = NoeVec3.fromBytes(f.readBytes(12))
            x = (f.readFloat())
            y = (f.readFloat())
            z = (f.readFloat())
            translation_arr.append((x,y,z))

        f.seek(16,1)

        RotCount = f.readUInt() # number of quats
        f.seek(8,1)
        print("TransCount=",TransCount)
        print("RotCount=",RotCount)
        
        for j in range(RotCount):
            x = f.readFloat()
            y = f.readFloat()
            z = f.readFloat()
            w = f.readFloat()
            Rot = NoeQuat((x,y,z,w))
            print("quat - original", Rot)
            #Rot = Rot.normalize()
            #Rotation_Arr.append((x,y,z,w)) #quat(x,y,z,w)
            frameMat = Rot.toMat43(transposed = 1)
            frameMat = frameMat.inverse()
            print("matrix_inverse =",frameMat)           
            #uat = frameMat.toQuat()
            #rint("quat", quat)
            frameMat[3] = translation_arr[s]
            #if TransCount >= RotCount:
            #if   s += 1
            #print(frameMat)
            animFrameMats.append(frameMat)
            
        
        f.seek(16,1)
        #a = f.tell()
    
    #bones must be a list of NoeBone objects, frameMats must be a flat list of NoeMat43 object
    if numFrames < TransCount:
        numFrames = TransCount
    print(numFrames)
    print(len(animFrameMats))
    anim = NoeAnim(animName, bones, 80, animFrameMats, animFrameRate) #numFrames
    anims.append(anim)

    return anims
    
#load mesh data
def Mesh(data, bones):
    
    ctx = rapi.rpgCreateContext()
    bs = NoeBitStream(data, NOE_LITTLEENDIAN)
   
    Magic = noeStrFromBytes(bs.readBytes(8), "ASCII")
    print(Magic)
    if Magic != "MOXBIKSA":
        print("worng file format")
    
    unk2 = bs.readInt()
    meshCount = bs.readInt()

    bs.seek(12,1)  
    
    textureCount = bs.readInt()
    materialCount = bs.readInt()
    mesh_boneCount = bs.readInt()
    null = bs.readInt()
    ukw4 = bs.readInt()
    
    bs.seek(60,1)
    
    boneName_Arr = []
    texName_Arr = []
    
    for l in range(len(bones)):
        print(l)
        print(bones[l].name)
    
    for i in range(mesh_boneCount):
        count = bs.readInt()
        boneName_Arr.append(ReadStringKnown(bs, count)) 

    for i in range(textureCount):
        count = bs.readInt()
        texName_Arr.append(ReadStringKnown(bs, count)) 

    for i in range(materialCount):
        bs.seek(80,1)
    
    for i in range(meshCount):
        nameLength = bs.readInt()
        meshName = ReadStringKnown(bs, nameLength)
    
        matID = bs.readInt()
        ukw2 = bs.readInt()
        vertCount = bs.readInt()
        faceCount = bs.readInt()
    
        Positions = []
        PolygonIndex = []
        Normals = []
        UV =[]
        weightList = []
        meshes = []
    
        for j in range(vertCount): # vert position
            bonesid = []
            weights = []
            vx = bs.readFloat()
            vy = bs.readFloat()
            vz = bs.readFloat()       
            Positions.append(NoeVec3([vx,vy,vz]))
              
            
            weight1 = bs.readFloat() 
            weight2 = bs.readFloat() 
            weight3 = bs.readFloat()
            weight4 = 0.0
                       

            for i in range(4):
                bone = bs.readUByte()
                if bone <= len(boneName_Arr):
                    name = boneName_Arr[bone]
                    for l in range(len(bones)):
                        if bones[l].name == name:
                            index = bones[l].index
                            break
                else:
                    index = 300
                if i == 0:
                   
                   bone1 = index
                if i == 1:
                   bone2 = index
                if i == 2:
                   bone3 = index
                if i == 3:
                   bone4 = index                                     

            
            if weight1 != 0:
                bonesid.append(bone1)
                weights.append(weight1)
            if weight2 != 0:
                bonesid.append(bone2)
                weights.append(weight2)
            if weight3 != 0:
                bonesid.append(bone3)
                weights.append(weight3)
            if weight4 != 0:
                bonesid.append(bone4)
                weights.append(weight4)
            
            weightList.append(NoeVertWeight(bonesid,weights))
            
            #print(j)
            #print(vx,vy,vz)
            #print(bonesid)
            #print(weights)
            
            
            nx = bs.readFloat()
            ny = bs.readFloat()
            nz = bs.readFloat()      
            Normals.append(NoeVec3([nx,ny,nz])) 
            
            tu = bs.readFloat()
            tv = bs.readFloat() * -1
            UV.append(NoeVec3([tu,tv,0.0])) 
            
        for j in range(0, int(faceCount)): # faces / triangles
            PolygonIndex.append(bs.readUShort())

    mesh = NoeMesh(PolygonIndex, Positions, meshName)
    mesh.setWeights(weightList)
    mesh.setNormals(Normals)
    mesh.setUVs(UV)
    meshes.append(mesh)
    
    return meshes
   
def noepyLoadModel(data, mdlList):
        ctx = rapi.rpgCreateContext()
               
        skel_data = rapi.loadPairedFile("skel_file- 1", ".bon")
        mesh_data = rapi.loadPairedFile("mesh_file- 2", ".ski")
        try:
            anim_data = rapi.loadPairedFile("anim_file - 3", ".stck")
        except:
            pass
        
        bones = Skeleton(skel_data) # get skeleton data
        anims = animation(anim_data, bones)    
        #print(anims)
        #print(anims[0].bones)
        #print(anims[0].name)
        #print(anims[0].numFrames)
        #print(anims[0].frameMats)
        #print(len((anims[0].frameMats)))
        
        meshes = Mesh(mesh_data, bones) # need to edit bone id index
        
        mdl = NoeModel(meshes)
        mdl.setBones(bones)           
        mdl.setAnims(anims) 
        
        mdlList.append(mdl)
        rapi.rpgClearBufferBinds()
        
        return 1                     

Re: Perfect world animation .stck for noesis

Posted: Wed Oct 30, 2019 10:22 am
by shakotay2
jayn23 wrote: Tue Oct 29, 2019 10:53 pm Hi,

In order to complete my basic noesis study i started working on a noesis script for .stck animation
Hi,
that's great! :)
i am able to get the correct quat and translation vector but for some frames they are not equal, for example frame 8 has 93 translation vectors and 97 rotation quats, so what do i do with the missing translation?
Afair (from my understanding) max interpolates the missing frames but I may be wrong.
additionally what is "at time((i-1) * timeMult)"? is this a maxscript thing? or something that is relevant for my understanding of animations?
"time" refers to the timeline for the animation (frames, translation/rotation).
timeMult = 2.0, just a variable killercracker introduced
in shakotay2 script keyframe 200 was used didnt understand what that was for either?
timeline thing again
btw: seems there's a bug in my script:

Code: Select all

    for fd=1 to Frame_cnt do
	    (
		    rot = RotationAnimation()
		    rot.BoneId = i
		    rot.KeyFrame = i*200-10
		    x = ReadFloat stream
		    y = ReadFloat stream
		    z = ReadFloat stream
		    w = ReadFloat stream
			
		    rot.Quaternion = quat x y z w
	
		    append allRotations rot
	    )
I'm pretty sure it should read
rot.KeyFrame = fd*200-10
but I can't check it because I don't have max installed any more. (Dependend on the (max) Frame_cnt "200" might be to big, better use 20 or such.)

Re: Perfect world animation .stck for noesis

Posted: Wed Oct 30, 2019 10:48 pm
by jayn23
Thanks again for your help :)

sorry in advance for the lengthy post,

what i wrote here wasnt correct
i am able to get the correct quat and translation vector but for some frames they are not equal, for example frame 8 has 93 translation vectors and 97 rotation quats, so what do i do with the missing translation?
to be exact, for every bone there is a set of translation vectors and a set of rotation matrix, for bone 8 there were 93 translation vectors and 97 rotation quats, some also have 1 vs 97 or 97 to 31 etc..

what i am assuming based on max code is that every rotation and translation have a specific time calculated by ((i-1) * timeMult) so for say bone 8, i would take the translation and matrix at time 120, and if at time 190 i have only a rotation and no translation i would take the last translation (meaning it didnt move)
is this correct?
is there a way in 3dsmax i can see all of my animation matrix? so i can verify if my data is correct?

i also think i am not using the correct noesis function for this, i found in inc_noesis.py NoeKeyFramedAnim class, i am going to try with this one

One last question - just because i am curious :D
on the left code by killercracker i see he just applays the inverse quat and translation
on the right code by you, you first apply b.pos to the rotation matrix from quat then transform it, why not apply allTranslations.Position instead of b.pos ?
for xentax1.png

Re: Perfect world animation .stck for noesis

Posted: Thu Oct 31, 2019 1:44 pm
by shakotay2
jayn23 wrote: Wed Oct 30, 2019 10:48 pmwhat i am assuming based on max code is that every rotation and translation have a specific time calculated by ((i-1) * timeMult) so for say bone 8, i would take the translation and matrix at time 120, and if at time 190 i have only a rotation and no translation i would take the last translation (meaning it didnt move)
is this correct?
sounds good :D (Can't confirm it, because I don't have max installed/used since years...)
is there a way in 3dsmax i can see all of my animation matrix? so i can verify if my data is correct?
depends on. In case you use a script, insert print commands.
In case you use an importer plugin (without source) I don't remember - didn't use max script API, if any.
(in blender you could use for example matrix=Blender.Mathutils.Matrix(bone.matrix['ARMATURESPACE']) for such)

One last question - just because i am curious :D
on the left code by killercracker i see he just applays the inverse quat and translation
on the right code by you, you first apply b.pos to the rotation matrix from quat then transform it, why not apply allTranslations[ i ].Position instead of b.pos ?
good question - that was my first max animation script (and most of the code was "borrowed" from TaylorMouse).
since my script didn't work correctly (while killerscracker's did) it's an academic question, isn't it?

Best way to get an answer, imho, is to try the different ways and print out the resulting matrixes - as you intended above.

btw: be carefull with [ i ], without the blanks the browser soft takes it as a command for the font (:

Re: Perfect world animation .stck for noesis

Posted: Thu Oct 31, 2019 7:52 pm
by jayn23
depends on. In case you use a script, insert print commands.
In case you use an importer plugin (without source) I don't remember - didn't use max script API, if any.
(in blender you could use for example matrix=Blender.Mathutils.Matrix(bone.matrix['ARMATURESPACE']) for such)
to be honest before i started learning noesis i tried learning how to script with the blender API and after a week of trying i realized there are many tutorials but each works only for a specific version and not many worked for the 2.8 version...
btw: be carefull with [ i ], without the blanks the browser soft takes it as a command for the font (:
ill make sure i do :P

by the way i got it to work :)

ill post the code later today just want to clean it up a bit.
now my next ,mission is to start practicing analyzing 3d models, currently i can get verts,faces and uv most of the time but bones and animations iv never even really tried

Thanks again for the help

here is the code:
fmt_PerfectWorld.rar

Re: Perfect world animation .stck for noesis

Posted: Thu Nov 07, 2019 10:26 am
by KingJuls
If I understand correctly now, can your Noesis Script load PW animations?

Re: Perfect world animation .stck for noesis

Posted: Thu Nov 07, 2019 7:23 pm
by jayn23
Yes it can load animation - iv tested it only on models + animation samples that were provided in the original post, so i cant guarantee it will work for all versions ... i can only hope :)

Re: Perfect world animation .stck for noesis

Posted: Fri Nov 08, 2019 7:57 am
by KingJuls
Okay no problem i will check :)
Maybe u can create a script for .bon Animation because many clients use this "old" animation data.

Re: Perfect world animation .stck for noesis

Posted: Mon Nov 11, 2019 12:17 pm
by CriticalError
many thanks for the great update, well maybe you can take a look into this files, use same format as pw, Saint Seiya Online, grateful if you can help, have a nice day.

https://www.mediafire.com/file/e2rv2eim ... es.7z/file

Re: Perfect world animation .stck for noesis

Posted: Tue Nov 12, 2019 1:46 pm
by jayn23
well maybe you can take a look into this files, use same format as pw, Saint Seiya Online, grateful if you can help, have a nice day.
Hi,

I took a look at the mesh + skeleton files and the format was almost identical just needed to add one for loop, i also found a bug in my original script that would have prevented loading models made up of multiple meshes. (hope i didnt break anything else when fixing it [roll] )

Saint Seiya Online version supports only mesh and skeleton at the moment, hopefully ill get some time to look at the animation, using search i noticed its had a few members that are far more experienced than me work on it in the past, so i have my doubts that i can...

Re: Perfect world animation .stck for noesis

Posted: Tue Nov 12, 2019 1:48 pm
by jayn23
This is the perfect world (original) script with bug fix

if i broke anything please let me know, didnt have time to do many tests

Re: Perfect world animation .stck for noesis

Posted: Tue Nov 12, 2019 2:12 pm
by KingJuls
Verrry Nice - thanks! :3

Can u also find out how can import Objectfiles (.mox)?

Re: Perfect world animation .stck for noesis

Posted: Tue Nov 12, 2019 2:36 pm
by CriticalError
jayn23 wrote: Tue Nov 12, 2019 1:46 pm
well maybe you can take a look into this files, use same format as pw, Saint Seiya Online, grateful if you can help, have a nice day.
Hi,

I took a look at the mesh + skeleton files and the format was almost identical just needed to add one for loop, i also found a bug in my original script that would have prevented loading models made up of multiple meshes. (hope i didnt break anything else when fixing it [roll] )

Saint Seiya Online version supports only mesh and skeleton at the moment, hopefully ill get some time to look at the animation, using search i noticed its had a few members that are far more experienced than me work on it in the past, so i have my doubts that i can...
well to fast, thanks a lot for support, one thing, edit one line of the script like this:

handle = noesis.register("Perefect World Mesh", ".ski;.bon;.stck")

to

handle = noesis.register("Saint Seiya", ".ski")

so now how we disable bon and stck? because if load ski ask for bon and stck after load all nothing appear in noesis.

Re: Perfect world animation .stck for noesis

Posted: Tue Nov 12, 2019 4:26 pm
by jayn23
handle = noesis.register("Perefect World Mesh", ".ski;.bon;.stck")

to

handle = noesis.register("Saint Seiya", ".ski")
my bad i forgot to change that :D
ill update the original post later today when i have time

It shouldn't be asking you for ,stk since i disabled it in Saint Seiya version.
I noticed that even after changing the handle it still tried to load the files using the perfect world script.
so you cant have both scripts in you plugin folder at the same time.

.bon file contains the skeleton so if you want skinned meshes i wouldn't disable it.
Can u also find out how can import Objectfiles (.mox)?
ill see what i can do..

Re: Perfect world animation .stck for noesis

Posted: Wed Nov 13, 2019 4:52 am
by CriticalError
jayn23 wrote: Tue Nov 12, 2019 4:26 pm
handle = noesis.register("Perefect World Mesh", ".ski;.bon;.stck")

to

handle = noesis.register("Saint Seiya", ".ski")
my bad i forgot to change that :D
ill update the original post later today when i have time

It shouldn't be asking you for ,stk since i disabled it in Saint Seiya version.
I noticed that even after changing the handle it still tried to load the files using the perfect world script.
so you cant have both scripts in you plugin folder at the same time.

.bon file contains the skeleton so if you want skinned meshes i wouldn't disable it.
understand but what I say before, I download a clean copy of noesis and only copy your script into plugin, check.

Image

Image

so now when I select ski to load it ask for .bon ok, after select a .bon it ask again to ski mesh 2, after select same mesh ski don't give error but noesis don't load nothing.

here is the test, and with all files same.

Image