feat: add browser-based MuJoCo simulation platform
build / setup (compute matrix) (pull_request) Has been cancelled
build / ${{ matrix.label }} (pull_request) Has been cancelled
build / macos-15-arm64-studio (pull_request) Has been cancelled
build / ubuntu-24.04-clang-18-studio (pull_request) Has been cancelled
build / ubuntu-24.04-gcc-14-studio (pull_request) Has been cancelled
build / windows-2025-ninja-studio (pull_request) Has been cancelled
build / ubuntu-24.04-clang-18-wasm (pull_request) Has been cancelled
build / ubuntu-24.04-clang-18-mjx (pull_request) Has been cancelled
lint / pre-commit (pull_request) Has been cancelled
build / setup (compute matrix) (pull_request) Has been cancelled
build / ${{ matrix.label }} (pull_request) Has been cancelled
build / macos-15-arm64-studio (pull_request) Has been cancelled
build / ubuntu-24.04-clang-18-studio (pull_request) Has been cancelled
build / ubuntu-24.04-gcc-14-studio (pull_request) Has been cancelled
build / windows-2025-ninja-studio (pull_request) Has been cancelled
build / ubuntu-24.04-clang-18-wasm (pull_request) Has been cancelled
build / ubuntu-24.04-clang-18-mjx (pull_request) Has been cancelled
lint / pre-commit (pull_request) Has been cancelled
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
import {LoadingManager,type Material,type Mesh,type Texture} from 'three';
|
||||
import {OBJExporter} from 'three/addons/exporters/OBJExporter.js';
|
||||
import {ColladaLoader} from 'three/addons/loaders/ColladaLoader.js';
|
||||
|
||||
const TRANSPARENT_PIXEL='data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=';
|
||||
|
||||
/**
|
||||
* 将 Collada 几何转换为 MuJoCo WASM 可读取的 OBJ。
|
||||
* ColladaLoader 会先统一为 Y-up;额外旋转到 MuJoCo 使用的 Z-up,并烘焙节点变换与单位缩放。
|
||||
*/
|
||||
export function convertDaeToObj(data:Uint8Array,path:string):Uint8Array {
|
||||
const manager=new LoadingManager();
|
||||
// 转换只需要几何。拦截贴图 URL,避免为浏览器内存文件发起无效网络请求。
|
||||
manager.setURLModifier(()=>TRANSPARENT_PIXEL);
|
||||
const loader=new ColladaLoader(manager);
|
||||
const text=new TextDecoder('utf-8').decode(data);
|
||||
const xml=new DOMParser().parseFromString(text,'application/xml');
|
||||
if(xml.querySelector('parsererror'))throw new Error('Collada XML 格式无效');
|
||||
const upAxis=xml.getElementsByTagName('up_axis')[0]?.textContent?.trim().toUpperCase()??'Y_UP';
|
||||
// 禁用 ColladaLoader 自带的 Z-up → Y-up 旋转,改为直接统一到 MuJoCo 的 Z-up。
|
||||
if(upAxis==='Z_UP')xml.getElementsByTagName('up_axis')[0]!.textContent='Y_UP';
|
||||
const normalized=new XMLSerializer().serializeToString(xml);
|
||||
const result=loader.parse(normalized,path.slice(0,path.lastIndexOf('/')+1));
|
||||
if(!result?.scene)throw new Error('Collada 文件无法解析');
|
||||
const scene=result.scene;
|
||||
if(upAxis==='Y_UP')scene.rotation.x+=Math.PI/2;
|
||||
else if(upAxis==='X_UP')scene.rotation.y-=Math.PI/2;
|
||||
scene.updateMatrixWorld(true);
|
||||
let meshCount=0;
|
||||
scene.traverse(object=>{
|
||||
const mesh=object as Mesh;
|
||||
if(!mesh.isMesh)return;
|
||||
meshCount+=1;
|
||||
const materials=Array.isArray(mesh.material)?mesh.material:[mesh.material];
|
||||
for(const material of materials)if(material)material.name='';
|
||||
});
|
||||
if(!meshCount)throw new Error('Collada 文件不包含可转换的三角网格');
|
||||
try {
|
||||
const output=new OBJExporter().parse(scene);
|
||||
if(!/^v\s/m.test(output)||!/^f\s/m.test(output))throw new Error('Collada 文件未生成有效三角面');
|
||||
return new TextEncoder().encode(output);
|
||||
} finally {
|
||||
scene.traverse(object=>{
|
||||
const mesh=object as Mesh;
|
||||
if(!mesh.isMesh)return;
|
||||
mesh.geometry?.dispose();
|
||||
const materials:Material[]=Array.isArray(mesh.material)?mesh.material:[mesh.material];
|
||||
for(const material of materials){
|
||||
for(const value of Object.values(material))if(value&&typeof value==='object'&&(value as Texture).isTexture)(value as Texture).dispose();
|
||||
material.dispose();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user