@needle-tools/engine
Version:
Needle Engine is a web-based runtime for 3D apps. It runs on your machine for development with great integrations into editors like Unity or Blender - and can be deployed onto any device! It is flexible, extensible and networking and XR are built-in.
110 lines (96 loc) • 4.19 kB
text/typescript
import { DataTextureLoader,EquirectangularReflectionMapping, Object3D, PerspectiveCamera, Scene, Vector3 } from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import { AssetReference } from "./engine_addressables.js";
import { getBoundingBox } from "./engine_three_utils.js";
declare type ComparisonSceneOptions = {
/**
* An array of model urls to load
*/
files: string[];
/**
* Optional dom element to attach the orbit controls to. By default this should be the WebGLRenderer.domElement
*/
domElement?: HTMLElement;
/**
* Can be a .hdr or .exr file url
*/
environment?: string;
}
/**
* A collection of utility methods for quickly spinning up test environments
*/
export class TestSceneUtils {
/**
* Use this method to quickly setup a scene to compare multiple models.
* @example
* ```ts
* const files = [
* "https://threejs.org/examples/models/gltf/RobotExpressive/RobotExpressive.glb",
* "https://threejs.org/examples/models/gltf/Lantern/glTF-Binary/Lantern.glb",
* ];
* const { scene, camera } = await TestUtils.createComparisonScene({ files });
* // this could now be assigned to the Needle Engine Context
* context.scene = scene;
* context.mainCamera = camera;
* ```
*/
static async createComparisonScene(opts: ComparisonSceneOptions) {
const { files } = opts;
const promises = Promise.all(files.map(file => new AssetReference(file).loadAssetAsync()));
const results = await promises;
const scene = new Scene();
let offset = 0;
for (const result of results) {
if (result instanceof Object3D) {
result.position.y = offset;
scene.add(result);
const box = getBoundingBox([result]);
offset += box.getSize(new Vector3()).y;
offset += .1;
}
}
const camera = new PerspectiveCamera(20);
scene.add(camera);
// Load an environment map
const environmentUrl = opts.environment || "https://dl.polyhaven.org/file/ph-assets/HDRIs/exr/1k/studio_small_09_1k.exr";
if (environmentUrl) {
let loader: DataTextureLoader | null = null;
if (environmentUrl.endsWith(".hdr")) {
const RGBELoader = (await import("three/examples/jsm/loaders/RGBELoader.js")).RGBELoader;
loader = new RGBELoader();
}
else if (environmentUrl.endsWith(".exr")) {
const EXRLoader = (await import("three/examples/jsm/loaders/EXRLoader.js")).EXRLoader;
loader = new EXRLoader();
}
if (loader) {
const envmap = await loader.loadAsync(environmentUrl).catch((e) => { console.error(e); return null; });
if (envmap) {
envmap.mapping = EquirectangularReflectionMapping;
envmap.needsUpdate = true;
scene.background = envmap;
scene.environment = envmap;
scene.backgroundBlurriness = .75;
}
}
else console.warn("Unsupported environment map format", environmentUrl);
}
const box = getBoundingBox(scene.children);
const center = box.getCenter(new Vector3());
const size = box.getSize(new Vector3());
const max = Math.max(size.x, size.y, size.z);
const distance = max / (2 * Math.tan(Math.PI * camera.fov / 360));
camera.position.set(center.x, center.y, distance);
camera.lookAt(center);
const orbit = new OrbitControls(camera, opts.domElement || document.body);
orbit.target = center;
orbit.update();
const element = (opts.domElement || document.body).getBoundingClientRect();
camera.aspect = element.width / element.height;
camera.updateProjectionMatrix();
return {
scene,
camera
}
}
}