CsvSerializers.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <AzCore/Serialization/Locale.h>
  9. #include <AzCore/std/string/conversions.h>
  10. #include <AzCore/IO/GenericStreams.h>
  11. #include <EMotionFX/Source/ActorInstance.h>
  12. #include <EMotionFX/Source/Node.h>
  13. #include <EMotionFX/Source/PoseDataFactory.h>
  14. #include <EMotionFX/Source/TransformData.h>
  15. #include <CsvSerializers.h>
  16. #include <FeatureSchema.h>
  17. namespace EMotionFX::MotionMatching
  18. {
  19. CsvWriterBase::~CsvWriterBase()
  20. {
  21. End();
  22. }
  23. bool CsvWriterBase::OpenFile(const char* filename, int openMode)
  24. {
  25. m_file.Close();
  26. if (!m_file.Open(filename, openMode))
  27. {
  28. End();
  29. return false;
  30. }
  31. return true;
  32. }
  33. void CsvWriterBase::End()
  34. {
  35. m_file.Close();
  36. m_tempBuffer.clear();
  37. }
  38. void CsvWriterBase::WriteLine(AZStd::string& line)
  39. {
  40. line = AZ::StringFunc::RStrip(line, ",");
  41. line += "\n";
  42. m_file.Write(line.data(), line.size());
  43. }
  44. void CsvWriterBase::WriteVector3ToString(const AZ::Vector3& vec, AZStd::string& text)
  45. {
  46. AZ::Locale::ScopedSerializationLocale scopedLocale; // ensure that %f is read/written using the "C" locale.
  47. text += AZStd::string::format("%.8f,%.8f,%.8f,", vec.GetX(), vec.GetY(), vec.GetZ());
  48. };
  49. void CsvWriterBase::WriteFloatArrayToString(const AZStd::vector<float>& values, AZStd::string& text)
  50. {
  51. AZ::Locale::ScopedSerializationLocale scopedLocale; // ensure that %f is read/written using the "C" locale.
  52. text.reserve(text.size() + values.size() * 10);
  53. for (float value : values)
  54. {
  55. text += AZStd::string::format("%.8f,", value);
  56. }
  57. }
  58. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  59. bool PoseWriterCsv::Begin(const char* filename, ActorInstance* actorInstance, const WriteSettings& writeSettings)
  60. {
  61. m_settings = writeSettings;
  62. if (!actorInstance)
  63. {
  64. return false;
  65. }
  66. if (!CsvWriterBase::OpenFile(filename, AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_CREATE_PATH | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY))
  67. {
  68. return false;
  69. }
  70. m_actorInstance = actorInstance;
  71. SaveColumnNamesToString(m_tempBuffer);
  72. return true;
  73. }
  74. void PoseWriterCsv::SaveColumnNamesToString(AZStd::string& outText)
  75. {
  76. const Skeleton* skeleton = m_actorInstance->GetActor()->GetSkeleton();
  77. const size_t numEnabledJoints = m_actorInstance->GetNumEnabledNodes();
  78. outText.clear();
  79. outText.reserve(50 * numEnabledJoints);
  80. auto SaveVector3ColumnNames = [](AZStd::string& text, const char* jointName, const char* vecName)
  81. {
  82. text += AZStd::string::format("%s.%s.X,%s.%s.Y,%s.%s.Z,", jointName, vecName, jointName, vecName, jointName, vecName);
  83. };
  84. for (size_t i = 0; i < numEnabledJoints; ++i)
  85. {
  86. const size_t jointIndex = m_actorInstance->GetEnabledNode(i);
  87. const Node* joint = skeleton->GetNode(jointIndex);
  88. // Position
  89. if (m_settings.m_writePositions)
  90. {
  91. SaveVector3ColumnNames(outText, joint->GetName(), "Pos");
  92. }
  93. // Rotation
  94. if (m_settings.m_writeRotations)
  95. {
  96. SaveVector3ColumnNames(outText, joint->GetName(), "RotBasisX");
  97. SaveVector3ColumnNames(outText, joint->GetName(), "RotBasisY");
  98. }
  99. }
  100. WriteLine(m_tempBuffer);
  101. }
  102. void PoseWriterCsv::WritePose(Pose& pose, const ETransformSpace transformSpace)
  103. {
  104. if (!IsReady() || !m_actorInstance)
  105. {
  106. return;
  107. }
  108. SavePoseToString(pose, transformSpace, m_tempBuffer);
  109. WriteLine(m_tempBuffer);
  110. }
  111. void PoseWriterCsv::SavePoseToString(Pose& pose, const ETransformSpace transformSpace, AZStd::string& outText)
  112. {
  113. const size_t numEnabledJoints = m_actorInstance->GetNumEnabledNodes();
  114. outText.clear();
  115. outText.reserve(10 * 3 * 3 * numEnabledJoints);
  116. for (size_t i = 0; i < numEnabledJoints; ++i)
  117. {
  118. const size_t jointIndex = m_actorInstance->GetEnabledNode(i);
  119. Transform transform = Transform::CreateIdentity();
  120. switch (transformSpace)
  121. {
  122. case TRANSFORM_SPACE_LOCAL:
  123. {
  124. transform = pose.GetLocalSpaceTransform(jointIndex);
  125. break;
  126. }
  127. case TRANSFORM_SPACE_MODEL:
  128. {
  129. transform = pose.GetModelSpaceTransform(jointIndex);
  130. break;
  131. }
  132. default:
  133. {
  134. AZ_Error("Motion Matching", false, "Unsupported transform space");
  135. break;
  136. }
  137. }
  138. // Position
  139. if (m_settings.m_writePositions)
  140. {
  141. const AZ::Vector3 position = transform.m_position;
  142. WriteVector3ToString(position, outText);
  143. }
  144. // Rotation
  145. // Store rotation as the X and Y axes The Z axis can be reconstructed by the cross product of the X and Y axes.
  146. if (m_settings.m_writeRotations)
  147. {
  148. const AZ::Quaternion rotation = transform.m_rotation;
  149. AZ::Matrix3x3 rotationMatrix = AZ::Matrix3x3::CreateFromQuaternion(rotation);
  150. WriteVector3ToString(rotationMatrix.GetBasisX().GetNormalizedSafe(), outText);
  151. WriteVector3ToString(rotationMatrix.GetBasisY().GetNormalizedSafe(), outText);
  152. }
  153. }
  154. }
  155. void PoseWriterCsv::End()
  156. {
  157. m_actorInstance = nullptr;
  158. CsvWriterBase::End();
  159. }
  160. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  161. bool QueryVectorWriterCsv::Begin(const char* filename, const FeatureSchema* featureSchema)
  162. {
  163. if (!featureSchema)
  164. {
  165. return false;
  166. }
  167. if (!CsvWriterBase::OpenFile(filename, AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_CREATE_PATH | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY))
  168. {
  169. return false;
  170. }
  171. // Save column names in the first row
  172. const AZStd::vector<AZStd::string> columnNames = featureSchema->CollectColumnNames();
  173. m_tempBuffer.clear();
  174. if (!columnNames.empty())
  175. {
  176. for (size_t i = 0; i < columnNames.size(); ++i)
  177. {
  178. if (i != 0)
  179. {
  180. m_tempBuffer += ",";
  181. }
  182. m_tempBuffer += columnNames[i];
  183. }
  184. }
  185. WriteLine(m_tempBuffer);
  186. return true;
  187. }
  188. void QueryVectorWriterCsv::Write(const QueryVector* queryVector)
  189. {
  190. if (!IsReady() || !queryVector)
  191. {
  192. return;
  193. }
  194. m_tempBuffer.clear();
  195. WriteFloatArrayToString(queryVector->GetData(), m_tempBuffer);
  196. WriteLine(m_tempBuffer);
  197. }
  198. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  199. bool BestMatchingFrameWriterCsv::Begin(const char* filename)
  200. {
  201. if (!CsvWriterBase::OpenFile(filename, AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_CREATE_PATH | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY))
  202. {
  203. return false;
  204. }
  205. m_tempBuffer = "Best Matching Frames";
  206. WriteLine(m_tempBuffer);
  207. return true;
  208. }
  209. void BestMatchingFrameWriterCsv::Write(size_t frame)
  210. {
  211. m_tempBuffer = AZStd::to_string(frame);
  212. WriteLine(m_tempBuffer);
  213. }
  214. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  215. PoseReaderCsv::~PoseReaderCsv()
  216. {
  217. End();
  218. }
  219. bool PoseReaderCsv::Begin(const char* filename, const ReadSettings& readSettings)
  220. {
  221. m_settings = readSettings;
  222. AZ::IO::SystemFile file;
  223. if (!file.Open(filename, AZ::IO::SystemFile::SF_OPEN_READ_ONLY))
  224. {
  225. return false;
  226. }
  227. AZ::IO::SystemFile::SizeType fileSize = file.Length();
  228. AZStd::string textBuffer;
  229. textBuffer.resize(fileSize);
  230. file.Read(fileSize, textBuffer.data());
  231. file.Close();
  232. AZStd::vector<AZStd::string> lines;
  233. AZ::StringFunc::Tokenize(textBuffer, lines, '\n');
  234. if (lines.empty())
  235. {
  236. return false;
  237. }
  238. m_columnNamesLine = lines[0];
  239. lines.erase(lines.begin());
  240. m_poseValueLines = lines;
  241. return true;
  242. }
  243. void PoseReaderCsv::ApplyPose(ActorInstance* actorInstance, Pose& pose, const ETransformSpace transformSpace, size_t index)
  244. {
  245. AZStd::vector<AZStd::string> valueTokens;
  246. AZ::StringFunc::Tokenize(m_poseValueLines[index], valueTokens, ',');
  247. Pose* bindPose = actorInstance->GetTransformData()->GetBindPose();
  248. size_t valueIndex = 0;
  249. const size_t numEnabledJoints = actorInstance->GetNumEnabledNodes();
  250. for (size_t i = 0; i < numEnabledJoints; ++i)
  251. {
  252. const size_t jointIndex = actorInstance->GetEnabledNode(i);
  253. Transform transform = bindPose->GetLocalSpaceTransform(jointIndex);
  254. switch (transformSpace)
  255. {
  256. case TRANSFORM_SPACE_LOCAL:
  257. {
  258. transform = pose.GetLocalSpaceTransform(jointIndex);
  259. break;
  260. }
  261. case TRANSFORM_SPACE_MODEL:
  262. {
  263. transform = pose.GetModelSpaceTransform(jointIndex);
  264. break;
  265. }
  266. default:
  267. {
  268. AZ_Error("Motion Matching", false, "Unsupported transform space");
  269. break;
  270. }
  271. }
  272. auto LoadVector3FromString = [&valueTokens](size_t& valueIndex, AZ::Vector3& outVec)
  273. {
  274. outVec.SetX(AZStd::stof(valueTokens[valueIndex + 0]));
  275. outVec.SetY(AZStd::stof(valueTokens[valueIndex + 1]));
  276. outVec.SetZ(AZStd::stof(valueTokens[valueIndex + 2]));
  277. valueIndex += 3;
  278. };
  279. // Position
  280. if (m_settings.m_readPositions)
  281. {
  282. LoadVector3FromString(valueIndex, transform.m_position);
  283. }
  284. // Rotation
  285. if (m_settings.m_readRotations)
  286. {
  287. // Load the X and Y axes.
  288. AZ::Vector3 basisX = AZ::Vector3::CreateZero();
  289. AZ::Vector3 basisY = AZ::Vector3::CreateZero();
  290. LoadVector3FromString(valueIndex, basisX);
  291. LoadVector3FromString(valueIndex, basisY);
  292. basisX.NormalizeSafe();
  293. basisY.NormalizeSafe();
  294. // Create a 3x3 rotation matrix by the X and Y axes and construct the Z-axis as the
  295. // cross-product of the X and Y axes.
  296. AZ::Matrix3x3 rotationMatrix = AZ::Matrix3x3::CreateIdentity();
  297. rotationMatrix.SetBasisX(basisX);
  298. rotationMatrix.SetBasisY(basisY);
  299. rotationMatrix.SetBasisZ(basisX.Cross(basisY));
  300. // Convert the rotation matrix to a quaternion.
  301. transform.m_rotation = AZ::Quaternion::CreateFromMatrix3x3(rotationMatrix);
  302. }
  303. switch (transformSpace)
  304. {
  305. case TRANSFORM_SPACE_LOCAL:
  306. {
  307. pose.SetLocalSpaceTransform(jointIndex, transform);
  308. break;
  309. }
  310. case TRANSFORM_SPACE_MODEL:
  311. {
  312. pose.SetModelSpaceTransform(jointIndex, transform);
  313. break;
  314. }
  315. default:
  316. {
  317. AZ_Error("Motion Matching", false, "Unsupported transform space");
  318. break;
  319. }
  320. }
  321. }
  322. }
  323. void PoseReaderCsv::End()
  324. {
  325. m_columnNamesLine.clear();
  326. m_poseValueLines.clear();
  327. }
  328. } // namespace EMotionFX::MotionMatching