Generate, reconstruct, inspect, and route production 3D assets for OpenMontage worlds using Atlas Cloud, fal.ai, licensed catalogs, and Blender.
日本語の概要は準備中です。原文の説明を表示しています。
Three.js interaction - raycasting, controls, mouse/touch input, object selection. Use when handling user input, implementing click detection, adding camera controls, or creating interactive 3D experiences.
インストールする前に、エージェントに与えられる指示の中身を確認できます。
import * as THREE from "three";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
// Camera controls
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
// Raycasting for click detection
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();
function onClick(event) {
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
const intersects = raycaster.intersectObjects(scene.children);
if (intersects.length > 0) {
console.log("Clicked:", intersects[0].object);
}
}
window.addEventListener("click", onClick);
const raycaster = new THREE.Raycaster();
// From camera (mouse picking)
raycaster.setFromCamera(mousePosition, camera);
// From any origin and direction
raycaster.set(origin, direction); // origin: Vector3, direction: normalized Vector3
// Get intersections
const intersects = raycaster.intersectObjects(objects, recursive);
// intersects array contains:
// {
// distance: number, // Distance from ray origin
// point: Vector3, // Intersection point in world coords
// face: Face3, // Intersected face
// faceIndex: number, // Face index
// object: Object3D, // Intersected object
// uv: Vector2, // UV coordinates at intersection
// uv1: Vector2, // Second UV channel
// normal: Vector3, // Interpolated face normal
// instanceId: number // For InstancedMesh
// }
const mouse = new THREE.Vector2();
function updateMouse(event) {
// For full window
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
}
// For specific canvas element
function updateMouseCanvas(event, canvas) {
const rect = canvas.getBoundingClientRect();
mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;
}
function onTouchStart(event) {
event.preventDefault();
if (event.touches.length === 1) {
const touch = event.touches[0];
mouse.x = (touch.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(touch.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
const intersects = raycaster.intersectObjects(clickableObjects);
if (intersects.length > 0) {
handleSelection(intersects[0]);
}
}
}
renderer.domElement.addEventListener("touchstart", onTouchStart);
const raycaster = new THREE.Raycaster();
// Near/far clipping (default: 0, Infinity)
raycaster.near = 0;
raycaster.far = 100;
// Line/Points precision
raycaster.params.Line.threshold = 0.1;
raycaster.params.Points.threshold = 0.1;
// Layers (only intersect objects on specific layers)
raycaster.layers.set(1);
// Only check specific objects
const clickables = [mesh1, mesh2, mesh3];
const intersects = raycaster.intersectObjects(clickables, false);
// Use layers for filtering
mesh1.layers.set(1); // Clickable layer
raycaster.layers.set(1);
// Throttle raycast for hover effects
let lastRaycast = 0;
function onMouseMove(event) {
const now = Date.now();
if (now - lastRaycast < 50) return; // 20fps max
lastRaycast = now;
// Raycast here
}
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
const controls = new OrbitControls(camera, renderer.domElement);
// Damping (smooth movement)
controls.enableDamping = true;
controls.dampingFactor = 0.05;
// Rotation limits
controls.minPolarAngle = 0; // Top
controls.maxPolarAngle = Math.PI / 2; // Horizon
controls.minAzimuthAngle = -Math.PI / 4; // Left
controls.maxAzimuthAngle = Math.PI / 4; // Right
// Zoom limits
controls.minDistance = 2;
controls.maxDistance = 50;
// Enable/disable features
controls.enableRotate = true;
controls.enableZoom = true;
controls.enablePan = true;
// Auto-rotate
controls.autoRotate = true;
controls.autoRotateSpeed = 2.0;
// Target (orbit point)
controls.target.set(0, 1, 0);
// Update in animation loop
function animate() {
controls.update(); // Required for damping and auto-rotate
renderer.render(scene, camera);
}
import { FlyControls } from "three/addons/controls/FlyControls.js";
const controls = new FlyControls(camera, renderer.domElement);
controls.movementSpeed = 10;
controls.rollSpeed = Math.PI / 24;
controls.dragToLook = true;
// Update with delta
function animate() {
controls.update(clock.getDelta());
renderer.render(scene, camera);
}
import { FirstPersonControls } from "three/addons/controls/FirstPersonControls.js";
const controls = new FirstPersonControls(camera, renderer.domElement);
controls.movementSpeed = 10;
controls.lookSpeed = 0.1;
controls.lookVertical = true;
controls.constrainVertical = true;
controls.verticalMin = Math.PI / 4;
controls.verticalMax = (Math.PI * 3) / 4;
function animate() {
controls.update(clock.getDelta());
}
import { PointerLockControls } from "three/addons/controls/PointerLockControls.js";
const controls = new PointerLockControls(camera, document.body);
// Lock pointer on click
document.addEventListener("click", () => {
controls.lock();
});
controls.addEventListener("lock", () => {
console.log("Pointer locked");
});
controls.addEventListener("unlock", () => {
console.log("Pointer unlocked");
});
// Movement
const velocity = new THREE.Vector3();
const direction = new THREE.Vector3();
const moveForward = false;
const moveBackward = false;
document.addEventListener("keydown", (event) => {
switch (event.code) {
case "KeyW":
moveForward = true;
break;
case "KeyS":
moveBackward = true;
break;
}
});
function animate() {
if (controls.isLocked) {
direction.z = Number(moveForward) - Number(moveBackward);
direction.normalize();
velocity.z -= direction.z * 0.1;
velocity.z *= 0.9; // Friction
controls.moveForward(-velocity.z);
}
}
import { TrackballControls } from "three/addons/controls/TrackballControls.js";
const controls = new TrackballControls(camera, renderer.domElement);
controls.rotateSpeed = 2.0;
controls.zoomSpeed = 1.2;
controls.panSpeed = 0.8;
controls.staticMoving = true;
function animate() {
controls.update();
}
import { MapControls } from "three/addons/controls/MapControls.js";
const controls = new MapControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.05;
controls.screenSpacePanning = false;
controls.maxPolarAngle = Math.PI / 2;
Gizmo for moving/rotating/scaling objects.
import { TransformControls } from "three/addons/controls/TransformControls.js";
const transformControls = new TransformControls(camera, renderer.domElement);
scene.add(transformControls);
// Attach to object
transformControls.attach(selectedMesh);
// Switch modes
transformControls.setMode("translate"); // 'translate', 'rotate', 'scale'
// Change space
transformControls.setSpace("local"); // 'local', 'world'
// Size
transformControls.setSize(1);
// Events
transformControls.addEventListener("dragging-changed", (event) => {
// Disable orbit controls while dragging
orbitControls.enabled = !event.value;
});
transformControls.addEventListener("change", () => {
renderer.render(scene, camera);
});
// Keyboard shortcuts
window.addEventListener("keydown", (event) => {
switch (event.key) {
case "g":
transformControls.setMode("translate");
break;
case "r":
transformControls.setMode("rotate");
break;
case "s":
transformControls.setMode("scale");
break;
case "Escape":
transformControls.detach();
break;
}
});
Drag objects directly.
import { DragControls } from "three/addons/controls/DragControls.js";
const draggableObjects = [mesh1, mesh2, mesh3];
const dragControls = new DragControls(
draggableObjects,
camera,
renderer.domElement,
);
dragControls.addEventListener("dragstart", (event) => {
orbitControls.enabled = false;
event.object.material.emissive.set(0xaaaaaa);
});
dragControls.addEventListener("drag", (event) => {
// Constrain to ground plane
event.object.position.y = 0;
});
dragControls.addEventListener("dragend", (event) => {
orbitControls.enabled = true;
event.object.material.emissive.set(0x000000);
});
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();
let selectedObject = null;
function onMouseDown(event) {
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
const intersects = raycaster.intersectObjects(selectableObjects);
// Deselect previous
if (selectedObject) {
selectedObject.material.emissive.set(0x000000);
}
// Select new
if (intersects.length > 0) {
selectedObject = intersects[0].object;
selectedObject.material.emissive.set(0x444444);
} else {
selectedObject = null;
}
}
import { SelectionBox } from "three/addons/interactive/SelectionBox.js";
import { SelectionHelper } from "three/addons/interactive/SelectionHelper.js";
const selectionBox = new SelectionBox(camera, scene);
const selectionHelper = new SelectionHelper(renderer, "selectBox"); // CSS class
document.addEventListener("pointerdown", (event) => {
selectionBox.startPoint.set(
(event.clientX / window.innerWidth) * 2 - 1,
-(event.clientY / window.innerHeight) * 2 + 1,
0.5,
);
});
document.addEventListener("pointermove", (event) => {
if (selectionHelper.isDown) {
selectionBox.endPoint.set(
(event.clientX / window.innerWidth) * 2 - 1,
-(event.clientY / window.innerHeight) * 2 + 1,
0.5,
);
}
});
document.addEventListener("pointerup", (event) => {
selectionBox.endPoint.set(
(event.clientX / window.innerWidth) * 2 - 1,
-(event.clientY / window.innerHeight) * 2 + 1,
0.5,
);
const selected = selectionBox.select();
console.log("Selected objects:", selected);
});
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();
let hoveredObject = null;
function onMouseMove(event) {
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
const intersects = raycaster.intersectObjects(hoverableObjects);
// Reset previous hover
if (hoveredObject) {
hoveredObject.material.color.set(hoveredObject.userData.originalColor);
document.body.style.cursor = "default";
}
// Apply new hover
if (intersects.length > 0) {
hoveredObject = intersects[0].object;
if (!hoveredObject.userData.originalColor) {
hoveredObject.userData.originalColor =
hoveredObject.material.color.getHex();
}
hoveredObject.material.color.set(0xff6600);
document.body.style.cursor = "pointer";
} else {
hoveredObject = null;
}
}
window.addEventListener("mousemove", onMouseMove);
const keys = {};
document.addEventListener("keydown", (event) => {
keys[event.code] = true;
});
document.addEventListener("keyup", (event) => {
keys[event.code] = false;
});
function update() {
const speed = 0.1;
if (keys["KeyW"]) player.position.z -= speed;
if (keys["KeyS"]) player.position.z += speed;
if (keys["KeyA"]) player.position.x -= speed;
if (keys["KeyD"]) player.position.x += speed;
if (keys["Space"]) player.position.y += speed;
if (keys["ShiftLeft"]) player.position.y -= speed;
}
function worldToScreen(position, camera) {
const vector = position.clone();
vector.project(camera);
return {
x: ((vector.x + 1) / 2) * window.innerWidth,
y: (-(vector.y - 1) / 2) * window.innerHeight,
};
}
// Position HTML element over 3D object
const screenPos = worldToScreen(mesh.position, camera);
element.style.left = screenPos.x + "px";
element.style.top = screenPos.y + "px";
function screenToWorld(screenX, screenY, camera, targetZ = 0) {
const vector = new THREE.Vector3(
(screenX / window.innerWidth) * 2 - 1,
-(screenY / window.innerHeight) * 2 + 1,
0.5,
);
vector.unproject(camera);
const dir = vector.sub(camera.position).normalize();
const distance = (targetZ - camera.position.z) / dir.z;
return camera.position.clone().add(dir.multiplyScalar(distance));
}
function getRayPlaneIntersection(mouse, camera, plane) {
const raycaster = new THREE.Raycaster();
raycaster.setFromCamera(mouse, camera);
const intersection = new THREE.Vector3();
raycaster.ray.intersectPlane(plane, intersection);
return intersection;
}
// Ground plane
const groundPlane = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0);
const worldPos = getRayPlaneIntersection(mouse, camera, groundPlane);
class InteractionManager {
constructor(camera, renderer, scene) {
this.camera = camera;
this.renderer = renderer;
this.scene = scene;
this.raycaster = new THREE.Raycaster();
this.mouse = new THREE.Vector2();
this.clickables = [];
this.bindEvents();
}
bindEvents() {
const canvas = this.renderer.domElement;
canvas.addEventListener("click", (e) => this.onClick(e));
canvas.addEventListener("mousemove", (e) => this.onMouseMove(e));
canvas.addEventListener("touchstart", (e) => this.onTouchStart(e));
}
updateMouse(event) {
const rect = this.renderer.domElement.getBoundingClientRect();
this.mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
this.mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;
}
getIntersects() {
this.raycaster.setFromCamera(this.mouse, this.camera);
return this.raycaster.intersectObjects(this.clickables, true);
}
onClick(event) {
this.updateMouse(event);
const intersects = this.getIntersects();
if (intersects.length > 0) {
const object = intersects[0].object;
if (object.userData.onClick) {
object.userData.onClick(intersects[0]);
}
}
}
addClickable(object, callback) {
this.clickables.push(object);
object.userData.onClick = callback;
}
dispose() {
// Remove event listeners
}
}
// Usage
const interaction = new InteractionManager(camera, renderer, scene);
interaction.addClickable(mesh, (intersect) => {
console.log("Clicked at:", intersect.point);
});
controls.enabled = false// Use simpler geometry for raycasting
const complexMesh = loadedModel;
const collisionMesh = new THREE.Mesh(
new THREE.BoxGeometry(1, 1, 1),
new THREE.MeshBasicMaterial({ visible: false }),
);
collisionMesh.userData.target = complexMesh;
clickables.push(collisionMesh);
threejs-fundamentals - Camera and scene setupthreejs-animation - Animating interactionsthreejs-shaders - Visual feedback effectsまだレビューはありません。使ってみた感想をお寄せください。
概要と使いどころ
Generate, reconstruct, inspect, and route production 3D assets for OpenMontage worlds using Atlas Cloud, fal.ai, licensed catalogs, and Blender.
日本語の概要は準備中です。原文の説明を表示しています。
AI music generation with ACE-Step 1.5 — background music, vocal tracks, covers, stem extraction for video production. Use when generating music, soundtracks, jingles, or working with audio stems. Triggers include background music, soundtrack, jingle, music generation, stem extraction, cover, style transfer, or musical composition tasks.
日本語の概要は準備中です。原文の説明を表示しています。
Build voice AI agents with ElevenLabs. Use when creating voice assistants, customer service bots, interactive voice characters, or any real-time voice conversation experience.
日本語の概要は準備中です。原文の説明を表示しています。
Generate AI videos from text prompts using multiple provider gateways. Use when: (1) Generating videos from text descriptions, (2) Creating AI-generated video clips for content production, (3) Image-to-video generation with a reference image, (4) Choosing between video generation providers (VEO, Kling, Sora, Runway, Seedance, MiniMax, Gemini Omni). Supports gateways: HeyGen API, fal.ai API, and the Gemini API (Gemini Omni Flash).
日本語の概要は準備中です。原文の説明を表示しています。
Generate AI videos from text prompts using multiple provider gateways. Use when: (1) Generating videos from text descriptions, (2) Creating AI-generated video clips for content production, (3) Image-to-video generation with a reference image, (4) Choosing between video generation providers (VEO, Kling, Sora, Runway, Seedance, MiniMax, Gemini Omni). Supports gateways: HeyGen API, fal.ai API, Kling official direct API, and the Gemini API (Gemini Omni Flash).
日本語の概要は準備中です。原文の説明を表示しています。
Generate or edit images and videos through the Atlas Cloud gateway. Use for Atlas-hosted Seedance 2.5/2.0, Gemini Omni Flash, MiniMax H3, Seedream 5.0, GPT Image 2, Nano Banana 2, or when one ATLASCLOUD_API_KEY should access multiple media model families.
日本語の概要は準備中です。原文の説明を表示しています。