Parcourir la source

Merge pull request #70 from godlikepanos/skinning

Skinning
Panagiotis Christopoulos Charitos il y a 5 ans
Parent
commit
8ebfb2f4cb
45 fichiers modifiés avec 1783 ajouts et 169 suppressions
  1. 1 0
      samples/CMakeLists.txt
  2. 6 0
      samples/common/Framework.cpp
  3. 3 0
      samples/skeletal_animation/CMakeLists.txt
  4. 82 0
      samples/skeletal_animation/Main.cpp
  5. 15 0
      samples/skeletal_animation/assets/Armature.002.ankiskel
  6. BIN
      samples/skeletal_animation/assets/Drone_diff.ankitex
  7. BIN
      samples/skeletal_animation/assets/Drone_normal.ankitex
  8. BIN
      samples/skeletal_animation/assets/Drone_roughness.ankitex
  9. BIN
      samples/skeletal_animation/assets/Mesh.ankimesh
  10. 9 0
      samples/skeletal_animation/assets/Mesh_Robot.001.ankimdl
  11. 26 0
      samples/skeletal_animation/assets/Robot.001.ankimtl
  12. 671 0
      samples/skeletal_animation/assets/float.001.ankianim
  13. BIN
      samples/skeletal_animation/assets/room.001.ankimesh
  14. 8 0
      samples/skeletal_animation/assets/room.001_room.red.ankimdl
  15. BIN
      samples/skeletal_animation/assets/room.002.ankimesh
  16. 8 0
      samples/skeletal_animation/assets/room.002_room.green.ankimdl
  17. BIN
      samples/skeletal_animation/assets/room.003.ankimesh
  18. 8 0
      samples/skeletal_animation/assets/room.003_room.blue.ankimdl
  19. BIN
      samples/skeletal_animation/assets/room.ankimesh
  20. 26 0
      samples/skeletal_animation/assets/room.ankimtl
  21. 26 0
      samples/skeletal_animation/assets/room.blue.ankimtl
  22. 26 0
      samples/skeletal_animation/assets/room.green.ankimtl
  23. 26 0
      samples/skeletal_animation/assets/room.red.ankimtl
  24. 8 0
      samples/skeletal_animation/assets/room_room.ankimdl
  25. 73 0
      samples/skeletal_animation/assets/scene.lua
  26. 80 0
      samples/skeletal_animation/assets/wave.001.ankianim
  27. 6 18
      shaders/GBufferGeneric.ankiprog
  28. 11 14
      shaders/SceneDebug.ankiprog
  29. 189 34
      src/anki/importer/GltfImporter.cpp
  30. 8 0
      src/anki/importer/GltfImporter.h
  31. 10 10
      src/anki/math/Quat.h
  32. 12 2
      src/anki/math/Transform.h
  33. 30 0
      src/anki/math/Vec.h
  34. 18 0
      src/anki/renderer/GBuffer.h
  35. 44 32
      src/anki/resource/AnimationResource.cpp
  36. 18 25
      src/anki/resource/SkeletonResource.cpp
  37. 5 0
      src/anki/resource/SkeletonResource.h
  38. 44 0
      src/anki/scene/DebugDrawer.cpp
  39. 21 0
      src/anki/scene/DebugDrawer.h
  40. 97 9
      src/anki/scene/ModelNode.cpp
  41. 4 2
      src/anki/scene/ModelNode.h
  42. 110 17
      src/anki/scene/components/SkinComponent.cpp
  43. 52 6
      src/anki/scene/components/SkinComponent.h
  44. 1 0
      src/anki/util/ProcessPosix.cpp
  45. 1 0
      src/anki/util/String.h

+ 1 - 0
samples/CMakeLists.txt

@@ -1,3 +1,4 @@
 add_subdirectory(simple_scene)
 add_subdirectory(sponza)
 add_subdirectory(physics_playground)
+add_subdirectory(skeletal_animation)

+ 6 - 0
samples/common/Framework.cpp

@@ -79,6 +79,12 @@ Error SampleApp::userMainLoop(Bool& quit)
 			(renderer.getCurrentDebugRenderTarget() == "SM_resolve") ? "" : "SM_resolve");
 	}
 
