awacke1 commited on
Commit
ccc8baf
Β·
verified Β·
1 Parent(s): bf30843

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +474 -103
index.html CHANGED
@@ -1,124 +1,495 @@
1
- <!-- index.html -->
2
  <!DOCTYPE html>
3
  <html>
4
  <head>
5
- <meta charset="utf-8">
6
- <title>Three.js Infinite World</title>
7
- <style>body{margin:0;overflow:hidden;}canvas{display:block;}</style>
8
- <!-- State injected here -->
 
 
 
 
 
 
 
 
 
 
9
  </head>
10
  <body>
11
- <script type="importmap">
12
- {"imports":{"three":"https://unpkg.com/[email protected]/build/three.module.js","three/addons/":"https://unpkg.com/[email protected]/examples/jsm/"}}
13
- </script>
14
- <script type="module">
15
- import * as THREE from 'three';
 
 
 
16
 
17
- let scene, camera, renderer, playerMesh, raycaster, mouse;
18
- let newlyPlacedObjects = [];
19
- const groundMeshes = {}, placeholderPlots=new Set();
20
- const SESSION_KEY='unsavedInfiniteWorldState';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
 
22
- function init(){
23
- scene=new THREE.Scene(); scene.background=new THREE.Color(0xabcdef);
24
- camera=new THREE.PerspectiveCamera(60,window.innerWidth/window.innerHeight,0.1,4000);
25
- camera.position.set(0,15,20);camera.lookAt(0,0,0);
26
- setupLighting(); setupGround(); setupPlayer();
27
- raycaster=new THREE.Raycaster(); mouse=new THREE.Vector2();
28
- renderer=new THREE.WebGLRenderer({antialias:true});
29
- renderer.setSize(window.innerWidth,window.innerHeight);
30
- renderer.shadowMap.enabled=true; document.body.appendChild(renderer.domElement);
31
- loadObjects(); restoreUnsavedState();
32
- window.addEventListener('mousemove',onMouseMove,false);
33
- window.addEventListener('click',onDocClick,false);
34
- window.addEventListener('keydown',onKeyDown);
35
- window.addEventListener('keyup',onKeyUp);
36
- window.teleportPlayer=teleportPlayer;
37
- window.getSaveDataAndPosition=getSaveDataAndPosition;
38
- window.resetNewlyPlacedObjects=resetNewlyPlacedObjects;
39
- setInterval(pollGameState,5000);
40
- animate();
41
- }
42
 
