Тестирование положения объектов к которым применен модификатор Constraint из кости.

Прошло некоторое время, и я понял что анимация персоонажа это нечто мелкое. Если же использовать какую либо навороченную арматуру, например к которой добавлены модификаторы Constraint то кватернионы полученные по тутору из ссылке не будут включать эти Constraint и арматура получется искаженная и негодная к дальнейшему использованию, поэтому решил бороться с этим явлением окольным путем, который хоть я и думаю что является изначально неправильным, но зато я буду сразу видеть результат, подобно тому как дровосек срузу видит дрова, которые рубит. Теперь я не хочу, что-бы кости цеплялись голова к хвосту, (неправильно выразился) я желаю расширить класс арматуры, с тем, что-бы кость цеплялась с своей родительской костью и включала перенос те. что-бы работало что-то подобное на рисунке ниже
кости с loc

Самое интересное, что мне нехочется ничего делать, в смысле думать, тк. мозг кипит, а толку 0. План такой Делаю фигуру с осями, долго объяснять, но нарисовалось вот что:
позы с рукой
Это два разных скрина, на нижнем покручена поза

На скриншете много копий одного и того-же объекта, к ним применен constraint с принуждением к определенной кости в арматуре, вручную см. рисунок
принуждение к ротации иперемещению
ротация и перемещение из кости.

Теперь я хочу затянуть это во флеш используя только матрицу объекта
Получить матрицу объекта, используя питон можно так
	  
import Blender
from Blender import *

def rawDataMatrix(m):
	i=0;
	s='';
	for i in range(4):
		s+= "%f," % m[i][0]
		s+= "%f," % m[i][1]
		s+= "%f," % m[i][2]
		s+= "%f" % m[i][3] 
		if (i!=3): s+= ",\n"
	return s;


obj = Blender.Object.Get('Cube')

outFile = open('D:\\blender\\lesson-bsod-animation\\export-matrix.txt', 'w')

outFile.write(rawDataMatrix(obj.matrix))


Теперь нужно получить эту матрицу для каждого объекта, я уже не хочу делать цикл и перебирать значения костей, а хочу просто выбрать какие кости мне нужны для экспорта, благо это сделать очень легко.
import Blender
from Blender import *

exportPath = 'D:\\FLEX\\3D\\test-constraint\\constraints\\resurse\\blender\\export-matrix.txt'
objAr = ['Cube', 'Cube_T','Cube_M','Cube_B']

def rawDataMatrix(m):
	i=0;
	s='';
	for i in range(4):
		s+= "%f," % m[i][0]
		s+= "%f," % m[i][1]
		s+= "%f," % m[i][2]
		s+= "%f" % m[i][3] 
		if (i!=3): s+= ","
	return s;



objNameStr = '';
objMatStr = '';

i=0;
for objName in objAr: 
	#вылавливание первого элемента
	if i!=0: 
		objNameStr+=',';
		objMatStr+=',';
	i+=1;
	#запись занчений
	objNameStr+='"'+objName+'"';
	objMatStr+='['+rawDataMatrix(Blender.Object.Get(objName).matrix)+']';

obj = Blender.Object.Get('Cube')

outFile = open(exportPath, 'w')

outFile.write('names = ['+objNameStr+']\n')
outFile.write('matrix = ['+objMatStr+']')


Я его запустил и получил
	  
names = ["Cube","Cube_T","Cube_M","Cube_B"]
matrix = [[0.007609,0.000000,-0.999971,0.000000,0.999971,...]]

Теперь соберу флеш файл
На тот раз я не захотел эмбить ресурс текстуры в виде jpg файла, а сделал эту операцию через flash ide cs3 в виде swc. Cделаю документ класс.
	  
package  
{
	import flash.display.*;
	import flash.geom.*;
	import lex.d3.*;
	
	/**
	 * ...
	 * @author www.murmadillo.tut.su
	 */
	public class Doc extends Sprite
	{
		private var objBone:Obj3d;
		private var names:Array;
		private var matrixs:Array;

		public function Doc() 
		{
			include '\\resurse\\blender\\export-matrix.txt'
			objBone = new Obj3d();
			
			with (objBone) {include "\\resurse\\blender\\bone-mesh.txt";}
			objBone.bmd = new bone_texture_rgb(0,0);
			//инвертировать текстурный битмап объекта
			objBone.mirrorBmdY();
			var renderObj:ObjRender = new ObjRender();
			var cam:Matrix3D = new Matrix3D();
			cam.appendScale(65, 65, 65);
			cam.appendRotation( 90, Vector3D.X_AXIS);
			
			x = 250;
			y = 300;
			
			//renderObj.render(o, cam, graphics);
			var i:int;
			var preCam:Matrix3D = new Matrix3D();
			var blendMat:Matrix3D = new Matrix3D();
			
			for (i = 0; i < matrixs.length; i++) {
				preCam.identity();
				blendMat.rawData = Vector.<Number>(matrixs[i]);
				preCam.append(blendMat);
				preCam.append(cam);
				renderObj.render(objBone, preCam, graphics);
			}
			
		}

	}

}

Компилруем, опа... компиляция прошла успешно, тк. сборка удалась и программа работает четко.

Маленькая рабочая арматурка
A я вот о чем подумал: а зачем мне в матрице кости 16 чисел??? В приципе должно с головой хватить 3 (ротаций euller), однако, надо при этом расчитывать арматуру, если честно - надоело, сделаю так: три числа loc и 3 euller и того 6.
Все равно кватернионы это дохлый номер, тк. я в них непойму ни фига.
Ха, прикол в том что Euller блендер экспортирует в градусах, когда понял, долго смеялся...

Расчетные данные модели используя Euller и location (loc)

Питон экспортер такой:
	  
import Blender
from Blender import *

exportPath = 'D:\\FLEX\\3D\\test-constraint\\constraints\\resurse\\blender\\export-loc-euller.txt'
objAr = ['Cube', 'Cube_T','Cube_M','Cube_B']

def raw3list(l):
	i=0;
	s='';
	s+= "%f," % l[0]
	s+= "%f," % l[1]
	s+= "%f" % l[2]
	return s;



objNameStr = '';
objEullerStr = '';
objLocStr = '';

i=0;
for objName in objAr: 
	#первый элемент без запятой
	if i!=0: 
		objNameStr+=',';
		objEullerStr+=',';
		objLocStr+=',';
	i+=1;
	#конец первый элемент - запись значений
	mat = Blender.Object.Get(objName).matrix;
	objLocStr+= '['+raw3list(mat.translationPart())+']';
	objNameStr+='"'+objName+'"';
	objEullerStr+='['+raw3list(mat.toEuler())+']';

obj = Blender.Object.Get('Cube')

outFile = open(exportPath, 'w')

outFile.write('names = ['+objNameStr+']\n')
outFile.write('eullers = ['+objEullerStr+']\n')
outFile.write('locs = ['+objLocStr+']')

Документ класс флеша
	  
package  
{
	import flash.display.*;
	import flash.geom.*;
	import lex.d3.*;
	
	/**
	 * ...
	 * @author www.murmadillo.tut.su
	 */
	public class DocEuller extends Sprite
	{
		private var objBone:Obj3d;
		private var names:Array;
		private var locs:Array;
		private var eullers:Array;

		public function DocEuller() 
		{
			include '\\resurse\\blender\\export-loc-euller.txt'
			objBone = new Obj3d();
			
			with (objBone) { include "\\resurse\\blender\\bone-mesh.txt"; }
			
			objBone.bmd = new bone_texture_rgb(0,0);
			//инвертировать текстурный битмап объекта
			objBone.mirrorBmdY();
			var renderObj:ObjRender = new ObjRender();
			var cam:Matrix3D = new Matrix3D();
			cam.appendScale(65, 65, 65);
			cam.appendRotation( 90, Vector3D.X_AXIS);
			
			x = 120;
			y = 180;
			
			//renderObj.render(o, cam, graphics);
			var i:int;
			var preCam:Matrix3D = new Matrix3D();
			var blendEuller:Matrix3D = new Matrix3D();
			var locVector:Vector3D = new Vector3D();
			
			for (i = 0; i < locs.length; i++) {
				preCam.identity();
				blendEuller = blendEullerToMatrix3D(eullers[i]);// Vector.<Number>(matrixs[i]);
				//locVector = new Vector3D(locs[i][0], locs[i][1], locs[i][2]);
				
				preCam.append(blendEuller);
				preCam.appendTranslation(locs[i][0], locs[i][1], locs[i][2]);
				
				preCam.append(cam);
				renderObj.render(objBone, preCam, graphics);
			}
			
		}

		public function blendEullerToMatrix3D(a:Array):Matrix3D {
			var m:Matrix3D = new Matrix3D();
			var euller:Vector.<Vector3D> = m.decompose(Orientation3D.EULER_ANGLES);
			var v:Vector3D = new Vector3D(gradToRad(a[0]), gradToRad(a[1]), gradToRad(a[2]));
			euller[1] = v;
			m.recompose(euller, Orientation3D.EULER_ANGLES);
			return m;
		}

		public function gradToRad(grad:Number): Number {
			return grad * (Math.PI / 180);
		}
	}

}