+	if(in.getKey(KeyCode::P) == 1)
+	{
+		renderer.setCurrentDebugRenderTarget(
+			(renderer.getCurrentDebugRenderTarget() == "GBuffer_normals") ? "" : "GBuffer_normals");
+	}
+
 	if(!getDisplayDeveloperConsole())
 	{
 		in.hideCursor(true);

+ 3 - 0
samples/skeletal_animation/CMakeLists.txt

@@ -0,0 +1,3 @@
+add_executable(skeletal_animation_sample Main.cpp ../common/Framework.cpp)
+target_link_libraries(skeletal_animation_sample anki)
+installExecutable(skeletal_animation_sample)

+ 82 - 0
samples/skeletal_animation/Main.cpp

@@ -0,0 +1,82 @@
+// Copyright (C) 2009-2020, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include <cstdio>
+#include "../common/Framework.h"
+
+using namespace anki;
+
+class MyApp : public SampleApp
+{
+public:
+	AnimationResourcePtr m_floatAnim;
+	AnimationResourcePtr m_waveAnim;
+
+	Error sampleExtraInit() override
+	{
+		ScriptResourcePtr script;
+		ANKI_CHECK(getResourceManager().loadResource("assets/scene.lua", script));
+		ANKI_CHECK(getScriptManager().evalString(script->getSource()));
+
+		ANKI_CHECK(getResourceManager().loadResource("assets/float.001.ankianim", m_floatAnim));
+		ANKI_CHECK(getResourceManager().loadResource("assets/wave.001.ankianim", m_waveAnim));
+
+		AnimationPlayInfo animInfo;
+		animInfo.m_startTime = 2.0;
+		animInfo.m_repeatTimes = -1.0;
+		getSceneGraph()
+			.findSceneNode("droid.001")
+			.getComponent<SkinComponent>()
+			.playAnimation(0, m_floatAnim, animInfo);
+
+		getMainRenderer().getOffscreenRenderer().getVolumetricFog().setFogParticleColor(Vec3(1.0f, 0.9f, 0.9f));
+		getMainRenderer().getOffscreenRenderer().getVolumetricFog().setParticleDensity(2.0f);
+		return Error::NONE;
+	}
+
+	Error userMainLoop(Bool& quit) override
+	{
+		if(getInput().getKey(KeyCode::H) == 1)
+		{
+			AnimationPlayInfo animInfo;
+			animInfo.m_startTime = 0.5;
+			animInfo.m_repeatTimes = 3.0;
+			animInfo.m_blendInTime = 0.5;
+			animInfo.m_blendOutTime = 0.35;
+			getSceneGraph()
+				.findSceneNode("droid.001")
+				.getComponent<SkinComponent>()
+				.playAnimation(1, m_waveAnim, animInfo);
+		}
+
+		return SampleApp::userMainLoop(quit);
+	}
+};
+
+int main(int argc, char* argv[])
+{
+	Error err = Error::NONE;
+
+	MyApp* app = new MyApp;
+	err = app->init(argc, argv, "skeletal_animation");
+	if(!err)
+	{
+		err = app->mainLoop();
+	}
+
+	if(err)
+	{
+		ANKI_LOGE("Error reported. To run %s you have to navigate to the /path/to/anki/samples/sponza. "
+				  "And then execute it",
+			argv[0]);
+	}
+	else
+	{
+		delete app;
+		ANKI_LOGI("Bye!!");
+	}
+
+	return 0;
+}

+ 15 - 0
samples/skeletal_animation/assets/Armature.002.ankiskel

@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<skeleton>
+	<bones>
+		<bone name="Body" boneTransform="-0.000000 0.000000 -1.000000 -0.000000 -0.000000 -1.000000 -0.000000 3.786524 -1.000000 0.000000 0.000000 -0.000000 0.000000 0.000000 -0.000000 1.000000 " transform="-0.000000 -0.000000 -1.000000 -0.036258 -0.000000 -1.000000 0.000000 3.786523 -1.000000 0.000000 0.000000 0.070229 0.000000 0.000000 0.000000 1.000000 " />
+		<bone name="ArmL1" parent="Body" boneTransform="-0.765200 -0.643793 -0.000001 1.614215 0.643792 -0.765200 0.000000 3.557550 -0.000001 -0.000000 1.000000 -0.000001 -0.000000 0.000000 -0.000000 1.000000 " transform="0.035125 -0.054510 -0.997895 0.000005 0.860321 0.509748 0.002438 0.025070 0.508542 -0.858595 0.064801 1.055127 0.000000 0.000000 0.000000 1.000000 " />
+		<bone name="ArmL2" parent="ArmL1" boneTransform="-0.766045 -0.642788 -0.000001 0.672452 0.642788 -0.766045 0.000000 3.558436 -0.000001 -0.000000 1.000000 -0.000001 -0.000000 0.000000 -0.000000 1.000000 " transform="0.953409 -0.299885 0.032885 0.937085 0.298847 0.953730 0.033020 0.000005 -0.041265 -0.021654 0.998914 0.000000 0.000000 0.000000 0.000000 1.000000 " />
+		<bone name="PinceR" parent="ArmL2" boneTransform="-0.766044 -0.642788 -0.000001 -0.305710 0.642788 -0.766044 0.000000 3.692573 -0.000001 -0.000000 1.000000 -0.000000 -0.000000 0.000000 -0.000000 1.000000 " transform="1.000000 -0.000000 -0.000000 0.978164 0.000000 1.000000 0.000000 -0.134138 0.000000 -0.000000 1.000000 0.000001 0.000000 0.000000 0.000000 1.000000 " />
+		<bone name="Flap3" parent="Body" boneTransform="0.000000 -1.000000 -0.000000 3.306338 -1.000000 -0.000000 0.000003 -0.000001 -0.000003 0.000000 -1.000000 0.831319 -0.000000 0.000000 -0.000000 1.000000 " transform="0.000000 -0.000003 1.000000 -0.831320 1.000000 0.000000 -0.000000 0.480187 -0.000000 1.000000 0.000002 -0.000000 0.000000 0.000000 0.000000 1.000000 " />
+		<bone name="Flap2" parent="Body" boneTransform="-0.000000 -1.000000 0.000000 3.306337 -0.500003 -0.000000 -0.866024 0.000003 0.866024 -0.000000 -0.500002 -0.831319 0.000000 0.000000 -0.000000 1.000000 " transform="0.217251 0.894560 0.390595 0.415657 0.866976 0.007022 -0.498300 0.480190 -0.448502 0.446893 -0.774037 -0.719946 0.000000 0.000000 0.000000 1.000000 " />
+		<bone name="Flap1" parent="Body" boneTransform="0.000000 -1.000000 -0.000000 3.306337 0.866025 0.000000 0.500001 0.831319 -0.500001 -0.000000 0.866025 0.000000 -0.000000 0.000000 -0.000000 1.000000 " transform="-0.000000 -0.500001 -0.866025 0.415664 1.000000 -0.000000 0.000000 0.480183 -0.000000 -0.866025 0.500001 0.719943 0.000000 0.000000 0.000000 1.000000 " />
+		<bone name="ArmR1" parent="Body" boneTransform="0.765200 -0.643793 -0.000000 1.614215 0.643793 0.765200 0.000001 -3.557551 -0.000000 -0.000001 1.000000 0.000002 -0.000000 0.000000 -0.000000 1.000000 " transform="0.000000 -0.000001 -1.000000 -0.000005 0.908864 -0.417093 0.000001 0.025063 -0.417093 -0.908864 0.000000 -1.055128 0.000000 0.000000 0.000000 1.000000 " />
+		<bone name="ArmR2" parent="ArmR1" boneTransform="0.766045 -0.642788 -0.000000 0.672452 0.642788 0.766045 0.000001 -3.558437 -0.000000 -0.000001 1.000000 0.000002 -0.000000 0.000000 -0.000000 1.000000 " transform="0.928770 0.031489 0.369317 0.937089 0.034001 0.984946 -0.169486 -0.000003 -0.369094 0.169970 0.913718 0.000000 0.000000 0.000000 0.000000 1.000000 " />
+		<bone name="PinceL" parent="ArmR2" boneTransform="0.766045 -0.642788 0.000000 -0.305712 0.642788 0.766045 0.000000 -3.692573 -0.000000 0.000000 1.000000 0.000000 -0.000000 0.000000 -0.000000 1.000000 " transform="1.000000 0.000000 -0.000001 0.978164 -0.000000 1.000000 0.000000 0.134136 0.000001 -0.000000 1.000000 0.000000 0.000000 0.000000 0.000000 1.000000 " />
+	</bones>
+</skeleton>

BIN
samples/skeletal_animation/assets/Drone_diff.ankitex


BIN
samples/skeletal_animation/assets/Drone_normal.ankitex


BIN
samples/skeletal_animation/assets/Drone_roughness.ankitex


BIN
samples/skeletal_animation/assets/Mesh.ankimesh


+ 9 - 0
samples/skeletal_animation/assets/Mesh_Robot.001.ankimdl

@@ -0,0 +1,9 @@
+<model>
+	<modelPatches>
+		<modelPatch>
+			<mesh>assets/Mesh.ankimesh</mesh>
+			<material>assets/Robot.001.ankimtl</material>
+		</modelPatch>
+	</modelPatches>
+	<skeleton>assets/Armature.002.ankiskel</skeleton>
+</model>

+ 26 - 0
samples/skeletal_animation/assets/Robot.001.ankimtl

@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!-- This file is auto generated by ImporterMaterial.cpp -->
+<material shaderProgram="shaders/GBufferGeneric.ankiprog">
+	<mutation>
+		<mutator name="DIFFUSE_TEX" value="1"/>
+		<mutator name="SPECULAR_TEX" value="0"/>
+		<mutator name="ROUGHNESS_TEX" value="1"/>
+		<mutator name="METAL_TEX" value="0"/>
+		<mutator name="NORMAL_TEX" value="1"/>
+		<mutator name="PARALLAX" value="0"/>
+		<mutator name="EMISSIVE_TEX" value="0"/>
+	</mutation>
+
+	<inputs>
+		
+
+		<input shaderVar="u_diffTex" value="assets/Drone_diff.ankitex"/>
+		<input shaderVar="m_specColor" value="0.040000 0.040000 0.040000"/>
+		<input shaderVar="u_roughnessTex" value="assets/Drone_roughness.ankitex"/>
+		<input shaderVar="m_metallic" value="1.000000"/>
+		<input shaderVar="u_normalTex" value="assets/Drone_normal.ankitex"/>
+		<input shaderVar="m_emission" value="0.000000 0.000000 0.000000"/>
+		<input shaderVar="m_subsurface" value="0.000000"/>
+		
+	</inputs>
+</material>

+ 671 - 0
samples/skeletal_animation/assets/float.001.ankianim

@@ -0,0 +1,671 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<animation>
+	<channels>
+		<channel name="ArmL2">
+			<positionKeys>
+				<key time="0.000000">0.937085 0.000005 0.000000</key>
+				<key time="2.600000">0.937085 0.000005 0.000000</key>
+			</positionKeys>
+			<rotationKeys>
+				<key time="0.000000">-0.013832 0.018759 0.151472 0.988187</key>
+				<key time="0.033333">-0.013830 0.018784 0.151703 0.988151</key>
+				<key time="0.066667">-0.013824 0.018860 0.152386 0.988044</key>
+				<key time="0.100000">-0.013814 0.018989 0.153509 0.987868</key>
+				<key time="0.133333">-0.013801 0.019171 0.155059 0.987623</key>
+				<key time="0.166667">-0.013786 0.019408 0.157023 0.987308</key>
+				<key time="0.200000">-0.013769 0.019702 0.159388 0.986923</key>
+				<key time="0.233333">-0.013749 0.020055 0.162140 0.986468</key>
+				<key time="0.266667">-0.013729 0.020466 0.165268 0.985941</key>
+				<key time="0.300000">-0.013708 0.020939 0.168757 0.985340</key>
+				<key time="0.333333">-0.013686 0.021473 0.172594 0.984664</key>
+				<key time="0.366667">-0.013665 0.022072 0.176767 0.983910</key>
+				<key time="0.400000">-0.013644 0.022735 0.181263 0.983077</key>
+				<key time="0.433333">-0.013624 0.023465 0.186068 0.982162</key>
+				<key time="0.466667">-0.013606 0.024263 0.191169 0.981163</key>
+				<key time="0.500000">-0.013589 0.025129 0.196553 0.980077</key>
+				<key time="0.533333">-0.013574 0.026067 0.202207 0.978902</key>
+				<key time="0.566667">-0.013562 0.027077 0.208119 0.977635</key>
+				<key time="0.600000">-0.013554 0.028160 0.214275 0.976273</key>
+				<key time="0.633333">-0.013548 0.029318 0.220663 0.974815</key>
+				<key time="0.666667">-0.013547 0.030553 0.227270 0.973258</key>
+				<key time="0.700000">-0.013548 0.031864 0.234078 0.971601</key>
+				<key time="0.733333">-0.013552 0.033240 0.241044 0.969850</key>
+				<key time="0.766667">-0.013559 0.034669 0.248120 0.968014</key>
+				<key time="0.800000">-0.013572 0.036138 0.255259 0.966102</key>
+				<key time="0.833333">-0.013592 0.037636 0.262412 0.964126</key>
+				<key time="0.866667">-0.013622 0.039148 0.269533 0.962099</key>
+				<key time="0.900000">-0.013663 0.040664 0.276573 0.960035</key>
+				<key time="0.933333">-0.013717 0.042170 0.283486 0.957951</key>
+				<key time="0.966667">-0.013786 0.043654 0.290224 0.955863</key>
+				<key time="1.000000">-0.013873 0.045103 0.296742 0.953791</key>
+				<key time="1.033333">-0.013978 0.046506 0.302994 0.951755</key>
+				<key time="1.066667">-0.014105 0.047849 0.308933 0.949775</key>
+				<key time="1.100000">-0.014254 0.049122 0.314517 0.947873</key>
+				<key time="1.133333">-0.014429 0.050311 0.319698 0.946073</key>
+				<key time="1.166667">-0.014630 0.051405 0.324435 0.944397</key>
+				<key time="1.200000">-0.014861 0.052392 0.328681 0.942869</key>
+				<key time="1.233333">-0.015122 0.053260 0.332395 0.941514</key>
+				<key time="1.266667">-0.015417 0.053997 0.335532 0.940354</key>
+				<key time="1.300000">-0.015747 0.054591 0.338049 0.939412</key>
+				<key time="1.333333">-0.016114 0.055030 0.339901 0.938712</key>
+				<key time="1.366667">-0.016520 0.055302 0.341044 0.938274</key>
+				<key time="1.400000">-0.016967 0.055396 0.341433 0.938119</key>
+				<key time="1.433333">-0.018001 0.054127 0.336195 0.940063</key>
+				<key time="1.466667">-0.019997 0.050669 0.321898 0.945206</key>
+				<key time="1.500000">-0.022702 0.045535 0.300620 0.952386</key>
+				<key time="1.533333">-0.025857 0.039249 0.274489 0.960441</key>
+				<key time="1.566667">-0.029199 0.032360 0.245755 0.968351</key>
+				<key time="1.600000">-0.032466 0.025442 0.216789 0.975347</key>
+				<key time="1.633333">-0.035407 0.019074 0.190037 0.980953</key>
+				<key time="1.666667">-0.037782 0.013833 0.167957 0.984973</key>
+				<key time="1.700000">-0.039369 0.010284 0.152969 0.987393</key>
+				<key time="1.733333">-0.039947 0.008978 0.147448 0.988222</key>
+				<key time="1.766667">-0.039834 0.009020 0.147466 0.988223</key>
+				<key time="1.800000">-0.039507 0.009143 0.147517 0.988228</key>
+				<key time="1.833333">-0.038985 0.009339 0.147599 0.988235</key>
+				<key time="1.866667">-0.038284 0.009602 0.147708 0.988243</key>
+				<key time="1.900000">-0.037422 0.009924 0.147843 0.988253</key>
+				<key time="1.933333">-0.036418 0.010301 0.147999 0.988263</key>
+				<key time="1.966667">-0.035289 0.010724 0.148175 0.988273</key>
+				<key time="2.000000">-0.034053 0.011187 0.148367 0.988283</key>
+				<key time="2.033333">-0.032728 0.011684 0.148573 0.988291</key>
+				<key time="2.066667">-0.031332 0.012207 0.148789 0.988297</key>
+				<key time="2.266667">-0.022452 0.015533 0.150158 0.988285</key>
+				<key time="2.300000">-0.021055 0.016056 0.150371 0.988275</key>
+				<key time="2.333333">-0.019729 0.016552 0.150574 0.988263</key>
+				<key time="2.366667">-0.018493 0.017015 0.150763 0.988251</key>
+				<key time="2.400000">-0.017363 0.017438 0.150935 0.988237</key>
+				<key time="2.433333">-0.016359 0.017814 0.151088 0.988224</key>
+				<key time="2.466667">-0.015497 0.018136 0.151219 0.988212</key>
+				<key time="2.500000">-0.014795 0.018399 0.151326 0.988202</key>
+				<key time="2.533333">-0.014272 0.018595 0.151405 0.988194</key>
+				<key time="2.566667">-0.013945 0.018717 0.151455 0.988189</key>
+				<key time="2.600000">-0.013832 0.018759 0.151472 0.988187</key>
+			</rotationKeys>
+		</channel>
+		<channel name="Body">
+			<positionKeys>
+				<key time="0.000000">-0.036258 3.786523 0.070229</key>
+				<key time="0.033333">-0.036258 3.790392 0.070229</key>
+				<key time="0.066667">-0.036258 3.801466 0.070229</key>
+				<key time="0.100000">-0.036258 3.818945 0.070229</key>
+				<key time="0.133333">-0.036258 3.842028 0.070229</key>
+				<key time="0.166667">-0.036258 3.869913 0.070229</key>
+				<key time="0.200000">-0.036258 3.901802 0.070229</key>
+				<key time="0.233333">-0.036258 3.936893 0.070229</key>
+				<key time="0.433333">-0.036258 4.169853 0.070229</key>
+				<key time="0.466667">-0.036258 4.204944 0.070229</key>
+				<key time="0.500000">-0.036258 4.236833 0.070229</key>
+				<key time="0.533333">-0.036258 4.264719 0.070229</key>
+				<key time="0.566667">-0.036258 4.287801 0.070229</key>
+				<key time="0.600000">-0.036258 4.305280 0.070229</key>
+				<key time="0.633333">-0.036258 4.316354 0.070229</key>
+				<key time="0.666667">-0.036258 4.320224 0.070229</key>
+				<key time="0.700000">-0.036258 4.317838 0.070229</key>
+				<key time="0.733333">-0.036258 4.311364 0.070229</key>
+				<key time="0.766667">-0.036258 4.301822 0.070229</key>
+				<key time="0.900000">-0.036258 4.253434 0.070229</key>
+				<key time="0.933333">-0.036258 4.243893 0.070229</key>
+				<key time="0.966667">-0.036258 4.237418 0.070229</key>
+				<key time="1.000000">-0.036258 4.235033 0.070229</key>
+				<key time="1.033333">-0.036258 4.239528 0.070229</key>
+				<key time="1.066667">-0.036258 4.251730 0.070229</key>
+				<key time="1.100000">-0.036258 4.269712 0.070229</key>
+				<key time="1.233333">-0.036258 4.360908 0.070229</key>
+				<key time="1.266667">-0.036258 4.378891 0.070229</key>
+				<key time="1.300000">-0.036258 4.391092 0.070229</key>
+				<key time="1.333333">-0.036258 4.395588 0.070229</key>
+				<key time="1.366667">-0.036258 4.393478 0.070229</key>
+				<key time="1.400000">-0.036258 4.387750 0.070229</key>
+				<key time="1.600000">-0.036258 4.328062 0.070229</key>
+				<key time="1.633333">-0.036258 4.322334 0.070229</key>
+				<key time="1.666667">-0.036258 4.320224 0.070229</key>
+				<key time="2.233333">-0.036258 4.320224 0.070229</key>
+				<key time="2.266667">-0.036258 4.307793 0.070229</key>
+				<key time="2.300000">-0.036258 4.273710 0.070229</key>
+				<key time="2.333333">-0.036258 4.222786 0.070229</key>
+				<key time="2.366667">-0.036258 4.159832 0.070229</key>
+				<key time="2.400000">-0.036258 4.089662 0.070229</key>
+				<key time="2.433333">-0.036258 4.017085 0.070229</key>
+				<key time="2.466667">-0.036258 3.946914 0.070229</key>
+				<key time="2.500000">-0.036258 3.883960 0.070229</key>
+				<key time="2.533333">-0.036258 3.833036 0.070229</key>
+				<key time="2.566667">-0.036258 3.798953 0.070229</key>
+				<key time="2.600000">-0.036258 3.786523 0.070229</key>
+			</positionKeys>
+			<rotationKeys>
+				<key time="0.000000">-0.707107 0.000000 0.707107 0.000000</key>
+				<key time="0.033333">-0.707078 0.000380 0.707136 -0.000028</key>
+				<key time="0.066667">-0.706989 0.001470 0.707223 -0.000110</key>
+				<key time="0.100000">-0.706837 0.003194 0.707369 -0.000242</key>
+				<key time="0.133333">-0.706619 0.005473 0.707573 -0.000419</key>
+				<key time="0.166667">-0.706331 0.008233 0.707834 -0.000639</key>
+				<key time="0.200000">-0.705969 0.011395 0.708150 -0.000897</key>
+				<key time="0.233333">-0.705531 0.014884 0.708522 -0.001190</key>
+				<key time="0.266667">-0.705013 0.018623 0.708949 -0.001513</key>
+				<key time="0.300000">-0.704412 0.022534 0.709431 -0.001863</key>
+				<key time="0.333333">-0.703728 0.026541 0.709970 -0.002236</key>
+				<key time="0.366667">-0.702959 0.030567 0.710568 -0.002628</key>
+				<key time="0.400000">-0.702107 0.034535 0.711227 -0.003036</key>
+				<key time="0.433333">-0.701171 0.038367 0.711952 -0.003454</key>
+				<key time="0.466667">-0.700153 0.041988 0.712747 -0.003880</key>
+				<key time="0.500000">-0.699056 0.045319 0.713616 -0.004310</key>
+				<key time="0.533333">-0.697884 0.048286 0.714566 -0.004740</key>
+				<key time="0.566667">-0.696638 0.050810 0.715603 -0.005165</key>
+				<key time="0.600000">-0.695323 0.052816 0.716732 -0.005583</key>
+				<key time="0.633333">-0.693943 0.054227 0.717960 -0.005989</key>
+				<key time="0.666667">-0.692499 0.054967 0.719294 -0.006379</key>
+				<key time="0.700000">-0.691008 0.054550 0.720747 -0.007486</key>
+				<key time="0.733333">-0.689480 0.052760 0.722314 -0.009831</key>
+				<key time="0.766667">0.687905 -0.049929 -0.723963 0.013094</key>
+				<key time="0.800000">0.686276 -0.046393 -0.725662 0.016957</key>
+				<key time="0.833333">0.684588 -0.042482 -0.727385 0.021102</key>
+				<key time="0.866667">0.682844 -0.038532 -0.729112 0.025206</key>
+				<key time="0.900000">0.681053 -0.034877 -0.730830 0.028952</key>
+				<key time="0.933333">0.679232 -0.031851 -0.732533 0.032022</key>
+				<key time="0.966667">0.677401 -0.029788 -0.734220 0.034094</key>
+				<key time="1.000000">0.675579 -0.029025 -0.735891 0.034855</key>
+				<key time="1.033333">0.673788 -0.029562 -0.737538 0.034265</key>
+				<key time="1.066667">0.672051 -0.031030 -0.739133 0.032663</key>
+				<key time="1.100000">0.670394 -0.033211 -0.740642 0.030289</key>
+				<key time="1.133333">0.668844 -0.035885 -0.742031 0.027381</key>
+				<key time="1.166667">0.667428 -0.038832 -0.743268 0.024179</key>
+				<key time="1.200000">0.666179 -0.041834 -0.744323 0.020923</key>
+				<key time="1.233333">0.665132 -0.044672 -0.745175 0.017851</key>
+				<key time="1.266667">0.664326 -0.047125 -0.745801 0.015209</key>
+				<key time="1.300000">0.663802 -0.048978 -0.746186 0.013229</key>
+				<key time="1.333333">0.663604 -0.050010 -0.746312 0.012157</key>
+				<key time="1.366667">0.663986 -0.051196 -0.745888 0.012365</key>
+				<key time="1.400000">0.665057 -0.053474 -0.744747 0.013856</key>
+				<key time="1.433333">0.666635 -0.056553 -0.743057 0.016321</key>
+				<key time="1.466667">0.668539 -0.060148 -0.740986 0.019456</key>
+				<key time="1.500000">0.670591 -0.063971 -0.738707 0.022950</key>
+				<key time="1.533333">0.672621 -0.067736 -0.736404 0.026499</key>
+				<key time="1.566667">0.674466 -0.071158 -0.734265 0.029792</key>
+				<key time="1.600000">0.675970 -0.073954 -0.732487 0.032523</key>
+				<key time="1.633333">0.676983 -0.075838 -0.731273 0.034386</key>
+				<key time="1.666667">0.677355 -0.076529 -0.730824 0.035075</key>
+				<key time="1.700000">0.677419 -0.075173 -0.730968 0.033721</key>
+				<key time="1.733333">0.677577 -0.071584 -0.731338 0.030161</key>
+				<key time="1.766667">0.677772 -0.066484 -0.731828 0.025146</key>
+				<key time="1.800000">0.677954 -0.060595 -0.732345 0.019432</key>
+				<key time="1.833333">0.678092 -0.054646 -0.732813 0.013772</key>
+				<key time="1.866667">-0.678178 0.049361 0.733183 -0.008925</key>
+				<key time="1.900000">-0.678224 0.045471 0.733426 -0.005645</key>
+				<key time="1.933333">-0.678246 0.043705 0.733519 -0.004688</key>
+				<key time="1.966667">-0.678364 0.042377 0.733476 -0.006263</key>
+				<key time="2.000000">-0.678677 0.039603 0.733304 -0.009710</key>
+				<key time="2.033333">0.679176 -0.035810 -0.732959 0.014477</key>
+				<key time="2.066667">0.679852 -0.031423 -0.732403 0.020020</key>
+				<key time="2.100000">0.680707 -0.026870 -0.731609 0.025787</key>
+				<key time="2.133333">0.681756 -0.022582 -0.730564 0.031225</key>
+				<key time="2.166667">0.683028 -0.018989 -0.729267 0.035790</key>
+				<key time="2.200000">0.684559 -0.016517 -0.727730 0.038930</key>
+				<key time="2.233333">0.686387 -0.015599 -0.725963 0.040098</key>
+				<key time="2.266667">0.688530 -0.015235 -0.723989 0.039165</key>
+				<key time="2.300000">0.690916 -0.014240 -0.721868 0.036607</key>
+				<key time="2.333333">0.693438 -0.012752 -0.719657 0.032783</key>
+				<key time="2.366667">0.695994 -0.010913 -0.717417 0.028054</key>
+				<key time="2.400000">0.698481 -0.008862 -0.715211 0.022781</key>
+				<key time="2.433333">0.700804 -0.006740 -0.713111 0.017326</key>
+				<key time="2.466667">0.702877 -0.004688 -0.711194 0.012052</key>
+				<key time="2.500000">-0.704618 0.002848 0.709543 -0.007322</key>
+				<key time="2.533333">-0.705952 0.001360 0.708250 -0.003495</key>
+				<key time="2.566667">-0.706805 0.000363 0.707408 -0.000934</key>
+				<key time="2.600000">-0.707107 0.000000 0.707107 0.000000</key>
+			</rotationKeys>
+		</channel>
+		<channel name="Flap3">
+			<positionKeys>
+				<key time="0.000000">-0.831320 0.480187 -0.000000</key>
+				<key time="2.600000">-0.831320 0.480187 -0.000000</key>
+			</positionKeys>
+			<rotationKeys>
+				<key time="0.000000">0.499999 0.499999 0.500000 0.500001</key>
+				<key time="2.600000">0.499999 0.499999 0.500000 0.500001</key>
+			</rotationKeys>
+		</channel>
+		<channel name="PinceR">
+			<positionKeys>
+				<key time="0.000000">0.978164 -0.134138 0.000001</key>
+				<key time="2.600000">0.978164 -0.134138 0.000001</key>
+			</positionKeys>
+		</channel>
+		<channel name="ArmL1">
+			<positionKeys>
+				<key time="0.000000">0.000005 0.025070 1.055127</key>
+				<key time="2.600000">0.000005 0.025070 1.055127</key>
+			</positionKeys>
+			<rotationKeys>
+				<key time="0.000000">-0.339329 -0.593679 0.360530 0.634365</key>
+				<key time="0.033333">-0.339699 -0.593297 0.361051 0.634229</key>
+				<key time="0.066667">-0.340751 -0.592206 0.362530 0.633840</key>
+				<key time="0.100000">-0.342398 -0.590489 0.364847 0.633224</key>
+				<key time="0.133333">-0.344551 -0.588229 0.367877 0.632406</key>
+				<key time="0.166667">-0.347121 -0.585507 0.371496 0.631410</key>
+				<key time="0.200000">-0.350018 -0.582405 0.375582 0.630261</key>
+				<key time="0.233333">-0.353155 -0.579008 0.380010 0.628986</key>
+				<key time="0.266667">-0.356445 -0.575401 0.384659 0.627613</key>
+				<key time="0.333333">-0.363139 -0.567918 0.394137 0.624704</key>
+				<key time="0.366667">-0.366377 -0.564229 0.398730 0.623240</key>
+				<key time="0.400000">-0.369433 -0.560703 0.403071 0.621824</key>
+				<key time="0.433333">-0.372229 -0.557440 0.407047 0.620499</key>
+				<key time="0.466667">-0.374688 -0.554542 0.410546 0.619309</key>
+				<key time="0.500000">-0.376732 -0.552110 0.413459 0.618303</key>
+				<key time="0.533333">-0.378287 -0.550247 0.415676 0.617528</key>
+				<key time="0.566667">-0.379277 -0.549056 0.417088 0.617029</key>
+				<key time="0.600000">-0.379624 -0.548637 0.417583 0.616853</key>
+				<key time="0.633333">-0.378890 -0.549562 0.416360 0.617308</key>
+				<key time="0.666667">-0.376803 -0.552177 0.412883 0.618586</key>
+				<key time="0.700000">-0.373527 -0.556233 0.407433 0.620549</key>
+				<key time="0.733333">-0.369224 -0.561473 0.400285 0.623052</key>
+				<key time="0.766667">-0.364053 -0.567641 0.391717 0.625945</key>
+				<key time="0.800000">-0.358178 -0.574480 0.382008 0.629087</key>
+				<key time="0.833333">-0.351772 -0.581745 0.371449 0.632341</key>
+				<key time="0.866667">-0.345011 -0.589198 0.360335 0.635588</key>
+				<key time="0.900000">-0.338083 -0.596619 0.348973 0.638720</key>
+				<key time="0.933333">-0.331183 -0.603804 0.337675 0.641649</key>
+				<key time="0.966667">-0.324513 -0.610565 0.326762 0.644304</key>
+				<key time="1.000000">-0.318278 -0.616733 0.316556 0.646630</key>
+				<key time="1.033333">-0.312638 -0.622198 0.307309 0.648605</key>
+				<key time="1.066667">-0.307599 -0.626993 0.299035 0.650263</key>
+				<key time="1.100000">-0.303141 -0.631169 0.291701 0.651645</key>
+				<key time="1.133333">-0.299244 -0.634776 0.285272 0.652788</key>
+				<key time="1.166667">-0.295882 -0.637858 0.279708 0.653723</key>
+				<key time="1.200000">-0.293030 -0.640456 0.274968 0.654478</key>
+				<key time="1.233333">-0.290660 -0.642608 0.271009 0.655077</key>
+				<key time="1.266667">-0.288742 -0.644349 0.267786 0.655541</key>
+				<key time="1.300000">-0.287244 -0.645711 0.265254 0.655888</key>
+				<key time="1.333333">-0.286137 -0.646723 0.263367 0.656135</key>
+				<key time="1.366667">-0.285387 -0.647414 0.262078 0.656296</key>
+				<key time="1.400000">-0.284962 -0.647810 0.261340 0.656385</key>
+				<key time="1.433333">-0.284827 -0.647937 0.261104 0.656412</key>
+				<key time="1.466667">-0.288164 -0.644777 0.266456 0.655915</key>
+				<key time="1.500000">-0.296907 -0.636279 0.280528 0.654446</key>
+				<key time="1.533333">-0.309121 -0.623855 0.300302 0.651973</key>
+				<key time="1.600000">-0.336335 -0.593683 0.344894 0.644568</key>
+				<key time="1.633333">-0.347802 -0.579853 0.363923 0.640597</key>
+				<key time="1.666667">-0.355748 -0.569849 0.377197 0.637525</key>
+				<key time="1.700000">-0.358724 -0.566009 0.382188 0.636304</key>
+				<key time="1.733333">-0.358029 -0.566741 0.381110 0.636691</key>
+				<key time="1.766667">-0.356067 -0.568798 0.378071 0.637768</key>
+				<key time="1.800000">-0.353018 -0.571965 0.373360 0.639403</key>
+				<key time="1.833333">-0.349059 -0.576024 0.367266 0.641459</key>
+				<key time="1.866667">-0.344368 -0.580756 0.360075 0.643800</key>
+				<key time="1.900000">-0.339130 -0.585946 0.352084 0.646294</key>
+				<key time="1.933333">-0.333531 -0.591386 0.343592 0.648817</key>
+				<key time="1.966667">-0.327771 -0.596877 0.334910 0.651260</key>
+				<key time="2.000000">-0.322052 -0.602233 0.326354 0.653522</key>
+				<key time="2.033333">-0.316584 -0.607280 0.318248 0.655518</key>
+				<key time="2.066667">-0.311582 -0.611855 0.310918 0.657177</key>
+				<key time="2.100000">-0.307263 -0.615805 0.304694 0.658434</key>
+				<key time="2.133333">-0.303848 -0.618985 0.299908 0.659234</key>
+				<key time="2.166667">-0.301554 -0.621252 0.296888 0.659522</key>
+				<key time="2.200000">-0.300599 -0.622458 0.295962 0.659237</key>
+				<key time="2.233333">-0.301201 -0.622458 0.297445 0.658294</key>
+				<key time="2.266667">-0.303243 -0.621343 0.301136 0.656730</key>
+				<key time="2.300000">-0.306431 -0.619302 0.306581 0.654655</key>
+				<key time="2.333333">-0.310470 -0.616524 0.313323 0.652177</key>
+				<key time="2.366667">-0.315064 -0.613203 0.320907 0.649411</key>
+				<key time="2.400000">-0.319926 -0.609543 0.328882 0.646483</key>
+				<key time="2.433333">-0.324770 -0.605766 0.336802 0.643535</key>
+				<key time="2.466667">-0.329322 -0.602104 0.344229 0.640722</key>
+				<key time="2.500000">-0.333313 -0.598806 0.350735 0.638215</key>
+				<key time="2.533333">-0.336482 -0.596129 0.355898 0.636197</key>
+				<key time="2.566667">-0.338574 -0.594334 0.359302 0.634852</key>
+				<key time="2.600000">-0.339329 -0.593679 0.360530 0.634365</key>
+			</rotationKeys>
+		</channel>
+		<channel name="ArmR2">
+			<positionKeys>
+				<key time="0.000000">0.937089 -0.000003 0.000000</key>
+				<key time="2.600000">0.937089 -0.000003 0.000000</key>
+			</positionKeys>
+			<rotationKeys>
+				<key time="0.000000">0.086756 0.188718 0.000642 0.978191</key>
+				<key time="0.033333">0.086561 0.189061 -0.000947 0.978142</key>
+				<key time="0.066667">0.086006 0.190032 -0.005471 0.977988</key>
+				<key time="0.100000">0.085131 0.191549 -0.012570 0.977703</key>
+				<key time="0.133333">0.083975 0.193524 -0.021883 0.977250</key>
+				<key time="0.166667">0.082580 0.195868 -0.033047 0.976588</key>
+				<key time="0.200000">0.080985 0.198493 -0.045696 0.975681</key>
+				<key time="0.233333">0.079232 0.201313 -0.059459 0.974505</key>
+				<key time="0.266667">0.077367 0.204243 -0.073958 0.973052</key>
+				<key time="0.300000">0.075436 0.207201 -0.088815 0.971334</key>
+				<key time="0.333333">0.073487 0.210110 -0.103651 0.969386</key>
+				<key time="0.366667">0.071570 0.212897 -0.118090 0.967268</key>
+				<key time="0.400000">0.069737 0.215497 -0.131757 0.965058</key>
+				<key time="0.433333">0.068042 0.217847 -0.144288 0.962857</key>
+				<key time="0.466667">0.066536 0.219889 -0.155323 0.960779</key>
+				<key time="0.500000">0.065274 0.221569 -0.164511 0.958949</key>
+				<key time="0.533333">0.064308 0.222837 -0.171502 0.957494</key>
+				<key time="0.566667">0.063690 0.223638 -0.175954 0.956541</key>
+				<key time="0.600000">0.063473 0.223918 -0.177516 0.956201</key>
+				<key time="0.633333">0.063527 0.223573 -0.176918 0.956389</key>
+				<key time="0.666667">0.063681 0.222581 -0.175198 0.956927</key>
+				<key time="0.700000">0.063925 0.221003 -0.172467 0.957772</key>
+				<key time="0.733333">0.064249 0.218902 -0.168833 0.958879</key>
+				<key time="0.766667">0.064641 0.216340 -0.164407 0.960203</key>
+				<key time="0.800000">0.065091 0.213377 -0.159296 0.961695</key>
+				<key time="0.833333">0.065588 0.210077 -0.153613 0.963311</key>
+				<key time="0.866667">0.066122 0.206503 -0.147467 0.965007</key>
+				<key time="0.900000">0.066681 0.202717 -0.140971 0.966740</key>
+				<key time="0.933333">0.067256 0.198786 -0.134238 0.968474</key>
+				<key time="0.966667">0.067836 0.194775 -0.127381 0.970173</key>
+				<key time="1.000000">0.068412 0.190751 -0.120516 0.971807</key>
+				<key time="1.033333">0.068973 0.186782 -0.113758 0.973352</key>
+				<key time="1.066667">0.069511 0.182936 -0.107223 0.974785</key>
+				<key time="1.100000">0.070018 0.179283 -0.101025 0.976089</key>
+				<key time="1.133333">0.070483 0.175891 -0.095282 0.977249</key>
+				<key time="1.166667">0.070899 0.172831 -0.090108 0.978255</key>
+				<key time="1.200000">0.071258 0.170172 -0.085618 0.979098</key>
+				<key time="1.233333">0.071551 0.167984 -0.081928 0.979770</key>
+				<key time="1.266667">0.071771 0.166336 -0.079150 0.980264</key>
+				<key time="1.300000">0.071909 0.165297 -0.077400 0.980569</key>
+				<key time="1.333333">0.071956 0.164935 -0.076791 0.980674</key>
+				<key time="1.366667">0.071402 0.167795 -0.081790 0.979825</key>
+				<key time="1.400000">0.069902 0.175443 -0.095196 0.977380</key>
+				<key time="1.433333">0.067695 0.186466 -0.114607 0.973403</key>
+				<key time="1.466667">0.065028 0.199433 -0.137588 0.968022</key>
+				<key time="1.500000">0.062167 0.212929 -0.161677 0.961591</key>
+				<key time="1.533333">0.059404 0.225587 -0.184433 0.954760</key>
+				<key time="1.566667">0.057044 0.236114 -0.203486 0.948467</key>
+				<key time="1.600000">0.055401 0.243297 -0.216553 0.943844</key>
+				<key time="1.633333">0.054785 0.245958 -0.221408 0.942063</key>
+				<key time="1.666667">0.054785 0.245866 -0.221395 0.942090</key>
+				<key time="1.700000">0.054789 0.245592 -0.221323 0.942178</key>
+				<key time="1.733333">0.054804 0.245138 -0.221145 0.942337</key>
+				<key time="1.766667">0.054837 0.244506 -0.220812 0.942577</key>
+				<key time="1.800000">0.054893 0.243700 -0.220275 0.942908</key>
+				<key time="1.833333">0.054979 0.242721 -0.219485 0.943340</key>
+				<key time="1.866667">0.055102 0.241572 -0.218395 0.943881</key>
+				<key time="1.900000">0.055269 0.240253 -0.216955 0.944539</key>
+				<key time="1.933333">0.055485 0.238769 -0.215116 0.945323</key>
+				<key time="1.966667">0.055756 0.237118 -0.212829 0.946240</key>
+				<key time="2.000000">0.056090 0.235304 -0.210043 0.947295</key>
+				<key time="2.033333">0.056493 0.233326 -0.206711 0.948493</key>
+				<key time="2.066667">0.056970 0.231185 -0.202779 0.949836</key>
+				<key time="2.100000">0.057528 0.228880 -0.198198 0.951326</key>
+				<key time="2.133333">0.058172 0.226411 -0.192917 0.952962</key>
+				<key time="2.166667">0.058909 0.223776 -0.186881 0.954740</key>
+				<key time="2.200000">0.059744 0.220975 -0.180040 0.956654</key>
+				<key time="2.233333">0.060682 0.218002 -0.172340 0.958693</key>
+				<key time="2.266667">0.061728 0.214857 -0.163727 0.960843</key>
+				<key time="2.300000">0.062888 0.211534 -0.154148 0.963087</key>
+				<key time="2.333333">0.064690 0.208124 -0.140922 0.965733</key>
+				<key time="2.366667">0.067444 0.204738 -0.122544 0.968771</key>
+				<key time="2.400000">0.070823 0.201416 -0.100748 0.971733</key>
+				<key time="2.433333">0.074502 0.198222 -0.077312 0.974259</key>
+				<key time="2.466667">0.078165 0.195258 -0.054056 0.976137</key>
+				<key time="2.500000">0.081508 0.192658 -0.032816 0.977324</key>
+				<key time="2.533333">0.084239 0.190584 -0.015419 0.977928</key>
+				<key time="2.566667">0.086080 0.189212 -0.003671 0.978149</key>
+				<key time="2.600000">0.086756 0.188718 0.000642 0.978191</key>
+			</rotationKeys>
+		</channel>
+		<channel name="Flap1">
+			<positionKeys>
+				<key time="0.000000">0.415664 0.480184 0.719943</key>
+				<key time="2.600000">0.415664 0.480184 0.719943</key>
+			</positionKeys>
+			<rotationKeys>
+				<key time="0.000000">-0.353553 -0.353553 0.612373 0.612373</key>
+				<key time="0.033333">-0.352875 -0.353801 0.612988 0.612005</key>
+				<key time="0.066667">-0.350932 -0.354510 0.614745 0.610948</key>
+				<key time="0.100000">-0.347859 -0.355623 0.617508 0.609270</key>
+				<key time="0.133333">-0.343789 -0.357082 0.621137 0.607034</key>
+				<key time="0.166667">-0.338854 -0.358827 0.625492 0.604302</key>
+				<key time="0.200000">-0.333188 -0.360799 0.630432 0.601138</key>
+				<key time="0.233333">-0.326925 -0.362939 0.635816 0.597607</key>
+				<key time="0.266667">-0.320203 -0.365192 0.641509 0.593777</key>
+				<key time="0.300000">-0.313161 -0.367503 0.647378 0.589724</key>
+				<key time="0.333333">-0.305942 -0.369821 0.653295 0.585523</key>
+				<key time="0.366667">-0.298691 -0.372097 0.659140 0.581259</key>
+				<key time="0.400000">-0.291554 -0.374289 0.664796 0.577019</key>
+				<key time="0.433333">-0.284680 -0.376353 0.670155 0.572895</key>
+				<key time="0.466667">-0.278218 -0.378253 0.675114 0.568983</key>
+				<key time="0.500000">-0.272319 -0.379953 0.679575 0.565381</key>
+				<key time="0.533333">-0.267133 -0.381421 0.683445 0.562193</key>
+				<key time="0.566667">-0.262812 -0.382625 0.686634 0.559519</key>
+				<key time="0.600000">-0.259507 -0.383535 0.689051 0.557464</key>
+				<key time="0.633333">-0.257369 -0.384118 0.690604 0.556130</key>
+				<key time="0.666667">-0.256549 -0.384341 0.691198 0.555617</key>
+				<key time="0.700000">-0.258179 -0.384281 0.685994 0.561324</key>
+				<key time="0.733333">-0.262733 -0.383946 0.671443 0.576820</key>
+				<key time="0.766667">-0.269320 -0.383105 0.649236 0.599325</key>
+				<key time="0.800000">-0.277019 -0.381537 0.621178 0.625962</key>
+				<key time="0.866667">-0.292399 -0.376096 0.556598 0.680627</key>
+				<key time="0.900000">-0.298768 -0.372715 0.525631 0.703941</key>
+				<key time="0.933333">-0.303686 -0.369565 0.499660 0.722175</key>
+				<key time="0.966667">-0.306901 -0.367301 0.481818 0.733998</key>
+				<key time="1.000000">-0.308188 -0.366571 0.475135 0.738168</key>
+				<key time="1.033333">-0.311538 -0.364605 0.480694 0.734126</key>
+				<key time="1.066667">-0.319888 -0.358959 0.495469 0.723416</key>
+				<key time="1.100000">-0.331321 -0.350649 0.516149 0.707716</key>
+				<key time="1.133333">-0.343937 -0.340791 0.539442 0.688891</key>
+				<key time="1.166667">-0.355957 -0.330692 0.562238 0.669198</key>
+				<key time="1.200000">-0.365804 -0.321857 0.581735 0.651290</key>
+				<key time="1.233333">-0.372106 -0.315917 0.595469 0.638083</key>
+				<key time="1.266667">-0.373594 -0.314531 0.601148 0.632549</key>
+				<key time="1.300000">-0.371240 -0.316634 0.600786 0.633230</key>
+				<key time="1.333333">-0.367090 -0.319916 0.598157 0.636481</key>
+				<key time="1.366667">-0.361428 -0.324127 0.593544 0.641885</key>
+				<key time="1.400000">-0.354537 -0.329007 0.587226 0.649017</key>
+				<key time="1.433333">-0.346705 -0.334297 0.579484 0.657450</key>
+				<key time="1.466667">-0.338229 -0.339741 0.570615 0.666765</key>
+				<key time="1.500000">-0.329419 -0.345096 0.560938 0.676565</key>
+				<key time="1.533333">-0.320602 -0.350128 0.550798 0.686474</key>
+				<key time="1.566667">-0.312116 -0.354620 0.540560 0.696149</key>
+				<key time="1.600000">-0.304310 -0.358374 0.530611 0.705276</key>
+				<key time="1.633333">-0.297412 -0.361311 0.521322 0.713599</key>
+				<key time="1.666667">-0.291382 -0.363537 0.512793 0.721097</key>
+				<key time="1.700000">-0.286169 -0.365135 0.505017 0.727834</key>
+				<key time="1.733333">-0.281718 -0.366187 0.497979 0.733866</key>
+				<key time="1.766667">-0.277974 -0.366769 0.491662 0.739243</key>
+				<key time="1.800000">-0.274878 -0.366954 0.486044 0.744008</key>
+				<key time="1.833333">-0.272371 -0.366813 0.481100 0.748201</key>
+				<key time="1.866667">-0.270394 -0.366414 0.476805 0.751854</key>
+				<key time="1.900000">-0.268885 -0.365824 0.473131 0.754997</key>
+				<key time="1.933333">-0.267783 -0.365109 0.470048 0.757656</key>
+				<key time="1.966667">-0.267025 -0.364332 0.467527 0.759854</key>
+				<key time="2.000000">-0.266548 -0.363557 0.465537 0.761613</key>
+				<key time="2.033333">-0.266290 -0.362846 0.464046 0.762951</key>
+				<key time="2.066667">-0.266185 -0.362262 0.463022 0.763887</key>
+				<key time="2.100000">-0.266169 -0.361866 0.462432 0.764436</key>
+				<key time="2.133333">-0.266179 -0.361721 0.462243 0.764616</key>
+				<key time="2.166667">-0.267555 -0.361741 0.464610 0.762690</key>
+				<key time="2.200000">-0.271398 -0.361775 0.471219 0.757242</key>
+				<key time="2.233333">-0.277268 -0.361764 0.481310 0.748725</key>
+				<key time="2.266667">-0.284712 -0.361640 0.494109 0.737572</key>
+				<key time="2.300000">-0.293275 -0.361339 0.508828 0.724237</key>
+				<key time="2.333333">-0.302503 -0.360814 0.524688 0.709231</key>
+				<key time="2.400000">-0.321218 -0.359064 0.556845 0.676621</key>
+				<key time="2.433333">-0.329905 -0.357914 0.571766 0.660412</key>
+				<key time="2.466667">-0.337668 -0.356690 0.585099 0.645300</key>
+				<key time="2.500000">-0.344195 -0.355508 0.596307 0.632109</key>
+				<key time="2.533333">-0.349202 -0.354501 0.604902 0.621675</key>
+				<key time="2.566667">-0.352415 -0.353808 0.610418 0.614828</key>
+				<key time="2.600000">-0.353553 -0.353553 0.612373 0.612373</key>
+			</rotationKeys>
+		</channel>
+		<channel name="PinceL">
+			<positionKeys>
+				<key time="0.000000">0.978164 0.134136 0.000000</key>
+				<key time="2.600000">0.978164 0.134136 0.000000</key>
+			</positionKeys>
+		</channel>
+		<channel name="Flap2">
+			<positionKeys>
+				<key time="0.000000">0.415657 0.480190 -0.719946</key>
+				<key time="2.600000">0.415657 0.480190 -0.719946</key>
+			</positionKeys>
+			<rotationKeys>
+				<key time="0.000000">0.704320 0.625261 -0.020554 0.335498</key>
+				<key time="0.033333">0.703828 0.626401 -0.019349 0.334476</key>
+				<key time="0.066667">0.702429 0.629571 -0.015945 0.331631</key>
+				<key time="0.100000">0.700222 0.634392 -0.010656 0.327294</key>
+				<key time="0.133333">0.697300 0.640475 -0.003800 0.321791</key>
+				<key time="0.166667">0.693760 0.647432 0.004306 0.315453</key>
+				<key time="0.200000">0.689714 0.654883 0.013338 0.308619</key>
+				<key time="0.233333">0.685293 0.662465 0.022972 0.301639</key>
+				<key time="0.266667">0.680655 0.669833 0.032881 0.294875</key>
+				<key time="0.300000">0.675978 0.676668 0.042737 0.288700</key>
+				<key time="0.333333">0.671465 0.682671 0.052218 0.283494</key>
+				<key time="0.366667">0.667333 0.687566 0.061003 0.279640</key>
+				<key time="0.400000">0.663808 0.691091 0.068777 0.277527</key>
+				<key time="0.433333">0.661119 0.692988 0.075230 0.277540</key>
+				<key time="0.466667">0.659485 0.692991 0.080053 0.280064</key>
+				<key time="0.500000">0.662161 0.685817 0.075581 0.292379</key>
+				<key time="0.533333">0.669472 0.669570 0.059414 0.316153</key>
+				<key time="0.566667">0.677425 0.649369 0.039236 0.343330</key>
+				<key time="0.600000">0.683060 0.631478 0.022913 0.366250</key>
+				<key time="0.633333">0.684526 0.623122 0.018292 0.377901</key>
+				<key time="0.666667">0.682485 0.625028 0.025574 0.378021</key>
+				<key time="0.700000">0.678881 0.631831 0.038474 0.372062</key>
+				<key time="0.733333">0.674007 0.641709 0.055024 0.361795</key>
+				<key time="0.766667">0.668236 0.652888 0.073230 0.349049</key>
+				<key time="0.800000">0.662126 0.663741 0.091081 0.335771</key>
+				<key time="0.833333">0.656450 0.672846 0.106591 0.324022</key>
+				<key time="0.866667">0.652147 0.678977 0.117820 0.315931</key>
+				<key time="0.900000">0.650215 0.680994 0.122873 0.313640</key>
+				<key time="0.933333">0.651054 0.678179 0.120011 0.319059</key>
+				<key time="0.966667">0.653887 0.671200 0.110086 0.331366</key>
+				<key time="1.000000">0.657821 0.660877 0.095076 0.348530</key>
+				<key time="1.033333">0.661998 0.648105 0.077006 0.368495</key>
+				<key time="1.066667">0.665722 0.633989 0.057976 0.389245</key>
+				<key time="1.100000">0.668539 0.619893 0.040145 0.408874</key>
+				<key time="1.133333">0.670247 0.607406 0.025668 0.425638</key>
+				<key time="1.166667">0.670834 0.598248 0.016665 0.437954</key>
+				<key time="1.200000">0.670335 0.594144 0.015217 0.444312</key>
+				<key time="1.233333">0.668895 0.594909 0.020856 0.445228</key>
+				<key time="1.266667">0.666714 0.598869 0.031199 0.442578</key>
+				<key time="1.300000">0.663787 0.605401 0.045459 0.436819</key>
+				<key time="1.333333">0.660087 0.613857 0.062833 0.428388</key>
+				<key time="1.366667">0.655605 0.623591 0.082498 0.417745</key>
+				<key time="1.400000">0.650380 0.633979 0.103602 0.405393</key>
+				<key time="1.433333">0.644526 0.644446 0.125273 0.391897</key>
+				<key time="1.466667">0.638241 0.654484 0.146635 0.377884</key>
+				<key time="1.500000">0.631810 0.663674 0.166829 0.364035</key>
+				<key time="1.533333">0.625590 0.671680 0.185028 0.351068</key>
+				<key time="1.566667">0.619993 0.678247 0.200452 0.339719</key>
+				<key time="1.600000">0.615463 0.683177 0.212358 0.330724</key>
+				<key time="1.633333">0.612444 0.686292 0.220032 0.324811</key>
+				<key time="1.666667">0.611354 0.687386 0.222754 0.322687</key>
+				<key time="1.700000">0.611783 0.687246 0.221875 0.322778</key>
+				<key time="1.733333">0.613026 0.686835 0.219321 0.323040</key>
+				<key time="1.766667">0.615011 0.686163 0.215213 0.323456</key>
+				<key time="1.800000">0.617666 0.685235 0.209671 0.324006</key>
+				<key time="1.833333">0.620913 0.684050 0.202812 0.324668</key>
+				<key time="1.866667">0.624673 0.682610 0.194755 0.325419</key>
+				<key time="1.900000">0.628866 0.680913 0.185619 0.326238</key>
+				<key time="1.933333">0.633413 0.678959 0.175523 0.327100</key>
+				<key time="1.966667">0.638233 0.676752 0.164594 0.327985</key>
+				<key time="2.000000">0.643250 0.674297 0.152958 0.328872</key>
+				<key time="2.033333">0.648387 0.671606 0.140747 0.329741</key>
+				<key time="2.066667">0.653574 0.668695 0.128095 0.330577</key>
+				<key time="2.100000">0.658741 0.665584 0.115139 0.331363</key>
+				<key time="2.133333">0.663827 0.662300 0.102021 0.332089</key>
+				<key time="2.166667">0.668774 0.658879 0.088881 0.332746</key>
+				<key time="2.200000">0.673531 0.655358 0.075862 0.333327</key>
+				<key time="2.233333">0.678053 0.651782 0.063107 0.333829</key>
+				<key time="2.266667">0.682299 0.648202 0.050759 0.334253</key>
+				<key time="2.300000">0.686238 0.644673 0.038958 0.334601</key>
+				<key time="2.333333">0.689841 0.641251 0.027841 0.334877</key>
+				<key time="2.366667">0.693085 0.637998 0.017546 0.335088</key>
+				<key time="2.400000">0.695952 0.634977 0.008205 0.335242</key>
+				<key time="2.433333">0.698425 0.632253 -0.000050 0.335350</key>
+				<key time="2.466667">0.700489 0.629888 -0.007090 0.335420</key>
+				<key time="2.500000">0.702128 0.627947 -0.012788 0.335462</key>
+				<key time="2.533333">0.703328 0.626491 -0.017016 0.335485</key>
+				<key time="2.566667">0.704067 0.625577 -0.019648 0.335495</key>
+				<key time="2.600000">0.704320 0.625261 -0.020554 0.335498</key>
+			</rotationKeys>
+		</channel>
+		<channel name="ArmR1">
+			<positionKeys>
+				<key time="0.000000">-0.000005 0.025063 -1.055128</key>
+				<key time="2.600000">-0.000005 0.025063 -1.055128</key>
+			</positionKeys>
+			<rotationKeys>
+				<key time="0.000000">-0.595209 -0.381741 0.595209 0.381742</key>
+				<key time="0.066667">-0.595166 -0.381799 0.595249 0.381688</key>
+				<key time="0.100000">-0.595065 -0.381937 0.595344 0.381560</key>
+				<key time="0.133333">-0.594868 -0.382204 0.595529 0.381310</key>
+				<key time="0.166667">-0.594543 -0.382645 0.595834 0.380898</key>
+				<key time="0.200000">-0.594058 -0.383303 0.596288 0.380282</key>
+				<key time="0.233333">-0.593379 -0.384220 0.596921 0.379422</key>
+				<key time="0.266667">-0.592474 -0.385439 0.597761 0.378277</key>
+				<key time="0.300000">-0.591308 -0.387002 0.598835 0.376803</key>
+				<key time="0.333333">-0.589845 -0.388950 0.600172 0.374959</key>
+				<key time="0.366667">-0.588050 -0.391324 0.601795 0.372700</key>
+				<key time="0.400000">-0.585883 -0.394163 0.603730 0.369982</key>
+				<key time="0.433333">-0.583304 -0.397506 0.605997 0.366760</key>
+				<key time="0.466667">-0.580272 -0.401389 0.608616 0.362986</key>
+				<key time="0.500000">-0.576742 -0.405845 0.611602 0.358611</key>
+				<key time="0.533333">-0.572665 -0.410906 0.614970 0.353587</key>
+				<key time="0.566667">-0.567993 -0.416600 0.618725 0.347861</key>
+				<key time="0.600000">-0.562672 -0.422951 0.622873 0.341383</key>
+				<key time="0.633333">-0.556603 -0.430251 0.627339 0.333950</key>
+				<key time="0.666667">-0.549964 -0.438378 0.631872 0.325735</key>
+				<key time="0.700000">-0.543134 -0.446746 0.636218 0.317254</key>
+				<key time="0.733333">-0.536522 -0.454789 0.640157 0.309046</key>
+				<key time="0.766667">-0.530569 -0.461958 0.643498 0.301667</key>
+				<key time="0.800000">-0.525733 -0.467723 0.646073 0.295685</key>
+				<key time="0.833333">-0.522489 -0.471565 0.647733 0.291676</key>
+				<key time="0.866667">-0.521305 -0.472963 0.648323 0.290213</key>
+				<key time="0.900000">-0.523004 -0.471505 0.648030 0.290185</key>
+				<key time="0.933333">-0.527773 -0.467387 0.647186 0.290095</key>
+				<key time="0.966667">-0.535112 -0.460976 0.645829 0.289934</key>
+				<key time="1.000000">-0.544508 -0.452635 0.643984 0.289684</key>
+				<key time="1.033333">-0.555446 -0.442732 0.641682 0.289330</key>
+				<key time="1.066667">-0.567415 -0.431650 0.638966 0.288863</key>
+				<key time="1.100000">-0.579914 -0.419791 0.635900 0.288282</key>
+				<key time="1.133333">-0.592461 -0.407581 0.632577 0.287600</key>
+				<key time="1.166667">-0.604598 -0.395466 0.629119 0.286841</key>
+				<key time="1.200000">-0.615894 -0.383908 0.625674 0.286044</key>
+				<key time="1.233333">-0.625949 -0.373382 0.622416 0.285256</key>
+				<key time="1.266667">-0.634389 -0.364367 0.619538 0.284537</key>
+				<key time="1.300000">-0.640857 -0.357342 0.617239 0.283947</key>
+				<key time="1.333333">-0.645004 -0.352784 0.615721 0.283552</key>
+				<key time="1.366667">-0.646470 -0.351162 0.615176 0.283409</key>
+				<key time="1.400000">-0.644114 -0.355087 0.616832 0.280266</key>
+				<key time="1.433333">-0.637791 -0.365392 0.621103 0.271925</key>
+				<key time="1.466667">-0.628582 -0.379837 0.626902 0.260005</key>
+				<key time="1.500000">-0.617650 -0.396195 0.633194 0.246176</key>
+				<key time="1.533333">-0.606337 -0.412309 0.639096 0.232194</key>
+				<key time="1.566667">-0.596173 -0.426142 0.643916 0.219893</key>
+				<key time="1.600000">-0.588827 -0.435790 0.647138 0.211145</key>
+				<key time="1.633333">-0.586008 -0.439419 0.648319 0.207817</key>
+				<key time="1.666667">-0.586671 -0.438757 0.648003 0.208333</key>
+				<key time="1.700000">-0.588529 -0.436867 0.647115 0.209818</key>
+				<key time="1.733333">-0.591382 -0.433893 0.645744 0.212177</key>
+				<key time="1.766667">-0.595026 -0.429975 0.643976 0.215315</key>
+				<key time="1.800000">-0.599259 -0.425252 0.641894 0.219137</key>
+				<key time="1.833333">-0.603878 -0.419866 0.639587 0.223546</key>
+				<key time="1.866667">-0.608685 -0.413960 0.637143 0.228450</key>
+				<key time="1.900000">-0.613486 -0.407682 0.634657 0.233755</key>
+				<key time="1.933333">-0.618094 -0.401180 0.632230 0.239374</key>
+				<key time="1.966667">-0.622329 -0.394606 0.629968 0.245219</key>
+				<key time="2.000000">-0.626017 -0.388117 0.627983 0.251208</key>
+				<key time="2.033333">-0.628988 -0.381870 0.626392 0.257261</key>
+				<key time="2.066667">-0.631078 -0.376023 0.625316 0.263302</key>
+				<key time="2.100000">-0.632114 -0.371189 0.624329 0.269933</key>
+				<key time="2.133333">-0.632113 -0.367772 0.622960 0.277666</key>
+				<key time="2.166667">-0.631196 -0.365614 0.621256 0.286286</key>
+				<key time="2.200000">-0.629480 -0.364556 0.619266 0.295575</key>
+				<key time="2.233333">-0.627085 -0.364438 0.617036 0.305314</key>
+				<key time="2.266667">-0.624134 -0.365101 0.614617 0.315284</key>
+				<key time="2.300000">-0.620755 -0.366387 0.612066 0.325268</key>
+				<key time="2.333333">-0.617082 -0.368142 0.609444 0.335051</key>
+				<key time="2.366667">-0.613258 -0.370215 0.606818 0.344423</key>
+				<key time="2.400000">-0.609430 -0.372459 0.604265 0.353176</key>
+				<key time="2.433333">-0.605752 -0.374732 0.601864 0.361111</key>
+				<key time="2.466667">-0.602381 -0.376894 0.599701 0.368030</key>
+				<key time="2.500000">-0.599480 -0.378811 0.597865 0.373742</key>
+				<key time="2.533333">-0.597211 -0.380347 0.596445 0.378058</key>
+				<key time="2.566667">-0.595735 -0.381370 0.595531 0.380788</key>
+				<key time="2.600000">-0.595209 -0.381741 0.595209 0.381742</key>
+			</rotationKeys>
+		</channel>
+	</channels>
+</animation>

BIN
samples/skeletal_animation/assets/room.001.ankimesh


+ 8 - 0
samples/skeletal_animation/assets/room.001_room.red.ankimdl

@@ -0,0 +1,8 @@
+<model>
+	<modelPatches>
+		<modelPatch>
+			<mesh>assets/room.001.ankimesh</mesh>
+			<material>assets/room.red.ankimtl</material>
+		</modelPatch>
+	</modelPatches>
+</model>

BIN
samples/skeletal_animation/assets/room.002.ankimesh


+ 8 - 0
samples/skeletal_animation/assets/room.002_room.green.ankimdl

@@ -0,0 +1,8 @@
+<model>
+	<modelPatches>
+		<modelPatch>
+			<mesh>assets/room.002.ankimesh</mesh>
+			<material>assets/room.green.ankimtl</material>
+		</modelPatch>
+	</modelPatches>
+</model>

BIN
samples/skeletal_animation/assets/room.003.ankimesh


+ 8 - 0
samples/skeletal_animation/assets/room.003_room.blue.ankimdl

@@ -0,0 +1,8 @@
+<model>
+	<modelPatches>
+		<modelPatch>
+			<mesh>assets/room.003.ankimesh</mesh>
+			<material>assets/room.blue.ankimtl</material>
+		</modelPatch>
+	</modelPatches>
+</model>

BIN
samples/skeletal_animation/assets/room.ankimesh


+ 26 - 0
samples/skeletal_animation/assets/room.ankimtl

@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!-- This file is auto generated by ImporterMaterial.cpp -->
+<material shaderProgram="shaders/GBufferGeneric.ankiprog">
+	<mutation>
+		<mutator name="DIFFUSE_TEX" value="0"/>
+		<mutator name="SPECULAR_TEX" value="0"/>
+		<mutator name="ROUGHNESS_TEX" value="0"/>
+		<mutator name="METAL_TEX" value="0"/>
+		<mutator name="NORMAL_TEX" value="0"/>
+		<mutator name="PARALLAX" value="0"/>
+		<mutator name="EMISSIVE_TEX" value="0"/>
+	</mutation>
+
+	<inputs>
+		
+
+		<input shaderVar="m_diffColor" value="0.613641 0.750176 0.800000"/>
+		<input shaderVar="m_specColor" value="0.040000 0.040000 0.040000"/>
+		<input shaderVar="m_roughness" value="0.500000"/>
+		<input shaderVar="m_metallic" value="0.000000"/>
+		
+		<input shaderVar="m_emission" value="0.000000 0.000000 0.000000"/>
+		<input shaderVar="m_subsurface" value="0.000000"/>
+		
+	</inputs>
+</material>

+ 26 - 0
samples/skeletal_animation/assets/room.blue.ankimtl

@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!-- This file is auto generated by ImporterMaterial.cpp -->
+<material shaderProgram="shaders/GBufferGeneric.ankiprog">
+	<mutation>
+		<mutator name="DIFFUSE_TEX" value="0"/>
+		<mutator name="SPECULAR_TEX" value="0"/>
+		<mutator name="ROUGHNESS_TEX" value="0"/>
+		<mutator name="METAL_TEX" value="0"/>
+		<mutator name="NORMAL_TEX" value="0"/>
+		<mutator name="PARALLAX" value="0"/>
+		<mutator name="EMISSIVE_TEX" value="0"/>
+	</mutation>
+
+	<inputs>
+		
+
+		<input shaderVar="m_diffColor" value="0.000000 0.000000 1.000000"/>
+		<input shaderVar="m_specColor" value="0.040000 0.040000 0.040000"/>
+		<input shaderVar="m_roughness" value="0.500000"/>
+		<input shaderVar="m_metallic" value="0.000000"/>
+		
+		<input shaderVar="m_emission" value="0.000000 0.000000 0.000000"/>
+		<input shaderVar="m_subsurface" value="0.000000"/>
+		
+	</inputs>
+</material>

+ 26 - 0
samples/skeletal_animation/assets/room.green.ankimtl

@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!-- This file is auto generated by ImporterMaterial.cpp -->
+<material shaderProgram="shaders/GBufferGeneric.ankiprog">
+	<mutation>
+		<mutator name="DIFFUSE_TEX" value="0"/>
+		<mutator name="SPECULAR_TEX" value="0"/>
+		<mutator name="ROUGHNESS_TEX" value="0"/>
+		<mutator name="METAL_TEX" value="0"/>
+		<mutator name="NORMAL_TEX" value="0"/>
+		<mutator name="PARALLAX" value="0"/>
+		<mutator name="EMISSIVE_TEX" value="0"/>
+	</mutation>
+
+	<inputs>
+		
+
+		<input shaderVar="m_diffColor" value="0.000000 1.000000 0.000000"/>
+		<input shaderVar="m_specColor" value="0.040000 0.040000 0.040000"/>
+		<input shaderVar="m_roughness" value="0.500000"/>
+		<input shaderVar="m_metallic" value="0.000000"/>
+		
+		<input shaderVar="m_emission" value="0.000000 0.000000 0.000000"/>
+		<input shaderVar="m_subsurface" value="0.000000"/>
+		
+	</inputs>
+</material>

+ 26 - 0
samples/skeletal_animation/assets/room.red.ankimtl

@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!-- This file is auto generated by ImporterMaterial.cpp -->
+<material shaderProgram="shaders/GBufferGeneric.ankiprog">
+	<mutation>
+		<mutator name="DIFFUSE_TEX" value="0"/>
+		<mutator name="SPECULAR_TEX" value="0"/>
+		<mutator name="ROUGHNESS_TEX" value="0"/>
+		<mutator name="METAL_TEX" value="0"/>
+		<mutator name="NORMAL_TEX" value="0"/>
+		<mutator name="PARALLAX" value="0"/>
+		<mutator name="EMISSIVE_TEX" value="0"/>
+	</mutation>
+
+	<inputs>
+		
+
+		<input shaderVar="m_diffColor" value="1.000000 0.000000 0.000000"/>
+		<input shaderVar="m_specColor" value="0.040000 0.040000 0.040000"/>
+		<input shaderVar="m_roughness" value="0.500000"/>
+		<input shaderVar="m_metallic" value="0.000000"/>
+		
+		<input shaderVar="m_emission" value="0.000000 0.000000 0.000000"/>
+		<input shaderVar="m_subsurface" value="0.000000"/>
+		
+	</inputs>
+</material>

+ 8 - 0
samples/skeletal_animation/assets/room_room.ankimdl

@@ -0,0 +1,8 @@
+<model>
+	<modelPatches>
+		<modelPatch>
+			<mesh>assets/room.ankimesh</mesh>
+			<material>assets/room.ankimtl</material>
+		</modelPatch>
+	</modelPatches>
+</model>

+ 73 - 0
samples/skeletal_animation/assets/scene.lua

@@ -0,0 +1,73 @@
+-- Generated by: /home/godlike/src/anki/vkbuilddbg/bin/gltf_importer droid.gltf /home/godlike/src/anki/samples/skeletal_animation/assets/ -rpath assets -texrpath assets
+local scene = getSceneGraph()
+local events = getEventManager()
+
+node = scene:newModelNode("droid.001", "assets/Mesh_Robot.001.ankimdl")
+trf = Transform.new()
+trf:setOrigin(Vec4.new(0.000000, 0.000000, 0.000000, 0))
+rot = Mat3x4.new()
+rot:setAll(1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000)
+trf:setRotation(rot)
+trf:setScale(1.000000)
+node:getSceneNodeBase():getMoveComponent():setLocalTransform(trf)
+
+node = scene:newModelNode("room", "assets/room_room.ankimdl")
+trf = Transform.new()
+trf:setOrigin(Vec4.new(0.000000, 11.142166, 0.000000, 0))
+rot = Mat3x4.new()
+rot:setAll(1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000)
+trf:setRotation(rot)
+trf:setScale(9.000000)
+node:getSceneNodeBase():getMoveComponent():setLocalTransform(trf)
+
+node = scene:newPointLightNode("Lamp_Orientation")
+lcomp = node:getSceneNodeBase():getLightComponent()
+lcomp:setDiffuseColor(Vec4.new(100.000000, 100.000000, 100.000000, 1))
+lcomp:setShadowEnabled(1)
+lcomp:setRadius(100.000000)
+trf = Transform.new()
+trf:setOrigin(Vec4.new(4.076245, 5.903862, -1.005454, 0))
+rot = Mat3x4.new()
+rot:setAll(-0.290865, -0.771101, 0.566393, 0.000000, -0.055189, 0.604525, 0.794672, 0.000000, -0.955171, 0.199883, -0.218391, 0.000000)
+trf:setRotation(rot)
+trf:setScale(1.000000)
+node:getSceneNodeBase():getMoveComponent():setLocalTransform(trf)
+
+node = scene:newPerspectiveCameraNode("Camera_Orientation")
+scene:setActiveCameraNode(node:getSceneNodeBase())
+frustumc = node:getSceneNodeBase():getFrustumComponent()
+frustumc:setPerspective(0.100000, 100.000000, getMainRenderer():getAspectRatio() * 0.750416, 0.750416)
+trf = Transform.new()
+trf:setOrigin(Vec4.new(5.526846, 8.527484, -6.015655, 0))
+rot = Mat3x4.new()
+rot:setAll(-0.712312, -0.312519, 0.628445, 0.000000, 0.000000, 0.895396, 0.445271, 0.000000, -0.701863, 0.317172, -0.637801, 0.000000)
+trf:setRotation(rot)
+trf:setScale(1.000000)
+node:getSceneNodeBase():getMoveComponent():setLocalTransform(trf)
+
+node = scene:newModelNode("room.001", "assets/room.001_room.red.ankimdl")
+trf = Transform.new()
+trf:setOrigin(Vec4.new(0.000000, 11.142166, 0.000000, 0))
+rot = Mat3x4.new()
+rot:setAll(1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000)
+trf:setRotation(rot)
+trf:setScale(9.000000)
+node:getSceneNodeBase():getMoveComponent():setLocalTransform(trf)
+
+node = scene:newModelNode("room.002", "assets/room.002_room.green.ankimdl")
+trf = Transform.new()
+trf:setOrigin(Vec4.new(0.000000, 11.142166, 0.000000, 0))
+rot = Mat3x4.new()
+rot:setAll(1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000)
+trf:setRotation(rot)
+trf:setScale(9.000000)
+node:getSceneNodeBase():getMoveComponent():setLocalTransform(trf)
+
+node = scene:newModelNode("room.003", "assets/room.003_room.blue.ankimdl")
+trf = Transform.new()
+trf:setOrigin(Vec4.new(0.000000, 11.142166, 0.000000, 0))
+rot = Mat3x4.new()
+rot:setAll(1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000)
+trf:setRotation(rot)
+trf:setScale(9.000000)
+node:getSceneNodeBase():getMoveComponent():setLocalTransform(trf)

+ 80 - 0
samples/skeletal_animation/assets/wave.001.ankianim

@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<animation>
+	<channels>
+		<channel name="ArmR2">
+			<positionKeys>
+				<key time="0.000000">0.937092 -0.000000 0.000000</key>
+				<key time="0.966667">0.937092 -0.000000 0.000000</key>
+			</positionKeys>
+			<rotationKeys>
+				<key time="0.000000">-0.121547 -0.071642 -0.648956 0.747629</key>
+				<key time="0.033333">-0.117754 -0.072687 -0.646295 0.750435</key>
+				<key time="0.066667">-0.106957 -0.075641 -0.638628 0.758283</key>
+				<key time="0.100000">-0.089967 -0.080238 -0.626285 0.770217</key>
+				<key time="0.133333">-0.067584 -0.086199 -0.609509 0.785176</key>
+				<key time="0.166667">-0.040673 -0.093222 -0.588572 0.802021</key>
+				<key time="0.200000">-0.010221 -0.100983 -0.563879 0.819597</key>
+				<key time="0.233333">0.022647 -0.109138 -0.536040 0.836801</key>
+				<key time="0.266667">0.056684 -0.117341 -0.505910 0.852686</key>
+				<key time="0.300000">0.090586 -0.125264 -0.474573 0.866535</key>
+				<key time="0.333333">0.123068 -0.132619 -0.443288 0.877931</key>
+				<key time="0.366667">0.152939 -0.139176 -0.413404 0.886757</key>
+				<key time="0.400000">0.179156 -0.144763 -0.386273 0.893163</key>
+				<key time="0.433333">0.200832 -0.149260 -0.363187 0.897487</key>
+				<key time="0.466667">0.217205 -0.152582 -0.345346 0.900154</key>
+				<key time="0.500000">0.227575 -0.154651 -0.333863 0.901570</key>
+				<key time="0.533333">0.231211 -0.155371 -0.329801 0.902016</key>
+				<key time="0.566667">0.225752 -0.154289 -0.335892 0.901337</key>
+				<key time="0.600000">0.210352 -0.151200 -0.352856 0.899101</key>
+				<key time="0.633333">0.186346 -0.146267 -0.378682 0.894696</key>
+				<key time="0.666667">0.155097 -0.139642 -0.411202 0.887332</key>
+				<key time="0.700000">0.118212 -0.131535 -0.448044 0.876345</key>
+				<key time="0.733333">0.077646 -0.122269 -0.486692 0.861483</key>
+				<key time="0.766667">0.035671 -0.112306 -0.524669 0.843112</key>
+				<key time="0.800000">-0.005291 -0.102221 -0.559782 0.822293</key>
+				<key time="0.833333">-0.042882 -0.092651 -0.590322 0.800685</key>
+				<key time="0.866667">-0.074996 -0.084237 -0.615128 0.780319</key>
+				<key time="0.900000">-0.099837 -0.077575 -0.633497 0.763345</key>
+				<key time="0.933333">-0.115853 -0.073209 -0.644956 0.751831</key>
+				<key time="0.966667">-0.121547 -0.071642 -0.648956 0.747629</key>
+			</rotationKeys>
+		</channel>
+		<channel name="ArmR1">
+			<positionKeys>
+				<key time="0.000000">-0.000006 0.025063 -1.055128</key>
+				<key time="0.966667">-0.000006 0.025063 -1.055128</key>
+			</positionKeys>
+			<rotationKeys>
+				<key time="0.000000">-0.479496 0.575716 0.013285 0.662162</key>
+				<key time="0.033333">-0.477419 0.575843 0.014721 0.663520</key>
+				<key time="0.066667">-0.471519 0.576178 0.018786 0.667335</key>
+				<key time="0.100000">-0.462269 0.576623 0.025115 0.673188</key>
+				<key time="0.133333">-0.450123 0.577064 0.033342 0.680643</key>
+				<key time="0.166667">-0.435549 0.577385 0.043096 0.689251</key>
+				<key time="0.200000">-0.419038 0.577482 0.053994 0.698574</key>
+				<key time="0.233333">-0.401123 0.577280 0.065643 0.708195</key>
+				<key time="0.300000">-0.363399 0.575857 0.089605 0.726843</key>
+				<key time="0.333333">-0.344840 0.574683 0.101124 0.735255</key>
+				<key time="0.366667">-0.327350 0.573303 0.111823 0.742739</key>
+				<key time="0.400000">-0.311592 0.571838 0.121336 0.749126</key>
+				<key time="0.433333">-0.298223 0.570434 0.129315 0.754285</key>
+				<key time="0.466667">-0.287892 0.569250 0.135424 0.758111</key>
+				<key time="0.500000">-0.281236 0.568442 0.139335 0.760504</key>
+				<key time="0.533333">-0.278879 0.568148 0.140715 0.761337</key>
+				<key time="0.566667">-0.282413 0.568588 0.138645 0.760085</key>
+				<key time="0.600000">-0.292242 0.569759 0.132858 0.756517</key>
+				<key time="0.633333">-0.307193 0.571392 0.123970 0.750849</key>
+				<key time="0.666667">-0.326068 0.573191 0.112601 0.743272</key>
+				<key time="0.700000">-0.347642 0.574879 0.099396 0.734016</key>
+				<key time="0.733333">-0.370684 0.576233 0.085036 0.723407</key>
+				<key time="0.766667">-0.393978 0.577112 0.070240 0.711892</key>
+				<key time="0.800000">-0.416360 0.577472 0.055747 0.700045</key>
+				<key time="0.833333">-0.436745 0.577367 0.042300 0.688558</key>
+				<key time="0.866667">-0.454141 0.576936 0.030631 0.678206</key>
+				<key time="0.900000">-0.467639 0.576376 0.021448 0.669809</key>
+				<key time="0.933333">-0.476379 0.575905 0.015439 0.664197</key>
+				<key time="0.966667">-0.479496 0.575716 0.013285 0.662162</key>
+			</rotationKeys>
+		</channel>
+	</channels>
+</animation>

+ 6 - 18
shaders/GBufferGeneric.ankiprog

@@ -146,28 +146,16 @@ Vec4 g_tangent = in_tangent;
 #if ANKI_BONES
 void skinning()
 {
-	Vec3 position = Vec3(0.0);
-	Vec3 normal = Vec3(0.0);
-	Vec3 tangent = Vec3(0.0);
-	for(U32 i = 0; i < 4; ++i)
+	Mat4 skinMat = u_ankiBoneTransforms[in_boneIndices[0]] * in_boneWeights[0];
+	ANKI_UNROLL for(U32 i = 1; i < 4; ++i)
 	{
-		const U32 boneIdx = in_boneIndices[i];
-		if(boneIdx < 0xFFFF)
-		{
-			const F32 boneWeight = in_boneWeights[i];
-
-			position += (u_ankiBoneTransforms[boneIdx] * Vec4(g_position * boneWeight, 1.0)).xyz;
-#	if ANKI_PASS == PASS_GB
-			normal += (u_ankiBoneTransforms[boneIdx] * Vec4(g_normal * boneWeight, 0.0)).xyz;
-			tangent += (u_ankiBoneTransforms[boneIdx] * Vec4(g_tangent.xyz * boneWeight, 0.0)).xyz;
-#	endif
-		}
+		skinMat += u_ankiBoneTransforms[in_boneIndices[i]] * in_boneWeights[i];
 	}
 
-	g_position = position;
+	g_position = (skinMat * Vec4(g_position, 1.0)).xyz;
 #	if ANKI_PASS == PASS_GB
-	g_tangent.xyz = tangent;
-	g_normal = normal;
+	g_tangent.xyz = (skinMat * Vec4(g_tangent.xyz, 0.0)).xyz;
+	g_normal = (skinMat * Vec4(g_normal, 0.0)).xyz;
 #	endif
 }
 #endif

+ 11 - 14
shaders/SceneDebug.ankiprog

@@ -8,6 +8,12 @@
 
 ANKI_SPECIALIZATION_CONSTANT_U32(INSTANCE_COUNT, 0, 1);
 
+layout(set = 1, binding = 0, row_major) uniform u0_
+{
+	Mat4 u_mvp[INSTANCE_COUNT];
+	Vec4 u_color;
+};
+
 #pragma anki start vert
 #include <shaders/Common.glsl>
 
@@ -17,12 +23,6 @@ layout(location = 1) in Vec2 in_uv;
 layout(location = 0) out Vec2 out_uv;
 #endif
 
-layout(set = 1, binding = 0, row_major) uniform u0_
-{
-	Mat4 u_mvp[INSTANCE_COUNT];
-	Vec4 u_color;
-};
-
 out gl_PerVertex
 {
 	Vec4 gl_Position;
@@ -39,6 +39,7 @@ void main()
 
 #pragma anki start frag
 #include <shaders/Common.glsl>
+#include <shaders/ImportanceSampling.glsl>
 
 #if COLOR_TEXTURE == 1
 layout(location = 0) in Vec2 in_uv;
@@ -46,12 +47,6 @@ layout(set = 1, binding = 1) uniform sampler u_trilinearRepeatSampler;
 layout(set = 1, binding = 2) uniform texture2D u_tex;
 #endif
 
-layout(set = 1, binding = 0, row_major) uniform u0_
-{
-	Mat4 u_mvp[INSTANCE_COUNT];
-	Vec4 u_color;
-};
-
 // NOTE: Don't eliminate the binding because it confuses the descriptor set creation
 #if DITHERED_DEPTH_TEST == 1
 layout(set = 0, binding = 0) uniform sampler u_nearestAnyClampSampler;
@@ -64,11 +59,13 @@ void main()
 {
 	// Check if we should skip the frag
 #if DITHERED_DEPTH_TEST == 1
+	const UVec2 random = rand3DPCG16(UVec3(UVec2(gl_FragCoord.xy), 1)).xy;
+	const Vec2 noise = Vec2(random) / 65536.0;
+	const F32 factor = noise.x * noise.y;
 	const Vec2 uv = gl_FragCoord.xy / Vec2(textureSize(u_depthRt, 0));
 	const F32 depthRef = textureLod(u_depthRt, u_nearestAnyClampSampler, uv, 0.0).r;
 	const Bool depthTestFailed = gl_FragCoord.z >= depthRef;
-	const IVec2 fragCoordi = IVec2(gl_FragCoord.xy);
-	if(depthTestFailed && ((fragCoordi.x + fragCoordi.y) % 8) != 0)
+	if(depthTestFailed && factor < 0.5)
 	{
 		discard;
 	}

+ 189 - 34
src/anki/importer/GltfImporter.cpp

@@ -835,19 +835,96 @@ Error GltfImporter::writeModel(const cgltf_mesh& mesh, CString skinName)
 	return Error::NONE;
 }
 
+template<typename T>
+class GltfAnimKey
+{
+public:
+	Second m_time;
+	T m_value;
+};
+
+class GltfAnimChannel
+{
+public:
+	StringAuto m_name;
+	DynamicArrayAuto<GltfAnimKey<Vec3>> m_positions;
+	DynamicArrayAuto<GltfAnimKey<Quat>> m_rotations;
+	DynamicArrayAuto<GltfAnimKey<F32>> m_scales;
+
+	GltfAnimChannel(GenericMemoryPoolAllocator<U8> alloc)
+		: m_name(alloc)
+		, m_positions(alloc)
+		, m_rotations(alloc)
+		, m_scales(alloc)
+	{
+	}
+};
+
+/// Optimize out same animation keys.
+template<typename T, typename TZeroFunc, typename TLerpFunc>
+static void optimizeChannel(
+	DynamicArrayAuto<GltfAnimKey<T>>& arr, const T& identity, TZeroFunc isZeroFunc, TLerpFunc lerpFunc)
+{
+	if(arr.getSize() < 3)
+	{
+		return;
+	}
+
+	DynamicArrayAuto<GltfAnimKey<T>> newArr(arr.getAllocator());
+	newArr.emplaceBack(arr.getFront());
+	for(U32 i = 1; i < arr.getSize() - 1; ++i)
+	{
+		const GltfAnimKey<T>& left = arr[i - 1];
+		const GltfAnimKey<T>& middle = arr[i];
+		const GltfAnimKey<T>& right = arr[i + 1];
+
+		if(left.m_value == middle.m_value && middle.m_value == right.m_value)
+		{
+			// Skip it
+		}
+		else
+		{
+			const F32 factor = F32((middle.m_time - left.m_time) / (right.m_time - left.m_time));
+			ANKI_ASSERT(factor > 0.0f && factor < 1.0f);
+			const T lerpRez = lerpFunc(left.m_value, right.m_value, factor);
+			if(isZeroFunc(middle.m_value - lerpRez))
+			{
+				// It's redundant, skip it
+			}
+			else
+			{
+				newArr.emplaceBack(middle);
+			}
+		}
+	}
+	newArr.emplaceBack(arr.getBack());
+	ANKI_ASSERT(newArr.getSize() <= arr.getSize());
+
+	// Check if identity
+	if(newArr.getSize() == 2 && isZeroFunc(newArr[0].m_value - newArr[1].m_value)
+		&& isZeroFunc(newArr[0].m_value - identity))
+	{
+		newArr.destroy();
+	}
+
+	arr.destroy();
+	arr = std::move(newArr);
+}
+
 Error GltfImporter::writeAnimation(const cgltf_animation& anim)
 {
 	StringAuto fname(m_alloc);
 	fname.sprintf("%s%s.ankianim", m_outDir.cstr(), anim.name);
+	fname = fixFilename(fname);
 	ANKI_GLTF_LOGI("Importing animation %s", fname.cstr());
 
 	// Gather the channels
 	HashMapAuto<CString, Array<const cgltf_animation_channel*, 3>> channelMap(m_alloc);
-
+	U32 channelCount = 0;
 	for(U i = 0; i < anim.channels_count; ++i)
 	{
 		const cgltf_animation_channel& channel = anim.channels[i];
-		StringAuto channelName = getNodeName(*channel.target_node);
+		const StringAuto channelName = getNodeName(*channel.target_node);
 
 		U idx;
 		switch(channel.target_path)
@@ -863,6 +940,7 @@ Error GltfImporter::writeAnimation(const cgltf_animation& anim)
 			break;
 		default:
 			ANKI_ASSERT(0);
+			idx = 0;
 		}
 
 		auto it = channelMap.find(channelName.toCString());
@@ -875,26 +953,22 @@ Error GltfImporter::writeAnimation(const cgltf_animation& anim)
 			Array<const cgltf_animation_channel*, 3> arr = {};
 			arr[idx] = &channel;
 			channelMap.emplace(channelName.toCString(), arr);
+			++channelCount;
 		}
 	}
 
-	// Write file
-	File file;
-	ANKI_CHECK(file.open(fname.toCString(), FileOpenFlag::WRITE));
-
-	ANKI_CHECK(file.writeText("%s\n<animation>\n", XML_HEADER));
-	ANKI_CHECK(file.writeText("\t<channels>\n"));
-
+	// Gather the keys
+	DynamicArrayAuto<GltfAnimChannel> tempChannels(m_alloc, channelCount, m_alloc);
+	channelCount = 0;
 	for(auto it = channelMap.getBegin(); it != channelMap.getEnd(); ++it)
 	{
 		Array<const cgltf_animation_channel*, 3> arr = *it;
-		const cgltf_animation_channel& channel = (arr[0]) ? *arr[0] : ((arr[1]) ? *arr[1] : *arr[2]);
-		StringAuto channelName = getNodeName(*channel.target_node);
+		const cgltf_animation_channel& anyChannel = (arr[0]) ? *arr[0] : ((arr[1]) ? *arr[1] : *arr[2]);
+		const StringAuto channelName = getNodeName(*anyChannel.target_node);
 
-		ANKI_CHECK(file.writeText("\t\t<channel name=\"%s\">\n", channelName.cstr()));
+		tempChannels[channelCount].m_name = channelName;
 
 		// Positions
-		ANKI_CHECK(file.writeText("\t\t\t<positionKeys>\n"));
 		if(arr[0])
 		{
 			const cgltf_animation_channel& channel = *arr[0];
@@ -910,17 +984,15 @@ Error GltfImporter::writeAnimation(const cgltf_animation& anim)
 
 			for(U32 i = 0; i < keys.getSize(); ++i)
 			{
-				ANKI_CHECK(file.writeText("\t\t\t\t<key time=\"%f\">%f %f %f</key>\n",
-					keys[i],
-					positions[i].x(),
-					positions[i].y(),
-					positions[i].z()));
+				GltfAnimKey<Vec3> key;
+				key.m_time = keys[i];
+				key.m_value = Vec3(positions[i].x(), positions[i].y(), positions[i].z());
+
+				tempChannels[channelCount].m_positions.emplaceBack(key);
 			}
 		}
-		ANKI_CHECK(file.writeText("\t\t\t</positionKeys>\n"));
 
 		// Rotations
-		ANKI_CHECK(file.writeText("\t\t\t<rotationKeys>\n"));
 		if(arr[1])
 		{
 			const cgltf_animation_channel& channel = *arr[1];
@@ -936,18 +1008,15 @@ Error GltfImporter::writeAnimation(const cgltf_animation& anim)
 
 			for(U32 i = 0; i < keys.getSize(); ++i)
 			{
-				ANKI_CHECK(file.writeText("\t\t\t\t<key time=\"%f\">%f %f %f %f</key>\n",
-					keys[i],
-					rotations[i].x(),
-					rotations[i].y(),
-					rotations[i].z(),
-					rotations[i].w()));
+				GltfAnimKey<Quat> key;
+				key.m_time = keys[i];
+				key.m_value = Quat(rotations[i].x(), rotations[i].y(), rotations[i].z(), rotations[i].w());
+
+				tempChannels[channelCount].m_rotations.emplaceBack(key);
 			}
 		}
-		ANKI_CHECK(file.writeText("\t\t\t</rotationKeys>\n"));
 
 		// Scales
-		ANKI_CHECK(file.writeText("\t\t\t<scaleKeys>\n"));
 		if(arr[2])
 		{
 			const cgltf_animation_channel& channel = *arr[2];
@@ -971,10 +1040,92 @@ Error GltfImporter::writeAnimation(const cgltf_animation& anim)
 					return Error::USER_DATA;
 				}
 
-				ANKI_CHECK(file.writeText("\t\t\t\t<key time=\"%f\">%f</key>\n", keys[i], scales[i].x()));
+				GltfAnimKey<F32> key;
+				key.m_time = keys[i];
+				key.m_value = scales[i][0];
+
+				if(absolute(key.m_value - 1.0f) <= scaleEpsilon)
+				{
+					key.m_value = 1.0f;
+				}
+
+				tempChannels[channelCount].m_scales.emplaceBack(key);
+			}
+		}
+
+		++channelCount;
+	}
+
+	// Optimize animation
+	constexpr F32 KILL_EPSILON = 0.001f; // 1 millimiter
+	for(GltfAnimChannel& channel : tempChannels)
+	{
+		optimizeChannel(channel.m_positions,
+			Vec3(0.0f),
+			[&](const Vec3& a) -> Bool { return a.abs() < KILL_EPSILON; },
+			[&](const Vec3& a, const Vec3& b, F32 u) -> Vec3 { return linearInterpolate(a, b, u); });
+		optimizeChannel(channel.m_rotations,
+			Quat::getIdentity(),
+			[&](const Quat& a) -> Bool { return a.abs() < Quat(EPSILON * 20.0f); },
+			[&](const Quat& a, const Quat& b, F32 u) -> Quat { return a.slerp(b, u); });
+		optimizeChannel(channel.m_scales,
+			1.0f,
+			[&](const F32& a) -> Bool { return absolute(a) < KILL_EPSILON; },
+			[&](const F32& a, const F32& b, F32 u) -> F32 { return linearInterpolate(a, b, u); });
+	}
+
+	// Write file
+	File file;
+	ANKI_CHECK(file.open(fname.toCString(), FileOpenFlag::WRITE));
+
+	ANKI_CHECK(file.writeText("%s\n<animation>\n", XML_HEADER));
+	ANKI_CHECK(file.writeText("\t<channels>\n"));
+
+	for(const GltfAnimChannel& channel : tempChannels)
+	{
+		ANKI_CHECK(file.writeText("\t\t<channel name=\"%s\">\n", channel.m_name.cstr()));
+
+		// Positions
+		if(channel.m_positions.getSize())
+		{
+			ANKI_CHECK(file.writeText("\t\t\t<positionKeys>\n"));
+			for(const GltfAnimKey<Vec3>& key : channel.m_positions)
+			{
+				ANKI_CHECK(file.writeText("\t\t\t\t<key time=\"%f\">%f %f %f</key>\n",
+					key.m_time,
+					key.m_value.x(),
+					key.m_value.y(),
+					key.m_value.z()));
+			}
+			ANKI_CHECK(file.writeText("\t\t\t</positionKeys>\n"));
+		}
+
+		// Rotations
+		if(channel.m_rotations.getSize())
+		{
+			ANKI_CHECK(file.writeText("\t\t\t<rotationKeys>\n"));
+			for(const GltfAnimKey<Quat>& key : channel.m_rotations)
+			{
+				ANKI_CHECK(file.writeText("\t\t\t\t<key time=\"%f\">%f %f %f %f</key>\n",
+					key.m_time,
+					key.m_value.x(),
+					key.m_value.y(),
+					key.m_value.z(),
+					key.m_value.w()));
+			}
+			ANKI_CHECK(file.writeText("\t\t\t</rotationKeys>\n"));
+		}
+
+		// Scales
+		if(channel.m_scales.getSize())
+		{
+			ANKI_CHECK(file.writeText("\t\t\t<scaleKeys>\n"));
+			for(const GltfAnimKey<F32>& key : channel.m_scales)
+			{
+				ANKI_CHECK(file.writeText("\t\t\t\t<key time=\"%f\">%f</key>\n", key.m_time, key.m_value));
 			}
+			ANKI_CHECK(file.writeText("\t\t\t</scaleKeys>\n"));
 		}
-		ANKI_CHECK(file.writeText("\t\t\t</scaleKeys>\n"));
 
 		ANKI_CHECK(file.writeText("\t\t</channel>\n"));
 	}