43
- function setupLighting(){
44
- scene.add(new THREE.AmbientLight(0xffffff,0.5));
45
- const dir=new THREE.DirectionalLight(0xffffff,1);dir.position.set(50,150,100);
46
- dir.castShadow=true;scene.add(dir);
47
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
48
 
49
- function setupGround(){
50
- const plots=window.PLOTS_METADATA||[];
51
- (plots.length?plots:[{grid_x:0,grid_z:0}]).forEach(p=>createGround(p.grid_x,p.grid_z,false));
52
- }
 
 
 
 
 
 
 
 
 
 
 
 
53
 
54
- function createGround(x,z,ph=false){
55
- const key=`${x}_${z}`; if(groundMeshes[key])return;
56
- const geo=new THREE.PlaneGeometry(window.PLOT_WIDTH,window.PLOT_DEPTH);
57
- const mat=new THREE.MeshStandardMaterial({color:ph?0x448844:0x55aa55,side:THREE.DoubleSide});
58
- const m=new THREE.Mesh(geo,mat); m.rotation.x=-Math.PI/2;
59
- m.position.set(x*window.PLOT_WIDTH+window.PLOT_WIDTH/2,-0.05,z*window.PLOT_DEPTH+window.PLOT_DEPTH/2);
60
- scene.add(m); groundMeshes[key]=m; if(ph)placeholderPlots.add(key);
61
- }
 
 
62
 
63
- function setupPlayer(){
64
- const geo=new THREE.CapsuleGeometry(0.4,0.8,4,8);
65
- playerMesh=new THREE.Mesh(geo,new THREE.MeshStandardMaterial({color:0x0000ff,roughness:0.6}));
66
- playerMesh.position.set(window.PLOT_WIDTH/2,1.2,window.PLOT_DEPTH/2);
67
- scene.add(playerMesh);
68
- }
 
 
 
69
 
70
- function loadObjects(){
71
- (window.ALL_INITIAL_OBJECTS||[]).forEach(d=>createAndPlace(d,false));
72
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
 
74
- function createAndPlace(d,newObj){
75
- const obj=window.KITS[d.type]?createPrefabKit(d.type):null;
76
- if(!obj) return;
77
- obj.position.set(d.pos_x||d.position.x,d.pos_y||d.position.y,d.pos_z||d.position.z);
78
- scene.add(obj); if(newObj)newlyPlacedObjects.push(obj);
79
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
 
81
- function createPrefabKit(name){
82
- const kit=window.KITS[name]||{}; const grp=new THREE.Group();
83
- kit.modules?.forEach(m=>{const md=createModule(m); if(md)grp.add(md)});
84
- return grp;
85
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
86
 
87
- function createModule(n){
88
- switch(n){
89
- case 'Cube': return new THREE.Mesh(new THREE.BoxGeometry(1,1,1),new THREE.MeshStandardMaterial());
90
- case 'Sphere': return new THREE.Mesh(new THREE.SphereGeometry(0.5,16,16),new THREE.MeshStandardMaterial());
91
- case 'Cylinder': return new THREE.Mesh(new THREE.CylinderGeometry(0.5,0.5,1,12),new THREE.MeshStandardMaterial());
92
- case 'Cone': return new THREE.Mesh(new THREE.ConeGeometry(0.5,1,12),new THREE.MeshStandardMaterial());
93
- default: console.warn('Unknown module',n); return null;
94
- }
95
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96
 
97
- function onDocClick(ev){
98
- if(window.SELECTED_OBJECT_TYPE==='None')return; const candidates=Object.values(groundMeshes);
99
- raycaster.setFromCamera(mouse,camera); const hit=raycaster.intersectObjects(candidates);
100
- if(!hit.length)return; const pt=hit[0].point;
101
- const mesh=createPrefabKit(window.SELECTED_OBJECT_TYPE);
102
- if(mesh){mesh.position.copy(pt);scene.add(mesh);newlyPlacedObjects.push(mesh);saveUnsaved();}
103
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
104
 
105
- function saveUnsaved(){sessionStorage.setItem(SESSION_KEY,JSON.stringify(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}}))));}
106
- function restoreUnsavedState(){const s=sessionStorage.getItem(SESSION_KEY); if(s){JSON.parse(s).forEach(d=>createAndPlace(d,true));}}
107
- function resetNewlyPlacedObjects(){sessionStorage.removeItem(SESSION_KEY);newlyPlacedObjects=[];}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
 
109
- function getSaveDataAndPosition(){const objs=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}}));
110
- const pos={x:playerMesh.position.x,y:playerMesh.position.y,z:playerMesh.position.z};return JSON.stringify({playerPosition:pos,objectsToSave:objs});
111
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
 
113
- function teleportPlayer(x,z){playerMesh.position.set(x,playerMesh.position.y,z);camera.position.set(x,playerMesh.position.y+15,z+20);camera.lookAt(playerMesh.position);}
114
- function pollGameState(){console.log('Polling',window.GAME_STATE);}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
115
 
116
- function onMouseMove(e){mouse.x=(e.clientX/window.innerWidth)*2-1;mouse.y=-(e.clientY/window.innerHeight)*2+1;}
117
- function onKeyDown(e){/* movement logic */}
118
- function onKeyUp(e){}
119
-
120
- function animate(){requestAnimationFrame(animate);renderer.render(scene,camera);}
121
- init();
122
- </script>
123
  </body>
124
- </html>
 
 
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
+ <!-- New: Polling function for game state -->
10
+ <script>
11
+ // Poll the shared game state every 5 seconds (for demonstration)
12
+ function pollGameState() {
13
+ console.log("Polling updated game state:", window.GAME_STATE);
14
+ // Here you could update the scene based on the new state.
15
+ }
16
+ setInterval(pollGameState, 5000);
17
+ </script>
18
  </head>
19
  <body>
20
+ <script type="importmap">
21
+ {
22
+ "imports": {
23
+ "three": "https://unpkg.com/[email protected]/build/three.module.js",
24
+ "three/addons/": "https://unpkg.com/three@0.163.0/examples/jsm/"
25
+ }
26
+ }
27
+ </script>
28
 
