1. Hardware
– Risc-V
– ARM
– AMD x86_64
– Intel x86_64
2. Distribution
– Debian 9,10
3. Desktop Environment
– LightDM + i3wm
4. Applications
– Krita
– Inkscape
– Gimp
– Blender
– Qute Browser
The Quest to become a Stallmanite
1. Hardware
– Risc-V
– ARM
– AMD x86_64
– Intel x86_64
2. Distribution
– Debian 9,10
3. Desktop Environment
– LightDM + i3wm
4. Applications
– Krita
– Inkscape
– Gimp
– Blender
– Qute Browser
http://www.xbdev.net/3dformats/milkshape/index.php
https://blender.stackexchange.com/questions/44637/how-can-i-manually-calculate-bpy-types-posebone-matrix-using-blenders-python-ap/44975
Bone 0 Input:
0.00 0.30 -0.00 0.00 0.16 -0.00 0.00 -0.11 -0.00 0.00 -0.30 -0.00 0.00 -0.36 -0.00 0.00 -0.40 -0.00 0.00 -0.48 -0.00 0.00 0.00 -0.00 0.00 3.00 -0.00 0.00 3.02 -0.00 0.00 2.89 -0.00 0.00 2.63 -0.00 0.00 2.30 -0.00 0.00 1.94 -0.00 0.00 1.58 -0.00 0.00 1.27 -0.00 0.00 1.05 -0.00 0.00 0.90 -0.00 0.00 0.76 -0.00 0.00 0.65 -0.00 0.00 0.54 -0.00 0.00 0.46 -0.00 0.00 0.39 -0.00 0.00 0.31 -0.00
Bone 0 Output:
0.00 0.30 -0.00 0.00 0.15 -0.00 0.00 -0.13 -0.00 0.00 -0.31 -0.00 0.00 -0.36 -0.00 0.00 -0.37 -0.00 0.00 -0.38 -0.00 0.00 -0.38 -0.00 0.00 -0.39 -0.00 0.00 -0.40 -0.00 0.00 -0.43 -0.00 0.00 -0.29 -0.00 0.00 1.29 -0.00 0.00 3.01 -0.00 0.00 2.95 -0.00 0.00 2.75 -0.00 0.00 2.44 -0.00 0.00 2.08 -0.00 0.00 1.71 -0.00 0.00 1.37 -0.00 0.00 1.11 -0.00 0.00 0.94 -0.00 0.00 0.79 -0.00 0.00 0.67 -0.00 0.00 0.56 -0.00 0.00 0.47 -0.00 0.00 0.39 -0.00 0.00 0.35 -0.00
Bone 1 Input:
0.00 3.70 0.20 0.00 3.70 0.20
Bone 1 Output:
0.00 3.70 0.20 0.00 3.70 0.20 0.00 3.70 0.20 0.00 3.70 0.20 0.00 3.70 0.20 0.00 3.70 0.20 0.00 3.70 0.20 0.00 3.70 0.20 0.00 3.70 0.20 0.00 3.70 0.20 0.00 3.70 0.20 0.00 3.70 0.20 0.00 3.70 0.20 0.00 3.70 0.20 0.00 3.70 0.20 0.00 3.70 0.20 0.00 3.70 0.20 0.00 3.70 0.20 0.00 3.70 0.20 0.00 3.70 0.20 0.00 3.70 0.20 0.00 3.70 0.20 0.00 3.70 0.20 0.00 3.70 0.20 0.00 3.70 0.20 0.00 3.70 0.20 0.00 3.70 0.20 0.00 3.70 0.20
Bone 32 Input:
5.45 0.00 0.00 5.45 0.00 0.00
Bone 32 Output:
5.45 0.00 0.00 5.45 0.00 0.00 5.45 0.00 0.00 5.45 0.00 0.00 5.45 0.00 0.00 5.45 0.00 0.00 5.45 0.00 0.00 5.45 0.00 0.00 5.45 0.00 0.00 5.45 0.00 0.00 5.45 0.00 0.00 5.45 0.00 0.00 5.45 0.00 0.00 5.45 0.00 0.00 5.45 0.00 0.00 5.45 0.00 0.00 5.45 0.00 0.00 5.45 0.00 0.00 5.45 0.00 0.00 5.45 0.00 0.00 5.45 0.00 0.00 5.45 0.00 0.00 5.45 0.00 0.00 5.45 0.00 0.00 5.45 0.00 0.00 5.45 0.00 0.00 5.45 0.00 0.00 5.45 0.00 0.00
gltf
<bpy_struct, EditBone("bone_000")> --- set bone transforms --- 63 0 <Matrix 4x4 (1.0000, 0.0000, 0.0000, -0.0000) (0.0000, 1.0000, 0.0000, 0.0000) (0.0000, 0.0000, 1.0000, -0.0000) (0.0000, 0.0000, 0.0000, 1.0000)> switch to pose --- Create Bone --- <bpy_struct, EditBone("bone_001")> --- set bone transforms --- 62 0 <Matrix 4x4 (1.0000, 0.0000, 0.0000, -0.0000) (0.0000, 1.0000, 0.0000, 3.7000) (0.0000, 0.0000, 1.0000, 0.2000) (0.0000, 0.0000, 0.0000, 1.0000)> switch to pose --- Create Bone --- <bpy_struct, EditBone("bone_002")> --- set bone transforms --- 61 0 <Matrix 4x4 (-0.0000, -1.0000, -0.0000, 0.0000) ( 0.9551, -0.0000, 0.2963, 3.7000) (-0.2963, 0.0000, 0.9551, 0.2000) ( 0.0000, 0.0000, 0.0000, 1.0000)> switch to pose --- Create Bone --- <bpy_struct, EditBone("bone_003")> --- set bone transforms --- 30 0 <Matrix 4x4 (-0.0000, -1.0000, -0.0000, -0.0001) ( 0.9988, -0.0000, 0.0499, 6.6001) (-0.0499, 0.0000, 0.9988, -0.6998) ( 0.0000, 0.0000, 0.0000, 1.0000)>
decompose
<bpy_struct, EditBone("bone_000")> --- set bone transforms --- 63 0 switch to pose <Matrix 4x4 (1.0000, 0.0000, 0.0000, -0.0000) (0.0000, 1.0000, 0.0000, 0.0000) (0.0000, 0.0000, 1.0000, -0.0000) (0.0000, 0.0000, 0.0000, 1.0000)> <Quaternion (w=1.0000, x=0.0000, y=0.0000, z=0.0000)> <Matrix 4x4 (1.0000, 0.0000, 0.0000, 0.0000) (0.0000, 1.0000, 0.0000, 0.0000) (0.0000, 0.0000, 1.0000, 0.0000) (0.0000, 0.0000, 0.0000, 1.0000)> --- Create Bone --- <bpy_struct, EditBone("bone_001")> --- set bone transforms --- 62 0 switch to pose <Matrix 4x4 (1.0000, 0.0000, 0.0000, -0.0000) (0.0000, 1.0000, 0.0000, 3.7000) (0.0000, 0.0000, 1.0000, 0.2000) (0.0000, 0.0000, 0.0000, 1.0000)> <Quaternion (w=1.0000, x=0.0000, y=0.0000, z=0.0000)> <Matrix 4x4 (1.0000, 0.0000, 0.0000, 0.0000) (0.0000, 1.0000, 0.0000, 0.0000) (0.0000, 0.0000, 1.0000, 0.0000) (0.0000, 0.0000, 0.0000, 1.0000)> --- Create Bone --- <bpy_struct, EditBone("bone_002")> --- set bone transforms --- 61 0 switch to pose <Matrix 4x4 (1.0000, 0.0000, 0.0000, 0.0000) (0.0000, 1.0000, 0.0000, 3.7000) (0.0000, 0.0000, 1.0000, 0.2000) (0.0000, 0.0000, 0.0000, 1.0000)> <Quaternion (w=0.6991, x=-0.1060, y=0.1060, z=0.6991)> <Matrix 4x4 (1.0000, 0.0000, 0.0000, 0.0000) (0.0000, 1.0000, 0.0000, 0.0000) (0.0000, 0.0000, 1.0000, 0.0000) (0.0000, 0.0000, 0.0000, 1.0000)>
https://devtalk.blender.org/t/vertex-weights-and-indices-for-a-bmesh/1457
Bones debug output:
nj : https://pastebin.com/E7LX5HvK
dmf : https://pastebin.com/ygURJ6ip
I guess this means I need to check the original values versus the provided values. I also need to check if I need to multiply by the parent.
Bones transformations:
DMF: https://pastebin.com/fBgT4MTk
NJ : https://pastebin.com/U2JdeCsm
NJF: https://pastebin.com/4VUt8buT
Xentax Import: http://wiki.xentax.com/index.php/Blender_Import_Guide#Bones.2C_weights
https://forum.odroid.com/viewtopic.php?f=96&t=30552
Got bones and weights working, next step is to figure out how to implement animations.
class NoeBone: def __init__(self, index, name, matrix, parentName = None, parentIndex = -1): self.index = index self.name = name self.setMatrix(matrix) self.parentName = parentName self.parentIndex = parentIndex #parent index may be specified instead of parentName, if it's more convenient. this is an index corresponding to self.index, and not the position in the list. def __repr__(self): return "(NoeBone:" + repr(self.index) + "," + self.name + "," + repr(self.parentName) + "," + repr(self.parentIndex) + ")" def setMatrix(self, matrix): if not isinstance(matrix, NoeMat43): noesis.doException("Invalid type provided for bone matrix") self._matrix = matrix def getMatrix(self): return self._matrix #keyframe data type class NoeKeyFramedValue: #value may be NoeQuat, NoeVec3, float, etc. depending on the data type specified def __init__(self, time, value): self.time = time self.value = value self.componentIndex = 0 def __repr__(self): return "NoeKFVal(time:" + repr(self.time) + " value:" + repr(self.value) + ")" def setComponentIndex(self, componentIndex): self.componentIndex = componentIndex #keyframed bone class class NoeKeyFramedBone: def __init__(self, boneIndex): self.boneIndex = boneIndex self.setRotation([]) self.setTranslation([]) self.setScale([]) #for the set methods, keys should be a list of NoeKeyFramedValue or an object with similarly available members def setRotation(self, keys, type = noesis.NOEKF_ROTATION_QUATERNION_4, interpolationType = noesis.NOEKF_INTERPOLATE_LINEAR): self.rotationKeys = keys self.rotationType = type self.rotationInterpolation = interpolationType def setTranslation(self, keys, type = noesis.NOEKF_TRANSLATION_VECTOR_3, interpolationType = noesis.NOEKF_INTERPOLATE_LINEAR): self.translationKeys = keys self.translationType = type self.translationInterpolation = interpolationType def setScale(self, keys, type = noesis.NOEKF_SCALE_SCALAR_1, interpolationType = noesis.NOEKF_INTERPOLATE_LINEAR): self.scaleKeys = keys self.scaleType = type self.scaleInterpolation = interpolationType #keyframed animation class class NoeKeyFramedAnim: def __init__(self, name, bones, kfBones, frameRate = 20.0, flags = 0): noesis.validateListType(bones, NoeBone) noesis.validateListType(kfBones, NoeKeyFramedBone) self.name = name self.bones = bones self.kfBones = kfBones self.frameRate = frameRate self.flags = flags def __repr__(self): return "(NoeKFAnim:" + self.name + ")" #main animation class #bones must be a list of NoeBone objects, frameMats must be a flat list of NoeMat43 objects class NoeAnim: def __init__(self, name, bones, numFrames, frameMats, frameRate = 20.0, flags = 0): noesis.validateListType(bones, NoeBone) noesis.validateListType(frameMats, NoeMat43) self.name = name self.bones = bones self.numFrames = numFrames self.frameMats = frameMats self.setFrameRate(frameRate) self.flags = flags def __repr__(self): return "(NoeAnim:" + self.name + "," + repr(self.numFrames) + "," + repr(self.frameRate) + ")" def setFrameRate(self, frameRate): self.frameRate = frameRate
Example Code:
bones = [] numBones = bs.readInt() for i in range(0, numBones): bone = noepyReadBone(bs) bones.append(bone) anims = [] numAnims = bs.readInt() for i in range(0, numAnims): animName = bs.readString() numAnimBones = bs.readInt() animBones = [] for j in range(0, numAnimBones): animBone = noepyReadBone(bs) animBones.append(animBone) animNumFrames = bs.readInt() animFrameRate = bs.readFloat() numFrameMats = bs.readInt() animFrameMats = [] for j in range(0, numFrameMats): frameMat = NoeMat43.fromBytes(bs.readBytes(48)) animFrameMats.append(frameMat) anim = NoeAnim(animName, animBones, animNumFrames, animFrameMats, animFrameRate) anims.append(anim)
More sample codes: ./plugins/python/fmt_gamebryo_nif.py
def loadTransformData(self, bs): if self.nif.fileVer >= nifVersion(20, 5, 0, 2): self.loadObject(bs) self.rotKeys = [] self.trnKeys = [] self.sclKeys = [] self.rotKeyType = noesis.NOEKF_INTERPOLATE_LINEAR self.trnKeyType = noesis.NOEKF_INTERPOLATE_LINEAR self.sclKeyType = noesis.NOEKF_INTERPOLATE_LINEAR numKeys = bs.readUInt() if numKeys > 0: rotKeyType = bs.readUInt() if rotKeyType == 0 or rotKeyType == 1 or rotKeyType == 4: self.rotKeyType = noesis.NOEKF_INTERPOLATE_LINEAR else: print("WARNING: Unsupported rotation key type:", rotKeyType) return for i in range(0, numKeys): if rotKeyType == 0 or rotKeyType == 1: #quats keyTime = bs.readFloat() w = bs.readFloat() keyVal = NoeQuat( (bs.readFloat(), bs.readFloat(), bs.readFloat(), w) ).toMat43(1).toQuat() self.rotKeys.append(NoeKeyFramedValue(keyTime, keyVal)) elif rotKeyType == 4: #radians xyz = [ [], [], [] ] for i in range(0, 3): numSubKeys = bs.readUInt() if numSubKeys > 0: radianKeyType = bs.readUInt() for j in range(0, numSubKeys): keyTime = bs.readFloat() if radianKeyType == 0 or radianKeyType == 1: xyz[i].append(NoeKeyFramedValue(keyTime, bs.readFloat()*noesis.g_flRadToDeg)) elif radianKeyType == 2: #possible todo - interpolate as bezier xyz[i].append(NoeKeyFramedValue(keyTime, bs.readFloat()*noesis.g_flRadToDeg)) bezierIn = bs.readFloat() bezierOut = bs.readFloat() else: print("WARNING: Unsupported radian rotation type:", radianKeyType) return #this is largely untested, because the model i found using it only used it for the eyes, #and it's hard to tell if it really works just based on eyes. (that's what she said) xyz = rapi.mergeKeyFramedFloats(xyz) for anglesKey in xyz: self.rotKeys.append(NoeKeyFramedValue(anglesKey.time, NoeAngles(anglesKey.value).toMat43_XYZ().toQuat()))
It looks like key framed value is going to be the best option for implementing the version of animations I have, which defines a time and then gives a set of transformations at that given time.