@@ -1005,6 +1156,7 @@ Error GltfImporter::writeSkeleton(const cgltf_skin& skin)
 	ANKI_CHECK(file.open(fname.toCString(), FileOpenFlag::WRITE));
 
 	ANKI_CHECK(file.writeText("%s\n<skeleton>\n", XML_HEADER));
+	ANKI_CHECK(file.writeText("\t<bones>\n", XML_HEADER));
 
 	for(U32 i = 0; i < skin.joints_count; ++i)
 	{
@@ -1013,17 +1165,19 @@ Error GltfImporter::writeSkeleton(const cgltf_skin& skin)
 		StringAuto parent(m_alloc);
 
 		// Name & parent
-		ANKI_CHECK(file.writeText("\t<bone name=\"%s\" ", getNodeName(boneNode).cstr()));
-		if(boneNode.parent)
+		ANKI_CHECK(file.writeText("\t\t<bone name=\"%s\" ", getNodeName(boneNode).cstr()));
+		if(boneNode.parent && getNodeName(*boneNode.parent) != skin.name)
 		{
 			ANKI_CHECK(file.writeText("parent=\"%s\" ", getNodeName(*boneNode.parent).cstr()));
 		}
 
 		// Bone transform
 		ANKI_CHECK(file.writeText("boneTransform=\""));
+		Mat4 btrf(&boneMats[i][0]);
+		btrf.transpose();
 		for(U32 j = 0; j < 16; j++)
 		{
-			ANKI_CHECK(file.writeText("%f ", boneMats[i][j]));
+			ANKI_CHECK(file.writeText("%f ", btrf[j]));
 		}
 		ANKI_CHECK(file.writeText("\" "));
 
@@ -1031,7 +1185,7 @@ Error GltfImporter::writeSkeleton(const cgltf_skin& skin)
 		Transform trf;
 		ANKI_CHECK(getNodeTransform(boneNode, trf));
 		Mat4 mat{trf};
-		ANKI_CHECK(file.writeText("tansform=\""));
+		ANKI_CHECK(file.writeText("transform=\""));
 		for(U j = 0; j < 16; j++)
 		{
 			ANKI_CHECK(file.writeText("%f ", mat[j]));
@@ -1041,6 +1195,7 @@ Error GltfImporter::writeSkeleton(const cgltf_skin& skin)
 		ANKI_CHECK(file.writeText("/>\n"));
 	}
 
+	ANKI_CHECK(file.writeText("\t</bones>\n"));
 	ANKI_CHECK(file.writeText("</skeleton>\n"));
 
 	return Error::NONE;

+ 8 - 0
src/anki/importer/GltfImporter.h

@@ -98,6 +98,14 @@ private:
 		visitAccessor<T>(accessor, [&](const T& val) { out.emplaceBack(val); });
 	}
 
