Spaces:
Sleeping
Sleeping
Update index.html
Browse files- index.html +36 -42
index.html
CHANGED
@@ -1,7 +1,7 @@
|
|
1 |
<!DOCTYPE html>
|
2 |
<html>
|
3 |
<head>
|
4 |
-
<title>Three.js Synced World (DB Backend)</title>
|
5 |
<style>
|
6 |
body { margin: 0; overflow: hidden; }
|
7 |
canvas { display: block; }
|
@@ -20,7 +20,7 @@
|
|
20 |
<script type="module">
|
21 |
import * as THREE from 'three';
|
22 |
|
23 |
-
//
|
24 |
let scene, camera, renderer, playerMesh;
|
25 |
let raycaster, mouse;
|
26 |
const keysPressed = {};
|
@@ -28,67 +28,61 @@
|
|
28 |
let newlyPlacedObjects = []; // For sessionStorage persistence between non-save reruns
|
29 |
const placeholderPlots = new Set();
|
30 |
const groundMeshes = {};
|
31 |
-
const allRenderedObjects = {}; //
|
32 |
|
33 |
-
const SESSION_STORAGE_KEY = '
|
34 |
|
35 |
-
// ---
|
36 |
const allInitialObjects = window.ALL_INITIAL_OBJECTS || []; // From DB
|
37 |
const plotsMetadata = window.PLOTS_METADATA || []; // From DB
|
38 |
const selectedObjectType = window.SELECTED_OBJECT_TYPE || "None";
|
39 |
const plotWidth = window.PLOT_WIDTH || 50.0;
|
40 |
const plotDepth = window.PLOT_DEPTH || 50.0;
|
41 |
|
|
|
42 |
const groundMaterial = new THREE.MeshStandardMaterial({ color: 0x55aa55, roughness: 0.9, metalness: 0.1, side: THREE.DoubleSide });
|
43 |
const placeholderGroundMaterial = new THREE.MeshStandardMaterial({ color: 0x448844, roughness: 0.95, metalness: 0.1, side: THREE.DoubleSide });
|
44 |
|
45 |
-
|
46 |
-
// --- init() function ---
|
47 |
function init() {
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(window.innerWidth, window.innerHeight); renderer.shadowMap.enabled = true; renderer.shadowMap.type = THREE.PCFSoftShadowMap; document.body.appendChild(renderer.domElement);
|
54 |
|
55 |
-
loadInitialObjects(); // Load from DB data
|
56 |
-
restoreUnsavedState(); // Restore
|
57 |
|
58 |
-
// Event Listeners
|
59 |
document.addEventListener('mousemove', onMouseMove, false); document.addEventListener('click', onDocumentClick, false); window.addEventListener('resize', onWindowResize, false); document.addEventListener('keydown', onKeyDown); document.addEventListener('keyup', onKeyUp);
|
60 |
-
|
61 |
-
// Define global functions needed by Python
|
62 |
window.teleportPlayer = teleportPlayer;
|
63 |
-
window.getSaveDataAndPosition = getSaveDataAndPosition;
|
64 |
-
// window.resetNewlyPlacedObjects = resetNewlyPlacedObjects; // No longer needed
|
65 |
|
66 |
-
console.log("Three.js Initialized (DB Backend
|
67 |
animate();
|
68 |
}
|
69 |
|
70 |
-
// ---
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
function setupPlayer() { const g=new THREE.CapsuleGeometry(0.4,0.8,4,8); const m=new THREE.MeshStandardMaterial({color:0x0000ff,roughness:0.6}); playerMesh=new THREE.Mesh(g,m); playerMesh.position.set(plotWidth/2,0.8,plotDepth/2); playerMesh.castShadow=true; playerMesh.receiveShadow=true; scene.add(playerMesh); }
|
76 |
-
|
77 |
|
78 |
-
// ---
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
|
|
83 |
|
84 |
-
// ---
|
85 |
-
// ... (Keep these sessionStorage functions exactly as in the previous version) ...
|
86 |
-
function saveUnsavedState() { try { const d = newlyPlacedObjects.map(o => ({obj_id:o.userData.obj_id, type:o.userData.type, position:{x:o.position.x,y:o.position.y,z:o.position.z}, rotation:{_x:o.rotation.x,_y:o.rotation.y,_z:o.rotation.z,_order:o.rotation.order}})); sessionStorage.setItem(SESSION_STORAGE_KEY, JSON.stringify(d)); } catch(e) { console.error("Session save error:", e); } }
|
87 |
-
function restoreUnsavedState() { try { const s=sessionStorage.getItem(SESSION_STORAGE_KEY); if(s) { const d=JSON.parse(s); if(Array.isArray(d)) { let c=0; d.forEach(o => { if(createAndPlaceObject(o, true)) c++;}); console.log(`Restored ${c} unsaved objects.`); } } } catch(e) { console.error("Session restore error:", e); sessionStorage.removeItem(SESSION_STORAGE_KEY); } }
|
88 |
-
// function clearUnsavedSessionState() { sessionStorage.removeItem(SESSION_STORAGE_KEY); newlyPlacedObjects = []; console.log("Cleared unsaved session state."); } // Might be useful later
|
89 |
-
|
90 |
-
// --- Object Creation Functions ---
|
91 |
-
// ... (Keep these exactly as before, ensuring they assign userData.type and userData.obj_id) ...
|
92 |
function createObjectBase(type) { return { userData: { type: type, obj_id: THREE.MathUtils.generateUUID() } }; }
|
93 |
function createSimpleHouse() { const base = createObjectBase("Simple House"); const group = new THREE.Group(); Object.assign(group, base); const mat1=new THREE.MeshStandardMaterial({color:0xffccaa,roughness:0.8}), mat2=new THREE.MeshStandardMaterial({color:0xaa5533,roughness:0.7}); const m1=new THREE.Mesh(new THREE.BoxGeometry(2,1.5,2.5),mat1); m1.position.y=0.75;m1.castShadow=true;m1.receiveShadow=true;group.add(m1); const m2=new THREE.Mesh(new THREE.ConeGeometry(1.8,1,4),mat2); m2.position.y=1.5+0.5;m2.rotation.y=Math.PI/4;m2.castShadow=true;m2.receiveShadow=true;group.add(m2); return group; }
|
94 |
-
function createTree() { const base=createObjectBase("Tree"); const group=new THREE.Group(); Object.assign(group,base); const mat1=new THREE.MeshStandardMaterial({color:0x8B4513,roughness:0.9}), mat2=new THREE.MeshStandardMaterial({color:0x228B22,roughness:0.8}); const m1=new THREE.Mesh(new THREE.CylinderGeometry(0.3,0.4,2,8),mat1); m1.position.y=1; m1.castShadow=true;m1.receiveShadow=true;group.add(m1); const m2=
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
<!DOCTYPE html>
|
2 |
<html>
|
3 |
<head>
|
4 |
+
<title>Three.js Synced World (DB Backend v2)</title>
|
5 |
<style>
|
6 |
body { margin: 0; overflow: hidden; }
|
7 |
canvas { display: block; }
|
|
|
20 |
<script type="module">
|
21 |
import * as THREE from 'three';
|
22 |
|
23 |
+
// --- Variables ---
|
24 |
let scene, camera, renderer, playerMesh;
|
25 |
let raycaster, mouse;
|
26 |
const keysPressed = {};
|
|
|
28 |
let newlyPlacedObjects = []; // For sessionStorage persistence between non-save reruns
|
29 |
const placeholderPlots = new Set();
|
30 |
const groundMeshes = {};
|
31 |
+
const allRenderedObjects = {}; // Tracks all current objects by ID: id -> mesh/group
|
32 |
|
33 |
+
const SESSION_STORAGE_KEY = 'unsavedDbWorldState_v3'; // New key for safety
|
34 |
|
35 |
+
// --- State from Python ---
|
36 |
const allInitialObjects = window.ALL_INITIAL_OBJECTS || []; // From DB
|
37 |
const plotsMetadata = window.PLOTS_METADATA || []; // From DB
|
38 |
const selectedObjectType = window.SELECTED_OBJECT_TYPE || "None";
|
39 |
const plotWidth = window.PLOT_WIDTH || 50.0;
|
40 |
const plotDepth = window.PLOT_DEPTH || 50.0;
|
41 |
|
42 |
+
// --- Materials ---
|
43 |
const groundMaterial = new THREE.MeshStandardMaterial({ color: 0x55aa55, roughness: 0.9, metalness: 0.1, side: THREE.DoubleSide });
|
44 |
const placeholderGroundMaterial = new THREE.MeshStandardMaterial({ color: 0x448844, roughness: 0.95, metalness: 0.1, side: THREE.DoubleSide });
|
45 |
|
46 |
+
// --- Initialization ---
|
|
|
47 |
function init() {
|
48 |
+
scene = new THREE.Scene(); scene.background = new THREE.Color(0xabcdef);
|
49 |
+
const aspect = window.innerWidth / window.innerHeight; camera = new THREE.PerspectiveCamera(60, aspect, 0.1, 4000); camera.position.set(plotWidth / 2, 15, plotDepth / 2 + 20); camera.lookAt(plotWidth / 2, 0, plotDepth/2); scene.add(camera);
|
50 |
+
setupLighting(); setupInitialGround(); setupPlayer();
|
51 |
+
raycaster = new THREE.Raycaster(); mouse = new THREE.Vector2();
|
52 |
+
renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(window.innerWidth, window.innerHeight); renderer.shadowMap.enabled = true; renderer.shadowMap.type = THREE.PCFSoftShadowMap; document.body.appendChild(renderer.domElement);
|
|
|
53 |
|
54 |
+
loadInitialObjects(); // Load from DB data passed by Python
|
55 |
+
restoreUnsavedState(); // Restore objects placed THIS session before last reload
|
56 |
|
57 |
+
// Event Listeners & Global functions
|
58 |
document.addEventListener('mousemove', onMouseMove, false); document.addEventListener('click', onDocumentClick, false); window.addEventListener('resize', onWindowResize, false); document.addEventListener('keydown', onKeyDown); document.addEventListener('keyup', onKeyUp);
|
|
|
|
|
59 |
window.teleportPlayer = teleportPlayer;
|
60 |
+
window.getSaveDataAndPosition = getSaveDataAndPosition;
|
|
|
61 |
|
62 |
+
console.log("Three.js Initialized (DB Backend v3). World ready.");
|
63 |
animate();
|
64 |
}
|
65 |
|
66 |
+
// --- Setup Functions ---
|
67 |
+
function setupLighting() { const a=new THREE.AmbientLight(0xffffff,0.5); scene.add(a); const d=new THREE.DirectionalLight(0xffffff,1.0); d.position.set(50,150,100); d.castShadow=true; d.shadow.mapSize.width=4096; d.shadow.mapSize.height=4096; d.shadow.camera.near=0.5; d.shadow.camera.far=500; d.shadow.camera.left=-150; d.shadow.camera.right=150; d.shadow.camera.top=150; d.shadow.camera.bottom=-150; d.shadow.bias=-0.001; scene.add(d); }
|
68 |
+
function setupInitialGround() { plotsMetadata.forEach(p => {createGroundPlane(p.grid_x,p.grid_z,false);}); if(plotsMetadata.length===0) {createGroundPlane(0,0,false);} }
|
69 |
+
function createGroundPlane(gx,gz,isPlaceholder) { const k=`<span class="math-inline">\{gx\}\_</span>{gz}`; if(groundMeshes[k]) return; const geo=new THREE.PlaneGeometry(plotWidth,plotDepth); const mat=isPlaceholder?placeholderGroundMaterial:groundMaterial; const mesh=new THREE.Mesh(geo,mat); mesh.rotation.x=-Math.PI/2; mesh.position.y=-0.05; mesh.position.x=gx*plotWidth+plotWidth/2; mesh.position.z=gz*plotDepth+plotDepth/2; mesh.receiveShadow=true; mesh.userData.gridKey=k; scene.add(mesh); groundMeshes[k]=mesh; if(isPlaceholder){placeholderPlots.add(k);} }
|
70 |
+
function setupPlayer() { const g=new THREE.CapsuleGeometry(0.4,0.8,4,8); const m=new THREE.MeshStandardMaterial({color:0x0000ff,roughness:0.6}); playerMesh=new THREE.Mesh(g,m); playerMesh.position.set(plotWidth/2, 0.8, plotDepth/2); playerMesh.castShadow=true; playerMesh.receiveShadow=true; scene.add(playerMesh); }
|
|
|
|
|
71 |
|
72 |
+
// --- Object Loading / State Management ---
|
73 |
+
function loadInitialObjects() { console.log(`Loading ${allInitialObjects.length} initial objects from DB.`); clearAllRenderedObjects(); allInitialObjects.forEach(d => { createAndPlaceObject(d, false); }); console.log("Finished initial DB object load."); }
|
74 |
+
function clearAllRenderedObjects() { console.log("Clearing all rendered objects..."); Object.values(allRenderedObjects).forEach(o => {if (o.parent) {o.parent.remove(o);/* Dispose geometry/material here if memory becomes an issue */}}); for (const k in allRenderedObjects) delete allRenderedObjects[k]; newlyPlacedObjects = []; } // Clear session tracker too on full load
|
75 |
+
function createAndPlaceObject(objData, isNewObjectForSession) { let obj=null; switch(objData.type){case "Simple House":obj=createSimpleHouse();break; case "Tree":obj=createTree();break; case "Rock":obj=createRock();break; case "Fence Post":obj=createFencePost();break; default: console.warn("Unknown type:", objData.type); return null;} if(obj){ obj.userData.obj_id=objData.obj_id||obj.userData.obj_id; if(allRenderedObjects[obj.userData.obj_id]){ console.warn(`Duplicate obj ID load skipped: ${obj.userData.obj_id}`); return null;} if(objData.position&&objData.position.x!==undefined){obj.position.set(objData.position.x,objData.position.y,objData.position.z);}else if(objData.pos_x!==undefined){obj.position.set(objData.pos_x,objData.pos_y,objData.pos_z);}else{obj.position.set(0,0.5,0);} if(objData.rotation){obj.rotation.set(objData.rotation._x,objData.rotation._y,objData.rotation._z,objData.rotation._order||'XYZ');}else if(objData.rot_x!==undefined){obj.rotation.set(objData.rot_x,objData.rot_y,objData.rot_z,objData.rot_order||'XYZ');} scene.add(obj); allRenderedObjects[obj.userData.obj_id]=obj; if(isNewObjectForSession){newlyPlacedObjects.push(obj);} return obj; } return null; }
|
76 |
+
function saveUnsavedState() { try { const d = newlyPlacedObjects.map(o => ({obj_id:o.userData.obj_id, type:o.userData.type, position:{x:o.position.x,y:o.position.y,z:o.position.z}, rotation:{_x:o.rotation.x,_y:o.rotation.y,_z:o.rotation.z,_order:o.rotation.order}})); sessionStorage.setItem(SESSION_STORAGE_KEY, JSON.stringify(d)); } catch(e) { console.error("Session save error:", e); } }
|
77 |
+
function restoreUnsavedState() { try { const s=sessionStorage.getItem(SESSION_STORAGE_KEY); if(s) { const d=JSON.parse(s); if(Array.isArray(d)) { let c=0; d.forEach(o => { if(createAndPlaceObject(o, true)) c++;}); console.log(`Restored ${c} unsaved objects from session.`); } } } catch(e) { console.error("Session restore error:", e); sessionStorage.removeItem(SESSION_STORAGE_KEY); } }
|
78 |
|
79 |
+
// --- Object Creation Primitives (Ensure userData includes obj_id) ---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
80 |
function createObjectBase(type) { return { userData: { type: type, obj_id: THREE.MathUtils.generateUUID() } }; }
|
81 |
function createSimpleHouse() { const base = createObjectBase("Simple House"); const group = new THREE.Group(); Object.assign(group, base); const mat1=new THREE.MeshStandardMaterial({color:0xffccaa,roughness:0.8}), mat2=new THREE.MeshStandardMaterial({color:0xaa5533,roughness:0.7}); const m1=new THREE.Mesh(new THREE.BoxGeometry(2,1.5,2.5),mat1); m1.position.y=0.75;m1.castShadow=true;m1.receiveShadow=true;group.add(m1); const m2=new THREE.Mesh(new THREE.ConeGeometry(1.8,1,4),mat2); m2.position.y=1.5+0.5;m2.rotation.y=Math.PI/4;m2.castShadow=true;m2.receiveShadow=true;group.add(m2); return group; }
|
82 |
+
function createTree() { const base=createObjectBase("Tree"); const group=new THREE.Group(); Object.assign(group,base); const mat1=new THREE.MeshStandardMaterial({color:0x8B4513,roughness:0.9}), mat2=new THREE.MeshStandardMaterial({color:0x228B22,roughness:0.8}); const m1=new THREE.Mesh(new THREE.CylinderGeometry(0.3,0.4,2,8),mat1); m1.position.y=1; m1.castShadow=true;m1.receiveShadow=true;group.add(m1); const m2=new THREE.Mesh(new THREE.IcosahedronGeometry(1.2,0),mat2); m2.position.y=2.8; m2.castShadow=true;m2.receiveShadow=true;group.add(m2); return group; }
|
83 |
+
function createRock() { const base=createObjectBase("Rock"); const mat=new THREE.MeshStandardMaterial({color:0xaaaaaa,roughness:0.8,metalness:0.1}); const rock=new THREE.Mesh(new THREE.IcosahedronGeometry(0.7,0),mat); Object.assign(rock,base); rock.position.y=0.35; rock.rotation.set(Math.random()*Math.PI, Math.random()*Math.PI, 0); rock.castShadow=true;rock.receiveShadow=true; return rock; }
|
84 |
+
function createFencePost() { const base=createObjectBase("Fence Post"); const mat=new THREE.MeshStandardMaterial({color:0xdeb887,roughness:0.9}); const post=new THREE.Mesh(new THREE.BoxGeometry(0.2,1.5,0.2),mat); Object.assign(post,base); post.position.y=0.75; post.castShadow=true;post.receiveShadow=true; return post; }
|
85 |
+
|
86 |
+
// --- Event Handlers ---
|
87 |
+
function onMouseMove(event) { mouse.x=(event.clientX/window.innerWidth)*2-1; mouse.y=-(event.clientY/window.innerHeight)*2+1; }
|
88 |
+
function onDocumentClick(event) { if (selectedObjectType==='None') return; const grounds=Object.values(groundMeshes); if (grounds.length===0) return; raycaster.setFromCamera(mouse,
|