//#pragma once
#ifndef AnimationrigState_H
#define AnimationrigState_H

#include <CommonMemoryStream.h>
#include <AnimationrigMisc.h>
#include <Animationrig.h>
#include <AnimationrigPhysics.h>
#include <AnimationrigWiggle.h>

#include <iostream>
#include <vector>

namespace Animrig {
	class StateRenderer {
	public:
		inline ObjectType objectType() { return fObjectType; };
		vector<unique_ptr<PhysicsGroup>> physicsGroups;		// physics
		// return state
		ObjectType stateInputObjectType = ObjectType::Any;
		bool rendererEnable;
		bool rendererVisible;
		bool rendererInheritedEnable;
		bool rendererInheritedVisible;
		// draw priority info
		// * design priority > 3D z position
		float drawPriorityZ;
		float drawPriorityDesign;
		// transform
		Vector3 transPosition;
		Matrix4 transMatrix;
		FloatColor transColor;
		FloatColor tintColor;
		Matrix4 cameraMatrix;
		// physics external transform force
		Matrix4 physicsExternalMatrix;

		virtual ~StateRenderer() { }

		// common
		virtual void clear() {
			stateInputObjectType = ObjectType::Any;
			rendererEnable = true;
			rendererVisible = true;
			rendererInheritedEnable = true;
			rendererInheritedVisible = true;
			drawPriorityZ = 0.0f;
			drawPriorityDesign = 0.0f;

			transMatrix = Misc::IdentityMatrix4;
			cameraMatrix = Misc::IdentityMatrix4;
			transColor = Misc::WhiteColor;
			tintColor = Misc::BlackColor;
			physicsExternalMatrix = Misc::IdentityMatrix4;

			physicsGroups.clear();
		}
		virtual void update(double deltatime, double deltaphysics=-1) {
			// object update code
			// Object.Update(deltatime);

			// physics
			//addForcePhysics();
			//updatePhysics(deltatime);
			//applyPhysics();

			// object transform update code
			// Object.SetupTransform();
		}
		double getPrioritySortValue()
		{
			return (-drawPriorityZ) + (+drawPriorityDesign * 100) * 0x10000;
		}
		// physics
		virtual void setupPhysics(const vector<wstring> &setting)
		{
			// need reset code
			physicsGroups.clear();
		}
		virtual void addPhysicsForce(const wstring &target, double x, double y, double z)
		{
			
		}
		virtual void preparePhysics() 
		{

		}
		virtual void applyPhysics(double deltatime)
		{

		}
		virtual void appeasePhysics()
		{

		}
		// spine
		virtual void setAnimation(int trackindex, int usetrack, const wstring &basename, bool loop, bool addanim, MixBlend trackmix, float duration, bool seekmode, float loopbegin, float loopend)
		{
			//
		}
		virtual void setTrackBlendAlpha(int trackindex, int usetrack, float alpha1, float alpha2, float alpha3, float alpha4, bool exists)
		{
			//
		}
		virtual void setTrackSeekTime(int trackindex, int usetrack, float time1, float time2, float time3, float time4, bool exists)
		{
			//
		}
		virtual void setMixDuration(float duration)
		{
			//
		}
		virtual void setSkin(const wstring &skinname) {
			//
		}
		virtual Matrix4 getBoneMatrix(const wstring &bonename)
		{
			return Misc::IdentityMatrix4;
		}
	protected:
		ObjectType fObjectType = ObjectType::Any;
		void setObjectType(ObjectType objecttype) { fObjectType = objecttype; }
	};

	class StateTrackLayer {
	public:
		static const int ValueCount = 16;   // state keep value count (*not input values)
		static const int SaveVersion = 1;
		inline LayerType layerType() { return fLayerType; }
		bool isPass;     // pass? (empty,disable)
		//   now state
		wstring string1, string2;
		float prevTrackAlpha;
		float currentTrackAlpha;
		float values[ValueCount];
		RotateOrder rotateOrder;
		//   previous track state
		float pPrevTrackAlpha;
		float pCurrentTrackAlpha;
		float pValues[ValueCount];
		RotateOrder pRotateOrder;

		StateTrackLayer(LayerType layertype)
		{
			fLayerType = layertype;
		}
		virtual ~StateTrackLayer() {}

		static void clearValues(float *v, LayerType layertype)
		{
			for (auto i = 0; i < ValueCount; i++)
				v[i] = 0.0f;

			switch (layertype)
			{
			case LayerType::State:
				// [0] enable
				v[0] = 1.0f;
				// [1] disable
				v[1] = 1.0f;
				break;
			case LayerType::Transform:
				// scale
				v[3] = 1.0f;
				v[4] = 1.0f;
				v[5] = 1.0f;
				// alpha
				v[9] = 1.0f;
				// color
				v[10] = 1.0f;
				v[11] = 1.0f;
				v[12] = 1.0f;
				// tint color
				break;
			}
		}
		void clear(bool clearprevious)
		{
			if (clearprevious)
			{
				clearValues(pValues, fLayerType);
				string1 = L"";
				string2 = L"";
				pPrevTrackAlpha = 1.0f;
				pCurrentTrackAlpha = 1.0f;
				pRotateOrder = RotateOrder::Pass;

			}
			else
			{
				for (auto i = 0; i < ValueCount; i++)
				{
					pValues[i] = values[i];
				}
				pPrevTrackAlpha = prevTrackAlpha;
				pCurrentTrackAlpha = currentTrackAlpha;
				pRotateOrder = rotateOrder;
			}

			clearValues(values, fLayerType);
			prevTrackAlpha = 1.0f;
			currentTrackAlpha = 1.0f;
			rotateOrder = RotateOrder::Pass;
		}
		void saveToBinary(MemoryStream *ms)
		{
			// version
			Misc::writeInteger(ms, SaveVersion);

			// type
			Misc::writeInteger(ms, (int)fLayerType);

			// pass? (empty,disable)
			Misc::writeBool(ms, isPass);

			// now state
			Misc::writeString(ms, string1);
			Misc::writeString(ms, string2);
			Misc::writeFloat(ms, prevTrackAlpha);
			Misc::writeFloat(ms, currentTrackAlpha);
			Misc::writeInteger(ms, ValueCount);  // value count
			for (auto i = 0; i < ValueCount; i++) {
				Misc::writeFloat(ms, values[i]);
			}
			Misc::writeInteger(ms, (int)rotateOrder);

			// prev state
			Misc::writeFloat(ms, pPrevTrackAlpha);
			Misc::writeFloat(ms, pCurrentTrackAlpha);
			Misc::writeInteger(ms, ValueCount);  // value count
			for (auto i = 0; i < ValueCount; i++)
			{
				Misc::writeFloat(ms, pValues[i]);
			}
			Misc::writeInteger(ms, (int)pRotateOrder);
		}
		bool loadFromBinary(MemoryStream *ms)
		{
			clear(false);

			int t = 0;
			try
			{
				// version
				auto ver = Misc::readInteger(ms, 0, SaveVersion);

				// type
				t = Misc::readInteger(ms);

				// pass? (empty,disable)
				isPass = Misc::readBool(ms);
				// now state
				string1 = Misc::readString(ms);
				string2 = Misc::readString(ms);
				prevTrackAlpha = Misc::readFloat(ms);
				currentTrackAlpha = Misc::readFloat(ms);
				{
					auto count = Misc::readInteger(ms, 0, 0xffff);  // value count
					for (auto i = 0; i < count; i++)
					{
						auto f = Misc::readFloat(ms, -0xffffff, +0xffffff);
						if (i < ValueCount) values[i] = f;
					}
				}
				rotateOrder = Misc::readRotateOrder(ms);
				// prev state
				pPrevTrackAlpha = Misc::readFloat(ms);
				pCurrentTrackAlpha = Misc::readFloat(ms);
				{
					auto count = Misc::readInteger(ms, 0, 0xffff);  // value count
					for (auto i = 0; i < count; i++)
					{
						auto f = Misc::readFloat(ms, -0xffffff, +0xffffff);
						if (i < ValueCount) pValues[i] = f;
					}
				}
				pRotateOrder = Misc::readRotateOrder(ms);
			}
			catch(...)
			{
				return false;
			}

			// type check
			if (t != (int)fLayerType) clear(false);

			return true;
		}
	protected:
		LayerType fLayerType = LayerType::None;

	};
	class StateTrackObject {
	public:
		inline ObjectType objectType() { return fObjectType; }
		vector<unique_ptr<StateTrackLayer>> layers;
		inline int layerCount() { return (int)layers.size(); }

		StateTrackObject(AnimObject *data)
		{
			//Layers = new
			fObjectType = data->objectType();

			// create layers
			for (auto &l : data->layers)
			{
				//auto ptr = unique_ptr<StateTrackLayer>(new StateTrackLayer(l->layerType()));
				layers.emplace_back(new StateTrackLayer(l->layerType()));
			}

			clear(true);
		}
		void clear(bool clearprevious)
		{
			for (auto &l : layers)
			{
				l->clear(clearprevious);
			}
		}
		void saveToBinary(MemoryStream *ms)
		{
			// layers
			Misc::writeHead(ms, L"tracklayers");
			Misc::writeInteger(ms, layerCount());
			for (auto &l : layers)
			{
				Misc::writeHead(ms, L"tracklayer");
				// data length
				auto endpoint = Misc::writeSkipBegin(ms);
				// layer data
				l->saveToBinary(ms);
				// write data length
				Misc::writeSkipEnd(ms, endpoint);
			}
		}
		bool loadFromBinary(MemoryStream *ms)
		{
			try
			{
				// layers
				Misc::readHead(ms, L"tracklayers");
				auto count = Misc::readInteger(ms, 0, 0xffff);
				for (auto i = 0; i < count; i++)
				{
					// header
					Misc::readHead(ms, L"tracklayer");
					// data length
					auto endpoint = Misc::readSkipBegin(ms);
					// data
					if (i < layerCount())
					{
						if (!layers[i]->loadFromBinary(ms)) return false;
					}
					// jump data endpoint
					Misc::jumpSkipEnd(ms, endpoint);
				}
			}
			catch (...)
			{
				return false;
			}
			return true;
		}
	protected:
		ObjectType fObjectType = ObjectType::Any;
	};
	class StateTrack {
	public:
		static const int SaveVersion = 2;
		//   track state
		int animationIndex;
		double playFrame;
		int prevPlayFrame;
		double selfClearLimit;
		//   control
		double playSpeed;
		bool pause;
		bool enable;
		bool disableLoop;
		EasingData trackAlphaEase;   // track alpha. fade in out.
		EasingData mixAlphaEase;     // previous animation value mix. fade mix.
		//   objects
		vector<unique_ptr<StateTrackObject>> objects;
		inline int objectCount() { return (int)objects.size(); }
		//   play animation
		Animation* animation() {
			if (animationIndex < 0) return nullptr;
			if (animationIndex > fData->animationCount() - 1) return nullptr;
			return fData->animations[animationIndex].get();
		}
		inline bool isSelfClear() { return selfClearLimit < Misc::FloatZero; }