+	StringAuto fixFilename(CString in) const
+	{
+		StringAuto out(m_alloc, in);
+		out.replaceAll("|", "_");
+		out.replaceAll(" ", "_");
+		return out;
+	}
+
 	// Resources
 	ANKI_USE_RESULT Error writeMesh(const cgltf_mesh& mesh, CString nameOverride, F32 decimateFactor);
 	ANKI_USE_RESULT Error writeMaterial(const cgltf_material& mtl);

+ 10 - 10
src/anki/math/Quat.h

@@ -72,18 +72,18 @@ public:
 	{
 	}
 
-	explicit TQuat(const TVec<T, 4>& v)
+	TQuat(const Base& v)
 		: Base(v.x(), v.y(), v.z(), v.w())
 	{
 	}
 
 	explicit TQuat(const TMat<T, 3, 3>& m3)
 	{
-		const T trace = m3(0, 0) + m3(1, 1) + m3(2, 2) + 1.0;
+		const T trace = m3(0, 0) + m3(1, 1) + m3(2, 2) + T(1.0);
 		if(trace > EPSILON)
 		{
-			T s = 0.5 / sqrt<T>(trace);
-			w() = 0.25 / s;
+			T s = T(0.5) / sqrt<T>(trace);
+			w() = T(0.25) / s;
 			x() = (m3(2, 1) - m3(1, 2)) * s;
 			y() = (m3(0, 2) - m3(2, 0)) * s;
 			z() = (m3(1, 0) - m3(0, 1)) * s;
@@ -92,27 +92,27 @@ public:
 		{
 			if(m3(0, 0) > m3(1, 1) && m3(0, 0) > m3(2, 2))
 			{
-				T s = 0.5 / sqrt<T>(1.0 + m3(0, 0) - m3(1, 1) - m3(2, 2));
+				T s = T(0.5) / sqrt<T>(T(1.0) + m3(0, 0) - m3(1, 1) - m3(2, 2));
 				w() = (m3(1, 2) - m3(2, 1)) * s;
-				x() = 0.25 / s;
+				x() = T(0.25) / s;
 				y() = (m3(0, 1) + m3(1, 0)) * s;
 				z() = (m3(0, 2) + m3(2, 0)) * s;
 			}
 			else if(m3(1, 1) > m3(2, 2))
 			{
-				T s = 0.5 / sqrt<T>(1.0 + m3(1, 1) - m3(0, 0) - m3(2, 2));
+				T s = T(0.5) / sqrt<T>(T(1.0) + m3(1, 1) - m3(0, 0) - m3(2, 2));
 				w() = (m3(0, 2) - m3(2, 0)) * s;
 				x() = (m3(0, 1) + m3(1, 0)) * s;
-				y() = 0.25 / s;
+				y() = T(0.25) / s;
 				z() = (m3(1, 2) + m3(2, 1)) * s;
 			}
 			else
 			{
-				T s = 0.5 / sqrt<T>(1.0 + m3(2, 2) - m3(0, 0) - m3(1, 1));
+				T s = T(0.5) / sqrt<T>(T(1.0) + m3(2, 2) - m3(0, 0) - m3(1, 1));
 				w() = (m3(0, 1) - m3(1, 0)) * s;
 				x() = (m3(0, 2) + m3(2, 0)) * s;
 				y() = (m3(1, 2) + m3(2, 1)) * s;
-				z() = 0.25 / s;
+				z() = T(0.25) / s;
 			}
 		}
 	}

+ 12 - 2
src/anki/math/Transform.h

@@ -34,9 +34,19 @@ public:
 
 	explicit TTransform(const TMat<T, 4, 4>& m4)
 	{
-		m_rotation = TMat<T, 3, 4>(m4.getRotationPart());
+		const TVec<T, 3> s0 = m4.getColumn(0).xyz();
+		const TVec<T, 3> s1 = m4.getColumn(1).xyz();
+		const TVec<T, 3> s2 = m4.getColumn(2).xyz();
+
+		const TVec<T, 3> scales(s0.getLength(), s1.getLength(), s2.getLength());
+		const T E = T(0.001);
+		(void)E;
+		ANKI_ASSERT(
+			isZero(scales.x() - scales.y(), E) && isZero(scales.y() - scales.z(), E) && "Expecting uniform scale");
+
+		m_rotation.setColumns(s0 / scales.x(), s1 / scales.x(), s2 / scales.x(), TVec<T, 3>(0.0));
 		m_origin = m4.getTranslationPart().xyz0();
-		m_scale = 1.0;
+		m_scale = scales.x();
 		checkW();
 	}
 

+ 30 - 0
src/anki/math/Vec.h

@@ -2752,6 +2752,36 @@ public:
 		(*this) ^= TVec(f);
 		return *this;
 	}