Очень сильно крутанул кости в блендере
сильно крученная позу
Поза где кости кручены сильно

Ctrl+enter

Аналогично
Теперь опять натягивать на меш, блин,. надоело. Так нирвану врубил, вроде жизнь налаживается.
Меш натянуть, так я ж это уже делал, только есть набольшая разница, теперь не надо расщитывать арматуру, тк. она уже готовая. Есть замечательный тутор по блендеру, где описывается процесс создания песочного человечка, а не дернуть ли мне его???
Я почти все сделал по тутору, однако меш получился страшненький, но вы не бойтесь, просто это дема, делаю быстро. Субсерф сгладил грани, но и создал кучу полигонов, а фиг с ним.
Теперь все-же надо получить арматуру. Самый простой способ - это поставить чела с дефолтными значениями арматуры и записать для него еулеры и локи. Прийдется вытащить данные дефолтной арматуры.
Написал базовый класс для loc и euller
	  
package  
{
	import flash.geom.*;
	/**
	 * ...
	 * @author www.murmadillo.tut.su
	 */
	public class LocRot
	{
		public var locs:Array;
		public var eullers:Array;
		public var names:Array;
		
		public var locObj:Object;
		public var rotObj:Object;
		
		public function LocRot() 
		{
			
		}

		public function init():void {
			var i:int;
			locObj = { };
			rotObj = { };
			for (i = 0; i < locs.length; i++) {
				locObj[names[i]] = new Vector3D(locs[i][0], locs[i][1], locs[i][2]);
				rotObj[names[i]] = blendEullerToMatrix3D(eullers[i]);
			}
		}

		public function blendEullerToMatrix3D(a:Array):Matrix3D {
			var m:Matrix3D = new Matrix3D();
			var euller:Vector.<Vector3D> = m.decompose(Orientation3D.EULER_ANGLES);
			var v:Vector3D = new Vector3D(gradToRad(a[0]), gradToRad(a[1]), gradToRad(a[2]));
			euller[1] = v;
			m.recompose(euller, Orientation3D.EULER_ANGLES);
			return m;
		}

		public function gradToRad(grad:Number): Number {
			return grad * (Math.PI / 180);
		}
	}

}

Теперь класс для позы без дефомаций, те. дефолтной позы
	  
package  
{
	import flash.geom.*;
	/**
	 * ...
	 * @author www.murmadillo.tut.su
	 */
	public class DefoltLocRot extends LocRot
	{

		public function DefoltLocRot() 
		{
			include '\\resurse\\blender\\defolt-loc-euller.txt'
			//include '\\resurse\\blender\\export-loc-euller.txt'
			init();
		}


	
	}

}

Мои классы из тутора анимации персоонажа кватернионами оказались очень удачными, их можно использовать для других целей, например для анимации еуллерами и локами
Документ класс получился такой.
	  
