mirror of
https://github.com/NousResearch/atropos.git
synced 2026-04-26 17:13:09 +00:00
linting, moved to community
This commit is contained in:
commit
2efb690a24
10 changed files with 1341 additions and 0 deletions
|
|
@ -0,0 +1,25 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>MVP Spatial RL Visualization</title>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="info">
|
||||
<h1>MVP Spatial RL Visualization</h1>
|
||||
<p>Status: <span id="status">Connecting...</span></p>
|
||||
<p>Task: <span id="taskDescription">N/A</span></p>
|
||||
</div>
|
||||
<div id="visualizationContainer">
|
||||
<!-- Canvas will be inserted here by Three.js -->
|
||||
</div>
|
||||
|
||||
<!-- Three.js Library -->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
|
||||
<!-- Optional: OrbitControls for camera interaction -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.js"></script>
|
||||
|
||||
<script type="module" src="main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
186
environments/community/padres_spatial/visualization/main.js
Normal file
186
environments/community/padres_spatial/visualization/main.js
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
// Make sure OrbitControls is loaded from CDN or included if you use it
|
||||
// If OrbitControls is loaded globally via script tag: const OrbitControls = window.OrbitControls;
|
||||
// const TWEEN = window.TWEEN; // If using TWEEN CDN
|
||||
|
||||
// --- Global Variables ---
|
||||
let scene, camera, renderer, controls;
|
||||
const objectsInScene = new Map(); // Stores THREE.Mesh objects by their ID
|
||||
const websocketUrl = "ws://localhost:8765";
|
||||
let socket;
|
||||
|
||||
// --- DOM Elements ---
|
||||
const statusElement = document.getElementById('status');
|
||||
const taskDescriptionElement = document.getElementById('taskDescription');
|
||||
const visualizationContainer = document.getElementById('visualizationContainer');
|
||||
|
||||
// --- Initialization ---
|
||||
function init() {
|
||||
// Scene
|
||||
scene = new THREE.Scene();
|
||||
scene.background = new THREE.Color(0xdddddd);
|
||||
|
||||
// Camera
|
||||
camera = new THREE.PerspectiveCamera(75, visualizationContainer.clientWidth / visualizationContainer.clientHeight, 0.1, 1000);
|
||||
camera.position.set(3, 4, 5); // Adjusted camera position
|
||||
camera.lookAt(0, 0, 0);
|
||||
|
||||
// Renderer
|
||||
renderer = new THREE.WebGLRenderer({ antialias: true });
|
||||
renderer.setSize(visualizationContainer.clientWidth, visualizationContainer.clientHeight);
|
||||
renderer.setPixelRatio(window.devicePixelRatio);
|
||||
renderer.shadowMap.enabled = true; // Enable shadows
|
||||
visualizationContainer.appendChild(renderer.domElement);
|
||||
|
||||
// Lights
|
||||
const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
|
||||
scene.add(ambientLight);
|
||||
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
|
||||
directionalLight.position.set(5, 10, 7);
|
||||
directionalLight.castShadow = true; // Enable shadow casting for this light
|
||||
// Configure shadow properties for better quality (optional)
|
||||
directionalLight.shadow.mapSize.width = 1024;
|
||||
directionalLight.shadow.mapSize.height = 1024;
|
||||
directionalLight.shadow.camera.near = 0.5;
|
||||
directionalLight.shadow.camera.far = 50;
|
||||
scene.add(directionalLight);
|
||||
|
||||
// Ground Plane
|
||||
const planeGeometry = new THREE.PlaneGeometry(20, 20);
|
||||
const planeMaterial = new THREE.MeshStandardMaterial({ color: 0xaaaaaa, roughness: 0.8 });
|
||||
const groundPlane = new THREE.Mesh(planeGeometry, planeMaterial);
|
||||
groundPlane.rotation.x = -Math.PI / 2;
|
||||
groundPlane.receiveShadow = true; // Allow plane to receive shadows
|
||||
scene.add(groundPlane);
|
||||
|
||||
// Controls (Optional, if OrbitControls is loaded)
|
||||
if (typeof OrbitControls !== 'undefined') {
|
||||
controls = new OrbitControls(camera, renderer.domElement);
|
||||
controls.enableDamping = true;
|
||||
controls.dampingFactor = 0.05;
|
||||
controls.screenSpacePanning = false;
|
||||
controls.minDistance = 2;
|
||||
controls.maxDistance = 20;
|
||||
controls.maxPolarAngle = Math.PI / 2 - 0.05; // Prevent camera from going below ground
|
||||
}
|
||||
|
||||
// Handle window resize
|
||||
window.addEventListener('resize', onWindowResize, false);
|
||||
|
||||
// Start animation loop
|
||||
animate();
|
||||
|
||||
// Connect to WebSocket
|
||||
connectWebSocket();
|
||||
}
|
||||
|
||||
// --- WebSocket Handling ---
|
||||
function connectWebSocket() {
|
||||
socket = new WebSocket(websocketUrl);
|
||||
statusElement.textContent = "Connecting to WebSocket...";
|
||||
|
||||
socket.onopen = () => {
|
||||
statusElement.textContent = "Connected to Physics Server!";
|
||||
console.log("WebSocket connected.");
|
||||
// You could send a "client_ready" message if needed
|
||||
};
|
||||
|
||||
socket.onmessage = (event) => {
|
||||
try {
|
||||
const message = JSON.parse(event.data);
|
||||
// console.log("Message from server:", message);
|
||||
if (message.type === "scene_update" || message.type === "initial_scene") {
|
||||
updateScene(message.payload); // payload is List[ObjectData]
|
||||
if (message.type === "initial_scene" && message.task_description) {
|
||||
taskDescriptionElement.textContent = message.task_description;
|
||||
}
|
||||
} else if (message.type === "task_info") { // Example for updating task description
|
||||
taskDescriptionElement.textContent = message.description || "N/A";
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Error processing message from server:", e, event.data);
|
||||
}
|
||||
};
|
||||
|
||||
socket.onerror = (error) => {
|
||||
statusElement.textContent = "WebSocket Error!";
|
||||
console.error("WebSocket Error:", error);
|
||||
};
|
||||
|
||||
socket.onclose = () => {
|
||||
statusElement.textContent = "Disconnected. Attempting to reconnect in 3s...";
|
||||
console.log("WebSocket disconnected. Reconnecting in 3 seconds...");
|
||||
setTimeout(connectWebSocket, 3000); // Simple reconnect logic
|
||||
};
|
||||
}
|
||||
|
||||
// --- Three.js Scene Updates ---
|
||||
function updateScene(objectStates) { // objectStates is List of Dicts from server
|
||||
const receivedIds = new Set();
|
||||
|
||||
objectStates.forEach(objState => {
|
||||
receivedIds.add(objState.id);
|
||||
let threeObject = objectsInScene.get(objState.id);
|
||||
|
||||
if (!threeObject) { // Object doesn't exist, create it
|
||||
let geometry;
|
||||
const scale = objState.scale || [1,1,1];
|
||||
if (objState.type === "cube") {
|
||||
geometry = new THREE.BoxGeometry(scale[0], scale[1], scale[2]);
|
||||
} else if (objState.type === "sphere") {
|
||||
geometry = new THREE.SphereGeometry(scale[0] / 2, 32, 16); // Assume scale[0] is diameter
|
||||
} else {
|
||||
console.warn("Unsupported object type for visualization:", objState.type);
|
||||
geometry = new THREE.BoxGeometry(1, 1, 1); // Default placeholder
|
||||
}
|
||||
|
||||
const color = new THREE.Color(...(objState.color_rgba ? objState.color_rgba.slice(0,3) : [0.5, 0.5, 0.5]));
|
||||
const material = new THREE.MeshStandardMaterial({ color: color, roughness: 0.5, metalness: 0.1 });
|
||||
|
||||
threeObject = new THREE.Mesh(geometry, material);
|
||||
threeObject.name = objState.id; // Useful for debugging
|
||||
threeObject.castShadow = true; // Object casts shadows
|
||||
threeObject.receiveShadow = false; // Usually objects don't receive shadows on themselves unless complex
|
||||
|
||||
scene.add(threeObject);
|
||||
objectsInScene.set(objState.id, threeObject);
|
||||
}
|
||||
|
||||
// Update position and orientation
|
||||
if (objState.position) {
|
||||
threeObject.position.set(...objState.position);
|
||||
}
|
||||
if (objState.orientation_quaternion) {
|
||||
threeObject.quaternion.set(...objState.orientation_quaternion);
|
||||
}
|
||||
// TODO: Update color or other properties if they can change dynamically
|
||||
});
|
||||
|
||||
// Remove objects that are in Three.js scene but not in the new state
|
||||
objectsInScene.forEach((obj, id) => {
|
||||
if (!receivedIds.has(id)) {
|
||||
scene.remove(obj);
|
||||
obj.geometry.dispose(); // Dispose geometry
|
||||
obj.material.dispose(); // Dispose material
|
||||
objectsInScene.delete(id);
|
||||
console.log(`Removed object ${id} from scene.`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// --- Animation Loop & Resize ---
|
||||
function animate() {
|
||||
requestAnimationFrame(animate);
|
||||
if (controls) {
|
||||
controls.update(); // Only if OrbitControls is used
|
||||
}
|
||||
renderer.render(scene, camera);
|
||||
}
|
||||
|
||||
function onWindowResize() {
|
||||
camera.aspect = visualizationContainer.clientWidth / visualizationContainer.clientHeight;
|
||||
camera.updateProjectionMatrix();
|
||||
renderer.setSize(visualizationContainer.clientWidth / visualizationContainer.clientHeight);
|
||||
}
|
||||
|
||||
// --- Start Everything ---
|
||||
init();
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: Arial, sans-serif;
|
||||
overflow: hidden;
|
||||
background-color: #f0f0f0;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
#container {
|
||||
display: flex;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
#canvas-container {
|
||||
flex: 0.7;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
#info-panel {
|
||||
flex: 0.3;
|
||||
padding: 20px;
|
||||
background-color: #f5f5f5;
|
||||
border-left: 1px solid #ddd;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
#score {
|
||||
font-size: 1.2em;
|
||||
font-weight: bold;
|
||||
margin: 10px 0;
|
||||
padding: 10px;
|
||||
background-color: #fff;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
#objects-info {
|
||||
margin-top: 20px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
#info {
|
||||
padding: 10px;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
#info h1 {
|
||||
margin-top: 0;
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
#visualizationContainer {
|
||||
width: 100vw;
|
||||
height: 80vh;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
canvas {
|
||||
display: block;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue