Code: Select all
# Noesis Python model import+export test module, imports/exports some data from/to a made-up format
from os.path import *
from math import *
from inc_noesis import *
MAIN_BON_FILE_NAME = ""
ROOT_DIR = ""
FILE_BON_PATH = ""
ROOT_DIR = dirname(dirname(dirname(abspath(__file__))))
FILE_BON_PATH = ROOT_DIR + "\\YulgangVN\\Bones\\"
MESH_LIST = []
BONE_LIST = []
ANI_LIST = []
# registerNoesisTypes is called by Noesis to allow the script to register formats.
# Do not implement this function in script files unless you want them to be dedicated format modules!
def registerNoesisTypes():
handle = noesis.register("YulgangVN - SKIN", ".skin;.bon;.ani")
noesis.setHandlerTypeCheck(handle, checkSkinType)
noesis.setHandlerLoadModel(handle, checkSkinLoad)
# noesis.setHandlerWriteModel(handle, noepyWriteModel)
# noesis.setHandlerWriteAnim(handle, noepyWriteAnim)
noesis.logPopup()
# print("The log can be useful for catching debug prints from preview loads.\nBut don't leave it on when you release your script, or it will probably annoy people.")
return 1
# check if it's this type based on the data
def checkSkinType(data):
if len(data) < 16:
return 0
return 1
# load the model
def checkSkinLoad(data, mdlList):
global MESH_LIST
global BONE_LIST
global ANI_LIST
ctx = rapi.rpgCreateContext()
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")
# create parser
yulgangParser = YulgangParser(data, boneData, aniData, fileExt)
MESH_LIST = yulgangParser.parseSkin()
BONE_LIST = yulgangParser.parseBon()
ANI_LIST = yulgangParser.parseAni()
mdl = NoeModel(MESH_LIST, BONE_LIST, ANI_LIST)
# important, don't forget to put your loaded model in the mdlList
mdlList.append(mdl)
return 1
def normalize(v, tolerance=0.0001):
mag2 = sum(n * n for n in v)
# print(mag2)
if mag2 != 0.0:
if abs(mag2 - 1.0) > tolerance:
mag = sqrt(mag2)
v = tuple(n / mag for n in v)
return v
def axisangle_to_q(v, theta):
v = normalize(v)
x, y, z = v
theta /= 2
w = cos(theta)
x = x * sin(theta)
y = y * sin(theta)
z = z * sin(theta)
return x, y, z, w
class YulgangParser(object):
def __init__(self, skinData, boneData, aniData, fileExt=""):
self.skinStream = NoeBitStream(skinData)
self.bonStream = NoeBitStream(boneData)
self.aniStream = NoeBitStream(aniData)
self.fileExt = fileExt
self.bones = {} # new
return
def parseBon(self):
global BONE_LIST
if self.bonStream is None:
print('Data stream is null')
# MAIN_BON_FILE_NAME = filePath.split('\\')[4]
# boneFile = open(FILE_BON_PATH + 'bone' + ".txt", "w")
boneNameList = [] # create arrray to contain all bone name
boneCount = self.bonStream.readInt() # read bone count
print('Bone Count: ' + str(boneCount))
noeBones = []
for i in range(boneCount):
currentOffset = self.bonStream.tell() # get current offset
boneName = self.bonStream.readString() # read current bone name
boneNameList.append(boneName)
self.bonStream.seek(currentOffset + 50,
NOESEEK_ABS) # seek 50 bytes
bonePName = self.bonStream.readString() # read parent bone name
self.bonStream.seek(currentOffset + 100,
NOESEEK_ABS) # seek 50 bytes
boneMatrix = NoeMat44.fromBytes(
self.bonStream.readBytes(64)).toMat43()
# create NoeBone
noeBone = NoeBone(len(noeBones), boneName,
boneMatrix, bonePName, -1)
# new - it creat dict bonename==> index
self.bones[boneName] = noeBone
noeBones.append(noeBone)
# check if bone doesnt have info from birnary file, create empty infor for it
# e.x: Scene Root
for noeBone in noeBones:
if noeBone.parentName not in boneNameList:
parentBone = NoeBone(
len(noeBones), noeBone.parentName, NoeMat43(), "", -1)
self.bones[parentBone.name] = parentBone
noeBones.append(parentBone) # add to main list
break
# calculate bone parent index
for bone in noeBones:
if bone.parentName in self.bones:
bone.parentIndex = self.bones[bone.parentName].index
# boneFile.write(str(bone.index) + "::" + bone.name + "::" +
# bone.parentName + "::" + str(bone.parentIndex) + "\n") # write to file
# print("Write file " + 'bone' + ".txt success!")
# boneFile.close() # close file
return noeBones
def parseSkin(self):
if self.skinStream is None:
print('Data stream is null')
elif self.fileExt != 'skin':
print('Selected file is not Skin format')
self.skinStream.readUByte() # number of mesh in file
vertexCount = self.skinStream.readInt()
indicesCount = self.skinStream.readInt() * 3
weightCount = self.skinStream.readInt()
materialCount = self.skinStream.readInt()
vertPostList = []
indiceList = []
weights = []
vertUVList = []
skinIdList = []
matList = []
skinWeightList = []
skinIndiceList = []
self.skinStream.readBytes(6 * 4) # unknow
self.skinStream.readBytes(16 * 4) # unknow
self.skinStream.readBytes(16 * 4) # unknow
for i in range(vertexCount):
curOffset = self.skinStream.tell()
vertPostList.append(NoeVec3.fromBytes(
self.skinStream.readBytes(12)))
if weightCount == 1:
seekOffset = curOffset + 36
bwgt = [1.0]
bidx = [self.skinStream.read("B")]
self.skinStream.readUByte()
self.skinStream.readUByte()
self.skinStream.readUByte()
self.skinStream.readBytes(4 * 3)
uv = NoeVec3()
uv.vec3 = self.skinStream.read("ff")+(0,)
vertUVList.append(uv)
elif weightCount == 2:
seekOffset = curOffset + 40
w1 = self.skinStream.readFloat()
w2 = 1.0 - w1
bwgt = [w1, w2]
bidx = self.skinStream.read("BB")
self.skinStream.readUByte()
self.skinStream.readUByte()
self.skinStream.readBytes(4 * 3)
uv = NoeVec3()
uv.vec3 = self.skinStream.read("ff")+(0,)
vertUVList.append(uv)
elif weightCount == 3:
seekOffset = curOffset + 44
w1 = self.skinStream.readFloat()
w2 = self.skinStream.readFloat()
w3 = 1.0 - w1 - w2
bwgt = [w1, w2, w3]
bidx = self.skinStream.read("BBB")
# print(bidx)
self.skinStream.readUByte()
self.skinStream.read("fff")
uv = NoeVec3()
uv.vec3 = self.skinStream.read("ff")+(0,)
vertUVList.append(uv)
elif weightCount == 4:
seekOffset = curOffset + 48
w1 = self.skinStream.readFloat()
w2 = self.skinStream.readFloat()
w3 = self.skinStream.readFloat()
w4 = 1.0 - w1 - w2 - w3
bwgt = [w1, w2, w3, w4]
bidx = self.skinStream.read("BBBB")
self.skinStream.read("fff")
uv = NoeVec3()
uv.vec3 = self.skinStream.read("ff")+(0,)
vertUVList.append(uv)
# weights.append(NoeVertWeight(bidx, bwgt))
self.skinStream.seek(seekOffset, NOESEEK_ABS)
skinWeightList.append(bwgt)
skinIndiceList.append(bidx)
# read indiceList
for i in range(indicesCount):
indiceList.append(self.skinStream.readShort())
# read matertial list
meshList = []
new = open("log.txt", 'w') # outside Noesis log file
for i in range(materialCount):
weights = []
new.write(str(i)+"\n")
mat = NoeMaterial("", "")
matList.append(mat)
self.skinStream.readInt()
matIDStart = self.skinStream.readInt() * 3
matIDCount = self.skinStream.readInt() * 3
self.skinStream.readInt()
self.skinStream.readInt()
boneMap = self.skinStream.read("28i")
# print(boneMap)
mesh = NoeMesh(indiceList[matIDStart:matIDStart+matIDCount], vertPostList, str(i).zfill(2))
mesh.uvs = vertUVList
# procedure to get correct vertex bone index
newSkinIndiceList = []
for i in range(vertexCount):
newSkinIndiceList.append(skinIndiceList[i])
for idx in indiceList[matIDStart:matIDStart+matIDCount]:
bidx = skinIndiceList[idx]
bwgt = skinWeightList[idx]
newbidx = []
new.write(str(idx)+str(bidx)+str(bwgt)+'\n')
for m in range(len(bidx)):
newbidx.append(boneMap[bidx[m]])
newSkinIndiceList[idx] = newbidx
for i in range(vertexCount):
weights.append(NoeVertWeight(
newSkinIndiceList[i], skinWeightList[i]))
mesh.weights = weights
meshList.append(mesh)
new.close()
return meshList
def parseAni(self):
global BONE_LIST
if self.aniStream is None:
print('Ani stream is null')
return 0
if len(BONE_LIST) == 0:
print('Bone List is empty.Please parse BON first')
return 0
print("bones:", len(self.bones))
animList = []
noeKeyFramedBones = []
self.aniStream.seek(512, NOESEEK_ABS) # ignore 512 bytes
boneCount = self.aniStream.readUByte() # get bone count
curBone = NoeBone(0, "", NoeMat43())
for i in range(boneCount):
currentOffset = self.aniStream.tell()
name = self.aniStream.readString() # read bone name
curBone = self.bones[name] # get bone by name
self.aniStream.seek(currentOffset + 50, NOESEEK_ABS) # seek 50 bytes
# get bone pose matrix
matrix44 = NoeMat44.fromBytes(self.aniStream.readBytes(64)) # .inverse()
# print(matrix44)
matrix43 = matrix44.toMat43()
# print(matrix43)
quatFromMatrix = matrix43.toQuat()
transFromMatrix = matrix43[2]
posNoeKeyFramedValues = [] # array contain list translation
translateFrameCount = self.aniStream.readInt()
for j in range(translateFrameCount):
time = self.aniStream.readInt() / 320.0
xp = self.aniStream.readFloat()
yp = self.aniStream.readFloat()
zp = self.aniStream.readFloat()
if time > 0: # ignroe frame 0
vector = NoeVec3((xp, yp, zp))
transMatrix = transFromMatrix.__mul__(vector)
# print(transMatrix)
posKeyFrameValue = NoeKeyFramedValue(time, vector) # create instance
posNoeKeyFramedValues.append(posKeyFrameValue) # add to list
rotNoeKeyFramedValues = [] # array contain list rotation
rotateFrameCount = self.aniStream.readInt()
for j in range(rotateFrameCount):
rotTime = self.aniStream.readInt() / 320.0
v = self.aniStream.read("3f") # read vector
angle = self.aniStream.read("f")[0] # read angle
# convert vector + angle to QUAT
if rotTime > 0: # ignore frame 0
quat = axisangle_to_q(v, angle)
rotMatrix = NoeQuat()
rotMatrix.quat = quat
rotMatrix = quatFromMatrix.__mul__(rotMatrix)
rotKeyFrameValue = NoeKeyFramedValue(
rotTime, rotMatrix) # create instance
rotNoeKeyFramedValues.append(
rotKeyFrameValue) # add to list
# create NoeKeyFramedBone
if len(self.bones) > 0:
actionBone = NoeKeyFramedBone(self.bones[name].index)
else:
actionBone = NoeKeyFramedBone(curBone.index)
actionBone.setRotation(rotNoeKeyFramedValues,
noesis.NOEKF_ROTATION_QUATERNION_4)
actionBone.setTranslation(
posNoeKeyFramedValues, noesis.NOEKF_TRANSLATION_VECTOR_3)
# add to keyframedBones list
noeKeyFramedBones.append(actionBone)
# create NoeKeyFramedAnim
anim = NoeKeyFramedAnim(
"Animation 1", BONE_LIST, noeKeyFramedBones, 10)
# add to animList
animList.append(anim)
return animList
Sample data: ( A Monster Cat in game)
Sample data 2: https://1drv.ms/u/s!AtK3xgihMkhwhI5X4qoASMisva14PQ (Blacksmith)
Blacksmith's IDLE animation demo: https://www.youtube.com/watch?v=AEAQaz0KKWo
Current problem is the translation animation (Mesh floating up and down a little)