29
+ <script type="module">
30
+ import * as THREE from 'three';
31
+ let scene, camera, renderer, playerMesh;
32
+ let raycaster, mouse;
33
+ const keysPressed = {};
34
+ const playerSpeed = 0.15;
35
+ let newlyPlacedObjects = []; // Track objects added THIS session for saving
36
+ const placeholderPlots = new Set();
37
+ const groundMeshes = {}; // Store ground mesh references
38
+ // --- Session Storage Key ---
39
+ const SESSION_STORAGE_KEY = 'unsavedInfiniteWorldState';
40
+ // --- Injected State from Streamlit ---
41
+ const allInitialObjects = window.ALL_INITIAL_OBJECTS || [];
42
+ const plotsMetadata = window.PLOTS_METADATA || [];
43
+ const selectedObjectType = window.SELECTED_OBJECT_TYPE || "None";
44
+ const plotWidth = window.PLOT_WIDTH || 50.0;
45
+ const plotDepth = window.PLOT_DEPTH || 50.0;
46
+ const groundMaterial = new THREE.MeshStandardMaterial({
47
+ color: 0x55aa55, roughness: 0.9, metalness: 0.1, side: THREE.DoubleSide
48
+ });
49
+ const placeholderGroundMaterial = new THREE.MeshStandardMaterial({
50
+ color: 0x448844, roughness: 0.95, metalness: 0.1, side: THREE.DoubleSide
51
+ });
52
 