		StateTrack(Animationrig *data, int animationindex) {
			fData = data;
			animationIndex = animationindex;
			Misc::easingValueClear(mixAlphaEase);
			Misc::easingValueClear(trackAlphaEase);

			// Objects = new
			rebuild();

			clear();
		}
		virtual ~StateTrack() {};

		void clear()
		{
			// play control
			enable = true;
			pause = false;
			playSpeed = 1.0f;
			disableLoop = false;

			Misc::easingValueEase(trackAlphaEase, 1.0f);
			Misc::easingValueEase(mixAlphaEase, 1.0f);
			selfClearLimit = 0x100000;

			reset(true);
		}
		void reset(bool clearprevious = false)
		{
			// clear & backup
			for (auto &o : objects)
			{
				o->clear(clearprevious);
			}
			// state reset
			playFrame = -1;
			prevPlayFrame = -1;
			// reset fade
			selfClearLimit = 0x100000;
			Misc::easingValueEase(trackAlphaEase, 1.0f);
			Misc::easingValueEase(mixAlphaEase, 1.0f);
		}
		void rebuild()
		{
			objects.clear();
			for (auto &o : fData->objects)
			{
				//auto ptr = unique_ptr<StateTrackObject>( new StateTrackObject(o.get()) );
				objects.emplace_back(new StateTrackObject(o.get()));
			}
			// update index
			animationIndex = min(max(animationIndex, 0), fData->animationCount() - 1);
		}
		void update(double deltaframe)
		{
			// easing
			if (!trackAlphaEase.done) Misc::easingValueUpdate(trackAlphaEase, deltaframe / fData->framerate);
			if (!mixAlphaEase.done) Misc::easingValueUpdate(mixAlphaEase, deltaframe / fData->framerate);
			// self clear
			if ((selfClearLimit < 0xffff) && (selfClearLimit > 0.0))
			{
				selfClearLimit = selfClearLimit - (deltaframe / fData->framerate);
			}

			playFrame = playFrame + deltaframe;
		}
		void saveToBinary(MemoryStream *ms)
		{
			// animation name
			// move upper class
			//s := '';
			//if track.Animation <> nil then s := track.Animation.Name;
			//TAnimrigMisc.WriteString(ms , s);

			// play frame
			Misc::writeFloat(ms, (float)playFrame);
			Misc::writeInteger(ms, prevPlayFrame);
			// control state
			Misc::writeFloat(ms, (float)playSpeed);
			Misc::writeBool(ms, pause);
			Misc::writeBool(ms, enable);
			Misc::writeBool(ms, disableLoop);
			Misc::writeFloat(ms, (float)selfClearLimit);
			// track alpha easing
			Misc::easingValueSaveToBinary(trackAlphaEase, ms);
			Misc::easingValueSaveToBinary(mixAlphaEase, ms);

			// object
			// header
			Misc::writeHead(ms, L"trackobjects");
			Misc::writeInteger(ms, objectCount());
			for (auto &o : objects)
			{
				// head
				Misc::writeHead(ms, L"trackobject");
				// data length
				auto endpoint = Misc::writeSkipBegin(ms);
				// object data
				o->saveToBinary(ms);
				// write data length
				Misc::writeSkipEnd(ms, endpoint);
			}
		}
		bool loadFromBinary(MemoryStream *ms, int ver)
		{
			try
			{
				// animation name
				// move upper class
				//s := TAnimrigMisc.ReadString(ms);
				//track := EntryTrack(i, s,true);

				// play frame
				playFrame = Misc::readFloat(ms, -0xffffff, +0xffffff, true);
				prevPlayFrame = Misc::readInteger(ms, -0xffffff, 0xffffff, true);
				// control state
				playSpeed = Misc::readFloat(ms, -0xffff, +0xffff, true);
				pause = Misc::readBool(ms);
				enable = Misc::readBool(ms);
				disableLoop = Misc::readBool(ms);
				if (ver >= 2) selfClearLimit = Misc::readFloat(ms);
				// track alpha easing
				if (ver >= 2)
				{
					Misc::easingValueLoadFromBinary(trackAlphaEase, ms);
					Misc::easingValueLoadFromBinary(mixAlphaEase, ms);
				}

				// object
				// header
				Misc::readHead(ms, L"trackobjects");
				auto count = Misc::readInteger(ms, 0, 0xffff);
				for (auto i = 0; i < count; i++)
				{
					// header
					Misc::readHead(ms, L"trackobject");
					// data length
					auto endpoint = Misc::readSkipBegin(ms);
					// data 
					if (i < objectCount())
					{
						if (!objects[i]->loadFromBinary(ms)) return false;
					}
					// jump data endpoint
					Misc::jumpSkipEnd(ms, endpoint);
				}
			}
			catch(...)
			{
				return false;
			}
			return true;
		}
		void fademix(float duration, Easing mode = Easing::Linear, float beta = 1.0f)
		{
			Misc::easingValueEase(mixAlphaEase, 0.0f);
			Misc::easingValueEase(mixAlphaEase, 1.0f, duration, mode, beta);
		}
		void fadein(float duration, float trackalpha = 1.0f, Easing mode = Easing::Linear, float beta = 1.0f)
		{
			Misc::easingValueEase(trackAlphaEase, 0.0f);
			Misc::easingValueEase(trackAlphaEase, trackalpha, duration, mode, beta);
			selfClearLimit = 0x100000;
		}
		void fadeout(float duration, Easing mode = Easing::Linear, float beta = 1.0f)
		{
			Misc::easingValueEase(trackAlphaEase, 0.0f, duration, mode, beta);
			selfClearLimit = duration;
		}
	protected:
		Animationrig *fData = nullptr;
	};
	class StateInput {
	public:
		inline ObjectType objectType() { return fObjectData->objectType(); }
		inline AnimObject *objectData() { return fObjectData; }
		void *userObject = nullptr;
		vector<EasingData> values;
		vector<float> defaultValues;
		vector<float> defaultRange;
		vector<wstring> valueNames;

		StateRenderer* renderer = nullptr;	

		inline int valueCount() { return (int)values.size(); }
		void setValueCount(int count) {
			if (valueCount() == count) return;

			values.resize(count);
			defaultValues.resize(count);
			defaultRange.resize(count);
			valueNames.resize(count);

			for (auto i = 0; i < count; i++)
			{
				Misc::easingValueClear(values[i]);
				defaultValues[i] = 0.0f;
				defaultRange[i] = 1.0f;
				valueNames[i] = L"";
			}
		}

		StateInput(AnimObject *o, int valuecount = Misc::InputCount)
		{
			fObjectData = o;
			userObject = nullptr;

			// values = new EasingData
			// defaultValues = new float[0];
			// defaultRange = new float[0];
			// valueNames = new string[0];

			setValueCount(valuecount);

			clear();
		}
		virtual ~StateInput() {}

		void clear()
		{
			renderer = nullptr;

			for (auto &e : values) {
				Misc::easingValueClear(e);
			}
		}
		void setupDefault(AnimObject *reserveobj)
		{
			int count = valueCount();
			for(auto &s1 : reserveobj->inputPanelField)
			{
				auto sl = Misc::divideString(',', s1);
				int index = -1;
				float alphax = 0.0f;
				float alphay = 0.0f;
				float range = 1.0f;
				wstring name = L"";
				for(auto &s2 : *sl)
				{
					auto p = (int)s2.find(':');
					if (p < 0) continue;
					auto v1 = Misc::slice(s2, 0, p);
					auto v2 = Misc::slice(s2, p + 1, 4096);

					if ((v1 == L"no") || (v1 == L"index")) index = Misc::toIntDef(v2, -1);
					else if (v1 == L"name") name = v2;
					else if (v1 == L"defx") alphax = (float)fmin(fmax(Misc::toFloatDef(v2, 0.0f), -0xffff), +0xffff);
					else if (v1 == L"defy") alphay = (float)fmin(fmax(Misc::toFloatDef(v2, 0.0f), -0xffff), +0xffff);
					else if ((v1 == L"scale") || (v1 == L"range")) range = (float)fmin(fmax(Misc::toFloatDef(v2, 1.0f), -0xffff), +0xffff);
				}

				if ((index >= 0) && (index * 2 + 1 < count))
				{
					defaultValues[index * 2 + 0] = alphax;
					defaultValues[index * 2 + 1] = alphay;
					defaultRange[index * 2 + 0] = range;
					defaultRange[index * 2 + 1] = range;
					valueNames[index * 2 + 0] = name;
					valueNames[index * 2 + 1] = name;
				}
				delete sl;
			}
		}
		void update(double deltatime)
		{
			for (auto &e : values) {
				Misc::easingValueUpdate(e, deltatime);
			}

			if (renderer != nullptr) 
				if (renderer->rendererInheritedEnable) renderer->update(deltatime);
		}
		void setValue(int index, float v, float duration = 0.0f, Easing mode = Easing::Linear, float beta = 1.0f)
		{
			if (index < 0) return;
			if (index > valueCount() - 1) return;

			Misc::easingValueEase(values[index], v, duration, mode, beta);
		}
		float getValue(int index)
		{
			if (index < 0) return 0.0f;
			if (index > valueCount() - 1) return 0.0f;

			return values[index].value;
		}
		void setDefaults(float duration = 0.0f, Easing mode = Easing::Linear, float beta = 1.0f)
		{
			auto count = valueCount();
			for (auto i = 0; i < count; i++)
			{
				setValue(i, defaultValues[i], duration, mode, beta);
			}
		}
		float convertSpecialFloat(float v)
		{
			if (v < Misc::InputFloatIndex) return v;

			int index = (int)floor(v - Misc::InputFloatIndex + 0.5);
			if (index > valueCount() - 1) return 0.0f;

			return values[index].value;
		}
			
