awacke1 commited on
Commit
eb688a8
·
verified ·
1 Parent(s): 6e7b462

Create index.html

Browse files
Files changed (1) hide show
  1. index.html +283 -0
index.html ADDED
@@ -0,0 +1,283 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Three.js Infinite World</title>
5
+ <style>
6
+ body { margin: 0; overflow: hidden; }
7
+ canvas { display: block; }
8
+ </style>
9
+ </head>
10
+ <body>
11
+ <script type="importmap">
12
+ {
13
+ "imports": {
14
+ "three": "https://unpkg.com/three@0.163.0/build/three.module.js",
15
+ "three/addons/": "https://unpkg.com/three@0.163.0/examples/jsm/"
16
+ }
17
+ }
18
+ </script>
19
+
20
+ <script type="module">
21
+ import * as THREE from 'three';
22
+
23
+ let scene, camera, renderer, playerMesh;
24
+ let raycaster, mouse;
25
+ const keysPressed = {};
26
+ const playerSpeed = 0.15;
27
+ let newlyPlacedObjects = []; // Track objects added THIS session for saving
28
+
29
+ // --- Access State from Streamlit ---
30
+ const allInitialObjects = window.ALL_INITIAL_OBJECTS || [];
31
+ const plotsMetadata = window.PLOTS_METADATA || [];
32
+ const selectedObjectType = window.SELECTED_OBJECT_TYPE || "None";
33
+ const plotWidth = window.PLOT_WIDTH || 50.0;
34
+ const plotDepth = window.PLOT_DEPTH || 50.0;
35
+ const globalState = window.GLOBAL_STATE || { objects: [] };
36
+
37
+ // --- Setup materials and scene
38
+ const groundMaterial = new THREE.MeshStandardMaterial({
39
+ color: 0x55aa55, roughness: 0.9, metalness: 0.1, side: THREE.DoubleSide
40
+ });
41
+
42
+ function init() {
43
+ scene = new THREE.Scene();
44
+ scene.background = new THREE.Color(0xabcdef);
45
+
46
+ const aspect = window.innerWidth / window.innerHeight;
47
+ camera = new THREE.PerspectiveCamera(60, aspect, 0.1, 4000);
48
+ camera.position.set(0, 15, 20);
49
+ camera.lookAt(0, 0, 0);
50
+ scene.add(camera);
51
+
52
+ setupLighting();
53
+ setupInitialGround();
54
+ setupPlayer();
55
+
56
+ raycaster = new THREE.Raycaster();
57
+ mouse = new THREE.Vector2();
58
+
59
+ renderer = new THREE.WebGLRenderer({ antialias: true });
60
+ renderer.setSize(window.innerWidth, window.innerHeight);
61
+ renderer.shadowMap.enabled = true;
62
+ renderer.shadowMap.type = THREE.PCFSoftShadowMap;
63
+ document.body.appendChild(renderer.domElement);
64
+
65
+ loadInitialObjects();
66
+ restoreUnsavedState();
67
+
68
+ // Event Listeners
69
+ document.addEventListener('mousemove', onMouseMove, false);
70
+ document.addEventListener('click', onDocumentClick, false);
71
+ window.addEventListener('resize', onWindowResize, false);
72
+ document.addEventListener('keydown', onKeyDown);
73
+ document.addEventListener('keyup', onKeyUp);
74
+
75
+ // Define global functions for Streamlit callbacks.
76
+ window.teleportPlayer = teleportPlayer;
77
+ window.getSaveDataAndPosition = getSaveDataAndPosition;
78
+ window.resetNewlyPlacedObjects = resetNewlyPlacedObjects;
79
+
80
+ console.log("Three.js Initialized. World ready.");
81
+ animate();
82
+ }
83
+
84
+ function setupLighting() {
85
+ const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
86
+ scene.add(ambientLight);
87
+ const directionalLight = new THREE.DirectionalLight(0xffffff, 1.0);
88
+ directionalLight.position.set(50, 150, 100);
89
+ directionalLight.castShadow = true;
90
+ directionalLight.shadow.mapSize.width = 4096;
91
+ directionalLight.shadow.mapSize.height = 4096;
92
+ directionalLight.shadow.camera.near = 0.5;
93
+ directionalLight.shadow.camera.far = 500;
94
+ directionalLight.shadow.camera.left = -150;
95
+ directionalLight.shadow.camera.right = 150;
96
+ directionalLight.shadow.camera.top = 150;
97
+ directionalLight.shadow.camera.bottom = -150;
98
+ directionalLight.shadow.bias = -0.001;
99
+ scene.add(directionalLight);
100
+ }
101
+
102
+ function setupInitialGround() {
103
+ console.log(`Setting up initial ground for ${plotsMetadata.length} plots.`);
104
+ plotsMetadata.forEach(plot => {
105
+ createGroundPlane(plot.grid_x, plot.grid_z, false);
106
+ });
107
+ if (plotsMetadata.length === 0) {
108
+ createGroundPlane(0, 0, false);
109
+ }
110
+ }
111
+
112
+ function createGroundPlane(gridX, gridZ, isPlaceholder) {
113
+ const gridKey = `${gridX}_${gridZ}`;
114
+ const groundGeometry = new THREE.PlaneGeometry(plotWidth, plotDepth);
115
+ const material = groundMaterial;
116
+ const groundMesh = new THREE.Mesh(groundGeometry, material);
117
+ groundMesh.rotation.x = -Math.PI / 2;
118
+ groundMesh.position.x = gridX * plotWidth + plotWidth / 2;
119
+ groundMesh.position.z = gridZ * plotDepth + plotDepth / 2;
120
+ groundMesh.receiveShadow = true;
121
+ scene.add(groundMesh);
122
+ }
123
+
124
+ function setupPlayer() {
125
+ const playerGeo = new THREE.CapsuleGeometry(0.4, 0.8, 4, 8);
126
+ const playerMat = new THREE.MeshStandardMaterial({ color: 0x0000ff, roughness: 0.6 });
127
+ playerMesh = new THREE.Mesh(playerGeo, playerMat);
128
+ playerMesh.position.set(plotWidth/2, 1, plotDepth/2);
129
+ playerMesh.castShadow = true;
130
+ scene.add(playerMesh);
131
+ }
132
+
133
+ function loadInitialObjects() {
134
+ console.log(`Loading ${allInitialObjects.length} initial objects from Python.`);
135
+ allInitialObjects.forEach(objData => { createAndPlaceObject(objData, false); });
136
+ // Also load objects from the global state (if any).
137
+ console.log(`Loading ${globalState.objects ? globalState.objects.length : 0} objects from global state.`);
138
+ if(globalState.objects) {
139
+ globalState.objects.forEach(objData => { createAndPlaceObject(objData, false); });
140
+ }
141
+ console.log("Finished loading objects.");
142
+ }
143
+
144
+ function createAndPlaceObject(objData, isNewObject) {
145
+ let loadedObject = null;
146
+ switch (objData.type) {
147
+ case "Simple House": loadedObject = createSimpleHouse(); break;
148
+ case "Tree": loadedObject = createTree(); break;
149
+ case "Rock": loadedObject = createRock(); break;
150
+ case "Fence Post": loadedObject = createFencePost(); break;
151
+ default: console.warn("Unknown object type:", objData.type); break;
152
+ }
153
+ if (loadedObject) {
154
+ // Prefer properties from objData using either new or old key names.
155
+ if(objData.position && objData.position.x !== undefined) {
156
+ loadedObject.position.set(objData.position.x, objData.position.y, objData.position.z);
157
+ } else if (objData.pos_x !== undefined) {
158
+ loadedObject.position.set(objData.pos_x, objData.pos_y, objData.pos_z);
159
+ }
160
+ if (objData.rotation) {
161
+ loadedObject.rotation.set(objData.rotation._x, objData.rotation._y, objData.rotation._z, objData.rotation._order || 'XYZ');
162
+ } else if (objData.rot_x !== undefined) {
163
+ loadedObject.rotation.set(objData.rot_x, objData.rot_y, objData.rot_z, objData.rot_order || 'XYZ');
164
+ }
165
+ loadedObject.userData.obj_id = objData.obj_id || loadedObject.userData.obj_id;
166
+ scene.add(loadedObject);
167
+ if (isNewObject) { newlyPlacedObjects.push(loadedObject); }
168
+ return loadedObject;
169
+ }
170
+ return null;
171
+ }
172
+
173
+ // Sample object creation functions.
174
+ function createSimpleHouse() {
175
+ const group = new THREE.Group();
176
+ const mat1 = new THREE.MeshStandardMaterial({color:0xffccaa});
177
+ const m1 = new THREE.Mesh(new THREE.BoxGeometry(2,1.5,2.5), mat1);
178
+ m1.position.y = 0.75;
179
+ group.add(m1);
180
+ return group;
181
+ }
182
+ function createTree() {
183
+ const group = new THREE.Group();
184
+ const mat1 = new THREE.MeshStandardMaterial({color:0x8B4513});
185
+ const m1 = new THREE.Mesh(new THREE.CylinderGeometry(0.3,0.4,2,8), mat1);
186
+ m1.position.y = 1;
187
+ group.add(m1);
188
+ const mat2 = new THREE.MeshStandardMaterial({color:0x228B22});
189
+ const m2 = new THREE.Mesh(new THREE.IcosahedronGeometry(1.2,0), mat2);
190
+ m2.position.y = 2.8;
191
+ group.add(m2);
192
+ return group;
193
+ }
194
+ function createRock() {
195
+ const mat = new THREE.MeshStandardMaterial({color:0xaaaaaa});
196
+ const rock = new THREE.Mesh(new THREE.IcosahedronGeometry(0.7,0), mat);
197
+ rock.position.y = 0.35;
198
+ rock.rotation.set(Math.random()*Math.PI, Math.random()*Math.PI, 0);
199
+ return rock;
200
+ }
201
+ function createFencePost() {
202
+ const mat = new THREE.MeshStandardMaterial({color:0xdeb887});
203
+ const post = new THREE.Mesh(new THREE.BoxGeometry(0.2,1.5,0.2), mat);
204
+ post.position.y = 0.75;
205
+ return post;
206
+ }
207
+
208
+ function onMouseMove(event) {
209
+ mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
210
+ mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
211
+ }
212
+
213
+ function onDocumentClick(event) {
214
+ if (selectedObjectType === "None") return;
215
+ raycaster.setFromCamera(mouse, camera);
216
+ const intersects = raycaster.intersectObjects(scene.children);
217
+ if (intersects.length > 0) {
218
+ const intersectPoint = intersects[0].point;
219
+ let newObject = null;
220
+ switch (selectedObjectType) {
221
+ case "Simple House": newObject = createSimpleHouse(); break;
222
+ case "Tree": newObject = createTree(); break;
223
+ case "Rock": newObject = createRock(); break;
224
+ case "Fence Post": newObject = createFencePost(); break;
225
+ default: return;
226
+ }
227
+ if (newObject) {
228
+ newObject.position.copy(intersectPoint);
229
+ scene.add(newObject);
230
+ newlyPlacedObjects.push(newObject);
231
+ console.log(`Placed new ${selectedObjectType}. Total new objects: ${newlyPlacedObjects.length}`);
232
+ }
233
+ }
234
+ }
235
+
236
+ function onKeyDown(event) { keysPressed[event.code] = true; }
237
+ function onKeyUp(event) { keysPressed[event.code] = false; }
238
+
239
+ function teleportPlayer(targetX, targetZ) {
240
+ if (playerMesh) {
241
+ playerMesh.position.x = targetX;
242
+ playerMesh.position.z = targetZ;
243
+ camera.position.x = targetX;
244
+ camera.position.z = targetZ + 20;
245
+ console.log("Player teleported.");
246
+ }
247
+ }
248
+
249
+ // This function packages new object data and player position for Streamlit.
250
+ function getSaveDataAndPosition() {
251
+ const objectsToSave = newlyPlacedObjects.map(obj => {
252
+ return {
253
+ obj_id: obj.userData.obj_id,
254
+ type: selectedObjectType,
255
+ position: { x: obj.position.x, y: obj.position.y, z: obj.position.z },
256
+ rotation: { _x: obj.rotation.x, _y: obj.rotation.y, _z: obj.rotation.z, _order: obj.rotation.order }
257
+ };
258
+ });
259
+ // Use the player mesh's position as the player position.
260
+ const playerPos = playerMesh ? { x: playerMesh.position.x, y: playerMesh.position.y, z: playerMesh.position.z } : {x:0, y:0, z:0};
261
+ const payload = {
262
+ playerPosition: playerPos,
263
+ objectsToSave: objectsToSave
264
+ };
265
+ console.log("Prepared payload:", payload);
266
+ // Clear newlyPlacedObjects after packaging.
267
+ newlyPlacedObjects = [];
268
+ return JSON.stringify(payload);
269
+ }
270
+
271
+ function resetNewlyPlacedObjects() {
272
+ newlyPlacedObjects = [];
273
+ }
274
+
275
+ function animate() {
276
+ requestAnimationFrame(animate);
277
+ renderer.render(scene, camera);
278
+ }
279
+
280
+ init();
281
+ </script>
282
+ </body>
283
+ </html>