mujoco-react 8.1.0 → 8.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +66 -45
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/core/SceneLoader.ts +77 -51
package/dist/index.js
CHANGED
|
@@ -391,6 +391,18 @@ function sceneObjectToXml(obj) {
|
|
|
391
391
|
const condim = obj.condim ? ` condim="${obj.condim}"` : "";
|
|
392
392
|
return `<body name="${obj.name}" pos="${pos}">${joint}<geom type="${obj.type}" size="${size}" rgba="${rgba}" contype="1" conaffinity="1"${mass}${friction}${solref}${solimp}${condim}/></body>`;
|
|
393
393
|
}
|
|
394
|
+
function ensureDir(mujoco, fname) {
|
|
395
|
+
const dirParts = fname.split("/");
|
|
396
|
+
dirParts.pop();
|
|
397
|
+
let currentPath = "/working";
|
|
398
|
+
for (const part of dirParts) {
|
|
399
|
+
currentPath += "/" + part;
|
|
400
|
+
try {
|
|
401
|
+
mujoco.FS.mkdir(currentPath);
|
|
402
|
+
} catch {
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
}
|
|
394
406
|
async function loadScene(mujoco, config, onProgress) {
|
|
395
407
|
try {
|
|
396
408
|
mujoco.FS.unmount("/working");
|
|
@@ -402,66 +414,75 @@ async function loadScene(mujoco, config, onProgress) {
|
|
|
402
414
|
}
|
|
403
415
|
const baseUrl = config.src.endsWith("/") ? config.src : config.src + "/";
|
|
404
416
|
const downloaded = /* @__PURE__ */ new Set();
|
|
405
|
-
const
|
|
417
|
+
const xmlQueue = [config.sceneFile];
|
|
418
|
+
const assetFiles = [];
|
|
406
419
|
const parser = new DOMParser();
|
|
407
|
-
while (
|
|
408
|
-
const fname =
|
|
420
|
+
while (xmlQueue.length > 0) {
|
|
421
|
+
const fname = xmlQueue.shift();
|
|
409
422
|
if (downloaded.has(fname)) continue;
|
|
410
423
|
downloaded.add(fname);
|
|
424
|
+
if (!fname.endsWith(".xml")) {
|
|
425
|
+
assetFiles.push(fname);
|
|
426
|
+
continue;
|
|
427
|
+
}
|
|
411
428
|
onProgress?.(`Downloading ${fname}...`);
|
|
412
429
|
const res = await fetch(baseUrl + fname);
|
|
413
430
|
if (!res.ok) {
|
|
414
431
|
console.warn(`Failed to fetch ${fname}: ${res.status} ${res.statusText}`);
|
|
415
432
|
continue;
|
|
416
433
|
}
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
if (fname.endsWith(".xml")) {
|
|
428
|
-
let text = await res.text();
|
|
429
|
-
for (const patch of config.xmlPatches ?? []) {
|
|
430
|
-
if (fname.endsWith(patch.target) || fname === patch.target) {
|
|
431
|
-
if (patch.replace) {
|
|
432
|
-
const [from, to] = patch.replace;
|
|
433
|
-
if (text.includes(from)) {
|
|
434
|
-
text = text.replace(from, to);
|
|
435
|
-
} else {
|
|
436
|
-
const preview = from.length > 80 ? `${from.slice(0, 80)}...` : from;
|
|
437
|
-
console.warn(`XML patch replace pattern not found in ${fname}: "${preview}"`);
|
|
438
|
-
}
|
|
434
|
+
let text = await res.text();
|
|
435
|
+
for (const patch of config.xmlPatches ?? []) {
|
|
436
|
+
if (fname.endsWith(patch.target) || fname === patch.target) {
|
|
437
|
+
if (patch.replace) {
|
|
438
|
+
const [from, to] = patch.replace;
|
|
439
|
+
if (text.includes(from)) {
|
|
440
|
+
text = text.replace(from, to);
|
|
441
|
+
} else {
|
|
442
|
+
const preview = from.length > 80 ? `${from.slice(0, 80)}...` : from;
|
|
443
|
+
console.warn(`XML patch replace pattern not found in ${fname}: "${preview}"`);
|
|
439
444
|
}
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
console.warn(`XML patch inject failed in ${fname}: could not find tag end after "${patch.injectAfter}"`);
|
|
448
|
-
}
|
|
445
|
+
}
|
|
446
|
+
if (patch.inject && patch.injectAfter) {
|
|
447
|
+
const idx = text.indexOf(patch.injectAfter);
|
|
448
|
+
if (idx !== -1) {
|
|
449
|
+
const tagEnd = text.indexOf(">", idx + patch.injectAfter.length);
|
|
450
|
+
if (tagEnd !== -1) {
|
|
451
|
+
text = text.slice(0, tagEnd + 1) + patch.inject + text.slice(tagEnd + 1);
|
|
449
452
|
} else {
|
|
450
|
-
|
|
451
|
-
console.warn(`XML patch inject anchor not found in ${fname}: "${preview}"`);
|
|
453
|
+
console.warn(`XML patch inject failed in ${fname}: could not find tag end after "${patch.injectAfter}"`);
|
|
452
454
|
}
|
|
455
|
+
} else {
|
|
456
|
+
const preview = patch.injectAfter.length > 80 ? `${patch.injectAfter.slice(0, 80)}...` : patch.injectAfter;
|
|
457
|
+
console.warn(`XML patch inject anchor not found in ${fname}: "${preview}"`);
|
|
453
458
|
}
|
|
454
459
|
}
|
|
455
460
|
}
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
|
|
461
|
+
}
|
|
462
|
+
if (fname === config.sceneFile && config.sceneObjects?.length) {
|
|
463
|
+
const xml = config.sceneObjects.map((obj) => sceneObjectToXml(obj)).join("");
|
|
464
|
+
text = text.replace("</worldbody>", xml + "</worldbody>");
|
|
465
|
+
}
|
|
466
|
+
ensureDir(mujoco, fname);
|
|
467
|
+
mujoco.FS.writeFile(`/working/${fname}`, text);
|
|
468
|
+
scanDependencies(text, fname, parser, downloaded, xmlQueue);
|
|
469
|
+
}
|
|
470
|
+
if (assetFiles.length > 0) {
|
|
471
|
+
onProgress?.(`Downloading ${assetFiles.length} assets...`);
|
|
472
|
+
const results = await Promise.all(
|
|
473
|
+
assetFiles.map(async (fname) => {
|
|
474
|
+
const res = await fetch(baseUrl + fname);
|
|
475
|
+
if (!res.ok) {
|
|
476
|
+
console.warn(`Failed to fetch ${fname}: ${res.status} ${res.statusText}`);
|
|
477
|
+
return null;
|
|
478
|
+
}
|
|
479
|
+
return { fname, buffer: new Uint8Array(await res.arrayBuffer()) };
|
|
480
|
+
})
|
|
481
|
+
);
|
|
482
|
+
for (const result of results) {
|
|
483
|
+
if (!result) continue;
|
|
484
|
+
ensureDir(mujoco, result.fname);
|
|
485
|
+
mujoco.FS.writeFile(`/working/${result.fname}`, result.buffer);
|
|
465
486
|
}
|
|
466
487
|
}
|
|
467
488
|
onProgress?.("Loading model...");
|