	protected:
		AnimObject *fObjectData;
	};
	// event layer callback
	struct StateEventData
	{
		// allow always
		bool ignition;
		// state
		StateInput *input;
		StateTrack *track;
		EventKey *key;
		// data
		wstring ID;
		wstring strings[2];
		float floats[2];
	};
	struct ObjectCache
	{
		Matrix4 matrix;
		bool inheritedEnable;
		bool inheritedVisible;
		float drawPriority;
		FloatColor transColor;
		FloatColor tintColor;
		int camMatrixIndex;
	};
	class AnimationrigState;
	using StateEventCallback = std::function<void(AnimationrigState*, StateEventData&)>;
	// ANimationrigState
	class AnimationrigState {
	public:
		static const int InputValueSaveVersion = 1;
		static const int SaveVersion =
			StateTrack::SaveVersion +
			StateTrackLayer::SaveVersion +
			InputValueSaveVersion +
			1;    // self version

		Matrix4 rootMatrix;
		StateEventCallback onEvent = nullptr;
		// align
		bool alignEnabled = false;
		float alignX = 0.0f;
		float alignY = 0.0f;
		float alignZ = 0.0f;
		// tracks
		vector<unique_ptr<StateTrack>> tracks;	// please use trackssSafe() safty function.
		inline int trackCount() { return (int)tracks.size(); }
		int usageTrackCount() {
			auto count = trackCount();
			for (auto i = count - 1; i >= 0; i--)
				if (tracks[i].get() != nullptr) return i + 1;
			return 0;
		}
		StateTrack* tracksSafe(int index)     // Allow out of range access
		{
			if (index < 0) return nullptr;
			if (index > trackCount() - 1) return nullptr;
			return tracks[index].get();
		}// Allow out of range access. safty.
		// inputs
		vector<unique_ptr<StateInput>> inputs;	// please use inputsSafe() safty function.
		inline int inputCount() { return (int)inputs.size(); }
		inline int objectCount() {return fData->objectCount(); }
		StateInput* inputsSafe(int index)     // Allow out of range access
		{
			if (index < 0) return nullptr;
			if (index > inputCount() - 1) return nullptr;
			return inputs[index].get();
		}// Allow out of range access. safty.
		Animationrig* data() { return fData; }

		AnimationrigState(Animationrig *data)
		{
			fData = data;

			// Tracks = new
			// Inputs:= new 
			rootMatrix = Misc::IdentityMatrix4;

			rebuild();

			if (data == nullptr)
			{
				throw exception("AnimationRigState : need AnimationRig class.");
			}
		}
		virtual ~AnimationrigState() {};

		void saveToBinary(MemoryStream *ms)
		{
			// header
			Misc::writeHead(ms, L"animrigstate");
			// version
			Misc::writeInteger(ms, SaveVersion);

			// tracks
			{
				Misc::writeHead(ms, L"tracks");
				auto count = usageTrackCount();
				Misc::writeInteger(ms, count);
				for (auto i = 0; i < count; i++)
				{
					// head
					Misc::writeHead(ms, L"track");
					// data length
					auto endpoint = Misc::writeSkipBegin(ms);
					// version
					Misc::writeInteger(ms, StateTrack::SaveVersion);

					auto track = tracks[i].get();
					if (track == nullptr)
					{
						// exists = false
						Misc::writeBool(ms, false);
					}
					else
					{
						// exists = true
						Misc::writeBool(ms, true);
						// track data
						wstring s = L"";
						if (track->animation() != nullptr) s = track->animation()->name;
						Misc::writeString(ms, s);
						track->saveToBinary(ms);
					}

					// write data length
					Misc::writeSkipEnd(ms, endpoint);
				}
			}
			// input value
			{
				// header
				Misc::writeHead(ms, L"inputobjects");
				auto count = objectCount();
				Misc::writeInteger(ms, count);
				for (auto i = 0; i < count; i++)
				{
					// header
					Misc::writeHead(ms, L"inputobject");
					// data length
					auto endpoint = Misc::writeSkipBegin(ms);
					// version
					Misc::writeInteger(ms, InputValueSaveVersion);
					// type
					Misc::writeInteger(ms, (int)(inputs[i]->objectType()));
					// head
					Misc::writeHead(ms, L"values");
					auto valuecount = inputs[i]->valueCount();
					Misc::writeInteger(ms, valuecount);
					for (auto j = 0; j < valuecount; j++)
					{
						Misc::easingValueSaveToBinary(inputs[i]->values[j], ms);
					}
					// write data length
					Misc::writeSkipEnd(ms, endpoint);
				}
			}
		}
		bool loadFromBinary(MemoryStream *ms)
		{
			try
			{
				// header
				Misc::readHead(ms, L"animrigstate");
				// version
				auto ver = Misc::readInteger(ms);

				// tracks
				{
					Misc::readHead(ms, L"tracks");
					auto count = Misc::readInteger(ms, 0, 0xffff, false, false);

					clearTracks();

					for (auto i = 0; i < count; i++)
					{
						// header
						Misc::readHead(ms, L"track");
						// data length
						auto endpoint = Misc::readSkipBegin(ms);
						// version
						auto trackver = Misc::readInteger(ms, 0, StateTrack::SaveVersion);
						// exists = true
						auto b = Misc::readBool(ms);
						if (b)
						{
							// animation name
							auto s = Misc::readString(ms);
							auto track = entryTrack(i, s, true);   // try set animation name. (*can not move Track.LoadFromBinary() because of need EntryTrack())
							if (track != nullptr)
								if (!track->loadFromBinary(ms, trackver)) return false;
						}
						// jump data endpoint
						Misc::jumpSkipEnd(ms, endpoint);
					}
				}
				// inputs
				{
					// clear
					{
						auto count = inputCount();
						for (auto i = 0; i < count; i++) {
							inputs[i]->clear();
						}
					}
					// header
					Misc::readHead(ms, L"inputobjects");
					auto count = Misc::readInteger(ms, 0, 0xffff);
					for (auto i = 0; i < count; i++)
					{
						// header
						Misc::readHead(ms, L"inputobject");
						// data length
						auto endpoint = Misc::readSkipBegin(ms);

						if (i < objectCount())
						{
							// version
							auto inputver = Misc::readInteger(ms, 0, InputValueSaveVersion);
							// type
							auto type_ = Misc::readInteger(ms);
							// head
							Misc::readHead(ms, L"values");
							// value count
							auto valuecount = Misc::readInteger(ms, 0, 0xff);
							// set length
							if (valuecount > inputs[i]->valueCount()) inputs[i]->setValueCount(valuecount);
							// valuse
							for (auto j = 0; j < valuecount; j++)
							{
								if (!Misc::easingValueLoadFromBinary(inputs[i]->values[j], ms)) return false;
							}

							// type check
							if ((int)inputs[i]->objectType() != type_) inputs[i]->clear();
						}
						// jump end point
						Misc::jumpSkipEnd(ms, endpoint);
					}
				}
			}
			catch(...)
			{
				return false;
			}

			return true;
		}
		void clearObjects()
		{
			for(auto &o : inputs)
			{
				o->clear();
			}
		}
		bool Seek(int targettrackindex, double time, bool framegrid) {
			if (targettrackindex < 0) return false;
			if (targettrackindex > trackCount() - 1) return false;
			auto track = tracks[targettrackindex].get();
			if (track == nullptr) return false;

			time = max(time, 0.0);

			double frame = time * fData->framerate;
			if (framegrid) {
				frame = floor(frame + 0.5);
			}
			double fps = 1.0 / fData->framerate;
			double d = frame - floor(track->playFrame + 0.5);

			// frame grid
			if (framegrid) {
				if (abs(d) < fps) return false;
			}

			// seek
			if (d >= 0.0)
			{
				update(d * fps, false, true);
				return false;
			}
			else
			{
				track->reset(true);
				update(time, true, true);   // skip mode
				return true;                // rewind flag
			}
		}
		void rebuild()
		{
			auto count = objectCount();
			// tempolary matrix
			objectCache.resize(objectCount());
			// object update priority
			objectUpdateOrder.resize(objectCount());
			{
				vector<double> pri(count);
				for (auto i = 0; i < count; i++)
				{
					pri[i] = 0;
				}

				for (auto i = 0; i < count; i++)
				{
					auto idx = fData->objects[i]->parentObjectIndex;
					auto level = 0;
					while (true)
					{
						if (idx < 0) break;   // none
						if (idx == i) break;   // self index
						if (idx >= count) break;   // limit over
						level++;
						idx = fData->objects[idx]->parentObjectIndex;
					}
					pri[i] = level;
				}

				Misc::getSortedIndexes(&objectUpdateOrder[0], &pri[0], count, true); //get sorted indexes array. ascending order.
			}

			// input objects
			inputs.clear();
			for(auto &o : fData->objects)
			{
				auto input = new StateInput(o.get());
				//auto ptr = unique_ptr<StateInput>(input);
				inputs.emplace_back(input);
				input->setupDefault(o.get());
				input->setDefaults();
			}

			// tracks
			for(auto &t : tracks)
			{
				if (t.get() == nullptr) continue;
				t->rebuild();
			}
		}
		StateTrack* entryTrack(int trackindex, int animindex, bool reset = false)
		{
			if (fData == nullptr) return nullptr;
			if (trackindex < 0) return nullptr;
			if (trackindex > 255) return nullptr;

			if (animindex < 0) return nullptr;
			if (animindex >= fData->animationCount()) return nullptr;

			// change track length
			changeTracks(trackindex + 1);

			// exists?
			if (tracks[trackindex].get() == nullptr)
			{
				auto ptr = unique_ptr<StateTrack>(new StateTrack(fData, animindex));
				tracks[trackindex] = std::move(ptr);
				return tracks[trackindex].get();
			}
			else
			{
				tracks[trackindex]->reset(reset);
				tracks[trackindex]->animationIndex = animindex;
				return tracks[trackindex].get();
			}
		}
		StateTrack* entryTrack(int trackindex, const wstring &animname, bool reset = false)
		{
			if (fData == nullptr) return nullptr;

			auto index = fData->search(animname);
			if (index == -1) return nullptr;

			return entryTrack(trackindex, index, reset);
		}
		void clearTrack(int trackindex)
		{
			if (trackindex < 0) return;
			if (trackindex >= trackCount()) return;

			// dispose & null
			tracks[trackindex].reset(nullptr);
			compactionTracks();
		}
		void clearTracks()
		{
			for (auto i = 0; i < trackCount(); i++)
			{
				tracks[i].reset(nullptr);
			}
			compactionTracks();
		}
		StateTrack* entryTrackFade(int trackindex, int animindex, float duration, Easing mode = Easing::InOutQuad, float beta = 1.0f) {
			if (duration < 0.001f) {
				return entryTrack(trackindex, animindex);
			}

			if (trackindex < 0) return nullptr;
			if (trackindex > 255) return nullptr;

			if (animindex < 0) return nullptr;
			if (animindex >= fData->animationCount()) return nullptr;

			// change track length
			changeTracks(trackindex + 1);

			if (tracks[trackindex].get() == nullptr)
			{
				// empty previous animation
				auto track = entryTrack(trackindex, animindex, false);
				if (track != nullptr) track->fadein(duration, 1.0f, mode, beta);
				return track;
			}
			else
			{
				// exists previous animation
				auto track = entryTrack(trackindex, animindex, false);
				if (track != nullptr) track->fademix(duration, mode, beta);
				return track;
			}
		}
		StateTrack* entryTrackFade(int trackindex, const wstring &animname, float duration, Easing mode = Easing::InOutQuad, float beta = 1.0f)
		{
			if (fData == nullptr) return nullptr;

			auto index = fData->search(animname);
			if (index == -1) return nullptr;

			return entryTrackFade(trackindex, index, duration, mode, beta);
		}
		StateTrack* clearTrackFade(int trackindex, float duration, Easing mode = Easing::InOutQuad, float beta = 1.0f)
		{
			if (duration < 0.001)
			{
				clearTrack(trackindex);
				return nullptr;
			}

			if (trackindex < 0) return nullptr;
			if (trackindex >= trackCount()) return nullptr;

			auto track = tracks[trackindex].get();
			if (track != nullptr) track->fadeout(duration, mode, beta);
			return track;
		}
		PhysicsMatrix4 getAlignMatrix()
		{
			PhysicsMatrix4 m = Misc::IdentityMatrix4;
			Misc::m4PushMove(
				m,
				-data()->offset.x - data()->size.x * alignX,
				-data()->offset.y - data()->size.y * alignY,
				-data()->offset.z - data()->size.z * alignZ
			);
			return m;
		}