+
+	Bool operator==(const T f) const
+	{
+		return *this == TVec(f);
+	}
+
+	Bool operator!=(const T f) const
+	{
+		return *this != TVec(f);
+	}
+
+	Bool operator<(const T f) const
+	{
+		return *this < TVec(f);
+	}
+
+	Bool operator<=(const T f) const
+	{
+		return *this <= TVec(f);
+	}
+
+	Bool operator>(const T f) const
+	{
+		return *this > TVec(f);
+	}
+
+	Bool operator>=(const T f) const
+	{
+		return *this >= TVec(f);
+	}
 	/// @}
 
 	/// @name Operators with other

+ 18 - 0
src/anki/renderer/GBuffer.h

@@ -21,6 +21,8 @@ public:
 	GBuffer(Renderer* r)
 		: RendererObject(r)
 	{
+		registerDebugRenderTarget("GBuffer_normals");
+		registerDebugRenderTarget("GBuffer_albedo");
 	}
 
 	~GBuffer();
@@ -40,6 +42,22 @@ public:
 		return m_depthRt;
 	}
 
+	void getDebugRenderTarget(CString rtName, RenderTargetHandle& handle) const override
+	{
+		if(rtName == "GBuffer_albedo")
+		{
+			handle = m_colorRts[0];
+		}
+		else if(rtName == "GBuffer_normals")
+		{
+			handle = m_colorRts[2];
+		}
+		else
+		{
+			ANKI_ASSERT(!"See file");
+		}
+	}
+
 private:
 	Array<RenderTargetDescription, GBUFFER_COLOR_ATTACHMENT_COUNT> m_colorRtDescrs;
 	RenderTargetDescription m_depthRtDescr;

