Factory function to create Crystal Portal Gate in your world.
export function createAsset(THREE, options = {}) {
const root = new THREE.Group();
root.name = options.name ?? "Crystal Portal Gate";
root.userData = { assetRole: "building", generatedBy: 'public asset batch' };
const cache = new Map();
function mat(def) {
const key = [def.color, def.opacity ?? 1, def.metalness ?? 0, def.roughness ?? 0.72, def.emissive ?? '', def.emissiveIntensity ?? 0].join('|');
if (cache.has(key)) return cache.get(key);
const material = new THREE.MeshStandardMaterial({ color: def.color, roughness: def.roughness ?? 0.72, metalness: def.metalness ?? 0, opacity: def.opacity ?? 1, transparent: (def.opacity ?? 1) < 1, emissive: def.emissive ?? '#000000', emissiveIntensity: def.emissiveIntensity ?? 0 });
cache.set(key, material);
return material;
}
const defs = [{"id":"base","name":"Stone Base","position":[0,0.15,0],"boxes":[{"name":"base","size":[2.7,0.3,1.05],"position":[0,0,0],"color":"#718096"},{"name":"steps","size":[1.8,0.22,1.2],"position":[0,0.24,0.05],"color":"#94a3b8"}]},{"id":"left_crystal","name":"Left Crystal","position":[-1.05,1.25,0],"boxes":[{"name":"shaft","size":[0.42,1.65,0.42],"position":[0,0,0],"color":"#58c7ff","emissive":"#1f8fff","emissiveIntensity":0.25},{"name":"cap","size":[0.28,0.42,0.28],"position":[0,0.98,0],"color":"#c8f7ff","emissive":"#58c7ff","emissiveIntensity":0.45}],"rotation":[0,0,-0.12]},{"id":"right_crystal","name":"Right Crystal","position":[1.05,1.25,0],"boxes":[{"name":"shaft","size":[0.42,1.65,0.42],"position":[0,0,0],"color":"#7a8cff","emissive":"#586dff","emissiveIntensity":0.28},{"name":"cap","size":[0.28,0.42,0.28],"position":[0,0.98,0],"color":"#d8dcff","emissive":"#8793ff","emissiveIntensity":0.45}],"rotation":[0,0,0.12]},{"id":"rune_ring","name":"Rune Ring","position":[0,1.55,0],"boxes":[{"name":"top","size":[0.38,0.18,0.12],"position":[0,1.08,0.03],"color":"#ffd66e","emissive":"#ffb000","emissiveIntensity":0.35},{"name":"bottom","size":[0.38,0.18,0.12],"position":[0,-1.08,0.03],"color":"#ffd66e","emissive":"#ffb000","emissiveIntensity":0.35},{"name":"left","size":[0.18,0.38,0.12],"position":[-1.08,0,0.03],"color":"#ffd66e","emissive":"#ffb000","emissiveIntensity":0.35},{"name":"right","size":[0.18,0.38,0.12],"position":[1.08,0,0.03],"color":"#ffd66e","emissive":"#ffb000","emissiveIntensity":0.35},{"name":"diag A","size":[0.22,0.22,0.12],"position":[-0.75,0.75,0.03],"color":"#ffe39a"},{"name":"diag B","size":[0.22,0.22,0.12],"position":[0.75,-0.75,0.03],"color":"#ffe39a"}]},{"id":"portal_core","name":"Portal Core","position":[0,1.55,0.02],"boxes":[{"name":"glow","size":[1.35,1.75,0.08],"position":[0,0,0],"color":"#6ee7ff","opacity":0.72,"emissive":"#2dd4ff","emissiveIntensity":0.65},{"name":"star","size":[0.42,0.42,0.1],"position":[0,0,0.03],"color":"#fff","opacity":0.82,"emissive":"#fff","emissiveIntensity":0.8}]},{"id":"spark_particles","name":"Spark Particles","position":[0,1.75,0.14],"boxes":[{"name":"spark A","size":[0.12,0.12,0.12],"position":[-0.72,0.42,0],"color":"#fef08a","emissive":"#facc15","emissiveIntensity":0.6},{"name":"spark B","size":[0.1,0.1,0.1],"position":[0.68,-0.28,0],"color":"#c4b5fd","emissive":"#8b5cf6","emissiveIntensity":0.6},{"name":"spark C","size":[0.09,0.09,0.09],"position":[0.28,0.72,0],"color":"#a7f3d0"}]}];
const groups = new Map();
for (const def of defs) {
const group = new THREE.Group();
group.name = def.name;
group.position.set(def.position[0], def.position[1], def.position[2]);
if (def.rotation) group.rotation.set(def.rotation[0], def.rotation[1], def.rotation[2]);
group.userData.partId = def.id;
group.userData.editable = true;
group.userData.selection = { id: def.id, kind: 'asset', label: def.name };
for (const item of def.boxes) {
const mesh = new THREE.Mesh(new THREE.BoxGeometry(item.size[0], item.size[1], item.size[2]), mat(item));
mesh.name = item.name;
mesh.position.set(item.position[0], item.position[1], item.position[2]);
if (item.rotation) mesh.rotation.set(item.rotation[0], item.rotation[1], item.rotation[2]);
mesh.castShadow = true;
mesh.receiveShadow = true;
group.add(mesh);
}
groups.set(def.id, group);
}
for (const def of defs) (def.parent ? groups.get(def.parent) : root).add(groups.get(def.id));
const scale = Number(options.scale ?? 1);
root.scale.setScalar(Number.isFinite(scale) ? scale : 1);
return root;
}