		void update(double deltatime, bool updateonce = false, bool seekmode = false) {
			// seekmode : pass enable & playspeed
			// updateonce : execute objectupdate() only once. but object animation time is shifted.
			//
			// *caution
			// keep spine-function execute order below
			// 1.Set/AddAnimation
			// 2.control Track.alpha Track.Duration
			// 3.ObjectUpdate(update -> apply -> updateWorldTransfor)

			// object setup
			{
				auto count = objectCount();
				for (auto i = 0; i < count; i++)
				{
					auto input = inputs[i].get();
					if (input->renderer == nullptr) continue;

					switch (input->objectType())
					{
					case ObjectType::Spine:
						auto spine = static_cast<SpineObject*>(fData->objects[i].get());
						input->renderer->setMixDuration(spine->mixDuration);
						break;
					}
				}
			}
			// first frame (prevkey & frame0) event
			for(auto &track : tracks)
			{
				if (track.get() == nullptr) continue;
				if (!track->enable) continue;

				if (track->playFrame < 0.0)
				{
					track->playFrame = 0.0;
					update_TrackEvent(track.get(), true, seekmode);
				}
			}
			{
				// execute per frame
				auto deltaframe = deltatime * fData->framerate;
				auto first = true;
				auto loopcount = 0;
				while (deltaframe >= 0.0)
				{
					// 0.0 once loop break
					if ((loopcount > 0) && (deltaframe < Misc::FloatZero)) break;

					double f;
					if (deltaframe >= 1.0)
						f = 1.0;
					else
						f = deltaframe;
					
					update_OneFrameUpdate(f,seekmode);

					if (!updateonce)
					{
						update_Interpolation(first, seekmode);
						update_Composite(f / fData->framerate);
						update_ObjectUpdate(f / fData->framerate);
						first = false;
					}

					deltaframe -= 1.0;
					loopcount++;
				}

				// skip
				if (updateonce)
				{
					auto dt = std::min(deltatime, 30.0);	// limitter

					update_Interpolation(first, seekmode);
					update_Composite(dt);
					update_ObjectUpdate(dt);
				}
			}
		}
		void updatePhysics(double deltatime)
		{
			for (auto i = 0; i < objectCount(); i++)
			{
				auto enable = fData->objects[i]->enable;
				if (!enable) continue;

				if (inputs[i]->renderer != nullptr) inputs[i]->renderer->update(0,deltatime);
			}
		}
		void appeasePhysics()
		{
			for (auto i = 0; i < objectCount(); i++)
			{
				if (inputs[i]->renderer != nullptr) inputs[i]->renderer->appeasePhysics();
			}
		}
		void setupPhysics() 
		{
			for (auto i = 0; i < objectCount(); i++)
			{
				if (inputs[i]->renderer != nullptr) inputs[i]->renderer->setupPhysics(fData->objects[i]->settingField);
			}
		}

	protected:
		Animationrig *fData = nullptr;
		vector<int> objectUpdateOrder;    // need speed
		double framePool = 0.0;
		vector<ObjectCache> objectCache;

		void compactionTracks()
		{
			auto usage = usageTrackCount();
			changeTracks(usage);
		}
		void changeTracks(int count)
		{
			if (count < 0) return;

			int usage = (count / 4) * 4 + 4;
			if (usage == trackCount()) return;

			// resize
			tracks.resize(usage);
		}
		int getSpineUseTrack(LayerType layertype)
		{
			switch (layertype)
			{
			case LayerType::AnimBlend1:
			case LayerType::SeekBlend1:
				return 1;
			case LayerType::AnimBlend2:
			case LayerType::SeekBlend2:
				return 2;
			case LayerType::AnimBlend4:
			case LayerType::SeekBlend4:
				return 4;
			}
			return 0;
		}