+ 44 - 32
src/anki/resource/AnimationResource.cpp

@@ -27,7 +27,6 @@ AnimationResource::~AnimationResource()
 Error AnimationResource::load(const ResourceFilename& filename, Bool async)
 {
 	XmlElement el;
-	Second ftmp;
 
 	m_startTime = MAX_SECOND;
 	Second maxTime = MIN_SECOND;
@@ -66,9 +65,8 @@ Error AnimationResource::load(const ResourceFilename& filename, Bool async)
 		AnimationChannel& ch = m_channels[channelCount];
 
 		// <name>
-		ANKI_CHECK(chEl.getChildElement("name", el));
 		CString strtmp;
-		ANKI_CHECK(el.getText(strtmp));
+		ANKI_CHECK(chEl.getAttributeText("name", strtmp));
 		ch.m_name.create(getAllocator(), strtmp);
 
 		XmlElement keysEl, keyEl;
@@ -89,16 +87,13 @@ Error AnimationResource::load(const ResourceFilename& filename, Bool async)
 			{
 				AnimationKeyframe<Vec3>& key = ch.m_positions[count++];
 
-				// <time>
-				ANKI_CHECK(keyEl.getChildElement("time", el));
-				ANKI_CHECK(el.getNumber(ftmp));
-				key.m_time = ftmp;
-				m_startTime = std::min(m_startTime, key.m_time);
-				maxTime = std::max(maxTime, key.m_time);
+				// time
+				ANKI_CHECK(keyEl.getAttributeNumber("time", key.m_time));
+				m_startTime = min(m_startTime, key.m_time);
+				maxTime = max(maxTime, key.m_time);
 
-				// <value>
-				ANKI_CHECK(keyEl.getChildElement("value", el));
-				ANKI_CHECK(el.getNumbers(key.m_value));
+				// value
+				ANKI_CHECK(keyEl.getNumbers(key.m_value));
 
 				// Check ident
 				if(key.m_value == Vec3(0.0))
@@ -112,7 +107,7 @@ Error AnimationResource::load(const ResourceFilename& filename, Bool async)
 		}
 
 		// <rotationKeys>
-		ANKI_CHECK(chEl.getChildElement("rotationKeys", keysEl));
+		ANKI_CHECK(chEl.getChildElementOptional("rotationKeys", keysEl));
 		if(keysEl)
 		{
 			ANKI_CHECK(keysEl.getChildElement("key", keyEl));
@@ -127,18 +122,13 @@ Error AnimationResource::load(const ResourceFilename& filename, Bool async)
 			{
 				AnimationKeyframe<Quat>& key = ch.m_rotations[count++];
 
-				// <time>
-				ANKI_CHECK(keyEl.getChildElement("time", el));
-				ANKI_CHECK(el.getNumber(ftmp));
-				key.m_time = ftmp;
-				m_startTime = std::min(m_startTime, key.m_time);
-				maxTime = std::max(maxTime, key.m_time);
+				// time
+				ANKI_CHECK(keyEl.getAttributeNumber("time", key.m_time));
+				m_startTime = min(m_startTime, key.m_time);
+				maxTime = max(maxTime, key.m_time);
 
-				// <value>
-				Vec4 tmp2;
-				ANKI_CHECK(keyEl.getChildElement("value", el));
-				ANKI_CHECK(el.getNumbers(tmp2));
-				key.m_value = Quat(tmp2);
+				// value
+				ANKI_CHECK(keyEl.getNumbers(key.m_value));
 
 				// Check ident
 				if(key.m_value == Quat::getIdentity())
@@ -167,20 +157,17 @@ Error AnimationResource::load(const ResourceFilename& filename, Bool async)
 			{
 				AnimationKeyframe<F32>& key = ch.m_scales[count++];
 
-				// <time>
-				ANKI_CHECK(keyEl.getChildElement("time", el));
-				ANKI_CHECK(el.getNumber(ftmp));
-				key.m_time = ftmp;
+				// time
+				ANKI_CHECK(keyEl.getAttributeNumber("time", key.m_time));
 				m_startTime = std::min(m_startTime, key.m_time);
 				maxTime = std::max(maxTime, key.m_time);
 
-				// <value>
+				// value
 				ANKI_CHECK(keyEl.getChildElement("value", el));
-				ANKI_CHECK(el.getNumber(ftmp));
-				key.m_value = F32(ftmp);
+				ANKI_CHECK(keyEl.getNumber(key.m_value));
 
 				// Check ident
-				if(isZero(key.m_value - 1.0))
+				if(isZero(key.m_value - 1.0f))
 				{
 					++identScaleCount;
 				}
@@ -218,6 +205,15 @@ Error AnimationResource::load(const ResourceFilename& filename, Bool async)
 
 void AnimationResource::interpolate(U32 channelIndex, Second time, Vec3& pos, Quat& rot, F32& scale) const
 {
+	pos = Vec3(0.0f);
+	rot = Quat::getIdentity();
+	scale = 1.0f;
+
+	if(ANKI_UNLIKELY(time < m_startTime))
+	{
+		return;
+	}
+
 	// Audjust time
 	if(time > m_startTime + m_duration)
 	{
@@ -260,6 +256,22 @@ void AnimationResource::interpolate(U32 channelIndex, Second time, Vec3& pos, Qu
 			}
 		}
 	}
+
+	// Scale
+	if(channel.m_scales.getSize() > 1)
+	{
+		for(U32 i = 0; i < channel.m_scales.getSize() - 1; ++i)
+		{
+			const AnimationKeyframe<F32>& left = channel.m_scales[i];
+			const AnimationKeyframe<F32>& right = channel.m_scales[i + 1];
+			if(time >= left.m_time && time <= right.m_time)
+			{
+				const Second u = (time - left.m_time) / (right.m_time - left.m_time);
+				scale = linearInterpolate(left.m_value, right.m_value, F32(u));
+				break;
+			}
+		}
+	}
 }
 
 } // end namespace anki

+ 18 - 25
src/anki/resource/SkeletonResource.cpp

@@ -49,31 +49,24 @@ Error SkeletonResource::load(const ResourceFilename& filename, Bool async)
 		Bone& bone = m_bones[boneCount];
 		bone.m_idx = boneCount;
 
-		// <name>
-		XmlElement nameEl;
-		ANKI_CHECK(boneEl.getChildElement("name", nameEl));
-		CString tmp;
-		ANKI_CHECK(nameEl.getText(tmp));
-		bone.m_name.create(getAllocator(), tmp);
-
-		// <transform>
-		XmlElement trfEl;
-		ANKI_CHECK(boneEl.getChildElement("transform", trfEl));
-		ANKI_CHECK(trfEl.getNumbers(bone.m_transform));
-
-		// <boneTransform>
-		XmlElement btrfEl;
-		ANKI_CHECK(boneEl.getChildElement("boneTransform", btrfEl));
-		ANKI_CHECK(btrfEl.getNumbers(bone.m_vertTrf));
-
-		// <parent>
-		XmlElement parentEl;
-		ANKI_CHECK(boneEl.getChildElementOptional("parent", parentEl));
-		if(parentEl)
+		// name
+		CString name;
+		ANKI_CHECK(boneEl.getAttributeText("name", name));
+		bone.m_name.create(getAllocator(), name);
+
+		// transform
+		ANKI_CHECK(boneEl.getAttributeNumbers("transform", bone.m_transform));
+
+		// boneTransform
+		ANKI_CHECK(boneEl.getAttributeNumbers("boneTransform", bone.m_vertTrf));
+
+		// parent
+		CString parent;
+		Bool hasParent;
+		ANKI_CHECK(boneEl.getAttributeTextOptional("parent", parent, hasParent));
+		if(hasParent)
 		{
-			CString parentName;
-			ANKI_CHECK(parentEl.getText(parentName));
-			boneParents.pushBack(parentName);
+			boneParents.pushBack(parent);
 		}
 		else
 		{
@@ -99,7 +92,7 @@ Error SkeletonResource::load(const ResourceFilename& filename, Bool async)
 	{
 		Bone& bone = m_bones[i];
 
-		if(!it->isEmpty())
+		if(it->getLength() > 0)
 		{
 			for(U32 j = 0; j < m_bones.getSize(); ++j)
 			{

+ 5 - 0
src/anki/resource/SkeletonResource.h

@@ -52,6 +52,11 @@ public:
 		return ConstWeakArray<Bone*>((m_childrenCount) ? &m_children[0] : nullptr, m_childrenCount);
 	}
 
+	const Bone* getParent() const
+	{
+		return m_parent;
+	}
+
 private:
 	String m_name; ///< The name of the bone
 

+ 44 - 0
src/anki/scene/DebugDrawer.cpp

@@ -382,6 +382,50 @@ void DebugDrawer2::drawCubes(ConstWeakArray<Mat4> mvps,
 	cmdb->drawElements(PrimitiveTopology::LINES, indexCount, mvps.getSize());
 }
 
+void DebugDrawer2::drawLines(ConstWeakArray<Mat4> mvps,
+	const Vec4& color,
+	F32 lineSize,
+	Bool ditherFailedDepth,
+	ConstWeakArray<Vec3> lines,
+	StagingGpuMemoryManager& stagingGpuAllocator,
+	CommandBufferPtr& cmdb) const
+{
+	ANKI_ASSERT(mvps.getSize() > 0);
+	ANKI_ASSERT(lines.getSize() > 0 && (lines.getSize() % 2) == 0);
+
+	// Verts
+	StagingGpuMemoryToken vertsToken;
+	Vec3* verts = static_cast<Vec3*>(
+		stagingGpuAllocator.allocateFrame(sizeof(Vec3) * lines.getSize(), StagingGpuMemoryType::VERTEX, vertsToken));
+	memcpy(verts, lines.getBegin(), lines.getSizeInBytes());
+
+	// Set the uniforms
+	StagingGpuMemoryToken unisToken;
+	Mat4* pmvps = static_cast<Mat4*>(stagingGpuAllocator.allocateFrame(
+		sizeof(Mat4) * mvps.getSize() + sizeof(Vec4), StagingGpuMemoryType::UNIFORM, unisToken));
+
+	memcpy(pmvps, &mvps[0], mvps.getSizeInBytes());
+	Vec4* pcolor = reinterpret_cast<Vec4*>(pmvps + mvps.getSize());
+	*pcolor = color;
+
+	// Setup state
+	ShaderProgramResourceVariantInitInfo variantInitInfo(m_prog);
+	variantInitInfo.addMutation("COLOR_TEXTURE", 0);
+	variantInitInfo.addMutation("DITHERED_DEPTH_TEST", U32(ditherFailedDepth != 0));
+	variantInitInfo.addConstant("INSTANCE_COUNT", mvps.getSize());
+	const ShaderProgramResourceVariant* variant;
+	m_prog->getOrCreateVariant(variantInitInfo, variant);
+	cmdb->bindShaderProgram(variant->getProgram());
+
+	cmdb->setVertexAttribute(0, 0, Format::R32G32B32_SFLOAT, 0);
+	cmdb->bindVertexBuffer(0, vertsToken.m_buffer, vertsToken.m_offset, sizeof(Vec3));
+
+	cmdb->bindUniformBuffer(1, 0, unisToken.m_buffer, unisToken.m_offset, unisToken.m_range);
+
+	cmdb->setLineWidth(lineSize);
+	cmdb->drawArrays(PrimitiveTopology::LINES, lines.getSize(), mvps.getSize());
+}
+
 void DebugDrawer2::drawBillboardTextures(const Mat4& projMat,
 	const Mat4& viewMat,
 	ConstWeakArray<Vec3> positions,

+ 21 - 0
src/anki/scene/DebugDrawer.h

@@ -153,6 +153,27 @@ public:
 			ConstWeakArray<Mat4>(&mvp, 1), color, lineSize, ditherFailedDepth, cubeSideSize, stagingGpuAllocator, cmdb);
 	}
 
+	void drawLines(ConstWeakArray<Mat4> mvps,
+		const Vec4& color,
+		F32 lineSize,
+		Bool ditherFailedDepth,
+		ConstWeakArray<Vec3> lines,
+		StagingGpuMemoryManager& stagingGpuAllocator,
+		CommandBufferPtr& cmdb) const;
+
+	void drawLine(const Mat4& mvp,
+		const Vec4& color,
+		F32 lineSize,
+		Bool ditherFailedDepth,
+		const Vec3& a,
+		const Vec3& b,
+		StagingGpuMemoryManager& stagingGpuAllocator,
+		CommandBufferPtr& cmdb) const
+	{
+		Array<Vec3, 2> points = {{a, b}};
+		drawLines(ConstWeakArray<Mat4>(&mvp, 1), color, lineSize, ditherFailedDepth, points, stagingGpuAllocator, cmdb);
+	}
+
 	void drawBillboardTextures(const Mat4& projMat,
 		const Mat4& viewMat,
 		ConstWeakArray<Vec3> positions,

+ 97 - 9
src/anki/scene/ModelNode.cpp

@@ -31,10 +31,40 @@ public:
 		updated = false;
 
 		const MoveComponent& move = node.getComponent<MoveComponent>();
-		if(move.getTimestamp() == node.getGlobalTimestamp())
+		const SkinComponent* skin = node.tryGetComponent<SkinComponent>();
+		if(move.getTimestamp() == node.getGlobalTimestamp()
+			|| (skin && skin->getTimestamp() == node.getGlobalTimestamp()))
 		{
 			ModelNode& mnode = static_cast<ModelNode&>(node);
-			mnode.onMoveComponentUpdate(move);
+			mnode.updateSpatialComponent(move);
+		}
+
+		return Error::NONE;
+	}
+};
+
+/// Feedback component.
+class ModelNode::SkinFeedbackComponent : public SceneComponent
+{
+public:
+	SkinFeedbackComponent()
+		: SceneComponent(SceneComponentType::NONE)
+	{
+	}
+
+	ANKI_USE_RESULT Error update(SceneNode& node, Second prevTime, Second crntTime, Bool& updated) override
+	{
+		updated = false;
+
+		const SkinComponent& skin = node.getComponent<SkinComponent>();
+		if(skin.getTimestamp() == node.getGlobalTimestamp())
+		{
+			ModelNode& mnode = static_cast<ModelNode&>(node);
+
+			const Aabb& box = skin.getBoneBoundingVolume();
+			mnode.m_obbLocal.setCenter((box.getMin() + box.getMax()) / 2.0f);
+			mnode.m_obbLocal.setExtend(box.getMax() - mnode.m_obbLocal.getCenter());
+			mnode.m_obbLocal.setRotation(Mat3x4::getIdentity());
 		}
 
 		return Error::NONE;
@@ -68,10 +98,11 @@ Error ModelNode::init(ModelResourcePtr resource, U32 modelPatchIdx)
 	if(m_model->getSkeleton().isCreated())
 	{
 		newComponent<SkinComponent>(this, m_model->getSkeleton());
+		newComponent<SkinFeedbackComponent>();
 	}
 	newComponent<MoveComponent>();
 	newComponent<MoveFeedbackComponent>();
-	newComponent<SpatialComponent>(this, &m_obb);
+	newComponent<SpatialComponent>(this, &m_obbWorld);
 	MaterialRenderComponent* rcomp =
 		newComponent<MaterialRenderComponent>(this, m_model->getModelPatches()[m_modelPatchIdx].getMaterial());
 	rcomp->setup(
@@ -82,6 +113,8 @@ Error ModelNode::init(ModelResourcePtr resource, U32 modelPatchIdx)
 		this,
 		m_mergeKey);
 
+	m_obbLocal = m_model->getModelPatches()[m_modelPatchIdx].getBoundingShape();
+
 	return Error::NONE;
 }
 
@@ -105,9 +138,9 @@ Error ModelNode::init(const CString& modelFname)
 	return Error::NONE;
 }
 
-void ModelNode::onMoveComponentUpdate(const MoveComponent& move)
+void ModelNode::updateSpatialComponent(const MoveComponent& move)
 {
-	m_obb = m_model->getModelPatches()[m_modelPatchIdx].getBoundingShape().getTransformed(move.getWorldTransform());
+	m_obbWorld = m_obbLocal.getTransformed(move.getWorldTransform());
 
 	SpatialComponent& sp = getComponent<SpatialComponent>();
 	sp.markForUpdate();
@@ -213,9 +246,9 @@ void ModelNode::draw(RenderQueueDrawContext& ctx, ConstWeakArray<void*> userData
 		{
 			const ModelNode& self2 = *static_cast<const ModelNode*>(userData[i]);
 
-			const Mat3 rot = self2.m_obb.getRotation().getRotationPart();
-			const Vec4 tsl = self2.m_obb.getCenter().xyz1();
-			const Vec3 scale = self2.m_obb.getExtend().xyz();
+			const Mat3 rot = self2.m_obbWorld.getRotation().getRotationPart();
+			const Vec4 tsl = self2.m_obbWorld.getCenter().xyz1();
+			const Vec3 scale = self2.m_obbWorld.getExtend().xyz();
 
 			// Set non uniform scale. Add a margin to avoid flickering
 			Mat3 nonUniScale = Mat3::getZero();
@@ -239,7 +272,7 @@ void ModelNode::draw(RenderQueueDrawContext& ctx, ConstWeakArray<void*> userData
 
 		m_dbgDrawer.drawCubes(ConstWeakArray<Mat4>(mvps, userData.getSize()),
 			Vec4(1.0f, 0.0f, 1.0f, 1.0f),
-			1.0f,
+			2.0f,
 			ctx.m_debugDrawFlags.get(RenderQueueDebugDrawFlag::DITHERED_DEPTH_TEST_ON),
 			2.0f,
 			*ctx.m_stagingGpuAllocator,
@@ -247,6 +280,61 @@ void ModelNode::draw(RenderQueueDrawContext& ctx, ConstWeakArray<void*> userData
 
 		ctx.m_frameAllocator.deleteArray(mvps, userData.getSize());
 
+		// Bones
+		if(m_model->getSkeleton())
+		{
+			const SkinComponent& skinc = getComponentAt<SkinComponent>(0);
+			SkeletonResourcePtr skeleton = skinc.getSkeleronResource();
+			const U32 boneCount = skinc.getBoneTransforms().getSize();
+
+			DynamicArrayAuto<Vec3> lines(ctx.m_frameAllocator);
+			lines.resizeStorage(boneCount * 2);
+			DynamicArrayAuto<Vec3> chidlessLines(ctx.m_frameAllocator);
+			for(U32 i = 0; i < boneCount; ++i)
+			{
+				const Bone& bone = skeleton->getBones()[i];
+				ANKI_ASSERT(bone.getIndex() == i);
+				const Vec4 point(0.0f, 0.0f, 0.0f, 1.0f);
+				const Bone* parent = bone.getParent();
+				Mat4 m = (parent)
+							 ? skinc.getBoneTransforms()[parent->getIndex()] * parent->getVertexTransform().getInverse()
+							 : Mat4::getIdentity();
+				const Vec3 a = (m * point).xyz();
+
+				m = skinc.getBoneTransforms()[i] * bone.getVertexTransform().getInverse();
+				const Vec3 b = (m * point).xyz();
+
+				lines.emplaceBack(a);
+				lines.emplaceBack(b);
+
+				if(bone.getChildren().getSize() == 0)
+				{
+					// If there are not children try to draw something for that bone as well
+					chidlessLines.emplaceBack(b);
+					const F32 len = (b - a).getLength();
+					const Vec3 c = b + (b - a).getNormalized() * len;
+					chidlessLines.emplaceBack(c);
+				}
+			}
+
+			const Mat4 mvp = ctx.m_viewProjectionMatrix * Mat4(getComponent<MoveComponent>().getWorldTransform());
+			m_dbgDrawer.drawLines(ConstWeakArray<Mat4>(&mvp, 1),
+				Vec4(1.0f),
+				20.0f,
+				ctx.m_debugDrawFlags.get(RenderQueueDebugDrawFlag::DITHERED_DEPTH_TEST_ON),
+				lines,
+				*ctx.m_stagingGpuAllocator,
+				cmdb);
+
+			m_dbgDrawer.drawLines(ConstWeakArray<Mat4>(&mvp, 1),
+				Vec4(0.7f, 0.7f, 0.7f, 1.0f),
+				5.0f,
+				ctx.m_debugDrawFlags.get(RenderQueueDebugDrawFlag::DITHERED_DEPTH_TEST_ON),
+				chidlessLines,
+				*ctx.m_stagingGpuAllocator,
+				cmdb);
+		}
+
 		// Restore state
 		if(!enableDepthTest)
 		{

+ 4 - 2
src/anki/scene/ModelNode.h

@@ -40,16 +40,18 @@ public:
 
 private:
 	class MoveFeedbackComponent;
+	class SkinFeedbackComponent;
 
 	ModelResourcePtr m_model; ///< The resource
 
-	Obb m_obb;
+	Obb m_obbLocal;
+	Obb m_obbWorld;
 	U64 m_mergeKey = 0;
 	U32 m_modelPatchIdx = 0;
 
 	DebugDrawer2 m_dbgDrawer;
 
-	void onMoveComponentUpdate(const MoveComponent& move);
+	void updateSpatialComponent(const MoveComponent& move);
 
 	void draw(RenderQueueDrawContext& ctx, ConstWeakArray<void*> userData) const;
 };

+ 110 - 17
src/anki/scene/components/SkinComponent.cpp

@@ -20,22 +20,39 @@ SkinComponent::SkinComponent(SceneNode* node, SkeletonResourcePtr skeleton)
 	ANKI_ASSERT(node);
 
 	m_boneTrfs.create(m_node->getAllocator(), m_skeleton->getBones().getSize());
-	for(Mat4& trf : m_boneTrfs)
+	m_animationTrfs.create(m_node->getAllocator(), m_skeleton->getBones().getSize());
+
+	for(U32 i = 0; i < m_boneTrfs.getSize(); ++i)
 	{
-		trf.setIdentity();
+		m_boneTrfs[i].setIdentity();
+		m_animationTrfs[i] = {Vec3(0.0f), Quat::getIdentity(), 1.0f};
 	}
 }
 
 SkinComponent::~SkinComponent()
 {
 	m_boneTrfs.destroy(m_node->getAllocator());
+	m_animationTrfs.destroy(m_node->getAllocator());
 }
 
-void SkinComponent::playAnimation(U track, AnimationResourcePtr anim, Second startTime, Bool repeat)
+void SkinComponent::playAnimation(U32 track, AnimationResourcePtr anim, const AnimationPlayInfo& info)
 {
+	const Second animDuration = anim->getDuration();
+
 	m_tracks[track].m_anim = anim;
-	m_tracks[track].m_time = startTime;
-	m_tracks[track].m_repeat = repeat;
+	m_tracks[track].m_absoluteStartTime = m_absoluteTime + info.m_startTime;
+	m_tracks[track].m_relativeTimePassed = 0.0;
+	if(info.m_repeatTimes > 0.0)
+	{
+		m_tracks[track].m_blendInTime = min(animDuration * info.m_repeatTimes, info.m_blendInTime);
+		m_tracks[track].m_blendOutTime = min(animDuration * info.m_repeatTimes, info.m_blendOutTime);
+	}
+	else
+	{
+		m_tracks[track].m_blendInTime = info.m_blendInTime;
+		m_tracks[track].m_blendOutTime = 0.0; // Irrelevant
+	}
+	m_tracks[track].m_repeatTimes = info.m_repeatTimes;
 }
 
 Error SkinComponent::update(SceneNode& node, Second prevTime, Second crntTime, Bool& updated)
@@ -43,7 +60,12 @@ Error SkinComponent::update(SceneNode& node, Second prevTime, Second crntTime, B
 	ANKI_ASSERT(&node == m_node);
 
 	updated = false;
-	const Second timeDiff = crntTime - prevTime;
+	const Second dt = crntTime - prevTime;
+
+	Vec4 minExtend(MAX_F32, MAX_F32, MAX_F32, 0.0f);
+	Vec4 maxExtend(MIN_F32, MIN_F32, MIN_F32, 0.0f);
+
+	BitSet<128> bonesAnimated(false);
 
 	for(Track& track : m_tracks)
 	{
@@ -52,13 +74,27 @@ Error SkinComponent::update(SceneNode& node, Second prevTime, Second crntTime, B
 			continue;
 		}
 
+		if(track.m_absoluteStartTime > m_absoluteTime)
+		{
+			// Hasn't started yet
+			continue;
+		}
+
+		const Second clipDuration = track.m_anim->getDuration();
+		const Second animationDuration = track.m_repeatTimes * clipDuration;
+
+		if(track.m_repeatTimes > 0.0 && track.m_relativeTimePassed > animationDuration)
+		{
+			// Animation finished
+			continue;
+		}
+
 		updated = true;
 
-		const Second animTime = track.m_time;
-		track.m_time += timeDiff;
+		const Second animTime = track.m_relativeTimePassed;
+		track.m_relativeTimePassed += dt;
 
 		// Iterate the animation channels and interpolate
-		BitSet<128> bonesAnimated(false);
 		for(U32 i = 0; i < track.m_anim->getChannels().getSize(); ++i)
 		{
 			const AnimationChannel& channel = track.m_anim->getChannels()[i];
@@ -68,6 +104,7 @@ Error SkinComponent::update(SceneNode& node, Second prevTime, Second crntTime, B
 				ANKI_SCENE_LOGW("Animation is referencing unknown bone \"%s\"", &channel.m_name[0]);
 				continue;
 			}
+			const U32 boneIdx = bone->getIndex();
 
 			// Interpolate
 			Vec3 position;
@@ -75,34 +112,90 @@ Error SkinComponent::update(SceneNode& node, Second prevTime, Second crntTime, B
 			F32 scale;
 			track.m_anim->interpolate(i, animTime, position, rotation, scale);
 
+			// Blend with previous track
+			if(bonesAnimated.get(boneIdx) && (track.m_blendInTime > 0.0 || track.m_blendOutTime > 0.0))
+			{
+				F32 blendInFactor;
+				if(track.m_blendInTime > 0.0)
+				{
+					blendInFactor = min(1.0f, F32(animTime / track.m_blendInTime));
+				}
+				else
+				{
+					blendInFactor = 1.0f;
+				}
+
+				F32 blendOutFactor;
+				if(track.m_blendOutTime > 0.0)
+				{
+					blendOutFactor = min(1.0f, F32((animationDuration - animTime) / track.m_blendOutTime));
+				}
+				else
+				{
+					blendOutFactor = 1.0f;
+				}
+
+				const F32 factor = blendInFactor * blendOutFactor;
+
+				if(factor < 1.0f)
+				{
+					const Trf& prevTrf = m_animationTrfs[boneIdx];
+
+					position = linearInterpolate(prevTrf.m_translation, position, factor);
+					rotation = prevTrf.m_rotation.slerp(rotation, factor);
+					scale = linearInterpolate(prevTrf.m_scale, scale, factor);
+				}
+			}
+
 			// Store
-			bonesAnimated.set(bone->getIndex());
-			m_boneTrfs[bone->getIndex()] = Mat4(position.xyz1(), Mat3(rotation), 1.0f) * bone->getVertexTransform();
+			bonesAnimated.set(boneIdx);
+			m_animationTrfs[boneIdx] = {position, rotation, scale};
 		}
+	}
+
+	// Always update the 1st time
+	updated = updated || (m_absoluteTime == 0.0);
 
+	if(updated)
+	{
 		// Walk the bone hierarchy to add additional transforms
-		visitBones(m_skeleton->getRootBone(), Mat4::getIdentity(), bonesAnimated);
+		visitBones(m_skeleton->getRootBone(), Mat4::getIdentity(), bonesAnimated, minExtend, maxExtend);
+
+		const Vec4 E(EPSILON, EPSILON, EPSILON, 0.0f);
+		m_boneBoundingVolume.setMin(minExtend - E);
+		m_boneBoundingVolume.setMax(maxExtend + E);
 	}
 
+	m_absoluteTime += dt;
+
 	return Error::NONE;
 }
 
-void SkinComponent::visitBones(const Bone& bone, const Mat4& parentTrf, const BitSet<128>& bonesAnimated)
+void SkinComponent::visitBones(
+	const Bone& bone, const Mat4& parentTrf, const BitSet<128>& bonesAnimated, Vec4& minExtend, Vec4& maxExtend)
 {
-	Mat4 myTrf = parentTrf * bone.getTransform();
+	Mat4 outMat;
 
 	if(bonesAnimated.get(bone.getIndex()))
 	{
-		m_boneTrfs[bone.getIndex()] = myTrf * m_boneTrfs[bone.getIndex()];
+		const Trf& t = m_animationTrfs[bone.getIndex()];
+		outMat = parentTrf * Mat4(t.m_translation.xyz1(), Mat3(t.m_rotation), t.m_scale);
 	}
 	else
 	{
-		m_boneTrfs[bone.getIndex()] = myTrf * bone.getVertexTransform();
+		outMat = parentTrf * bone.getTransform();
 	}
 
+	m_boneTrfs[bone.getIndex()] = outMat * bone.getVertexTransform();
+
+	// Update volume
+	const Vec4 bonePos = outMat * Vec4(0.0f, 0.0f, 0.0f, 1.0f);
+	minExtend = minExtend.min(bonePos.xyz0());
+	maxExtend = maxExtend.max(bonePos.xyz0());
+
 	for(const Bone* child : bone.getChildren())
 	{
-		visitBones(*child, myTrf, bonesAnimated);
+		visitBones(*child, outMat, bonesAnimated, minExtend, maxExtend);
 	}
 }
 

+ 52 - 6
src/anki/scene/components/SkinComponent.h

@@ -7,6 +7,7 @@
 
 #include <anki/scene/components/SceneComponent.h>
 #include <anki/resource/Forward.h>
+#include <anki/collision/Aabb.h>
 #include <anki/util/Forward.h>
 #include <anki/Math.h>
 
@@ -16,12 +17,29 @@ namespace anki
 /// @addtogroup scene
 /// @{
 
+/// @memberof SkinComponent
+class AnimationPlayInfo
+{
+public:
+	/// The time the animation will start after being pushed in SkinComponent::playAnimation().
+	Second m_startTime = 0.0;
+
+	/// Negative means infinite.
+	F32 m_repeatTimes = 1.0f;
+
+	/// The time from when the animation starts until it fully replaces the animations of previous tracks.
+	Second m_blendInTime = 0.0f;
+
+	/// The time from when the animation ends until it until it has zero influence to the animations of previous tracks.
+	Second m_blendOutTime = 0.0f;
+};
+
 /// Skin component.
 class SkinComponent : public SceneComponent
 {
 public:
-	static const SceneComponentType CLASS_TYPE = SceneComponentType::SKIN;
-	static const U MAX_ANIMATION_TRACKS = 2;
+	static constexpr SceneComponentType CLASS_TYPE = SceneComponentType::SKIN;
+	static constexpr U32 MAX_ANIMATION_TRACKS = 4;
 
 	SkinComponent(SceneNode* node, SkeletonResourcePtr skeleton);
 
@@ -29,28 +47,56 @@ public:
 
 	ANKI_USE_RESULT Error update(SceneNode& node, Second prevTime, Second crntTime, Bool& updated) override;
 
-	void playAnimation(U track, AnimationResourcePtr anim, Second startTime, Bool repeat);
+	void playAnimation(U32 track, AnimationResourcePtr anim, const AnimationPlayInfo& info);
 
 	const DynamicArray<Mat4>& getBoneTransforms() const
 	{
 		return m_boneTrfs;
 	}
 
+	const SkeletonResourcePtr& getSkeleronResource() const
+	{
+		return m_skeleton;
+	}
+
+	const Aabb& getBoneBoundingVolume() const
+	{
+		return m_boneBoundingVolume;
+	}
+
 private:
 	class Track
 	{
 	public:
 		AnimationResourcePtr m_anim;
-		F64 m_time;
-		Bool m_repeat;
+		Second m_absoluteStartTime = 0.0;
+		Second m_relativeTimePassed = 0.0;
+		Second m_blendInTime = 0.0;
+		Second m_blendOutTime = 0.0f;
+		F32 m_repeatTimes = 1.0f;
+	};
+
+	class Trf
+	{
+	public:
+		Vec3 m_translation;
+		Quat m_rotation;
+		F32 m_scale;
 	};
 
 	SceneNode* m_node;
 	SkeletonResourcePtr m_skeleton;
 	DynamicArray<Mat4> m_boneTrfs;
+	DynamicArray<Trf> m_animationTrfs;
+	Aabb m_boneBoundingVolume{Vec3(-1.0f), Vec3(1.0f)};
 	Array<Track, MAX_ANIMATION_TRACKS> m_tracks;
+	Second m_absoluteTime = 0.0;
 
-	void visitBones(const Bone& bone, const Mat4& parentTrf, const BitSet<128, U8>& bonesAnimated);
+	void visitBones(const Bone& bone,
+		const Mat4& parentTrf,
+		const BitSet<128, U8>& bonesAnimated,
+		Vec4& minExtend,
+		Vec4& maxExtend);
 };
 /// @}
 

+ 1 - 0
src/anki/util/ProcessPosix.cpp

@@ -148,6 +148,7 @@ Error Process::kill(ProcessKillSignal k)
 		break;
 	default:
 		ANKI_ASSERT(0);
+		sig = 0;
 	};
 
 	const pid_t p = ::kill(m_pid, sig);

+ 1 - 0
src/anki/util/String.h

@@ -701,6 +701,7 @@ public:
 	/// Move one string to this one.
 	StringAuto& operator=(StringAuto&& b)
 	{
+		destroy();
 		move(b);
 		return *this;
 	}