File size: 6,141 Bytes
c50b518
926eeea
 
 
c50b518
 
 
 
926eeea
 
c50b518
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
926eeea
c50b518
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Three.js Infinite World</title>
  <style>body{margin:0;overflow:hidden;}canvas{display:block;}</style>
  <!-- State injected here -->
</head>
<body>
  <script type="importmap">
  {"imports":{"three":"https://unpkg.com/[email protected]/build/three.module.js","three/addons/":"https://unpkg.com/[email protected]/examples/jsm/"}}
  </script>
  <script type="module">
    import * as THREE from 'three';

    let scene, camera, renderer, playerMesh, raycaster, mouse;
    let newlyPlacedObjects = [];
    const groundMeshes = {}, placeholderPlots=new Set();
    const SESSION_KEY='unsavedInfiniteWorldState';

    function init(){
      scene=new THREE.Scene(); scene.background=new THREE.Color(0xabcdef);
      camera=new THREE.PerspectiveCamera(60,window.innerWidth/window.innerHeight,0.1,4000);
      camera.position.set(0,15,20);camera.lookAt(0,0,0);
      setupLighting(); setupGround(); setupPlayer();
      raycaster=new THREE.Raycaster(); mouse=new THREE.Vector2();
      renderer=new THREE.WebGLRenderer({antialias:true});
      renderer.setSize(window.innerWidth,window.innerHeight);
      renderer.shadowMap.enabled=true; document.body.appendChild(renderer.domElement);
      loadObjects(); restoreUnsavedState();
      window.addEventListener('mousemove',onMouseMove,false);
      window.addEventListener('click',onDocClick,false);
      window.addEventListener('keydown',onKeyDown);
      window.addEventListener('keyup',onKeyUp);
      window.teleportPlayer=teleportPlayer;
      window.getSaveDataAndPosition=getSaveDataAndPosition;
      window.resetNewlyPlacedObjects=resetNewlyPlacedObjects;
      setInterval(pollGameState,5000);
      animate();
    }

    function setupLighting(){
      scene.add(new THREE.AmbientLight(0xffffff,0.5));
      const dir=new THREE.DirectionalLight(0xffffff,1);dir.position.set(50,150,100);
      dir.castShadow=true;scene.add(dir);
    }

    function setupGround(){
      const plots=window.PLOTS_METADATA||[];
      (plots.length?plots:[{grid_x:0,grid_z:0}]).forEach(p=>createGround(p.grid_x,p.grid_z,false));
    }

    function createGround(x,z,ph=false){
      const key=`${x}_${z}`; if(groundMeshes[key])return;
      const geo=new THREE.PlaneGeometry(window.PLOT_WIDTH,window.PLOT_DEPTH);
      const mat=new THREE.MeshStandardMaterial({color:ph?0x448844:0x55aa55,side:THREE.DoubleSide});
      const m=new THREE.Mesh(geo,mat); m.rotation.x=-Math.PI/2;
      m.position.set(x*window.PLOT_WIDTH+window.PLOT_WIDTH/2,-0.05,z*window.PLOT_DEPTH+window.PLOT_DEPTH/2);
      scene.add(m); groundMeshes[key]=m; if(ph)placeholderPlots.add(key);
    }

    function setupPlayer(){
      const geo=new THREE.CapsuleGeometry(0.4,0.8,4,8);
      playerMesh=new THREE.Mesh(geo,new THREE.MeshStandardMaterial({color:0x0000ff,roughness:0.6}));
      playerMesh.position.set(window.PLOT_WIDTH/2,1.2,window.PLOT_DEPTH/2);
      scene.add(playerMesh);
    }

    function loadObjects(){
      (window.ALL_INITIAL_OBJECTS||[]).forEach(d=>createAndPlace(d,false));
    }

    function createAndPlace(d,newObj){
      const obj=window.KITS[d.type]?createPrefabKit(d.type):null;
      if(!obj) return;
      obj.position.set(d.pos_x||d.position.x,d.pos_y||d.position.y,d.pos_z||d.position.z);
      scene.add(obj); if(newObj)newlyPlacedObjects.push(obj);
    }

    function createPrefabKit(name){
      const kit=window.KITS[name]||{}; const grp=new THREE.Group();
      kit.modules?.forEach(m=>{const md=createModule(m); if(md)grp.add(md)});
      return grp;
    }

    function createModule(n){
      switch(n){
        case 'Cube': return new THREE.Mesh(new THREE.BoxGeometry(1,1,1),new THREE.MeshStandardMaterial());
        case 'Sphere': return new THREE.Mesh(new THREE.SphereGeometry(0.5,16,16),new THREE.MeshStandardMaterial());
        case 'Cylinder': return new THREE.Mesh(new THREE.CylinderGeometry(0.5,0.5,1,12),new THREE.MeshStandardMaterial());
        case 'Cone': return new THREE.Mesh(new THREE.ConeGeometry(0.5,1,12),new THREE.MeshStandardMaterial());
        default: console.warn('Unknown module',n); return null;
      }
    }

    function onDocClick(ev){
      if(window.SELECTED_OBJECT_TYPE==='None')return; const candidates=Object.values(groundMeshes);
      raycaster.setFromCamera(mouse,camera); const hit=raycaster.intersectObjects(candidates);
      if(!hit.length)return; const pt=hit[0].point;
      const mesh=createPrefabKit(window.SELECTED_OBJECT_TYPE);
      if(mesh){mesh.position.copy(pt);scene.add(mesh);newlyPlacedObjects.push(mesh);saveUnsaved();}
    }

    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}}))));}
    function restoreUnsavedState(){const s=sessionStorage.getItem(SESSION_KEY); if(s){JSON.parse(s).forEach(d=>createAndPlace(d,true));}}
    function resetNewlyPlacedObjects(){sessionStorage.removeItem(SESSION_KEY);newlyPlacedObjects=[];}

    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}}));
      const pos={x:playerMesh.position.x,y:playerMesh.position.y,z:playerMesh.position.z};return JSON.stringify({playerPosition:pos,objectsToSave:objs});
    }

    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);}
    function pollGameState(){console.log('Polling',window.GAME_STATE);}

    function onMouseMove(e){mouse.x=(e.clientX/window.innerWidth)*2-1;mouse.y=-(e.clientY/window.innerHeight)*2+1;}
    function onKeyDown(e){/* movement logic */}
    function onKeyUp(e){}

    function animate(){requestAnimationFrame(animate);renderer.render(scene,camera);}
    init();
  </script>
</body>
</html>