package  
{
	import flash.display.*;
	import flash.geom.*;
	import flash.events.*;
	import lex.d3.*;

	import lex.comp.blank.*;
	import lex.comp.*;
	/**
	 * ...
	 * @author www.murmadillo.tut.su
	 */
	public class DocEuller extends Sprite
	{
		private var objBone:VisioBone;
		private var names:Array;
		private var locs:Array;
		private var eullers:Array;
		
		private var defoltLocRot:DefoltLocRot;
		private var frameLocRot:FrameLocRot;

		private var gas:Obj3d;

		private var gasSh:Shape;
		private var boneSh:Shape;
		private var armature:Armature;
		private var anim:EullerLocAnimation;
		
		private var frCount:int;
		private var frMax:int = 20;
		private var frRevers:Boolean = false;
		//private var visioBone:VisioBone = new VisioBone();
		private var slider:BSlider;
		private var stopBtn:SelBtnTxt;
		private var showBoneBtn:SelBtnTxt;
		private var rotator:Boolean = false;
		private var noBone:Boolean = false;
		private var cx:Number;
		private var cy:Number;
		private var cz:Number;
		
		private var preCam:Matrix3D = new Matrix3D();
		private var cam:Matrix3D = new Matrix3D();
		private var camScale:Number = 30;
		private var scaleObj:Number = 30;
		private var renderObj:ObjRender = new ObjRender();
		
		public function DocEuller() 
		{
			defoltLocRot = new DefoltLocRot();
			
			frameLocRot = new FrameLocRot();
			
			objBone = new VisioBone();
			
			anim = new EullerLocAnimation();
			
			gas = new Obj3d();
			with (gas) { include '\\resurse\\blender\\gas-mesh.txt' }
			
			armature = new Armature();
			with (armature) {include '\\resurse\\blender\\armature.txt' }
			gas.bmd = new gas_bmd(0,0);
			//инвертировать текстурный битмап объекта
			gas.mirrorBmdY();
			
			armature.init();
			
			gas.usePose = true;
			
			gas.initGroup();
			
			gas.armature = armature;
			
			
			
			//var cam:Matrix3D = new Matrix3D();
			
			cam.appendScale(camScale, camScale, camScale);
			cam.appendRotation( 90, Vector3D.X_AXIS);
			cam.appendRotation( 90, Vector3D.Y_AXIS);
			
			
			addChild(gasSh = new Shape());
			boneSh = new Shape();
			gasSh.x = boneSh.x = 280;
			gasSh.y = boneSh.y = 280;
			
			//renderObj.render(o, cam, graphics);
			var i:int;
			
			var blendEuller:Matrix3D = new Matrix3D();
			var locVector:Vector3D = new Vector3D();
			var gasScale:Number = 0.3;
			for (i = 0; i < defoltLocRot.locs.length; i++) {
				preCam.identity();
				blendEuller = blendEullerToMatrix3D(defoltLocRot.eullers[i]);// Vector.<Number>(matrixs[i]);
				//locVector = new Vector3D(locs[i][0], locs[i][1], locs[i][2]);
				
				preCam.append(blendEuller);
				preCam.appendTranslation(defoltLocRot.locs[i][0], defoltLocRot.locs[i][1], defoltLocRot.locs[i][2]);
				
				preCam.append(cam);
				renderObj.render(objBone, preCam, boneSh.graphics);
				
			}

			preCam.identity();
			preCam.append(cam);
			//preCam.appendScale(gasScale,gasScale,gasScale);
			
			
			
			
			
			//начало подстановки
			addChild(slider = sliderGen());
			addEventListener(Event.ENTER_FRAME, enterFrameHandler);
			slider.addEventListener(Event.CHANGE, changeSliderHandler);
			
			addChild(stopBtn = new SelBtnTxt('Стой'));
			stopBtn.x = 100;
			stopBtn.y = 374;
			stopBtn.sel = false;
			rotator = true;
			enterFrameHandler();
			
			addChild(showBoneBtn = new SelBtnTxt('Кости'));
			showBoneBtn.x = 375;
			showBoneBtn.y = 374;
			showBoneBtn.sel = false;
			showBoneBtn.addEventListener(MouseEvent.CLICK, showBoneClickHandler);
			
		}
		private function showBoneClickHandler(e:MouseEvent):void {
			rotator = true;
			noBone = showBoneBtn.sel;
			enterFrameHandler();
		}

		private function changeSliderHandler(e:Event):void {
			//подготовка углов камеры
			cam.identity();
			rotator = true;
			//cx = anim.centralBoneLoc[frCount][0];
			//cy = anim.centralBoneLoc[frCount][1];
			
			cam.appendScale(scaleObj, scaleObj, scaleObj);
			
			
			
			cam.appendRotation( 90, Vector3D.X_AXIS);
			cam.appendRotation(180 - slider.position * 360, Vector3D.Y_AXIS);
			
			//cam.prependTranslation( -cx, -cy, 0);
			
			if (!stopBtn.sel) enterFrameHandler();
		}

		private function enterFrameHandler(e:Event = null):void {
			if (stopBtn.sel || rotator) {
				rotator = false;
				if (stopBtn.sel) if (frRevers) frCount--; else frCount++;
				if (frCount < 0) { frRevers = false; frCount = 0 }
				if (frCount >= frMax) { frRevers = true; frCount = frMax }
				
				renderBear();
			}
		}

		private function clearBone():void {
			//var i:int;
			//for (i = 0; i < chapeArr.length; i++) chapeArr[i].graphics.clear();
		}

		//рендер перца
		private function renderBear():void {
			var len:int;
			var b:Bone;
			var i:int;
			
			var frame:int = frCount;
			len = armature.bones.length;
			frameLocRot.locs = anim.bigLocs[frame];
			frameLocRot.eullers = anim.bigEullers[frame];
			frameLocRot.init();
			
			for (i = 0; i < len; i++) {
				b = armature.bones[i];
				b.head = new Vector3D(defoltLocRot.locObj[b.name].x, defoltLocRot.locObj[b.name].y, defoltLocRot.locObj[b.name].z);
				b.inverseCompBasicRot = defoltLocRot.rotObj[b.name].clone();
				b.inverseCompBasicRot.invert();
				b.localMat = frameLocRot.rotObj[b.name].clone();
				b.localMat.appendTranslation(frameLocRot.locObj[b.name].x, frameLocRot.locObj[b.name].y, frameLocRot.locObj[b.name].z);
			}

			preCam.identity();
			preCam.append(cam);
			gasSh.graphics.clear();
			renderObj.render(gas, preCam, gasSh.graphics);
		}

		private function sliderGen():BSlider {
			var s:BSlider = new BSlider();
			s.modifyGripToRect = true;
			s.setSize(200, 10);
			s.position = 0.5;
			s.reaction = 0.1;
			s.direction = 'x';
			s.x = 170;
			s.y = 380;
			return s;
		}

		public function blendEullerToMatrix3D(a:Array):Matrix3D {
			var m:Matrix3D = new Matrix3D();
			var euller:Vector.<Vector3D> = m.decompose(Orientation3D.EULER_ANGLES);
			var v:Vector3D = new Vector3D(gradToRad(a[0]), gradToRad(a[1]), gradToRad(a[2]));
			euller[1] = v;
			m.recompose(euller, Orientation3D.EULER_ANGLES);
			return m;
		}

		public function gradToRad(grad:Number): Number {
			return grad * (Math.PI / 180);
		}
	}

}

