| 247 | } |
| 248 | |
| 249 | void UMjPhysicsEngine::Compile() |
| 250 | { |
| 251 | PreCompile(); |
| 252 | |
| 253 | UE_LOG(LogURLab, Log, TEXT("Compiling MuJoCo model")); |
| 254 | m_LastCompileError.Empty(); |
| 255 | m_model = mj_compile(m_spec, &m_vfs); |
| 256 | |
| 257 | if (!m_model) |
| 258 | { |
| 259 | const char* spec_error = mjs_getError(m_spec); |
| 260 | m_LastCompileError = spec_error ? UTF8_TO_TCHAR(spec_error) : TEXT("Unknown compile error"); |
| 261 | UE_LOG(LogURLab, Error, TEXT("Model compilation failed: %s"), *m_LastCompileError); |
| 262 | #if WITH_EDITOR |
| 263 | FMessageDialog::Open(EAppMsgType::Ok, |
| 264 | FText::Format(NSLOCTEXT("URLab", "CompileError", "MuJoCo compile failed:\n\n{0}"), FText::FromString(m_LastCompileError))); |
| 265 | #endif |
| 266 | return; |
| 267 | } |
| 268 | |
| 269 | if (bSaveDebugXml) |
| 270 | { |
| 271 | FString CacheDir = FPaths::Combine(FPaths::ProjectSavedDir(), TEXT("URLab")); |
| 272 | IFileManager::Get().MakeDirectory(*CacheDir, true); |
| 273 | |
| 274 | int model_size = mj_sizeModel(m_model); |
| 275 | FString MjbPath = FPaths::Combine(CacheDir, TEXT("scene_compiled.mjb")); |
| 276 | mj_saveModel(m_model, TCHAR_TO_UTF8(*MjbPath), nullptr, model_size); |
| 277 | |
| 278 | static constexpr int32 kXmlBufferSize = 100 * 1024 * 1024; |
| 279 | static constexpr int32 kSaveErrorBufferSize = 10000; |
| 280 | char* xmlBuf = (char*)FMemory::Malloc(kXmlBufferSize); |
| 281 | if (xmlBuf) |
| 282 | { |
| 283 | FMemory::Memzero(xmlBuf, kXmlBufferSize); |
| 284 | char saveError[kSaveErrorBufferSize] = ""; |
| 285 | int xmlResult = mj_saveXMLString(m_spec, xmlBuf, kXmlBufferSize, saveError, sizeof(saveError)); |
| 286 | int xmlLen = FCStringAnsi::Strlen(xmlBuf); |
| 287 | if (xmlResult == 0 && xmlLen > 0) |
| 288 | { |
| 289 | FString XmlPath = FPaths::Combine(CacheDir, TEXT("scene_compiled.xml")); |
| 290 | FString XmlContent = UTF8_TO_TCHAR(xmlBuf); |
| 291 | |
| 292 | XmlContent.ReplaceInline(TEXT("//"), TEXT("/")); |
| 293 | |
| 294 | { |
| 295 | bool bChanged = true; |
| 296 | while (bChanged) |
| 297 | { |
| 298 | int32 Prev = XmlContent.Len(); |
| 299 | XmlContent.ReplaceInline(TEXT("file=\"../"), TEXT("file=\""), ESearchCase::CaseSensitive); |
| 300 | bChanged = (XmlContent.Len() != Prev); |
| 301 | } |
| 302 | } |
| 303 | |
| 304 | XmlContent.ReplaceInline(TEXT("\\"), TEXT("/")); |
| 305 | |
| 306 | { |