		void update_ObjectUpdate(double deltatime_)
		{
			auto count = objectCount();
			for (auto i = 0; i < count; i++)
			{
				if (!fData->objects[i]->enable) continue;
				inputs[i]->update(deltatime_);
			}
		}
		void update_OneFrameUpdate(double deltaframe, bool seekmode) {
			auto count = trackCount();
			for (auto i = 0; i < count; i++)
			{
				auto track = tracks[i].get();
				if (track == nullptr) continue;

				if (track->isSelfClear())
				{
					clearTrack(i);
					continue;
				}

				if (track->enable)
				{
					if (seekmode)
					{
						track->update(deltaframe);
					}
					else
					{
						if (!track->pause)
							track->update(deltaframe * track->playSpeed);
					}
					update_TrackEvent(track, false, seekmode);
				}
			}
		}
		void update_TrackEvent(StateTrack *track, bool prevkeyevent, bool seekmode)
		{
			// inner(local) function
			struct LocalFunc {
				static void inEventAnimationBlend(StateTrackLayer *layer, StateInput *input, AnimBlend4Key *key, int trackindex, int usetrack)
				{
					if (input == nullptr) return;
					if (input->renderer == nullptr) return;
					// name
					if (key->animation == L"none") return;
					// allow
					if (!key->animationAllowAlways)
						if (layer->string1 == key->animation) return;
					// loop area
					auto lb = key->animationLoopBegin;
					auto le = key->animationLoopEnd;
					if ((lb > le) || lb < 0.0) {
						lb = 0.0f;
						le = 0.0f;
					}

					input->renderer->setAnimation(
						trackindex, usetrack,
						key->animation,
						key->animationLoop,
						key->animationAdd,
						key->animationMixBlend,
						key->animationMixDuration,
						false,
						lb,le
					);
					layer->string1 = key->animation;
				}
				static void inEventSeekTimeBlend(StateTrackLayer *layer, StateInput *input, SeekBlend4Key *key, int trackindex, int usetrack)
				{
					if (input == nullptr) return;
					if (input->renderer == nullptr) return;
					// name
					if (key->animation == L"none") return;
					// allow
					if (!key->animationAllowAlways)
						if (layer->string1 == key->animation) return;
					// loop area
					auto lb = key->animationLoopBegin;
					auto le = key->animationLoopEnd;
					if ((lb > le) || lb < 0.0) {
						lb = 0.0f;
						le = 0.0f;
					}

					input->renderer->setAnimation(
						trackindex, usetrack,
						key->animation,
						key->animationLoop,
						key->animationAdd,
						key->animationMixBlend,
						key->animationMixDuration,
						true,
						lb,le
					);
					layer->string1 = key->animation;
				}
				static void inEventEvent(
					StateTrackLayer *layer, StateInput *input, EventKey *key, StateTrack *track, 
					AnimationrigState *state, bool seekmode, StateEventCallback func)
				{
					// sender : TAnimrigState
					// owner : Object
					// renderer : TRenderer
					// string[0,1]
					// float[0,1]
					if (func == nullptr) return;
					// seek flag
					if (seekmode) return;
					// ignition
					if (key->ignitionAlways) return;
					// name
					if (key->ID == L"none") return;
					if (key->ID == L"") return;
					if (!key->allowAlways)
						if (key->ID == layer->string1) return;

					StateEventData rec; // struct
					rec.ignition = false;
					rec.input = input;
					rec.track = track;
					rec.key = key;
					rec.ID = key->ID;
					rec.strings[0] = key->eventString[0];
					rec.strings[1] = key->eventString[1];
					rec.floats[0] = input->convertSpecialFloat(key->eventFloat[0]);
					rec.floats[1] = input->convertSpecialFloat(key->eventFloat[1]);
					func(state, rec);

					layer->string1 = key->ID;
				}
			};
			
			if (track->animation() == nullptr) return;
			// loop
			int f = (int)floor(track->animation()->getLoopedFrame(track->playFrame));
			if (track->prevPlayFrame == f) return;
			// object
			for (auto i = 0; i < objectCount(); i++)
			{
				auto obj = track->animation()->objects[i].get();
				auto objectenable = fData->objects[i]->enable;  // reserve object
				int trackindex = 0;
				int layercount = obj->layerCount();
				for (auto j = 0; j < layercount; j++)
				{
					// layer
					auto layer = obj->layers[j].get();
					// spine use track count
					// * It is not affected by layer.enable. keep layer.index and spine.trackindex.
					auto usetrack = getSpineUseTrack(layer->layerType());

					if (layer->enable)
					{
						// prevkey(first only)
						if ((f == 0) && (prevkeyevent))
						{
							auto key = layer->prevKey.get();
							if (objectenable)
							{
								switch (key->layerType())
								{
								case LayerType::AnimBlend1:
								case LayerType::AnimBlend2:
								case LayerType::AnimBlend4:
									LocalFunc::inEventAnimationBlend(
										track->objects[i]->layers[j].get(), 
										inputs[i].get(),
										static_cast<AnimBlend4Key*>(key), 
										trackindex, usetrack
									);
									break;
								case LayerType::SeekBlend1:
								case LayerType::SeekBlend2:
								case LayerType::SeekBlend4:
									LocalFunc::inEventSeekTimeBlend(
										track->objects[i]->layers[j].get(),
										inputs[i].get(),
										static_cast<SeekBlend4Key*>(key), 
										trackindex, usetrack
									);
									break;
								case LayerType::Event:
									LocalFunc::inEventEvent(
										track->objects[i]->layers[j].get(),
										inputs[i].get(),
										static_cast<EventKey*>(key),
										track,
										this,seekmode,onEvent
									);
									break;
								}
							}
						}
						// keys
						// * Optimize some day :Q
						auto count = layer->keyCount();
						for (auto k = 0; k < count; k++)
						{
							auto key = layer->keys[k].get();
							if (key->frame > f) break;
							if (key->frame != f) continue;
							if (objectenable)
							{
								switch (key->layerType())
								{
								case LayerType::AnimBlend1:
								case LayerType::AnimBlend2:
								case LayerType::AnimBlend4:
									LocalFunc::inEventAnimationBlend(
										track->objects[i]->layers[j].get(),
										inputs[i].get(),
										static_cast<AnimBlend4Key*>(key),
										trackindex, usetrack
									);
									break;
								case LayerType::SeekBlend1:
								case LayerType::SeekBlend2:
								case LayerType::SeekBlend4:
									LocalFunc::inEventSeekTimeBlend(
										track->objects[i]->layers[j].get(),
										inputs[i].get(),
										static_cast<SeekBlend4Key*>(key),
										trackindex, usetrack
									);
									break;
								case LayerType::Event:
									LocalFunc::inEventEvent(
										track->objects[i]->layers[j].get(),
										inputs[i].get(),
										static_cast<EventKey*>(key),
										track,
										this, seekmode, onEvent
									);
									break;
								}
							}
						}
					}
					// spine trackset index
					trackindex = trackindex + usetrack;
				}
			}
			// prev play frame
			track->prevPlayFrame = f;
		}
		void update_Interpolation(bool first, bool seekmode){
			// inner(local)func
			struct LocalFunc {
				static float inInterpolationValue(StateInput *input, float x0, float x1, float x2, float x3, float a, float b, Spline splinemode)
				{
					if (splinemode == Spline::Linear)
					{
						// 2 key : linear
						if (x1 >= Misc::InputFloatIndex) x1 = input->convertSpecialFloat(x1);
						if (x2 >= Misc::InputFloatIndex) x2 = input->convertSpecialFloat(x2);
						return x1 + (x2 - x1) * a;
					}
					else
					{
						// 4key : spline
						if (x0 >= Misc::InputFloatIndex) x0 = input->convertSpecialFloat(x0);
						if (x1 >= Misc::InputFloatIndex) x1 = input->convertSpecialFloat(x1);
						if (x2 >= Misc::InputFloatIndex) x2 = input->convertSpecialFloat(x2);
						if (x3 >= Misc::InputFloatIndex) x3 = input->convertSpecialFloat(x3);
						return Misc::splineFloat(x0, x1, x2, x3, a, b, splinemode);
					}
				}
				static void inInterpolationCommon(StateInput *input, StateTrackLayer *layer, AnimKey *key0, AnimKey *key1, AnimKey *key2, AnimKey *key3, float a)
				{
					// track blend alpha
					auto aa = Misc::easingFloat(0.0f, 1.0f, a, key1->trackAlphaPower, key1->trackAlphaEasing);
					if (key1->isPrevKey)
					{
						layer->prevTrackAlpha = Misc::easingLinear(layer->pPrevTrackAlpha, key2->prevTrackAlpha, aa);
						layer->currentTrackAlpha = Misc::easingLinear(layer->pCurrentTrackAlpha, key2->currentTrackAlpha, aa);
					}
					else
					{
						layer->prevTrackAlpha = Misc::easingLinear(key1->prevTrackAlpha, key2->prevTrackAlpha, aa);
						layer->currentTrackAlpha = Misc::easingLinear(key1->currentTrackAlpha, key2->currentTrackAlpha, aa);
					}
				}
				static void inInterpolationAnimationBlend(StateInput *input, StateTrackLayer *layer, AnimBlend4Key *key0, AnimBlend4Key *key1, AnimBlend4Key *key2, AnimBlend4Key *key3, float a)
				{
					auto aa = Misc::easingFloat(0.0f, 1.0f, a, key1->blendAlphaPower, key1->blendAlphaEasing);
					auto bb = 1.0f;
					auto ss = key1->blendAlphaSpline;

					if (key1->isPrevKey)
					{
						layer->values[0] = inInterpolationValue(input, layer->pValues[0], layer->pValues[0], key2->blendAlphaX, key3->blendAlphaX, aa, bb, ss);
						layer->values[1] = inInterpolationValue(input, layer->pValues[1], layer->pValues[1], key2->blendAlphaY, key3->blendAlphaY, aa, bb, ss);
					}
					else
					{
						layer->values[0] = inInterpolationValue(input, key0->blendAlphaX, key1->blendAlphaX, key2->blendAlphaX, key3->blendAlphaX, aa, bb, ss);
						layer->values[1] = inInterpolationValue(input, key0->blendAlphaY, key1->blendAlphaY, key2->blendAlphaY, key3->blendAlphaY, aa, bb, ss);
					}
				}
				static void inInterpolationSeekTimeBlend(StateInput *input, StateTrackLayer *layer, SeekBlend4Key *key0, SeekBlend4Key *key1, SeekBlend4Key *key2, SeekBlend4Key *key3, float a)
				{
					auto aa = Misc::easingFloat(0.0f, 1.0f, a, key1->seekTimePower, key1->seekTimeEasing);
					auto bb = 1.0f;
					auto ss = key1->seekTimeSpline;

					if (key1->isPrevKey)
					{
						layer->values[0] = inInterpolationValue(input, layer->pValues[0], layer->pValues[0], key2->seekTimeX, key3->seekTimeX, aa, bb, ss);
						layer->values[1] = inInterpolationValue(input, layer->pValues[1], layer->pValues[1], key2->seekTimeY, key3->seekTimeY, aa, bb, ss);
					}
					else
					{
						layer->values[0] = inInterpolationValue(input, key0->seekTimeX, key1->seekTimeX, key2->seekTimeX, key3->seekTimeX, aa, bb, ss);
						layer->values[1] = inInterpolationValue(input, key0->seekTimeY, key1->seekTimeY, key2->seekTimeY, key3->seekTimeY, aa, bb, ss);
					}
				}
				static void inInterpolationTransform(StateInput *input, StateTrackLayer *layer, TransformKey *key0, TransformKey *key1, TransformKey *key2, TransformKey *key3, float a)
				{
					auto aa = Misc::easingFloat(0.0f, 1.0f, a, key1->transformPower, key1->transformEasing);
					auto bb = 1.0f;
					auto ss = key1->transSpline;

					if (key1->isPrevKey)
					{
						// position
						layer->values[0] = inInterpolationValue(input, layer->pValues[0], layer->pValues[0], key2->position.x, key3->position.x, aa, bb, ss);
						layer->values[1] = inInterpolationValue(input, layer->pValues[1], layer->pValues[1], key2->position.y, key3->position.y, aa, bb, ss);
						layer->values[2] = inInterpolationValue(input, layer->pValues[2], layer->pValues[2], key2->position.z, key3->position.z, aa, bb, ss);
						// scale
						layer->values[3] = inInterpolationValue(input, layer->pValues[3], layer->pValues[3], key2->scale.x, key3->scale.x, aa, bb, ss);
						layer->values[4] = inInterpolationValue(input, layer->pValues[4], layer->pValues[4], key2->scale.y, key3->scale.y, aa, bb, ss);
						layer->values[5] = inInterpolationValue(input, layer->pValues[5], layer->pValues[5], key2->scale.z, key3->scale.z, aa, bb, ss);
						// rotate
						layer->values[6] = inInterpolationValue(input, layer->pValues[6], layer->pValues[6], key2->rotate.x, key3->rotate.x, aa, bb, ss);
						layer->values[7] = inInterpolationValue(input, layer->pValues[7], layer->pValues[7], key2->rotate.y, key3->rotate.y, aa, bb, ss);
						layer->values[8] = inInterpolationValue(input, layer->pValues[8], layer->pValues[8], key2->rotate.z, key3->rotate.z, aa, bb, ss);
						layer->rotateOrder = layer->pRotateOrder;
						// color (not support spline)
						aa = Misc::easingFloat(0.0f, 1.0f, a, key1->colorPower, key1->colorEasing);
						layer->values[9]  = Misc::easingLinear(layer->pValues[9], key2->colorAlpha, aa);
						layer->values[10] = Misc::easingLinear(layer->pValues[10] * 0xff, float(key2->mulColor >> 16 & 0xff), aa) * (1.0f / 0xff);
						layer->values[11] = Misc::easingLinear(layer->pValues[11] * 0xff, float(key2->mulColor >> 8 & 0xff), aa) * (1.0f / 0xff);
						layer->values[12] = Misc::easingLinear(layer->pValues[12] * 0xff, float(key2->mulColor & 0xff), aa) * (1.0f / 0xff);
						// tint color
						layer->values[13] = Misc::easingLinear(layer->pValues[13] * 0xff, float(key2->tintColor >> 16 & 0xff), aa) * (1.0f / 0xff);
						layer->values[14] = Misc::easingLinear(layer->pValues[14] * 0xff, float(key2->tintColor >> 8 & 0xff), aa) * (1.0f / 0xff);
						layer->values[15] = Misc::easingLinear(layer->pValues[15] * 0xff, float(key2->tintColor & 0xff), aa) * (1.0f / 0xff);
					}
					else
					{
						// position
						layer->values[0] = inInterpolationValue(input, key0->position.x, key1->position.x, key2->position.x, key3->position.x, aa, bb, ss);
						layer->values[1] = inInterpolationValue(input, key0->position.y, key1->position.y, key2->position.y, key3->position.y, aa, bb, ss);
						layer->values[2] = inInterpolationValue(input, key0->position.z, key1->position.z, key2->position.z, key3->position.z, aa, bb, ss);
						// scale
						layer->values[3] = inInterpolationValue(input, key0->scale.x, key1->scale.x, key2->scale.x, key3->scale.x, aa, bb, ss);
						layer->values[4] = inInterpolationValue(input, key0->scale.y, key1->scale.y, key2->scale.y, key3->scale.y, aa, bb, ss);
						layer->values[5] = inInterpolationValue(input, key0->scale.z, key1->scale.z, key2->scale.z, key3->scale.z, aa, bb, ss);
						// rotate
						layer->values[6] = inInterpolationValue(input, key0->rotate.x, key1->rotate.x, key2->rotate.x, key3->rotate.x, aa, bb, ss);
						layer->values[7] = inInterpolationValue(input, key0->rotate.y, key1->rotate.y, key2->rotate.y, key3->rotate.y, aa, bb, ss);
						layer->values[8] = inInterpolationValue(input, key0->rotate.z, key1->rotate.z, key2->rotate.z, key3->rotate.z, aa, bb, ss);
						layer->rotateOrder = key1->rotateOrder;
						// color (not support spline)
						aa = Misc::easingFloat(0.0f, 1.0f, a, key1->colorPower, key1->colorEasing);
						layer->values[9] = Misc::easingLinear(key1->colorAlpha, key2->colorAlpha, aa);
						layer->values[10] = Misc::easingLinear(float(key1->mulColor >> 16 & 0xff), float(key2->mulColor >> 16 & 0xff), aa) * (1.0f / 0xff);
						layer->values[11] = Misc::easingLinear(float(key1->mulColor >> 8 & 0xff), float(key2->mulColor >> 8 & 0xff), aa) * (1.0f / 0xff);
						layer->values[12] = Misc::easingLinear(float(key1->mulColor & 0xff), float(key2->mulColor & 0xff), aa) * (1.0f / 0xff);
						// tint
						layer->values[13] = Misc::easingLinear(float(key1->tintColor >> 16 & 0xff), float(key2->tintColor >> 16 & 0xff), aa) * (1.0f / 0xff);
						layer->values[14] = Misc::easingLinear(float(key1->tintColor >> 8 & 0xff), float(key2->tintColor >> 8 & 0xff), aa) * (1.0f / 0xff);
						layer->values[15] = Misc::easingLinear(float(key1->tintColor & 0xff), float(key2->tintColor & 0xff), aa) * (1.0f / 0xff);
					}
				}
				static void inInterpolationPriority(StateInput *input, StateTrackLayer *layer, PriorityKey *key0, PriorityKey *key1, PriorityKey *key2, PriorityKey *key3, float a)
				{
					auto aa = Misc::easingFloat(0.0f, 1.0f, a, key1->priorityPower, key1->priorityEasing);

					if (key1->isPrevKey)
					{
						layer->values[0] = Misc::easingLinear(layer->pValues[0], key2->priority, aa);
					}
					else
					{
						layer->values[0] = Misc::easingLinear(key1->priority, key2->priority, aa);
					}
				}
				static void inInterpolationEvent(
					StateInput *input, StateTrack *track_, StateTrackLayer *layer, EventKey *key,
					AnimationrigState *state, bool seekmode, StateEventCallback func)
				{
					if (func == nullptr) return;
					// seek flag
					if (seekmode) return;
					// ignition
					if (!key->ignitionAlways) return;
					// name
					if (key->ID == L"none") return;
					if (key->ID == L"") return;
					// * unsupport allowdiffrent
					// if not key1.AllowAlways then
					//   if key1.ID = layer.EventID then exit;
					StateEventData rec;
					rec.ignition = true;
					rec.input = input;
					rec.track = track_;
					rec.key = key;
					rec.ID = key->ID;
					rec.strings[0] = key->eventString[0];
					rec.strings[1] = key->eventString[1];
					rec.floats[0] = input->convertSpecialFloat(key->eventFloat[0]);
					rec.floats[1] = input->convertSpecialFloat(key->eventFloat[1]);
					func(state,rec);

					layer->string1 = key->ID;
				}
				static void inInterpolationState(StateInput *input, StateTrackLayer *layer, StateKey *key0, StateKey *key1, StateKey *key2, StateKey *key3, float a)
				{
					layer->values[0] = 0.0f;
					layer->values[1] = 0.0f;
					if (key1->enable) layer->values[0] = 1.0f;
					if (key1->visible) layer->values[1] = 1.0f;
				}
				static void inInterpolationPhysicsForce(StateInput *input, StateTrackLayer *layer, PhysicsForceKey *key0, PhysicsForceKey *key1, PhysicsForceKey *key2, PhysicsForceKey *key3, float a, double pt)
				{
					float ww = 0.0f;
					auto aa = Misc::easingFloat(0.0f, 1.0f, a, key1->forcePower, key1->forceEasing);
					auto bb = 1.0f;
					auto ss = key1->forceSpline;
					layer->string1 = key1->physicsTarget;

					if (key1->isPrevKey)
					{
						layer->values[0] = inInterpolationValue(input, layer->pValues[0], layer->pValues[0], key2->forceVector.x, key3->forceVector.x, aa, bb, ss);
						layer->values[1] = inInterpolationValue(input, layer->pValues[1], layer->pValues[1], key2->forceVector.y, key3->forceVector.y, aa, bb, ss);
						layer->values[2] = inInterpolationValue(input, layer->pValues[2], layer->pValues[2], key2->forceVector.z, key3->forceVector.z, aa, bb, ss);
						ww = 0.0f;
					}
					else
					{
						layer->values[0] = inInterpolationValue(input, key0->forceVector.x, key1->forceVector.x, key2->forceVector.x, key3->forceVector.x, aa, bb, ss);
						layer->values[1] = inInterpolationValue(input, key0->forceVector.y, key1->forceVector.y, key2->forceVector.y, key3->forceVector.y, aa, bb, ss);
						layer->values[2] = inInterpolationValue(input, key0->forceVector.z, key1->forceVector.z, key2->forceVector.z, key3->forceVector.z, aa, bb, ss);
						ww = key1->wiggleRatio + (key2->wiggleRatio - key1->wiggleRatio) * aa;
					}

					if (ww > 0.0)
					{
						aa = ww * (Wiggle::random(pt * (double)key1->wiggleCycle, WiggleType::BSpline, 275) - 0.5f);
						layer->values[0] *= layer->values[0] * aa;
						layer->values[1] *= layer->values[1] * aa;
						layer->values[2] *= layer->values[2] * aa;
					}
				}
			};

			for (auto &t : tracks)
			{
				auto track = t.get();
				if (track == nullptr) continue;
				if (!track->enable) continue;

				auto anim = track->animation();
				if (anim == nullptr) return;
				// loop
				double f = anim->getLoopedFrame(track->playFrame);
				// object
				for (auto i = 0; i < objectCount(); i++)
				{
					auto obj = anim->objects[i].get();
					auto layercount = obj->layerCount();
					for (auto j = 0; j < layercount; j++)
					{
						auto layer = obj->layers[j].get();
						auto tracklayer = track->objects[i]->layers[j].get();
						auto keycount = layer->keyCount();
						tracklayer->isPass = false;
						// key empty
						if (keycount == 0) tracklayer->isPass = true;
						// enable
						if (!layer->enable) tracklayer->isPass = true;
						// pass
						if (tracklayer->isPass)
						{
							StateTrackLayer::clearValues(&tracklayer->values[0], tracklayer->layerType());   // static
							continue;
						}

						// get current key
						AnimKey *key0, *key1, *key2, *key3;
						double d = 0.0;
						int index = layer->getKeyIndex((int)floor(f));
						if (index == -1)
						{
							// between prevkey(0frame) to key[0]
							key1 = layer->prevKey.get();
							if (keycount > 0)
								key2 = layer->keys[0].get();
							else
								key2 = key1;

							d = (f - 0.0) / max(key2->frame - 0.0, 1.0);
							key0 = key1;
							if (1 < keycount)
								key3 = layer->keys[1].get();
							else
								key3 = key2;
						}
						else
						{
							// keys[]
							// current key
							key1 = layer->keys[index].get();
							// next key
							if (index + 1 < keycount)
							{
								key2 = layer->keys[index + 1].get();
								d = (f - key1->frame) / max(key2->frame - key1->frame, 1);
							}
							else
							{
								// last key
								key2 = key1;
								d = 0.0;
							}
							// previous key
							if (index - 1 < 0)
								key0 = layer->prevKey.get();
							else
								key0 = layer->keys[index - 1].get();
							// next next key
							if (index + 2 < keycount)
								key3 = layer->keys[index + 2].get();
							else
								key3 = key2;
						}
						// common
						LocalFunc::inInterpolationCommon(
							inputs[i].get(),
							tracklayer,                    // track.layers (output)
							key0, key1, key2, key3,
							(float)d
						);
						// layer type
						switch (layer->layerType())
						{
						case LayerType::AnimBlend1:
						case LayerType::AnimBlend2:
						case LayerType::AnimBlend4:
							LocalFunc::inInterpolationAnimationBlend(
								inputs[i].get(),
								tracklayer,
								static_cast<AnimBlend4Key*>(key0), static_cast<AnimBlend4Key*>(key1), static_cast<AnimBlend4Key*>(key2), static_cast<AnimBlend4Key*>(key3),
								(float)d
							);
							break;
						case LayerType::SeekBlend1:
						case LayerType::SeekBlend2:
						case LayerType::SeekBlend4:
							LocalFunc::inInterpolationSeekTimeBlend(
								inputs[i].get(),
								tracklayer,
								static_cast<SeekBlend4Key*>(key0), static_cast<SeekBlend4Key*>(key1), static_cast<SeekBlend4Key*>(key2), static_cast<SeekBlend4Key*>(key3),
								(float)d
							);
							break;
						case LayerType::Transform:
							LocalFunc::inInterpolationTransform(
								inputs[i].get(),
								tracklayer,
								static_cast<TransformKey*>(key0), static_cast<TransformKey*>(key1), static_cast<TransformKey*>(key2), static_cast<TransformKey*>(key3),
								(float)d
							);
							break;
						case LayerType::Priority:
							LocalFunc::inInterpolationPriority(
								inputs[i].get(),
								tracklayer,
								static_cast<PriorityKey*>(key0), static_cast<PriorityKey*>(key1), static_cast<PriorityKey*>(key2), static_cast<PriorityKey*>(key3),
								(float)d
							);
							break;
						case LayerType::State:
							LocalFunc::inInterpolationState(
								inputs[i].get(),
								tracklayer,
								static_cast<StateKey*>(key0), static_cast<StateKey*>(key1), static_cast<StateKey*>(key2), static_cast<StateKey*>(key3),
								(float)d
							);
							break;
						case LayerType::PhysicsForce:
							LocalFunc::inInterpolationPhysicsForce(
								inputs[i].get(),
								tracklayer,
								static_cast<PhysicsForceKey*>(key0), static_cast<PhysicsForceKey*>(key1), static_cast<PhysicsForceKey*>(key2), static_cast<PhysicsForceKey*>(key3),
								(float)d, track->playFrame / fData->framerate
							);
							break;
						case LayerType::Event:
							LocalFunc::inInterpolationEvent(
								inputs[i].get(),
								track,
								tracklayer,
								static_cast<EventKey*>(key1)
								,this,seekmode,onEvent
							);
							break;
						}
					}
				}
			}
		}
		void update_Composite(double deltatime)
		{
			// innner(local) func
			struct LocalFunc {
				static bool inCalcCommonLayer(StateTrack *track, float *mix, int mixcount, int objectindex, int layerindex)
				{
					if (track == nullptr) return false;
					if (track->animation() == nullptr) return false;

					auto layer = track->objects[objectindex]->layers[layerindex].get();
					if (layer->isPass)
						if (track->mixAlphaEase.done) return false;      // fade blend

					auto aa1 = (float)(1.0 - track->mixAlphaEase.value);
					for (auto i = 0; i < mixcount; i++)
					{
						mix[i] = layer->values[i] + (layer->pValues[i] - layer->values[i]) * aa1;
					}
					return true;
				}
			};
			// main
			Vector3 zeroposition;
			Misc::v3SetVector(zeroposition, 0.0f, 0.0f, 0.0f);

			// align
			Matrix4 alignedmatrix;
			if (alignEnabled)
			{
				alignedmatrix = getAlignMatrix();
				Misc::m4PushMatrix(alignedmatrix, rootMatrix);
			}
			else {
				alignedmatrix = rootMatrix;
			}

			float sum[StateTrackLayer::ValueCount];
			float mix[StateTrackLayer::ValueCount];

			for (auto o = 0; o < objectCount(); o++)
			{
				// object update order (parent object link)
				auto objectindex = objectUpdateOrder[o];
				auto obj = fData->objects[objectindex].get();     // reserve object.
				auto renderer = inputs[objectindex]->renderer; // renderer object
				auto layercount = obj->layerCount();

				// enable / visible
				auto enable = obj->enable;
				auto visible = obj->visible;
				// offset , scale
				auto transmatrix = Misc::IdentityMatrix4;
				Misc::m4PushMove(transmatrix, -obj->offset.x, -obj->offset.y, -obj->offset.z);
				Misc::m4PushScale(transmatrix, obj->scale.x, obj->scale.y, obj->scale.z);
				// color
				auto transcolor = Misc::WhiteColor;
				auto tintcolor = Misc::BlackColor;
				// etc
				auto trackindex = 0;     // spine track index
				auto drawpriority = obj->drawPriority;
				auto rotateorder = RotateOrder::Pass;
				auto spinelayerexists = false;

				// composite layer direction : blendalpha
				for (auto i = 0; i < layercount; i++)
				{
					// spine use track count
					// * It is not affected by layer.enable. keep layer.index and spine.trackindex.
					auto layertype = obj->layers[i]->layerType();
					auto usetrack = getSpineUseTrack(layertype);

					// Split code someday ...
					switch (layertype)
					{
					case LayerType::AnimBlend1:
					case LayerType::AnimBlend2:
					case LayerType::AnimBlend4:
						StateTrackLayer::clearValues(&sum[0], LayerType::AnimBlend4);    // static
						spinelayerexists = false;
						for (auto j = 0; j < trackCount(); j++)
						{
							auto track = tracks[j].get();
							if (track == nullptr) continue;
							if (!LocalFunc::inCalcCommonLayer(track, mix, 2, objectindex, i)) continue;
							auto layer = track->objects[objectindex]->layers[i].get();
							// track blend
							auto aa3 = track->trackAlphaEase.value;
							auto aa1 = 1.0f + (layer->prevTrackAlpha - 1.0f) * aa3;
							auto aa2 = layer->currentTrackAlpha * aa3;
							sum[0] = sum[0] * aa1 + mix[0] * aa2;
							sum[1] = sum[1] * aa1 + mix[1] * aa2;
							spinelayerexists = true;
						}
						{
							auto bb1 = min(max(sum[1], 0.0f), +2.0f);
							auto bb2 = min(max(sum[0], 0.0f), +2.0f);
							auto bb3 = -min(max(sum[1], -2.0f), 0.0f);
							auto bb4 = -min(max(sum[0], -2.0f), 0.0f);
							if ((renderer != nullptr) && (enable))
								renderer->setTrackBlendAlpha(
									trackindex, usetrack,
									bb1, bb2, bb3, bb4,
									spinelayerexists    // exists enable layer layer
								);
						}
						break;
					case LayerType::SeekBlend1:
					case LayerType::SeekBlend2:
					case LayerType::SeekBlend4:
						StateTrackLayer::clearValues(&sum[0], LayerType::SeekBlend4);  // static
						spinelayerexists = false;
						for (auto j = 0; j < trackCount(); j++)
						{
							auto track = tracks[j].get();
							if (track == nullptr) continue;
							if (!LocalFunc::inCalcCommonLayer(track, mix, 2, objectindex, i)) continue;
							auto layer = track->objects[objectindex]->layers[i].get();
							// track blend
							auto aa3 = track->trackAlphaEase.value;
							auto aa1 = 1.0f + (layer->prevTrackAlpha - 1.0f) * aa3;
							auto aa2 = layer->currentTrackAlpha * aa3;
							sum[0] = sum[0] * aa1 + mix[0] * aa2;
							sum[1] = sum[1] * aa1 + mix[1] * aa2;
							spinelayerexists = true;
						}
						{
							auto tt1 = min(max(sum[1], 0.0f), +3600.0f);
							auto tt2 = min(max(sum[0], 0.0f), +3600.0f);
							auto tt3 = -min(max(sum[1], -3600.0f), 0.0f);
							auto tt4 = -min(max(sum[0], -3600.0f), 0.0f);
							if ((renderer != nullptr) && (enable))
							{
								renderer->setTrackBlendAlpha(
									trackindex, usetrack,
									1.0f, 1.0f, 1.0f, 1.0f,
									spinelayerexists    // exists enable layer layer
								);
								renderer->setTrackSeekTime(
									trackindex, usetrack,
									tt1, tt2, tt3, tt4,
									spinelayerexists    // exists enable layer layer
								);
							}
						}
						break;
					case LayerType::Transform:
						StateTrackLayer::clearValues(&sum[0], LayerType::Transform);  // static
						rotateorder = RotateOrder::Pass;
						for (auto j = 0; j < trackCount(); j++)
						{
							auto track = tracks[j].get();
							if (track == nullptr) continue;
							if (!LocalFunc::inCalcCommonLayer(track, mix, 3 + 3 + 3 + 4, objectindex, i)) continue;
							auto layer = track->objects[objectindex]->layers[i].get();
							// track blend
							auto aa3 = track->trackAlphaEase.value;
							auto aa1 = 1.0f + (layer->prevTrackAlpha - 1.0f) * aa3;
							auto aa2 = layer->currentTrackAlpha * aa3;
							// move
							sum[0] = sum[0] * aa1 + mix[0] * aa2;
							sum[1] = sum[1] * aa1 + mix[1] * aa2;
							sum[2] = sum[2] * aa1 + mix[2] * aa2;
							// scale
							sum[3] = (1.0f + (sum[3] - 1.0f) * aa1) * (1.0f + (mix[3] - 1.0f) * aa2);
							sum[4] = (1.0f + (sum[4] - 1.0f) * aa1) * (1.0f + (mix[4] - 1.0f) * aa2);
							sum[5] = (1.0f + (sum[5] - 1.0f) * aa1) * (1.0f + (mix[5] - 1.0f) * aa2);
							// rotate
							sum[6] = sum[6] * aa1 + mix[6] * aa2;
							sum[7] = sum[7] * aa1 + mix[7] * aa2;
							sum[8] = sum[8] * aa1 + mix[8] * aa2;
							// rotatemode
							if (layer->rotateOrder != RotateOrder::Pass) rotateorder = layer->rotateOrder;
							// alpha,color
							sum[9] = (1.0f + (sum[9] - 1.0f) * aa1) * (1.0f + (mix[9] - 1.0f) * aa2);
							sum[10] = (1.0f + (sum[10] - 1.0f) * aa1) * (1.0f + (mix[10] - 1.0f) * aa2);
							sum[11] = (1.0f + (sum[11] - 1.0f) * aa1) * (1.0f + (mix[11] - 1.0f) * aa2);
							sum[12] = (1.0f + (sum[12] - 1.0f) * aa1) * (1.0f + (mix[12] - 1.0f) * aa2);
							// tint color
							sum[13] = sum[13] * aa1 + mix[13] * aa2;
							sum[14] = sum[14] * aa1 + mix[14] * aa2;
							sum[15] = sum[15] * aa1 + mix[15] * aa2;
						}
						{
							if (rotateorder == RotateOrder::Pass) rotateorder = RotateOrder::ZYX;
							// composite transform
							Misc::m4PushScale(transmatrix, sum[3], sum[4], sum[5]);
							Misc::m4PushRotate(transmatrix, sum[6], sum[7], sum[8], rotateorder);   // rotate mode
							Misc::m4PushMove(transmatrix, sum[0], sum[1], sum[2]);
							// color
							transcolor.a *= sum[9];
							transcolor.r *= sum[10];
							transcolor.g *= sum[11];
							transcolor.b *= sum[12];
							tintcolor.r = min(tintcolor.r + sum[13], 1.0f);
							tintcolor.g = min(tintcolor.g + sum[14], 1.0f);
							tintcolor.b = min(tintcolor.b + sum[15], 1.0f);
						}
						break;
					case LayerType::Priority:
						StateTrackLayer::clearValues(&sum[0], LayerType::Priority);  // static
						for (auto j = 0; j < trackCount(); j++)
						{
							auto track = tracks[j].get();
							if (track == nullptr) continue;
							if (!LocalFunc::inCalcCommonLayer(track, mix, 1, objectindex, i)) continue;
							auto layer = track->objects[objectindex]->layers[i].get();
							// track blend
							auto aa3 = track->trackAlphaEase.value;
							auto aa1 = 1.0f + (layer->prevTrackAlpha - 1.0f) * aa3;
							auto aa2 = layer->currentTrackAlpha * aa3;
							sum[0] = sum[0] * aa1 + mix[0] * aa2;
						}
						{
							drawpriority = drawpriority + sum[0];
						}
						break;
					case LayerType::State:
						StateTrackLayer::clearValues(sum, LayerType::State);
						for (auto j = 0; j < trackCount(); j++)
						{
							auto track = tracks[j].get();
							if (track == nullptr) continue;
							auto layer = track->objects[objectindex]->layers[i].get();
							enable = enable & (layer->values[0] > 0.0f);
							visible = visible & (layer->values[1] > 0.0f);
						}
						break;
					case LayerType::PhysicsForce:
						StateTrackLayer::clearValues(sum, LayerType::PhysicsForce);
						for (auto j = 0; j < trackCount(); j++)
						{
							auto track = tracks[j].get();
							if (track == nullptr) continue;
							if (!LocalFunc::inCalcCommonLayer(track, mix, 3, objectindex, i)) continue;
							auto layer = track->objects[objectindex]->layers[i].get();
							// current track alpha only
							auto aa3 = layer->currentTrackAlpha * track->trackAlphaEase.value;
							aa3 = (float)(aa3 * deltatime);    // apply dt
							if (renderer != nullptr) {
								if ((layer->string1 == L"self") || (layer->string1 == L""))
									renderer->addPhysicsForce(layer->string1, mix[0] * aa3, mix[1] * aa3, mix[2] * aa3);
								else
									Update_AddPhysicsObjectTarget(layer->string1, layer->string2, mix[0] * aa3, mix[1] * aa3, mix[2] * aa3);
							}
						}
						break;
					}
					// spine track index
					trackindex = trackindex + usetrack;
				}

				// group
				auto cache = &objectCache[objectindex];
				auto parentindex = fData->objects[objectindex]->parentObjectIndex;
				if ((parentindex >= 0) && (parentindex < objectCount()) && (parentindex != objectindex))
				{
					auto pcache = &objectCache[parentindex];
					// composite parent matrix
					auto s = fData->objects[objectindex]->parentBoneName;
					if ((s == L"") || (s == L"none") || (inputs[parentindex]->renderer == nullptr))
					{
						Misc::m4PushMatrix(transmatrix, objectCache[parentindex].matrix);
						cache->matrix = transmatrix; // hard copy
					}
					else
					{
						auto tempmatrix = inputs[parentindex]->renderer->getBoneMatrix(s);
						Misc::m4PushMatrix(transmatrix, tempmatrix);
						Misc::m4PushMatrix(transmatrix, objectCache[parentindex].matrix);
						cache->matrix = transmatrix; // hard copy
					}

					cache->inheritedEnable = enable & pcache->inheritedEnable;
					cache->inheritedVisible = visible & pcache->inheritedVisible;
					cache->drawPriority = drawpriority + pcache->drawPriority;
					cache->transColor.setMul(transcolor , pcache->transColor);
					cache->tintColor.setAdd(tintcolor, pcache->tintColor);
				}
				else
				{
					Misc::m4PushMatrix(transmatrix, alignedmatrix);
					cache->matrix = transmatrix; // hard copy
					cache->inheritedEnable = enable;
					cache->inheritedVisible = visible;
					cache->drawPriority = drawpriority;
					cache->transColor = transcolor;
					cache->tintColor = tintcolor;
				}

				// state
				if (renderer != nullptr)
				{
					renderer->stateInputObjectType = inputs[objectindex]->objectType();
					renderer->rendererEnable = enable;
					renderer->rendererVisible = visible;
					renderer->rendererInheritedEnable = cache->inheritedEnable;
					renderer->rendererInheritedVisible = cache->inheritedVisible;
					renderer->transColor = cache->transColor;
					renderer->tintColor = cache->tintColor;
					renderer->transMatrix = cache->matrix;   // hard copy
					//renderer->transPosition = Misc::m4Transform(zeroposition, transmatrix);		// move to update_CameraMatrix();
					//renderer->drawPriorityZ = renderer->transPosition.z;
					renderer->drawPriorityDesign = cache->drawPriority;
				}
			}

			// update camera matrix
			update_CameraMatrix();
		}
		void update_CameraMatrix() {
			int n = objectCount();
			// setup
			for (auto i = 0; i < n; i++)
			{
				objectCache[i].camMatrixIndex = 0;
				if (inputs[i]->renderer != nullptr) inputs[i]->renderer->cameraMatrix = Misc::IdentityMatrix4;
			}
			// update camera matrix
			for (auto i = 0; i < n; i++)
			{
				int index = i;
				// enable
				if (!objectCache[index].inheritedEnable) continue;
				// camera
				if (inputs[index]->objectType() == ObjectType::Camera)
				{
					// make camera matrix
					auto obj = static_cast<CameraObject*>(fData->objects[index].get());
					auto m = Misc::IdentityMatrix4;
					Misc::m4PushMove(m, -obj->rotateOffset.x, -obj->rotateOffset.y, -obj->rotateOffset.z);
					bool b;
					auto im = Misc::m4InvertMatrix(objectCache[index].matrix, b);     // ummm....
					Misc::m4PushMatrix(m, im);
					Misc::m4PushMove(m, +obj->rotateOffset.x, +obj->rotateOffset.y, +obj->rotateOffset.z);
					// set self camera matrix
					if (inputs[index]->renderer != nullptr) inputs[index]->renderer->cameraMatrix = m;

					auto area1 = obj->AreaBegin;
					auto area2 = obj->AreaEnd;
					if (area1 > area2) {
						auto t = area2;
						area2 = area1;
						area1 = t;
					}
			
					// set target camera matrix
					for (auto j = 0; j < n; j++)
					{
						if (inputs[j]->objectType() == ObjectType::Camera) continue;

						if (area1 != area2) {
							if (objectCache[j].drawPriority < area1) continue;
							if (objectCache[j].drawPriority > area2) continue;
						}

						auto renderer = inputs[j]->renderer;
						if (renderer != nullptr)
						{
							// matrix
							if (objectCache[j].camMatrixIndex == 0)
							{
								renderer->cameraMatrix = m;
							}
							else
							{
								Misc::m4PushMatrix(renderer->cameraMatrix, m);
							}
							// color
							renderer->transColor.setMul(renderer->transColor , objectCache[index].transColor);
							renderer->tintColor.setAdd(renderer->tintColor, objectCache[index].tintColor);
							objectCache[j].camMatrixIndex++;
						}
					}
				}
			}
			// update transform position
			for (auto &inp : inputs) {
				if (inp->renderer != nullptr) {
					Vector3 v;
					Misc::v3SetVector(v, 0, 0, 0);
					v = Misc::m4Transform(v, inp->renderer->transMatrix);
					v = Misc::m4Transform(v, inp->renderer->cameraMatrix);
					inp->renderer->transPosition = v;
					inp->renderer->drawPriorityZ = v.z;
				}
			}
		}
		void Update_AddPhysicsObjectTarget(const wstring &objecttarget, const wstring &physicstarget, float ax, float ay, float az){
			auto all = objecttarget == L"all";
			if (objecttarget == L"") return;
		
			for (auto &inp : inputs) {
				if (inp->renderer == nullptr) continue;

				if (all || (objecttarget == inp->objectData()->name) || (objecttarget == inp->objectData()->aliasName)) {
					inp->renderer->addPhysicsForce(physicstarget, ax, ay, az);
				}
			}
		}
	};
}

#endif