Кстати, вот так выглядит экспортер анимации из блендера просто переключаются фреймы и заносится в строку положение loc и euller
	  
import Blender
from Blender import *

exportPath = 'D:\\FLEX\\3D\\test-constraint\\constraints\\resurse\\blender\\opt-loc-eullers.txt'
objAr = ['b', 'a_L','a_R','l_L','l_R']

#число кадров
frameBeginIndex = 1;
frameEndIndex = 23;


def raw3list(l):
	i=0;
	s='';
	s+= "%f," % l[0]
	s+= "%f," % l[1]
	s+= "%f" % l[2]
	return s;
#





i=0;
f=0;
objNameStr = '';
bigEuller = ''
bigLoc = ''
#переход по фреймам
for frameCount in range(frameEndIndex - frameBeginIndex):
	#первый фрейма без запятой
	if f!=0: 
		bigEuller+=',\n';
		bigLoc+=',\n';
	f+=1;
	
	currentFrame = frameCount + frameBeginIndex
	Blender.Set('curframe',currentFrame)
	Blender.Redraw()
	
	
	
	objEullerStr = '';
	objLocStr = '';
	objNameStr = '';
	i=0
	for objName in objAr:
		#первый элемент без запятой
		if i!=0: 
			objNameStr+=',';
			objEullerStr+=',';
			objLocStr+=',';
		i+=1;
		#конец первый элемент - запись значений
		mat = Blender.Object.Get(objName).matrix;
		objLocStr+= '['+raw3list(mat.translationPart())+']';
		objNameStr+='"'+objName+'"';
		objEullerStr+='['+raw3list(mat.toEuler())+']';
	
	bigEuller+='['+objEullerStr+']'
	bigLoc+='['+objLocStr+']'


outFile = open(exportPath, 'w')

outFile.write('names = ['+objNameStr+']\n')
outFile.write('bigEullers = ['+bigEuller+']\n')
outFile.write('bigLocs = ['+bigLoc+']')

Все анимацию надергали собираем флешку

кнопка кости не работает, не захотел ее удалять, но и кости делать тоже не захотел.
Вес флешки получился удачный, 47кБ.
Если чего-то забыл, см архив. Забыл в архив сунуть файлы с двигом ищите их в архив с тутора человечка из спичечных коробков.