53
+ // ─── Helper to tag every object/group with type & unique ID ──────────────
54
+ function createObjectBase(type) {
55
+ return { userData: { type: type, obj_id: THREE.MathUtils.generateUUID() } };
56
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
 
58
+ // ─── Primitive creators ─────────────────────────────────────────────────
59
+ function createSimpleHouse() {
60
+ const base = createObjectBase("Simple House");
61
+ const group = new THREE.Group();
62
+ Object.assign(group, base);
63
+ const mat1 = new THREE.MeshStandardMaterial({ color: 0xffccaa, roughness: 0.8 });
64
+ const mat2 = new THREE.MeshStandardMaterial({ color: 0xaa5533, roughness: 0.7 });
65
+ const m1 = new THREE.Mesh(new THREE.BoxGeometry(2, 1.5, 2.5), mat1);
66
+ m1.position.y = 1.5/2;
67
+ m1.castShadow = m1.receiveShadow = true;
68
+ group.add(m1);
69
+ const m2 = new THREE.Mesh(new THREE.ConeGeometry(1.8, 1, 4), mat2);
70
+ m2.position.y = 1.5 + 0.5;
71
+ m2.rotation.y = Math.PI/4;
72
+ m2.castShadow = m2.receiveShadow = true;
73
+ group.add(m2);
74
+ return group;
75
+ }
76
 
77
+ function createTree() {
78
+ const base = createObjectBase("Tree");
79
+ const group = new THREE.Group();
80
+ Object.assign(group, base);
81
+ const matTrunk = new THREE.MeshStandardMaterial({ color: 0x8B4513, roughness: 0.9 });
82
+ const matFoliage = new THREE.MeshStandardMaterial({ color: 0x228B22, roughness: 0.8 });
83
+ const trunk = new THREE.Mesh(new THREE.CylinderGeometry(0.3, 0.4, 2, 8), matTrunk);
84
+ trunk.position.y = 1;
85
+ trunk.castShadow = trunk.receiveShadow = true;
86
+ group.add(trunk);
87
+ const foliage = new THREE.Mesh(new THREE.IcosahedronGeometry(1.2, 0), matFoliage);
88
+ foliage.position.y = 2.8;
89
+ foliage.castShadow = foliage.receiveShadow = true;
90
+ group.add(foliage);
91
+ return group;
92
+ }
93
 
94
+ function createRock() {
95
+ const base = createObjectBase("Rock");
96
+ const mat = new THREE.MeshStandardMaterial({ color: 0xaaaaaa, roughness: 0.8, metalness: 0.1 });
97
+ const rock = new THREE.Mesh(new THREE.IcosahedronGeometry(0.7, 0), mat);
98
+ Object.assign(rock, base);
99
+ rock.position.y = 0.35;
100
+ rock.rotation.set(Math.random()*Math.PI, Math.random()*Math.PI, 0);
101
+ rock.castShadow = rock.receiveShadow = true;
102
+ return rock;
103
+ }
104
 
105
+ function createFencePost() {
106
+ const base = createObjectBase("Fence Post");
107
+ const mat = new THREE.MeshStandardMaterial({ color: 0xdeb887, roughness: 0.9 });
108
+ const post = new THREE.Mesh(new THREE.BoxGeometry(0.2, 1.5, 0.2), mat);
109
+ Object.assign(post, base);
110
+ post.position.y = 0.75;
111
+ post.castShadow = post.receiveShadow = true;
112
+ return post;
113
+ }
114
 
115
+ // ─── 1. Cyberpunk City Builder Kit ────────────────────────────────────────
116
+ function createCyberpunkWallPanel({
117
+ width=2, height=3, depth=0.2,
118
+ baseMat={ color:0x555555, metalness:0.8, roughness:0.4 },
119
+ trimMat={ emissive:0x00ffff, emissiveIntensity:1.2 },
120
+ position=new THREE.Vector3()
121
+ } = {}) {
122
+ const panelBase = new THREE.BoxGeometry(width, height, depth);
123
+ const panel = new THREE.Mesh(panelBase, new THREE.MeshStandardMaterial(baseMat));
124
+ panel.position.copy(position);
125
+ return panel;
126
+ }
127
+ function createRooftopACUnit({
128
+ width=1, height=0.5, depth=1,
129
+ matProps={ color:0x777777, roughness:0.7, metalness:0.3 },
130
+ position=new THREE.Vector3()
131
+ } = {}) {
132
+ const unit = new THREE.Mesh(
133
+ new THREE.BoxGeometry(width, height, depth),
134
+ new THREE.MeshStandardMaterial(matProps)
135
+ );
136
+ unit.position.copy(position);
137
+ return unit;
138
+ }
139
+ function createHolographicWindowDisplay({
140
+ width=1.5, height=1, position=new THREE.Vector3()
141
+ } = {}) {
142
+ const geom = new THREE.PlaneGeometry(width, height);
143
+ const mat = new THREE.MeshBasicMaterial({
144
+ color:0xffffff, transparent:true, opacity:0.6
145
+ });
146
+ const disp = new THREE.Mesh(geom, mat);
147
+ disp.position.copy(position);
148
+ return disp;
149
+ }
150
+ function createGreebleSet({
151
+ count=10, scale=0.2, position=new THREE.Vector3()
152
+ } = {}) {
153
+ const grp = new THREE.Group();
154
+ for(let i=0; i<count; i++){
155
+ const dx = (Math.random()-0.5)*2;
156
+ const dy = (Math.random()-0.5)*2;
157
+ const dz = (Math.random()-0.5)*0.2;
158
+ const g = new THREE.Mesh(
159
+ new THREE.BoxGeometry(scale, scale, scale),
160
+ new THREE.MeshStandardMaterial({ color:0x444444, roughness:0.6, metalness:0.4 })
161
+ );
162
+ g.position.copy(position).add(new THREE.Vector3(dx,dy,dz));
163
+ grp.add(g);
164
+ }
165
+ return grp;
166
+ }
167
+ function createCyberpunkKit() {
168
+ const base = createObjectBase("Cyberpunk City Builder Kit");
169
+ const kit = new THREE.Group();
170
+ Object.assign(kit, base);
171
+ kit.add(createCyberpunkWallPanel({ position:new THREE.Vector3(0,1,0) }));
172
+ kit.add(createRooftopACUnit({ position:new THREE.Vector3(3,0.5,0) }));
173
+ kit.add(createHolographicWindowDisplay({ position:new THREE.Vector3(6,1,0) }));
174
+ kit.add(createGreebleSet({ position:new THREE.Vector3(9,0,0) }));
175
+ return kit;
176
+ }
177
 
178
+ // ─── 2. POLYGON – Fantasy Kingdom Pack ───────────────────────────────────
179
+ function createKingFigure({ position=new THREE.Vector3() } = {}) {
180
+ const base = createObjectBase("POLYGON - Fantasy Kingdom Pack");
181
+ const mesh = new THREE.Mesh(
182
+ new THREE.CylinderGeometry(0.3,0.5,1.8,6),
183
+ new THREE.MeshStandardMaterial({ color:0xffd700, metalness:0.9, roughness:0.2 })
184
+ );
185
+ Object.assign(mesh, base);
186
+ mesh.position.copy(position);
187
+ return mesh;
188
+ }
189
+ function createSoldierFigure({ position=new THREE.Vector3() } = {}) {
190
+ const base = createObjectBase("POLYGON - Fantasy Kingdom Pack");
191
+ const mesh = new THREE.Mesh(
192
+ new THREE.BoxGeometry(0.5,1.6,0.4),
193
+ new THREE.MeshStandardMaterial({ color:0x888888, metalness:0.6, roughness:0.4 })
194
+ );
195
+ Object.assign(mesh, base);
196
+ mesh.position.copy(position);
197
+ return mesh;
198
+ }
199
+ function createMageFigure({ position=new THREE.Vector3() } = {}) {
200
+ const base = createObjectBase("POLYGON - Fantasy Kingdom Pack");
201
+ const mesh = new THREE.Mesh(
202
+ new THREE.ConeGeometry(0.4,1.5,6),
203
+ new THREE.MeshStandardMaterial({ color:0x9933ff, roughness:0.5 })
204
+ );
205
+ Object.assign(mesh, base);
206
+ mesh.position.copy(position);
207
+ return mesh;
208
+ }
209
+ function createFantasyKingdomPack() {
210
+ const grp = new THREE.Group();
211
+ Object.assign(grp, createObjectBase("POLYGON - Fantasy Kingdom Pack"));
212
+ grp.add(createKingFigure({ position:new THREE.Vector3(0,0,0) }));
213
+ grp.add(createSoldierFigure({ position:new THREE.Vector3(2,0,0) }));
214
+ grp.add(createMageFigure({ position:new THREE.Vector3(4,0,0) }));
215
+ return grp;
216
+ }
217
 
218
+ // ─── 3. HEROIC FANTASY CREATURES FULL PACK VOLΒ 1 ─────────────────────────
219
+ function createDemonicCreatureBase({ position=new THREE.Vector3() }={}) {
220
+ const base = createObjectBase("HEROIC FANTASY CREATURES FULL PACK VOLΒ 1");
221
+ const mesh = new THREE.Mesh(
222
+ new THREE.SphereGeometry(0.8,8,6),
223
+ new THREE.MeshStandardMaterial({ color:0x550000, roughness:0.7 })
224
+ );
225
+ Object.assign(mesh, base);
226
+ mesh.position.copy(position);
227
+ return mesh;
228
+ }
229
+ function createFantasyAnimalBase({ position=new THREE.Vector3() }={}) {
230
+ const base = createObjectBase("HEROIC FANTASY CREATURES FULL PACK VOLΒ 1");
231
+ const mesh = new THREE.Mesh(
232
+ new THREE.BoxGeometry(1.2,0.6,2),
233
+ new THREE.MeshStandardMaterial({ color:0x665533, roughness:0.8 })
234
+ );
235
+ Object.assign(mesh, base);
236
+ mesh.position.copy(position);
237
+ return mesh;
238
+ }
239
+ function createFantasyLizardBase({ position=new THREE.Vector3() }={}) {
240
+ const base = createObjectBase("HEROIC FANTASY CREATURES FULL PACK VOLΒ 1");
241
+ const mesh = new THREE.Mesh(
242
+ new THREE.ConeGeometry(0.3,1,6),
243
+ new THREE.MeshStandardMaterial({ color:0x339933, roughness:0.6 })
244
+ );
245
+ Object.assign(mesh, base);
246
+ mesh.position.copy(position);
247
+ return mesh;
248
+ }
249
+ function createLivingDeadBase({ position=new THREE.Vector3() }={}) {
250
+ const base = createObjectBase("HEROIC FANTASY CREATURES FULL PACK VOLΒ 1");
251
+ const mesh = new THREE.Mesh(
252
+ new THREE.BoxGeometry(0.8,1.8,0.5),
253
+ new THREE.MeshStandardMaterial({ color:0x777777, roughness:0.9 })
254
+ );
255
+ Object.assign(mesh, base);
256
+ mesh.position.copy(position);
257
+ return mesh;
258
+ }
259
+ function createFantasyVillainBase({ position=new THREE.Vector3() }={}) {
260
+ const base = createObjectBase("HEROIC FANTASY CREATURES FULL PACK VOLΒ 1");
261
+ const mesh = new THREE.Mesh(
262
+ new THREE.OctahedronGeometry(0.9,0),
263
+ new THREE.MeshStandardMaterial({ color:0x220022, roughness:0.7 })
264
+ );
265
+ Object.assign(mesh, base);
266
+ mesh.position.copy(position);
267
+ return mesh;
268
+ }
269
+ function createMythologicalCreatureBase({ position=new THREE.Vector3() }={}) {
270
+ const base = createObjectBase("HEROIC FANTASY CREATURES FULL PACK VOLΒ 1");
271
+ const mesh = new THREE.Mesh(
272
+ new THREE.TorusKnotGeometry(0.5,0.2,100,16),
273
+ new THREE.MeshStandardMaterial({ color:0xdddd00, roughness:0.5 })
274
+ );
275
+ Object.assign(mesh, base);
276
+ mesh.position.copy(position);
277
+ return mesh;
278
+ }
279
+ function createHeroicFantasyCreaturesPack() {
280
+ const grp = new THREE.Group();
281
+ Object.assign(grp, createObjectBase("HEROIC FANTASY CREATURES FULL PACK VOLΒ 1"));
282
+ grp.add(createDemonicCreatureBase({ position:new THREE.Vector3(0,0,0) }));
283
+ grp.add(createFantasyAnimalBase({ position:new THREE.Vector3(3,0,0) }));
284
+ grp.add(createFantasyLizardBase({ position:new THREE.Vector3(6,0,0) }));
285
+ grp.add(createLivingDeadBase({ position:new THREE.Vector3(9,0,0) }));
286
+ grp.add(createFantasyVillainBase({ position:new THREE.Vector3(12,0,0) }));
287
+ grp.add(createMythologicalCreatureBase({ position:new THREE.Vector3(15,0,0) }));
288
+ return grp;
289
+ }
290
 
291
+ // ─── 4. POLYGON – Apocalypse Pack (Low Poly) ─────────────────────────────
292
+ function createZombieFigure({ position=new THREE.Vector3() }={}) {
293
+ const base = createObjectBase("POLYGON - Apocalypse Pack");
294
+ const mesh = new THREE.Mesh(
295
+ new THREE.BoxGeometry(0.5,1.7,0.4),
296
+ new THREE.MeshStandardMaterial({ color:0x445544, roughness:0.9 })
297
+ );
298
+ Object.assign(mesh, base);
299
+ mesh.position.copy(position);
300
+ return mesh;
301
+ }
302
+ function createSurvivorFigure({ position=new THREE.Vector3() }={}) {
303
+ const base = createObjectBase("POLYGON - Apocalypse Pack");
304
+ const mesh = new THREE.Mesh(
305
+ new THREE.BoxGeometry(0.5,1.7,0.4),
306
+ new THREE.MeshStandardMaterial({ color:0x885522, roughness:0.7 })
307
+ );
308
+ Object.assign(mesh, base);
309
+ mesh.position.copy(position);
310
+ return mesh;
311
+ }
312
+ function createBackpack({ position=new THREE.Vector3() }={}) {
313
+ const base = createObjectBase("POLYGON - Apocalypse Pack");
314
+ const mesh = new THREE.Mesh(
315
+ new THREE.BoxGeometry(0.6,0.8,0.3),
316
+ new THREE.MeshStandardMaterial({ color:0x333333, roughness:0.8 })
317
+ );
318
+ Object.assign(mesh, base);
319
+ mesh.position.copy(position);
320
+ return mesh;
321
+ }
322
+ function createMakeshiftArmor({ position=new THREE.Vector3() }={}) {
323
+ const base = createObjectBase("POLYGON - Apocalypse Pack");
324
+ const mesh = new THREE.Mesh(
325
+ new THREE.BoxGeometry(1,1.2,0.2),
326
+ new THREE.MeshStandardMaterial({ color:0x555555, roughness:0.9 })
327
+ );
328
+ Object.assign(mesh, base);
329
+ mesh.position.copy(position);
330
+ return mesh;
331
+ }
332
+ function createBuggyFrame({ position=new THREE.Vector3() }={}) {
333
+ const base = createObjectBase("POLYGON - Apocalypse Pack");
334
+ const mesh = new THREE.Mesh(
335
+ new THREE.BoxGeometry(2,0.5,1.2),
336
+ new THREE.MeshStandardMaterial({ color:0x666666, roughness:0.7 })
337
+ );
338
+ Object.assign(mesh, base);
339
+ mesh.position.copy(position);
340
+ return mesh;
341
+ }
342
+ function createApocalypsePack() {
343
+ const grp = new THREE.Group();
344
+ Object.assign(grp, createObjectBase("POLYGON - Apocalypse Pack"));
345
+ grp.add(createZombieFigure({ position:new THREE.Vector3(0,0,0) }));
346
+ grp.add(createSurvivorFigure({ position:new THREE.Vector3(3,0,0) }));
347
+ grp.add(createBackpack({ position:new THREE.Vector3(6,0,0) }));
348
+ grp.add(createMakeshiftArmor({ position:new THREE.Vector3(9,0,0) }));
349
+ grp.add(createBuggyFrame({ position:new THREE.Vector3(12,0,0) }));
350
+ return grp;
351
+ }
352
 
353
+ // ─── Existing loader that places from Python data ────────────────────────
354
+ function createAndPlaceObject(objData, isNewObject) {
355
+ let loadedObject = null;
356
+ switch (objData.type) {
357
+ case "Simple House": loadedObject = createSimpleHouse(); break;
358
+ case "Tree": loadedObject = createTree(); break;
359
+ case "Rock": loadedObject = createRock(); break;
360
+ case "Fence Post": loadedObject = createFencePost(); break;
361
+ case "Cyberpunk City Builder Kit":
362
+ loadedObject = createCyberpunkKit(); break;
363
+ case "POLYGON - Fantasy Kingdom Pack":
364
+ loadedObject = createFantasyKingdomPack(); break;
365
+ case "HEROIC FANTASY CREATURES FULL PACK VOLΒ 1":
366
+ loadedObject = createHeroicFantasyCreaturesPack(); break;
367
+ case "POLYGON - Apocalypse Pack":
368
+ loadedObject = createApocalypsePack(); break;
369
+ default: console.warn("Unknown object type in data:", objData.type); break;
370
+ }
371
+ if (loadedObject) {
372
+ // position & rotation from CSV or JSON
373
+ if (objData.position && objData.position.x !== undefined) {
374
+ loadedObject.position.set(objData.position.x, objData.position.y, objData.position.z);
375
+ } else if (objData.pos_x !== undefined) {
376
+ loadedObject.position.set(objData.pos_x, objData.pos_y, objData.pos_z);
377
+ }
378
+ if (objData.rotation) {
379
+ loadedObject.rotation.set(
380
+ objData.rotation._x, objData.rotation._y, objData.rotation._z
381
+ );
382
+ } else if (objData.rot_x !== undefined) {
383
+ loadedObject.rotation.set(
384
+ objData.rot_x, objData.rot_y, objData.rot_z
385
+ );
386
+ }
387
+ scene.add(loadedObject);
388
+ if (isNewObject) newlyPlacedObjects.push(loadedObject);
389
+ return loadedObject;
390
+ }
391
+ return null;
392
+ }
393
 
394
+ // ─── Setup, Animate etc. (unchanged) ────────────────────────────────────
395
+ function init() {
396
+ scene = new THREE.Scene();
397
+ scene.background = new THREE.Color(0xabcdef);
398
+ const aspect = window.innerWidth / window.innerHeight;
399
+ camera = new THREE.PerspectiveCamera(60, aspect, 0.1, 4000);
400
+ camera.position.set(0,15,20);
401
+ camera.lookAt(0,0,0);
402
+ scene.add(camera);
403
+ setupLighting();
404
+ setupInitialGround();
405
+ setupPlayer();
406
+ raycaster = new THREE.Raycaster();
407
+ mouse = new THREE.Vector2();
408
+ renderer = new THREE.WebGLRenderer({ antialias: true });
409
+ renderer.setSize(window.innerWidth, window.innerHeight);
410
+ renderer.shadowMap.enabled = true;
411
+ renderer.shadowMap.type = THREE.PCFSoftShadowMap;
412
+ document.body.appendChild(renderer.domElement);
413
+ loadInitialObjects();
414
+ restoreUnsavedState();
415
+ document.addEventListener('mousemove', onMouseMove, false);
416
+ document.addEventListener('click', onDocumentClick, false);
417
+ window.addEventListener('resize', onWindowResize, false);
418
+ document.addEventListener('keydown', onKeyDown);
419
+ document.addEventListener('keyup', onKeyUp);
420
+ window.teleportPlayer = teleportPlayer;
421
+ window.getSaveDataAndPosition = getSaveDataAndPosition;
422
+ window.resetNewlyPlacedObjects = resetNewlyPlacedObjects;
423
+ console.log("Three.js Initialized. World ready.");
424
+ animate();
425
+ }
426
 
427
+ // ─── Click to place logic ───────────────────────────────────────────────
428
+ function onDocumentClick(event) {
429
+ if (selectedObjectType === "None") return;
430
+ const grounds = Object.values(groundMeshes);
431
+ if (!grounds.length) return;
432
+ raycaster.setFromCamera(mouse, camera);
433
+ const hits = raycaster.intersectObjects(grounds);
434
+ if (hits.length) {
435
+ const pt = hits[0].point;
436
+ let newObj = null;
437
+ switch (selectedObjectType) {
438
+ case "Simple House": newObj = createSimpleHouse(); break;
439
+ case "Tree": newObj = createTree(); break;
440
+ case "Rock": newObj = createRock(); break;
441
+ case "Fence Post": newObj = createFencePost(); break;
442
+ case "Cyberpunk City Builder Kit":
443
+ newObj = createCyberpunkKit(); break;
444
+ case "POLYGON - Fantasy Kingdom Pack":
445
+ newObj = createFantasyKingdomPack(); break;
446
+ case "HEROIC FANTASY CREATURES FULL PACK VOLΒ 1":
447
+ newObj = createHeroicFantasyCreaturesPack(); break;
448
+ case "POLYGON - Apocalypse Pack":
449
+ newObj = createApocalypsePack(); break;
450
+ default: return;
451
+ }
452
+ if (newObj) {
453
+ newObj.position.copy(pt);
454
+ newObj.position.y += 0.01;
455
+ scene.add(newObj);
456
+ newlyPlacedObjects.push(newObj);
457
+ saveUnsavedState();
458
+ console.log(`Placed new ${selectedObjectType}. Unsaved total: ${newlyPlacedObjects.length}`);
459
+ }
460
+ }
461
+ }
462
 
463
+ // ─── Rest of your functions: save/restore state, movement, animate, etc. ──
464
+ function saveUnsavedState() { /* unchanged */ }
465
+ function restoreUnsavedState() { /* unchanged */ }
466
+ function clearUnsavedState() { /* unchanged */ }
467
+ function teleportPlayer(x,z) { /* unchanged */ }
468
+ function getSaveDataAndPosition() { /* unchanged */ }
469
+ function resetNewlyPlacedObjects() { /* unchanged */ }
470
+ function updatePlayerMovement() { /* unchanged */ }
471
+ function checkAndExpandGround() { /* unchanged */ }
472
+ function updateCamera() { /* unchanged */ }
473
+ function onMouseMove(event) {
474
+ mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
475
+ mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
476
+ }
477
+ function onKeyDown(e) { keysPressed[e.code] = true; }
478
+ function onKeyUp(e) { keysPressed[e.code] = false; }
479
+ function onWindowResize() {
480
+ camera.aspect = window.innerWidth / window.innerHeight;
481
+ camera.updateProjectionMatrix();
482
+ renderer.setSize(window.innerWidth, window.innerHeight);
483
+ }
484
+ function animate() {
485
+ requestAnimationFrame(animate);
486
+ updatePlayerMovement();
487
+ updateCamera();
488
+ renderer.render(scene, camera);
489
+ }
490
 
491
+ // ─── Kick it off ────────────────────────────────────────────────────────
492
+ init();
493
+ </script>
 
 
 
 
494
  </body>
495
+ </html>